Use OCI tools for container publishing

This commit is contained in:
adminroot committed 2026-08-25 12:15:03 +00:00
1 parent 046d5fe11b
commit 00a3a9dd82
2 files changed
+119 -67

No files matched your search

+108 -51
View File
@@ -5,7 +5,8 @@ set -euo pipefail
: "${RELEASE_TAG:?RELEASE_TAG is required}"
: "${IMAGE_REPOSITORY:?IMAGE_REPOSITORY is required}"
CRANE_BIN="${CRANE_BIN:-crane}"
SKOPEO_BIN="${SKOPEO_BIN:-skopeo}"
UMOCI_BIN="${UMOCI_BIN:-umoci}"
ALPINE_IMAGE="${ALPINE_IMAGE:-docker.io/library/alpine:latest}"
DIST_DIR="${DIST_DIR:-dist}"
SOURCE_URL="${SOURCE_URL:-}"
@@ -33,23 +34,14 @@ cleanup() {
}
trap cleanup EXIT
crane_args=()
if [ "${CRANE_INSECURE:-0}" = 1 ]; then
crane_args+=(--insecure)
fi
crane() {
"$CRANE_BIN" "${crane_args[@]}" "$@"
}
created="$(date --utc --date="@$SOURCE_DATE_EPOCH" '+%Y-%m-%dT%H:%M:%SZ')"
platform_digest=''
build_platform() {
local arch="$1"
local binary="$2"
local root="$work_dir/root-$arch"
local layer="$work_dir/pixhelf-$arch.tar"
local platform_ref="$IMAGE_REPOSITORY:$version-$arch"
local layout="$work_dir/oci-$arch"
test -x "$binary"
install -d -m 0755 "$root/usr/local/bin" "$root/gallery"
@@ -66,58 +58,125 @@ build_platform() {
-cf "$layer" \
cache gallery usr
crane append \
--platform "linux/$arch" \
--base "$ALPINE_IMAGE" \
--new_layer "$layer" \
--new_tag "$platform_ref" \
--set-base-image-annotations
"$SKOPEO_BIN" copy \
--override-os linux \
--override-arch "$arch" \
"docker://$ALPINE_IMAGE" \
"oci:$layout:base"
"$UMOCI_BIN" raw add-layer \
--image "$layout:base" \
--history.created "$created" \
--history.created_by "pixhelf release $RELEASE_TAG" \
"$layer"
"$UMOCI_BIN" config \
--image "$layout:base" \
--created "$created" \
--os linux \
--architecture "$arch" \
--config.user=65532:65532 \
--config.entrypoint=/usr/local/bin/pixhelf \
--config.cmd=--gallery-dir \
--config.cmd=/gallery \
--config.cmd=--cache-dir \
--config.cmd=/cache \
--config.cmd=--listen \
--config.cmd=0.0.0.0:3002 \
--config.workingdir=/ \
--config.exposedports=3002/tcp \
--config.stopsignal=SIGTERM \
--config.label=org.opencontainers.image.title=Pixhelf \
--config.label="org.opencontainers.image.description=Self-hosted image gallery" \
--config.label="org.opencontainers.image.version=$version" \
--config.label="org.opencontainers.image.revision=$REVISION" \
--config.label="org.opencontainers.image.created=$created" \
--config.label="org.opencontainers.image.source=$SOURCE_URL"
crane mutate "$platform_ref" \
--platform "linux/$arch" \
--set-platform "linux/$arch" \
--entrypoint=/usr/local/bin/pixhelf \
--cmd=--gallery-dir \
--cmd=/gallery \
--cmd=--cache-dir \
--cmd=/cache \
--cmd=--listen \
--cmd=0.0.0.0:3002 \
--user=65532:65532 \
--workdir=/ \
--exposed-ports=3002/tcp \
--label=org.opencontainers.image.title=Pixhelf \
--label="org.opencontainers.image.description=Self-hosted image gallery" \
--label="org.opencontainers.image.version=$version" \
--label="org.opencontainers.image.revision=$REVISION" \
--label="org.opencontainers.image.created=$created" \
--label="org.opencontainers.image.source=$SOURCE_URL" \
--tag "$platform_ref"
platform_digest="$(crane digest "$platform_ref")"
echo "Published linux/$arch as $platform_ref@$platform_digest"
echo "Built linux/$arch OCI image from $ALPINE_IMAGE"
}
build_platform amd64 "$DIST_DIR/pixhelf-amd64-linux"
amd64_digest="$platform_digest"
build_platform arm64 "$DIST_DIR/pixhelf-arm64-linux"
arm64_digest="$platform_digest"
combined_layout="$work_dir/oci-multi"
install -d -m 0755 "$combined_layout/blobs/sha256"
cp -a "$work_dir/oci-amd64/blobs/sha256/." "$combined_layout/blobs/sha256/"
cp -a "$work_dir/oci-arm64/blobs/sha256/." "$combined_layout/blobs/sha256/"
AMD64_LAYOUT="$work_dir/oci-amd64" \
ARM64_LAYOUT="$work_dir/oci-arm64" \
COMBINED_LAYOUT="$combined_layout" \
IMAGE_TAG="$version" \
node <<'NODE'
const crypto = require("node:crypto");
const fs = require("node:fs");
const path = require("node:path");
function platformDescriptor(layout, architecture) {
const index = JSON.parse(fs.readFileSync(path.join(layout, "index.json"), "utf8"));
const descriptor = index.manifests.find(
(candidate) => candidate.annotations?.["org.opencontainers.image.ref.name"] === "base",
);
if (!descriptor) throw new Error(`Cannot find image manifest in ${layout}`);
const result = structuredClone(descriptor);
delete result.annotations;
result.platform = { architecture, os: "linux" };
return result;
}
const manifests = [
platformDescriptor(process.env.AMD64_LAYOUT, "amd64"),
platformDescriptor(process.env.ARM64_LAYOUT, "arm64"),
];
const imageIndex = Buffer.from(JSON.stringify({
schemaVersion: 2,
mediaType: "application/vnd.oci.image.index.v1+json",
manifests,
}));
const digest = crypto.createHash("sha256").update(imageIndex).digest("hex");
const combined = process.env.COMBINED_LAYOUT;
fs.writeFileSync(path.join(combined, "blobs", "sha256", digest), imageIndex);
fs.writeFileSync(path.join(combined, "oci-layout"), JSON.stringify({ imageLayoutVersion: "1.0.0" }));
fs.writeFileSync(path.join(combined, "index.json"), JSON.stringify({
schemaVersion: 2,
mediaType: "application/vnd.oci.image.index.v1+json",
manifests: [{
mediaType: "application/vnd.oci.image.index.v1+json",
digest: `sha256:${digest}`,
size: imageIndex.length,
annotations: { "org.opencontainers.image.ref.name": process.env.IMAGE_TAG },
}],
}));
NODE
destination_args=()
inspect_args=()
if [ "${REGISTRY_INSECURE:-0}" = 1 ]; then
destination_args+=(--dest-tls-verify=false)
inspect_args+=(--tls-verify=false)
fi
publish_tag() {
local tag="$1"
"$SKOPEO_BIN" copy \
--all \
"${destination_args[@]}" \
"oci:$combined_layout:$version" \
"docker://$IMAGE_REPOSITORY:$tag"
echo "Published $IMAGE_REPOSITORY:$tag"
}
canonical_ref="$IMAGE_REPOSITORY:$version"
crane index append \
--manifest "$IMAGE_REPOSITORY@$amd64_digest" \
--manifest "$IMAGE_REPOSITORY@$arm64_digest" \
--tag "$canonical_ref"
publish_tag "$version"
if [ "$RELEASE_TAG" != "$version" ]; then
crane tag "$canonical_ref" "$RELEASE_TAG"
publish_tag "$RELEASE_TAG"
fi
case "$version" in
*-*) echo "Prerelease detected; latest tag was not updated" ;;
*) crane tag "$canonical_ref" latest ;;
*) publish_tag latest ;;
esac
crane manifest "$canonical_ref" | node -e '
"$SKOPEO_BIN" inspect "${inspect_args[@]}" --raw "docker://$canonical_ref" | node -e '
let input = "";
process.stdin.on("data", (chunk) => input += chunk);
process.stdin.on("end", () => {
@@ -133,5 +192,3 @@ process.stdin.on("end", () => {
console.log(`Verified multi-platform image: ${[...platforms].sort().join(", ")}`);
});
'
echo "Published $canonical_ref"
+11 -16
View File
@@ -198,22 +198,14 @@ jobs:
printf '\n'
done
- name: Install container publishing tool
- name: Install container publishing tools
if: startsWith(forgejo.ref, 'refs/tags/v')
run: |
set -eu
crane_version=0.21.9
crane_archive="go-containerregistry_Linux_x86_64.tar.gz"
crane_sha256=5c16d8ddb971cb1d5e6ed8b1e743da8224414eeba2c2762d8f1a61b2f095699e
crane_url="https://github.com/google/go-containerregistry/releases/download/v${crane_version}/${crane_archive}"
curl --location --silent --show-error --fail \
--output "/tmp/${crane_archive}" \
"$crane_url"
printf '%s %s\n' "$crane_sha256" "/tmp/${crane_archive}" \
| sha256sum --check
tar -xzf "/tmp/${crane_archive}" -C /usr/local/bin crane
crane version
apt-get update
apt-get install --yes --no-install-recommends skopeo umoci
skopeo --version
umoci --version
- name: Publish multi-platform Alpine container
if: startsWith(forgejo.ref, 'refs/tags/v')
@@ -229,10 +221,13 @@ jobs:
image_repository="${registry}/${image_path}"
test -n "$token"
export DOCKER_CONFIG=/tmp/pixhelf-container-auth
install -d -m 0700 "$DOCKER_CONFIG"
export REGISTRY_AUTH_FILE=/tmp/pixhelf-container-auth.json
printf '%s' "$token" \
| crane auth login "$registry" --username "$actor" --password-stdin
| skopeo login \
--authfile "$REGISTRY_AUTH_FILE" \
--username "$actor" \
--password-stdin \
"$registry"
export RELEASE_TAG
export IMAGE_REPOSITORY="$image_repository"