@fixprompt/cli 0.0.1
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 +86 -0
- package/dist/cli.js +238 -0
- package/package.json +33 -0
package/README.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# @fixprompt/cli
|
|
2
|
+
|
|
3
|
+
FixPrompt command-line tool. One subcommand for now: `deploy-start`, which posts a deploy marker to the broker so the dashboard knows what changed.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @fixprompt/cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or just one-shot it with no install:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx @fixprompt/cli deploy-start --status success
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## `fixprompt deploy-start`
|
|
18
|
+
|
|
19
|
+
Tells the broker that a deploy just happened. Reads your `commit_sha` / `branch` / `repo` / `commit_message` automatically when run inside a git working tree or under one of these CI providers:
|
|
20
|
+
|
|
21
|
+
- GitHub Actions (`GITHUB_SHA`, `GITHUB_REF_NAME`, `GITHUB_REPOSITORY`)
|
|
22
|
+
- Vercel (`VERCEL_GIT_COMMIT_SHA`, `VERCEL_GIT_COMMIT_REF`, `VERCEL_GIT_REPO_*`)
|
|
23
|
+
- Railway (`RAILWAY_GIT_COMMIT_SHA`, `RAILWAY_GIT_BRANCH`, …)
|
|
24
|
+
|
|
25
|
+
### Auth
|
|
26
|
+
|
|
27
|
+
Pass via env vars (preferred for CI):
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
export LOGHUB_SOURCE=my-app-prod # from project creation
|
|
31
|
+
export LOGHUB_KEY=k_... # from project creation / Rotate Key
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Or via flags:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
fixprompt deploy-start \
|
|
38
|
+
--source my-app-prod \
|
|
39
|
+
--key k_... \
|
|
40
|
+
--status success
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Flags
|
|
44
|
+
|
|
45
|
+
| flag | default | purpose |
|
|
46
|
+
|------|---------|---------|
|
|
47
|
+
| `--source <slug-prod>` | `$LOGHUB_SOURCE` / `$FIXPROMPT_SOURCE` | project source label |
|
|
48
|
+
| `--key <k_...>` | `$LOGHUB_KEY` / `$FIXPROMPT_KEY` | ingest key |
|
|
49
|
+
| `--endpoint <url>` | `$FIXPROMPT_ENDPOINT` or `https://geosloghub-production.up.railway.app` | broker URL |
|
|
50
|
+
| `--status <state>` | `in_progress` | `in_progress` \| `success` \| `failed` |
|
|
51
|
+
| `--commit-sha <sha>` | auto | override auto-detected commit |
|
|
52
|
+
| `--branch <name>` | auto | override auto-detected branch |
|
|
53
|
+
| `--message <text>` | auto | override auto-detected commit subject |
|
|
54
|
+
|
|
55
|
+
### EAS / Expo
|
|
56
|
+
|
|
57
|
+
In `eas-build-post-install` or a custom workflow step:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npx --yes @fixprompt/cli deploy-start --status success
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
EAS env vars (`EAS_BUILD_GIT_COMMIT_HASH`, `EAS_BUILD_GIT_BRANCH_NAME`) aren't recognised yet — pass them explicitly until V2:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npx --yes @fixprompt/cli deploy-start \
|
|
67
|
+
--status success \
|
|
68
|
+
--commit-sha "$EAS_BUILD_GIT_COMMIT_HASH" \
|
|
69
|
+
--branch "$EAS_BUILD_GIT_BRANCH_NAME"
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### GitHub Actions
|
|
73
|
+
|
|
74
|
+
Use the prebuilt composite action instead — same effect, less typing:
|
|
75
|
+
|
|
76
|
+
```yaml
|
|
77
|
+
- uses: geos/fixprompt-deploy@v1
|
|
78
|
+
with:
|
|
79
|
+
source: ${{ secrets.FIXPROMPT_SOURCE }}
|
|
80
|
+
key: ${{ secrets.FIXPROMPT_KEY }}
|
|
81
|
+
status: success
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## License
|
|
85
|
+
|
|
86
|
+
UNLICENSED (closed beta).
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/detect.ts
|
|
4
|
+
import { execSync } from "child_process";
|
|
5
|
+
function git(args) {
|
|
6
|
+
try {
|
|
7
|
+
return execSync(`git ${args}`, {
|
|
8
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
9
|
+
}).toString().trim();
|
|
10
|
+
} catch {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function gitRemoteToRepoUrl(remoteUrl) {
|
|
15
|
+
if (!remoteUrl) return { repo: null, webBase: null };
|
|
16
|
+
const sshMatch = remoteUrl.match(/git@([^:]+):(.+?)(\.git)?$/);
|
|
17
|
+
if (sshMatch) {
|
|
18
|
+
const [, host, slug] = sshMatch;
|
|
19
|
+
return { repo: slug, webBase: `https://${host}/${slug}` };
|
|
20
|
+
}
|
|
21
|
+
const httpsMatch = remoteUrl.match(/^https?:\/\/([^/]+)\/(.+?)(\.git)?$/);
|
|
22
|
+
if (httpsMatch) {
|
|
23
|
+
const [, host, slug] = httpsMatch;
|
|
24
|
+
return { repo: slug, webBase: `https://${host}/${slug}` };
|
|
25
|
+
}
|
|
26
|
+
return { repo: null, webBase: null };
|
|
27
|
+
}
|
|
28
|
+
function detectFromCi() {
|
|
29
|
+
const env = process.env;
|
|
30
|
+
if (env.GITHUB_ACTIONS === "true") {
|
|
31
|
+
return {
|
|
32
|
+
provider: "github-actions",
|
|
33
|
+
commit_sha: env.GITHUB_SHA ?? "",
|
|
34
|
+
branch: env.GITHUB_REF_NAME ?? null,
|
|
35
|
+
repo: env.GITHUB_REPOSITORY ?? null,
|
|
36
|
+
commit_url: env.GITHUB_SERVER_URL && env.GITHUB_REPOSITORY && env.GITHUB_SHA ? `${env.GITHUB_SERVER_URL}/${env.GITHUB_REPOSITORY}/commit/${env.GITHUB_SHA}` : null
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
if (env.VERCEL === "1" || env.VERCEL === "true") {
|
|
40
|
+
return {
|
|
41
|
+
provider: "vercel",
|
|
42
|
+
commit_sha: env.VERCEL_GIT_COMMIT_SHA ?? "",
|
|
43
|
+
branch: env.VERCEL_GIT_COMMIT_REF ?? null,
|
|
44
|
+
repo: env.VERCEL_GIT_REPO_OWNER && env.VERCEL_GIT_REPO_SLUG ? `${env.VERCEL_GIT_REPO_OWNER}/${env.VERCEL_GIT_REPO_SLUG}` : null,
|
|
45
|
+
commit_message: env.VERCEL_GIT_COMMIT_MESSAGE ?? null
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
if (env.RAILWAY_ENVIRONMENT) {
|
|
49
|
+
return {
|
|
50
|
+
provider: "railway",
|
|
51
|
+
commit_sha: env.RAILWAY_GIT_COMMIT_SHA ?? "",
|
|
52
|
+
branch: env.RAILWAY_GIT_BRANCH ?? null,
|
|
53
|
+
repo: env.RAILWAY_GIT_REPO_OWNER && env.RAILWAY_GIT_REPO_NAME ? `${env.RAILWAY_GIT_REPO_OWNER}/${env.RAILWAY_GIT_REPO_NAME}` : null,
|
|
54
|
+
commit_message: env.RAILWAY_GIT_COMMIT_MESSAGE ?? null
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return {};
|
|
58
|
+
}
|
|
59
|
+
function detectFromGit() {
|
|
60
|
+
const sha = git("rev-parse HEAD");
|
|
61
|
+
const branch = git("rev-parse --abbrev-ref HEAD");
|
|
62
|
+
const remote = git("config --get remote.origin.url");
|
|
63
|
+
const message = git("log -1 --pretty=format:%s");
|
|
64
|
+
const { repo, webBase } = gitRemoteToRepoUrl(remote);
|
|
65
|
+
return {
|
|
66
|
+
provider: "manual",
|
|
67
|
+
commit_sha: sha ?? "",
|
|
68
|
+
branch: branch ?? null,
|
|
69
|
+
repo: repo ?? null,
|
|
70
|
+
commit_message: message ?? null,
|
|
71
|
+
commit_url: webBase && sha ? `${webBase}/commit/${sha}` : null
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function detectContext() {
|
|
75
|
+
const ci = detectFromCi();
|
|
76
|
+
const ciHasSha = typeof ci.commit_sha === "string" && ci.commit_sha.length > 0;
|
|
77
|
+
const git2 = detectFromGit();
|
|
78
|
+
const merged = {
|
|
79
|
+
provider: ci.provider ?? git2.provider ?? "manual",
|
|
80
|
+
commit_sha: (ciHasSha ? ci.commit_sha : git2.commit_sha) ?? "",
|
|
81
|
+
branch: ci.branch ?? git2.branch ?? null,
|
|
82
|
+
repo: ci.repo ?? git2.repo ?? null,
|
|
83
|
+
commit_message: ci.commit_message ?? git2.commit_message ?? null,
|
|
84
|
+
commit_url: ci.commit_url ?? git2.commit_url ?? null
|
|
85
|
+
};
|
|
86
|
+
return merged;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// src/version.ts
|
|
90
|
+
var CLI_NAME = "@fixprompt/cli";
|
|
91
|
+
var CLI_VERSION = "0.0.1";
|
|
92
|
+
|
|
93
|
+
// src/cli.ts
|
|
94
|
+
var DEFAULT_ENDPOINT = "https://geosloghub-production.up.railway.app";
|
|
95
|
+
function parseArgs(argv) {
|
|
96
|
+
const flags = {};
|
|
97
|
+
const positional = [];
|
|
98
|
+
let i = 0;
|
|
99
|
+
const command = argv[0] ?? "help";
|
|
100
|
+
for (i = 1; i < argv.length; i++) {
|
|
101
|
+
const tok = argv[i];
|
|
102
|
+
if (tok.startsWith("--")) {
|
|
103
|
+
const eq = tok.indexOf("=");
|
|
104
|
+
if (eq !== -1) {
|
|
105
|
+
flags[tok.slice(2, eq)] = tok.slice(eq + 1);
|
|
106
|
+
} else {
|
|
107
|
+
const next = argv[i + 1];
|
|
108
|
+
if (next && !next.startsWith("--")) {
|
|
109
|
+
flags[tok.slice(2)] = next;
|
|
110
|
+
i++;
|
|
111
|
+
} else {
|
|
112
|
+
flags[tok.slice(2)] = true;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
} else {
|
|
116
|
+
positional.push(tok);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return { command, flags, positional };
|
|
120
|
+
}
|
|
121
|
+
function helpText() {
|
|
122
|
+
return `${CLI_NAME} v${CLI_VERSION}
|
|
123
|
+
|
|
124
|
+
Usage:
|
|
125
|
+
fixprompt deploy-start [options]
|
|
126
|
+
fixprompt help
|
|
127
|
+
|
|
128
|
+
deploy-start options:
|
|
129
|
+
--source <slug> LOGHUB_SOURCE (default: $LOGHUB_SOURCE or $FIXPROMPT_SOURCE env)
|
|
130
|
+
--key <k_...> LOGHUB_KEY (default: $LOGHUB_KEY or $FIXPROMPT_KEY env)
|
|
131
|
+
--endpoint <url> Broker URL (default: $FIXPROMPT_ENDPOINT or ${DEFAULT_ENDPOINT})
|
|
132
|
+
--status <state> in_progress | success | failed (default: in_progress)
|
|
133
|
+
--commit-sha <sha> Override auto-detected commit
|
|
134
|
+
--branch <name> Override auto-detected branch
|
|
135
|
+
--message <text> Override auto-detected commit subject
|
|
136
|
+
|
|
137
|
+
Auto-detected CI providers: GitHub Actions, Vercel, Railway.
|
|
138
|
+
`;
|
|
139
|
+
}
|
|
140
|
+
function readSource(flags) {
|
|
141
|
+
const v = flags.source ?? process.env.LOGHUB_SOURCE ?? process.env.FIXPROMPT_SOURCE;
|
|
142
|
+
if (!v) {
|
|
143
|
+
console.error("Missing --source (or $LOGHUB_SOURCE / $FIXPROMPT_SOURCE)");
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
return v;
|
|
147
|
+
}
|
|
148
|
+
function readKey(flags) {
|
|
149
|
+
const v = flags.key ?? process.env.LOGHUB_KEY ?? process.env.FIXPROMPT_KEY;
|
|
150
|
+
if (!v) {
|
|
151
|
+
console.error("Missing --key (or $LOGHUB_KEY / $FIXPROMPT_KEY)");
|
|
152
|
+
process.exit(1);
|
|
153
|
+
}
|
|
154
|
+
return v;
|
|
155
|
+
}
|
|
156
|
+
function readEndpoint(flags) {
|
|
157
|
+
const v = flags.endpoint ?? process.env.FIXPROMPT_ENDPOINT ?? DEFAULT_ENDPOINT;
|
|
158
|
+
return v.replace(/\/$/, "");
|
|
159
|
+
}
|
|
160
|
+
async function deployStart(args) {
|
|
161
|
+
const source = readSource(args.flags);
|
|
162
|
+
const key = readKey(args.flags);
|
|
163
|
+
const endpoint = readEndpoint(args.flags);
|
|
164
|
+
const status = args.flags.status ?? "in_progress";
|
|
165
|
+
const ctx = detectContext();
|
|
166
|
+
const commit_sha = args.flags["commit-sha"] ?? ctx.commit_sha;
|
|
167
|
+
if (!commit_sha) {
|
|
168
|
+
console.error(
|
|
169
|
+
"Could not detect a commit_sha. Run inside a git working tree or pass --commit-sha."
|
|
170
|
+
);
|
|
171
|
+
process.exit(1);
|
|
172
|
+
}
|
|
173
|
+
const payload = {
|
|
174
|
+
commit_sha,
|
|
175
|
+
branch: args.flags.branch ?? ctx.branch,
|
|
176
|
+
repo: ctx.repo,
|
|
177
|
+
provider: ctx.provider,
|
|
178
|
+
commit_message: args.flags.message ?? ctx.commit_message,
|
|
179
|
+
commit_url: ctx.commit_url,
|
|
180
|
+
fixprompt_version: CLI_VERSION,
|
|
181
|
+
status
|
|
182
|
+
};
|
|
183
|
+
const url = `${endpoint}/deployments`;
|
|
184
|
+
const res = await fetch(url, {
|
|
185
|
+
method: "POST",
|
|
186
|
+
headers: {
|
|
187
|
+
"Content-Type": "application/json",
|
|
188
|
+
"x-loghub-source": source,
|
|
189
|
+
"x-loghub-key": key
|
|
190
|
+
},
|
|
191
|
+
body: JSON.stringify(payload)
|
|
192
|
+
});
|
|
193
|
+
const text = await res.text();
|
|
194
|
+
if (!res.ok) {
|
|
195
|
+
console.error(`\u2717 ${res.status} ${res.statusText}: ${text}`);
|
|
196
|
+
process.exit(1);
|
|
197
|
+
}
|
|
198
|
+
let parsed;
|
|
199
|
+
try {
|
|
200
|
+
parsed = JSON.parse(text);
|
|
201
|
+
} catch {
|
|
202
|
+
parsed = { raw: text };
|
|
203
|
+
}
|
|
204
|
+
console.log(
|
|
205
|
+
`\u2713 Deploy marker sent \u2014 ${commit_sha.slice(0, 7)} on ${payload.branch ?? "?"} via ${payload.provider}`
|
|
206
|
+
);
|
|
207
|
+
if (parsed.id) console.log(` deployment_id: ${parsed.id}`);
|
|
208
|
+
}
|
|
209
|
+
async function main() {
|
|
210
|
+
const args = parseArgs(process.argv.slice(2));
|
|
211
|
+
switch (args.command) {
|
|
212
|
+
case "deploy-start":
|
|
213
|
+
await deployStart(args);
|
|
214
|
+
break;
|
|
215
|
+
case "help":
|
|
216
|
+
case "--help":
|
|
217
|
+
case "-h":
|
|
218
|
+
console.log(helpText());
|
|
219
|
+
break;
|
|
220
|
+
case "version":
|
|
221
|
+
case "--version":
|
|
222
|
+
case "-v":
|
|
223
|
+
console.log(`${CLI_NAME} v${CLI_VERSION}`);
|
|
224
|
+
break;
|
|
225
|
+
default:
|
|
226
|
+
console.error(`Unknown command: ${args.command}
|
|
227
|
+
`);
|
|
228
|
+
console.error(helpText());
|
|
229
|
+
process.exit(1);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
main().catch((err) => {
|
|
233
|
+
console.error(`fatal: ${err.message ?? err}`);
|
|
234
|
+
process.exit(1);
|
|
235
|
+
});
|
|
236
|
+
export {
|
|
237
|
+
deployStart
|
|
238
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fixprompt/cli",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "FixPrompt CLI — annotate deployments and ship them to the broker so the dashboard knows what changed.",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/Geos-LLC/FixPrompt.git",
|
|
9
|
+
"directory": "cli"
|
|
10
|
+
},
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"access": "public"
|
|
13
|
+
},
|
|
14
|
+
"type": "module",
|
|
15
|
+
"bin": {
|
|
16
|
+
"fixprompt": "dist/cli.js"
|
|
17
|
+
},
|
|
18
|
+
"files": ["dist", "README.md"],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsup",
|
|
21
|
+
"dev": "tsup --watch",
|
|
22
|
+
"test": "vitest run",
|
|
23
|
+
"typecheck": "tsc --noEmit",
|
|
24
|
+
"prepare": "npm run build",
|
|
25
|
+
"prepublishOnly": "npm run typecheck && npm run test && npm run build"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/node": "^20.0.0",
|
|
29
|
+
"tsup": "^8.3.5",
|
|
30
|
+
"typescript": "^5.4.0",
|
|
31
|
+
"vitest": "^2.1.0"
|
|
32
|
+
}
|
|
33
|
+
}
|