-
Notifications
You must be signed in to change notification settings - Fork 1
161 lines (139 loc) · 6.89 KB
/
Copy pathtemplate-init.yml
File metadata and controls
161 lines (139 loc) · 6.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
name: Template initialisation
on:
push:
branches:
- main # runs after the very first push that creates the repo
jobs:
init:
# Do not run in the original template repository itself
if: github.event.repository.name != 'python-template'
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
issues: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Derive project‑specific names
id: names
run: |
echo "repo_slug=${{ github.event.repository.name }}" >> "$GITHUB_OUTPUT"
snake=$(echo "${{ github.event.repository.name }}" | tr '-' '_')
echo "repo_snake=$snake" >> "$GITHUB_OUTPUT"
echo "repo_owner=${{ github.event.repository.owner.login }}" >> "$GITHUB_OUTPUT"
- name: Create working branch
run: git switch -c template-init
- name: Replace repo owner
# The previous form `${{ ... }} != marksverdhei` pre-substituted only the
# left side, leaving GHA to evaluate `<owner_login> != marksverdhei` as
# an expression — both unquoted identifiers resolved to null context
# refs, so `null != null` was always false and this step was *always
# skipped*. New repos kept marksverdhei references unreplaced. The
# comparison has to live entirely inside one expression scope.
if: github.event.repository.owner.login != 'marksverdhei'
run: |
repo_owner="${{ steps.names.outputs.repo_owner }}"
# replace repo owner placeholder
grep -lr --exclude-dir=.git 'marksverdhei' . | xargs -r sed -i "s/marksverdhei/${repo_owner}/g"
- name: Replace placeholders inside files
env:
REPO_DESC: ${{ github.event.repository.description }}
run: |
repo_slug="${{ steps.names.outputs.repo_slug }}"
repo_snake="${{ steps.names.outputs.repo_snake }}"
# replace dash-style placeholder
grep -lr --exclude-dir=.git 'python-template' . | xargs -r sed -i "s/python-template/${repo_slug}/g"
# replace snake_case placeholder
grep -lr --exclude-dir=.git 'python_template' . | xargs -r sed -i "s/python_template/${repo_snake}/g"
# replace pyproject.toml description placeholder
# Done in Python so arbitrary characters in the description (slashes,
# quotes, shell metachars) can't break sed parsing or shell parsing.
python3 - <<'PY'
import os, pathlib
path = pathlib.Path("pyproject.toml")
text = path.read_text()
desc = os.environ.get("REPO_DESC", "")
if desc:
text = text.replace("Add your description here", desc, 1)
else:
text = "\n".join(
line for line in text.splitlines()
if line.strip() != 'description = "Add your description here"'
) + "\n"
path.write_text(text)
PY
- name: Rename files and directories
run: |
repo_slug="${{ steps.names.outputs.repo_slug }}"
repo_snake="${{ steps.names.outputs.repo_snake }}"
# function to rename safely preserving path depth
rename_path() {
old_path="$1"; new_path="$2";
mkdir -p "$(dirname "$new_path")"
git mv "$old_path" "$new_path"
}
export -f rename_path
# dash‑style paths
find . -depth -name '*python-template*' -print0 | while IFS= read -r -d '' f; do
rename_path "$f" "${f//python-template/$repo_slug}";
done
# snake_case paths
find . -depth -name '*python_template*' -print0 | while IFS= read -r -d '' f; do
rename_path "$f" "${f//python_template/$repo_snake}";
done
- name: Remove this workflow so it never runs again
run: git rm -f .github/workflows/template-init.yml
- name: Commit changes
run: |
git config --global user.name 'github-actions[bot]'
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
git add -u
git commit -m "chore: initialize project from template" || echo "Nothing to commit"
- name: Push branch
run: git push -f origin template-init
- name: Create pull request
id: create_pr
continue-on-error: true
env:
GH_TOKEN: ${{ github.token }}
run: |
issue_title="Grant GitHub Actions permission to create pull requests"
# look for an open issue with that exact title
issue_num=$(gh issue list --state open --search "in:title \"$issue_title\"" --json number --jq '.[0].number')
# Base PR body text
pr_body=$'Automated placeholder replacement, path renames, and self‑cleanup.'
# Append closing reference if the issue exists
if [ -n "$issue_num" ]; then
pr_body+=$' Fixes #'"$issue_num"
fi
# Create the PR
gh pr create \
--title "Initialize project from template" \
--body "$pr_body" \
--base ${{ github.ref }} \
--head template-init
- name: Create issue to grant PR permissions (if missing)
if: steps.create_pr.outcome == 'failure'
env:
GH_TOKEN: ${{ github.token }}
run: |
set -e
issue_title="Grant GitHub Actions permission to create pull requests"
# Does an open issue with this exact title already exist?
existing=$(gh issue list --state open --search "in:title \"$issue_title\"" --json number --jq '.[0].number')
if [ -z "$existing" ]; then
echo "Creating new issue: $issue_title"
gh issue create \
--title "$issue_title" \
--body $'The workflow attempted to create a pull request but failed with the following error:\n\n```\npull request create failed: GraphQL: GitHub Actions is not permitted to create or approve pull requests (createPullRequest)\n```\n\nPlease grant the required permission by visiting the following link:\n\nhttps://github.com/${{ github.repository }}/settings/actions\n\nThen, in **Actions → General → Workflow permissions**, choose **Read and write permissions** (and optionally **Allow GitHub Actions to create and approve pull requests**).\n\nAfter updating the setting, re‑run the workflow to complete the project initialisation.'
else
echo "Issue #$existing already exists – skipping creation."
fi
- name: Fail workflow because PR could not be created
if: steps.create_pr.outcome == 'failure'
run: |
echo "Failing workflow due to failed PR creation. Please grant the required permissions and re-run." >&2
exit 1