Skip to content

Commit

Permalink
Automated changelogs [WIP]
Browse files Browse the repository at this point in the history
  • Loading branch information
infinisil committed Apr 16, 2024
1 parent 62d820f commit 3387bac
Show file tree
Hide file tree
Showing 10 changed files with 221 additions and 1 deletion.
21 changes: 21 additions & 0 deletions .github/workflows/check-changelog.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: Changelog
on:
pull_request:
branches:
- main
# Edited such that we can detect changes to the description
types: [opened, synchronize, reopened, edited]

jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
depth: 2

- name: check changelog
run: scripts/check-changelog.sh . ${{ github.event.pull_request.number }}
env:
GH_TOKEN: ${{ github.token }}

40 changes: 40 additions & 0 deletions .github/workflows/regular-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Regular Version
on:
workflow_dispatch: # Allows triggering manually
schedule:
- cron: '47 14 * * 2' # runs every Tuesday at 14:47 UTC (chosen somewhat randomly)

jobs:
version:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
# Needed so we can determine the commits where the changelogs have been added
depth: 0

- uses: cachix/install-nix-action@v26

- name: Increment version and assemble changelog
id: version
run: |
version=$(./scripts/version.sh .)
echo "version=$version" >> "$GITHUB_OUTPUTS"
env:
GH_TOKEN: ${{ github.token }}

- name: Create Pull Request
uses: peter-evans/create-pull-request@v6
with:
# To trigger CI for automated PRs, we use a separate machine account
# See https://github.com/peter-evans/create-pull-request/blob/main/docs/concepts-guidelines.md#workarounds-to-trigger-further-workflow-runs
# and https://github.com/peter-evans/create-pull-request/blob/main/docs/concepts-guidelines.md#push-pull-request-branches-to-a-fork
token: ${{ secrets.MACHINE_USER_PAT }}
path: repo
push-to-fork: infinixbot/nixpkgs-check-by-name
committer: infinixbot <[email protected]>
author: infinixbot <[email protected]>
commit-message: "Version ${{ github.steps.version.outputs.version }}"
branch: version
title: "Version ${{ github.steps.version.outputs.version }}"
body: "Automated version update. Merging this PR will trigger a release."
18 changes: 18 additions & 0 deletions changes/unreleased/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Changelogs

To add a changelog, add a Markdown file to a subdirectory depending on the effort required to update to
that version:

- [Major](./major): A large effort. This will cause a version bump from e.g. 0.1.2 to 1.0.0
- [Medium](./medium): Some effort. This will cause a version bump from e.g. 0.1.2 to 1.2.0
- [Minor](./minor): Little/no effort. This will cause a version bump from e.g. 0.1.2 to 0.1.3

Therefore, the versions use [EffVer](https://jacobtomlinson.dev/effver/).

The Markdown file must have the `.md` file ending, and be of the form

```markdown
# Some descriptive title of the change

Optionally more information
```
Empty file.
Empty file.
Empty file.
16 changes: 16 additions & 0 deletions default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,22 @@ let
'';
};

# Run regularly by CI and turned into a PR
autoVersion =
pkgs.writeShellApplication {
name = "auto-version";
runtimeInputs = with pkgs; [
coreutils
git
github-cli
jq
cargo
toml-cli
cargo-edit
];
text = builtins.readFile ./scripts/version.sh;
};

