Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pkgs/by-name: Enable gradual migration checks and add run-local.sh #274591

Merged
merged 8 commits into from
Dec 21, 2023
131 changes: 16 additions & 115 deletions .github/workflows/check-by-name.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Checks pkgs/by-name (see pkgs/by-name/README.md)
# using the nixpkgs-check-by-name tool (see pkgs/test/nixpkgs-check-by-name)
#
# When you make changes to this workflow, also update pkgs/test/nixpkgs-check-by-name/scripts/run-local.sh adequately
name: Check pkgs/by-name

# The pre-built tool is fetched from a channel,
Expand All @@ -20,6 +22,9 @@ jobs:
# The default of 6 hours is definitely too long
timeout-minutes: 10
steps:
# This step has to be in this file,
# because it's needed to determine which revision of the repository to fetch,
# and we can only use other files from the repository once it's fetched.
- name: Resolving the merge commit
env:
GH_TOKEN: ${{ github.token }}
Expand Down Expand Up @@ -72,124 +77,20 @@ jobs:
ref: ${{ env.mergedSha }}
# Fetches the merge commit and its parents
fetch-depth: 2
- name: Determining PR git hashes
- name: Checking out base branch
run: |
# For pull_request_target this is the same as $GITHUB_SHA
echo "baseSha=$(git rev-parse HEAD^1)" >> "$GITHUB_ENV"

echo "headSha=$(git rev-parse HEAD^2)" >> "$GITHUB_ENV"
base=$(mktemp -d)
git worktree add "$base" "$(git rev-parse HEAD^1)"
echo "base=$base" >> "$GITHUB_ENV"
- uses: cachix/install-nix-action@7ac1ec25491415c381d9b62f0657c7a028df52a7 # v24
- name: Determining channel to use for dependencies
run: |
echo "Determining the preferred channel to use for PR base branch $GITHUB_BASE_REF"
if [[ "$GITHUB_BASE_REF" =~ ^(release|staging|staging-next)-([0-9][0-9]\.[0-9][0-9])$ ]]; then
# Use the release channel for all PRs to release-XX.YY, staging-XX.YY and staging-next-XX.YY
channel=nixos-${BASH_REMATCH[2]}
echo "PR is for a release branch, preferred channel is $channel"
else
# Use the nixos-unstable channel for all other PRs
channel=nixos-unstable
echo "PR is for a non-release branch, preferred channel is $channel"
fi
# Check that the channel exists. It doesn't exist for fresh release branches
if ! curl -fSs "https://channels.nixos.org/$channel"; then
# Fall back to nixos-unstable, makes sense for fresh release branches
echo "Preferred channel $channel could not be fetched, falling back to nixos-unstable"
channel=nixos-unstable
fi
echo "channel=$channel" >> "$GITHUB_ENV"
- name: Fetching latest version of channel
run: |
echo "Fetching latest version of channel $channel"
# This is probably the easiest way to get Nix to output the path to a downloaded channel!
nixpkgs=$(nix-instantiate --find-file nixpkgs -I nixpkgs=channel:"$channel")
# This file only exists in channels
rev=$(<"$nixpkgs"/.git-revision)
echo "Channel $channel is at revision $rev"
echo "nixpkgs=$nixpkgs" >> "$GITHUB_ENV"
echo "rev=$rev" >> "$GITHUB_ENV"
- name: Fetching pre-built nixpkgs-check-by-name from the channel
run: |
echo "Fetching pre-built nixpkgs-check-by-name from channel $channel at revision $rev"
# Passing --max-jobs 0 makes sure that we won't build anything
nix-build "$nixpkgs" -A tests.nixpkgs-check-by-name --max-jobs 0
- name: Fetching the tool
run: pkgs/test/nixpkgs-check-by-name/scripts/fetch-tool.sh "$GITHUB_BASE_REF" result
- name: Running nixpkgs-check-by-name
run: |
echo "Checking whether the check succeeds on the base branch $GITHUB_BASE_REF"
git checkout -q "$baseSha"
if baseOutput=$(result/bin/nixpkgs-check-by-name . 2>&1); then
baseSuccess=1
else
baseSuccess=
fi
printf "%s\n" "$baseOutput"

echo "Checking whether the check would succeed after merging this pull request"
git checkout -q "$mergedSha"
if mergedOutput=$(result/bin/nixpkgs-check-by-name . 2>&1); then
mergedSuccess=1
exitCode=0
if result/bin/nixpkgs-check-by-name --base "$base" .; then
exit 0
else
mergedSuccess=
exitCode=1
exitCode=$?
echo "To run locally: ./pkgs/test/nixpkgs-check-by-name/scripts/run-local.sh $GITHUB_BASE_REF https://github.com/$GITHUB_REPOSITORY.git"
exit "$exitCode"
fi
printf "%s\n" "$mergedOutput"

resultToEmoji() {
if [[ -n "$1" ]]; then
echo ":heavy_check_mark:"
else
echo ":x:"
fi
}

