Skip to content

chore: version packages #57

chore: version packages

chore: version packages #57

Workflow file for this run

name: Build and Release App
on:
push:
tags:
- "v*"
workflow_dispatch:
inputs:
version_suffix:
description: "Version suffix (leave empty to use commit SHA)"
required: false
default: ""
permissions:
contents: write
jobs:
ci:
name: βœ… Lint, Format & Test
uses: ./.github/workflows/ci.yml
secrets: inherit
build:
name: πŸ”¨ Build (${{ matrix.profile }})
runs-on: ubuntu-latest
timeout-minutes: 60
needs: ci
strategy:
fail-fast: false
matrix:
profile: [internal, production]
steps:
- name: πŸ—οΈ Checkout
uses: actions/checkout@v4
- name: πŸ“¦ Setup Node
uses: actions/setup-node@v4
with:
node-version: 24.x
- name: β˜• Setup Java
uses: actions/setup-java@v4
with:
distribution: "temurin"
java-version: "17"
- name: πŸ€– Setup Android SDK
uses: android-actions/setup-android@v3
- name: πŸ“ Get yarn cache directory
id: yarn-cache
run: echo "dir=$(yarn config get cacheFolder)" >> $GITHUB_OUTPUT
- name: πŸ“¦ Cache yarn downloads
uses: actions/cache@v4
with:
path: ${{ steps.yarn-cache.outputs.dir }}
key: ${{ runner.os }}-yarn-cache-${{ hashFiles('yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-cache-
- name: πŸ“¦ Cache node_modules
uses: actions/cache@v4
with:
path: node_modules
key: ${{ runner.os }}-node-modules-${{ hashFiles('yarn.lock') }}
restore-keys: |
${{ runner.os }}-node-modules-
- name: 🐘 Cache Gradle
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: πŸ› οΈ Setup EAS CLI
uses: expo/expo-github-action@v8
with:
eas-version: latest
token: ${{ secrets.EXPO_TOKEN }}
- name: πŸ“₯ Install dependencies
run: yarn install --immutable
- name: πŸ“ Extract changelog for this version
id: changelog
run: |
echo "CHANGELOG=" >> $GITHUB_OUTPUT
- name: πŸ”§ Determine version name
id: version
run: |
if [ "${{ github.event_name }}" == "push" ] && [ "${{ startsWith(github.ref, 'refs/tags/') }}" == "true" ]; then
VERSION="${GITHUB_REF#refs/tags/}"
echo "version_name=$VERSION" >> $GITHUB_OUTPUT
echo "Using tag version: $VERSION"
elif [ -n "${{ github.event.inputs.version_suffix }}" ]; then
VERSION="${{ github.event.inputs.version_suffix }}"
echo "version_name=$VERSION" >> $GITHUB_OUTPUT
echo "Using custom version: $VERSION"
else
VERSION="${GITHUB_SHA:0:7}"
echo "version_name=$VERSION" >> $GITHUB_OUTPUT
echo "Using commit SHA: $VERSION"
fi
- name: πŸš€ Build Locally (${{ matrix.profile }})
run: |
echo "Building locally on GitHub runner..."
if [ "${{ matrix.profile }}" == "production" ]; then
EXT="aab"
else
EXT="apk"
fi
eas build --platform android --profile ${{ matrix.profile }} --local --non-interactive --output=./expense-buddy-${{ steps.version.outputs.version_name }}-${{ matrix.profile }}.$EXT
- name: πŸ“€ Upload build as artifact
uses: actions/upload-artifact@v4
with:
name: expense-buddy-${{ steps.version.outputs.version_name }}-${{ matrix.profile }}
path: expense-buddy-${{ steps.version.outputs.version_name }}-${{ matrix.profile }}.${{ matrix.profile == 'production' && 'aab' || 'apk' }}
- name: πŸš€ Submit to Google Play Store (Production)
if: matrix.profile == 'production'
continue-on-error: true
run: |
eas submit --platform android --profile production --path expense-buddy-${{ steps.version.outputs.version_name }}-production.aab --non-interactive
- name: πŸ’¬ Comment on merged PRs
if: matrix.profile == 'production'
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GH_PAT }}
script: |
const version = '${{ steps.version.outputs.version_name }}';
const tag = version.startsWith('v') ? version : `v${version}`;
const owner = context.repo.owner;
const repo = context.repo.repo;
console.log(`Processing release comments for tag: ${tag}`);
// 1. Get list of tags to find the previous one
const { data: tags } = await github.rest.repos.listTags({
owner,
repo,
per_page: 10
});
// Tags are returned latest first
const currentTagIndex = tags.findIndex(t => t.name === tag);
// If tag not found, it might be a manual build for a future tag
if (currentTagIndex === -1) {
console.log(`Tag ${tag} not found in repository. Skipping comments.`);
return;
}
const previousTag = tags[currentTagIndex + 1]?.name;
if (!previousTag) {
console.log('No previous tag found, likely first release. Skipping PR comments.');
return;
}
console.log(`Comparing releases: ${previousTag} ... ${tag}`);
// 2. Compare commits
const comparison = await github.rest.repos.compareCommits({
owner,
repo,
base: previousTag,
head: tag
});
// 3. Find unique PRs
const prNumbers = new Set();
for (const commit of comparison.data.commits) {
const { data: associatedPrs } = await github.rest.repos.listPullRequestsAssociatedWithCommit({
owner,
repo,
commit_sha: commit.sha
});
for (const pr of associatedPrs) {
if (pr.merged_at) {
prNumbers.add(pr.number);
}
}
}
console.log(`Found ${prNumbers.size} PRs to notify: ${Array.from(prNumbers).join(', ')}`);
// 4. Comment on each PR
for (const prNumber of prNumbers) {
try {
await github.rest.issues.createComment({
owner,
repo,
issue_number: prNumber,
body: `πŸš€ Released in [${tag}](https://github.com/${owner}/${repo}/releases/tag/${tag})`
});
console.log(`βœ… Commented on PR #${prNumber}`);
} catch (error) {
console.error(`❌ Failed to comment on PR #${prNumber}:`, error);
}
}
release:
name: πŸŽ‰ Create GitHub Release
runs-on: ubuntu-latest
needs: build
if: startsWith(github.ref, 'refs/tags/')
steps:
- name: πŸ—οΈ Checkout
uses: actions/checkout@v4
- name: οΏ½ Determine version name
id: version
run: |
VERSION="${GITHUB_REF#refs/tags/}"
echo "version_name=$VERSION" >> $GITHUB_OUTPUT
VERSION_NO_V="${VERSION#v}"
echo "version_no_v=$VERSION_NO_V" >> $GITHUB_OUTPUT
- name: πŸ“ Extract changelog for this version
id: changelog
run: |
VERSION_NO_V="${{ steps.version.outputs.version_no_v }}"
if [ -f "CHANGELOG.md" ]; then
echo "Found CHANGELOG.md, extracting section for $VERSION_NO_V"
CHANGELOG_CONTENT=$(awk -v version="$VERSION_NO_V" '
/^## / {
if (found) exit
if ($0 ~ version) {
found=1
next
}
}
found { print }
' CHANGELOG.md)
if [ -n "$CHANGELOG_CONTENT" ]; then
echo "CHANGELOG<<EOF" >> $GITHUB_OUTPUT
echo "$CHANGELOG_CONTENT" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
echo "βœ… Changelog extracted successfully"
else
echo "CHANGELOG=_No changelog entry found for $VERSION_NO_V_" >> $GITHUB_OUTPUT
echo "⚠️ No changelog entry found for $VERSION_NO_V"
fi
else
echo "CHANGELOG=_No CHANGELOG.md found._" >> $GITHUB_OUTPUT
echo "⚠️ CHANGELOG.md not found"
fi
- name: πŸ“₯ Download all app artifacts
uses: actions/download-artifact@v4
with:
pattern: expense-buddy-${{ steps.version.outputs.version_name }}-*
merge-multiple: true
- name: πŸŽ‰ Create GitHub Release
uses: softprops/action-gh-release@v1
with:
files: |
expense-buddy-${{ steps.version.outputs.version_name }}-internal.apk
draft: false
prerelease: false
generate_release_notes: true
body: |
## πŸ“± Get it on Google Play
For the best experience with automatic updates, install from the Play Store:
**[Download from Google Play](https://play.google.com/store/apps/details?id=com.sudokoi.expensebuddy)**
---
## πŸ“¦ APK Download
Alternatively, download directly from the Assets below:
- `expense-buddy-${{ steps.version.outputs.version_name }}-internal.apk`
---
## πŸ“ Changelog
${{ steps.changelog.outputs.CHANGELOG }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}