Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions .github/scripts/bump_version.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#!/bin/bash
set -e

# Usage: ./bump_version.sh [develop|production] [major|minor|patch]

MODE=$1
TYPE=${2:-patch}

PUBSPEC_FILE="pubspec.yaml"
VERSION_FILE="app_version.txt"

# 1. Read current version from app_version.txt
if [ ! -f "$VERSION_FILE" ]; then
echo "Error: $VERSION_FILE not found."
exit 1
fi

FULL_VERSION=$(cat "$VERSION_FILE")

# Validate format
if [[ $FULL_VERSION =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)\+([0-9]+)$ ]]; then
MAJOR="${BASH_REMATCH[1]}"
MINOR="${BASH_REMATCH[2]}"
PATCH="${BASH_REMATCH[3]}"
BUILD="${BASH_REMATCH[4]}"
else
echo "Error: Invalid version format in $VERSION_FILE: $FULL_VERSION"
echo "Expected format: X.Y.Z+BUILD"
exit 1
fi

# 2. Calculate New Version
NEW_BUILD=$((BUILD + 1))

if [ "$MODE" == "production" ]; then
if [ "$TYPE" == "major" ]; then
MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0
elif [ "$TYPE" == "minor" ]; then
MINOR=$((MINOR + 1)); PATCH=0
else
PATCH=$((PATCH + 1))
fi
NEW_VERSION="$MAJOR.$MINOR.$PATCH+$NEW_BUILD"
else
NEW_VERSION="$MAJOR.$MINOR.$PATCH+$NEW_BUILD"
fi

echo "Bumping version: $FULL_VERSION -> $NEW_VERSION"

# 3. Update Files
echo "$NEW_VERSION" > "$VERSION_FILE"
sed -i -E "s/^version: .*/version: $NEW_VERSION/" "$PUBSPEC_FILE"

# 4. Output for GitHub Actions
if [ -n "$GITHUB_OUTPUT" ]; then
echo "NEW_VERSION=$NEW_VERSION" >> "$GITHUB_OUTPUT"
fi
134 changes: 134 additions & 0 deletions .github/workflows/deploy_beta.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
name: "CI: Deploy Beta"

on:
push:
branches: [develop]

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
# 1. Validate Version
validate_version:
name: "Validate Version"
uses: ./.github/workflows/validate_version.yaml
with:
working-directory: ./packages/uni_app
secrets:
google_service_account_json: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_JSON }}
asc_key_id: ${{ secrets.ASC_KEY_ID }}
asc_issuer_id: ${{ secrets.ASC_ISSUER_ID }}
asc_key_content: ${{ secrets.ASC_KEY_CONTENT }}

# 2. Bump Version
bump_version:
name: "Bump Version"
needs: validate_version
runs-on: ubuntu-latest
permissions:
contents: write
defaults:
run:
working-directory: ./packages/uni_app
outputs:
commit_sha: ${{ steps.commit.outputs.COMMIT_SHA }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
token: ${{ secrets.NIAEFEUPBOT_PAT }}
fetch-depth: 0

- name: Run Bump Version Script
run: |
# Script path must now be relative to packages/uni_app
chmod +x ../../.github/scripts/bump_version.sh
../../.github/scripts/bump_version.sh develop

- name: Commit & Push Version Bump
id: commit
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

git add app_version.txt pubspec.yaml

if ! git diff --staged --quiet; then
NEW_VERSION=$(cat app_version.txt)
git commit -m "chore(ci): Bump build to $NEW_VERSION [skip ci]"
git push origin HEAD:develop
echo "COMMIT_SHA=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
else
echo "No version change detected."
echo "COMMIT_SHA=${{ github.sha }}" >> $GITHUB_OUTPUT
fi

# 3. Deploy Matrix (Android & iOS)
deploy_beta:
name: "Deploy ${{ matrix.platform }} (Beta)"
needs: bump_version
runs-on: ${{ matrix.os }}
defaults:
run:
working-directory: ./packages/uni_app
strategy:
fail-fast: false
matrix:
include:
- platform: android
os: ubuntu-latest
lane: deploy_beta
- platform: ios
os: macos-latest
lane: deploy_testflight

steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: ${{ needs.bump_version.outputs.commit_sha }}

- name: Set up Java
uses: actions/setup-java@v4
with:
distribution: "temurin"
java-version: "17"

- name: Set up Flutter
uses: subosito/flutter-action@v2
with:
channel: stable
cache: true

- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2'
bundler-cache: true
working-directory: ./packages/uni_app

- name: Create .env file
run: echo "${{ vars.UNI_ENV_FILE }}" > ./assets/env/.env

- name: Setup Android Keystore
if: matrix.platform == 'android'
run: |
echo "${{ secrets.UPLOAD_KEYSTORE_BASE64 }}" | base64 --decode > /tmp/upload-keystore.jks

- name: Run Fastlane
env:
# Android Secrets
ANDROID_JSON_KEY_FILE: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_JSON }}
ANDROID_KEYSTORE_FILE_PATH: /tmp/upload-keystore.jks
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.UPLOAD_KEYSTORE_PASSWORD }}
ANDROID_KEY_ALIAS: upload
ANDROID_KEY_PASSWORD: ${{ secrets.UPLOAD_KEY_PASSWORD }}
# iOS Secrets
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
MATCH_GIT_BASIC_AUTHORIZATION: ${{ secrets.MATCH_GIT_BASIC_AUTHORIZATION }}
ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
ASC_KEY_CONTENT: ${{ secrets.ASC_KEY_CONTENT }}
run: |
bundle exec fastlane ${{ matrix.platform }} ${{ matrix.lane }}
124 changes: 124 additions & 0 deletions .github/workflows/validate_version.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@

