-
Notifications
You must be signed in to change notification settings - Fork 2
ci: enforce version sync invariant #34
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
Open
lexfrei
wants to merge
5
commits into
main
Choose a base branch
from
ci/version-sync-invariant
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
2a9230b
ci: enforce version sync invariant across dependency groups
lexfrei d3787be
ci: address review feedback on version-sync invariant
lexfrei d6dc0ba
ci: tighten version-invariant coverage after second review
lexfrei 24e7993
ci(versions): use full variable name in cozy-installer report label
lexfrei 50961b9
ci(versions): guard against silent empty yq extractions
lexfrei File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,123 @@ | ||
| #!/usr/bin/env bash | ||
| # Enforce that version strings stay in sync across files that must agree. | ||
| # Three independent invariants are checked; any drift fails the run. | ||
| # | ||
| # 1. cozy-installer chart version: | ||
| # - galaxy.yml:version | ||
| # - roles/cozystack/defaults/main.yml:cozystack_chart_version | ||
| # - examples/{rhel,suse,ubuntu}/requirements.yml: cozystack.installer.version | ||
| # (leading "v" normalised away before comparison so formats can vary) | ||
| # | ||
| # 2. k3s binary version: | ||
| # - tests/ci-inventory.yml:k3s_version | ||
| # - examples/{rhel,suse,ubuntu}/inventory.yml:k3s_version | ||
| # | ||
| # 3. k3s.orchestration collection version: | ||
| # - tests/requirements.yml: k3s.orchestration.version | ||
| # - examples/{rhel,suse,ubuntu}/requirements.yml: k3s.orchestration.version | ||
| # | ||
| # Requires mikefarah/yq (preinstalled on GitHub-hosted ubuntu runners). | ||
|
|
||
| set -euo pipefail | ||
| # Propagate failures from command substitutions ($(...)) into the outer | ||
| # assignment so a yq extraction error is not silently swallowed into an | ||
| # empty value. Requires bash 4.4+; ubuntu-latest and macOS brew-bash both | ||
| # qualify. | ||
| shopt -s inherit_errexit | ||
|
|
||
| if ! command -v yq >/dev/null 2>&1; then | ||
| echo "check-versions.sh: yq (mikefarah) is required but was not found on PATH" >&2 | ||
| exit 2 | ||
| fi | ||
|
|
||
| cd "$(dirname "$0")/.." | ||
|
|
||
| get_collection_version() { | ||
| local file="$1" name="$2" | ||
| NAME="$name" yq --exit-status \ | ||
| '(.collections[] | select(.name == strenv(NAME)) | .version)' "$file" | ||
| } | ||
|
|
||
| strip_v() { | ||
| printf '%s\n' "${1#v}" | ||
| } | ||
|
|
||
| # Compare an arbitrary number of (label, value) pairs; returns 0 if all | ||
| # values are equal, 1 otherwise. Prints OK/DRIFT report. | ||
| report() { | ||
| local label="$1" | ||
| shift | ||
| local -a pairs=("$@") | ||
| local first="${pairs[1]}" | ||
| if [ -z "$first" ]; then | ||
| printf 'DRIFT in %s: reference value is empty (yq extraction failed?)\n' \ | ||
| "$label" >&2 | ||
| return 1 | ||
| fi | ||
| local drift=0 | ||
| local i | ||
| for ((i = 1; i < ${#pairs[@]}; i += 2)); do | ||
| if [ "${pairs[i]}" != "$first" ]; then | ||
| drift=1 | ||
| break | ||
| fi | ||
| done | ||
| if [ "$drift" -eq 1 ]; then | ||
| printf 'DRIFT in %s:\n' "$label" >&2 | ||
| for ((i = 0; i < ${#pairs[@]}; i += 2)); do | ||
| printf ' %-48s = %s\n' "${pairs[i]}" "${pairs[i + 1]}" >&2 | ||
| done | ||
| return 1 | ||
| fi | ||
| printf 'OK %-20s = %s\n' "$label" "$first" | ||
| return 0 | ||
| } | ||
|
|
||
| err=0 | ||
|
|
||
| # 1. cozy-installer — normalise every value with strip_v so future format | ||
| # choices (e.g. adding a "v" to galaxy.yml) stay equivalent. | ||
| cozy_galaxy=$(strip_v "$(yq --exit-status '.version' galaxy.yml)") | ||
| cozy_role=$(strip_v "$(yq --exit-status '.cozystack_chart_version' roles/cozystack/defaults/main.yml)") | ||
| cozy_rhel=$(strip_v "$(get_collection_version examples/rhel/requirements.yml cozystack.installer)") | ||
| cozy_suse=$(strip_v "$(get_collection_version examples/suse/requirements.yml cozystack.installer)") | ||
| cozy_ubuntu=$(strip_v "$(get_collection_version examples/ubuntu/requirements.yml cozystack.installer)") | ||
|
|
||
| report "cozy-installer" \ | ||
| "galaxy.yml:version" "$cozy_galaxy" \ | ||
| "roles/cozystack/defaults/main.yml:cozystack_chart_version" "$cozy_role" \ | ||
| "examples/rhel/requirements.yml" "$cozy_rhel" \ | ||
| "examples/suse/requirements.yml" "$cozy_suse" \ | ||
| "examples/ubuntu/requirements.yml" "$cozy_ubuntu" \ | ||
| || err=1 | ||
|
|
||
| # 2. k3s binary — no strip_v: every inventory uses the v-prefixed | ||
| # k3s_version form (e.g. "v1.35.3+k3s1"), so values are already | ||
| # directly comparable. Adding a new inventory without the "v" prefix | ||
| # would intentionally fail this check. | ||
| k3s_ci=$(yq --exit-status '.cluster.vars.k3s_version' tests/ci-inventory.yml) | ||
| k3s_rhel=$(yq --exit-status '.cluster.vars.k3s_version' examples/rhel/inventory.yml) | ||
| k3s_suse=$(yq --exit-status '.cluster.vars.k3s_version' examples/suse/inventory.yml) | ||
| k3s_ubuntu=$(yq --exit-status '.cluster.vars.k3s_version' examples/ubuntu/inventory.yml) | ||
|
|
||
| report "k3s" \ | ||
| "tests/ci-inventory.yml" "$k3s_ci" \ | ||
| "examples/rhel/inventory.yml" "$k3s_rhel" \ | ||
| "examples/suse/inventory.yml" "$k3s_suse" \ | ||
| "examples/ubuntu/inventory.yml" "$k3s_ubuntu" \ | ||
| || err=1 | ||
|
|
||
| # 3. k3s.orchestration | ||
| orch_tests=$(get_collection_version tests/requirements.yml k3s.orchestration) | ||
| orch_rhel=$(get_collection_version examples/rhel/requirements.yml k3s.orchestration) | ||
| orch_suse=$(get_collection_version examples/suse/requirements.yml k3s.orchestration) | ||
| orch_ubuntu=$(get_collection_version examples/ubuntu/requirements.yml k3s.orchestration) | ||
|
|
||
| report "k3s.orchestration" \ | ||
| "tests/requirements.yml" "$orch_tests" \ | ||
| "examples/rhel/requirements.yml" "$orch_rhel" \ | ||
| "examples/suse/requirements.yml" "$orch_suse" \ | ||
| "examples/ubuntu/requirements.yml" "$orch_ubuntu" \ | ||
| || err=1 | ||
|
|
||
| exit "$err" | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| #!/usr/bin/env bash | ||
| # Tests for hack/check-versions.sh. | ||
| # | ||
| # Copies the repo tree to a tmpdir, runs the script once against the clean | ||
| # tree (expect exit 0), then for each invariant perturbs a single file and | ||
| # asserts exit 1 with the expected "DRIFT in <group>" label on stderr. | ||
| # Ensures a future refactor of check-versions.sh does not silently stop | ||
| # detecting drift — it must always detect and exit nonzero. | ||
|
|
||
| set -euo pipefail | ||
|
|
||
| SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | ||
| REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" | ||
|
|
||
| tmpdir=$(mktemp -d) | ||
| trap 'rm -rf "$tmpdir"' EXIT | ||
|
|
||
| rsync --archive --exclude='.git' --exclude='*.bak' "$REPO_ROOT"/ "$tmpdir"/ | ||
|
|
||
| fail() { | ||
| echo "TEST FAIL: $*" >&2 | ||
| exit 1 | ||
| } | ||
|
|
||
| # Positive: clean tree reports no drift. | ||
| echo "-- positive: clean tree must exit 0 --" | ||
| if ! (cd "$tmpdir" && ./hack/check-versions.sh) >/dev/null; then | ||
| fail "clean tree unexpectedly reported drift" | ||
| fi | ||
|
|
||
| # Negative helper: mutate a single field in a copy of the repo, run the | ||
| # script, expect nonzero exit and the right DRIFT label on stderr. | ||
| run_negative() { | ||
| local label="$1" file="$2" yq_expr="$3" new_value="$4" | ||
| echo "-- negative: perturb ${file} (${label}) --" | ||
| cp "$tmpdir/$file" "$tmpdir/$file.bak" | ||
| NEW="$new_value" yq --inplace "$yq_expr" "$tmpdir/$file" | ||
| local stderr_file="$tmpdir/stderr.log" | ||
| local rc=0 | ||
| (cd "$tmpdir" && ./hack/check-versions.sh) 2>"$stderr_file" >/dev/null || rc=$? | ||
| mv "$tmpdir/$file.bak" "$tmpdir/$file" | ||
| if [ "$rc" -eq 0 ]; then | ||
| cat "$stderr_file" >&2 | ||
| fail "perturbed ${file} but script returned 0 (expected nonzero)" | ||
| fi | ||
| if ! grep --quiet "DRIFT in ${label}" "$stderr_file"; then | ||
| cat "$stderr_file" >&2 | ||
| fail "perturbed ${file} but stderr did not contain 'DRIFT in ${label}'" | ||
| fi | ||
| } | ||
|
|
||
| run_negative "cozy-installer" "galaxy.yml" \ | ||
| '.version = strenv(NEW)' "0.0.0-test" | ||
|
|
||
| run_negative "k3s" "tests/ci-inventory.yml" \ | ||
| '.cluster.vars.k3s_version = strenv(NEW)' "v0.0.0-test" | ||
|
|
||
| # Perturb a middle entry (not the first one paired to report()) to guard | ||
| # against a future refactor of report()'s "reference value" logic silently | ||
| # missing drift in anything but the reference file. | ||
| run_negative "k3s" "examples/suse/inventory.yml" \ | ||
| '.cluster.vars.k3s_version = strenv(NEW)' "v0.0.0-test" | ||
|
|
||
| run_negative "k3s.orchestration" "tests/requirements.yml" \ | ||
| '(.collections[] | select(.name == "k3s.orchestration") | .version) = strenv(NEW)' "0.0.0-test" | ||
|
|
||
| # Silent-failure guard: if a tracked key is deleted (yq extraction yields | ||
| # an error / empty), the script must still exit nonzero instead of | ||
| # reporting OK on an empty string. Covers the inherit_errexit and the | ||
| # empty-first guard in report(). | ||
| echo "-- negative: delete galaxy.yml:version key (must not silently report OK) --" | ||
| cp "$tmpdir/galaxy.yml" "$tmpdir/galaxy.yml.bak" | ||
| yq --inplace 'del(.version)' "$tmpdir/galaxy.yml" | ||
| rc=0 | ||
| (cd "$tmpdir" && ./hack/check-versions.sh) >/dev/null 2>&1 || rc=$? | ||
| mv "$tmpdir/galaxy.yml.bak" "$tmpdir/galaxy.yml" | ||
| if [ "$rc" -eq 0 ]; then | ||
| fail "deleted galaxy.yml:version but script returned 0" | ||
| fi | ||
|
|
||
| echo "OK: all check-versions tests passed" |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.