# Tests the tool on the pinned Nixpkgs tree, this is a good sanity check
nixpkgsCheck = pkgs.runCommand "test-nixpkgs-check-by-name" {
nativeBuildInputs = [
Expand Down
39 changes: 39 additions & 0 deletions scripts/check-changelog.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/usr/bin/env bash

set -euo pipefail
shopt -s nullglob

root=$1
prNumber=$2

nonUserFacingString='this is not a user-facing change'

# Run this first to validate files
for file in "$root"/changes/unreleased/*/*; do
if [[ "$(basename "$file")" == ".gitkeep" ]]; then
continue
fi
if [[ ! "$file" == *.md ]]; then
echo "File $file: Must be a markdown file with file ending .md"
exit 1
fi
if [[ "$(sed -n '/^#/=' "$file")" != "1" ]]; then
echo "File $file: The first line must start with #, while all others must not start with #"
exit 1
fi
done

if gh api \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
/repos/NixOS/nixpkgs-check-by-name/pulls/"$prNumber" \
| jq -r '.body' \
| grep -i "$nonUserFacingString"; then
echo "Not a user-facing change, no changelog necessary"
elif [[ -z "$(git -C "$root" log HEAD^..HEAD --name-only "$root"/changes/unreleased)" ]]; then
echo "If this PR contains a user-facing change, add a changelog in ./changes/unreleased"
echo "Otherwise, add \"$nonUserFacingString\" to the PR description"
exit 1
else
echo "This is a user-facing change and there is a changelog"
fi
5 changes: 4 additions & 1 deletion scripts/release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@ To import it:
```bash
gzip -cd '"$artifactName"' | nix-store --import | tail -1
```
'
## Changes
'"$(tail -1 "$root"/changes/released/"$version".md)"

echo "Creating draft release"
if ! release=$(gh api \
Expand Down
83 changes: 83 additions & 0 deletions scripts/version.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#!/usr/bin/env bash

set -euo pipefail
shopt -s nullglob

root=$1

[[ "$(toml get --raw Cargo.toml package.version)" =~ ([0-9]+)\.([0-9]+)\.([0-9]+) ]]
splitVersion=( "${BASH_REMATCH[@]:1}" )

majorChanges=( "$root"/changes/unreleased/major/*.md )
mediumChanges=( "$root"/changes/unreleased/medium/*.md )
minorChanges=( "$root"/changes/unreleased/minor/*.md )

if (( ${#majorChanges[@]} > 0 )); then
(( splitVersion[0]++ )) || true
splitVersion[1]=0
splitVersion[2]=0
elif (( ${#mediumChanges[@]} > 0 )); then
(( splitVersion[1]++ )) || true
splitVersion[2]=0
elif (( ${#minorChanges[@]} > 0 )); then
(( splitVersion[2]++ )) || true
else
echo >&2 "No changes"
exit 0
fi

next=${splitVersion[0]}.${splitVersion[1]}.${splitVersion[2]}
releaseFile=$root/changes/released/${next}.md
mkdir -p "$(dirname "$releaseFile")"

echo "# Version $next ($(date -I))" > "$releaseFile"
echo "" >> "$releaseFile"

# shellcheck disable=SC2016
for file in "${majorChanges[@]}" "${mediumChanges[@]}" "${minorChanges[@]}"; do
commit=$(git log -1 --format=%H -- "$file")
if ! gh api graphql \
-f sha="$commit" \
-f query='
query ($sha: String) {
repository(owner: "NixOS", name: "nixpkgs-check-by-name") {
commit: object(expression: $sha) {
... on Commit {
associatedPullRequests(first: 100) {
nodes {
merged
baseRefName
baseRepository { nameWithOwner }
number
author { login }
}
}
}
}
}
}' | \
jq --exit-status -r --arg file "$file" '
.data.repository.commit.associatedPullRequests?.nodes?[]?
| select(
# We need to make sure to get the right PR, there can be many
.merged and
.baseRepository.nameWithOwner == "NixOS/nixpkgs-check-by-name" and
.baseRefName == "main")
| "\(.number) \(.author.login) \($ARGS.named.file)"'; then
echo >&2 "Couldn't get PR for file $file"
exit 1
fi
done | \
sort -n | \
while read -r number author file; do
# Replace the first line `# <title>` by `- <title> by @author in #number`
# All other non-empty lines are indented with 2 spaces to make the markdown formatting work
sed "$file" >> "$releaseFile" \
-e "1s|#[[:space:]]\(.*\)|- \1 by [@$author](https://github.com/$author) in [#$number](https://github.com/NixOS/nixpkgs-check-by-name/pull/$number)|" \
-e '2,$s/^\(.\)/ \1/'

rm "$file"
done

cargo set-version "$next"
echo "$next"

0 comments on commit 3387bac

Please sign in to comment.