From a5b80af4289406b9e54e6e567ee183b0d233a7af Mon Sep 17 00:00:00 2001 From: Ahmed Allam Date: Sun, 4 Oct 2026 17:57:26 +0000 Subject: [PATCH] ci: split CI into per-concern reusable workflows behind one ci-passed gate Add uv lock --check, a wheel install smoke test, a PyInstaller dry run, actionlint and zizmor on the workflows, CodeQL, and Dependabot for action pins, uv, Go and npm dependencies. Release caches are disabled so zizmor's cache-poisoning audit passes. --- .github/actions/setup-python-env/action.yml | 21 ++++ .github/dependabot.yml | 49 ++++++++ .github/workflows/build-release.yml | 4 +- .github/workflows/ci-package.yml | 55 +++++++++ .github/workflows/ci-python.yml | 61 ++++++++++ .github/workflows/ci-tui.yml | 26 +++++ .github/workflows/ci-viewer.yml | 27 +++++ .github/workflows/ci-workflows.yml | 37 +++++++ .github/workflows/ci.yml | 117 +++++--------------- .github/workflows/codeql.yml | 48 ++++++++ 10 files changed, 356 insertions(+), 89 deletions(-) create mode 100644 .github/actions/setup-python-env/action.yml create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/ci-package.yml create mode 100644 .github/workflows/ci-python.yml create mode 100644 .github/workflows/ci-tui.yml create mode 100644 .github/workflows/ci-viewer.yml create mode 100644 .github/workflows/ci-workflows.yml create mode 100644 .github/workflows/codeql.yml diff --git a/.github/actions/setup-python-env/action.yml b/.github/actions/setup-python-env/action.yml new file mode 100644 index 00000000..ab70c894 --- /dev/null +++ b/.github/actions/setup-python-env/action.yml @@ -0,0 +1,21 @@ +name: Set up Python environment +description: Install Python and uv, then sync the locked project environment. + +inputs: + python-version: + description: Python version to install. + default: '3.12' + +runs: + using: composite + steps: + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + with: + python-version: ${{ inputs.python-version }} + - uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2 + with: + enable-cache: true + - run: uv sync --frozen --python "$PYTHON_VERSION" + shell: bash + env: + PYTHON_VERSION: ${{ inputs.python-version }} diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..9aa16bf1 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,49 @@ +version: 2 + +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + cooldown: + default-days: 7 + groups: + actions: + patterns: + - '*' + + - package-ecosystem: uv + directory: / + schedule: + interval: weekly + cooldown: + default-days: 7 + groups: + python: + update-types: + - minor + - patch + + - package-ecosystem: gomod + directory: /strix/interface/tui + schedule: + interval: weekly + cooldown: + default-days: 7 + groups: + go: + update-types: + - minor + - patch + + - package-ecosystem: npm + directory: /strix/interface/viewer/frontend + schedule: + interval: weekly + cooldown: + default-days: 7 + groups: + viewer: + update-types: + - minor + - patch diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index 9e5292b5..86e3da4c 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -43,12 +43,14 @@ jobs: python-version: '3.12' - uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2 + with: + enable-cache: false - uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 with: go-version: '1.24.x' check-latest: true - cache-dependency-path: strix/interface/tui/go.sum + cache: false - name: Build shell: bash diff --git a/.github/workflows/ci-package.yml b/.github/workflows/ci-package.yml new file mode 100644 index 00000000..942b9844 --- /dev/null +++ b/.github/workflows/ci-package.yml @@ -0,0 +1,55 @@ +name: Package + +on: + workflow_call: + +permissions: + contents: read + +jobs: + wheel: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + with: + persist-credentials: false + - uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 + with: + go-version-file: strix/interface/tui/go.mod + cache-dependency-path: strix/interface/tui/go.sum + - uses: ./.github/actions/setup-python-env + - run: uv build --wheel + - name: Wheel ships the TUI sidecar and the viewer bundle + run: | + uv run --frozen python - <<'PY' + import glob, zipfile + (wheel,) = glob.glob("dist/*.whl") + names = zipfile.ZipFile(wheel).namelist() + assert "strix/bin/strix-tui" in names, names + assert "strix/interface/viewer/static/index.html" in names, names + assert not any(name.startswith("strix/interface/viewer/frontend/") for name in names) + assert not any(name.startswith("strix/interface/tui/cmd/") for name in names) + PY + - name: Install into a clean environment and run the CLI + run: | + uv venv --python 3.12 /tmp/strix-smoke + uv pip install --python /tmp/strix-smoke/bin/python dist/*.whl + /tmp/strix-smoke/bin/strix --version + /tmp/strix-smoke/bin/strix --help >/dev/null + + binary: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + with: + persist-credentials: false + - uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 + with: + go-version-file: strix/interface/tui/go.mod + cache-dependency-path: strix/interface/tui/go.sum + - uses: ./.github/actions/setup-python-env + - run: make tui-build + - run: uv run --frozen pyinstaller strix.spec --noconfirm + - run: dist/strix --version + - name: Binary bundles the TUI sidecar + run: uv run --frozen pyi-archive_viewer -l dist/strix | grep -E "strix/bin/strix-tui" >/dev/null diff --git a/.github/workflows/ci-python.yml b/.github/workflows/ci-python.yml new file mode 100644 index 00000000..d1ee0591 --- /dev/null +++ b/.github/workflows/ci-python.yml @@ -0,0 +1,61 @@ +name: Python + +on: + workflow_call: + +permissions: + contents: read + +jobs: + lock: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + with: + persist-credentials: false + - uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2 + - run: uv lock --check + + ruff: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + with: + persist-credentials: false + - uses: ./.github/actions/setup-python-env + - run: uv run --frozen ruff check . + - run: uv run --frozen ruff format --check . + + mypy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + with: + persist-credentials: false + - uses: ./.github/actions/setup-python-env + - run: uv run --frozen mypy strix/ + + bandit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + with: + persist-credentials: false + - uses: ./.github/actions/setup-python-env + - run: uv run --frozen bandit -r strix/ -c pyproject.toml + + pytest: + name: pytest (${{ matrix.python-version }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ['3.12', '3.13', '3.14'] + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + with: + persist-credentials: false + - uses: ./.github/actions/setup-python-env + with: + python-version: ${{ matrix.python-version }} + - run: uv run --frozen pytest -q diff --git a/.github/workflows/ci-tui.yml b/.github/workflows/ci-tui.yml new file mode 100644 index 00000000..1dac8219 --- /dev/null +++ b/.github/workflows/ci-tui.yml @@ -0,0 +1,26 @@ +name: TUI + +on: + workflow_call: + +permissions: + contents: read + +jobs: + go: + runs-on: ubuntu-latest + defaults: + run: + working-directory: strix/interface/tui + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + with: + persist-credentials: false + - uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 + with: + go-version-file: strix/interface/tui/go.mod + cache-dependency-path: strix/interface/tui/go.sum + - run: test -z "$(gofmt -l .)" + - run: go vet ./... + - run: CGO_ENABLED=0 go build -trimpath -o /dev/null ./cmd/strix-tui + - run: go test -race ./... diff --git a/.github/workflows/ci-viewer.yml b/.github/workflows/ci-viewer.yml new file mode 100644 index 00000000..4bfc9361 --- /dev/null +++ b/.github/workflows/ci-viewer.yml @@ -0,0 +1,27 @@ +name: Viewer + +on: + workflow_call: + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + defaults: + run: + working-directory: strix/interface/viewer/frontend + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + with: + persist-credentials: false + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: '24' + cache: npm + cache-dependency-path: strix/interface/viewer/frontend/package-lock.json + - run: npm ci --no-audit --no-fund + - run: npm run build + - name: Committed viewer bundle matches the build + run: git -C "$GITHUB_WORKSPACE" diff --exit-code --stat -- strix/interface/viewer/static && test -z "$(git -C "$GITHUB_WORKSPACE" status --porcelain -- strix/interface/viewer/static)" diff --git a/.github/workflows/ci-workflows.yml b/.github/workflows/ci-workflows.yml new file mode 100644 index 00000000..5796e0d2 --- /dev/null +++ b/.github/workflows/ci-workflows.yml @@ -0,0 +1,37 @@ +name: Workflows + +on: + workflow_call: + +permissions: + contents: read + +jobs: + actionlint: + runs-on: ubuntu-latest + env: + ACTIONLINT_VERSION: 1.7.12 + ACTIONLINT_SCRIPT_REF: 914e7df21a07ef503a81201c76d2b11c789d3fca # v1.7.12 + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + with: + persist-credentials: false + - name: Install actionlint + run: | + mkdir -p "$RUNNER_TEMP/actionlint" + curl -fsSL --retry 5 -o "$RUNNER_TEMP/actionlint/download.bash" \ + "https://raw.githubusercontent.com/rhysd/actionlint/$ACTIONLINT_SCRIPT_REF/scripts/download-actionlint.bash" + bash "$RUNNER_TEMP/actionlint/download.bash" "$ACTIONLINT_VERSION" "$RUNNER_TEMP/actionlint" + - run: | + "$RUNNER_TEMP/actionlint/actionlint" -color + + zizmor: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + with: + persist-credentials: false + - uses: zizmorcore/zizmor-action@cc914d7f3750a2d13d75c7f184a1060aa0e9d482 # v0.6.4 + with: + advanced-security: false + version: 1.22.0 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 88dfa4ec..9ae8e763 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,97 +15,38 @@ concurrency: cancel-in-progress: true jobs: - ruff: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - with: - persist-credentials: false - - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 - with: - python-version: '3.12' - - uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2 - - run: uv sync --frozen - - run: uv run --frozen ruff check . - - run: uv run --frozen ruff format --check . - - mypy: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - with: - persist-credentials: false - - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 - with: - python-version: '3.12' - - uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2 - - run: uv sync --frozen - - run: uv run --frozen mypy strix/ - - bandit: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - with: - persist-credentials: false - - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 - with: - python-version: '3.12' - - uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2 - - run: uv sync --frozen - - run: uv run --frozen bandit -r strix/ -c pyproject.toml - - pytest: - name: pytest (${{ matrix.python-version }}) - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - python-version: ['3.12', '3.13', '3.14'] - steps: - - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - with: - persist-credentials: false - - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 - with: - python-version: ${{ matrix.python-version }} - - uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2 - - run: uv sync --frozen --python ${{ matrix.python-version }} - - run: uv run --frozen pytest -q + python: + uses: ./.github/workflows/ci-python.yml tui: - runs-on: ubuntu-latest - defaults: - run: - working-directory: strix/interface/tui - steps: - - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - with: - persist-credentials: false - - uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 - with: - go-version-file: strix/interface/tui/go.mod - cache-dependency-path: strix/interface/tui/go.sum - - run: test -z "$(gofmt -l .)" - - run: go vet ./... - - run: CGO_ENABLED=0 go build -trimpath -o /dev/null ./cmd/strix-tui - - run: go test -race ./... + uses: ./.github/workflows/ci-tui.yml viewer: + uses: ./.github/workflows/ci-viewer.yml + + package: + uses: ./.github/workflows/ci-package.yml + + workflows: + uses: ./.github/workflows/ci-workflows.yml + + ci-passed: + name: ci-passed + if: always() + needs: + - python + - tui + - viewer + - package + - workflows runs-on: ubuntu-latest - defaults: - run: - working-directory: strix/interface/viewer/frontend steps: - - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - with: - persist-credentials: false - - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 - with: - node-version: '24' - cache: npm - cache-dependency-path: strix/interface/viewer/frontend/package-lock.json - - run: npm ci --no-audit --no-fund - - run: npm run build - - name: Committed viewer bundle matches the build - run: git -C "$GITHUB_WORKSPACE" diff --exit-code --stat -- strix/interface/viewer/static && test -z "$(git -C "$GITHUB_WORKSPACE" status --porcelain -- strix/interface/viewer/static)" + - name: Every job succeeded + env: + NEEDS: ${{ toJSON(needs) }} + run: | + failing=$(jq -r 'to_entries[] | select(.value.result != "success") | "\(.key): \(.value.result)"' <<< "$NEEDS") + if [ -n "$failing" ]; then + echo "$failing" + exit 1 + fi diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 00000000..c77c3187 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,48 @@ +name: CodeQL + +on: + push: + branches: + - main + pull_request: + branches: + - main + schedule: + - cron: '17 6 * * 1' + +permissions: + contents: read + +jobs: + analyze: + name: analyze (${{ matrix.language }}) + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write + strategy: + fail-fast: false + matrix: + include: + - language: python + build-mode: none + - language: go + build-mode: autobuild + - language: javascript-typescript + build-mode: none + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + with: + persist-credentials: false + - uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 + if: matrix.language == 'go' + with: + go-version-file: strix/interface/tui/go.mod + cache-dependency-path: strix/interface/tui/go.sum + - uses: github/codeql-action/init@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + - uses: github/codeql-action/analyze@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2 + with: + category: /language:${{ matrix.language }}