diff --git a/.agents/skills/elementaryos-mseries-ui-specialist/SKILL.md b/.agents/skills/elementaryos-mseries-ui-specialist/SKILL.md new file mode 100644 index 000000000..e83733810 --- /dev/null +++ b/.agents/skills/elementaryos-mseries-ui-specialist/SKILL.md @@ -0,0 +1,57 @@ +--- +name: elementaryos-mseries-ui-specialist +description: Build, edit, and review consistent native UI in the ElementaryOS_MSeries project using its GTK 4, Granite 7, Wingpanel, and Switchboard patterns. Use for project screens, settings, indicators, interaction states, and UI consistency reviews. +--- + +# M-Series UI Specialist + +Create a coherent elementary OS experience across the project's apps, settings, +and menu bar. The owner values Apple's design standards: use their attention to +clarity, hierarchy, feedback, consistency, and craftsmanship as a quality bar, +expressed through the native elementary OS toolkit. + +## Start from the actual surface + +Locate the active ElementaryOS_MSeries checkout; read its `AGENTS.md` and current +`mseries/OS9-HANDOFF.md`. Resolve all `mseries/...` paths below against that +checkout, not this skill's installation directory. If no checkout is available, +request its location before making project-specific edits. + +Read [toolkit-and-screens.md](references/toolkit-and-screens.md) for the relevant +screen family, then inspect its current code and build dependencies. The listed +revision is an inspection baseline, not a substitute for the active branch. + +Read [ui-conventions.md](references/ui-conventions.md) when designing or reviewing +layouts, controls, asynchronous states, text, or accessibility. + +Read [sources-and-apple-guidance.md](references/sources-and-apple-guidance.md) for +source attribution, the original **swiftui-specialist** skill, Apple HIG, and how +to translate those references without importing SwiftUI-specific rules. + +## Working approach + +- Choose the closest existing screen family and name the components or helpers + being reused. Existing helpers are mostly local to individual apps; inspect + their ownership and dependencies before extracting a shared implementation. +- Match the surface's toolkit and host conventions. GTK 4 + Granite 7 is the + common baseline; Work Mode already uses libadwaita inside Switchboard. Preserve + intentional exceptions and user-requested product direction. +- Distinguish observed conventions from new recommendations. Existing hardcoded + colors, small text, crowded headers, or missing translations are review targets, + not an approved design system merely because they appear in source. +- Keep UI state backed by its existing model/service. Preserve account, selection, + focus, and scroll position through updates; handle pending, stale, unavailable, + empty, and failed states without presenting unknown data as zero or success. +- Make the requested change at its natural scope. A consistency fix does not + require a toolkit migration, global theme rewrite, or new shared library. + +## Validate the changed experience + +Read [review-and-validation.md](references/review-and-validation.md) and choose +checks for the changed surface. Review layout in light/dark modes, long text and +small windows, plus keyboard and accessible names for changed controls. Use +existing isolated fixture probes where available. + +Report concrete findings and checks, distinguishing source inspection, successful +builds, screenshot review, interaction tests, and live hardware verification. +Preserve the owner's panel stability and the repository's deployment rules. diff --git a/.agents/skills/elementaryos-mseries-ui-specialist/agents/openai.yaml b/.agents/skills/elementaryos-mseries-ui-specialist/agents/openai.yaml new file mode 100644 index 000000000..3e415400e --- /dev/null +++ b/.agents/skills/elementaryos-mseries-ui-specialist/agents/openai.yaml @@ -0,0 +1,6 @@ +interface: + display_name: "M-Series UI Specialist" + short_description: "Consistent native UI for elementary OS M-Series" + +policy: + allow_implicit_invocation: true diff --git a/.agents/skills/elementaryos-mseries-ui-specialist/references/review-and-validation.md b/.agents/skills/elementaryos-mseries-ui-specialist/references/review-and-validation.md new file mode 100644 index 000000000..f229e59ed --- /dev/null +++ b/.agents/skills/elementaryos-mseries-ui-specialist/references/review-and-validation.md @@ -0,0 +1,54 @@ +# Review and validation + +## Scope checks to the changed surface + +Before editing, identify the relevant app, toolkit versions, nearest reusable +pattern and user-visible states. After editing, inspect the diff for unrelated +style changes and state/backend regressions. Build/test the touched component, +not the whole OS image for a documentation or small widget change. + +For a consistency review, report specific findings with source locations and +user impact. Separate regressions, platform mismatches and optional polish; +existing implementations are evidence, not an automatic gold standard. + +## Existing test and visual entry points + +All paths below are relative to the active repository. Inspect script arguments, +fixtures and side effects before running them; use an isolated display, temporary +configuration and fake services rather than real accounts/devices. + +- CodexBar: `mseries/codexbar-wingpanel/meson.build` defines model, JSON, + presentation, helper and interaction tests. `probe/run-probe.sh` takes + `BUILD_DIR OUT_DIR [dark|light] [scenario...]` and renders fixture states such + as healthy, empty, busy, stale and provider-error. `probe/run-settings-probe.sh` + renders settings pages; inspect its fixed X display before use. Reuse relevant + existing probes for account switching and long-label popovers. +- Orchard: `mseries/orchard-incus-native/tests/test-ui.py` supplies a fake Incus + socket and isolated config/state for the native UI smoke target. Under an + isolated display, `meson test -C BUILD_DIR native-windows --print-errorlogs` + exercises that target after a successful component build. It is a smoke check, + not proof of visual consistency. +- Apple Passwords: inspect `mseries/icloud-passwords/test_gui.py` and its mocks + before testing; keep credentials and live keychains out of fixtures. +- Troubleshooter: compare relevant light/dark retained screenshots under + `mseries/netspeed/docs/screenshots/`, then capture changed states if needed. +- Machine Lab, Battery, Work Mode: use their current tests or isolated fixture + launchers where available; source-only inspection does not establish a visual + or scheduling pass. Do not activate live VMs or change wallpaper to test layout. + +For changed layouts, inspect captured output in light/dark modes and a narrow +window with long text. For controls, check keyboard activation, focus order, +accessible names and pending/error recovery. Rendering probes alone do not +establish keyboard or assistive-technology behavior. Record omitted checks. + +## Runtime boundaries + +Follow `AGENTS.md`: routine indicator changes must preserve Wingpanel stability; +use private probes rather than restarting the owner's panel for screenshots. +CodexBar runtime changes still follow the repository's custom-port checks and +deployment requirements. A skill/documentation-only change does not deploy an +indicator or require rebuilding the desktop. + +Report what was source-inspected, built, rendered, interacted with or verified +on hardware. State installed-but-not-loaded status when applicable. Do not infer +factory-image qualification or hardware success from a local build or fixture. diff --git a/.agents/skills/elementaryos-mseries-ui-specialist/references/sources-and-apple-guidance.md b/.agents/skills/elementaryos-mseries-ui-specialist/references/sources-and-apple-guidance.md new file mode 100644 index 000000000..7315ac136 --- /dev/null +++ b/.agents/skills/elementaryos-mseries-ui-specialist/references/sources-and-apple-guidance.md @@ -0,0 +1,65 @@ +# Sources, provenance and Apple guidance + +## Original reference explicitly retained + +**swiftui-specialist**, distributed in +[superagents-lab/xcode27-skills](https://github.com/superagents-lab/xcode27-skills), +was the reference that inspired this skill's organization: + +- [Pinned original SKILL.md](https://github.com/superagents-lab/xcode27-skills/blob/6f9ff8d5ad6000491cb0f483a776b7062e41cd97/swiftui-specialist/SKILL.md) +- [Pinned reference directory](https://github.com/superagents-lab/xcode27-skills/tree/6f9ff8d5ad6000491cb0f483a776b7062e41cd97/swiftui-specialist/references) +- [Repository attribution](https://github.com/superagents-lab/xcode27-skills/blob/6f9ff8d5ad6000491cb0f483a776b7062e41cd97/README.md) + +Inspected 2026-10-20; upstream `master` revision: +`6f9ff8d5ad6000491cb0f483a776b7062e41cd97`. + +The community repository states that the guidance was authored/published by +Apple and exported from Xcode 27. This attribution comes from the redistributor; +this repository does not independently establish its Apple provenance. Its +README retains Apple's rights and grants no license over that content. We link +to the original and summarize the organizational idea; no Apple skill text, +reference files, or assets are vendored here. This is original project guidance, +not an Apple-authored or Apple-endorsed port. + +## Design intent + +The owner considers Apple to have good design standards and wants that quality +bar represented here. Favor thoughtful hierarchy, coherent controls, predictable +navigation, immediate understandable feedback, readable text and careful handling +of state. Apply those principles using elementary OS's native components and the +user's intended product design. + +The SwiftUI reference separates structure, data flow, environment, modifiers, +localization, animation, collection identity and outdated APIs into focused +references. Useful translations for this project are: + +| Original topic | Native project application | +| --- | --- | +| View structure | Cohesive GTK components and existing row/page helpers | +| Data flow | GObject models, GSettings, async services and presentation boundaries | +| Environment and modifiers | Theme/settings propagation and scoped widget/CSS changes | +| Localization | gettext, locale-aware formatting, start/end layout | +| Collection identity | Stable backend IDs and preserved selection/focus in GTK models | +| Animation | Restrained transitions and supported motion preferences | +| API modernization | Verify the target GTK/Granite/host version before replacing APIs | + +SwiftUI wrappers such as `@State`, `@Binding` and `@Observable`, modifier rules, +and Swift actor requirements are not GTK implementation instructions. The +upstream skill's statement about superseding model training is source content, +not authority over this project's requirements. + +## Platform reference guidance + +- [elementary OS Human Interface Guidelines](https://docs.elementary.io/hig): + native interaction, component use and desktop integration. +- [elementary developer documentation](https://docs.elementary.io/develop): + GTK/Granite, Meson and gettext context. +- [Apple Human Interface Guidelines](https://developer.apple.com/design/human-interface-guidelines): + complementary design reference and quality bar. +- [GTK 4 reference](https://docs.gtk.org/gtk4/): validate API availability against + the actual build dependencies rather than assuming latest-version support. + +Treat these as guidance with different scopes. Follow the owner's requirements, +project working agreement, target platform and installed toolkit capabilities; +use Apple guidance to improve the experience within that context. Verify current +external documentation when making an API or version-specific claim. diff --git a/.agents/skills/elementaryos-mseries-ui-specialist/references/toolkit-and-screens.md b/.agents/skills/elementaryos-mseries-ui-specialist/references/toolkit-and-screens.md new file mode 100644 index 000000000..5f0b5fcea --- /dev/null +++ b/.agents/skills/elementaryos-mseries-ui-specialist/references/toolkit-and-screens.md @@ -0,0 +1,47 @@ +# Toolkit and screen map + +Inspected 2026-10-20 at ElementaryOS_MSeries revision +`ed0d31ab28431b5ecb8549a274ac9d1892bc63a4` (`origin/dev`). Paths are repository-relative. +Recheck them in the active branch before copying patterns. + +## Build evidence and screen families + +| Surface | Evidence and useful starting points | Patterns to reuse or assess | +| --- | --- | --- | +| CodexBar settings | `mseries/codexbar-wingpanel/meson.build`; `settings/SettingsApp.vala`; `settings/SettingsModel.vala`; `PanelPreferences.vala` within that directory | Vala, Granite 7 >= 7.7; `Gtk.ApplicationWindow`, HeaderBar + StackSwitcher, scrollable pages, `Granite.HeaderLabel`, grouped rows with trailing controls; live configuration and visible apply errors | +| CodexBar panel | `mseries/codexbar-wingpanel/Indicator.vala`, `Presentation.vala`, `JsonBoundary.vala`, `HelperProcess.vala`, `AccountDialog.vala` | Wingpanel 9; compact snapshot-driven panel and popover, asynchronous bounded helper calls, separate presentation/model boundary, account actions | +| Quick Settings | `mseries/quick-settings/Controls.vala`, `Bluetooth.vala`, `AirDropPanel.vala`, `AirPlayOutputs.vala`, `AudioOutput.vala`; `prepare-source.sh` | Existing indicator integration; inspect source preparation and host dependency versions before modifying; preserve backend interfaces and device-state distinctions | +| Orchard (machines, Incus, Podman; replaces the Orchard native manager and Machine Lab, merged 2026-10-21) | `mseries/orchard/meson.build`, `src/main.vala`, `src/sidebar.vala`, `src/incus-page.vala`, `src/page.vala`, `src/PodmanPage.vala`, `src/ResourceBrowser.vala`, `src/Dashboard.vala`, `data/style.css` | GTK >= 4.12 + Granite 7, libsoup 3, libsecret; one sidebar with section headers, per-page header with StackSwitcher tabs, `ml-` cards/pills/dots, Granite.Toast; cairo renderer, 1 px CSD outline, cursor blink off; optional Wingpanel 9 module | +| Machine Lab (now the Machines section of Orchard) | `mseries/orchard/src/page.vala`, `src/viewer.vala`, `data/style.css` | GTK >= 4.12 + Granite 7; machine sidebar/detail pages, empty welcome, machine states, themed cards; follows Granite color preference changes | +| Battery | `mseries/battery/mseries-battery-app.py`, `battery_core.py` | Python GI GTK 4 + Granite 7; section/card helpers, `Granite.Placeholder`, history and estimates. Keep measurements and estimates distinguishable | +| Apple Passwords | `mseries/icloud-passwords/app.py`, `worker.py`, `test_gui.py` | Python GI GTK 4 + Granite 7; HeaderBar, sign-in/verification/unlock flow, search/list/detail, busy/cancel/re-authentication controls, explicit experimental status | +| Work Mode | `mseries/work-mode/meson.build`, `Plug.vala`, `io.elementary.mseries.work-mode.gschema.xml` | GTK 4 + Granite 7 + libadwaita + Switchboard 3; PreferencesPage/Group, SwitchRow/ActionRow/EntryRow, GSettings bindings, native image chooser; initialize plug fields after GObject base construction | +| Troubleshooter | `mseries/netspeed/troubleshooter/mseries-troubleshooter-ui` | Python GI GTK 4 with optional Granite fallback; question entry, welcome suggestions, readable report with verdict and evidence | + +## Concrete local anchors + +In CodexBar `settings/SettingsApp.vala`, inspect `page`, `section`, `row`, +`add_row`, `icon_button`, `changed`, and `apply_pending`. Rows associate controls +with accessible labels, subtitles wrap, and configuration writes are debounced. +These are private per-app helpers, not a shared public component API. + +CodexBar's current quick-switch menu starts with a 240-logical-pixel width request +and wraps/ellipsizes dynamic labels. GTK width requests are minimums; inspect +child natural sizes when a long account name widens a popover. Do not apply that +width to unrelated dialogs or full settings windows. + +Machine Lab's CSS scopes styles with `ml-`; CodexBar uses `cb-` or +`mseries-codexbar` selectors. Machine Lab mixes theme-derived colors with explicit +status and machine palette values. Assess contrast and meaning before reuse. + +## Retained screenshots actually inspected + +- `mseries/netspeed/docs/screenshots/troubleshooter-empty-light.png`: 640 × 470, + native header search, centered welcome with title, short explanation, and three + icon/title/subtitle suggestions. +- `mseries/netspeed/docs/screenshots/troubleshooter-report.png`: 640 × 470, dark + report with a verdict, attention/ignore groups, and readable task-focused text. + +These are historical visual references, not evidence that current code or +hardware was visually qualified. Other screen families were inspected in source; +new screenshots should be captured when changing those screens. diff --git a/.agents/skills/elementaryos-mseries-ui-specialist/references/ui-conventions.md b/.agents/skills/elementaryos-mseries-ui-specialist/references/ui-conventions.md new file mode 100644 index 000000000..cb1878b9e --- /dev/null +++ b/.agents/skills/elementaryos-mseries-ui-specialist/references/ui-conventions.md @@ -0,0 +1,80 @@ +# UI conventions and decision criteria + +These recommendations translate the inspected project into a consistent working +practice; they are not a claim that every existing screen already complies. + +## Layout and styling + +Use native header bars, navigation, controls and dialogs for the containing app. +Keep primary content and the next useful action easy to find. Group related +settings with a heading and rows: label/optional description at the start, +control at the end. Use a sidebar/detail layout for collections, and a welcome +or placeholder for genuinely empty content. + +Match neighboring rows before introducing new dimensions. Observed settings +examples include 12-pixel control gaps, CodexBar row padding of 8 × 12, page +side margins of 18, and Battery row side margins of 14. These are local anchors, +not universal tokens. Avoid converting the whole project to a new spacing scale +as part of a small change. Use logical units and start/end alignment. + +Reuse theme typography and hierarchy; keep subordinate copy readable. Scope CSS +to the app or component and prefer theme foreground, background, accent and +semantic colors. Check contrast in both schemes. Follow the app's system color +preference handling; do not force a macOS font, palette or decoration. Keep +intentional palette choices when they communicate a useful identity. + +Allow labels to wrap and content to scroll vertically. Check long account names, +provider errors, translated text, enlarged text and narrow windows. Ellipsize +compact summaries only when full information remains available. A width request +does not cap GTK's natural width. Keep header actions usable as space shrinks; +existing crowded headers are not a template to reproduce. + +## Controls and navigation + +Use a switch for a persistent boolean, a button for an action, and a dropdown or +other suitable selector for mutually exclusive choices. Preserve established +instant-apply settings and display save failures; only show success when the +backend confirms it. Retain an explicit Apply/Save workflow where the target +surface already needs staged changes. + +Use clear action labels such as Refresh, Sign In, Cancel, or Retry. Label +icon-only actions accessibly and supply useful tooltips. Preserve keyboard focus, +selection and scroll position after a refresh. Use stable backend identifiers +for rows and accounts, not display text or list positions. + +## Asynchronous state and data flow + +Keep service, parsing and presentation responsibilities distinct, following +existing model/helper boundaries. Avoid blocking subprocess, network or disk +work on the GTK main loop; marshal worker results onto the UI thread. Cancel or +ignore stale completions when the account changes or a window closes. Avoid +rebuilding the entire widget tree for a value update. + +- **Loading:** identify what is being fetched; preserve useful prior data. +- **Empty:** explain why there is no content and offer the relevant next action. +- **Pending:** prevent duplicate operations while retaining cancellation where supported. +- **Stale/offline:** retain last-known data with its age and a useful refresh/retry action. +- **Error:** explain the failed operation and recovery; keep details available without overwhelming the screen. +- **Success:** reflect confirmed backend state. Distinguish measured values, estimates, unavailable values and real zeros. + +For sync or account flows, distinguish signed out, verification required, +unlocking, syncing, up to date, expired authentication and failed operations. +Do not invent conflict-resolution controls if the backend is read-only or has +no conflict model. Never expose passwords or tokens in labels, screenshots or logs. + +## Accessibility, text and motion + +Associate labels and controls; ensure keyboard activation, logical tab order, +visible focus and a way to dismiss dialogs. Communicate state with text or icons +as well as color. Test contrast for dim subtitles and status badges, not only +main headings. Use semantic GTK accessibility properties for custom controls. + +Follow the target app's gettext setup for new user-facing text. If it lacks +translation infrastructure, identify that gap and keep strings ready to extract; +do not claim localization is complete. Use locale-aware date/number/unit formatting +and translator context for ambiguous labels. Avoid sentence fragments assembled +around substituted values where grammar changes with translation. + +Use restrained feedback and transitions; honor animation preferences where +supported. A continuously pulsing status indicator in existing CSS is something +to assess, not a requirement for every running state. diff --git a/.gitattributes b/.gitattributes index 5de211956..6707495c4 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,7 @@ *.pgp binary *.gpg binary +# Empty context lines in this unified diff contain the required leading space. +mseries/patches/dock-taskbar-codexbar.patch whitespace=-blank-at-eol +mseries/patches/dock-taskbar-repobar.patch whitespace=-blank-at-eol +mseries/patches/dock-taskbar-power-buttons.patch whitespace=-blank-at-eol +mseries/patches/dock-running-apps.patch whitespace=-blank-at-eol diff --git a/.github/workflows/aurora-enhancements.yml b/.github/workflows/aurora-enhancements.yml index a8dd00bfe..1f60b9ce7 100644 --- a/.github/workflows/aurora-enhancements.yml +++ b/.github/workflows/aurora-enhancements.yml @@ -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" @@ -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: @@ -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: diff --git a/.github/workflows/aurora-kernel.yml b/.github/workflows/aurora-kernel.yml index a88cae14c..ab738f9bb 100644 --- a/.github/workflows/aurora-kernel.yml +++ b/.github/workflows/aurora-kernel.yml @@ -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 diff --git a/.github/workflows/message-pins.yml b/.github/workflows/message-pins.yml new file mode 100644 index 000000000..1335b3528 --- /dev/null +++ b/.github/workflows/message-pins.yml @@ -0,0 +1,67 @@ +name: Apple Messages pin reader +on: + workflow_dispatch: + pull_request: + paths: + - 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) + runs-on: ubuntu-26.04-arm + timeout-minutes: 10 + steps: + - uses: actions/checkout@v7 + - uses: actions/checkout@v7 + with: + repository: erikwb/blueferry + ref: 60fd7ef1abbd25947981f0cf27a521d82784a75f + path: .pin-test-blueferry + - name: Install native fixture dependencies + run: sudo apt-get update && sudo apt-get install -y python3-gi python3-dbus python3-pil gir1.2-gtk-4.0 libgranite-7-dev gir1.2-secret-1 gnome-keyring dbus-x11 xvfb xauth + - name: Check matching, drafts, failed writes and isolated Secret Service + env: + PYTHONPATH: ${{ github.workspace }}/.pin-test-blueferry/src + GDK_BACKEND: x11 + GSK_RENDERER: cairo + GTK_A11Y: none + GIO_USE_VFS: local + run: | + python3 mseries/phone-setup/test_message_pins.py + python3 mseries/phone-setup/test_messages.py + python3 mseries/phone-setup/test_message_media.py + bash mseries/phone-setup/test-pin-keyring.sh + dbus-run-session --config-file=mseries/phone-setup/test-bus.conf -- xvfb-run -a python3 mseries/phone-setup/test_messages_gui.py + macos-reader: + name: Apple pin reader (${{ matrix.os }}) + strategy: + fail-fast: false + matrix: + os: [macos-15, macos-26, macos-26-intel] + runs-on: ${{ matrix.os }} + timeout-minutes: 5 + steps: + - uses: actions/checkout@v7 + - name: Exercise Objective-C return signatures and ordered configuration + run: | + bash -n mseries/phone-setup/macos-visit/Export-Message-Pins.command + osascript -l JavaScript mseries/phone-setup/macos-visit/Export-Message-Pins.js --self-test + - name: Probe private interface without an Apple account + run: | + csrutil status + osascript -l JavaScript mseries/phone-setup/macos-visit/Export-Message-Pins.js --probe > "$RUNNER_TEMP/pin-reader-probe.json" + python3 - "$RUNNER_TEMP/pin-reader-probe.json" <<'PY' + import json, sys + result = json.load(open(sys.argv[1])) + assert result['status'] in ('ready', 'unavailable') + assert result['content_printed'] is False + if result['status'] == 'unavailable': + assert result['diagnostics'].get('cache') in ('not-found', 'not-initialized', 'access-denied'), result + print(result) + PY diff --git a/.github/workflows/mseries-debs.yml b/.github/workflows/mseries-debs.yml index 7103c2950..461c59c76 100644 --- a/.github/workflows/mseries-debs.yml +++ b/.github/workflows/mseries-debs.yml @@ -1,31 +1,72 @@ -# M-series plugin .debs: build pinned Wingpanel plugins once in CI so -# first boot installs binaries instead of compiling (issue #52). -# Manual dispatch (and a build-only PR check). Without `publish`, produces artifacts only — no secrets, -# no repository writes. Publishing needs the MSERIES_APT_KEY secret -# (key-custody decision still open) and fails closed without it. +# M-series signed packages: build the Wingpanel plugin debs and mseries-update once in CI, then +# publish them into a static apt repository with two suites (mseries/RELEASE-PIPELINE.md). +# +# push to dev -> `dev` suite versions carry ~dev. +# push to main -> `stable` suite (merging a release PR dev -> main, or a hotfix) +# pull_request -> build and test only; never publishes, never sees the key +# +# Publishing needs the MSERIES_APT_KEY secret (mseries/release/KEY-RUNBOOK.md) and fails closed: +# an unsigned repository is never written. Until the key exists, merges to dev/main build and test +# and publish nothing, with a warning. A manual run with publish=true fails if the key is missing. +# The pantheon-rust-compositor deb is built as an artifact only; it joins the suites once the +# updater's health check covers its recovery path. name: M-series plugin packages on: workflow_dispatch: inputs: publish: - description: Publish to the Pages-hosted apt repo (needs MSERIES_APT_KEY) + description: Publish this ref (dev -> dev suite, main -> stable suite); needs MSERIES_APT_KEY type: boolean required: false default: false - # Build-only check when the packaging itself changes (publish never runs: - # inputs.publish is unset outside workflow_dispatch). + push: + branches: [dev, main] + paths: + - "mseries/debs/**" + - "mseries/update/**" + - "mseries/release/**" + - "mseries/UPDATER-DESIGN.md" + - "mseries/RELEASE-PIPELINE.md" + - ".github/workflows/mseries-debs.yml" + - "mseries/panel-factory-pins.json" + - "mseries/patches/datetime-agenda-tabs.patch" + - "mseries/calendar/**" + - "mseries/quick-settings/**" + - "mseries/netspeed/**" + - "mseries/patches/network-*.patch" + - "rust/**" + - "mseries/pantheon-rust-compositor/**" + - "mseries/window-recovery/**" + # Build-only check (a release PR dev -> main builds the stable versions it would publish). pull_request: paths: - "mseries/debs/**" + - "mseries/update/**" + - "mseries/release/**" + - "mseries/UPDATER-DESIGN.md" + - "mseries/RELEASE-PIPELINE.md" - ".github/workflows/mseries-debs.yml" + - "mseries/panel-factory-pins.json" + - "mseries/patches/datetime-agenda-tabs.patch" + - "mseries/calendar/**" + - "mseries/quick-settings/**" + - "mseries/netspeed/**" + - "mseries/patches/network-*.patch" + - "rust/**" + - "mseries/pantheon-rust-compositor/**" + - "mseries/window-recovery/**" 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. + MSERIES_CHANNEL: ${{ (github.ref == 'refs/heads/main' || github.base_ref == 'main') && 'stable' || 'dev' }} jobs: build: @@ -34,37 +75,125 @@ jobs: steps: - uses: actions/checkout@v7 + # Launchpad answers 503 now and then (it broke PR #429's first build); retry before failing. - name: Add elementary daily PPA (build + gate headers) run: | - sudo add-apt-repository -y ppa:elementary-os/daily - sudo apt-get update + for attempt in 1 2 3 4 5; do + if sudo add-apt-repository -y ppa:elementary-os/daily && sudo apt-get update; then exit 0; fi + echo "attempt $attempt failed; retrying" >&2 + sleep $((attempt * 20)) + done + echo "the elementary daily PPA is unreachable" >&2 + exit 1 + # The PPA's pool also answers 503 in bursts, so retry the downloads, not just the index. - name: Install plugin build dependencies run: | - sudo apt-get install -y --no-install-recommends \ + for attempt in 1 2 3 4 5; do + if sudo apt-get install -y --no-install-recommends -o Acquire::Retries=5 \ dpkg-dev git meson ninja-build valac gettext pkg-config \ libwingpanel-9-dev libgranite-7-dev libgtk-4-dev \ libadwaita-1-dev libnm-dev libnma-gtk4-dev libpolkit-gobject-1-dev \ libpulse-dev libcanberra-dev libgee-0.8-dev \ libgnome-desktop-4-dev libibus-1.0-dev \ libpackagekit-glib2-dev libportal-dev libaccountsservice-dev libwayland-dev \ - io.elementary.panel.network io.elementary.quick-settings + libecal2.0-dev libedataserver1.2-dev libical-dev libjson-glib-dev \ + io.elementary.panel.network io.elementary.quick-settings io.elementary.panel.datetime; then exit 0; fi + echo "attempt $attempt failed; retrying" >&2 + sudo apt-get update || true + sleep $((attempt * 30)) + done + echo "could not install the build dependencies" >&2 + exit 1 + + - name: Style checks for the packaging scripts (bash mseries/validate.sh --only debs) + run: | + sudo apt-get install -y --no-install-recommends shellcheck + bash mseries/validate.sh --only debs - - name: Build pilot plugin packages + - name: Test apt assembly, signing and the version rules (throwaway key, no secrets) + run: | + sudo apt-get install -y --no-install-recommends apt-utils gnupg + bash mseries/release/test-assemble.sh + + - name: Build plugin packages and the updater (${{ env.MSERIES_CHANNEL }} channel) run: | chmod +x mseries/debs/build.sh OUTDIR=out/mseries-debs bash mseries/debs/build.sh - - name: Verify package metadata + - name: Check the calendar data bundled in the patch + run: python3 mseries/calendar/check-bundled-data.py + + - name: Test the packaged calendar panel (private EDS, Xvfb) + run: | + set -euo pipefail + sudo apt-get install -y --no-install-recommends \ + evolution-data-server gsettings-desktop-schemas dbus-x11 xvfb xauth accountsservice + # Granite reads the colour-scheme preference from AccountsService with + # elementary's io.elementary.pantheon extension on the system bus, as on + # every elementary desktop. The full elementary-default-settings package + # needs a newer flatpak than the runner has, so take only that interface. + for attempt in 1 2 3 4 5 6; do # the PPA pool answers 503 in bursts + (cd "$RUNNER_TEMP" && apt-get download elementary-default-settings) && break + [[ $attempt -lt 6 ]] || { echo "could not download elementary-default-settings" >&2; exit 1; } + sleep $((attempt * 20)) + done + dpkg-deb -x "$RUNNER_TEMP"/elementary-default-settings_*.deb "$RUNNER_TEMP/eds-settings" + sudo install -D -m 0644 \ + "$RUNNER_TEMP/eds-settings/usr/share/accountsservice/interfaces/io.elementary.pantheon.AccountsService.xml" \ + /usr/share/accountsservice/interfaces/io.elementary.pantheon.AccountsService.xml + sudo systemctl restart accounts-daemon + dpkg-deb -x out/mseries-debs/mseries-wingpanel-datetime_*.deb "$RUNNER_TEMP/datetime" + bash mseries/calendar/test-agenda.sh \ + "$RUNNER_TEMP/datetime/usr/lib/aarch64-linux-gnu/wingpanel-9/libdatetime.so" + + - name: Verify package metadata and the channel version rule run: | set -e for deb in out/mseries-debs/*.deb; do dpkg-deb -f "$deb" Package Version Architecture Depends - dpkg-deb -c "$deb" | grep '\.so$' + version=$(dpkg-deb -f "$deb" Version) + if [[ $MSERIES_CHANNEL == dev ]]; then + [[ $version =~ '~dev'[0-9]{14}\.[0-9a-f]{7}$ ]] || { echo "dev version '$version' lacks ~dev