@@ -37,9 +37,9 @@ All scripts must be run from the **repository root**.
3737
3838---
3939
40- ## Orchestrator — ` prefetch-all.sh `
40+ ## Orchestrator ` prefetch-all.sh `
4141
42- ** For most local and CI use, this is the only script you need to run.**
42+ ** For most local and CI use, this is the main script you need to run.**
4343
4444` prefetch-all.sh ` orchestrates all four lockfile generators in the correct
4545order, downloading dependencies into ` cachi2/output/deps/ ` . After running it,
@@ -66,6 +66,7 @@ Then build with make:
6666``` bash
6767# On macOS use gmake
6868gmake codeserver-ubi9-python-3.12 BUILD_ARCH=linux/arm64 PUSH_IMAGES=no
69+ # OR using Local podman build (Please scroll downn to Local podman build section for more detail.)
6970```
7071
7172### Options
@@ -75,18 +76,30 @@ gmake codeserver-ubi9-python-3.12 BUILD_ARCH=linux/arm64 PUSH_IMAGES=no
7576| ` --component-dir DIR ` | Component directory (required), e.g. ` codeserver/ubi9-python-3.12 ` |
7677| ` --rhds ` | Use downstream (RHDS) lockfiles instead of upstream (ODH, the default) |
7778| ` --flavor NAME ` | Lock file flavor (default: ` cpu ` ) |
78- | ` --tekton-file FILE ` | Tekton PipelineRun YAML for npm path discovery (auto-detected from ` .tekton/ ` if omitted) |
7979| ` --activation-key KEY ` | Red Hat activation key for RHEL RPMs (optional) |
8080| ` --org ORG ` | Red Hat organization ID for RHEL RPMs (optional) |
8181
8282### What it does
8383
8484| Step | Condition | Script called |
8585| ------| -----------| ---------------|
86- | 1. Generic artifacts | ` artifacts.in.yaml ` exists | ` create-artifact-lockfile.py ` |
87- | 2. Pip wheels | ` pyproject.toml ` exists | ` create-requirements-lockfile.sh --download ` |
88- | 3. NPM packages | ` package-lock.json ` files found | ` download-npm.sh ` |
89- | 4. RPMs | ` rpms.in.yaml ` exists | ` hermeto-fetch-rpm.sh ` (if lockfile committed) or ` create-rpm-lockfile.sh --download ` |
86+ | 1. Generic artifacts | ` prefetch-input/<variant>/artifacts.in.yaml ` exists | ` create-artifact-lockfile.py ` |
87+ | 2. Pip wheels | ` pyproject.toml ` exists in component dir | ` create-requirements-lockfile.sh --download ` |
88+ | 3. NPM packages | Tekton PipelineRun found for component (see below) | ` download-npm.sh --tekton-file ` |
89+ | 4. RPMs | ` prefetch-input/<variant>/rpms.in.yaml ` exists | ` hermeto-fetch-rpm.sh ` (if lockfile committed) or ` create-rpm-lockfile.sh --download ` |
90+
91+ ** Variant directory:** Lockfiles live under ` prefetch-input/odh/ ` (upstream) or
92+ ` prefetch-input/rhds/ ` (downstream). If that directory is missing, steps 1 and 4
93+ are skipped; steps 2 (pip) and 3 (npm) still run when their inputs exist
94+ (` pyproject.toml ` , or a Tekton file for the component).
95+
96+ ** Step 3 (NPM):** The script finds the Tekton file automatically via
97+ ` find_tekton_yaml ` : it looks for a ` .tekton/*pull-request*.yaml ` whose
98+ ` dockerfile ` param matches this component, RHDS first
99+ (` COMPONENT_DIR/Dockerfile.konflux.* ` ), then ODH (` COMPONENT_DIR/Dockerfile.* ` ).
100+ If no Tekton file is found, npm is skipped. If the Tekton file has no
101+ ` npm ` -type ` prefetch-input ` entries, ` download-npm.sh ` exits successfully
102+ (nothing to download).
90103
91104Steps are skipped if their input files don't exist. For RPMs, if
92105` rpms.lock.yaml ` is already committed, it downloads directly (skipping
@@ -95,11 +108,13 @@ lockfile regeneration) — this avoids cross-platform issues on arm64 CI runners
95108### GitHub Actions integration
96109
97110The GHA workflow template (` .github/workflows/build-notebooks-TEMPLATE.yaml ` )
98- calls ` prefetch-all.sh ` automatically for codeserver targets before running
99- ` make ` . Non-codeserver targets skip the prefetch step entirely. After the
100- build, container tests run (e.g. ` tests/containers ` with pytest); image
101- metadata is read from both Docker ` Config ` and ` ContainerConfig ` so labels
102- work when the daemon is Podman (see
111+ derives the component directory from the ** Makefile** (dry-run of the build
112+ target, parsing ` #*# Image build directory: <...> ` ), so it works for all image
113+ targets (codeserver, jupyter-* , runtime-* , rstudio-* , base-images-* ). Prefetch
114+ runs when ` COMPONENT_DIR/prefetch-input ` exists; otherwise the step is skipped.
115+ After the build, container tests run (e.g. ` tests/containers ` with pytest);
116+ image metadata is read from both Docker ` Config ` and ` ContainerConfig ` so
117+ labels work when the daemon is Podman (see
103118[ tests/containers/docs/github-vs-local-image-metadata.md] ( ../../tests/containers/docs/github-vs-local-image-metadata.md ) ).
104119
105120** uv version:** The repo root ` uv.toml ` specifies the ` uv ` version (e.g.
@@ -110,15 +125,17 @@ work when the daemon is Podman (see
110125
111126## Individual tools
112127
113- The four scripts below can also be run individually for debugging or partial
114- updates. ` prefetch-all.sh ` calls them internally.
128+ The five options below can be used for hermetic builds. Scripts 1–4 can also be
129+ run individually for debugging or partial updates; ` prefetch-all.sh ` calls them
130+ internally. Option 5 (Git submodule) is a manual setup.
115131
116132| # | Type | Main script | What it generates |
117133| ---| ------| -------------| -------------------|
118134| 1 | Generic | [ create-artifact-lockfile.py] ( #1-generic-artifacts--create-artifact-lockfilepy ) | ` artifacts.lock.yaml ` |
119135| 2 | RPM | [ create-rpm-lockfile.sh] ( #2-rpm-packages--create-rpm-lockfilesh ) | ` rpms.lock.yaml ` |
120136| 3 | npm | [ download-npm.sh] ( #3-npm-packages--download-npmsh ) | Downloaded tarballs in ` cachi2/output/deps/npm/ ` |
121137| 4 | pip (RHOAI) | [ create-requirements-lockfile.sh] ( #4-pip-packages-rhoai--create-requirements-lockfilesh ) | ` pylock.<flavor>.toml ` + ` requirements.<flavor>.txt ` |
138+ | 5 | Git submodule | (manual setup) | [ Pinned repo under prefetch-input/] ( #5-git-submodule ) |
122139
123140### Helper scripts (used internally by the main tools)
124141
@@ -516,7 +533,8 @@ collisions. Files that already exist are skipped.
516533- ` --lock-file <path>` — process a single `package-lock.json`.
517534- ` --tekton-file <path>` — parse a Tekton PipelineRun YAML to discover all
518535 ` npm` -type `prefetch-input` paths, then process every `package-lock.json`
519- found under them.
536+ found under them. If the file has **no** `npm`-type entries, the script
537+ exits 0 (nothing to download) instead of erroring.
520538
521539Both flags can be combined. URLs that are already local (`file:///cachi2/...`)
522540are automatically skipped.
@@ -707,39 +725,89 @@ python3 scripts/lockfile-generators/helpers/download-pip-packages.py \
707725
708726---
709727
728+ # # 5. Git submodule
729+
730+ The notebooks repository uses external code (e.g. code-server) that is normally
731+ cloned during the Docker build. For hermetic builds, Konflux can prefetch these
732+ dependencies via **git submodules** : the external repo is added as a submodule
733+ under `prefetch-input/` and pinned to a specific commit or tag. The build then
734+ uses the checked-out tree instead of running `git clone` at build time.
735+
736+ # ## Setup
737+
738+ Run from the **repository root**. Replace the submodule URL and
739+ ` <component>/prefetch-input/<name>` with your target path (e.g.
740+ ` codeserver/ubi9-python-3.12/prefetch-input/code-server` ).
741+
742+ ` ` ` bash
743+ # Add the external repo as a submodule under prefetch-input
744+ git submodule add https://github.com/coder/code-server.git \
745+ codeserver/ubi9-python-3.12/prefetch-input/code-server
746+
747+ # Pin to a specific tag (or commit)
748+ cd codeserver/ubi9-python-3.12/prefetch-input/code-server
749+ git fetch --tags
750+ git checkout tags/v4.104.0
751+ git submodule update --init --recursive # pull nested submodules if any
752+
753+ # Commit the submodule and .gitmodules
754+ cd ../..
755+ git add -A .
756+ git commit -m "Added submodule code-server"
757+ ` ` `
758+
759+ Use the same tag or commit that your Dockerfile or build scripts expect, so the
760+ hermetic build uses an identical source tree.
761+
762+ ---
763+
710764# # Appendix: Local podman build
711765
712766After running `prefetch-all.sh`, the **recommended** way to build is via make :
713767
714768` ` ` bash
715- # Makefile auto-detects cachi2/output/ and injects --volume + LOCAL_BUILD=true
769+ # Make sets LOCAL_BUILD=true for hermetic targets; mounts cachi2/output when it exists
716770gmake codeserver-ubi9-python-3.12 BUILD_ARCH=linux/arm64 PUSH_IMAGES=no
717771` ` `
718772
719- The Makefile evaluates each target independently : ` CACHI2_VOLUME ` is only set
720- when both `cachi2/output/` exists AND the target directory has a
721- ` prefetch-input/ ` subdirectory. Non-hermetic targets are completely unaffected.
773+ The Makefile sets `LOCAL_BUILD=true` for any target that has `prefetch-input/`;
774+ it adds the cachi2 volume only when `cachi2/output/` exists (after prefetch).
775+ Non-hermetic targets are unaffected.
722776
723777# ## Alternative: manual podman build
724778
725- For developers who want to run `podman build` directly, the key flags are :
779+ Running `podman build` directly differs from `gmake` in these ways :
780+
781+ | Aspect | `gmake codeserver-ubi9-python-3.12 BUILD_ARCH=... PUSH_IMAGES=no` | Manual `podman build ...` |
782+ |--------|-------------------------------------------------------------------|---------------------------|
783+ | **Build context** | Minimal (via `scripts/sandbox.py` : only files needed by the Dockerfile) | Full repo (`.`). |
784+ | **Volume** | `--volume $(ROOT_DIR)cachi2/output:/cachi2/output:Z` (mounts only `cachi2/output`) | Often `-v ./cachi2:/cachi2` (mounts whole dir); equivalent is `-v ./cachi2/output:/cachi2/output:z`. |
785+ | **Build args** | From `build-args/cpu.conf` : ` INDEX_URL` , `BASE_IMAGE`, `PYLOCK_FLAVOR` | You must pass these (and `LOCAL_BUILD=true`) explicitly. |
786+ | **Tag** | `$(IMAGE_REGISTRY):codeserver-ubi9-python-3.12-$(RELEASE)_$(DATE)` | Whatever you pass with `-t`. |
787+ | **Label** | `--label release=$(RELEASE)` | Omitted unless you add it. |
788+ | **Cache** | Default `CONTAINER_BUILD_CACHE_ARGS ?= --no-cache` | Podman uses its default cache unless you pass `--no-cache`. |
789+
790+ To approximate the make build when running podman manually, use the same volume
791+ path as make and pass all build-args from `build-args/cpu.conf` :
726792
727- - ` -v $(realpath ./cachi2):/cachi2:z` bind-mount the prefetched dependencies
728- so the Dockerfile can install from them offline.
729- - ` --build-arg LOCAL_BUILD=true` signals the Dockerfile that this is a local
730- build (configures dnf to use the local cachi2 RPM repo).
793+ - Bind-mount **only** `cachi2/output` at `/cachi2/output` (same as make).
794+ - Pass `LOCAL_BUILD=true` and the same `BASE_IMAGE`, `PYLOCK_FLAVOR`, and
795+ ` INDEX_URL` as in `codeserver/ubi9-python-3.12/build-args/cpu.conf`.
731796
732797` ` ` bash
798+ # Same volume path as Makefile; build-args from build-args/cpu.conf
733799podman build \
734800 -f codeserver/ubi9-python-3.12/Dockerfile.cpu \
735- --platform linux/amd64 \
801+ --platform linux/arm64 \
736802 -t code-server-test \
737803 --build-arg LOCAL_BUILD=true \
738804 --build-arg BASE_IMAGE=quay.io/opendatahub/odh-base-image-cpu-py312-c9s:latest \
739805 --build-arg PYLOCK_FLAVOR=cpu \
740- -v "$(realpath ./cachi2):/cachi2:z" \
806+ --build-arg INDEX_URL=https://console.redhat.com/api/pypi/public-rhai/rhoai/3.4-EA1/cpu-ubi9/simple/ \
807+ -v "$(realpath ./cachi2/output):/cachi2/output:z" \
741808 .
742809` ` `
743810
744- To build for a different architecture, change `--platform` and `ARCH`
745- accordingly (e.g. `linux/arm64` / `aarch64`, `linux/ppc64le` / `ppc64le`).
811+ To build for a different architecture, change `--platform` (e.g. `linux/amd64`,
812+ ` linux/arm64` , `linux/ppc64le`). The manual command uses the **full repo** as
813+ context; make uses a **sandboxed** context for reproducibility.
0 commit comments