# Print a markdown summary in GitHub actions
{
echo "| Nixpkgs version | Check result |"
echo "| --- | --- |"
echo "| Latest base commit | $(resultToEmoji "$baseSuccess") |"
echo "| After merging this PR | $(resultToEmoji "$mergedSuccess") |"
echo ""

if [[ -n "$baseSuccess" ]]; then
if [[ -n "$mergedSuccess" ]]; then
echo "The check succeeds on both the base branch and after merging this PR"
else
echo "The check succeeds on the base branch, but would fail after merging this PR:"
echo "\`\`\`"
echo "$mergedOutput"
echo "\`\`\`"
echo ""
fi
else
if [[ -n "$mergedSuccess" ]]; then
echo "The check fails on the base branch, but this PR fixes it, nicely done!"
else
echo "The check fails on both the base branch and after merging this PR, unknown if only this PRs changes would satisfy the check, the base branch needs to be fixed first."
echo ""
echo "Failure on the base branch:"
echo "\`\`\`"
echo "$baseOutput"
echo "\`\`\`"
echo ""
echo "Failure after merging this PR:"
echo "\`\`\`"
echo "$mergedOutput"
echo "\`\`\`"
echo ""
fi
fi

echo "### Details"
echo "- nixpkgs-check-by-name tool:"
echo " - Channel: $channel"
echo " - Nixpkgs commit: [$rev](https://github.com/${GITHUB_REPOSITORY}/commit/$rev)"
echo " - Store path: \`$(realpath result)\`"
echo "- Tested Nixpkgs:"
echo " - Base branch: $GITHUB_BASE_REF"
echo " - Latest base branch commit: [$baseSha](https://github.com/${GITHUB_REPOSITORY}/commit/$baseSha)"
echo " - Latest PR commit: [$headSha](https://github.com/${GITHUB_REPOSITORY}/commit/$headSha)"
echo " - Merge commit: [$mergedSha](https://github.com/${GITHUB_REPOSITORY}/commit/$mergedSha)"
} >> "$GITHUB_STEP_SUMMARY"

exit "$exitCode"
11 changes: 5 additions & 6 deletions pkgs/by-name/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,11 @@ There's some limitations as to which packages can be defined using this structur

