From d2e762efdd57adb29605d24cb0f32e34e2d20c2b Mon Sep 17 00:00:00 2001 From: Anish Pallati Date: Thu, 16 Jul 2026 06:56:33 -0400 Subject: [PATCH 1/3] feat: add secretspec-action for GitHub and Forgejo Actions Signed-off-by: Anish Pallati Assisted-by: Claude Fable 5 --- secretspec-action/README.md | 37 +++++++++++++++++++++++++++ secretspec-action/action.yml | 48 ++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 secretspec-action/README.md create mode 100644 secretspec-action/action.yml diff --git a/secretspec-action/README.md b/secretspec-action/README.md new file mode 100644 index 00000000..c348224d --- /dev/null +++ b/secretspec-action/README.md @@ -0,0 +1,37 @@ +# secretspec-action + +Resolve the secrets declared in `secretspec.toml` and expose them to the rest of a GitHub or Forgejo Actions job. + +The action installs the `secretspec` CLI and runs `secretspec export --format gha`, which masks every value in the runner log (`::add-mask::`) and appends `KEY=value` to `$GITHUB_ENV`. Every later step in the job sees the secrets as ordinary environment variables. + +## Usage + +```yaml +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - uses: cachix/secretspec/secretspec-action@main + with: + profile: production + provider: env + - run: ./deploy.sh # DATABASE_URL etc. are in the environment +``` + +## Inputs + +| Input | Default | Description | +|-------|---------|-------------| +| `profile` | project default | secretspec profile to resolve | +| `provider` | user/project config | provider name, alias, or URI to resolve from | +| `version` | `latest` | secretspec release tag to install, or `latest` | +| `working-directory` | `.` | directory containing `secretspec.toml` | + +A missing required secret fails the step, so the action doubles as a CI gate. + +## Requirements + +- A secretspec release that includes `secretspec export`. +- Linux, macOS, or Windows runners. The prebuilt Linux binary links glibc and libdbus (for the keyring provider); GitHub-hosted runner images ship both, but a minimal `container:` image (alpine, distroless) will not. +- Provider credentials must already be available to the job, e.g. `provider: env` with repo secrets, or a Vault/OpenBao token obtained from an earlier OIDC exchange step. diff --git a/secretspec-action/action.yml b/secretspec-action/action.yml new file mode 100644 index 00000000..dfd27916 --- /dev/null +++ b/secretspec-action/action.yml @@ -0,0 +1,48 @@ +name: secretspec +description: >- + Resolve secrets declared in secretspec.toml and expose them to the rest of + the job. Values are masked in the runner log and appended to $GITHUB_ENV, so + later steps see them as environment variables. + +inputs: + profile: + description: secretspec profile to resolve + required: false + default: "" + provider: + description: provider name, alias, or URI to resolve from + required: false + default: "" + version: + description: secretspec release tag to install, or "latest" + required: false + default: latest + working-directory: + description: directory containing secretspec.toml + required: false + default: "." + +runs: + using: composite + steps: + - name: Install secretspec + shell: bash + env: + VERSION: ${{ inputs.version }} + run: | + if [ "$VERSION" = latest ]; then + curl --proto '=https' --tlsv1.2 -sSfL https://install.secretspec.dev | sh + else + # cargo-dist publishes a pinned installer per release tag + curl --proto '=https' --tlsv1.2 -sSfL \ + "https://github.com/cachix/secretspec/releases/download/v${VERSION#v}/secretspec-installer.sh" | sh + fi + echo "${CARGO_HOME:-$HOME/.cargo}/bin" >> "$GITHUB_PATH" + + - name: Export secrets to the job environment + shell: bash + working-directory: ${{ inputs.working-directory }} + env: + SECRETSPEC_PROFILE: ${{ inputs.profile }} + SECRETSPEC_PROVIDER: ${{ inputs.provider }} + run: secretspec export --format gha From eece83d4bdab6e095ba15cd919c7ae3af4ab17e9 Mon Sep 17 00:00:00 2001 From: Anish Pallati Date: Sun, 19 Jul 2026 02:03:59 -0400 Subject: [PATCH 2/3] docs: add continuous integration guide Signed-off-by: Anish Pallati --- docs/astro.config.ts | 5 +++- docs/src/content/docs/ci.md | 54 +++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 docs/src/content/docs/ci.md diff --git a/docs/astro.config.ts b/docs/astro.config.ts index 812378fe..78d3e336 100644 --- a/docs/astro.config.ts +++ b/docs/astro.config.ts @@ -139,7 +139,10 @@ Secrets can be stored in: keyring (default), dotenv files, environment variables sidebar: [ { label: "Getting Started", - items: [{ label: "Quick Start", slug: "quick-start" }], + items: [ + { label: "Quick Start", slug: "quick-start" }, + { label: "Continuous Integration", slug: "ci" }, + ], }, { label: "Concepts", diff --git a/docs/src/content/docs/ci.md b/docs/src/content/docs/ci.md new file mode 100644 index 00000000..56aa9c40 --- /dev/null +++ b/docs/src/content/docs/ci.md @@ -0,0 +1,54 @@ +--- +title: Continuous Integration +description: Resolve secrets from secretspec.toml in GitHub Actions, Forgejo Actions, and other CI systems +--- + +In a GitHub or Forgejo Actions job, `secretspec-action` installs the CLI and runs `secretspec export --format gha`, which masks every value in the runner log and appends `KEY=value` to `$GITHUB_ENV`. Every later step, including third-party actions, then sees the secrets as ordinary environment variables. + +```yaml +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - uses: cachix/secretspec/secretspec-action@main + with: + profile: production + - run: ./deploy.sh +``` + +A missing required secret fails the step before the job runs anything with an incomplete environment. + +## Fetching from a secret manager + +For secrets kept in a dedicated store, resolve them on the runner with the matching provider, shown here with [Vault or OpenBao](/providers/vault/). Other stores plug in the same way with their own credentials. + +Grant the job `id-token: write` and select `?auth=jwt` with a `role`. Vault exchanges the runner's OIDC token for a client token, so nothing is stored on the platform. + +```yaml + - uses: cachix/secretspec/secretspec-action@main + with: + profile: production + provider: vault://vault.example.com:8200/secret?auth=jwt&role=ci +``` + +Without an OIDC identity to draw on, select `?auth=approle` instead and pass `VAULT_ROLE_ID` and `VAULT_SECRET_ID` as CI secrets. + +```yaml + - uses: cachix/secretspec/secretspec-action@main + with: + profile: production + provider: vault://vault.example.com:8200/secret?auth=approle + env: + VAULT_ROLE_ID: ${{ secrets.VAULT_ROLE_ID }} + VAULT_SECRET_ID: ${{ secrets.VAULT_SECRET_ID }} +``` + +## Other CI systems + +`secretspec-action` is a convenience wrapper around commands that work anywhere the CLI is installed. + +- `secretspec run -- ` runs a single command with the secrets confined to its environment. +- `secretspec export` writes the resolved secrets to stdout for a tool that cannot be wrapped, such as a containerized pipeline. `eval "$(secretspec export)"` loads them into the current shell, while `--format dotenv` and `--format json` feed other consumers. + +Both resolve through the same provider chain and fail on a missing required secret. From 0e2cc4d4a8bc088e54a0745f168a6a529618349e Mon Sep 17 00:00:00 2001 From: Anish Pallati Date: Thu, 23 Jul 2026 12:31:25 -0400 Subject: [PATCH 3/3] refactor: drop in-repo secretspec-action Signed-off-by: Anish Pallati Assisted-by: Claude Opus 4.8 --- docs/src/content/docs/ci.md | 6 ++--- secretspec-action/README.md | 37 --------------------------- secretspec-action/action.yml | 48 ------------------------------------ 3 files changed, 3 insertions(+), 88 deletions(-) delete mode 100644 secretspec-action/README.md delete mode 100644 secretspec-action/action.yml diff --git a/docs/src/content/docs/ci.md b/docs/src/content/docs/ci.md index 56aa9c40..e65b2837 100644 --- a/docs/src/content/docs/ci.md +++ b/docs/src/content/docs/ci.md @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 - - uses: cachix/secretspec/secretspec-action@main + - uses: cachix/secretspec-action@main with: profile: production - run: ./deploy.sh @@ -26,7 +26,7 @@ For secrets kept in a dedicated store, resolve them on the runner with the match Grant the job `id-token: write` and select `?auth=jwt` with a `role`. Vault exchanges the runner's OIDC token for a client token, so nothing is stored on the platform. ```yaml - - uses: cachix/secretspec/secretspec-action@main + - uses: cachix/secretspec-action@main with: profile: production provider: vault://vault.example.com:8200/secret?auth=jwt&role=ci @@ -35,7 +35,7 @@ Grant the job `id-token: write` and select `?auth=jwt` with a `role`. Vault exch Without an OIDC identity to draw on, select `?auth=approle` instead and pass `VAULT_ROLE_ID` and `VAULT_SECRET_ID` as CI secrets. ```yaml - - uses: cachix/secretspec/secretspec-action@main + - uses: cachix/secretspec-action@main with: profile: production provider: vault://vault.example.com:8200/secret?auth=approle diff --git a/secretspec-action/README.md b/secretspec-action/README.md deleted file mode 100644 index c348224d..00000000 --- a/secretspec-action/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# secretspec-action - -Resolve the secrets declared in `secretspec.toml` and expose them to the rest of a GitHub or Forgejo Actions job. - -The action installs the `secretspec` CLI and runs `secretspec export --format gha`, which masks every value in the runner log (`::add-mask::`) and appends `KEY=value` to `$GITHUB_ENV`. Every later step in the job sees the secrets as ordinary environment variables. - -## Usage - -```yaml -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v7 - - uses: cachix/secretspec/secretspec-action@main - with: - profile: production - provider: env - - run: ./deploy.sh # DATABASE_URL etc. are in the environment -``` - -## Inputs - -| Input | Default | Description | -|-------|---------|-------------| -| `profile` | project default | secretspec profile to resolve | -| `provider` | user/project config | provider name, alias, or URI to resolve from | -| `version` | `latest` | secretspec release tag to install, or `latest` | -| `working-directory` | `.` | directory containing `secretspec.toml` | - -A missing required secret fails the step, so the action doubles as a CI gate. - -## Requirements - -- A secretspec release that includes `secretspec export`. -- Linux, macOS, or Windows runners. The prebuilt Linux binary links glibc and libdbus (for the keyring provider); GitHub-hosted runner images ship both, but a minimal `container:` image (alpine, distroless) will not. -- Provider credentials must already be available to the job, e.g. `provider: env` with repo secrets, or a Vault/OpenBao token obtained from an earlier OIDC exchange step. diff --git a/secretspec-action/action.yml b/secretspec-action/action.yml deleted file mode 100644 index dfd27916..00000000 --- a/secretspec-action/action.yml +++ /dev/null @@ -1,48 +0,0 @@ -name: secretspec -description: >- - Resolve secrets declared in secretspec.toml and expose them to the rest of - the job. Values are masked in the runner log and appended to $GITHUB_ENV, so - later steps see them as environment variables. - -inputs: - profile: - description: secretspec profile to resolve - required: false - default: "" - provider: - description: provider name, alias, or URI to resolve from - required: false - default: "" - version: - description: secretspec release tag to install, or "latest" - required: false - default: latest - working-directory: - description: directory containing secretspec.toml - required: false - default: "." - -runs: - using: composite - steps: - - name: Install secretspec - shell: bash - env: - VERSION: ${{ inputs.version }} - run: | - if [ "$VERSION" = latest ]; then - curl --proto '=https' --tlsv1.2 -sSfL https://install.secretspec.dev | sh - else - # cargo-dist publishes a pinned installer per release tag - curl --proto '=https' --tlsv1.2 -sSfL \ - "https://github.com/cachix/secretspec/releases/download/v${VERSION#v}/secretspec-installer.sh" | sh - fi - echo "${CARGO_HOME:-$HOME/.cargo}/bin" >> "$GITHUB_PATH" - - - name: Export secrets to the job environment - shell: bash - working-directory: ${{ inputs.working-directory }} - env: - SECRETSPEC_PROFILE: ${{ inputs.profile }} - SECRETSPEC_PROVIDER: ${{ inputs.provider }} - run: secretspec export --format gha