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
128 changes: 128 additions & 0 deletions .github/workflows/published-release-assets.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
name: Prepare draft release assets

on:
workflow_dispatch:
inputs:
tag:
description: Existing version tag for the draft release, for example v0.1.0
required: true
type: string
draft_release_id:
description: Numeric GitHub release ID of the unpublished draft for that tag
required: true
type: string

permissions:
contents: read
actions: read

concurrency:
group: draft-release-assets-${{ inputs.tag }}
cancel-in-progress: false

jobs:
compatibility:
name: ${{ matrix.base_cli.name }} / Python ${{ matrix.python-version }}
runs-on: ubuntu-latest
timeout-minutes: 20
env:
RELEASE_TAG: ${{ inputs.tag }}
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.13"]
base_cli:
- name: minimum-0.4.3
spec: "==0.4.3"
- name: latest-supported
spec: ">=0.4.3,<0.5"
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
with:
ref: ${{ inputs.tag }}
- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: ${{ matrix.python-version }}
- name: Verify the release tag matches VERSION
run: test "$(tr -d '[:space:]' < VERSION)" = "${RELEASE_TAG#v}"
- name: Build the released demo wheel
run: python -m pip wheel --no-deps --wheel-dir dist .
- name: Install the framework version under test
run: python -m pip install "base-cli${{ matrix.base_cli.spec }}" "pytest>=8,<9"
- name: Install the release wheel without source dependencies
run: python -m pip install --no-deps dist/*.whl
- name: Run installed-wheel consumer tests
run: python -m pytest -q
- name: Record the exact compatibility evidence
env:
PYTHON_VERSION: ${{ matrix.python-version }}
BASE_CLI_REQUEST: ${{ matrix.base_cli.spec }}
run: |
python - <<'PY' > compatibility.txt
from importlib.metadata import version
import os

print(f"python={os.environ['PYTHON_VERSION']}")
print(f"base-cli-request={os.environ['BASE_CLI_REQUEST']}")
print(f"base-cli-installed={version('base-cli')}")
print(f"base-cli-demo-installed={version('base-cli-demo')}")
PY
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: compatibility-${{ matrix.base_cli.name }}-python-${{ matrix.python-version }}
path: compatibility.txt
if-no-files-found: error

assets:
needs: [compatibility]
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
actions: read
contents: write
env:
RELEASE_TAG: ${{ inputs.tag }}
DRAFT_RELEASE_ID: ${{ inputs.draft_release_id }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
with:
ref: ${{ inputs.tag }}
- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.13"
- name: Verify the release tag matches VERSION
run: test "$(tr -d '[:space:]' < VERSION)" = "${RELEASE_TAG#v}"
- name: Verify the selected release is an unpublished draft for this tag
run: |
test "$(gh api "repos/$GITHUB_REPOSITORY/releases/$DRAFT_RELEASE_ID" --jq '.draft')" = true
test "$(gh api "repos/$GITHUB_REPOSITORY/releases/$DRAFT_RELEASE_ID" --jq '.prerelease')" = false
test "$(gh api "repos/$GITHUB_REPOSITORY/releases/$DRAFT_RELEASE_ID" --jq '.tag_name')" = "$RELEASE_TAG"
- name: Install package and release checks
run: python -m pip install ".[dev]"
- name: Run the authoritative consumer and package gates
run: |
./tests/validate.sh
./tests/package.sh
- name: Build the release distributions
run: python -m build --sdist --wheel --outdir release-dist .
- name: Verify distribution metadata
run: python -m twine check release-dist/*
- name: Install and test the built release wheel
run: |
python -m pip install --no-deps release-dist/*.whl
python -m pytest -q
- name: Download compatibility evidence from this workflow run
run: gh run download "$GITHUB_RUN_ID" --repo "$GITHUB_REPOSITORY" --pattern 'compatibility-*' --dir release-dist/compatibility-evidence
- name: Prepare evidence and immutable checksums
run: |
{
printf 'Release tag: %s\n' "$RELEASE_TAG"
printf 'Declared Base-CLI range: base-cli>=0.4.3,<0.5\n\n'
find release-dist/compatibility-evidence -name compatibility.txt -print -exec cat {} \;
} > release-dist/COMPATIBILITY.txt
sha256sum release-dist/*.whl release-dist/*.tar.gz release-dist/COMPATIBILITY.txt > release-dist/SHA256SUMS.txt
- name: Attach verified assets to the unpublished draft
run: gh release upload "$RELEASE_TAG" release-dist/*.whl release-dist/*.tar.gz release-dist/COMPATIBILITY.txt release-dist/SHA256SUMS.txt --clobber --repo "$GITHUB_REPOSITORY"
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,19 @@ $ northstar --help
$ northstar --quiet status
```

### Install an existing release

To install the first published demo release without cloning the repository:

```bash
python -m pip install \
"https://github.com/basefoundry/base-cli-demo/releases/download/v0.1.0/base_cli_demo-0.1.0-py3-none-any.whl"
```

Verify the artifact against `SHA256SUMS.txt` on the same GitHub Release. The
wheel depends on the released `base-cli>=0.4.3,<0.5` API line; it does not
install Base or require a Base workspace.

The default environment is `dev`. Select another fixture environment with the
framework lifecycle option:

Expand Down
45 changes: 30 additions & 15 deletions docs/release-process.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Release Process

This repository uses the Base release contract. The machine-readable release
metadata lives in `base_manifest.yaml`; the guarded `basectl release`
commands use that contract for readiness checks, notes, tags, and GitHub
Releases.
metadata lives in `base_manifest.yaml`; `basectl release check/plan/notes` use
that contract for readiness and release notes. GitHub immutable releases must
be prepared as drafts so validated assets are attached before publication.

## Standard Sequence

Expand All @@ -23,29 +23,44 @@ Releases.
```bash
basectl release check --version X.Y.Z
basectl release plan --version X.Y.Z
basectl release notes --version X.Y.Z
basectl release notes --version X.Y.Z > RELEASE_NOTES.md
basectl release publish --version X.Y.Z --dry-run
```

7. Publish only after the checks pass. Use `--yes` only from a trusted
non-interactive release shell:
7. After separate publication authorization, verify immutable releases are
enabled for the repository, then create the annotated version tag for the
exact reviewed `main` commit. Create a GitHub Release draft for that existing
tag and add the release notes from step 6 (`RELEASE_NOTES.md`). Do not run
`basectl release publish --yes` for this immutable-release path: it creates
a published release directly, leaving no draft stage for attaching the
validated assets.

```bash
basectl release publish --version X.Y.Z --yes
git tag -a vX.Y.Z -m "base-cli-demo vX.Y.Z"
git push origin vX.Y.Z
gh release create vX.Y.Z --verify-tag --draft --title "vX.Y.Z" --notes-file RELEASE_NOTES.md
```

8. Verify the annotated tag and GitHub Release for `basefoundry/base-cli-demo`.
9. The `Release package` workflow validates the tagged version, runs the
package gate, installs the built wheel, and uploads the wheel/source
distributions as a workflow artifact. Base's `basectl release publish`
remains the guarded publisher for GitHub Release notes; this demo workflow
does not imply a PyPI or Base-CLI release.
10. Complete every declared downstream handoff. For Homebrew, update the tap
8. Dispatch `Prepare draft release assets` with the tag and draft release ID
(`gh release view vX.Y.Z --json databaseId --jq .databaseId`). The workflow
verifies that the selected release is still a draft for that tag, tests the
wheel against the minimum and latest supported Base-CLI releases on Python
3.10 and 3.13, then attaches the wheel, sdist, exact compatibility evidence,
and SHA-256 checksums. It never creates a tag or release and never publishes
the draft.
9. Review the draft assets and verify the repository has immutable releases
enabled. Then publish the draft in GitHub. When immutable releases are
enabled, GitHub locks the tag and assets at publication; see the official
[immutable releases guidance](https://docs.github.com/en/code-security/concepts/supply-chain-security/immutable-releases).
10. Confirm the README's version-pinned installation path in a clean
environment, download the checksum/evidence files, and verify package
filenames and hashes against the published assets.
11. Complete every declared downstream handoff. For Homebrew, update the tap
formula to the published archive and checksum, run the formula tests and
audit, publish required bottles, and verify install and upgrade paths. If a
downstream repository pins this project by commit, update and validate that
pin after the release.
11. Record the release and downstream URLs on the release issue, then remove
12. Record the release and downstream URLs on the release issue, then remove
the release worktree and merged branches when safe.

## Repository Contract
Expand Down
Loading