generic-packs: support oci:// references via install-oci-pack (#4164)

This commit is contained in:
Chip Wolf ‮
2026-07-16 17:29:05 -05:00
committed by GitHub
parent 3302bdb3cb
commit 591f37bfa5
7 changed files with 84 additions and 1 deletions
+20
View File
@@ -151,6 +151,26 @@ GENERIC_PACKS_SUFFIX=.zip
would expand to `https://cdn.example.org/configs-v9.0.1.zip,https://cdn.example.org/mods-v4.3.6.zip`. would expand to `https://cdn.example.org/configs-v9.0.1.zip,https://cdn.example.org/mods-v4.3.6.zip`.
### Generic packs from an OCI registry
An entry can also reference a pack stored as an [OCI](https://opencontainers.org/) artifact in a container registry by prefixing it with `oci://`. The reference may use a tag or an immutable `@sha256:` digest:
```
GENERIC_PACKS=oci://ghcr.io/itzg/oci-modpack-template/tech:latest,oci://ghcr.io/itzg/oci-modpack-template/magic:latest
```
OCI, URL, and local-path entries can be mixed in the same list and are applied in the order given. Each artifact's layers are pulled into a content-addressed cache under `/data/packs/oci`, so a digest already on disk (for example a base layer shared between packs) is not downloaded again. `GENERIC_PACKS_PREFIX`/`GENERIC_PACKS_SUFFIX`, `GENERIC_PACKS_DISABLE_MODS`, and the update/checksum behaviour below all apply to OCI entries exactly as they do to URLs.
The artifact must be a Minecraft modpack artifact (artifact type `application/vnd.itzg.minecraft.modpack.v1+json` with `…modpack.layer.v1.tar+gzip` layers); any other reference is rejected before its contents touch `/data`. Validation and registry authentication are handled by `mc-image-helper`.
For a private registry, point `GENERIC_PACKS_OCI_AUTH_FILE` at a registry login file (the `auth.json`/`config.json` produced by `docker login`, `podman login`, etc.):
```
GENERIC_PACKS_OCI_AUTH_FILE=/run/secrets/registry-auth.json
```
When it is unset, a credentials file at `~/.config/containers/auth.json` or `~/.docker/config.json` is used if present, otherwise the pull is anonymous.
If applying large generic packs, the update can be time-consuming. To skip the update set `SKIP_GENERIC_PACK_UPDATE_CHECK` to "true". Conversely, the generic pack(s) can be forced to be applied by setting `FORCE_GENERIC_PACK_UPDATE` to "true". If applying large generic packs, the update can be time-consuming. To skip the update set `SKIP_GENERIC_PACK_UPDATE_CHECK` to "true". Conversely, the generic pack(s) can be forced to be applied by setting `FORCE_GENERIC_PACK_UPDATE` to "true".
The most time-consuming portion of the generic pack update is generating and comparing the SHA1 checksum. To skip the checksum generation, set `SKIP_GENERIC_PACK_CHECKSUM` to "true". The most time-consuming portion of the generic pack update is generating and comparing the SHA1 checksum. To skip the checksum generation, set `SKIP_GENERIC_PACK_CHECKSUM` to "true".
+32 -1
View File
@@ -181,7 +181,38 @@ function handleGenericPacks() {
packFiles=() packFiles=()
for packEntry in "${packs[@]}"; do for packEntry in "${packs[@]}"; do
pack="${GENERIC_PACKS_PREFIX}${packEntry}${GENERIC_PACKS_SUFFIX}" pack="${GENERIC_PACKS_PREFIX}${packEntry}${GENERIC_PACKS_SUFFIX}"
if isURL "${pack}"; then if isOCIRef "${pack}"; then
# All OCI refs share one staging dir so layers common to multiple
# sibling artifacts (the point of layered distribution) are pulled
# exactly once across all of GENERIC_PACKS.
mkdir -p /data/packs/oci /data/.tmp
log "Pulling OCI generic pack from $pack"
# A list file is used rather than stdout because mc-image-helper INFO
# logs share that stream; it holds one absolute layer path per line in
# apply order.
layerListFile=$(mktemp -p /data/.tmp)
ociArgs=(install-oci-pack
--ref "$pack"
--output-directory /data/packs/oci
--filename-strategy digest
--layer-list-file "$layerListFile")
if [[ "${GENERIC_PACKS_OCI_AUTH_FILE:-}" ]]; then
ociArgs+=(--auth-file "${GENERIC_PACKS_OCI_AUTH_FILE}")
fi
if ! mc-image-helper "${ociArgs[@]}"; then
logError "Failed to pull OCI generic pack $pack"
logError "(OCI generic packs require mc-image-helper with install-oci-pack support)"
rm -f "$layerListFile"
exit 2
fi
mapfile -t ociLayers < "$layerListFile"
rm -f "$layerListFile"
if [ ${#ociLayers[@]} -eq 0 ]; then
logError "OCI generic pack $pack resolved to zero layers"
exit 2
fi
packFiles+=("${ociLayers[@]}")
elif isURL "${pack}"; then
mkdir -p /data/packs mkdir -p /data/packs
log "Downloading generic pack from $pack" log "Downloading generic pack from $pack"
if ! outfile=$(get -o /data/packs --output-filename --skip-up-to-date "$pack"); then if ! outfile=$(get -o /data/packs --output-filename --skip-up-to-date "$pack"); then
+6
View File
@@ -173,6 +173,12 @@ function isURL() {
[[ $value =~ ^(https?|ftp):// ]] [[ $value =~ ^(https?|ftp):// ]]
} }
function isOCIRef() {
local value=$1
[[ $value =~ ^oci:// ]]
}
function isValidFileURL() { function isValidFileURL() {
suffix=${1:?Missing required suffix arg} suffix=${1:?Missing required suffix arg}
url=${2:?Missing required url arg} url=${2:?Missing required url arg}
@@ -0,0 +1,18 @@
services:
mc:
image: ${IMAGE_TO_TEST:-itzg/minecraft-server}
environment:
EULA: "true"
SETUP_ONLY: "true"
# two OCI packs that share a common base layer; the shared blob is
# pulled into /data/packs/oci once and reused for the second pack
GENERIC_PACKS: oci://ghcr.io/itzg/oci-modpack-template/tech:latest,oci://ghcr.io/itzg/oci-modpack-template/magic:latest
LOG_TIMESTAMP: "true"
# the following are only used to speed up test execution
TYPE: CUSTOM
CUSTOM_SERVER: /servers/fake.jar
VERSION: 1.18.1
DEBUG: "true"
volumes:
- ./data:/data
- ./fake.jar:/servers/fake.jar
@@ -0,0 +1 @@
[[ $EXTENDED_TESTS ]] || exit 1
@@ -0,0 +1,7 @@
set -e
# OCI layers were cached
[ -d /data/packs/oci ]
compgen -G "/data/packs/oci/sha256:*" > /dev/null
# pack content was extracted and applied to /data
[ -d /data/mods ]
compgen -G "/data/mods/*" > /dev/null