From ca62f74dda6e733edb0629ef53a31fa54a50a37a Mon Sep 17 00:00:00 2001 From: Derek Date: Wed, 7 Oct 2026 10:25:34 +1100 Subject: [PATCH] fix: take container and git-scrub from brew on mac Apple container now comes from the homebrew-core formula, which tracks Apple's releases the same day, so brew upgrade keeps it current and the signed .pkg fetch is gone. A Mac that already has the .pkg gets the brew copy first, then Apple's own uninstall-container.sh removes the .pkg files and keeps the user's container data. The formula needs Apple silicon and macOS 26, so the install is skipped anywhere else rather than collecting a warning. git-scrub on macOS now comes from the hyperi-io tap, which each git-scrub release rewrites. The release-tarball binary in /usr/local/bin and its cached tarballs are removed first, because on an Intel Mac that path is where brew links. Linux keeps the re-fetched release tarball. --- ansible/roles/contributor/tasks/git_scrub.yml | 80 +++++++++++--- ansible/roles/soe/tasks/colima.yml | 102 ++++++++---------- docs/install-matrix.md | 8 +- 3 files changed, 112 insertions(+), 78 deletions(-) diff --git a/ansible/roles/contributor/tasks/git_scrub.yml b/ansible/roles/contributor/tasks/git_scrub.yml index c695922..ef689fe 100644 --- a/ansible/roles/contributor/tasks/git_scrub.yml +++ b/ansible/roles/contributor/tasks/git_scrub.yml @@ -7,17 +7,72 @@ # failure: gitleaks scans FULL history, so a secret removed from HEAD still # fails `hyperi-ci check`. soe inherits it through meta/dependencies. # -# GitHub release tarball on every platform (Tier 3). hyperi-update pulls the -# latest on Linux. On macOS only a re-run of this role does, because the macOS -# updater leaves everything to brew. It is the only rung: not on crates.io, no -# git-scrub path on downloads.hyperi.io, and the release's git-scrub.rb is not -# in the hyperi-io tap. Move macOS to community.general.homebrew once that -# formula is tapped. +# macOS takes the formula in the hyperi-io tap, which each git-scrub release +# rewrites, so brew upgrade keeps it current. Linux takes the GitHub release +# tarball (Tier 3) and hyperi-update re-fetches it: not on crates.io and no +# git-scrub path on downloads.hyperi.io. # # The asset unpacks into a directory named after itself, so the extracted binary # path carries the version. The tag has a leading `v`; the filename does not. -- name: Install git-scrub (re-fetched GitHub release, Tier 3) +- name: Install git-scrub (macOS) + when: ansible_facts['distribution'] == 'MacOSX' + block: + - name: Tap the hyperi-io formulae (macOS) + community.general.homebrew_tap: + name: hyperi-io/tap + state: present + become: false + environment: "{{ homebrew_env }}" + + # Earlier runs of this role put the release binary here. On an Intel Mac it + # is also the path brew links into, so it has to go before the install. + - name: Check for a git-scrub binary from the release tarball (macOS) + ansible.builtin.stat: + path: /usr/local/bin/git-scrub + register: contributor_git_scrub_legacy + + - name: Remove the release-tarball git-scrub binary (macOS) + ansible.builtin.file: + path: /usr/local/bin/git-scrub + state: absent + become: true + when: + - contributor_git_scrub_legacy.stat.exists + - not contributor_git_scrub_legacy.stat.islnk + + - name: Install git-scrub via Homebrew (macOS) + community.general.homebrew: + name: hyperi-io/tap/git-scrub + state: present + become: false + environment: "{{ homebrew_env }}" + + - name: Find release tarballs left by earlier runs (macOS) + ansible.builtin.find: + paths: /tmp + patterns: 'git-scrub-*-darwin-*.tar.gz' + register: contributor_git_scrub_tarballs + + - name: Remove release tarballs left by earlier runs (macOS) + ansible.builtin.file: + path: "{{ item.path }}" + state: absent + loop: "{{ contributor_git_scrub_tarballs.files }}" + loop_control: + label: "{{ item.path }}" + + rescue: + - name: Record that git-scrub did not install (macOS) + # noqa: var-naming[no-role-prefix] -- deploy_warnings is the shared + # accumulator reported by playbooks/main.yml post_tasks. + ansible.builtin.set_fact: + deploy_warnings: >- + {{ deploy_warnings | default([]) + + ['git-scrub: ' ~ (ansible_failed_result.msg | default('brew tap/install failed'))] }} + +- name: Install git-scrub (re-fetched GitHub release, Tier 3, Linux) + when: ansible_facts['distribution'] in ['Ubuntu', 'Fedora'] block: - name: Get latest git-scrub version from GitHub API ansible.builtin.uri: @@ -35,8 +90,7 @@ - name: Build the git-scrub asset name ansible.builtin.set_fact: contributor_git_scrub_stem: >- - git-scrub-{{ contributor_git_scrub_ref | regex_replace('^v', '') }}-{{ - 'darwin' if ansible_facts['distribution'] == 'MacOSX' else 'linux' }}-{{ hyperi_arch_deb }} + git-scrub-{{ contributor_git_scrub_ref | regex_replace('^v', '') }}-linux-{{ hyperi_arch_deb }} # The tarball is KEPT, and that is what makes the role idempotent: get_url # re-reports ok for an unchanged asset, so the extract and install below @@ -58,14 +112,6 @@ path: /usr/local/bin/git-scrub register: contributor_git_scrub_installed - # Root-owned on Linux, absent on a fresh Apple Silicon box. - - name: Ensure /usr/local/bin exists - ansible.builtin.file: - path: /usr/local/bin - state: directory - mode: '0755' - become: true - # A new asset OR a missing binary: the second repairs drift on a box where # the tarball is still cached but the binary was removed. - name: Install git-scrub diff --git a/ansible/roles/soe/tasks/colima.yml b/ansible/roles/soe/tasks/colima.yml index 20ecf12..77bd1c3 100644 --- a/ansible/roles/soe/tasks/colima.yml +++ b/ansible/roles/soe/tasks/colima.yml @@ -3,10 +3,9 @@ # Docker CLI (bring-your-own-daemon); HyperI Macs get a daemon by default: # - colima: runs docker-ce in a small Virtualization.framework VM -- the same # engine Linux runs natively. Homebrew formula (Tier 1, brew upgrade sweeps it). -# - Apple `container`: native per-container micro-VMs on Apple silicon. Ships -# ONLY as a signed .pkg on GitHub releases (no brew channel), so a re-run of -# this role installs the latest. hyperi-update does not touch it. Optional: -# warn, never abort. +# - Apple `container`: native per-container micro-VMs on Apple silicon. The +# homebrew-core formula (Tier 1, brew upgrade sweeps it), which needs Apple +# silicon and macOS 26. Optional: warn, never abort. # Both are wrapped warn-and-continue so a single failure does not sink the soe run. - name: Install colima (Docker daemon for macOS) @@ -167,53 +166,52 @@ {{ deploy_warnings | default([]) + ['colima-wiring: ' ~ (ansible_failed_result.msg | default('autostart/socket setup failed'))] }} +# The formula declares `depends_on arch: :arm64` and `macos: :tahoe`, so any +# other Mac would only collect a warning. - name: Install Apple container (native macOS containers) + when: + - hyperi_arch_deb == 'arm64' + - ansible_facts['distribution_major_version'] | int >= 26 block: - - name: Get the latest Apple container release - ansible.builtin.uri: - url: https://api.github.com/repos/apple/container/releases/latest - return_content: true - headers: "{{ hyperi_github_headers }}" - register: soe_container_release - check_mode: false - - # Prefer the signed/notarized .pkg (avoids a Gatekeeper bypass); fall back to - # any .pkg. Concatenating [signed] + [all] and taking `first` gives that - # preference in one expression. - - name: Select the container installer package (prefer the signed .pkg) - ansible.builtin.set_fact: - soe_container_pkg_url: >- - {{ ((soe_container_release.json.assets - | selectattr('name', 'search', '(?i)signed.*\.pkg$') - | map(attribute='browser_download_url') | list) - + (soe_container_release.json.assets - | selectattr('name', 'search', '\.pkg$') - | map(attribute='browser_download_url') | list)) | first }} - - # Root-owned, randomly-named temp (mkstemp, mode 0600) -- NOT a predictable - # world-writable /tmp path a local user could swap before the root installer - # reads it (TOCTOU). The `always` block removes it on success OR failure. - - name: Create a root-owned temp path for the installer - ansible.builtin.tempfile: - state: file - suffix: .pkg - become: true - register: soe_container_tmp - - - name: Download the Apple container installer - ansible.builtin.get_url: - url: "{{ soe_container_pkg_url }}" - dest: "{{ soe_container_tmp.path }}" - mode: '0600' - force: true - become: true + - name: Install Apple container via Homebrew + community.general.homebrew: + name: container + state: present + become: false + environment: "{{ homebrew_env }}" - - name: Install Apple container from the signed package + # Earlier runs of this role installed Apple's signed .pkg into /usr/local. + # Its receipt is the record of exactly which files that put there. + - name: Check for the Apple container .pkg receipt ansible.builtin.command: - cmd: "installer -pkg {{ soe_container_tmp.path }} -target /" - become: true - register: soe_container_install - changed_when: "'successful' in (soe_container_install.stdout | lower)" + cmd: pkgutil --pkg-info com.apple.container-installer + register: soe_container_pkg_receipt + changed_when: false + failed_when: false + check_mode: false + + # Removed only once the brew copy is in place, so the host is never left + # without a container CLI. + - name: Remove the Apple container .pkg install + when: soe_container_pkg_receipt.rc == 0 + block: + # The uninstaller refuses while the services run, and as root it would + # only look in root's launchd domain, not the user's where they live. + - name: Stop the .pkg container services + ansible.builtin.command: + cmd: /usr/local/bin/container system stop + become: false + register: soe_container_pkg_stop + changed_when: soe_container_pkg_stop.rc == 0 + failed_when: false + + # -k keeps ~/Library/Application Support/com.apple.container, which the + # brew copy reads as its own data. + - name: Uninstall the .pkg with Apple's own uninstaller + ansible.builtin.command: + cmd: /usr/local/bin/uninstall-container.sh -k + removes: /usr/local/bin/uninstall-container.sh + become: true rescue: - name: Record that Apple container did not install @@ -221,12 +219,4 @@ ansible.builtin.set_fact: deploy_warnings: >- {{ deploy_warnings | default([]) - + ['apple-container: ' ~ (ansible_failed_result.msg | default('download/install failed'))] }} - - always: - - name: Remove the downloaded installer - ansible.builtin.file: - path: "{{ soe_container_tmp.path }}" - state: absent - become: true - when: soe_container_tmp.path is defined + + ['apple-container: ' ~ (ansible_failed_result.msg | default('brew install or .pkg removal failed'))] }} diff --git a/docs/install-matrix.md b/docs/install-matrix.md index fe99b7e..a625ee2 100644 --- a/docs/install-matrix.md +++ b/docs/install-matrix.md @@ -316,9 +316,7 @@ table used to say the opposite. Almost every macOS path resolves to brew or a cask. The language managers that remain there carry no formula at all: `alint` and `maid` have none, and semantic-release needs its plugin set installed alongside it, which only npm -gives. `git-scrub` is the one release-tarball exception -- its formula exists in -the release but is not in the hyperi-io tap, so macOS takes the darwin asset -until it is tapped. +gives. Cloudflare publishes no flarectl binary and no distro packages it, so both platforms build it from source. It also lives on cloudflare-go's `v0` branch -- @@ -354,7 +352,7 @@ The one HashiCorp tool installed: BUSL, with no open-source fork, so it is its o | vulture | all | Ubuntu apt / Fedora uv-tool (Tier 2) / brew | | typos | all | cargo (Tier 2) / brew | | maid (mermaid validator, used by `/docs`) | all | npm global (Tier 2) | -| git-scrub (git-history scrubber) | all | github-binary (Tier 3: re-fetch) | +| git-scrub (git-history scrubber) | all | github-binary (Tier 3: re-fetch) / brew tap | | macbash (macOS bash portability checker) | all | Linux downloads.hyperi.io binary (Tier 3, digest-verified) / brew tap | `hyperi-ci` is a Python tool from PyPI, installed via `uv tool` and refreshed to @@ -374,7 +372,7 @@ hyperi-ci. | Slack | all | vendor-repo / cask | | LibreOffice (org office suite) | Linux | distro repo | | Nemo, GNOME extensions (gext), fonts | Linux | distro / uv-tool / vendored | -| colima + Apple `container` (macOS only) | macOS | brew / github-binary | +| colima + Apple `container` (macOS only; `container` needs Apple silicon and macOS 26) | macOS | brew | | Arcane container UI (opt-in `soe_arcane_enabled`; `soe_arcane_long_session` for a year-long login) | all | container image | | Local ClickHouse + Redpanda (opt-in `soe_local_services_enabled`) | all | container image | | removals / update_command / admin-scripts (opt-in `never`, on for soe) | Linux | tombstones + scripts |