ci: refactor CI to use mise for shared tool setup (#25727)

This commit is contained in:
Thomas Kosiewski
2026-06-01 15:55:19 +02:00
committed by GitHub
parent 644820cb28
commit fe257666d7
31 changed files with 995 additions and 611 deletions
-7
View File
@@ -5,7 +5,6 @@
# - go.mod
# - mise.toml (the dogfood image installs from this manifest)
# - flake.nix
# - .github/actions/setup-go/action.yml
# The version of Go in go.mod is considered the source of truth.
set -euo pipefail
@@ -19,23 +18,17 @@ IGNORE_NIX=${IGNORE_NIX:-false}
GO_VERSION_GO_MOD=$(grep -Eo 'go [0-9]+\.[0-9]+\.[0-9]+' ./go.mod | cut -d' ' -f2)
GO_VERSION_MISE_TOML=$(grep -Eo '^go = "[0-9]+\.[0-9]+\.[0-9]+"' ./mise.toml | sed -E 's/.*"([^"]+)"/\1/')
GO_VERSION_SETUP_GO=$(yq '.inputs.version.default' .github/actions/setup-go/action.yaml)
GO_VERSION_FLAKE_NIX=$(grep -Eo '\bgo_[0-9]+_[0-9]+\b' ./flake.nix)
# Convert to major.minor format.
GO_VERSION_FLAKE_NIX_MAJOR_MINOR=$(echo "$GO_VERSION_FLAKE_NIX" | cut -d '_' -f 2-3 | tr '_' '.')
log "INFO : go.mod : $GO_VERSION_GO_MOD"
log "INFO : mise.toml : $GO_VERSION_MISE_TOML"
log "INFO : setup-go/action.yaml : $GO_VERSION_SETUP_GO"
log "INFO : flake.nix : $GO_VERSION_FLAKE_NIX_MAJOR_MINOR"
if [ "$GO_VERSION_GO_MOD" != "$GO_VERSION_MISE_TOML" ]; then
error "Go version mismatch between go.mod and mise.toml"
fi
if [ "$GO_VERSION_GO_MOD" != "$GO_VERSION_SETUP_GO" ]; then
error "Go version mismatch between go.mod and .github/actions/setup-go/action.yaml"
fi
# At the time of writing, Nix only constrains the major.minor version.
# We need to check that specifically.
if [ "$IGNORE_NIX" = "false" ]; then
+150
View File
@@ -0,0 +1,150 @@
#!/usr/bin/env bash
# This script checks the mise values used by CI and dogfood images:
# - mise.toml min_version is the source of truth for the mise version.
# - .github/actions/setup-mise/checksums.toml stores pinned binary checksums.
# - .github/actions/setup-mise/action.yml
# - flake.nix
# - scripts/dogfood/mise-oci-wrapper.sh
# - dogfood/coder/ubuntu-*/Dockerfile.base
set -euo pipefail
# shellcheck source=scripts/lib.sh
source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
cdroot
check_not_empty() {
local label="$1"
local value="$2"
log "INFO : ${label}: ${value}"
if [[ -z "${value}" ]]; then
error "Missing mise value for ${label}"
fi
}
check_equal() {
local label="$1"
local actual="$2"
local expected="$3"
check_not_empty "${label}" "${actual}"
if [[ "${actual}" != "${expected}" ]]; then
error "Mise mismatch for ${label}: expected ${expected}, got ${actual}"
fi
}
check_sha256_format() {
local label="$1"
local value="$2"
if [[ -z "${value}" ]]; then
error "Missing mise value for ${label}"
fi
if [[ ! "${value}" =~ ^[a-f0-9]{64}$ ]]; then
error "Expected 64-character lowercase SHA256 for ${label}: ${value}"
fi
}
mise_version="$(sed -n 's/^min_version = "\([^"]*\)"/\1/p' mise.toml)"
check_not_empty "mise.toml min_version" "${mise_version}"
action_version="$(
awk '
$1 == "mise-version:" { in_input = 1; next }
in_input && /^ [A-Za-z0-9_-]+:/ { exit }
in_input && $1 == "default:" {
gsub(/"/, "", $2)
print $2
exit
}
' .github/actions/setup-mise/action.yml
)"
check_equal ".github/actions/setup-mise/action.yml" "${action_version}" "${mise_version}"
checksum_version="$(
awk -v version="${mise_version}" '
$0 == "[\"" version "\"]" {
print version
exit
}
' .github/actions/setup-mise/checksums.toml
)"
check_equal ".github/actions/setup-mise/checksums.toml" "${checksum_version}" "${mise_version}"
declare -A setup_mise_checksums=()
for target in linux-x64 linux-arm64 macos-x64 macos-arm64 windows-x64; do
checksum="$(./scripts/mise_checksum.sh .github/actions/setup-mise/checksums.toml "${mise_version}" "${target}")"
check_not_empty ".github/actions/setup-mise/checksums.toml ${target}" "${checksum}"
check_sha256_format ".github/actions/setup-mise/checksums.toml ${target}" "${checksum}"
setup_mise_checksums["${target}"]="${checksum}"
done
linux_x64_checksum="${setup_mise_checksums["linux-x64"]}"
sri_sha256_to_hex() {
local label="$1"
local sri="$2"
if [[ "${sri}" != sha256-* ]]; then
error "Expected SRI SHA256 hash for ${label}: ${sri}"
fi
printf '%s' "${sri#sha256-}" | openssl base64 -A -d | od -An -tx1 -v | tr -d ' \n'
}
flake_version="$(
awk '
/^[[:space:]]*mise = / { in_mise = 1; next }
in_mise && /^[[:space:]]*version = / {
gsub(/[";]/, "", $3)
print $3
exit
}
in_mise && /^[[:space:]]*};/ { exit }
' flake.nix
)"
check_equal "flake.nix" "${flake_version}" "${mise_version}"
declare -A flake_targets=(
["x86_64-linux"]="linux-x64"
["aarch64-linux"]="linux-arm64"
["x86_64-darwin"]="macos-x64"
["aarch64-darwin"]="macos-arm64"
)
for system in "${!flake_targets[@]}"; do
target="${flake_targets[${system}]}"
expected_checksum="${setup_mise_checksums[${target}]}"
flake_hash="$(
awk -v nix_system="${system}" '
/^[[:space:]]*hash = \{/ { in_hash = 1; next }
in_hash && $1 == nix_system {
gsub(/[";]/, "", $3)
print $3
exit
}
in_hash && /^[[:space:]]*};/ { exit }
' flake.nix
)"
check_not_empty "flake.nix ${system} hash" "${flake_hash}"
actual_checksum="$(sri_sha256_to_hex "flake.nix ${system}" "${flake_hash}")"
check_equal "flake.nix ${system} sha256" "${actual_checksum}" "${expected_checksum}"
done
wrapper_version="$(sed -n 's/^MISE_VERSION="v\([^"]*\)"/\1/p' scripts/dogfood/mise-oci-wrapper.sh)"
check_equal "scripts/dogfood/mise-oci-wrapper.sh" "${wrapper_version}" "${mise_version}"
wrapper_checksum="$(sed -n 's/^MISE_SHA256="\([a-f0-9]*\)"/\1/p' scripts/dogfood/mise-oci-wrapper.sh)"
check_equal "scripts/dogfood/mise-oci-wrapper.sh sha256" "${wrapper_checksum}" "${linux_x64_checksum}"
check_sha256_format "scripts/dogfood/mise-oci-wrapper.sh sha256" "${wrapper_checksum}"
for dockerfile in dogfood/coder/ubuntu-*/Dockerfile.base; do
dockerfile_version="$(sed -n 's/.*MISE_VERSION=v\([0-9.]*\).*/\1/p' "${dockerfile}" | head -n 1)"
check_equal "${dockerfile}" "${dockerfile_version}" "${mise_version}"
dockerfile_checksum="$(sed -n 's/.*MISE_SHA256=\([a-f0-9]*\).*/\1/p' "${dockerfile}" | head -n 1)"
check_equal "${dockerfile} sha256" "${dockerfile_checksum}" "${linux_x64_checksum}"
check_sha256_format "${dockerfile} sha256" "${dockerfile_checksum}"
done
log "Mise version check passed, all versions are ${mise_version}"
+30
View File
@@ -0,0 +1,30 @@
#!/usr/bin/env bash
# Print the pinned mise SHA256 checksum for a version and release target.
set -euo pipefail
if [[ "$#" -ne 3 ]]; then
echo "usage: $0 <checksums.toml> <mise-version> <target>" >&2
exit 1
fi
checksums_file="$1"
mise_version="$2"
target="$3"
awk -F= -v version="${mise_version}" -v target="${target}" '
$0 == "[\"" version "\"]" { in_table = 1; next }
/^\[/ { in_table = 0 }
in_table {
key = $1
gsub(/^[[:space:]]+|[[:space:]]+$/, "", key)
if (key == target) {
value = $2
gsub(/^[[:space:]]+|[[:space:]]+$/, "", value)
gsub(/^"|"$/, "", value)
print value
exit
}
}
' "${checksums_file}"
+4 -62
View File
@@ -1,7 +1,6 @@
#!/usr/bin/env bash
# This script determines if a commit in either the main branch or a
# `release/x.y` branch should be deployed to dogfood.
# This script determines if the current branch should be deployed to dogfood.
#
# To avoid masking unrelated failures, this script will return 0 in either case,
# and will print `DEPLOY` or `NOOP` to stdout.
@@ -11,73 +10,16 @@ set -euo pipefail
source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
cdroot
deploy_branch=main
# Determine the current branch name and check that it is one of the supported
# branch names.
branch_name=$(git branch --show-current)
# Short circuit: we no longer deploy release branches to dogfood, and instead
# test them on the stable deployment.
# We no longer deploy release branches to dogfood, and instead test them on the
# stable deployment.
# TODO: once we're happy with the new deployment process, we can remove this
# script and the related github workflow stuff.
# script and the related GitHub workflow.
if [[ "$branch_name" == "main" ]]; then
log "VERDICT: DEPLOY"
echo "DEPLOY" # stdout
exit 0
else
log "VERDICT: NOOP"
echo "NOOP" # stdout
exit 0
fi
if [[ "$branch_name" != "main" && ! "$branch_name" =~ ^release/[0-9]+\.[0-9]+$ ]]; then
error "Current branch '$branch_name' is not a supported branch name for dogfood, must be 'main' or 'release/x.y'"
fi
log "Current branch '$branch_name'"
# Determine the remote name
remote=$(git remote -v | grep coder/coder | awk '{print $1}' | head -n1)
if [[ -z "${remote}" ]]; then
error "Could not find remote for coder/coder"
fi
log "Using remote '$remote'"
# Step 1: List all release branches and sort them by major/minor so we can find
# the latest release branch.
release_branches=$(
git branch -r --format='%(refname:short)' |
grep -E "${remote}/release/[0-9]+\.[0-9]+$" |
sed "s|${remote}/||" |
sort -V
)
# As a sanity check, release/2.26 should exist.
if ! echo "$release_branches" | grep "release/2.26" >/dev/null; then
error "Could not find existing release branches. Did you run 'git fetch -ap ${remote}'?"
fi
latest_release_branch=$(echo "$release_branches" | tail -n 1)
latest_release_branch_version=${latest_release_branch#release/}
log "Latest release branch: $latest_release_branch"
log "Latest release branch version: $latest_release_branch_version"
# Step 2: check if a matching tag `v<x.y>.0` exists. If it does not, we will
# use the release branch as the deploy branch.
if ! git rev-parse "refs/tags/v${latest_release_branch_version}.0" >/dev/null 2>&1; then
log "Tag 'v${latest_release_branch_version}.0' does not exist, using release branch as deploy branch"
deploy_branch=$latest_release_branch
else
log "Matching tag 'v${latest_release_branch_version}.0' exists, using main as deploy branch"
fi
log "Deploy branch: $deploy_branch"
# Finally, check if the current branch is the deploy branch.
log
if [[ "$branch_name" != "$deploy_branch" ]]; then
log "VERDICT: DO NOT DEPLOY"
echo "NOOP" # stdout
else
log "VERDICT: DEPLOY"
echo "DEPLOY" # stdout
fi
-46
View File
@@ -1,46 +0,0 @@
#!/usr/bin/env bash
# Usage: ./zizmor.sh [args...]
#
# This script is a wrapper around the zizmor Docker image. Zizmor lints GitHub
# actions workflows.
#
# We use Docker to run zizmor since it's written in Rust and is difficult to
# install on Ubuntu runners without building it with a Rust toolchain, which
# takes a long time.
#
# The repo is mounted at /repo and the working directory is set to /repo.
set -euo pipefail
# shellcheck source=scripts/lib.sh
source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
cdroot
image_tag="ghcr.io/zizmorcore/zizmor:1.11.0"
docker_args=(
"--rm"
"--volume" "$(pwd):/repo"
"--workdir" "/repo"
"--network" "host"
)
if [[ -t 0 ]]; then
docker_args+=("-it")
fi
# If no GH_TOKEN is set, try to get one from `gh auth token`.
if [[ "${GH_TOKEN:-}" == "" ]] && command -v gh &>/dev/null; then
set +e
GH_TOKEN="$(gh auth token)"
export GH_TOKEN
set -e
fi
# Pass through the GitHub token if it's set, which allows zizmor to scan
# imported workflows too.
if [[ "${GH_TOKEN:-}" != "" ]]; then
docker_args+=("--env" "GH_TOKEN")
fi
logrun exec docker run "${docker_args[@]}" "$image_tag" "$@"