Skip to content

Merge pull request #26 from baskduf/fix/release-blocking-findings #67

Merge pull request #26 from baskduf/fix/release-blocking-findings

Merge pull request #26 from baskduf/fix/release-blocking-findings #67

Workflow file for this run

name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
quality:
name: quality / python ${{ matrix.python-version }}
runs-on: ubuntu-latest
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12"]
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
cache: pip
- name: Install test tools
run: python -m pip install --upgrade pip pytest build pyyaml
- name: Compile Python files
run: python -m py_compile skills/artic/scripts/*.py tests/*.py scripts/*.py
- name: Run package tests
run: python -m pytest -q
- name: Smoke scaffold Artic docs
run: python skills/artic/scripts/scaffold_artic_files.py --root /tmp/artic-smoke
- name: Validate scaffolded Artic docs
run: python skills/artic/scripts/validate_artic_outputs.py --root /tmp/artic-smoke
- name: Lint scaffolded DESIGN.md without warnings
run: |
set -euo pipefail
npx -y @google/design.md lint /tmp/artic-smoke/DESIGN.md | tee /tmp/artic-design-lint.json
python - <<'PY'
import json, pathlib
data = json.loads(pathlib.Path('/tmp/artic-design-lint.json').read_text())
summary = data.get('summary', {})
assert summary.get('errors', 0) == 0, data
assert summary.get('warnings', 0) == 0, data
PY
- name: Build Python package metadata
run: python -m build --sdist --wheel
- name: Verify package archives are clean
run: |
python scripts/check_release_artifacts.py --require-payload dist/artic-*.tar.gz
python scripts/check_release_artifacts.py dist/artic-*.whl
- name: Verify built package metadata
run: |
python - <<'PY'
import email.parser, pathlib, re, tarfile, zipfile
expected_name = 'artic'
match = re.search(r'^version = "([^"]+)"', pathlib.Path('pyproject.toml').read_text(), re.MULTILINE)
assert match, 'pyproject.toml missing version'
expected_version = match.group(1)
wheels = list(pathlib.Path('dist').glob('*.whl'))
sdists = list(pathlib.Path('dist').glob('*.tar.gz'))
assert wheels, 'missing wheel artifact'
assert sdists, 'missing sdist artifact'
with zipfile.ZipFile(wheels[0]) as zf:
metadata_name = next(n for n in zf.namelist() if n.endswith('.dist-info/METADATA'))
meta = email.parser.Parser().parsestr(zf.read(metadata_name).decode())
assert meta['Name'] == expected_name, meta['Name']
assert meta['Version'] == expected_version, meta['Version']
with tarfile.open(sdists[0]) as tf:
names = tf.getnames()
assert any(name.endswith('README.md') for name in names), 'sdist missing README.md'
assert any(name.endswith('skills/artic/SKILL.md') for name in names), 'sdist missing Artic skill payload'
assert any(name.endswith('plugins/claude-artic/.claude-plugin/plugin.json') for name in names), 'sdist missing Claude plugin manifest'
assert any(name.endswith('plugins/codex-artic/.codex-plugin/plugin.json') for name in names), 'sdist missing Codex plugin manifest'
PY
repository-integrity:
name: repository integrity
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Lint GitHub Actions workflows
run: |
set -euo pipefail
curl -sSL https://github.com/rhysd/actionlint/releases/download/v1.7.12/actionlint_1.7.12_linux_amd64.tar.gz -o /tmp/actionlint.tar.gz
tar -xzf /tmp/actionlint.tar.gz -C /tmp actionlint
/tmp/actionlint
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.12"
cache: pip
- name: Install tools
run: python -m pip install --upgrade pip pytest pyyaml
- name: Validate workflow YAML parses
run: |
python - <<'PY'
import pathlib, yaml
for path in pathlib.Path('.github/workflows').glob('*.yml'):
with path.open() as f:
yaml.safe_load(f)
PY
- name: Run integrity tests
run: python -m pytest -q tests/test_artic_package.py
- name: Check for common committed secrets
run: |
python - <<'PY'
import pathlib, re, sys
patterns = [
re.compile(r'(?i)(api[_-]?key|secret|password|passwd|token)\s*[:=]\s*["\'][^"\']{8,}["\']'),
re.compile(r'gho_[A-Za-z0-9_]{20,}'),
re.compile(r'github_pat_[A-Za-z0-9_]{20,}'),
re.compile(r'sk-[A-Za-z0-9]{20,}'),
]
skip_parts = {'.git', '.pytest_cache', '__pycache__', 'dist', 'build'}
findings = []
for path in pathlib.Path('.').rglob('*'):
if not path.is_file() or skip_parts.intersection(path.parts):
continue
try:
text = path.read_text(encoding='utf-8')
except UnicodeDecodeError:
continue
for line_no, line in enumerate(text.splitlines(), 1):
if any(p.search(line) for p in patterns):
findings.append(f'{path}:{line_no}: possible secret pattern')
if findings:
print('\n'.join(findings))
sys.exit(1)
PY
release-dry-run:
name: release dry-run
runs-on: ubuntu-latest
timeout-minutes: 10
needs: [quality, repository-integrity]
if: github.event_name != 'pull_request'
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.12"
cache: pip
- name: Install build tools
run: python -m pip install --upgrade pip build
- name: Build release archives
run: |
set -euo pipefail
VERSION=$(python - <<'PY'
import pathlib, re
match = re.search(r'^version = "([^"]+)"', pathlib.Path('pyproject.toml').read_text(), re.MULTILINE)
assert match, 'pyproject.toml missing version'
print(match.group(1))
PY
)
RELEASE_TAG="v${VERSION}"
python -m build --sdist --wheel
python scripts/build_skill_archive.py --output "dist/artic-skill-${RELEASE_TAG}.tar.gz"
expected=(
"artic-${VERSION}.tar.gz"
"artic-${VERSION}-py3-none-any.whl"
"artic-skill-${RELEASE_TAG}.tar.gz"
)
for file in "${expected[@]}"; do
test -s "dist/$file"
done
(cd dist && shasum -a 256 "${expected[@]}" > SHA256SUMS.txt)
python scripts/check_release_artifacts.py --require-payload \
"dist/artic-${VERSION}.tar.gz" \
"dist/artic-skill-${RELEASE_TAG}.tar.gz"
python scripts/check_release_artifacts.py "dist/artic-${VERSION}-py3-none-any.whl"
- name: Upload dry-run artifacts
uses: actions/upload-artifact@v4
with:
name: artic-release-dry-run
path: |
dist/*
if-no-files-found: error