v1.1.12 #29
Workflow file for this run
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
| name: Google Play Production Release | |
| on: | |
| release: | |
| types: | |
| - published | |
| permissions: | |
| contents: write | |
| concurrency: | |
| group: google-play-production | |
| cancel-in-progress: false | |
| env: | |
| PACKAGE_NAME: com.yourssu.ssutime.v2 | |
| jobs: | |
| publish: | |
| name: Build and publish production release | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Set up JDK | |
| uses: actions/setup-java@v4 | |
| with: | |
| distribution: temurin | |
| java-version: "21" | |
| - name: Set up Gradle | |
| uses: gradle/actions/setup-gradle@v4 | |
| - name: Validate required secrets | |
| env: | |
| PLAY_SERVICE_ACCOUNT_JSON: ${{ secrets.ANDROID_GOOGLE_SERVICE_JSON }} | |
| FIREBASE_GOOGLE_SERVICES_JSON: ${{ secrets.ANDROID_FIREBASE_GOOGLE_SERVICES_JSON }} | |
| UPLOAD_KEYSTORE_BASE64: ${{ secrets.ANDROID_UPLOAD_KEYSTORE_BASE64 }} | |
| UPLOAD_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_UPLOAD_KEYSTORE_PASSWORD }} | |
| UPLOAD_KEY_ALIAS: ${{ secrets.ANDROID_UPLOAD_KEY_ALIAS }} | |
| POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }} | |
| run: | | |
| test -n "$PLAY_SERVICE_ACCOUNT_JSON" || { echo "::error::Missing ANDROID_GOOGLE_SERVICE_JSON secret."; exit 1; } | |
| test -n "$FIREBASE_GOOGLE_SERVICES_JSON" || { echo "::error::Missing ANDROID_FIREBASE_GOOGLE_SERVICES_JSON secret for app/google-services.json."; exit 1; } | |
| test -n "$UPLOAD_KEYSTORE_BASE64" || { echo "::error::Missing ANDROID_UPLOAD_KEYSTORE_BASE64 secret."; exit 1; } | |
| test -n "$UPLOAD_KEYSTORE_PASSWORD" || { echo "::error::Missing ANDROID_UPLOAD_KEYSTORE_PASSWORD secret."; exit 1; } | |
| test -n "$UPLOAD_KEY_ALIAS" || { echo "::error::Missing ANDROID_UPLOAD_KEY_ALIAS secret."; exit 1; } | |
| test -n "$POSTHOG_API_KEY" || { echo "::error::Missing POSTHOG_API_KEY secret."; exit 1; } | |
| - name: Materialize private Android files | |
| env: | |
| FIREBASE_GOOGLE_SERVICES_JSON: ${{ secrets.ANDROID_FIREBASE_GOOGLE_SERVICES_JSON }} | |
| UPLOAD_KEYSTORE_BASE64: ${{ secrets.ANDROID_UPLOAD_KEYSTORE_BASE64 }} | |
| run: | | |
| printf '%s' "$FIREBASE_GOOGLE_SERVICES_JSON" > app/google-services.json | |
| printf '%s' "$UPLOAD_KEYSTORE_BASE64" | base64 --decode > "$RUNNER_TEMP/upload-keystore.jks" | |
| - name: Create Play release notes | |
| env: | |
| RELEASE_BODY: ${{ github.event.release.body }} | |
| run: | | |
| node <<'NODE' | |
| const fs = require('fs'); | |
| const requiredSections = [ | |
| { locale: 'en-US', title: 'English', headings: ['english', 'en', 'en-us'] }, | |
| { locale: 'ko-KR', title: '한국어', headings: ['한국어', 'korean', 'ko', 'ko-kr'] }, | |
| ]; | |
| const headingToLocale = new Map(); | |
| for (const section of requiredSections) { | |
| for (const heading of section.headings) { | |
| headingToLocale.set(normalizeHeading(heading), section.locale); | |
| } | |
| } | |
| const body = (process.env.RELEASE_BODY || '').replace(/\r\n/g, '\n').trim(); | |
| const sections = new Map(requiredSections.map((section) => [section.locale, ''])); | |
| let currentLocale = null; | |
| for (const line of body.split('\n')) { | |
| const headingMatch = line.match(/^#{2,6}\s+(.+?)\s*#*\s*$/); | |
| if (headingMatch) { | |
| const locale = headingToLocale.get(normalizeHeading(headingMatch[1])); | |
| if (locale) { | |
| currentLocale = locale; | |
| continue; | |
| } | |
| } | |
| if (currentLocale) { | |
| sections.set(currentLocale, sections.get(currentLocale) + line + '\n'); | |
| } | |
| } | |
| const missingSections = requiredSections.filter((section) => !sections.get(section.locale).trim()); | |
| if (missingSections.length > 0) { | |
| const missing = missingSections.map((section) => '## ' + section.title).join(', '); | |
| console.error('::error::Missing required release note section(s): ' + missing); | |
| process.exit(1); | |
| } | |
| fs.mkdirSync('distribution/whatsnew', { recursive: true }); | |
| for (const section of requiredSections) { | |
| const notes = Array.from(sections.get(section.locale).trim()).slice(0, 500).join(''); | |
| fs.writeFileSync('distribution/whatsnew/whatsnew-' + section.locale, notes + '\n'); | |
| console.log('Created Play release notes for ' + section.locale + '.'); | |
| } | |
| function normalizeHeading(value) { | |
| return value.trim().toLowerCase(); | |
| } | |
| NODE | |
| - name: Build release APK and AAB | |
| env: | |
| ANDROID_KEYSTORE_PATH: ${{ runner.temp }}/upload-keystore.jks | |
| ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_UPLOAD_KEYSTORE_PASSWORD }} | |
| ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_UPLOAD_KEY_ALIAS }} | |
| ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_UPLOAD_KEY_PASSWORD }} | |
| POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }} | |
| run: ./gradlew --no-daemon clean test assembleRelease bundleRelease | |
| - name: Attach build outputs to GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ github.event.release.tag_name }} | |
| files: | | |
| app/build/outputs/apk/release/*.apk | |
| app/build/outputs/bundle/release/app-release.aab | |
| - name: Upload AAB to Google Play production | |
| uses: r0adkll/upload-google-play@v1 | |
| with: | |
| serviceAccountJsonPlainText: ${{ secrets.ANDROID_GOOGLE_SERVICE_JSON }} | |
| packageName: ${{ env.PACKAGE_NAME }} | |
| releaseFiles: app/build/outputs/bundle/release/app-release.aab | |
| tracks: production | |
| status: completed | |
| releaseName: ${{ github.event.release.tag_name }} | |
| inAppUpdatePriority: 0 | |
| whatsNewDirectory: distribution/whatsnew | |
| mappingFile: app/build/outputs/mapping/release/mapping.txt | |
| changesNotSentForReview: false |