Publish DMG Release #11
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: Publish DMG Release | |
on: | |
workflow_dispatch: | |
inputs: | |
asana-task-url: | |
description: "Asana release task URL" | |
required: true | |
type: string | |
tag: | |
description: "Tag to publish" | |
required: true | |
type: string | |
release-type: | |
description: "Release type" | |
required: true | |
type: choice | |
options: | |
- internal | |
- public | |
- hotfix | |
jobs: | |
# This is only run for public and hotfix releases | |
# Internal release has been tagged as part of code_freeze or bump_interal_release workflows | |
tag-public-release: | |
name: Tag public release | |
if: ${{ github.event.inputs.release-type != 'internal' }} | |
uses: ./.github/workflows/tag_release.yml | |
with: | |
asana-task-url: ${{ github.event.inputs.asana-task-url }} | |
branch: ${{ github.ref_name }} | |
prerelease: false | |
secrets: | |
ASANA_ACCESS_TOKEN: ${{ secrets.ASANA_ACCESS_TOKEN }} | |
GHA_ELEVATED_PERMISSIONS_TOKEN: ${{ secrets.GHA_ELEVATED_PERMISSIONS_TOKEN }} | |
publish-to-sparkle: | |
name: Publish a release to Sparkle | |
needs: [tag-public-release] | |
# Allow to run even if the tag-public-release job was skipped (e.g. for internal releases) | |
# or failed (for public releases or hotfixes), because tagging doesn't block publishing the release | |
if: always() | |
runs-on: macos-13-xlarge | |
timeout-minutes: 10 | |
env: | |
SPARKLE_DIR: ${{ github.workspace }}/sparkle-updates | |
steps: | |
- name: Verify the tag | |
id: verify-tag | |
env: | |
tag: ${{ github.event.inputs.tag }} | |
run: | | |
tag_regex='^[0-9]+\.[0-9]+\.[0-9]+-[0-9]+$' | |
if [[ ! "$tag" =~ $tag_regex ]]; then | |
echo "::error::The provided tag ($tag) has incorrect format (attempted to match ${tag_regex})." | |
exit 1 | |
fi | |
echo "release-version=${tag//-/.}" >> $GITHUB_OUTPUT | |
- name: Check out the code | |
uses: actions/checkout@v4 | |
with: | |
submodules: recursive | |
- name: Select Xcode | |
run: sudo xcode-select -s /Applications/Xcode_$(<.xcode-version).app/Contents/Developer | |
- name: Set up Sparkle tools | |
env: | |
SPARKLE_URL: https://github.com/sparkle-project/Sparkle/releases/download/${{ vars.SPARKLE_VERSION }}/Sparkle-${{ vars.SPARKLE_VERSION }}.tar.xz | |
run: | | |
curl -fLSs $SPARKLE_URL | tar xJ bin | |
echo "${{ github.workspace }}/bin" >> $GITHUB_PATH | |
- name: Fetch DMG | |
id: fetch-dmg | |
if: ${{ github.event.inputs.release-type != 'public' }} | |
env: | |
DMG_NAME: duckduckgo-${{ steps.verify-tag.outputs.release-version }}.dmg | |
run: | | |
# Public release doesn't need fetching a DMG (it's already uploaded to S3) | |
if [[ "${{ github.event.inputs.release-type }}" != 'public' ]]; then | |
DMG_URL="${{ vars.DMG_URL_ROOT }}${DMG_NAME}" | |
curl -fLSs -o "$DMG_NAME" "$DMG_URL" | |
fi | |
echo "dmg-name=$DMG_NAME" >> $GITHUB_OUTPUT | |
echo "dmg-path=$DMG_NAME" >> $GITHUB_OUTPUT | |
- name: Extract Asana Task ID | |
id: task-id | |
uses: ./.github/actions/asana-extract-task-id | |
with: | |
task-url: ${{ github.event.inputs.asana-task-url }} | |
- name: Fetch release notes | |
env: | |
TASK_ID: ${{ steps.task-id.outputs.task-id }} | |
ASANA_ACCESS_TOKEN: ${{ secrets.ASANA_ACCESS_TOKEN }} | |
run: | | |
curl -fLSs "https://app.asana.com/api/1.0/tasks/${TASK_ID}?opt_fields=notes" \ | |
-H "Authorization: Bearer ${ASANA_ACCESS_TOKEN}" \ | |
| jq -r .data.notes \ | |
| ./scripts/extract_release_notes.sh > release_notes.txt | |
echo "RELEASE_NOTES_FILE=release_notes.txt" >> $GITHUB_ENV | |
- name: Generate appcast | |
id: appcast | |
env: | |
DMG_PATH: ${{ steps.fetch-dmg.outputs.dmg-path }} | |
SPARKLE_PRIVATE_KEY: ${{ secrets.SPARKLE_PRIVATE_KEY }} | |
RELEASE_TYPE: ${{ github.event.inputs.release-type }} | |
VERSION: ${{ steps.verify-tag.outputs.release-version }} | |
run: | | |
echo -n "$SPARKLE_PRIVATE_KEY" > sparkle_private_key | |
chmod 600 sparkle_private_key | |
case "$RELEASE_TYPE" in | |
"internal") | |
./scripts/appcast_manager/appcastManager.swift \ | |
--release-to-internal-channel \ | |
--dmg ${DMG_PATH} \ | |
--release-notes release_notes.txt \ | |
--key sparkle_private_key | |
;; | |
"public") | |
./scripts/appcast_manager/appcastManager.swift \ | |
--release-to-public-channel \ | |
--version ${VERSION} \ | |
--release-notes release_notes.txt \ | |
--key sparkle_private_key | |
;; | |
"hotfix") | |
./scripts/appcast_manager/appcastManager.swift \ | |
--release-hotfix-to-public-channel \ | |
--dmg ${DMG_PATH} \ | |
--release-notes release_notes.txt \ | |
--key sparkle_private_key | |
;; | |
*) | |
;; | |
esac | |
appcast_patch_name="appcast2-${VERSION}.patch" | |
mv -f ${{ env.SPARKLE_DIR }}/appcast_diff.txt ${{ env.SPARKLE_DIR }}/${appcast_patch_name} | |
echo "appcast-patch-name=${appcast_patch_name}" >> $GITHUB_OUTPUT | |
- name: Upload appcast diff artifact | |
uses: actions/upload-artifact@v4 | |
with: | |
name: ${{ steps.appcast.outputs.appcast-patch-name }} | |
path: ${{ env.SPARKLE_DIR }}/${{ steps.appcast.outputs.appcast-patch-name }} | |
- name: Upload to S3 | |
id: upload | |
env: | |
AWS_ACCESS_KEY_ID: ${{ secrets.TEST_AWS_ACCESS_KEY_ID_RELEASE_S3 }} | |
AWS_SECRET_ACCESS_KEY: ${{ secrets.TEST_AWS_SECRET_ACCESS_KEY_RELEASE_S3 }} | |
AWS_DEFAULT_REGION: ${{ vars.TEST_AWS_DEFAULT_REGION }} | |
RELEASE_TYPE: ${{ github.event.inputs.release-type }} | |
VERSION: ${{ steps.verify-tag.outputs.release-version }} | |
run: | | |
# Back up existing appcast2.xml | |
OLD_APPCAST_NAME=appcast2_old.xml | |
echo "OLD_APPCAST_NAME=${OLD_APPCAST_NAME}" >> $GITHUB_ENV | |
curl -fLSs "${{ vars.TEST_DMG_URL_ROOT }}appcast2.xml" --output "${OLD_APPCAST_NAME}" | |
# Upload files to S3 | |
if [[ "${RELEASE_TYPE}" == "internal" ]]; then | |
./scripts/upload_to_s3/upload_to_s3.sh --run --force | |
else | |
./scripts/upload_to_s3/upload_to_s3.sh --run --force --overwrite-duckduckgo-dmg "${VERSION}" | |
fi | |
if [[ -f "${{ env.SPARKLE_DIR }}/uploaded_files_list.txt" ]]; then | |
echo "FILES_UPLOADED=$(awk '{ print "<li><code>"$1"</code></li>"; }' < ${{ env.SPARKLE_DIR }}/uploaded_files_list.txt | tr '\n' ' ')" >> $GITHUB_ENV | |
else | |
echo "FILES_UPLOADED='No files uploaded.'" >> $GITHUB_ENV | |
fi | |
- name: Set common environment variables | |
if: always() | |
env: | |
DMG_NAME: ${{ steps.fetch-dmg.outputs.dmg-name }} | |
run: | | |
echo "APPCAST_PATCH_NAME=${{ steps.appcast.outputs.appcast-patch-name }}" >> $GITHUB_ENV | |
echo "DMG_NAME=${DMG_NAME}" >> $GITHUB_ENV | |
echo "DMG_URL=${{ vars.DMG_URL_ROOT }}${DMG_NAME}" >> $GITHUB_ENV | |
echo "RELEASE_BUCKET_NAME=${{ vars.TEST_RELEASE_BUCKET_NAME }}" >> $GITHUB_ENV | |
echo "RELEASE_BUCKET_PREFIX=${{ vars.TEST_RELEASE_BUCKET_PREFIX }}" >> $GITHUB_ENV | |
echo "RELEASE_TASK_ID=${{ steps.task-id.outputs.task-id }}" >> $GITHUB_ENV | |
echo "TAG=${{ github.event.inputs.tag }}" >> $GITHUB_ENV | |
echo "VERSION=${{ steps.verify-tag.outputs.release-version }}" >> $GITHUB_ENV | |
echo "WORKFLOW_URL=https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" >> $GITHUB_ENV | |
- name: Set up Asana templates | |
if: always() | |
id: asana-templates | |
env: | |
RELEASE_TYPE: ${{ github.event.inputs.release-type }} | |
run: | | |
if [[ ${{ steps.upload.outcome }} == "success" ]]; then | |
if [[ "${RELEASE_TYPE}" == "internal" ]]; then | |
echo "task-template=validate-check-for-updates-internal" >> $GITHUB_OUTPUT | |
echo "comment-template=validate-check-for-updates-internal" >> $GITHUB_OUTPUT | |
echo "release-task-comment-template=internal-release-complete" >> $GITHUB_OUTPUT | |
else | |
echo "task-template=validate-check-for-updates-public" >> $GITHUB_OUTPUT | |
echo "comment-template=validate-check-for-updates-public" >> $GITHUB_OUTPUT | |
echo "release-task-comment-template=public-release-complete" >> $GITHUB_OUTPUT | |
fi | |
else | |
echo "task-template=appcast-failed-${RELEASE_TYPE}" >> $GITHUB_OUTPUT | |
echo "comment-template=appcast-failed-${RELEASE_TYPE}" >> $GITHUB_OUTPUT | |
fi | |
- name: Create Asana task | |
id: create-task | |
if: always() | |
uses: ./.github/actions/asana-create-action-item | |
with: | |
access-token: ${{ secrets.ASANA_ACCESS_TOKEN }} | |
release-task-url: ${{ github.event.inputs.asana-task-url }} | |
template-name: ${{ steps.asana-templates.outputs.task-template }} | |
- name: Upload patch to the Asana task | |
id: upload-patch | |
if: success() | |
uses: ./.github/actions/asana-upload | |
with: | |
access-token: ${{ secrets.ASANA_ACCESS_TOKEN }} | |
file-name: ${{ env.SPARKLE_DIR }}/${{ steps.appcast.outputs.appcast-patch-name }} | |
task-id: ${{ steps.create-task.outputs.new-task-id }} | |
- name: Upload old appcast file to the Asana task | |
id: upload-old-appcast | |
if: success() | |
uses: ./.github/actions/asana-upload | |
with: | |
access-token: ${{ secrets.ASANA_ACCESS_TOKEN }} | |
file-name: ${{ env.OLD_APPCAST_NAME }} | |
task-id: ${{ steps.create-task.outputs.new-task-id }} | |
- name: Upload release notes to the Asana task | |
id: upload-release-notes | |
if: success() | |
uses: ./.github/actions/asana-upload | |
with: | |
access-token: ${{ secrets.ASANA_ACCESS_TOKEN }} | |
file-name: ${{ env.RELEASE_NOTES_FILE }} | |
task-id: ${{ steps.create-task.outputs.new-task-id }} | |
- name: Report status | |
if: always() | |
uses: ./.github/actions/asana-log-message | |
env: | |
ASSIGNEE_ID: ${{ steps.create-task.outputs.assignee-id }} | |
TASK_ID: ${{ steps.create-task.outputs.new-task-id }} | |
with: | |
access-token: ${{ secrets.ASANA_ACCESS_TOKEN }} | |
task-url: ${{ github.event.inputs.asana-task-url }} | |
template-name: ${{ steps.asana-templates.outputs.comment-template }} | |
- name: Add a comment to the release task | |
if: success() | |
uses: ./.github/actions/asana-add-comment | |
with: | |
access-token: ${{ secrets.ASANA_ACCESS_TOKEN }} | |
task-url: ${{ github.event.inputs.asana-task-url }} | |
template-name: ${{ steps.asana-templates.outputs.release-task-comment-template }} |