Files
upstream-activitywatch/scripts/package/RESEARCH_FEED.md
upstream-mirror c5c5af8cde
Some checks failed
Research feed publisher tests / test (push) Failing after 3s
Release / Pre-flight checks (push) Failing after 0s
Release / Research Edition — patch and packaging smoke tests (push) Has been skipped
Release / Build Qt artifacts (false, 22, ubuntu-22.04, 3.9, ${{ github.event_name == 'pull_request' || (github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/')) }}, false, false) (push) Has been skipped
Release / Build Qt artifacts (false, 22, ubuntu-22.04, 3.9, false, false, false) (push) Has been skipped
Release / Build Qt artifacts (manylinux_2_28 — glibc 2.28 ABI floor) (22, 3.9) (push) Has been skipped
Release / Generate release notes (push) Has been skipped
Release / Create dev release tag (push) Has been skipped
CodeQL / Analyze (javascript) (push) Failing after 1s
CodeQL / Analyze (python) (push) Failing after 1s
Release / Build Qt artifacts (false, 22, windows-latest, 3.9, false, false, false) (push) Has been cancelled
Release / Build Tauri artifacts (false, 22, macos-15-intel, 3.9, false, false, false) (push) Has been cancelled
Release / Build Tauri artifacts (false, 22, macos-latest, 3.9, false, false, false) (push) Has been cancelled
Release / Build Tauri artifacts (false, 22, ubuntu-24.04, 3.9, ${{ github.event_name == 'pull_request' || (github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/')) }}, false, false) (push) Has been cancelled
Release / Build Tauri artifacts (false, 22, ubuntu-24.04, 3.9, false, false, false) (push) Has been cancelled
Release / Build Tauri artifacts (false, 22, ubuntu-24.04-arm, 3.9, false, false, false) (push) Has been cancelled
Release / Build Tauri artifacts (false, 22, windows-latest, 3.9, false, false, false) (push) Has been cancelled
Release / Build Qt artifacts (false, 22, macos-15-intel, 3.9, false, false, false) (push) Has been cancelled
Release / Build Qt artifacts (false, 22, macos-latest, 3.9, false, false, false) (push) Has been cancelled
Release / Publish draft release (push) Has been cancelled
snapshot of https://github.com/ActivityWatch/activitywatch.git (master, 2026-09-15; blobs > 131072B omitted)
2026-09-15 22:29:06 +05:00

4.8 KiB

Research updater feed publisher

publish_research_feed.py prepares an ordinary commit of latest-research.json on the research-updates branch. It preserves branch history and existing files. The default invocation changes only a new local checkout. --publish explicitly enables the remote update.

This command is manual. There is no release-published trigger. Before live use, the release owner must verify research signing keys, bundle contents, installed-app upgrades, and the disposition of older research clients that still contain the standard updater endpoint/key. The publisher does not prove those properties or enable an update channel in an installed client.

Prepare and review

Requires Git, a configured Git author, and uv (the script declares its semver dependency). Run from the ActivityWatch checkout, using a new output directory for each attempt:

REPO_ROOT=$(git rev-parse --show-toplevel)
TAG=v0.14.0b6-research
MANIFEST=/absolute/path/to/reviewed/latest-research.json
CANDIDATE=/absolute/path/to/new-feed-candidate

# Read the current remote head. Use the literal 'absent' only if this
# succeeds and returns no matching ref (initial provisioning).
git ls-remote --refs https://github.com/ActivityWatch/activitywatch.git \
  refs/heads/research-updates
PARENT=<full-sha-from-the-output>

uv run --script "$REPO_ROOT/scripts/package/publish_research_feed.py" \
  --tag "$TAG" --manifest "$MANIFEST" --expected-parent "$PARENT" \
  --work-dir "$CANDIDATE"

git -C "$CANDIDATE" show HEAD

The supplied manifest must already use normalized SemVer, for example 0.14.0-beta.6. Tags and asset filenames retain their release spelling (v0.14.0b6-research / activitywatch-tauri-research-0.14.0b6-…). This publisher accepts stable and a/b/rc research tags, not development builds. Build/app version normalization is owned by the release configuration work; this command validates the tag/version association without modifying builds.

Validation requires:

  • An unauthenticated GitHub API read of the exact published, immutable, non-draft research release.
  • All five release targets: macOS arm64/x86_64, Linux arm64/x86_64, and Windows x86_64. A reduced platform rollout requires an explicit code review.
  • A supported updater bundle and nonempty uploaded .sig asset for each platform, under the exact selected release and research filename prefix.
  • The manifest signature must equal the publicly downloaded .sig contents. Cryptographic signature verification and embedded payload-version/edition verification remain release-owner gates; this command does not download the potentially large bundles.
  • A valid static updater manifest, with RFC3339 publication date and string notes. Unknown fields are refused to avoid alternate updater parsing paths.

Publish after release acceptance

After reviewing the candidate and satisfying the release gates, rerun the same invocation with a new work directory and --publish. It repeats all release checks and compares the feed's current SemVer before committing. Git's configured credentials authorize the push; public release reads do not use those credentials. Keep the recorded parent unchanged; a stale parent is an error, not permission to overwrite new work.

An older version is refused. Equal SemVer precedence with different content (including metadata/build-identifier changes) is refused. Identical parsed manifest content is an explicit no-op. Formatting/key order differences alone do not mint another commit. A changed release needs a higher version.

The publisher checks the remote parent at fetch and in pre-push against Git's advertised OID, then pushes normally without force. Git's receive-side old-OID check closes the remaining race. The wrapper invokes any existing pre-push hook with the original arguments/stdin and preserves its refusal. Concurrent creation, advancement, deletion, or rewind requires a fresh invocation with a reread parent; version ordering is evaluated again, so a late b5 job cannot replace b6 even after retry. Failed candidates remain on disk for inspection.

After a successful push, verify the public feed separately: https://raw.githubusercontent.com/ActivityWatch/activitywatch/research-updates/latest-research.json. A Git push alone does not prove CDN freshness or a successful installed update. Standard release publication does not invoke this command or change this branch.

Tests

uv run --with pytest --with 'semver>=3,<4' python3 -m pytest \
  "$REPO_ROOT/scripts/tests/test_publish_research_feed.py" -q

The tests use release fixtures and temporary local Git remotes. They cover provisioning, advancement, replay, rollback refusal, racing publishers, branch deletion/rewind, incomplete releases, metadata validation, and existing push-hook enforcement. They never publish to GitHub.