diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 973f73fc..f2807bf7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -635,3 +635,63 @@ jobs: run: nix develop .#ci -c ./scripts/ci/verify-release-artifacts.sh artifacts macos ios web latest-json env: MAPLE_ENFORCE_IOS_SIGNED_REPRODUCIBILITY: "1" + + publish-release-verification-guide: + needs: verify-release-artifacts + runs-on: ubuntu-latest + permissions: + contents: write + id-token: write + attestations: write + artifact-metadata: write + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # was v4 + with: + persist-credentials: false + + - name: Install Nix + uses: DeterminateSystems/nix-installer-action@ef8a148080ab6020fd15196c2084a2eea5ff2d25 # was v22 + with: + github-token: "" + + - name: Download release artifacts + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + RELEASE_TAG: ${{ github.event.release.tag_name }} + run: | + mkdir -p artifacts + gh release download "${RELEASE_TAG}" -D artifacts + rm -f \ + artifacts/MAPLE_RELEASE_VERIFICATION.md \ + artifacts/MAPLE_RELEASE_VERIFICATION.md.sha256 + + - name: Generate release verification guide + env: + RELEASE_TAG: ${{ github.event.release.tag_name }} + run: | + nix develop .#ci -c ./scripts/ci/release-verification-guide.sh \ + artifacts \ + MAPLE_RELEASE_VERIFICATION.md + sha256sum MAPLE_RELEASE_VERIFICATION.md > MAPLE_RELEASE_VERIFICATION.md.sha256 + sha256sum \ + MAPLE_RELEASE_VERIFICATION.md \ + MAPLE_RELEASE_VERIFICATION.md.sha256 \ + > release-verification-guide-artifacts.sha256 + cat release-verification-guide-artifacts.sha256 + + - name: Attest release verification guide + # Keep the guide upload running if GitHub token policy rejects artifact attestation. + continue-on-error: true + uses: actions/attest@281a49d4cbb0a72c9575a50d18f6deb515a11deb # was v4 + with: + subject-checksums: release-verification-guide-artifacts.sha256 + + - name: Upload release verification guide + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + RELEASE_TAG: ${{ github.event.release.tag_name }} + run: | + gh release upload "${RELEASE_TAG}" \ + MAPLE_RELEASE_VERIFICATION.md \ + MAPLE_RELEASE_VERIFICATION.md.sha256 \ + --clobber diff --git a/scripts/ci/ios-release.sh b/scripts/ci/ios-release.sh index 6802e89e..2588aec9 100755 --- a/scripts/ci/ios-release.sh +++ b/scripts/ci/ios-release.sh @@ -162,7 +162,8 @@ write_ios_canonical_app_manifest_diff() { sed -E 's/^([0-9a-f]{64}) (.*)$/\2 \1/' "${signed_manifest}" | LC_ALL=C sort > "${signed_by_path}" comm -3 "${unsigned_by_path}" "${signed_by_path}" > "${out}" if [ ! -s "${out}" ]; then - printf 'No canonical file manifest differences after stripping Apple signing metadata.\n' > "${out}" + rm -f "${out}" + printf 'verified-ios-canonical-file-manifest-no-diff %s %s\n' "$(basename "${unsigned_manifest}")" "$(basename "${signed_manifest}")" fi rm -f "${unsigned_by_path}" "${signed_by_path}" } @@ -200,7 +201,11 @@ if [ "${signed_app_canonical_hash}" != "${unsigned_app_canonical_hash}" ]; then echo "unsigned=${unsigned_app_canonical_hash}" >&2 echo "archive_canonical=${signed_app_canonical_hash}" >&2 echo "Canonical iOS archive app file manifest diff diagnostic:" >&2 - sed -n '1,80p' "${repro_dir}/ios-release-archive-app-vs-unsigned-canonical.diff.txt" >&2 + if [ -s "${repro_dir}/ios-release-archive-app-vs-unsigned-canonical.diff.txt" ]; then + sed -n '1,80p' "${repro_dir}/ios-release-archive-app-vs-unsigned-canonical.diff.txt" >&2 + else + echo "No canonical iOS archive app file manifest diff was produced." >&2 + fi else printf 'verified-ios-archive-app %s %s\n' "${signed_app_canonical_hash}" "$(repo_relative_path "${signed_app}")" fi @@ -235,7 +240,11 @@ for artifact in "${ios_artifacts[@]}"; do echo "unsigned=${unsigned_app_canonical_hash}" >&2 echo "ipa_payload=${ipa_canonical_hash}" >&2 echo "Canonical iOS IPA payload file manifest diff diagnostic:" >&2 - sed -n '1,80p' "${payload_diff}" >&2 + if [ -s "${payload_diff}" ]; then + sed -n '1,80p' "${payload_diff}" >&2 + else + echo "No canonical iOS IPA payload file manifest diff was produced." >&2 + fi if [ "${MAPLE_ENFORCE_IOS_SIGNED_REPRODUCIBILITY:-0}" = "1" ]; then exit 1 fi diff --git a/scripts/ci/release-verification-guide.sh b/scripts/ci/release-verification-guide.sh new file mode 100755 index 00000000..aac56e51 --- /dev/null +++ b/scripts/ci/release-verification-guide.sh @@ -0,0 +1,141 @@ +#!/usr/bin/env bash +set -euo pipefail + +source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/_common.sh" + +usage() { + cat >&2 <<'EOF' +usage: release-verification-guide.sh + +Generates the user-facing release verification guide that is uploaded as a +release asset after CI verifies the release artifacts. +EOF +} + +artifacts_dir="${1:-}" +out="${2:-}" + +if [ -z "${artifacts_dir}" ] || [ -z "${out}" ] || [ "${artifacts_dir}" = "-h" ] || [ "${artifacts_dir}" = "--help" ]; then + usage + exit 2 +fi + +if [ ! -d "${artifacts_dir}" ]; then + echo "Artifacts directory does not exist: ${artifacts_dir}" >&2 + exit 1 +fi + +repo="${GITHUB_REPOSITORY:-OpenSecretCloud/Maple}" +release_tag="${RELEASE_TAG:-${GITHUB_REF_NAME:-}}" +if [ -z "${release_tag}" ]; then + echo "RELEASE_TAG or GITHUB_REF_NAME is required." >&2 + exit 1 +fi + +source_commit="$(git -C "${REPO_ROOT}" rev-parse HEAD)" +release_url="https://github.com/${repo}/releases/tag/${release_tag}" +repo_url="https://github.com/${repo}.git" +source_ref="refs/tags/${release_tag}" +clone_dir="maple-${release_tag}" + +asset_names=() +while IFS= read -r -d '' file; do + asset_names+=("$(basename "${file}")") +done < <(find "${artifacts_dir}" -maxdepth 1 -type f -print0 | LC_ALL=C sort -z) + +mkdir -p "$(dirname "${out}")" + +{ + cat < "${out}" + +printf 'wrote-release-verification-guide %s\n' "${out}"