vuer-release build/release flow (release-tool CLI internals)

The release tool is readable source, not a black box

TechTeamer/vuer-release-cli is a plain Python repo. The release_tool binary published as a GitHub release asset is just a packaged build of it. Everything below was read out of that source on 2026-07-22 and corrects several assumptions previously inferred from artifacts (release.json files, Dockerfiles, Harbor tags).

Companion notes: vuer-release-cut-recipe = how to cut a release; this note = what the tool actually does when you do.

The flow end to end

release-tool release create project <p>   (local, interactive)
        │  asks VERSION / BUILD_NUMBER / TAG per component
        ▼
projects/<p>/release/<N>/release.json  → commit → push tag <p>@<N>   (or base@<N>)
        ▼  tag matching *@*
.github/workflows/autobuild.yml  (self-hosted runner)
        │  parse NAME@VERSION → checkout
        │  secrets.RELEASE_PAT → ~/.git-credentials (HTTPS, x-access-token)
        │  download release_tool asset from TechTeamer/vuer-release-cli
        ▼
release-tool gen project|base   →   build project|base   →   publish login/push
                                                              HARBOR_USER / HARBOR_SECRET
  1. Developer, locally: release-tool release create project <project> prompts for VERSION / BUILD_NUMBER / TAG per component, writes projects/<p>/release/<N>/release.json, commits it, pushes the git tag <p>@<N>.
  2. Tag push matching *@* fires .github/workflows/autobuild.yml on a self-hosted runner.
  3. Workflow: parse NAME@VERSION from the tag → checkout → write secrets.RELEASE_PAT into ~/.git-credentials (HTTPS auth as x-access-token) → download the release_tool asset → genbuildpublish login + publish push to Harbor with HARBOR_USER / HARBOR_SECRET.

release create vs gen — who writes what

release create writes COMPONENT_LIST. gen only reads it.

A common mis-model is that gen snapshots the component config into release.json at build time. It does not — the two steps are cleanly separated.

Step 1 — release create (interactive, local):

  1. commands/release/release.py:239-259 get_project_component_env_values() merges base then project: reads base/components/<c>/component_env_values.json, then projects/<p>/components/<c>/component_env_values.json, does env_values.pop("PROJECT_NAME", "") and env_values.update(project_env_values)project wins.
  2. release.py:284-297 ask_component_env_values()release.py:269-281 ask_env_values() prompts for every key, with the merged value as default=value.
  3. The confirmed values are written to projects/<p>/release/<N>/release.json (release.py:356).

So component_env_values.json supplies interactive prompt defaults, not an automatic snapshot. The operator confirms or overrides.

Step 2 — gen (CI): commands/gen/gen.py:76 is return release_json["COMPONENT_LIST"]. It reads release.json only and never re-reads component_env_values.json. Once release.json exists, the component env files are irrelevant to the build.

TAG is NOT a required env value — omit it and the component is silently never cloned

REQUIRED_ENV_VALUES = {"VERSION": "", "BUILD_NUMBER": ""} (release.py:49), merged via with_required_env_values() (release.py:262-266). TAG is not in that set — it is prompted only if it already appears in the merged component env values.

  • By design for janus: janus has no TAG key in base/components/janus/component_env_values.json nor in any partner’s janus file → janus never gets a TAG → has_source() is false → the CLI never clones janus. Its source is fetched by install/install-janus-build-env.sh from JANUS_REPOSITORY + JANUS_VERSION_COMMIT.
  • The trap: add a new base component and forget TAG in its component_env_values.json, and it will silently never be cloned — no error, just missing source.

Where the janus pin really lives

projects/<p>/components/janus/component_env_values.json (VERSION / JANUS_REPOSITORY / JANUS_VERSION_COMMIT / BUILD_NUMBER) is the default at the release create prompt; pressing enter accepts it into release.json. Effective, but overridable at release time — not immutable. This corrects the older claim in vuer-release-cut-recipe that gen overwrites hand-set pins.

Corrections (each verified in CLI source)

TAG — not VERSION — is the git ref the source is fetched from

gen.py::download_source calls git.pull_remote_tag(owner_url, repo_name=component["NAME"], tag=component["TAG"], …)git clone --branch <TAG> --single-branch --depth 1.

VERSION is used only for the image-tag string and the generated .env: make_image_tag{registry}/{PROJECT_NAME}/{NAME}:{VERSION}.{BUILD_NUMBER}-{SECURITY_NUMBER}.

Evidence: projects/nusz/release/16/release.json pins vuer_oss VERSION 1.9.11.47 / TAG nusz-1.9.11.47, and the vuer_oss repo has no bare 1.9.11.47 tag. Partner tags look like nusz-1.9.11.47, demo-1.9.11.29, dap-1.9.11.NN.

