Skip to content

Commit f303b0c

Browse files
Update template_update.yml
1 parent 3de5530 commit f303b0c

1 file changed

Lines changed: 185 additions & 153 deletions

File tree

.github/workflows/template_update.yml

Lines changed: 185 additions & 153 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ on:
1111
description: 'Branch of the project repo to update'
1212
required: true
1313
type: string
14+
update_workflows:
15+
description: 'Include .github/workflows updates from template?'
16+
required: false
17+
type: boolean
18+
default: false
1419

1520
permissions:
1621
contents: write
@@ -25,164 +30,191 @@ jobs:
2530
new_sha: ${{ steps.new_sha.outputs.sha }}
2631

2732
steps:
28-
- name: Checkout project repo
29-
uses: actions/checkout@v4
30-
with:
31-
repository: ${{ github.repository }}
32-
ref: ${{ inputs.repo_branch }}
33-
sparse-checkout: .cookiecutter.json
34-
sparse-checkout-cone-mode: false
35-
36-
- name: Read old template SHA
37-
id: old_sha
38-
run: |
39-
SHA=$(jq -r '.cookiecutter.template_sha // empty' .cookiecutter.json)
40-
if [ -z "$SHA" ]; then
41-
echo "::error ::.cookiecutter.json is missing cookiecutter.template_sha"
42-
exit 1
43-
fi
44-
echo "sha=$SHA" >> "$GITHUB_OUTPUT"
45-
46-
- name: Fetch new template SHA
47-
id: new_sha
48-
run: |
49-
echo "sha=$(git ls-remote '${{ inputs.template_repo }}' HEAD | cut -f1)" >> "$GITHUB_OUTPUT"
50-
51-
- name: Compare SHAs
52-
id: compare
53-
run: |
54-
if [ "${{ steps.old_sha.outputs.sha }}" = "${{ steps.new_sha.outputs.sha }}" ]; then
55-
echo "✂️ SHA unchanged; nothing to update."
56-
echo "sha_changed=false" >> "$GITHUB_OUTPUT"
57-
else
58-
echo "sha_changed=true" >> "$GITHUB_OUTPUT"
59-
fi
33+
- name: Checkout project repo
34+
uses: actions/checkout@v4
35+
with:
36+
repository: ${{ github.repository }}
37+
ref: ${{ inputs.repo_branch }}
38+
sparse-checkout: .cookiecutter.json
39+
sparse-checkout-cone-mode: false
40+
41+
- name: Read old template SHA
42+
id: old_sha
43+
run: |
44+
SHA=$(jq -r '.cookiecutter.template_sha // empty' .cookiecutter.json)
45+
if [ -z "$SHA" ]; then
46+
echo "::error ::.cookiecutter.json is missing cookiecutter.template_sha"
47+
exit 1
48+
fi
49+
echo "sha=$SHA" >> "$GITHUB_OUTPUT"
50+
51+
- name: Fetch new template SHA
52+
id: new_sha
53+
run: |
54+
echo "sha=$(git ls-remote '${{ inputs.template_repo }}' HEAD | cut -f1)" >> "$GITHUB_OUTPUT"
55+
56+
- name: Compare SHAs
57+
id: compare
58+
run: |
59+
if [ "${{ steps.old_sha.outputs.sha }}" = "${{ steps.new_sha.outputs.sha }}" ]; then
60+
echo "✂️ SHA unchanged; nothing to update."
61+
echo "sha_changed=false" >> "$GITHUB_OUTPUT"
62+
else
63+
echo "sha_changed=true" >> "$GITHUB_OUTPUT"
64+
fi
6065
6166
template-update:
6267
needs: check-update
6368
if: needs.check-update.outputs.sha_changed == 'true'
6469
runs-on: ubuntu-latest
6570

