Make Notarized DMG Release #697
Workflow file for this run
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Make Notarized DMG Release | |
on: | |
workflow_dispatch: | |
inputs: | |
release-type: | |
description: "Build type (product review or public release)" | |
required: true | |
default: review | |
type: choice | |
options: | |
- review | |
- release | |
create-dmg: | |
description: "Create DMG image" | |
required: true | |
default: false | |
type: boolean | |
asana-task-url: | |
description: "Asana release task URL" | |
required: false | |
type: string | |
workflow_call: | |
inputs: | |
release-type: | |
description: "Build type (product review or public release)" | |
required: true | |
default: release | |
type: string | |
create-dmg: | |
description: "Create DMG image" | |
required: true | |
default: true | |
type: boolean | |
asana-task-url: | |
description: "Asana release task URL" | |
required: true | |
type: string | |
branch: | |
description: "Branch name" | |
required: false | |
type: string | |
secrets: | |
BUILD_CERTIFICATE_BASE64: | |
required: true | |
P12_PASSWORD: | |
required: true | |
KEYCHAIN_PASSWORD: | |
required: true | |
REVIEW_PROVISION_PROFILE_BASE64: | |
required: true | |
RELEASE_PROVISION_PROFILE_BASE64: | |
required: true | |
DBP_AGENT_RELEASE_PROVISION_PROFILE_BASE64: | |
required: true | |
DBP_AGENT_REVIEW_PROVISION_PROFILE_BASE64: | |
required: true | |
NETP_SYSEX_RELEASE_PROVISION_PROFILE_BASE64_V2: | |
required: true | |
NETP_SYSEX_REVIEW_PROVISION_PROFILE_BASE64_V2: | |
required: true | |
NETP_AGENT_RELEASE_PROVISION_PROFILE_BASE64_V2: | |
required: true | |
NETP_AGENT_REVIEW_PROVISION_PROFILE_BASE64_V2: | |
required: true | |
NETP_NOTIFICATIONS_RELEASE_PROVISION_PROFILE_BASE64: | |
required: true | |
NETP_NOTIFICATIONS_REVIEW_PROVISION_PROFILE_BASE64: | |
required: true | |
APPLE_API_KEY_BASE64: | |
required: true | |
APPLE_API_KEY_ID: | |
required: true | |
APPLE_API_KEY_ISSUER: | |
required: true | |
ASANA_ACCESS_TOKEN: | |
required: true | |
MM_HANDLES_BASE64: | |
required: true | |
MM_WEBHOOK_URL: | |
required: true | |
AWS_ACCESS_KEY_ID: | |
required: true | |
AWS_ACCESS_KEY_ID_RELEASE_S3: | |
required: true | |
AWS_SECRET_ACCESS_KEY: | |
required: true | |
AWS_SECRET_ACCESS_KEY_RELEASE_S3: | |
required: true | |
jobs: | |
export-notarized-app: | |
name: Export Notarized App | |
runs-on: macos-13-xlarge | |
outputs: | |
app-version: ${{ steps.set-outputs.outputs.app-version }} | |
app-name: ${{ steps.set-outputs.outputs.app-name }} | |
upload-to: ${{ steps.is-official-release.outputs.upload-to }} | |
env: | |
release-type: ${{ github.event.inputs.release-type || inputs.release-type }} | |
asana-task-url: ${{ github.event.inputs.asana-task-url || inputs.asana-task-url }} | |
branch: ${{ inputs.branch || github.ref_name }} | |
steps: | |
- name: Check out the code | |
uses: actions/checkout@v4 | |
with: | |
submodules: recursive | |
ref: ${{ env.branch }} | |
- name: Check if this is an official release build | |
id: is-official-release | |
env: | |
is-official-release: ${{ env.release-type == 'release' && (startsWith(env.branch, 'release') || startsWith(env.branch, 'hotfix')) }} | |
run: | | |
if [[ "${{ env.is-official-release }}" == "true" ]]; then | |
echo "upload-to=s3" >> $GITHUB_OUTPUT | |
echo "upload-to=s3" >> $GITHUB_ENV | |
elif [[ -n "${{ env.asana-task-url }}" ]]; then | |
echo "upload-to=asana" >> $GITHUB_OUTPUT | |
echo "upload-to=asana" >> $GITHUB_ENV | |
fi | |
- name: Install Apple Developer ID Application certificate | |
uses: ./.github/actions/install-certs-and-profiles | |
with: | |
BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }} | |
P12_PASSWORD: ${{ secrets.P12_PASSWORD }} | |
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} | |
REVIEW_PROVISION_PROFILE_BASE64: ${{ secrets.REVIEW_PROVISION_PROFILE_BASE64 }} | |
RELEASE_PROVISION_PROFILE_BASE64: ${{ secrets.RELEASE_PROVISION_PROFILE_BASE64 }} | |
DBP_AGENT_RELEASE_PROVISION_PROFILE_BASE64: ${{ secrets.DBP_AGENT_RELEASE_PROVISION_PROFILE_BASE64 }} | |
DBP_AGENT_REVIEW_PROVISION_PROFILE_BASE64: ${{ secrets.DBP_AGENT_REVIEW_PROVISION_PROFILE_BASE64 }} | |
NETP_SYSEX_RELEASE_PROVISION_PROFILE_BASE64: ${{ secrets.NETP_SYSEX_RELEASE_PROVISION_PROFILE_BASE64_V2 }} | |
NETP_SYSEX_REVIEW_PROVISION_PROFILE_BASE64: ${{ secrets.NETP_SYSEX_REVIEW_PROVISION_PROFILE_BASE64_V2 }} | |
NETP_AGENT_RELEASE_PROVISION_PROFILE_BASE64: ${{ secrets.NETP_AGENT_RELEASE_PROVISION_PROFILE_BASE64_V2 }} | |
NETP_AGENT_REVIEW_PROVISION_PROFILE_BASE64: ${{ secrets.NETP_AGENT_REVIEW_PROVISION_PROFILE_BASE64_V2 }} | |
NETP_NOTIFICATIONS_RELEASE_PROVISION_PROFILE_BASE64: ${{ secrets.NETP_NOTIFICATIONS_RELEASE_PROVISION_PROFILE_BASE64 }} | |
NETP_NOTIFICATIONS_REVIEW_PROVISION_PROFILE_BASE64: ${{ secrets.NETP_NOTIFICATIONS_REVIEW_PROVISION_PROFILE_BASE64 }} | |
- name: Select Xcode | |
run: sudo xcode-select -s /Applications/Xcode_$(<.xcode-version).app/Contents/Developer | |
- name: Archive and notarize the app | |
id: archive | |
env: | |
APPLE_API_KEY_BASE64: ${{ secrets.APPLE_API_KEY_BASE64 }} | |
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }} | |
APPLE_API_KEY_ISSUER: ${{ secrets.APPLE_API_KEY_ISSUER }} | |
ASANA_ACCESS_TOKEN: ${{ secrets.ASANA_ACCESS_TOKEN }} | |
run: | | |
# import API Key from secrets | |
export APPLE_API_KEY_PATH="$RUNNER_TEMP/apple_api_key.pem" | |
echo -n "$APPLE_API_KEY_BASE64" | base64 --decode -o $APPLE_API_KEY_PATH | |
if [[ "${{ runner.debug }}" == "1" ]]; then | |
./scripts/archive.sh ${{ env.release-type }} -r | |
else | |
./scripts/archive.sh ${{ env.release-type }} | |
fi | |
- name: Set app name and version | |
id: set-outputs | |
run: | | |
echo "app-version=${{ env.app-version }}" >> $GITHUB_OUTPUT | |
echo "app-name=${{ env.app-name }}" >> $GITHUB_OUTPUT | |
echo "dsym-name=DuckDuckGo-${{ env.app-version }}-dSYM.zip" >> $GITHUB_OUTPUT | |
- name: Upload app artifact | |
uses: actions/upload-artifact@v4 | |
with: | |
name: DuckDuckGo-${{ env.release-type }}-${{ env.app-version }}.app | |
path: ${{ github.workspace }}/release/DuckDuckGo-${{ env.app-version }}.zip | |
- name: Upload dSYMs artifact | |
uses: actions/upload-artifact@v4 | |
with: | |
name: DuckDuckGo-${{ env.release-type }}-dSYM-${{ env.app-version }} | |
path: ${{ github.workspace }}/release/${{ steps.set-outputs.outputs.dsym-name }} | |
- name: Extract Asana Task ID | |
id: task-id | |
if: ${{ env.upload-to == 'asana' }} | |
uses: ./.github/actions/asana-extract-task-id | |
with: | |
task-url: ${{ env.asana-task-url }} | |
- name: Upload dSYMs to Asana | |
if: ${{ env.upload-to == 'asana' }} | |
uses: ./.github/actions/asana-upload | |
with: | |
access-token: ${{ secrets.ASANA_ACCESS_TOKEN }} | |
file-name: ${{ github.workspace }}/release/DuckDuckGo-${{ env.app-version }}-dSYM.zip | |
task-id: ${{ steps.task-id.outputs.task-id }} | |
- name: Upload dSYMs to S3 | |
id: upload-dsyms-to-s3 | |
if: ${{ env.upload-to == 's3' }} | |
env: | |
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} | |
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | |
AWS_DEFAULT_REGION: ${{ vars.AWS_DEFAULT_REGION }} | |
DSYM_S3_PATH: s3://${{ vars.DSYM_BUCKET_NAME }}/${{ vars.DSYM_BUCKET_PREFIX }}/${{ steps.set-outputs.outputs.dsym-name }} | |
run: | | |
echo "dsym-s3-path=${DSYM_S3_PATH}" >> $GITHUB_OUTPUT | |
aws s3 cp ${{ github.workspace }}/release/${{ steps.set-outputs.outputs.dsym-name }} ${{ env.DSYM_S3_PATH }} | |
- name: Report success | |
if: ${{ env.upload-to == 's3' }} | |
uses: ./.github/actions/asana-log-message | |
env: | |
DSYM_S3_PATH: ${{ steps.upload-dsyms-to-s3.outputs.dsym-s3-path }} | |
TAG: ${{ env.app-version }} | |
WORKFLOW_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
with: | |
task-url: ${{ env.asana-task-url }} | |
access-token: ${{ secrets.ASANA_ACCESS_TOKEN }} | |
template-name: debug-symbols-uploaded | |
create-dmg: | |
name: Create DMG | |
needs: export-notarized-app | |
if: ${{ github.event.inputs.create-dmg == true || inputs.create-dmg == true }} | |
# use macos-12 for creating DMGs as macos-13 beta runners can't run AppleScript: https://app.asana.com/0/0/1204523592790998/f | |
runs-on: macos-12 | |
env: | |
app-version: ${{ needs.export-notarized-app.outputs.app-version }} | |
app-name: ${{ needs.export-notarized-app.outputs.app-name }} | |
asana-task-url: ${{ github.event.inputs.asana-task-url || inputs.asana-task-url }} | |
upload-to: ${{ needs.export-notarized-app.outputs.upload-to }} | |
release-type: ${{ github.event.inputs.release-type || inputs.release-type }} | |
steps: | |
- name: Check out the code | |
uses: actions/checkout@v4 | |
with: | |
submodules: recursive | |
ref: ${{ inputs.branch || github.ref_name }} | |
sparse-checkout: .github | |
- name: Fetch app bundle | |
uses: actions/download-artifact@v4 | |
with: | |
name: DuckDuckGo-${{ env.release-type }}-${{ env.app-version }}.app | |
path: ${{ github.workspace }}/dmg | |
- name: Extract app bundle | |
run: | | |
ditto -xk DuckDuckGo-${{ env.app-version }}.zip . | |
rm -f DuckDuckGo-${{ env.app-version }}.zip | |
working-directory: ${{ github.workspace }}/dmg | |
- name: Install create-dmg | |
run: brew install create-dmg | |
- name: Create DMG | |
id: create-dmg | |
env: | |
GH_TOKEN: ${{ github.token }} | |
run: | | |
dmg="duckduckgo-${{ env.app-version }}.dmg" | |
curl -fLSs $(gh api https://api.github.com/repos/${{ github.repository }}/contents/scripts/assets/dmg-background.png?ref=${{ github.ref }} --jq .download_url) \ | |
--output dmg-background.png | |
create-dmg --volname "${{ env.app-name }}" \ | |
--icon "${{ env.app-name }}.app" 140 160 \ | |
--background "dmg-background.png" \ | |
--window-size 600 400 \ | |
--icon-size 120 \ | |
--app-drop-link 430 160 "${dmg}" \ | |
"dmg" | |
echo "dmg=${dmg}" >> $GITHUB_OUTPUT | |
- name: Upload DMG artifact | |
uses: actions/upload-artifact@v4 | |
with: | |
name: DuckDuckGo-${{ env.release-type }}-${{ env.app-version }}.dmg | |
path: ${{ github.workspace }}/${{ steps.create-dmg.outputs.dmg }} | |
- name: Upload DMG to S3 | |
if: ${{ env.upload-to == 's3' }} | |
env: | |
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID_RELEASE_S3 }} | |
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY_RELEASE_S3 }} | |
AWS_DEFAULT_REGION: ${{ vars.AWS_DEFAULT_REGION }} | |
RELEASE_BUCKET_NAME: ${{ vars.RELEASE_BUCKET_NAME }} | |
RELEASE_BUCKET_PREFIX: ${{ vars.RELEASE_BUCKET_PREFIX }} | |
run: | | |
aws s3 cp \ | |
${{ github.workspace }}/${{ steps.create-dmg.outputs.dmg }} \ | |
s3://${{ env.RELEASE_BUCKET_NAME }}/${{ env.RELEASE_BUCKET_PREFIX }}/ \ | |
--acl public-read | |
- name: Report success | |
if: ${{ env.upload-to == 's3' }} | |
uses: ./.github/actions/asana-log-message | |
env: | |
DMG_URL: ${{ vars.DMG_URL_ROOT }}${{ steps.create-dmg.outputs.dmg }} | |
TAG: ${{ env.app-version }} | |
WORKFLOW_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
with: | |
task-url: ${{ env.asana-task-url }} | |
access-token: ${{ secrets.ASANA_ACCESS_TOKEN }} | |
template-name: dmg-uploaded | |
- name: Extract Asana Task ID | |
id: task-id | |
if: ${{ env.upload-to == 'asana' }} | |
uses: ./.github/actions/asana-extract-task-id | |
with: | |
task-url: ${{ env.asana-task-url }} | |
- name: Upload DMG to Asana | |
if: ${{ env.upload-to == 'asana' }} | |
uses: ./.github/actions/asana-upload | |
with: | |
access-token: ${{ secrets.ASANA_ACCESS_TOKEN }} | |
file-name: ${{ github.workspace }}/duckduckgo-${{ env.app-version }}.dmg | |
task-id: ${{ steps.task-id.outputs.task-id }} | |
mattermost: | |
name: Send Mattermost message | |
needs: [export-notarized-app, create-dmg] | |
if: always() | |
runs-on: ubuntu-latest | |
env: | |
success: ${{ (needs.export-notarized-app.result == 'success') && (needs.create-dmg.result == 'success' || needs.create-dmg.result == 'skipped') }} | |
failure: ${{ (needs.export-notarized-app.result == 'failure') || (needs.create-dmg.result == 'failure') }} | |
steps: | |
- name: Send Mattermost message | |
if: ${{ env.success || env.failure }} # Don't execute when cancelled | |
env: | |
ASANA_TASK_URL: ${{ github.event.inputs.asana-task-url || inputs.asana-task-url }} | |
GH_TOKEN: ${{ github.token }} | |
RELEASE_TYPE: ${{ github.event.inputs.release-type || inputs.release-type }} | |
WORKFLOW_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
run: | | |
curl -fLSs $(gh api https://api.github.com/repos/${{ github.repository }}/contents/scripts/assets/release-mm-template.json?ref=${{ github.ref }} --jq .download_url) \ | |
--output message-template.json | |
export MM_USER_HANDLE=$(base64 -d <<< ${{ secrets.MM_HANDLES_BASE64 }} | jq ".${{ github.actor }}" | tr -d '"') | |
if [[ -z "${MM_USER_HANDLE}" ]]; then | |
echo "Mattermost user handle not known for ${{ github.actor }}, skipping sending message" | |
else | |
if [[ -n "${ASANA_TASK_URL}" ]]; then | |
export ASANA_LINK=" | [:asana: Asana task](${ASANA_TASK_URL})" | |
fi | |
if [[ "${{ env.success }}" == "true" ]]; then | |
status="success" | |
else | |
status="failure" | |
fi | |
curl -s -H 'Content-type: application/json' \ | |
-d "$(envsubst < message-template.json | jq ".${status}")" \ | |
${{ secrets.MM_WEBHOOK_URL }} | |
fi |