diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 00d5002..b881d58 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,6 +8,9 @@ on: # Re-run on workflow file edits so changes to CI itself are validated workflow_dispatch: {} +permissions: + contents: read + # Cancel in-progress runs on the same branch when new commits arrive concurrency: group: ci-${{ github.ref }} @@ -29,10 +32,14 @@ jobs: uses: ./.github/workflows/windows.yml lint: + permissions: + contents: read name: Lint (ruff) runs-on: ubuntu-latest steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: Set up uv uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 @@ -58,11 +65,15 @@ jobs: uv run python docs/_tools/generate_metrics.py --check docs: + permissions: + contents: read name: Documentation runs-on: ubuntu-latest steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 - - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: "3.12" - run: pip install -r requirements-docs.txt @@ -72,6 +83,8 @@ jobs: run: git diff --exit-code test: + permissions: + contents: read name: Tests (py${{ matrix.python-version }}) runs-on: ubuntu-latest strategy: @@ -79,7 +92,9 @@ jobs: matrix: python-version: ["3.12", "3.13", "3.14"] steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: Set up uv uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 @@ -94,10 +109,14 @@ jobs: run: uv run --all-extras pytest -q owned-contracts: + permissions: + contents: read name: Owned recipe contracts runs-on: ubuntu-latest steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 with: python-version: "3.12" @@ -145,14 +164,18 @@ jobs: tests/test_repository_source_collection.py tasksmith-runtimes: + permissions: + contents: read name: Tasksmith coding runtime contracts runs-on: ubuntu-latest steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 with: python-version: "3.12" - - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: "24" - run: uv sync --extra tasksmith --frozen @@ -162,11 +185,15 @@ jobs: TASKSMITH_TEST_RUNTIME=$(uv run python -c 'from repo2rlenv.tasksmith.runtime import directory; print(directory())') node --test tests/test_tasksmith_pi.mjs tests/test_tasksmith_opencode.mjs build: + permissions: + contents: read name: Build sdist + wheel runs-on: ubuntu-latest needs: [lint, test, owned-contracts, tasksmith-runtimes, windows] steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: Set up uv uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 @@ -185,7 +212,7 @@ jobs: /tmp/check-install/bin/python tests/check_distribution.py dist - name: Upload distribution artifacts - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: repo2rlenv-dist-${{ github.sha }} path: dist/ diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 04abeb6..be30d9a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,6 +26,9 @@ on: description: "Tag to release (e.g. v0.3.0); must already exist" required: true +permissions: + contents: read + concurrency: group: release-${{ github.ref }} cancel-in-progress: false @@ -39,6 +42,8 @@ jobs: # Sanity gate — run the full test suite on the release tag before we publish. test: + permissions: + contents: read name: Tests on release tag (py${{ matrix.python-version }}) runs-on: ubuntu-latest strategy: @@ -46,8 +51,9 @@ jobs: matrix: python-version: ["3.12", "3.13", "3.14"] steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: + persist-credentials: false ref: ${{ github.event.release.tag_name || inputs.tag }} - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 @@ -59,19 +65,23 @@ jobs: - run: uv run --all-extras pytest -q tasksmith-runtimes: + permissions: + contents: read name: Tasksmith coding runtime contracts runs-on: ubuntu-latest steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: + persist-credentials: false ref: ${{ github.event.release.tag_name || inputs.tag }} - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 with: python-version: "3.12" enable-cache: false - - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: "24" + package-manager-cache: false - run: uv sync --extra tasksmith --frozen - run: uv run repo2rlenv tasksmith install-runtime - name: Exercise real SDKs against local mock servers @@ -79,14 +89,17 @@ jobs: TASKSMITH_TEST_RUNTIME=$(uv run python -c 'from repo2rlenv.tasksmith.runtime import directory; print(directory())') node --test tests/test_tasksmith_pi.mjs tests/test_tasksmith_opencode.mjs build: + permissions: + contents: read name: Build sdist + wheel runs-on: ubuntu-latest needs: [test, tasksmith-runtimes, windows] outputs: version: ${{ steps.read-version.outputs.version }} steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: + persist-credentials: false ref: ${{ github.event.release.tag_name || inputs.tag }} - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 @@ -103,9 +116,12 @@ jobs: echo "Package version: $version" - name: Verify tag matches package version + env: + RELEASE_TAG: ${{ github.event.release.tag_name || inputs.tag }} + PKG_VERSION: ${{ steps.read-version.outputs.version }} run: | - tag="${{ github.event.release.tag_name || inputs.tag }}" - expected="v${{ steps.read-version.outputs.version }}" + tag="$RELEASE_TAG" + expected="v$PKG_VERSION" if [ "$tag" != "$expected" ]; then echo "::error::Release tag $tag does not match package version $expected — bump pyproject.toml first" exit 1 @@ -123,13 +139,14 @@ jobs: /tmp/check-install/bin/repo2rlenv --version /tmp/check-install/bin/python tests/check_distribution.py dist - - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: release-dist path: dist/ retention-days: 30 publish-pypi: + permissions: {} name: Publish to PyPI (API token) runs-on: ubuntu-latest needs: [build] @@ -137,13 +154,13 @@ jobs: name: pypi url: https://pypi.org/p/repo2rlenv steps: - - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: release-dist path: dist/ - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1 + uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2 with: # API-token auth via the PYPI_API_TOKEN repo secret. (We can move # to OIDC trusted-publishing later by deleting `password:` and adding @@ -163,7 +180,7 @@ jobs: permissions: contents: write steps: - - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: release-dist path: dist/ @@ -171,7 +188,9 @@ jobs: - name: Upload distribution files to the GitHub Release env: GH_TOKEN: ${{ github.token }} + RELEASE_TAG: ${{ github.event.release.tag_name }} + REPO_SLUG: ${{ github.repository }} run: | - gh release upload "${{ github.event.release.tag_name }}" dist/* \ - --repo "${{ github.repository }}" \ + gh release upload "$RELEASE_TAG" dist/* \ + --repo "$REPO_SLUG" \ --clobber diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 1917b48..092a42c 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -23,8 +23,9 @@ jobs: PYTHON_DOTENV_DISABLED: "1" NO_COLOR: "1" steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: + persist-credentials: false ref: ${{ inputs.ref }} - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 with: