mirror of
https://github.com/n8n-io/n8n.git
synced 2026-09-19 01:45:48 +08:00
feat: Publish a pointer-compressed n8n image variant (#36720)
This commit is contained in:
@@ -27,6 +27,7 @@ const ALLOW_REFS = [
|
||||
export function parseTargets(env) {
|
||||
return [
|
||||
{ label: 'n8n', image: env.N8N_IMAGE, digest: env.N8N_DIGEST },
|
||||
{ label: 'n8n-pc', image: env.N8N_PC_IMAGE, digest: env.N8N_PC_DIGEST },
|
||||
{ label: 'runners', image: env.RUNNERS_IMAGE, digest: env.RUNNERS_DIGEST },
|
||||
{ label: 'runners-distroless', image: env.DISTROLESS_IMAGE, digest: env.DISTROLESS_DIGEST },
|
||||
].filter((t) => t.image && t.digest);
|
||||
|
||||
@@ -18,6 +18,11 @@ class TagGenerator {
|
||||
versionSuffix = '-distroless';
|
||||
}
|
||||
|
||||
if (image === 'n8n-pc') {
|
||||
imageName = 'n8n';
|
||||
versionSuffix = '-pc';
|
||||
}
|
||||
|
||||
const platformSuffix = platform ? `-${platform.split('/').pop()}` : '';
|
||||
const fullVersion = `${version}${versionSuffix}${platformSuffix}`;
|
||||
|
||||
@@ -82,12 +87,12 @@ class TagGenerator {
|
||||
}
|
||||
|
||||
generateAll({ version, platform, includeDockerHub = false, sha = '', date = '' }) {
|
||||
const images = ['n8n', 'runners', 'runners-distroless'];
|
||||
const images = ['n8n', 'n8n-pc', 'runners', 'runners-distroless'];
|
||||
const results = {};
|
||||
|
||||
for (const image of images) {
|
||||
const tags = this.generate({ image, version, platform, includeDockerHub, sha, date });
|
||||
const prefix = image.replace('-distroless', '_distroless');
|
||||
const prefix = image.replaceAll('-', '_');
|
||||
results[prefix] = tags;
|
||||
|
||||
if (this.githubOutput) {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
*
|
||||
* Environment variables:
|
||||
* N8N_TAG - Full image reference for n8n image
|
||||
* N8N_PC_TAG - Full image reference for the pointer-compressed n8n image
|
||||
* RUNNERS_TAG - Full image reference for runners image
|
||||
* DISTROLESS_TAG - Full image reference for runners-distroless image
|
||||
* GITHUB_OUTPUT - Path to GitHub Actions output file (optional)
|
||||
@@ -36,17 +37,21 @@ function setOutput(name, value) {
|
||||
}
|
||||
|
||||
const n8nTag = process.env.N8N_TAG || '';
|
||||
const n8nPcTag = process.env.N8N_PC_TAG || '';
|
||||
const runnersTag = process.env.RUNNERS_TAG || '';
|
||||
const distrolessTag = process.env.DISTROLESS_TAG || '';
|
||||
|
||||
const results = {
|
||||
n8n: { digest: getDigest(n8nTag), image: getImageName(n8nTag) },
|
||||
n8n_pc: { digest: getDigest(n8nPcTag), image: getImageName(n8nPcTag) },
|
||||
runners: { digest: getDigest(runnersTag), image: getImageName(runnersTag) },
|
||||
runners_distroless: { digest: getDigest(distrolessTag), image: getImageName(distrolessTag) },
|
||||
};
|
||||
|
||||
setOutput('n8n_digest', results.n8n.digest);
|
||||
setOutput('n8n_image', results.n8n.image);
|
||||
setOutput('n8n_pc_digest', results.n8n_pc.digest);
|
||||
setOutput('n8n_pc_image', results.n8n_pc.image);
|
||||
setOutput('runners_digest', results.runners.digest);
|
||||
setOutput('runners_image', results.runners.image);
|
||||
setOutput('runners_distroless_digest', results.runners_distroless.digest);
|
||||
@@ -54,10 +59,12 @@ setOutput('runners_distroless_image', results.runners_distroless.image);
|
||||
|
||||
console.log('=== Manifest Digests ===');
|
||||
console.log(`n8n: ${results.n8n.digest || 'N/A'}`);
|
||||
console.log(`n8n-pc: ${results.n8n_pc.digest || 'N/A'}`);
|
||||
console.log(`runners: ${results.runners.digest || 'N/A'}`);
|
||||
console.log(`runners-distroless: ${results.runners_distroless.digest || 'N/A'}`);
|
||||
console.log('');
|
||||
console.log('=== Image Names ===');
|
||||
console.log(`n8n: ${results.n8n.image || 'N/A'}`);
|
||||
console.log(`n8n-pc: ${results.n8n_pc.image || 'N/A'}`);
|
||||
console.log(`runners: ${results.runners.image || 'N/A'}`);
|
||||
console.log(`runners-distroless: ${results.runners_distroless.image || 'N/A'}`);
|
||||
|
||||
@@ -131,6 +131,9 @@ jobs:
|
||||
outputs:
|
||||
image_ref: ${{ steps.determine-tags.outputs.n8n_primary_tag }}
|
||||
primary_ghcr_manifest_tag: ${{ steps.determine-tags.outputs.n8n_primary_tag }}
|
||||
n8n_pc_primary_ghcr_manifest_tag: ${{ steps.determine-tags.outputs.n8n_pc_primary_tag }}
|
||||
n8n_pc_sha_manifest_tag: ${{ steps.determine-tags.outputs.n8n_pc_sha_primary_tag }}
|
||||
n8n_pc_date_manifest_tag: ${{ steps.determine-tags.outputs.n8n_pc_date_primary_tag }}
|
||||
runners_primary_ghcr_manifest_tag: ${{ steps.determine-tags.outputs.runners_primary_tag }}
|
||||
runners_distroless_primary_ghcr_manifest_tag: ${{ steps.determine-tags.outputs.runners_distroless_primary_tag }}
|
||||
n8n_sha_manifest_tag: ${{ steps.determine-tags.outputs.n8n_sha_primary_tag }}
|
||||
@@ -206,6 +209,26 @@ jobs:
|
||||
push: ${{ needs.determine-build-context.outputs.push_enabled == 'true' }}
|
||||
tags: ${{ steps.determine-tags.outputs.n8n_tags }}
|
||||
|
||||
- name: Build and push n8n -pc Docker image
|
||||
id: build-n8n-pc
|
||||
if: needs.determine-build-context.outputs.push_enabled == 'true'
|
||||
uses: useblacksmith/build-push-action@30c71162f16ea2c27c3e21523255d209b8b538c1 # v2
|
||||
with:
|
||||
context: .
|
||||
file: ./docker/images/n8n/Dockerfile
|
||||
build-args: |
|
||||
NODE_VERSION=${{ env.NODE_VERSION }}
|
||||
N8N_VERSION=${{ needs.determine-build-context.outputs.n8n_version }}
|
||||
N8N_RELEASE_TYPE=${{ needs.determine-build-context.outputs.release_type }}
|
||||
BUILDER_IMAGE=n8nio/node-pc:26.5.1-dev@sha256:9ecfe9d5c7ea835bccc604d06c3cb76a837b64496d4a999338cb8b53a50bb0bd
|
||||
RUNTIME_IMAGE=n8nio/node-pc:26.5.1@sha256:e535408e7815904c98f81fb0203f7f657047d7db3d4edc77de44d2ea72c6bf18
|
||||
IMAGE_DESCRIPTION=Workflow Automation Tool (pointer-compressed variant, internal to n8n Cloud, no support or stability guarantees)
|
||||
platforms: ${{ matrix.docker_platform }}
|
||||
provenance: false # Disabled - using SLSA L3 generator for isolated provenance
|
||||
sbom: true
|
||||
push: ${{ needs.determine-build-context.outputs.push_enabled == 'true' }}
|
||||
tags: ${{ steps.determine-tags.outputs.n8n_pc_tags }}
|
||||
|
||||
- name: Kafka native binding smoke check
|
||||
# `load: true` isn't an option here: with sbom:true, buildx produces a manifest
|
||||
# list even for a single platform, and the docker exporter (what --load uses)
|
||||
@@ -264,6 +287,8 @@ jobs:
|
||||
outputs:
|
||||
n8n_digest: ${{ steps.get-digests.outputs.n8n_digest }}
|
||||
n8n_image: ${{ steps.get-digests.outputs.n8n_image }}
|
||||
n8n_pc_digest: ${{ steps.get-digests.outputs.n8n_pc_digest }}
|
||||
n8n_pc_image: ${{ steps.get-digests.outputs.n8n_pc_image }}
|
||||
runners_digest: ${{ steps.get-digests.outputs.runners_digest }}
|
||||
runners_image: ${{ steps.get-digests.outputs.runners_image }}
|
||||
runners_distroless_digest: ${{ steps.get-digests.outputs.runners_distroless_digest }}
|
||||
@@ -286,6 +311,7 @@ jobs:
|
||||
- name: Create GHCR multi-arch manifests
|
||||
env:
|
||||
N8N_DATE_MANIFEST_TAG: ${{ needs.build-and-push-docker.outputs.n8n_date_manifest_tag }}
|
||||
N8N_PC_DATE_MANIFEST_TAG: ${{ needs.build-and-push-docker.outputs.n8n_pc_date_manifest_tag }}
|
||||
RUNNERS_DATE_MANIFEST_TAG: ${{ needs.build-and-push-docker.outputs.runners_date_manifest_tag }}
|
||||
RUNNERS_DISTROLESS_DATE_MANIFEST_TAG: ${{ needs.build-and-push-docker.outputs.runners_distroless_date_manifest_tag }}
|
||||
run: |
|
||||
@@ -318,11 +344,13 @@ jobs:
|
||||
|
||||
# Create manifests for all images
|
||||
create_manifest "n8n" "${{ needs.build-and-push-docker.outputs.primary_ghcr_manifest_tag }}"
|
||||
create_manifest "n8n-pc" "${{ needs.build-and-push-docker.outputs.n8n_pc_primary_ghcr_manifest_tag }}"
|
||||
create_manifest "runners" "${{ needs.build-and-push-docker.outputs.runners_primary_ghcr_manifest_tag }}"
|
||||
create_manifest "runners-distroless" "${{ needs.build-and-push-docker.outputs.runners_distroless_primary_ghcr_manifest_tag }}"
|
||||
|
||||
# Create SHA-tagged manifests (immutable references for deployments)
|
||||
create_manifest "n8n (sha)" "${{ needs.build-and-push-docker.outputs.n8n_sha_manifest_tag }}"
|
||||
create_manifest "n8n-pc (sha)" "${{ needs.build-and-push-docker.outputs.n8n_pc_sha_manifest_tag }}"
|
||||
create_manifest "runners (sha)" "${{ needs.build-and-push-docker.outputs.runners_sha_manifest_tag }}"
|
||||
create_manifest "runners-distroless (sha)" "${{ needs.build-and-push-docker.outputs.runners_distroless_sha_manifest_tag }}"
|
||||
|
||||
@@ -330,6 +358,7 @@ jobs:
|
||||
# date_tag input was set (e.g. nightly), and create_manifest skips empty tags —
|
||||
# so these calls are no-ops on non-dated builds.
|
||||
create_manifest "n8n (date)" "$N8N_DATE_MANIFEST_TAG"
|
||||
create_manifest "n8n-pc (date)" "$N8N_PC_DATE_MANIFEST_TAG"
|
||||
create_manifest "runners (date)" "$RUNNERS_DATE_MANIFEST_TAG"
|
||||
create_manifest "runners-distroless (date)" "$RUNNERS_DISTROLESS_DATE_MANIFEST_TAG"
|
||||
|
||||
@@ -346,13 +375,15 @@ jobs:
|
||||
# Create manifests for each image type
|
||||
declare -A images=(
|
||||
["n8n"]="${VERSION}"
|
||||
["n8n-pc"]="${VERSION}-pc"
|
||||
["runners"]="${VERSION}"
|
||||
["runners-distroless"]="${VERSION}-distroless"
|
||||
)
|
||||
|
||||
for image in "${!images[@]}"; do
|
||||
TAG_SUFFIX="${images[$image]}"
|
||||
IMAGE_NAME="${image//-distroless/}" # Remove -distroless from image name
|
||||
IMAGE_NAME="${image//-distroless/}" # Remove the variant suffix from the image name
|
||||
IMAGE_NAME="${IMAGE_NAME//-pc/}"
|
||||
|
||||
echo "Creating Docker Hub manifest for $image"
|
||||
docker buildx imagetools create \
|
||||
@@ -365,6 +396,8 @@ jobs:
|
||||
# to match docker-tags.mjs format: nightly-abc1234-distroless (not nightly-distroless-abc1234)
|
||||
if [[ "$image" == *"-distroless"* ]]; then
|
||||
SHA_SUFFIX="${VERSION}-${SHORT_SHA}-distroless"
|
||||
elif [[ "$image" == *"-pc"* ]]; then
|
||||
SHA_SUFFIX="${VERSION}-${SHORT_SHA}-pc"
|
||||
else
|
||||
SHA_SUFFIX="${TAG_SUFFIX}-${SHORT_SHA}"
|
||||
fi
|
||||
@@ -379,6 +412,8 @@ jobs:
|
||||
if [[ -n "$DATE_TAG" ]]; then
|
||||
if [[ "$image" == *"-distroless"* ]]; then
|
||||
DATE_SUFFIX="${VERSION}-${DATE_TAG}-distroless"
|
||||
elif [[ "$image" == *"-pc"* ]]; then
|
||||
DATE_SUFFIX="${VERSION}-${DATE_TAG}-pc"
|
||||
else
|
||||
DATE_SUFFIX="${TAG_SUFFIX}-${DATE_TAG}"
|
||||
fi
|
||||
@@ -394,6 +429,7 @@ jobs:
|
||||
id: get-digests
|
||||
env:
|
||||
N8N_TAG: ${{ needs.build-and-push-docker.outputs.primary_ghcr_manifest_tag }}
|
||||
N8N_PC_TAG: ${{ needs.build-and-push-docker.outputs.n8n_pc_primary_ghcr_manifest_tag }}
|
||||
RUNNERS_TAG: ${{ needs.build-and-push-docker.outputs.runners_primary_ghcr_manifest_tag }}
|
||||
DISTROLESS_TAG: ${{ needs.build-and-push-docker.outputs.runners_distroless_primary_ghcr_manifest_tag }}
|
||||
run: node .github/scripts/docker/get-manifest-digests.mjs
|
||||
@@ -432,6 +468,25 @@ jobs:
|
||||
secrets:
|
||||
registry-password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
provenance-n8n-pc:
|
||||
name: SLSA Provenance (n8n-pc)
|
||||
needs: [determine-build-context, build-and-push-docker, create_multi_arch_manifest]
|
||||
if: |
|
||||
needs.create_multi_arch_manifest.result == 'success' &&
|
||||
needs.create_multi_arch_manifest.outputs.n8n_pc_digest != ''
|
||||
permissions:
|
||||
id-token: write
|
||||
packages: write
|
||||
actions: read
|
||||
# SLSA L3 Provenance - Must use version tags (@vX.Y.Z), NOT SHAs
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.1.0
|
||||
with:
|
||||
image: ${{ needs.create_multi_arch_manifest.outputs.n8n_pc_image }}
|
||||
digest: ${{ needs.create_multi_arch_manifest.outputs.n8n_pc_digest }}
|
||||
registry-username: ${{ github.actor }}
|
||||
secrets:
|
||||
registry-password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
provenance-runners:
|
||||
name: SLSA Provenance (runners)
|
||||
needs: [determine-build-context, build-and-push-docker, create_multi_arch_manifest]
|
||||
@@ -479,6 +534,7 @@ jobs:
|
||||
build-and-push-docker,
|
||||
create_multi_arch_manifest,
|
||||
provenance-n8n,
|
||||
provenance-n8n-pc,
|
||||
provenance-runners,
|
||||
provenance-runners-distroless,
|
||||
]
|
||||
@@ -514,6 +570,14 @@ jobs:
|
||||
--predicate security/vex.openvex.json \
|
||||
${{ needs.create_multi_arch_manifest.outputs.n8n_image }}@${{ needs.create_multi_arch_manifest.outputs.n8n_digest }}
|
||||
|
||||
- name: Attest VEX to n8n-pc image
|
||||
if: needs.create_multi_arch_manifest.outputs.n8n_pc_digest != ''
|
||||
run: |
|
||||
cosign attest --yes \
|
||||
--type openvex \
|
||||
--predicate security/vex.openvex.json \
|
||||
${{ needs.create_multi_arch_manifest.outputs.n8n_pc_image }}@${{ needs.create_multi_arch_manifest.outputs.n8n_pc_digest }}
|
||||
|
||||
- name: Attest VEX to runners image
|
||||
if: needs.create_multi_arch_manifest.outputs.runners_digest != ''
|
||||
run: |
|
||||
@@ -544,6 +608,7 @@ jobs:
|
||||
build-and-push-docker,
|
||||
create_multi_arch_manifest,
|
||||
provenance-n8n,
|
||||
provenance-n8n-pc,
|
||||
provenance-runners,
|
||||
provenance-runners-distroless,
|
||||
]
|
||||
@@ -583,6 +648,8 @@ jobs:
|
||||
env:
|
||||
N8N_IMAGE: ${{ needs.create_multi_arch_manifest.outputs.n8n_image }}
|
||||
N8N_DIGEST: ${{ needs.create_multi_arch_manifest.outputs.n8n_digest }}
|
||||
N8N_PC_IMAGE: ${{ needs.create_multi_arch_manifest.outputs.n8n_pc_image }}
|
||||
N8N_PC_DIGEST: ${{ needs.create_multi_arch_manifest.outputs.n8n_pc_digest }}
|
||||
RUNNERS_IMAGE: ${{ needs.create_multi_arch_manifest.outputs.runners_image }}
|
||||
RUNNERS_DIGEST: ${{ needs.create_multi_arch_manifest.outputs.runners_digest }}
|
||||
DISTROLESS_IMAGE: ${{ needs.create_multi_arch_manifest.outputs.runners_distroless_image }}
|
||||
@@ -602,6 +669,19 @@ jobs:
|
||||
image_ref: ${{ needs.build-and-push-docker.outputs.image_ref }}
|
||||
secrets: inherit
|
||||
|
||||
security-scan-pc:
|
||||
name: Security Scan (n8n-pc)
|
||||
needs: [determine-build-context, build-and-push-docker, create_multi_arch_manifest]
|
||||
if: |
|
||||
success() &&
|
||||
(needs.determine-build-context.outputs.release_type == 'stable' ||
|
||||
needs.determine-build-context.outputs.release_type == 'nightly' ||
|
||||
needs.determine-build-context.outputs.release_type == 'rc')
|
||||
uses: ./.github/workflows/security-trivy-scan-callable.yml
|
||||
with:
|
||||
image_ref: ${{ needs.build-and-push-docker.outputs.n8n_pc_primary_ghcr_manifest_tag }}
|
||||
secrets: inherit
|
||||
|
||||
security-scan-runners:
|
||||
name: Security Scan (runners)
|
||||
needs: [determine-build-context, build-and-push-docker, create_multi_arch_manifest]
|
||||
|
||||
Reference in New Issue
Block a user