@softwareseva/cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +19 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +26 -0
- package/dist/bin.js.map +1 -0
- package/dist/commands/add.d.ts +6 -0
- package/dist/commands/add.d.ts.map +1 -0
- package/dist/commands/add.js +44 -0
- package/dist/commands/add.js.map +1 -0
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +97 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/init.d.ts +5 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +20 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/migrate.d.ts +6 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +37 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/modules.d.ts +14 -0
- package/dist/commands/modules.d.ts.map +1 -0
- package/dist/commands/modules.js +104 -0
- package/dist/commands/modules.js.map +1 -0
- package/dist/commands/secrets.d.ts +10 -0
- package/dist/commands/secrets.d.ts.map +1 -0
- package/dist/commands/secrets.js +55 -0
- package/dist/commands/secrets.js.map +1 -0
- package/dist/commands/update.d.ts +4 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +57 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/modules.d.ts +42 -0
- package/dist/modules.d.ts.map +1 -0
- package/dist/modules.js +69 -0
- package/dist/modules.js.map +1 -0
- package/dist/util.d.ts +38 -0
- package/dist/util.d.ts.map +1 -0
- package/dist/util.js +74 -0
- package/dist/util.js.map +1 -0
- package/package.json +34 -0
- package/secrets.schema.json +27 -0
- package/templates/AGENTS.md +31 -0
- package/templates/CLAUDE.md +3 -0
- package/templates/GEMINI.md +3 -0
- package/templates/gitignore +19 -0
- package/templates/modules/deploy-cloudflare/files/deploy-api.yml +45 -0
- package/templates/modules/deploy-cloudflare/files/deploy-web.yml +44 -0
- package/templates/modules/deploy-cloudflare/files/web-wrangler.jsonc +10 -0
- package/templates/modules/deploy-cloudflare/module.json +21 -0
- package/templates/modules/deploy-cloudflare/secrets.json +7 -0
- package/templates/modules/deploy-fastlane/files/Gemfile +3 -0
- package/templates/modules/deploy-fastlane/files/android/Appfile +1 -0
- package/templates/modules/deploy-fastlane/files/android/Fastfile +33 -0
- package/templates/modules/deploy-fastlane/files/android/key.properties.example +5 -0
- package/templates/modules/deploy-fastlane/files/ios/Appfile +2 -0
- package/templates/modules/deploy-fastlane/files/ios/ExportOptions.plist +19 -0
- package/templates/modules/deploy-fastlane/files/ios/Fastfile +52 -0
- package/templates/modules/deploy-fastlane/files/ios/Matchfile +5 -0
- package/templates/modules/deploy-fastlane/files/mobile-release.yml +87 -0
- package/templates/modules/deploy-fastlane/module.json +29 -0
- package/templates/modules/deploy-fastlane/secrets.json +16 -0
- package/templates/renovate.json +7 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>method</key>
|
|
6
|
+
<string>app-store-connect</string>
|
|
7
|
+
<key>teamID</key>
|
|
8
|
+
<string>{{APPLE_TEAM_ID}}</string>
|
|
9
|
+
<key>signingStyle</key>
|
|
10
|
+
<string>manual</string>
|
|
11
|
+
<key>provisioningProfiles</key>
|
|
12
|
+
<dict>
|
|
13
|
+
<key>{{BUNDLE_ID}}</key>
|
|
14
|
+
<string>match AppStore {{BUNDLE_ID}}</string>
|
|
15
|
+
</dict>
|
|
16
|
+
<key>uploadSymbols</key>
|
|
17
|
+
<true/>
|
|
18
|
+
</dict>
|
|
19
|
+
</plist>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# iOS lanes. Run from ios/: bundle exec fastlane <lane>. Paths are resolved from this file, not the working directory.
|
|
2
|
+
default_platform(:ios)
|
|
3
|
+
|
|
4
|
+
APP_ROOT = File.expand_path("../..", __dir__)
|
|
5
|
+
BUNDLE_ID = "{{BUNDLE_ID}}"
|
|
6
|
+
|
|
7
|
+
platform :ios do
|
|
8
|
+
def api_key
|
|
9
|
+
app_store_connect_api_key(
|
|
10
|
+
key_id: ENV.fetch("APP_STORE_CONNECT_API_KEY_ID"),
|
|
11
|
+
issuer_id: ENV.fetch("APP_STORE_CONNECT_ISSUER_ID"),
|
|
12
|
+
key_content: ENV.fetch("APP_STORE_CONNECT_API_KEY_CONTENT"),
|
|
13
|
+
is_key_content_base64: true
|
|
14
|
+
)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def build_ipa(key)
|
|
18
|
+
setup_ci if ENV["CI"]
|
|
19
|
+
match(type: "appstore", readonly: is_ci, api_key: key)
|
|
20
|
+
update_code_signing_settings(
|
|
21
|
+
use_automatic_signing: false,
|
|
22
|
+
path: File.join(APP_ROOT, "ios/Runner.xcodeproj"),
|
|
23
|
+
targets: ["Runner"],
|
|
24
|
+
team_id: CredentialsManager::AppfileConfig.try_fetch_value(:team_id),
|
|
25
|
+
profile_name: "match AppStore #{BUNDLE_ID}",
|
|
26
|
+
code_sign_identity: "Apple Distribution"
|
|
27
|
+
)
|
|
28
|
+
build_number = ENV["BUILD_NUMBER"] || (latest_testflight_build_number(api_key: key, app_identifier: BUNDLE_ID) + 1).to_s
|
|
29
|
+
Dir.chdir(APP_ROOT) do
|
|
30
|
+
sh("flutter", "build", "ipa", "--release", "--build-number=#{build_number}", "--export-options-plist=ios/ExportOptions.plist")
|
|
31
|
+
end
|
|
32
|
+
Dir[File.join(APP_ROOT, "build/ios/ipa/*.ipa")].first || UI.user_error!("No .ipa produced")
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
desc "Create or fetch signing certificates and profiles (run once on a Mac with admin access)"
|
|
36
|
+
lane :certificates do
|
|
37
|
+
match(type: "development", api_key: api_key)
|
|
38
|
+
match(type: "appstore", api_key: api_key)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
desc "Build and upload to TestFlight"
|
|
42
|
+
lane :beta do
|
|
43
|
+
key = api_key
|
|
44
|
+
upload_to_testflight(api_key: key, ipa: build_ipa(key), skip_waiting_for_build_processing: true)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
desc "Build and upload to App Store Connect (submit for review in the web UI)"
|
|
48
|
+
lane :release do
|
|
49
|
+
key = api_key
|
|
50
|
+
upload_to_app_store(api_key: key, ipa: build_ipa(key), skip_metadata: true, skip_screenshots: true, submit_for_review: false, precheck_include_in_app_purchases: false)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
name: mobile-release
|
|
2
|
+
|
|
3
|
+
# Manual run, or push a tag like mobile-v1.4.0 to ship both platforms to their test tracks.
|
|
4
|
+
on:
|
|
5
|
+
workflow_dispatch:
|
|
6
|
+
inputs:
|
|
7
|
+
platform:
|
|
8
|
+
type: choice
|
|
9
|
+
options: [both, ios, android]
|
|
10
|
+
default: both
|
|
11
|
+
lane:
|
|
12
|
+
type: choice
|
|
13
|
+
options: [beta, release]
|
|
14
|
+
default: beta
|
|
15
|
+
push:
|
|
16
|
+
tags: ["mobile-v*"]
|
|
17
|
+
|
|
18
|
+
env:
|
|
19
|
+
LANE: ${{ inputs.lane || 'beta' }}
|
|
20
|
+
BUILD_NUMBER: ${{ github.run_number }}
|
|
21
|
+
|
|
22
|
+
jobs:
|
|
23
|
+
ios:
|
|
24
|
+
if: ${{ (inputs.platform || 'both') != 'android' }}
|
|
25
|
+
runs-on: macos-15
|
|
26
|
+
defaults:
|
|
27
|
+
run:
|
|
28
|
+
working-directory: {{APP_DIR}}
|
|
29
|
+
steps:
|
|
30
|
+
- uses: actions/checkout@v4
|
|
31
|
+
- uses: subosito/flutter-action@v2
|
|
32
|
+
with:
|
|
33
|
+
channel: {{FLUTTER_VERSION}}
|
|
34
|
+
cache: true
|
|
35
|
+
- uses: ruby/setup-ruby@v1
|
|
36
|
+
with:
|
|
37
|
+
ruby-version: "3.3"
|
|
38
|
+
bundler-cache: true
|
|
39
|
+
working-directory: {{APP_DIR}}
|
|
40
|
+
- run: flutter pub get
|
|
41
|
+
- run: bundle exec fastlane ios "$LANE"
|
|
42
|
+
working-directory: {{APP_DIR}}/ios
|
|
43
|
+
env:
|
|
44
|
+
BUNDLE_GEMFILE: ${{ github.workspace }}/{{APP_DIR}}/Gemfile
|
|
45
|
+
APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
|
|
46
|
+
APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}
|
|
47
|
+
APP_STORE_CONNECT_API_KEY_CONTENT: ${{ secrets.APP_STORE_CONNECT_API_KEY_CONTENT }}
|
|
48
|
+
MATCH_GIT_URL: ${{ secrets.MATCH_GIT_URL }}
|
|
49
|
+
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
|
|
50
|
+
MATCH_GIT_BASIC_AUTHORIZATION: ${{ secrets.MATCH_GIT_BASIC_AUTHORIZATION }}
|
|
51
|
+
|
|
52
|
+
android:
|
|
53
|
+
if: ${{ (inputs.platform || 'both') != 'ios' }}
|
|
54
|
+
runs-on: ubuntu-latest
|
|
55
|
+
defaults:
|
|
56
|
+
run:
|
|
57
|
+
working-directory: {{APP_DIR}}
|
|
58
|
+
steps:
|
|
59
|
+
- uses: actions/checkout@v4
|
|
60
|
+
- uses: actions/setup-java@v4
|
|
61
|
+
with:
|
|
62
|
+
distribution: temurin
|
|
63
|
+
java-version: "17"
|
|
64
|
+
- uses: subosito/flutter-action@v2
|
|
65
|
+
with:
|
|
66
|
+
channel: {{FLUTTER_VERSION}}
|
|
67
|
+
cache: true
|
|
68
|
+
- uses: ruby/setup-ruby@v1
|
|
69
|
+
with:
|
|
70
|
+
ruby-version: "3.3"
|
|
71
|
+
bundler-cache: true
|
|
72
|
+
working-directory: {{APP_DIR}}
|
|
73
|
+
- name: Write signing config
|
|
74
|
+
env:
|
|
75
|
+
ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
|
|
76
|
+
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
|
|
77
|
+
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
|
|
78
|
+
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
|
|
79
|
+
run: |
|
|
80
|
+
echo "$ANDROID_KEYSTORE_BASE64" | base64 --decode > android/app/upload-keystore.jks
|
|
81
|
+
printf 'storePassword=%s\nkeyPassword=%s\nkeyAlias=%s\nstoreFile=upload-keystore.jks\n' "$ANDROID_KEYSTORE_PASSWORD" "$ANDROID_KEY_PASSWORD" "$ANDROID_KEY_ALIAS" > android/key.properties
|
|
82
|
+
- run: flutter pub get
|
|
83
|
+
- run: bundle exec fastlane android "$LANE"
|
|
84
|
+
working-directory: {{APP_DIR}}/android
|
|
85
|
+
env:
|
|
86
|
+
BUNDLE_GEMFILE: ${{ github.workspace }}/{{APP_DIR}}/Gemfile
|
|
87
|
+
PLAY_SERVICE_ACCOUNT_JSON: ${{ secrets.PLAY_SERVICE_ACCOUNT_JSON }}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "deploy-fastlane",
|
|
3
|
+
"description": "fastlane lanes and a GitHub Actions workflow that build a Flutter app and upload it to TestFlight / the App Store and Google Play, with certificates from fastlane match and an App Store Connect API key.",
|
|
4
|
+
"vars": {
|
|
5
|
+
"APP_DIR": { "detect": "flutterDir", "default": ".", "help": "Flutter project directory relative to the repo root" },
|
|
6
|
+
"BUNDLE_ID": { "detect": "iosBundleId", "default": "com.example.app" },
|
|
7
|
+
"PACKAGE_NAME": { "detect": "androidPackage", "default": "com.example.app" },
|
|
8
|
+
"APPLE_TEAM_ID": { "default": "ABCDE12345", "help": "10-character Apple team id" },
|
|
9
|
+
"FLUTTER_VERSION": { "default": "stable" }
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
{ "from": "Gemfile", "to": "{{APP_DIR}}/Gemfile" },
|
|
13
|
+
{ "from": "ios/Fastfile", "to": "{{APP_DIR}}/ios/fastlane/Fastfile" },
|
|
14
|
+
{ "from": "ios/Appfile", "to": "{{APP_DIR}}/ios/fastlane/Appfile" },
|
|
15
|
+
{ "from": "ios/Matchfile", "to": "{{APP_DIR}}/ios/fastlane/Matchfile" },
|
|
16
|
+
{ "from": "ios/ExportOptions.plist", "to": "{{APP_DIR}}/ios/ExportOptions.plist" },
|
|
17
|
+
{ "from": "android/Fastfile", "to": "{{APP_DIR}}/android/fastlane/Fastfile" },
|
|
18
|
+
{ "from": "android/Appfile", "to": "{{APP_DIR}}/android/fastlane/Appfile" },
|
|
19
|
+
{ "from": "android/key.properties.example", "to": "{{APP_DIR}}/android/key.properties.example" },
|
|
20
|
+
{ "from": "mobile-release.yml", "to": ".github/workflows/mobile-release.yml" }
|
|
21
|
+
],
|
|
22
|
+
"next": [
|
|
23
|
+
"Android signing: add the key.properties block from the deploy-flutter-fastlane skill (references/android-signing.md) to {{APP_DIR}}/android/app/build.gradle.kts.",
|
|
24
|
+
"Create the upload keystore once: keytool -genkey -v -keystore upload-keystore.jks -keyalg RSA -keysize 2048 -validity 10000 -alias upload (keep it out of git; back it up in a password manager).",
|
|
25
|
+
"Create a private git repo for match certificates, then from {{APP_DIR}}/ios run: bundle install && bundle exec fastlane certificates",
|
|
26
|
+
"Upload the first Android build to Google Play by hand (Play Console requires it before the API can upload).",
|
|
27
|
+
"Set the repository secrets listed by: npx @softwareseva/cli secrets"
|
|
28
|
+
]
|
|
29
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"package": "deploy-fastlane",
|
|
3
|
+
"secrets": [
|
|
4
|
+
{ "name": "APP_STORE_CONNECT_API_KEY_ID", "usedBy": "iOS upload", "description": "Key id of an App Store Connect API key with App Manager access.", "generate": "App Store Connect > Users and Access > Integrations > App Store Connect API > Team Keys > +", "store": ["ci-secret"] },
|
|
5
|
+
{ "name": "APP_STORE_CONNECT_ISSUER_ID", "usedBy": "iOS upload", "description": "Issuer id shown above the keys list.", "generate": "same page as the key", "store": ["ci-secret"] },
|
|
6
|
+
{ "name": "APP_STORE_CONNECT_API_KEY_CONTENT", "usedBy": "iOS upload", "description": "The AuthKey_XXXX.p8 file, base64-encoded (downloadable once).", "generate": "base64 -i AuthKey_XXXX.p8 | pbcopy", "store": ["ci-secret", "keychain"], "gitignore": ["*.p8"] },
|
|
7
|
+
{ "name": "MATCH_GIT_URL", "usedBy": "iOS signing", "description": "HTTPS URL of the private repo that stores encrypted certificates and profiles.", "generate": "create an empty private repository, e.g. github.com/you/certificates", "store": ["ci-secret"] },
|
|
8
|
+
{ "name": "MATCH_PASSWORD", "usedBy": "iOS signing", "description": "Passphrase that encrypts the match repo.", "generate": "chosen the first time you run fastlane match; store it in a password manager", "store": ["ci-secret", "keychain"], "rotate": "only by re-encrypting the repo (fastlane match change_password)" },
|
|
9
|
+
{ "name": "MATCH_GIT_BASIC_AUTHORIZATION", "usedBy": "iOS signing", "description": "base64 of 'username:token' for a token that can read the match repo.", "generate": "GitHub fine-grained token with read access to the certificates repo; then: printf 'user:token' | base64", "store": ["ci-secret"], "rotate": "when the token expires" },
|
|
10
|
+
{ "name": "ANDROID_KEYSTORE_BASE64", "usedBy": "Android signing", "description": "The upload keystore (.jks), base64-encoded.", "generate": "keytool -genkey ... (see next steps); then base64 -i upload-keystore.jks | pbcopy", "store": ["ci-secret", "keychain"], "gitignore": ["*.jks", "*.keystore", "key.properties"] },
|
|
11
|
+
{ "name": "ANDROID_KEYSTORE_PASSWORD", "usedBy": "Android signing", "description": "Store password of the upload keystore.", "generate": "chosen when creating the keystore", "store": ["ci-secret", "keychain"] },
|
|
12
|
+
{ "name": "ANDROID_KEY_ALIAS", "usedBy": "Android signing", "description": "Alias of the upload key, usually 'upload'.", "generate": "chosen when creating the keystore", "store": ["ci-secret"] },
|
|
13
|
+
{ "name": "ANDROID_KEY_PASSWORD", "usedBy": "Android signing", "description": "Password of the upload key.", "generate": "chosen when creating the keystore", "store": ["ci-secret", "keychain"] },
|
|
14
|
+
{ "name": "PLAY_SERVICE_ACCOUNT_JSON", "usedBy": "Android upload", "description": "JSON key of a Google Cloud service account invited to Play Console with release permissions.", "generate": "Google Cloud > IAM > Service accounts > create > Keys > JSON; Play Console > Users and permissions > invite the service account email with 'Release to testing tracks' (and production if needed).", "store": ["ci-secret"], "gitignore": ["play-service-account*.json"] }
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
|
3
|
+
"extends": ["config:recommended"],
|
|
4
|
+
"packageRules": [
|
|
5
|
+
{ "groupName": "kashi", "matchPackageNames": ["/^@softwareseva\\//", "/^kashi_/"], "automerge": false, "schedule": ["before 6am on monday"] }
|
|
6
|
+
]
|
|
7
|
+
}
|