Skip to content
This repository was archived by the owner on Nov 8, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .github/workflows/aurora-enhancements.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ name: Aurora enhancement candidate
on:
pull_request:
paths:
- "mseries/ci/**"
- "mseries/aurora/candidate.py"
- "mseries/aurora/release.py"
- "mseries/aurora/release.json"
Expand All @@ -22,8 +23,8 @@ permissions:
contents: read

concurrency:
group: aurora-enhancements-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
build:
Expand All @@ -37,11 +38,10 @@ jobs:
steps:
- uses: actions/checkout@v7
- name: Install native build tools
timeout-minutes: 20
run: |
sudo apt-get update
sudo apt-get install -y bc bison build-essential cpio flex libelf-dev libssl-dev \
pahole clang-22 llvm-22 lld-22 libclang-22-dev bindgen zstd device-tree-compiler kmod rsync
rustup toolchain install 1.93.1 --profile minimal --component rust-src --component rustfmt
sudo --preserve-env=GITHUB_ACTIONS python3 mseries/ci/install_aurora_tools.py
timeout --kill-after=15s 300 rustup toolchain install 1.93.1 --profile minimal --component rust-src --component rustfmt
echo /usr/lib/llvm-22/bin >> "$GITHUB_PATH"
- name: Prepare pinned sources and patches
env:
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/aurora-kernel.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ on:
permissions:
contents: read