6671
steps:
67-
- name: Checkout project repo
68-
uses: actions/checkout@v4
69-
with:
70-
repository: ${{ github.repository }}
71-
ref: ${{ inputs.repo_branch }}
72-
fetch-depth: 0
73-
submodules: false
74-
75-
- name: Set up Python
76-
uses: actions/setup-python@v5
77-
with:
78-
python-version: '3.x'
79-
80-
- name: Install dependencies
81-
run: pip install cookiecutter
82-
83-
- name: Check if update branch already exists
84-
run: |
85-
BRANCH="template-update-${{ needs.check-update.outputs.new_sha }}"
86-
if git ls-remote --exit-code --heads origin "$BRANCH" >/dev/null 2>&1; then
87-
echo "::error ::Branch '$BRANCH' already exists. A template update PR may already be open."
88-
exit 1
89-
fi
90-
91-
- name: Clone template at new SHA
92-
run: git clone '${{ inputs.template_repo }}' /tmp/template-source
93-
94-
- name: Run cookiecutter replay
95-
run: |
96-
rm -rf ~/.cookiecutters/*
97-
cookiecutter /tmp/template-source \
98-
--replay-file .cookiecutter.json \
99-
--overwrite-if-exists \
100-
--output-dir /tmp/rendered
101-
102-
- name: Get rendered project directory name
103-
id: rendered_dir
104-
run: |
105-
DIR=$(ls /tmp/rendered)
106-
echo "name=$DIR" >> "$GITHUB_OUTPUT"
107-
108-
- name: Create template branch and merge
109-
id: merge
110-
shell: bash
111-
env:
112-
GH_TOKEN: ${{ secrets.token || github.token }}
113-
run: |
114-
set -euo pipefail
115-
116-
RENDERED="/tmp/rendered/${{ steps.rendered_dir.outputs.name }}"
117-
OLD_SHA="${{ needs.check-update.outputs.old_sha }}"
118-
NEW_SHA="${{ needs.check-update.outputs.new_sha }}"
119-
BRANCH="template-update-${NEW_SHA}"
120-
121-
git config user.name "GitHub Actions Bot"
122-
git config user.email "actions@github.com"
123-
124-
# Save current branch name
125-
CURRENT_BRANCH="${{ inputs.repo_branch }}"
126-
127-
# Create an orphan branch with the rendered template content
128-
git checkout --orphan template-rendered
129-
git rm -rf --cached .
130-
rm -rf $(ls -A | grep -v '^\.git$')
131-
132-
# Copy rendered content
133-
cp -r "$RENDERED"/. .
134-
135-
# Preserve .cookiecutter.json from original branch with updated SHA
136-
git show "${CURRENT_BRANCH}:.cookiecutter.json" > .cookiecutter.json
137-
jq ".cookiecutter.template_sha = \"${NEW_SHA}\"" \
138-
.cookiecutter.json > tmp.json && mv tmp.json .cookiecutter.json
139-
140-
git add -A
141-
git commit -m "Template render at ${NEW_SHA}"
142-
143-
# Save the commit SHA of template-rendered
144-
TEMPLATE_COMMIT=$(git rev-parse HEAD)
145-
146-
# Switch back to repo branch and create update branch
147-
git checkout "${CURRENT_BRANCH}"
148-
git checkout -b "$BRANCH"
149-
150-
# Merge using the commit SHA instead of branch name
151-
if git merge "$TEMPLATE_COMMIT" --allow-unrelated-histories --no-edit -m "chore: update from template ${OLD_SHA} → ${NEW_SHA}"; then
152-
echo "merge_had_conflicts=false" >> "$GITHUB_OUTPUT"
153-
else
154-
echo "::warning ::Merge had conflicts. They will be included in the PR for manual resolution."
155-
echo "merge_had_conflicts=true" >> "$GITHUB_OUTPUT"
72+
- name: Checkout project repo
73+
uses: actions/checkout@v4
74+
with:
75+
repository: ${{ github.repository }}
76+
ref: ${{ inputs.repo_branch }}
77+
fetch-depth: 0
78+
submodules: false
79+
80+
- name: Capture base SHA
81+
shell: bash
82+
run: |
83+
echo "BASE_SHA=$(git rev-parse HEAD)" >> "$GITHUB_ENV"
84+
85+
- name: Set up Python
86+
uses: actions/setup-python@v5
87+
with:
88+
python-version: '3.x'
89+
90+
- name: Install dependencies
91+
run: pip install cookiecutter
92+
93+
- name: Check if update branch already exists
94+
run: |
95+
BRANCH="template-update-${{ needs.check-update.outputs.new_sha }}"
96+
if git ls-remote --exit-code --heads origin "$BRANCH" >/dev/null 2>&1; then
97+
echo "::error ::Branch '$BRANCH' already exists. A template update PR may already be open."
98+
exit 1
99+
fi
100+
101+
- name: Clone template at new SHA
102+
run: git clone '${{ inputs.template_repo }}' /tmp/template-source
103+
104+
- name: Run cookiecutter replay
105+
run: |
106+
rm -rf ~/.cookiecutters/*
107+
cookiecutter /tmp/template-source \
108+
--replay-file .cookiecutter.json \
109+
--overwrite-if-exists \
110+
--output-dir /tmp/rendered
111+
112+
- name: Get rendered project directory name
113+
id: rendered_dir
114+
run: |
115+
DIR=$(ls /tmp/rendered)
116+
echo "name=$DIR" >> "$GITHUB_OUTPUT"
117+
118+
- name: Create template branch, merge, and push
119+
id: merge
120+
shell: bash
121+
env:
122+
GH_TOKEN: ${{ secrets.WORKFLOW_TOKEN || github.token }}
123+
run: |
124+
set -euo pipefail
125+
126+
RENDERED="/tmp/rendered/${{ steps.rendered_dir.outputs.name }}"
127+
OLD_SHA="${{ needs.check-update.outputs.old_sha }}"
128+
NEW_SHA="${{ needs.check-update.outputs.new_sha }}"
129+
BRANCH="template-update-${NEW_SHA}"
130+
131+
git config user.name "GitHub Actions Bot"
132+
git config user.email "actions@github.com"
133+
134+
CURRENT_BRANCH="${{ inputs.repo_branch }}"
135+
136+
# Create an orphan branch with the rendered template content
137+
git checkout --orphan template-rendered
138+
git rm -rf --cached . >/dev/null 2>&1 || true
139+
rm -rf $(ls -A | grep -v '^\.git$') || true
140+
141+
# Copy rendered content
142+
cp -r "$RENDERED"/. .
143+
144+
# Preserve .cookiecutter.json from original branch with updated SHA
145+
git show "${CURRENT_BRANCH}:.cookiecutter.json" > .cookiecutter.json
146+
jq ".cookiecutter.template_sha = \"${NEW_SHA}\"" \
147+
.cookiecutter.json > tmp.json && mv tmp.json .cookiecutter.json
148+
156149
git add -A
157-
git commit -m "chore: update from template ${OLD_SHA} → ${NEW_SHA} (with conflicts)"
158-
fi
159-
160-
if ! git remote get-url origin >/dev/null 2>&1; then
161-
git remote add origin "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
162-
fi
163-
164-
git push origin "$BRANCH"
165-
echo "branch=$BRANCH" >> "$GITHUB_OUTPUT"
166-
167-
- name: Create pull request
168-
env:
169-
GITHUB_TOKEN: ${{ secrets.token || github.token }}
170-
run: |
171-
OLD_SHA="${{ needs.check-update.outputs.old_sha }}"
172-
NEW_SHA="${{ needs.check-update.outputs.new_sha }}"
173-
BRANCH="${{ steps.merge.outputs.branch }}"
174-
MERGE_HAD_CONFLICTS="${{ steps.merge.outputs.merge_had_conflicts }}"
175-
176-
if [ "$MERGE_HAD_CONFLICTS" = "true" ]; then
177-
BODY="Updates from template SHA \`${OLD_SHA}\` → \`${NEW_SHA}\`
178-
179-
⚠️ **Merge conflicts detected**: This PR contains conflict markers that need manual resolution. Search for \`<<<<<<<\` in the changed files."
180-
else
181-
BODY="Updates from template SHA \`${OLD_SHA}\` → \`${NEW_SHA}\`"
182-
fi
183-
184-
gh pr create \
185-
--title "chore: update from template ${OLD_SHA} → ${NEW_SHA}" \
186-
--body "$BODY" \
187-
--base '${{ inputs.repo_branch }}' \
188-
--head "$BRANCH"
150+
git commit -m "Template render at ${NEW_SHA}"
151+
152+
TEMPLATE_COMMIT=$(git rev-parse HEAD)
153+
154+
# Switch back to repo branch and create update branch
155+
git checkout "${CURRENT_BRANCH}"
156+
git checkout -b "$BRANCH"
157+
158+
MERGE_HAD_CONFLICTS=false
159+
160+
# Attempt the merge; if it fails, keep conflict markers in working tree
161+
if git merge "$TEMPLATE_COMMIT" --allow-unrelated-histories --no-edit -m "chore: update from template ${OLD_SHA} → ${NEW_SHA}"; then
162+
MERGE_HAD_CONFLICTS=false
163+
else
164+
echo "::warning ::Merge had conflicts. They will be included in the PR for manual resolution."
165+
MERGE_HAD_CONFLICTS=true
166+
fi
167+
168+
# Conditionally revert .github/workflows back to base (omit workflow updates)
169+
if [ "${{ inputs.update_workflows }}" != "true" ]; then
170+
echo "update_workflows=false → reverting .github/workflows to base commit: $BASE_SHA"
171+
if [ -d ".github/workflows" ]; then
172+
git restore --source="$BASE_SHA" --staged --worktree .github/workflows || true
173+
fi
174+
fi
175+
176+
# If merge had conflicts, commit AFTER optional workflow revert
177+
if [ "$MERGE_HAD_CONFLICTS" = "true" ]; then
178+
git add -A
179+
git commit -m "chore: update from template ${OLD_SHA} → ${NEW_SHA} (with conflicts)"
180+
else
181+
# Merge succeeded and created a merge commit already.
182+
# If we reverted workflows (or made any post-merge edits), commit those changes.
183+
if ! git diff --cached --quiet; then
184+
git add -A
185+
git commit -m "chore: omit workflow updates"
186+
fi
187+
fi
188+
189+
# Ensure origin exists
190+
if ! git remote get-url origin >/dev/null 2>&1; then
191+
git remote add origin "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
192+
fi
193+
194+
git push origin "$BRANCH"
195+
196+
echo "branch=$BRANCH" >> "$GITHUB_OUTPUT"
197+
echo "merge_had_conflicts=$MERGE_HAD_CONFLICTS" >> "$GITHUB_OUTPUT"
198+
199+
- name: Create pull request
200+
env:
201+
GITHUB_TOKEN: ${{ secrets.WORKFLOW_TOKEN || github.token }}
202+
run: |
203+
OLD_SHA="${{ needs.check-update.outputs.old_sha }}"
204+
NEW_SHA="${{ needs.check-update.outputs.new_sha }}"
205+
BRANCH="${{ steps.merge.outputs.branch }}"
206+
MERGE_HAD_CONFLICTS="${{ steps.merge.outputs.merge_had_conflicts }}"
207+
208+
if [ "$MERGE_HAD_CONFLICTS" = "true" ]; then
209+
BODY="Updates from template SHA \`${OLD_SHA}\` → \`${NEW_SHA}\`
210+
211+
⚠️ **Merge conflicts detected**: This PR contains conflict markers that need manual resolution. Search for \`<<<<<<<\` in the changed files."
212+
else
213+
BODY="Updates from template SHA \`${OLD_SHA}\` → \`${NEW_SHA}\`"
214+
fi
215+
216+
gh pr create \
217+
--title "chore: update from template ${OLD_SHA} → ${NEW_SHA}" \
218+
--body "$BODY" \
219+
--base '${{ inputs.repo_branch }}' \
220+
--head "$BRANCH"

0 commit comments

Comments
 (0)