Skip to content
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
47 changes: 44 additions & 3 deletions .github/workflows/private-ca-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:
set -euo pipefail
release="$(gh api "repos/${UPSTREAM_REPOSITORY}/releases/latest")"
tag="$(jq -r '.tag_name' <<<"${release}")"
[[ "${tag}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]] || { echo "not a stable tag: ${tag}" >&2; exit 1; }
tag="$(scripts/normalize-private-ca-desktop-tag.sh "${tag}")"
existing="$(gh issue list --state all --search "[Buzz update] ${tag} available in:title" --json number --jq '.[0].number // empty')"
[[ -z "${existing}" ]] || { echo "ticket already exists: #${existing}"; exit 0; }
work="$(mktemp -d)"; trap 'rm -rf "${work}"' EXIT
Expand Down Expand Up @@ -78,7 +78,7 @@ jobs:
if [[ "${patch_status}" == conflict ]]; then
labels+=(--label remediation-required)
fi
gh issue create --title "[Buzz update] ${tag} available" "${labels[@]}" --body-file "${work}/issue.md"
gh issue create --title "[Buzz update] ${tag} available" "${labels[@]}" --assignee BrianInAz --body-file "${work}/issue.md"

skip:
if: >-
Expand All @@ -97,6 +97,47 @@ jobs:
gh issue comment "${{ github.event.issue.number }}" --body "Skipped by @BrianInAz; no private-CA package was built."
gh issue close "${{ github.event.issue.number }}"

built:
if: >-
github.event_name == 'issues' && github.event.action == 'labeled' &&
github.event.label.name == 'built' && github.actor == 'BrianInAz'
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
issues: write
steps:
- name: Record the local package handoff
env:
GH_TOKEN: ${{ github.token }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
run: |
set -euo pipefail
body="$(gh issue view "${ISSUE_NUMBER}" --json body --jq .body)"
grep -Eq '^<!-- buzz-private-ca-release tag=(desktop-)?v[0-9]+\.[0-9]+\.[0-9]+ source_sha=[0-9a-f]{40} patch_sha=[0-9a-f]{40} patch_status=clean -->$' <<<"${body}" || { echo "not a clean monitor-created ticket" >&2; exit 1; }
gh issue comment "${ISSUE_NUMBER}" --body "Built by @BrianInAz on the approved Apple Silicon Mac. Record the immutable artifact, checksum, private WSS gate, and local acceptance evidence before adding \`accepted\`."

accepted:
if: >-
github.event_name == 'issues' && github.event.action == 'labeled' &&
github.event.label.name == 'accepted' && github.actor == 'BrianInAz'
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
issues: write
steps:
- name: Close an accepted release ticket
env:
GH_TOKEN: ${{ github.token }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
run: |
set -euo pipefail
body="$(gh issue view "${ISSUE_NUMBER}" --json body --jq .body)"
grep -Eq '^<!-- buzz-private-ca-release tag=(desktop-)?v[0-9]+\.[0-9]+\.[0-9]+ source_sha=[0-9a-f]{40} patch_sha=[0-9a-f]{40} patch_status=clean -->$' <<<"${body}" || { echo "not a clean monitor-created ticket" >&2; exit 1; }
labels="$(gh issue view "${ISSUE_NUMBER}" --json labels --jq '.labels[].name')"
grep -Fxq built <<<"${labels}" || { echo "accepted requires the built lifecycle state" >&2; exit 1; }
gh issue comment "${ISSUE_NUMBER}" --body "Accepted by @BrianInAz after private WSS, state-preserving install, restart, reconnect, and history-restoration evidence was recorded."
gh issue close "${ISSUE_NUMBER}"

approval:
if: >-
github.event_name == 'issues' && github.event.action == 'labeled' &&
Expand All @@ -118,7 +159,7 @@ jobs:
run: |
set -euo pipefail
body="$(gh issue view "${ISSUE_NUMBER}" --json body --jq .body)"
marker="$(grep -E '^<!-- buzz-private-ca-release tag=v[0-9]+\.[0-9]+\.[0-9]+ source_sha=[0-9a-f]{40} patch_sha=[0-9a-f]{40} patch_status=clean -->$' <<<"${body}")"
marker="$(grep -E '^<!-- buzz-private-ca-release tag=(desktop-)?v[0-9]+\.[0-9]+\.[0-9]+ source_sha=[0-9a-f]{40} patch_sha=[0-9a-f]{40} patch_status=clean -->$' <<<"${body}")"
[[ -n "${marker}" ]] || { echo "not a clean monitor-created ticket" >&2; exit 1; }
tag="$(sed -E 's/.*tag=([^ ]+).*/\1/' <<<"${marker}")"
source_sha="$(sed -E 's/.*source_sha=([^ ]+).*/\1/' <<<"${marker}")"
Expand Down
36 changes: 25 additions & 11 deletions docs/operations/private-ca-desktop-release-lifecycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,17 @@ that repository's normal `develop` then targeted `main` promotion flow.

## Durable task state

The private operational task is Beads `tailscale-vault-of5-213`, a child of
`tailscale-vault-of5-211.13`. Every stopping point records branch, SHA, tests,
URLs, blockers, and the exact next action there.
The authoritative cross-service process is tracked by Beads epic
`tailscale-vault-of5-213`. Buzz release-monitor repair and retirement work is
child `tailscale-vault-of5-213.1`. Every stopping point records branch, SHA,
tests, URLs, blockers, and the exact next action there. Historical delivery
evidence remains in the GitHub release tickets and must not be substituted for
the open retirement tracker.

Before resuming, another agent must read, in order:

1. This document.
2. Beads `tailscale-vault-of5-213` and its parent.
2. Beads `tailscale-vault-of5-213` and child `tailscale-vault-of5-213.1`.
3. [BrianInAz/buzz#1](https://github.com/BrianInAz/buzz/issues/1).
4. The current branch, status, remotes, upstream activity, and CI results.

Expand All @@ -77,14 +80,14 @@ directories or stale chat transcripts; request the approved plan.

## Daily release monitor

The fork workflow runs daily at 15:05 UTC and by manual dispatch. It observes
new stable desktop releases in `block/buzz`; prereleases require explicit
manual selection.
The fork workflow runs daily at 15:05 UTC and by manual dispatch. It accepts
both historical `vX.Y.Z` and current `desktop-vX.Y.Z` stable desktop release
tags from `block/buzz`. Relay tags, prereleases, and malformed tags fail closed.

For a new immutable upstream release, it records the tag, source SHA,
publication time, release notes, comparison link, private-patch applicability,
and any explicitly recorded upstream trust fix. It creates exactly one assigned
issue titled `[Buzz update] vX.Y.Z available` with labels:
and any explicitly recorded upstream trust fix. It creates exactly one issue
assigned to `BrianInAz`, titled `[Buzz update] <tag> available`, with labels:

```text
buzz-update
Expand All @@ -104,7 +107,17 @@ issue was generated by the monitor, the source tag still resolves to the
recorded SHA, and the pinned patch applies cleanly. Public comments and issue
text never become command input. `skip` closes the ticket without a build. A
conflict or failed preflight applies `remediation-required` and publishes no
package.
package. `built` records the approved-Mac handoff but does not close the ticket.
`accepted` is valid only after `built`; it records the private WSS, install,
restart/reconnect, and history-restoration acceptance and then closes the
ticket.

The monitor failure on
[run 30707522015](https://github.com/BrianInAz/buzz/actions/runs/30707522015)
triggered the tag-format correction: upstream's latest desktop release changed
to the `desktop-vX.Y.Z` namespace. After this correction is merged, a manual
dispatch must create exactly one unapproved `desktop-v0.5.3` decision ticket.
That dispatch does not authorize a package build or installation.

## Hosted validation and local package

Expand Down Expand Up @@ -216,4 +229,5 @@ acceptance, rollback bundle, Beads evidence, and documentation are all green.
- Official: `/Applications/Buzz.app/Contents/MacOS/buzz-desktop` (PID 41857, `xyz.block.buzz.app`)
- Development: `target/debug/buzz-desktop` (PID 76852, `xyz.block.buzz.app.dev.fix-macos-private-ca-websocket`)
- Rollback archive: `/Users/b/Applications/Buzz Rollback/v0.5.2/Buzz.app`

- Lifecycle closeout: issue #3 is assigned to `BrianInAz`, labeled `built` and
`accepted`, and closed only after the evidence above is preserved.
19 changes: 19 additions & 0 deletions scripts/normalize-private-ca-desktop-tag.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/usr/bin/env bash

set -euo pipefail

if [[ "$#" -ne 1 ]]; then
echo "usage: $0 <release-tag>" >&2
exit 64
fi

tag="$1"

if [[ "${tag}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]] ||
[[ "${tag}" =~ ^desktop-v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
printf '%s\n' "${tag}"
exit 0
fi

echo "not a stable Buzz Desktop tag: ${tag}" >&2
exit 1
27 changes: 27 additions & 0 deletions scripts/test-private-ca-release-contract.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ set -euo pipefail

workflow=".github/workflows/private-ca-release.yml"
local_builder="scripts/build-private-ca-macos.sh"
tag_parser="scripts/normalize-private-ca-desktop-tag.sh"

if [[ ! -f "${workflow}" ]]; then
echo "missing ${workflow}" >&2
Expand Down Expand Up @@ -44,6 +45,13 @@ require 'desktop/src-tauri/binaries'
require 'gh issue close'
require 'Local macOS package handoff'
require 'scripts/build-private-ca-macos.sh'
require 'scripts/normalize-private-ca-desktop-tag.sh'
require '--assignee BrianInAz'
require 'ticket already exists'
require "github.event.label.name == 'built'"
require "github.event.label.name == 'accepted'"
require 'accepted requires the built lifecycle state'
require 'not a clean monitor-created ticket'

forbid 'BUZZ_TEST_WSS_URL'
forbid 'buzz.bjzy.me'
Expand All @@ -61,6 +69,25 @@ if [[ ! -x "${local_builder}" ]]; then
exit 1
fi

if [[ ! -x "${tag_parser}" ]]; then
echo "missing executable ${tag_parser}" >&2
exit 1
fi

for tag in v0.5.2 desktop-v0.5.3; do
if [[ "$("${tag_parser}" "${tag}")" != "${tag}" ]]; then
echo "${tag_parser} must accept stable desktop tag ${tag}" >&2
exit 1
fi
done

for tag in relay-v0.5.3 desktop-v0.5.3-rc.1 v0.5.3-beta.1 nonsense; do
if "${tag_parser}" "${tag}" >/dev/null 2>&1; then
echo "${tag_parser} must reject non-stable desktop tag ${tag}" >&2
exit 1
fi
done

for expected in 'set -euo pipefail' 'createUpdaterArtifacts": false' 'codesign --verify --deep --strict' 'hdiutil create'; do
if ! grep -F -q -- "${expected}" "${local_builder}"; then
echo "${local_builder} must contain: ${expected}" >&2
Expand Down
Loading