diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 04f7b0e..ccb6a5b 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -10,7 +10,7 @@ on: jobs: call-docker-build: - uses: ycpss91255-docker/template/.github/workflows/build-worker.yaml@v0.10.0-rc2 + uses: ycpss91255-docker/template/.github/workflows/build-worker.yaml@v0.10.0 with: image_name: ros1_bridge build_runtime: true @@ -19,6 +19,6 @@ jobs: call-release: needs: call-docker-build if: startsWith(github.ref, 'refs/tags/') - uses: ycpss91255-docker/template/.github/workflows/release-worker.yaml@v0.10.0-rc2 + uses: ycpss91255-docker/template/.github/workflows/release-worker.yaml@v0.10.0 with: archive_name_prefix: ros1_bridge diff --git a/doc/changelog/CHANGELOG.md b/doc/changelog/CHANGELOG.md index 1e66bec..77d702d 100644 --- a/doc/changelog/CHANGELOG.md +++ b/doc/changelog/CHANGELOG.md @@ -8,12 +8,14 @@ versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] ### Changed -- Upgrade `template/` subtree to [v0.10.0-rc2](https://github.com/ycpss91255-docker/template/releases/tag/v0.10.0-rc2). Bundles: +- Upgrade `template/` subtree to [v0.10.0](https://github.com/ycpss91255-docker/template/releases/tag/v0.10.0). Bundles: - **Compose `runtime` service** auto-emitted by `setup.sh` when Dockerfile declares `FROM … AS runtime` (closes template #108). `./run.sh -t runtime` no longer errors with "no such service". - **`run.sh` arg realignment** (closes template #118, BREAKING): target is now `-t/--target` flag (default `devel`); positional args become CMD passthrough (empty → Dockerfile CMD, non-empty → override); `-d + cmd` → exit 2 error. `./run.sh` bare unchanged (devel bash). Migration inside this repo: `./run.sh runtime` now written as `./run.sh -t runtime` (auto-runs `parameter_bridge` attached). `./run.sh -t runtime bash` drops into runtime shell for debug. - - **arm64 test-tools** hotfix — `Dockerfile.test-tools` `ARG TARGETARCH=amd64` default used to shadow BuildKit's auto-inject (moby/buildkit#3403), so `:v0.9.13` / `:v0.10.0-rc1` GHCR arm64 variants shipped x86_64 shellcheck / hadolint. v0.10.0-rc2 drops the default; arm64 binaries are now genuinely aarch64 (verified via `docker cp` + `file`). - - Intermediate releases (v0.9.11/12/13, v0.10.0-rc1) are superseded; this PR pins `main.yaml` directly to `@v0.10.0-rc2`. -- Pin `main.yaml` reusable workflows to [`@v0.10.0-rc2`](https://github.com/ycpss91255-docker/template/releases/tag/v0.10.0-rc2). + - **arm64 test-tools** hotfix — `Dockerfile.test-tools` `ARG TARGETARCH=amd64` default used to shadow BuildKit's auto-inject (moby/buildkit#3403), so `:v0.9.13` / `:v0.10.0-rc1` GHCR arm64 variants shipped x86_64 shellcheck / hadolint. v0.10.0-rc2+ drops the default; arm64 binaries are now genuinely aarch64 (verified via `docker cp` + `file`). + - **`--reset-conf` flag on `build.sh`** (closes template #124) — restores `setup.conf` to the template default in one step, backing up the existing `setup.conf` → `setup.conf.bak` and `.env` → `.env.bak` first. `-y` / `--yes` skips the confirmation prompt. + - **`upgrade.sh` sed regex fix** (closes template #61) — handles semver pre-release tags so future RC upgrades stop producing `-rcN-rcM` double suffixes. + - Intermediate releases (v0.9.11 through v0.10.0-rc2) are superseded; this PR pins `main.yaml` directly to `@v0.10.0`. +- Pin `main.yaml` reusable workflows to [`@v0.10.0`](https://github.com/ycpss91255-docker/template/releases/tag/v0.10.0). - Rebuild `devel` stage from `ros:foxy-ros-base-focal` (multi-arch) plus the ROS 1 snapshot apt repo instead of the amd64-only `osrf/ros:foxy-ros1-bridge`. Enables Jetson (arm64) support. - `ENV ROS1_DISTRO=noetic` / `ENV ROS2_DISTRO=foxy` now baked into the image so downstream scripts can reference the distro names without hardcoding. - Test stage lint target uses `COPY script/*.sh /lint/` (glob) to pick up new scripts automatically. diff --git a/template/.version b/template/.version index 4b03ff2..bf057db 100644 --- a/template/.version +++ b/template/.version @@ -1 +1 @@ -v0.10.0-rc2 +v0.10.0 diff --git a/template/doc/changelog/CHANGELOG.md b/template/doc/changelog/CHANGELOG.md index d8d7c49..a31c3ab 100644 --- a/template/doc/changelog/CHANGELOG.md +++ b/template/doc/changelog/CHANGELOG.md @@ -7,6 +7,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [v0.10.0] - 2026-04-24 + +First stable minor bump post-v0.9.x. Cuts the rc2 feature work + two fixes. **Recommended upgrade path** for all downstream repos (rc1 / rc2 supersede everything earlier, see rc1/rc2 notes below for the full run-phase UX realignment + arm64 test-tools hotfix). + +### Added +- **`--reset-conf` flag** on `build.sh` (closes #124). Overwrites `setup.conf` with the template default, backing up the previous `setup.conf` → `setup.conf.bak` and `.env` → `.env.bak` first. Interactive confirmation prompt; `-y` / `--yes` skips it. Internally delegates to the new `./template/init.sh --gen-conf --force` backend. Triggers a `setup.sh` rerun afterward so `.env` + `compose.yaml` regenerate from the fresh conf. +- `./template/init.sh --gen-conf --force` — backend for the above. Without `--force`, `--gen-conf` still refuses to clobber an existing `setup.conf` (unchanged default). +- New-repo `.gitignore` template gains `setup.conf.bak` and `.env.bak` entries so the reset backups never get committed by accident. + +### Fixed +- **`upgrade.sh` main.yaml sed regex now handles semver pre-release tags** (closes #61). The prior `[0-9.]*` character class stopped at the first `-`, so upgrading from an existing RC tag (e.g. `v0.10.0-rc1` → `-rc2`) left the old `-rcN` suffix in place and produced `@v0.10.0-rc2-rc1`. First surfaced when ros1_bridge ran `./template/upgrade.sh v0.10.0-rc2` from `@v0.9.13`. Regex now anchored on full semver shape (`\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?`). Two regression tests added covering RC → RC and RC → stable transitions. + +### Release summary + +Cumulative highlights from rc1 + rc2 rolled up here for discoverability: + +- **Run-phase UX realignment (BREAKING, from rc1, closes #118)**: `./run.sh` target now moves behind explicit `-t/--target`; positional args become CMD passthrough matching `docker run [cmd]`. Migration: `./run.sh runtime` → `./run.sh -t runtime`; plain `./run.sh` unchanged. +- **Compose `runtime` service auto-emission (from rc1, closes #108)**: `setup.sh` detects `FROM … AS runtime` in the Dockerfile and emits a paired service extending `devel`, so `./run.sh -t runtime` actually works. +- **arm64 `test-tools` binaries are now genuinely aarch64 (from rc2)**: `Dockerfile.test-tools` `ARG TARGETARCH=amd64` default used to shadow BuildKit's auto-inject (moby/buildkit#3403), shipping x86_64 shellcheck / hadolint inside the arm64 image. v0.10.0-rc2+ drops the default; multi-arch GHCR `:v0.10.0` variants carry the right binaries per arch. + +Downstream repos upgrading from v0.9.x straight to v0.10.0 should: + +1. `./template/upgrade.sh v0.10.0` +2. Dockerfile: adopt `ARG TEST_TOOLS_IMAGE="test-tools:local"` + `FROM ${TEST_TOOLS_IMAGE} AS test-tools-stage` + `COPY --from=test-tools-stage` (see `template/dockerfile/Dockerfile.example`). +3. Audit any `./run.sh ` call sites and rewrite as `./run.sh -t `. + ## [v0.10.0-rc2] - 2026-04-24 Second release candidate. Ships the arm64 test-tools hotfix that v0.10.0-rc1 / v0.9.13 both missed — **strongly recommended** over rc1 for any downstream repo enabling the arm64 build matrix. diff --git a/template/doc/test/TEST.md b/template/doc/test/TEST.md index daca168..98c8b2d 100644 --- a/template/doc/test/TEST.md +++ b/template/doc/test/TEST.md @@ -1,6 +1,6 @@ # TEST.md -Template self-tests: **675 tests** total (632 unit + 43 integration). +Template self-tests: **685 tests** total (642 unit + 43 integration). ## Test Files @@ -92,7 +92,7 @@ a canned response; exercised with `TUI_STUB_RESPONSE` / `TUI_STUB_EXIT`. | `_tui_checklist` (passes `--separate-output`) | 1 | | `_tui_msgbox` / `_tui_yesno` (correct flags, propagates exit code) | 2 | -### test/unit/build_sh_spec.bats (32) +### test/unit/build_sh_spec.bats (35) Unit tests for `build.sh` argument handling and control flow. Uses a sandbox tree mirroring the expected layout (build.sh + `template/` subtree @@ -188,7 +188,7 @@ conditional GPU deploy block + GUI env/volumes + extra volumes from | `runtime detection is robust against weird whitespace` | regex tolerance | | `runtime detection ignores non-runtime stage names` | strict match | -### test/unit/template_spec.bats (116) +### test/unit/template_spec.bats (118) | Test | Description | |------|-------------| @@ -288,6 +288,8 @@ conditional GPU deploy block + GUI env/volumes + extra volumes from | `upgrade.sh --gen-conf delegates to init.sh --gen-conf` | Delegation | | `upgrade.sh --help mentions --gen-conf` | Help text | | `upgrade.sh updates main.yaml @tag without clobbering release-worker.yaml` | sed regression | +| `upgrade.sh main.yaml sed handles semver pre-release tags (RC → RC)` | `-rcN-rcN` regression | +| `upgrade.sh main.yaml sed handles stable → stable + RC → stable transitions` | RC → stable cleanup | | `build-worker.yaml: no legacy in-job test-tools build step` | v0.9.13 GHCR migration | | `build-worker.yaml: resolves template version from GITHUB_WORKFLOW_REF` | GHCR tag resolution | | `build-worker.yaml: test build passes TEST_TOOLS_IMAGE build-arg` | build-arg wiring | @@ -344,7 +346,7 @@ conditional GPU deploy block + GUI env/volumes + extra volumes from | `_run_shellcheck: picks up every .sh file in script/docker/` | `find` covers new scripts | | `_run_shellcheck: exits non-zero when shellcheck fails on any script` | Strict-mode propagation | -### test/unit/init_spec.bats (13) +### test/unit/init_spec.bats (18) Unit coverage for `init.sh` helpers that previous rounds exercised only through the Level-1 integration test. Complements diff --git a/template/init.sh b/template/init.sh index f5e3546..8746cb2 100755 --- a/template/init.sh +++ b/template/init.sh @@ -202,10 +202,14 @@ jobs: YAML _log " Created .github/workflows/main.yaml" - # .gitignore (compose.yaml + .env are setup.sh-generated artifacts) + # .gitignore (compose.yaml + .env are setup.sh-generated artifacts; + # *.bak siblings are written by `./template/init.sh --gen-conf --force` + # on a reset-conf, see #124 / issue #60 — keep them local-only.) cat > .gitignore <<'GIT' .env +.env.bak compose.yaml +setup.conf.bak coverage/ .Dockerfile.generated GIT @@ -278,13 +282,28 @@ _init_existing_repo() { _gen_setup_conf() { local _src="${TEMPLATE_DIR}/setup.conf" local _dst="${REPO_ROOT}/setup.conf" + local _force="${1:-false}" if [[ ! -f "${_src}" ]]; then _error "Template setup.conf not found at ${_src}" fi if [[ -f "${_dst}" ]]; then - _error "setup.conf already exists at ${_dst}. Remove it first or edit directly." + if [[ "${_force}" != "true" ]]; then + _error "setup.conf already exists at ${_dst}. Remove it first or edit directly." + fi + # --force path: back up the existing conf (and .env, since a reset + # will regenerate it from the new conf baseline) to *.bak siblings + # before overwriting. `.gitignore` ignores these so they never get + # committed by accident. + local _bak="${_dst}.bak" + cp -f "${_dst}" "${_bak}" + _log "Backed up existing setup.conf → ${_bak}" + if [[ -f "${REPO_ROOT}/.env" ]]; then + local _env_bak="${REPO_ROOT}/.env.bak" + cp -f "${REPO_ROOT}/.env" "${_env_bak}" + _log "Backed up existing .env → ${_env_bak}" + fi fi - cp "${_src}" "${_dst}" + cp -f "${_src}" "${_dst}" _log "Created ${_dst}" _log "Edit it to customize runtime settings for this repo." } @@ -310,7 +329,7 @@ _error() { printf "[init] ERROR: %s\n" "$*" >&2; exit 1; } main() { if [[ "${1:-}" =~ ^(-h|--help)$ ]]; then cat >&2 <<'EOF' -Usage: ./template/init.sh [--gen-conf] +Usage: ./template/init.sh [--gen-conf [--force]] Initialize a repo with template. Auto-detects: - Has Dockerfile → create symlinks, then run setup.sh @@ -322,7 +341,10 @@ Options: --gen-conf Copy template/setup.conf to /setup.conf so the user can override any section (image / build / deploy / gui / network / volumes). Refuses to overwrite an - existing per-repo setup.conf. + existing per-repo setup.conf unless --force is given. + --force With --gen-conf: overwrite existing setup.conf, + backing up the previous setup.conf to setup.conf.bak + and .env to .env.bak first. Run from the repo root after: git subtree add --prefix=template \ @@ -334,7 +356,9 @@ EOF cd "${REPO_ROOT}" if [[ "${1:-}" == "--gen-conf" ]]; then - _gen_setup_conf + local _force=false + [[ "${2:-}" == "--force" ]] && _force=true + _gen_setup_conf "${_force}" return 0 fi diff --git a/template/script/docker/build.sh b/template/script/docker/build.sh index 347484a..e4f2dac 100755 --- a/template/script/docker/build.sh +++ b/template/script/docker/build.sh @@ -49,12 +49,15 @@ usage() { case "${_LANG}" in zh-TW) cat >&2 <<'EOF' -用法: ./build.sh [-h] [-s|--setup] [--no-cache] [--clean-tools] [--dry-run] [--lang ] [TARGET] +用法: ./build.sh [-h] [-s|--setup] [--reset-conf] [-y|--yes] [--no-cache] [--clean-tools] [--dry-run] [--lang ] [TARGET] 選項: -h, --help 顯示此說明 -s, --setup 強制重跑 setup.sh 重新生成 .env + compose.yaml (預設:.env 不存在時自動 bootstrap;存在時僅印 drift warning) + --reset-conf 用 template 預設值覆蓋 setup.conf(先備份到 setup.conf.bak + + .env.bak;需確認,可用 -y 跳過)。之後會自動重跑 setup。 + -y, --yes 略過 --reset-conf 的互動確認 --no-cache 強制不使用 cache 重建 --clean-tools build 結束後移除 test-tools:local image(預設保留以加速下次 build) --dry-run 只印出將執行的 docker 指令,不實際執行 @@ -68,12 +71,15 @@ EOF ;; zh-CN) cat >&2 <<'EOF' -用法: ./build.sh [-h] [-s|--setup] [--no-cache] [--clean-tools] [--dry-run] [--lang ] [TARGET] +用法: ./build.sh [-h] [-s|--setup] [--reset-conf] [-y|--yes] [--no-cache] [--clean-tools] [--dry-run] [--lang ] [TARGET] 选项: -h, --help 显示此说明 -s, --setup 强制重跑 setup.sh 重新生成 .env + compose.yaml (默认:.env 不存在时自动 bootstrap;存在时仅打印 drift warning) + --reset-conf 用 template 默认值覆盖 setup.conf(先备份到 setup.conf.bak + + .env.bak;需确认,可用 -y 跳过)。之后会自动重跑 setup。 + -y, --yes 跳过 --reset-conf 的交互确认 --no-cache 强制不使用 cache 重建 --clean-tools build 结束后移除 test-tools:local image(默认保留以加速下次 build) --dry-run 只打印将执行的 docker 命令,不实际执行 @@ -87,12 +93,16 @@ EOF ;; ja) cat >&2 <<'EOF' -使用法: ./build.sh [-h] [-s|--setup] [--no-cache] [--clean-tools] [--dry-run] [--lang ] [TARGET] +使用法: ./build.sh [-h] [-s|--setup] [--reset-conf] [-y|--yes] [--no-cache] [--clean-tools] [--dry-run] [--lang ] [TARGET] オプション: -h, --help このヘルプを表示 -s, --setup setup.sh を強制実行して .env + compose.yaml を再生成 (デフォルト:.env が無ければ自動 bootstrap、あれば drift warning のみ) + --reset-conf setup.conf をテンプレのデフォルトで上書き(setup.conf.bak + + .env.bak にバックアップ;確認プロンプト、-y でスキップ)。 + その後 setup を再実行。 + -y, --yes --reset-conf の確認プロンプトをスキップ --no-cache キャッシュを使わず強制リビルド --clean-tools build 終了後に test-tools:local image を削除(デフォルトは保持) --dry-run 実行される docker コマンドを表示するのみ(実行はしない) @@ -106,12 +116,18 @@ EOF ;; *) cat >&2 <<'EOF' -Usage: ./build.sh [-h] [-s|--setup] [--no-cache] [--clean-tools] [--dry-run] [--lang ] [TARGET] +Usage: ./build.sh [-h] [-s|--setup] [--reset-conf] [-y|--yes] [--no-cache] [--clean-tools] [--dry-run] [--lang ] [TARGET] Options: -h, --help Show this help -s, --setup Force rerun setup.sh to regenerate .env + compose.yaml (default: auto-bootstrap if .env missing; warn on drift if present) + --reset-conf Overwrite setup.conf with template defaults (backs up the + existing setup.conf → setup.conf.bak and .env → .env.bak + first). Prompts for confirmation; pass -y to skip. Triggers + a setup.sh rerun afterward so .env + compose.yaml follow + the fresh conf. + -y, --yes Skip the --reset-conf confirmation prompt --no-cache Force rebuild without cache --clean-tools Remove test-tools:local image after build (default: keep for faster next build) --dry-run Print the docker commands that would run, but do not execute @@ -129,6 +145,8 @@ EOF main() { local RUN_SETUP=false + local RESET_CONF=false + local ASSUME_YES=false local NO_CACHE=false local CLEAN_TOOLS=false local TARGET="devel" @@ -143,6 +161,14 @@ main() { RUN_SETUP=true shift ;; + --reset-conf) + RESET_CONF=true + shift + ;; + -y|--yes) + ASSUME_YES=true + shift + ;; --no-cache) NO_CACHE=true shift @@ -168,6 +194,38 @@ main() { done export DRY_RUN + # --reset-conf: delegate to init.sh --gen-conf --force. Confirms unless + # -y/--yes is passed. Backs up the existing setup.conf + .env to + # *.bak siblings (git-ignored) before overwriting, so the reset is + # recoverable. Runs before the normal bootstrap/drift flow below so + # subsequent setup.sh invocation regenerates .env + compose.yaml from + # the fresh conf. + if [[ "${RESET_CONF}" == true ]]; then + local _conf="${FILE_PATH}/setup.conf" + local _env="${FILE_PATH}/.env" + if [[ -f "${_conf}" || -f "${_env}" ]]; then + if [[ "${ASSUME_YES}" != true && "${DRY_RUN}" != true ]]; then + printf "[build] --reset-conf will overwrite:\n" >&2 + [[ -f "${_conf}" ]] && printf " %s (backup → %s.bak)\n" "${_conf}" "${_conf}" >&2 + [[ -f "${_env}" ]] && printf " %s (backup → %s.bak)\n" "${_env}" "${_env}" >&2 + printf "[build] proceed? [y/N] " >&2 + local _reply + read -r _reply + case "${_reply}" in + y|Y|yes|YES) ;; + *) printf "[build] aborted.\n" >&2; exit 1 ;; + esac + fi + fi + if [[ "${DRY_RUN}" == true ]]; then + printf "[dry-run] %s/template/init.sh --gen-conf --force\n" "${FILE_PATH}" + else + bash "${FILE_PATH}/template/init.sh" --gen-conf --force + fi + # Force a fresh setup.sh run so .env + compose.yaml follow the new conf. + RUN_SETUP=true + fi + local _setup="${FILE_PATH}/template/script/docker/setup.sh" local _tui="${FILE_PATH}/setup_tui.sh" diff --git a/template/test/unit/build_sh_spec.bats b/template/test/unit/build_sh_spec.bats index 513ccad..911a853 100644 --- a/template/test/unit/build_sh_spec.bats +++ b/template/test/unit/build_sh_spec.bats @@ -501,3 +501,34 @@ EOS assert_failure assert_output --partial "エラー" } + +# ════════════════════════════════════════════════════════════════════ +# --reset-conf flag (issue #60 / #124) +# ════════════════════════════════════════════════════════════════════ + +@test "build.sh --reset-conf --yes --dry-run prints init.sh --gen-conf --force cmd" { + # -y skips the interactive prompt; --dry-run makes the init.sh call + # a printf instead of an exec so we can assert it without sandbox + # side effects. + echo "old" > "${SANDBOX}/setup.conf" + run bash "${SANDBOX}/build.sh" --reset-conf --yes --dry-run + assert_success + assert_output --partial "[dry-run]" + assert_output --partial "template/init.sh --gen-conf --force" +} + +@test "build.sh --reset-conf is mentioned in usage help" { + run bash "${SANDBOX}/build.sh" --help + assert_success + assert_output --partial "--reset-conf" + assert_output --partial "setup.conf.bak" +} + +@test "build.sh --reset-conf with no existing setup.conf / .env skips prompt" { + # Nothing to overwrite → no confirmation needed, --dry-run just prints + # the init.sh call and exits cleanly. + rm -f "${SANDBOX}/setup.conf" "${SANDBOX}/.env" + run bash "${SANDBOX}/build.sh" --reset-conf --dry-run + assert_success + refute_output --partial "proceed?" +} diff --git a/template/test/unit/init_spec.bats b/template/test/unit/init_spec.bats index 5d007e3..7d229a4 100644 --- a/template/test/unit/init_spec.bats +++ b/template/test/unit/init_spec.bats @@ -219,3 +219,67 @@ REMOTE # Custom file should still be a regular file, not a symlink assert [ ! -L "${TMP_REPO}/.hadolint.yaml" ] } + +# ════════════════════════════════════════════════════════════════════ +# _gen_setup_conf --force (reset path, issue #124 / #60) +# ════════════════════════════════════════════════════════════════════ + +@test "_gen_setup_conf default refuses to overwrite existing setup.conf" { + printf "[image]\nrules = @basename\n" > "${TMP_REPO}/template/setup.conf" + echo "existing user config" > "${TMP_REPO}/setup.conf" + _source_init + run _gen_setup_conf "false" + assert_failure + assert_output --partial "already exists" +} + +@test "_gen_setup_conf --force overwrites and backs up existing setup.conf" { + printf "[image]\nrules = @basename\n" > "${TMP_REPO}/template/setup.conf" + echo "old user conf" > "${TMP_REPO}/setup.conf" + _source_init + run _gen_setup_conf "true" + assert_success + # new setup.conf must come from template + run cat "${TMP_REPO}/setup.conf" + assert_output --partial "rules = @basename" + # backup must contain the pre-overwrite user content + assert [ -f "${TMP_REPO}/setup.conf.bak" ] + run cat "${TMP_REPO}/setup.conf.bak" + assert_output "old user conf" +} + +@test "_gen_setup_conf --force also backs up .env to .env.bak" { + printf "[image]\nrules = @basename\n" > "${TMP_REPO}/template/setup.conf" + echo "user conf" > "${TMP_REPO}/setup.conf" + echo "USER_NAME=existing" > "${TMP_REPO}/.env" + _source_init + run _gen_setup_conf "true" + assert_success + assert [ -f "${TMP_REPO}/.env.bak" ] + run cat "${TMP_REPO}/.env.bak" + assert_output "USER_NAME=existing" +} + +@test "_gen_setup_conf --force on clean repo does not create spurious .bak" { + # No pre-existing setup.conf → first-time provision, nothing to back up. + printf "[image]\nrules = @basename\n" > "${TMP_REPO}/template/setup.conf" + rm -f "${TMP_REPO}/setup.conf" "${TMP_REPO}/.env" + _source_init + run _gen_setup_conf "true" + assert_success + assert [ ! -f "${TMP_REPO}/setup.conf.bak" ] + assert [ ! -f "${TMP_REPO}/.env.bak" ] +} + +# ════════════════════════════════════════════════════════════════════ +# _create_new_repo .gitignore covers the *.bak siblings +# ════════════════════════════════════════════════════════════════════ + +@test "_create_new_repo: .gitignore includes setup.conf.bak and .env.bak" { + _source_init + _create_new_repo "main" + run grep -Fxq setup.conf.bak "${TMP_REPO}/.gitignore" + assert_success + run grep -Fxq .env.bak "${TMP_REPO}/.gitignore" + assert_success +} diff --git a/template/test/unit/template_spec.bats b/template/test/unit/template_spec.bats index 172da17..611f13f 100644 --- a/template/test/unit/template_spec.bats +++ b/template/test/unit/template_spec.bats @@ -719,6 +719,73 @@ EOF rm -rf "${_tmp}" } +@test "upgrade.sh main.yaml sed handles semver pre-release tags (RC → RC)" { + # Regression: the previous `[0-9.]*` character class stopped at the + # first `-`, so upgrading from an existing RC tag left the old + # `-rcN` suffix in place and the new version got appended after it + # (e.g. @v0.10.0-rc1 → -rc2 produced `@v0.10.0-rc2-rc1`). + local _tmp _yaml + _tmp="$(mktemp -d)" + _yaml="${_tmp}/main.yaml" + cat > "${_yaml}" <<'EOF' +jobs: + call-docker-build: + uses: ycpss91255-docker/template/.github/workflows/build-worker.yaml@v0.10.0-rc1 + call-release: + uses: ycpss91255-docker/template/.github/workflows/release-worker.yaml@v0.10.0-rc1 +EOF + local _seds + _seds="$(grep -E "^[[:space:]]*sed -i" /source/upgrade.sh)" + while IFS= read -r _line; do + # shellcheck disable=SC2001 + _line="$(echo "${_line}" | sed "s|\${main_yaml}|${_yaml}|g; s|\${target_ver}|v0.10.0-rc2|g")" + eval "${_line}" + done <<< "${_seds}" + + # Must produce the clean new tag — no leftover `-rc1` suffix. + run grep -c 'build-worker.yaml@v0.10.0-rc2$' "${_yaml}" + assert_output "1" + run grep -c 'release-worker.yaml@v0.10.0-rc2$' "${_yaml}" + assert_output "1" + # And no double suffix anywhere. + run grep -c '@v0.10.0-rc2-rc' "${_yaml}" + assert_output "0" + + rm -rf "${_tmp}" +} + +@test "upgrade.sh main.yaml sed handles stable → stable + RC → stable transitions" { + # Edge cases around the pre-release group: from plain semver to plain, + # and from RC back to plain stable (e.g. v0.10.0-rc2 → v0.10.0). + local _tmp _yaml + _tmp="$(mktemp -d)" + _yaml="${_tmp}/main.yaml" + cat > "${_yaml}" <<'EOF' +jobs: + call-docker-build: + uses: ycpss91255-docker/template/.github/workflows/build-worker.yaml@v0.10.0-rc2 + call-release: + uses: ycpss91255-docker/template/.github/workflows/release-worker.yaml@v0.9.9 +EOF + local _seds + _seds="$(grep -E "^[[:space:]]*sed -i" /source/upgrade.sh)" + while IFS= read -r _line; do + # shellcheck disable=SC2001 + _line="$(echo "${_line}" | sed "s|\${main_yaml}|${_yaml}|g; s|\${target_ver}|v0.10.0|g")" + eval "${_line}" + done <<< "${_seds}" + + run grep -c 'build-worker.yaml@v0.10.0$' "${_yaml}" + assert_output "1" + run grep -c 'release-worker.yaml@v0.10.0$' "${_yaml}" + assert_output "1" + # Must not leave stale -rc2 anywhere in the file. + run grep -c 'rc2' "${_yaml}" + assert_output "0" + + rm -rf "${_tmp}" +} + # ════════════════════════════════════════════════════════════════════ # build-worker.yaml: GHCR test-tools migration (D plan) # ════════════════════════════════════════════════════════════════════ diff --git a/template/upgrade.sh b/template/upgrade.sh index bc4a921..634fbfc 100755 --- a/template/upgrade.sh +++ b/template/upgrade.sh @@ -186,10 +186,14 @@ _upgrade() { _log "Step 4/4: update workflow @tag references" local main_yaml="${REPO_ROOT}/.github/workflows/main.yaml" if [[ -f "${main_yaml}" ]]; then - # Replace @vX.Y.Z with new version in reusable workflow references. - # Match each worker file by name to avoid greedy patterns clobbering siblings. - sed -i "s|build-worker\.yaml@v[0-9.]*|build-worker.yaml@${target_ver}|g" "${main_yaml}" - sed -i "s|release-worker\.yaml@v[0-9.]*|release-worker.yaml@${target_ver}|g" "${main_yaml}" + # Replace @vX.Y.Z(-prerelease)? with new version in reusable workflow + # references. Match each worker file by name to avoid greedy patterns + # clobbering siblings. The `-E` regex anchors on a full semver shape + # (optional pre-release per §9) — the prior `[0-9.]*` stopped at the + # first `-`, so upgrading from an RC tag (e.g. v0.10.0-rc1 → -rc2) + # left the old suffix in place and produced `@v0.10.0-rc2-rc1`. + sed -i -E "s|build-worker\.yaml@v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?|build-worker.yaml@${target_ver}|g" "${main_yaml}" + sed -i -E "s|release-worker\.yaml@v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?|release-worker.yaml@${target_ver}|g" "${main_yaml}" git add "${main_yaml}" fi