From 721099bb96666ed2bf9f9065d0d369af4fdd49f6 Mon Sep 17 00:00:00 2001 From: Maksim Khimchenko Date: Thu, 4 Jun 2026 18:36:43 +0300 Subject: [PATCH 01/12] add vex vars Signed-off-by: Maksim Khimchenko --- build/action.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/build/action.yml b/build/action.yml index 2d182ca..b82f9d2 100644 --- a/build/action.yml +++ b/build/action.yml @@ -31,6 +31,24 @@ inputs: source_repo_ssh_key: description: "SSH private key for the source repository" required: false + cosign_vault_address: + description: "Vault adddress" + required: false + cosign_vault_key: + description: "Key in the vault" + required: false + cosign_transit_secret_engine_path: + description: "Transit engine secret path" + required: false + cosign_auth_role: + description: "Auth role" + required: false + registry_user: + description: "User to authenticate to registry" + required: false + registry_password: + description: "Password to authenticate to registry" + required: false runs: using: "composite" @@ -124,6 +142,12 @@ runs: SVACE_ANALYZE_HOST: "${{ inputs.svace_analyze_host }}" SVACE_ANALYZE_SSH_USER: "${{ inputs.svace_analyze_ssh_user }}" SVACE_ENABLED: "${{ inputs.svace_enabled }}" + COSIGN_VAULT_ADDRESS: "${{ inputs.cosign_vault_address }}" + COSIGN_VAULT_KEY: "${{ inputs.cosign_vault_key }}" + COSIGN_TRANSIT_SECRET_ENGINE_PATH: "${{ inputs.cosign_transit_secret_engine_path }}" + COSIGN_AUTH_ROLE: "${{ inputs.cosign_auth_role }}" + REGISTRY_USER: "${{ inputs.registry_user }}" + REGISTRY_PASSWORD: "${{ inputs.registry_password }}" - name: Cleanup ssh-agent if: ${{ steps.ssh_agent.outcome != 'skipped' && always() }} From 02f3ef8a4e2fd5934fc10b2598a06cd87cdd3216 Mon Sep 17 00:00:00 2001 From: Maksim Khimchenko Date: Thu, 4 Jun 2026 19:26:32 +0300 Subject: [PATCH 02/12] add upload artifact Signed-off-by: Maksim Khimchenko --- build/action.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/build/action.yml b/build/action.yml index b82f9d2..add88d9 100644 --- a/build/action.yml +++ b/build/action.yml @@ -53,6 +53,12 @@ inputs: runs: using: "composite" steps: + - name: Calculate edition + shell: bash + run: | + edition=$(echo "${{ inputs.source_repo }}" | sed -nE 's|.*/[^/]*deckhouse[^/]*/([^/]+)/modules.*|\1|p') + echo "EDITION=${edition}" > $GITHUB_ENV + - name: Set secondary repo if input exists shell: bash if: ${{ inputs.secondary_repo != '' }} @@ -181,3 +187,10 @@ runs: --oci-empty-base \ --new_layer "" \ --new_tag "${{ inputs.module_source }}:${{ inputs.module_name }}" + + - name: Save build report + if: ${{ always() && (steps.build.outcome == 'success' || steps.build.outcome == 'failure') }} + uses: actions/upload-artifact@v4.4.0 + with: + name: build_report_${EDITION} + path: images_tags_werf.json From 46a0b0ad0105cbee5a666e96a88a35507f27ca89 Mon Sep 17 00:00:00 2001 From: Maksim Khimchenko Date: Thu, 4 Jun 2026 22:12:21 +0300 Subject: [PATCH 03/12] add edition output Signed-off-by: Maksim Khimchenko --- build/action.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/build/action.yml b/build/action.yml index add88d9..679da6c 100644 --- a/build/action.yml +++ b/build/action.yml @@ -57,6 +57,12 @@ runs: shell: bash run: | edition=$(echo "${{ inputs.source_repo }}" | sed -nE 's|.*/[^/]*deckhouse[^/]*/([^/]+)/modules.*|\1|p') + if [ -n "$edition" ]; then + echo "Detected edition: "$edition" + else + echo "No edition has been detected." + fi + echo "EDITION=${edition}" > $GITHUB_ENV - name: Set secondary repo if input exists From 590f886c73aaca271642527f00d7146f69098ea6 Mon Sep 17 00:00:00 2001 From: Maksim Khimchenko Date: Thu, 4 Jun 2026 22:23:34 +0300 Subject: [PATCH 04/12] fix quotation Signed-off-by: Maksim Khimchenko --- build/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/action.yml b/build/action.yml index 679da6c..4d4d06d 100644 --- a/build/action.yml +++ b/build/action.yml @@ -58,7 +58,7 @@ runs: run: | edition=$(echo "${{ inputs.source_repo }}" | sed -nE 's|.*/[^/]*deckhouse[^/]*/([^/]+)/modules.*|\1|p') if [ -n "$edition" ]; then - echo "Detected edition: "$edition" + echo "Detected edition: $edition" else echo "No edition has been detected." fi From 31a8e2b5c3890b2709a29601d8debe664172d1ab Mon Sep 17 00:00:00 2001 From: Maksim Khimchenko Date: Thu, 4 Jun 2026 22:55:37 +0300 Subject: [PATCH 05/12] fix build report naming Signed-off-by: Maksim Khimchenko --- build/action.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/build/action.yml b/build/action.yml index 4d4d06d..264a6ea 100644 --- a/build/action.yml +++ b/build/action.yml @@ -54,17 +54,18 @@ runs: using: "composite" steps: - name: Calculate edition + id: calc_edition shell: bash run: | edition=$(echo "${{ inputs.source_repo }}" | sed -nE 's|.*/[^/]*deckhouse[^/]*/([^/]+)/modules.*|\1|p') if [ -n "$edition" ]; then echo "Detected edition: $edition" + echo "EDITION=${edition}" >> $GITHUB_ENV + echo "build_report_postfix=_${edition}" >> $GITHUB_OUTPUT else echo "No edition has been detected." fi - echo "EDITION=${edition}" > $GITHUB_ENV - - name: Set secondary repo if input exists shell: bash if: ${{ inputs.secondary_repo != '' }} @@ -198,5 +199,5 @@ runs: if: ${{ always() && (steps.build.outcome == 'success' || steps.build.outcome == 'failure') }} uses: actions/upload-artifact@v4.4.0 with: - name: build_report_${EDITION} + name: build_report${{ steps.calc_edition.outputs.build_report_postfix }} path: images_tags_werf.json From f29c1fa8a7bc39d0660ea5849fc34fe3e2fb74df Mon Sep 17 00:00:00 2001 From: Maksim Khimchenko Date: Fri, 5 Jun 2026 02:13:05 +0300 Subject: [PATCH 06/12] add bundle copy attestation Signed-off-by: Maksim Khimchenko --- build/action.yml | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/build/action.yml b/build/action.yml index 264a6ea..1d5a694 100644 --- a/build/action.yml +++ b/build/action.yml @@ -173,11 +173,22 @@ runs: - name: Bundle the module image shell: bash run: | - IMAGE_SRC="$(jq -r '.Images."bundle".DockerImageName' images_tags_werf.json)" - IMAGE_DST="$(jq -r '.Images.bundle.DockerRepo' images_tags_werf.json):${{ inputs.module_tag }}" + IMAGE_REPO="$(jq -r '.Images.bundle.DockerRepo' images_tags_werf.json)" + IMAGE_SRC="$(jq -r '.Images.bundle.DockerImageName' images_tags_werf.json)" + IMAGE_DST="${IMAGE_REPO}:${{ inputs.module_tag }}" echo "✨ Bundle image : Pushing ${IMAGE_SRC} to ${IMAGE_DST}" crane copy ${IMAGE_SRC} ${IMAGE_DST} + # Copying attestation for bundle + IMAGE_DIGEST="$(jq -r ".Images.bundle.DockerImageDigest" images_tags_werf.json)" + IMAGE_DIGEST="${IMAGE_DIGEST#sha256:}" + if (crane manifest "${IMAGE_REPO}:sha256-${IMAGE_DIGEST}.att" > /dev/null 2>&1); then + echo "✨ [$(date -u)] Copying 'bundle' attestation" + crane copy "${IMAGE_REPO}:sha256-${IMAGE_DIGEST}.att" "${IMAGE_REPO}:sha256-${IMAGE_DIGEST}.att" + else + echo "✨ [$(date -u)] Attestation for 'bundle' not found, skipping..." + fi + - name: Prepare the release-channel image shell: bash run: | From d8ac1ee3ea25314370bab3397842cf4c419a3987 Mon Sep 17 00:00:00 2001 From: Maksim Khimchenko Date: Fri, 5 Jun 2026 17:56:20 +0300 Subject: [PATCH 07/12] use edition as inputs instead of trying to calculate Signed-off-by: Maksim Khimchenko --- build/action.yml | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/build/action.yml b/build/action.yml index 1d5a694..c83350a 100644 --- a/build/action.yml +++ b/build/action.yml @@ -49,17 +49,21 @@ inputs: registry_password: description: "Password to authenticate to registry" required: false + edition: + description: "Edition of the builded artifact" + required: false + default: '' runs: using: "composite" steps: - - name: Calculate edition - id: calc_edition + - name: Set edition + id: set_edition shell: bash run: | - edition=$(echo "${{ inputs.source_repo }}" | sed -nE 's|.*/[^/]*deckhouse[^/]*/([^/]+)/modules.*|\1|p') + edition="${{ inputs.edition }}" if [ -n "$edition" ]; then - echo "Detected edition: $edition" + echo "Set edition: $edition" echo "EDITION=${edition}" >> $GITHUB_ENV echo "build_report_postfix=_${edition}" >> $GITHUB_OUTPUT else @@ -208,7 +212,7 @@ runs: - name: Save build report if: ${{ always() && (steps.build.outcome == 'success' || steps.build.outcome == 'failure') }} - uses: actions/upload-artifact@v4.4.0 + uses: actions/upload-artifact@v8.0.1 with: - name: build_report${{ steps.calc_edition.outputs.build_report_postfix }} + name: build_report${{ steps.set_edition.outputs.build_report_postfix }} path: images_tags_werf.json From fed90730e0bb47ef8d66eeb43c80f38b92aed21d Mon Sep 17 00:00:00 2001 From: Maksim Khimchenko Date: Fri, 5 Jun 2026 18:42:22 +0300 Subject: [PATCH 08/12] fix upload artifact version Signed-off-by: Maksim Khimchenko --- build/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/action.yml b/build/action.yml index c83350a..54f84fe 100644 --- a/build/action.yml +++ b/build/action.yml @@ -212,7 +212,7 @@ runs: - name: Save build report if: ${{ always() && (steps.build.outcome == 'success' || steps.build.outcome == 'failure') }} - uses: actions/upload-artifact@v8.0.1 + uses: actions/upload-artifact@v7.0.1 with: name: build_report${{ steps.set_edition.outputs.build_report_postfix }} path: images_tags_werf.json From 63a3c32317384c7a25fa2efbf3e45a0ffe714f08 Mon Sep 17 00:00:00 2001 From: Maksim Khimchenko Date: Mon, 8 Jun 2026 10:45:20 +0300 Subject: [PATCH 09/12] add overwrite on uploading artifacts Signed-off-by: Maksim Khimchenko --- build/action.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/build/action.yml b/build/action.yml index 54f84fe..39cc86a 100644 --- a/build/action.yml +++ b/build/action.yml @@ -216,3 +216,4 @@ runs: with: name: build_report${{ steps.set_edition.outputs.build_report_postfix }} path: images_tags_werf.json + overwrite: true From 6de53d943e74de95ecfe7934295e42f266c93c81 Mon Sep 17 00:00:00 2001 From: Maksim Khimchenko Date: Thu, 11 Jun 2026 18:07:04 +0300 Subject: [PATCH 10/12] change vault var names Signed-off-by: Maksim Khimchenko --- build/action.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/build/action.yml b/build/action.yml index 39cc86a..3e8bc8f 100644 --- a/build/action.yml +++ b/build/action.yml @@ -31,17 +31,17 @@ inputs: source_repo_ssh_key: description: "SSH private key for the source repository" required: false - cosign_vault_address: + vault_addr: description: "Vault adddress" required: false - cosign_vault_key: + vault_key: description: "Key in the vault" required: false - cosign_transit_secret_engine_path: + transit_secret_engine_path: description: "Transit engine secret path" required: false - cosign_auth_role: - description: "Auth role" + vault_role: + description: "Vault auth role" required: false registry_user: description: "User to authenticate to registry" @@ -159,10 +159,10 @@ runs: SVACE_ANALYZE_HOST: "${{ inputs.svace_analyze_host }}" SVACE_ANALYZE_SSH_USER: "${{ inputs.svace_analyze_ssh_user }}" SVACE_ENABLED: "${{ inputs.svace_enabled }}" - COSIGN_VAULT_ADDRESS: "${{ inputs.cosign_vault_address }}" - COSIGN_VAULT_KEY: "${{ inputs.cosign_vault_key }}" - COSIGN_TRANSIT_SECRET_ENGINE_PATH: "${{ inputs.cosign_transit_secret_engine_path }}" - COSIGN_AUTH_ROLE: "${{ inputs.cosign_auth_role }}" + VAULT_ADDR: "${{ inputs.vault_addr }}" + VAULT_KEY: "${{ inputs.vault_key }}" + TRANSIT_SECRET_ENGINE_PATH: "${{ inputs.transit_secret_engine_path }}" + VAULT_ROLE: "${{ inputs.vault_role }}" REGISTRY_USER: "${{ inputs.registry_user }}" REGISTRY_PASSWORD: "${{ inputs.registry_password }}" From 49bab6aa997419440777086af9097fd668105979 Mon Sep 17 00:00:00 2001 From: Maksim Khimchenko Date: Tue, 23 Jun 2026 13:21:57 +0300 Subject: [PATCH 11/12] change vars order Signed-off-by: Maksim Khimchenko --- build/action.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/action.yml b/build/action.yml index 3e8bc8f..6c29dca 100644 --- a/build/action.yml +++ b/build/action.yml @@ -156,15 +156,15 @@ runs: werf build --repo=${{ inputs.module_source }}/${{ inputs.module_name }} --save-build-report --build-report-path images_tags_werf.json env: MODULES_MODULE_TAG: ${{ inputs.module_tag }} + REGISTRY_USER: "${{ inputs.registry_user }}" + REGISTRY_PASSWORD: "${{ inputs.registry_password }}" SVACE_ANALYZE_HOST: "${{ inputs.svace_analyze_host }}" SVACE_ANALYZE_SSH_USER: "${{ inputs.svace_analyze_ssh_user }}" SVACE_ENABLED: "${{ inputs.svace_enabled }}" VAULT_ADDR: "${{ inputs.vault_addr }}" VAULT_KEY: "${{ inputs.vault_key }}" - TRANSIT_SECRET_ENGINE_PATH: "${{ inputs.transit_secret_engine_path }}" VAULT_ROLE: "${{ inputs.vault_role }}" - REGISTRY_USER: "${{ inputs.registry_user }}" - REGISTRY_PASSWORD: "${{ inputs.registry_password }}" + TRANSIT_SECRET_ENGINE_PATH: "${{ inputs.transit_secret_engine_path }}" - name: Cleanup ssh-agent if: ${{ steps.ssh_agent.outcome != 'skipped' && always() }} From 3ca8b40020994dd2fe6e1a9d7f2ceb214c99d960 Mon Sep 17 00:00:00 2001 From: Maksim Khimchenko Date: Wed, 24 Jun 2026 10:05:50 +0300 Subject: [PATCH 12/12] update docs Signed-off-by: Maksim Khimchenko --- .docs/build.md | 112 +++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 10 ++++- 2 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 .docs/build.md diff --git a/.docs/build.md b/.docs/build.md new file mode 100644 index 0000000..247fdbb --- /dev/null +++ b/.docs/build.md @@ -0,0 +1,112 @@ +# Build action + +## Description + +The **build** composite action builds a Deckhouse module with [werf](https://werf.io/), pushes bundle and release-channel images to the registry, registers the module tag, and optionally copies VEX attestations for the bundle image. + +Run **setup** before **build** in the same job. + +```yaml +deckhouse/modules-actions/build@main +``` + +## New in `vex-attestations` + +### Edition and build report artifact + +Pass `edition` when building edition-specific modules (e.g. `fe`, `ee`). The action: + +1. Exports `EDITION` for werf. +2. Uploads `images_tags_werf.json` as a workflow artifact named `build_report_` (or `build_report` when edition is empty). + +The artifact is saved even when the build step fails, so downstream jobs can inspect image metadata. + +### Vault and registry authentication + +Optional inputs are forwarded to the werf build environment: + +| Input | Env var | Description | +|-------|---------|-------------| +| `vault_addr` | `VAULT_ADDR` | Vault server address | +| `vault_key` | `VAULT_KEY` | Key name in Vault | +| `vault_role` | `VAULT_ROLE` | Vault auth role | +| `transit_secret_engine_path` | `TRANSIT_SECRET_ENGINE_PATH` | Transit secrets engine path | +| `registry_user` | `REGISTRY_USER` | Registry username - is used for cosign attestations | +| `registry_password` | `REGISTRY_PASSWORD` | Registry password - is used for cosign attestations | + +### Bundle VEX attestation + +After `crane copy` pushes the bundle image, the action checks for an attestation tag: + +``` +:sha256-.att +``` + +If the manifest exists, it is copied to the same tag on the destination repo. If not, the step logs a skip message and continues. + +## Inputs + +### Required + +| Input | Description | +|-------|-------------| +| `module_source` | Registry repository for the module, e.g. `registry.example.com/module-source` | +| `module_name` | Module name, e.g. `my-module` | +| `module_tag` | Version tag, e.g. `v1.21.1` or branch name | + +### Optional + +| Input | Description | +|-------|-------------| +| `secondary_repo` | Secondary registry path for the module | +| `source_repo` | Source Git repository URL | +| `source_repo_ssh_key` | SSH key for `source_repo` | +| `svace_enabled` | Enable Svace static analysis integration | +| `svace_analyze_host` | Svace analyze server hostname | +| `svace_analyze_ssh_user` | SSH user for Svace server | +| `svace_analyze_ssh_key` | SSH private key for Svace server | +| `edition` | Module edition; sets `EDITION` and artifact name suffix | +| `vault_addr` | Vault address for signing/secrets | +| `vault_key` | Vault key name | +| `vault_role` | Vault JWT/OIDC role | +| `transit_secret_engine_path` | Vault transit engine path | +| `registry_user` | Registry username for werf build | +| `registry_password` | Registry password for werf build | + +## Example + +```yaml +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: deckhouse/modules-actions/setup@v4 + with: + registry: registry.deckhouse.io + registry_login: ${{ secrets.REGISTRY_LOGIN }} + registry_password: ${{ secrets.REGISTRY_PASSWORD }} + - uses: deckhouse/modules-actions/build@main + with: + module_source: registry.deckhouse.io/deckhouse/fe/modules + module_name: my-module + module_tag: ${{ github.ref_name }} + edition: fe + vault_addr: ${{ secrets.VAULT_ADDR }} + vault_key: ${{ secrets.VAULT_KEY }} + vault_role: ${{ secrets.VAULT_ROLE }} + transit_secret_engine_path: ${{ secrets.TRANSIT_SECRET_ENGINE_PATH }} + registry_user: ${{ secrets.REGISTRY_USER }} + registry_password: ${{ secrets.REGISTRY_PASSWORD }} +``` + +## Artifacts + +| Artifact name | Contents | When | +|---------------|----------|------| +| `build_report` | `images_tags_werf.json` | Build finished (success or failure), no `edition` | +| `build_report_` | `images_tags_werf.json` | Build finished (success or failure), `edition` set | + +Full workflow example: [.examples/build.yml](../.examples/build.yml). + +PR changelog for this feature set: [CHANGELOG-vex-attestations.md](./CHANGELOG-vex-attestations.md). diff --git a/README.md b/README.md index a2469df..ed4dfbe 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ This repository contains GitHub Actions workflows for building and deploying mod | Workflow | Description | |---------------------------------------|-----------------------------------------------------------------------------------------------------------------------| | [**setup**](./setup/action.yml) | Sets up the environment for building and deploying modules. This workflow **must** be run before any other workflows. | -| [**build**](./build/action.yml) | Builds the Deckhouse modules using the [werf](https://werf.io/) tool. | +| [**build**](./build/action.yml) | Builds Deckhouse modules with [werf](https://werf.io/), copies bundle VEX attestations, and uploads the build report. See [build docs](./.docs/build.md). | | [**deploy**](./deploy/action.yml) | Deploys the Deckhouse modules to the one of selected release channels. | | [**cve_scan**](./cve_scan/action.yml) | Trivy CVE Scan of module images. Documentation can be found [here](./.docs/cve_scan.md) | | [**svace_analyze**](./svace_analyze/action.yml) | Include svace analyze action to analyze and import builds made with svace tool | @@ -42,3 +42,11 @@ To use these GitHub Action workflows in your own repository: 3. Make sure to configure any necessary secrets or environment variables in your GitHub repository settings to enable secure deployment. 4. Trigger the workflows manually or automatically on each push, pull request, or any other event as needed. + +## Documentation + +| Topic | Description | +|-------|-------------| +| [Build action](./.docs/build.md) | Vault/registry inputs, edition support, VEX attestation copy, build report artifacts | +| [CVE scan](./.docs/cve_scan.md) | Trivy scanning and DefectDojo upload | +| [VEX attestations changelog](./.docs/CHANGELOG-vex-attestations.md) | Changes in branch `vex-attestations` vs `main` |