Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
103 changes: 103 additions & 0 deletions .github/workflows/publish-staging.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
name: Staging pre-release and publish to PyPI

# rc pre-release stream: on every push to staging, validate, cut a PEP 440
# pre-release (2.YY.M.DD.SEQrcN), and publish it to PyPI so cowork-server staging
# consumes an immutable, versioned Anton artifact instead of a mutable git branch
# or a hand-pinned commit (ENG-1159). Resolvers ignore pre-releases unless a
# specifier names one, and PyPI's `info.version` (which the prod desktop updater
# reads) excludes them, so prod installs can never pick these up.
Comment thread
pnewsam marked this conversation as resolved.
#
# The publish job MUST live in this top-level workflow — PyPI Trusted Publishing
# does not support reusable workflows and matches the OIDC claim on THIS
# filename. Register publish-staging.yml as a trusted publisher (environment
# `pypi`) at the anton-agent PyPI project's publishing settings.
#
# Anton has nothing to self-pin (it does not depend on itself), so — unlike
# cowork-server's staging publisher — this builds from a clean checkout of the
# tag and needs no version-pretend override: hatch-vcs derives the exact rc
# version straight from the tag, identical to the stable path in release.yml.
#
# No workflow-level `permissions:` block: only the release job needs
# `contents: write`; every other job declares its own, so the repo default
# (read) applies to the rest.
#
# run-tree-ok: PyPI Trusted Publishing binds to this top-level workflow file.

on:
push:
branches: [staging]
workflow_dispatch:

concurrency:
group: staging-prerelease-${{ github.ref }}
cancel-in-progress: false

jobs:
unit-tests:
permissions:
contents: read
uses: ./.github/workflows/tests.yml

release:
needs: unit-tests
# workflow_dispatch can be pointed at any ref; only staging mints rc tags.
if: github.ref == 'refs/heads/staging'
permissions:
contents: write # tag push + release creation
uses: mindsdb/github-actions/.github/workflows/calver-release.yml@main
with:
calver-major: "2"
prerelease: true
runs-on: ubuntu-latest

publish:
name: Build and publish pre-release to PyPI
needs: release
runs-on: ubuntu-latest
environment: pypi
permissions:
contents: read
id-token: write # required for trusted publisher (OIDC)
steps:
- uses: actions/checkout@v4
with:
ref: ${{ needs.release.outputs.tag }}
fetch-depth: 0 # hatch-vcs needs tags to derive version
Comment thread
pnewsam marked this conversation as resolved.

- name: Setup uv
uses: astral-sh/setup-uv@v5
with:
python-version: "3.12"

- name: Build package
run: uv build
Comment thread
pnewsam marked this conversation as resolved.

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
# Trusted publisher (OIDC) — publish-staging.yml must be registered at
# the anton-agent PyPI project's publishing settings (environment: pypi).

notify:
# Alert the eng channel if ANY job in the staging release/publish pipeline
# fails.
# One job covers both outcomes: a `uses:` job cannot branch on status, so
# the aggregate result picks the failed or recovered message, and a
# cancelled run stays silent.
needs: [unit-tests, release, publish]
if: ${{ github.ref == 'refs/heads/staging' && !cancelled() && !contains(needs.*.result, 'cancelled') }}
permissions:
contents: read
actions: read # the prior-run lookup behind the recovery message
uses: mindsdb/github-actions/.github/workflows/notify-main-failure.yml@main
with:
env-name: "staging prerelease"
status: ${{ contains(needs.*.result, 'failure') && 'failed' || 'recovered' }}
Comment thread
pnewsam marked this conversation as resolved.
# This publisher only ever runs on staging, so the alert is always
# freeze-scoped: reserve release-blocking escalation for the freeze
# window rather than paging the eng channel on every midweek failure.
# Preserves the policy the shared CI notify applied via
# `freeze-scoped: ${{ github.ref_name == 'staging' }}` before this
# pipeline took ownership of the alert.
freeze-scoped: true
runs-on: ubuntu-latest
secrets: inherit
44 changes: 35 additions & 9 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
name: Auto-release on push to main
name: Auto-release and publish to PyPI