# New commits replace only checks for this workflow and PR. Publishing is not preempted.
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
package:
runs-on: ubuntu-26.04-arm
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/message-pins.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ on:
- mseries/phone-setup/**
- mseries/install-phone-setup.sh
- .github/workflows/message-pins.yml
# New commits replace only checks for this workflow and PR. Publishing is not preempted.
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
linux-import:
name: Native pin import and private keyring (Linux)
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/mseries-debs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ permissions:
contents: read

concurrency:
group: mseries-debs-${{ github.ref }}
cancel-in-progress: false
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

env:
# stable for main and for a release PR into main; dev for everything else.
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/mseries-diskspace-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ permissions:
contents: read

concurrency:
group: mseries-diskspace-app-${{ github.ref }}
cancel-in-progress: true
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
app:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/mseries-updates-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ permissions:
contents: read

concurrency:
group: mseries-updates-app-${{ github.ref }}
cancel-in-progress: true
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
app:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/proposed-9.0.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ on:
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
call-reusable-build:
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/validate-mseries.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ on:
- "docs/local-build-status.html"
- ".github/workflows/**"

# New commits replace only checks for this workflow and PR. Publishing is not preempted.
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
teams-web:
name: Teams desktop launcher contracts
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/verification-codes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ on:
- mseries/phone-setup/*code*.py
- mseries/install-verification-codes.sh
- .github/workflows/verification-codes.yml
# New commits replace only checks for this workflow and PR. Publishing is not preempted.
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
codes:
name: Private native field insertion and shortcut preservation (arm64)
Expand Down
38 changes: 38 additions & 0 deletions .github/workflows/workflow-policy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Workflow cancellation policy

on:
pull_request:
paths:
- '.github/workflows/**'
- 'mseries/ci/**'
push:
branches: [dev]
paths:
- '.github/workflows/**'
- 'mseries/ci/**'
workflow_dispatch:

permissions:
contents: read

# New commits replace only checks for this workflow and PR. Publishing is not preempted.
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
policy:
name: Verify superseded PR cancellation
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7.1.0
with:
python-version: '3.12'
- name: Install YAML parser
run: python3 -m pip install --disable-pip-version-check -r mseries/ci/requirements.txt
- name: Check all workflows and regression cases
run: |
python3 mseries/ci/workflow_policy.py
python3 -B -m unittest discover -s mseries/ci -p 'test_*.py' -v
35 changes: 35 additions & 0 deletions mseries/OS9-HANDOFF.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,38 @@
## Bounded Aurora CI dependency downloads — 2026-10-22

The unified72 check spent 47m17s fetching 144 MB from the Azure Ubuntu
mirror at 50.9 kB/s, before kernel compilation. Aurora CI now first uses the
official HTTPS Ubuntu ports archive, then HTTPS Azure as a fallback. Metadata
fetches are limited to 60 seconds and package downloads to 240 seconds per
mirror. Packages retain normal Ubuntu signature verification. Installation
starts only after a successful download and uses `--no-download`; the timeout
does not interrupt dpkg to switch mirrors. Runner source files are not modified.
Rustup has a five-minute bound and the dependency step a 20-minute bound.

Validation: 17 CI regression tests passed, including slow/erroring mirrors,
failed metadata, no installation after failed downloads, preserved signed
sources, host execution refusal, and workflow wiring. Full GitHub kernel build
and package inspection still need to pass on the updated PR head before merge.
No desktop installation, kernel change or service restart.

## Superseded PR CI cancellation — 2026-10-22

Source-only: all ten PR workflows now group by workflow name plus PR number
(with branch ref as the non-PR fallback), cancelling in-progress runs only for
`pull_request` events. This prevents old revisions of one PR from accumulating
runner jobs across the shared account, as happened while Rupert awaited deployment.
Different PRs/workflows stay independent; pushes/manual runs are not preempted.
The package publisher keeps its global `mseries-apt-publish` lock and release-only
workflows retain their existing policy. A lightweight workflow checks the policy
on every workflow change, including future PR workflows.

Validation: nine regression tests passed, including removal/misconfiguration
mutations for every PR workflow, duplicate workflow names, group isolation, and
release/publisher lock preservation. No desktop install or service restart.
Next: merge into dev after CI; existing branches need a new PR run using this
policy. Already-running legacy checks are not retroactively cancelled. This
removes superseded-run backlog, not contention from legitimate current builds.

## Signal and Messages: INCOMPLETE / DEFERRED — 2026-10-22

Owner explicitly deferred both apps and rejected the current combined launcher
Expand Down
35 changes: 35 additions & 0 deletions mseries/ROADMAP.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,38 @@
## Bounded Aurora CI dependency downloads — 2026-10-22

The unified72 check spent 47m17s fetching 144 MB from the Azure Ubuntu
mirror at 50.9 kB/s, before kernel compilation. Aurora CI now first uses the
official HTTPS Ubuntu ports archive, then HTTPS Azure as a fallback. Metadata
fetches are limited to 60 seconds and package downloads to 240 seconds per
mirror. Packages retain normal Ubuntu signature verification. Installation
starts only after a successful download and uses `--no-download`; the timeout
does not interrupt dpkg to switch mirrors. Runner source files are not modified.
Rustup has a five-minute bound and the dependency step a 20-minute bound.

Validation: 17 CI regression tests passed, including slow/erroring mirrors,
failed metadata, no installation after failed downloads, preserved signed
sources, host execution refusal, and workflow wiring. Full GitHub kernel build
and package inspection still need to pass on the updated PR head before merge.
No desktop installation, kernel change or service restart.

## Superseded PR CI cancellation — 2026-10-22

Source-only: all ten PR workflows now group by workflow name plus PR number
(with branch ref as the non-PR fallback), cancelling in-progress runs only for
`pull_request` events. This prevents old revisions of one PR from accumulating
runner jobs across the shared account, as happened while Rupert awaited deployment.
Different PRs/workflows stay independent; pushes/manual runs are not preempted.
The package publisher keeps its global `mseries-apt-publish` lock and release-only
workflows retain their existing policy. A lightweight workflow checks the policy
on every workflow change, including future PR workflows.

Validation: nine regression tests passed, including removal/misconfiguration
mutations for every PR workflow, duplicate workflow names, group isolation, and
release/publisher lock preservation. No desktop install or service restart.
Next: merge into dev after CI; existing branches need a new PR run using this
policy. Already-running legacy checks are not retroactively cancelled. This
removes superseded-run backlog, not contention from legitimate current builds.

## Signal and Messages: INCOMPLETE / DEFERRED — 2026-10-22

Owner explicitly deferred both apps and rejected the current combined launcher
Expand Down
48 changes: 48 additions & 0 deletions mseries/ci/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# PR workflow cancellation

Every workflow triggered by `pull_request` uses a workflow-scoped, PR-scoped
concurrency group. New revisions cancel the whole older run, including queued
jobs and its matrix. Different workflows and PRs do not cancel each other.
Cancellation is conditional on the PR event: manual and publishing runs keep
serialization without being interrupted. The package publisher's separate
cross-branch lock remains unchanged.

Run the same checks as CI:

```sh
python3 -m pip install -r mseries/ci/requirements.txt
python3 mseries/ci/workflow_policy.py
python3 -B -m unittest discover -s mseries/ci -p 'test_*.py' -v
```

The policy checks every PR workflow, so adding a new workflow without cancellation
fails. It also requires unique workflow names because the name is part of the
concurrency group. Regression tests deliberately remove or misconfigure policies
and check group isolation and preservation of publisher/release serialization.

This takes effect for runs using the updated workflow revision. It does not
cancel existing legacy runs retroactively or reserve runner capacity for Rupert;
current builds still share the GitHub account's runner capacity. GitHub's
[concurrency documentation](https://docs.github.com/en/actions/how-tos/write-workflows/choose-when-workflows-run/control-workflow-concurrency)
describes the cancellation semantics used here.

For a live cancellation check, record the current PR run IDs, then push a newer
commit to the same PR. Confirm the older active runs finish as `cancelled` and
replacement runs refer to the new head. Record another PR's run before and after
to check isolation. A manual package run on the test branch with `publish=false`
can serve as the non-PR control: a PR push must not cancel it. Never enable
publishing just to test cancellation. The regression suite separately checks
that the publishing job excludes PR events and retains its non-cancelling global
lock. Stop an unfinished synthetic manual control after recording the result.

## Aurora dependency downloads

`install_aurora_tools.py` runs only as root on an ephemeral Ubuntu ARM64 GitHub
runner. It uses a temporary signed Ubuntu source configuration, trying the
official HTTPS ports archive before HTTPS Azure. Existing runner sources and
third-party repositories are untouched. Each mirror gets 60 seconds for metadata
and 240 seconds for a download-only install, with connection timeouts and limited
retries. Failure switches mirrors before dpkg begins. Once verified archives are
ready, installation runs with `--no-download`. Both mirror failures stop CI.
Normal Ubuntu archive signature verification remains enabled. Rustup and the
overall workflow dependency step have separate time limits.
69 changes: 69 additions & 0 deletions mseries/ci/install_aurora_tools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#!/usr/bin/env python3
"""Bounded, signed Ubuntu ARM64 dependency downloads for ephemeral CI runners."""
import os
from pathlib import Path
import re
import subprocess
import tempfile

MIRRORS = ('https://ports.ubuntu.com/ubuntu-ports', 'https://azure.archive.ubuntu.com/ubuntu')
PACKAGES = ('bc bison build-essential cpio flex libelf-dev libssl-dev pahole clang-22 llvm-22 '
'lld-22 libclang-22-dev bindgen zstd device-tree-compiler kmod rsync').split()


def sources(codename, mirror):
if not re.fullmatch(r'[a-z][a-z0-9]+', codename) or mirror not in MIRRORS:
raise ValueError('Unsupported Ubuntu source')
return (f'Types: deb\nURIs: {mirror}\nSuites: {codename} {codename}-updates '
f'{codename}-backports {codename}-security\nComponents: main restricted universe multiverse\n'
'Architectures: arm64\nSigned-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg\n')


def invoke(command):
print('+ ' + ' '.join(command), flush=True)
return subprocess.run(command, stdin=subprocess.DEVNULL, check=False,
env={**os.environ, 'DEBIAN_FRONTEND': 'noninteractive'}).returncode


def install(codename, architecture, root, run=invoke):
if architecture != 'arm64':
raise ValueError('This installer is only for Ubuntu ARM64 CI')
root = Path(root)
root.chmod(0o755) # APT's unprivileged downloader must traverse its scratch directory.
source_dir = root / 'sources'
source_dir.mkdir()
lists = root / 'lists'
lists.mkdir()
options = ['-o', 'Dir::Etc::sourcelist=/dev/null', '-o', f'Dir::Etc::sourceparts={source_dir}',
'-o', f'Dir::State::lists={lists}', '-o', 'Acquire::Retries=1',
'-o', 'Acquire::http::Timeout=30', '-o', 'Acquire::https::Timeout=30',
'-o', 'APT::Update::Error-Mode=any', '-o', 'Dpkg::Use-Pty=0']
apt = ['apt-get', *options]
for mirror in MIRRORS:
(source_dir / 'ubuntu.sources').write_text(sources(codename, mirror))
print(f'Downloading signed dependencies from {mirror}', flush=True)
if run(['timeout', '--kill-after=15s', '60', *apt, 'update']) != 0:
continue
# Bound the download, not dpkg: a slow mirror must never strand an install.
if run(['timeout', '--kill-after=15s', '240', *apt, '--download-only', '--no-install-recommends',
'install', '-y', *PACKAGES]) != 0:
continue
if run([*apt, '--no-download', '--no-install-recommends', 'install', '-y', *PACKAGES]) != 0:
raise RuntimeError('Package installation failed after verified download')
return
raise RuntimeError('Both Ubuntu mirrors failed or exceeded the download time budget')


def main():
if os.environ.get('GITHUB_ACTIONS') != 'true' or os.geteuid() != 0:
raise RuntimeError('Run as root only on an ephemeral GitHub Actions runner')
release = dict(line.split('=', 1) for line in Path('/etc/os-release').read_text().splitlines() if '=' in line)
if release.get('ID', '').strip('"') != 'ubuntu':
raise RuntimeError('Ubuntu runner required')
architecture = subprocess.check_output(['dpkg', '--print-architecture'], text=True).strip()
with tempfile.TemporaryDirectory(prefix='aurora-apt-') as folder:
install(release['VERSION_CODENAME'].strip('"'), architecture, folder)


if __name__ == '__main__':
main()
1 change: 1 addition & 0 deletions mseries/ci/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PyYAML==6.0.3
Loading
Loading