run402 1.13.1 → 1.13.3
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/lib/deploy.mjs +16 -3
- package/lib/manifest.mjs +17 -0
- package/lib/manifest.test.mjs +39 -1
- package/lib/projects.mjs +7 -0
- package/package.json +1 -1
package/lib/deploy.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from "fs";
|
|
2
2
|
import { dirname, resolve } from "path";
|
|
3
3
|
import { API, allowanceAuthHeaders, findProject } from "./config.mjs";
|
|
4
|
-
import { resolveFilePathsInManifest } from "./manifest.mjs";
|
|
4
|
+
import { resolveFilePathsInManifest, resolveMigrationsFile } from "./manifest.mjs";
|
|
5
5
|
|
|
6
6
|
const HELP = `run402 deploy — Deploy to an existing project on Run402
|
|
7
7
|
|
|
@@ -17,7 +17,8 @@ Options:
|
|
|
17
17
|
Manifest format (JSON):
|
|
18
18
|
{
|
|
19
19
|
"project_id": "prj_...",
|
|
20
|
-
"migrations": "CREATE TABLE items (
|
|
20
|
+
"migrations": "CREATE TABLE items (...)",
|
|
21
|
+
"migrations_file": "setup.sql",
|
|
21
22
|
"rls": {
|
|
22
23
|
"template": "public_read_write",
|
|
23
24
|
"tables": [{ "table": "items" }]
|
|
@@ -37,6 +38,14 @@ Manifest format (JSON):
|
|
|
37
38
|
project_id is required (provision first with 'run402 provision').
|
|
38
39
|
All other fields are optional.
|
|
39
40
|
|
|
41
|
+
Migrations can be inline or read from a file:
|
|
42
|
+
"migrations": "CREATE TABLE ..." ← inline SQL
|
|
43
|
+
"migrations_file": "setup.sql" ← read from disk
|
|
44
|
+
Use migrations_file when your SQL contains JSONB literals or other
|
|
45
|
+
characters that are painful to escape inside a JSON string.
|
|
46
|
+
Paths are resolved relative to the manifest file's directory.
|
|
47
|
+
If both are present, migrations_file wins.
|
|
48
|
+
|
|
40
49
|
Files can use either inline "data" or a local "path":
|
|
41
50
|
{ "file": "index.html", "data": "<html>...</html>" } ← inline content
|
|
42
51
|
{ "file": "style.css", "path": "./dist/style.css" } ← read from disk
|
|
@@ -83,7 +92,11 @@ export async function run(args) {
|
|
|
83
92
|
|
|
84
93
|
const raw = opts.manifest ? readFileSync(opts.manifest, "utf-8") : await readStdin();
|
|
85
94
|
const manifest = JSON.parse(raw);
|
|
86
|
-
if (opts.manifest)
|
|
95
|
+
if (opts.manifest) {
|
|
96
|
+
const baseDir = dirname(resolve(opts.manifest));
|
|
97
|
+
resolveMigrationsFile(manifest, baseDir);
|
|
98
|
+
resolveFilePathsInManifest(manifest, baseDir);
|
|
99
|
+
}
|
|
87
100
|
|
|
88
101
|
// --project flag overrides manifest's project_id
|
|
89
102
|
if (opts.project) manifest.project_id = opts.project;
|
package/lib/manifest.mjs
CHANGED
|
@@ -6,6 +6,23 @@ const TEXT_EXTS = new Set([
|
|
|
6
6
|
".json", ".svg", ".xml", ".txt", ".md", ".yaml", ".yml", ".toml", ".csv",
|
|
7
7
|
]);
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* If the manifest has `migrations_file` instead of (or in addition to) `migrations`,
|
|
11
|
+
* read the SQL from that file path and set `migrations` to its contents.
|
|
12
|
+
* `migrations_file` is resolved relative to `baseDir`.
|
|
13
|
+
*
|
|
14
|
+
* @param {object} manifest Parsed manifest JSON (mutated in place)
|
|
15
|
+
* @param {string} baseDir Directory to resolve relative paths from
|
|
16
|
+
* @returns {object} The same manifest object
|
|
17
|
+
*/
|
|
18
|
+
export function resolveMigrationsFile(manifest, baseDir) {
|
|
19
|
+
if (!manifest.migrations_file) return manifest;
|
|
20
|
+
const abs = resolve(baseDir, manifest.migrations_file);
|
|
21
|
+
manifest.migrations = readFileSync(abs, "utf-8");
|
|
22
|
+
delete manifest.migrations_file;
|
|
23
|
+
return manifest;
|
|
24
|
+
}
|
|
25
|
+
|
|
9
26
|
/**
|
|
10
27
|
* Resolve `path` fields in a manifest's files array.
|
|
11
28
|
*
|
package/lib/manifest.test.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import assert from "node:assert/strict";
|
|
|
3
3
|
import { mkdtempSync, writeFileSync, rmSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
6
|
-
import { resolveFilePathsInManifest } from "./manifest.mjs";
|
|
6
|
+
import { resolveFilePathsInManifest, resolveMigrationsFile } from "./manifest.mjs";
|
|
7
7
|
|
|
8
8
|
let tempDir;
|
|
9
9
|
|
|
@@ -13,6 +13,7 @@ before(() => {
|
|
|
13
13
|
writeFileSync(join(tempDir, "index.html"), "<!DOCTYPE html><html><body>Hello</body></html>");
|
|
14
14
|
writeFileSync(join(tempDir, "style.css"), "body { margin: 0; }");
|
|
15
15
|
writeFileSync(join(tempDir, "logo.png"), Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a])); // PNG header
|
|
16
|
+
writeFileSync(join(tempDir, "setup.sql"), "CREATE TABLE items (id serial PRIMARY KEY, data jsonb);\nINSERT INTO items (data) VALUES ('[{\"x\":0.5}]');");
|
|
16
17
|
});
|
|
17
18
|
|
|
18
19
|
after(() => {
|
|
@@ -88,3 +89,40 @@ describe("resolveFilePathsInManifest", () => {
|
|
|
88
89
|
);
|
|
89
90
|
});
|
|
90
91
|
});
|
|
92
|
+
|
|
93
|
+
describe("resolveMigrationsFile", () => {
|
|
94
|
+
it("reads SQL from migrations_file and sets migrations", () => {
|
|
95
|
+
const manifest = { migrations_file: "setup.sql" };
|
|
96
|
+
resolveMigrationsFile(manifest, tempDir);
|
|
97
|
+
assert.ok(manifest.migrations.includes("CREATE TABLE items"));
|
|
98
|
+
assert.ok(manifest.migrations.includes('[{"x":0.5}]'), "should preserve JSON literals without escaping issues");
|
|
99
|
+
assert.equal(manifest.migrations_file, undefined, "migrations_file should be removed");
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("overwrites inline migrations when migrations_file is present", () => {
|
|
103
|
+
const manifest = { migrations: "SELECT 1", migrations_file: "setup.sql" };
|
|
104
|
+
resolveMigrationsFile(manifest, tempDir);
|
|
105
|
+
assert.ok(manifest.migrations.includes("CREATE TABLE items"));
|
|
106
|
+
assert.equal(manifest.migrations_file, undefined);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("leaves manifest untouched when no migrations_file", () => {
|
|
110
|
+
const manifest = { migrations: "SELECT 1" };
|
|
111
|
+
resolveMigrationsFile(manifest, tempDir);
|
|
112
|
+
assert.equal(manifest.migrations, "SELECT 1");
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("handles manifest with neither migrations nor migrations_file", () => {
|
|
116
|
+
const manifest = { files: [] };
|
|
117
|
+
resolveMigrationsFile(manifest, tempDir);
|
|
118
|
+
assert.equal(manifest.migrations, undefined);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("throws on missing migrations file", () => {
|
|
122
|
+
const manifest = { migrations_file: "does-not-exist.sql" };
|
|
123
|
+
assert.throws(
|
|
124
|
+
() => resolveMigrationsFile(manifest, tempDir),
|
|
125
|
+
/ENOENT/,
|
|
126
|
+
);
|
|
127
|
+
});
|
|
128
|
+
});
|
package/lib/projects.mjs
CHANGED
|
@@ -11,6 +11,7 @@ Subcommands:
|
|
|
11
11
|
use <id> Set the active project (used as default for other commands)
|
|
12
12
|
list List all your projects (IDs, URLs, active marker)
|
|
13
13
|
info <id> Show project details: REST URL, keys
|
|
14
|
+
keys <id> Print anon_key and service_key as JSON
|
|
14
15
|
sql <id> "<query>" Run a SQL query against a project's Postgres DB
|
|
15
16
|
rest <id> <table> [params] Query a table via the REST API (PostgREST)
|
|
16
17
|
usage <id> Show compute/storage usage for a project
|
|
@@ -108,6 +109,11 @@ async function info(projectId) {
|
|
|
108
109
|
}, null, 2));
|
|
109
110
|
}
|
|
110
111
|
|
|
112
|
+
async function keys(projectId) {
|
|
113
|
+
const p = findProject(projectId);
|
|
114
|
+
console.log(JSON.stringify({ project_id: projectId, anon_key: p.anon_key, service_key: p.service_key }, null, 2));
|
|
115
|
+
}
|
|
116
|
+
|
|
111
117
|
async function sqlCmd(projectId, query) {
|
|
112
118
|
const p = findProject(projectId);
|
|
113
119
|
const res = await fetch(`${API}/projects/v1/admin/${projectId}/sql`, { method: "POST", headers: { "Authorization": `Bearer ${p.service_key}`, "Content-Type": "text/plain" }, body: query });
|
|
@@ -185,6 +191,7 @@ export async function run(sub, args) {
|
|
|
185
191
|
case "use": await use(args[0]); break;
|
|
186
192
|
case "list": await list(); break;
|
|
187
193
|
case "info": await info(args[0]); break;
|
|
194
|
+
case "keys": await keys(args[0]); break;
|
|
188
195
|
case "sql": await sqlCmd(args[0], args[1]); break;
|
|
189
196
|
case "rest": await rest(args[0], args[1], args[2]); break;
|
|
190
197
|
case "usage": await usage(args[0]); break;
|
package/package.json
CHANGED