aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'tests/publish-integration')
-rwxr-xr-xtests/publish-integration185+185 −0
1 files changed, 185 insertions, 0 deletions
diff --git a/tests/publish-integration b/tests/publish-integration
new file mode 100755
--- /dev/null
+++ b/tests/publish-integration
@@ -0,0 +1,185 @@
+#!/bin/sh
+# SPDX-FileCopyrightText: 2026 Nikolay Govorov
+# SPDX-License-Identifier: 0BSD
+
+set -eu
+
+root=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)
+work=$(mktemp -d)
+trap 'rm -rf "$work"' EXIT HUP INT TERM
+remote="$work/remote/integration"
+mkdir -p "$work/bin" "$work/input" "$remote" "$work/fixture"
+
+printf 'publish integration fixture\n' > "$work/fixture/publish-contract"
+cat > "$work/fixture/nfpm.yaml" <<EOF
+name: publish-contract
+arch: \${ARCH}
+version: \${VERSION}
+platform: linux
+maintainer: Dimidium Labs <me@govorov.online>
+description: Shared publish task integration fixture
+license: 0BSD
+contents:
+ - src: $work/fixture/publish-contract
+ dst: /usr/local/bin/publish-contract
+deb:
+ signature:
+ method: debsign
+ key_id: \${GPG_KEY_ID}
+ key_file: \${SIGNING_PRIVATE_KEY}
+rpm:
+ signature:
+ key_id: \${GPG_KEY_ID}
+ key_file: \${SIGNING_PRIVATE_KEY}
+apk:
+ signature:
+ key_file: \${APK_SIGNING_KEY}
+ key_name: packages.\${PACKAGE_KEY_VERSION}
+EOF
+
+export GNUPGHOME="$work/source-gnupg"
+mkdir -m 700 "$GNUPGHOME"
+gpg --batch --pinentry-mode loopback --passphrase integration-pass \
+ --quick-generate-key 'Publish Integration <publish@example.invalid>' rsa2048 sign 1d >/dev/null
+fingerprint=$(gpg --batch --with-colons --list-secret-keys |
+ awk -F: '$1 == "fpr" { print $10; exit }')
+gpg --batch --pinentry-mode loopback --passphrase integration-pass \
+ --armor --export-secret-keys "$fingerprint" > "$work/private.gpg"
+openssl genrsa -out "$work/private.rsa" 2048 >/dev/null 2>&1
+# Organization keys are provisioned independently under a versioned archive.
+key_version=0001
+mkdir -p "$remote/keys"
+gpg --batch --armor --export "$fingerprint" > "$remote/packages.gpg"
+cp "$remote/packages.gpg" "$remote/keys/packages.$key_version.gpg"
+openssl rsa -in "$work/private.rsa" -pubout \
+ -out "$remote/keys/packages.$key_version.rsa.pub" >/dev/null 2>&1
+
+gpg_private_key=$(cat "$work/private.gpg")
+apk_private_key=$(cat "$work/private.rsa")
+
+package_version() {
+ version=$1
+ GPG_PRIVATE_KEY="$gpg_private_key" \
+ GPG_PASSPHRASE=integration-pass \
+ GPG_KEY_ID="$fingerprint" \
+ APK_PRIVATE_KEY="$apk_private_key" \
+ PACKAGE_KEY_VERSION="$key_version" \
+ mise --cd "$root" run package -- \
+ --config "$work/fixture/nfpm.yaml" \
+ --version "$version" \
+ --arch amd64 \
+ --output "$work/input" \
+ deb rpm apk
+}
+
+publish() {
+ PUBLISH_REMOTE="$work/remote" \
+ PUBLISH_TEST_LOG="$work/s3.log" \
+ PYTHONPATH="$root/tests/fakes" \
+ PATH="$work/bin:$PATH" \
+ S3_BUCKET=integration \
+ S3_ENDPOINT=https://example.invalid \
+ S3_PUBLIC_URL=https://pkg.dimidiumlabs.io \
+ S3_ACCESS_KEY_ID=integration \
+ S3_SECRET_ACCESS_KEY=integration \
+ GPG_PRIVATE_KEY="$gpg_private_key" \
+ GPG_PASSPHRASE=integration-pass \
+ GPG_KEY_ID="$fingerprint" \
+ APK_PRIVATE_KEY="$apk_private_key" \
+ PACKAGE_KEY_VERSION="$key_version" \
+ "$root/tasks/publish" \
+ --service publish-contract \
+ --channel nightly \
+ --input "$work/input" \
+ deb rpm apk
+}
+
+package_version '1.2.3~nightly.42'
+publish
+
+apt_root="$remote/publish-contract/apt"
+rpm_root="$remote/publish-contract/rpm/nightly"
+apk_root="$remote/publish-contract/apk/nightly/x86_64"
+
+test -f "$remote/packages.gpg"
+test -f "$remote/keys/packages.$key_version.gpg"
+test -f "$remote/keys/packages.$key_version.rsa.pub"
+test "$(find "$apt_root/pool/nightly" -name '*.deb' | wc -l)" -eq 1
+test "$(find "$rpm_root" -maxdepth 1 -name '*.rpm' | wc -l)" -eq 1
+test "$(find "$apk_root" -maxdepth 1 -name '*.apk' | wc -l)" -eq 1
+grep -q '^Package: publish-contract$' \
+ "$apt_root/dists/nightly/main/binary-amd64/Packages"
+grep -q 'baseurl=https://pkg.dimidiumlabs.io/publish-contract/rpm/nightly/' \
+ "$rpm_root/publish-contract-nightly.repo"
+
+keyring="$work/packages.gpg"
+gpg --batch --dearmor -o "$keyring" "$remote/packages.gpg"
+gpgv --keyring "$keyring" "$apt_root/dists/nightly/InRelease" >/dev/null
+gpgv --keyring "$keyring" "$rpm_root/repodata/repomd.xml.asc" \
+ "$rpm_root/repodata/repomd.xml" >/dev/null
+
+apk_tool=$(find "$HOME/.cache/mise" "$HOME/.local/share/mise" -type f -name apk.static 2>/dev/null | head -n 1 || true)
+if [ -z "$apk_tool" ]; then
+ archive="$work/apk-tools-static.apk"
+ curl -fsSL \
+ https://dl-cdn.alpinelinux.org/alpine/v3.22/main/x86_64/apk-tools-static-2.14.10-r0.apk \
+ -o "$archive"
+ printf '%s %s\n' c86e3822764e5fe19f41ce2e13553e48cac1ea4e74f858338e8d44bf0b616b61 "$archive" |
+ sha256sum -c - >/dev/null
+ mkdir -p "$work/apk-tools"
+ tar -xzf "$archive" -C "$work/apk-tools" sbin/apk.static 2>/dev/null
+ apk_tool="$work/apk-tools/sbin/apk.static"
+fi
+mkdir "$work/apk-keys"
+cp "$remote/keys/packages.$key_version.rsa.pub" "$work/apk-keys/"
+"$apk_tool" verify --keys-dir "$work/apk-keys" "$apk_root/APKINDEX.tar.gz" >/dev/null
+
+# A later publication keeps every package payload, regenerates metadata, and
+# removes stale metadata only inside the corresponding repository prefixes.
+printf stale > "$apt_root/dists/nightly/stale"
+printf stale > "$rpm_root/repodata/stale"
+rm -rf "$work/input"
+mkdir "$work/input"
+package_version '1.2.3~nightly.43'
+publish
+test "$(find "$apt_root/pool/nightly" -name '*.deb' | wc -l)" -eq 2
+test "$(find "$rpm_root" -maxdepth 1 -name '*.rpm' | wc -l)" -eq 2
+test "$(find "$apk_root" -maxdepth 1 -name '*.apk' | wc -l)" -eq 2
+test ! -e "$apt_root/dists/nightly/stale"
+test ! -e "$rpm_root/repodata/stale"
+test "$(grep -c '^Package: publish-contract$' \
+ "$apt_root/dists/nightly/main/binary-amd64/Packages")" -eq 2
+"$apk_tool" verify --keys-dir "$work/apk-keys" "$apk_root/APKINDEX.tar.gz" >/dev/null
+
+# Existing package names and organization keys are immutable.
+deb=$(find "$work/input" -maxdepth 1 -name '*.deb')
+printf '\nchanged\n' >> "$deb"
+if publish 2>/dev/null; then
+ echo 'publish integration: replaced an immutable package payload' >&2
+ exit 1
+fi
+cp "$apt_root/pool/nightly/${deb##*/}" "$deb"
+lock="$remote/publish-contract/_locks/nightly"
+mkdir -p "${lock%/*}"
+printf '{"expires":9999999999}\n' > "$lock"
+if publish 2>/dev/null; then
+ echo 'publish integration: ignored an active publication lock' >&2
+ exit 1
+fi
+rm "$lock"
+printf 'different key\n' > "$remote/keys/packages.$key_version.gpg"
+if publish 2>/dev/null; then
+ echo 'publish integration: ignored a mismatched versioned key' >&2
+ exit 1
+fi
+
+# Metadata cleanup must never escape generated repository prefixes.
+if awk '$1 == "s3" && $2 == "delete" && $3 !~ /^publish-contract\/(apt\/dists|rpm\/nightly\/repodata|_locks\/nightly)/' "$work/s3.log" | grep . >/dev/null; then
+ echo 'publish integration: deleted outside a metadata prefix' >&2
+ exit 1
+fi
+if PYTHONPATH="$root/tests/fakes" "$root/tasks/publish" \
+ --service '../escape' --channel nightly --input "$work/input" deb 2>/dev/null; then
+ echo 'publish integration: accepted an unsafe service name' >&2
+ exit 1
+fi