Skip to content

Commit b7d30d8

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

File tree

11 files changed

+222
-1
lines changed

11 files changed

+222
-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

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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+
# Needed so we can determine the commits where the changelogs have been added
14+
depth: 0
15+
16+
- uses: cachix/install-nix-action@v26
17+
18+
- name: Increment version and assemble changelog
19+
id: version
20+
run: |
21+
version=$(./scripts/version.sh .)
22+
echo "version=$version" >> "$GITHUB_OUTPUTS"
23+
env:
24+
GH_TOKEN: ${{ github.token }}
25+
26+
- name: Create Pull Request
27+
uses: peter-evans/create-pull-request@v6
28+
with:
29+
# To trigger CI for automated PRs, we use a separate machine account
30+
# See https://github.com/peter-evans/create-pull-request/blob/main/docs/concepts-guidelines.md#workarounds-to-trigger-further-workflow-runs
31+
# and https://github.com/peter-evans/create-pull-request/blob/main/docs/concepts-guidelines.md#push-pull-request-branches-to-a-fork
32+
token: ${{ secrets.MACHINE_USER_PAT }}
33+
path: repo
34+
push-to-fork: infinixbot/nixpkgs-check-by-name
35+
committer: infinixbot <[email protected]>
36+
author: infinixbot <[email protected]>
37+
commit-message: "Version ${{ github.steps.version.outputs.version }}"
38+
branch: version
39+
title: "Version ${{ github.steps.version.outputs.version }}"
40+
body: "Automated version update. Merging this PR will trigger a release."

changes/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
!.gitignore

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

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
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+
(( splitVersion[0]++ )) || true
17+
splitVersion[1]=0
18+
splitVersion[2]=0
19+
elif (( ${#mediumChanges[@]} > 0 )); then
20+
(( splitVersion[1]++ )) || true
21+
splitVersion[2]=0
22+
elif (( ${#minorChanges[@]} > 0 )); then
23+
(( splitVersion[2]++ )) || true
24+
else
25+
echo >&2 "No changes"
26+
exit 0
27+
fi
28+
29+
next=${splitVersion[0]}.${splitVersion[1]}.${splitVersion[2]}
30+
releaseFile=$root/changes/released/${next}.md
31+
mkdir -p "$(dirname "$releaseFile")"
32+
33+
echo "# Version $next ($(date -I))" > "$releaseFile"
34+
echo "" >> "$releaseFile"
35+
36+
# shellcheck disable=SC2016
37+
for file in "${majorChanges[@]}" "${mediumChanges[@]}" "${minorChanges[@]}"; do
38+
commit=$(git log -1 --format=%H -- "$file")
39+
if ! gh api graphql \
40+
-f sha="$commit" \
41+
-f query='
42+
query ($sha: String) {
43+
repository(owner: "NixOS", name: "nixpkgs-check-by-name") {
44+
commit: object(expression: $sha) {
45+
... on Commit {
46+
associatedPullRequests(first: 100) {
47+
nodes {
48+
merged
49+
baseRefName
50+
baseRepository { nameWithOwner }
51+
number
52+
author { login }
53+
}
54+
}
55+
}
56+
}
57+
}
58+
}' | \
59+
jq --exit-status -r --arg file "$file" '
60+
.data.repository.commit.associatedPullRequests?.nodes?[]?
61+
| select(
62+
# We need to make sure to get the right PR, there can be many
63+
.merged and
64+
.baseRepository.nameWithOwner == "NixOS/nixpkgs-check-by-name" and
65+
.baseRefName == "main")
66+
| "\(.number) \(.author.login) \($ARGS.named.file)"'; then
67+
echo >&2 "Couldn't get PR for file $file"
68+
exit 1
69+
fi
70+
done | \
71+
sort -n | \
72+
while read -r number author file; do
73+
# Replace the first line `# <title>` by `- <title> by @author in #number`
74+
# All other non-empty lines are indented with 2 spaces to make the markdown formatting work
75+
sed "$file" >> "$releaseFile" \
76+
-e "1s|#[[:space:]]\(.*\)|- \1 by [@$author](https://github.com/$author) in [#$number](https://github.com/NixOS/nixpkgs-check-by-name/pull/$number)|" \
77+
-e '2,$s/^\(.\)/ \1/'
78+
79+
rm "$file"
80+
done
81+
82+
cargo set-version "$next"
83+
echo "$next"

0 commit comments

Comments
 (0)