Record native build provenance
This commit is contained in:
@@ -49,6 +49,9 @@ on:
|
||||
source-sha:
|
||||
description: Exact commit used for the build
|
||||
value: ${{ jobs.build.outputs.source-sha }}
|
||||
artifact-name:
|
||||
description: Workflow artifact containing the AAB and provenance
|
||||
value: ${{ jobs.build.outputs.artifact-name }}
|
||||
secrets:
|
||||
EXPO_TOKEN:
|
||||
required: true
|
||||
@@ -88,9 +91,10 @@ jobs:
|
||||
group: android-build
|
||||
cancel-in-progress: false
|
||||
outputs:
|
||||
package-version: ${{ steps.get-build-info.outputs.PACKAGE_VERSION }}
|
||||
version-code: ${{ steps.get-build-info.outputs.BSKY_ANDROID_VERSION_CODE }}
|
||||
package-version: ${{ steps.package.outputs.version }}
|
||||
version-code: ${{ steps.aab-version.outputs.version-code }}
|
||||
source-sha: ${{ steps.source.outputs.sha }}
|
||||
artifact-name: android-aab-${{ github.run_id }}
|
||||
steps:
|
||||
- name: ⬇️ Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
@@ -114,6 +118,11 @@ jobs:
|
||||
distribution: "temurin"
|
||||
java-version: "17"
|
||||
|
||||
- name: 🔧 Setup bundletool
|
||||
uses: amyu/setup-bundletool@cc2e1857284660bd625e43f2c8a45626f034302f # v1.1
|
||||
with:
|
||||
version: "1.18.3"
|
||||
|
||||
- name: 🔤 Compile translations
|
||||
uses: ./.github/actions/compile-i18n
|
||||
|
||||
@@ -139,28 +148,61 @@ jobs:
|
||||
sentry-release: ${{ steps.env.outputs.release-version }}
|
||||
sentry-dist: ${{ steps.env.outputs.bundle-identifier }}
|
||||
|
||||
- name: 📚 Get version from package.json
|
||||
id: get-build-info
|
||||
run: bash scripts/setGitHubOutput.sh
|
||||
- name: 📚 Get package version
|
||||
id: package
|
||||
run: echo "version=$(jq -r .version package.json)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
# Hands the built bundle off to the submit / universalApk jobs. Retention is
|
||||
# deliberately short (1 day) since it's only an intra-run handoff artifact.
|
||||
- name: 🔢 Read version code from AAB
|
||||
id: aab-version
|
||||
run: |
|
||||
version_code=$(bundletool dump manifest --bundle=build.aab --xpath=/manifest/@android:versionCode)
|
||||
[[ "$version_code" =~ ^[1-9][0-9]*$ ]] || {
|
||||
echo "::error::Could not read a positive Android version code from build.aab"
|
||||
exit 1
|
||||
}
|
||||
echo "AAB version code: $version_code"
|
||||
echo "version-code=$version_code" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Record artifact provenance
|
||||
env:
|
||||
SOURCE_SHA: ${{ steps.source.outputs.sha }}
|
||||
PACKAGE_VERSION: ${{ steps.package.outputs.version }}
|
||||
VERSION_CODE: ${{ steps.aab-version.outputs.version-code }}
|
||||
run: |
|
||||
checksum=$(sha256sum build.aab | awk '{print $1}')
|
||||
jq -n \
|
||||
--arg sourceSha "$SOURCE_SHA" \
|
||||
--arg packageVersion "$PACKAGE_VERSION" \
|
||||
--arg versionCode "$VERSION_CODE" \
|
||||
--arg artifactFilename "build.aab" \
|
||||
--arg artifactSha256 "$checksum" \
|
||||
'{sourceSha: $sourceSha, packageVersion: $packageVersion, versionCode: $versionCode, artifactFilename: $artifactFilename, artifactSha256: $artifactSha256, submissionState: "notStarted"}' \
|
||||
> android-build-metadata.json
|
||||
cat android-build-metadata.json
|
||||
|
||||
# Keep the exact build and its provenance available for inspection and failed-job retries.
|
||||
- name: 🚀 Upload AAB artifact
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: android-aab-${{ github.run_id }}
|
||||
retention-days: 1
|
||||
retention-days: 14
|
||||
if-no-files-found: error
|
||||
path: build.aab
|
||||
path: |
|
||||
build.aab
|
||||
android-build-metadata.json
|
||||
|
||||
- name: 📝 Write build summary
|
||||
env:
|
||||
REMOTE_VERSION_CODE: ${{ steps.get-build-info.outputs.BSKY_ANDROID_VERSION_CODE }}
|
||||
VERSION_CODE: ${{ steps.aab-version.outputs.version-code }}
|
||||
SOURCE_SHA: ${{ steps.source.outputs.sha }}
|
||||
run: |
|
||||
artifact_sha256=$(jq -r .artifactSha256 android-build-metadata.json)
|
||||
{
|
||||
echo "### Android build number"
|
||||
echo
|
||||
echo "\`$REMOTE_VERSION_CODE\`"
|
||||
echo "- Version code: \`$VERSION_CODE\`"
|
||||
echo "- Source SHA: \`$SOURCE_SHA\`"
|
||||
echo "- AAB SHA-256: \`$artifact_sha256\`"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
submit:
|
||||
@@ -192,6 +234,29 @@ jobs:
|
||||
PROFILE: ${{ inputs.profile || 'testflight-android' }}
|
||||
run: pnpm eas submit -p android --profile $PROFILE --non-interactive --path build.aab
|
||||
|
||||
- name: Record submission receipt
|
||||
env:
|
||||
SOURCE_SHA: ${{ needs.build.outputs.source-sha }}
|
||||
PACKAGE_VERSION: ${{ needs.build.outputs.package-version }}
|
||||
VERSION_CODE: ${{ needs.build.outputs.version-code }}
|
||||
run: |
|
||||
artifact_sha256=$(jq -r .artifactSha256 android-build-metadata.json)
|
||||
jq -n \
|
||||
--arg sourceSha "$SOURCE_SHA" \
|
||||
--arg packageVersion "$PACKAGE_VERSION" \
|
||||
--arg versionCode "$VERSION_CODE" \
|
||||
--arg artifactSha256 "$artifact_sha256" \
|
||||
'{sourceSha: $sourceSha, packageVersion: $packageVersion, versionCode: $versionCode, artifactSha256: $artifactSha256, submissionState: "submitted"}' \
|
||||
> android-submission.json
|
||||
|
||||
- name: Upload submission receipt
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: android-submission-${{ github.run_id }}
|
||||
path: android-submission.json
|
||||
retention-days: 14
|
||||
if-no-files-found: error
|
||||
|
||||
- name: 🔔 Notify Slack of Play Store Submission
|
||||
if: ${{ inputs.profile == 'production' }}
|
||||
uses: slackapi/slack-github-action@dcb1066f776dd043e64d0e8ba94ca15cc7e1875d # v4.0.0
|
||||
|
||||
@@ -10,6 +10,10 @@ on:
|
||||
options:
|
||||
- testflight
|
||||
- production
|
||||
submit:
|
||||
type: boolean
|
||||
description: Submit the build to App Store Connect
|
||||
default: true
|
||||
testFlightGroup:
|
||||
type: choice
|
||||
description: TestFlight group to assign the build to after submitting
|
||||
@@ -34,6 +38,10 @@ on:
|
||||
type: string
|
||||
description: Build profile to use
|
||||
required: true
|
||||
submit:
|
||||
type: boolean
|
||||
description: Submit the build to App Store Connect
|
||||
default: true
|
||||
testFlightGroup:
|
||||
type: string
|
||||
description: TestFlight group to assign the build to after submitting ("none" to skip)
|
||||
@@ -58,6 +66,9 @@ on:
|
||||
source-sha:
|
||||
description: Exact commit used for the build
|
||||
value: ${{ jobs.build.outputs.source-sha }}
|
||||
artifact-name:
|
||||
description: Workflow artifact containing the IPA and provenance
|
||||
value: ${{ jobs.build.outputs.artifact-name }}
|
||||
secrets:
|
||||
EXPO_TOKEN:
|
||||
required: true
|
||||
@@ -98,6 +109,7 @@ jobs:
|
||||
package-version: ${{ steps.get-build-info.outputs.PACKAGE_VERSION }}
|
||||
build-number: ${{ steps.ipa-build-number.outputs.build-number }}
|
||||
source-sha: ${{ steps.source.outputs.sha }}
|
||||
artifact-name: ios-build-${{ github.run_id }}
|
||||
steps:
|
||||
- name: ⬇️ Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
@@ -225,26 +237,48 @@ jobs:
|
||||
echo "IPA build number: $build_number"
|
||||
echo "build-number=$build_number" >> "$GITHUB_OUTPUT"
|
||||
|
||||
# Hand the IPA and dSYM off to the submit job. Retention is deliberately short since
|
||||
# this artifact only exists to bridge the two jobs within a single run.
|
||||
- name: Record artifact provenance
|
||||
env:
|
||||
SOURCE_SHA: ${{ steps.source.outputs.sha }}
|
||||
PACKAGE_VERSION: ${{ steps.get-build-info.outputs.PACKAGE_VERSION }}
|
||||
BUILD_NUMBER: ${{ steps.ipa-build-number.outputs.build-number }}
|
||||
run: |
|
||||
artifact="$BUILD_DIR/Bluesky.ipa"
|
||||
checksum=$(shasum -a 256 "$artifact" | awk '{print $1}')
|
||||
jq -n \
|
||||
--arg sourceSha "$SOURCE_SHA" \
|
||||
--arg packageVersion "$PACKAGE_VERSION" \
|
||||
--arg buildNumber "$BUILD_NUMBER" \
|
||||
--arg artifactFilename "Bluesky.ipa" \
|
||||
--arg artifactSha256 "$checksum" \
|
||||
'{sourceSha: $sourceSha, packageVersion: $packageVersion, buildNumber: $buildNumber, artifactFilename: $artifactFilename, artifactSha256: $artifactSha256, submissionState: "notStarted"}' \
|
||||
> ios-build-metadata.json
|
||||
cat ios-build-metadata.json
|
||||
|
||||
# Keep the exact build and its provenance available for inspection and failed-job retries.
|
||||
- name: 🚀 Upload build artifact
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: ios-build-${{ github.run_id }}
|
||||
retention-days: 1
|
||||
retention-days: 14
|
||||
if-no-files-found: error
|
||||
path: |
|
||||
${{ env.BUILD_DIR }}/Bluesky.ipa
|
||||
${{ env.BUILD_DIR }}/Bluesky.app.dSYM.zip
|
||||
ios-build-metadata.json
|
||||
|
||||
- name: 📝 Write build summary
|
||||
env:
|
||||
REMOTE_BUILD_NUMBER: ${{ steps.get-build-info.outputs.BSKY_IOS_BUILD_NUMBER }}
|
||||
BUILD_NUMBER: ${{ steps.ipa-build-number.outputs.build-number }}
|
||||
SOURCE_SHA: ${{ steps.source.outputs.sha }}
|
||||
run: |
|
||||
artifact_sha256=$(jq -r .artifactSha256 ios-build-metadata.json)
|
||||
{
|
||||
echo "### iOS build number"
|
||||
echo
|
||||
echo "\`$REMOTE_BUILD_NUMBER\`"
|
||||
echo "- Build number: \`$BUILD_NUMBER\`"
|
||||
echo "- Source SHA: \`$SOURCE_SHA\`"
|
||||
echo "- IPA SHA-256: \`$artifact_sha256\`"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
submit:
|
||||
@@ -252,6 +286,7 @@ jobs:
|
||||
# Submission and dSYM upload are I/O bound and don't need the xlarge builder.
|
||||
runs-on: macos-26
|
||||
needs: [build]
|
||||
if: ${{ inputs.submit != false }}
|
||||
steps:
|
||||
- name: ⬇️ Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
@@ -274,6 +309,29 @@ jobs:
|
||||
- name: 🚀 Deploy
|
||||
run: pnpm eas submit -p ios --non-interactive --path ios-build/Bluesky.ipa
|
||||
|
||||
- name: Record submission receipt
|
||||
env:
|
||||
SOURCE_SHA: ${{ needs.build.outputs.source-sha }}
|
||||
PACKAGE_VERSION: ${{ needs.build.outputs.package-version }}
|
||||
BUILD_NUMBER: ${{ needs.build.outputs.build-number }}
|
||||
run: |
|
||||
artifact_sha256=$(jq -r .artifactSha256 ios-build/ios-build-metadata.json)
|
||||
jq -n \
|
||||
--arg sourceSha "$SOURCE_SHA" \
|
||||
--arg packageVersion "$PACKAGE_VERSION" \
|
||||
--arg buildNumber "$BUILD_NUMBER" \
|
||||
--arg artifactSha256 "$artifact_sha256" \
|
||||
'{sourceSha: $sourceSha, packageVersion: $packageVersion, buildNumber: $buildNumber, artifactSha256: $artifactSha256, submissionState: "submitted"}' \
|
||||
> ios-submission.json
|
||||
|
||||
- name: Upload submission receipt
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: ios-submission-${{ github.run_id }}
|
||||
path: ios-submission.json
|
||||
retention-days: 14
|
||||
if-no-files-found: error
|
||||
|
||||
- name: 🪲 Upload dSYM to Sentry
|
||||
env:
|
||||
SENTRY_ORG: blueskyweb
|
||||
|
||||
@@ -59,3 +59,7 @@ The release model can be exercised locally with `node scripts/release/cli.mjs`.
|
||||
## Manual preview
|
||||
|
||||
The **Prepare Cactus Release** workflow accepts a release version and optional source ref. It validates the checked-out package and Expo versions, generates a provisional changelog from commit titles, uploads the prepared release document as an artifact, and summarizes every derived identifier. It has read-only repository permissions and does not create a branch, tag, commit, or GitHub Release.
|
||||
|
||||
## Build provenance
|
||||
|
||||
Native build artifacts are retained with metadata recording their exact source SHA, package version, artifact-derived build number, filename, SHA-256 checksum, and submission state. Store submission can be disabled so production-profile artifacts can be inspected without publishing them.
|
||||
|
||||
Reference in New Issue
Block a user