A new base component does NOT need its own base@N release first

BASE_COMPONENT_IMAGE_TAG / PROJECT_COMPONENT_IMAGE_TAG / RELEASE_COMPONENT_IMAGE_TAG are all the same locally-computed string and are never resolved against the registry.

build.py::build_project_components builds the base stage first (context base/components/<NAME>), then the project stage, whose FROM $BASE_COMPONENT_IMAGE_TAG resolves to the just-built local image.

Corroborating artifacts: vuer_css / vuer_oss appear in zero base/release/*/release.json, yet every partner does FROM $BASE_COMPONENT_IMAGE_TAG; and demo-project@1 built resource-manager 1.0.3 while base@5 had published 1.0.7.

Only hard requirement: base/components/<NAME>/Dockerfile exists.

Component NAME == GitHub repo name (no override key exists)

repo_name = component["NAME"], cloned from GIT_REMOTE_ORG = https://github.com/TechTeamer. There is no repo-override key in the CLI, so a component directory name must equal its GitHub repo name.

JANUS_REPOSITORY is a red herring — it is consumed only by install/install-janus-build-env.sh for the janus source wget, never by the CLI clone.

  • gen.py::rm_always sanitizes the source tree before packaging: removes .yarnrc, config/dev.json, .git, and every supervisor_* / nginx_* file whose name does not contain docker. That is why app repos can safely ship *_dev.conf variants next to *_docker.conf even though install/configure-app.sh symlinks all supervisor*.conf / nginx*.conf into the conf dirs.
  • Tarball layout: make_source_package<NAME>-<TAG>; pkg/util.py::tarball_dir tars with the directory named <NAME>, so the archive’s top-level dir is the component NAME. Hence base Dockerfiles can do cd /workspace && tar -xzf … && chown -R $DOCKER_USER:$DOCKER_USER $NAME with ENV APP_HOME /workspace/$NAME.
  • A component is “available” to a project purely by having a directory at projects/<p>/components/<c>/. pkg/ensure.py::project_components lists components.subdirs() and hard-fails Unknown component otherwise. Inside that dir both component_env_values.json and Dockerfile are optional (get_json(default={}); gen/build guard with .exists()). The release/.gitkeep file is required, because ReleaseDir.next_release_version() calls subdirs().

Do NOT scaffold with release-tool component create

release_tool/template/component.j2 is stale: it emits the old --mount=…,source=/install style instead of from=install-scripts, plus a mangled nginx/redis block. Copy an existing base/components/*/Dockerfile instead.

Pre-existing latent issue

base/release/6 lists pdfservice, but TechTeamer/pdfservice does not resolve → a re-run of gen base -r 6 would break.

Applied to FKITDEV-8349 (DÁP)

Branch feat/FKITDEV-8349-dap in vuer-release, commits 97149bd + 409a28e, solo-author Andras Lederer <25370292+wowjeeez@users.noreply.github.com>, subject-only commit messages.

Adds projects/dap/ (janus 1.4.1 / cc0fdca8, vuer_css, vuer_oss, resource-manager, dap-demo-partner) plus a new base component base/components/dap-demo-partner/, derived from base/components/vuer_css/Dockerfile minus install-redis.sh (the app needs nginx, has no redis client).

  • The legacy vuer_build/partner/dap/dap_demo_partner/ image is obsolete. It was a bespoke 2-stage UBI8 build that cloned dap-demo-partner.git in-build using an SSH deploy key (docker_dap_demo_partner.key, never committed — .gitignore has *.key). The CLI clones over HTTPS with RELEASE_PAT: no SSH key, no new repo secret.
  • Naming is forced: the component must be dap-demo-partner (hyphens), because NAME → repo name and dap_demo_partner would 404. This renames the published image harbor.techteamer.com/dap-facekom/dap_demo_partner:……/dap-demo-partner:… — a deployment-visible change.
  • Release-time TAG values (the easy mistake):
    componentTAG shapenote
    dap-demo-partner1.0.7.1repo tags are 1.0.0.1 … 1.0.7.1 — there is no bare 1.0.7
    resource-manager1.0.8plain tags
    vuer_css / vuer_ossdap-1.9.11.NNpartner-prefixed
  • Merge to master before release create — the CLI tags from the current branch.

vuer-release-cut-recipe · FKITDEV-8349 · FKITDEV-8344-vuer-release-migration-batch · FKITDEV-8354-mvm-supervisor-config-dedup · FKITDEV-8252 · release-process · release-automation-design · client-registry · FaceKom