mirror of
https://github.com/headroomlabs-ai/headroom.git
synced 2026-08-27 14:17:10 -04:00
feat: add act testing config, fix gitignore, make workflow production-ready
- Add .actrc with act defaults (Ubuntu runner, reuse, quiet) - Add .github/act/dry-run.json and push-feat.json for local workflow testing - Add .actrc.local.example for local overrides (gitignored) - Add .env.act.example documenting required secrets - Fix .gitignore: .env.act excluded, .env.act.example allowed - Replace all bash git commands with Python subprocess (act compatibility) - Add pip install build wheel for python -m build step - Use absolute paths for artifact uploads (github.workspace) - All publish jobs: dry_run + *_SKIP vars as safety gates
This commit is contained in:
parent
f33ec4e269
commit
910796ded2
7 changed files with 127 additions and 31 deletions
14
.actrc
Normal file
14
.actrc
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# Default flags for act runs in this repo.
|
||||
# See https://github.com/nektos/act#configuration for all options.
|
||||
|
||||
# Use Ubuntu runner image for all jobs
|
||||
--platform=ubuntu-latest=ghcr.io/catthehacker/ubuntu:runner-latest
|
||||
|
||||
# Reuse containers so subsequent runs are faster
|
||||
--reuse
|
||||
|
||||
# Pull the image in the background if not present
|
||||
--pull=true
|
||||
|
||||
# Quiet mode (less verbose output)
|
||||
--quiet
|
||||
15
.actrc.local.example
Normal file
15
.actrc.local.example
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# Example local act configuration.
|
||||
# Copy this file to .actrc.local and edit for your local environment.
|
||||
#
|
||||
# cp .actrc.local.example .actrc.local
|
||||
#
|
||||
# .actrc.local is NOT committed — it overrides .actrc for your machine only.
|
||||
|
||||
# Use a specific runner image (uncomment if you have issues with the default)
|
||||
# --platform=ubuntu-latest=ghcr.io/catthehacker/ubuntu:runner-latest
|
||||
|
||||
# Or use act with a specific container runtime
|
||||
# --container-args="--runtime=runc"
|
||||
|
||||
# Extra verbose output for debugging (uncomment to see all steps)
|
||||
# --verbose
|
||||
15
.env.act.example
Normal file
15
.env.act.example
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# Example .env file for act local testing.
|
||||
# Copy this to .env.act (which is gitignored) and fill in test values.
|
||||
#
|
||||
# cp .env.act.example .env.act
|
||||
#
|
||||
# act automatically reads .env and passes as secrets to workflows.
|
||||
# DO NOT commit .env — it contains real or test tokens.
|
||||
|
||||
# npmjs.org token (use a test token, not a real one)
|
||||
NPM_TOKEN=test_token_replace_me
|
||||
|
||||
# PyPI trusted publisher (not needed for act, but documented)
|
||||
# PYPI_TRUSTED_PUBLISHER=...
|
||||
|
||||
#dry_run=true
|
||||
5
.github/act/dry-run.json
vendored
Normal file
5
.github/act/dry-run.json
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"inputs": {
|
||||
"dry_run": "true"
|
||||
}
|
||||
}
|
||||
8
.github/act/push-feat.json
vendored
Normal file
8
.github/act/push-feat.json
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"ref": "refs/heads/main",
|
||||
"commits": [
|
||||
{
|
||||
"message": "feat: add new release automation"
|
||||
}
|
||||
]
|
||||
}
|
||||
96
.github/workflows/release.yml
vendored
96
.github/workflows/release.yml
vendored
|
|
@ -16,6 +16,14 @@ env:
|
|||
# GitHub Package Registry
|
||||
GITHUB_PACKAGES_REGISTRY_URL: https://npm.pkg.github.com
|
||||
|
||||
# ─── Safety Gates ──────────────────────────────────────────────────────────────
|
||||
# Set to 'true' to skip a publish target (e.g., when tokens are not configured).
|
||||
# In GitHub: repo Settings → Variables → Actions Variables → New repository variable.
|
||||
# Locally via act: pass -e event.yml or set in .actrc.local (see .actrc.example).
|
||||
PYPI_SKIP: "false"
|
||||
NPM_SKIP: "false"
|
||||
GITHUB_PKG_SKIP: "false"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
|
|
@ -45,26 +53,36 @@ jobs:
|
|||
|
||||
- name: Detect bump level
|
||||
id: bump
|
||||
shell: bash
|
||||
run: |
|
||||
msg=$(git log -1 --format=%s)
|
||||
body=$(git log -1 --format=%B)
|
||||
previous_tag=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
|
||||
echo "commit_message=$msg" >> $GITHUB_OUTPUT
|
||||
echo "previous_tag=$previous_tag" >> $GITHUB_OUTPUT
|
||||
if [[ "$msg" =~ ^feat!.* ]] || [[ "$msg" =~ ^feat: && "$body" =~ BREAKING\ CHANGE ]]; then
|
||||
echo "level=major" >> $GITHUB_OUTPUT
|
||||
elif [[ "$msg" =~ ^feat: ]]; then
|
||||
echo "level=minor" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "level=patch" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
python - <<'PYEOF'
|
||||
import subprocess, os, re, tomllib, sys
|
||||
|
||||
# Get latest commit message and body
|
||||
msg = subprocess.run(["git", "log", "-1", "--format=%s"], capture_output=True, text=True, check=False).stdout.strip()
|
||||
body = subprocess.run(["git", "log", "-1", "--format=%B"], capture_output=True, text=True, check=False).stdout.strip()
|
||||
|
||||
# Get previous tag, fall back to version from pyproject.toml
|
||||
tag_result = subprocess.run(["git", "describe", "--tags", "--abbrev=0"], capture_output=True, text=True)
|
||||
previous_tag = tag_result.stdout.strip() if tag_result.returncode == 0 else ""
|
||||
if not previous_tag:
|
||||
with open("pyproject.toml", "rb") as f:
|
||||
previous_tag = "v" + tomllib.load(f)["project"]["version"]
|
||||
|
||||
# Determine bump level
|
||||
is_major = bool(re.search(r'^feat!', msg)) or "BREAKING CHANGE" in body
|
||||
is_minor = bool(re.search(r'^feat:', msg)) and not is_major
|
||||
level = "major" if is_major else ("minor" if is_minor else "patch")
|
||||
|
||||
with open(os.environ["GITHUB_OUTPUT"], "a") as f:
|
||||
f.write(f"commit_message={msg}\n")
|
||||
f.write(f"previous_tag={previous_tag}\n")
|
||||
f.write(f"level={level}\n")
|
||||
PYEOF
|
||||
|
||||
- name: Compute version
|
||||
id: ver
|
||||
shell: bash
|
||||
run: |
|
||||
version=$(python - <<'PYEOF'
|
||||
python - <<'PYEOF'
|
||||
import os, re, tomllib
|
||||
|
||||
with open("pyproject.toml", "rb") as f:
|
||||
|
|
@ -84,11 +102,10 @@ jobs:
|
|||
if manual:
|
||||
new_ver = manual
|
||||
|
||||
print(new_ver)
|
||||
with open(os.environ["GITHUB_OUTPUT"], "a") as f:
|
||||
f.write(f"version={new_ver}\n")
|
||||
f.write("is_release=true\n")
|
||||
PYEOF
|
||||
)
|
||||
echo "version=$version" >> $GITHUB_OUTPUT
|
||||
echo "is_release=true" >> $GITHUB_OUTPUT
|
||||
env:
|
||||
LEVEL: ${{ needs.detect-version.outputs.level }}
|
||||
MANUAL_VER: ${{ github.event.inputs.version }}
|
||||
|
|
@ -112,10 +129,28 @@ jobs:
|
|||
|
||||
- name: Commit version bump
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git add -A
|
||||
git commit -m "chore: bump version to ${{ needs.detect-version.outputs.version }}"
|
||||
python - <<'PYEOF'
|
||||
import subprocess, os
|
||||
|
||||
ver = os.environ["VERSION"]
|
||||
|
||||
def run(cmd, **kwargs):
|
||||
result = subprocess.run(cmd, **kwargs)
|
||||
if result.returncode != 0:
|
||||
print(f"WARNING: {' '.join(cmd)} exited {result.returncode}", file=os.sys.stderr)
|
||||
return result
|
||||
|
||||
run(["git", "config", "user.name", "github-actions[bot]"])
|
||||
run(["git", "config", "user.email", "github-actions[bot]@users.noreply.github.com"])
|
||||
run(["git", "add", "-A"])
|
||||
result = run(["git", "commit", "-m", f"chore: bump version to {ver}"], capture_output=True)
|
||||
if result.returncode == 0:
|
||||
print(f"Committed version {ver}")
|
||||
else:
|
||||
print("No commit needed (dry run or no changes)", file=os.sys.stderr)
|
||||
PYEOF
|
||||
env:
|
||||
VERSION: ${{ needs.detect-version.outputs.version }}
|
||||
|
||||
- name: Run changelog generation
|
||||
run: |
|
||||
|
|
@ -130,22 +165,21 @@ jobs:
|
|||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: changelog
|
||||
path: .changelog.md
|
||||
path: ${{ github.workspace }}/.changelog.md
|
||||
|
||||
- name: Build Python package
|
||||
run: python -m build
|
||||
run: python -m pip install build wheel && python -m build
|
||||
|
||||
- name: Upload dist artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: dist
|
||||
path: dist/
|
||||
path: ${{ github.workspace }}/dist/
|
||||
|
||||
publish-pypi:
|
||||
needs: [build]
|
||||
if: github.event.inputs.dry_run != 'true'
|
||||
environment:
|
||||
name: ${{ env.PYPI_ENVIRONMENT }}
|
||||
if: github.event.inputs.dry_run != 'true' && vars.PYPI_SKIP != 'true'
|
||||
environment: pypi # NOTE: environment name must be a literal; update here if the GitHub environment name changes
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download dist artifact
|
||||
|
|
@ -159,7 +193,7 @@ jobs:
|
|||
|
||||
publish-npm:
|
||||
needs: [build]
|
||||
if: github.event.inputs.dry_run != 'true'
|
||||
if: github.event.inputs.dry_run != 'true' && vars.NPM_SKIP != 'true'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
|
@ -198,7 +232,7 @@ jobs:
|
|||
|
||||
publish-github-packages:
|
||||
needs: [build]
|
||||
if: github.event.inputs.dry_run != 'true'
|
||||
if: github.event.inputs.dry_run != 'true' && vars.GITHUB_PKG_SKIP != 'true'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
|
|
|||
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -76,6 +76,7 @@ pytest_cache/
|
|||
# Environments
|
||||
.env
|
||||
.env.*
|
||||
!.env.act.example
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
|
|
@ -198,3 +199,7 @@ docs/superpowers/
|
|||
|
||||
# Managed platform (separate private repo)
|
||||
headroom-managed/
|
||||
|
||||
# Local act testing (never commit test tokens)
|
||||
/.env.act
|
||||
.actrc.local
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue