sloss-cli 1.0.0 → 1.2.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/README.md +187 -15
- package/bin/sloss.js +19 -0
- package/package.json +15 -4
- package/skills/sloss/SKILL.md +171 -0
- package/src/client.js +27 -0
- package/src/commands/build.js +115 -0
- package/src/format.js +16 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# sloss-cli
|
|
2
2
|
|
|
3
|
-
Command-line interface for [Sloss](https://github.com/aualdrich/sloss) — a self-hosted
|
|
3
|
+
Command-line interface for [Sloss](https://github.com/aualdrich/sloss) — a self-hosted build distribution server for Expo apps. Supports iOS and Android.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -8,38 +8,170 @@ Command-line interface for [Sloss](https://github.com/aualdrich/sloss) — a sel
|
|
|
8
8
|
npm install -g sloss-cli
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Or as a project dependency:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install --save-dev sloss-cli
|
|
15
|
+
# or
|
|
16
|
+
bun add --dev sloss-cli
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Setup
|
|
20
|
+
|
|
21
|
+
### 1. Create a Sloss account
|
|
22
|
+
|
|
23
|
+
Sign up at your team's Sloss instance (e.g. `https://sloss.example.com/signup`). After signing up, you'll receive an API key on the onboarding page. You can also find it under **Settings → API Key**.
|
|
24
|
+
|
|
25
|
+
### 2. Authenticate the CLI
|
|
12
26
|
|
|
13
27
|
```bash
|
|
14
|
-
# Authenticate (saves API key to ~/.config/sloss/credentials.json)
|
|
15
28
|
sloss login
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
This prompts for your email and password, fetches your API key, and saves it to `~/.config/sloss/credentials.json`. You only need to do this once per machine.
|
|
32
|
+
|
|
33
|
+
Alternatively, set the `SLOSS_API_KEY` environment variable or pass `--api-key` to any command.
|
|
34
|
+
|
|
35
|
+
### 3. Add `.sloss.json` to your Expo project
|
|
36
|
+
|
|
37
|
+
Create a `.sloss.json` file in your Expo project root (next to `app.json`):
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"type": "expo",
|
|
42
|
+
"app_name": "MyApp",
|
|
43
|
+
"bundle_id": "com.mycompany.myapp",
|
|
44
|
+
"version_file": "app.version.json",
|
|
45
|
+
"profiles": {
|
|
46
|
+
"development": {
|
|
47
|
+
"bundle_id_suffix": ".dev"
|
|
48
|
+
},
|
|
49
|
+
"preview": {
|
|
50
|
+
"bundle_id_suffix": ".preview"
|
|
51
|
+
},
|
|
52
|
+
"production": {
|
|
53
|
+
"bundle_id_suffix": ""
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
| Field | Description |
|
|
60
|
+
|-------|-------------|
|
|
61
|
+
| `type` | Always `"expo"` |
|
|
62
|
+
| `app_name` | Display name for the build on Sloss |
|
|
63
|
+
| `bundle_id` | Base bundle identifier |
|
|
64
|
+
| `version_file` | Path to a JSON file containing `version` and `buildNumber` (relative to project root) |
|
|
65
|
+
| `profiles` | Build profile config — `bundle_id_suffix` is appended to `bundle_id` per profile |
|
|
66
|
+
|
|
67
|
+
### 4. Create `app.version.json`
|
|
68
|
+
|
|
69
|
+
Sloss reads version info from a dedicated file so the build agent can bump versions independently:
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"version": "1.0.0",
|
|
74
|
+
"buildNumber": "1"
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Reference this in your `app.json` / `app.config.js` so Expo picks it up:
|
|
79
|
+
|
|
80
|
+
```js
|
|
81
|
+
// app.config.js
|
|
82
|
+
const version = require('./app.version.json');
|
|
83
|
+
|
|
84
|
+
module.exports = {
|
|
85
|
+
expo: {
|
|
86
|
+
version: version.version,
|
|
87
|
+
ios: { buildNumber: String(version.buildNumber) },
|
|
88
|
+
android: { versionCode: parseInt(version.buildNumber, 10) },
|
|
89
|
+
// ... rest of your config
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Usage
|
|
95
|
+
|
|
96
|
+
### Queue a build
|
|
97
|
+
|
|
98
|
+
From your Expo project directory:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
# iOS development build (default)
|
|
102
|
+
sloss build
|
|
103
|
+
|
|
104
|
+
# Android preview build
|
|
105
|
+
sloss build --platform android --profile preview
|
|
106
|
+
|
|
107
|
+
# Production build with minor version bump
|
|
108
|
+
sloss build --profile production --bump minor
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
The CLI packages your project via `git archive`, uploads it to Sloss, and a build agent picks it up. You'll get a link to the build page with live logs.
|
|
112
|
+
|
|
113
|
+
#### Build options
|
|
16
114
|
|
|
17
|
-
|
|
115
|
+
| Flag | Default | Description |
|
|
116
|
+
|------|---------|-------------|
|
|
117
|
+
| `--platform <platform>` | `ios` | `ios` or `android` |
|
|
118
|
+
| `--profile <profile>` | `development` | `development`, `preview`, or `production` |
|
|
119
|
+
| `--bump <type>` | `patch` | Version bump for production builds: `patch`, `minor`, or `major` |
|
|
120
|
+
| `--dir <path>` | `.` | Project directory (if not running from project root) |
|
|
121
|
+
|
|
122
|
+
### List builds
|
|
123
|
+
|
|
124
|
+
```bash
|
|
18
125
|
sloss list
|
|
126
|
+
sloss list --limit 20
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Get build details
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
sloss info <build-id>
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Upload a pre-built artifact
|
|
136
|
+
|
|
137
|
+
If you already have an IPA or APK (e.g. from a local build):
|
|
19
138
|
|
|
139
|
+
```bash
|
|
20
140
|
# Upload an IPA
|
|
21
|
-
sloss upload
|
|
141
|
+
sloss upload ./build/MyApp.ipa --platform ios --profile preview
|
|
22
142
|
|
|
23
143
|
# Upload an APK
|
|
24
|
-
sloss upload
|
|
144
|
+
sloss upload ./build/app-release.apk --platform android --profile production
|
|
145
|
+
```
|
|
25
146
|
|
|
26
|
-
|
|
27
|
-
sloss info <build-id>
|
|
147
|
+
#### Upload options
|
|
28
148
|
|
|
29
|
-
|
|
149
|
+
| Flag | Description |
|
|
150
|
+
|------|-------------|
|
|
151
|
+
| `--platform <platform>` | **Required.** `ios` or `android` |
|
|
152
|
+
| `--profile <profile>` | Build profile: `development`, `preview`, or `production` |
|
|
153
|
+
| `--app-name <name>` | App display name |
|
|
154
|
+
| `--version <version>` | Version string |
|
|
155
|
+
| `--build-number <number>` | Build number |
|
|
156
|
+
|
|
157
|
+
### Delete a build
|
|
158
|
+
|
|
159
|
+
```bash
|
|
30
160
|
sloss delete <build-id>
|
|
31
161
|
```
|
|
32
162
|
|
|
33
163
|
## Authentication
|
|
34
164
|
|
|
35
165
|
API key resolution order (highest → lowest priority):
|
|
166
|
+
|
|
36
167
|
1. `--api-key` CLI flag
|
|
37
168
|
2. `SLOSS_API_KEY` environment variable
|
|
38
169
|
3. `~/.config/sloss/credentials.json` (saved by `sloss login`)
|
|
39
170
|
|
|
40
171
|
## Server URL
|
|
41
172
|
|
|
42
|
-
URL resolution order:
|
|
173
|
+
URL resolution order (highest → lowest priority):
|
|
174
|
+
|
|
43
175
|
1. `--url` CLI flag
|
|
44
176
|
2. `SLOSS_URL` environment variable
|
|
45
177
|
3. `~/.config/sloss/credentials.json`
|
|
@@ -47,14 +179,54 @@ URL resolution order:
|
|
|
47
179
|
|
|
48
180
|
## Global Options
|
|
49
181
|
|
|
182
|
+
| Flag | Description |
|
|
183
|
+
|------|-------------|
|
|
184
|
+
| `--api-key <key>` | Override API key |
|
|
185
|
+
| `--url <url>` | Override server URL |
|
|
186
|
+
| `--json` | Output as JSON |
|
|
187
|
+
| `--version` | Show CLI version |
|
|
188
|
+
| `--help` | Show help |
|
|
189
|
+
|
|
190
|
+
## How It Works
|
|
191
|
+
|
|
192
|
+
1. `sloss build` reads `.sloss.json` from your project root
|
|
193
|
+
2. Your committed source is packaged into a tarball via `git archive`
|
|
194
|
+
3. The tarball is uploaded to your Sloss server
|
|
195
|
+
4. A build agent picks up the job and builds it locally using Xcode (iOS) or Gradle (Android)
|
|
196
|
+
5. The finished IPA/APK is uploaded back to Sloss
|
|
197
|
+
6. You get a build page with install links and QR codes for on-device installation
|
|
198
|
+
|
|
199
|
+
## OpenClaw Skill
|
|
200
|
+
|
|
201
|
+
This package includes an [OpenClaw](https://openclaw.ai) skill so your AI agent can use the Sloss CLI. After installing `sloss-cli`, add the skill directory to your OpenClaw config:
|
|
202
|
+
|
|
203
|
+
```json5
|
|
204
|
+
// ~/.openclaw/openclaw.json
|
|
205
|
+
{
|
|
206
|
+
skills: {
|
|
207
|
+
load: {
|
|
208
|
+
extraDirs: ["./node_modules/sloss-cli/skills"]
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Or copy the skill into your workspace:
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
cp -r ./node_modules/sloss-cli/skills/sloss <workspace>/skills/sloss
|
|
50
218
|
```
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
219
|
+
|
|
220
|
+
The skill is also available on [ClawHub](https://clawhub.com):
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
clawhub install sloss
|
|
56
224
|
```
|
|
57
225
|
|
|
226
|
+
## Related
|
|
227
|
+
|
|
228
|
+
- **[Sloss Server](https://github.com/aualdrich/sloss)** — The self-hosted distribution server
|
|
229
|
+
|
|
58
230
|
## License
|
|
59
231
|
|
|
60
232
|
MIT
|
package/bin/sloss.js
CHANGED
|
@@ -11,6 +11,7 @@ import { listCommand } from '../src/commands/list.js';
|
|
|
11
11
|
import { uploadCommand } from '../src/commands/upload.js';
|
|
12
12
|
import { infoCommand } from '../src/commands/info.js';
|
|
13
13
|
import { deleteCommand } from '../src/commands/delete.js';
|
|
14
|
+
import { buildCommand } from '../src/commands/build.js';
|
|
14
15
|
|
|
15
16
|
const program = new Command();
|
|
16
17
|
|
|
@@ -100,4 +101,22 @@ program
|
|
|
100
101
|
}
|
|
101
102
|
});
|
|
102
103
|
|
|
104
|
+
// Build command
|
|
105
|
+
program
|
|
106
|
+
.command('build')
|
|
107
|
+
.description('Queue a build via the Sloss build agent')
|
|
108
|
+
.option('--platform <platform>', 'Platform (ios or android)', 'ios')
|
|
109
|
+
.option('--profile <profile>', 'Build profile (development, preview, production)', 'development')
|
|
110
|
+
.option('--bump <type>', 'Version bump type for production (patch, minor, major)', 'patch')
|
|
111
|
+
.option('--dir <path>', 'Project directory (default: current directory)', '.')
|
|
112
|
+
.action(async (options) => {
|
|
113
|
+
try {
|
|
114
|
+
const config = resolveConfig(program.opts());
|
|
115
|
+
await buildCommand(options, config);
|
|
116
|
+
} catch (error) {
|
|
117
|
+
console.error(`Error: ${error.message}`);
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
103
122
|
program.parse();
|
package/package.json
CHANGED
|
@@ -1,22 +1,33 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sloss-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "CLI for Sloss — a self-hosted IPA/APK distribution server",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"sloss": "
|
|
7
|
+
"sloss": "bin/sloss.js"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"bin/",
|
|
11
11
|
"src/",
|
|
12
|
+
"skills/",
|
|
12
13
|
"README.md"
|
|
13
14
|
],
|
|
14
|
-
"keywords": [
|
|
15
|
+
"keywords": [
|
|
16
|
+
"sloss",
|
|
17
|
+
"ios",
|
|
18
|
+
"android",
|
|
19
|
+
"ipa",
|
|
20
|
+
"apk",
|
|
21
|
+
"build",
|
|
22
|
+
"distribution",
|
|
23
|
+
"testflight",
|
|
24
|
+
"diawi"
|
|
25
|
+
],
|
|
15
26
|
"author": "Front Porch Software",
|
|
16
27
|
"license": "MIT",
|
|
17
28
|
"repository": {
|
|
18
29
|
"type": "git",
|
|
19
|
-
"url": "https://github.com/aualdrich/sloss-cli.git"
|
|
30
|
+
"url": "git+https://github.com/aualdrich/sloss-cli.git"
|
|
20
31
|
},
|
|
21
32
|
"engines": {
|
|
22
33
|
"node": ">=18.0.0"
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sloss
|
|
3
|
+
description: "Build, distribute, and manage Expo app builds (iOS & Android) via the Sloss CLI. Use when queuing builds, listing builds, uploading artifacts, checking build status, or managing the Sloss build server."
|
|
4
|
+
homepage: https://github.com/aualdrich/sloss-cli
|
|
5
|
+
metadata: {"openclaw":{"emoji":"🏭","requires":{"bins":["sloss"]},"install":[{"id":"npm","kind":"node","package":"sloss-cli","bins":["sloss"],"label":"Install Sloss CLI (npm)"}]}}
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Sloss — Build Distribution for Expo Apps
|
|
9
|
+
|
|
10
|
+
Sloss is a self-hosted build distribution server for Expo apps (iOS & Android). The `sloss` CLI queues builds, uploads artifacts, and manages builds from the terminal.
|
|
11
|
+
|
|
12
|
+
## Prerequisites
|
|
13
|
+
|
|
14
|
+
1. `sloss` CLI installed (`sloss --version` to verify)
|
|
15
|
+
2. A Sloss server instance (self-hosted)
|
|
16
|
+
3. An account on the Sloss server with an API key
|
|
17
|
+
|
|
18
|
+
## Authentication
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# Login once — saves API key to ~/.config/sloss/credentials.json
|
|
22
|
+
sloss login
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
API key resolution (highest → lowest priority):
|
|
26
|
+
1. `--api-key` flag
|
|
27
|
+
2. `SLOSS_API_KEY` environment variable
|
|
28
|
+
3. `~/.config/sloss/credentials.json`
|
|
29
|
+
|
|
30
|
+
Server URL resolution (highest → lowest priority):
|
|
31
|
+
1. `--url` flag
|
|
32
|
+
2. `SLOSS_URL` environment variable
|
|
33
|
+
3. `~/.config/sloss/credentials.json`
|
|
34
|
+
|
|
35
|
+
## Queue a Build
|
|
36
|
+
|
|
37
|
+
Run from the Expo project root (requires `.sloss.json` config file):
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# iOS development build (default)
|
|
41
|
+
sloss build
|
|
42
|
+
|
|
43
|
+
# Android preview build
|
|
44
|
+
sloss build --platform android --profile preview
|
|
45
|
+
|
|
46
|
+
# Production build with minor version bump
|
|
47
|
+
sloss build --profile production --bump minor
|
|
48
|
+
|
|
49
|
+
# Build from a different directory
|
|
50
|
+
sloss build --dir /path/to/expo/project
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Build options
|
|
54
|
+
|
|
55
|
+
| Flag | Default | Values |
|
|
56
|
+
|------|---------|--------|
|
|
57
|
+
| `--platform` | `ios` | `ios`, `android` |
|
|
58
|
+
| `--profile` | `development` | `development`, `preview`, `production` |
|
|
59
|
+
| `--bump` | `patch` | `patch`, `minor`, `major` |
|
|
60
|
+
| `--dir` | `.` | Path to project root |
|
|
61
|
+
|
|
62
|
+
### Build flow
|
|
63
|
+
|
|
64
|
+
1. CLI reads `.sloss.json` from the project root
|
|
65
|
+
2. Source is packaged via `git archive` (respects `.gitignore`)
|
|
66
|
+
3. Tarball is uploaded to the Sloss server
|
|
67
|
+
4. A build agent picks up the job and builds locally (Xcode for iOS, Gradle for Android)
|
|
68
|
+
5. The finished IPA/APK is uploaded back to Sloss
|
|
69
|
+
6. A build page with live logs and install links is available on the server
|
|
70
|
+
|
|
71
|
+
## List Builds
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
sloss list
|
|
75
|
+
sloss list --limit 20
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Build Details
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
sloss info <build-id>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Upload a Pre-Built Artifact
|
|
85
|
+
|
|
86
|
+
If you already have an IPA or APK:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
# Upload an IPA
|
|
90
|
+
sloss upload ./App.ipa --platform ios --profile preview
|
|
91
|
+
|
|
92
|
+
# Upload an APK
|
|
93
|
+
sloss upload ./app.apk --platform android --profile development
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Upload options:
|
|
97
|
+
|
|
98
|
+
| Flag | Description |
|
|
99
|
+
|------|-------------|
|
|
100
|
+
| `--platform` | **Required.** `ios` or `android` |
|
|
101
|
+
| `--profile` | `development`, `preview`, or `production` |
|
|
102
|
+
| `--app-name` | App display name |
|
|
103
|
+
| `--version` | Version string |
|
|
104
|
+
| `--build-number` | Build number |
|
|
105
|
+
|
|
106
|
+
## Delete a Build
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
sloss delete <build-id>
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Project Configuration
|
|
113
|
+
|
|
114
|
+
### `.sloss.json`
|
|
115
|
+
|
|
116
|
+
Place in the Expo project root:
|
|
117
|
+
|
|
118
|
+
```json
|
|
119
|
+
{
|
|
120
|
+
"type": "expo",
|
|
121
|
+
"app_name": "MyApp",
|
|
122
|
+
"bundle_id": "com.example.myapp",
|
|
123
|
+
"version_file": "app.version.json",
|
|
124
|
+
"profiles": {
|
|
125
|
+
"development": { "bundle_id_suffix": ".dev" },
|
|
126
|
+
"preview": { "bundle_id_suffix": ".preview" },
|
|
127
|
+
"production": { "bundle_id_suffix": "" }
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### `app.version.json`
|
|
133
|
+
|
|
134
|
+
```json
|
|
135
|
+
{
|
|
136
|
+
"version": "1.0.0",
|
|
137
|
+
"buildNumber": "1"
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Reference this in `app.config.js` so Expo picks it up:
|
|
142
|
+
|
|
143
|
+
```js
|
|
144
|
+
const version = require('./app.version.json');
|
|
145
|
+
module.exports = {
|
|
146
|
+
expo: {
|
|
147
|
+
version: version.version,
|
|
148
|
+
ios: { buildNumber: String(version.buildNumber) },
|
|
149
|
+
android: { versionCode: parseInt(version.buildNumber, 10) },
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Global Flags
|
|
155
|
+
|
|
156
|
+
| Flag | Description |
|
|
157
|
+
|------|-------------|
|
|
158
|
+
| `--api-key <key>` | Override API key |
|
|
159
|
+
| `--url <url>` | Override server URL |
|
|
160
|
+
| `--json` | JSON output |
|
|
161
|
+
| `--version` | Show CLI version |
|
|
162
|
+
| `--help` | Show help |
|
|
163
|
+
|
|
164
|
+
## Output Formats
|
|
165
|
+
|
|
166
|
+
All commands support `--json` for structured JSON output:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
sloss list --json
|
|
170
|
+
sloss info <build-id> --json
|
|
171
|
+
```
|
package/src/client.js
CHANGED
|
@@ -61,6 +61,33 @@ export class SlossClient {
|
|
|
61
61
|
return await this.request('DELETE', `/api/uploads/${id}`);
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
async startBuild(tarballPath, metadata = {}) {
|
|
65
|
+
const fs = await import('fs/promises');
|
|
66
|
+
const path = await import('path');
|
|
67
|
+
|
|
68
|
+
const fileBuffer = await fs.readFile(tarballPath);
|
|
69
|
+
const fileName = path.basename(tarballPath);
|
|
70
|
+
|
|
71
|
+
const formData = new FormData();
|
|
72
|
+
const blob = new Blob([fileBuffer]);
|
|
73
|
+
formData.append('tarball', blob, fileName);
|
|
74
|
+
|
|
75
|
+
// Add build metadata
|
|
76
|
+
if (metadata.profile) formData.append('profile', metadata.profile);
|
|
77
|
+
if (metadata.platform) formData.append('platform', metadata.platform);
|
|
78
|
+
if (metadata.bump) formData.append('bump', metadata.bump);
|
|
79
|
+
if (metadata.appName) formData.append('app_name', metadata.appName);
|
|
80
|
+
if (metadata.bundleId) formData.append('bundle_id', metadata.bundleId);
|
|
81
|
+
if (metadata.version) formData.append('version', metadata.version);
|
|
82
|
+
if (metadata.buildNumber) formData.append('build_number', metadata.buildNumber);
|
|
83
|
+
|
|
84
|
+
return await this.request('POST', '/api/builds/start', formData);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async getBuild(id) {
|
|
88
|
+
return await this.request('GET', `/api/builds/${id}`);
|
|
89
|
+
}
|
|
90
|
+
|
|
64
91
|
async uploadFile(filePath, platform, metadata = {}) {
|
|
65
92
|
const fs = await import('fs/promises');
|
|
66
93
|
const path = await import('path');
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build command - Tar up the project and queue a remote build via Sloss agent
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { SlossClient } from '../client.js';
|
|
6
|
+
import { existsSync, readFileSync } from 'fs';
|
|
7
|
+
import { resolve, basename } from 'path';
|
|
8
|
+
import { execSync } from 'child_process';
|
|
9
|
+
import { tmpdir } from 'os';
|
|
10
|
+
import { join } from 'path';
|
|
11
|
+
import { formatBuild } from '../format.js';
|
|
12
|
+
|
|
13
|
+
export async function buildCommand(options, config) {
|
|
14
|
+
const platform = (options.platform || 'ios').toLowerCase();
|
|
15
|
+
const profile = (options.profile || 'development').toLowerCase();
|
|
16
|
+
const bump = options.bump || 'patch';
|
|
17
|
+
const projectDir = resolve(options.dir || '.');
|
|
18
|
+
|
|
19
|
+
// Validate platform
|
|
20
|
+
if (!['ios', 'android'].includes(platform)) {
|
|
21
|
+
throw new Error('Platform must be "ios" or "android"');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Validate profile
|
|
25
|
+
if (!['development', 'preview', 'production'].includes(profile)) {
|
|
26
|
+
throw new Error('Profile must be "development", "preview", or "production"');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Check for .sloss.json
|
|
30
|
+
const slossConfigPath = join(projectDir, '.sloss.json');
|
|
31
|
+
if (!existsSync(slossConfigPath)) {
|
|
32
|
+
throw new Error(
|
|
33
|
+
`.sloss.json not found in ${projectDir}\n` +
|
|
34
|
+
' Create a .sloss.json config file in your project root.\n' +
|
|
35
|
+
' See: https://github.com/aualdrich/sloss#build-agent'
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Read config for display
|
|
40
|
+
const slossConfig = JSON.parse(readFileSync(slossConfigPath, 'utf8'));
|
|
41
|
+
|
|
42
|
+
console.log('╔══════════════════════════════════════════╗');
|
|
43
|
+
console.log('║ SLOSS BUILD ║');
|
|
44
|
+
console.log('╠══════════════════════════════════════════╣');
|
|
45
|
+
console.log(`║ app : ${(slossConfig.app_name || '—').padEnd(27)} ║`);
|
|
46
|
+
console.log(`║ platform : ${platform.padEnd(27)} ║`);
|
|
47
|
+
console.log(`║ profile : ${profile.padEnd(27)} ║`);
|
|
48
|
+
console.log(`║ bump : ${bump.padEnd(27)} ║`);
|
|
49
|
+
console.log(`║ server : ${config.baseUrl.padEnd(27)} ║`);
|
|
50
|
+
console.log('╚══════════════════════════════════════════╝');
|
|
51
|
+
console.log('');
|
|
52
|
+
|
|
53
|
+
// Create tarball
|
|
54
|
+
console.log('📦 Packaging project...');
|
|
55
|
+
const tarballPath = join(tmpdir(), `sloss-build-${Date.now()}.tar.gz`);
|
|
56
|
+
|
|
57
|
+
// Use git archive if in a git repo (respects .gitignore), otherwise tar with excludes
|
|
58
|
+
let tarCmd;
|
|
59
|
+
try {
|
|
60
|
+
execSync('git rev-parse --git-dir', { cwd: projectDir, stdio: 'pipe' });
|
|
61
|
+
// git archive from the repo root, only including the project subdir if needed
|
|
62
|
+
const gitRoot = execSync('git rev-parse --show-toplevel', { cwd: projectDir, encoding: 'utf8' }).trim();
|
|
63
|
+
const relPath = projectDir.replace(gitRoot, '').replace(/^\//, '');
|
|
64
|
+
|
|
65
|
+
if (relPath) {
|
|
66
|
+
// Project is in a subdirectory — include only that dir
|
|
67
|
+
tarCmd = `cd "${gitRoot}" && git archive --format=tar HEAD -- "${relPath}" | gzip > "${tarballPath}"`;
|
|
68
|
+
} else {
|
|
69
|
+
tarCmd = `cd "${projectDir}" && git archive --format=tar.gz HEAD > "${tarballPath}"`;
|
|
70
|
+
}
|
|
71
|
+
} catch {
|
|
72
|
+
// Not a git repo — fall back to tar with common excludes
|
|
73
|
+
tarCmd = `cd "${projectDir}" && tar czf "${tarballPath}" --exclude=node_modules --exclude=.git --exclude=ios/build --exclude=android/build .`;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
execSync(tarCmd, { stdio: 'pipe' });
|
|
77
|
+
|
|
78
|
+
// Get tarball size for display
|
|
79
|
+
const { statSync } = await import('fs');
|
|
80
|
+
const tarSize = statSync(tarballPath).size;
|
|
81
|
+
const sizeMB = (tarSize / (1024 * 1024)).toFixed(1);
|
|
82
|
+
console.log(` → ${sizeMB} MB`);
|
|
83
|
+
|
|
84
|
+
// Read version info from .sloss.json's version_file
|
|
85
|
+
let version = '';
|
|
86
|
+
let buildNumber = '';
|
|
87
|
+
if (slossConfig.version_file) {
|
|
88
|
+
const versionFilePath = join(projectDir, slossConfig.version_file);
|
|
89
|
+
if (existsSync(versionFilePath)) {
|
|
90
|
+
const versionData = JSON.parse(readFileSync(versionFilePath, 'utf8'));
|
|
91
|
+
version = versionData.version || '';
|
|
92
|
+
buildNumber = versionData.buildNumber || '';
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Upload tarball to start build
|
|
97
|
+
console.log('🚀 Queuing build...');
|
|
98
|
+
const client = new SlossClient(config.baseUrl, config.apiKey);
|
|
99
|
+
const result = await client.startBuild(tarballPath, {
|
|
100
|
+
profile,
|
|
101
|
+
platform,
|
|
102
|
+
bump,
|
|
103
|
+
appName: slossConfig.app_name || '',
|
|
104
|
+
bundleId: slossConfig.bundle_id || '',
|
|
105
|
+
version,
|
|
106
|
+
buildNumber,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// Clean up tarball
|
|
110
|
+
const { unlinkSync } = await import('fs');
|
|
111
|
+
try { unlinkSync(tarballPath); } catch { /* ignore */ }
|
|
112
|
+
|
|
113
|
+
console.log('');
|
|
114
|
+
console.log(formatBuild(result, config.jsonMode));
|
|
115
|
+
}
|
package/src/format.js
CHANGED
|
@@ -82,6 +82,22 @@ export function formatUpload(result, jsonMode = false) {
|
|
|
82
82
|
return lines.join('\n');
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
export function formatBuild(result, jsonMode = false) {
|
|
86
|
+
if (jsonMode) {
|
|
87
|
+
return JSON.stringify(result, null, 2);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const lines = [
|
|
91
|
+
'✅ Build queued!',
|
|
92
|
+
'',
|
|
93
|
+
`Build ID: ${result.id}`,
|
|
94
|
+
`Status: ${result.status || 'queued'}`,
|
|
95
|
+
`Page URL: ${result.page_url}`,
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
return lines.join('\n');
|
|
99
|
+
}
|
|
100
|
+
|
|
85
101
|
export function formatDelete(jsonMode = false) {
|
|
86
102
|
if (jsonMode) {
|
|
87
103
|
return JSON.stringify({ ok: true }, null, 2);
|