@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.
Files changed (65) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +19 -0
  3. package/dist/bin.d.ts +3 -0
  4. package/dist/bin.d.ts.map +1 -0
  5. package/dist/bin.js +26 -0
  6. package/dist/bin.js.map +1 -0
  7. package/dist/commands/add.d.ts +6 -0
  8. package/dist/commands/add.d.ts.map +1 -0
  9. package/dist/commands/add.js +44 -0
  10. package/dist/commands/add.js.map +1 -0
  11. package/dist/commands/doctor.d.ts +2 -0
  12. package/dist/commands/doctor.d.ts.map +1 -0
  13. package/dist/commands/doctor.js +97 -0
  14. package/dist/commands/doctor.js.map +1 -0
  15. package/dist/commands/init.d.ts +5 -0
  16. package/dist/commands/init.d.ts.map +1 -0
  17. package/dist/commands/init.js +20 -0
  18. package/dist/commands/init.js.map +1 -0
  19. package/dist/commands/migrate.d.ts +6 -0
  20. package/dist/commands/migrate.d.ts.map +1 -0
  21. package/dist/commands/migrate.js +37 -0
  22. package/dist/commands/migrate.js.map +1 -0
  23. package/dist/commands/modules.d.ts +14 -0
  24. package/dist/commands/modules.d.ts.map +1 -0
  25. package/dist/commands/modules.js +104 -0
  26. package/dist/commands/modules.js.map +1 -0
  27. package/dist/commands/secrets.d.ts +10 -0
  28. package/dist/commands/secrets.d.ts.map +1 -0
  29. package/dist/commands/secrets.js +55 -0
  30. package/dist/commands/secrets.js.map +1 -0
  31. package/dist/commands/update.d.ts +4 -0
  32. package/dist/commands/update.d.ts.map +1 -0
  33. package/dist/commands/update.js +57 -0
  34. package/dist/commands/update.js.map +1 -0
  35. package/dist/modules.d.ts +42 -0
  36. package/dist/modules.d.ts.map +1 -0
  37. package/dist/modules.js +69 -0
  38. package/dist/modules.js.map +1 -0
  39. package/dist/util.d.ts +38 -0
  40. package/dist/util.d.ts.map +1 -0
  41. package/dist/util.js +74 -0
  42. package/dist/util.js.map +1 -0
  43. package/package.json +34 -0
  44. package/secrets.schema.json +27 -0
  45. package/templates/AGENTS.md +31 -0
  46. package/templates/CLAUDE.md +3 -0
  47. package/templates/GEMINI.md +3 -0
  48. package/templates/gitignore +19 -0
  49. package/templates/modules/deploy-cloudflare/files/deploy-api.yml +45 -0
  50. package/templates/modules/deploy-cloudflare/files/deploy-web.yml +44 -0
  51. package/templates/modules/deploy-cloudflare/files/web-wrangler.jsonc +10 -0
  52. package/templates/modules/deploy-cloudflare/module.json +21 -0
  53. package/templates/modules/deploy-cloudflare/secrets.json +7 -0
  54. package/templates/modules/deploy-fastlane/files/Gemfile +3 -0
  55. package/templates/modules/deploy-fastlane/files/android/Appfile +1 -0
  56. package/templates/modules/deploy-fastlane/files/android/Fastfile +33 -0
  57. package/templates/modules/deploy-fastlane/files/android/key.properties.example +5 -0
  58. package/templates/modules/deploy-fastlane/files/ios/Appfile +2 -0
  59. package/templates/modules/deploy-fastlane/files/ios/ExportOptions.plist +19 -0
  60. package/templates/modules/deploy-fastlane/files/ios/Fastfile +52 -0
  61. package/templates/modules/deploy-fastlane/files/ios/Matchfile +5 -0
  62. package/templates/modules/deploy-fastlane/files/mobile-release.yml +87 -0
  63. package/templates/modules/deploy-fastlane/module.json +29 -0
  64. package/templates/modules/deploy-fastlane/secrets.json +16 -0
  65. 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,5 @@
1
+ git_url(ENV["MATCH_GIT_URL"])
2
+ storage_mode("git")
3
+ type("appstore")
4
+ app_identifier(["{{BUNDLE_ID}}"])
5
+ team_id("{{APPLE_TEAM_ID}}")
@@ -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
+ }