Skip to content
Open
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
30 changes: 30 additions & 0 deletions .github/workflows/pr-fast-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ jobs:
outputs:
app: ${{ steps.filter.outputs.app }}
ci: ${{ steps.filter.outputs.ci }}
packaging: ${{ steps.filter.outputs.packaging }}
steps:
- uses: dorny/paths-filter@v3
id: filter
Expand Down Expand Up @@ -60,6 +61,10 @@ jobs:
- 'docs/bootstrap/**'
- '.env.example'
- 'CODEOWNERS'
packaging:
- 'pyproject.toml'
- '.github/workflows/release.yml'
- 'scripts/release/**'

fast-checks:
name: Fast Checks
Expand Down Expand Up @@ -89,6 +94,29 @@ jobs:
- name: Scan repository for secret patterns
run: bash scripts/check-detect-secrets.sh --all-files

package-build:
name: Package Build
runs-on: ubuntu-latest
timeout-minutes: 10
needs: changes
if: >-
github.event.pull_request.draft == false &&
needs.changes.outputs.packaging == 'true'
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}

- uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}

- name: Install build backend
run: python -m pip install --upgrade pip build

- name: Build distribution artifacts
run: bash scripts/release/dry-run.sh --skip-fast-checks

ci-gate:
name: CI Gate
runs-on: ['self-hosted', 'synology', 'shell-only', 'private']
Expand All @@ -97,13 +125,15 @@ jobs:
- changes
- fast-checks
- validate-secrets
- package-build
steps:
- name: Check required PR jobs
env:
RESULTS: >-
changes=${{ needs.changes.result }}
fast-checks=${{ needs.fast-checks.result }}
validate-secrets=${{ needs.validate-secrets.result }}
package-build=${{ needs.package-build.result }}
run: |
failed=0
for entry in $RESULTS; do
Expand Down
71 changes: 71 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: Release

on:
push:
tags:
- 'v*.*.*'

permissions:
contents: write

defaults:
run:
shell: bash

jobs:
github-release:
name: Build GitHub Release
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: '3.12'

- name: Install build backend
run: python -m pip install --upgrade pip build

- name: Validate tag and build artifacts
env:
RELEASE_TAG: ${{ github.ref_name }}
run: bash scripts/release/dry-run.sh

- name: Extract changelog body
env:
RELEASE_TAG: ${{ github.ref_name }}
run: |
python - <<'PY' > release-notes.md
from pathlib import Path
import os
import re
import sys

tag = os.environ["RELEASE_TAG"]
version = tag.removeprefix("v")
changelog = Path("CHANGELOG.md")
if not changelog.exists():
raise SystemExit("CHANGELOG.md is required for release notes")
Comment on lines +47 to +49
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Avoid hard-failing releases on missing CHANGELOG.md

This workflow unconditionally exits when CHANGELOG.md is absent, but the repository currently has no CHANGELOG.md, so every tag push that triggers release.yml will fail before creating a GitHub Release. Because this commit also enables capabilities.release, the new release path is effectively unusable until a changelog file is added or a fallback note strategy is implemented.

Useful? React with 👍 / 👎.


text = changelog.read_text()
pattern = rf"^## \[{re.escape(version)}\][^\n]*\n(?P<body>.*?)(?=^## \[|\Z)"
match = re.search(pattern, text, flags=re.MULTILINE | re.DOTALL)
if not match:
raise SystemExit(f"CHANGELOG.md has no section for {version}")

body = match.group("body").strip()
if not body:
raise SystemExit(f"CHANGELOG.md section for {version} is empty")
sys.stdout.write(body + "\n")
PY

- name: Create GitHub Release
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ github.ref_name }}
run: |
gh release create "$RELEASE_TAG" \
--title "$RELEASE_TAG" \
--notes-file release-notes.md \
dist/*.tar.gz dist/*.whl dist/SHA256SUMS
6 changes: 4 additions & 2 deletions project.bootstrap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ repo:
- .github/workflows/pr-fast-ci.yml
- .github/workflows/extended-validation.yml
- .github/workflows/claude.yml
- .github/workflows/release.yml
- scripts/check-detect-secrets.sh
- scripts/ci/**
- scripts/release/**
- scripts/codex-cloud/**
- scripts/claude-cloud/**
- scripts/claude/**
Expand Down Expand Up @@ -109,8 +111,8 @@ capabilities:
provider: cloudflare-pages
outputDir: dist
release:
enabled: false
kind: none
enabled: true
kind: github-release
docsPublish:
enabled: false
containers:
Expand Down
61 changes: 61 additions & 0 deletions scripts/release/dry-run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#!/usr/bin/env bash
set -euo pipefail

RUN_FAST_CHECKS=1
for arg in "$@"; do
case "$arg" in
--skip-fast-checks)
RUN_FAST_CHECKS=0
;;
*)
echo "usage: $0 [--skip-fast-checks]" >&2
exit 2
;;
esac
done

if [[ -z "${PYTHON_BIN:-}" ]]; then
if command -v python3.12 >/dev/null 2>&1; then
PYTHON_BIN=python3.12
else
PYTHON_BIN=python3
fi
fi

PACKAGE_VERSION="$("$PYTHON_BIN" - <<'PY'
from pathlib import Path
import tomllib

project = tomllib.loads(Path("pyproject.toml").read_text())["project"]
print(project["version"])
PY
)"
RELEASE_TAG="${RELEASE_TAG:-v$PACKAGE_VERSION}"

if [[ "$RELEASE_TAG" != "v$PACKAGE_VERSION" ]]; then
echo "release tag $RELEASE_TAG does not match pyproject.toml version $PACKAGE_VERSION" >&2
exit 1
fi

if [[ "$RUN_FAST_CHECKS" == "1" ]]; then
bash scripts/ci/run-fast-checks.sh
fi

"$PYTHON_BIN" - <<'PY'
import importlib.util
import sys

if importlib.util.find_spec("build") is None:
raise SystemExit("python -m build is unavailable; install it with: python -m pip install build")
PY

rm -rf dist
"$PYTHON_BIN" -m build

(
cd dist
find . -maxdepth 1 -type f ! -name SHA256SUMS -print0 | sort -z | xargs -0 shasum -a 256 > SHA256SUMS
)

echo "Built release artifacts for $RELEASE_TAG:"
ls -1 dist
Loading