Skip to content

feat: update changelog for version 1.1.48, fix Google Maps short-link… #107

feat: update changelog for version 1.1.48, fix Google Maps short-link…

feat: update changelog for version 1.1.48, fix Google Maps short-link… #107

Workflow file for this run

name: Self-Hosted Mobile Build & Submit
on:
push:
branches: [main]
paths-ignore:
- "**/*.md"
- "documents/**"
- "logs/**"
workflow_dispatch:
concurrency:
group: self-hosted-mobile-${{ github.ref }}
cancel-in-progress: ${{ github.event_name != 'workflow_dispatch' }}
jobs:
build:
name: CI checks + Local Build (ios + android + wear)
runs-on: [self-hosted, macOS, eclipse-timer]
env:
GOOGLE_MAPS_ANDROID_API_KEY: ${{ secrets.GOOGLE_MAPS_ANDROID_API_KEY }}
SENTRY_DISABLE_AUTO_UPLOAD: "true"
SENTRY_DISABLE_XCODE_DEBUG_UPLOAD: "true"
SENTRY_ALLOW_FAILURE: "true"
SENTRY_CLI_EXECUTABLE: "/usr/bin/true"
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile --prefer-offline
- name: Typecheck
run: pnpm typecheck
- name: Lint
run: pnpm lint
- name: Test
run: pnpm test
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "17"
cache: gradle
- name: Setup Android SDK tools
uses: android-actions/setup-android@v3
- name: Install Android SDK packages
run: |
set -euo pipefail
sdk_root=""
for candidate in \
"${ANDROID_SDK_ROOT:-}" \
"${ANDROID_HOME:-}" \
"$HOME/Library/Android/sdk" \
"$HOME/Android/Sdk" \
"/usr/local/share/android-sdk" \
"/opt/homebrew/share/android-sdk"
do
if [ -n "$candidate" ] && [ -d "$candidate" ]; then
sdk_root="$candidate"
break
fi
done
if [ ! -d "$sdk_root" ]; then
echo "Android SDK directory not found."
echo "Install Android SDK on the runner or set ANDROID_SDK_ROOT/ANDROID_HOME."
exit 1
fi
missing_packages=()
[ -d "$sdk_root/platform-tools" ] || missing_packages+=("platform-tools")
[ -d "$sdk_root/platforms/android-36" ] || missing_packages+=("platforms;android-36")
[ -d "$sdk_root/build-tools/36.0.0" ] || missing_packages+=("build-tools;36.0.0")
[ -d "$sdk_root/ndk/27.1.12297006" ] || missing_packages+=("ndk;27.1.12297006")
if [ "${#missing_packages[@]}" -eq 0 ]; then
echo "All required Android SDK packages are already installed in $sdk_root."
exit 0
fi
echo "Installing missing Android SDK packages: ${missing_packages[*]}"
yes | sdkmanager --licenses >/dev/null || true
sdkmanager "${missing_packages[@]}"
- name: Setup EAS CLI
uses: expo/expo-github-action@v8
with:
eas-version: latest
token: ${{ secrets.EXPO_TOKEN }}
packager: pnpm
- name: Validate Google Maps Android key
run: |
if [ -z "$GOOGLE_MAPS_ANDROID_API_KEY" ]; then
echo "Missing GOOGLE_MAPS_ANDROID_API_KEY GitHub secret."
echo "Add it in: Settings -> Secrets and variables -> Actions."
exit 1
fi
- name: Build iOS and Android (AAB + APK + Wear AAB + Wear APK)
working-directory: apps/mobile
env:
EAS_LOCAL_BUILD_ARTIFACTS_DIR: ${{ github.workspace }}/artifacts/raw
SENTRY_DISABLE_AUTO_UPLOAD: "true"
SENTRY_DISABLE_XCODE_DEBUG_UPLOAD: "true"
SENTRY_ALLOW_FAILURE: "true"
SENTRY_CLI_EXECUTABLE: "/usr/bin/true"
run: |
set -euo pipefail
pnpm exec eas build --profile production --platform ios --local --non-interactive
sdk_root=""
for candidate in \
"${ANDROID_SDK_ROOT:-}" \
"${ANDROID_HOME:-}" \
"$HOME/Library/Android/sdk" \
"$HOME/Android/Sdk" \
"/usr/local/share/android-sdk" \
"/opt/homebrew/share/android-sdk"
do
if [ -n "$candidate" ] && [ -d "$candidate" ]; then
sdk_root="$candidate"
break
fi
done
if [ ! -d "$sdk_root" ]; then
echo "Android SDK directory not found."
echo "Install Android SDK on the runner or set ANDROID_SDK_ROOT/ANDROID_HOME."
exit 1
fi
export ANDROID_SDK_ROOT="$sdk_root"
export ANDROID_HOME="$sdk_root"
escaped_sdk_root="${sdk_root// /\\ }"
printf 'sdk.dir=%s\n' "$escaped_sdk_root" > android/local.properties
echo "Using Android SDK at $sdk_root"
pnpm exec eas build --profile production --platform android --local --non-interactive
pnpm exec eas build --profile production-apk --platform android --local --non-interactive
pnpm exec eas build --profile production-wear --platform android --local --non-interactive
pnpm exec eas build --profile production-wear-apk --platform android --local --non-interactive
- name: Collect build artifacts
run: |
set -euo pipefail
mkdir -p artifacts/submission
version="$(node -p "JSON.parse(require('fs').readFileSync('apps/mobile/package.json', 'utf8')).version")"
artifact_prefix="eclipse-timer-v${version}"
ios_asset_name="${artifact_prefix}.ipa"
android_aab_asset_name="${artifact_prefix}.aab"
android_apk_asset_name="${artifact_prefix}.apk"
wear_aab_asset_name="${artifact_prefix}-wear.aab"
wear_apk_asset_name="${artifact_prefix}-wear.apk"
ios_artifact="$(find artifacts/raw -type f -name '*.ipa' | head -n 1 || true)"
if [ -z "$ios_artifact" ]; then
echo "Missing iOS artifact (.ipa) in artifacts/raw."
exit 1
fi
cp "$ios_artifact" "artifacts/submission/$ios_asset_name"
aab_artifacts=()
while IFS= read -r artifact; do
aab_artifacts+=("$artifact")
done < <(find artifacts/raw -type f -name '*.aab' | sort)
if [ "${#aab_artifacts[@]}" -eq 0 ]; then
echo "Missing Android artifacts (.aab) in artifacts/raw."
exit 1
fi
android_aab_artifact="$(printf '%s\n' "${aab_artifacts[@]}" | grep -iv 'wear' | head -n 1 || true)"
if [ -z "$android_aab_artifact" ]; then
android_aab_artifact="${aab_artifacts[0]}"
fi
if [ -z "$android_aab_artifact" ]; then
echo "Missing Android artifact (.aab) in artifacts/raw."
exit 1
fi
cp "$android_aab_artifact" "artifacts/submission/$android_aab_asset_name"
wear_aab_artifact="$(find artifacts/raw -type f -name 'wear-release.aab' | head -n 1 || true)"
if [ -z "$wear_aab_artifact" ]; then
wear_aab_artifact="$(printf '%s\n' "${aab_artifacts[@]}" | grep -i 'wear' | head -n 1 || true)"
fi
if [ -z "$wear_aab_artifact" ] && [ "${#aab_artifacts[@]}" -ge 2 ]; then
wear_aab_artifact="${aab_artifacts[$((${#aab_artifacts[@]} - 1))]}"
fi
if [ -z "$wear_aab_artifact" ] || [ "$wear_aab_artifact" = "$android_aab_artifact" ]; then
echo "Missing Wear OS artifact (.aab) in artifacts/raw."
exit 1
fi
cp "$wear_aab_artifact" "artifacts/submission/$wear_aab_asset_name"
apk_artifacts=()
while IFS= read -r artifact; do
apk_artifacts+=("$artifact")
done < <(find artifacts/raw -type f -name '*.apk' | sort)
if [ "${#apk_artifacts[@]}" -eq 0 ]; then
echo "Missing Android artifacts (.apk) in artifacts/raw."
exit 1
fi
android_apk_artifact="$(printf '%s\n' "${apk_artifacts[@]}" | grep -iv 'wear' | head -n 1 || true)"
if [ -z "$android_apk_artifact" ]; then
android_apk_artifact="${apk_artifacts[0]}"
fi
if [ -z "$android_apk_artifact" ]; then
echo "Missing Android artifact (.apk) in artifacts/raw."
exit 1
fi
cp "$android_apk_artifact" "artifacts/submission/$android_apk_asset_name"
wear_apk_artifact="$(find artifacts/raw -type f -name 'wear-release.apk' | head -n 1 || true)"
if [ -z "$wear_apk_artifact" ]; then
wear_apk_artifact="$(printf '%s\n' "${apk_artifacts[@]}" | grep -i 'wear' | head -n 1 || true)"
fi
if [ -z "$wear_apk_artifact" ] && [ "${#apk_artifacts[@]}" -ge 2 ]; then
wear_apk_artifact="${apk_artifacts[$((${#apk_artifacts[@]} - 1))]}"
fi
if [ -z "$wear_apk_artifact" ] || [ "$wear_apk_artifact" = "$android_apk_artifact" ]; then
echo "Missing Wear OS artifact (.apk) in artifacts/raw."
exit 1
fi
cp "$wear_apk_artifact" "artifacts/submission/$wear_apk_asset_name"
ls -lah artifacts/submission
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: local-mobile-builds-${{ github.run_id }}
path: artifacts/submission
if-no-files-found: error
release_gate:
name: Check mobile version change
runs-on: [self-hosted, macOS, eclipse-timer]
outputs:
should_submit: ${{ steps.gate.outputs.should_submit }}
current_version: ${{ steps.gate.outputs.current_version }}
previous_version: ${{ steps.gate.outputs.previous_version }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 1
fetch-tags: true
- name: Determine submit eligibility
id: gate
shell: bash
run: |
set -euo pipefail
current_version="$(node -p "JSON.parse(require('fs').readFileSync('apps/mobile/package.json', 'utf8')).version")"
latest_tag="$(git tag -l 'v[0-9]*.[0-9]*.[0-9]*' --sort=-v:refname | head -n 1 || true)"
previous_version=""
if [ -n "$latest_tag" ]; then
previous_version="${latest_tag#v}"
fi
comparison_result="$(
CURRENT_VERSION="$current_version" PREVIOUS_VERSION="$previous_version" node - <<'NODE'
const semver = /^\d+\.\d+\.\d+$/;
const currentVersion = process.env.CURRENT_VERSION || "";
const previousVersion = process.env.PREVIOUS_VERSION || "";
const fail = (message) => {
console.error(message);
process.exit(1);
};
if (!semver.test(currentVersion)) {
fail(`apps/mobile/package.json version must be x.y.z. Found: ${currentVersion}`);
}
if (!previousVersion) {
process.stdout.write("first_release");
process.exit(0);
}
if (!semver.test(previousVersion)) {
fail(`Latest release tag must use format vX.Y.Z. Found: v${previousVersion}`);
}
const parse = (value) => value.split(".").map((part) => Number(part));
const compare = (left, right) => left[0] - right[0] || left[1] - right[1] || left[2] - right[2];
const versionComparison = compare(parse(currentVersion), parse(previousVersion));
if (versionComparison > 0) {
process.stdout.write("incremented");
} else if (versionComparison === 0) {
process.stdout.write("unchanged");
} else {
process.stdout.write("decremented");
}
NODE
)"
should_submit="false"
if [ "$comparison_result" = "first_release" ] || [ "$comparison_result" = "incremented" ]; then
should_submit="true"
fi
echo "current_version=$current_version" >> "$GITHUB_OUTPUT"
echo "previous_version=$previous_version" >> "$GITHUB_OUTPUT"
echo "should_submit=$should_submit" >> "$GITHUB_OUTPUT"
if [ "$should_submit" = "true" ]; then
if [ "$comparison_result" = "first_release" ]; then
echo "No previous mobile release tag found."
echo "Submit job will run for both iOS and Android."
else
echo "Detected mobile version increment: $previous_version -> $current_version"
echo "Submit job will run for both iOS and Android."
fi
elif [ "$comparison_result" = "unchanged" ]; then
echo "Mobile version unchanged from latest release tag: $current_version"
echo "Submit job will be skipped."
elif [ "$comparison_result" = "decremented" ]; then
echo "Mobile version $current_version is lower than latest release tag $previous_version."
echo "Submit job will be skipped."
else
echo "Unable to determine mobile release eligibility."
echo "Submit job will be skipped."
fi
submit:
name: Submit to stores
needs: [build, release_gate]
if: ${{ needs.release_gate.outputs.should_submit == 'true' }}
runs-on: [self-hosted, macOS, eclipse-timer]
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 1
fetch-tags: true
- name: Validate release version bump
id: release_meta
shell: bash
run: |
set -euo pipefail
latest_tag="$(git tag -l 'v[0-9]*.[0-9]*.[0-9]*' --sort=-v:refname | head -n 1 || true)"
LATEST_TAG="$latest_tag" node - <<'NODE'
const fs = require('fs');
const app = JSON.parse(fs.readFileSync('apps/mobile/app.json', 'utf8'));
const pkg = JSON.parse(fs.readFileSync('apps/mobile/package.json', 'utf8'));
const latestTag = process.env.LATEST_TAG || '';
const packageVersion = pkg.version;
const semver = /^\d+\.\d+\.\d+$/;
const fail = (message) => {
console.error(message);
process.exit(1);
};
if (typeof packageVersion !== 'string' || !semver.test(packageVersion)) {
fail(`apps/mobile/package.json version must be x.y.z. Found: ${packageVersion}`);
}
if (app?.expo?.version !== undefined) {
fail(
`apps/mobile/app.json expo.version must be omitted. Version is sourced from apps/mobile/package.json via apps/mobile/app.config.ts.`
);
}
if (app?.expo?.runtimeVersion !== undefined) {
fail(
`apps/mobile/app.json expo.runtimeVersion must be omitted. Runtime version is derived from apps/mobile/package.json via apps/mobile/app.config.ts.`
);
}
const parse = (value) => value.split('.').map((part) => Number(part));
const compare = (left, right) => left[0] - right[0] || left[1] - right[1] || left[2] - right[2];
const currentVersion = packageVersion;
const latestVersion = latestTag.replace(/^v/, '');
if (latestTag) {
if (!semver.test(latestVersion)) {
fail(`Latest release tag must use format vX.Y.Z. Found: ${latestTag}`);
}
if (compare(parse(currentVersion), parse(latestVersion)) <= 0) {
fail(
`Release version must be incremented. Current version ${currentVersion} must be greater than latest tag ${latestTag}.`
);
}
}
const outputPath = process.env.GITHUB_OUTPUT;
if (!outputPath) {
fail('GITHUB_OUTPUT is not set.');
}
fs.appendFileSync(outputPath, `version=${currentVersion}\n`);
fs.appendFileSync(outputPath, `previous_version=${latestVersion}\n`);
fs.appendFileSync(outputPath, `tag_name=v${currentVersion}\n`);
fs.appendFileSync(outputPath, `release_name=Eclipse Timer v${currentVersion}\n`);
const comparisonInfo = latestTag ? ` > ${latestTag}` : ' (no prior release tag found)';
console.log(`Release version validated: v${currentVersion}${comparisonInfo}`);
NODE
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: local-mobile-builds-${{ github.run_id }}
path: artifacts/submission
- name: Resolve release artifact names
id: artifact_names
shell: bash
run: |
set -euo pipefail
version="${{ steps.release_meta.outputs.version }}"
artifact_prefix="eclipse-timer-v${version}"
{
echo "ios=${artifact_prefix}.ipa"
echo "android_aab=${artifact_prefix}.aab"
echo "android_apk=${artifact_prefix}.apk"
echo "wear_aab=${artifact_prefix}-wear.aab"
echo "wear_apk=${artifact_prefix}-wear.apk"
} >> "$GITHUB_OUTPUT"
- name: Validate submission artifacts
run: |
set -euo pipefail
artifact_root="${{ github.workspace }}/artifacts/submission"
ios_asset="${{ steps.artifact_names.outputs.ios }}"
android_aab_asset="${{ steps.artifact_names.outputs.android_aab }}"
android_apk_asset="${{ steps.artifact_names.outputs.android_apk }}"
wear_aab_asset="${{ steps.artifact_names.outputs.wear_aab }}"
wear_apk_asset="${{ steps.artifact_names.outputs.wear_apk }}"
if [ ! -f "$artifact_root/$ios_asset" ]; then
echo "Missing iOS artifact at $artifact_root/$ios_asset."
exit 1
fi
if [ ! -f "$artifact_root/$android_aab_asset" ]; then
echo "Missing Android artifact at $artifact_root/$android_aab_asset."
exit 1
fi
if [ ! -f "$artifact_root/$android_apk_asset" ]; then
echo "Missing Android artifact at $artifact_root/$android_apk_asset."
exit 1
fi
if [ ! -f "$artifact_root/$wear_aab_asset" ]; then
echo "Missing Wear OS artifact at $artifact_root/$wear_aab_asset."
exit 1
fi
if [ ! -f "$artifact_root/$wear_apk_asset" ]; then
echo "Missing Wear OS artifact at $artifact_root/$wear_apk_asset."
exit 1
fi
- name: Prepare GitHub release assets
id: release_assets
shell: bash
run: |
set -euo pipefail
{
echo "files<<EOF"
echo "artifacts/submission/${{ steps.artifact_names.outputs.ios }}"
echo "artifacts/submission/${{ steps.artifact_names.outputs.android_aab }}"
echo "artifacts/submission/${{ steps.artifact_names.outputs.android_apk }}"
echo "artifacts/submission/${{ steps.artifact_names.outputs.wear_aab }}"
echo "artifacts/submission/${{ steps.artifact_names.outputs.wear_apk }}"
echo "EOF"
} >> "$GITHUB_OUTPUT"
- name: Prepare GitHub release notes from changelog
id: github_release_notes
shell: bash
run: |
set -euo pipefail
current_version="${{ steps.release_meta.outputs.version }}"
notes_file="artifacts/submission/github-release-notes.md"
CURRENT_VERSION="$current_version" NOTES_FILE="$notes_file" node - <<'NODE'
const fs = require("fs");
const currentVersion = process.env.CURRENT_VERSION ?? "";
const notesFile = process.env.NOTES_FILE ?? "";
const semver = /^\d+\.\d+\.\d+$/;
if (!semver.test(currentVersion)) {
console.error(`Invalid release version: ${currentVersion}`);
process.exit(1);
}
if (!notesFile) {
console.error("Release notes output path is missing.");
process.exit(1);
}
let changelog = "";
try {
changelog = fs.readFileSync("CHANGELOG.md", "utf8");
} catch {
console.error("CHANGELOG.md not found.");
process.exit(1);
}
const escapedVersion = currentVersion.replace(/\./g, "\\.");
const headingRegex = new RegExp(`^## \\[${escapedVersion}\\].*$`, "m");
const headingMatch = changelog.match(headingRegex);
if (!headingMatch || headingMatch.index === undefined) {
console.error(`No CHANGELOG entry found for version ${currentVersion}.`);
process.exit(1);
}
const fromHeading = changelog.slice(headingMatch.index);
const nextHeadingIndex = fromHeading.slice(headingMatch[0].length).search(/\n## \[/);
const notes =
nextHeadingIndex === -1
? fromHeading.trim()
: fromHeading.slice(0, headingMatch[0].length + nextHeadingIndex).trim();
if (!notes) {
console.error(`CHANGELOG entry for version ${currentVersion} is empty.`);
process.exit(1);
}
fs.mkdirSync("artifacts/submission", { recursive: true });
fs.writeFileSync(notesFile, `${notes}\n`);
NODE
echo "body_path=$notes_file" >> "$GITHUB_OUTPUT"
- name: Prepare store release notes from changelog (optional)
id: store_notes
shell: bash
run: |
set -euo pipefail
current_version="${{ steps.release_meta.outputs.version }}"
previous_version="${{ steps.release_meta.outputs.previous_version }}"
notes="$(
CURRENT_VERSION="$current_version" PREVIOUS_VERSION="$previous_version" node - <<'NODE'
const fs = require("fs");
const currentVersion = process.env.CURRENT_VERSION ?? "";
const previousVersion = process.env.PREVIOUS_VERSION ?? "";
const semver = /^\d+\.\d+\.\d+$/;
if (!semver.test(currentVersion)) {
process.exit(0);
}
let changelog = "";
try {
changelog = fs.readFileSync("CHANGELOG.md", "utf8");
} catch {
process.exit(0);
}
if (previousVersion && !semver.test(previousVersion)) {
process.exit(0);
}
const parse = (value) => value.split(".").map((part) => Number(part));
const compare = (left, right) => left[0] - right[0] || left[1] - right[1] || left[2] - right[2];
const entries = [];
const lines = changelog.split(/\r?\n/);
let activeVersion = "";
let activeBody = [];
for (const line of lines) {
const headingMatch = line.match(/^## \[(\d+\.\d+\.\d+)\][^\n]*$/);
if (headingMatch) {
if (activeVersion) {
entries.push({ version: activeVersion, body: activeBody.join("\n") });
}
activeVersion = headingMatch[1];
activeBody = [];
continue;
}
if (activeVersion) {
activeBody.push(line);
}
}
if (activeVersion) {
entries.push({ version: activeVersion, body: activeBody.join("\n") });
}
const currentParsed = parse(currentVersion);
const previousParsed = previousVersion ? parse(previousVersion) : null;
const selectedEntries = entries.filter(({ version }) => {
const versionParsed = parse(version);
if (compare(versionParsed, currentParsed) > 0) {
return false;
}
if (!previousParsed) {
return version === currentVersion;
}
return compare(versionParsed, previousParsed) > 0;
});
const backtickChar = String.fromCharCode(96);
const normalizeBody = (body) =>
body
.split(/\r?\n/)
.map((line) => line.trim())
.filter(Boolean)
.filter((line) => !/^###\s+/i.test(line))
.map((line) => line.replace(/^[-*]\s+/, ""))
.map((line) => line.replace(/\*\*/g, ""))
.map((line) => line.split(backtickChar).join(""))
.map((line) => line.trim())
.filter(Boolean);
const rendered = [];
for (const entry of selectedEntries) {
const normalizedLines = normalizeBody(entry.body);
if (!normalizedLines.length) {
continue;
}
rendered.push(`v${entry.version}`);
for (const line of normalizedLines) {
rendered.push(`- ${line}`);
}
rendered.push("");
}
if (!rendered.length) {
process.exit(0);
}
process.stdout.write(rendered.join("\n").trim());
NODE
)"
if [ -z "${notes//[[:space:]]/}" ]; then
echo "::notice::No matching CHANGELOG.md notes for version range ${previous_version:-<none>}..$current_version. Continuing without store release notes."
echo "has_store_notes=false" >> "$GITHUB_OUTPUT"
exit 0
fi
compact_notes="$(printf '%s' "$notes" | tr '\n' ' ' | tr -s '[:space:]' ' ' | sed -e 's/^ //' -e 's/ $//')"
testflight_notes="$notes"
testflight_max_chars=4000
if [ "${#testflight_notes}" -gt "$testflight_max_chars" ]; then
testflight_notes="${testflight_notes:0:$((testflight_max_chars - 1))}…"
fi
play_notes="$compact_notes"
play_max_chars=500
if [ "${#play_notes}" -gt "$play_max_chars" ]; then
play_notes="${play_notes:0:$((play_max_chars - 3))}..."
fi
mkdir -p artifacts/submission/whatsnew
printf '%s' "$play_notes" > artifacts/submission/whatsnew/whatsnew-en-US
{
echo "testflight_release_notes<<EOF"
printf '%s\n' "$testflight_notes"
echo "EOF"
} >> "$GITHUB_OUTPUT"
echo "has_store_notes=true" >> "$GITHUB_OUTPUT"
echo "play_whatsnew_dir=artifacts/submission/whatsnew" >> "$GITHUB_OUTPUT"
- name: Upload iOS artifact to App Store Connect
uses: apple-actions/upload-testflight-build@v3
with:
app-path: artifacts/submission/${{ steps.artifact_names.outputs.ios }}
issuer-id: ${{ secrets.APPSTORE_ISSUER_ID }}
api-key-id: ${{ secrets.APPSTORE_API_KEY_ID }}
api-private-key: ${{ secrets.APPSTORE_API_PRIVATE_KEY }}
- name: Upload Android artifact to Google Play (with notes)
if: ${{ steps.store_notes.outputs.has_store_notes == 'true' }}
uses: r0adkll/upload-google-play@v1
with:
serviceAccountJsonPlainText: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_JSON }}
packageName: com.lallimaven.eclipsetimer
releaseFiles: artifacts/submission/${{ steps.artifact_names.outputs.android_aab }}
track: internal
whatsNewDirectory: ${{ steps.store_notes.outputs.play_whatsnew_dir }}
- name: Upload Android artifact to Google Play
if: ${{ steps.store_notes.outputs.has_store_notes != 'true' }}
uses: r0adkll/upload-google-play@v1
with:
serviceAccountJsonPlainText: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_JSON }}
packageName: com.lallimaven.eclipsetimer
releaseFiles: artifacts/submission/${{ steps.artifact_names.outputs.android_aab }}
track: internal
- name: Upload Wear OS artifact to Google Play (with notes, non-blocking)
id: wear_upload_with_notes
if: ${{ steps.store_notes.outputs.has_store_notes == 'true' }}
continue-on-error: true
uses: r0adkll/upload-google-play@v1
with:
serviceAccountJsonPlainText: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_JSON }}
packageName: com.lallimaven.eclipsetimer
releaseFiles: artifacts/submission/${{ steps.artifact_names.outputs.wear_aab }}
track: internal
whatsNewDirectory: ${{ steps.store_notes.outputs.play_whatsnew_dir }}
- name: Upload Wear OS artifact to Google Play (non-blocking)
id: wear_upload_without_notes
if: ${{ steps.store_notes.outputs.has_store_notes != 'true' }}
continue-on-error: true
uses: r0adkll/upload-google-play@v1
with:
serviceAccountJsonPlainText: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_JSON }}
packageName: com.lallimaven.eclipsetimer
releaseFiles: artifacts/submission/${{ steps.artifact_names.outputs.wear_aab }}
track: internal
- name: Log Wear OS Play upload failure (with notes)
if: ${{ steps.wear_upload_with_notes.outcome == 'failure' }}
run: |
echo "::error::Wear OS AAB upload to Google Play failed (with notes). Continuing pipeline and proceeding to GitHub release."
- name: Log Wear OS Play upload failure
if: ${{ steps.wear_upload_without_notes.outcome == 'failure' }}
run: |
echo "::error::Wear OS AAB upload to Google Play failed. Continuing pipeline and proceeding to GitHub release."
- name: Create GitHub release with mobile artifacts
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.release_meta.outputs.tag_name }}
name: ${{ steps.release_meta.outputs.release_name }}
body_path: ${{ steps.github_release_notes.outputs.body_path }}
generate_release_notes: false
files: ${{ steps.release_assets.outputs.files }}