CI performs [certain checks](../test/nixpkgs-check-by-name/README.md#validity-checks) on the `pkgs/by-name` structure.
This is done using the [`nixpkgs-check-by-name` tool](../test/nixpkgs-check-by-name).
The version of this tool used is the one that corresponds to the NixOS channel of the PR base branch.
See [here](../../.github/workflows/check-by-name.yml) for details.

The tool can be run locally using
You can locally emulate the CI check using

```bash
nix-build -A tests.nixpkgs-check-by-name
result/bin/nixpkgs-check-by-name .
```
$ ./pkgs/test/nixpkgs-check-by-name/scripts/run-local.sh master
RaitoBezarius marked this conversation as resolved.
Show resolved Hide resolved
```

See [here](../../.github/workflows/check-by-name.yml) for more info.
26 changes: 26 additions & 0 deletions pkgs/test/nixpkgs-check-by-name/scripts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# CI-related Scripts

This directory contains scripts used and related to the CI running the `pkgs/by-name` checks in Nixpkgs. See also the [CI GitHub Action](../../../../.github/workflows/check-by-name.yml).

## `./run-local.sh BASE_BRANCH [REPOSITORY]`

Runs the `pkgs/by-name` check on the HEAD commit, closely matching what CI does.

Note that this can't do exactly the same as CI,
because CI needs to rely on GitHub's server-side Git history to compute the mergeability of PRs before the check can be started.
In turn when running locally, we don't want to have to push commits to test them,
and we can also rely on the local Git history to do the mergeability check.

Arguments:
- `BASE_BRANCH`: The base branch to use, e.g. master or release-23.11
- `REPOSITORY`: The repository to fetch the base branch from, defaults to https://github.com/NixOS/nixpkgs.git

## `./fetch-tool.sh BASE_BRANCH OUTPUT_PATH`

Fetches the Hydra-prebuilt nixpkgs-check-by-name to use from the NixOS channel corresponding to the given base branch.

This script is used both by [`./run-local.sh`](#run-local-sh-base-branch-repository) and CI.

Arguments:
- `BASE_BRANCH`: The base branch to use, e.g. master or release-23.11
- `OUTPUT_PATH`: The output symlink path for the tool
45 changes: 45 additions & 0 deletions pkgs/test/nixpkgs-check-by-name/scripts/fetch-tool.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/usr/bin/env bash
# Fetches the prebuilt nixpkgs-check-by-name to use from
# the NixOS channel corresponding to the given base branch

set -euo pipefail

if (( $# < 2 )); then
echo >&2 "Usage: $0 BASE_BRANCH OUTPUT_PATH"
echo >&2 "BASE_BRANCH: The base branch to use, e.g. master or release-23.11"
echo >&2 "OUTPUT_PATH: The output symlink path for the tool"
exit 1
fi
baseBranch=$1
output=$2

echo >&2 -n "Determining the channel to use for PR base branch $baseBranch.. "
if [[ "$baseBranch" =~ ^(release|staging|staging-next)-([0-9][0-9]\.[0-9][0-9])$ ]]; then
# Use the release channel for all PRs to release-XX.YY, staging-XX.YY and staging-next-XX.YY
preferredChannel=nixos-${BASH_REMATCH[2]}
else
# Use the nixos-unstable channel for all other PRs
preferredChannel=nixos-unstable
fi

# Check that the channel exists. It doesn't exist for fresh release branches
if curl -fSs "https://channels.nixos.org/$preferredChannel"; then
channel=$preferredChannel
echo >&2 "$channel"
else
# Fall back to nixos-unstable, makes sense for fresh release branches
channel=nixos-unstable
echo >&2 -e "\e[33mWarning: Preferred channel $preferredChannel could not be fetched, using fallback: $channel\e[0m"
fi

echo >&2 -n "Fetching latest version of channel $channel.. "
# This is probably the easiest way to get Nix to output the path to a downloaded channel!
nixpkgs=$(nix-instantiate --find-file nixpkgs -I nixpkgs=channel:"$channel")
echo >&2 "$nixpkgs"

# This file only exists in channels
echo >&2 -e "Git revision of channel $channel is \e[34m$(<"$nixpkgs/.git-revision")\e[0m"

echo >&2 -n "Fetching the prebuilt version of nixpkgs-check-by-name.. "
nix-build -o "$output" "$nixpkgs" -A tests.nixpkgs-check-by-name -j 0 >/dev/null
realpath >&2 "$output"
66 changes: 66 additions & 0 deletions pkgs/test/nixpkgs-check-by-name/scripts/run-local.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#!/usr/bin/env bash
# shellcheck disable=SC2016
infinisil marked this conversation as resolved.
Show resolved Hide resolved

set -euo pipefail

cleanup_commands=()
cleanup() {
echo -n >&2 "Cleaning up.. "
infinisil marked this conversation as resolved.
Show resolved Hide resolved
# Run all cleanup commands in inverse order
for (( i=${#cleanup_commands[@]}-1; i>=0; i-- )); do
eval "${cleanup_commands[i]}"
infinisil marked this conversation as resolved.
Show resolved Hide resolved
done
echo >&2 "Done"
}
trap cleanup exit

tmp=$(mktemp -d)
cleanup_commands+=('rmdir "$tmp"')
infinisil marked this conversation as resolved.
Show resolved Hide resolved

repo=https://github.com/NixOS/nixpkgs.git

if (( $# != 0 )); then
baseBranch=$1
shift
else
echo >&2 "Usage: $0 BASE_BRANCH [REPOSITORY]"
echo >&2 "BASE_BRANCH: The base branch to use, e.g. master or release-23.11"
echo >&2 "REPOSITORY: The repository to fetch the base branch from, defaults to $repo"
exit 1
fi

if (( $# != 0 )); then
repo=$1
shift
fi

if [[ -n "$(git status --porcelain)" ]]; then
echo >&2 -e "\e[33mWarning: Dirty tree, uncommitted changes won't be taken into account\e[0m"
fi
headSha=$(git rev-parse HEAD)
echo >&2 -e "Using HEAD commit \e[34m$headSha\e[0m"

echo >&2 -n "Creating Git worktree for the HEAD commit in $tmp/merged.. "
git worktree add --detach -q "$tmp/merged" HEAD
cleanup_commands+=('git worktree remove --force "$tmp/merged"')
echo >&2 "Done"

echo >&2 -n "Fetching base branch $baseBranch to compare against.. "
git fetch -q "$repo" refs/heads/"$baseBranch"
baseSha=$(git rev-parse FETCH_HEAD)
echo >&2 -e "\e[34m$baseSha\e[0m"

echo >&2 -n "Creating Git worktree for the base branch in $tmp/base.. "
git worktree add -q "$tmp/base" "$baseSha"
cleanup_commands+=('git worktree remove --force "$tmp/base"')
echo >&2 "Done"

echo >&2 -n "Merging base branch into the HEAD commit in $tmp/merged.. "
git -C "$tmp/merged" merge -q --no-edit "$baseSha"
echo >&2 -e "\e[34m$(git -C "$tmp/merged" rev-parse HEAD)\e[0m"

"$tmp/merged/pkgs/test/nixpkgs-check-by-name/scripts/fetch-tool.sh" "$baseBranch" "$tmp/tool"
cleanup_commands+=('rm "$tmp/tool"')

echo >&2 "Running nixpkgs-check-by-name.."
"$tmp/tool/bin/nixpkgs-check-by-name" --base "$tmp/base" "$tmp/merged"