Skip to content

Commit dc75860

Browse files
committed
ci: add per-PR example publishing workflow
Add the Publish Example Branches workflow (and the extended setup-copier-template action plus its multi-resource fixture) so this PR publishes example/pr-<number>-typescript-<cloud>-<fixture> branches rendered from its own head, for reviewing the generated output. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FHtZ26sF1zNNrEh1wDGzBa
1 parent a7d7179 commit dc75860

3 files changed

Lines changed: 195 additions & 1 deletion

File tree

.github/actions/setup-copier-template/action.yaml

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,17 @@ inputs:
88
template-cloud-service:
99
required: true
1010
description: Template cloud service
11+
resources-fixture:
12+
required: false
13+
default: "single"
14+
description: >-
15+
Which resources fixture to render: "single" (the default single resource
16+
derived from the project name) or "multi" (two resources sharing one
17+
container with different operation subsets).
18+
output-dir:
19+
required: false
20+
default: "KittenClaws"
21+
description: Directory to render the generated project into.
1122

1223
runs:
1324
using: "composite"
@@ -28,6 +39,10 @@ runs:
2839

2940
- name: Create Copier Template
3041
run: |
42+
extra_args=()
43+
if [ "${{ inputs.resources-fixture }}" = "multi" ]; then
44+
extra_args+=(--data-file "${{ github.action_path }}/fixtures/multi-resources.yml")
45+
fi
3146
copier copy --defaults --trust \
3247
--data project_name="KittenClaws" \
3348
--data project_description="Kitties got claws...beware" \
@@ -38,5 +53,6 @@ runs:
3853
--data author="Sir Meowsalots" \
3954
--data cloud_service="${{ inputs.template-cloud-service }}" \
4055
--data open_source_license="MIT license" \
41-
./${{ inputs.template-language }} ./KittenClaws
56+
"${extra_args[@]}" \
57+
./${{ inputs.template-language }} ./${{ inputs.output-dir }}
4258
shell: bash
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Multi-resource showcase for the published example branches.
2+
#
3+
# Two resources sharing a single storage container ("animals") with different
4+
# operation subsets — Cat exposes PATCH (update) while Dog exposes PUT
5+
# (replace) — to demonstrate configurable endpoints, shared storage, and
6+
# per-operation method subsetting in one generated project.
7+
resources:
8+
- name: "Cat"
9+
endpoint: "cats"
10+
container: "animals"
11+
operations: ["list", "get_by_id", "create", "update", "delete"]
12+
- name: "Dog"
13+
endpoint: "dogs"
14+
container: "animals"
15+
operations: ["list", "get_by_id", "create", "replace", "delete"]
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
name: Publish Example Branches
2+
3+
# Renders template combinations (language x cloud x resources fixture) with
4+
# Copier and force-publishes each generated project to its own example branch,
5+
# so the generated output is browsable without running Copier locally.
6+
#
7+
# Branch naming:
8+
# push to main -> example/<language>-<cloud>-<fixture>
9+
# pull request -> example/pr-<number>-<language>-<cloud>-<fixture>
10+
#
11+
# On a pull request only the language(s) whose template dir changed are
12+
# rendered (so a Python PR publishes only example/pr-<n>-python-* branches);
13+
# pushes to main render all four languages.
14+
#
15+
# Full-fidelity publishing (including each generated project's own
16+
# .github/workflows/build-pipeline.yml) requires a Personal Access Token with
17+
# `repo` + `workflow` scopes stored as the `EXAMPLES_PUBLISH_TOKEN` secret. When
18+
# that secret is absent the job falls back to the default GITHUB_TOKEN, which is
19+
# not permitted to push workflow files — so the generated workflows are
20+
# relocated to `.github/workflows-example/` and a note is added.
21+
22+
on:
23+
push:
24+
branches:
25+
- main
26+
paths:
27+
- go/**
28+
- python/**
29+
- typescript/**
30+
- dotnet/**
31+
- .github/workflows/publish-examples.yml
32+
- .github/actions/setup-copier-template/**
33+
pull_request:
34+
branches:
35+
- main
36+
paths:
37+
- go/**
38+
- python/**
39+
- typescript/**
40+
- dotnet/**
41+
- .github/workflows/publish-examples.yml
42+
- .github/actions/setup-copier-template/**
43+
workflow_dispatch:
44+
45+
permissions:
46+
contents: write
47+
48+
concurrency:
49+
group: publish-examples-${{ github.ref }}
50+
cancel-in-progress: true
51+
52+
jobs:
53+
resolve:
54+
runs-on: ubuntu-latest
55+
outputs:
56+
languages: ${{ steps.pick.outputs.languages }}
57+
steps:
58+
- name: Check out
59+
uses: actions/checkout@v6
60+
with:
61+
fetch-depth: 0
62+
63+
- name: Pick languages to render
64+
id: pick
65+
env:
66+
EVENT: ${{ github.event_name }}
67+
BASE_SHA: ${{ github.event.pull_request.base.sha }}
68+
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
69+
run: |
70+
set -euo pipefail
71+
all=(go python typescript dotnet)
72+
sel=()
73+
if [ "${EVENT}" = "pull_request" ]; then
74+
changed=$(git diff --name-only "${BASE_SHA}" "${HEAD_SHA}" || true)
75+
for l in "${all[@]}"; do
76+
if printf '%s\n' "${changed}" | grep -qE "^${l}/"; then
77+
sel+=("${l}")
78+
fi
79+
done
80+
# If only shared files changed (e.g. the workflow itself), render all.
81+
if [ ${#sel[@]} -eq 0 ]; then sel=("${all[@]}"); fi
82+
else
83+
sel=("${all[@]}")
84+
fi
85+
json=$(printf '%s\n' "${sel[@]}" | jq -R . | jq -cs .)
86+
echo "languages=${json}" >> "$GITHUB_OUTPUT"
87+
echo "Selected languages: ${json}"
88+
89+
publish-examples:
90+
needs: resolve
91+
runs-on: ubuntu-latest
92+
strategy:
93+
fail-fast: false
94+
matrix:
95+
language: ${{ fromJSON(needs.resolve.outputs.languages) }}
96+
cloud:
97+
- name: "Azure Function App"
98+
slug: azure
99+
- name: "GCP Cloud Function"
100+
slug: gcp
101+
- name: "AWS Lambda"
102+
slug: aws
103+
fixture:
104+
- single
105+
- multi
106+
steps:
107+
- name: Check out
108+
uses: actions/checkout@v6
109+
with:
110+
ref: ${{ github.event.pull_request.head.sha || github.sha }}
111+
112+
- name: Render example project
113+
uses: ./.github/actions/setup-copier-template
114+
with:
115+
template-language: ${{ matrix.language }}
116+
template-cloud-service: ${{ matrix.cloud.name }}
117+
resources-fixture: ${{ matrix.fixture }}
118+
output-dir: _example
119+
120+
- name: Publish to example branch
121+
env:
122+
PUBLISH_TOKEN: ${{ secrets.EXAMPLES_PUBLISH_TOKEN || github.token }}
123+
HAS_PAT: ${{ secrets.EXAMPLES_PUBLISH_TOKEN != '' }}
124+
BRANCH: ${{ github.event_name == 'pull_request' && format('example/pr-{0}-{1}-{2}-{3}', github.event.pull_request.number, matrix.language, matrix.cloud.slug, matrix.fixture) || format('example/{0}-{1}-{2}', matrix.language, matrix.cloud.slug, matrix.fixture) }}
125+
COMBO: "${{ matrix.language }} / ${{ matrix.cloud.name }} / ${{ matrix.fixture }}"
126+
SOURCE_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
127+
run: |
128+
set -euo pipefail
129+
cd _example
130+
131+
# The default GITHUB_TOKEN cannot push files under .github/workflows/.
132+
# Without a publish PAT, relocate the generated workflows so the
133+
# example branch can still be published.
134+
if [ "${HAS_PAT}" != "true" ] && [ -d .github/workflows ]; then
135+
mkdir -p .github/workflows-example
136+
mv .github/workflows/* .github/workflows-example/
137+
rmdir .github/workflows
138+
{
139+
printf '%s\n' \
140+
'# Generated example' \
141+
'' \
142+
'Published automatically by the `Publish Example Branches` workflow.' \
143+
'' \
144+
'The generated CI lives in `.github/workflows-example/` instead of' \
145+
'`.github/workflows/` because the default Actions token cannot publish' \
146+
'workflow files. Configure an `EXAMPLES_PUBLISH_TOKEN` PAT (with `repo`' \
147+
'and `workflow` scopes) to publish these examples with full fidelity.'
148+
} > EXAMPLES_NOTE.md
149+
fi
150+
151+
git init -q
152+
git config user.name "github-actions[bot]"
153+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
154+
git checkout -q -b "${BRANCH}"
155+
git add -A
156+
git commit -q \
157+
-m "chore(examples): ${COMBO}" \
158+
-m "Generated by Copier from ${SOURCE_SHA}."
159+
git push -q --force \
160+
"https://x-access-token:${PUBLISH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" \
161+
"HEAD:refs/heads/${BRANCH}"
162+
163+
echo "Published \`${BRANCH}\`" >> "$GITHUB_STEP_SUMMARY"

0 commit comments

Comments
 (0)