# This action requires the following secrets to be set on the repository:
# GH_USER_TOKEN: GitHub user token, to be used by ncu and to push changes
name: Comment on PR on Nix changes
on:
workflow_run:
workflows: [Nix files edited]
types: [completed]
permissions: {}
jobs:
aggregate-results:
if: ${{ github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'pull_request' }}
name: Aggregate results
runs-on: ubuntu-slim
permissions:
pull-requests: write
statuses: write
steps:
- name: Download artefacts
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
run-id: ${{ github.event.workflow_run.id }}
# Using privileged token to be able to download file from a different workflow
github-token: ${{ secrets.GH_USER_TOKEN }}
- name: Fetch PR data
id: pr-data
run: |
mv requisites-*/* .
ls
if grep -nvHE '^/nix/store/[0-9a-df-np-sv-z]{32}-[A-Za-z0-9?=._+-]+$' requisites-*.list; then
echo 'Unexpected line format in requisites list; refusing to process.' >&2
exit 1
fi
echo "NUMBER=$(grep -E '^[0-9]+$' pr_number.txt || true)" >> "$GITHUB_OUTPUT"
- name: Aggregate results
if: ${{ steps.pr-data.outputs.NUMBER }}
run: |
set -x
> requisites-before.list
> requisites-after.list
{
echo '| Platform | Number of requisites | Proportion of new derivations |'
echo '| :-: | -: | -: |'
TOTAL_BEFORE=0
TOTAL_AFTER=0
TOTAL_CHANGED=0
QUOTE='`'
for PLATFORM in x86_64-linux aarch64-linux x86_64-darwin aarch64-darwin; do
if [ ! -f "requisites-${PLATFORM}-before.list" ] || [ ! -f "requisites-${PLATFORM}-after.list" ]; then
echo "| ${QUOTE}${PLATFORM}${QUOTE} | _missing data_ | _missing data_ |"
continue
fi
BEFORE=$(wc -l < "requisites-${PLATFORM}-before.list")
TOTAL_BEFORE=$(($TOTAL_BEFORE + $BEFORE))
AFTER=$(wc -l < "requisites-${PLATFORM}-after.list")
TOTAL_AFTER=$(($TOTAL_AFTER + $AFTER))
DIFF="_no changes_"
[ "$BEFORE" = "$AFTER" ] || DIFF="**$([ "$AFTER" -lt "$BEFORE" ] || echo '+')$(($AFTER - $BEFORE))**"
CHANGED=$(git diff --no-index "requisites-${PLATFORM}-before.list" "requisites-${PLATFORM}-after.list" | grep -c '^+[^+]' || true)
TOTAL_CHANGED=$(($TOTAL_CHANGED + $CHANGED))
PERCENT=100
[ "$CHANGED" = "$AFTER" ] || {
PERMILLE=$((1000 * $CHANGED / $AFTER))
PERCENT="$((PERMILLE / 10)).$((PERMILLE % 10))"
}
echo "| ${QUOTE}${PLATFORM}${QUOTE} | $BEFORE -> $AFTER => $DIFF | $CHANGED / $AFTER = $PERCENT% |"
for STATE in before after; do
# Split store paths into package name, output (or source archive extension), and version.
awk -v OFS='\t' -v PLATFORM="$PLATFORM" '{
name = substr($0, 45)
pname = name
output = "out"
version = "_unversioned_"
if (match(name, /-[^A-Za-z]/)) {
pname = substr(name, 1, RSTART - 1)
version = substr(name, RSTART + 1)
if (match(version, /-[a-z]+$/) || match(version, /\.(tar(\.[a-z0-9]+)?|tgz|tbz2?|txz|zip|patch|diff)$/)) {
output = substr(version, RSTART + 1)
version = substr(version, 1, RSTART - 1)
}
}
print pname, output, version, PLATFORM, $0
}' "requisites-${PLATFORM}-${STATE}.list" >> "requisites-${STATE}.list"
done
done
DIFF="_no changes_"
[ "$TOTAL_BEFORE" = "$TOTAL_AFTER" ] || DIFF="**$([ "$TOTAL_AFTER" -lt "$TOTAL_BEFORE" ] || echo '+')$(($TOTAL_AFTER - $TOTAL_BEFORE))**"
PERCENT=100
[ "$TOTAL_CHANGED" = "$TOTAL_AFTER" ] || {
PERMILLE=$((1000 * $TOTAL_CHANGED / $TOTAL_AFTER))
PERCENT="$((PERMILLE / 10)).$((PERMILLE % 10))"
}
echo "| **Total** | $TOTAL_BEFORE -> $TOTAL_AFTER => $DIFF | $TOTAL_CHANGED / $TOTAL_AFTER = $PERCENT% |"
} > comment.md
sort requisites-before.list > requisites-before.sort
sort requisites-after.list > requisites-after.sort
if git diff -U0 --no-color --no-index requisites-before.sort requisites-after.sort > requisites.diff; then
echo 'No changes detected.'
else
awk -F '\t' '
function insert(list, v, n, a, i, out, done) {
n = split(list, a, " ")
for (i = 1; i <= n; i++) if (a[i] == v) return list
out = ""; done = 0
for (i = 1; i <= n; i++) {
if (!done && v < a[i]) { out = out " " v; done = 1 }
out = out " " a[i]
}
if (!done) out = out " " v
return substr(out, 2)
}
function join(list, pre, post, s) {
s = pre list post
gsub(/ /, post ", " pre, s)
return s
}
/^[-+]/ && NF == 5 {
key = substr($1, 2) SUBSEP $2 SUBSEP $4
if (substr($1, 1, 1) == "-") removed[key] = insert(removed[key], $3)
else added[key] = insert(added[key], $3)
}
END {
for (key in removed) if (!(key in added)) added[key] = ""
for (key in added) {
split(key, k, SUBSEP)
r = (key in removed) ? removed[key] : ""
a = added[key]
if (r == a) change = "_no version change_: " join(a)
else if (r == "") change = "_added_: " join(a, "**", "**")
else if (a == "") change = "_removed_: " join(r)
else change = join(r) " -> " join(a, "**", "**")
platforms[k[1] SUBSEP k[2] SUBSEP change] = insert(platforms[k[1] SUBSEP k[2] SUBSEP change], k[3])
}
for (key in platforms) {
split(key, k, SUBSEP)
row = k[1] SUBSEP k[3] SUBSEP join(platforms[key])
outputs[row] = insert(outputs[row], k[2])
}
for (row in outputs) {
split(row, k, SUBSEP)
n = split(outputs[row], o, " ")
name = ""
for (i = 1; i <= n; i++) name = name ", `" k[1] (o[i] == "out" ? "" : "." o[i]) "`"
rank = k[2] ~ /^_added_/ ? 0 : k[2] ~ /^_removed_/ ? 2 : k[2] ~ /^_no version change_/ ? 3 : 1
print rank "\t| " substr(name, 3) " | " k[2] " | " k[3] " |"
}
}
' requisites.diff > versions.tsv
{
echo
echo '<details><summary>Version changes</summary>'
echo
echo '| Package | Version | Platforms |'
echo '| :- | :- | :- |'
LC_ALL=C sort -t '`' -k1,1 -k2,2 -k3 versions.tsv | cut -f2-
echo
echo '</details>'
} >> comment.md
gh pr comment -R "$GITHUB_REPOSITORY" "$PR_NUMBER" -F comment.md
fi
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ steps.pr-data.outputs.NUMBER }}
- name: Report status on the PR
if: ${{ always() }}
run: |
gh api --method POST "repos/${GITHUB_REPOSITORY}/statuses/${HEAD_SHA}" \
-f state="$STATE" \
-f context="$GITHUB_WORKFLOW" \
-f description="$DESCRIPTION" \
-f target_url="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}"
env:
GH_TOKEN: ${{ github.token }}
HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
STATE: ${{ job.status == 'success' && 'success' || 'failure' }}
DESCRIPTION: ${{ job.status == 'success' && 'Nix requisites changes aggregated' || 'Failed to aggregate Nix requisites changes' }}