Skip to content

Commit 30ce501

Browse files
committed
Added Nix Update Workflow
1 parent ad053cc commit 30ce501

File tree

1 file changed

+285
-0
lines changed

1 file changed

+285
-0
lines changed
+285
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
name: Update
2+
on:
3+
schedule:
4+
- cron: "0 12 * * *"
5+
concurrency:
6+
group: "update-${{ github.ref_name }}"
7+
cancel-in-progress: true
8+
9+
# Allow pushing and creating PRs
10+
permissions:
11+
contents: write
12+
pull-requests: write
13+
14+
jobs:
15+
update:
16+
name: Update the flake inputs and generate options
17+
runs-on: ubuntu-latest
18+
timeout-minutes: 40
19+
if: github.event_name != 'schedule' || github.repository == 'nix-community/nixvim'
20+
env:
21+
repo: ${{ github.repository }}
22+
base_branch: ${{ github.ref_name }}
23+
pr_branch: update/${{ github.ref_name }}
24+
workflow_run_id: ${{ github.run_id }}
25+
workflow_run_url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
26+
27+
steps:
28+
- name: Checkout repository
29+
uses: actions/checkout@v4
30+
with:
31+
ssh-key: ${{ secrets.CI_UPDATE_SSH_KEY }}
32+
33+
- name: Install Nix
34+
uses: cachix/install-nix-action@v30
35+
with:
36+
nix_path: nixpkgs=channel:nixos-unstable
37+
github_access_token: ${{ secrets.GITHUB_TOKEN }}
38+
39+
- name: Configure git
40+
run: |
41+
git config user.name 'github-actions[bot]'
42+
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
43+
44+
- name: Create update branch
45+
run: |
46+
git branch -D "$pr_branch" || echo "Nothing to delete"
47+
git switch -c "$pr_branch"
48+
49+
- name: Get info on the current PR
50+
id: open_pr_info
51+
env:
52+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
53+
run: |
54+
# Query for info about the already open update PR
55+
info=$(
56+
gh api graphql -F owner='{owner}' -F repo='{repo}' -F branch="$pr_branch" -f query='
57+
query($owner:String!, $repo:String!, $branch:String!) {
58+
repository(owner: $owner, name: $repo) {
59+
pullRequests(first: 1, states: OPEN, headRefName: $branch) {
60+
nodes {
61+
number
62+
url
63+
}
64+
}
65+
}
66+
}
67+
' | jq --raw-output '
68+
.data.repository.pullRequests.nodes[]
69+
| to_entries[]
70+
| "\(.key)=\(.value)"
71+
'
72+
)
73+
if [[ -n "$info" ]]; then
74+
echo "PR info:"
75+
echo "$info"
76+
echo "$info" >> $GITHUB_OUTPUT
77+
else
78+
echo "No PR is currently open"
79+
fi
80+
81+
- name: Fetch current PR's branch
82+
if: steps.open_pr_info.outputs.number
83+
run: |
84+
git fetch origin "$pr_branch"
85+
git branch --set-upstream-to "origin/$pr_branch"
86+
87+
- name: Update root flake.lock
88+
id: root_flake_lock
89+
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
90+
run: |
91+
old=$(git show --no-patch --format=%h)
92+
nix flake update --commit-lock-file
93+
new=$(git show --no-patch --format=%h)
94+
if [ "$old" != "$new" ]; then
95+
echo "body<<EOF" >> "$GITHUB_OUTPUT"
96+
git show --no-patch --format=%b >> "$GITHUB_OUTPUT"
97+
echo "EOF" >> "$GITHUB_OUTPUT"
98+
fi
99+
100+
- name: Apply commits from the open PR
101+
id: re_apply
102+
if: (github.event_name == 'workflow_dispatch' || github.event_name == 'schedule') && steps.open_pr_info.outputs.number
103+
run: |
104+
# The base is the most recent commit on the remote branch by github-actions[bot]
105+
# This should be a flake.lock bump or a generated-files update
106+
# We will cherry-pick all commits on the remote _after_ the $base commit
107+
remote="origin/$pr_branch"
108+
author_rxp='^github-actions\[bot\] <41898282+github-actions\[bot\]@users\.noreply\.github\.com>$'
109+
base=$(git rev-list --author="$author_rxp" --max-count=1 "$remote")
110+
commits=( $(git rev-list --reverse "$base..$remote") )
111+
if [[ -n "$commits" ]]; then
112+
echo "Applying ${#commits[@]} commits..."
113+
echo "count=${#commits[@]}" >> $GITHUB_OUTPUT
114+
git cherry-pick \
115+
--strategy-option=theirs \
116+
--empty=drop \
117+
"${commits[@]}"
118+
else
119+
echo "Nothing to re-apply"
120+
fi
121+
122+
- name: Check if there are differences to push
123+
id: diff
124+
env:
125+
pr_num: ${{ steps.open_pr_info.outputs.number }}
126+
run: |
127+
if [[ -n "$pr_num" ]]; then
128+
remote="origin/$pr_branch"
129+
else
130+
remote="origin/$base_branch"
131+
fi
132+
diff=( $(git diff --cached --name-only "$remote") )
133+
if [[ -n "$diff" ]]; then
134+
echo "${#diff[@]} files different to $remote"
135+
for file in "${diff[@]}"; do
136+
echo "- $file"
137+
done
138+
echo "count=${#diff[@]}" >> $GITHUB_OUTPUT
139+
else
140+
echo "No files are different to $remote"
141+
fi
142+
143+
- name: Create or Update Pull Request
144+
id: updated_pr
145+
if: steps.diff.outputs.count
146+
env:
147+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
148+
pr_num: ${{ steps.open_pr_info.outputs.number }}
149+
title: |
150+
[${{ github.ref_name }}] Update flake.lock & generated files
151+
root_lock: ${{ steps.root_flake_lock.outputs.body }}
152+
dev_lock: ${{ steps.dev_flake_lock.outputs.body }}
153+
generated: ${{ steps.generate.outputs.body }}
154+
run: |
155+
echo "Pushing to remote branch $pr_branch"
156+
git push --force --set-upstream origin "$pr_branch"
157+
158+
echo "Writing PR body file"
159+
(
160+
if [[ -z "$root_lock$dev_lock$generated" ]]; then
161+
echo '## No changes'
162+
echo
163+
fi
164+
if [[ -n "$root_lock" ]]; then
165+
echo '## Root lockfile'
166+
echo '```'
167+
echo "$root_lock"
168+
echo '```'
169+
echo
170+
fi
171+
if [[ -n "$dev_lock" ]]; then
172+
echo '## Dev lockfile'
173+
echo '```'
174+
echo "$dev_lock"
175+
echo '```'
176+
echo
177+
fi
178+
if [[ -n "$generated" ]]; then
179+
echo '## Generated files'
180+
echo "$generated"
181+
echo
182+
fi
183+
run_cmd='gh workflow run update.yml'
184+
if [[ "$base_branch" != "main" ]]; then
185+
run_cmd+=" --ref $base_branch"
186+
fi
187+
echo '---'
188+
echo
189+
echo -n 'This PR was most recently updated by workflow run '
190+
echo "[$workflow_run_id]($workflow_run_url)."
191+
echo
192+
echo -n 'You can re-run the update by going to the '
193+
echo -n '[workflow'"'"'s page](https://github.com/nix-community/nixvim/actions/workflows/update.yml) '
194+
echo 'or by using the `gh` command:'
195+
echo '```sh'
196+
echo "$run_cmd"
197+
echo '```'
198+
echo
199+
echo -n 'If needed, you can also specify workflow inputs on the command line, '
200+
echo 'using the `-F --field`, `-f --raw-field`, or `--json` flags.'
201+
echo 'See `gh workflow run --help`.'
202+
echo
203+
) > body.md
204+
205+
if [[ -n "$pr_num" ]]; then
206+
echo "Editing existing PR #$pr_num"
207+
operation=updated
208+
gh pr edit "$pr_num" --body-file body.md
209+
else
210+
echo "Creating new PR"
211+
operation=created
212+
gh pr create \
213+
--base "$base_branch" \
214+
--title "$title" \
215+
--body-file body.md
216+
fi
217+
218+
pr_info=$(
219+
# Get info from `gh pr view`
220+
gh pr view --json 'headRefName,number,url' --jq '
221+
to_entries[]
222+
| .key |=
223+
# Rename headRefName -> branch
224+
if . == "headRefName" then "branch"
225+
else . end
226+
| "\(.key)=\(.value)"
227+
'
228+
# Get additional info locally
229+
echo "head=$(git rev-parse HEAD)"
230+
echo "operation=$operation"
231+
)
232+
echo "PR Info:"
233+
echo "$pr_info"
234+
echo "$pr_info" >> $GITHUB_OUTPUT
235+
236+
- name: Print summary
237+
if: steps.updated_pr.outputs.number
238+
env:
239+
pr_num: ${{ steps.updated_pr.outputs.number }}
240+
pr_url: ${{ steps.updated_pr.outputs.url }}
241+
pr_branch: ${{ steps.updated_pr.outputs.branch }}
242+
head: ${{ steps.updated_pr.outputs.head }}
243+
operation: ${{ steps.updated_pr.outputs.operation }}
244+
re_apply_count: ${{ steps.re_apply.outputs.count }}
245+
run: |
246+
short=${head:0:6}
247+
# stdout
248+
echo "${short} pushed to ${pr_branch}"
249+
echo "#${pr_num} was ${operation}: ${pr_url}"
250+
( # markdown summary
251+
echo "## ${{ github.ref_name }}"
252+
echo
253+
echo "\`${short}\` pushed to \`${pr_branch}\`"
254+
echo
255+
echo "[#${pr_num}](${pr_url}) was ${operation}."
256+
echo
257+
if [[ -n "$re_apply_count" ]]; then
258+
echo "Re-applied $re_apply_count commits from the existing PR."
259+
fi
260+
echo
261+
) >> $GITHUB_STEP_SUMMARY
262+
263+
- name: Print cancellation summary
264+
if: (!steps.updated_pr.outputs.number)
265+
env:
266+
pr_num: ${{ steps.open_pr_info.outputs.number }}
267+
pr_url: ${{ steps.open_pr_info.outputs.url }}
268+
changes: ${{ steps.diff.outputs.count || '0' }}
269+
re_apply_count: ${{ steps.re_apply.outputs.count }}
270+
run: |
271+
(
272+
echo "## Not updated"
273+
echo
274+
echo -n "$changes files with differences compared to "
275+
if [[ -n "$pr_num" ]]; then
276+
echo "[#$pr_num]($pr_url)."
277+
else
278+
echo "\`$base_branch\`"
279+
fi
280+
echo
281+
if [[ -n "$re_apply_count" ]]; then
282+
echo "Re-applied $re_apply_count commits from the existing PR."
283+
fi
284+
echo
285+
) >> $GITHUB_STEP_SUMMARY

0 commit comments

Comments
 (0)