@@ -21,167 +21,152 @@ jobs:
2121 runs-on : ubuntu-latest
2222
2323 steps :
24- - name : Checkout project repo
25- uses : actions/checkout@v4
26- with :
27- repository : ${{ github.repository }}
28- ref : ${{ inputs.repo_branch }}
29- fetch-depth : 0
30- submodules : false
31-
32- - name : Set up Python
33- uses : actions/setup-python@v5
34- with :
35- python-version : ' 3.x'
36-
37- - name : Install dependencies
38- run : pip install cookiecutter jq
39-
40- - name : Read old template SHA
41- id : old_sha
42- run : |
43- SHA=$(jq -r '.cookiecutter.template_sha // empty' .cookiecutter.json)
44- if [ -z "$SHA" ]; then
45- echo "::error ::.cookiecutter.json is missing cookiecutter.template_sha"
46- exit 1
47- fi
48- echo "sha=$SHA" >> "$GITHUB_OUTPUT"
49-
50- - name : Fetch new template SHA
51- id : new_sha
52- run : |
53- echo "sha=$(git ls-remote '${{ inputs.template_repo }}' HEAD | cut -f1)" >> "$GITHUB_OUTPUT"
54-
55- - name : Determine if Template was updated
56- run : |
57- if [ "${{ steps.old_sha.outputs.sha }}" = "${{ steps.new_sha.outputs.sha }}" ]; then
58- echo "SHA_CHANGED=false" >> "$GITHUB_ENV"
59- else
60- echo "SHA_CHANGED=true" >> "$GITHUB_ENV"
61- fi
62-
63- - name : Exit if SHA unchanged
64- if : env.SHA_CHANGED == 'false'
65- run : echo "✂️ SHA unchanged; nothing to update."
66-
67- - name : Clone template at OLD SHA
68- if : env.SHA_CHANGED == 'true'
69- run : |
70- git clone '${{ inputs.template_repo }}' base-template
71- git -C base-template checkout '${{ steps.old_sha.outputs.sha }}'
72-
73- - name : Clone template at NEW SHA
74- if : env.SHA_CHANGED == 'true'
75- run : git clone '${{ inputs.template_repo }}' template-source
76-
77- - name : Render OLD template (base-template → template-base)
78- if : env.SHA_CHANGED == 'true'
79- run : |
80- rm -rf ~/.cookiecutters/*
81- mkdir -p template-base
82- cookiecutter base-template \
83- --replay-file .cookiecutter.json \
84- --overwrite-if-exists \
85- --output-dir template-base
86-
87- - name : Render NEW template (template-source → template-new)
88- if : env.SHA_CHANGED == 'true'
89- run : |
90- rm -rf ~/.cookiecutters/*
91- mkdir -p template-new
92- cookiecutter template-source \
93- --replay-file .cookiecutter.json \
94- --overwrite-if-exists \
95- --output-dir template-new
96-
97- - name : Apply patch & raise PR
98- if : env.SHA_CHANGED == 'true'
99- shell : bash
100- run : |
101- diff -ruN template-base template-new > update.patch || true
102-
103- if [ ! -s update.patch ]; then
104- echo "ℹ️ No template diffs; only SHA bump will occur."
105- else
106- # 1) Pre-create target directories for NEW files (paths normalized for -p1)
107- awk '
108- /^\+\+\+ / {
109- f=$2
110- if (f == "/dev/null") next
111- # Normalize path for -p1:
112- if (f ~ /^[ab]\//) { sub(/^[ab]\//, "", f) } # git-style (a/ or b/)
113- else { sub(/^[^/]+\//, "", f) } # strip "template-new/" (or "template-base/")
114- if (match(f, /(.+)\//, m)) print m[1]
115- }
116- ' update.patch | sort -u | xargs -r mkdir -p
117-
118- # 2) Seed missing files from template-base so patch has a base to modify
119- # (If a file exists in template-base but not in the repo, copy it over.)
120- while IFS= read -r OLD; do
121- [ "$OLD" = "/dev/null" ] && continue
122- TGT="$OLD"
123- if [[ "$TGT" =~ ^[ab]/ ]]; then
124- TGT="${TGT#*/}"
125- else
126- TGT="${TGT#*/}"
127- fi
128- mkdir -p "$(dirname "$TGT")"
129- if [ ! -e "$TGT" ] && [ -f "$OLD" ]; then
130- cp -a "$OLD" "$TGT"
131- fi
132- done < <(awk '/^--- /{print $2}' update.patch)
133-
134- # 3) Apply patch non-interactively; fail on conflicts
135- if ! patch --batch -p1 < update.patch; then
136- echo "::error ::Patch could not be applied; possible conflict or missing files."
24+ - name : Checkout project repo
25+ uses : actions/checkout@v4
26+ with :
27+ repository : ${{ github.repository }}
28+ ref : ${{ inputs.repo_branch }}
29+ fetch-depth : 0
30+ submodules : false
31+
32+ - name : Set up Python
33+ uses : actions/setup-python@v5
34+ with :
35+ python-version : ' 3.x'
36+
37+ - name : Install dependencies
38+ shell : bash
39+ run : |
40+ sudo apt-get update
41+ sudo apt-get install -y jq
42+ pip install cookiecutter
43+
44+ - name : Read old template SHA
45+ id : old_sha
46+ shell : bash
47+ run : |
48+ SHA=$(jq -r '.cookiecutter.template_sha // empty' .cookiecutter.json)
49+ if [ -z "$SHA" ]; then
50+ echo "::error ::.cookiecutter.json is missing cookiecutter.template_sha"
13751 exit 1
13852 fi
53+ echo "sha=$SHA" >> "$GITHUB_OUTPUT"
54+
55+ - name : Fetch new template SHA
56+ id : new_sha
57+ shell : bash
58+ run : |
59+ echo "sha=$(git ls-remote '${{ inputs.template_repo }}' HEAD | cut -f1)" >> "$GITHUB_OUTPUT"
60+
61+ - name : Determine if Template was updated
62+ shell : bash
63+ run : |
64+ if [ "${{ steps.old_sha.outputs.sha }}" = "${{ steps.new_sha.outputs.sha }}" ]; then
65+ echo "SHA_CHANGED=false" >> "$GITHUB_ENV"
66+ else
67+ echo "SHA_CHANGED=true" >> "$GITHUB_ENV"
68+ fi
13969
140- git add -A
141- echo "✅ Applied template changes"
142- fi
143-
144- jq ".cookiecutter.template_sha = \"${{ steps.new_sha.outputs.sha }}\"" \
145- .cookiecutter.json > tmp && mv tmp .cookiecutter.json
146- git add .cookiecutter.json
147-
148- BRANCH="template-update-${{ steps.new_sha.outputs.sha }}"
149- git checkout -b "$BRANCH"
150- git config user.name "GitHub Actions Bot"
151- git config user.email "actions@github.com"
152- git commit -am "chore: merge template updates ${{ steps.old_sha.outputs.sha }} → ${{ steps.new_sha.outputs.sha }}"
153- git push origin "$BRANCH"
154-
155- - name : Create pull request
156- if : env.SHA_CHANGED == 'true'
157- env :
158- GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
159- run : |
160- gh pr create \
161- --title "chore: merge template updates ${{ steps.old_sha.outputs.sha }} → ${{ steps.new_sha.outputs.sha }}" \
162- --body "Updates cookiecutter.template_sha from ${{ steps.old_sha.outputs.sha }} to ${{ steps.new_sha.outputs.sha }}" \
163- --base '${{ inputs.repo_branch }}' \
164- --head "$BRANCH"
165- - name : Final cleanup (remove temp dirs & neutralize bad .gitmodules)
166- if : always()
167- shell : bash
168- run : |
169- set -euo pipefail
170-
171- # Remove temporary working folders & patch file
172- rm -rf base-template template-source template-base template-new update.patch
173-
174- # Defensive: if a stray/invalid .gitmodules exists (missing URL entries),
175- # delete it so actions/checkout post-job doesn't crash on submodule foreach.
176- if [ -f .gitmodules ]; then
177- if ! git config -f .gitmodules --get-regexp '^submodule\..*\.url$' >/dev/null 2>&1; then
178- echo "Removing invalid .gitmodules (no submodule URLs found)."
179- rm -f .gitmodules
180- # Purge any lingering local submodule config and modules dir
181- git config --local --name-only --get-regexp '^submodule\.' \
182- | xargs -r -n1 -I {} git config --local --unset-all "{}"
183- rm -rf .git/modules || true
70+ - name : Exit if SHA unchanged
71+ if : env.SHA_CHANGED == 'false'
72+ run : echo "✂️ SHA unchanged; nothing to update."
73+
74+ - name : Render templates + apply patch (temp work OUTSIDE repo) + commit branch
75+ if : env.SHA_CHANGED == 'true'
76+ shell : bash
77+ run : |
78+ set -euo pipefail
79+
80+ WORKDIR="${RUNNER_TEMP}/cc-template-update"
81+ BASE_REPO="${WORKDIR}/base-template"
82+ NEW_REPO="${WORKDIR}/template-source"
83+ OUT_BASE="${WORKDIR}/template-base"
84+ OUT_NEW="${WORKDIR}/template-new"
85+ PATCHFILE="${WORKDIR}/update.patch"
86+
87+ rm -rf "$WORKDIR"
88+ mkdir -p "$WORKDIR" "$OUT_BASE" "$OUT_NEW"
89+
90+ echo "Cloning OLD template SHA..."
91+ git clone '${{ inputs.template_repo }}' "$BASE_REPO"
92+ git -C "$BASE_REPO" checkout '${{ steps.old_sha.outputs.sha }}'
93+
94+ echo "Cloning NEW template SHA..."
95+ git clone '${{ inputs.template_repo }}' "$NEW_REPO"
96+
97+ echo "Rendering OLD template..."
98+ rm -rf ~/.cookiecutters/*
99+ cookiecutter "$BASE_REPO" \
100+ --replay-file .cookiecutter.json \
101+ --overwrite-if-exists \
102+ --output-dir "$OUT_BASE"
103+
104+ echo "Rendering NEW template..."
105+ rm -rf ~/.cookiecutters/*
106+ cookiecutter "$NEW_REPO" \
107+ --replay-file .cookiecutter.json \
108+ --overwrite-if-exists \
109+ --output-dir "$OUT_NEW"
110+
111+ echo "Diffing renders..."
112+ diff -ruN "$OUT_BASE" "$OUT_NEW" > "$PATCHFILE" || true
113+
114+ if [ ! -s "$PATCHFILE" ]; then
115+ echo "ℹ️ No template diffs; only SHA bump will occur."
116+ else
117+ echo "Pre-creating dirs for new files..."
118+ awk '
119+ /^\+\+\+ / {
120+ f=$2
121+ if (f == "/dev/null") next
122+ # remove leading directory (template-new/ or template-base/)
123+ sub(/^[^/]+\//, "", f)
124+ if (match(f, /(.+)\//, m)) print m[1]
125+ }
126+ ' "$PATCHFILE" | sort -u | xargs -r mkdir -p
127+
128+ echo "Seeding missing base files so patch can modify them..."
129+ awk '/^--- /{print $2}' "$PATCHFILE" | while read -r OLD; do
130+ [ "$OLD" = "/dev/null" ] && continue
131+ # strip leading dir from diff path
132+ REL="${OLD#*/}"
133+ SRC="$OUT_BASE/${REL}"
134+ TGT="${REL}"
135+ mkdir -p "$(dirname "$TGT")"
136+ if [ ! -e "$TGT" ] && [ -f "$SRC" ]; then
137+ cp -a "$SRC" "$TGT"
138+ fi
139+ done
140+
141+ echo "Applying patch..."
142+ patch --batch -p1 < "$PATCHFILE"
143+ echo "✅ Applied template changes"
184144 fi
185- fi
186145
187- echo "Cleanup complete."
146+ echo "Updating template SHA in .cookiecutter.json..."
147+ jq ".cookiecutter.template_sha = \"${{ steps.new_sha.outputs.sha }}\"" \
148+ .cookiecutter.json > tmp && mv tmp .cookiecutter.json
149+
150+ BRANCH="template-update-${{ steps.new_sha.outputs.sha }}"
151+ echo "BRANCH=$BRANCH" >> "$GITHUB_ENV"
152+
153+ git checkout -b "$BRANCH"
154+ git config user.name "GitHub Actions Bot"
155+ git config user.email "actions@github.com"
156+
157+ git add -A
158+ git commit -m "chore: merge template updates ${{ steps.old_sha.outputs.sha }} → ${{ steps.new_sha.outputs.sha }}"
159+ git push origin "$BRANCH"
160+
161+ - name : Create pull request
162+ if : env.SHA_CHANGED == 'true'
163+ env :
164+ GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
165+ shell : bash
166+ run : |
167+ gh pr create \
168+ --title "chore: merge template updates ${{ steps.old_sha.outputs.sha }} → ${{ steps.new_sha.outputs.sha }}" \
169+ --body "Updates cookiecutter.template_sha from ${{ steps.old_sha.outputs.sha }} to ${{ steps.new_sha.outputs.sha }}" \
170+ --base '${{ inputs.repo_branch }}' \
171+ --head "${{ env.BRANCH }}"
172+
0 commit comments