Skip to content

Commit 1415b20

Browse files
authored
Merge pull request #442 from posit-dev/feature/fork-safe-pr-workflow
Add fork-safe PR build workflow
2 parents 91f6e80 + 5a07624 commit 1415b20

2 files changed

Lines changed: 231 additions & 0 deletions

File tree

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
# Fork-safe PR build workflow for Posit container images.
2+
#
3+
# Unlike bakery-build-native.yml this workflow never pushes images.
4+
# Fork PRs get a read-only GITHUB_TOKEN with no access to repo secrets,
5+
# so registry logins and caching are conditional on the PR source.
6+
#
7+
# Security policy: No ${{ }} expressions in `run:` blocks.
8+
# All expression values are assigned to `env:` and referenced as
9+
# shell variables. This prevents script injection from runtime values
10+
# (matrix outputs, secrets) and keeps the rule enforceable by zizmor
11+
# without per-expression exceptions.
12+
13+
name: Bakery PR Build
14+
15+
on:
16+
workflow_call:
17+
inputs:
18+
version:
19+
description: "Bakery version to install"
20+
default: "main"
21+
required: false
22+
type: string
23+
context:
24+
description: "Path to the bakery context"
25+
default: "."
26+
required: false
27+
type: string
28+
dev-versions:
29+
description: "Whether to include development versions [default: exclude]"
30+
default: "exclude"
31+
required: false
32+
type: string
33+
matrix-versions:
34+
description: "Whether to include matrix versions [default: exclude]"
35+
default: "exclude"
36+
required: false
37+
type: string
38+
retry:
39+
description: "Number of times to retry a failed build"
40+
default: 1
41+
required: false
42+
type: number
43+
amd64-builder:
44+
description: "Runner label for amd64 builds"
45+
default: "ubuntu-latest-4x"
46+
required: false
47+
type: string
48+
arm64-builder:
49+
description: "Runner label for arm64 builds"
50+
default: "ubuntu-24.04-arm64-4-core"
51+
required: false
52+
type: string
53+
# NO secrets section — only inherited GITHUB_TOKEN
54+
55+
defaults:
56+
run:
57+
shell: bash
58+
59+
jobs:
60+
detect:
61+
name: Detect Fork
62+
runs-on: ubuntu-latest
63+
permissions: {}
64+
outputs:
65+
is-fork: ${{ steps.check.outputs.is-fork }}
66+
steps:
67+
- name: Check fork status
68+
id: check
69+
env:
70+
IS_FORK: ${{ github.event.pull_request.head.repo.fork == true }}
71+
run: echo "is-fork=$IS_FORK" >> "$GITHUB_OUTPUT"
72+
73+
matrix:
74+
name: Image Matrix
75+
runs-on: ubuntu-latest
76+
permissions:
77+
contents: read
78+
outputs:
79+
platform-matrix: ${{ steps.images-by-platform.outputs.platform_matrix }}
80+
versions-matrix: ${{ steps.images-by-version.outputs.versions_matrix }}
81+
82+
steps:
83+
- name: Checkout
84+
uses: actions/checkout@v6
85+
86+
- name: Install
87+
uses: "posit-dev/images-shared/setup-bakery@main"
88+
with:
89+
version: ${{ inputs.version }}
90+
91+
- name: Images by Version/Platform
92+
id: images-by-platform
93+
env:
94+
DEV_VERSIONS: ${{ inputs.dev-versions }}
95+
MATRIX_VERSIONS: ${{ inputs.matrix-versions }}
96+
BAKERY_CONTEXT: ${{ inputs.context }}
97+
run: |
98+
echo "platform_matrix=$(bakery ci matrix --quiet --dev-versions "$DEV_VERSIONS" --matrix-versions "$MATRIX_VERSIONS" --context "$BAKERY_CONTEXT" | jq --compact-output .)" >> "$GITHUB_OUTPUT"
99+
100+
- name: Images by Version
101+
id: images-by-version
102+
env:
103+
DEV_VERSIONS: ${{ inputs.dev-versions }}
104+
MATRIX_VERSIONS: ${{ inputs.matrix-versions }}
105+
BAKERY_CONTEXT: ${{ inputs.context }}
106+
run: |
107+
echo "versions_matrix=$(bakery ci matrix --quiet --dev-versions "$DEV_VERSIONS" --matrix-versions "$MATRIX_VERSIONS" --exclude platform --context "$BAKERY_CONTEXT" | jq --compact-output .)" >> "$GITHUB_OUTPUT"
108+
109+
build-test:
110+
name: "Build/Test ${{ matrix.img.image }}:${{ matrix.img.version }} (${{ matrix.img.platform }})"
111+
needs:
112+
- detect
113+
- matrix
114+
permissions:
115+
contents: read
116+
packages: write
117+
strategy:
118+
fail-fast: false
119+
matrix:
120+
img: ${{ fromJson(needs.matrix.outputs.platform-matrix) }}
121+
runs-on: ${{ matrix.img.platform == 'linux/arm64' && inputs.arm64-builder || inputs.amd64-builder }}
122+
# Skip arm64 for fork PRs — paid runners may not be available
123+
if: needs.detect.outputs.is-fork != 'true' || matrix.img.platform != 'linux/arm64'
124+
125+
steps:
126+
- name: Checkout
127+
uses: actions/checkout@v6
128+
129+
- name: Setup bakery
130+
uses: "posit-dev/images-shared/setup-bakery@main"
131+
with:
132+
version: ${{ inputs.version }}
133+
134+
- name: Setup goss
135+
uses: "posit-dev/images-shared/setup-goss@main"
136+
137+
- name: Set up Docker
138+
uses: docker/setup-docker-action@v5
139+
with:
140+
daemon-config: |
141+
{
142+
"features": {
143+
"containerd-snapshotter": true
144+
}
145+
}
146+
147+
- name: Setup docker buildx
148+
uses: docker/setup-buildx-action@v4
149+
150+
- name: Setup ORAS CLI
151+
uses: oras-project/setup-oras@v1
152+
153+
- name: Login to GitHub Container Registry
154+
if: needs.detect.outputs.is-fork != 'true'
155+
uses: docker/login-action@v4
156+
with:
157+
registry: ghcr.io
158+
username: ${{ github.actor }}
159+
password: ${{ secrets.GITHUB_TOKEN }}
160+
161+
- name: Normalize platform
162+
id: normalize-platform
163+
env:
164+
BUILD_PLATFORM: ${{ matrix.img.platform }}
165+
run: |
166+
PLATFORM=${BUILD_PLATFORM#linux/}
167+
echo "platform=$PLATFORM" >> "$GITHUB_OUTPUT"
168+
169+
- name: Build
170+
env:
171+
IS_FORK: ${{ needs.detect.outputs.is-fork }}
172+
GIT_SHA: ${{ github.sha }}
173+
RETRY: ${{ inputs.retry }}
174+
IMAGE_NAME: ${{ matrix.img.image }}
175+
IMAGE_VERSION: ${{ matrix.img.version }}
176+
IMAGE_PLATFORM: ${{ matrix.img.platform }}
177+
DEV_VERSIONS: ${{ inputs.dev-versions }}
178+
MATRIX_VERSIONS: ${{ inputs.matrix-versions }}
179+
BAKERY_CONTEXT: ${{ inputs.context }}
180+
REGISTRY_OWNER: ${{ github.repository_owner }}
181+
run: |
182+
CACHE_FLAGS=""
183+
if [ "$IS_FORK" != "true" ]; then
184+
CACHE_FLAGS="--cache-registry ghcr.io/${REGISTRY_OWNER}"
185+
fi
186+
bakery build \
187+
--strategy build --pull --load \
188+
--retry "$RETRY" \
189+
--image-name "^${IMAGE_NAME}$" \
190+
--image-version "$IMAGE_VERSION" \
191+
--image-platform "$IMAGE_PLATFORM" \
192+
--dev-versions "$DEV_VERSIONS" \
193+
--matrix-versions "$MATRIX_VERSIONS" \
194+
$CACHE_FLAGS \
195+
--context "$BAKERY_CONTEXT"
196+
197+
- name: Test
198+
env:
199+
IMAGE_NAME: ${{ matrix.img.image }}
200+
IMAGE_VERSION: ${{ matrix.img.version }}
201+
IMAGE_PLATFORM: ${{ matrix.img.platform }}
202+
DEV_VERSIONS: ${{ inputs.dev-versions }}
203+
MATRIX_VERSIONS: ${{ inputs.matrix-versions }}
204+
BAKERY_CONTEXT: ${{ inputs.context }}
205+
run: |
206+
GOSS_PATH=${GITHUB_WORKSPACE}/tools/goss \
207+
DGOSS_PATH=${GITHUB_WORKSPACE}/tools/dgoss \
208+
bakery run dgoss \
209+
--image-name "^${IMAGE_NAME}$" \
210+
--image-version "$IMAGE_VERSION" \
211+
--image-platform "$IMAGE_PLATFORM" \
212+
--dev-versions "$DEV_VERSIONS" \
213+
--matrix-versions "$MATRIX_VERSIONS" \
214+
--context "$BAKERY_CONTEXT"

.github/workflows/ci.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,13 @@ jobs:
2828
- test
2929
- bakery
3030
- bakery-native
31+
- bakery-pr
3132
- release
3233

3334
steps:
3435
- uses: re-actors/alls-green@release/v1
3536
with:
37+
allowed-skips: bakery-pr
3638
jobs: ${{ toJSON(needs) }}
3739

3840
test:
@@ -141,6 +143,21 @@ jobs:
141143
context: "./posit-bakery/test/resources/multiplatform/"
142144
dev-versions: include
143145

146+
bakery-pr:
147+
name: Bakery PR Build
148+
if: github.event_name == 'pull_request'
149+
permissions:
150+
contents: read
151+
packages: write
152+
153+
uses: "./.github/workflows/bakery-build-pr.yml"
154+
with:
155+
# Don't pass version — default "main" is correct here. The head_ref
156+
# pattern used by other jobs breaks on fork PRs because the fork's
157+
# branch doesn't exist in posit-dev/images-shared.
158+
context: "./posit-bakery/test/resources/multiplatform/"
159+
dev-versions: include
160+
144161
with-macros-clean-caches:
145162
name: Clean Caches (with-macros suite)
146163
permissions:

0 commit comments

Comments
 (0)