@makefully/adaptfully 4.0.0 → 4.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/CHANGELOG.md +8 -0
- package/README.md +15 -1
- package/docs/credentials/README.md +50 -0
- package/docs/credentials/android.md +124 -0
- package/docs/credentials/ios.md +182 -0
- package/docs/credentials/steam.md +99 -0
- package/lib/node/android-keystore.js +243 -0
- package/lib/node/apple-signing.js +392 -0
- package/lib/node/index.js +20 -0
- package/lib/node/pipeline.js +11 -3
- package/lib/node/publish-credentials.js +47 -1
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project are documented in this file.
|
|
4
4
|
|
|
5
|
+
## 4.1.0 — 2026-09-04
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **`adaptfully android-keystore`** — Generate debug/release keystores with `keytool` and write `assets/meta/deployments/android/build.json`.
|
|
10
|
+
- **`adaptfully apple-signing`** — Generate CSR/key (OpenSSL), export `.p12` from an Apple `.cer`, place provisioning profiles under `ios/apple/`, optional `apple.p12Password` in `build.json`.
|
|
11
|
+
- **Credential setup guides** — First-time walkthroughs under [`docs/credentials/`](docs/credentials/README.md) for iOS, Android, and Steam (full manual steps + helper skip markers).
|
|
12
|
+
|
|
5
13
|
## 4.0.0 — 2026-09-03
|
|
6
14
|
|
|
7
15
|
### Removed
|
package/README.md
CHANGED
|
@@ -29,6 +29,8 @@ adaptfully deploy steam # prebuild + POST to Wrapfully ste
|
|
|
29
29
|
adaptfully steam-publish # one-time: log in with steamcmd → assets/meta/deployments/steam/steam.json
|
|
30
30
|
adaptfully google-publish --from ./sa.json # one-time: import Play service account → deployments/android/google.json
|
|
31
31
|
adaptfully apple-publish # one-time: write App Store Connect creds → deployments/ios/apple.json
|
|
32
|
+
adaptfully android-keystore # one-time: generate keystores + android build.json
|
|
33
|
+
adaptfully apple-signing # one-time: CSR / .p12 / provisioning files under ios/apple/
|
|
32
34
|
```
|
|
33
35
|
|
|
34
36
|
| Stage | What it does |
|
|
@@ -39,6 +41,10 @@ adaptfully apple-publish # one-time: write App Store Connec
|
|
|
39
41
|
| `steam-publish` | One-time local setup: install steamcmd, interactive login, write the `steam` deployment's `steam.json` (also updates `.gitignore`) |
|
|
40
42
|
| `google-publish` | One-time local setup: import a Play Console service-account JSON into the `android` deployment folder (also updates `.gitignore`) |
|
|
41
43
|
| `apple-publish` | One-time local setup: write App Store Connect credentials into the `ios` deployment folder (also updates `.gitignore`) |
|
|
44
|
+
| `android-keystore` | One-time local setup: generate debug/release keystores and write `android` `build.json` (also updates `.gitignore`) |
|
|
45
|
+
| `apple-signing` | One-time local setup: CSR → Apple `.cer` → `.p12` / provisioning profiles under `ios/apple/` (also updates `.gitignore`) |
|
|
46
|
+
|
|
47
|
+
First-time walkthroughs (manual steps + helper skip markers): [docs/credentials/](docs/credentials/README.md).
|
|
42
48
|
|
|
43
49
|
`wrapfully-deploy` is a compatibility alias for `adaptfully deploy` when invoked with a Wrapfully builder name (`steam`, `win`, `android`, etc.).
|
|
44
50
|
|
|
@@ -547,10 +553,14 @@ For a single platform, pass the specific builder name.
|
|
|
547
553
|
|
|
548
554
|
### Platform package requirements
|
|
549
555
|
|
|
556
|
+
**First-time setup:** see [docs/credentials/](docs/credentials/README.md) for iOS, Android, and Steam walkthroughs (CLI helpers + full manual steps).
|
|
557
|
+
|
|
550
558
|
Signing keys, provisioning profiles, and store credentials go in the deployment folder `./assets/meta/deployments/<deployment>/` on disk (the selected deployment is sent as `meta/publish/` in the zip). The examples below use `<deployment>` as a placeholder for the target key (e.g. `steam`, `web-prod`). **These files contain secrets** — add them to `.gitignore` and never commit them to a public repository. (Projects that have not adopted the `deployments/` layout may still place these under the legacy `./assets/meta/publish/`.)
|
|
551
559
|
|
|
552
560
|
#### Android (`android`, `android-dev`)
|
|
553
561
|
|
|
562
|
+
First-time setup: [docs/credentials/android.md](docs/credentials/android.md) (`android-keystore`, `google-publish`).
|
|
563
|
+
|
|
554
564
|
Place keystore files in `assets/meta/deployments/<deployment>/android/`. Include `assets/meta/deployments/<deployment>/build.json`:
|
|
555
565
|
|
|
556
566
|
```json
|
|
@@ -602,6 +612,8 @@ The file looks like:
|
|
|
602
612
|
|
|
603
613
|
#### Apple (`ios`, `ios-dev`, `ios-sim`, `mac`)
|
|
604
614
|
|
|
615
|
+
First-time setup: [docs/credentials/ios.md](docs/credentials/ios.md) (`apple-signing`, `apple-publish`).
|
|
616
|
+
|
|
605
617
|
**Capacitor iOS** signing is file-based under the deployment folder:
|
|
606
618
|
|
|
607
619
|
```
|
|
@@ -614,7 +626,7 @@ assets/meta/deployments/<deployment>/
|
|
|
614
626
|
build.json # optional: { "apple": { "p12Password": "..." } }
|
|
615
627
|
```
|
|
616
628
|
|
|
617
|
-
`adaptfully build` (zip-only) still packages this deployment folder for Capacitor so device debug builds can sign. Optional `build.json` may set `apple.p12Password` for the `.p12` passphrase (
|
|
629
|
+
`adaptfully build` (zip-only) still packages this deployment folder for Capacitor so device debug builds can sign. Optional `build.json` may set `apple.p12Password` for the `.p12` passphrase (defaults to empty when omitted). Team ID and profile UUID are read from the `.mobileprovision`. Enable **Sign in with Apple** on the App ID when using Capgo Apple auth.
|
|
618
630
|
|
|
619
631
|
To deploy to the App Store, include `assets/meta/deployments/<deployment>/apple.json`. The easiest path is:
|
|
620
632
|
|
|
@@ -638,6 +650,8 @@ iOS IPA upload uses `username` + `password` via `xcrun altool --upload-app`. `ca
|
|
|
638
650
|
|
|
639
651
|
#### Steam (`steam`, `steam-dev`)
|
|
640
652
|
|
|
653
|
+
First-time setup: [docs/credentials/steam.md](docs/credentials/steam.md) (`steam-publish`).
|
|
654
|
+
|
|
641
655
|
`steam-dev` builds debug Electron binaries for Windows, Mac, and Linux without uploading to Steam. No `steam.json` credentials are required.
|
|
642
656
|
|
|
643
657
|
For release uploads, run **`adaptfully steam-publish`** once on your machine. It installs steamcmd if needed, logs you in interactively (enter a Steam Guard code if prompted), and writes `assets/meta/deployments/steam/steam.json`:
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Credential setup guides
|
|
2
|
+
|
|
3
|
+
One-time local setup for signing and store-upload credentials under
|
|
4
|
+
`assets/meta/deployments/<deployment>/`.
|
|
5
|
+
|
|
6
|
+
These guides stay focused on **files in your game repo** and Adaptfully CLI helpers.
|
|
7
|
+
They do not cover which OS builds the artifact.
|
|
8
|
+
|
|
9
|
+
## Guides
|
|
10
|
+
|
|
11
|
+
| Platform | Guide | Typical deployment folder |
|
|
12
|
+
|----------|--------|---------------------------|
|
|
13
|
+
| iOS | [ios.md](./ios.md) | `assets/meta/deployments/ios/` |
|
|
14
|
+
| Android | [android.md](./android.md) | `assets/meta/deployments/android/` |
|
|
15
|
+
| Steam | [steam.md](./steam.md) | `assets/meta/deployments/steam/` |
|
|
16
|
+
|
|
17
|
+
## Helpers
|
|
18
|
+
|
|
19
|
+
| Command | Writes |
|
|
20
|
+
|---------|--------|
|
|
21
|
+
| `npx adaptfully android-keystore` | `android/*.keystore` + `build.json` |
|
|
22
|
+
| `npx adaptfully google-publish --from <sa.json>` | `google.json` |
|
|
23
|
+
| `npx adaptfully apple-signing` | CSR/key work files, `apple/*.p12`, optional `.mobileprovision` + `build.json` `apple.p12Password` |
|
|
24
|
+
| `npx adaptfully apple-publish` | `apple.json` (App Store Connect upload login) |
|
|
25
|
+
| `npx adaptfully steam-publish` | `steam.json` |
|
|
26
|
+
|
|
27
|
+
Each guide lists the **full manual process** and marks **Helper** steps with **skip to step N** when you use a command instead.
|
|
28
|
+
|
|
29
|
+
## Debug vs release
|
|
30
|
+
|
|
31
|
+
| Goal | Credentials needed? |
|
|
32
|
+
|------|---------------------|
|
|
33
|
+
| `android-dev` / local debug APK | Project debug keystore (recommended) or host default |
|
|
34
|
+
| `android` Play release | Release keystore + `google.json` |
|
|
35
|
+
| `ios-dev` device IPA | `development.p12` + `development.mobileprovision` |
|
|
36
|
+
| `ios` App Store | `distribution.p12` + `app-store.mobileprovision` + `apple.json` |
|
|
37
|
+
| `ios-sim` | No device signing files |
|
|
38
|
+
| `steam-dev` | None |
|
|
39
|
+
| `steam` upload | `steam.json` |
|
|
40
|
+
|
|
41
|
+
## Secrets
|
|
42
|
+
|
|
43
|
+
Do not commit credential files. Helpers update `.gitignore` with Adaptfully patterns such as:
|
|
44
|
+
|
|
45
|
+
- `assets/meta/deployments/**/build.json`
|
|
46
|
+
- `assets/meta/deployments/**/google.json`
|
|
47
|
+
- `assets/meta/deployments/**/apple.json`
|
|
48
|
+
- `assets/meta/deployments/**/steam.json`
|
|
49
|
+
- `assets/meta/deployments/**/android/`
|
|
50
|
+
- `assets/meta/deployments/**/apple/`
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# Android credentials
|
|
2
|
+
|
|
3
|
+
Set up signing keystores and (for Play uploads) a Google Play service account.
|
|
4
|
+
|
|
5
|
+
**Default folder:** `assets/meta/deployments/android/`
|
|
6
|
+
|
|
7
|
+
## Recommended path
|
|
8
|
+
|
|
9
|
+
1. **Helper:** `npx adaptfully android-keystore` — creates debug + release keystores and `build.json`.
|
|
10
|
+
2. In Google Play Console, create a service account and download its JSON key (manual).
|
|
11
|
+
3. **Helper:** `npx adaptfully google-publish --from ./path/to/sa.json` — writes `google.json`.
|
|
12
|
+
|
|
13
|
+
Debug-only: `npx adaptfully android-keystore --debug-only`.
|
|
14
|
+
|
|
15
|
+
## What you need
|
|
16
|
+
|
|
17
|
+
| Goal | Files |
|
|
18
|
+
|------|--------|
|
|
19
|
+
| Debug / `android-dev` | `android/debug.keystore` + `build.json` `android.debug` block |
|
|
20
|
+
| Play release / `android` | `android/release.keystore` + `build.json` `android.release` + `google.json` |
|
|
21
|
+
|
|
22
|
+
`build.json` keystore paths are relative to the deployment folder (the directory that contains `build.json`).
|
|
23
|
+
|
|
24
|
+
## Full steps
|
|
25
|
+
|
|
26
|
+
### 1. Prerequisites
|
|
27
|
+
|
|
28
|
+
- JDK on PATH so `keytool` works (`keytool -help`).
|
|
29
|
+
- A Play Console app (for release uploads).
|
|
30
|
+
|
|
31
|
+
### 2. Helper: generate keystores
|
|
32
|
+
|
|
33
|
+
**Helper:** `npx adaptfully android-keystore [--deployment android] [--debug-only] [--yes]`
|
|
34
|
+
|
|
35
|
+
If you run this, **skip to step 5** (Play service account). For debug-only, you can stop after this step.
|
|
36
|
+
|
|
37
|
+
Flags:
|
|
38
|
+
|
|
39
|
+
- `--debug-only` — only the debug keystore + `build.json` debug block
|
|
40
|
+
- `--yes` — overwrite existing `build.json` without prompting
|
|
41
|
+
- `--alias`, `--store-password`, `--key-password`, `--cn` — non-interactive release keystore fields
|
|
42
|
+
|
|
43
|
+
### 3. Manual alternative: create keystores with keytool
|
|
44
|
+
|
|
45
|
+
Skip this if you used step 2.
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
mkdir -p assets/meta/deployments/android/android
|
|
49
|
+
|
|
50
|
+
keytool -genkeypair -v \
|
|
51
|
+
-keystore assets/meta/deployments/android/android/debug.keystore \
|
|
52
|
+
-alias androiddebugkey -keyalg RSA -keysize 2048 -validity 10000 \
|
|
53
|
+
-storepass android -keypass android \
|
|
54
|
+
-dname "CN=Android Debug,O=Android,C=US"
|
|
55
|
+
|
|
56
|
+
keytool -genkeypair -v \
|
|
57
|
+
-keystore assets/meta/deployments/android/android/release.keystore \
|
|
58
|
+
-alias upload -keyalg RSA -keysize 2048 -validity 10000 \
|
|
59
|
+
-storepass "(your store password)" -keypass "(your key password)" \
|
|
60
|
+
-dname "CN=Your Studio,O=Your Studio,C=US"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 4. Manual alternative: write `build.json`
|
|
64
|
+
|
|
65
|
+
Skip this if you used step 2.
|
|
66
|
+
|
|
67
|
+
Create `assets/meta/deployments/android/build.json`:
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"android": {
|
|
72
|
+
"debug": {
|
|
73
|
+
"keystore": "./android/debug.keystore",
|
|
74
|
+
"packageType": "apk",
|
|
75
|
+
"storePassword": "android",
|
|
76
|
+
"alias": "androiddebugkey",
|
|
77
|
+
"password": "android",
|
|
78
|
+
"keystoreType": ""
|
|
79
|
+
},
|
|
80
|
+
"release": {
|
|
81
|
+
"keystore": "./android/release.keystore",
|
|
82
|
+
"packageType": "bundle",
|
|
83
|
+
"storePassword": "(your store password)",
|
|
84
|
+
"alias": "upload",
|
|
85
|
+
"password": "(your key password)",
|
|
86
|
+
"keystoreType": ""
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 5. Create a Play Console service account (manual)
|
|
93
|
+
|
|
94
|
+
Required only for Play uploads (`adaptfully release android` / deploy to Google).
|
|
95
|
+
|
|
96
|
+
1. Open [Google Play Console](https://play.google.com/console/) → **Setup** → **API access** (or Google Cloud IAM linked to the Play developer account).
|
|
97
|
+
2. Create or select a service account with permission to manage releases.
|
|
98
|
+
3. Download a JSON key for that service account.
|
|
99
|
+
4. Invite the service account email in Play Console with the right app permissions.
|
|
100
|
+
|
|
101
|
+
### 6. Helper: import `google.json`
|
|
102
|
+
|
|
103
|
+
**Helper:** `npx adaptfully google-publish --from ./path/to/service-account.json [--deployment android]`
|
|
104
|
+
|
|
105
|
+
If you run this, **skip to step 8**.
|
|
106
|
+
|
|
107
|
+
### 7. Manual alternative: copy the service account JSON
|
|
108
|
+
|
|
109
|
+
Skip this if you used step 6.
|
|
110
|
+
|
|
111
|
+
Copy the downloaded key to `assets/meta/deployments/android/google.json`. Ensure `manifest.json` includes `"type": "google"` (helpers write this for you).
|
|
112
|
+
|
|
113
|
+
### 8. Done when
|
|
114
|
+
|
|
115
|
+
- [ ] `build.json` exists with debug (and release, if shipping)
|
|
116
|
+
- [ ] Keystore files exist at the paths listed in `build.json`
|
|
117
|
+
- [ ] For Play uploads: `google.json` is present and the service account can access the app
|
|
118
|
+
- [ ] Credential paths are gitignored
|
|
119
|
+
|
|
120
|
+
## Notes
|
|
121
|
+
|
|
122
|
+
- Prefer **project-owned** keystores so debug installs stay reproducible across machines.
|
|
123
|
+
- Some build hosts supply a default debug keystore when `build.json` is missing; do not rely on that for shared projects.
|
|
124
|
+
- Keep release keystore passwords offline; losing the release keystore can block Play updates for that signing key.
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# iOS credentials
|
|
2
|
+
|
|
3
|
+
Set up Apple signing files and (for App Store uploads) App Store Connect login credentials.
|
|
4
|
+
|
|
5
|
+
**Default folder:** `assets/meta/deployments/ios/`
|
|
6
|
+
|
|
7
|
+
You can create signing material on **macOS or Windows**. The sections below branch where the local tools differ.
|
|
8
|
+
|
|
9
|
+
## Recommended path
|
|
10
|
+
|
|
11
|
+
1. **Helper:** `npx adaptfully apple-signing --kind development` (and/or `--kind distribution`) — generates a CSR, walks portal checklist, exports `.p12`, optionally copies the provisioning profile.
|
|
12
|
+
2. Complete the Apple Developer portal steps the helper prints (upload CSR, download `.cer` / `.mobileprovision`).
|
|
13
|
+
3. For App Store uploads only — **Helper:** `npx adaptfully apple-publish` — writes `apple.json`.
|
|
14
|
+
|
|
15
|
+
Simulator builds (`ios-sim`) need no device signing files.
|
|
16
|
+
|
|
17
|
+
## What you need
|
|
18
|
+
|
|
19
|
+
| Goal | Files |
|
|
20
|
+
|------|--------|
|
|
21
|
+
| Device debug (`ios-dev`) | `apple/development.p12`, `apple/development.mobileprovision` |
|
|
22
|
+
| App Store (`ios` release) | `apple/distribution.p12`, `apple/app-store.mobileprovision`, `apple.json` |
|
|
23
|
+
| Simulator (`ios-sim`) | None |
|
|
24
|
+
|
|
25
|
+
Optional `build.json` next to `apple/`:
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"apple": {
|
|
30
|
+
"p12Password": "(passphrase used when exporting the .p12)"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
If omitted, the build pipeline treats the passphrase as empty.
|
|
36
|
+
|
|
37
|
+
## Prerequisites
|
|
38
|
+
|
|
39
|
+
- Apple Developer Program membership.
|
|
40
|
+
- An App ID matching your platform `packageName`.
|
|
41
|
+
- If you use Capgo Apple social login: enable **Sign in with Apple** on that App ID.
|
|
42
|
+
- For development profiles: registered device UDIDs.
|
|
43
|
+
- OpenSSL on PATH for the helper / Windows manual path (`openssl version`). macOS Keychain users can still use the helper if OpenSSL is available (e.g. via Homebrew or Xcode CLT tooling).
|
|
44
|
+
|
|
45
|
+
## Full steps
|
|
46
|
+
|
|
47
|
+
### 1. Helper: CSR, `.p12`, and profile placement
|
|
48
|
+
|
|
49
|
+
**Helper:** `npx adaptfully apple-signing [--kind development|distribution] [--csr-only] [--from-cer path] [--provision path] [--p12-password secret]`
|
|
50
|
+
|
|
51
|
+
If you run the full interactive helper (not `--csr-only`), **skip to step 7** after it finishes (or step 8 for App Store Connect login).
|
|
52
|
+
|
|
53
|
+
Useful flags:
|
|
54
|
+
|
|
55
|
+
- `--csr-only` — stop after writing the CSR; later continue with `--from-cer`
|
|
56
|
+
- `--from-cer ./Certificates.cer` — skip CSR generation when `apple/.work/private.key` already exists
|
|
57
|
+
- `--provision ./Profile.mobileprovision` — copy/rename into `apple/`
|
|
58
|
+
- `--kind development` or `--kind distribution`
|
|
59
|
+
|
|
60
|
+
Work files live under `assets/meta/deployments/ios/apple/.work/` by default (`private.key` + CSR).
|
|
61
|
+
|
|
62
|
+
### 2. Create a Certificate Signing Request (manual)
|
|
63
|
+
|
|
64
|
+
Skip this if you used step 1 (or will use `--from-cer` with an existing key from step 1).
|
|
65
|
+
|
|
66
|
+
#### macOS (Keychain Access)
|
|
67
|
+
|
|
68
|
+
1. Open **Keychain Access** → **Certificate Assistant** → **Request a Certificate From a Certificate Authority…**
|
|
69
|
+
2. Enter your email and common name; choose **Saved to disk**.
|
|
70
|
+
3. Save the `.certSigningRequest` file.
|
|
71
|
+
|
|
72
|
+
#### Windows / OpenSSL (any OS)
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
mkdir -p assets/meta/deployments/ios/apple/.work
|
|
76
|
+
cd assets/meta/deployments/ios/apple/.work
|
|
77
|
+
|
|
78
|
+
openssl genrsa -out private.key 2048
|
|
79
|
+
openssl req -new -key private.key -out CertificateSigningRequest.certSigningRequest -subj "/emailAddress=you@example.com/CN=Your Name/C=US"
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### 3. Create the certificate on the Apple Developer site (manual)
|
|
83
|
+
|
|
84
|
+
Always required — Apple must issue the certificate.
|
|
85
|
+
|
|
86
|
+
1. Open [Certificates](https://developer.apple.com/account/resources/certificates/list).
|
|
87
|
+
2. Create **Apple Development** (device debug) and/or **Apple Distribution** (App Store).
|
|
88
|
+
3. Upload the CSR from step 1 or 2.
|
|
89
|
+
4. Download the `.cer` file.
|
|
90
|
+
|
|
91
|
+
### 4. Export a `.p12` (manual)
|
|
92
|
+
|
|
93
|
+
Skip this if step 1 already exported `development.p12` / `distribution.p12`.
|
|
94
|
+
|
|
95
|
+
#### macOS (Keychain Access)
|
|
96
|
+
|
|
97
|
+
1. Double-click the `.cer` to import it into Keychain Access.
|
|
98
|
+
2. Find the certificate (and its private key) → **Export…** → `.p12`.
|
|
99
|
+
3. Choose a passphrase; remember it for `build.json` `apple.p12Password`.
|
|
100
|
+
4. Save as `assets/meta/deployments/ios/apple/development.p12` or `distribution.p12`.
|
|
101
|
+
|
|
102
|
+
#### Windows / OpenSSL
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
# From the folder that has private.key and the downloaded .cer
|
|
106
|
+
openssl x509 -in Certificates.cer -inform DER -out cert.pem -outform PEM
|
|
107
|
+
openssl pkcs12 -export -inkey private.key -in cert.pem -out development.p12
|
|
108
|
+
# or distribution.p12 — use the matching kind
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Move the `.p12` to `assets/meta/deployments/ios/apple/`.
|
|
112
|
+
|
|
113
|
+
If DER conversion fails, try importing the `.cer` as PEM directly in the `pkcs12 -export` `-in` argument.
|
|
114
|
+
|
|
115
|
+
### 5. Create and download a provisioning profile (manual)
|
|
116
|
+
|
|
117
|
+
Always required for device / App Store signing.
|
|
118
|
+
|
|
119
|
+
1. Register devices (development) under [Devices](https://developer.apple.com/account/resources/devices/list).
|
|
120
|
+
2. Open [Profiles](https://developer.apple.com/account/resources/profiles/list).
|
|
121
|
+
3. Create a **Development** profile or **App Store** profile for your App ID and certificate.
|
|
122
|
+
4. Download the `.mobileprovision`.
|
|
123
|
+
|
|
124
|
+
### 6. Place the provisioning profile (manual)
|
|
125
|
+
|
|
126
|
+
Skip this if step 1 copied it with `--provision`.
|
|
127
|
+
|
|
128
|
+
Rename/copy to:
|
|
129
|
+
|
|
130
|
+
- Development → `assets/meta/deployments/ios/apple/development.mobileprovision`
|
|
131
|
+
- App Store → `assets/meta/deployments/ios/apple/app-store.mobileprovision`
|
|
132
|
+
|
|
133
|
+
### 7. Optional: record the `.p12` passphrase
|
|
134
|
+
|
|
135
|
+
Skip this if `apple-signing` already wrote `build.json`.
|
|
136
|
+
|
|
137
|
+
Create or merge `assets/meta/deployments/ios/build.json`:
|
|
138
|
+
|
|
139
|
+
```json
|
|
140
|
+
{
|
|
141
|
+
"apple": {
|
|
142
|
+
"p12Password": "(your .p12 passphrase)"
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### 8. Helper: App Store Connect login (`apple.json`)
|
|
148
|
+
|
|
149
|
+
Required only for App Store uploads.
|
|
150
|
+
|
|
151
|
+
**Helper:** `npx adaptfully apple-publish [--deployment ios]`
|
|
152
|
+
|
|
153
|
+
If you run this, **skip to step 10**.
|
|
154
|
+
|
|
155
|
+
You need an [app-specific password](https://appleid.apple.com/) for your Apple ID.
|
|
156
|
+
|
|
157
|
+
### 9. Manual alternative: write `apple.json`
|
|
158
|
+
|
|
159
|
+
Skip this if you used step 8.
|
|
160
|
+
|
|
161
|
+
```json
|
|
162
|
+
{
|
|
163
|
+
"category": "public.app-category.games",
|
|
164
|
+
"identity": "(your team id)",
|
|
165
|
+
"username": "(Apple ID email)",
|
|
166
|
+
"password": "(app-specific password)"
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### 10. Done when
|
|
171
|
+
|
|
172
|
+
- [ ] For `ios-dev`: `apple/development.p12` + `apple/development.mobileprovision`
|
|
173
|
+
- [ ] For App Store: `apple/distribution.p12` + `apple/app-store.mobileprovision` + `apple.json`
|
|
174
|
+
- [ ] `apple.p12Password` matches the `.p12` passphrase when the passphrase is non-empty
|
|
175
|
+
- [ ] App ID matches `packageName`; Sign in with Apple enabled if you use Capgo Apple auth
|
|
176
|
+
- [ ] Credential paths are gitignored
|
|
177
|
+
|
|
178
|
+
## Notes
|
|
179
|
+
|
|
180
|
+
- Filenames under `apple/` must match the table above.
|
|
181
|
+
- You can create credentials on Windows even if the IPA is built elsewhere.
|
|
182
|
+
- Re-run `apple-signing --kind …` separately for development and distribution when you need both.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Steam credentials
|
|
2
|
+
|
|
3
|
+
Set up Steamworks upload login for release deploys. Debug Steam builds need no upload credentials.
|
|
4
|
+
|
|
5
|
+
**Default folder:** `assets/meta/deployments/steam/`
|
|
6
|
+
|
|
7
|
+
## Recommended path
|
|
8
|
+
|
|
9
|
+
1. Create or choose a Steamworks **build account** (manual; prefer a dedicated publisher account).
|
|
10
|
+
2. **Helper:** `npx adaptfully steam-publish` — installs steamcmd if needed, logs in interactively, writes `steam.json`.
|
|
11
|
+
3. Set upload `steamId` on the deployment `manifest.json` (and client `platforms.*.steamId` if you use Steamworks auth).
|
|
12
|
+
|
|
13
|
+
## What you need
|
|
14
|
+
|
|
15
|
+
| Goal | Files |
|
|
16
|
+
|------|--------|
|
|
17
|
+
| `steam-dev` (local Electron debug) | None |
|
|
18
|
+
| `steam` release upload | `steam.json` (+ `manifest.json` with upload `steamId`) |
|
|
19
|
+
|
|
20
|
+
## Full steps
|
|
21
|
+
|
|
22
|
+
### 1. Prerequisites
|
|
23
|
+
|
|
24
|
+
- A Steamworks partner app.
|
|
25
|
+
- A Steam account allowed to upload builds (Steam Guard may be enabled).
|
|
26
|
+
|
|
27
|
+
### 2. Helper: write `steam.json`
|
|
28
|
+
|
|
29
|
+
**Helper:** `npx adaptfully steam-publish [--username U] [--password P] [--deployment steam]`
|
|
30
|
+
|
|
31
|
+
If you run this, **skip to step 6**.
|
|
32
|
+
|
|
33
|
+
The helper installs steamcmd under `~/.adaptfully/steamcmd` when needed, logs in (enter a Steam Guard code if prompted), and writes `assets/meta/deployments/steam/steam.json`.
|
|
34
|
+
|
|
35
|
+
### 3. Manual alternative: install steamcmd
|
|
36
|
+
|
|
37
|
+
Skip this if you used step 2.
|
|
38
|
+
|
|
39
|
+
Download steamcmd for your OS from Valve and unpack it somewhere durable.
|
|
40
|
+
|
|
41
|
+
### 4. Manual alternative: log in once
|
|
42
|
+
|
|
43
|
+
Skip this if you used step 2.
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
steamcmd +login YOUR_USER YOUR_PASSWORD +quit
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Complete Steam Guard if prompted. After a successful login, steamcmd stores session files under its install directory (`config/config.vdf`, and an `ssfn*` sentry file when Guard is enabled).
|
|
50
|
+
|
|
51
|
+
### 5. Manual alternative: assemble `steam.json`
|
|
52
|
+
|
|
53
|
+
Skip this if you used step 2.
|
|
54
|
+
|
|
55
|
+
Create `assets/meta/deployments/steam/steam.json`:
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"username": "(your Steam build account)",
|
|
60
|
+
"password": "(your password)",
|
|
61
|
+
"configVdf": "(base64 of config/config.vdf)",
|
|
62
|
+
"sentryFileName": "(ssfn filename — only with Steam Guard)",
|
|
63
|
+
"sentryFile": "(base64 of the ssfn file — only with Steam Guard)"
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Accounts **without** Steam Guard only need `username`, `password`, and `configVdf`.
|
|
68
|
+
|
|
69
|
+
### 6. App IDs (client vs upload)
|
|
70
|
+
|
|
71
|
+
These are independent:
|
|
72
|
+
|
|
73
|
+
| Setting | Purpose |
|
|
74
|
+
|---------|---------|
|
|
75
|
+
| `config.platforms.<name>.steamId` | Client Steamworks / `steam-auth` |
|
|
76
|
+
| `assets/meta/deployments/steam/manifest.json` → `steamId` | Depot upload target |
|
|
77
|
+
|
|
78
|
+
You can ship with Steam auth and no upload credentials, or upload without client Steamworks.
|
|
79
|
+
|
|
80
|
+
Example `manifest.json`:
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"type": "steam",
|
|
85
|
+
"steamId": 480
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 7. Done when
|
|
90
|
+
|
|
91
|
+
- [ ] For uploads: `steam.json` exists and login tokens are valid
|
|
92
|
+
- [ ] Upload `steamId` is set on the deployment manifest
|
|
93
|
+
- [ ] Client `steamId` is set on the platform if you use Steamworks auth
|
|
94
|
+
- [ ] Credential files are gitignored
|
|
95
|
+
|
|
96
|
+
## Notes
|
|
97
|
+
|
|
98
|
+
- Re-run `adaptfully steam-publish` when Steam invalidates the token (new machine, password change, or expired Guard).
|
|
99
|
+
- `steam-dev` never requires `steam.json`.
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import fsp from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import {
|
|
5
|
+
defaultDeploymentCredentialPath,
|
|
6
|
+
ensureDeploymentManifest,
|
|
7
|
+
ensurePublishCredentialsGitignore,
|
|
8
|
+
parsePublishCredentialArgv,
|
|
9
|
+
promptConfirm,
|
|
10
|
+
promptRequired,
|
|
11
|
+
} from './publish-credentials.js';
|
|
12
|
+
|
|
13
|
+
const DEBUG_ALIAS = 'androiddebugkey';
|
|
14
|
+
const DEBUG_PASSWORD = 'android';
|
|
15
|
+
const DEBUG_DNAME = 'CN=Android Debug,O=Android,C=US';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @param {string} [projectRoot]
|
|
19
|
+
* @param {string} [deploymentKey='android']
|
|
20
|
+
*/
|
|
21
|
+
export function defaultAndroidDeploymentDir(projectRoot = '.', deploymentKey = 'android') {
|
|
22
|
+
return path.dirname(defaultDeploymentCredentialPath(projectRoot, deploymentKey, 'build.json'));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @param {{
|
|
27
|
+
* debugOnly?: boolean,
|
|
28
|
+
* debug?: { keystore: string, storePassword: string, alias: string, password: string },
|
|
29
|
+
* release?: { keystore: string, storePassword: string, alias: string, password: string },
|
|
30
|
+
* }} options
|
|
31
|
+
*/
|
|
32
|
+
export function buildAndroidBuildJson(options = {}) {
|
|
33
|
+
/** @type {Record<string, unknown>} */
|
|
34
|
+
const android = {
|
|
35
|
+
debug: {
|
|
36
|
+
keystore: options.debug?.keystore ?? './android/debug.keystore',
|
|
37
|
+
packageType: 'apk',
|
|
38
|
+
storePassword: options.debug?.storePassword ?? DEBUG_PASSWORD,
|
|
39
|
+
alias: options.debug?.alias ?? DEBUG_ALIAS,
|
|
40
|
+
password: options.debug?.password ?? DEBUG_PASSWORD,
|
|
41
|
+
keystoreType: '',
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
if (!options.debugOnly && options.release) {
|
|
46
|
+
android.release = {
|
|
47
|
+
keystore: options.release.keystore,
|
|
48
|
+
packageType: 'bundle',
|
|
49
|
+
storePassword: options.release.storePassword,
|
|
50
|
+
alias: options.release.alias,
|
|
51
|
+
password: options.release.password,
|
|
52
|
+
keystoreType: '',
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return { android };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @param {string[]} args
|
|
61
|
+
* @param {{ command?: string }} [options]
|
|
62
|
+
* @returns {Promise<void>}
|
|
63
|
+
*/
|
|
64
|
+
export function runKeytool(args, options = {}) {
|
|
65
|
+
const command = options.command ?? 'keytool';
|
|
66
|
+
|
|
67
|
+
return new Promise((resolve, reject) => {
|
|
68
|
+
const child = spawn(command, args, {
|
|
69
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
70
|
+
shell: process.platform === 'win32',
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
let stderr = '';
|
|
74
|
+
child.stderr?.on('data', (chunk) => {
|
|
75
|
+
stderr += String(chunk);
|
|
76
|
+
});
|
|
77
|
+
child.on('error', (err) => {
|
|
78
|
+
reject(new Error(
|
|
79
|
+
`Unable to run "${command}". Install a JDK and ensure keytool is on PATH. (${err.message})`,
|
|
80
|
+
));
|
|
81
|
+
});
|
|
82
|
+
child.on('close', (code) => {
|
|
83
|
+
if (code === 0) {
|
|
84
|
+
resolve();
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
reject(new Error(`keytool failed (exit ${code}): ${stderr.trim() || 'no output'}`));
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* @param {{
|
|
94
|
+
* keystorePath: string,
|
|
95
|
+
* alias: string,
|
|
96
|
+
* storePassword: string,
|
|
97
|
+
* keyPassword: string,
|
|
98
|
+
* dname: string,
|
|
99
|
+
* validityDays?: number,
|
|
100
|
+
* runKeytoolFn?: typeof runKeytool,
|
|
101
|
+
* }} options
|
|
102
|
+
*/
|
|
103
|
+
export async function generateKeystore(options) {
|
|
104
|
+
const run = options.runKeytoolFn ?? runKeytool;
|
|
105
|
+
await fsp.mkdir(path.dirname(options.keystorePath), { recursive: true });
|
|
106
|
+
await run([
|
|
107
|
+
'-genkeypair',
|
|
108
|
+
'-v',
|
|
109
|
+
'-keystore', options.keystorePath,
|
|
110
|
+
'-alias', options.alias,
|
|
111
|
+
'-keyalg', 'RSA',
|
|
112
|
+
'-keysize', '2048',
|
|
113
|
+
'-validity', String(options.validityDays ?? 10000),
|
|
114
|
+
'-storepass', options.storePassword,
|
|
115
|
+
'-keypass', options.keyPassword,
|
|
116
|
+
'-dname', options.dname,
|
|
117
|
+
]);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* @param {{
|
|
122
|
+
* deployment?: string,
|
|
123
|
+
* projectRoot?: string,
|
|
124
|
+
* debugOnly?: boolean,
|
|
125
|
+
* yes?: boolean,
|
|
126
|
+
* alias?: string,
|
|
127
|
+
* storePassword?: string,
|
|
128
|
+
* keyPassword?: string,
|
|
129
|
+
* cn?: string,
|
|
130
|
+
* log?: (message: string) => void,
|
|
131
|
+
* runKeytoolFn?: typeof runKeytool,
|
|
132
|
+
* }} [options]
|
|
133
|
+
*/
|
|
134
|
+
export async function runAndroidKeystore(options = {}) {
|
|
135
|
+
const log = options.log ?? console.log;
|
|
136
|
+
const projectRoot = options.projectRoot ?? '.';
|
|
137
|
+
const deploymentKey = options.deployment ?? 'android';
|
|
138
|
+
const deploymentDir = defaultAndroidDeploymentDir(projectRoot, deploymentKey);
|
|
139
|
+
const keystoreDir = path.join(deploymentDir, 'android');
|
|
140
|
+
const buildJsonPath = path.join(deploymentDir, 'build.json');
|
|
141
|
+
const debugOnly = options.debugOnly === true;
|
|
142
|
+
|
|
143
|
+
let buildJsonExists = false;
|
|
144
|
+
try {
|
|
145
|
+
await fsp.access(buildJsonPath);
|
|
146
|
+
buildJsonExists = true;
|
|
147
|
+
} catch {
|
|
148
|
+
buildJsonExists = false;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (buildJsonExists) {
|
|
152
|
+
const overwrite = options.yes === true
|
|
153
|
+
|| await promptConfirm(`Overwrite existing ${buildJsonPath}?`, false);
|
|
154
|
+
if (!overwrite) {
|
|
155
|
+
throw new Error(`Refusing to overwrite ${buildJsonPath}. Pass --yes to overwrite.`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const debugKeystorePath = path.join(keystoreDir, 'debug.keystore');
|
|
160
|
+
log(`adaptfully: generating debug keystore → ${debugKeystorePath}`);
|
|
161
|
+
await generateKeystore({
|
|
162
|
+
keystorePath: debugKeystorePath,
|
|
163
|
+
alias: DEBUG_ALIAS,
|
|
164
|
+
storePassword: DEBUG_PASSWORD,
|
|
165
|
+
keyPassword: DEBUG_PASSWORD,
|
|
166
|
+
dname: DEBUG_DNAME,
|
|
167
|
+
runKeytoolFn: options.runKeytoolFn,
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
/** @type {{ keystore: string, storePassword: string, alias: string, password: string } | undefined} */
|
|
171
|
+
let release;
|
|
172
|
+
|
|
173
|
+
if (!debugOnly) {
|
|
174
|
+
const alias = options.alias
|
|
175
|
+
?? await promptRequired('Release keystore alias: ');
|
|
176
|
+
const storePassword = options.storePassword
|
|
177
|
+
?? await promptRequired('Release store password: ');
|
|
178
|
+
const keyPassword = options.keyPassword
|
|
179
|
+
?? options.storePassword
|
|
180
|
+
?? await promptRequired('Release key password (often same as store): ');
|
|
181
|
+
const cn = options.cn
|
|
182
|
+
?? await promptRequired('Certificate CN (e.g. Your Studio): ');
|
|
183
|
+
|
|
184
|
+
const releaseKeystorePath = path.join(keystoreDir, 'release.keystore');
|
|
185
|
+
log(`adaptfully: generating release keystore → ${releaseKeystorePath}`);
|
|
186
|
+
await generateKeystore({
|
|
187
|
+
keystorePath: releaseKeystorePath,
|
|
188
|
+
alias,
|
|
189
|
+
storePassword,
|
|
190
|
+
keyPassword,
|
|
191
|
+
dname: `CN=${cn},O=${cn},C=US`,
|
|
192
|
+
runKeytoolFn: options.runKeytoolFn,
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
release = {
|
|
196
|
+
keystore: './android/release.keystore',
|
|
197
|
+
storePassword,
|
|
198
|
+
alias,
|
|
199
|
+
password: keyPassword,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const buildJson = buildAndroidBuildJson({
|
|
204
|
+
debugOnly,
|
|
205
|
+
debug: {
|
|
206
|
+
keystore: './android/debug.keystore',
|
|
207
|
+
storePassword: DEBUG_PASSWORD,
|
|
208
|
+
alias: DEBUG_ALIAS,
|
|
209
|
+
password: DEBUG_PASSWORD,
|
|
210
|
+
},
|
|
211
|
+
release,
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
await fsp.mkdir(deploymentDir, { recursive: true });
|
|
215
|
+
await fsp.writeFile(buildJsonPath, `${JSON.stringify(buildJson, null, 4)}\n`);
|
|
216
|
+
await ensureDeploymentManifest(deploymentDir, 'google', log);
|
|
217
|
+
await ensurePublishCredentialsGitignore(projectRoot, {
|
|
218
|
+
extraPaths: [buildJsonPath, keystoreDir],
|
|
219
|
+
log,
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
log(`adaptfully: wrote ${buildJsonPath}`);
|
|
223
|
+
if (debugOnly) {
|
|
224
|
+
log('adaptfully: debug-only keystore ready. For Play uploads, re-run without --debug-only, then use google-publish.');
|
|
225
|
+
} else {
|
|
226
|
+
log('adaptfully: next — create a Play Console service account JSON, then: adaptfully google-publish --from <sa.json>');
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return {
|
|
230
|
+
deploymentKey,
|
|
231
|
+
deploymentDir,
|
|
232
|
+
buildJsonPath,
|
|
233
|
+
buildJson,
|
|
234
|
+
debugOnly,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* @param {string[]} [argv]
|
|
240
|
+
*/
|
|
241
|
+
export async function androidKeystoreFromCli(argv = process.argv) {
|
|
242
|
+
return runAndroidKeystore(parsePublishCredentialArgv(argv));
|
|
243
|
+
}
|
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import fsp from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import {
|
|
5
|
+
defaultDeploymentCredentialPath,
|
|
6
|
+
ensureDeploymentManifest,
|
|
7
|
+
ensurePublishCredentialsGitignore,
|
|
8
|
+
parsePublishCredentialArgv,
|
|
9
|
+
promptConfirm,
|
|
10
|
+
promptRequired,
|
|
11
|
+
resolveUserPath,
|
|
12
|
+
} from './publish-credentials.js';
|
|
13
|
+
|
|
14
|
+
/** @typedef {'development' | 'distribution'} AppleSigningKind */
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @param {string} [projectRoot]
|
|
18
|
+
* @param {string} [deploymentKey='ios']
|
|
19
|
+
*/
|
|
20
|
+
export function defaultAppleSigningDir(projectRoot = '.', deploymentKey = 'ios') {
|
|
21
|
+
return path.join(
|
|
22
|
+
path.dirname(defaultDeploymentCredentialPath(projectRoot, deploymentKey, 'apple.json')),
|
|
23
|
+
'apple',
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param {string} kind
|
|
29
|
+
* @returns {AppleSigningKind}
|
|
30
|
+
*/
|
|
31
|
+
export function normalizeAppleSigningKind(kind) {
|
|
32
|
+
const value = String(kind ?? '').trim().toLowerCase();
|
|
33
|
+
if (value === 'development' || value === 'dev' || value === 'debug') {
|
|
34
|
+
return 'development';
|
|
35
|
+
}
|
|
36
|
+
if (value === 'distribution' || value === 'dist' || value === 'release' || value === 'app-store') {
|
|
37
|
+
return 'distribution';
|
|
38
|
+
}
|
|
39
|
+
throw new Error(
|
|
40
|
+
`Unknown Apple signing kind "${kind}". Use "development" or "distribution".`,
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @param {AppleSigningKind} kind
|
|
46
|
+
*/
|
|
47
|
+
export function appleSigningFilenames(kind) {
|
|
48
|
+
if (kind === 'development') {
|
|
49
|
+
return {
|
|
50
|
+
p12: 'development.p12',
|
|
51
|
+
provision: 'development.mobileprovision',
|
|
52
|
+
certLabel: 'Apple Development',
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
p12: 'distribution.p12',
|
|
57
|
+
provision: 'app-store.mobileprovision',
|
|
58
|
+
certLabel: 'Apple Distribution',
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Merge or create build.json with apple.p12Password.
|
|
64
|
+
* @param {object | null | undefined} existing
|
|
65
|
+
* @param {string} p12Password
|
|
66
|
+
*/
|
|
67
|
+
export function mergeAppleP12PasswordBuildJson(existing, p12Password) {
|
|
68
|
+
/** @type {Record<string, unknown>} */
|
|
69
|
+
const next = existing && typeof existing === 'object' && !Array.isArray(existing)
|
|
70
|
+
? { ...existing }
|
|
71
|
+
: {};
|
|
72
|
+
const apple = next.apple && typeof next.apple === 'object' && !Array.isArray(next.apple)
|
|
73
|
+
? { .../** @type {Record<string, unknown>} */ (next.apple) }
|
|
74
|
+
: {};
|
|
75
|
+
apple.p12Password = p12Password;
|
|
76
|
+
next.apple = apple;
|
|
77
|
+
return next;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @param {string[]} args
|
|
82
|
+
* @param {{ command?: string }} [options]
|
|
83
|
+
* @returns {Promise<void>}
|
|
84
|
+
*/
|
|
85
|
+
export function runOpenssl(args, options = {}) {
|
|
86
|
+
const command = options.command ?? 'openssl';
|
|
87
|
+
|
|
88
|
+
return new Promise((resolve, reject) => {
|
|
89
|
+
const child = spawn(command, args, {
|
|
90
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
91
|
+
shell: process.platform === 'win32',
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
let stderr = '';
|
|
95
|
+
child.stderr?.on('data', (chunk) => {
|
|
96
|
+
stderr += String(chunk);
|
|
97
|
+
});
|
|
98
|
+
child.on('error', (err) => {
|
|
99
|
+
reject(new Error(
|
|
100
|
+
`Unable to run "${command}". Install OpenSSL and ensure it is on PATH. (${err.message})`,
|
|
101
|
+
));
|
|
102
|
+
});
|
|
103
|
+
child.on('close', (code) => {
|
|
104
|
+
if (code === 0) {
|
|
105
|
+
resolve();
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
reject(new Error(`openssl failed (exit ${code}): ${stderr.trim() || 'no output'}`));
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @param {{
|
|
115
|
+
* workdir: string,
|
|
116
|
+
* cn: string,
|
|
117
|
+
* email?: string,
|
|
118
|
+
* runOpensslFn?: typeof runOpenssl,
|
|
119
|
+
* }} options
|
|
120
|
+
*/
|
|
121
|
+
export async function generateAppleCsr(options) {
|
|
122
|
+
const run = options.runOpensslFn ?? runOpenssl;
|
|
123
|
+
await fsp.mkdir(options.workdir, { recursive: true });
|
|
124
|
+
|
|
125
|
+
const keyPath = path.join(options.workdir, 'private.key');
|
|
126
|
+
const csrPath = path.join(options.workdir, 'CertificateSigningRequest.certSigningRequest');
|
|
127
|
+
const subject = options.email
|
|
128
|
+
? `/emailAddress=${options.email}/CN=${options.cn}/C=US`
|
|
129
|
+
: `/CN=${options.cn}/C=US`;
|
|
130
|
+
|
|
131
|
+
await run(['genrsa', '-out', keyPath, '2048']);
|
|
132
|
+
await run([
|
|
133
|
+
'req',
|
|
134
|
+
'-new',
|
|
135
|
+
'-key', keyPath,
|
|
136
|
+
'-out', csrPath,
|
|
137
|
+
'-subj', subject,
|
|
138
|
+
]);
|
|
139
|
+
|
|
140
|
+
return { keyPath, csrPath };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* @param {{
|
|
145
|
+
* cerPath: string,
|
|
146
|
+
* keyPath: string,
|
|
147
|
+
* p12Path: string,
|
|
148
|
+
* p12Password: string,
|
|
149
|
+
* runOpensslFn?: typeof runOpenssl,
|
|
150
|
+
* }} options
|
|
151
|
+
*/
|
|
152
|
+
export async function exportAppleP12(options) {
|
|
153
|
+
const run = options.runOpensslFn ?? runOpenssl;
|
|
154
|
+
await fsp.mkdir(path.dirname(options.p12Path), { recursive: true });
|
|
155
|
+
|
|
156
|
+
const pemPath = `${options.cerPath}.pem`;
|
|
157
|
+
await run(['x509', '-in', options.cerPath, '-inform', 'DER', '-out', pemPath, '-outform', 'PEM']);
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
await run([
|
|
161
|
+
'pkcs12',
|
|
162
|
+
'-export',
|
|
163
|
+
'-inkey', options.keyPath,
|
|
164
|
+
'-in', pemPath,
|
|
165
|
+
'-out', options.p12Path,
|
|
166
|
+
'-passout', `pass:${options.p12Password}`,
|
|
167
|
+
]);
|
|
168
|
+
} catch (derErr) {
|
|
169
|
+
// Some downloads are already PEM
|
|
170
|
+
try {
|
|
171
|
+
await run([
|
|
172
|
+
'pkcs12',
|
|
173
|
+
'-export',
|
|
174
|
+
'-inkey', options.keyPath,
|
|
175
|
+
'-in', options.cerPath,
|
|
176
|
+
'-out', options.p12Path,
|
|
177
|
+
'-passout', `pass:${options.p12Password}`,
|
|
178
|
+
]);
|
|
179
|
+
} catch {
|
|
180
|
+
throw derErr;
|
|
181
|
+
}
|
|
182
|
+
} finally {
|
|
183
|
+
try {
|
|
184
|
+
await fsp.unlink(pemPath);
|
|
185
|
+
} catch {
|
|
186
|
+
// ignore
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* @param {(message: string) => void} log
|
|
193
|
+
* @param {AppleSigningKind} kind
|
|
194
|
+
* @param {string} csrPath
|
|
195
|
+
*/
|
|
196
|
+
function printPortalChecklist(log, kind, csrPath) {
|
|
197
|
+
const names = appleSigningFilenames(kind);
|
|
198
|
+
log('');
|
|
199
|
+
log('adaptfully: Apple Developer portal steps (browser):');
|
|
200
|
+
log(` 1. Open https://developer.apple.com/account/resources/certificates/list`);
|
|
201
|
+
log(` 2. Create a new "${names.certLabel}" certificate`);
|
|
202
|
+
log(` 3. Upload CSR: ${csrPath}`);
|
|
203
|
+
log(' 4. Download the .cer file');
|
|
204
|
+
log(' 5. Ensure your App ID exists; enable Sign in with Apple if you use Capgo Apple auth');
|
|
205
|
+
if (kind === 'development') {
|
|
206
|
+
log(' 6. Register device UDIDs, then create a Development provisioning profile');
|
|
207
|
+
} else {
|
|
208
|
+
log(' 6. Create an App Store Connect provisioning profile for the App ID');
|
|
209
|
+
}
|
|
210
|
+
log(' 7. Download the .mobileprovision file');
|
|
211
|
+
log('');
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* @param {{
|
|
216
|
+
* deployment?: string,
|
|
217
|
+
* projectRoot?: string,
|
|
218
|
+
* workdir?: string,
|
|
219
|
+
* csrOnly?: boolean,
|
|
220
|
+
* fromCer?: string,
|
|
221
|
+
* kind?: string,
|
|
222
|
+
* provision?: string,
|
|
223
|
+
* p12Password?: string,
|
|
224
|
+
* cn?: string,
|
|
225
|
+
* email?: string,
|
|
226
|
+
* yes?: boolean,
|
|
227
|
+
* log?: (message: string) => void,
|
|
228
|
+
* runOpensslFn?: typeof runOpenssl,
|
|
229
|
+
* }} [options]
|
|
230
|
+
*/
|
|
231
|
+
export async function runAppleSigning(options = {}) {
|
|
232
|
+
const log = options.log ?? console.log;
|
|
233
|
+
const projectRoot = options.projectRoot ?? '.';
|
|
234
|
+
const deploymentKey = options.deployment ?? 'ios';
|
|
235
|
+
const appleDir = defaultAppleSigningDir(projectRoot, deploymentKey);
|
|
236
|
+
const deploymentDir = path.dirname(appleDir);
|
|
237
|
+
const workdir = path.resolve(
|
|
238
|
+
options.workdir
|
|
239
|
+
?? path.join(appleDir, '.work'),
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
let kind = options.kind
|
|
243
|
+
? normalizeAppleSigningKind(options.kind)
|
|
244
|
+
: null;
|
|
245
|
+
|
|
246
|
+
if (!kind && (options.fromCer || options.provision || !options.csrOnly)) {
|
|
247
|
+
kind = normalizeAppleSigningKind(
|
|
248
|
+
await promptRequired('Signing kind (development|distribution): ', options.kind),
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (!kind) {
|
|
253
|
+
kind = 'development';
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const names = appleSigningFilenames(kind);
|
|
257
|
+
const keyPath = path.join(workdir, 'private.key');
|
|
258
|
+
const csrPath = path.join(workdir, 'CertificateSigningRequest.certSigningRequest');
|
|
259
|
+
|
|
260
|
+
let createdCsr = false;
|
|
261
|
+
try {
|
|
262
|
+
await fsp.access(keyPath);
|
|
263
|
+
await fsp.access(csrPath);
|
|
264
|
+
} catch {
|
|
265
|
+
const cn = options.cn ?? await promptRequired('Common name for CSR (e.g. Your Name): ');
|
|
266
|
+
const email = options.email;
|
|
267
|
+
log(`adaptfully: generating CSR in ${workdir}`);
|
|
268
|
+
await generateAppleCsr({
|
|
269
|
+
workdir,
|
|
270
|
+
cn,
|
|
271
|
+
email,
|
|
272
|
+
runOpensslFn: options.runOpensslFn,
|
|
273
|
+
});
|
|
274
|
+
createdCsr = true;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
printPortalChecklist(log, kind, csrPath);
|
|
278
|
+
|
|
279
|
+
if (options.csrOnly) {
|
|
280
|
+
await ensurePublishCredentialsGitignore(projectRoot, {
|
|
281
|
+
extraPaths: [appleDir],
|
|
282
|
+
log,
|
|
283
|
+
});
|
|
284
|
+
log('adaptfully: --csr-only set; stop here, then re-run with --from-cer after downloading the .cer');
|
|
285
|
+
return {
|
|
286
|
+
deploymentKey,
|
|
287
|
+
appleDir,
|
|
288
|
+
workdir,
|
|
289
|
+
csrPath,
|
|
290
|
+
keyPath,
|
|
291
|
+
kind,
|
|
292
|
+
csrOnly: true,
|
|
293
|
+
createdCsr,
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const cerPath = resolveUserPath(
|
|
298
|
+
await promptRequired('Path to downloaded Apple .cer: ', options.fromCer),
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
const p12Password = options.p12Password != null
|
|
302
|
+
? String(options.p12Password)
|
|
303
|
+
: await promptRequired('Passphrase for the .p12 (remember this for build.json apple.p12Password): ');
|
|
304
|
+
|
|
305
|
+
const p12Path = path.join(appleDir, names.p12);
|
|
306
|
+
log(`adaptfully: exporting ${p12Path}`);
|
|
307
|
+
await exportAppleP12({
|
|
308
|
+
cerPath,
|
|
309
|
+
keyPath,
|
|
310
|
+
p12Path,
|
|
311
|
+
p12Password,
|
|
312
|
+
runOpensslFn: options.runOpensslFn,
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
const buildJsonPath = path.join(deploymentDir, 'build.json');
|
|
316
|
+
let existingBuild = null;
|
|
317
|
+
try {
|
|
318
|
+
existingBuild = JSON.parse(await fsp.readFile(buildJsonPath, 'utf8'));
|
|
319
|
+
} catch {
|
|
320
|
+
existingBuild = null;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const writePassword = options.yes === true
|
|
324
|
+
|| p12Password === ''
|
|
325
|
+
|| await promptConfirm(
|
|
326
|
+
`Write apple.p12Password into ${buildJsonPath}?`,
|
|
327
|
+
true,
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
if (writePassword) {
|
|
331
|
+
const merged = mergeAppleP12PasswordBuildJson(existingBuild, p12Password);
|
|
332
|
+
await fsp.mkdir(deploymentDir, { recursive: true });
|
|
333
|
+
await fsp.writeFile(buildJsonPath, `${JSON.stringify(merged, null, 4)}\n`);
|
|
334
|
+
log(`adaptfully: wrote ${buildJsonPath}`);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
let provisionPath = options.provision;
|
|
338
|
+
if (!provisionPath) {
|
|
339
|
+
if (options.yes === true) {
|
|
340
|
+
log(`adaptfully: --yes set without --provision; skipping provisioning profile copy`);
|
|
341
|
+
} else {
|
|
342
|
+
const rlAnswer = await promptRequired(
|
|
343
|
+
`Path to downloaded ${names.provision} (type skip to finish later): `,
|
|
344
|
+
);
|
|
345
|
+
if (rlAnswer.toLowerCase() !== 'skip') {
|
|
346
|
+
provisionPath = rlAnswer;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (provisionPath) {
|
|
352
|
+
const dest = path.join(appleDir, names.provision);
|
|
353
|
+
await fsp.mkdir(appleDir, { recursive: true });
|
|
354
|
+
await fsp.copyFile(resolveUserPath(provisionPath), dest);
|
|
355
|
+
log(`adaptfully: copied provisioning profile → ${dest}`);
|
|
356
|
+
} else {
|
|
357
|
+
log(`adaptfully: skipped provisioning profile — place ${names.provision} under ${appleDir} when ready`);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
await ensureDeploymentManifest(deploymentDir, 'apple', log);
|
|
361
|
+
await ensurePublishCredentialsGitignore(projectRoot, {
|
|
362
|
+
extraPaths: [appleDir, buildJsonPath],
|
|
363
|
+
log,
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
log(`adaptfully: signing files under ${appleDir}`);
|
|
367
|
+
if (kind === 'distribution') {
|
|
368
|
+
log('adaptfully: next for App Store upload — adaptfully apple-publish');
|
|
369
|
+
} else {
|
|
370
|
+
log('adaptfully: development signing ready for ios-dev device builds');
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
return {
|
|
374
|
+
deploymentKey,
|
|
375
|
+
appleDir,
|
|
376
|
+
workdir,
|
|
377
|
+
csrPath,
|
|
378
|
+
keyPath,
|
|
379
|
+
p12Path,
|
|
380
|
+
kind,
|
|
381
|
+
csrOnly: false,
|
|
382
|
+
createdCsr,
|
|
383
|
+
provisionPath: provisionPath ? resolveUserPath(provisionPath) : null,
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* @param {string[]} [argv]
|
|
389
|
+
*/
|
|
390
|
+
export async function appleSigningFromCli(argv = process.argv) {
|
|
391
|
+
return runAppleSigning(parsePublishCredentialArgv(argv));
|
|
392
|
+
}
|
package/lib/node/index.js
CHANGED
|
@@ -62,6 +62,7 @@ export {
|
|
|
62
62
|
ensurePublishCredentialsGitignore,
|
|
63
63
|
normalizeUserPath,
|
|
64
64
|
parsePublishCredentialArgv,
|
|
65
|
+
promptConfirm,
|
|
65
66
|
promptRequired,
|
|
66
67
|
PUBLISH_CREDENTIAL_GITIGNORE_PATTERNS,
|
|
67
68
|
resolveUserPath,
|
|
@@ -90,3 +91,22 @@ export {
|
|
|
90
91
|
runApplePublish,
|
|
91
92
|
validateApplePublishJson,
|
|
92
93
|
} from './apple-publish.js';
|
|
94
|
+
export {
|
|
95
|
+
androidKeystoreFromCli,
|
|
96
|
+
buildAndroidBuildJson,
|
|
97
|
+
defaultAndroidDeploymentDir,
|
|
98
|
+
generateKeystore,
|
|
99
|
+
runAndroidKeystore,
|
|
100
|
+
runKeytool,
|
|
101
|
+
} from './android-keystore.js';
|
|
102
|
+
export {
|
|
103
|
+
appleSigningFilenames,
|
|
104
|
+
appleSigningFromCli,
|
|
105
|
+
defaultAppleSigningDir,
|
|
106
|
+
exportAppleP12,
|
|
107
|
+
generateAppleCsr,
|
|
108
|
+
mergeAppleP12PasswordBuildJson,
|
|
109
|
+
normalizeAppleSigningKind,
|
|
110
|
+
runAppleSigning,
|
|
111
|
+
runOpenssl,
|
|
112
|
+
} from './apple-signing.js';
|
package/lib/node/pipeline.js
CHANGED
|
@@ -12,6 +12,8 @@ import {
|
|
|
12
12
|
import { steamPublishFromCli } from './steam-publish.js';
|
|
13
13
|
import { googlePublishFromCli } from './google-publish.js';
|
|
14
14
|
import { applePublishFromCli } from './apple-publish.js';
|
|
15
|
+
import { androidKeystoreFromCli } from './android-keystore.js';
|
|
16
|
+
import { appleSigningFromCli } from './apple-signing.js';
|
|
15
17
|
|
|
16
18
|
/** @typedef {'prebuild' | 'build' | 'deploy' | 'release'} AdaptfullyStage */
|
|
17
19
|
|
|
@@ -20,6 +22,8 @@ const PUBLISH_COMMANDS = {
|
|
|
20
22
|
'steam-publish': steamPublishFromCli,
|
|
21
23
|
'google-publish': googlePublishFromCli,
|
|
22
24
|
'apple-publish': applePublishFromCli,
|
|
25
|
+
'android-keystore': androidKeystoreFromCli,
|
|
26
|
+
'apple-signing': appleSigningFromCli,
|
|
23
27
|
};
|
|
24
28
|
|
|
25
29
|
/**
|
|
@@ -202,6 +206,8 @@ export async function adaptfullyFromCli(argv = process.argv) {
|
|
|
202
206
|
+ ' adaptfully steam-publish [--username U] [--password P] [--deployment steam] [--output path]\n'
|
|
203
207
|
+ ' adaptfully google-publish [--from service-account.json] [--deployment android] [--output path]\n'
|
|
204
208
|
+ ' adaptfully apple-publish [--deployment ios] [--category C] [--identity I] [--username U] [--password P]\n'
|
|
209
|
+
+ ' adaptfully android-keystore [--deployment android] [--debug-only] [--yes]\n'
|
|
210
|
+
+ ' adaptfully apple-signing [--deployment ios] [--kind development|distribution] [--csr-only] [--from-cer path] [--provision path]\n'
|
|
205
211
|
+ '\n'
|
|
206
212
|
+ 'Stages:\n'
|
|
207
213
|
+ ' prebuild Copy deploy/ and apply platform registrations → output/<platform>-prebuild/\n'
|
|
@@ -213,9 +219,11 @@ export async function adaptfullyFromCli(argv = process.argv) {
|
|
|
213
219
|
+ ' --artifact <path> Prior build artifact directory (deploy only; default: ./output/ after build).\n'
|
|
214
220
|
+ '\n'
|
|
215
221
|
+ 'Publish credential helpers (one-time local setup):\n'
|
|
216
|
-
+ ' steam-publish
|
|
217
|
-
+ ' google-publish
|
|
218
|
-
+ ' apple-publish
|
|
222
|
+
+ ' steam-publish Install steamcmd if needed, log in, write steam deployment steam.json\n'
|
|
223
|
+
+ ' google-publish Import a Play service-account JSON into the android deployment folder\n'
|
|
224
|
+
+ ' apple-publish Write App Store Connect credentials into the ios deployment folder\n'
|
|
225
|
+
+ ' android-keystore Generate debug/release keystores and write android build.json\n'
|
|
226
|
+
+ ' apple-signing Generate CSR, export .p12, place provisioning profiles under ios/apple/',
|
|
219
227
|
);
|
|
220
228
|
}
|
|
221
229
|
|
|
@@ -211,13 +211,33 @@ export async function promptRequired(prompt, existing) {
|
|
|
211
211
|
}
|
|
212
212
|
}
|
|
213
213
|
|
|
214
|
+
/**
|
|
215
|
+
* @param {string} prompt
|
|
216
|
+
* @param {boolean} [defaultYes=false]
|
|
217
|
+
* @returns {Promise<boolean>}
|
|
218
|
+
*/
|
|
219
|
+
export async function promptConfirm(prompt, defaultYes = false) {
|
|
220
|
+
const rl = readline.createInterface({ input, output });
|
|
221
|
+
const suffix = defaultYes ? ' [Y/n] ' : ' [y/N] ';
|
|
222
|
+
|
|
223
|
+
try {
|
|
224
|
+
const answer = String(await rl.question(`${prompt}${suffix}`)).trim().toLowerCase();
|
|
225
|
+
if (!answer) {
|
|
226
|
+
return defaultYes;
|
|
227
|
+
}
|
|
228
|
+
return answer === 'y' || answer === 'yes';
|
|
229
|
+
} finally {
|
|
230
|
+
rl.close();
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
214
234
|
/**
|
|
215
235
|
* Parse shared CLI flags used by publish credential helpers.
|
|
216
236
|
* @param {string[]} argv
|
|
217
237
|
* @param {number} [startIndex=3]
|
|
218
238
|
*/
|
|
219
239
|
export function parsePublishCredentialArgv(argv, startIndex = 3) {
|
|
220
|
-
/** @type {Record<string, string>} */
|
|
240
|
+
/** @type {Record<string, string | boolean>} */
|
|
221
241
|
const options = {
|
|
222
242
|
projectRoot: '.',
|
|
223
243
|
};
|
|
@@ -243,6 +263,32 @@ export function parsePublishCredentialArgv(argv, startIndex = 3) {
|
|
|
243
263
|
options.username = argv[++i];
|
|
244
264
|
} else if (arg === '--password' && argv[i + 1]) {
|
|
245
265
|
options.password = argv[++i];
|
|
266
|
+
} else if (arg === '--debug-only') {
|
|
267
|
+
options.debugOnly = true;
|
|
268
|
+
} else if (arg === '--csr-only') {
|
|
269
|
+
options.csrOnly = true;
|
|
270
|
+
} else if (arg === '--yes' || arg === '-y') {
|
|
271
|
+
options.yes = true;
|
|
272
|
+
} else if (arg === '--from-cer' && argv[i + 1]) {
|
|
273
|
+
options.fromCer = normalizeUserPath(argv[++i]);
|
|
274
|
+
} else if (arg === '--kind' && argv[i + 1]) {
|
|
275
|
+
options.kind = argv[++i];
|
|
276
|
+
} else if (arg === '--provision' && argv[i + 1]) {
|
|
277
|
+
options.provision = normalizeUserPath(argv[++i]);
|
|
278
|
+
} else if (arg === '--workdir' && argv[i + 1]) {
|
|
279
|
+
options.workdir = normalizeUserPath(argv[++i]);
|
|
280
|
+
} else if (arg === '--alias' && argv[i + 1]) {
|
|
281
|
+
options.alias = argv[++i];
|
|
282
|
+
} else if (arg === '--store-password' && argv[i + 1]) {
|
|
283
|
+
options.storePassword = argv[++i];
|
|
284
|
+
} else if (arg === '--key-password' && argv[i + 1]) {
|
|
285
|
+
options.keyPassword = argv[++i];
|
|
286
|
+
} else if (arg === '--p12-password' && argv[i + 1]) {
|
|
287
|
+
options.p12Password = argv[++i];
|
|
288
|
+
} else if (arg === '--cn' && argv[i + 1]) {
|
|
289
|
+
options.cn = argv[++i];
|
|
290
|
+
} else if (arg === '--email' && argv[i + 1]) {
|
|
291
|
+
options.email = argv[++i];
|
|
246
292
|
}
|
|
247
293
|
}
|
|
248
294
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@makefully/adaptfully",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "Platform abstraction and Wrapfully deploy client for Makefully games",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/node/index.js",
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"files": [
|
|
18
18
|
"bin",
|
|
19
19
|
"lib",
|
|
20
|
+
"docs",
|
|
20
21
|
"CHANGELOG.md",
|
|
21
22
|
"LICENSE",
|
|
22
23
|
"README.md"
|