Browse Source
* Add GitHub Actions workflow for publishing mobile releases * Fix version comparison logic in GitHub Actions workflow for mobile release * Add artifact name composition step in GitHub Actions workflow * Fix version comparison logic in GitHub Actions workflow for mobile release * Refactor GitHub Actions workflow to enhance version handling and project type input * Enhance version release logging to include version name in GitHub Actions workflow * Update GitHub release command to publish the release * Update .github/workflows/_publish-mobile-github-release.yml Co-authored-by: Álison Fernandes <vvolkgang@users.noreply.github.com> * Update .github/workflows/_publish-mobile-github-release.yml Co-authored-by: Álison Fernandes <vvolkgang@users.noreply.github.com> * fix workflow linter --------- Co-authored-by: Álison Fernandes <vvolkgang@users.noreply.github.com>testing-branch
1 changed files with 287 additions and 0 deletions
@ -0,0 +1,287 @@
@@ -0,0 +1,287 @@
|
||||
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 |
||||
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 "::warning::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_NAME" ] && [ "$LATEST_DRAFT_VERSION_NUMBER" == "$STORE_VERSION_NUMBER" ]; then |
||||
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: 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 |
||||
Loading…
Reference in new issue