From f8de2072e821000021a8fa27b72116d99bc40eec Mon Sep 17 00:00:00 2001 From: jaydeep869 Date: Tue, 29 Sep 2026 18:26:45 +0530 Subject: [PATCH] ci: publish Python packages with trusted publishing Signed-off-by: jaydeep869 Assisted-by: OpenAI Codex --- .dockerignore | 2 + .github/workflows/release-smoke.yml | 8 +- .github/workflows/release.yml | 111 +++++++++++------- README.md | 29 ++--- docs/install/README.md | 8 +- docs/install/container.md | 22 ++-- docs/install/from-source.md | 2 +- docs/install/pypi.md | 34 +++--- packages/darnit-baseline/pyproject.toml | 6 +- packages/darnit-csl/pyproject.toml | 6 +- packages/darnit-gittuf/pyproject.toml | 8 +- .../darnit-reproducibility/pyproject.toml | 21 ++++ packages/darnit/pyproject.toml | 8 +- packaging/README.md | 57 ++++----- packaging/RECOVERY.md | 2 +- packaging/container/Dockerfile | 30 +++-- packaging/container/README.md | 24 ++-- packaging/pypi/public-packages.txt | 1 + pyproject.toml | 6 +- 19 files changed, 229 insertions(+), 156 deletions(-) diff --git a/.dockerignore b/.dockerignore index 1f24ea68..f91b9ac1 100644 --- a/.dockerignore +++ b/.dockerignore @@ -12,6 +12,8 @@ __pycache__/ .coverage .mypy_cache/ dist/ +!dist/ +!dist/** build/ node_modules/ *.log diff --git a/.github/workflows/release-smoke.yml b/.github/workflows/release-smoke.yml index 0cb20b89..d51e64fd 100644 --- a/.github/workflows/release-smoke.yml +++ b/.github/workflows/release-smoke.yml @@ -76,7 +76,7 @@ jobs: strategy: fail-fast: false matrix: - package: [darnit, darnit-baseline, darnit-gittuf, darnit-mcp] + package: [darnit-core, darnit-baseline, darnit-csl, darnit-gittuf, darnit-reproducibility, darnit-mcp] steps: - name: Checkout (for public-packages.txt sanity check) uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v4 @@ -145,9 +145,15 @@ jobs: "darnit-baseline") "$venv/bin/python" -c "import darnit_baseline; print('darnit_baseline imports OK')" ;; + "darnit-csl") + "$venv/bin/python" -c "import darnit_csl; print('darnit_csl imports OK')" + ;; "darnit-gittuf") "$venv/bin/python" -c "import darnit_gittuf; print('darnit_gittuf imports OK')" ;; + "darnit-reproducibility") + "$venv/bin/python" -c "import darnit_reproducibility; print('darnit_reproducibility imports OK')" + ;; *) echo "::error::No smoke recipe defined for package '$PKG'" exit 1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6f82a975..55424f68 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,15 +8,14 @@ name: Release # What this workflow does today: # 1. preflight - tag/version parity, clean tree, tests, lint, sync # 2. build - per-package wheels + sdists to a shared artifact -# 3. publish-* - upload each public package to PyPI via API token +# 3. publish-* - upload each public package to PyPI/TestPyPI via Trusted Publishing # 4. container - build + push + cosign-sign the multi-arch container image # 5. release - create the GitHub Release with install instructions # 6. finalize - summary line to the run's step summary # -# Auth model: PyPI publishes use the `PYPI_API_TOKEN` repo secret (account- -# scoped). Trusted-publisher setup is nicer long-term but requires per-project -# UI config in PyPI, which was blocking. Container signing uses cosign keyless -# OIDC as before; that path does not depend on PyPI auth. +# Auth model: PyPI publishes use PyPI Trusted Publishing (GitHub OIDC), so no +# long-lived PyPI API token is required. Container signing uses cosign keyless +# OIDC as before. # # Version format: `vX.Y.Z` or `vX.Y.ZrcN` (no dash before rc; matches PEP 440 # canonical form and the preflight parse-tag regex). Pre-release tags are @@ -218,21 +217,21 @@ jobs: if-no-files-found: error retention-days: 7 - # Publish jobs use `password: ${{ secrets.PYPI_API_TOKEN }}` (account-scoped - # token). Migrate to project-scoped tokens once the projects exist on PyPI. - # - # Sequenced via `needs:` because darnit-baseline, darnit-gittuf, + # Sequenced via `needs:` because darnit-baseline, darnit-csl, darnit-gittuf, # darnit-reproducibility, and darnit-mcp declare `darnit-core>=...` runtime - # deps; darnit-mcp additionally depends on the other four. Publishing them - # before the deps are on the index briefly produces unresolvable wheels. + # deps; darnit-mcp additionally depends on baseline, gittuf, and + # reproducibility. Publishing them before the deps are on the index briefly + # produces unresolvable wheels. publish-darnit-core: name: Publish darnit-core to PyPI needs: [preflight, build] runs-on: ubuntu-latest + environment: release timeout-minutes: 10 permissions: contents: read + id-token: write steps: - name: Download dist artifacts uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 @@ -244,16 +243,19 @@ jobs: uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1 with: packages-dir: dist/darnit-core/ - password: ${{ secrets.PYPI_API_TOKEN }} + repository-url: ${{ needs.preflight.outputs.is_prerelease == 'true' && 'https://test.pypi.org/legacy/' || 'https://upload.pypi.org/legacy/' }} skip-existing: true + attestations: true publish-darnit-baseline: name: Publish darnit-baseline to PyPI needs: [preflight, build, publish-darnit-core] runs-on: ubuntu-latest + environment: release timeout-minutes: 10 permissions: contents: read + id-token: write steps: - name: Download dist artifacts uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 @@ -265,16 +267,43 @@ jobs: uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1 with: packages-dir: dist/darnit-baseline/ - password: ${{ secrets.PYPI_API_TOKEN }} + repository-url: ${{ needs.preflight.outputs.is_prerelease == 'true' && 'https://test.pypi.org/legacy/' || 'https://upload.pypi.org/legacy/' }} skip-existing: true + attestations: true + + publish-darnit-csl: + name: Publish darnit-csl to PyPI + needs: [preflight, build, publish-darnit-core] + runs-on: ubuntu-latest + environment: release + timeout-minutes: 10 + permissions: + contents: read + id-token: write + steps: + - name: Download dist artifacts + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: dist + path: dist/ + + - name: Publish darnit-csl + uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1 + with: + packages-dir: dist/darnit-csl/ + repository-url: ${{ needs.preflight.outputs.is_prerelease == 'true' && 'https://test.pypi.org/legacy/' || 'https://upload.pypi.org/legacy/' }} + skip-existing: true + attestations: true publish-darnit-gittuf: name: Publish darnit-gittuf to PyPI needs: [preflight, build, publish-darnit-core] runs-on: ubuntu-latest + environment: release timeout-minutes: 10 permissions: contents: read + id-token: write steps: - name: Download dist artifacts uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 @@ -286,16 +315,19 @@ jobs: uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1 with: packages-dir: dist/darnit-gittuf/ - password: ${{ secrets.PYPI_API_TOKEN }} + repository-url: ${{ needs.preflight.outputs.is_prerelease == 'true' && 'https://test.pypi.org/legacy/' || 'https://upload.pypi.org/legacy/' }} skip-existing: true + attestations: true publish-darnit-reproducibility: name: Publish darnit-reproducibility to PyPI needs: [preflight, build, publish-darnit-core] runs-on: ubuntu-latest + environment: release timeout-minutes: 10 permissions: contents: read + id-token: write steps: - name: Download dist artifacts uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 @@ -307,8 +339,9 @@ jobs: uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1 with: packages-dir: dist/darnit-reproducibility/ - password: ${{ secrets.PYPI_API_TOKEN }} + repository-url: ${{ needs.preflight.outputs.is_prerelease == 'true' && 'https://test.pypi.org/legacy/' || 'https://upload.pypi.org/legacy/' }} skip-existing: true + attestations: true publish-darnit-mcp: name: Publish darnit-mcp to PyPI @@ -316,12 +349,15 @@ jobs: - preflight - build - publish-darnit-baseline + - publish-darnit-csl - publish-darnit-gittuf - publish-darnit-reproducibility runs-on: ubuntu-latest + environment: release timeout-minutes: 10 permissions: contents: read + id-token: write steps: - name: Download dist artifacts uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 @@ -333,17 +369,19 @@ jobs: uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1 with: packages-dir: dist/darnit-mcp/ - password: ${{ secrets.PYPI_API_TOKEN }} + repository-url: ${{ needs.preflight.outputs.is_prerelease == 'true' && 'https://test.pypi.org/legacy/' || 'https://upload.pypi.org/legacy/' }} skip-existing: true + attestations: true - # Build + push + cosign-sign the multi-arch container image. Depends on - # publish-darnit-mcp because the Dockerfile does `pip install - # darnit-mcp==` at build time - the package must be live before - # the image build can resolve it. + # Build + push + cosign-sign the multi-arch container image. Depends on the + # PyPI publish jobs so the image only ships after the public package set is + # uploaded. The Dockerfile installs Darnit packages from this run's build + # artifacts and resolves only external dependencies from PyPI. container_build_push: name: Build, push, and sign container image needs: - preflight + - build - publish-darnit-mcp runs-on: ubuntu-latest timeout-minutes: 20 @@ -375,6 +413,12 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + - name: Download dist artifacts + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: dist + path: dist/ + - name: Compute image tags id: tags env: @@ -398,26 +442,6 @@ jobs: } >> "$GITHUB_OUTPUT" echo "Tagging: $tags" - - name: Wait for darnit-mcp on PyPI - env: - VERSION: ${{ needs.preflight.outputs.version }} - run: | - set -euo pipefail - # PyPI's CDN typically caches the index for a few minutes after a - # new upload. Poll up to 2 minutes so the image build is not racing - # publication. - url="https://pypi.org/pypi/darnit-mcp/${VERSION}/json" - for attempt in $(seq 1 24); do - if curl -fsSL "$url" >/dev/null 2>&1; then - echo "darnit-mcp ${VERSION} is visible on PyPI (attempt $attempt)" - exit 0 - fi - echo "Waiting for darnit-mcp ${VERSION} on PyPI (attempt $attempt/24)..." - sleep 5 - done - echo "::error::darnit-mcp ${VERSION} did not appear on PyPI within 2 minutes" - exit 1 - - name: Build and push multi-arch image id: build_push uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v6 @@ -469,9 +493,11 @@ jobs: if [ "$IS_PRERELEASE" = "true" ]; then flags="--prerelease" title="darnit ${VERSION} (pre-release)" + pypi_install="pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ --pre darnit-mcp==${VERSION}" else flags="--latest" title="darnit ${VERSION}" + pypi_install="pip install darnit-mcp==${VERSION}" fi body_file=$(mktemp) cat > "$body_file" < [!NOTE] -> PyPI, pipx, `uv tool install`, container, Homebrew, and standalone-binary -> channels are not published yet - tracked in -> [#229](https://github.com/kusari-oss/darnit/issues/229) (which depends on -> PyPI publishing, [#228](https://github.com/kusari-oss/darnit/issues/228)). -> Until those land, install from source with [`uv`](https://docs.astral.sh/uv/): +> PyPI packages are published as `darnit-mcp` plus the workspace packages it +> depends on. `pipx` or `uv tool install` is the recommended path for most +> users. Native install paths such as Homebrew and standalone binaries remain +> tracked in [#229](https://github.com/darnitdevorg/darnit/issues/229). ```bash -git clone https://github.com/kusari-oss/darnit -cd darnit -uv sync +pipx install darnit-mcp +# or +uv tool install darnit-mcp -uv run darnit audit /path/to/repo -uv run darnit serve --framework openssf-baseline # MCP server mode +darnit audit /path/to/repo +darnit serve --framework openssf-baseline # MCP server mode ``` -Once [#229](https://github.com/kusari-oss/darnit/issues/229) lands, `pipx install darnit-mcp` -and `uv tool install darnit-mcp` will become the recommended end-user paths, with -container, Homebrew, standalone-binary, and Claude Code plugin channels documented in +For source installs, pre-releases, containers, and other channels, see [`docs/install/README.md`](docs/install/README.md). ### Optional: Opengrep for taint analysis @@ -93,12 +90,12 @@ The tree-sitter discovery pipeline is tuned for **web-service shapes**. What wor | Go HTTP service (`net/http`, chi, gorilla) | Thin — HTTP route registration + `sql.Open` only | | Go CLI built on [`spf13/cobra`](https://github.com/spf13/cobra) | Moderate — command families discovered, STRIDE assigned heuristically by import set; `needs reviewer attention` marker on every finding ([feature 014](specs/014-cobra-threat-model/spec.md)) | | YAML / GitHub Actions workflows | Some — overly-broad permissions and similar config issues | -| Python CLI frameworks (argparse, click, typer) | Not modeled — sibling to the Go cobra work ([#264](https://github.com/kusari-oss/darnit/issues/264)) | -| Other Go CLI frameworks (urfave/cli, kingpin) / message handlers / gRPC | Not modeled — out of scope for the cobra pass ([#262](https://github.com/kusari-oss/darnit/issues/262)) | +| Python CLI frameworks (argparse, click, typer) | Not modeled — sibling to the Go cobra work ([#264](https://github.com/darnitdevorg/darnit/issues/264)) | +| Other Go CLI frameworks (urfave/cli, kingpin) / message handlers / gRPC | Not modeled — out of scope for the cobra pass ([#262](https://github.com/darnitdevorg/darnit/issues/262)) | | Crypto/signing **client** libraries (sigstore-python, in-toto) | Out of scope — these call out rather than receive; entry-point queries don't fire | | Systems software, daemons, libraries, ML pipelines | Not modeled | -If your project doesn't match a supported shape, the generator will still write a report — but it will likely show "Total findings: 0" because no entry points were discovered. That's a coverage gap on our side, not a clean bill of health. Expanding the query set is [tracked in our issue tracker](https://github.com/kusari-oss/darnit/issues?q=is%3Aissue+threat-model+coverage). +If your project doesn't match a supported shape, the generator will still write a report — but it will likely show "Total findings: 0" because no entry points were discovered. That's a coverage gap on our side, not a clean bill of health. Expanding the query set is [tracked in our issue tracker](https://github.com/darnitdevorg/darnit/issues?q=is%3Aissue+threat-model+coverage). ## How to Use Darnit diff --git a/docs/install/README.md b/docs/install/README.md index 0c1791fb..420c5409 100644 --- a/docs/install/README.md +++ b/docs/install/README.md @@ -20,7 +20,7 @@ If you're not sure: **`pipx install darnit-mcp`** works for most users and is th - **One `darnit` command** on PATH after install. Use `darnit audit`, `darnit remediate`, `darnit list-controls`, etc. - **Same version, same artifact identity.** A release tag (`v0.1.0`) produces a Sigstore-signed PyPI wheel, a cosign-signed container image, four cosign-signed binaries, a Homebrew formula bump, and a Claude Code plugin zip — all derived from the same tagged commit. `darnit --version` reports the same string regardless of which channel installed it. -- **Verifiable signing identity.** Every channel ships with a signature you can verify back to `kusari-oss/darnit`'s `release.yml` workflow. The exact verification command differs per channel; each page has it. +- **Verifiable signing identity.** Every channel ships with a signature you can verify back to `darnitdevorg/darnit`'s `release.yml` workflow. The exact verification command differs per channel; each page has it. ## What differs @@ -44,13 +44,13 @@ Every install page has a "Verify" section with the exact command. The common sha # PyPI (Sigstore) python -m sigstore verify identity \ --bundle \ - --cert-identity-regexp '^https://github\.com/kusari-oss/darnit/' \ + --cert-identity-regexp '^https://github\.com/darnitdevorg/darnit/' \ --cert-oidc-issuer https://token.actions.githubusercontent.com \ # Container / binary (cosign) cosign verify[-blob] \ - --certificate-identity-regexp '^https://github\.com/kusari-oss/darnit/' \ + --certificate-identity-regexp '^https://github\.com/darnitdevorg/darnit/' \ --certificate-oidc-issuer https://token.actions.githubusercontent.com \ ``` @@ -62,7 +62,7 @@ If any of these fail with "identity mismatch", **do not trust the artifact** — Occasionally a release will succeed on some channels and fail on others (PyPI succeeded, container build failed, etc.). When that happens: - The successful channels stay published — they were signed correctly. -- A `release-failure` issue appears on the [upstream repo](https://github.com/kusari-oss/darnit/labels/release-failure) naming the failed channel. +- A `release-failure` issue appears on the [upstream repo](https://github.com/darnitdevorg/darnit/labels/release-failure) naming the failed channel. - The release notes include a per-channel timing table; channels that failed or exceeded the SC-007 budget are flagged. If you're trying to use a channel that's behind, check the latest release notes and the `release-failure` label. diff --git a/docs/install/container.md b/docs/install/container.md index fc3a206d..1eb20c69 100644 --- a/docs/install/container.md +++ b/docs/install/container.md @@ -5,7 +5,7 @@ This is the recommended install path for **CI/CD pipelines** and any environment ## Run a one-shot audit ```bash -docker run --rm -v "$PWD:/repo" ghcr.io/kusari-oss/darnit:v0.1.0 audit +docker run --rm -v "$PWD:/repo" ghcr.io/darnitdevorg/darnit:v0.1.0 audit ``` For `podman` users, substitute `podman` for `docker` — same flags. @@ -42,17 +42,17 @@ The entrypoint dispatches the first argument: ## Verify the image signature -Every release tag is signed with cosign keyless OIDC. The signing identity binds the image to the `release.yml` workflow in `kusari-oss/darnit`. +Every release tag is signed with cosign keyless OIDC. The signing identity binds the image to the `release.yml` workflow in `darnitdevorg/darnit`. ```bash -cosign verify ghcr.io/kusari-oss/darnit:v0.1.0 \ - --certificate-identity-regexp '^https://github\.com/kusari-oss/darnit/\.github/workflows/release\.yml@' \ +cosign verify ghcr.io/darnitdevorg/darnit:v0.1.0 \ + --certificate-identity-regexp '^https://github\.com/darnitdevorg/darnit/\.github/workflows/release\.yml@' \ --certificate-oidc-issuer https://token.actions.githubusercontent.com ``` A passing verification proves: - The image bytes match what was signed. -- The signer was the `release.yml` workflow in `kusari-oss/darnit`. +- The signer was the `release.yml` workflow in `darnitdevorg/darnit`. - The OIDC issuer was GitHub Actions. The `:edge` rolling build is **not** signed. @@ -62,16 +62,16 @@ The `:edge` rolling build is **not** signed. Each signed image has an SPDX-JSON SBOM attached as a cosign attestation: ```bash -cosign download attestation ghcr.io/kusari-oss/darnit:v0.1.0 \ +cosign download attestation ghcr.io/darnitdevorg/darnit:v0.1.0 \ | jq -r '.payload' | base64 -d | jq '.predicate' > darnit-sbom.spdx.json ``` Verify the SBOM's signing identity at the same time: ```bash -cosign verify-attestation ghcr.io/kusari-oss/darnit:v0.1.0 \ +cosign verify-attestation ghcr.io/darnitdevorg/darnit:v0.1.0 \ --type spdx \ - --certificate-identity-regexp '^https://github\.com/kusari-oss/darnit/\.github/workflows/release\.yml@' \ + --certificate-identity-regexp '^https://github\.com/darnitdevorg/darnit/\.github/workflows/release\.yml@' \ --certificate-oidc-issuer https://token.actions.githubusercontent.com ``` @@ -98,7 +98,7 @@ jobs: docker run --rm \ -v "$PWD:/repo" \ -v "${{ github.workspace }}/.audit:/audit-out" \ - ghcr.io/kusari-oss/darnit:v0.1.0 \ + ghcr.io/darnitdevorg/darnit:v0.1.0 \ audit --output /audit-out/report.md ``` @@ -106,7 +106,7 @@ jobs: ```yaml audit: - image: ghcr.io/kusari-oss/darnit:v0.1.0 + image: ghcr.io/darnitdevorg/darnit:v0.1.0 script: - darnit audit ``` @@ -125,7 +125,7 @@ The compressed image targets **300 MiB**. Each release records the measured size | Symptom | Cause | |---|---| -| `Error response from daemon: manifest unknown` | The tag does not exist. Check `gh release list --repo kusari-oss/darnit` for valid versions. | +| `Error response from daemon: manifest unknown` | The tag does not exist. Check `gh release list --repo darnitdevorg/darnit` for valid versions. | | `exec format error` on Apple Silicon | The image was pulled for the wrong architecture. Use `docker pull --platform linux/arm64 ...` or let Docker pick automatically. | | `cosign verify` fails with "no matching signatures" | You're trying to verify an `:edge` image (unsigned) or the tag was published before this signing scheme. | | The audit complains about missing `git`/`gh` | The image bundles both — re-pull, since the bundled binaries might have been overridden by a previous `--volume` mount. | diff --git a/docs/install/from-source.md b/docs/install/from-source.md index fd7bd01f..3364f1e3 100644 --- a/docs/install/from-source.md +++ b/docs/install/from-source.md @@ -90,7 +90,7 @@ Two reasons this doesn't work today: 1. The **workspace root** (`darnit-mcp`) is a virtual package — it has `[tool.uv.workspace]` but no source of its own. `setuptools` errors out trying to build it directly. 2. Installing a **single package by `subdirectory=`** works structurally, but its workspace-sibling dependencies (`darnit-baseline`, `darnit-gittuf`) aren't on PyPI yet. uv tries to resolve them from PyPI, gets 404, fails. -After v0.1.0 puts all four packages on PyPI, `uv tool install darnit-mcp` works in one shot — no clone needed. +After v0.1.0 puts the public packages on PyPI, `uv tool install darnit-mcp` works in one shot — no clone needed. ## See also diff --git a/docs/install/pypi.md b/docs/install/pypi.md index 8d861dd9..d5f7be70 100644 --- a/docs/install/pypi.md +++ b/docs/install/pypi.md @@ -1,21 +1,21 @@ # Install darnit from PyPI -This is the recommended install path for users who have **Python 3.11 or 3.12** already installed. Examples assume version `0.1.0` — substitute the version you want. +This is the recommended install path for users who have **Python 3.11 or 3.12** already installed. Examples use `X.Y.Z`; substitute the version you want. ## Pick an install command ```bash # pipx — isolated install, recommended for end users -pipx install darnit-mcp==0.1.0 +pipx install darnit-mcp==X.Y.Z # uv — fastest, modern -uv tool install darnit-mcp==0.1.0 +uv tool install darnit-mcp==X.Y.Z # pip — works in any virtualenv -pip install darnit-mcp==0.1.0 +pip install darnit-mcp==X.Y.Z ``` -After install, `darnit --version` should print `0.1.0`. +After install, `darnit --version` should print `X.Y.Z`. ## Pre-releases (TestPyPI) @@ -26,7 +26,7 @@ pip install \ --index-url https://test.pypi.org/simple/ \ --extra-index-url https://pypi.org/simple/ \ --pre \ - darnit-mcp==0.1.0rc1 + darnit-mcp==X.Y.Zrc1 ``` The `--extra-index-url` is required so dependencies of darnit that exist only on PyPI (e.g. `tree-sitter`, `mcp`, `pydantic`) can still be resolved. @@ -40,7 +40,7 @@ Every release attaches a [PEP 740](https://peps.python.org/pep-0740/) Sigstore a If your pip is 25.0 or newer, `--verify-attestations` does the whole thing automatically: ```bash -pip install --verify-attestations darnit-mcp==0.1.0 +pip install --verify-attestations darnit-mcp==X.Y.Z ``` pip refuses to install if the attestation is missing or fails to verify against PyPI's public certs. @@ -54,11 +54,11 @@ If you need to verify outside an install context (security scanning, attestation pip install 'sigstore>=3.0.0' # Download the wheel -pip download --no-deps darnit-mcp==0.1.0 +pip download --no-deps darnit-mcp==X.Y.Z # Fetch the PEP 740 attestation bundle from PyPI's provenance API curl -fsSL \ - "https://pypi.org/integrity/darnit-mcp/0.1.0/darnit_mcp-0.1.0-py3-none-any.whl/provenance" \ + "https://pypi.org/integrity/darnit-mcp/X.Y.Z/darnit_mcp-X.Y.Z-py3-none-any.whl/provenance" \ -o provenance.json # Extract the first attestation as a sigstore-readable bundle @@ -70,31 +70,33 @@ with open('attestation.sigstore.json', 'w') as out: json.dump(data['attestation_bundles'][0]['attestations'][0], out) " -# Verify against the canonical kusari-oss/darnit identity +# Verify against the canonical darnitdevorg/darnit identity python -m sigstore verify identity \ --bundle attestation.sigstore.json \ - --cert-identity-regexp '^https://github\.com/kusari-oss/darnit/\.github/workflows/release\.yml@' \ + --cert-identity-regexp '^https://github\.com/darnitdevorg/darnit/\.github/workflows/release\.yml@' \ --cert-oidc-issuer https://token.actions.githubusercontent.com \ - darnit_mcp-0.1.0-py3-none-any.whl + darnit_mcp-X.Y.Z-py3-none-any.whl ``` A passing verification proves: - The wheel bytes match exactly what was signed. -- The signer was the `release.yml` workflow in `kusari-oss/darnit`. +- The signer was the `release.yml` workflow in `darnitdevorg/darnit`. - The OIDC issuer was GitHub Actions (not some other identity provider). For TestPyPI pre-releases, substitute `test.pypi.org` for `pypi.org` in the provenance URL. ## Public package set -Each release publishes four packages in lockstep: +Each release publishes six packages in lockstep: | Package | Purpose | |---|---| -| `darnit` | Core framework (you'll usually install `darnit-mcp`, which pulls this in) | +| `darnit-core` | Core framework (you'll usually install `darnit-mcp`, which pulls this in) | | `darnit-baseline` | OpenSSF Baseline compliance implementation | +| `darnit-csl` | Community Specification License compliance plugin | | `darnit-gittuf` | Gittuf policy plugin | +| `darnit-reproducibility` | Scientific reproducibility checks plugin | | `darnit-mcp` | The MCP server entry point — installs the `darnit` CLI | For most users, `pip install darnit-mcp` is the right command. The other packages exist for users who want only the framework, only a specific implementation, or who are writing their own implementation plugin (see [`docs/packaging-plugins.md`](../packaging-plugins.md)). @@ -112,4 +114,4 @@ For most users, `pip install darnit-mcp` is the right command. The other package | `ERROR: Package requires a different Python` | Host Python is older than 3.11. Install Python 3.11+ or use [pipx](https://pipx.pypa.io/) with an explicit `--python` flag. | | `Could not find a version that satisfies the requirement` (for a pre-release) | Missing `--pre` flag or wrong `--index-url`. | | Sigstore verification fails with "no attestation bundles" | The release was published before PEP 740 attestations existed, or the attestation hasn't propagated yet (rare; retry in a few minutes). | -| Sigstore verification fails with "identity mismatch" | The wheel was not signed by `kusari-oss/darnit`'s release workflow. **Do not trust this artifact.** Report it via the project's security policy. | +| Sigstore verification fails with "identity mismatch" | The wheel was not signed by `darnitdevorg/darnit`'s release workflow. **Do not trust this artifact.** Report it via the project's security policy. | diff --git a/packages/darnit-baseline/pyproject.toml b/packages/darnit-baseline/pyproject.toml index e5c94047..9755bd67 100644 --- a/packages/darnit-baseline/pyproject.toml +++ b/packages/darnit-baseline/pyproject.toml @@ -33,9 +33,9 @@ dependencies = [ ] [project.urls] -Homepage = "https://github.com/kusari-oss/darnit" -Repository = "https://github.com/kusari-oss/darnit" -Issues = "https://github.com/kusari-oss/darnit/issues" +Homepage = "https://github.com/darnitdevorg/darnit" +Repository = "https://github.com/darnitdevorg/darnit" +Issues = "https://github.com/darnitdevorg/darnit/issues" [project.entry-points."darnit.implementations"] openssf-baseline = "darnit_baseline:register" diff --git a/packages/darnit-csl/pyproject.toml b/packages/darnit-csl/pyproject.toml index abded910..f6c3c4f2 100644 --- a/packages/darnit-csl/pyproject.toml +++ b/packages/darnit-csl/pyproject.toml @@ -24,9 +24,9 @@ dependencies = [ ] [project.urls] -Homepage = "https://github.com/kusari-oss/darnit" -Repository = "https://github.com/kusari-oss/darnit" -Issues = "https://github.com/kusari-oss/darnit/issues" +Homepage = "https://github.com/darnitdevorg/darnit" +Repository = "https://github.com/darnitdevorg/darnit" +Issues = "https://github.com/darnitdevorg/darnit/issues" # Discoverability: the implementation entry point. [project.entry-points."darnit.implementations"] diff --git a/packages/darnit-gittuf/pyproject.toml b/packages/darnit-gittuf/pyproject.toml index 2f644b4c..f9395843 100644 --- a/packages/darnit-gittuf/pyproject.toml +++ b/packages/darnit-gittuf/pyproject.toml @@ -25,9 +25,9 @@ dependencies = [ ] [project.urls] -Homepage = "https://github.com/kusari-oss/darnit" -Repository = "https://github.com/kusari-oss/darnit" -Issues = "https://github.com/kusari-oss/darnit/issues" +Homepage = "https://github.com/darnitdevorg/darnit" +Repository = "https://github.com/darnitdevorg/darnit" +Issues = "https://github.com/darnitdevorg/darnit/issues" [project.entry-points."darnit.implementations"] gittuf = "darnit_gittuf:register" @@ -42,4 +42,4 @@ build-backend = "hatchling.build" [tool.hatch.build.targets.wheel] packages = ["src/darnit_gittuf"] # gittuf.toml lives at src/darnit_gittuf/gittuf.toml (feature 021) and is -# packaged automatically by the entry above; no force-include needed. \ No newline at end of file +# packaged automatically by the entry above; no force-include needed. diff --git a/packages/darnit-reproducibility/pyproject.toml b/packages/darnit-reproducibility/pyproject.toml index 910e760e..a410352e 100644 --- a/packages/darnit-reproducibility/pyproject.toml +++ b/packages/darnit-reproducibility/pyproject.toml @@ -4,6 +4,22 @@ version = "0.1.0" description = "Scientific reproducibility checks plugin for darnit" readme = "README.md" requires-python = ">=3.11" +license = "Apache-2.0" +authors = [ + { name = "Kusari", email = "info@kusari.dev" }, +] +keywords = ["security", "compliance", "reproducibility", "research", "artifacts"] +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "License :: OSI Approved :: Apache Software License", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Topic :: Security", + "Topic :: Software Development :: Quality Assurance", +] dependencies = [ "darnit-core>=0.1.0", # PEP 440 requirement parsing for the requirements.txt pin classifier. @@ -14,6 +30,11 @@ dependencies = [ "packaging>=23.0", ] +[project.urls] +Homepage = "https://github.com/darnitdevorg/darnit" +Repository = "https://github.com/darnitdevorg/darnit" +Issues = "https://github.com/darnitdevorg/darnit/issues" + [project.entry-points."darnit.implementations"] reproducibility = "darnit_reproducibility:register" diff --git a/packages/darnit/pyproject.toml b/packages/darnit/pyproject.toml index 1acc5d34..f9748fcc 100644 --- a/packages/darnit/pyproject.toml +++ b/packages/darnit/pyproject.toml @@ -42,9 +42,9 @@ dependencies = [ ] [project.urls] -Homepage = "https://github.com/kusari-oss/darnit" -Repository = "https://github.com/kusari-oss/darnit" -Issues = "https://github.com/kusari-oss/darnit/issues" +Homepage = "https://github.com/darnitdevorg/darnit" +Repository = "https://github.com/darnitdevorg/darnit" +Issues = "https://github.com/darnitdevorg/darnit/issues" [project.scripts] darnit = "darnit.cli:main" @@ -95,4 +95,4 @@ requires = ["hatchling"] build-backend = "hatchling.build" [tool.hatch.build.targets.wheel] -packages = ["src/darnit"] \ No newline at end of file +packages = ["src/darnit"] diff --git a/packaging/README.md b/packaging/README.md index 2f207a91..683a3101 100644 --- a/packaging/README.md +++ b/packaging/README.md @@ -14,7 +14,7 @@ Maintainer-facing documentation for the darnit release pipeline. End-user instal The tag-driven pipeline in `release.yml` currently ships **PyPI + container** only. Binary, Homebrew, and Claude Code plugin channels are designed but not wired up; they will be re-introduced per waybill's incremental pattern as separate PRs. -PyPI publishing uses an **account-scoped API token** (`PYPI_API_TOKEN` repo secret) rather than trusted publishers. Trusted publishers are the better long-term posture (no long-lived secrets, per-project scoping) but the per-project PyPI UI setup was blocking; tokens ship the pipeline today and can be migrated later. See "External setup" below for the token flow. +PyPI publishing uses **Trusted Publishing** (GitHub OIDC) for both PyPI and TestPyPI. Stable tags publish to PyPI; release-candidate tags publish to TestPyPI. No long-lived PyPI API token is required. Sections below marked *(deferred)* describe channels the workflow does not currently drive. @@ -38,32 +38,34 @@ Authoritative list: [`packaging/pypi/public-packages.txt`](pypi/public-packages. ## External setup (one-time) -Before the first release works end-to-end, a maintainer with admin access must: +Before the next release works end-to-end, a maintainer with admin access must: -### PyPI API token (current) +### PyPI Trusted Publishing -The workflow authenticates every `publish-*` job with the `PYPI_API_TOKEN` repo secret. Setup, one time: +Configure Trusted Publishing for each public package on PyPI: -1. Under a PyPI account with 2FA enabled (Account settings -> Two-factor authentication), create a new API token: - - https://pypi.org/manage/account/token/ - - Name: `darnit-github-actions` (or similar) - - Scope: **Entire account (all projects)**. Project-scoped tokens require the projects to already exist; the account-scoped token is only needed for first-time publish, then narrow down. -2. Copy the full token (starts with `pypi-`). -3. Add as a **repository secret** (not environment secret) on `darnitdevorg/darnit`: - ```bash - gh secret set PYPI_API_TOKEN --repo darnitdevorg/darnit - ``` -4. After the first release lands and the five projects exist on PyPI, replace with per-project tokens for scope reduction (optional; not required for correctness). +1. Open the package's PyPI project settings, then Publishing. +2. Add a GitHub Trusted Publisher: + - Owner: `darnitdevorg` + - Repository: `darnit` + - Workflow: `release.yml` + - Environment: `release` +3. Repeat for every package in [`packaging/pypi/public-packages.txt`](pypi/public-packages.txt). -If the token is bad, the first `publish-*` job in `release.yml` fails immediately with a 403 and no packages are published. Fix the secret and re-trigger via `workflow_dispatch` on the same tag; no need to bump the version. +The public package set is currently: -### PyPI Trusted Publishing *(deferred)* +- `darnit-core` +- `darnit-baseline` +- `darnit-csl` +- `darnit-gittuf` +- `darnit-reproducibility` +- `darnit-mcp` -Better long-term posture; skipped for v0.1.0 because per-project UI setup was blocking. To migrate later, per public package: on the project's "Publishing" page add a Trusted Publisher with Owner: `darnitdevorg`, Repository: `darnit`, Workflow: `release.yml`, Environment: `release`. Then swap `password: ${{ secrets.PYPI_API_TOKEN }}` in each `publish-*` job for the trusted-publisher config, add `id-token: write` permission, and re-add `environment: release`. +If a project does not exist yet on PyPI, create it with PyPI's pending publisher flow or ask a PyPI project owner to add the Trusted Publisher after the first manual creation. Do not reintroduce account-scoped API tokens. -### TestPyPI *(deferred)* +### TestPyPI -Not currently used. rc tags publish directly to real PyPI as GitHub prereleases. +Configure the same Trusted Publisher entries on TestPyPI. Release-candidate tags (`vX.Y.ZrcN`) publish to TestPyPI. The container build installs Darnit packages from the workflow's built wheel artifacts and resolves external dependencies from PyPI. ### Homebrew tap *(deferred)* @@ -74,9 +76,9 @@ Not currently used. rc tags publish directly to real PyPI as GitHub prereleases. The dispatch step in `release.yml` uses bearer auth (`Authorization: Bearer ${HOMEBREW_TAP_TOKEN}`), which works identically for both options. Swap between them at any time without touching the workflow. -3. Store the credential as the `HOMEBREW_TAP_TOKEN` secret on the `release` environment of `kusari-oss/darnit`: +3. Store the credential as the `HOMEBREW_TAP_TOKEN` secret on the `release` environment of `darnitdevorg/darnit`: ```bash - gh secret set HOMEBREW_TAP_TOKEN --repo kusari-oss/darnit --env release + gh secret set HOMEBREW_TAP_TOKEN --repo darnitdevorg/darnit --env release ``` ## Doing a release @@ -86,14 +88,15 @@ Not currently used. rc tags publish directly to real PyPI as GitHub prereleases. 1. **Sync `main` from upstream and confirm CI is green.** ```bash git checkout main - git fetch upstream && git merge --ff-only upstream/main + git fetch origin && git merge --ff-only origin/main gh run list --branch main --limit 5 ``` 2. **Decide the version.** Stable releases are `vX.Y.Z`; pre-releases are `vX.Y.ZrcN` (no hyphen — PEP 440 canonical). Use a pre-release tag if this is the first run after a non-trivial release-pipeline change. -3. **Bump `version` in every public `pyproject.toml`.** The five public packages must agree exactly with the tag (preflight enforces this): +3. **Bump `version` in every public `pyproject.toml`.** The six public packages must agree exactly with the tag (preflight enforces this): - `pyproject.toml` (the root `darnit-mcp` package) - `packages/darnit/pyproject.toml` (name: `darnit-core`) - `packages/darnit-baseline/pyproject.toml` + - `packages/darnit-csl/pyproject.toml` - `packages/darnit-gittuf/pyproject.toml` - `packages/darnit-reproducibility/pyproject.toml` 4. **Sync and verify locally:** @@ -107,21 +110,21 @@ Not currently used. rc tags publish directly to real PyPI as GitHub prereleases. ```bash git add -p # review carefully git commit -m "release: vX.Y.Z" - git push upstream main + git push origin main ``` ### Cut the tag ```bash git tag vX.Y.Z # or vX.Y.ZrcN for pre-release -git push upstream vX.Y.Z +git push origin vX.Y.Z ``` The `release.yml` workflow triggers automatically. **No `--force` or amend** — once a tag is pushed and `release.yml` starts, the only way out of a bad release is roll-forward to a new tag. ### Monitor -1. Open the [Actions tab](https://github.com/kusari-oss/darnit/actions). The `Release` workflow should appear within a few seconds of tag push. +1. Open the [Actions tab](https://github.com/darnitdevorg/darnit/actions). The `Release` workflow should appear within a few seconds of tag push. 2. Watch `preflight` first. If it fails: - Most common: version mismatch between tag and `pyproject.toml`. Delete the tag (`git push upstream --delete vX.Y.Z`), fix the bump commit, push a new tag. - Other gates (lint/tests/sync/doc-gen) should have been caught in pre-flight above. If they fire here, you skipped step 4. @@ -136,7 +139,7 @@ The `release.yml` workflow triggers automatically. **No `--force` or amend** — - Verify the user-facing surfaces: ```bash pip install darnit-mcp==X.Y.Z - docker pull ghcr.io/kusari-oss/darnit:vX.Y.Z + docker pull ghcr.io/darnitdevorg/darnit:vX.Y.Z # stable only: brew tap kusari-oss/tap && brew install darnit ``` diff --git a/packaging/RECOVERY.md b/packaging/RECOVERY.md index 5bb0614c..f9281086 100644 --- a/packaging/RECOVERY.md +++ b/packaging/RECOVERY.md @@ -396,7 +396,7 @@ Recovery is generally simpler than for the other channels because the zip is ful **Symptom**: `plugin_behavioral_smoke` installs uv, extracts the plugin, then `darnit-mcp-runner --help` exits non-zero — but structural smoke passed. **Common causes**: -- `uvx --from darnit-mcp== darnit-mcp --help` failed because PyPI/TestPyPI hasn't propagated the new version yet (rare; the `container_build_push` job's PyPI-propagation wait usually fixes this for downstream jobs). +- `uvx --from darnit-mcp== darnit-mcp --help` failed because PyPI/TestPyPI hasn't propagated the new version yet. - A change to darnit-mcp's CLI broke `--help` (very unlikely — `--help` is one of the most stable surfaces). **Procedure**: diff --git a/packaging/container/Dockerfile b/packaging/container/Dockerfile index b1b662c0..ebb2f5a3 100644 --- a/packaging/container/Dockerfile +++ b/packaging/container/Dockerfile @@ -11,9 +11,9 @@ # venv + the runtime CLIs darnit's controls need (git, gh) — no build # toolchain, no apt cache, no pip cache. # -# Build arg: VERSION — the exact darnit-mcp release version to install. -# The release workflow passes the tagged version; for local testing, -# override with --build-arg VERSION=. +# Build args: +# VERSION — the exact darnit-mcp release version to install. +# dist/ — release.yml copies this run's built wheels into the Docker context. # Base image is pinned to the major-minor tag rather than a digest. Pinning # to a digest is stronger but requires per-release digest churn; we accept @@ -33,12 +33,24 @@ RUN test -n "$VERSION" || (echo "ERROR: --build-arg VERSION is required" >&2 && # darnit packages are pure Python or arrive with prebuilt wheels. WORKDIR /opt +COPY dist /tmp/dist -# Install darnit-mcp from PyPI at the pinned version into a venv. The -# venv gets copied wholesale into the runtime stage. +# Install the Darnit wheels produced by this release run into a venv. External +# dependencies still resolve from PyPI, but Darnit packages never come from +# TestPyPI during rc image builds. RUN python -m venv /opt/venv \ && /opt/venv/bin/pip install --no-cache-dir --upgrade pip \ - && /opt/venv/bin/pip install --no-cache-dir "darnit-mcp==${VERSION}" + && for pkg in darnit-core darnit-baseline darnit-gittuf darnit-reproducibility darnit-mcp; do \ + test -f /tmp/dist/${pkg}/*-${VERSION}-*.whl || (echo "ERROR: missing ${pkg} wheel for ${VERSION}" >&2 && exit 1); \ + done \ + && /opt/venv/bin/pip install --no-cache-dir \ + --index-url https://pypi.org/simple/ \ + --pre \ + /tmp/dist/darnit-core/*-${VERSION}-*.whl \ + /tmp/dist/darnit-baseline/*-${VERSION}-*.whl \ + /tmp/dist/darnit-gittuf/*-${VERSION}-*.whl \ + /tmp/dist/darnit-reproducibility/*-${VERSION}-*.whl \ + /tmp/dist/darnit-mcp/*-${VERSION}-*.whl # ---- Stage 2: runtime ------------------------------------------------------ FROM python:3.12-slim-bookworm @@ -47,11 +59,11 @@ ARG VERSION LABEL org.opencontainers.image.title="darnit" LABEL org.opencontainers.image.description="AI-powered compliance auditing framework" -LABEL org.opencontainers.image.source="https://github.com/kusari-oss/darnit" +LABEL org.opencontainers.image.source="https://github.com/darnitdevorg/darnit" LABEL org.opencontainers.image.licenses="Apache-2.0" LABEL org.opencontainers.image.vendor="Kusari" -LABEL org.opencontainers.image.url="https://github.com/kusari-oss/darnit" -LABEL org.opencontainers.image.documentation="https://github.com/kusari-oss/darnit/blob/main/docs/install/container.md" +LABEL org.opencontainers.image.url="https://github.com/darnitdevorg/darnit" +LABEL org.opencontainers.image.documentation="https://github.com/darnitdevorg/darnit/blob/main/docs/install/container.md" # Install runtime CLIs the audit controls invoke (git for repo inspection; # gh for GitHub API calls). Pin to apt's stable channel; the GitHub CLI diff --git a/packaging/container/README.md b/packaging/container/README.md index 6d55ba32..f13e7942 100644 --- a/packaging/container/README.md +++ b/packaging/container/README.md @@ -1,6 +1,6 @@ # darnit container image -Official darnit container image published to `ghcr.io/kusari-oss/darnit` on every release tag. +Official darnit container image published to `ghcr.io/darnitdevorg/darnit` on every release tag. > End-user install documentation: [`docs/install/container.md`](../../docs/install/container.md). This README sits next to the Dockerfile and is the source for the image's overview on GHCR. @@ -8,29 +8,29 @@ Official darnit container image published to `ghcr.io/kusari-oss/darnit` on ever ```bash # Pin to a specific release -docker pull ghcr.io/kusari-oss/darnit:v0.1.0 +docker pull ghcr.io/darnitdevorg/darnit:v0.1.0 # Latest stable -docker pull ghcr.io/kusari-oss/darnit:latest +docker pull ghcr.io/darnitdevorg/darnit:latest # Pre-release (not promoted to :latest) -docker pull ghcr.io/kusari-oss/darnit:v0.1.0rc1 +docker pull ghcr.io/darnitdevorg/darnit:v0.1.0rc1 # Rolling build of main (unsigned) -docker pull ghcr.io/kusari-oss/darnit:edge +docker pull ghcr.io/darnitdevorg/darnit:edge ``` ## Run ```bash # Audit the current directory -docker run --rm -v "$PWD:/repo" ghcr.io/kusari-oss/darnit:latest audit +docker run --rm -v "$PWD:/repo" ghcr.io/darnitdevorg/darnit:latest audit # Run as the MCP server (stdio) -docker run --rm -i ghcr.io/kusari-oss/darnit:latest mcp +docker run --rm -i ghcr.io/darnitdevorg/darnit:latest mcp # Print the bundled version -docker run --rm ghcr.io/kusari-oss/darnit:latest --version +docker run --rm ghcr.io/darnitdevorg/darnit:latest --version ``` The image's `WORKDIR` is `/repo`. The entrypoint dispatches the first arg to `darnit` (for `audit`/`remediate`/`list-controls`/`plan`/`profiles`/`validate`/`--version`/`--help`), to `darnit-mcp` for `mcp`, or executes the user's command directly otherwise. @@ -38,7 +38,7 @@ The image's `WORKDIR` is `/repo`. The entrypoint dispatches the first arg to `da ## Image contents - **Base**: `python:3.12-slim-bookworm` -- **darnit**: installed from PyPI via `pip install darnit-mcp==` (the version is recorded as the `org.opencontainers.image.version` OCI label) +- **darnit**: installed from the release workflow's built wheels for the pinned `darnit-mcp` version (the version is recorded as the `org.opencontainers.image.version` OCI label) - **CLIs**: `git`, `gh` (GitHub CLI from `cli.github.com`) - **Runtime user**: `darnit` (uid 10001), non-root @@ -61,8 +61,8 @@ The release image is multi-arch: Every release tag (stable and pre-release) is signed with cosign keyless OIDC. ```bash -cosign verify ghcr.io/kusari-oss/darnit:v0.1.0 \ - --certificate-identity-regexp '^https://github\.com/kusari-oss/darnit/\.github/workflows/release\.yml@' \ +cosign verify ghcr.io/darnitdevorg/darnit:v0.1.0 \ + --certificate-identity-regexp '^https://github\.com/darnitdevorg/darnit/\.github/workflows/release\.yml@' \ --certificate-oidc-issuer https://token.actions.githubusercontent.com ``` @@ -73,7 +73,7 @@ The `:edge` rolling build is **not** signed — it's for development only. Each signed image has an SPDX-JSON SBOM attached via cosign attestation: ```bash -cosign download attestation ghcr.io/kusari-oss/darnit:v0.1.0 \ +cosign download attestation ghcr.io/darnitdevorg/darnit:v0.1.0 \ | jq -r '.payload' | base64 -d | jq '.predicate' > darnit-sbom.spdx.json ``` diff --git a/packaging/pypi/public-packages.txt b/packaging/pypi/public-packages.txt index b4e37e0b..0ffd4f25 100644 --- a/packaging/pypi/public-packages.txt +++ b/packaging/pypi/public-packages.txt @@ -1,5 +1,6 @@ darnit-core darnit-baseline +darnit-csl darnit-gittuf darnit-reproducibility darnit-mcp diff --git a/pyproject.toml b/pyproject.toml index 4765d9d0..38e8a4db 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,9 +25,9 @@ dependencies = [ ] [project.urls] -Homepage = "https://github.com/kusari-oss/darnit" -Repository = "https://github.com/kusari-oss/darnit" -Issues = "https://github.com/kusari-oss/darnit/issues" +Homepage = "https://github.com/darnitdevorg/darnit" +Repository = "https://github.com/darnitdevorg/darnit" +Issues = "https://github.com/darnitdevorg/darnit/issues" # darnit-mcp is a metadata-only meta-package: no source, only dependencies # that pull in darnit-core, darnit-baseline, darnit-gittuf, and