From 302d0a2d752aff2c83f57450e925a38fa058917a Mon Sep 17 00:00:00 2001 From: KaifAhmad1 Date: Sun, 30 Aug 2026 16:41:46 +0530 Subject: [PATCH] fix: address Qodo review findings on CI/release hardening - setup-semantica action: pass extras/version through env vars instead of interpolating ${{ inputs.* }} directly into the bash script, which was a script-injection vector for any caller deriving these from event/matrix data - install-matrix: trigger on the Release workflow's completion (workflow_run) instead of `release: published`, since the GitHub release is created before the PyPI upload runs - the old trigger could race the publish and silently verify the prior version; also assert the installed version matches the triggering release tag - examples/ci/github-actions.yml: pin actions/checkout and actions/setup-python to the same verified commit SHAs used elsewhere in this repo instead of mutable v5/v6 tags, and document how to pin the setup-semantica@main reference for production use - examples/ci templates + README: make the requirements.txt install step conditional (guard with `-f requirements.txt`) and call out pyproject.toml/Poetry/Pipenv as alternatives, since the templates previously assumed every project has a requirements.txt; CircleCI's cache key also no longer hashes a file that may not exist --- .github/actions/setup-semantica/action.yml | 10 +++++--- .github/workflows/install-matrix.yml | 27 +++++++++++++++++++--- examples/ci/README.md | 11 ++++++++- examples/ci/circleci-config.yml | 13 ++++++++--- examples/ci/github-actions.yml | 18 ++++++++++++--- examples/ci/gitlab-ci.yml | 5 +++- 6 files changed, 70 insertions(+), 14 deletions(-) diff --git a/.github/actions/setup-semantica/action.yml b/.github/actions/setup-semantica/action.yml index 05227f16..9c818d2a 100644 --- a/.github/actions/setup-semantica/action.yml +++ b/.github/actions/setup-semantica/action.yml @@ -31,13 +31,17 @@ runs: - name: Install semantica shell: bash + env: + SEMANTICA_EXTRAS: ${{ inputs.extras }} + SEMANTICA_VERSION: ${{ inputs.version }} run: | python -m pip install --upgrade pip - if [ -n "${{ inputs.extras }}" ]; then - pip install "semantica[${{ inputs.extras }}]${{ inputs.version }}" + if [ -n "$SEMANTICA_EXTRAS" ]; then + spec="semantica[$SEMANTICA_EXTRAS]$SEMANTICA_VERSION" else - pip install "semantica${{ inputs.version }}" + spec="semantica$SEMANTICA_VERSION" fi + python -m pip install -- "$spec" - name: Verify install id: verify diff --git a/.github/workflows/install-matrix.yml b/.github/workflows/install-matrix.yml index 3c4b24d3..138e332d 100644 --- a/.github/workflows/install-matrix.yml +++ b/.github/workflows/install-matrix.yml @@ -6,12 +6,19 @@ permissions: on: schedule: - cron: '0 6 * * 1' # weekly, catches upstream dependency breakage between releases - release: - types: [published] + workflow_run: + # The Release workflow publishes the GitHub release *before* it uploads to + # PyPI (see release.yml), so triggering on `release: published` would race + # the PyPI upload and could pass by silently installing the prior version. + # workflow_run fires only after the whole Release workflow - including the + # PyPI publish step - has finished. + workflows: ['Release'] + types: [completed] workflow_dispatch: jobs: verify-install: + if: github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' name: pip install semantica (${{ matrix.os }}, py${{ matrix.python-version }}) runs-on: ${{ matrix.os }} strategy: @@ -21,7 +28,8 @@ jobs: python-version: ['3.9', '3.10', '3.11', '3.12'] steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 - - uses: ./.github/actions/setup-semantica + - id: setup-semantica + uses: ./.github/actions/setup-semantica with: python-version: ${{ matrix.python-version }} @@ -32,3 +40,16 @@ jobs: import semantica print('semantica', semantica.__version__, 'installed and importable') " + + - name: Verify version matches the release that triggered this run + if: github.event_name == 'workflow_run' + shell: bash + env: + EXPECTED_TAG: ${{ github.event.workflow_run.head_branch }} + INSTALLED_VERSION: ${{ steps.setup-semantica.outputs.version }} + run: | + expected="${EXPECTED_TAG#v}" + if [ -n "$expected" ] && [ "$expected" != "$INSTALLED_VERSION" ]; then + echo "::error::pip installed semantica $INSTALLED_VERSION but the release that triggered this run was $EXPECTED_TAG - PyPI may still be propagating, or the publish failed." + exit 1 + fi diff --git a/examples/ci/README.md b/examples/ci/README.md index ad1557b4..814f2197 100644 --- a/examples/ci/README.md +++ b/examples/ci/README.md @@ -3,7 +3,9 @@ Copy-paste starting points for wiring `semantica` into your own project's CI. Each file is a complete, working config — rename it into your project (see the comment at the top of each file for the target path) and swap the smoke-test / test step for whatever your project does with -Semantica. +Semantica. Each template installs `semantica` unconditionally and your own project's dependencies +only if a `requirements.txt` is present; if your project uses `pyproject.toml`, Poetry, or Pipenv +instead, adjust the marked install line (each file calls it out inline). | File | Target path in your repo | | ---- | ------------------------- | @@ -22,5 +24,12 @@ Semantica's reusable composite action instead: # version: '==0.6.7' # optional, pin an exact release ``` +`@main` always tracks this repo's default branch, which is convenient but — like any mutable +ref — can change out from under you between runs. For production CI, pin it to a commit SHA +instead (find one via `git rev-parse` against a tagged release, or the commit history for +[`.github/actions/setup-semantica/`](../../.github/actions/setup-semantica/)) and update the pin +deliberately when you want to pick up changes, the same way this repo's own workflows are pinned +(see [`verify-action-pins.yml`](../../.github/workflows/verify-action-pins.yml)). + It installs Python, caches pip, installs `semantica`, and verifies the import — see [`.github/actions/setup-semantica/action.yml`](../../.github/actions/setup-semantica/action.yml). diff --git a/examples/ci/circleci-config.yml b/examples/ci/circleci-config.yml index 94664625..6e19ee2d 100644 --- a/examples/ci/circleci-config.yml +++ b/examples/ci/circleci-config.yml @@ -7,17 +7,24 @@ jobs: - image: cimg/python:3.11 steps: - checkout + # A content-hashed cache key (e.g. `{{ checksum "requirements.txt" }}`) + # is more precise but breaks if that exact file doesn't exist in your + # project - swap in one matched to however you declare dependencies + # once you've adjusted the install step below. - restore_cache: keys: - - pip-cache-{{ checksum "requirements.txt" }} + - pip-cache-v1 - run: name: Install dependencies command: | pip install --upgrade pip pip install semantica - pip install -r requirements.txt # your project's own dependencies + # Install your own project's dependencies however your project + # declares them - adjust this to match, e.g. `pip install -e .` + # for pyproject.toml / setup.cfg, or `poetry install`. + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - save_cache: - key: pip-cache-{{ checksum "requirements.txt" }} + key: pip-cache-v1 paths: - ~/.cache/pip - run: diff --git a/examples/ci/github-actions.yml b/examples/ci/github-actions.yml index 41598ba5..856c56b8 100644 --- a/examples/ci/github-actions.yml +++ b/examples/ci/github-actions.yml @@ -3,6 +3,12 @@ # Installs Semantica and runs a smoke import + your test suite. Swap the # smoke-test step for whatever your project actually does with Semantica # (build a context graph, run an ingest pipeline, etc.). +# +# Third-party actions below are pinned to a commit SHA rather than a mutable +# tag - a moved tag can silently swap in different code. Update the pin (and +# the trailing "# vX" comment) deliberately when you want a newer version; +# see semantica-agi/semantica's own .github/workflows/verify-action-pins.yml +# for one way to keep pins honest automatically. name: Semantica on: @@ -15,9 +21,9 @@ jobs: test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 - - uses: actions/setup-python@v6 + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7 with: python-version: '3.11' cache: 'pip' @@ -26,7 +32,13 @@ jobs: run: | python -m pip install --upgrade pip pip install semantica - pip install -r requirements.txt # your project's own dependencies + # Install your own project's dependencies however your project + # declares them - adjust this to match. Examples: + # pip install -r requirements.txt + # pip install -e . # pyproject.toml / setup.cfg + # pip install -e ".[dev]" + # poetry install + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - name: Run tests run: pytest diff --git a/examples/ci/gitlab-ci.yml b/examples/ci/gitlab-ci.yml index c32ff053..7f97fe62 100644 --- a/examples/ci/gitlab-ci.yml +++ b/examples/ci/gitlab-ci.yml @@ -9,7 +9,10 @@ semantica-test: script: - pip install --upgrade pip - pip install semantica - - pip install -r requirements.txt # your project's own dependencies + # Install your own project's dependencies however your project declares + # them - adjust this to match, e.g. `pip install -e .` for pyproject.toml + # / setup.cfg, or `poetry install`. + - if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - python -c "import semantica; print('semantica', semantica.__version__)" - pytest rules: