Skip to content

Commit b1166b3

Browse files
committed
Automated changelogs [WIP]
1 parent 62d820f commit b1166b3

File tree

10 files changed

+225
-1
lines changed

10 files changed

+225
-1
lines changed

.github/workflows/check-changelog.yml

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: Changelog
2+
on:
3+
pull_request:
4+
branches:
5+
- main
6+
# Edited such that we can detect changes to the description
7+
types: [opened, synchronize, reopened, edited]
8+
9+
jobs:
10+
check:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- uses: actions/checkout@v4
14+
with:
15+
depth: 2
16+
17+
- name: check changelog
18+
run: scripts/check-changelog.sh . ${{ github.event.pull_request.number }}
19+
env:
20+
GH_TOKEN: ${{ github.token }}
21+

.github/workflows/regular-release.yml

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
name: Regular Version
2+
on:
3+
workflow_dispatch: # Allows triggering manually
4+
schedule:
5+
- cron: '47 14 * * 2' # runs every Tuesday at 14:47 UTC (chosen somewhat randomly)
6+
7+
jobs:
8+
version:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: actions/checkout@v4
12+
with:
13+
# This fetches the entire Git history.
14+
# This is needed so we can determine the commits (and therefore PRs)
15+
# where the changelogs have been added
16+
depth: 0
17+
18+
- uses: cachix/install-nix-action@v26
19+
20+
- name: Increment version and assemble changelog
21+
id: version
22+
run: |
23+
version=$(./scripts/version.sh .)
24+
echo "version=$version" >> "$GITHUB_OUTPUTS"
25+
env:
26+
GH_TOKEN: ${{ github.token }}
27+
28+
- name: Create Pull Request
29+
uses: peter-evans/create-pull-request@v6
30+
with:
31+
# To trigger CI for automated PRs, we use a separate machine account
32+
# See https://github.com/peter-evans/create-pull-request/blob/main/docs/concepts-guidelines.md#workarounds-to-trigger-further-workflow-runs
33+
# and https://github.com/peter-evans/create-pull-request/blob/main/docs/concepts-guidelines.md#push-pull-request-branches-to-a-fork
34+
token: ${{ secrets.MACHINE_USER_PAT }}
35+
path: repo
36+
push-to-fork: infinixbot/nixpkgs-check-by-name
37+
committer: infinixbot <[email protected]>
38+
author: infinixbot <[email protected]>
39+
commit-message: "Version ${{ github.steps.version.outputs.version }}"
40+
branch: version
41+
title: "Version ${{ github.steps.version.outputs.version }}"
42+
body: "Automated version update. Merging this PR will trigger a release."

changes/unreleased/README.md

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Changelogs
2+
3+
To add a changelog, add a Markdown file to a subdirectory depending on the effort required to update to
4+
that version:
5+
6+
- [Major](./major): A large effort. This will cause a version bump from e.g. 0.1.2 to 1.0.0
7+
- [Medium](./medium): Some effort. This will cause a version bump from e.g. 0.1.2 to 1.2.0
8+
- [Minor](./minor): Little/no effort. This will cause a version bump from e.g. 0.1.2 to 0.1.3
9+
10+
Therefore, the versions use [EffVer](https://jacobtomlinson.dev/effver/).
11+
12+
The Markdown file must have the `.md` file ending, and be of the form
13+
14+
```markdown
15+
# Some descriptive title of the change
16+
17+
Optionally more information
18+
```

changes/unreleased/major/.gitkeep

Whitespace-only changes.

changes/unreleased/medium/.gitkeep

Whitespace-only changes.

changes/unreleased/minor/.gitkeep

Whitespace-only changes.

default.nix

+16
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,22 @@ let
112112
'';
113113
};
114114

115+
# Run regularly by CI and turned into a PR
116+
autoVersion =
117+
pkgs.writeShellApplication {
118+
name = "auto-version";
119+
runtimeInputs = with pkgs; [
120+
coreutils
121+
git
122+
github-cli
123+
jq
124+
cargo
125+
toml-cli
126+
cargo-edit
127+
];
128+
text = builtins.readFile ./scripts/version.sh;
129+
};
130+
115131
# Tests the tool on the pinned Nixpkgs tree, this is a good sanity check
116132
nixpkgsCheck = pkgs.runCommand "test-nixpkgs-check-by-name" {
117133
nativeBuildInputs = [

scripts/check-changelog.sh

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#!/usr/bin/env bash
2+
3+
set -euo pipefail
4+
shopt -s nullglob
5+
6+
root=$1
7+
prNumber=$2
8+
9+
nonUserFacingString='this is not a user-facing change'
10+
11+
# Run this first to validate files
12+
for file in "$root"/changes/unreleased/*/*; do
13+
if [[ "$(basename "$file")" == ".gitkeep" ]]; then
14+
continue
15+
fi
16+
if [[ ! "$file" == *.md ]]; then
17+
echo "File $file: Must be a markdown file with file ending .md"
18+
exit 1
19+
fi
20+
if [[ "$(sed -n '/^#/=' "$file")" != "1" ]]; then
21+
echo "File $file: The first line must start with #, while all others must not start with #"
22+
exit 1
23+
fi
24+
done
25+
26+
if gh api \
27+
-H "Accept: application/vnd.github+json" \
28+
-H "X-GitHub-Api-Version: 2022-11-28" \
29+
/repos/NixOS/nixpkgs-check-by-name/pulls/"$prNumber" \
30+
| jq -r '.body' \
31+
| grep -i "$nonUserFacingString"; then
32+
echo "Not a user-facing change, no changelog necessary"
33+
elif [[ -z "$(git -C "$root" log HEAD^..HEAD --name-only "$root"/changes/unreleased)" ]]; then
34+
echo "If this PR contains a user-facing change, add a changelog in ./changes/unreleased"
35+
echo "Otherwise, add \"$nonUserFacingString\" to the PR description"
36+
exit 1
37+
else
38+
echo "This is a user-facing change and there is a changelog"
39+
fi

scripts/release.sh

+4-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,10 @@ To import it:
4646
```bash
4747
gzip -cd '"$artifactName"' | nix-store --import | tail -1
4848
```
49-
'
49+
50+
## Changes
51+
52+
'"$(tail -1 "$root"/changes/released/"$version".md)"
5053

5154
echo "Creating draft release"
5255
if ! release=$(gh api \

scripts/version.sh

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
#!/usr/bin/env bash
2+
3+
set -euo pipefail
4+
shopt -s nullglob
5+
6+
root=$1
7+
8+
[[ "$(toml get --raw Cargo.toml package.version)" =~ ([0-9]+)\.([0-9]+)\.([0-9]+) ]]
9+
splitVersion=( "${BASH_REMATCH[@]:1}" )
10+
11+
majorChanges=( "$root"/changes/unreleased/major/*.md )
12+
mediumChanges=( "$root"/changes/unreleased/medium/*.md )
13+
minorChanges=( "$root"/changes/unreleased/minor/*.md )
14+
15+
if (( ${#majorChanges[@]} > 0 )); then
16+
# If we didn't have `|| true` this would exit the program due to `set -e`,
17+
# because (( ... )) returns the incremental value, which is treated as the exit code..
18+
(( splitVersion[0]++ )) || true
19+
splitVersion[1]=0
20+
splitVersion[2]=0
21+
elif (( ${#mediumChanges[@]} > 0 )); then
22+
(( splitVersion[1]++ )) || true
23+
splitVersion[2]=0
24+
elif (( ${#minorChanges[@]} > 0 )); then
25+
(( splitVersion[2]++ )) || true
26+
else
27+
echo >&2 "No changes"
28+
exit 0
29+
fi
30+
31+
next=${splitVersion[0]}.${splitVersion[1]}.${splitVersion[2]}
32+
releaseFile=$root/changes/released/${next}.md
33+
mkdir -p "$(dirname "$releaseFile")"
34+
35+
echo "# Version $next ($(date --iso-8601 --utc))" > "$releaseFile"
36+
echo "" >> "$releaseFile"
37+
38+
# shellcheck disable=SC2016
39+
for file in "${majorChanges[@]}" "${mediumChanges[@]}" "${minorChanges[@]}"; do
40+
commit=$(git log -1 --format=%H -- "$file")
41+
if ! gh api graphql \
42+
-f sha="$commit" \
43+
-f query='
44+
query ($sha: String) {
45+
repository(owner: "NixOS", name: "nixpkgs-check-by-name") {
46+
commit: object(expression: $sha) {
47+
... on Commit {
48+
associatedPullRequests(first: 100) {
49+
nodes {
50+
merged
51+
baseRefName
52+
baseRepository { nameWithOwner }
53+
number
54+
author { login }
55+
}
56+
}
57+
}
58+
}
59+
}
60+
}' | \
61+
jq --exit-status -r --arg file "$file" '
62+
.data.repository.commit.associatedPullRequests?.nodes?[]?
63+
| select(
64+
# We need to make sure to get the right PR, there can be many
65+
.merged and
66+
.baseRepository.nameWithOwner == "NixOS/nixpkgs-check-by-name" and
67+
.baseRefName == "main")
68+
| "\(.number) \(.author.login) \($ARGS.named.file)"'; then
69+
echo >&2 "Couldn't get PR for file $file"
70+
exit 1
71+
fi
72+
done | \
73+
sort -n | \
74+
while read -r number author file; do
75+
# Replace the first line `# <title>` by `- <title> by @author in #number`
76+
# All other non-empty lines are indented with 2 spaces to make the markdown formatting work
77+
sed "$file" >> "$releaseFile" \
78+
-e "1s|#[[:space:]]\(.*\)|- \1 by [@$author](https://github.com/$author) in [#$number](https://github.com/NixOS/nixpkgs-check-by-name/pull/$number)|" \
79+
-e '2,$s/^\(.\)/ \1/'
80+
81+
rm "$file"
82+
done
83+
84+
cargo set-version "$next"
85+
echo "$next"

0 commit comments

Comments
 (0)