-
Notifications
You must be signed in to change notification settings - Fork 2
175 lines (157 loc) · 6.47 KB
/
Copy pathrelease.yaml
File metadata and controls
175 lines (157 loc) · 6.47 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
name: Release
on:
push:
tags:
- "v*"
# Least privilege by default; the image job widens to what it needs.
permissions:
contents: read
jobs:
image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write # push to ghcr
id-token: write # keyless cosign signing via GitHub OIDC
steps:
- uses: actions/checkout@v7
- uses: docker/setup-qemu-action@v4
- uses: docker/setup-buildx-action@v4
- uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Docker metadata (tags + OCI labels/annotations)
id: meta
uses: docker/metadata-action@v6
with:
images: ghcr.io/${{ github.repository }}
# {{version}} strips the leading "v" (v0.1.0 -> 0.1.0) to match Chart.AppVersion;
# {{major}}.{{minor}} -> 0.1. flavor latest=auto (default) adds :latest for
# non-prerelease semver tags. Emits org.opencontainers.image.* labels (source,
# revision, version, created, licenses) so ghcr links the package to this repo.
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
env:
DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index
- name: Build + push multi-arch
id: build
uses: docker/build-push-action@v7
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
annotations: ${{ steps.meta.outputs.annotations }}
# Stamp the binary so its /api/version matches the OCI image.version label + chart appVersion.
build-args: |
KNOT_VERSION=${{ steps.meta.outputs.version }}
KNOT_COMMIT=${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
# Supply-chain attestations attached to the image manifest in ghcr.
provenance: mode=max
sbom: true
- name: Install cosign
# Deliberately held at v3 (cosign 2.x) while every other action here
# moved to its current major. v4 installs cosign 3.x, which writes a
# different signature bundle format — that is a change consumers feel,
# not just CI, and anyone verifying knot images with a cosign 2.x
# verifier would start failing. Bump once downstream verifiers are
# known to handle cosign 3 bundles.
uses: sigstore/cosign-installer@v3
- name: Sign image (keyless)
# Sign by digest so the signature covers every tag that points at this build.
env:
DIGEST: ${{ steps.build.outputs.digest }}
run: cosign sign --yes "ghcr.io/${{ github.repository }}@${DIGEST}"
chart:
runs-on: ubuntu-latest
needs: image
permissions:
contents: read
packages: write # push the OCI chart to ghcr
steps:
- uses: actions/checkout@v7
- name: Set up Helm
uses: azure/setup-helm@v5
with:
version: v3.21.4
- name: Package + push chart to ghcr (OCI)
# Both the chart version and appVersion are forced to the stripped git tag so a release
# publishes exactly the chart+image this tag built (the version baked into Chart.yaml is
# only a placeholder for local dev). Lands at oci://ghcr.io/<owner>/charts/knot:<tag>.
env:
REF: ${{ github.ref_name }}
run: |
VERSION="${REF#v}" # v0.1.0 -> 0.1.0
helm registry login ghcr.io -u "${{ github.actor }}" -p "${{ secrets.GITHUB_TOKEN }}"
mkdir -p dist
helm package deploy/helm/knot --version "$VERSION" --app-version "$VERSION" -d dist
helm push dist/knot-*.tgz "oci://ghcr.io/${{ github.repository_owner }}/charts"
release:
runs-on: ubuntu-latest
needs: [image, chart]
permissions:
contents: write # create the GitHub Release
steps:
- uses: actions/checkout@v7
- name: Derive versions
id: v
run: |
# Image and chart are both published under the stripped git tag (see the chart job).
echo "image=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
echo "chart=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
- name: Build the release body
env:
VERSION: ${{ steps.v.outputs.image }}
CHART: ${{ steps.v.outputs.chart }}
REPO: ${{ github.repository }}
OWNER: ${{ github.repository_owner }}
REF: ${{ github.ref_name }}
run: |
set -euo pipefail
# Lift this tag's section out of CHANGELOG.md so the release page shows
# the hand-written notes rather than only the generated commit list.
# Written as one file because action-gh-release takes `body` OR
# `body_path`, not both.
awk -v v="$VERSION" '
$0 ~ "^## \\[" v "\\]" { found = 1; next }
found && /^## \[/ { exit }
found { print }
' CHANGELOG.md | sed '/./,$!d' > /tmp/notes.md
if [ ! -s /tmp/notes.md ]; then
# Not fatal — the artifacts still matter — but it means the tag was
# cut without a changelog entry, which is worth surfacing.
echo "::warning::no CHANGELOG.md section for $VERSION"
printf 'See [CHANGELOG.md](https://github.com/%s/blob/%s/CHANGELOG.md).\n' \
"$REPO" "$REF" > /tmp/notes.md
fi
{
cat /tmp/notes.md
cat <<EOF
---
### Container image
\`ghcr.io/$REPO:$VERSION\` — multi-arch (amd64/arm64), cosign-signed, with SBOM + SLSA provenance.
### Helm chart
\`\`\`sh
helm install knot oci://ghcr.io/$OWNER/charts/knot \\
--version $CHART \\
-n knot --create-namespace \\
--set database.url='postgres://…' \\
--set session.key="\$(openssl rand -base64 32)" \\
--set baseUrl=https://knot.example.com
\`\`\`
Full values: [chart README](https://github.com/$REPO/tree/$REF/deploy/helm/knot).
EOF
} > /tmp/release-body.md
echo "--- release body ---"
cat /tmp/release-body.md
- name: Create GitHub Release
uses: softprops/action-gh-release@v3
with:
generate_release_notes: true
body_path: /tmp/release-body.md