From c8def2b44a3ecdb5973052d7328cd3c0729f80f1 Mon Sep 17 00:00:00 2001 From: moauto <54212639+mo-auto@users.noreply.github.com> Date: Wed, 24 Jun 2026 18:25:29 +0300 Subject: [PATCH 1/2] ci(release): pin source versions + fix demo image tag & installer JANS_APP_VERSION (test feedback) - Pin FLEX_SOURCE_VERSION to the released flex commit (--flex-source-sha, passed by release-trigger as the source-ref HEAD) and JANS_SOURCE_VERSION to the commit the mapped jans release pins (fetched from the jans vX.Y.Z docker-jans-all-in-one Dockerfile), so images build the exact released sources. - start_flex_aio_demo.sh GLUU_VERSION is the flex-all-in-one image tag, so it now takes the suffixed tag (e.g. 6.2.0-1) instead of the bare version. - flex_setup.py app_versions: JANS_APP_VERSION -> the mapped jans version and JANS_BUILD emptied, so the installer pulls the jans release (not 0.0.0-nightly). verify() now guards both. Signed-off-by: moauto <54212639+mo-auto@users.noreply.github.com> --- .github/workflows/release-trigger.yml | 11 +++++- automation/release/version_bump.py | 57 +++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-trigger.yml b/.github/workflows/release-trigger.yml index 162c38e2b..b6e4823f9 100644 --- a/.github/workflows/release-trigger.yml +++ b/.github/workflows/release-trigger.yml @@ -89,9 +89,16 @@ jobs: run: git checkout --detach - name: Bump every owned version in the working tree + env: + VERSION: ${{ github.event.inputs.version }} + DOCKER_SUFFIX: ${{ github.event.inputs.docker_suffix }} run: | - python3 automation/release/version_bump.py "${{ github.event.inputs.version }}" \ - --docker-suffix "${{ github.event.inputs.docker_suffix }}" + # --flex-source-sha pins FLEX_SOURCE_VERSION to the released commit (HEAD here is the + # source ref, before the bump commit); version_bump also pins JANS_SOURCE_VERSION to the + # jans release's value. + python3 automation/release/version_bump.py "${VERSION}" \ + --docker-suffix "${DOCKER_SUFFIX}" \ + --flex-source-sha "$(git rev-parse HEAD)" # Safety net: abort BEFORE committing/pushing if any owned version was missed. - name: Verify the tree is fully bumped diff --git a/automation/release/version_bump.py b/automation/release/version_bump.py index 90a0e6e9d..8f1d1c136 100644 --- a/automation/release/version_bump.py +++ b/automation/release/version_bump.py @@ -301,6 +301,8 @@ def bump(version, docker_suffix="1"): image_tag_arg_subs = [ (r'(ARG\s+FLEX_BASE_VERSION=)0\.0\.0-nightly', rf"\g<1>{flex_tag}"), (r'(ARG\s+BASE_VERSION=)0\.0\.0-nightly', rf"\g<1>{jans_tag}"), + # demo scripts: GLUU_VERSION is the flex-all-in-one image tag, so it is suffixed. + (r'(GLUU_VERSION=["\x27]?)0\.0\.0-nightly', rf"\g<1>{flex_tag}"), ] # 4. The remaining "0.0.0-nightly" / "0.0.0--nightly" sentinels are Flex-release @@ -344,9 +346,51 @@ def bump(version, docker_suffix="1"): if sub(pkg, r'(?m)^( "version":\s*")0\.0\.0(",?)$', rf"\g<1>{version}\g<2>"): changed.append(pkg) + # flex-linux-setup app_versions: JANS_APP_VERSION -> the mapped jans version and JANS_BUILD + # emptied, so the installer pulls the jans *release* (not 0.0.0-nightly) at flex release time. + flex_setup = "flex-linux-setup/flex_linux_setup/flex_setup.py" + if (ROOT / flex_setup).exists(): + n = sub(flex_setup, r'("JANS_APP_VERSION":\s*")0\.0\.0(")', rf"\g<1>{jv}\g<2>") + n += sub(flex_setup, r'("JANS_BUILD":\s*")-nightly(")', r"\g<1>\g<2>") + if n: + changed.append(flex_setup) + return sorted(set(changed)) +def fetch_jans_source_version(jans_ver): + """The JANS_SOURCE_VERSION pinned in the jans release Dockerfiles -- so flex builds the + same jans source the jans release did.""" + import urllib.request + url = (f"https://raw.githubusercontent.com/JanssenProject/jans/" + f"v{jans_ver}/docker-jans-all-in-one/Dockerfile") + try: + with urllib.request.urlopen(url, timeout=30) as resp: + text = resp.read().decode("utf-8") + except Exception as e: + raise SystemExit(f"could not fetch jans v{jans_ver} Dockerfile to pin JANS_SOURCE_VERSION: {e}") + m = re.search(r"JANS_SOURCE_VERSION=([0-9a-f]{40})", text) + if not m: + raise SystemExit(f"no JANS_SOURCE_VERSION found in jans v{jans_ver} Dockerfile") + return m.group(1) + + +def pin_source_versions(flex_sha, jans_ver): + """Release mode: pin FLEX_SOURCE_VERSION to the released flex commit and JANS_SOURCE_VERSION + to the commit the jans release pinned, so each image builds the exact released sources.""" + if not re.fullmatch(r"[0-9a-f]{40}", flex_sha or ""): + raise SystemExit(f"--flex-source-sha must be a 40-char commit sha, got: {flex_sha!r}") + jans_sha = fetch_jans_source_version(jans_ver) + changed = [] + for rel in grep_files("FLEX_SOURCE_VERSION="): + if sub(rel, r"(FLEX_SOURCE_VERSION=)[0-9a-f]{40}", rf"\g<1>{flex_sha}"): + changed.append(rel) + for rel in grep_files("JANS_SOURCE_VERSION="): + if sub(rel, r"(JANS_SOURCE_VERSION=)[0-9a-f]{40}", rf"\g<1>{jans_sha}"): + changed.append(rel) + return changed + + # --------------------------------------------------------------------------- # # verify -- the safety net; any leftover owned sentinel fails the release # --------------------------------------------------------------------------- # @@ -377,6 +421,14 @@ def verify(): if (ROOT / pkg).exists() and re.search(r'(?m)^ "version":\s*"0\.0\.0"', read(pkg)): problems.append(f"{pkg}: top-level version still '0.0.0'") + flex_setup = "flex-linux-setup/flex_linux_setup/flex_setup.py" + if (ROOT / flex_setup).exists(): + txt = read(flex_setup) + if re.search(r'"JANS_APP_VERSION":\s*"0\.0\.0"', txt): + problems.append(f"{flex_setup}: JANS_APP_VERSION still '0.0.0'") + if re.search(r'"JANS_BUILD":\s*"-nightly"', txt): + problems.append(f"{flex_setup}: JANS_BUILD still '-nightly'") + if problems: print("VERSION VERIFY FAILED -- owned versions left at the dev sentinel:\n") for p in problems: @@ -397,6 +449,9 @@ def main(): ap.add_argument("--root", type=Path, default=Path.cwd(), help="repo root (default: cwd)") ap.add_argument("--docker-suffix", default="1", help="suffix for image tags: '1' -> X.Y.Z-1 (default: 1)") + ap.add_argument("--flex-source-sha", metavar="SHA", + help="release mode: pin FLEX_SOURCE_VERSION to this flex commit and " + "JANS_SOURCE_VERSION to the jans release's pinned commit") args = ap.parse_args() if not VERSION_RE.match(args.version): @@ -411,6 +466,8 @@ def main(): sys.exit(0 if verify() else 1) changed = bump(args.version, args.docker_suffix) + if args.flex_source_sha: + changed = sorted(set(changed + pin_source_versions(args.flex_source_sha, jans_version(args.version)))) mode = "DRY RUN -- would change" if DRY else "changed" jv = jans_version(args.version) From af3457bf04c69a44496741dded871fc35fff14b6 Mon Sep 17 00:00:00 2001 From: moauto <54212639+mo-auto@users.noreply.github.com> Date: Wed, 24 Jun 2026 18:59:59 +0300 Subject: [PATCH 2/2] ci(release): fail closed if a source-version pin is incomplete (review follow-up) pin_source_versions() rewrote only [0-9a-f]{40} values, so a FLEX_SOURCE_VERSION/JANS_SOURCE_VERSION that wasn't 40-hex (a branch name, an already-different sha) was silently skipped and the image would build from stale source. Add a postcondition that re-scans every owned assignment and aborts the release if any literal value still differs from the requested sha (${VAR} indirections are ignored; skipped under --dry-run). Signed-off-by: moauto <54212639+mo-auto@users.noreply.github.com> --- automation/release/version_bump.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/automation/release/version_bump.py b/automation/release/version_bump.py index 8f1d1c136..0bebb15e7 100644 --- a/automation/release/version_bump.py +++ b/automation/release/version_bump.py @@ -388,6 +388,25 @@ def pin_source_versions(flex_sha, jans_ver): for rel in grep_files("JANS_SOURCE_VERSION="): if sub(rel, r"(JANS_SOURCE_VERSION=)[0-9a-f]{40}", rf"\g<1>{jans_sha}"): changed.append(rel) + + # Fail closed: after the rewrites, every owned assignment must equal the requested sha. A value + # the regex could not match (non-40-hex, a branch name, an already-different sha) would otherwise + # silently ship an image built from stale source. ``${VAR}`` indirections are left alone; skipped + # under --dry-run, which writes nothing. + if not DRY: + stale = [] + for rel in grep_files("FLEX_SOURCE_VERSION="): + stale += [f"{rel}: FLEX_SOURCE_VERSION={v}" + for v in re.findall(r"FLEX_SOURCE_VERSION=(\S+)", read(rel)) + if not v.startswith("$") and v != flex_sha] + for rel in grep_files("JANS_SOURCE_VERSION="): + stale += [f"{rel}: JANS_SOURCE_VERSION={v}" + for v in re.findall(r"JANS_SOURCE_VERSION=(\S+)", read(rel)) + if not v.startswith("$") and v != jans_sha] + if stale: + raise SystemExit( + f"source-version pin incomplete (expected flex={flex_sha} jans={jans_sha}):\n " + + "\n ".join(stale)) return changed