Skip to content
This repository was archived by the owner on Jun 16, 2026. It is now read-only.

Commit 25a4858

Browse files
committed
adds nix workflows, adds container-lifecycle
1 parent 4c2eab2 commit 25a4858

6 files changed

Lines changed: 697 additions & 48 deletions

File tree

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
name: Build Nix OCI Image
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
nix-target:
7+
description: 'Nix flake build target (e.g. devguardOCI)'
8+
required: true
9+
type: string
10+
image-name:
11+
description: 'Full OCI image name without tag (e.g. ghcr.io/l3montree-dev/devguard/scanner)'
12+
required: true
13+
type: string
14+
artifact-name-suffix:
15+
description: 'Suffix appended to artifact names to avoid conflicts when building multiple images in the same workflow'
16+
required: false
17+
type: string
18+
default: ''
19+
asset-name:
20+
description: 'DevGuard asset name for supply chain tracking'
21+
required: true
22+
type: string
23+
api-url:
24+
description: 'DevGuard API URL'
25+
required: false
26+
type: string
27+
default: 'https://api.devguard.org'
28+
secrets:
29+
devguard-token:
30+
required: false
31+
outputs:
32+
image-tag:
33+
description: 'Full image tag of the built image'
34+
value: ${{ jobs.build.outputs.image-tag }}
35+
image-digest:
36+
description: 'Digest of the built image'
37+
value: ${{ jobs.build.outputs.image-digest }}
38+
artifact-purl:
39+
description: 'Package URL (PURL) of the built artifact'
40+
value: ${{ jobs.build.outputs.artifact-purl }}
41+
42+
jobs:
43+
build:
44+
runs-on: ubuntu-latest
45+
outputs:
46+
image-tag: ${{ steps.set-image-tag.outputs.image_tag }}
47+
image-digest: ${{ steps.get-digest.outputs.digest }}
48+
artifact-purl: ${{ steps.set-purl.outputs.purl }}
49+
steps:
50+
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 - https://github.com/actions/checkout/releases/tag/v5.0.0
51+
with:
52+
fetch-depth: 0 # needed for flake.nix to resolve self.shortRev
53+
persist-credentials: false
54+
55+
- name: In-Toto Provenance record start
56+
uses: docker://ghcr.io/l3montree-dev/devguard/scanner:v1.0.0
57+
with:
58+
args: devguard-scanner intoto start --step=build --token=${{ secrets.devguard-token }} --apiUrl=${{ inputs.api-url }} --assetName=${{ inputs.asset-name }} --supplyChainId=${{ github.sha }}
59+
continue-on-error: true
60+
61+
- uses: cachix/install-nix-action@v31
62+
with:
63+
extra_nix_config: |
64+
experimental-features = nix-command flakes
65+
66+
- uses: imjasonh/setup-crane@v0.1
67+
68+
- name: Build OCI image with Nix
69+
run: nix build .#${{ inputs.nix-target }}
70+
71+
- name: Prepare image.tar
72+
run: gunzip -c "$(readlink -f result)" > image.tar
73+
74+
- name: Get image digest
75+
id: get-digest
76+
run: |
77+
crane digest --tarball=image.tar > image-digest.txt
78+
echo "digest=$(cat image-digest.txt)" >> $GITHUB_OUTPUT
79+
80+
- name: Upload oci-image artifact
81+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - https://github.com/actions/upload-artifact/releases/tag/v4.6.2
82+
with:
83+
name: oci-image${{ inputs.artifact-name-suffix }}
84+
path: image.tar
85+
86+
- name: Set image tag
87+
id: set-image-tag
88+
run: |
89+
if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
90+
IMAGE_TAG="${{ inputs.image-name }}:${GITHUB_REF#refs/tags/}"
91+
else
92+
branch=${GITHUB_REF##*/}
93+
sha=${GITHUB_SHA::8}
94+
ts=$(date +%s)
95+
IMAGE_TAG="${{ inputs.image-name }}:${branch}-${sha}-${ts}"
96+
fi
97+
IMAGE_TAG=$(echo "$IMAGE_TAG" | tr '[:upper:]' '[:lower:]')
98+
echo "$IMAGE_TAG" > image-tag.txt
99+
echo "IMAGE_TAG=$IMAGE_TAG" >> $GITHUB_ENV
100+
echo "image_tag=$IMAGE_TAG" >> $GITHUB_OUTPUT
101+
102+
- name: Upload image-tag artifact
103+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - https://github.com/actions/upload-artifact/releases/tag/v4.6.2
104+
with:
105+
name: image-tag${{ inputs.artifact-name-suffix }}
106+
path: image-tag.txt
107+
108+
- name: Upload image-digest artifact
109+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - https://github.com/actions/upload-artifact/releases/tag/v4.6.2
110+
with:
111+
name: image-digest${{ inputs.artifact-name-suffix }}
112+
path: image-digest.txt
113+
114+
- name: Set artifact PURL
115+
id: set-purl
116+
run: |
117+
REGISTRY_AND_IMAGE=${IMAGE_TAG%:*}
118+
NAME=${REGISTRY_AND_IMAGE##*/}
119+
PURL="pkg:oci/$NAME?repository_url=$REGISTRY_AND_IMAGE"
120+
echo "$PURL" > artifact-purl.txt
121+
SAFE_PURL=$(echo -n "$PURL" | jq -s -R -r @uri)
122+
echo "$SAFE_PURL" > artifact-purl-safe.txt
123+
echo "purl=$PURL" >> $GITHUB_OUTPUT
124+
125+
- name: Upload artifact-purl artifact
126+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - https://github.com/actions/upload-artifact/releases/tag/v4.6.2
127+
with:
128+
name: artifact-purl${{ inputs.artifact-name-suffix }}
129+
path: artifact-purl.txt
130+
131+
- name: Upload artifact-purl-safe artifact
132+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - https://github.com/actions/upload-artifact/releases/tag/v4.6.2
133+
with:
134+
name: artifact-purl-safe${{ inputs.artifact-name-suffix }}
135+
path: artifact-purl-safe.txt
136+
137+
- name: In-Toto Provenance record stop
138+
uses: docker://ghcr.io/l3montree-dev/devguard/scanner:v1.0.0
139+
with:
140+
args: devguard-scanner intoto stop --step=build --products=image-digest.txt --products=image-tag.txt --token=${{ secrets.devguard-token }} --apiUrl=${{ inputs.api-url }} --assetName=${{ inputs.asset-name }} --supplyChainId=${{ github.sha }} --generateSlsaProvenance
141+
continue-on-error: true
142+
143+
- name: Upload SLSA Provenance
144+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - https://github.com/actions/upload-artifact/releases/tag/v4.6.2
145+
with:
146+
name: build${{ inputs.artifact-name-suffix }}.provenance.json
147+
path: build.provenance.json
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
name: Code Scanning
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
asset-name:
7+
description: 'Name of the asset to be scanned'
8+
type: string
9+
required: true
10+
11+
api-url:
12+
type: string
13+
required: false
14+
default: "https://api.devguard.org"
15+
16+
path:
17+
description: 'Path to the source code to be scanned'
18+
type: string
19+
required: false
20+
default: "/github/workspace"
21+
22+
web-ui:
23+
type: string
24+
required: false
25+
default: "https://app.devguard.org"
26+
description: "The URL of the DevGuard Web UI. This is used to link the results in the DevGuard Web UI."
27+
28+
fail-on-risk:
29+
description: 'Fail the job if a risk is higher than the configured threshold, e.g. critical, high, medium, low'
30+
type: string
31+
required: false
32+
33+
fail-on-cvss:
34+
description: 'Fail the job if a CVSS score is higher than the configured threshold, e.g. critical, high, medium, low'
35+
type: string
36+
required: false
37+
38+
continue-on-open-code-risk:
39+
type: boolean
40+
required: false
41+
default: true
42+
43+
secrets:
44+
devguard-token:
45+
description: 'DevGuard API token'
46+
required: true
47+
48+
49+
jobs:
50+
call-secret-scanning:
51+
uses: ./.github/workflows/secret-scanning.yml
52+
with:
53+
asset-name: ${{ inputs.asset-name }}
54+
api-url: ${{ inputs.api-url }}
55+
path: ${{ inputs.path }}
56+
web-ui: ${{ inputs.web-ui }}
57+
continue-on-open-code-risk: ${{ inputs.continue-on-open-code-risk }}
58+
secrets:
59+
devguard-token: ${{ secrets.devguard-token }}
60+
61+
call-sast:
62+
uses: ./.github/workflows/static-application-security-testing.yml
63+
with:
64+
asset-name: ${{ inputs.asset-name }}
65+
api-url: ${{ inputs.api-url }}
66+
path: ${{ inputs.path }}
67+
web-ui: ${{ inputs.web-ui }}
68+
continue-on-open-code-risk: ${{ inputs.continue-on-open-code-risk }}
69+
secrets:
70+
devguard-token: ${{ secrets.devguard-token }}
71+
72+
call-iac:
73+
uses: ./.github/workflows/iac.yml
74+
with:
75+
asset-name: ${{ inputs.asset-name }}
76+
api-url: ${{ inputs.api-url }}
77+
path: ${{ inputs.path }}
78+
web-ui: ${{ inputs.web-ui }}
79+
continue-on-open-code-risk: ${{ inputs.continue-on-open-code-risk }}
80+
secrets:
81+
devguard-token: ${{ secrets.devguard-token }}
82+
83+
call-software-composition-analysis:
84+
uses: ./.github/workflows/software-composition-analysis.yml
85+
with:
86+
asset-name: ${{ inputs.asset-name }}
87+
api-url: ${{ inputs.api-url }}
88+
path: ${{ inputs.path }}
89+
fail-on-risk: ${{ inputs.fail-on-risk }}
90+
fail-on-cvss: ${{ inputs.fail-on-cvss }}
91+
web-ui: ${{ inputs.web-ui }}
92+
secrets:
93+
devguard-token: ${{ secrets.devguard-token }}
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
name: Container Lifecycle (Nix)
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
nix-target:
7+
description: 'Nix flake build target (e.g. devguardOCI)'
8+
required: true
9+
type: string
10+
11+
image-name:
12+
description: 'Full OCI image name without tag (e.g. ghcr.io/l3montree-dev/devguard/scanner)'
13+
required: true
14+
type: string
15+
16+
asset-name:
17+
description: 'DevGuard asset name for supply chain tracking'
18+
required: true
19+
type: string
20+
21+
api-url:
22+
description: 'DevGuard API URL'
23+
required: false
24+
type: string
25+
default: 'https://api.devguard.org'
26+
27+
web-ui:
28+
description: 'DevGuard web UI URL'
29+
required: false
30+
type: string
31+
default: 'https://app.devguard.org'
32+
33+
artifact-name-suffix:
34+
description: 'Suffix appended to artifact names to avoid conflicts when building multiple images in the same workflow'
35+
required: false
36+
type: string
37+
default: ''
38+
39+
should-deploy:
40+
description: 'Whether to push the image to the container registry'
41+
required: false
42+
type: boolean
43+
default: true
44+
45+
fail-on-risk:
46+
description: 'Fail if risk level reaches this threshold (none|low|medium|high|critical)'
47+
required: false
48+
type: string
49+
50+
fail-on-cvss:
51+
description: 'Fail if CVSS score reaches this threshold (none|low|medium|high|critical)'
52+
required: false
53+
type: string
54+
55+
secrets:
56+
devguard-token:
57+
required: true
58+
59+
outputs:
60+
image-tag:
61+
description: 'Full image tag of the built image'
62+
value: ${{ jobs.build-image.outputs.image-tag }}
63+
image-digest:
64+
description: 'Digest of the built image'
65+
value: ${{ jobs.build-image.outputs.image-digest }}
66+
artifact-purl:
67+
description: 'Package URL (PURL) of the built artifact'
68+
value: ${{ jobs.build-image.outputs.artifact-purl }}
69+
70+
71+
permissions:
72+
contents: read
73+
packages: write
74+
75+
jobs:
76+
build-image:
77+
uses: ./.github/workflows/build-nix-image.yml
78+
permissions:
79+
contents: read
80+
with:
81+
nix-target: ${{ inputs.nix-target }}
82+
image-name: ${{ inputs.image-name }}
83+
artifact-name-suffix: ${{ inputs.artifact-name-suffix }}
84+
asset-name: ${{ inputs.asset-name }}
85+
api-url: ${{ inputs.api-url }}
86+
secrets:
87+
devguard-token: ${{ secrets.devguard-token }}
88+
89+
container-scanning:
90+
needs: build-image
91+
uses: ./.github/workflows/container-scanning.yml
92+
permissions:
93+
contents: read
94+
security-events: write
95+
with:
96+
asset-name: ${{ inputs.asset-name }}
97+
api-url: ${{ inputs.api-url }}
98+
artifact-name: ${{ needs.build-image.outputs.artifact-purl }}
99+
web-ui: ${{ inputs.web-ui }}
100+
fail-on-cvss: ${{ inputs.fail-on-cvss }}
101+
fail-on-risk: ${{ inputs.fail-on-risk }}
102+
image-suffix: ${{ inputs.artifact-name-suffix }}
103+
secrets:
104+
devguard-token: ${{ secrets.devguard-token }}
105+
106+
deploy:
107+
if: ${{ inputs.should-deploy }}
108+
needs: [build-image, container-scanning]
109+
uses: ./.github/workflows/deploy.yml
110+
permissions:
111+
contents: read
112+
packages: write
113+
with:
114+
asset-name: ${{ inputs.asset-name }}
115+
api-url: ${{ inputs.api-url }}
116+
image-suffix: ${{ inputs.artifact-name-suffix }}
117+
secrets:
118+
devguard-token: ${{ secrets.devguard-token }}
119+
120+
sign:
121+
if: ${{ inputs.should-deploy }}
122+
needs: [build-image, container-scanning, deploy]
123+
uses: ./.github/workflows/sign.yml
124+
permissions:
125+
contents: read
126+
packages: write
127+
with:
128+
asset-name: ${{ inputs.asset-name }}
129+
api-url: ${{ inputs.api-url }}
130+
artifact-name: ${{ needs.build-image.outputs.artifact-purl }}
131+
image-suffix: ${{ inputs.artifact-name-suffix }}
132+
secrets:
133+
devguard-token: ${{ secrets.devguard-token }}
134+
135+
attest:
136+
if: ${{ inputs.should-deploy }}
137+
needs: [build-image, container-scanning, deploy]
138+
uses: ./.github/workflows/attest.yml
139+
permissions:
140+
contents: read
141+
packages: write
142+
with:
143+
asset-name: ${{ inputs.asset-name }}
144+
api-url: ${{ inputs.api-url }}
145+
artifact-name: ${{ needs.build-image.outputs.artifact-purl }}
146+
image-suffix: ${{ inputs.artifact-name-suffix }}
147+
secrets:
148+
devguard-token: ${{ secrets.devguard-token }}

0 commit comments

Comments
 (0)