diff options
Diffstat (limited to 'tests/publish-integration')
| -rwxr-xr-x | tests/publish-integration | 185 | +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 |
