diff --git a/.github/actions/setup-python-env/action.yml b/.github/actions/setup-python-env/action.yml new file mode 100644 index 0000000000..ab70c894fd --- /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 0000000000..9aa16bf171 --- /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 9e5292b528..86e3da4c1c 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 0000000000..942b9844b8 --- /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 0000000000..d1ee05915b --- /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 0000000000..1dac8219f8 --- /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 0000000000..4bfc93613e --- /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 0000000000..5796e0d2a9 --- /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 new file mode 100644 index 0000000000..9ae8e7636b --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,52 @@ +name: CI + +on: + pull_request: + push: + branches: + - main + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + python: + uses: ./.github/workflows/ci-python.yml + + tui: + 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 + steps: + - 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/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1de107b31e..ceaae9717e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,7 @@ exclude: ^strix/interface/viewer/static/assets/ repos: # Ruff for fast linting and formatting - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.13 + rev: v0.15.20 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] diff --git a/tests/test_tool_call_ids_providers.py b/tests/test_tool_call_ids_providers.py index dc72196e92..e6dfda29a1 100644 --- a/tests/test_tool_call_ids_providers.py +++ b/tests/test_tool_call_ids_providers.py @@ -106,7 +106,7 @@ class _Gateway(BaseHTTPRequestHandler): def log_message(self, *args: Any) -> None: pass - def do_POST(self) -> None: # noqa: N802 + def do_POST(self) -> None: length = int(self.headers.get("Content-Length", 0)) body = json.loads(self.rfile.read(length) or b"{}") messages = body.get("messages", [])