permissions:
contents: write
# Stable release stream: on every push to main, validate, cut a CalVer release,
# publish it to PyPI, and run live release e2e. Staging pre-releases (rc) live in
# publish-staging.yml so each stream keeps its own run tree, permissions,
# notifications, and publishing identity.
#
# The publish job MUST live in this top-level workflow — PyPI Trusted Publishing
# does not support reusable workflows and matches the OIDC claim on THIS
# filename. Register release.yml as a trusted publisher (environment `pypi`) at
# https://pypi.org/manage/project/anton-agent/settings/publishing/
#
# No workflow-level `permissions:` block: only the release job needs
# `contents: write`; every other job declares its own, so the repo default
# (read) applies to the rest.
#
# run-tree-ok: PyPI Trusted Publishing binds to this top-level workflow file.

on:
push:
Expand All @@ -13,7 +26,17 @@ concurrency:
cancel-in-progress: false

jobs:
unit-tests:
permissions:
contents: read
uses: ./.github/workflows/tests.yml

auto-release:
needs: unit-tests
# workflow_dispatch can be pointed at any ref; only main cuts a stable release.
if: github.ref == 'refs/heads/main'
permissions:
contents: write # tag push + release creation
uses: mindsdb/github-actions/.github/workflows/calver-release.yml@main
with:
calver-major: "2"
Expand All @@ -25,6 +48,7 @@ jobs:
runs-on: ubuntu-latest
environment: pypi
permissions:
contents: read
id-token: write # required for trusted publisher (OIDC)
steps:
- uses: actions/checkout@v4
Expand All @@ -42,24 +66,26 @@ jobs:

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
# Uses trusted publisher (OIDC) — no API token needed.
# Configure at: https://pypi.org/manage/project/anton/settings/publishing/
# Trusted publisher (OIDC) — release.yml must be registered at the
# anton-agent PyPI project's publishing settings (environment: pypi).

e2e:
needs: auto-release
permissions:
contents: read
uses: ./.github/workflows/tests_e2e_release.yml
with:
tag: ${{ needs.auto-release.outputs.tag }}
secrets: inherit

notify:
# Alert the eng channel if ANY job in the release pipeline fails (tag, PyPI
# publish, or release e2e).
# Alert the eng channel if ANY job in the release pipeline fails (tests, tag,
# PyPI publish, or release e2e).
# One job covers both outcomes: a `uses:` job cannot branch on status, so
# the aggregate result picks the failed or recovered message, and a
# cancelled run stays silent.
needs: [auto-release, publish, e2e]
if: ${{ !cancelled() && !contains(needs.*.result, 'cancelled') }}
needs: [unit-tests, auto-release, publish, e2e]
if: ${{ github.ref == 'refs/heads/main' && !cancelled() && !contains(needs.*.result, 'cancelled') }}
permissions:
contents: read
actions: read # the prior-run lookup behind the recovery message
Expand Down
34 changes: 8 additions & 26 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@ name: CI
permissions:
contents: read

# Unit + stub-e2e validation. Runs directly on pull requests, and is CALLED as a
# reusable (workflow_call) by the release publishers — release.yml on main and
# publish-staging.yml on staging — so a branch push validates exactly once,
# inside the release run tree, instead of as a second disconnected push run.
# Failure notification lives in the calling publisher, not here (mirrors the
# cowork-server tests-unit.yml reusable pattern).
on:
pull_request:
branches: [main,staging]
push:
branches: [main,staging]
branches: [main, staging]
workflow_call:

jobs:
run-tests:
Expand All @@ -31,26 +36,3 @@ jobs:

- name: Run E2E tests (stub)
run: uv run --group dev pytest tests/e2e/ -v

notify:
# Alert the eng channel if CI fails on a direct push to main/staging. PR runs
# are skipped — the author sees those directly.
# One job covers both outcomes: a `uses:` job cannot branch on status, so
# the aggregate result picks the failed or recovered message, and a
# cancelled run stays silent.
needs: [run-tests]
if: ${{ !cancelled() && !contains(needs.*.result, 'cancelled') && github.event_name == 'push' }}
permissions:
contents: read
actions: read # the prior-run lookup behind the recovery message
uses: mindsdb/github-actions/.github/workflows/notify-main-failure.yml@main
with:
env-name: "CI"
status: ${{ contains(needs.*.result, 'failure') && 'failed' || 'recovered' }}
# This workflow covers main AND staging from one notify job, so the scoping
# has to be decided per run rather than per file: a staging failure is only
# release-blocking once the freeze window is open, while a main failure
# always is. Hardcoding `true` here would mute main too.
freeze-scoped: ${{ github.ref_name == 'staging' }}
runs-on: ubuntu-latest
secrets: inherit
Loading