gitfed-ctl: base "current version" on the live deployment, not VERSION
Found live: a previous run's git pull can succeed (advancing the checkout's VERSION) while a later step fails, leaving the checkout ahead of what's actually deployed. Reading VERSION for the "current" comparison then reports "up to date" even though production is still on the old image, silently skipping the breaking-change wizard for a version that was never actually deployed. Now reads the real image tag via kubectl get deployment gitfed instead.
2 files changed
+36 −9
M
CHANGELOG.md
+4 −0
M
cmd/gitfed-ctl/changelog.go
+32 −9
CHANGELOG.md
@@ -2,6 +2,10 @@
A bullet starting with `**BREAKING:**` flags a change gitfed-ctl's update wizard makes you acknowledge individually before it will let you upgrade past that version.
+## 1.2.14
+
+- Fixed a real bug found live (Thomas's instance): `gitfed-ctl`'s "current version" came from the checkout's own `VERSION` file, not what's actually deployed. If an earlier run's `git pull` succeeded but a later step (build/import/apply/rollout) failed, the checkout ends up ahead of the live deployment — the next check then read the already-advanced `VERSION` and reported "up to date" even though production was still running the old image, silently skipping the breaking-change wizard for a version that was never actually deployed. "Current version" now comes from `kubectl get deployment gitfed`'s actual image tag, not the checkout.
+
## 1.2.13
- Fixed a real bug found live: `gitfed-ctl`'s update wizard only ever captured the "build" step's output, so any other step failing (pull, import, apply, rollout) showed a bare "Erreur : exit status 1" with no way to tell why. Every step's output is now captured and shown when it's the one running or the one that failed. Also stopped discarding `kubectl exec`'s stderr in the backup step for the same reason.
cmd/gitfed-ctl/changelog.go
@@ -3,7 +3,6 @@ package main
import (
"context"
"fmt"
- "os"
"strconv"
"strings"
)
@@ -26,10 +25,18 @@ type versionChangelog struct {
TagMissing bool
}
-// checkForUpdates fetches tags and compares the local VERSION file against
-// whatever VERSION says at the tip of the configured upstream branch,
-// without touching the working tree (no pull yet — that only happens once
-// the wizard is confirmed).
+// checkForUpdates fetches tags and compares what's actually deployed on the
+// cluster right now against whatever VERSION says at the tip of the
+// configured upstream branch, without touching the working tree (no pull
+// yet — that only happens once the wizard is confirmed).
+//
+// "current" deliberately comes from the live deployment (kubectl), not the
+// checkout's own VERSION file — found live: a previous run's git pull can
+// succeed (advancing VERSION) while a later step (build/import/apply/
+// rollout) fails, leaving the checkout ahead of what's actually running.
+// Reading VERSION here would then report "up to date" even though
+// production is still on the old image, and silently skip re-showing any
+// breaking changes for a version that was never actually deployed.
func checkForUpdates(ctx context.Context, sourceDir string) (current, target string, upToDate bool, err error) {
if _, err := runQuiet(ctx, sourceDir, "git", "fetch", "--tags"); err != nil {
return "", "", false, fmt.Errorf("git fetch: %w", err)
@@ -44,17 +51,33 @@ func checkForUpdates(ctx context.Context, sourceDir string) (current, target str
if err != nil {
return "", "", false, fmt.Errorf("read VERSION at %s: %w", ref, err)
}
+ target = strings.TrimSpace(targetRaw)
- currentBytes, err := os.ReadFile(sourceDir + "/VERSION")
+ current, err = deployedVersion(ctx)
if err != nil {
- return "", "", false, fmt.Errorf("read local VERSION: %w", err)
+ return "", "", false, fmt.Errorf("read deployed version: %w", err)
}
- current = strings.TrimSpace(string(currentBytes))
- target = strings.TrimSpace(targetRaw)
return current, target, current == target, nil
}
+// deployedVersion reads the image tag actually configured on the live
+// "gitfed" deployment (namespace gitfed, container "server" — see
+// deploy/k8s/deployment.yaml) via kubectl, the source of truth for "what's
+// really running" this check needs.
+func deployedVersion(ctx context.Context) (string, error) {
+ out, err := runQuiet(ctx, "", "kubectl", "-n", "gitfed", "get", "deployment", "gitfed",
+ "-o", `jsonpath={.spec.template.spec.containers[?(@.name=="server")].image}`)
+ if err != nil {
+ return "", err
+ }
+ tag := strings.TrimPrefix(strings.TrimSpace(out), "gitfed:")
+ if tag == "" {
+ return "", fmt.Errorf("empty image tag reported by kubectl")
+ }
+ return tag, nil
+}
+
// upstreamRef resolves the branch's configured tracking remote, falling
// back to origin/main if none is set (e.g. a checkout cloned in a way that
// never set one up).