-
Notifications
You must be signed in to change notification settings - Fork 3
191 lines (177 loc) · 8.26 KB
/
_docker-pipeline.yml
File metadata and controls
191 lines (177 loc) · 8.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
name: _docker-pipeline (reusable)
# Reusable workflow — the single lego brick for all Docker CI steps.
#
# Called by smoke-test.yml (push: false) and publish-docker.yml (push: true).
# Step visibility is controlled by the push/tag_push inputs; the caller sets permissions.
#
# Two modes:
# push: false → build + smoke test + integration test (main image only)
# push: true → above + push to GHCR/Docker Hub + update floating v-tag
#
# Permissions required from the calling workflow:
# push: false → contents: read
# push: true → contents: write, packages: write
on:
workflow_call:
inputs:
name:
description: "Image name, e.g. socket-basics"
type: string
required: true
dockerfile:
description: "Path to Dockerfile relative to repo root"
type: string
required: true
context:
description: "Docker build context"
type: string
required: false
default: "."
check_set:
description: "Smoke-test tool set: main or app-tests"
type: string
required: true
push:
description: "Push to GHCR and Docker Hub after testing"
type: boolean
required: false
default: false
tag_push:
description: >
True when the caller was triggered by a tag push (e.g. v2.0.0).
Controls the floating major-version tag update and the 'latest' Docker tag.
Passed explicitly rather than relying on github.ref_type inside the callee,
since context propagation in reusable workflows can be ambiguous.
type: boolean
required: false
default: false
version:
description: "Semver without v prefix (e.g. 2.0.0) — used for OCI labels and push tags"
type: string
required: false
default: "dev"
secrets:
DOCKERHUB_USERNAME:
required: false
DOCKERHUB_TOKEN:
required: false
jobs:
pipeline:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: 🔨 Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
# GHCR login runs before the build — needed to pull ghcr.io/astral-sh/uv.
- name: Login to GHCR
if: inputs.push
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ github.token }}
# Docker Hub login is deferred until after the build and tests.
# A repo-scoped Docker Hub token (socketdev/socket-basics only) would cause
# 401s if active during the build, since BuildKit uses it for ALL Docker Hub
# requests including pulling public base images (python, trivy, trufflehog).
# Those public images pull fine without auth; only the push needs credentials.
- name: Extract image metadata
if: inputs.push
id: meta
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
with:
images: |
ghcr.io/socketdev/${{ inputs.name }}
${{ secrets.DOCKERHUB_USERNAME }}/${{ inputs.name }}
# Disable the automatic :latest tag — metadata-action adds it by default
# for semver tag pushes. Mutable tags are inappropriate for a security tool.
flavor: |
latest=false
tags: |
# Tag push (v2.0.0) → exact immutable version tag only.
# Minor (2.0) and latest tags are intentionally omitted.
type=semver,pattern={{version}}
# workflow_dispatch re-publish → use the version input directly
type=raw,value=${{ inputs.version }},enable=${{ !inputs.tag_push }}
labels: |
org.opencontainers.image.title=${{ inputs.name }}
org.opencontainers.image.source=https://github.com/SocketDev/socket-basics
# ── Step 1: Build ──────────────────────────────────────────────────────
# Loads image into the local Docker daemon without pushing.
# Writes all layers to the GHA cache so the push step is just an upload.
- name: 🔨 Build (load for testing)
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2
with:
# zizmor: ignore[template-injection] — safe: always hardcoded "." from same-repo callers; passed as array element to exec, not shell-interpolated
context: ${{ inputs.context }}
file: ${{ inputs.dockerfile }}
load: true
push: false
tags: ${{ inputs.name }}:pipeline-test
build-args: |
SOCKET_BASICS_VERSION=${{ inputs.version }}
VCS_REF=${{ github.sha }}
BUILD_DATE=${{ github.event.repository.updated_at }}
cache-from: type=gha,scope=${{ inputs.name }}
cache-to: type=gha,mode=max,scope=${{ inputs.name }}
# Disable attestations for the test build — provenance/SBOM cause BuildKit
# to pull docker/buildkit-syft-scanner from Docker Hub, which fails with a
# repo-scoped token. Attestations are enabled on the push step only.
provenance: false
sbom: false
# ── Step 2: Smoke test ─────────────────────────────────────────────────
- name: 🧪 Smoke test
env:
IMAGE_NAME: ${{ inputs.name }}
CHECK_SET: ${{ inputs.check_set }}
run: |
bash ./scripts/smoke-test-docker.sh \
--skip-build \
--image-tag "$IMAGE_NAME:pipeline-test" \
--check-set "$CHECK_SET"
# ── Step 3: Integration test (main image only) ─────────────────────────
- name: 🔬 Integration test
if: inputs.name == 'socket-basics'
env:
IMAGE_NAME: ${{ inputs.name }}
run: |
bash ./scripts/integration-test-docker.sh \
--image-tag "$IMAGE_NAME:pipeline-test"
# ── Step 4: Push to registries (publish mode only) ─────────────────────
# Docker Hub login happens here — after build and tests, immediately before
# push. Keeping it here prevents the repo-scoped token from interfering
# with public image pulls during the build step.
- name: Login to Docker Hub
if: inputs.push
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
# All layers are in the GHA cache from step 1 — this is just an upload.
- name: 🚀 Push to registries
if: inputs.push
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2
with:
# zizmor: ignore[template-injection] — safe: always hardcoded "." from same-repo callers; passed as array element to exec, not shell-interpolated
context: ${{ inputs.context }}
file: ${{ inputs.dockerfile }}
load: false
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
SOCKET_BASICS_VERSION=${{ inputs.version }}
VCS_REF=${{ github.sha }}
BUILD_DATE=${{ github.event.repository.updated_at }}
cache-from: type=gha,scope=${{ inputs.name }}
# SBOM and provenance generation pull docker/buildkit-syft-scanner from
# Docker Hub, which fails with a repo-scoped token. Disabled until a
# token with broader Docker Hub read access is available.
provenance: false
sbom: false
# Floating major version tags (v2 → latest v2.x.y) have been intentionally
# removed. Mutable tags are structurally equivalent to :latest and are
# inappropriate for a security tool. Users should pin to an immutable
# version tag or digest and use Dependabot to manage upgrades.