From 166c1b1c2ae2f8e61ce5ba39ff5923e6bee8c20c Mon Sep 17 00:00:00 2001 From: Allison Thackston <73732028+althack@users.noreply.github.com> Date: Mon, 17 Aug 2026 15:08:28 -0700 Subject: [PATCH] Keep internal feature references on the release major --- .github/actions/smoke-test/build.sh | 2 +- .github/scripts/set-devcontainer-version.sh | 34 ++++++++++++ .../scripts/test-set-devcontainer-version.sh | 54 +++++++++++++++++++ .github/workflows/release.yaml | 11 ++++ .github/workflows/test-pr.yaml | 11 ++++ 5 files changed, 111 insertions(+), 1 deletion(-) create mode 100755 .github/scripts/test-set-devcontainer-version.sh diff --git a/.github/actions/smoke-test/build.sh b/.github/actions/smoke-test/build.sh index 3148347..16a6160 100755 --- a/.github/actions/smoke-test/build.sh +++ b/.github/actions/smoke-test/build.sh @@ -64,7 +64,7 @@ if [ "${TEMPLATE_ID}" = "gz" ]; then mkdir -p "${SRC_DIR}/.devcontainer/local-features" for feature in linux-x11-forwarding linux-pulseaudio-forwarding; do cp -R "features/src/${feature}" "${SRC_DIR}/.devcontainer/local-features/${feature}" - sed -i "s#ghcr.io/althack/devcontainers/${feature}:0#./local-features/${feature}#g" \ + sed -E -i "s#ghcr.io/althack/devcontainers/${feature}:[0-9]+#./local-features/${feature}#g" \ "${SRC_DIR}/.devcontainer/devcontainer.json" done fi diff --git a/.github/scripts/set-devcontainer-version.sh b/.github/scripts/set-devcontainer-version.sh index 28fa405..0c81f1d 100755 --- a/.github/scripts/set-devcontainer-version.sh +++ b/.github/scripts/set-devcontainer-version.sh @@ -21,6 +21,8 @@ if [[ "${version}" == "0.0.0" ]]; then exit 1 fi +major="${version%%.*}" + mapfile -t manifest_files < <( find features/src templates/src -type f \ \( -name 'devcontainer-feature.json' -o -name 'devcontainer-template.json' \) \ @@ -40,6 +42,27 @@ for manifest_file in "${manifest_files[@]}"; do echo "Updated ${manifest_file} to version ${version}" done +mapfile -t template_json_files < <(find templates/src -type f -name '*.json' | sort) + +for json_file in "${template_json_files[@]}"; do + tmp_file="$(mktemp)" + jq --arg major "${major}" ' + walk( + if type == "string" then + gsub( + "ghcr\\.io/althack/devcontainers/(?[A-Za-z0-9._-]+):0"; + "ghcr.io/althack/devcontainers/\\(.feature):\\($major)" + ) + else + . + end + ) + ' "${json_file}" > "${tmp_file}" + chmod --reference="${json_file}" "${tmp_file}" + mv "${tmp_file}" "${json_file}" + echo "Updated internal Feature references in ${json_file} to major ${major}" +done + for manifest_file in "${manifest_files[@]}"; do manifest_version="$(jq -r '.version' "${manifest_file}")" if [[ "${manifest_version}" != "${version}" ]]; then @@ -47,3 +70,14 @@ for manifest_file in "${manifest_files[@]}"; do exit 1 fi done + +while IFS= read -r internal_reference; do + if [[ ! "${internal_reference}" =~ ^ghcr\.io/althack/devcontainers/[^:]+:${major}$ ]]; then + echo "${internal_reference} does not use release major ${major}." >&2 + exit 1 + fi +done < <( + for json_file in "${template_json_files[@]}"; do + jq -r '.. | strings | select(test("^ghcr\\.io/althack/devcontainers/[^:]+:[^:]+$"))' "${json_file}" + done +) diff --git a/.github/scripts/test-set-devcontainer-version.sh b/.github/scripts/test-set-devcontainer-version.sh new file mode 100755 index 0000000..1c10d3e --- /dev/null +++ b/.github/scripts/test-set-devcontainer-version.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash + +set -euo pipefail + +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +repo_dir="$(cd "${script_dir}/../.." && pwd)" +test_root="$(mktemp -d)" +trap 'rm -rf "${test_root}"' EXIT + +run_case() { + local version="$1" + local expected_major="$2" + local case_dir="${test_root}/${version}" + mkdir -p "${case_dir}/.github/scripts" + cp -R "${repo_dir}/features" "${case_dir}/features" + cp -R "${repo_dir}/templates" "${case_dir}/templates" + cp "${repo_dir}/.github/scripts/set-devcontainer-version.sh" "${case_dir}/.github/scripts/" + + ( + cd "${case_dir}" + bash .github/scripts/set-devcontainer-version.sh "${version}" + ) + + while IFS= read -r manifest_file; do + [[ "$(jq -r '.version' "${case_dir}/${manifest_file}")" == "${version}" ]] + done < <( + cd "${case_dir}" + find features/src templates/src -type f \ + \( -name 'devcontainer-feature.json' -o -name 'devcontainer-template.json' \) \ + | sort + ) + + while IFS= read -r internal_reference; do + [[ "${internal_reference}" =~ ^ghcr\.io/althack/devcontainers/[^:]+:${expected_major}$ ]] + done < <( + find "${case_dir}/templates/src" -type f -name '*.json' -print0 \ + | xargs -0 -r -n1 jq -r '.. | strings | select(test("^ghcr\\.io/althack/devcontainers/[^:]+:[^:]+$"))' + ) + + mapfile -t json_files < <(find "${case_dir}" -type f -name '*.json' | sort) + sha256sum "${json_files[@]}" > "${case_dir}/before.sha256" + ( + cd "${case_dir}" + bash .github/scripts/set-devcontainer-version.sh "${version}" + ) + sha256sum "${json_files[@]}" > "${case_dir}/after.sha256" + cmp "${case_dir}/before.sha256" "${case_dir}/after.sha256" +} + +run_case 1.0.0 1 +run_case 2.3.0 2 +run_case 2.0.0-rc.1 2 + +echo "Release version stamping tests passed." diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index ea6eb30..e916bc8 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -92,6 +92,7 @@ jobs: EXPECTED_VERSION: ${{ steps.settings.outputs.version }} run: | set -euo pipefail + expected_major="${EXPECTED_VERSION%%.*}" while IFS= read -r manifest_file; do manifest_version="$(jq -r '.version' "${manifest_file}")" @@ -105,6 +106,16 @@ jobs: | sort ) + while IFS= read -r internal_reference; do + if [[ ! "${internal_reference}" =~ ^ghcr\.io/althack/devcontainers/[^:]+:${expected_major}$ ]]; then + echo "${internal_reference} does not use release major ${expected_major}." >&2 + exit 1 + fi + done < <( + find templates/src -type f -name '*.json' -print0 \ + | xargs -0 -r -n1 jq -r '.. | strings | select(test("^ghcr\\.io/althack/devcontainers/[^:]+:[^:]+$"))' + ) + - name: Package release source bundle if: ${{ github.event_name == 'release' }} id: package diff --git a/.github/workflows/test-pr.yaml b/.github/workflows/test-pr.yaml index a2b6da1..2f96f97 100644 --- a/.github/workflows/test-pr.yaml +++ b/.github/workflows/test-pr.yaml @@ -196,6 +196,14 @@ jobs: - name: Verify generated docs are up to date run: git diff --exit-code -- features/src templates/src + release-version-test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Test release version stamping + run: bash .github/scripts/test-set-devcontainer-version.sh + complete: needs: - docs-check @@ -207,6 +215,7 @@ jobs: - ros2-idempotency-test - ros2-scenario-tests - smoke-test + - release-version-test if: always() runs-on: ubuntu-latest steps: @@ -221,6 +230,7 @@ jobs: ROS2_SCENARIO_TESTS_RESULT: ${{ needs.ros2-scenario-tests.result }} FEATURE_PACKAGE_RESULT: ${{ needs.feature-package.result }} DOCS_CHECK_RESULT: ${{ needs.docs-check.result }} + RELEASE_VERSION_TEST_RESULT: ${{ needs.release-version-test.result }} run: | set -euo pipefail @@ -243,5 +253,6 @@ jobs: check_result "ros2-scenario-tests" "${ROS2_SCENARIO_TESTS_RESULT}" check_result "feature-package" "${FEATURE_PACKAGE_RESULT}" check_result "docs-check" "${DOCS_CHECK_RESULT}" + check_result "release-version-test" "${RELEASE_VERSION_TEST_RESULT}" echo "All required jobs completed successfully."