From f804e25a6674109bbd895019d7e849f7e3470334 Mon Sep 17 00:00:00 2001 From: boban Date: Sun, 5 Jul 2026 13:13:09 +0200 Subject: [PATCH] =?UTF-8?q?refactor(release):=20single=20stable=20channel?= =?UTF-8?q?=20=E2=80=94=20cut=20internal=20release=20candidates=20(-rc),?= =?UTF-8?q?=20remove=20all=20beta=20wording?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 --- .github/workflows/ci-staging.yml | 2 +- .github/workflows/yank.yml | 4 +-- CHANGELOG.md | 22 +++++++----- app/Livewire/Release/Index.php | 22 ++++++------ app/Services/PipelineStatus.php | 12 +++---- app/Services/PromotionService.php | 4 +-- app/Services/ReleasePlanner.php | 14 ++++---- config/clusev.php | 4 +-- docker/release/README.md | 6 ++-- docker/release/clusev-release.sh | 10 +++--- docker/release/serve-request.test.sh | 18 +++++----- lang/de/release.php | 30 ++++++++-------- lang/en/release.php | 30 ++++++++-------- .../views/livewire/release/index.blade.php | 16 ++++----- scripts/promote.sh | 2 +- scripts/promote.test.sh | 2 +- tests/Feature/PipelineStatusTest.php | 36 +++++++++---------- tests/Feature/PromotionServiceTest.php | 8 ++--- tests/Feature/ReleaseBridgeTest.php | 4 +-- tests/Feature/ReleaseCheckerChannelTest.php | 8 ++--- tests/Feature/ReleasePageTest.php | 20 +++++------ tests/Feature/TerminalTest.php | 4 +-- tests/Feature/VersionUpdateCheckTest.php | 8 ++--- tests/Unit/ReleasePlannerTest.php | 14 ++++---- 24 files changed, 153 insertions(+), 147 deletions(-) diff --git a/.github/workflows/ci-staging.yml b/.github/workflows/ci-staging.yml index bcea219..7eda38f 100644 --- a/.github/workflows/ci-staging.yml +++ b/.github/workflows/ci-staging.yml @@ -105,7 +105,7 @@ jobs: # Gitea-free code tree; promote.sh's leak-guard is the last line of defence. promote: needs: [test, images] - # Stable = a clean vX.Y.Z with NO pre-release suffix. Excludes -rc AND any accidental -beta/-alpha. + # Stable = a clean vX.Y.Z with NO pre-release suffix. Excludes -rc AND any accidental pre-release suffix. if: ${{ success() && startsWith(github.ref_name, 'v') && !contains(github.ref_name, '-') }} runs-on: ubuntu-latest environment: production diff --git a/.github/workflows/yank.yml b/.github/workflows/yank.yml index 541737d..a817122 100644 --- a/.github/workflows/yank.yml +++ b/.github/workflows/yank.yml @@ -3,7 +3,7 @@ on: workflow_dispatch: inputs: tag: - description: 'Tag to retract from the public repo (e.g. v0.10.0 or v0.10.0-beta1)' + description: 'Tag to retract from the public repo (e.g. v0.10.0)' required: true permissions: @@ -18,7 +18,7 @@ jobs: env: TAG: ${{ inputs.tag }} run: | - echo "$TAG" | grep -qE '^v[0-9]+\.[0-9]+\.[0-9]+(-beta[0-9]+)?$' \ + echo "$TAG" | grep -qE '^v[0-9]+\.[0-9]+\.[0-9]+$' \ || { echo "refusing to yank an unrecognised tag: $TAG" >&2; exit 1; } - name: Checkout public repo uses: actions/checkout@v5 diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e37f62..75bbf9d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ Alle nennenswerten Änderungen an Clusev. Format nach **Release-Prozess:** Feature-Commits sammeln sich auf dem Branch. Ist ein Stand reif, wird er nach `main` gemerged, als `vX.Y.Z` getaggt und hier unter einer eigenen Versionsüberschrift dokumentiert. Nutzer beziehen ausschließlich -getaggte Releases (Kanal `stable`, optional `beta`) — niemals Entwicklungs-Builds. +getaggte Releases (Kanal `stable`) — niemals Entwicklungs-Builds. **Was hier steht:** nur, was Nutzer der Installation merken — knapp, ein Eintrag pro Änderung, gruppiert nach _Hinzugefügt / Geändert / Behoben / Sicherheit_. Rein @@ -18,7 +18,13 @@ sondern in die git-Historie. _Keine offenen Änderungen — der nächste Stand wird hier gesammelt und als `vX.Y.Z` getaggt._ -## [0.11.0-beta1] - 2026-06-26 +## [0.11.0] - 2026-07-05 + +### Geändert +- **Ein einziger stabiler Release-Kanal.** Nutzer erhalten ausschließlich finalisierte `vX.Y.Z`-Releases; + die interne Release-Steuerung schneidet jetzt Release Candidates statt Vorab-Kanälen. + +## [0.11.0-rc1] - 2026-06-26 ### Hinzugefügt - **Web-Terminal.** Vollwertiges SSH-Terminal direkt im Browser — eines pro Server und eines für den @@ -47,7 +53,7 @@ _Keine offenen Änderungen — der nächste Stand wird hier gesammelt und als `v - **Proxy-Vertrauen gehärtet:** nur noch private Netze werden vertraut, damit ein gefälschter `X-Forwarded-For` die IP-basierten Brute-Force-Limits nicht mehr umgehen kann. -## [0.10.0-beta1] - 2026-06-25 +## [0.10.0-rc1] - 2026-06-25 ### Hinzugefügt - **Verlaufs-Graf auf der Server-Detailseite.** CPU, RAM und Disk über Zeit — Zeitraum wählbar @@ -64,7 +70,7 @@ _Keine offenen Änderungen — der nächste Stand wird hier gesammelt und als `v ## [0.9.68] - 2026-06-23 ### Intern -- **Release-Tooling im Dev-Dashboard + CI-Pipeline.** Beta schneiden, Live-Pipeline-Status, +- **Release-Tooling im Dev-Dashboard + CI-Pipeline.** Release Candidate schneiden, Live-Pipeline-Status, Veröffentlichen / Promote-to-Stable / Yank — für Nutzer der Installation nicht sichtbar. Details in der git-Historie. (Sammelt die internen Versionen 0.9.59–0.9.68.) @@ -81,8 +87,8 @@ _Keine offenen Änderungen — der nächste Stand wird hier gesammelt und als `v ## [0.9.57] - 2026-06-22 ### Hinzugefügt -- **Release-Kanäle `stable` und `beta`.** Nutzer beziehen stabile Releases (`vX.Y.Z`); wer früher - testen will, wählt den Beta-Kanal (`vX.Y.Z-betaN`) — niemals Entwicklungs-Builds. +- **Stabiler Release-Kanal `stable`.** Nutzer beziehen ausschließlich stabile Releases (`vX.Y.Z`) — + niemals Entwicklungs-Builds. ## [0.9.56] - 2026-06-22 @@ -725,7 +731,7 @@ _Keine offenen Änderungen — der nächste Stand wird hier gesammelt und als `v Version veröffentlicht war. Der neueste Tag im Kanal wird jetzt über die **öffentliche, lesende Tag-API des Git-Hosts** abgefragt (anonym — kein Token verlässt das Panel), mit lokalem `.git` nur noch als Dev-Fallback; das Ergebnis ist gecacht, sodass ein Seitenaufruf nie eine Netzwerkanfrage - auslöst (nur der Knopf selbst). Kanal-Logik: `stable` bietet nur `vX.Y.Z`, `beta` auch Vorab-Tags. + auslöst (nur der Knopf selbst). Kanal-Logik: `stable` bietet ausschließlich `vX.Y.Z` an. Schlägt die Abfrage fehl (offline o. Ä.), degradiert sie still statt zu blockieren. ## [0.9.6] - 2026-06-19 @@ -1295,7 +1301,7 @@ Erste geschnittene Version — die komplette v1-Basis inklusive Server- und Pane - Panel-Selbst-Härtung: Security-Header + umgebungsbewusste CSP, 2FA-Brute-Force- Drossel, Session-Cookie-Härtung. - Release-/Versionierungsmodell: echte semantische Versionen + Git-Tags, Changelog - pro Version, Kanäle `stable`/`beta` (kein nutzer-seitiges `dev`). + pro Version, Kanal `stable` (kein nutzer-seitiges `dev`). [Unreleased]: https://github.com/clusev/clusev/compare/v0.2.1...HEAD [0.2.1]: https://github.com/clusev/clusev/compare/v0.2.0...v0.2.1 diff --git a/app/Livewire/Release/Index.php b/app/Livewire/Release/Index.php index a198d6d..df399da 100644 --- a/app/Livewire/Release/Index.php +++ b/app/Livewire/Release/Index.php @@ -16,8 +16,8 @@ use Livewire\Attributes\On; use Livewire\Component; /** - * Dev-only Release control. "Deploy to Staging" cuts a beta of an allowed target version via the - * host release bridge (bump → commit → tag → push to Gitea). The page is reachable only when + * Dev-only Release control. "Deploy to Staging" cuts a release candidate of an allowed target + * version via the host release bridge (bump → commit → tag → push to Gitea). The page is reachable only when * config('clusev.release_controls') is true; mount() re-guards as defence-in-depth. * * Deploy-to-Staging is irreversible (it pushes a tag the host CI acts on), so it is gated behind the @@ -37,7 +37,7 @@ class Index extends Component /** The target X.Y.Z being staged (for display while it runs). */ public ?string $pendingTarget = null; - /** The tag of the last successfully-pushed staging beta (shown until the next deploy). */ + /** The tag of the last successfully-pushed staging release candidate (shown until the next deploy). */ public ?string $lastTag = null; public function mount(): void @@ -46,7 +46,7 @@ class Index extends Component abort_unless(auth()->user()?->can('manage-panel'), 403); } - /** Opens the wire-elements/modal confirm dialog (R5) before cutting a staging beta of $target. */ + /** Opens the wire-elements/modal confirm dialog (R5) before cutting a staging release candidate of $target. */ public function confirmDeployStaging(string $target, ReleasePlanner $planner): void { abort_unless(auth()->user()?->can('manage-panel'), 403); @@ -80,7 +80,7 @@ class Index extends Component $this->deployStaging($target, $planner, $bridge); } - /** Cut + push a beta of $target via the host bridge. $target must be one of the proposed values. */ + /** Cut + push a release candidate of $target via the host bridge. $target must be one of the proposed values. */ public function deployStaging(string $target, ReleasePlanner $planner, ReleaseBridge $bridge): void { abort_unless(auth()->user()?->can('manage-panel'), 403); @@ -160,12 +160,12 @@ class Index extends Component $pipeline->refresh(); } - /** The current in-flight beta tag (vX.Y.Z-betaN), or null when the running version is stable. */ - private function currentBetaTag(): ?string + /** The current in-flight release-candidate tag (vX.Y.Z-rcN), or null when the running version is stable. */ + private function currentRcTag(): ?string { $v = (string) config('clusev.version'); - return str_contains($v, '-beta') ? 'v'.ltrim($v, 'vV') : null; + return str_contains($v, '-rc') ? 'v'.ltrim($v, 'vV') : null; } /** Per-user throttle shared by all promotion dispatches (auto-expiring, never a lockout). */ @@ -197,7 +197,7 @@ class Index extends Component public function confirmDeployPublic(): void { abort_unless(auth()->user()?->can('manage-panel'), 403); - $tag = $this->currentBetaTag(); + $tag = $this->currentRcTag(); if ($tag === null) { return; } @@ -216,7 +216,7 @@ class Index extends Component return; } $tag = (string) ($payload['params']['tag'] ?? ''); - if ($tag === '' || $this->currentBetaTag() !== $tag || ! $this->throttlePromote()) { + if ($tag === '' || $this->currentRcTag() !== $tag || ! $this->throttlePromote()) { return; } $ok = $promotion->deployPublic($tag); @@ -288,7 +288,7 @@ class Index extends Component return view('livewire.release.index', [ 'current' => $current, 'targets' => app(ReleasePlanner::class)->proposedTargets($current), - 'pipeline' => app(PipelineStatus::class)->forTrackedBeta(), + 'pipeline' => app(PipelineStatus::class)->forTrackedRc(), 'publicTags' => app(PromotionService::class)->publicTags(), ])->title(__('release.title')); } diff --git a/app/Services/PipelineStatus.php b/app/Services/PipelineStatus.php index 373f242..47deba8 100644 --- a/app/Services/PipelineStatus.php +++ b/app/Services/PipelineStatus.php @@ -7,9 +7,9 @@ use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Http; /** - * Live status of the build pipeline for the currently-cut beta, for the dev Release page: tag (Gitea), - * mirror (GitHub-private), CI (GitHub Actions). These are global build facts of one beta — there is no - * "deployed" step because any beta-channel server pulls a release itself (no single staging target). + * Live status of the build pipeline for the currently-cut release candidate, for the dev Release page: tag (Gitea), + * mirror (GitHub-private), CI (GitHub Actions). These are global build facts of one release candidate — there is no + * "deployed" step because any server tracking the candidate pulls a release itself (no single staging target). * Each step is cached briefly; any failure degrades to 'unknown' (never throws). The read-only GitHub * token and the private staging slug come from config (env) and are never rendered or put in a URL. */ @@ -20,10 +20,10 @@ class PipelineStatus /** * @return array{tag:string, steps:array, ciRunning:bool}|null */ - public function forTrackedBeta(): ?array + public function forTrackedRc(): ?array { $version = (string) config('clusev.version'); - if (! str_contains($version, '-beta')) { + if (! str_contains($version, '-rc')) { return null; } $tag = 'v'.ltrim($version, 'vV'); @@ -47,7 +47,7 @@ class PipelineStatus public function refresh(): void { $version = (string) config('clusev.version'); - if (! str_contains($version, '-beta')) { + if (! str_contains($version, '-rc')) { return; } $tag = 'v'.ltrim($version, 'vV'); diff --git a/app/Services/PromotionService.php b/app/Services/PromotionService.php index f73ee9f..0d0b1b1 100644 --- a/app/Services/PromotionService.php +++ b/app/Services/PromotionService.php @@ -14,9 +14,9 @@ use Illuminate\Support\Facades\Http; */ class PromotionService { - public function deployPublic(string $betaTag): bool + public function deployPublic(string $rcTag): bool { - return $this->dispatch('promote-public.yml', ['tag' => $betaTag]); + return $this->dispatch('promote-public.yml', ['tag' => $rcTag]); } public function yank(string $tag): bool diff --git a/app/Services/ReleasePlanner.php b/app/Services/ReleasePlanner.php index 96f2e24..140b57e 100644 --- a/app/Services/ReleasePlanner.php +++ b/app/Services/ReleasePlanner.php @@ -3,16 +3,16 @@ namespace App\Services; /** - * Pure semver arithmetic for the Release page: given the current version, what beta-target versions - * may the operator cut? The dashboard computes the target X.Y.Z; the host bridge computes -betaN. + * Pure semver arithmetic for the Release page: given the current version, what rc-target versions + * may the operator cut? The dashboard computes the target X.Y.Z; the host bridge computes -rcN. */ class ReleasePlanner { /** - * Proposed target versions (bare X.Y.Z) for the current version. A beta also offers continuing - * its own base (another beta of the same target). + * Proposed target versions (bare X.Y.Z) for the current version. A release candidate also offers + * continuing its own base (another release candidate of the same target). * - * @return array{patch:string, minor:string, major:string, continueBeta?:string} + * @return array{patch:string, minor:string, major:string, continueRc?:string} */ public function proposedTargets(string $current): array { @@ -26,8 +26,8 @@ class ReleasePlanner 'major' => ($maj + 1).'.0.0', ]; - if (str_contains($current, '-beta')) { - $targets['continueBeta'] = "{$maj}.{$min}.{$pat}"; + if (str_contains($current, '-rc')) { + $targets['continueRc'] = "{$maj}.{$min}.{$pat}"; } return $targets; diff --git a/config/clusev.php b/config/clusev.php index d13faaa..7cb7ead 100644 --- a/config/clusev.php +++ b/config/clusev.php @@ -2,7 +2,7 @@ return [ // First tagged release is v0.1.0 (semantic, not -dev). - 'version' => '0.11.0-beta1', + 'version' => '0.11.0', // Deployed commit + branch. install.sh bakes these into .env from the host's .git (the prod // image ships no .git); the Versions page prefers them and falls back to a live .git read in @@ -12,7 +12,7 @@ return [ 'branch' => env('CLUSEV_BUILD_BRANCH') ?: null, ], - // Default user channel. Only 'stable' and 'beta' are ever offered to users. + // Single stable release channel — users only ever receive finalized vX.Y.Z releases. 'channel' => 'stable', // Install-time panel domain (APP_DOMAIN). The dashboard Setting overrides it; this diff --git a/docker/release/README.md b/docker/release/README.md index e09843b..36e0acd 100644 --- a/docker/release/README.md +++ b/docker/release/README.md @@ -1,10 +1,10 @@ # Release bridge (host watcher) — DEV ONLY -The dashboard "Release" page (Deploy to Staging) cuts a beta by asking the **host** to do the git +The dashboard "Release" page (Deploy to Staging) cuts a release candidate by asking the **host** to do the git work, so the container never holds git credentials — the same isolation as the WireGuard bridge. - `clusev-release.sh serve-request` — reads `run/release-request.json` (the dashboard wrote it), - re-validates, bumps `config/clusev.php`, commits, tags `vX.Y.Z-betaN`, pushes to Gitea (token read + re-validates, bumps `config/clusev.php`, commits, tags `vX.Y.Z-rcN`, pushes to Gitea (token read host-side from `/home/nexxo/.env.gitea`), and writes `run/release-result-.json` for the dashboard to poll. Roll back leaves no half-release if the push fails. - `clusev-release-request.path` / `.service` — a systemd `.path` watcher that runs the handler when a @@ -30,5 +30,5 @@ lands where the watcher looks. ## Verify (manual gate) This is the end-to-end gate (like the WireGuard runbook): with the watcher enabled and the flag on, -open `/release`, click a bump button, confirm, and check that `vX.Y.Z-betaN` lands on Gitea (and, via +open `/release`, click a bump button, confirm, and check that `vX.Y.Z-rcN` lands on Gitea (and, via the push-mirror, on GitHub-private). The handler never logs the Gitea token. diff --git a/docker/release/clusev-release.sh b/docker/release/clusev-release.sh index 739f7b8..8b1df9b 100755 --- a/docker/release/clusev-release.sh +++ b/docker/release/clusev-release.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash # Host side of the dashboard release bridge. `serve-request` reads run/release-request.json (the -# dashboard wrote it), cuts a beta — bump config version, commit, tag vX.Y.Z-betaN, push to Gitea — +# dashboard wrote it), cuts a release candidate — bump config version, commit, tag vX.Y.Z-rcN, push to Gitea — # and writes a result the dashboard polls. ALL git/token operations run here; the container never # holds credentials. `stage ` is the same path without the request file (used by the test). set -euo pipefail @@ -43,7 +43,7 @@ _precheck() { [ "$(git -C "$PROJ" rev-parse HEAD)" = "$(git -C "$PROJ" rev-parse "origin/${BRANCH}")" ] || { echo "unpushed"; return 1; } } -# Cut + push a beta of . Echoes the new tag on success; reason on stderr + non-zero on failure. +# Cut + push a release candidate of . Echoes the new tag on success; reason on stderr + non-zero on failure. _do_stage() { # target local target="$1" cur base cur="$(grep -oE "'version' => '[^']*'" "${PROJ}/config/clusev.php" | head -n1 | sed -E "s/.*=> '//; s/'$//" || true)" @@ -51,11 +51,11 @@ _do_stage() { # target _ver_ge "$target" "$base" || { echo "downgrade-rejected" >&2; return 1; } local n=0 t k - for t in $(git -C "$PROJ" tag -l "v${target}-beta*"); do - k="${t##*-beta}" + for t in $(git -C "$PROJ" tag -l "v${target}-rc*"); do + k="${t##*-rc}" case "$k" in ''|*[!0-9]*) ;; *) [ "$k" -gt "$n" ] && n="$k" ;; esac done - local ver="${target}-beta$((n + 1))" newtag="v${target}-beta$((n + 1))" + local ver="${target}-rc$((n + 1))" newtag="v${target}-rc$((n + 1))" sed -i -E "s/('version' => ')[^']*(')/\\1${ver}\\2/" "${PROJ}/config/clusev.php" git -C "$PROJ" commit -q -am "chore(release): ${ver}" || { echo "commit-failed" >&2; git -C "$PROJ" checkout -- . 2>/dev/null || true; return 1; } diff --git a/docker/release/serve-request.test.sh b/docker/release/serve-request.test.sh index 2cc9e12..789c921 100644 --- a/docker/release/serve-request.test.sh +++ b/docker/release/serve-request.test.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Test clusev-release.sh `stage`: bump config version, commit, tag vX.Y.Z-betaN, push to a (local) -# Gitea-stand-in remote; beta auto-increment; refusals (dirty / unpushed / downgrade); push-fail rollback. +# Test clusev-release.sh `stage`: bump config version, commit, tag vX.Y.Z-rcN, push to a (local) +# Gitea-stand-in remote; rc auto-increment; refusals (dirty / unpushed / downgrade); push-fail rollback. set -euo pipefail here="$(cd "$(dirname "$0")" && pwd)" work="$(mktemp -d)"; trap 'rm -rf "$work"' EXIT @@ -17,15 +17,15 @@ git -C "$work/proj" add -A && git -C "$work/proj" commit -qm init && git -C "$wo printf 'GIT_ACCESS_TOKEN=dummy\n' > "$work/.env.gitea" run() { CLUSEV_DIR="$work/proj" CLUSEV_RELEASE_BRANCH=main CLUSEV_GITEA_ENV="$work/.env.gitea" bash "$here/clusev-release.sh" "$@"; } -# happy path: minor -> 0.10.0-beta1, config bumped, tag pushed to the remote +# happy path: minor -> 0.10.0-rc1, config bumped, tag pushed to the remote tag="$(run stage 0.10.0)" -test "$tag" = "v0.10.0-beta1" || { echo "FAIL: expected v0.10.0-beta1, got '$tag'"; exit 1; } -grep -q "'version' => '0.10.0-beta1'" "$work/proj/config/clusev.php" || { echo "FAIL: config not bumped"; exit 1; } -git -C "$work/remote.git" tag | grep -qx 'v0.10.0-beta1' || { echo "FAIL: tag not pushed to remote"; exit 1; } +test "$tag" = "v0.10.0-rc1" || { echo "FAIL: expected v0.10.0-rc1, got '$tag'"; exit 1; } +grep -q "'version' => '0.10.0-rc1'" "$work/proj/config/clusev.php" || { echo "FAIL: config not bumped"; exit 1; } +git -C "$work/remote.git" tag | grep -qx 'v0.10.0-rc1' || { echo "FAIL: tag not pushed to remote"; exit 1; } -# second run, same target -> beta2 +# second run, same target -> rc2 tag2="$(run stage 0.10.0)" -test "$tag2" = "v0.10.0-beta2" || { echo "FAIL: expected beta2, got '$tag2'"; exit 1; } +test "$tag2" = "v0.10.0-rc2" || { echo "FAIL: expected rc2, got '$tag2'"; exit 1; } # downgrade rejected (target below the current base 0.10.0) if run stage 0.9.0 2>/dev/null; then echo "FAIL: downgrade not rejected"; exit 1; fi @@ -49,7 +49,7 @@ chmod +x "$work/remote.git/hooks/pre-receive" before="$(git -C "$work/proj" rev-parse HEAD)" if run stage 0.11.0 2>/dev/null; then echo "FAIL: push to a rejecting remote should fail"; exit 1; fi rm -f "$work/remote.git/hooks/pre-receive" -git -C "$work/proj" tag | grep -qx 'v0.11.0-beta1' && { echo "FAIL: tag not rolled back after push reject"; exit 1; } +git -C "$work/proj" tag | grep -qx 'v0.11.0-rc1' && { echo "FAIL: tag not rolled back after push reject"; exit 1; } test "$(git -C "$work/proj" rev-parse HEAD)" = "$before" || { echo "FAIL: HEAD not rolled back after push reject"; exit 1; } test -z "$(git -C "$work/proj" status --porcelain)" || { echo "FAIL: config not restored (dirty) after rollback"; exit 1; } diff --git a/lang/de/release.php b/lang/de/release.php index 85e3870..d6bc3a2 100644 --- a/lang/de/release.php +++ b/lang/de/release.php @@ -5,7 +5,7 @@ return [ 'nav' => 'Release', 'eyebrow' => 'Release-Pipeline', 'heading' => 'Release-Steuerung', - 'subtitle' => 'Beta auf Staging schneiden — nur auf der Dev-Instanz.', + 'subtitle' => 'Release Candidate auf Staging schneiden — nur auf der Dev-Instanz.', 'dev_instance' => 'Dev-Instanz', 'current' => 'Aktuelle Version', @@ -13,28 +13,28 @@ return [ 'source' => 'Quelle', // option tiles - 'choose' => 'Beta schneiden', - 'kind_continue' => 'Nächste Beta', + 'choose' => 'Release Candidate schneiden', + 'kind_continue' => 'Nächster RC', 'kind_patch' => 'Patch', 'kind_minor' => 'Minor', 'kind_major' => 'Major', - 'tile_beta' => 'Beta-Tag', - 'hint' => 'Schneidet einen Beta-Tag und pusht ihn nach Gitea → Mirror → CI → Staging.', + 'tile_rc' => 'RC-Tag', + 'hint' => 'Schneidet einen RC-Tag und pusht ihn nach Gitea → Mirror → CI → Staging.', // running / result 'running_title' => 'Release läuft', - 'running' => 'Beta :target wird geschnitten und gepusht …', + 'running' => 'Release Candidate :target wird geschnitten und gepusht …', 'in_progress' => 'läuft', - 'staged' => 'Beta :tag gepusht → Mirror → CI → Staging.', + 'staged' => 'Release Candidate :tag gepusht → Mirror → CI → Staging.', 'staged_label' => 'Zuletzt gepusht:', 'failed' => 'Release fehlgeschlagen: :reason', - // pipeline rail (Tag → Mirror → CI; build readiness of a beta) + // pipeline rail (Tag → Mirror → CI; build readiness of a release candidate) 'pipeline' => 'Pipeline', 'pipe_sub_gitea' => 'Quelle · Tag wird hier erzeugt', 'pipe_sub_mirror' => 'Automatischer Push-Mirror', 'pipe_sub_ci' => 'Tests laufen', - 'no_beta_running' => 'Keine Beta in Arbeit.', + 'no_rc_running' => 'Kein Release Candidate in Arbeit.', 'state_done' => 'fertig', 'state_running' => 'läuft …', 'state_failed' => 'fehlgeschlagen', @@ -42,9 +42,9 @@ return [ 'state_unknown' => 'unbekannt', // confirm modal (R5) - 'confirm_title' => 'Beta auf Staging schneiden?', - 'confirm_body' => 'Schneidet eine Beta von :target, taggt sie und pusht nach Gitea → Mirror → CI → Staging. Der Tag ist unwiderruflich.', - 'confirm_action' => 'Beta schneiden', + 'confirm_title' => 'Release Candidate auf Staging schneiden?', + 'confirm_body' => 'Schneidet einen Release Candidate von :target, taggt ihn und pusht nach Gitea → Mirror → CI → Staging. Der Tag ist unwiderruflich.', + 'confirm_action' => 'Release Candidate schneiden', // errors 'invalid_target' => 'Ungültige Ziel-Version.', @@ -57,12 +57,12 @@ return [ 'deploy_public' => 'Deploy to Public (:tag)', 'promote_stable' => 'Promote to Stable (:v)', 'publish_hint' => 'Löst einen GitHub-Workflow aus, der die Version ins öffentliche Repo befördert.', - 'public_confirm_title' => 'Beta öffentlich machen?', - 'public_confirm_body' => 'Befördert :tag in den öffentlichen Beta-Kanal (clusev/clusev).', + 'public_confirm_title' => 'Release Candidate öffentlich machen?', + 'public_confirm_body' => 'Befördert :tag ins öffentliche Repo (clusev/clusev).', 'public_action' => 'Veröffentlichen', 'public_dispatched' => 'Public-Promotion ausgelöst: :tag.', 'stable_confirm_title' => 'Als Stable freigeben?', - 'stable_confirm_body' => 'Finalisiert die Beta als stabile :v im öffentlichen Stable-Kanal.', + 'stable_confirm_body' => 'Finalisiert den Release Candidate als stabile :v im öffentlichen Stable-Kanal.', 'stable_action' => 'Stable freigeben', 'stable_dispatched' => 'Stable-Promotion ausgelöst: :v.', 'promote_failed' => 'Auslösen fehlgeschlagen — Token-Scope (Actions: Write) prüfen.', diff --git a/lang/en/release.php b/lang/en/release.php index 8237cdc..7db06ac 100644 --- a/lang/en/release.php +++ b/lang/en/release.php @@ -5,7 +5,7 @@ return [ 'nav' => 'Release', 'eyebrow' => 'Release pipeline', 'heading' => 'Release control', - 'subtitle' => 'Cut a staging beta — dev instance only.', + 'subtitle' => 'Cut a staging release candidate — dev instance only.', 'dev_instance' => 'Dev instance', 'current' => 'Current version', @@ -13,28 +13,28 @@ return [ 'source' => 'Source', // option tiles - 'choose' => 'Cut a beta', - 'kind_continue' => 'Next beta', + 'choose' => 'Cut a release candidate', + 'kind_continue' => 'Next RC', 'kind_patch' => 'Patch', 'kind_minor' => 'Minor', 'kind_major' => 'Major', - 'tile_beta' => 'Beta tag', - 'hint' => 'Cuts a beta tag and pushes it to Gitea → mirror → CI → staging.', + 'tile_rc' => 'RC tag', + 'hint' => 'Cuts an RC tag and pushes it to Gitea → mirror → CI → staging.', // running / result 'running_title' => 'Release running', - 'running' => 'Cutting and pushing beta :target …', + 'running' => 'Cutting and pushing release candidate :target …', 'in_progress' => 'running', - 'staged' => 'Beta :tag pushed → mirror → CI → staging.', + 'staged' => 'Release candidate :tag pushed → mirror → CI → staging.', 'staged_label' => 'Last pushed:', 'failed' => 'Release failed: :reason', - // pipeline rail (Tag → Mirror → CI; build readiness of a beta) + // pipeline rail (Tag → Mirror → CI; build readiness of a release candidate) 'pipeline' => 'Pipeline', 'pipe_sub_gitea' => 'Source · tag is cut here', 'pipe_sub_mirror' => 'Automatic push mirror', 'pipe_sub_ci' => 'Tests run', - 'no_beta_running' => 'No beta in flight.', + 'no_rc_running' => 'No release candidate in flight.', 'state_done' => 'done', 'state_running' => 'running …', 'state_failed' => 'failed', @@ -42,9 +42,9 @@ return [ 'state_unknown' => 'unknown', // confirm modal (R5) - 'confirm_title' => 'Cut a staging beta?', - 'confirm_body' => 'Cuts a beta of :target, tags it and pushes to Gitea → mirror → CI → staging. The tag is irreversible.', - 'confirm_action' => 'Cut beta', + 'confirm_title' => 'Cut a staging release candidate?', + 'confirm_body' => 'Cuts a release candidate of :target, tags it and pushes to Gitea → mirror → CI → staging. The tag is irreversible.', + 'confirm_action' => 'Cut release candidate', // errors 'invalid_target' => 'Invalid target version.', @@ -57,12 +57,12 @@ return [ 'deploy_public' => 'Deploy to Public (:tag)', 'promote_stable' => 'Promote to Stable (:v)', 'publish_hint' => 'Triggers a GitHub workflow that promotes the version to the public repo.', - 'public_confirm_title' => 'Make the beta public?', - 'public_confirm_body' => 'Promotes :tag to the public beta channel (clusev/clusev).', + 'public_confirm_title' => 'Make the release candidate public?', + 'public_confirm_body' => 'Promotes :tag to the public repo (clusev/clusev).', 'public_action' => 'Publish', 'public_dispatched' => 'Public promotion triggered: :tag.', 'stable_confirm_title' => 'Release as stable?', - 'stable_confirm_body' => 'Finalises the beta as stable :v in the public stable channel.', + 'stable_confirm_body' => 'Finalises the release candidate as stable :v in the public stable channel.', 'stable_action' => 'Release stable', 'stable_dispatched' => 'Stable promotion triggered: :v.', 'promote_failed' => 'Dispatch failed — check the token scope (Actions: Write).', diff --git a/resources/views/livewire/release/index.blade.php b/resources/views/livewire/release/index.blade.php index 98745cf..cb5f51d 100644 --- a/resources/views/livewire/release/index.blade.php +++ b/resources/views/livewire/release/index.blade.php @@ -1,9 +1,9 @@ @php - $isBeta = str_contains($current, '-beta'); + $isRc = str_contains($current, '-rc'); $tiles = []; - if (isset($targets['continueBeta'])) { - $tiles[] = ['target' => $targets['continueBeta'], 'label' => __('release.kind_continue'), 'icon' => 'rotate', 'primary' => true]; + if (isset($targets['continueRc'])) { + $tiles[] = ['target' => $targets['continueRc'], 'label' => __('release.kind_continue'), 'icon' => 'rotate', 'primary' => true]; } $tiles[] = ['target' => $targets['patch'], 'label' => __('release.kind_patch'), 'icon' => 'tag', 'primary' => false]; $tiles[] = ['target' => $targets['minor'], 'label' => __('release.kind_minor'), 'icon' => 'tag', 'primary' => false]; @@ -39,7 +39,7 @@
{{ __('release.channel') }}
-
{{ $isBeta ? 'beta' : 'stable' }}
+
{{ $isRc ? 'rc' : 'stable' }}
{{ __('release.source') }}
@@ -106,7 +106,7 @@ 'text-accent-text' => $t['primary'], 'text-ink' => ! $t['primary'], ])>{{ $t['target'] }} - {{ __('release.tile_beta') }} + {{ __('release.tile_rc') }}
@endforeach @@ -115,8 +115,8 @@ @endif - @if ($isBeta) - {{-- Publish the current beta --}} + @if ($isRc) + {{-- Publish the current release candidate --}}

