Repository navigation
162 lines (158 loc) · 8.99 KB
/
Copy pathuat-dev.yml
File metadata and controls
162 lines (158 loc) · 8.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# UAT / internal-tester builds on merge to `dev` (or manual dispatch).
#
# OPTIONAL cloud path. The FAST path is local: `scripts/uat.sh` on your Mac (minutes).
# This runs the SAME script on a single macOS runner so CI and local are ONE pipeline
# (one source of truth): it computes the next beta version, generates release notes,
# builds every artifact BEFORE publishing any, uploads Android -> Play "internal" +
# iOS -> TestFlight, then cuts the GitHub prerelease tag with the APK + AAB attached.
#
# Cost note: iOS on GitHub's free macOS runners is slow (~30-90 min) and macOS minutes
# bill at 10x. That's the price of a fully-consistent CI path; the local script stays the
# fast default. Use workflow_dispatch with platform=android for a quick Play-only run.
#
# Uses the credentials ALREADY stored as repo secrets (nothing new to add):
# Android: ANDROID_KEYSTORE_BASE64, ANDROID_KEYSTORE_PASSWORD, ANDROID_KEY_ALIAS,
# ANDROID_KEY_PASSWORD, PLAY_STORE_JSON_KEY
# iOS: ASC_API_KEY_P8, ASC_KEY_ID, ASC_ISSUER_ID (TestFlight upload)
# IOS_CERTIFICATE_P12, IOS_CERTIFICATE_PASSWORD, IOS_PROVISION_PROFILE,
# KEYCHAIN_PASSWORD (signing)
name: UAT build (dev → internal testers)
on:
# Manual only. Betas are cut by hand (scripts/uat.sh locally, or this dispatch) — pushing
# to dev must NOT auto-build/publish a beta.
workflow_dispatch:
inputs:
platform:
description: Which platform(s) to build
type: choice
default: both
options: [both, ios, android]
concurrency:
group: uat-${{ github.ref }}
# Do NOT cancel a run in flight: once it starts publishing (Play + TestFlight), a mid-run
# cancel could half-publish. Let it finish; the next push queues behind it.
cancel-in-progress: false
permissions:
contents: write # uat.sh pushes the beta bump commit + tag and creates the GH release
jobs:
beta:
# Single macOS runner: it has Xcode AND the Android SDK, so one machine builds both and
# runs scripts/uat.sh end to end (bump -> build all -> publish all -> tag -> GH release).
# Skip runs triggered by the bot's back-merge of main -> dev (that push isn't a real
# change to ship a beta for); real human pushes to dev and manual dispatch still run.
if: ${{ github.event_name == 'workflow_dispatch' || github.actor != 'github-actions[bot]' }}
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
with:
submodules: false # pro/ is a private repo — fetch it below with a PAT
fetch-depth: 0 # uat.sh needs full tag history to compute the next beta N
- name: Check out pro submodule (private)
# pro/ lives in a separate private repo; the default GITHUB_TOKEN can't read it, so
# authenticate with PRO_SUBMODULE_PAT (same mechanism as ci.yml). uat.sh's build needs
# the real @offgrid/pro package present.
env:
PRO_PAT: ${{ secrets.PRO_SUBMODULE_PAT }}
run: |
if [ -z "$PRO_PAT" ]; then echo "PRO_SUBMODULE_PAT not set — cannot fetch private pro/ submodule"; exit 1; fi
git config --global url."https://x-access-token:${PRO_PAT}@github.com/".insteadOf "https://github.com/"
git submodule update --init --recursive pro
git config --global --unset url."https://x-access-token:${PRO_PAT}@github.com/".insteadOf || true
- name: Check out shared packages
uses: actions/checkout@v4
with:
repository: off-grid-ai/shared
token: ${{ secrets.PRO_SUBMODULE_PAT }}
path: _shared
ref: main
persist-credentials: false
- uses: actions/setup-node@v4
with: { node-version: '20', cache: npm }
- name: Build shared packages
run: |
mv _shared ../shared
npm --prefix ../shared ci
npm --prefix ../shared/packages/automation run build
npm --prefix ../shared/packages/sync run build
npm --prefix ../shared/packages/rag run build
npm --prefix ../shared/packages/models run build
npm --prefix ../shared/packages/speech run build
npm --prefix ../shared/packages/ui run build
- run: npm ci
- uses: actions/setup-java@v4
with: { distribution: temurin, java-version: '17' }
- uses: ruby/setup-ruby@v1
with: { ruby-version: '3.3', bundler-cache: true }
- name: pod install
# Use the CocoaPods pinned in Gemfile.lock, so Podfile.lock is reproduced as committed.
# Restoring a lock rewritten by another CocoaPods left Pods/Manifest.lock out of step with
# it, and the iOS archive then stopped with "The sandbox is not in sync with the Podfile.lock".
# On the runner even the pinned CocoaPods still rewrites two lines of Podfile.lock. uat.sh
# needs the committed lock, and the archive's Check Pods Manifest.lock step compares it with
# Pods/Manifest.lock, so print the drift, restore the lock and record it as the manifest.
run: |
cd ios && bundle exec pod install
cd .. && git diff -- ios/Podfile.lock
git checkout -- ios/Podfile.lock
cp ios/Podfile.lock ios/Pods/Manifest.lock
- name: Decode signing secrets
run: |
echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 --decode > "$RUNNER_TEMP/release.keystore"
# PLAY_STORE_JSON_KEY may be stored raw-JSON or base64 — handle both.
echo "${{ secrets.PLAY_STORE_JSON_KEY }}" | base64 --decode > "$RUNNER_TEMP/play-key.json" \
|| printf '%s' '${{ secrets.PLAY_STORE_JSON_KEY }}' > "$RUNNER_TEMP/play-key.json"
- name: Configure git for the beta bump commit + push
# actions/checkout leaves HEAD detached; uat.sh commits the bump and `git push`es it,
# which needs a real branch with an upstream. Re-attach to the triggering branch.
run: |
git config user.name "offgrid-ci"
git config user.email "ci@offgridmobile.ai"
git checkout -B "$GITHUB_REF_NAME"
git branch --set-upstream-to="origin/$GITHUB_REF_NAME" "$GITHUB_REF_NAME" || true
# setup-ruby's bundler-cache writes its path/deployment settings into the tracked
# .bundle/config (gems stay in vendor/bundle, which that file already names) and can
# rewrite Gemfile.lock. uat.sh refuses a dirty tree, so restore them and show anything
# else that is still dirty. Podfile.lock is not restored: it must match the installed pods.
git checkout -- .bundle/config Gemfile.lock 2>/dev/null || true
git status --porcelain
- name: Ship beta (scripts/uat.sh)
env:
# Which platform(s): push events build both; manual dispatch honors the picker.
UAT_PLATFORM_ARG: ${{ github.event_name == 'workflow_dispatch' && inputs.platform != 'both' && format('--{0}', inputs.platform) || '' }}
# gh release create / git push
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Android signing — build.gradle reads these via project.hasProperty(...), so pass
# them as Gradle project properties (ORG_GRADLE_PROJECT_<name>), not plain env.
ORG_GRADLE_PROJECT_OFFGRID_UPLOAD_STORE_FILE: ${{ runner.temp }}/release.keystore
ORG_GRADLE_PROJECT_OFFGRID_UPLOAD_STORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
ORG_GRADLE_PROJECT_OFFGRID_UPLOAD_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ORG_GRADLE_PROJECT_OFFGRID_UPLOAD_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
# Play upload
PLAY_STORE_JSON_KEY_PATH: ${{ runner.temp }}/play-key.json
# iOS auth + signing (imported into a temp keychain by the Fastfile's setup_signing)
ASC_API_KEY_P8: ${{ secrets.ASC_API_KEY_P8 }}
ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
IOS_CERTIFICATE_P12: ${{ secrets.IOS_CERTIFICATE_P12 }}
IOS_CERTIFICATE_PASSWORD: ${{ secrets.IOS_CERTIFICATE_PASSWORD }}
IOS_PROVISION_PROFILE: ${{ secrets.IOS_PROVISION_PROFILE }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
FASTLANE_SKIP_UPDATE_CHECK: '1'
FASTLANE_HIDE_CHANGELOG: '1'
# uat.sh announces the beta in Slack; without this it falls back to .env.keygen, which
# only exists on a developer machine, and silently skips the message.
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
run: bash scripts/uat.sh $UAT_PLATFORM_ARG
- name: Keep the built APK, AAB and IPA
# Saved even when a later uat.sh step fails (e.g. the push after the store uploads), so the
# exact files sent to Play and TestFlight can still be attached to the GitHub release.
if: always()
uses: actions/upload-artifact@v4
with:
name: uat-builds
path: |
android/app/build/outputs/apk/release/app-release.apk
android/app/build/outputs/bundle/release/app-release.aab
build/OffgridMobile.ipa
if-no-files-found: warn
retention-days: 30