Skip to content

Commit 51ed8ed

Browse files
authored
feat: 🐳 multi-stage Docker builds, immutable release pipeline, CHANGELOG automation (#46)
* Multi-stage builds with pinned binary deps, slim base image Signed-off-by: lelia <lelia@socket.dev> * Introduce publish-docker workflow and matrix logic Signed-off-by: lelia <lelia@socket.dev> * Add dependabot config for keeping docker images up to date Signed-off-by: lelia <lelia@socket.dev> * Add config for autogen release notes Signed-off-by: lelia <lelia@socket.dev> * Update git ignore rules Signed-off-by: lelia <lelia@socket.dev> * Add dynamic CI matrix script for new workflow Signed-off-by: lelia <lelia@socket.dev> * Update docker smoketest to reflect new build patterns Signed-off-by: lelia <lelia@socket.dev> * Add integration tests for new docker builds Signed-off-by: lelia <lelia@socket.dev> * Add initial CHANGELOG with versioning note, plus script for automated upates Signed-off-by: lelia <lelia@socket.dev> * Update docs to cover prebuilt image usage, v2 pinning strategies, release workflow Signed-off-by: lelia <lelia@socket.dev> * Update integration test script to use less-heavy scan for CI Signed-off-by: lelia <lelia@socket.dev> * Fix auto-generated matrix job names Signed-off-by: lelia <lelia@socket.dev> * Replace implicit dependency with explicit tag_push: bool input Signed-off-by: lelia <lelia@socket.dev> * Track node version with Dependabot for consistency Signed-off-by: lelia <lelia@socket.dev> * Point usage docs at dockerhub instead of GHCR for stability Signed-off-by: lelia <lelia@socket.dev> * Bump oss toolchain versions in docs Signed-off-by: lelia <lelia@socket.dev> * Update dockerfile to use latest oss toolchain versions Signed-off-by: lelia <lelia@socket.dev> * Remove unused matrix script Signed-off-by: lelia <lelia@socket.dev> * Remove matrix logic and flatten GHA workflow jobs Signed-off-by: lelia <lelia@socket.dev> * Bump uv version per changelog Signed-off-by: lelia <lelia@socket.dev> * Update action manifest to reference plan for release workflow sequence Signed-off-by: lelia <lelia@socket.dev> * Add github PR template for new release workflow checklist Signed-off-by: lelia <lelia@socket.dev> * Remove deprecated version bump script logic Signed-off-by: lelia <lelia@socket.dev> * Add assertion test to enforce action.yml update on release Signed-off-by: lelia <lelia@socket.dev> * Add note in PR template about action.yml CI enforcement Signed-off-by: lelia <lelia@socket.dev> * Update release workflow docs to cover PR template and CI enforcement Signed-off-by: lelia <lelia@socket.dev> * Update README to use prebuilt image, move testing docs Signed-off-by: lelia <lelia@socket.dev> * Update GHA docs to focus on user guidance, move release process to separate doc Signed-off-by: lelia <lelia@socket.dev> * Remove deprecated precommit hook docs Signed-off-by: lelia <lelia@socket.dev> * Add dedicated doc for new release process + checklist Signed-off-by: lelia <lelia@socket.dev> * Add initial PR template to facilitate new release PR workflow + checklist Signed-off-by: lelia <lelia@socket.dev> * Update publish workflow to commit action.yml back to main Signed-off-by: lelia <lelia@socket.dev> * Update PR release template to clarify action.yml process Signed-off-by: lelia <lelia@socket.dev> * Update release docs to reflect new action.yml handling Signed-off-by: lelia <lelia@socket.dev> * feat: remove floating v2 tag and :latest β€” immutable releases only Signed-off-by: lelia <lelia@socket.dev> * docs: rewrite pinning strategy guidance with our security philosophy Signed-off-by: lelia <lelia@socket.dev> * ci: add commit-lint workflow for Conventional Commits enforcement Signed-off-by: lelia <lelia@socket.dev> * docs(changelog): update changelog to reflect current git tags, update Unreleased for PR Signed-off-by: lelia <lelia@socket.dev> --------- Signed-off-by: lelia <lelia@socket.dev>
1 parent 9467be6 commit 51ed8ed

27 files changed

+1659
-508
lines changed

β€Ž.claude/commands/bump-version.mdβ€Ž

Lines changed: 0 additions & 35 deletions
This file was deleted.

β€Ž.commitlintrc.ymlβ€Ž

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
extends:
2+
- '@commitlint/config-conventional'
3+
4+
# Conventional commit types allowed in this repo.
5+
# PR titles are validated against this config on every PR (commit-lint.yml)
6+
# since most merges are squash merges β€” the PR title becomes the commit on main.
7+
#
8+
# Format: <type>(<optional scope>): <description>
9+
# Examples:
10+
# feat(docker): add versioned Node stage
11+
# fix: correct TruffleHog tag format
12+
# docs: update pinning strategy guidance
13+
# chore(deps): bump Trivy to 0.69.3
14+
# ci: add commit-lint workflow
15+
16+
rules:
17+
type-enum:
18+
- 2
19+
- always
20+
- [feat, fix, docs, chore, ci, refactor, test, perf, revert]
21+
subject-case:
22+
- 2
23+
- always
24+
- sentence-case
25+
header-max-length:
26+
- 2
27+
- always
28+
- 100

β€Ž.dockerignoreβ€Ž

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Git
2+
.git/
3+
.gitignore
4+
.gitmodules
5+
6+
# CI / GitHub
7+
.github/
8+
9+
# Tests and test apps
10+
tests/
11+
app_tests/
12+
13+
# Docs and scripts (not needed in image)
14+
docs/
15+
scripts/
16+
17+
# Markdown (keep README.md β€” it's copied explicitly in the Dockerfile)
18+
*.md
19+
!README.md
20+
21+
# Python build artifacts
22+
__pycache__/
23+
*.pyc
24+
*.pyo
25+
*.pyd
26+
.pytest_cache/
27+
*.egg-info/
28+
dist/
29+
build/
30+
31+
# Virtual environments
32+
.venv/
33+
venv/
34+
35+
# Local config / secrets
36+
.env
37+
.env.*
38+
39+
# Editor
40+
.vscode/
41+
.idea/
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
## Summary
2+
3+
<!-- What does this PR do? Why? -->
4+
5+
## Changes
6+
7+
<!-- Bullet points are fine. Link to relevant issues/tickets if applicable. -->
8+
9+
## Testing
10+
11+
<!-- How was this tested? Local smoke test, CI, manual verification, etc. -->
12+
13+
---
14+
15+
### Release checklist (skip for non-release PRs)
16+
17+
<!-- Only fill this out if this PR is cutting a new release (e.g. v2.1.0). -->
18+
19+
- [ ] `socket_basics/version.py` updated to new version
20+
- [ ] `pyproject.toml` `version:` field updated to match
21+
- [ ] `action.yml` `image:` ref updated to `docker://ghcr.io/socketdev/socket-basics:<new-version>` *(auto-updated by `publish-docker.yml` after v2.0.0; manual update required only for the initial v2.0.0 release)*
22+
- [ ] `CHANGELOG.md` `[Unreleased]` section reviewed *(note: this content is replaced by auto-generated release notes when the tag fires β€” see [docs/releasing.md](../docs/releasing.md#changelog-and-release-notes))*
23+
24+
> ⚠️ **After merging:** run `publish-docker.yml` via `workflow_dispatch` with the new version
25+
> **before** creating the git tag. The image must exist in GHCR before the tag is pushed.
26+
> See [docs/releasing.md](../docs/releasing.md) for the full process.

β€Ž.github/dependabot.ymlβ€Ž

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
version: 2
2+
updates:
3+
4+
# Main Dockerfile β€” tracks aquasec/trivy, trufflesecurity/trufflehog,
5+
# ghcr.io/astral-sh/uv, and python base image.
6+
# NOTE: OPENGREP_VERSION is not trackable via Dependabot (no Docker image);
7+
# update it manually in the Dockerfile ARG.
8+
- package-ecosystem: "docker"
9+
directory: "/"
10+
schedule:
11+
interval: "weekly"
12+
labels:
13+
- "dependencies"
14+
- "docker"
15+
16+
# app_tests Dockerfile β€” same as above, plus golang and securego/gosec.
17+
- package-ecosystem: "docker"
18+
directory: "/app_tests"
19+
schedule:
20+
interval: "weekly"
21+
labels:
22+
- "dependencies"
23+
- "docker"
24+
25+
# GitHub Actions β€” tracks all uses: ... action versions.
26+
- package-ecosystem: "github-actions"
27+
directory: "/"
28+
schedule:
29+
interval: "weekly"
30+
labels:
31+
- "dependencies"
32+
- "github-actions"

β€Ž.github/release.ymlβ€Ž

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Configures GitHub's auto-generated release notes categories.
2+
# Used by `gh release create --generate-notes` in publish-docker.yml,
3+
# and visible in the GitHub "Generate release notes" UI when drafting releases.
4+
# https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes
5+
6+
changelog:
7+
categories:
8+
- title: "πŸš€ New Features"
9+
labels:
10+
- enhancement
11+
- feature
12+
- title: "πŸ› Bug Fixes"
13+
labels:
14+
- bug
15+
- fix
16+
- title: "πŸ”’ Security"
17+
labels:
18+
- security
19+
- title: "πŸ“¦ Dependencies"
20+
labels:
21+
- dependencies
22+
- docker
23+
- title: "βš™οΈ CI / Build"
24+
labels:
25+
- ci
26+
- github-actions
27+
- build
28+
- title: "πŸ“š Documentation"
29+
labels:
30+
- documentation
31+
- docs
32+
- title: "πŸ”§ Other Changes"
33+
labels:
34+
- "*"
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
name: _docker-pipeline (reusable)
2+
3+
# Reusable workflow β€” the single lego brick for all Docker CI steps.
4+
#
5+
# Called by smoke-test.yml (push: false) and publish-docker.yml (push: true).
6+
# Step visibility is controlled by the push/tag_push inputs; the caller sets permissions.
7+
#
8+
# Two modes:
9+
# push: false β†’ build + smoke test + integration test (main image only)
10+
# push: true β†’ above + push to GHCR/Docker Hub + update floating v-tag
11+
#
12+
# Permissions required from the calling workflow:
13+
# push: false β†’ contents: read
14+
# push: true β†’ contents: write, packages: write
15+
16+
on:
17+
workflow_call:
18+
inputs:
19+
name:
20+
description: "Image name, e.g. socket-basics"
21+
type: string
22+
required: true
23+
dockerfile:
24+
description: "Path to Dockerfile relative to repo root"
25+
type: string
26+
required: true
27+
context:
28+
description: "Docker build context"
29+
type: string
30+
required: false
31+
default: "."
32+
check_set:
33+
description: "Smoke-test tool set: main or app-tests"
34+
type: string
35+
required: true
36+
push:
37+
description: "Push to GHCR and Docker Hub after testing"
38+
type: boolean
39+
required: false
40+
default: false
41+
tag_push:
42+
description: >
43+
True when the caller was triggered by a tag push (e.g. v2.0.0).
44+
Controls the floating major-version tag update and the 'latest' Docker tag.
45+
Passed explicitly rather than relying on github.ref_type inside the callee,
46+
since context propagation in reusable workflows can be ambiguous.
47+
type: boolean
48+
required: false
49+
default: false
50+
version:
51+
description: "Semver without v prefix (e.g. 2.0.0) β€” used for OCI labels and push tags"
52+
type: string
53+
required: false
54+
default: "dev"
55+
secrets:
56+
DOCKERHUB_USERNAME:
57+
required: false
58+
DOCKERHUB_TOKEN:
59+
required: false
60+
61+
jobs:
62+
pipeline:
63+
runs-on: ubuntu-latest
64+
timeout-minutes: 60
65+
66+
steps:
67+
- name: Checkout
68+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
69+
70+
- name: πŸ”¨ Set up Docker Buildx
71+
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
72+
73+
# Logins and metadata are only needed in push mode
74+
- name: Login to GHCR
75+
if: inputs.push
76+
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
77+
with:
78+
registry: ghcr.io
79+
username: ${{ github.actor }}
80+
password: ${{ github.token }}
81+
82+
- name: Login to Docker Hub
83+
if: inputs.push
84+
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
85+
with:
86+
username: ${{ secrets.DOCKERHUB_USERNAME }}
87+
password: ${{ secrets.DOCKERHUB_TOKEN }}
88+
89+
- name: Extract image metadata
90+
if: inputs.push
91+
id: meta
92+
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
93+
with:
94+
images: |
95+
ghcr.io/socketdev/${{ inputs.name }}
96+
${{ secrets.DOCKERHUB_USERNAME }}/${{ inputs.name }}
97+
tags: |
98+
# Tag push (v2.0.0) β†’ immutable Docker tags 2.0.0 and 2.0 only.
99+
# :latest and floating major tags (v2) are intentionally omitted β€”
100+
# this is a security tool and mutable tags set the wrong example.
101+
# Users should pin to a specific version or digest; Dependabot manages upgrades.
102+
type=semver,pattern={{version}}
103+
type=semver,pattern={{major}}.{{minor}}
104+
# workflow_dispatch re-publish β†’ use the version input directly
105+
type=raw,value=${{ inputs.version }},enable=${{ !inputs.tag_push }}
106+
labels: |
107+
org.opencontainers.image.title=${{ inputs.name }}
108+
org.opencontainers.image.source=https://github.com/SocketDev/socket-basics
109+
110+
# ── Step 1: Build ──────────────────────────────────────────────────────
111+
# Loads image into the local Docker daemon without pushing.
112+
# Writes all layers to the GHA cache so the push step is just an upload.
113+
- name: πŸ”¨ Build (load for testing)
114+
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2
115+
with:
116+
context: ${{ inputs.context }}
117+
file: ${{ inputs.dockerfile }}
118+
load: true
119+
push: false
120+
tags: ${{ inputs.name }}:pipeline-test
121+
build-args: |
122+
SOCKET_BASICS_VERSION=${{ inputs.version }}
123+
VCS_REF=${{ github.sha }}
124+
BUILD_DATE=${{ github.event.repository.updated_at }}
125+
cache-from: type=gha,scope=${{ inputs.name }}
126+
cache-to: type=gha,mode=max,scope=${{ inputs.name }}
127+
128+
# ── Step 2: Smoke test ─────────────────────────────────────────────────
129+
- name: πŸ§ͺ Smoke test
130+
run: |
131+
bash ./scripts/smoke-test-docker.sh \
132+
--skip-build \
133+
--image-tag ${{ inputs.name }}:pipeline-test \
134+
--check-set ${{ inputs.check_set }}
135+
136+
# ── Step 3: Integration test (main image only) ─────────────────────────
137+
- name: πŸ”¬ Integration test
138+
if: inputs.name == 'socket-basics'
139+
run: |
140+
bash ./scripts/integration-test-docker.sh \
141+
--image-tag ${{ inputs.name }}:pipeline-test
142+
143+
# ── Step 4: Push to registries (publish mode only) ─────────────────────
144+
# All layers are in the GHA cache from step 1 β€” this is just an upload.
145+
- name: πŸš€ Push to registries
146+
if: inputs.push
147+
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2
148+
with:
149+
context: ${{ inputs.context }}
150+
file: ${{ inputs.dockerfile }}
151+
load: false
152+
push: true
153+
tags: ${{ steps.meta.outputs.tags }}
154+
labels: ${{ steps.meta.outputs.labels }}
155+
build-args: |
156+
SOCKET_BASICS_VERSION=${{ inputs.version }}
157+
VCS_REF=${{ github.sha }}
158+
BUILD_DATE=${{ github.event.repository.updated_at }}
159+
cache-from: type=gha,scope=${{ inputs.name }}
160+
provenance: true
161+
sbom: true
162+
163+
# Floating major version tags (v2 β†’ latest v2.x.y) have been intentionally
164+
# removed. Mutable tags are structurally equivalent to :latest and are
165+
# inappropriate for a security tool. Users should pin to an immutable
166+
# version tag or digest and use Dependabot to manage upgrades.

0 commit comments

Comments
Β (0)