{{ __('release.publish') }}

@@ -150,7 +150,7 @@ {{-- Right: live pipeline rail --}} @if ($pipeline === null) -

{{ __('release.no_beta_running') }}

+

{{ __('release.no_rc_running') }}

@else
    @foreach ($pipeline['steps'] as $s) diff --git a/scripts/promote.sh b/scripts/promote.sh index 6ea2884..ae7f2f0 100755 --- a/scripts/promote.sh +++ b/scripts/promote.sh @@ -55,7 +55,7 @@ while IFS= read -r pat; do done <<< "$forbidden" git -C "$pub" add -A -# --allow-empty: stable promotion re-tags the same source commit as the last beta, so the +# --allow-empty: stable promotion re-tags the same source commit as the last release candidate, so the # extracted tree is byte-identical and there is nothing to stage — still produce a release commit. git -C "$pub" commit -q --allow-empty -m "$msg" git -C "$pub" tag "$tag" diff --git a/scripts/promote.test.sh b/scripts/promote.test.sh index 8ad3794..0d8aedf 100755 --- a/scripts/promote.test.sh +++ b/scripts/promote.test.sh @@ -35,7 +35,7 @@ test -d "$work/pub/.git" || { echo "FAIL: public .git clobbered"; exit git -C "$work/pub" rev-parse v1.0.0 >/dev/null 2>&1 || { echo "FAIL: tag missing"; exit 1; } git -C "$work/pub" log -1 --pretty=%s | grep -qx 'Release v1.0.0' || { echo "FAIL: commit message"; exit 1; } -# Beta -> stable: stable re-tags the SAME source commit, so the extracted tree is byte-identical +# RC -> stable: stable re-tags the SAME source commit, so the extracted tree is byte-identical # to the last promotion. Promote a second tag (same tree) into the SAME pub repo and assert it # still creates the tag — guards against `git commit` aborting on "nothing to commit". git -C "$work/src" tag v1.0.1 v1.0.0 diff --git a/tests/Feature/PipelineStatusTest.php b/tests/Feature/PipelineStatusTest.php index 78af98b..bf07b30 100644 --- a/tests/Feature/PipelineStatusTest.php +++ b/tests/Feature/PipelineStatusTest.php @@ -19,7 +19,7 @@ class PipelineStatusTest extends TestCase { parent::setUp(); Cache::flush(); - config()->set('clusev.version', '0.9.61-beta1'); + config()->set('clusev.version', '0.9.61-rc1'); config()->set('clusev.github_token', 'tok_secret'); config()->set('clusev.staging_slug', self::SLUG); } @@ -46,27 +46,27 @@ class PipelineStatusTest extends TestCase Http::swap(new HttpFactory(app('events'))); } - public function test_non_beta_version_has_no_pipeline(): void + public function test_non_rc_version_has_no_pipeline(): void { config()->set('clusev.version', '0.9.60'); - $this->assertNull(app(PipelineStatus::class)->forTrackedBeta()); + $this->assertNull(app(PipelineStatus::class)->forTrackedRc()); } - public function test_tag_step_is_done_for_the_running_beta(): void + public function test_tag_step_is_done_for_the_running_rc(): void { Http::fake(['api.github.com/*' => Http::response([], 404)]); - $p = app(PipelineStatus::class)->forTrackedBeta(); - $this->assertSame('v0.9.61-beta1', $p['tag']); + $p = app(PipelineStatus::class)->forTrackedRc(); + $this->assertSame('v0.9.61-rc1', $p['tag']); $this->assertSame('done', $this->step($p, 'tag')['state']); } public function test_mirror_done_when_the_tag_exists_on_github(): void { Http::fake([ - 'api.github.com/repos/acme/staging/git/refs/tags/v0.9.61-beta1' => Http::response(['ref' => 'refs/tags/v0.9.61-beta1']), + 'api.github.com/repos/acme/staging/git/refs/tags/v0.9.61-rc1' => Http::response(['ref' => 'refs/tags/v0.9.61-rc1']), 'api.github.com/repos/acme/staging/actions/runs*' => Http::response(['workflow_runs' => []]), ]); - $p = app(PipelineStatus::class)->forTrackedBeta(); + $p = app(PipelineStatus::class)->forTrackedRc(); $this->assertSame('done', $this->step($p, 'mirror')['state']); } @@ -76,14 +76,14 @@ class PipelineStatusTest extends TestCase 'api.github.com/repos/acme/staging/git/refs/tags/*' => Http::response([], 404), 'api.github.com/repos/acme/staging/actions/runs*' => Http::response(['workflow_runs' => []]), ]); - $p = app(PipelineStatus::class)->forTrackedBeta(); + $p = app(PipelineStatus::class)->forTrackedRc(); $this->assertSame('pending', $this->step($p, 'mirror')['state']); } public function test_ci_running_then_done_then_failed(): void { $run = fn (string $status, ?string $conclusion) => Http::response(['workflow_runs' => [[ - 'head_branch' => 'v0.9.61-beta1', 'status' => $status, 'conclusion' => $conclusion, + 'head_branch' => 'v0.9.61-rc1', 'status' => $status, 'conclusion' => $conclusion, 'html_url' => 'https://github.com/acme/staging/actions/runs/1', ]]]); @@ -91,7 +91,7 @@ class PipelineStatusTest extends TestCase 'api.github.com/repos/acme/staging/git/refs/tags/*' => Http::response(['ref' => 'x']), 'api.github.com/repos/acme/staging/actions/runs*' => $run('in_progress', null), ]); - $p = app(PipelineStatus::class)->forTrackedBeta(); + $p = app(PipelineStatus::class)->forTrackedRc(); $this->assertSame('running', $this->step($p, 'ci')['state']); $this->assertTrue($p['ciRunning']); $this->assertStringContainsString('runs/1', $this->step($p, 'ci')['url']); @@ -101,14 +101,14 @@ class PipelineStatusTest extends TestCase 'api.github.com/repos/acme/staging/git/refs/tags/*' => Http::response(['ref' => 'x']), 'api.github.com/repos/acme/staging/actions/runs*' => $run('completed', 'success'), ]); - $this->assertSame('done', $this->step(app(PipelineStatus::class)->forTrackedBeta(), 'ci')['state']); + $this->assertSame('done', $this->step(app(PipelineStatus::class)->forTrackedRc(), 'ci')['state']); $this->resetForNextPhase(); Http::fake([ 'api.github.com/repos/acme/staging/git/refs/tags/*' => Http::response(['ref' => 'x']), 'api.github.com/repos/acme/staging/actions/runs*' => $run('completed', 'failure'), ]); - $this->assertSame('failed', $this->step(app(PipelineStatus::class)->forTrackedBeta(), 'ci')['state']); + $this->assertSame('failed', $this->step(app(PipelineStatus::class)->forTrackedRc(), 'ci')['state']); } public function test_ci_pending_when_no_run_matches_the_tag(): void @@ -117,14 +117,14 @@ class PipelineStatusTest extends TestCase 'api.github.com/repos/acme/staging/git/refs/tags/*' => Http::response(['ref' => 'x']), 'api.github.com/repos/acme/staging/actions/runs*' => Http::response(['workflow_runs' => [['head_branch' => 'v9.9.9', 'status' => 'completed', 'conclusion' => 'success']]]), ]); - $this->assertSame('pending', $this->step(app(PipelineStatus::class)->forTrackedBeta(), 'ci')['state']); + $this->assertSame('pending', $this->step(app(PipelineStatus::class)->forTrackedRc(), 'ci')['state']); } public function test_no_token_makes_mirror_and_ci_unknown_without_calling_github(): void { config()->set('clusev.github_token', ''); Http::fake(); - $p = app(PipelineStatus::class)->forTrackedBeta(); + $p = app(PipelineStatus::class)->forTrackedRc(); $this->assertSame('unknown', $this->step($p, 'mirror')['state']); $this->assertSame('unknown', $this->step($p, 'ci')['state']); Http::assertNothingSent(); @@ -133,7 +133,7 @@ class PipelineStatusTest extends TestCase public function test_token_is_sent_in_the_header_never_in_the_url(): void { Http::fake(['api.github.com/*' => Http::response(['ref' => 'x'])]); - app(PipelineStatus::class)->forTrackedBeta(); + app(PipelineStatus::class)->forTrackedRc(); Http::assertSent(fn ($req) => $req->hasHeader('Authorization') && ! str_contains($req->url(), 'tok_secret')); } @@ -141,7 +141,7 @@ class PipelineStatusTest extends TestCase public function test_github_5xx_degrades_to_unknown(): void { Http::fake(['api.github.com/*' => Http::response('boom', 500)]); - $p = app(PipelineStatus::class)->forTrackedBeta(); + $p = app(PipelineStatus::class)->forTrackedRc(); $this->assertSame('unknown', $this->step($p, 'mirror')['state']); $this->assertSame('unknown', $this->step($p, 'ci')['state']); } @@ -149,7 +149,7 @@ class PipelineStatusTest extends TestCase public function test_github_throwing_degrades_to_unknown_never_throws(): void { Http::fake(['api.github.com/*' => fn () => throw new \RuntimeException('net down')]); - $p = app(PipelineStatus::class)->forTrackedBeta(); + $p = app(PipelineStatus::class)->forTrackedRc(); $this->assertSame('unknown', $this->step($p, 'mirror')['state']); $this->assertSame('unknown', $this->step($p, 'ci')['state']); } diff --git a/tests/Feature/PromotionServiceTest.php b/tests/Feature/PromotionServiceTest.php index a077634..6ea0e5e 100644 --- a/tests/Feature/PromotionServiceTest.php +++ b/tests/Feature/PromotionServiceTest.php @@ -28,12 +28,12 @@ class PromotionServiceTest extends TestCase { Http::fake(['api.github.com/*' => Http::response('', 204)]); - $this->assertTrue(app(PromotionService::class)->deployPublic('v0.10.0-beta1')); + $this->assertTrue(app(PromotionService::class)->deployPublic('v0.10.0-rc1')); Http::assertSent(fn ($req) => $req->method() === 'POST' && str_contains($req->url(), 'repos/acme/staging/actions/workflows/promote-public.yml/dispatches') && $req['ref'] === 'feat/v1-foundation' - && $req['inputs']['tag'] === 'v0.10.0-beta1' + && $req['inputs']['tag'] === 'v0.10.0-rc1' && $req->hasHeader('Authorization') && ! str_contains($req->url(), 'tok_secret')); } @@ -51,7 +51,7 @@ class PromotionServiceTest extends TestCase public function test_dispatch_returns_false_on_a_github_error(): void { Http::fake(['api.github.com/*' => Http::response('forbidden', 403)]); - $this->assertFalse(app(PromotionService::class)->deployPublic('v0.10.0-beta1')); + $this->assertFalse(app(PromotionService::class)->deployPublic('v0.10.0-rc1')); } public function test_dispatch_without_a_token_does_not_call_github(): void @@ -59,7 +59,7 @@ class PromotionServiceTest extends TestCase config()->set('clusev.github_token', ''); Http::fake(); - $this->assertFalse(app(PromotionService::class)->deployPublic('v0.10.0-beta1')); + $this->assertFalse(app(PromotionService::class)->deployPublic('v0.10.0-rc1')); Http::assertNothingSent(); } diff --git a/tests/Feature/ReleaseBridgeTest.php b/tests/Feature/ReleaseBridgeTest.php index 3dcc24b..fd8bd3a 100644 --- a/tests/Feature/ReleaseBridgeTest.php +++ b/tests/Feature/ReleaseBridgeTest.php @@ -55,13 +55,13 @@ class ReleaseBridgeTest extends TestCase $id = app(ReleaseBridge::class)->requestStaging('0.10.0'); file_put_contents( $this->dir."/release-result-{$id}.json", - json_encode(['id' => $id, 'ok' => true, 'tag' => 'v0.10.0-beta1', 'message' => 'ok']) + json_encode(['id' => $id, 'ok' => true, 'tag' => 'v0.10.0-rc1', 'message' => 'ok']) ); $res = app(ReleaseBridge::class)->result($id); $this->assertTrue($res['ok']); - $this->assertSame('v0.10.0-beta1', $res['tag']); + $this->assertSame('v0.10.0-rc1', $res['tag']); } public function test_result_is_null_until_the_host_replies(): void diff --git a/tests/Feature/ReleaseCheckerChannelTest.php b/tests/Feature/ReleaseCheckerChannelTest.php index fad7c8b..e7c7000 100644 --- a/tests/Feature/ReleaseCheckerChannelTest.php +++ b/tests/Feature/ReleaseCheckerChannelTest.php @@ -11,16 +11,16 @@ class ReleaseCheckerChannelTest extends TestCase { use RefreshDatabase; - public function test_channel_is_always_stable_even_if_a_beta_setting_lingers(): void + public function test_channel_is_always_stable_even_if_a_prerelease_setting_lingers(): void { - Setting::put('release_channel', 'beta'); // a stale setting from before the drop + Setting::put('release_channel', 'rc'); // a stale setting from before the drop $this->assertSame('stable', app(ReleaseChecker::class)->channel()); } - public function test_newest_version_ignores_rc_and_beta_prereleases(): void + public function test_newest_version_ignores_rc_prereleases(): void { $checker = app(ReleaseChecker::class); - $tags = ['v0.11.0', 'v0.12.0-rc1', 'v0.12.0-beta2', 'v0.10.0']; + $tags = ['v0.11.0', 'v0.12.0-rc1', 'v0.12.0-rc2', 'v0.10.0']; $this->assertSame('0.11.0', $checker->newestVersion($tags, 'stable')); } } diff --git a/tests/Feature/ReleasePageTest.php b/tests/Feature/ReleasePageTest.php index 93e52ef..20ab477 100644 --- a/tests/Feature/ReleasePageTest.php +++ b/tests/Feature/ReleasePageTest.php @@ -76,10 +76,10 @@ class ReleasePageTest extends TestCase $id = $c->get('pendingId'); file_put_contents( $this->dir."/release-result-{$id}.json", - json_encode(['id' => $id, 'ok' => true, 'tag' => 'v0.10.0-beta1', 'message' => 'ok']) + json_encode(['id' => $id, 'ok' => true, 'tag' => 'v0.10.0-rc1', 'message' => 'ok']) ); - $c->call('pollResult')->assertSet('pendingId', null)->assertSee('v0.10.0-beta1'); + $c->call('pollResult')->assertSet('pendingId', null)->assertSee('v0.10.0-rc1'); } public function test_poll_result_audits_a_host_failure_as_an_error(): void @@ -101,14 +101,14 @@ class ReleasePageTest extends TestCase Livewire::test(Index::class)->assertStatus(404); } - public function test_render_includes_the_live_pipeline_for_a_beta(): void + public function test_render_includes_the_live_pipeline_for_an_rc(): void { - config()->set('clusev.version', '0.9.61-beta1'); + config()->set('clusev.version', '0.9.61-rc1'); config()->set('clusev.staging_slug', ''); // degrade: no GitHub calls config()->set('clusev.github_token', ''); Livewire::test(Index::class) - ->assertViewHas('pipeline', fn ($p) => is_array($p) && $p['tag'] === 'v0.9.61-beta1'); + ->assertViewHas('pipeline', fn ($p) => is_array($p) && $p['tag'] === 'v0.9.61-rc1'); } public function test_render_pipeline_is_null_for_a_stable_version(): void @@ -117,15 +117,15 @@ class ReleasePageTest extends TestCase Livewire::test(Index::class)->assertViewHas('pipeline', null); } - public function test_deploy_public_dispatches_and_audits_for_a_beta(): void + public function test_deploy_public_dispatches_and_audits_for_an_rc(): void { - config()->set('clusev.version', '0.10.0-beta1'); + config()->set('clusev.version', '0.10.0-rc1'); config()->set('clusev.github_token', 'tok'); config()->set('clusev.staging_slug', 'acme/staging'); config()->set('clusev.release_branch', 'feat/v1-foundation'); Http::fake(['api.github.com/*' => Http::response('', 204)]); - $token = ConfirmToken::issue('releasePublic', ['tag' => 'v0.10.0-beta1']); + $token = ConfirmToken::issue('releasePublic', ['tag' => 'v0.10.0-rc1']); ConfirmToken::confirm($token); // the modal confirmation step Livewire::test(Index::class)->call('applyDeployPublic', $token); @@ -158,11 +158,11 @@ class ReleasePageTest extends TestCase public function test_publish_actions_are_a_no_op_for_a_stable_version(): void { - config()->set('clusev.version', '0.10.0'); // not a beta + config()->set('clusev.version', '0.10.0'); // not an rc config()->set('clusev.public_slug', ''); // degrade: no public-tags read on render Http::fake(); - $token = ConfirmToken::issue('releasePublic', ['tag' => 'v0.10.0-beta1']); + $token = ConfirmToken::issue('releasePublic', ['tag' => 'v0.10.0-rc1']); ConfirmToken::confirm($token); // the modal confirmation step Livewire::test(Index::class)->call('applyDeployPublic', $token); diff --git a/tests/Feature/TerminalTest.php b/tests/Feature/TerminalTest.php index ac367d5..055808e 100644 --- a/tests/Feature/TerminalTest.php +++ b/tests/Feature/TerminalTest.php @@ -236,12 +236,12 @@ class TerminalTest extends TestCase public function test_search_filters_the_server_rail(): void { Server::create(['name' => 'alpha-web', 'ip' => '10.0.0.1', 'status' => 'online']); - Server::create(['name' => 'beta-db', 'ip' => '10.0.0.2', 'status' => 'online']); + Server::create(['name' => 'delta-db', 'ip' => '10.0.0.2', 'status' => 'online']); Livewire::test(Index::class) ->set('search', 'alpha') ->assertSee('alpha-web') - ->assertDontSee('beta-db'); + ->assertDontSee('delta-db'); } public function test_search_box_appears_once_there_is_more_than_one_server(): void diff --git a/tests/Feature/VersionUpdateCheckTest.php b/tests/Feature/VersionUpdateCheckTest.php index 3ce7ccc..1f2f7fb 100644 --- a/tests/Feature/VersionUpdateCheckTest.php +++ b/tests/Feature/VersionUpdateCheckTest.php @@ -114,16 +114,16 @@ class VersionUpdateCheckTest extends TestCase { config()->set('clusev.version', '0.9.6'); Http::fake([self::TAGS_URL => Http::response([ - ['name' => 'v1.0.0-beta1'], ['name' => 'v0.9.6'], + ['name' => 'v1.0.0-rc1'], ['name' => 'v0.9.6'], ])]); - // stable: 1.0.0-beta1 is not offered, 0.9.6 == installed → current + // stable: 1.0.0-rc1 is not offered, 0.9.6 == installed → current Setting::put('release_channel', 'stable'); Livewire::test(Index::class)->call('checkUpdates')->assertSet('updateState', 'current'); - // a stale beta setting from before the channel drop is ignored → still current + // a stale prerelease setting from before the channel drop is ignored → still current Cache::flush(); - Setting::put('release_channel', 'beta'); + Setting::put('release_channel', 'rc'); Livewire::test(Index::class) ->call('checkUpdates') ->assertSet('updateState', 'current'); diff --git a/tests/Unit/ReleasePlannerTest.php b/tests/Unit/ReleasePlannerTest.php index 2469301..eed45a6 100644 --- a/tests/Unit/ReleasePlannerTest.php +++ b/tests/Unit/ReleasePlannerTest.php @@ -15,21 +15,21 @@ class ReleasePlannerTest extends TestCase $this->planner = new ReleasePlanner; } - public function test_stable_version_offers_patch_minor_major_without_continue_beta(): void + public function test_stable_version_offers_patch_minor_major_without_continue_rc(): void { $t = $this->planner->proposedTargets('0.9.58'); $this->assertSame('0.9.59', $t['patch']); $this->assertSame('0.10.0', $t['minor']); $this->assertSame('1.0.0', $t['major']); - $this->assertArrayNotHasKey('continueBeta', $t); + $this->assertArrayNotHasKey('continueRc', $t); } - public function test_beta_version_also_offers_continue_beta_of_its_base(): void + public function test_rc_version_also_offers_continue_rc_of_its_base(): void { - $t = $this->planner->proposedTargets('0.10.0-beta3'); + $t = $this->planner->proposedTargets('0.10.0-rc3'); - $this->assertSame('0.10.0', $t['continueBeta']); + $this->assertSame('0.10.0', $t['continueRc']); $this->assertSame('0.10.1', $t['patch']); $this->assertSame('0.11.0', $t['minor']); $this->assertSame('1.0.0', $t['major']); @@ -42,9 +42,9 @@ class ReleasePlannerTest extends TestCase public function test_allowed_targets_are_the_proposed_values(): void { - $allowed = $this->planner->allowedTargets('0.10.0-beta1'); + $allowed = $this->planner->allowedTargets('0.10.0-rc1'); - $this->assertContains('0.10.0', $allowed); // continueBeta + $this->assertContains('0.10.0', $allowed); // continueRc $this->assertContains('0.11.0', $allowed); // minor $this->assertNotContains('0.9.0', $allowed); }