@makefully/adaptfully 3.6.1 → 3.8.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 +21 -0
- package/README.md +55 -29
- package/lib/node/archive.js +105 -7
- package/lib/node/artifacts.js +58 -0
- package/lib/node/deploy.js +59 -11
- package/lib/node/index.js +8 -2
- package/lib/node/packagers/electron.js +17 -3
- package/lib/node/packagers/index.js +9 -4
- package/lib/node/pipeline.js +146 -30
- package/lib/node/registrations.js +110 -8
- package/lib/node/steam-auth.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project are documented in this file.
|
|
4
4
|
|
|
5
|
+
## 3.8.0 — 2026-07-10
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- **`steam-auth` Steamworks app ID** moves from top-level `config.steamId` to **`platforms.<name>.steamId`**. Steam **upload** app IDs belong in each deployment’s `manifest.json` (Wrapfully), not in Adaptfully client config.
|
|
10
|
+
|
|
11
|
+
## 3.7.0 — 2026-07-09
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- **`adaptfully release <platform>`** stage — single Wrapfully call for build + configured deployments.
|
|
16
|
+
- **`resolveBuildSpec()`** — returns `{ family, targets, platformKey, steamworks, deployments }` for multi-target platforms.
|
|
17
|
+
- **`createSourceArchive`**, **`createDeployArchive`**, **`createReleaseArchive`** — split zip profiles for build/deploy/release.
|
|
18
|
+
- **`--artifact <path>`** on deploy for promoting a prior build artifact.
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- **`send()`** targets Wrapfully v2 URLs: `/{family}/build`, `/{family}/release`, `/deploy/{key}`.
|
|
23
|
+
- Default **`deployments`** when omitted is `["zip"]` instead of the platform key.
|
|
24
|
+
- **`builder`** in platform config may be a string or array (e.g. `["win","mac","linux"]`).
|
|
25
|
+
|
|
5
26
|
## 3.6.1 — 2026-07-06
|
|
6
27
|
|
|
7
28
|
### Changed
|
package/README.md
CHANGED
|
@@ -21,22 +21,44 @@ Games register platform services before load and retrieve them in-game. Adaptful
|
|
|
21
21
|
### Pipeline stages
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
|
-
adaptfully prebuild web
|
|
25
|
-
adaptfully build steam
|
|
26
|
-
adaptfully deploy web
|
|
27
|
-
adaptfully deploy
|
|
28
|
-
adaptfully steam
|
|
24
|
+
adaptfully prebuild web # deploy/ → output/web-prebuild/
|
|
25
|
+
adaptfully build steam # prebuild + zip and send to Wrapfully (default deployment)
|
|
26
|
+
adaptfully deploy web # prebuild + POST to Wrapfully for each of web's deployments
|
|
27
|
+
adaptfully deploy web --deployment web-prod # deploy to a single named deployment
|
|
28
|
+
adaptfully deploy steam # prebuild + POST to Wrapfully steam builder
|
|
29
|
+
adaptfully steam-auth # one-time: log in with steamcmd → assets/meta/deployments/steam/steam.json
|
|
29
30
|
```
|
|
30
31
|
|
|
31
32
|
| Stage | What it does |
|
|
32
33
|
|-------|----------------|
|
|
33
34
|
| `prebuild` | Copy `deploy/` to `output/<platform>-prebuild/` and inject registrations into `config.htmlInjections` |
|
|
34
|
-
| `build` | Prebuild, then POST the result to Wrapfully |
|
|
35
|
-
| `deploy` |
|
|
36
|
-
| `steam-auth` | One-time local setup: install steamcmd, interactive login, write `
|
|
35
|
+
| `build` | Prebuild, then POST the result to Wrapfully (artifact zip only) |
|
|
36
|
+
| `deploy` | POST the prior build artifact from `./output/` to each configured deployment (or `--deployment`) |
|
|
37
|
+
| `steam-auth` | One-time local setup: install steamcmd, interactive login, write the `steam` deployment's `steam.json` |
|
|
37
38
|
|
|
38
39
|
`wrapfully-deploy` is a compatibility alias for `adaptfully deploy` when invoked with a Wrapfully builder name (`steam`, `win`, `android`, etc.).
|
|
39
40
|
|
|
41
|
+
### Platforms vs. deployments
|
|
42
|
+
|
|
43
|
+
A **platform** (`config.platforms.<key>`) describes *how* to build — registrations, packager, and the Wrapfully builder. A **deployment** describes *where* a build is sent, along with the credentials for that target. This lets a single build fan out to several targets of the same platform type (for example, a staging and a production SFTP host).
|
|
44
|
+
|
|
45
|
+
- A platform may declare `"deployments": ["<key>", ...]`. Each key maps to a credential folder at `assets/meta/deployments/<key>/`.
|
|
46
|
+
- If a platform has no `deployments` array, the platform key itself is the single default deployment key (so `steam` → `assets/meta/deployments/steam/`).
|
|
47
|
+
- `adaptfully deploy <platform>` prebuilds once, then sends to Wrapfully once per deployment. `--deployment <key>` narrows it to one.
|
|
48
|
+
- Only the selected deployment's folder is shipped in each zip (as `meta/publish/`), so other targets' credentials never travel with a build.
|
|
49
|
+
|
|
50
|
+
```json
|
|
51
|
+
"config": {
|
|
52
|
+
"platforms": {
|
|
53
|
+
"web": { "registrations": { "auth": "google-auth" }, "deployments": ["web-testing", "web-prod"] },
|
|
54
|
+
"steam": { "packager": "electron", "registrations": { "auth": "steam-auth" } },
|
|
55
|
+
"steam-dev": { "packager": "electron", "registrations": { "auth": "steam-auth" }, "deployments": ["steam"] }
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
> **Back-compat:** projects with no `assets/meta/deployments/` directory keep the legacy behavior — the entire `assets/meta/` tree (including `assets/meta/publish/`) is shipped as `meta/`, and a single set of credentials is read from `meta/publish/`.
|
|
61
|
+
|
|
40
62
|
Place `<!-- adaptfully -->` / `<!-- /adaptfully -->` markers in your HTML templates where registrations should be injected (typically between split bundle scripts, before `account.js` runs).
|
|
41
63
|
|
|
42
64
|
```javascript
|
|
@@ -113,12 +135,11 @@ Each platform entry may set a **`packager`** (`web`, `electron`, `cordova`, or `
|
|
|
113
135
|
| `cordova` | `cordova.js` stub, `game-config.js`, CSP/viewport HTML extras |
|
|
114
136
|
| `capacitor` | `game-config.js` (more Capacitor-specific output planned) |
|
|
115
137
|
|
|
116
|
-
**`steam-auth` requires `packager: "electron"`** and `
|
|
138
|
+
**`steam-auth` requires `packager: "electron"`** and `platforms.<name>.steamId` (Steamworks app ID for the client preload). Steam **upload** app IDs live separately in each Steam deployment’s `manifest.json` — see Wrapfully docs.
|
|
117
139
|
|
|
118
140
|
```json
|
|
119
141
|
{
|
|
120
142
|
"config": {
|
|
121
|
-
"steamId": 719140,
|
|
122
143
|
"platforms": {
|
|
123
144
|
"web": {
|
|
124
145
|
"packager": "web",
|
|
@@ -126,6 +147,7 @@ Each platform entry may set a **`packager`** (`web`, `electron`, `cordova`, or `
|
|
|
126
147
|
},
|
|
127
148
|
"steam": {
|
|
128
149
|
"packager": "electron",
|
|
150
|
+
"steamId": 1234567,
|
|
129
151
|
"registrations": { "auth": "steam-auth" }
|
|
130
152
|
}
|
|
131
153
|
}
|
|
@@ -138,7 +160,7 @@ Each platform entry may set a **`packager`** (`web`, `electron`, `cordova`, or `
|
|
|
138
160
|
When `steam-auth` is registered on an **`electron`** platform, Adaptfully prebuild writes:
|
|
139
161
|
|
|
140
162
|
- **`main.js`** — Electron shell with Steam overlay enabled and a preload script wired in
|
|
141
|
-
- **`preload.js`** — initializes [steamworks.js](https://github.com/ceifa/steamworks.js) with `
|
|
163
|
+
- **`preload.js`** — initializes [steamworks.js](https://github.com/ceifa/steamworks.js) with `platforms.<name>.steamId` and exposes `window.__ADAPTFULLY_STEAMWORKS__`
|
|
142
164
|
|
|
143
165
|
The renderer `steam-auth` plugin reads the Steam ID from that bridge. When Steam is available, `autoLogin()` succeeds immediately with `{ id: steamId64, email: '' }`. Optional config keys:
|
|
144
166
|
|
|
@@ -297,16 +319,20 @@ mygame/
|
|
|
297
319
|
│ ├── web-prebuild/ # after adaptfully prebuild web
|
|
298
320
|
│ └── steam-prebuild/ # after adaptfully prebuild steam
|
|
299
321
|
└── assets/
|
|
300
|
-
└── meta/
|
|
322
|
+
└── meta/ # shared files packaged as meta/ in the zip
|
|
301
323
|
├── icon-foreground.png
|
|
302
324
|
├── icon-background.png
|
|
303
|
-
└──
|
|
304
|
-
├──
|
|
305
|
-
|
|
306
|
-
├──
|
|
307
|
-
└──
|
|
325
|
+
└── deployments/ # one folder per deployment target (creds)
|
|
326
|
+
├── web-testing/
|
|
327
|
+
│ └── sftp.json
|
|
328
|
+
├── web-prod/
|
|
329
|
+
│ └── sftp.json
|
|
330
|
+
└── steam/ # default deployment for the `steam` platform
|
|
331
|
+
└── steam.json
|
|
308
332
|
```
|
|
309
333
|
|
|
334
|
+
The selected deployment's folder is shipped as `meta/publish/` in the zip, so per-platform credential file names (`build.json`, `sftp.json`, `steam.json`, `apple.json`, `google.json`, `ms.json`, `android/`, `ms/`) live inside `assets/meta/deployments/<key>/`.
|
|
335
|
+
|
|
310
336
|
Icons (`icon-foreground.png`, `icon-background.png`) are required for mobile, desktop, and Steam builds.
|
|
311
337
|
|
|
312
338
|
### Icons
|
|
@@ -345,7 +371,6 @@ Standard npm fields (`name`, `version`, `description`) are used directly. Add a
|
|
|
345
371
|
"scope": "https://example.com/games/",
|
|
346
372
|
"themeColor": "#1a1a2e",
|
|
347
373
|
"twitterId": "@examplegames",
|
|
348
|
-
"steamId": 1234567,
|
|
349
374
|
"deployFolder": "deploy",
|
|
350
375
|
"platforms": {
|
|
351
376
|
"web": {
|
|
@@ -356,6 +381,7 @@ Standard npm fields (`name`, `version`, `description`) are used directly. Add a
|
|
|
356
381
|
},
|
|
357
382
|
"steam": {
|
|
358
383
|
"packager": "electron",
|
|
384
|
+
"steamId": 1234567,
|
|
359
385
|
"registrations": {
|
|
360
386
|
"auth": "steam-auth",
|
|
361
387
|
"storage": "javascript/adaptfully-bridge.js"
|
|
@@ -381,7 +407,6 @@ Standard npm fields (`name`, `version`, `description`) are used directly. Add a
|
|
|
381
407
|
| `scope` | Web/PWA | Base URL scope for the web app |
|
|
382
408
|
| `themeColor` | Cordova, Electron, UWP, web | Loading screen / theme color |
|
|
383
409
|
| `twitterId` | Web | Twitter handle for meta tags |
|
|
384
|
-
| `steamId` | Steam | Steam app ID |
|
|
385
410
|
| `deployFolder` | Client | Neutral deploy directory staged before prebuild (default: `deploy`) |
|
|
386
411
|
| `htmlInjections` | Prebuild | Deploy-relative HTML paths to inject (default: `["index.html"]`) |
|
|
387
412
|
| `outputFolder` | Client | Prebuild output root (default: `output`) |
|
|
@@ -389,6 +414,7 @@ Standard npm fields (`name`, `version`, `description`) are used directly. Add a
|
|
|
389
414
|
| `platforms.<name>.builder` | Build/deploy | Override Wrapfully builder for a platform (default: `web` → `webapp`, others match platform key) |
|
|
390
415
|
| `platforms.<name>.builders` | wrapfully-deploy | Map additional Wrapfully builder names to a platform |
|
|
391
416
|
| `platforms.<name>.packager` | Prebuild | `web` (default), `electron`, `cordova`, or `capacitor` — selects the packager class that adds platform-specific files during prebuild |
|
|
417
|
+
| `platforms.<name>.steamId` | Electron + steam-auth | Steamworks app ID baked into `preload.js` (client). Upload app IDs belong in the Steam deployment `manifest.json` |
|
|
392
418
|
| `properties` | Cordova | Cordova config.xml entries (plugins, allow-navigation, etc.) |
|
|
393
419
|
|
|
394
420
|
### `wrapfully.json`
|
|
@@ -436,11 +462,11 @@ For a single platform, pass the specific builder name rather than a composite.
|
|
|
436
462
|
|
|
437
463
|
### Platform package requirements
|
|
438
464
|
|
|
439
|
-
Signing keys, provisioning profiles, and store credentials go in `./assets/meta/
|
|
465
|
+
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/`.)
|
|
440
466
|
|
|
441
467
|
#### Android (`android`, `android-dev`)
|
|
442
468
|
|
|
443
|
-
Place keystore files in `assets/meta/
|
|
469
|
+
Place keystore files in `assets/meta/deployments/<deployment>/android/`. Include `assets/meta/deployments/<deployment>/build.json`:
|
|
444
470
|
|
|
445
471
|
```json
|
|
446
472
|
{
|
|
@@ -465,7 +491,7 @@ Place keystore files in `assets/meta/publish/android/`. Include `assets/meta/pub
|
|
|
465
491
|
}
|
|
466
492
|
```
|
|
467
493
|
|
|
468
|
-
To deploy to Google Play, also include `assets/meta/
|
|
494
|
+
To deploy to Google Play, also include `assets/meta/deployments/<deployment>/google.json`:
|
|
469
495
|
|
|
470
496
|
```json
|
|
471
497
|
{
|
|
@@ -484,7 +510,7 @@ To deploy to Google Play, also include `assets/meta/publish/google.json`:
|
|
|
484
510
|
|
|
485
511
|
#### Apple (`ios`, `ios-dev`, `ios-sim`, `mac`, `apple`, `apple-dev`)
|
|
486
512
|
|
|
487
|
-
Include `assets/meta/
|
|
513
|
+
Include `assets/meta/deployments/<deployment>/build.json` with iOS signing settings:
|
|
488
514
|
|
|
489
515
|
```json
|
|
490
516
|
{
|
|
@@ -507,7 +533,7 @@ Include `assets/meta/publish/build.json` with iOS signing settings:
|
|
|
507
533
|
}
|
|
508
534
|
```
|
|
509
535
|
|
|
510
|
-
To deploy to the App Store, include `assets/meta/
|
|
536
|
+
To deploy to the App Store, include `assets/meta/deployments/<deployment>/apple.json`:
|
|
511
537
|
|
|
512
538
|
```json
|
|
513
539
|
{
|
|
@@ -526,7 +552,7 @@ Requires the Android and Apple package requirements above.
|
|
|
526
552
|
|
|
527
553
|
`steam-dev` builds debug Electron binaries for Windows, Mac, and Linux without uploading to Steam. No `steam.json` credentials are required.
|
|
528
554
|
|
|
529
|
-
For release uploads, run **`adaptfully steam-auth`** once on your machine. It installs steamcmd if needed, logs you in interactively (enter a Steam Guard code if prompted), and writes `assets/meta/
|
|
555
|
+
For release uploads, run **`adaptfully steam-auth`** 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`:
|
|
530
556
|
|
|
531
557
|
```json
|
|
532
558
|
{
|
|
@@ -542,7 +568,7 @@ Accounts **without** Steam Guard only need `username`, `password`, and `configVd
|
|
|
542
568
|
|
|
543
569
|
Re-run `adaptfully steam-auth` if Steam invalidates the token (new machine, password change, or expired guard).
|
|
544
570
|
|
|
545
|
-
|
|
571
|
+
For Steamworks / `steam-auth` client builds, set `platforms.<name>.steamId`. For Steam **uploads**, set `steamId` on the deployment’s `manifest.json` (see Wrapfully). Those are independent: you can deploy without steam-auth, or use steam-auth without uploading.
|
|
546
572
|
|
|
547
573
|
Steam builds can run on either the Windows or Mac server. The server that receives the request builds its own platforms and requests the rest from the other server (Windows builds `win` and requests `mac`/`linux`; Mac builds `mac`/`linux` and requests `win`). Install the Steamworks SDK ContentBuilder on any server that will upload to Steam.
|
|
548
574
|
|
|
@@ -552,11 +578,11 @@ When builds relay between servers, `meta/publish/` credentials travel in the zip
|
|
|
552
578
|
|
|
553
579
|
`-dev` builders produce debug Electron apps with DevTools enabled and the application menu visible. Dev builds skip code signing, notarization, and Steam upload. No publish credentials are required for dev builds.
|
|
554
580
|
|
|
555
|
-
Release `win` builds can be signed with `assets/meta/
|
|
581
|
+
Release `win` builds can be signed with `assets/meta/deployments/<deployment>/ms.json` (see Windows below). Release `mac` builds can use `assets/meta/deployments/<deployment>/apple.json` for signing and notarization (see Apple above).
|
|
556
582
|
|
|
557
583
|
#### Web (`web` / `webapp`)
|
|
558
584
|
|
|
559
|
-
`adaptfully deploy web` prebuilds and POSTs to the Wrapfully **`webapp`** builder (same conduit as Steam).
|
|
585
|
+
`adaptfully deploy web` prebuilds and POSTs to the Wrapfully **`webapp`** builder (same conduit as Steam). Put an `sftp.json` in each web deployment folder (e.g. `assets/meta/deployments/web-prod/sftp.json`); the selected deployment is zipped as `meta/publish/sftp.json` for Wrapfully to deploy via SFTP. The inner key stays the builder name (`webapp`).
|
|
560
586
|
|
|
561
587
|
```json
|
|
562
588
|
{
|
|
@@ -578,7 +604,7 @@ See [Wrapfully README](https://github.com/Makefully-Studios/wrapfully-client) fo
|
|
|
578
604
|
|
|
579
605
|
#### Windows (`win`, `win-dev`, `uwp`)
|
|
580
606
|
|
|
581
|
-
To sign the app, place your certificate at `assets/meta/
|
|
607
|
+
To sign the app, place your certificate at `assets/meta/deployments/<deployment>/ms/packcert.pfx` and include `assets/meta/deployments/<deployment>/ms.json`:
|
|
582
608
|
|
|
583
609
|
```json
|
|
584
610
|
{
|
package/lib/node/archive.js
CHANGED
|
@@ -1,12 +1,57 @@
|
|
|
1
1
|
import archiver from 'archiver';
|
|
2
2
|
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
const META_DIR = 'assets/meta';
|
|
6
|
+
const DEPLOYMENTS_DIRNAME = 'deployments';
|
|
7
|
+
const PUBLISH_DIRNAME = 'publish';
|
|
3
8
|
|
|
4
9
|
/**
|
|
5
|
-
* @param {
|
|
6
|
-
* @param {string}
|
|
7
|
-
* @param {
|
|
10
|
+
* @param {import('archiver').Archiver} zip
|
|
11
|
+
* @param {string} [publishDir] Single deployment folder
|
|
12
|
+
* @param {string[]} [deploymentDirs] Multiple deployment folders for release
|
|
8
13
|
*/
|
|
9
|
-
|
|
14
|
+
function appendMeta(zip, publishDir, deploymentDirs = []) {
|
|
15
|
+
if (!fs.existsSync(META_DIR)) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const deploymentsRoot = path.join(META_DIR, DEPLOYMENTS_DIRNAME);
|
|
20
|
+
|
|
21
|
+
if (!fs.existsSync(deploymentsRoot)) {
|
|
22
|
+
zip.directory(`${META_DIR}/`, 'meta');
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
for (const entry of fs.readdirSync(META_DIR, { withFileTypes: true })) {
|
|
27
|
+
if (entry.name === DEPLOYMENTS_DIRNAME || entry.name === PUBLISH_DIRNAME) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const fullPath = path.join(META_DIR, entry.name);
|
|
32
|
+
if (entry.isDirectory()) {
|
|
33
|
+
zip.directory(`${fullPath}/`, `meta/${entry.name}`);
|
|
34
|
+
} else {
|
|
35
|
+
zip.file(fullPath, { name: `meta/${entry.name}` });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (publishDir && fs.existsSync(publishDir)) {
|
|
40
|
+
zip.directory(`${publishDir}/`, `meta/${PUBLISH_DIRNAME}`);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
for (const deploymentDir of deploymentDirs) {
|
|
45
|
+
if (!fs.existsSync(deploymentDir)) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const deploymentKey = path.basename(deploymentDir);
|
|
50
|
+
zip.directory(`${deploymentDir}/`, `meta/${DEPLOYMENTS_DIRNAME}/${deploymentKey}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function createZipStream(deployFolder, contents, options = {}) {
|
|
10
55
|
const zip = archiver('zip', { zlib: { level: 0 } });
|
|
11
56
|
|
|
12
57
|
zip.on('warning', (err) => {
|
|
@@ -33,11 +78,64 @@ export function createArchive(deployFolder, contents, options = {}) {
|
|
|
33
78
|
zip.directory(`${deployFolder}/`, 'deploy');
|
|
34
79
|
zip.file(`${deployFolder}/index.html`, { name: 'deploy/index.html' });
|
|
35
80
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
81
|
+
|
|
82
|
+
appendMeta(zip, options.publishDir, options.deploymentDirs);
|
|
39
83
|
zip.append(contents, { name: 'package.json' });
|
|
40
84
|
zip.finalize();
|
|
41
85
|
|
|
42
86
|
return zip;
|
|
43
87
|
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Game source zip for /build and /release (no credentials unless release).
|
|
91
|
+
*
|
|
92
|
+
* @param {string} deployFolder
|
|
93
|
+
* @param {string} contents
|
|
94
|
+
* @param {{ indexHtml?: string, publishDir?: string, deploymentDirs?: string[] }} [options]
|
|
95
|
+
*/
|
|
96
|
+
export function createSourceArchive(deployFolder, contents, options = {}) {
|
|
97
|
+
return createZipStream(deployFolder, contents, options);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* @deprecated Use createSourceArchive, createDeployArchive, or createReleaseArchive.
|
|
102
|
+
*/
|
|
103
|
+
export function createArchive(deployFolder, contents, options = {}) {
|
|
104
|
+
return createSourceArchive(deployFolder, contents, options);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Prior artifact zip plus one deployment folder for /deploy.
|
|
109
|
+
*
|
|
110
|
+
* @param {string} artifactPath
|
|
111
|
+
* @param {string} publishDir
|
|
112
|
+
* @param {string} [contents]
|
|
113
|
+
*/
|
|
114
|
+
export function createDeployArchive(artifactPath, publishDir, contents) {
|
|
115
|
+
const zip = archiver('zip', { zlib: { level: 0 } });
|
|
116
|
+
|
|
117
|
+
zip.directory(`${artifactPath}/`, false);
|
|
118
|
+
appendMeta(zip, publishDir);
|
|
119
|
+
|
|
120
|
+
if (contents) {
|
|
121
|
+
zip.append(contents, { name: 'package.json' });
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
zip.finalize();
|
|
125
|
+
return zip;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Game source zip with all non-zip deployment credential folders for /release.
|
|
130
|
+
*
|
|
131
|
+
* @param {string} deployFolder
|
|
132
|
+
* @param {string} contents
|
|
133
|
+
* @param {string[]} deploymentDirs
|
|
134
|
+
* @param {{ indexHtml?: string }} [options]
|
|
135
|
+
*/
|
|
136
|
+
export function createReleaseArchive(deployFolder, contents, deploymentDirs, options = {}) {
|
|
137
|
+
return createZipStream(deployFolder, contents, {
|
|
138
|
+
...options,
|
|
139
|
+
deploymentDirs,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
const MANIFEST_NAME = 'wrapfully-build.json';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Directory where build responses are extracted (contains wrapfully-build.json).
|
|
8
|
+
*
|
|
9
|
+
* @param {{ config?: { outputFolder?: string } }} pkg
|
|
10
|
+
* @param {string} [outputRoot='output']
|
|
11
|
+
*/
|
|
12
|
+
export function buildOutputDir(pkg, outputRoot = 'output') {
|
|
13
|
+
const outputFolder = pkg.config?.outputFolder || outputRoot;
|
|
14
|
+
return path.resolve(outputFolder);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Resolve the artifact directory from a prior `adaptfully build` (or compatible zip extract).
|
|
19
|
+
*
|
|
20
|
+
* @param {{ name: string, version: string, config?: { outputFolder?: string } }} pkg
|
|
21
|
+
* @param {{ outputRoot?: string, platformKey?: string }} [options]
|
|
22
|
+
* @returns {string}
|
|
23
|
+
*/
|
|
24
|
+
export function resolveBuildArtifactDir(pkg, options = {}) {
|
|
25
|
+
const artifactDir = buildOutputDir(pkg, options.outputRoot);
|
|
26
|
+
const manifestPath = path.join(artifactDir, MANIFEST_NAME);
|
|
27
|
+
|
|
28
|
+
if (!fs.existsSync(manifestPath)) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
'No prior build artifact found in '
|
|
31
|
+
+ `${artifactDir}. Run \`adaptfully build <platform>\` first or pass --artifact <path>.`,
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let manifest;
|
|
36
|
+
try {
|
|
37
|
+
manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
38
|
+
} catch {
|
|
39
|
+
throw new Error(`Unable to read ${manifestPath}`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const gameId = `${pkg.name}-${pkg.version}`;
|
|
43
|
+
if (manifest.gameId && manifest.gameId !== gameId) {
|
|
44
|
+
throw new Error(
|
|
45
|
+
`Build artifact is for "${manifest.gameId}" but package.json is ${gameId}. `
|
|
46
|
+
+ 'Run a fresh build or pass --artifact <path>.',
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (options.platformKey && manifest.platformKey && manifest.platformKey !== options.platformKey) {
|
|
51
|
+
throw new Error(
|
|
52
|
+
`Build artifact is for platform "${manifest.platformKey}" but deploy targets "${options.platformKey}". `
|
|
53
|
+
+ 'Run a fresh build or pass --artifact <path>.',
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return artifactDir;
|
|
58
|
+
}
|
package/lib/node/deploy.js
CHANGED
|
@@ -3,31 +3,79 @@ import fs from 'node:fs';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { pipeline } from 'node:stream/promises';
|
|
5
5
|
import unzipper from 'unzip-stream';
|
|
6
|
-
import {
|
|
6
|
+
import { createDeployArchive, createReleaseArchive, createSourceArchive } from './archive.js';
|
|
7
7
|
import { listHtmlFilesRecursive } from './fs-utils.js';
|
|
8
8
|
import { printBuildReport } from './report.js';
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* @param {string} gameId
|
|
12
|
+
* @param {string} platformKey
|
|
13
|
+
*/
|
|
14
|
+
function buildInfoParam(gameId, platformKey) {
|
|
15
|
+
return platformKey && platformKey !== gameId ? `${gameId}_${platformKey}` : gameId;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @param {string} server
|
|
20
|
+
* @param {'build' | 'deploy' | 'release'} stage
|
|
21
|
+
* @param {string} family
|
|
22
|
+
* @param {string} gameId
|
|
23
|
+
* @param {string} platformKey
|
|
24
|
+
* @param {string} [deploymentKey]
|
|
25
|
+
*/
|
|
26
|
+
function resolveWrapfullyUrl(server, stage, family, gameId, platformKey, deploymentKey) {
|
|
27
|
+
const info = buildInfoParam(gameId, platformKey);
|
|
28
|
+
|
|
29
|
+
if (stage === 'deploy') {
|
|
30
|
+
return `${server}deploy/${deploymentKey}/${info}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return `${server}${family}/${stage}/${info}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
10
36
|
/**
|
|
11
37
|
* @param {string} gameId
|
|
12
38
|
* @param {string} contents
|
|
13
39
|
* @param {string} server
|
|
14
|
-
* @param {
|
|
15
|
-
* @param {string}
|
|
40
|
+
* @param {'build' | 'deploy' | 'release'} stage
|
|
41
|
+
* @param {string} family
|
|
42
|
+
* @param {string} deployFolder
|
|
16
43
|
* @param {{ name: string, version: string }} pkg
|
|
17
44
|
* @param {'extract' | string} mode
|
|
18
|
-
* @param {{ log?: (message: string) => void }} [options]
|
|
45
|
+
* @param {{ log?: (message: string) => void, publishDir?: string, deploymentDirs?: string[], platformKey?: string, deploymentKey?: string, artifactPath?: string }} [options]
|
|
19
46
|
*/
|
|
20
|
-
export async function send(gameId, contents, server,
|
|
47
|
+
export async function send(gameId, contents, server, stage, family, deployFolder, pkg, mode = 'extract', options = {}) {
|
|
21
48
|
const log = options.log ?? console.log;
|
|
49
|
+
const platformKey = options.platformKey ?? family;
|
|
22
50
|
const destination = mode === 'extract'
|
|
23
51
|
? unzipper.Extract({ path: './output/', concurrency: 1 })
|
|
24
|
-
: fs.createWriteStream(`./output/${pkg.name}-${pkg.version}-${
|
|
52
|
+
: fs.createWriteStream(`./output/${pkg.name}-${pkg.version}-${stage}-${family}.zip`);
|
|
53
|
+
|
|
54
|
+
let archiveStream;
|
|
55
|
+
|
|
56
|
+
if (stage === 'deploy') {
|
|
57
|
+
if (!options.artifactPath) {
|
|
58
|
+
throw new Error('deploy stage requires options.artifactPath to the prior build artifact directory');
|
|
59
|
+
}
|
|
60
|
+
if (!options.deploymentKey) {
|
|
61
|
+
throw new Error('deploy stage requires options.deploymentKey');
|
|
62
|
+
}
|
|
63
|
+
archiveStream = createDeployArchive(options.artifactPath, options.publishDir, contents);
|
|
64
|
+
} else if (stage === 'release') {
|
|
65
|
+
archiveStream = createReleaseArchive(deployFolder, contents, options.deploymentDirs ?? []);
|
|
66
|
+
} else {
|
|
67
|
+
archiveStream = createSourceArchive(deployFolder, contents);
|
|
68
|
+
}
|
|
25
69
|
|
|
26
|
-
const
|
|
27
|
-
log(`adaptfully:
|
|
70
|
+
const url = resolveWrapfullyUrl(server, stage, family, gameId, platformKey, options.deploymentKey);
|
|
71
|
+
log(`adaptfully: POST ${url}`);
|
|
72
|
+
|
|
73
|
+
if (stage !== 'deploy') {
|
|
74
|
+
const htmlFiles = listHtmlFilesRecursive(deployFolder);
|
|
75
|
+
log(`adaptfully: sending ${htmlFiles.length} HTML file(s) from ${path.resolve(deployFolder)}`);
|
|
76
|
+
}
|
|
28
77
|
|
|
29
|
-
const
|
|
30
|
-
const { data } = await axios.post(`${server}${builder}/${gameId}`, archiveStream, {
|
|
78
|
+
const { data } = await axios.post(url, archiveStream, {
|
|
31
79
|
maxRedirects: 0,
|
|
32
80
|
responseType: 'stream',
|
|
33
81
|
});
|
|
@@ -47,6 +95,6 @@ export async function send(gameId, contents, server, builder, deployFolder, pkg,
|
|
|
47
95
|
}
|
|
48
96
|
|
|
49
97
|
if (mode === 'extract') {
|
|
50
|
-
printBuildReport(
|
|
98
|
+
printBuildReport(`${stage}-${family}`, pkg);
|
|
51
99
|
}
|
|
52
100
|
}
|
package/lib/node/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { buildOutputDir, resolveBuildArtifactDir } from './artifacts.js';
|
|
2
|
+
export { createArchive, createDeployArchive, createReleaseArchive, createSourceArchive } from './archive.js';
|
|
2
3
|
export { loadProjectConfig, resolveServerUrl } from './config.js';
|
|
3
4
|
export { send } from './deploy.js';
|
|
4
|
-
export { adaptfullyFromCli, runAdaptfullyStage } from './pipeline.js';
|
|
5
|
+
export { adaptfullyFromCli, parseStageArgs, runAdaptfullyStage } from './pipeline.js';
|
|
5
6
|
export { prebuildPlatform, prebuildOutputDir, resolveHtmlInjections } from './prebuild.js';
|
|
6
7
|
export { getPackageRoot, getRuntimeDir, resolveRuntimeScript } from './paths.js';
|
|
7
8
|
export {
|
|
@@ -28,9 +29,14 @@ export {
|
|
|
28
29
|
collectRegistrationParts,
|
|
29
30
|
injectAdaptfullyRegistrations,
|
|
30
31
|
resolveBuilderForPlatform,
|
|
32
|
+
resolveBuildSpec,
|
|
31
33
|
resolveCliPlatformAndBuilder,
|
|
34
|
+
resolveDeploymentsForPlatform,
|
|
35
|
+
resolveFamily,
|
|
36
|
+
resolveFamilyFromTarget,
|
|
32
37
|
resolvePlatformKey,
|
|
33
38
|
resolvePlatformRegistrationsByKey,
|
|
39
|
+
resolvePublishDir,
|
|
34
40
|
resolveRegistrationAssets,
|
|
35
41
|
} from './registrations.js';
|
|
36
42
|
export { printBuildReport } from './report.js';
|
|
@@ -178,6 +178,19 @@ export function buildElectronPreload(steamAppId) {
|
|
|
178
178
|
return ELECTRON_PRELOAD_HEAD + steamAppId + ELECTRON_PRELOAD_TAIL;
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
+
/**
|
|
182
|
+
* Steamworks app ID for steam-auth preload, from the active platform config.
|
|
183
|
+
* @param {string | null | undefined} platformKey
|
|
184
|
+
* @param {{ config?: { platforms?: Record<string, { steamId?: number | string }> } }} pkg
|
|
185
|
+
* @returns {number | string | undefined}
|
|
186
|
+
*/
|
|
187
|
+
export function resolvePlatformSteamId(platformKey, pkg) {
|
|
188
|
+
if (!platformKey) {
|
|
189
|
+
return undefined;
|
|
190
|
+
}
|
|
191
|
+
return pkg.config?.platforms?.[platformKey]?.steamId;
|
|
192
|
+
}
|
|
193
|
+
|
|
181
194
|
export class ElectronPackager extends Packager {
|
|
182
195
|
/** @type {'electron'} */
|
|
183
196
|
static id = 'electron';
|
|
@@ -188,10 +201,10 @@ export class ElectronPackager extends Packager {
|
|
|
188
201
|
validate() {
|
|
189
202
|
super.validate();
|
|
190
203
|
|
|
191
|
-
if (this.usesPlugin('steam-auth') && !this.pkg
|
|
204
|
+
if (this.usesPlugin('steam-auth') && !resolvePlatformSteamId(this.platformKey, this.pkg)) {
|
|
192
205
|
const platformLabel = this.platformKey ?? this.platforms.join(', ');
|
|
193
206
|
throw new Error(
|
|
194
|
-
`Platform "${platformLabel}" uses steam-auth but
|
|
207
|
+
`Platform "${platformLabel}" uses steam-auth but platforms.${platformLabel}.steamId is not set.`,
|
|
195
208
|
);
|
|
196
209
|
}
|
|
197
210
|
}
|
|
@@ -212,9 +225,10 @@ export class ElectronPackager extends Packager {
|
|
|
212
225
|
const mainContent = buildElectronMain(withSteam, this.pkg.config?.themeColor);
|
|
213
226
|
|
|
214
227
|
if (withSteam) {
|
|
228
|
+
const steamId = resolvePlatformSteamId(this.platformKey, this.pkg);
|
|
215
229
|
fs.writeFileSync(
|
|
216
230
|
path.join(dest, 'preload.js'),
|
|
217
|
-
buildElectronPreload(
|
|
231
|
+
buildElectronPreload(steamId),
|
|
218
232
|
);
|
|
219
233
|
this.log('adaptfully: write preload.js (steam-auth)');
|
|
220
234
|
}
|
|
@@ -6,7 +6,12 @@ import { WebPackager } from './web.js';
|
|
|
6
6
|
|
|
7
7
|
export { Packager, VALID_PACKAGERS } from './base.js';
|
|
8
8
|
export { WebPackager } from './web.js';
|
|
9
|
-
export {
|
|
9
|
+
export {
|
|
10
|
+
ElectronPackager,
|
|
11
|
+
buildElectronMain,
|
|
12
|
+
buildElectronPreload,
|
|
13
|
+
resolvePlatformSteamId,
|
|
14
|
+
} from './electron.js';
|
|
10
15
|
export { CordovaPackager } from './cordova.js';
|
|
11
16
|
export { CapacitorPackager } from './capacitor.js';
|
|
12
17
|
|
|
@@ -22,7 +27,7 @@ const PACKAGER_REGISTRY = {
|
|
|
22
27
|
|
|
23
28
|
/**
|
|
24
29
|
* @param {string} platformKey
|
|
25
|
-
* @param {{ config?: { platforms?: Record<string, { packager?: string, registrations?: Record<string, string
|
|
30
|
+
* @param {{ config?: { platforms?: Record<string, { packager?: string, registrations?: Record<string, string>, steamId?: number | string }> } }} pkg
|
|
26
31
|
* @param {{ log?: (message: string) => void, platforms?: string[] }} [options]
|
|
27
32
|
* @returns {Packager}
|
|
28
33
|
*/
|
|
@@ -54,7 +59,7 @@ export function resolvePlatformPackager(platformKey, pkg) {
|
|
|
54
59
|
|
|
55
60
|
/**
|
|
56
61
|
* @param {string} platformKey
|
|
57
|
-
* @param {{ config?: { platforms?: Record<string, { packager?: string, registrations?: Record<string, string
|
|
62
|
+
* @param {{ config?: { platforms?: Record<string, { packager?: string, registrations?: Record<string, string>, steamId?: number | string }> } }} pkg
|
|
58
63
|
*/
|
|
59
64
|
export function validatePlatformPackager(platformKey, pkg) {
|
|
60
65
|
createPackagerForPlatform(platformKey, pkg).validate();
|
|
@@ -70,7 +75,7 @@ export function usesSteamAuth(registrations) {
|
|
|
70
75
|
/**
|
|
71
76
|
* @param {string} dest
|
|
72
77
|
* @param {string} platformKey
|
|
73
|
-
* @param {{ config?: { platforms?: Record<string, { packager?: string, registrations?: Record<string, string
|
|
78
|
+
* @param {{ config?: { platforms?: Record<string, { packager?: string, registrations?: Record<string, string>, steamId?: number | string }> } }} pkg
|
|
74
79
|
* @param {{ log?: (message: string) => void, platforms?: string[] }} [options]
|
|
75
80
|
*/
|
|
76
81
|
export function applyPackagerTemplates(dest, platformKey, pkg, options = {}) {
|
package/lib/node/pipeline.js
CHANGED
|
@@ -1,50 +1,130 @@
|
|
|
1
1
|
import { loadProjectConfig, resolveServerUrl } from './config.js';
|
|
2
2
|
import { send } from './deploy.js';
|
|
3
|
+
import { resolveBuildArtifactDir } from './artifacts.js';
|
|
3
4
|
import { prebuildPlatform } from './prebuild.js';
|
|
4
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
resolveBuildSpec,
|
|
7
|
+
resolveDeploymentsForPlatform,
|
|
8
|
+
resolvePublishDir,
|
|
9
|
+
} from './registrations.js';
|
|
5
10
|
import { steamAuthFromCli } from './steam-auth.js';
|
|
6
11
|
|
|
7
|
-
/** @typedef {'prebuild' | 'build' | 'deploy'} AdaptfullyStage */
|
|
12
|
+
/** @typedef {'prebuild' | 'build' | 'deploy' | 'release'} AdaptfullyStage */
|
|
8
13
|
|
|
9
|
-
const VALID_STAGES = new Set(['prebuild', 'build', 'deploy']);
|
|
14
|
+
const VALID_STAGES = new Set(['prebuild', 'build', 'deploy', 'release']);
|
|
10
15
|
|
|
11
16
|
/**
|
|
12
17
|
* @param {AdaptfullyStage} stage
|
|
13
18
|
* @param {string} platformKey
|
|
14
|
-
* @param {{ pkg: object, deployFolder: string, server?: string, mode?: string,
|
|
19
|
+
* @param {{ pkg: object, deployFolder: string, server?: string, mode?: string, deployment?: string, artifactPath?: string, log?: (message: string) => void, outputRoot?: string }} options
|
|
15
20
|
*/
|
|
16
21
|
export async function runAdaptfullyStage(stage, platformKey, options) {
|
|
17
22
|
if (!VALID_STAGES.has(stage)) {
|
|
18
|
-
throw new Error(`Unknown adaptfully stage "${stage}". Expected: prebuild, build, or
|
|
23
|
+
throw new Error(`Unknown adaptfully stage "${stage}". Expected: prebuild, build, deploy, or release.`);
|
|
19
24
|
}
|
|
20
25
|
|
|
21
26
|
const log = options.log ?? console.log;
|
|
22
27
|
const { pkg, deployFolder } = options;
|
|
28
|
+
const buildSpec = resolveBuildSpec(platformKey, pkg);
|
|
29
|
+
let deployments = resolveDeploymentsForPlatform(platformKey, pkg);
|
|
23
30
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
31
|
+
if (options.deployment) {
|
|
32
|
+
if (!deployments.includes(options.deployment)) {
|
|
33
|
+
throw new Error(
|
|
34
|
+
`Deployment "${options.deployment}" is not configured for platform "${platformKey}". `
|
|
35
|
+
+ `Available deployments: ${deployments.join(', ')}`,
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
deployments = [options.deployment];
|
|
39
|
+
} else if (stage === 'build') {
|
|
40
|
+
deployments = ['zip'];
|
|
41
|
+
} else if (stage === 'deploy') {
|
|
42
|
+
deployments = deployments.filter((key) => key !== 'zip');
|
|
43
|
+
} else if (stage === 'release') {
|
|
44
|
+
deployments = deployments.filter((key) => key !== 'zip');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let artifactPath = options.artifactPath;
|
|
48
|
+
if (stage === 'deploy' && !artifactPath) {
|
|
49
|
+
artifactPath = resolveBuildArtifactDir(pkg, {
|
|
50
|
+
outputRoot: options.outputRoot,
|
|
51
|
+
platformKey,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const prebuiltDir = stage === 'deploy'
|
|
56
|
+
? artifactPath
|
|
57
|
+
: prebuildPlatform(deployFolder, platformKey, pkg, {
|
|
58
|
+
log,
|
|
59
|
+
outputRoot: options.outputRoot,
|
|
60
|
+
});
|
|
28
61
|
|
|
29
62
|
if (stage === 'prebuild') {
|
|
30
|
-
return { prebuiltDir, platformKey };
|
|
63
|
+
return { prebuiltDir, platformKey, buildSpec };
|
|
31
64
|
}
|
|
32
65
|
|
|
33
|
-
const
|
|
34
|
-
|
|
66
|
+
const gameId = `${pkg.name}-${pkg.version}`;
|
|
67
|
+
const contents = JSON.stringify(pkg);
|
|
68
|
+
/** @type {{ deploymentKey: string, stage: string, family: string }[]} */
|
|
69
|
+
const sent = [];
|
|
35
70
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
71
|
+
if (stage === 'release') {
|
|
72
|
+
const deploymentDirs = deployments.map((key) => resolvePublishDir(key));
|
|
73
|
+
|
|
74
|
+
log(`adaptfully: ${stage} ${platformKey} → ${buildSpec.family} (${buildSpec.targets.join(', ')})`);
|
|
75
|
+
|
|
76
|
+
await send(
|
|
77
|
+
gameId,
|
|
78
|
+
contents,
|
|
79
|
+
options.server,
|
|
80
|
+
'release',
|
|
81
|
+
buildSpec.family,
|
|
82
|
+
prebuiltDir,
|
|
83
|
+
pkg,
|
|
84
|
+
options.mode ?? 'extract',
|
|
85
|
+
{
|
|
86
|
+
log,
|
|
87
|
+
platformKey,
|
|
88
|
+
deploymentDirs,
|
|
89
|
+
},
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
sent.push({ deploymentKey: 'release', stage, family: buildSpec.family });
|
|
93
|
+
return { prebuiltDir, platformKey, buildSpec, deployments: sent, artifactPath };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
for (const deploymentKey of deployments) {
|
|
97
|
+
const publishDir = deploymentKey === 'zip' ? undefined : resolvePublishDir(deploymentKey);
|
|
98
|
+
const wrapStage = stage === 'deploy' ? 'deploy' : 'build';
|
|
99
|
+
|
|
100
|
+
log(
|
|
101
|
+
`adaptfully: ${stage} ${platformKey} → `
|
|
102
|
+
+ `${deploymentKey === 'zip' ? 'artifact zip' : `deployment "${deploymentKey}"`} `
|
|
103
|
+
+ `via Wrapfully ${buildSpec.family}`,
|
|
104
|
+
);
|
|
46
105
|
|
|
47
|
-
|
|
106
|
+
await send(
|
|
107
|
+
gameId,
|
|
108
|
+
contents,
|
|
109
|
+
options.server,
|
|
110
|
+
wrapStage,
|
|
111
|
+
buildSpec.family,
|
|
112
|
+
prebuiltDir,
|
|
113
|
+
pkg,
|
|
114
|
+
options.mode ?? 'extract',
|
|
115
|
+
{
|
|
116
|
+
log,
|
|
117
|
+
publishDir,
|
|
118
|
+
platformKey,
|
|
119
|
+
deploymentKey: wrapStage === 'deploy' ? deploymentKey : undefined,
|
|
120
|
+
artifactPath,
|
|
121
|
+
},
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
sent.push({ deploymentKey, stage: wrapStage, family: buildSpec.family });
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return { prebuiltDir, platformKey, buildSpec, deployments: sent, artifactPath };
|
|
48
128
|
}
|
|
49
129
|
|
|
50
130
|
/**
|
|
@@ -58,22 +138,27 @@ export async function adaptfullyFromCli(argv = process.argv) {
|
|
|
58
138
|
}
|
|
59
139
|
|
|
60
140
|
const stage = command;
|
|
61
|
-
const
|
|
62
|
-
const
|
|
63
|
-
const
|
|
141
|
+
const { positionals, deployment, artifactPath } = parseStageArgs(argv.slice(3));
|
|
142
|
+
const platformKey = positionals[0];
|
|
143
|
+
const cliServer = positionals[1];
|
|
144
|
+
const mode = positionals[2] ?? 'extract';
|
|
64
145
|
|
|
65
146
|
if (!stage || !platformKey) {
|
|
66
147
|
throw new Error(
|
|
67
148
|
'Usage:\n'
|
|
68
|
-
+ ' adaptfully <prebuild|build|deploy> <platform> [server] [mode]\n'
|
|
149
|
+
+ ' adaptfully <prebuild|build|deploy|release> <platform> [server] [mode] [--deployment <key>] [--artifact <path>]\n'
|
|
69
150
|
+ ' adaptfully steam-auth [--username U] [--password P] [--output path]\n'
|
|
70
151
|
+ '\n'
|
|
71
152
|
+ 'Stages:\n'
|
|
72
153
|
+ ' prebuild Copy deploy/ and apply platform registrations → output/<platform>-prebuild/\n'
|
|
73
|
-
+ ' build prebuild +
|
|
74
|
-
+ ' deploy
|
|
154
|
+
+ ' build prebuild + POST /{family}/build (zip artifact only)\n'
|
|
155
|
+
+ ' deploy POST /deploy/{key} with prior artifact + deployment credentials\n'
|
|
156
|
+
+ ' release prebuild + POST /{family}/release (build + configured deployments)\n'
|
|
75
157
|
+ '\n'
|
|
76
|
-
+ '
|
|
158
|
+
+ ' --deployment <key> Target a single named deployment.\n'
|
|
159
|
+
+ ' --artifact <path> Prior build artifact directory (deploy only; default: ./output/ after build).\n'
|
|
160
|
+
+ '\n'
|
|
161
|
+
+ ' steam-auth Install steamcmd if needed, log in interactively, write steam deployment steam.json',
|
|
77
162
|
);
|
|
78
163
|
}
|
|
79
164
|
|
|
@@ -86,5 +171,36 @@ export async function adaptfullyFromCli(argv = process.argv) {
|
|
|
86
171
|
deployFolder,
|
|
87
172
|
server,
|
|
88
173
|
mode,
|
|
174
|
+
deployment,
|
|
175
|
+
artifactPath,
|
|
89
176
|
});
|
|
90
177
|
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* @param {string[]} args
|
|
181
|
+
* @returns {{ positionals: string[], deployment?: string, artifactPath?: string }}
|
|
182
|
+
*/
|
|
183
|
+
export function parseStageArgs(args) {
|
|
184
|
+
/** @type {string[]} */
|
|
185
|
+
const positionals = [];
|
|
186
|
+
let deployment;
|
|
187
|
+
let artifactPath;
|
|
188
|
+
|
|
189
|
+
for (let i = 0; i < args.length; i++) {
|
|
190
|
+
const arg = args[i];
|
|
191
|
+
|
|
192
|
+
if (arg === '--deployment' && args[i + 1]) {
|
|
193
|
+
deployment = args[++i];
|
|
194
|
+
} else if (arg.startsWith('--deployment=')) {
|
|
195
|
+
deployment = arg.slice('--deployment='.length);
|
|
196
|
+
} else if (arg === '--artifact' && args[i + 1]) {
|
|
197
|
+
artifactPath = args[++i];
|
|
198
|
+
} else if (arg.startsWith('--artifact=')) {
|
|
199
|
+
artifactPath = arg.slice('--artifact='.length);
|
|
200
|
+
} else {
|
|
201
|
+
positionals.push(arg);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return { positionals, deployment, artifactPath };
|
|
206
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
2
3
|
import { resolveRuntimeScript } from './paths.js';
|
|
3
4
|
|
|
4
5
|
/** @typedef {Record<string, string>} RegistrationMap */
|
|
@@ -299,20 +300,119 @@ export function adaptfullyInjectionForPlatform(platformKey, pkg, options = {}) {
|
|
|
299
300
|
}
|
|
300
301
|
|
|
301
302
|
/**
|
|
303
|
+
* Resolve the deployment target keys for a platform. A platform may declare a
|
|
304
|
+
* `deployments` array (each entry maps to assets/meta/deployments/<key>/), which
|
|
305
|
+
* lets one build fan out to multiple targets of the same platform type. When no
|
|
306
|
+
* `deployments` array is present, the default is `["zip"]` (artifact download only).
|
|
307
|
+
*
|
|
302
308
|
* @param {string} platformKey
|
|
303
|
-
* @param {{ config?: { platforms?: Record<string, {
|
|
309
|
+
* @param {{ config?: { platforms?: Record<string, { deployments?: string[] }> } }} pkg
|
|
310
|
+
* @returns {string[]}
|
|
304
311
|
*/
|
|
305
|
-
export function
|
|
306
|
-
const
|
|
307
|
-
|
|
308
|
-
|
|
312
|
+
export function resolveDeploymentsForPlatform(platformKey, pkg) {
|
|
313
|
+
const deployments = pkg.config?.platforms?.[platformKey]?.deployments;
|
|
314
|
+
|
|
315
|
+
if (Array.isArray(deployments) && deployments.length > 0) {
|
|
316
|
+
for (const key of deployments) {
|
|
317
|
+
if (typeof key !== 'string' || !key.length) {
|
|
318
|
+
throw new Error(
|
|
319
|
+
`config.platforms.${platformKey}.deployments entries must be non-empty strings`,
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return [...deployments];
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
return ['zip'];
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Absolute-or-cwd-relative path to a deployment's credential/publish folder.
|
|
331
|
+
*
|
|
332
|
+
* @param {string} deploymentKey
|
|
333
|
+
* @param {string} [metaDir='assets/meta']
|
|
334
|
+
* @returns {string}
|
|
335
|
+
*/
|
|
336
|
+
export function resolvePublishDir(deploymentKey, metaDir = 'assets/meta') {
|
|
337
|
+
return path.join(metaDir, 'deployments', deploymentKey);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const ELECTRON_TARGETS = new Set([
|
|
341
|
+
'win', 'win-dev', 'mac', 'mac-dev', 'linux', 'linux-dev',
|
|
342
|
+
]);
|
|
343
|
+
const CORDOVA_TARGETS = new Set([
|
|
344
|
+
'android', 'android-dev', 'ios', 'ios-dev', 'ios-sim',
|
|
345
|
+
]);
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* @param {string} target
|
|
349
|
+
* @returns {string | null}
|
|
350
|
+
*/
|
|
351
|
+
export function resolveFamilyFromTarget(target) {
|
|
352
|
+
if (ELECTRON_TARGETS.has(target)) {
|
|
353
|
+
return 'electron';
|
|
354
|
+
}
|
|
355
|
+
if (CORDOVA_TARGETS.has(target)) {
|
|
356
|
+
return 'cordova';
|
|
357
|
+
}
|
|
358
|
+
if (target === 'webapp' || target === 'web') {
|
|
359
|
+
return 'webapp';
|
|
360
|
+
}
|
|
361
|
+
if (target === 'uwp') {
|
|
362
|
+
return 'uwp';
|
|
363
|
+
}
|
|
364
|
+
return null;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* @param {string[]} targets
|
|
369
|
+
* @param {string} [packager]
|
|
370
|
+
*/
|
|
371
|
+
export function resolveFamily(targets, packager) {
|
|
372
|
+
if (packager === 'electron') {
|
|
373
|
+
return 'electron';
|
|
374
|
+
}
|
|
375
|
+
if (packager === 'cordova' || packager === 'capacitor') {
|
|
376
|
+
return 'cordova';
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const families = [...new Set(targets.map((target) => resolveFamilyFromTarget(target)).filter(Boolean))];
|
|
380
|
+
|
|
381
|
+
if (families.length === 1) {
|
|
382
|
+
return families[0];
|
|
309
383
|
}
|
|
310
384
|
|
|
311
|
-
if (
|
|
385
|
+
if (targets.length === 1 && targets[0] === 'webapp') {
|
|
312
386
|
return 'webapp';
|
|
313
387
|
}
|
|
314
388
|
|
|
315
|
-
|
|
389
|
+
throw new Error(`Cannot resolve builder family for targets [${targets.join(', ')}]`);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* @param {string} platformKey
|
|
394
|
+
* @param {{ config?: { platforms?: Record<string, { builder?: string | string[], packager?: string, steamworks?: boolean, deployments?: string[] }> } }} pkg
|
|
395
|
+
*/
|
|
396
|
+
export function resolveBuildSpec(platformKey, pkg) {
|
|
397
|
+
const platform = pkg.config?.platforms?.[platformKey];
|
|
398
|
+
const builder = platform?.builder ?? (platformKey === 'web' ? 'webapp' : platformKey);
|
|
399
|
+
const targets = Array.isArray(builder) ? [...builder] : [builder];
|
|
400
|
+
|
|
401
|
+
return {
|
|
402
|
+
family: resolveFamily(targets, platform?.packager),
|
|
403
|
+
targets,
|
|
404
|
+
platformKey,
|
|
405
|
+
steamworks: platform?.steamworks === true,
|
|
406
|
+
deployments: resolveDeploymentsForPlatform(platformKey, pkg),
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* @param {string} platformKey
|
|
412
|
+
* @param {{ config?: { platforms?: Record<string, { builder?: string | string[] }> } }} pkg
|
|
413
|
+
*/
|
|
414
|
+
export function resolveBuilderForPlatform(platformKey, pkg) {
|
|
415
|
+
return resolveBuildSpec(platformKey, pkg).family;
|
|
316
416
|
}
|
|
317
417
|
|
|
318
418
|
/**
|
|
@@ -323,9 +423,11 @@ export function resolveCliPlatformAndBuilder(arg, pkg) {
|
|
|
323
423
|
const platforms = pkg.config?.platforms ?? {};
|
|
324
424
|
|
|
325
425
|
if (platforms[arg]) {
|
|
426
|
+
const spec = resolveBuildSpec(arg, pkg);
|
|
326
427
|
return {
|
|
327
428
|
platformKey: arg,
|
|
328
|
-
builder:
|
|
429
|
+
builder: spec.family,
|
|
430
|
+
buildSpec: spec,
|
|
329
431
|
};
|
|
330
432
|
}
|
|
331
433
|
|
package/lib/node/steam-auth.js
CHANGED
|
@@ -26,7 +26,7 @@ const STEAMCMD_INSTALL = {
|
|
|
26
26
|
* @param {string} [projectRoot]
|
|
27
27
|
*/
|
|
28
28
|
export function defaultSteamPublishPath(projectRoot = '.') {
|
|
29
|
-
return path.resolve(projectRoot, 'assets', 'meta', '
|
|
29
|
+
return path.resolve(projectRoot, 'assets', 'meta', 'deployments', 'steam', 'steam.json');
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
/**
|