@makefully/adaptfully 2.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 +64 -0
- package/LICENSE +21 -0
- package/README.md +430 -0
- package/bin/wrapfully-deploy.js +7 -0
- package/lib/node/archive.js +34 -0
- package/lib/node/config.js +42 -0
- package/lib/node/deploy.js +69 -0
- package/lib/node/distribution.js +138 -0
- package/lib/node/index.js +18 -0
- package/lib/node/report.js +42 -0
- package/lib/runtime/auth/_helpers.js +38 -0
- package/lib/runtime/auth/dev-auth.js +78 -0
- package/lib/runtime/auth/google-auth.js +176 -0
- package/lib/runtime/auth/steam-auth.js +50 -0
- package/lib/runtime/core.js +61 -0
- package/lib/runtime/platform.js +89 -0
- package/package.json +61 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
## 2.1.0 — 2026-06-15
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- Node tooling is now full ESM (`import`/`export`) with no `.cjs` / `.mjs` split.
|
|
10
|
+
- Deploy logic split into focused modules: `archive.js`, `config.js`, `deploy.js`, `report.js`.
|
|
11
|
+
- Runtime uses ES classes, private fields, optional chaining, and shared `auth/_helpers.js`.
|
|
12
|
+
- Minimum Node version raised to 18; dependencies updated (`archiver` 7, `axios` 1.7).
|
|
13
|
+
|
|
14
|
+
## 2.0.0 — 2026-06-15
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- **Adaptfully** runtime library: `adaptfully.register()` / `adaptfully.get()` for platform services.
|
|
19
|
+
- Auth plugins: Google (`adaptfully.auth.Google`), Steam (`adaptfully.auth.Steam`), and dev (`adaptfully.auth.Dev`).
|
|
20
|
+
- `Platform` wrapper for uniform auth API across deployment channels.
|
|
21
|
+
- Node build helpers: `getAuthScriptsForChannel()`, `authRegistrationScript()`, `filterIncludesForBuildChannel()`, and related exports.
|
|
22
|
+
- Programmatic deploy API exported from `lib/node/deploy.js`.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- Package renamed from `@makefully/wrapfully-client` to `@makefully/adaptfully`.
|
|
27
|
+
- Deploy CLI moved to `bin/wrapfully-deploy.js`; root `deploy.js` remains as a compatibility shim.
|
|
28
|
+
|
|
29
|
+
## 1.2.0 — 2026-06-11
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
|
|
33
|
+
- Documentation for Electron debug builders: `win-dev`, `mac-dev`, `linux-dev`, and `steam-dev`.
|
|
34
|
+
|
|
35
|
+
## 1.1.2 — 2026-06-12
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
|
|
39
|
+
- npm trusted publishing workflow: upgrade npm explicitly, unset stale `NODE_AUTH_TOKEN`, and use `https://` repository URL format required by OIDC.
|
|
40
|
+
|
|
41
|
+
## 1.1.1 — 2026-06-11
|
|
42
|
+
|
|
43
|
+
### Added
|
|
44
|
+
|
|
45
|
+
- npm trusted publishing workflow (`.github/workflows/publish.yml`) with OIDC — no `NPM_TOKEN` required.
|
|
46
|
+
- `PUBLISHING.md` maintainer guide; publishes automatically on version bumps to `main`.
|
|
47
|
+
|
|
48
|
+
## 1.1.0 — 2026-06-11
|
|
49
|
+
|
|
50
|
+
### Added
|
|
51
|
+
|
|
52
|
+
- Reads `wrapfully-status.json` from `./output/` after extraction and prints build events to the console.
|
|
53
|
+
- Exits with code 1 when the server reports build errors, so CI and scripts can detect failures.
|
|
54
|
+
- Icons documentation in README (1536×1536 layered PNG requirements).
|
|
55
|
+
- Documentation for Steam cross-platform routing and credential relay between build servers.
|
|
56
|
+
|
|
57
|
+
### Changed
|
|
58
|
+
|
|
59
|
+
- Deploy waits for the response stream to finish before reporting build status.
|
|
60
|
+
- Legacy `{name}-{version}-{builder}.txt` status files are still printed when `wrapfully-status.json` is absent.
|
|
61
|
+
|
|
62
|
+
## 1.0.1
|
|
63
|
+
|
|
64
|
+
Initial published client with zip-and-post deploy flow.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) Makefully Studios
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
# Adaptfully
|
|
2
|
+
|
|
3
|
+
Platform abstraction and Wrapfully deploy client for Makefully games.
|
|
4
|
+
|
|
5
|
+
- **Adaptfully runtime** — shared auth and platform services via `adaptfully.register()` / `adaptfully.get()`
|
|
6
|
+
- **Wrapfully deploy** — zip-and-post CLI for building desktop, mobile, and Steam packages
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @makefully/adaptfully
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Maintainers: see [PUBLISHING.md](PUBLISHING.md) for npm trusted publishing setup.
|
|
15
|
+
|
|
16
|
+
## Adaptfully runtime
|
|
17
|
+
|
|
18
|
+
Games register platform services before load and retrieve them in-game. Auth is selected at **build time** by the game's build tooling — the game never chooses Google vs Steam directly.
|
|
19
|
+
|
|
20
|
+
```javascript
|
|
21
|
+
// Set by the build (before account.js loads):
|
|
22
|
+
adaptfully.register('auth', adaptfully.auth.Google);
|
|
23
|
+
|
|
24
|
+
// In-game:
|
|
25
|
+
var platform = adaptfully.get('auth');
|
|
26
|
+
platform.login(function (result) { /* ... */ });
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Auth plugins
|
|
30
|
+
|
|
31
|
+
| Plugin | Registration | Used for |
|
|
32
|
+
|--------|--------------|----------|
|
|
33
|
+
| `adaptfully.auth.Google` | `adaptfully.register('auth', adaptfully.auth.Google)` | Web, Android, iOS |
|
|
34
|
+
| `adaptfully.auth.Steam` | `adaptfully.register('auth', adaptfully.auth.Steam)` | Steam / Electron |
|
|
35
|
+
| `adaptfully.auth.Dev` | `adaptfully.register('auth', adaptfully.auth.Dev)` | Local dev (test user) |
|
|
36
|
+
|
|
37
|
+
Games can register shared dependencies before auth:
|
|
38
|
+
|
|
39
|
+
```javascript
|
|
40
|
+
adaptfully.register('storage', myStorage);
|
|
41
|
+
adaptfully.register('config', {
|
|
42
|
+
googleClientId: '...',
|
|
43
|
+
googleTokenKey: 'mygame_google_token',
|
|
44
|
+
apiBase: 'https://api.example.com/',
|
|
45
|
+
});
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Node build helpers
|
|
49
|
+
|
|
50
|
+
```javascript
|
|
51
|
+
import {
|
|
52
|
+
getAuthScriptsForChannel,
|
|
53
|
+
authRegistrationScript,
|
|
54
|
+
filterIncludesForBuildChannel,
|
|
55
|
+
extScriptsForBuildChannel,
|
|
56
|
+
} from '@makefully/adaptfully';
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`getAuthScriptsForChannel('web')` returns ordered runtime script paths. `authRegistrationScript('steam')` returns the inline registration snippet for that channel.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Wrapfully deploy
|
|
64
|
+
|
|
65
|
+
Zips your web build and configuration, POSTs them to a Wrapfully build server, and saves the platform build artifacts it returns to `./output/`.
|
|
66
|
+
|
|
67
|
+
## Quick start
|
|
68
|
+
|
|
69
|
+
1. Build your web app into a deploy folder (default: `./deploy/`, must include `index.html`).
|
|
70
|
+
2. Add build configuration to `package.json` (see [Configuration](#configuration)).
|
|
71
|
+
3. Add icons and any signing credentials under `./assets/meta/`.
|
|
72
|
+
4. Deploy to your build server:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
npx wrapfully-deploy android http://build.example.com:9630/
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Build artifacts are written to `./output/`.
|
|
79
|
+
|
|
80
|
+
## Usage
|
|
81
|
+
|
|
82
|
+
Run from your project root:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
npx wrapfully-deploy [builder] [server] [mode]
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
| Argument | Default | Description |
|
|
89
|
+
|----------|---------|-------------|
|
|
90
|
+
| `builder` | `all` | Build target (see [Builders](#builders)). **Must be a valid builder name** — `all` is not a valid endpoint; always pass a platform. |
|
|
91
|
+
| `server` | see below | Base URL of the build server |
|
|
92
|
+
| `mode` | `extract` | `extract` unpacks the response zip into `./output/`; any other value saves `./output/{name}-{version}-{builder}.zip` |
|
|
93
|
+
|
|
94
|
+
Examples:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
# Android release build
|
|
98
|
+
npx wrapfully-deploy android http://build.example.com:9630/
|
|
99
|
+
|
|
100
|
+
# Mac build using server from environment variable
|
|
101
|
+
export WRAPFULLY_SERVER=http://build.example.com:9630/
|
|
102
|
+
npx wrapfully-deploy mac
|
|
103
|
+
|
|
104
|
+
# Save the response as a zip instead of extracting
|
|
105
|
+
npx wrapfully-deploy win http://build.example.com:9630/ zip
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Add scripts to your project's `package.json`:
|
|
109
|
+
|
|
110
|
+
```json
|
|
111
|
+
{
|
|
112
|
+
"scripts": {
|
|
113
|
+
"deploy:android": "wrapfully-deploy android",
|
|
114
|
+
"deploy:mac": "wrapfully-deploy mac"
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Set `WRAPFULLY_SERVER` or a `server` field in `wrapfully.json` so scripts do not need the address on every invocation.
|
|
120
|
+
|
|
121
|
+
### Server address
|
|
122
|
+
|
|
123
|
+
The server URL is resolved in this order:
|
|
124
|
+
|
|
125
|
+
1. CLI argument
|
|
126
|
+
2. `WRAPFULLY_SERVER` environment variable
|
|
127
|
+
3. `server` field in `wrapfully.json`
|
|
128
|
+
4. `http://localhost:9630/`
|
|
129
|
+
|
|
130
|
+
Keep server addresses and credentials out of version control — use environment variables or a gitignored `wrapfully.json`.
|
|
131
|
+
|
|
132
|
+
## What gets sent
|
|
133
|
+
|
|
134
|
+
The client POSTs a zip stream to:
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
{server}{builder}/{name}-{version}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
For example, a project named `mygame` at version `1.2.0` with builder `android`:
|
|
141
|
+
|
|
142
|
+
```
|
|
143
|
+
http://build.example.com:9630/android/mygame-1.2.0
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
The server extracts the zip, reads the embedded `package.json`, runs the build for that platform, and streams a zip of artifacts back to the client.
|
|
147
|
+
|
|
148
|
+
### Zip contents
|
|
149
|
+
|
|
150
|
+
| Archive path | Source on disk | Purpose |
|
|
151
|
+
|--------------|----------------|---------|
|
|
152
|
+
| `deploy/` | `{deployFolder}/` (default `./deploy/`) | Built web app (HTML, JS, assets) |
|
|
153
|
+
| `deploy/index.html` | `{deployFolder}/index.html` | Entry point (also included via the directory) |
|
|
154
|
+
| `meta/` | `./assets/meta/` (if present) | Icons, signing keys, and publish credentials |
|
|
155
|
+
| `package.json` | project root | Merged `package.json` + `wrapfully.json` config |
|
|
156
|
+
|
|
157
|
+
### Project layout
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
mygame/
|
|
161
|
+
├── package.json # npm metadata + config block (see below)
|
|
162
|
+
├── wrapfully.json # optional — merged into config
|
|
163
|
+
├── deploy/ # built web app (or set deployFolder in config)
|
|
164
|
+
│ └── index.html
|
|
165
|
+
└── assets/
|
|
166
|
+
└── meta/ # packaged as meta/ in the zip
|
|
167
|
+
├── icon-foreground.png
|
|
168
|
+
├── icon-background.png
|
|
169
|
+
└── publish/ # platform signing & deploy credentials
|
|
170
|
+
├── build.json
|
|
171
|
+
├── android/
|
|
172
|
+
├── apple.json
|
|
173
|
+
└── ...
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Icons (`icon-foreground.png`, `icon-background.png`) are required for mobile, desktop, and Steam builds.
|
|
177
|
+
|
|
178
|
+
### Icons
|
|
179
|
+
|
|
180
|
+
Place two layered PNG files in `./assets/meta/` (packaged as `meta/` in the zip):
|
|
181
|
+
|
|
182
|
+
| File | Purpose |
|
|
183
|
+
|------|---------|
|
|
184
|
+
| `icon-foreground.png` | Foreground layer (typically the character or subject) |
|
|
185
|
+
| `icon-background.png` | Background layer (typically the scene or environment) |
|
|
186
|
+
|
|
187
|
+
The build server composites the foreground over the background, applies a binding/logo overlay, and generates the icon sizes each platform needs.
|
|
188
|
+
|
|
189
|
+
**Recommended format:** 1536×1536 pixel square PNGs for both files. Images with other dimensions are scaled to 1536×1536 automatically, but matching the target size produces the sharpest results.
|
|
190
|
+
|
|
191
|
+
## Configuration
|
|
192
|
+
|
|
193
|
+
Build settings are read from `package.json`. The client merges any `wrapfully.json` fields into `package.json`'s `config` object before sending.
|
|
194
|
+
|
|
195
|
+
### `package.json`
|
|
196
|
+
|
|
197
|
+
Standard npm fields (`name`, `version`, `description`) are used directly. Add a `config` block:
|
|
198
|
+
|
|
199
|
+
```json
|
|
200
|
+
{
|
|
201
|
+
"name": "mygame",
|
|
202
|
+
"version": "1.2.0",
|
|
203
|
+
"description": "My game",
|
|
204
|
+
"config": {
|
|
205
|
+
"title": "My Game",
|
|
206
|
+
"packageName": "com.example.mygame",
|
|
207
|
+
"publisherDisplayName": "Example Games",
|
|
208
|
+
"publisherFullName": "Example Games LLC",
|
|
209
|
+
"publisherWebsite": "https://example.com",
|
|
210
|
+
"publisherEmailAddress": "hello@example.com",
|
|
211
|
+
"scope": "https://example.com/games/",
|
|
212
|
+
"themeColor": "#1a1a2e",
|
|
213
|
+
"twitterId": "@examplegames",
|
|
214
|
+
"steamId": 1234567,
|
|
215
|
+
"properties": [
|
|
216
|
+
{ "tag": "plugin", "name": "cordova-plugin-inappbrowser" },
|
|
217
|
+
{ "tag": "allow-navigation", "href": "*" }
|
|
218
|
+
]
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
| Field | Used by | Description |
|
|
224
|
+
|-------|---------|-------------|
|
|
225
|
+
| `title` | All | Display name shown in stores and app shells |
|
|
226
|
+
| `packageName` | Cordova, Electron, UWP | Reverse-DNS identifier (`com.company.game`) |
|
|
227
|
+
| `publisherDisplayName` | Cordova, Electron, web | Short publisher name |
|
|
228
|
+
| `publisherFullName` | Electron | Legal entity name for copyright |
|
|
229
|
+
| `publisherWebsite` | Cordova, web | Company URL |
|
|
230
|
+
| `publisherEmailAddress` | Cordova | Contact email |
|
|
231
|
+
| `scope` | Web/PWA | Base URL scope for the web app |
|
|
232
|
+
| `themeColor` | Cordova, UWP, web | Loading screen / theme color |
|
|
233
|
+
| `twitterId` | Web | Twitter handle for meta tags |
|
|
234
|
+
| `steamId` | Steam | Steam app ID |
|
|
235
|
+
| `properties` | Cordova | Cordova config.xml entries (plugins, allow-navigation, etc.) |
|
|
236
|
+
| `deployFolder` | Client | Deploy directory name (default: `deploy`) |
|
|
237
|
+
|
|
238
|
+
### `wrapfully.json`
|
|
239
|
+
|
|
240
|
+
Optional. Fields are shallow-merged into `package.json`'s `config`:
|
|
241
|
+
|
|
242
|
+
```json
|
|
243
|
+
{
|
|
244
|
+
"deployFolder": "dist",
|
|
245
|
+
"server": "http://build.example.com:9630/",
|
|
246
|
+
"title": "My Game",
|
|
247
|
+
"packageName": "com.example.mygame"
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Use this to set the server address or override config per environment without editing `package.json`.
|
|
252
|
+
|
|
253
|
+
## Builders
|
|
254
|
+
|
|
255
|
+
Each builder name becomes a path segment on the server. Some builds require a specific host OS on the server side; composite builders fan out to multiple platforms automatically.
|
|
256
|
+
|
|
257
|
+
| Builder | Output |
|
|
258
|
+
|---------|--------|
|
|
259
|
+
| `android` | Release Android (.aab) |
|
|
260
|
+
| `android-dev` | Debug Android (.apk) |
|
|
261
|
+
| `ios` | Release iOS (.ipa) |
|
|
262
|
+
| `ios-dev` | Debug iOS (.ipa) |
|
|
263
|
+
| `ios-sim` | iOS Simulator (.app) |
|
|
264
|
+
| `mac` | Release Mac (.app) |
|
|
265
|
+
| `mac-dev` | Debug Mac (.app) with DevTools |
|
|
266
|
+
| `win` | Windows portable (.exe) |
|
|
267
|
+
| `win-dev` | Debug Windows portable with DevTools |
|
|
268
|
+
| `linux` | Linux build |
|
|
269
|
+
| `linux-dev` | Debug Linux build with DevTools |
|
|
270
|
+
| `uwp` | Universal Windows Package |
|
|
271
|
+
| `webapp` | Service-worker web app (optionally SFTP deploy) |
|
|
272
|
+
| `steam` | Windows + Mac + Linux, uploads to Steam |
|
|
273
|
+
| `steam-dev` | Debug Windows + Mac + Linux, no Steam upload |
|
|
274
|
+
| `cordova` | Release Android + iOS |
|
|
275
|
+
| `cordova-dev` | Debug Android + iOS |
|
|
276
|
+
| `apple` | Release Mac + iOS |
|
|
277
|
+
| `apple-dev` | Release Mac + debug iOS |
|
|
278
|
+
|
|
279
|
+
For a single platform, pass the specific builder name rather than a composite.
|
|
280
|
+
|
|
281
|
+
### Platform package requirements
|
|
282
|
+
|
|
283
|
+
Signing keys, provisioning profiles, and store credentials go in `./assets/meta/publish/` on disk (sent as `meta/publish/` in the zip). **These files contain secrets** — add them to `.gitignore` and never commit them to a public repository.
|
|
284
|
+
|
|
285
|
+
#### Android (`android`, `android-dev`)
|
|
286
|
+
|
|
287
|
+
Place keystore files in `assets/meta/publish/android/`. Include `assets/meta/publish/build.json`:
|
|
288
|
+
|
|
289
|
+
```json
|
|
290
|
+
{
|
|
291
|
+
"android": {
|
|
292
|
+
"debug": {
|
|
293
|
+
"keystore": "./android/debug.keystore",
|
|
294
|
+
"packageType": "apk",
|
|
295
|
+
"storePassword": "android",
|
|
296
|
+
"alias": "androiddebugkey",
|
|
297
|
+
"password": "android",
|
|
298
|
+
"keystoreType": ""
|
|
299
|
+
},
|
|
300
|
+
"release": {
|
|
301
|
+
"keystore": "./android/release.keystore",
|
|
302
|
+
"packageType": "bundle",
|
|
303
|
+
"storePassword": "(your store password)",
|
|
304
|
+
"alias": "(your alias)",
|
|
305
|
+
"password": "(your password)",
|
|
306
|
+
"keystoreType": ""
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
To deploy to Google Play, also include `assets/meta/publish/google.json`:
|
|
313
|
+
|
|
314
|
+
```json
|
|
315
|
+
{
|
|
316
|
+
"type": "service_account",
|
|
317
|
+
"project_id": "(your project id)",
|
|
318
|
+
"private_key_id": "(your private key id)",
|
|
319
|
+
"private_key": "(your private key)",
|
|
320
|
+
"client_email": "(your service account email)",
|
|
321
|
+
"client_id": "(your client id)",
|
|
322
|
+
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
|
323
|
+
"token_uri": "https://oauth2.googleapis.com/token",
|
|
324
|
+
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
|
|
325
|
+
"client_x509_cert_url": "(your service account cert URL)"
|
|
326
|
+
}
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
#### Apple (`ios`, `ios-dev`, `ios-sim`, `mac`, `apple`, `apple-dev`)
|
|
330
|
+
|
|
331
|
+
Include `assets/meta/publish/build.json` with iOS signing settings:
|
|
332
|
+
|
|
333
|
+
```json
|
|
334
|
+
{
|
|
335
|
+
"ios": {
|
|
336
|
+
"debug": {
|
|
337
|
+
"codeSignIdentity": "iPhone Development",
|
|
338
|
+
"provisioningProfile": "(your development provisioning profile id)",
|
|
339
|
+
"developmentTeam": "(your team id)",
|
|
340
|
+
"packageType": "development",
|
|
341
|
+
"automaticProvisioning": false
|
|
342
|
+
},
|
|
343
|
+
"release": {
|
|
344
|
+
"codeSignIdentity": "iPhone Distribution",
|
|
345
|
+
"provisioningProfile": "(your distribution provisioning profile id)",
|
|
346
|
+
"developmentTeam": "(your team id)",
|
|
347
|
+
"packageType": "app-store",
|
|
348
|
+
"automaticProvisioning": false
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
To deploy to the App Store, include `assets/meta/publish/apple.json`:
|
|
355
|
+
|
|
356
|
+
```json
|
|
357
|
+
{
|
|
358
|
+
"category": "(your app's category)",
|
|
359
|
+
"identity": "(your team identity)",
|
|
360
|
+
"username": "(your username)",
|
|
361
|
+
"password": "(your password)"
|
|
362
|
+
}
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
#### Cordova (`cordova`, `cordova-dev`)
|
|
366
|
+
|
|
367
|
+
Requires the Android and Apple package requirements above.
|
|
368
|
+
|
|
369
|
+
#### Steam (`steam`, `steam-dev`)
|
|
370
|
+
|
|
371
|
+
`steam-dev` builds debug Electron binaries for Windows, Mac, and Linux without uploading to Steam. No `steam.json` credentials are required.
|
|
372
|
+
|
|
373
|
+
For release uploads, include `assets/meta/publish/steam.json`:
|
|
374
|
+
|
|
375
|
+
```json
|
|
376
|
+
{
|
|
377
|
+
"username": "(your username)",
|
|
378
|
+
"password": "(your password)"
|
|
379
|
+
}
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
Also set `steamId` in your `config` block.
|
|
383
|
+
|
|
384
|
+
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.
|
|
385
|
+
|
|
386
|
+
When builds relay between servers, `meta/publish/` credentials travel in the zip with the game payload.
|
|
387
|
+
|
|
388
|
+
#### Electron (`win`, `win-dev`, `mac`, `mac-dev`, `linux`, `linux-dev`, `steam`, `steam-dev`)
|
|
389
|
+
|
|
390
|
+
`-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.
|
|
391
|
+
|
|
392
|
+
Release `win` builds can be signed with `assets/meta/publish/ms.json` (see Windows below). Release `mac` builds can use `assets/meta/publish/apple.json` for signing and notarization (see Apple above).
|
|
393
|
+
|
|
394
|
+
#### Web app (`webapp`)
|
|
395
|
+
|
|
396
|
+
To deploy via SFTP, include `assets/meta/publish/sftp.json`:
|
|
397
|
+
|
|
398
|
+
```json
|
|
399
|
+
{
|
|
400
|
+
"webapp": {
|
|
401
|
+
"host": "(your sftp host)",
|
|
402
|
+
"port": 22,
|
|
403
|
+
"user": "(your username)",
|
|
404
|
+
"password": "(your password)",
|
|
405
|
+
"path": "(the sftp subdirectory in which to publish the app)"
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
#### Windows (`win`, `win-dev`, `uwp`)
|
|
411
|
+
|
|
412
|
+
To sign the app, place your certificate at `assets/meta/publish/ms/packcert.pfx` and include `assets/meta/publish/ms.json`:
|
|
413
|
+
|
|
414
|
+
```json
|
|
415
|
+
{
|
|
416
|
+
"publisherName": "CN=(your publisher id)",
|
|
417
|
+
"certificateFile": "./ms/packcert.pfx",
|
|
418
|
+
"password": "(your password)"
|
|
419
|
+
}
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
## Response
|
|
423
|
+
|
|
424
|
+
The server responds with a zip stream containing build artifacts (`.apk`, `.aab`, `.ipa`, `.app`, `.exe`, etc.) and optional status files. By default the client extracts this into `./output/`. Use a non-`extract` mode value to save the raw response zip instead.
|
|
425
|
+
|
|
426
|
+
Every build also includes `wrapfully-status.json` with structured `success`, `warn`, and `error` events. The client prints these after extraction and exits with code 1 if any errors were reported, so build failures do not crash the server silently.
|
|
427
|
+
|
|
428
|
+
## License
|
|
429
|
+
|
|
430
|
+
MIT
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import archiver from 'archiver';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @param {string} deployFolder
|
|
6
|
+
* @param {string} contents - serialized package.json for the zip
|
|
7
|
+
*/
|
|
8
|
+
export function createArchive(deployFolder, contents) {
|
|
9
|
+
const zip = archiver('zip', { zlib: { level: 0 } });
|
|
10
|
+
|
|
11
|
+
zip.on('warning', (err) => {
|
|
12
|
+
if (err.code === 'ENOENT') {
|
|
13
|
+
console.log(err);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
throw err;
|
|
17
|
+
});
|
|
18
|
+
zip.on('error', (err) => {
|
|
19
|
+
throw err;
|
|
20
|
+
});
|
|
21
|
+
zip.on('close', () => {
|
|
22
|
+
console.log(`Zipped ${zip.pointer()} total bytes`);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
zip.directory(`${deployFolder}/`, 'deploy');
|
|
26
|
+
zip.file(`${deployFolder}/index.html`, { name: 'deploy/index.html' });
|
|
27
|
+
if (fs.existsSync('assets/meta/')) {
|
|
28
|
+
zip.directory('assets/meta/', 'meta');
|
|
29
|
+
}
|
|
30
|
+
zip.append(contents, { name: 'package.json' });
|
|
31
|
+
zip.finalize();
|
|
32
|
+
|
|
33
|
+
return zip;
|
|
34
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
|
|
3
|
+
const DEFAULT_SERVER = 'http://localhost:9630/';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @param {string} [projectRoot='.']
|
|
7
|
+
*/
|
|
8
|
+
export async function loadProjectConfig(projectRoot = '.') {
|
|
9
|
+
const pkgPath = `${projectRoot}/package.json`;
|
|
10
|
+
const wrapfullyPath = `${projectRoot}/wrapfully.json`;
|
|
11
|
+
|
|
12
|
+
const pkg = JSON.parse(await fs.readFile(pkgPath, 'utf8'));
|
|
13
|
+
let wrapfullyConfig = {};
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
wrapfullyConfig = JSON.parse(await fs.readFile(wrapfullyPath, 'utf8'));
|
|
17
|
+
} catch (err) {
|
|
18
|
+
if (/** @type {NodeJS.ErrnoException} */ (err).code !== 'ENOENT') {
|
|
19
|
+
throw err;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
pkg.config = {
|
|
24
|
+
...pkg.config,
|
|
25
|
+
...wrapfullyConfig,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
return { pkg, wrapfullyConfig };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @param {{ server?: string }} wrapfullyConfig
|
|
33
|
+
* @param {string} [cliServer]
|
|
34
|
+
*/
|
|
35
|
+
export function resolveServerUrl(wrapfullyConfig, cliServer) {
|
|
36
|
+
return (
|
|
37
|
+
cliServer
|
|
38
|
+
|| process.env.WRAPFULLY_SERVER
|
|
39
|
+
|| wrapfullyConfig.server
|
|
40
|
+
|| DEFAULT_SERVER
|
|
41
|
+
).replace(/\/?$/, '/');
|
|
42
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import { pipeline } from 'node:stream/promises';
|
|
4
|
+
import unzipper from 'unzip-stream';
|
|
5
|
+
import { createArchive } from './archive.js';
|
|
6
|
+
import { loadProjectConfig, resolveServerUrl } from './config.js';
|
|
7
|
+
import { printBuildReport } from './report.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @param {string} gameId
|
|
11
|
+
* @param {string} contents
|
|
12
|
+
* @param {string} server
|
|
13
|
+
* @param {string} builder
|
|
14
|
+
* @param {string} deployFolder
|
|
15
|
+
* @param {{ name: string, version: string }} pkg
|
|
16
|
+
* @param {'extract' | string} mode
|
|
17
|
+
*/
|
|
18
|
+
export async function send(gameId, contents, server, builder, deployFolder, pkg, mode = 'extract') {
|
|
19
|
+
const destination = mode === 'extract'
|
|
20
|
+
? unzipper.Extract({ path: './output/', concurrency: 1 })
|
|
21
|
+
: fs.createWriteStream(`./output/${pkg.name}-${pkg.version}-${builder}.zip`);
|
|
22
|
+
|
|
23
|
+
const archiveStream = createArchive(deployFolder, contents);
|
|
24
|
+
const { data } = await axios.post(`${server}${builder}/${gameId}`, archiveStream, {
|
|
25
|
+
maxRedirects: 0,
|
|
26
|
+
responseType: 'stream',
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
archiveStream.on('close', () => {
|
|
30
|
+
console.log('completed send');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
await pipeline(data, destination);
|
|
35
|
+
} catch (err) {
|
|
36
|
+
if (/** @type {NodeJS.ErrnoException} */ (err).code === 'ECONNREFUSED') {
|
|
37
|
+
console.error(`Cannot connect to Wrapfully server "${server}"`);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
throw err;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (mode === 'extract') {
|
|
44
|
+
printBuildReport(builder, pkg);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @param {string[]} [argv=process.argv]
|
|
50
|
+
*/
|
|
51
|
+
export async function deployFromCli(argv = process.argv) {
|
|
52
|
+
const builder = argv[2] ?? 'all';
|
|
53
|
+
const cliServer = argv[3];
|
|
54
|
+
const mode = argv[4] ?? 'extract';
|
|
55
|
+
|
|
56
|
+
const { pkg, wrapfullyConfig } = await loadProjectConfig();
|
|
57
|
+
const server = resolveServerUrl(wrapfullyConfig, cliServer);
|
|
58
|
+
const deployFolder = pkg.config?.deployFolder || 'deploy';
|
|
59
|
+
|
|
60
|
+
await send(
|
|
61
|
+
`${pkg.name}-${pkg.version}`,
|
|
62
|
+
JSON.stringify(pkg),
|
|
63
|
+
server,
|
|
64
|
+
builder,
|
|
65
|
+
deployFolder,
|
|
66
|
+
pkg,
|
|
67
|
+
mode,
|
|
68
|
+
);
|
|
69
|
+
}
|