mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
ci: refactor CI to use mise for shared tool setup (#25727)
This commit is contained in:
@@ -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
|
||||
|
||||
Executable
+150
@@ -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}"
|
||||
Executable
+30
@@ -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}"
|
||||
@@ -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
|
||||
|
||||
@@ -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" "$@"
|
||||
Reference in New Issue
Block a user