Restyle aai init starter templates with the AssemblyAI brand system #233
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| pull_request: | |
| branches: [main] | |
| types: [opened, reopened, ready_for_review, synchronize] | |
| push: | |
| branches: [main] # PRs are covered by pull_request (incl. synchronize); | |
| # scoping push to main avoids double-running every PR commit. | |
| # Least privilege: CI only needs to read the repo. Actions are pinned to commit | |
| # SHAs (a moved tag can't silently change what runs); Dependabot keeps them current. | |
| permissions: | |
| contents: read | |
| # Cancel superseded runs when new commits land on a PR/branch, but never cancel a | |
| # main run (don't kill an in-flight merge build). | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} | |
| jobs: | |
| check: | |
| name: lint + typecheck + tests (py${{ matrix.python-version }}) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| # Test both ends of the supported range: 3.12 is the floor (requires-python), | |
| # 3.13 is what the Homebrew formula ships. fail-fast off so one version's | |
| # failure doesn't mask the other's. | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python-version: ["3.12", "3.13"] | |
| # Pin the interpreter every `uv run`/`uv build` in check.sh resolves to, so the | |
| # matrix actually exercises each version rather than whatever uv would pick. | |
| env: | |
| UV_PYTHON: ${{ matrix.python-version }} | |
| steps: | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 | |
| with: | |
| persist-credentials: false # no job pushes; don't leave the token in .git/config | |
| - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| cache: pip | |
| # PortAudio backs sounddevice; ffmpeg decodes non-WAV/URL audio (the `--sample` | |
| # stream tests build a FileSource for the hosted sample, which needs ffmpeg). | |
| - name: System deps (PortAudio + ffmpeg) | |
| run: sudo apt-get update && sudo apt-get install -y libportaudio2 ffmpeg | |
| # check.sh lints Markdown and template JS/CSS via Node CLIs; pin to the | |
| # versions used locally. The runner ships Node, so a global npm install suffices. | |
| - name: Node lint CLIs | |
| run: npm install -g markdownlint-cli@0.45.0 prettier@3.8.3 | |
| # check.sh runs every tool through `uv run` / `uv build` for a locked, | |
| # reproducible env, so only uv must be on PATH (installed from PyPI to match | |
| # the repo's pip-based, no-new-action posture). `uv run` itself syncs the | |
| # project + dev group into .venv, so no `pip install -e .` is needed here. | |
| - name: Install | |
| run: python -m pip install uv | |
| # actionlint and gitleaks are Go binaries (no PyPI wheel), so check.sh self-skips | |
| # them locally like shellcheck. Build them here with the runner's preinstalled Go, | |
| # pinned to a release tag, and put GOPATH/bin on PATH so check.sh enforces them. | |
| # (gitleaks v8's Go module path is still github.com/zricethezav/gitleaks/v8.) | |
| - name: Workflow + secret scanners (actionlint, gitleaks) | |
| run: | | |
| go install github.com/rhysd/actionlint/cmd/actionlint@v1.7.7 | |
| go install github.com/zricethezav/gitleaks/v8@v8.21.2 | |
| echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH" | |
| - name: Lint, typecheck, test | |
| run: ./scripts/check.sh | |
| # Branch protection requires a check literally named "lint + typecheck + tests", | |
| # but `check` is a matrix, so its contexts are suffixed "(py3.12)" / "(py3.13)". | |
| # Re-publish the un-suffixed name here, green only when every matrix cell passed | |
| # (if: always() + an explicit result check, so a failed/skipped/cancelled matrix | |
| # can't satisfy the required check). Point branch protection at this one stable | |
| # name and matrix changes never break the required check again. | |
| check-result: | |
| name: lint + typecheck + tests | |
| needs: [check] | |
| if: always() | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| steps: | |
| - name: Require every py-version matrix cell to have passed | |
| run: | | |
| if [ "${{ needs.check.result }}" != "success" ]; then | |
| echo "check matrix result: ${{ needs.check.result }}" | |
| exit 1 | |
| fi | |
| echo "all py-version matrix cells passed" | |
| lint-formula: | |
| name: brew style (Homebrew formula) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 | |
| with: | |
| persist-credentials: false # no job pushes; don't leave the token in .git/config | |
| # Homebrew's formula linters live inside `brew`, so set it up on the runner. | |
| # Homebrew/actions is a monorepo (setup-homebrew is a subpath); pin it to a | |
| # commit SHA like every other action here — Dependabot keeps it current. | |
| - uses: Homebrew/actions/setup-homebrew@2ebcf16054461267868620b1414507f3ccc765c1 | |
| # `brew style` is the RuboCop-based formula linter and runs fully offline, so | |
| # it lints idioms (resource/depends_on ordering, on_linux scoping) on every PR. | |
| # A real `brew install` + `brew test` build runs in the `formula-install` job | |
| # below (it repoints the formula at a branch-source tarball with a computed | |
| # sha256, sidestepping the placeholder that stays until the v0.1.0 tag is cut). | |
| # The stricter `brew audit --strict --online` still belongs in a release job. | |
| - name: Lint the formula | |
| run: brew style ./Formula/aai.rb | |
| pre-commit: | |
| name: pre-commit | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 | |
| with: | |
| persist-credentials: false # no job pushes; don't leave the token in .git/config | |
| - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version: "3.12" | |
| cache: pip | |
| # PortAudio backs sounddevice; ffmpeg decodes the `--sample` stream source. | |
| - name: System deps (PortAudio + ffmpeg) | |
| run: sudo apt-get update && sudo apt-get install -y libportaudio2 ffmpeg | |
| # The local pytest hook runs `uv run --frozen python -m pytest`, so the tests | |
| # resolve the LOCKED dependency versions (uv.lock) rather than the newest | |
| # release `pip install` would pull — which is what keeps the byte-exact | |
| # `--help` snapshots stable. Install uv and materialize the frozen env here. | |
| - name: Install | |
| run: | | |
| python -m pip install --upgrade pip uv | |
| uv sync --frozen | |
| - uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1 | |
| build: | |
| name: build + twine check | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 | |
| with: | |
| persist-credentials: false # no job pushes; don't leave the token in .git/config | |
| - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version: "3.12" | |
| cache: pip | |
| - name: Build wheel + sdist | |
| run: | | |
| python -m pip install build twine | |
| python -m build | |
| - name: Validate metadata | |
| run: twine check dist/* | |
| audit: | |
| name: pip-audit (dependency CVEs) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 | |
| with: | |
| persist-credentials: false # no job pushes; don't leave the token in .git/config | |
| - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version: "3.12" | |
| cache: pip | |
| - name: Audit runtime dependencies for known CVEs | |
| run: | | |
| # Keep build tooling current first: pip-audit scans the whole environment, | |
| # so a pip/setuptools advisory that a one-line upgrade fixes would otherwise | |
| # fail the gate on something that isn't one of our runtime dependencies. | |
| python -m pip install --upgrade pip setuptools | |
| python -m pip install -e . pip-audit | |
| # Append `--ignore-vuln <ID>` to accept an unfixable transitive advisory. | |
| python -m pip_audit | |
| install-smoke: | |
| name: package install — real (${{ matrix.os }}) | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - os: ubuntu-latest | |
| kfilter: "" # all branches: pipx + pip --user + uv tool | |
| - os: macos-latest | |
| kfilter: '-k "pipx or uv_tool"' # pip --user is flaky on macOS (PEP 668) | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 | |
| with: | |
| persist-credentials: false # no job pushes; don't leave the token in .git/config | |
| - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version: "3.12" | |
| cache: pip | |
| # `aai --version` imports the package, which pulls in sounddevice (needs | |
| # PortAudio) and ffmpeg-backed sources. Match the other jobs' system deps. | |
| - name: System deps (Linux) | |
| if: runner.os == 'Linux' | |
| run: sudo apt-get update && sudo apt-get install -y libportaudio2 ffmpeg | |
| - name: System deps (macOS) | |
| if: runner.os == 'macOS' | |
| run: brew install portaudio ffmpeg | |
| # Use the system interpreter (no virtualenv) so install.sh's `pip --user` | |
| # branch is allowed. Editable install makes `aai_cli` importable for the | |
| # test's __version__ check; uv builds the wheel and drives the uv tool | |
| # branch; pipx drives the pipx branch (which doubles as `pipx install`). | |
| - name: Tooling | |
| run: | | |
| python -m pip install --upgrade pip # need pip >= 25.1 for --group | |
| python -m pip install -e . --group dev uv pipx | |
| - name: Real install smoke | |
| run: python -m pytest -q -m install_script ${{ matrix.kfilter }} | |
| formula-install: | |
| # Real `brew install` of the Homebrew formula, built from THIS branch's source. | |
| # macOS only: that's where the README points brew users first, and where the | |
| # macOS keyring backend (no on_linux jeepney/secretstorage) actually runs. | |
| name: brew install (real, macOS) | |
| runs-on: macos-latest | |
| timeout-minutes: 40 | |
| steps: | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 | |
| with: | |
| persist-credentials: false # no job pushes; don't leave the token in .git/config | |
| - uses: Homebrew/actions/setup-homebrew@2ebcf16054461267868620b1414507f3ccc765c1 | |
| # The committed formula's stable `url` points at a not-yet-cut release tag | |
| # with a placeholder sha256, so a vanilla install can't build the branch. | |
| # Repoint it at a source tarball of THIS checkout (the merge commit CI runs | |
| # on) with the real checksum, so `brew install` compiles the branch through | |
| # the formula's own resource list — the part most likely to drift from | |
| # pyproject/uv.lock. count=1 keeps every resource url/sha256 untouched. | |
| - name: Pin the formula to the branch source | |
| run: | | |
| set -euo pipefail | |
| version="$(grep -m1 '^version' pyproject.toml | sed -E 's/.*"([^"]+)".*/\1/')" | |
| tarball="$RUNNER_TEMP/aai-${version}.tar.gz" | |
| # Mirror a GitHub archive: a single top-level dir brew strips on extract. | |
| git archive --format=tar.gz --prefix="aai-${version}/" -o "$tarball" HEAD | |
| sha="$(shasum -a 256 "$tarball" | awk '{print $1}')" | |
| python3 - "Formula/aai.rb" "$tarball" "$sha" <<'PY' | |
| import re, sys, pathlib | |
| formula, tarball, sha = sys.argv[1], sys.argv[2], sys.argv[3] | |
| p = pathlib.Path(formula) | |
| src = p.read_text() | |
| src = re.sub(r'url ".*?"', f'url "file://{tarball}"', src, count=1) | |
| src = re.sub(r"sha256 .*", f'sha256 "{sha}"', src, count=1) | |
| p.write_text(src) | |
| PY | |
| grep -nE '^ (url|sha256) ' Formula/aai.rb | head -2 | |
| # Newer Homebrew refuses formulae outside a tap, so install through a | |
| # throwaway local tap holding the pinned formula (--no-git avoids needing a | |
| # git identity on the runner). --build-from-source compiles the full resource | |
| # list (rust + cryptography native builds); `brew test` then runs the | |
| # formula's own `test do` block, asserting `aai --version`. | |
| - name: brew install + test | |
| run: | | |
| set -euo pipefail | |
| brew tap-new --no-git aai/local | |
| cp Formula/aai.rb "$(brew --repository aai/local)/Formula/aai.rb" | |
| brew install --build-from-source --formula aai/local/aai | |
| aai --version | |
| brew test aai/local/aai |