Skip to content

Commit 6b6745a

Browse files
committed
tools: aggregate Nix changes in a table
On large updates, the raw diff is not really human parsable. It can sometimes exceed the maximum length for comments, resulting in no diff being shown. To make reviews easier / more likely to spot something wrong, it now outputs a table, and report failures in PR checks. Signed-off-by: Antoine du Hamel <duhamelantoine1995@gmail.com> PR-URL: #67348 Reviewed-By: Filip Skokan <panva.ip@gmail.com> Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
1 parent 2f89ed5 commit 6b6745a

1 file changed

Lines changed: 102 additions & 18 deletions

File tree

‎.github/workflows/nix-changes-comment.yml‎

Lines changed: 102 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ jobs:
1616
runs-on: ubuntu-slim
1717
permissions:
1818
pull-requests: write
19+
statuses: write
1920
steps:
2021
- name: Download artefacts
2122
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
@@ -41,30 +42,50 @@ jobs:
4142
set -x
4243
> requisites-before.list
4344
> requisites-after.list
44-
TABLE=$(
45+
{
4546
echo '| Platform | Number of requisites | Proportion of new derivations |'
4647
echo '| :-: | -: | -: |'
4748
TOTAL_BEFORE=0
4849
TOTAL_AFTER=0
4950
TOTAL_CHANGED=0
5051
QUOTE='`'
5152
for PLATFORM in x86_64-linux aarch64-linux x86_64-darwin aarch64-darwin; do
53+
if [ ! -f "requisites-${PLATFORM}-before.list" ] || [ ! -f "requisites-${PLATFORM}-after.list" ]; then
54+
echo "| ${QUOTE}${PLATFORM}${QUOTE} | _missing data_ | _missing data_ |"
55+
continue
56+
fi
5257
BEFORE=$(wc -l < "requisites-${PLATFORM}-before.list")
5358
TOTAL_BEFORE=$(($TOTAL_BEFORE + $BEFORE))
5459
AFTER=$(wc -l < "requisites-${PLATFORM}-after.list")
5560
TOTAL_AFTER=$(($TOTAL_AFTER + $AFTER))
5661
DIFF="_no changes_"
5762
[ "$BEFORE" = "$AFTER" ] || DIFF="**$([ "$AFTER" -lt "$BEFORE" ] || echo '+')$(($AFTER - $BEFORE))**"
58-
CHANGED=$(git diff --no-index "requisites-${PLATFORM}-before.list" "requisites-${PLATFORM}-after.list" | grep -c '^+[^+]')
63+
CHANGED=$(git diff --no-index "requisites-${PLATFORM}-before.list" "requisites-${PLATFORM}-after.list" | grep -c '^+[^+]' || true)
5964
TOTAL_CHANGED=$(($TOTAL_CHANGED + $CHANGED))
6065
PERCENT=100
6166
[ "$CHANGED" = "$AFTER" ] || {
6267
PERMILLE=$((1000 * $CHANGED / $AFTER))
6368
PERCENT="$((PERMILLE / 10)).$((PERMILLE % 10))"
6469
}
6570
echo "| ${QUOTE}${PLATFORM}${QUOTE} | $BEFORE -> $AFTER => $DIFF | $CHANGED / $AFTER = $PERCENT% |"
66-
awk '{ print $0 " ('"$PLATFORM"')" }' "requisites-${PLATFORM}-before.list" >> requisites-before.list
67-
awk '{ print $0 " ('"$PLATFORM"')" }' "requisites-${PLATFORM}-after.list" >> requisites-after.list
71+
for STATE in before after; do
72+
# Split store paths into package name, output (or source archive extension), and version.
73+
awk -v OFS='\t' -v PLATFORM="$PLATFORM" '{
74+
name = substr($0, 45)
75+
pname = name
76+
output = "out"
77+
version = "_unversioned_"
78+
if (match(name, /-[^A-Za-z]/)) {
79+
pname = substr(name, 1, RSTART - 1)
80+
version = substr(name, RSTART + 1)
81+
if (match(version, /-[a-z]+$/) || match(version, /\.(tar(\.[a-z0-9]+)?|tgz|tbz2?|txz|zip|patch|diff)$/)) {
82+
output = substr(version, RSTART + 1)
83+
version = substr(version, 1, RSTART - 1)
84+
}
85+
}
86+
print pname, output, version, PLATFORM, $0
87+
}' "requisites-${PLATFORM}-${STATE}.list" >> "requisites-${STATE}.list"
88+
done
6889
done
6990
7091
DIFF="_no changes_"
@@ -75,24 +96,87 @@ jobs:
7596
PERCENT="$((PERMILLE / 10)).$((PERMILLE % 10))"
7697
}
7798
echo "| **Total** | $TOTAL_BEFORE -> $TOTAL_AFTER => $DIFF | $TOTAL_CHANGED / $TOTAL_AFTER = $PERCENT% |"
78-
)
79-
sort -k1.45 requisites-before.list > requisites-before.sort
80-
sort -k1.45 requisites-after.list > requisites-after.sort
99+
} > comment.md
100+
sort requisites-before.list > requisites-before.sort
101+
sort requisites-after.list > requisites-after.sort
81102
if git diff -U0 --no-color --no-index requisites-before.sort requisites-after.sort > requisites.diff; then
82103
echo 'No changes detected.'
83104
else
84-
{
85-
echo "$TABLE"
86-
echo
87-
echo '<details><summary>Changelog</summary>'
88-
echo
89-
echo '```diff'
90-
tail -n +5 requisites.diff
91-
echo '```'
92-
echo
93-
echo '</details>'
94-
} | gh pr comment -R "$GITHUB_REPOSITORY" "$PR_NUMBER" -F -
105+
awk -F '\t' '
106+
function insert(list, v, n, a, i, out, done) {
107+
n = split(list, a, " ")
108+
for (i = 1; i <= n; i++) if (a[i] == v) return list
109+
out = ""; done = 0
110+
for (i = 1; i <= n; i++) {
111+
if (!done && v < a[i]) { out = out " " v; done = 1 }
112+
out = out " " a[i]
113+
}
114+
if (!done) out = out " " v
115+
return substr(out, 2)
116+
}
117+
function join(list, pre, post, s) {
118+
s = pre list post
119+
gsub(/ /, post ", " pre, s)
120+
return s
121+
}
122+
/^[-+]/ && NF == 5 {
123+
key = substr($1, 2) SUBSEP $2 SUBSEP $4
124+
if (substr($1, 1, 1) == "-") removed[key] = insert(removed[key], $3)
125+
else added[key] = insert(added[key], $3)
126+
}
127+
END {
128+
for (key in removed) if (!(key in added)) added[key] = ""
129+
for (key in added) {
130+
split(key, k, SUBSEP)
131+
r = (key in removed) ? removed[key] : ""
132+
a = added[key]
133+
if (r == a) change = "_no version change_: " join(a)
134+
else if (r == "") change = "_added_: " join(a, "**", "**")
135+
else if (a == "") change = "_removed_: " join(r)
136+
else change = join(r) " -> " join(a, "**", "**")
137+
platforms[k[1] SUBSEP k[2] SUBSEP change] = insert(platforms[k[1] SUBSEP k[2] SUBSEP change], k[3])
138+
}
139+
for (key in platforms) {
140+
split(key, k, SUBSEP)
141+
row = k[1] SUBSEP k[3] SUBSEP join(platforms[key])
142+
outputs[row] = insert(outputs[row], k[2])
143+
}
144+
for (row in outputs) {
145+
split(row, k, SUBSEP)
146+
n = split(outputs[row], o, " ")
147+
name = ""
148+
for (i = 1; i <= n; i++) name = name ", `" k[1] (o[i] == "out" ? "" : "." o[i]) "`"
149+
rank = k[2] ~ /^_added_/ ? 0 : k[2] ~ /^_removed_/ ? 2 : k[2] ~ /^_no version change_/ ? 3 : 1
150+
print rank "\t| " substr(name, 3) " | " k[2] " | " k[3] " |"
151+
}
152+
}
153+
' requisites.diff > versions.tsv
154+
{
155+
echo
156+
echo '<details><summary>Version changes</summary>'
157+
echo
158+
echo '| Package | Version | Platforms |'
159+
echo '| :- | :- | :- |'
160+
LC_ALL=C sort -t '`' -k1,1 -k2,2 -k3 versions.tsv | cut -f2-
161+
echo
162+
echo '</details>'
163+
} >> comment.md
164+
gh pr comment -R "$GITHUB_REPOSITORY" "$PR_NUMBER" -F comment.md
95165
fi
96166
env:
97167
GH_TOKEN: ${{ github.token }}
98168
PR_NUMBER: ${{ steps.pr-data.outputs.NUMBER }}
169+
170+
- name: Report status on the PR
171+
if: ${{ always() }}
172+
run: |
173+
gh api --method POST "repos/${GITHUB_REPOSITORY}/statuses/${HEAD_SHA}" \
174+
-f state="$STATE" \
175+
-f context="$GITHUB_WORKFLOW" \
176+
-f description="$DESCRIPTION" \
177+
-f target_url="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}"
178+
env:
179+
GH_TOKEN: ${{ github.token }}
180+
HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
181+
STATE: ${{ job.status == 'success' && 'success' || 'failure' }}
182+
DESCRIPTION: ${{ job.status == 'success' && 'Nix requisites changes aggregated' || 'Failed to aggregate Nix requisites changes' }}

0 commit comments

Comments
 (0)