Skip to content

Commit ceff423

Browse files
Seldaekalcohol
authored andcommitted
Harden workflows to pass zizmor (pedantic)
- Pin all actions (docker/*, actions/*) to commit SHAs at their latest releases - Add explicit permissions: workflow-level contents: read; packages: write on the build and merge jobs (ghcr push via GITHUB_TOKEN) - Add concurrency limits and persist-credentials: false on checkouts - Pass matrix/env/step values via step env to avoid template injection, and use jq --arg for the registry slugs in the manifest-merge steps - determine-composer-version action: emit step outputs instead of writing to $GITHUB_ENV (avoids the github-env code-execution vector) - Give all jobs explicit names
1 parent dbe9a87 commit ceff423

5 files changed

Lines changed: 223 additions & 124 deletions

File tree

.github/actions/determine-composer-version/action.yaml

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,37 @@
44

55
name: "Determine composer version from Dockerfile"
66

7-
description: "Determines the composer version from a Dockerfile and exports it as COMPOSER_VERSION_MAJOR, COMPOSER_VERSION_MAJOR_MINOR, and COMPOSER_VERSION_MAJOR_MINOR_PATCH environment variables."
7+
description: "Determines the composer version from a Dockerfile and exposes it as major, major-minor, and major-minor-patch step outputs."
88

99
inputs:
1010
working-directory:
1111
default: "."
1212
description: "Which directory to use as working directory"
1313
required: true
1414

15+
outputs:
16+
major:
17+
description: "Composer major version (e.g. 2)"
18+
value: ${{ steps.determine.outputs.major }}
19+
major-minor:
20+
description: "Composer major.minor version (e.g. 2.8)"
21+
value: ${{ steps.determine.outputs.major-minor }}
22+
major-minor-patch:
23+
description: "Composer major.minor.patch version (e.g. 2.8.1)"
24+
value: ${{ steps.determine.outputs.major-minor-patch }}
25+
1526
runs:
1627
using: "composite"
1728

1829
steps:
1930
- name: "Determine composer version from Dockerfile"
31+
id: determine
2032
run: |
21-
echo "COMPOSER_VERSION_MAJOR=$(grep -oP 'ENV COMPOSER_VERSION=\K\d+\.\d+\.\d+(-RC\d+)?' Dockerfile | cut -d '.' -f 1)" >> $GITHUB_ENV
22-
echo "COMPOSER_VERSION_MAJOR_MINOR=$(grep -oP 'ENV COMPOSER_VERSION=\K\d+\.\d+\.\d+(-RC\d+)?' Dockerfile | cut -d '.' -f 1,2)" >> $GITHUB_ENV
23-
echo "COMPOSER_VERSION_MAJOR_MINOR_PATCH=$(grep -oP 'ENV COMPOSER_VERSION=\K\d+\.\d+\.\d+(-RC\d+)?' Dockerfile)" >> $GITHUB_ENV
33+
version="$(grep -oP 'ENV COMPOSER_VERSION=\K\d+\.\d+\.\d+(-RC\d+)?' Dockerfile)"
34+
{
35+
echo "major=$(echo "$version" | cut -d '.' -f 1)"
36+
echo "major-minor=$(echo "$version" | cut -d '.' -f 1,2)"
37+
echo "major-minor-patch=$version"
38+
} >> "$GITHUB_OUTPUT"
2439
shell: "bash"
2540
working-directory: "${{ inputs.working-directory }}"

.github/workflows/1.10.yaml

Lines changed: 64 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,17 @@ env:
2020
GHCR_SLUG: ghcr.io/composer/docker
2121
DIRECTORY: '1.10'
2222

23+
permissions:
24+
contents: read
25+
26+
concurrency:
27+
group: ${{ github.workflow }}-${{ github.ref }}
28+
cancel-in-progress: true
29+
2330
jobs:
2431

2532
prepare:
33+
name: "Prepare metadata"
2634

2735
runs-on: ubuntu-latest
2836

@@ -37,16 +45,19 @@ jobs:
3745
steps:
3846

3947
- name: Checkout
40-
uses: actions/checkout@v6
48+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
49+
with:
50+
persist-credentials: false
4151

4252
- name: Determine Composer version from Dockerfile
53+
id: composer-version
4354
uses: ./.github/actions/determine-composer-version
4455
with:
4556
working-directory: ${{ env.DIRECTORY }}
4657

4758
- name: Docker metadata (full image)
4859
id: meta-full
49-
uses: docker/metadata-action@v6
60+
uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0
5061
with:
5162
images: |
5263
${{ env.DOCKERHUB_SLUG }}
@@ -55,16 +66,16 @@ jobs:
5566
flavor: |
5667
latest=false
5768
tags: |
58-
type=semver,pattern={{version}},value=${{ env.COMPOSER_VERSION_MAJOR_MINOR_PATCH }}
59-
type=semver,pattern={{major}}.{{minor}},value=${{ env.COMPOSER_VERSION_MAJOR_MINOR_PATCH }}
69+
type=semver,pattern={{version}},value=${{ steps.composer-version.outputs.major-minor-patch }}
70+
type=semver,pattern={{major}}.{{minor}},value=${{ steps.composer-version.outputs.major-minor-patch }}
6071
labels: |
6172
org.opencontainers.image.vendor=Composer
6273
org.opencontainers.image.title=Composer
6374
org.opencontainers.image.description=PHP runtime image with Composer
6475
6576
- name: Docker metadata (binary-only image)
6677
id: meta-bin
67-
uses: docker/metadata-action@v6
78+
uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0
6879
with:
6980
images: |
7081
${{ env.DOCKERHUB_SLUG }}
@@ -74,18 +85,23 @@ jobs:
7485
latest=false
7586
suffix=-bin
7687
tags: |
77-
type=semver,pattern={{version}},value=${{ env.COMPOSER_VERSION_MAJOR_MINOR_PATCH }}
78-
type=semver,pattern={{major}}.{{minor}},value=${{ env.COMPOSER_VERSION_MAJOR_MINOR_PATCH }}
88+
type=semver,pattern={{version}},value=${{ steps.composer-version.outputs.major-minor-patch }}
89+
type=semver,pattern={{major}}.{{minor}},value=${{ steps.composer-version.outputs.major-minor-patch }}
7990
labels: |
8091
org.opencontainers.image.vendor=Composer
8192
org.opencontainers.image.title=Composer
8293
org.opencontainers.image.description=Image with Composer binary only
8394
8495
build:
96+
name: "Build images"
8597

8698
runs-on: ubuntu-latest
8799
timeout-minutes: 20
88100

101+
permissions:
102+
contents: read
103+
packages: write # pushes image layers to ghcr.io via GITHUB_TOKEN
104+
89105
needs:
90106
- prepare
91107

@@ -108,47 +124,50 @@ jobs:
108124
steps:
109125

110126
- name: Checkout
111-
uses: actions/checkout@v6
127+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
128+
with:
129+
persist-credentials: false
112130

113131
- name: Prepare
132+
env:
133+
PLATFORM: ${{ matrix.platform }}
114134
run: |
115-
platform=${{ matrix.platform }}
116-
echo "PLATFORM_SLUG=${platform//\//-}" >> $GITHUB_ENV
135+
echo "PLATFORM_SLUG=${PLATFORM//\//-}" >> "$GITHUB_ENV"
117136
118137
- name: Set up QEMU
119-
uses: docker/setup-qemu-action@v4
138+
uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0
120139
with:
121140
platforms: ${{ matrix.platform }}
122141

123142
- name: Set up Docker Buildx
124-
uses: docker/setup-buildx-action@v4
143+
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
125144

126145
- name: Login to Docker Hub
127146
if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
128-
uses: docker/login-action@v4
147+
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
129148
with:
130149
username: ${{ secrets.DOCKERHUB_USERNAME }}
131150
password: ${{ secrets.DOCKERHUB_PASSWORD }}
132151

133152
- name: Login to Amazon Public ECR
134153
if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
135-
uses: docker/login-action@v4
154+
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
136155
with:
137156
registry: public.ecr.aws
138157
username: ${{ secrets.AWS_ECR_ACCESS_KEY }}
139158
password: ${{ secrets.AWS_ECR_SECRET_KEY }}
140159

141160
- name: Login to Github Container Registry
142161
if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
143-
uses: docker/login-action@v4
162+
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
144163
with:
145164
registry: ghcr.io
146165
username: ${{ github.repository_owner }}
147166
password: ${{ secrets.GITHUB_TOKEN }}
148167

149168
- name: Build full image
150169
id: build-full
151-
uses: docker/build-push-action@v7
170+
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
152171
with:
153172
context: ${{ env.DIRECTORY }}
154173
target: binary-with-runtime
@@ -158,7 +177,7 @@ jobs:
158177

159178
- name: Build binary-only image
160179
id: build-bin
161-
uses: docker/build-push-action@v7
180+
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
162181
with:
163182
context: ${{ env.DIRECTORY }}
164183
target: standalone-binary
@@ -167,15 +186,16 @@ jobs:
167186
outputs: type=image,"name=${{ env.DOCKERHUB_SLUG }},${{ env.ECR_SLUG }},${{ env.GHCR_SLUG }}",push-by-digest=true,name-canonical=true,push=${{ github.ref == 'refs/heads/main' && github.event_name != 'pull_request' }}
168187

169188
- name: Export digests
189+
env:
190+
DIGEST_FULL: ${{ steps.build-full.outputs.digest }}
191+
DIGEST_BIN: ${{ steps.build-bin.outputs.digest }}
170192
run: |
171193
mkdir -p /tmp/digests/{full,bin}
172-
digest="${{ steps.build-full.outputs.digest }}"
173-
touch "/tmp/digests/full/${digest#sha256:}"
174-
digest="${{ steps.build-bin.outputs.digest }}"
175-
touch "/tmp/digests/bin/${digest#sha256:}"
194+
touch "/tmp/digests/full/${DIGEST_FULL#sha256:}"
195+
touch "/tmp/digests/bin/${DIGEST_BIN#sha256:}"
176196
177197
- name: Upload digests
178-
uses: actions/upload-artifact@v7
198+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
179199
with:
180200
name: digests-${{ env.PLATFORM_SLUG }}
181201
path: |
@@ -185,42 +205,47 @@ jobs:
185205
retention-days: 1
186206

187207
merge:
208+
name: "Merge & push manifests"
188209

189210
if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
190211

191212
runs-on: ubuntu-latest
192213

214+
permissions:
215+
contents: read
216+
packages: write # pushes the manifest list to ghcr.io via GITHUB_TOKEN
217+
193218
needs:
194219
- prepare
195220
- build
196221

197222
steps:
198223

199224
- name: Download digests
200-
uses: actions/download-artifact@v8
225+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
201226
with:
202227
path: /tmp/digests
203228
pattern: digests-*
204229
merge-multiple: true
205230

206231
- name: Set up Docker Buildx
207-
uses: docker/setup-buildx-action@v4
232+
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
208233

209234
- name: Login to Docker Hub
210-
uses: docker/login-action@v4
235+
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
211236
with:
212237
username: ${{ secrets.DOCKERHUB_USERNAME }}
213238
password: ${{ secrets.DOCKERHUB_PASSWORD }}
214239

215240
- name: Login to Amazon Public ECR
216-
uses: docker/login-action@v4
241+
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
217242
with:
218243
registry: public.ecr.aws
219244
username: ${{ secrets.AWS_ECR_ACCESS_KEY }}
220245
password: ${{ secrets.AWS_ECR_SECRET_KEY }}
221246

222247
- name: Login to Github Container Registry
223-
uses: docker/login-action@v4
248+
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
224249
with:
225250
registry: ghcr.io
226251
username: ${{ github.repository_owner }}
@@ -231,21 +256,21 @@ jobs:
231256
env:
232257
JSON: ${{ needs.prepare.outputs.full-json }}
233258
run: |
234-
docker buildx imagetools create $(jq -cr '.tags | map(select(startswith("${{ env.DOCKERHUB_SLUG }}")) | "--tag " + .) | join(" ")' <<< "$JSON") \
235-
$(printf '${{ env.DOCKERHUB_SLUG }}@sha256:%s ' *)
236-
docker buildx imagetools create $(jq -cr '.tags | map(select(startswith("${{ env.ECR_SLUG }}")) | "--tag " + .) | join(" ")' <<< "$JSON") \
237-
$(printf '${{ env.ECR_SLUG }}@sha256:%s ' *)
238-
docker buildx imagetools create $(jq -cr '.tags | map(select(startswith("${{ env.GHCR_SLUG }}")) | "--tag " + .) | join(" ")' <<< "$JSON") \
239-
$(printf '${{ env.GHCR_SLUG }}@sha256:%s ' *)
259+
docker buildx imagetools create $(jq -cr --arg slug "$DOCKERHUB_SLUG" '.tags | map(select(startswith($slug)) | "--tag " + .) | join(" ")' <<< "$JSON") \
260+
$(printf "$DOCKERHUB_SLUG@sha256:%s " *)
261+
docker buildx imagetools create $(jq -cr --arg slug "$ECR_SLUG" '.tags | map(select(startswith($slug)) | "--tag " + .) | join(" ")' <<< "$JSON") \
262+
$(printf "$ECR_SLUG@sha256:%s " *)
263+
docker buildx imagetools create $(jq -cr --arg slug "$GHCR_SLUG" '.tags | map(select(startswith($slug)) | "--tag " + .) | join(" ")' <<< "$JSON") \
264+
$(printf "$GHCR_SLUG@sha256:%s " *)
240265
241266
- name: Create manifest list for binary-only image and push
242267
working-directory: /tmp/digests/bin
243268
env:
244269
JSON: ${{ needs.prepare.outputs.bin-json }}
245270
run: |
246-
docker buildx imagetools create $(jq -cr '.tags | map(select(startswith("${{ env.DOCKERHUB_SLUG }}")) | "--tag " + .) | join(" ")' <<< "$JSON") \
247-
$(printf '${{ env.DOCKERHUB_SLUG }}@sha256:%s ' *)
248-
docker buildx imagetools create $(jq -cr '.tags | map(select(startswith("${{ env.ECR_SLUG }}")) | "--tag " + .) | join(" ")' <<< "$JSON") \
249-
$(printf '${{ env.ECR_SLUG }}@sha256:%s ' *)
250-
docker buildx imagetools create $(jq -cr '.tags | map(select(startswith("${{ env.GHCR_SLUG }}")) | "--tag " + .) | join(" ")' <<< "$JSON") \
251-
$(printf '${{ env.GHCR_SLUG }}@sha256:%s ' *)
271+
docker buildx imagetools create $(jq -cr --arg slug "$DOCKERHUB_SLUG" '.tags | map(select(startswith($slug)) | "--tag " + .) | join(" ")' <<< "$JSON") \
272+
$(printf "$DOCKERHUB_SLUG@sha256:%s " *)
273+
docker buildx imagetools create $(jq -cr --arg slug "$ECR_SLUG" '.tags | map(select(startswith($slug)) | "--tag " + .) | join(" ")' <<< "$JSON") \
274+
$(printf "$ECR_SLUG@sha256:%s " *)
275+
docker buildx imagetools create $(jq -cr --arg slug "$GHCR_SLUG" '.tags | map(select(startswith($slug)) | "--tag " + .) | join(" ")' <<< "$JSON") \
276+
$(printf "$GHCR_SLUG@sha256:%s " *)

0 commit comments

Comments
 (0)