# Validate Version Workflow
name: "Validate Version"


on:
workflow_call:
inputs:
working-directory:
description: "Path to the uni_app working directory"
required: false
type: string
default: ./packages/uni_app
android_track:
description: "Google Play track to query (internal, beta, production)"
required: false
type: string
default: beta
ios_channel:
description: "iOS source to query (testflight, appstore)"
required: false
type: string
default: testflight
secrets:
google_service_account_json:
description: "Google service account JSON for Play Console"
required: true
asc_key_id:
description: "App Store Connect API Key ID"
required: true
asc_issuer_id:
description: "App Store Connect Issuer ID"
required: true
asc_key_content:
description: "App Store Connect API Key content (p8)"
required: true
outputs:
current_version:
description: "Version read from app_version.txt"
value: ${{ jobs.validate.outputs.current_version }}
build_number:
description: "Build number parsed from version"
value: ${{ jobs.validate.outputs.build_number }}
android_store_build_number:
description: "Latest Android beta build number from Play Console"
value: ${{ jobs.validate.outputs.android_store_build_number }}
ios_store_build_number:
description: "Latest iOS build number from selected channel"
value: ${{ jobs.validate.outputs.ios_store_build_number }}


jobs:
validate:
name: "Validate Version"
runs-on: ubuntu-latest
defaults:
run:
working-directory: ${{ inputs.working-directory }}
outputs:
current_version: ${{ steps.read_version.outputs.CURRENT_VERSION }}
build_number: ${{ steps.read_version.outputs.BUILD_NUMBER }}
android_store_build_number: ${{ steps.android_store.outputs.ANDROID_STORE_BUILD_NUMBER }}
ios_store_build_number: ${{ steps.ios_store.outputs.IOS_STORE_BUILD_NUMBER }}
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Read Current Version
id: read_version
run: |
# Read version from app_version.txt and extract build number
FULL_VERSION=$(cat app_version.txt)
echo "CURRENT_VERSION=$FULL_VERSION" >> $GITHUB_OUTPUT
BUILD_NUMBER=$(echo "$FULL_VERSION" | cut -d'+' -f2)
echo "BUILD_NUMBER=$BUILD_NUMBER" >> $GITHUB_OUTPUT

- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2'
bundler-cache: true
working-directory: ${{ inputs.working-directory }}

- name: Get Latest Android Store Version
id: android_store
env:
GOOGLE_SERVICE_ACCOUNT_JSON: ${{ secrets.google_service_account_json }}
run: |
echo "$GOOGLE_SERVICE_ACCOUNT_JSON" > /tmp/google_service_account.json
export SUPPLY_JSON_KEY=/tmp/google_service_account.json
ANDROID_BUILD=$(bundle exec fastlane run google_play_track_version_codes track:${{ inputs.android_track }} -q 2>/dev/null | grep -oE '[0-9]+' | head -1 || echo "0")
echo "ANDROID_STORE_BUILD_NUMBER=$ANDROID_BUILD" >> $GITHUB_OUTPUT
rm /tmp/google_service_account.json

- name: Get Latest iOS Store Version
id: ios_store
env:
ASC_KEY_ID: ${{ secrets.asc_key_id }}
ASC_ISSUER_ID: ${{ secrets.asc_issuer_id }}
ASC_KEY_CONTENT: ${{ secrets.asc_key_content }}
run: |
IOS_BUILD="0"
if [ "${{ inputs.ios_channel }}" = "appstore" ]; then
IOS_BUILD=$(bundle exec fastlane run app_store_build_number -q 2>/dev/null | grep -oE '[0-9]+' | tail -1 || echo "0")
else
IOS_BUILD=$(bundle exec fastlane run latest_testflight_build_number -q 2>/dev/null | grep -oE '[0-9]+' | tail -1 || echo "0")
fi
echo "IOS_STORE_BUILD_NUMBER=$IOS_BUILD" >> $GITHUB_OUTPUT

- name: Validate Build Numbers
run: |
# Ensure current build number is greater than both store versions
CURRENT_BUILD="${{ steps.read_version.outputs.BUILD_NUMBER }}"
ANDROID_BUILD="${{ steps.android_store.outputs.ANDROID_STORE_BUILD_NUMBER }}"
IOS_BUILD="${{ steps.ios_store.outputs.IOS_STORE_BUILD_NUMBER }}"

echo "Current build: $CURRENT_BUILD"
echo "Android store build: $ANDROID_BUILD"
echo "iOS store build: $IOS_BUILD"

if [ "$CURRENT_BUILD" -le "$ANDROID_BUILD" ] || [ "$CURRENT_BUILD" -le "$IOS_BUILD" ]; then
echo "Error: Current build number ($CURRENT_BUILD) must be greater than both store versions (Android: $ANDROID_BUILD, iOS: $IOS_BUILD)"
exit 1
fi
2 changes: 1 addition & 1 deletion packages/uni_app/app_version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.2.0-beta.27+435
2.2.0+435
Loading