Merge pull request #139 from donetick/fastline
Integrate with Fastline to automate App stores/ Playstore release
This commit is contained in:
2
.bundle/config
Normal file
2
.bundle/config
Normal file
@@ -0,0 +1,2 @@
|
||||
---
|
||||
BUNDLE_PATH: "vendor/bundle"
|
||||
21
.gitignore
vendored
21
.gitignore
vendored
@@ -24,4 +24,23 @@ dist-ssr
|
||||
*.sw?
|
||||
|
||||
resources/android/**/*
|
||||
resources/ios/**/*
|
||||
resources/ios/**/*
|
||||
|
||||
# Secrets (pulled via scripts/pull-secrets.sh)
|
||||
.env.production
|
||||
android/app/google-services.json
|
||||
android/app/release/*.jks
|
||||
android/keystore.properties
|
||||
android/local.properties
|
||||
android/play-service-account.json
|
||||
ios/App/App/GoogleService-Info.plist
|
||||
ios/*.p8
|
||||
|
||||
# Release build outputs
|
||||
build/android
|
||||
build/ios
|
||||
fastlane/report.xml
|
||||
fastlane/Preview.html
|
||||
fastlane/.env
|
||||
fastlane/*.log
|
||||
vendor/bundle
|
||||
85
AUTOMATED_RELEASE.md
Normal file
85
AUTOMATED_RELEASE.md
Normal file
@@ -0,0 +1,85 @@
|
||||
# Automated Release Pipeline
|
||||
|
||||
Builds and (optionally) uploads signed Android and iOS release artifacts from the CLI — no Android Studio or Xcode GUI needed for routine releases.
|
||||
|
||||
## Quick start
|
||||
|
||||
```sh
|
||||
./scripts/pull-secrets.sh # pulls all signing secrets from Vaultwarden
|
||||
./scripts/release.sh # bump patch version, build + sign both platforms
|
||||
./scripts/release.sh --upload # also upload: Android → Play internal track, iOS → TestFlight
|
||||
```
|
||||
|
||||
Common flags on `release.sh`:
|
||||
|
||||
| Flag | Effect |
|
||||
|---|---|
|
||||
| `--android` / `--ios` | Build only one platform |
|
||||
| `--bump minor\|major` | Bump type (default: `patch`) |
|
||||
| `--skip-bump` | Build with the current version, don't bump |
|
||||
| `--upload` | Upload after building |
|
||||
| `--track TRACK` | Play Store track for `--upload` (default: `internal`) |
|
||||
|
||||
Outputs:
|
||||
- Android: `android/app/build/outputs/bundle/release/app-release.aab`
|
||||
- iOS: `build/ios/Donetick.ipa`
|
||||
|
||||
## One-time machine setup
|
||||
|
||||
1. `bundle install` — installs fastlane (Ruby gems, local to the repo via `vendor/bundle`)
|
||||
2. `./scripts/pull-secrets.sh` — pulls all secrets below from Vaultwarden
|
||||
3. `android/local.properties` needs `sdk.dir=<path to Android SDK>` — not pulled from vault, machine-specific. Android Studio creates this automatically; if building headless-only, create it manually.
|
||||
4. Export `ASC_ISSUER_ID` (App Store Connect API key issuer ID) — either in your shell profile, or add it to the `Donetick Env Production` vault note so `pull-secrets.sh` delivers it into `.env.production` (which `release.sh` auto-sources).
|
||||
5. A working Apple Distribution certificate + the `Donetick App Store(fastline)` provisioning profile must be installed in your keychain / `~/Library/MobileDevice/Provisioning Profiles/` (see "iOS signing model" below) — **not** currently pulled from vault, machine-specific one-time setup.
|
||||
|
||||
## Secrets pulled by `scripts/pull-secrets.sh`
|
||||
|
||||
All secrets live in Vaultwarden (`https://www.bitwarden.com`) as Secure Notes, named exactly as below:
|
||||
|
||||
| Vault item name | Written to | Encoding |
|
||||
|---|---|---|
|
||||
| Donetick Google Services Android | `android/app/google-services.json` | raw |
|
||||
| Donetick Android Keystore | `android/app/release/donetick.jks` | base64 |
|
||||
| Donetick Keystore Password | (used inline for `android/keystore.properties`) | raw |
|
||||
| Donetick Google Play Service Account | `android/play-service-account.json` | raw |
|
||||
| Donetick Google Services iOS | `ios/App/App/GoogleService-Info.plist` | raw |
|
||||
| Donetick App Store Connect Key | `ios/AuthKey_84F695CDQ3.p8` | base64 |
|
||||
| Donetick Env Production | `.env.production` | raw |
|
||||
|
||||
None of these files are committed to git — all covered by `.gitignore`.
|
||||
|
||||
## iOS signing model (important — don't relitigate this)
|
||||
|
||||
The App target uses **manual signing for Release**, not Automatic. This was a deliberate choice, not the default:
|
||||
|
||||
- The project has **two** Apple Distribution-type certificates on the Apple Developer account: an Xcode Cloud–managed one (private key never leaves Apple, can't be used for local CLI signing) and a plain one created via Xcode → Settings → Accounts → Manage Certificates (private key is in the local keychain, usable). Automatic signing kept binding to the wrong (Managed) one.
|
||||
- Fix: `ios/App/App.xcodeproj/project.pbxproj` → App target → Release config has `CODE_SIGN_STYLE = Manual`, `CODE_SIGN_IDENTITY = "Apple Distribution"`, `PROVISIONING_PROFILE_SPECIFIER = "Donetick App Store(fastline)"`. Debug config is untouched (still Automatic, for local dev builds in Xcode).
|
||||
- `fastlane/Fastfile`'s `ios release` lane passes explicit `export_options: { signingStyle: "manual", provisioningProfiles: {...} }` to `build_app` — do **not** switch this back to `-allowProvisioningUpdates`/automatic without a good reason, it re-triggers the ambiguous cert selection.
|
||||
|
||||
**If you ever add/change an entitlement** (new Capability in Signing & Capabilities, e.g. a new permission), the existing provisioning profile becomes stale and exports will fail with an error like `"App.app" requires a provisioning profile with the X feature`. Fix:
|
||||
1. developer.apple.com → Identifiers → `com.donetick.app` → confirm the new capability is checked
|
||||
2. developer.apple.com → Profiles → find `Donetick App Store(fastline)` → regenerate it → download
|
||||
3. Install it locally: get its UUID (`security cms -D -i <file>.mobileprovision | plutil -extract UUID xml1 -o - -`) and copy to `~/Library/MobileDevice/Provisioning Profiles/<uuid>.mobileprovision`
|
||||
|
||||
**If the Distribution certificate ever expires/is revoked**, redo the one-time Xcode step: Settings → Accounts → Manage Certificates → `+` → Apple Distribution, then regenerate the profile as above.
|
||||
|
||||
## Android signing model
|
||||
|
||||
Standard keystore signing via `android/keystore.properties`, read by `android/app/build.gradle`. One historical gotcha already fixed: `storeFile` must be `release/donetick.jks` (relative to the `android/app` module dir), **not** `app/release/donetick.jks` — that extra `app/` silently resolved to a double-nested wrong path and broke CLI builds (Android Studio may have masked this before).
|
||||
|
||||
## Play Store upload gotchas
|
||||
|
||||
- The service account (`fastline-471@donetick-5f910.iam.gserviceaccount.com`) needs the **Google Play Android Developer API enabled** in its GCP project (`donetick-5f910`), **and** explicit per-app release permission granted in Play Console → Users and permissions (not just account-level access) — **and you have to actually click Save**, the UI will silently not persist it otherwise.
|
||||
- Google Play Console permission changes can take time to propagate on Google's backend even after saving (historically documented as up to ~24h, though in practice it was near-instant once actually saved).
|
||||
- The Play Developer API requires the app to have had **at least one manual release** uploaded through the Play Console website before API uploads work at all — not an issue for Donetick (already satisfied) but relevant if this is ever set up for a brand-new app.
|
||||
|
||||
## Fastlane lanes reference
|
||||
|
||||
```sh
|
||||
bundle exec fastlane android release # build signed .aab
|
||||
bundle exec fastlane android apk # build signed .apk (sideloading/testing)
|
||||
bundle exec fastlane android upload [track:X] # upload .aab to Play Store (default track: internal)
|
||||
|
||||
bundle exec fastlane ios release # build signed .ipa
|
||||
bundle exec fastlane ios upload # upload .ipa to TestFlight
|
||||
```
|
||||
233
Gemfile.lock
Normal file
233
Gemfile.lock
Normal file
@@ -0,0 +1,233 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
CFPropertyList (3.0.9)
|
||||
abbrev (0.1.2)
|
||||
addressable (2.9.0)
|
||||
public_suffix (>= 2.0.2, < 8.0)
|
||||
artifactory (3.0.17)
|
||||
atomos (0.1.3)
|
||||
aws-eventstream (1.3.2)
|
||||
aws-partitions (1.1109.0)
|
||||
aws-sdk-core (3.224.1)
|
||||
aws-eventstream (~> 1, >= 1.3.0)
|
||||
aws-partitions (~> 1, >= 1.992.0)
|
||||
aws-sigv4 (~> 1.9)
|
||||
base64
|
||||
jmespath (~> 1, >= 1.6.1)
|
||||
logger
|
||||
aws-sdk-kms (1.101.0)
|
||||
aws-sdk-core (~> 3, >= 3.216.0)
|
||||
aws-sigv4 (~> 1.5)
|
||||
aws-sdk-s3 (1.188.0)
|
||||
aws-sdk-core (~> 3, >= 3.224.1)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.5)
|
||||
aws-sigv4 (1.11.0)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
babosa (1.0.4)
|
||||
base64 (0.2.0)
|
||||
claide (1.1.0)
|
||||
colored (1.2)
|
||||
colored2 (3.1.2)
|
||||
commander (4.6.0)
|
||||
highline (~> 2.0.0)
|
||||
csv (3.3.5)
|
||||
declarative (0.0.20)
|
||||
digest-crc (0.7.0)
|
||||
rake (>= 12.0.0, < 14.0.0)
|
||||
domain_name (0.5.20190701)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
dotenv (2.8.1)
|
||||
emoji_regex (3.2.3)
|
||||
excon (0.109.0)
|
||||
faraday (1.10.6)
|
||||
faraday-em_http (~> 1.0)
|
||||
faraday-em_synchrony (~> 1.0)
|
||||
faraday-excon (~> 1.1)
|
||||
faraday-httpclient (~> 1.0)
|
||||
faraday-multipart (~> 1.0)
|
||||
faraday-net_http (~> 1.0)
|
||||
faraday-net_http_persistent (~> 1.0)
|
||||
faraday-patron (~> 1.0)
|
||||
faraday-rack (~> 1.0)
|
||||
faraday-retry (~> 1.0)
|
||||
ruby2_keywords (>= 0.0.4)
|
||||
faraday-cookie_jar (0.0.8)
|
||||
faraday (>= 0.8.0)
|
||||
http-cookie (>= 1.0.0)
|
||||
faraday-em_http (1.0.0)
|
||||
faraday-em_synchrony (1.0.1)
|
||||
faraday-excon (1.1.0)
|
||||
faraday-httpclient (1.0.1)
|
||||
faraday-multipart (1.2.0)
|
||||
multipart-post (~> 2.0)
|
||||
faraday-net_http (1.0.2)
|
||||
faraday-net_http_persistent (1.2.0)
|
||||
faraday-patron (1.0.0)
|
||||
faraday-rack (1.0.0)
|
||||
faraday-retry (1.0.4)
|
||||
faraday_middleware (1.2.1)
|
||||
faraday (~> 1.0)
|
||||
fastimage (2.4.1)
|
||||
fastlane (2.230.0)
|
||||
CFPropertyList (>= 2.3, < 4.0.0)
|
||||
abbrev (~> 0.1.2)
|
||||
addressable (>= 2.8, < 3.0.0)
|
||||
artifactory (~> 3.0)
|
||||
aws-sdk-s3 (~> 1.0)
|
||||
babosa (>= 1.0.3, < 2.0.0)
|
||||
base64 (~> 0.2.0)
|
||||
bundler (>= 1.12.0, < 3.0.0)
|
||||
colored (~> 1.2)
|
||||
commander (~> 4.6)
|
||||
csv (~> 3.3)
|
||||
dotenv (>= 2.1.1, < 3.0.0)
|
||||
emoji_regex (>= 0.1, < 4.0)
|
||||
excon (>= 0.71.0, < 1.0.0)
|
||||
faraday (~> 1.0)
|
||||
faraday-cookie_jar (~> 0.0.6)
|
||||
faraday_middleware (~> 1.0)
|
||||
fastimage (>= 2.1.0, < 3.0.0)
|
||||
fastlane-sirp (>= 1.0.0)
|
||||
gh_inspector (>= 1.1.2, < 2.0.0)
|
||||
google-apis-androidpublisher_v3 (~> 0.3)
|
||||
google-apis-playcustomapp_v1 (~> 0.1)
|
||||
google-cloud-env (>= 1.6.0, < 2.0.0)
|
||||
google-cloud-storage (~> 1.31)
|
||||
highline (~> 2.0)
|
||||
http-cookie (~> 1.0.5)
|
||||
json (< 3.0.0)
|
||||
jwt (>= 2.1.0, < 3)
|
||||
logger (>= 1.6, < 2.0)
|
||||
mini_magick (>= 4.9.4, < 5.0.0)
|
||||
multipart-post (>= 2.0.0, < 3.0.0)
|
||||
mutex_m (~> 0.3.0)
|
||||
naturally (~> 2.2)
|
||||
nkf (~> 0.2.0)
|
||||
optparse (>= 0.1.1, < 1.0.0)
|
||||
plist (>= 3.1.0, < 4.0.0)
|
||||
rubyzip (>= 2.0.0, < 3.0.0)
|
||||
security (= 0.1.5)
|
||||
simctl (~> 1.6.3)
|
||||
terminal-notifier (>= 2.0.0, < 3.0.0)
|
||||
terminal-table (~> 3)
|
||||
tty-screen (>= 0.6.3, < 1.0.0)
|
||||
tty-spinner (>= 0.8.0, < 1.0.0)
|
||||
word_wrap (~> 1.0.0)
|
||||
xcodeproj (>= 1.13.0, < 2.0.0)
|
||||
xcpretty (~> 0.4.1)
|
||||
xcpretty-travis-formatter (>= 0.0.3, < 2.0.0)
|
||||
fastlane-sirp (1.1.0)
|
||||
gh_inspector (1.1.3)
|
||||
google-apis-androidpublisher_v3 (0.54.0)
|
||||
google-apis-core (>= 0.11.0, < 2.a)
|
||||
google-apis-core (0.11.3)
|
||||
addressable (~> 2.5, >= 2.5.1)
|
||||
googleauth (>= 0.16.2, < 2.a)
|
||||
httpclient (>= 2.8.1, < 3.a)
|
||||
mini_mime (~> 1.0)
|
||||
representable (~> 3.0)
|
||||
retriable (>= 2.0, < 4.a)
|
||||
rexml
|
||||
google-apis-iamcredentials_v1 (0.17.0)
|
||||
google-apis-core (>= 0.11.0, < 2.a)
|
||||
google-apis-playcustomapp_v1 (0.13.0)
|
||||
google-apis-core (>= 0.11.0, < 2.a)
|
||||
google-apis-storage_v1 (0.29.0)
|
||||
google-apis-core (>= 0.11.0, < 2.a)
|
||||
google-cloud-core (1.6.1)
|
||||
google-cloud-env (>= 1.0, < 3.a)
|
||||
google-cloud-errors (~> 1.0)
|
||||
google-cloud-env (1.6.0)
|
||||
faraday (>= 0.17.3, < 3.0)
|
||||
google-cloud-errors (1.3.1)
|
||||
google-cloud-storage (1.45.0)
|
||||
addressable (~> 2.8)
|
||||
digest-crc (~> 0.4)
|
||||
google-apis-iamcredentials_v1 (~> 0.1)
|
||||
google-apis-storage_v1 (~> 0.29.0)
|
||||
google-cloud-core (~> 1.6)
|
||||
googleauth (>= 0.16.2, < 2.a)
|
||||
mini_mime (~> 1.0)
|
||||
googleauth (1.8.1)
|
||||
faraday (>= 0.17.3, < 3.a)
|
||||
jwt (>= 1.4, < 3.0)
|
||||
multi_json (~> 1.11)
|
||||
os (>= 0.9, < 2.0)
|
||||
signet (>= 0.16, < 2.a)
|
||||
highline (2.0.3)
|
||||
http-cookie (1.0.8)
|
||||
domain_name (~> 0.5)
|
||||
httpclient (2.9.0)
|
||||
mutex_m
|
||||
jmespath (1.6.2)
|
||||
json (2.7.6)
|
||||
jwt (2.10.3)
|
||||
base64
|
||||
logger (1.7.0)
|
||||
mini_magick (4.13.2)
|
||||
mini_mime (1.1.5)
|
||||
multi_json (1.15.0)
|
||||
multipart-post (2.4.1)
|
||||
mutex_m (0.3.0)
|
||||
nanaimo (0.4.0)
|
||||
naturally (2.3.0)
|
||||
nkf (0.2.0)
|
||||
optparse (0.8.1)
|
||||
os (1.1.4)
|
||||
plist (3.7.2)
|
||||
public_suffix (5.1.1)
|
||||
rake (13.4.2)
|
||||
representable (3.2.0)
|
||||
declarative (< 0.1.0)
|
||||
trailblazer-option (>= 0.1.1, < 0.2.0)
|
||||
uber (< 0.2.0)
|
||||
retriable (3.8.0)
|
||||
rexml (3.4.4)
|
||||
rouge (3.28.0)
|
||||
ruby2_keywords (0.0.5)
|
||||
rubyzip (2.4.1)
|
||||
security (0.1.5)
|
||||
signet (0.18.0)
|
||||
addressable (~> 2.8)
|
||||
faraday (>= 0.17.5, < 3.a)
|
||||
jwt (>= 1.5, < 3.0)
|
||||
multi_json (~> 1.10)
|
||||
simctl (1.6.10)
|
||||
CFPropertyList
|
||||
naturally
|
||||
terminal-notifier (2.0.0)
|
||||
terminal-table (3.0.2)
|
||||
unicode-display_width (>= 1.1.1, < 3)
|
||||
trailblazer-option (0.1.2)
|
||||
tty-cursor (0.7.1)
|
||||
tty-screen (0.8.2)
|
||||
tty-spinner (0.9.3)
|
||||
tty-cursor (~> 0.7)
|
||||
uber (0.1.0)
|
||||
unf (0.2.0)
|
||||
unicode-display_width (2.6.0)
|
||||
word_wrap (1.0.0)
|
||||
xcodeproj (1.28.1)
|
||||
CFPropertyList (>= 2.3.3, < 4.0)
|
||||
atomos (~> 0.1.3)
|
||||
base64
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
colored2 (~> 3.1)
|
||||
nanaimo (~> 0.4.0)
|
||||
nkf
|
||||
rexml (>= 3.3.6, < 4.0)
|
||||
xcpretty (0.4.1)
|
||||
rouge (~> 3.28.0)
|
||||
xcpretty-travis-formatter (1.0.1)
|
||||
xcpretty (~> 0.2, >= 0.0.7)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
fastlane
|
||||
|
||||
BUNDLED WITH
|
||||
1.17.2
|
||||
@@ -13,8 +13,8 @@ android {
|
||||
applicationId "com.donetick.app"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 28
|
||||
versionName "1.2.7"
|
||||
versionCode 31
|
||||
versionName "1.2.10"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
aaptOptions {
|
||||
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
||||
|
||||
4
fastlane/Appfile
Normal file
4
fastlane/Appfile
Normal file
@@ -0,0 +1,4 @@
|
||||
app_identifier("com.donetick.app")
|
||||
team_id("6UJJ78R3BS")
|
||||
|
||||
apple_id(ENV["APPLE_ID"]) if ENV["APPLE_ID"]
|
||||
113
fastlane/Fastfile
Normal file
113
fastlane/Fastfile
Normal file
@@ -0,0 +1,113 @@
|
||||
require "shellwords"
|
||||
|
||||
default_platform(:ios)
|
||||
|
||||
IOS_KEY_PATH = File.expand_path("../ios/AuthKey_84F695CDQ3.p8")
|
||||
IOS_OUTPUT_DIR = File.expand_path("../build/ios")
|
||||
ANDROID_PROJECT_DIR = File.expand_path("../android")
|
||||
PLAY_JSON_KEY_PATH = File.expand_path("../android/play-service-account.json")
|
||||
|
||||
platform :ios do
|
||||
desc "Build a signed, App Store-ready .ipa"
|
||||
lane :release do
|
||||
Dir.chdir(File.expand_path("../ios/App")) do
|
||||
Bundler.with_clean_env do
|
||||
sh("pod install --repo-update")
|
||||
end
|
||||
end
|
||||
|
||||
build_app(
|
||||
workspace: "ios/App/App.xcworkspace",
|
||||
scheme: "App",
|
||||
export_method: "app-store",
|
||||
output_directory: IOS_OUTPUT_DIR,
|
||||
output_name: "Donetick.ipa",
|
||||
export_options: {
|
||||
signingStyle: "manual",
|
||||
provisioningProfiles: {
|
||||
"com.donetick.app" => "Donetick App Store(fastline)",
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
UI.success("Signed .ipa at #{IOS_OUTPUT_DIR}/Donetick.ipa")
|
||||
end
|
||||
|
||||
desc "Upload the built .ipa to TestFlight"
|
||||
lane :upload do
|
||||
UI.user_error!("Missing #{IOS_KEY_PATH} — run scripts/pull-secrets.sh first") unless File.exist?(IOS_KEY_PATH)
|
||||
UI.user_error!("ASC_ISSUER_ID is not set (App Store Connect API key issuer id)") if ENV["ASC_ISSUER_ID"].to_s.empty?
|
||||
|
||||
ipa_path = File.join(IOS_OUTPUT_DIR, "Donetick.ipa")
|
||||
UI.user_error!("Missing #{ipa_path} — run `fastlane ios release` first") unless File.exist?(ipa_path)
|
||||
|
||||
api_key = app_store_connect_api_key(
|
||||
key_id: ENV["ASC_KEY_ID"] || "84F695CDQ3",
|
||||
issuer_id: ENV["ASC_ISSUER_ID"],
|
||||
key_filepath: IOS_KEY_PATH,
|
||||
duration: 1200,
|
||||
in_house: false,
|
||||
)
|
||||
|
||||
upload_to_testflight(
|
||||
api_key: api_key,
|
||||
ipa: ipa_path,
|
||||
skip_waiting_for_build_processing: true,
|
||||
skip_submission: true,
|
||||
)
|
||||
|
||||
UI.success("Uploaded to TestFlight")
|
||||
end
|
||||
end
|
||||
|
||||
platform :android do
|
||||
desc "Build a signed release .aab"
|
||||
lane :release do
|
||||
keystore_properties = File.join(ANDROID_PROJECT_DIR, "keystore.properties")
|
||||
UI.user_error!("Missing #{keystore_properties} — run scripts/pull-secrets.sh first") unless File.exist?(keystore_properties)
|
||||
|
||||
gradle(
|
||||
task: "bundle",
|
||||
build_type: "Release",
|
||||
project_dir: "#{ANDROID_PROJECT_DIR}/",
|
||||
)
|
||||
|
||||
aab_path = lane_context[SharedValues::GRADLE_AAB_OUTPUT_PATH]
|
||||
UI.success("Signed .aab at #{aab_path}")
|
||||
end
|
||||
|
||||
desc "Build a signed release .apk (for sideloading/testing)"
|
||||
lane :apk do
|
||||
keystore_properties = File.join(ANDROID_PROJECT_DIR, "keystore.properties")
|
||||
UI.user_error!("Missing #{keystore_properties} — run scripts/pull-secrets.sh first") unless File.exist?(keystore_properties)
|
||||
|
||||
gradle(
|
||||
task: "assemble",
|
||||
build_type: "Release",
|
||||
project_dir: "#{ANDROID_PROJECT_DIR}/",
|
||||
)
|
||||
|
||||
apk_path = lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH]
|
||||
UI.success("Signed .apk at #{apk_path}")
|
||||
end
|
||||
|
||||
desc "Upload the built .aab to the Play Store (internal testing track by default)"
|
||||
lane :upload do |options|
|
||||
UI.user_error!("Missing #{PLAY_JSON_KEY_PATH} — run scripts/pull-secrets.sh first") unless File.exist?(PLAY_JSON_KEY_PATH)
|
||||
|
||||
aab_path = File.join(ANDROID_PROJECT_DIR, "app/build/outputs/bundle/release/app-release.aab")
|
||||
UI.user_error!("Missing #{aab_path} — run `fastlane android release` first") unless File.exist?(aab_path)
|
||||
|
||||
upload_to_play_store(
|
||||
json_key: PLAY_JSON_KEY_PATH,
|
||||
package_name: "com.donetick.app",
|
||||
aab: aab_path,
|
||||
track: options[:track] || "internal",
|
||||
skip_upload_metadata: true,
|
||||
skip_upload_images: true,
|
||||
skip_upload_screenshots: true,
|
||||
)
|
||||
|
||||
UI.success("Uploaded to Play Store (#{options[:track] || 'internal'} track)")
|
||||
end
|
||||
end
|
||||
69
fastlane/README.md
Normal file
69
fastlane/README.md
Normal file
@@ -0,0 +1,69 @@
|
||||
fastlane documentation
|
||||
----
|
||||
|
||||
# Installation
|
||||
|
||||
Make sure you have the latest version of the Xcode command line tools installed:
|
||||
|
||||
```sh
|
||||
xcode-select --install
|
||||
```
|
||||
|
||||
For _fastlane_ installation instructions, see [Installing _fastlane_](https://docs.fastlane.tools/#installing-fastlane)
|
||||
|
||||
# Available Actions
|
||||
|
||||
## iOS
|
||||
|
||||
### ios release
|
||||
|
||||
```sh
|
||||
[bundle exec] fastlane ios release
|
||||
```
|
||||
|
||||
Build a signed, App Store-ready .ipa
|
||||
|
||||
### ios upload
|
||||
|
||||
```sh
|
||||
[bundle exec] fastlane ios upload
|
||||
```
|
||||
|
||||
Upload the built .ipa to TestFlight
|
||||
|
||||
----
|
||||
|
||||
|
||||
## Android
|
||||
|
||||
### android release
|
||||
|
||||
```sh
|
||||
[bundle exec] fastlane android release
|
||||
```
|
||||
|
||||
Build a signed release .aab
|
||||
|
||||
### android apk
|
||||
|
||||
```sh
|
||||
[bundle exec] fastlane android apk
|
||||
```
|
||||
|
||||
Build a signed release .apk (for sideloading/testing)
|
||||
|
||||
### android upload
|
||||
|
||||
```sh
|
||||
[bundle exec] fastlane android upload
|
||||
```
|
||||
|
||||
Upload the built .aab to the Play Store (internal testing track by default)
|
||||
|
||||
----
|
||||
|
||||
This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run.
|
||||
|
||||
More information about _fastlane_ can be found on [fastlane.tools](https://fastlane.tools).
|
||||
|
||||
The documentation of _fastlane_ can be found on [docs.fastlane.tools](https://docs.fastlane.tools).
|
||||
@@ -274,7 +274,6 @@
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
@@ -331,7 +330,6 @@
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
@@ -360,13 +358,13 @@
|
||||
CODE_SIGN_ALLOW_ENTITLEMENTS_MODIFICATION = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = App/App.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 28;
|
||||
CURRENT_PROJECT_VERSION = 31;
|
||||
DEVELOPMENT_TEAM = 6UJJ78R3BS;
|
||||
INFOPLIST_FILE = App/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
MARKETING_VERSION = 1.2.10;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.donetick.app;
|
||||
MARKETING_VERSION = 1.2.7;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
@@ -380,13 +378,15 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_ALLOW_ENTITLEMENTS_MODIFICATION = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = App/App.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 28;
|
||||
CODE_SIGN_IDENTITY = "Apple Distribution";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "Donetick App Store(fastline)";
|
||||
CURRENT_PROJECT_VERSION = 31;
|
||||
DEVELOPMENT_TEAM = 6UJJ78R3BS;
|
||||
INFOPLIST_FILE = App/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
MARKETING_VERSION = 1.2.7;
|
||||
MARKETING_VERSION = 1.2.10;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.donetick.app;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
|
||||
|
||||
@@ -31,7 +31,7 @@ PODS:
|
||||
- Capacitor
|
||||
- CapacitorNetwork (8.0.1):
|
||||
- Capacitor
|
||||
- CapacitorPluginSafeArea (5.0.0):
|
||||
- CapacitorPluginSafeArea (5.0.1):
|
||||
- Capacitor
|
||||
- CapacitorPreferences (8.0.1):
|
||||
- Capacitor
|
||||
@@ -41,9 +41,9 @@ PODS:
|
||||
- Capacitor
|
||||
- CapgoCapacitorDocumentScanner (8.4.0):
|
||||
- Capacitor
|
||||
- CapgoCapacitorNfc (8.1.7):
|
||||
- CapgoCapacitorNfc (8.2.2):
|
||||
- Capacitor
|
||||
- CapgoCapacitorSocialLogin (8.3.34):
|
||||
- CapgoCapacitorSocialLogin (8.3.36):
|
||||
- Alamofire (~> 5.10.2)
|
||||
- Capacitor
|
||||
- FBSDKCoreKit (~> 18.0)
|
||||
@@ -249,13 +249,13 @@ SPEC CHECKSUMS:
|
||||
CapacitorLocalLlm: a05516151a02923a9e7dae9949d3817e85e321f0
|
||||
CapacitorLocalNotifications: 2615aa008f608b95d3921a778ee1988abf1e6148
|
||||
CapacitorNetwork: 8812ce60d11fb63d8f2e4ba51a49b2e59892ebe2
|
||||
CapacitorPluginSafeArea: 9d0682951c011bf0b36e513a89103c5fbff7ac49
|
||||
CapacitorPluginSafeArea: 874619c00586248f1694210e72038123d422c2d9
|
||||
CapacitorPreferences: cca2021f386efb75947c850334447d9ff22b14f1
|
||||
CapacitorPushNotifications: ec08d589c226a2c0db7c032ec1bf5b044ec85f8e
|
||||
CapacitorStatusBar: 01d5763b4ed720de5ce2edbc938de6a98f4c8f32
|
||||
CapgoCapacitorDocumentScanner: 7ad9e8ed9c054d551bfc948660d995b9545723d8
|
||||
CapgoCapacitorNfc: 5bf5a951ca231d7c40f1ac20549d2abcd1b8f939
|
||||
CapgoCapacitorSocialLogin: b0e67630b9d9b5f14fff774c825c1b8fa85eff7b
|
||||
CapgoCapacitorNfc: 8ea158143c441e1cf6d231a971e375511558d027
|
||||
CapgoCapacitorSocialLogin: adffda2bec52a765bdaebf3fa9a6083bd9341d60
|
||||
FBAEMKit: 64088ff0380f50e4a56e2ee07d984f7f1aef7595
|
||||
FBSDKCoreKit: 8390ea3a8fac186f444275f31d7512e760b47cba
|
||||
FBSDKCoreKit_Basics: 3a0005c78b355388bf2df5c6dbe6381b77ea4be3
|
||||
|
||||
428
package-lock.json
generated
428
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "donetick",
|
||||
"private": true,
|
||||
"version": "1.2.7",
|
||||
"version": "1.2.10",
|
||||
"type": "module",
|
||||
"engines": {
|
||||
"node": ">=20.0.0",
|
||||
|
||||
98
scripts/release.sh
Executable file
98
scripts/release.sh
Executable file
@@ -0,0 +1,98 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
cd "$REPO_ROOT"
|
||||
|
||||
PLATFORM="both"
|
||||
BUMP="patch"
|
||||
SKIP_BUMP=false
|
||||
UPLOAD=false
|
||||
ANDROID_TRACK="internal"
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: $(basename "$0") [--android|--ios] [--bump patch|minor|major] [--skip-bump] [--upload] [--track TRACK]
|
||||
|
||||
Builds signed release artifacts for Android (.aab) and/or iOS (.ipa).
|
||||
|
||||
--android Build Android only
|
||||
--ios Build iOS only
|
||||
--bump TYPE Version bump type before building (default: patch)
|
||||
--skip-bump Don't bump the version, build with the current one
|
||||
--upload Also upload: Android to Play Store, iOS to TestFlight
|
||||
--track TRACK Play Store track for --upload (default: internal)
|
||||
-h, --help Show this help
|
||||
|
||||
Requires:
|
||||
- scripts/pull-secrets.sh to have been run (keystore, google-services, App Store Connect key, Play service account)
|
||||
- ASC_ISSUER_ID env var set (App Store Connect API key issuer id) for iOS builds/uploads
|
||||
- bundle install (fastlane) run at least once
|
||||
EOF
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--android) PLATFORM="android"; shift ;;
|
||||
--ios) PLATFORM="ios"; shift ;;
|
||||
--bump) BUMP="$2"; shift 2 ;;
|
||||
--skip-bump) SKIP_BUMP=true; shift ;;
|
||||
--upload) UPLOAD=true; shift ;;
|
||||
--track) ANDROID_TRACK="$2"; shift 2 ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) echo "Unknown option: $1" >&2; usage; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -f "$REPO_ROOT/.env.production" ]; then
|
||||
set -a
|
||||
# shellcheck disable=SC1091
|
||||
source "$REPO_ROOT/.env.production"
|
||||
set +a
|
||||
fi
|
||||
|
||||
if [ "$SKIP_BUMP" = false ]; then
|
||||
echo "→ Bumping version ($BUMP)"
|
||||
node bump-version.js "$BUMP"
|
||||
fi
|
||||
|
||||
echo "→ Building web assets"
|
||||
npm run build
|
||||
|
||||
echo "→ Syncing Capacitor"
|
||||
npx cap sync
|
||||
|
||||
if [ -f Gemfile ] && command -v bundle >/dev/null 2>&1; then
|
||||
RUN="bundle exec fastlane"
|
||||
else
|
||||
RUN="fastlane"
|
||||
fi
|
||||
|
||||
if [ "$PLATFORM" = "android" ] || [ "$PLATFORM" = "both" ]; then
|
||||
echo "→ Building signed Android release (.aab)"
|
||||
$RUN android release
|
||||
|
||||
if [ "$UPLOAD" = true ]; then
|
||||
echo "→ Uploading Android release to Play Store ($ANDROID_TRACK track)"
|
||||
$RUN android upload track:"$ANDROID_TRACK"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$PLATFORM" = "ios" ] || [ "$PLATFORM" = "both" ]; then
|
||||
echo "→ Building signed iOS release (.ipa)"
|
||||
$RUN ios release
|
||||
|
||||
if [ "$UPLOAD" = true ]; then
|
||||
echo "→ Uploading iOS release to TestFlight"
|
||||
$RUN ios upload
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "✓ Release build complete"
|
||||
[ -f android/app/build/outputs/bundle/release/app-release.aab ] && echo " Android: android/app/build/outputs/bundle/release/app-release.aab"
|
||||
[ -f build/ios/Donetick.ipa ] && echo " iOS: build/ios/Donetick.ipa"
|
||||
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " git add . && git commit -m \"Release \$(node -p \"require('./package.json').version\")\""
|
||||
echo " git tag v\$(node -p \"require('./package.json').version\") && git push origin develop --tags"
|
||||
Reference in New Issue
Block a user