-
Notifications
You must be signed in to change notification settings - Fork 13
[BRE-769] Add GitHub Actions workflow for publishing mobile releases #401
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
michalchecinski
wants to merge
9
commits into
main
Choose a base branch
from
BRE-769-Use-Fastlane-to-keep-github-releases-in-sync-with-mobile-deploy-versions
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+288
โ0
Open
Changes from 6 commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
87f1a2b
Add GitHub Actions workflow for publishing mobile releases
michalchecinski ed033a3
Fix version comparison logic in GitHub Actions workflow for mobile reโฆ
michalchecinski 21f73a1
Add artifact name composition step in GitHub Actions workflow
michalchecinski 6f17163
Fix version comparison logic in GitHub Actions workflow for mobile reโฆ
michalchecinski ed01ba0
Refactor GitHub Actions workflow to enhance version handling and projโฆ
michalchecinski e5e98ee
Enhance version release logging to include version name in GitHub Actโฆ
michalchecinski 7e9be01
Update GitHub release command to publish the release
michalchecinski 64c5ea1
Update .github/workflows/_publish-mobile-github-release.yml
michalchecinski cef0b23
Update .github/workflows/_publish-mobile-github-release.yml
michalchecinski File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,288 @@ | ||
name: _publish-mobile-github-release | ||
|
||
on: | ||
workflow_call: | ||
inputs: | ||
release_name: | ||
description: 'Name prefix of the release to publish (e.g. "Password Manager")' | ||
type: string | ||
default: "" | ||
workflow_name: | ||
description: 'Name of the workflow to check for previous runs (e.g. publish-github-release.yml)' | ||
type: string | ||
required: true | ||
credentials_filename: | ||
description: 'Name of the credentials file to download from Azure Blob Storage (e.g. "google-play-credentials.json")' | ||
type: string | ||
required: true | ||
check_release_command: | ||
description: > | ||
Shell command to check if a release is already published. | ||
Use $CREDENTIALS_PATH for the path to credentials file. | ||
Example: 'bundle exec fastlane android getLatestVersion serviceCredentialsFile:$CREDENTIALS_PATH' | ||
type: string | ||
required: true | ||
project_type: | ||
description: 'Type of the project (e.g. "android" or "ios")' | ||
type: string | ||
default: "android" | ||
required: true | ||
|
||
|
||
|
||
jobs: | ||
publish-release: | ||
name: Publish GitHub Release ${{ inputs.release_name }} | ||
runs-on: ubuntu-24.04 | ||
permissions: | ||
contents: write | ||
actions: read | ||
|
||
steps: | ||
- name: Check out repository | ||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | ||
with: | ||
fetch-depth: 0 | ||
|
||
- name: Get latest draft name | ||
id: get_latest_draft | ||
env: | ||
GITHUB_TOKEN: ${{ github.token }} | ||
run: | | ||
latest_release=$(gh release list --json name,tagName,isDraft,isPrerelease -L 10 --jq 'first(.[] | select((.name | test("${{ inputs.release_name }}"; "i")) and (.isDraft == true)))') | ||
|
||
is_latest_draft="false" | ||
|
||
if [ "$latest_release" != "null" ] && [ -n "$latest_release" ]; then | ||
is_latest_draft=$(jq -r '.isDraft' <<< $latest_release) | ||
fi | ||
|
||
echo "is_latest_draft=$is_latest_draft" >> $GITHUB_OUTPUT | ||
|
||
if [ "$is_latest_draft" != "true" ]; then | ||
echo "No draft found" | ||
exit 0 | ||
fi | ||
|
||
latest_draft_version_name=$(echo "$latest_release" | jq -r '.name' | grep -oE '[0-9]+\.[0-9]+\.[0-9]+') | ||
echo "latest_draft_version_name=$latest_draft_version_name" >> $GITHUB_OUTPUT | ||
|
||
latest_draft_version_number=$(echo "$latest_release" | jq -r '.name' | grep -oE '\([0-9]+\)' | sed 's/[()]//g') | ||
echo "latest_draft_version_number=$latest_draft_version_number" >> $GITHUB_OUTPUT | ||
|
||
latest_draft_name=$(jq -r '.name' <<< $latest_release) | ||
echo "latest_draft_name=$latest_draft_name" >> $GITHUB_OUTPUT | ||
|
||
# Retrieve the previous run ID and run state to determine the status of the last workflow execution. | ||
# This is done to prevent the workflow from publishing a release that was already published, | ||
# but then deleted and reverted to draft for any reason. | ||
# It ensures the workflow does not process the same release multiple times if it was reverted. | ||
- name: Get previous run ID | ||
id: get_previous_run | ||
env: | ||
GH_TOKEN: ${{ github.token }} | ||
WORKFLOW_NAME: ${{ inputs.workflow_name }} | ||
run: | | ||
previous_run_id=$(gh run list --workflow=$WORKFLOW_NAME --status=success --limit 1 --json databaseId --jq '.[0].databaseId // empty') | ||
|
||
if [ -n "$previous_run_id" ] && [ "$previous_run_id" != "null" ]; then | ||
echo "Found previous successful scheduled run: $previous_run_id" | ||
echo "previous_run_id=$previous_run_id" >> $GITHUB_OUTPUT | ||
echo "has_previous_run=true" >> $GITHUB_OUTPUT | ||
else | ||
echo "No previous successful scheduled run found" | ||
echo "has_previous_run=false" >> $GITHUB_OUTPUT | ||
fi | ||
|
||
- name: Compose artifact name | ||
id: compose_artifact_name | ||
run: | | ||
artifact_name=$(echo "release-info-${{ inputs.release_name }}" | tr '[:upper:]' '[:lower:]' | sed 's/ /-/g') | ||
echo "artifact_name=$artifact_name" >> $GITHUB_OUTPUT | ||
|
||
- name: Download previous run state | ||
id: previous_state | ||
if: steps.get_previous_run.outputs.has_previous_run == 'true' | ||
continue-on-error: true | ||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 | ||
with: | ||
name: ${{ steps.compose_artifact_name.outputs.artifact_name }} | ||
run-id: ${{ steps.get_previous_run.outputs.previous_run_id }} | ||
github-token: ${{ github.token }} | ||
|
||
- name: Parse previous state | ||
id: parse_previous_state | ||
if: steps.get_previous_run.outputs.has_previous_run == 'true' | ||
run: | | ||
if [ -f "release-info.json" ]; then | ||
|
||
previous_release_tag=$(jq -r '.release_tag // empty' release-info.json) | ||
previous_initial_state=$(jq -r '.initial_state // empty' release-info.json) | ||
previous_changed_to=$(jq -r '.changed_to_state // empty' release-info.json) | ||
|
||
echo "previous_release_tag=$previous_release_tag" >> $GITHUB_OUTPUT | ||
echo "previous_initial_state=$previous_initial_state" >> $GITHUB_OUTPUT | ||
echo "previous_changed_to=$previous_changed_to" >> $GITHUB_OUTPUT | ||
echo "previous_timestamp=$previous_timestamp" >> $GITHUB_OUTPUT | ||
echo "has_previous_state=true" >> $GITHUB_OUTPUT | ||
|
||
echo "Previous run processed: $previous_release_tag (changed from: $previous_initial_state to: $previous_changed_to)" | ||
else | ||
echo "::error:: No valid release-info.json found in previous artifact" | ||
echo "has_previous_state=false" >> $GITHUB_OUTPUT | ||
fi | ||
|
||
- name: Check if release was already processed | ||
id: check_already_processed | ||
env: | ||
CURRENT_RELEASE: ${{ steps.get_latest_draft.outputs.latest_draft_version_name }} | ||
PREVIOUS_RELEASE: ${{ steps.parse_previous_state.outputs.previous_release_tag }} | ||
PREVIOUS_INITIAL_STATE: ${{ steps.parse_previous_state.outputs.previous_initial_state }} | ||
PREVIOUS_CHANGED_TO: ${{ steps.parse_previous_state.outputs.previous_changed_to }} | ||
HAS_PREVIOUS_STATE: ${{ steps.parse_previous_state.outputs.has_previous_state }} | ||
run: | | ||
should_skip=false | ||
|
||
if [ "$HAS_PREVIOUS_STATE" == "true" ] && [ "$PREVIOUS_RELEASE" != "" ] && [ "$CURRENT_RELEASE" == "$PREVIOUS_RELEASE" ]; then | ||
if [ "$PREVIOUS_CHANGED_TO" == "published" ] || ([ "$PREVIOUS_INITIAL_STATE" == "published" ] && [ "$PREVIOUS_CHANGED_TO" == "none" ]); then | ||
echo "::error:: Release $CURRENT_RELEASE was already processed and published by this workflow" | ||
echo "This suggests the release was manually reverted to draft after being published" | ||
echo "Skipping to prevent duplicate processing" | ||
|
||
echo "## ::error:: Workflow Skipped" >> $GITHUB_STEP_SUMMARY | ||
echo "Release \`$CURRENT_RELEASE\` was already processed by this workflow." >> $GITHUB_STEP_SUMMARY | ||
echo "To force reprocessing, either:" >> $GITHUB_STEP_SUMMARY | ||
echo "- Use manual workflow dispatch" >> $GITHUB_STEP_SUMMARY | ||
echo "- Create a new release version" >> $GITHUB_STEP_SUMMARY | ||
|
||
should_skip=true | ||
fi | ||
fi | ||
|
||
echo "should_skip=$should_skip" >> $GITHUB_OUTPUT | ||
|
||
- name: Log in to Azure | ||
if: steps.check_already_processed.outputs.should_skip == 'false' | ||
uses: Azure/login@cb79c773a3cfa27f31f25eb3f677781210c9ce3d # v1.6.1 | ||
with: | ||
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} | ||
|
||
- name: Configure Ruby | ||
if: steps.check_already_processed.outputs.should_skip == 'false' | ||
uses: ruby/setup-ruby@ca041f971d66735f3e5ff1e21cc13e2d51e7e535 # v1.233.0 | ||
with: | ||
bundler-cache: true | ||
|
||
- name: Install Fastlane | ||
if: steps.check_already_processed.outputs.should_skip == 'false' | ||
run: | | ||
gem install bundler:2.2.27 | ||
|
||
- name: Download Store credentials | ||
if: steps.check_already_processed.outputs.should_skip == 'false' | ||
env: | ||
ACCOUNT_NAME: bitwardenci | ||
CONTAINER_NAME: mobile | ||
CREDENTIALS_FILE_NAME: ${{ inputs.credentials_filename }} | ||
run: | | ||
mkdir -p ${{ github.workspace }}/secrets | ||
|
||
az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME \ | ||
--name $CREDENTIALS_FILE_NAME --file ${{ github.workspace }}/secrets/$CREDENTIALS_FILE_NAME --output none | ||
|
||
- name: Get store versions | ||
if: steps.check_already_processed.outputs.should_skip == 'false' && inputs.check_release_command != '' | ||
id: get_store_versions | ||
env: | ||
CREDENTIALS_PATH: ${{ github.workspace }}/secrets/${{ inputs.credentials_filename }} | ||
run: | | ||
echo "Running custom release check command..." | ||
echo "Command: ${{ inputs.check_release_command }}" | ||
|
||
OUTPUT=$(eval "${{ inputs.check_release_command }}") | ||
|
||
version_name=$(echo "$OUTPUT" | grep 'version_name: ' | cut -d' ' -f3) | ||
version_number=$(echo "$OUTPUT" | grep 'version_number: ' | cut -d' ' -f3) | ||
|
||
echo "store_version_name=$version_name" >> $GITHUB_OUTPUT | ||
echo "store_version_number=$version_number" >> $GITHUB_OUTPUT | ||
|
||
- name: Check if version is already released | ||
if: steps.check_already_processed.outputs.should_skip == 'false' | ||
id: check_version | ||
env: | ||
LATEST_DRAFT_VERSION_NAME: ${{ steps.get_latest_draft.outputs.latest_draft_version_name }} | ||
LATEST_DRAFT_VERSION_NUMBER: ${{ steps.get_latest_draft.outputs.latest_draft_version_number }} | ||
STORE_VERSION_NAME: ${{ steps.get_store_versions.outputs.store_version_name }} | ||
STORE_VERSION_NUMBER: ${{ steps.get_store_versions.outputs.store_version_number }} | ||
run: | | ||
if [ "${{ inputs.project_type }}" == "ios" ]; then | ||
if [ "$LATEST_DRAFT_VERSION_NAME" == "$STORE_VERSION_NUMBER" ] && [ "$LATEST_DRAFT_VERSION_NUMBER" == "$STORE_VERSION_NUMBER" ]; then | ||
michalchecinski marked this conversation as resolved.
Show resolved
Hide resolved
|
||
echo "iOS: Version name $LATEST_DRAFT_VERSION_NAME and version number $LATEST_DRAFT_VERSION_NUMBER is already released on store" | ||
echo "version_released=true" >> $GITHUB_OUTPUT | ||
else | ||
echo "iOS: Version $LATEST_DRAFT_VERSION_NAME ($LATEST_DRAFT_VERSION_NUMBER) is not released on store. Latest version in the store is $STORE_VERSION_NAME ($STORE_VERSION_NUMBER)" | ||
echo "version_released=false" >> $GITHUB_OUTPUT | ||
fi | ||
else | ||
if [ "$LATEST_DRAFT_VERSION_NUMBER" == "$STORE_VERSION_NUMBER" ]; then | ||
echo "Version $LATEST_DRAFT_VERSION_NUMBER is already released on store" | ||
echo "version_released=true" >> $GITHUB_OUTPUT | ||
else | ||
echo "Version $LATEST_DRAFT_VERSION_NUMBER is not released on store. Latest version in the store is $STORE_VERSION_NUMBER, with version name: $STORE_VERSION_NAME" | ||
echo "version_released=false" >> $GITHUB_OUTPUT | ||
fi | ||
fi | ||
|
||
- name: Make GitHub release latest and non-pre-release | ||
if: steps.check_version.outputs.version_released == 'true' | ||
env: | ||
TAG: ${{ steps.get_latest_draft.outputs.latest_draft_version_name }} | ||
GH_TOKEN: ${{ github.token }} | ||
run: echo "Publish" # gh release edit $TAG --prerelease=false --latest --draft=false | ||
|
||
- name: Create workflow state artifact | ||
run: | | ||
if [ -f "release-info.json" ]; then | ||
echo "release-info.json already exists, removing it" | ||
rm -f release-info.json | ||
fi | ||
|
||
if [ "${{ steps.get_latest_draft.outputs.is_latest_draft }}" == "true" ]; then | ||
release_tag="${{ steps.get_latest_draft.outputs.latest_draft_version_name }}" | ||
else | ||
release_tag="${{ steps.parse_previous_state.outputs.previous_release_tag }}" | ||
fi | ||
|
||
if [ "${{ steps.check_already_processed.outputs.should_skip }}" == "true" ]; then | ||
initial_state="draft" | ||
changed_to_state="none" | ||
elif [ "${{ steps.get_latest_draft.outputs.is_latest_draft }}" == "true" ] && [ "${{ steps.check_already_processed.outputs.should_skip }}" == "false" ]; then | ||
initial_state="draft" | ||
if [ "${{ steps.check_version.outputs.version_released }}" == "true" ]; then | ||
changed_to_state="published" | ||
else | ||
changed_to_state="none" | ||
fi | ||
elif [ "${{ steps.get_latest_draft.outputs.is_latest_draft }}" == "false" ]; then | ||
initial_state="published" | ||
changed_to_state="none" | ||
fi | ||
|
||
json=$(jq -n \ | ||
--arg release_tag "$release_tag" \ | ||
--arg initial_state "$initial_state" \ | ||
--arg changed_to_state "$changed_to_state" \ | ||
'{release_tag: $release_tag, initial_state: $initial_state, changed_to_state: $changed_to_state}') | ||
|
||
echo "$json" > release-info.json | ||
|
||
echo '```json' >> $GITHUB_STEP_SUMMARY | ||
echo "$json" >> $GITHUB_STEP_SUMMARY | ||
echo '```' >> $GITHUB_STEP_SUMMARY | ||
|
||
- name: Upload workflow state artifact | ||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | ||
with: | ||
name: ${{ steps.compose_artifact_name.outputs.artifact_name }} | ||
path: release-info.json |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.