runcloud 0.1.99 → 0.1.101
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/dist/commands/create.js +30 -106
- package/dist/commands/environment.js +3 -3
- package/dist/config.js +3 -0
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/commands/create.js
CHANGED
|
@@ -1,135 +1,59 @@
|
|
|
1
|
-
import { existsSync
|
|
2
|
-
import {
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
3
|
import { ApiClient, ApiError, friendlyApiError } from '../api.js';
|
|
4
|
-
import { requireCredentials,
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
const POLL_TIMEOUT_MS = 20 * 60 * 1_000;
|
|
8
|
-
function projectName(dir, requested) {
|
|
9
|
-
const value = requested?.trim() || basename(resolve(dir));
|
|
10
|
-
if (value.length < 1 || value.length > 100) {
|
|
11
|
-
throw new Error('Project name must be between 1 and 100 characters.');
|
|
12
|
-
}
|
|
13
|
-
return value;
|
|
14
|
-
}
|
|
15
|
-
export function scaffoldV3Backend(dir, name) {
|
|
16
|
-
const serviceRoot = join(dir, 'backend', 'api-service');
|
|
17
|
-
mkdirSync(join(serviceRoot, 'src'), { recursive: true });
|
|
18
|
-
writeFileSync(join(dir, 'backend', 'newly.services.json'), `${JSON.stringify({
|
|
19
|
-
services: [{
|
|
20
|
-
name: 'api',
|
|
21
|
-
path: 'api-service',
|
|
22
|
-
type: 'worker',
|
|
23
|
-
public: true,
|
|
24
|
-
bindings: { DATABASE_URL: { database: 'app' } },
|
|
25
|
-
}],
|
|
26
|
-
}, null, 2)}\n`);
|
|
27
|
-
writeFileSync(join(serviceRoot, 'package.json'), `${JSON.stringify({
|
|
28
|
-
name: `@newly/${name.toLowerCase().replace(/[^a-z0-9-]+/g, '-').replace(/^-|-$/g, '') || 'api'}`,
|
|
29
|
-
private: true,
|
|
30
|
-
type: 'module',
|
|
31
|
-
}, null, 2)}\n`);
|
|
32
|
-
writeFileSync(join(serviceRoot, 'src', 'index.ts'), `interface Env {
|
|
33
|
-
DATABASE_URL: string;
|
|
34
|
-
NEWLY_ENVIRONMENT_ID: string;
|
|
35
|
-
NEWLY_PROJECT_ID: string;
|
|
36
|
-
NEWLY_SERVICE_NAME: string;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function json(value: unknown, status = 200): Response {
|
|
40
|
-
return Response.json(value, { status });
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export default {
|
|
44
|
-
async fetch(request: Request, env: Env): Promise<Response> {
|
|
45
|
-
const url = new URL(request.url);
|
|
46
|
-
if (url.pathname === "/health") {
|
|
47
|
-
return json({ ok: true, environmentId: env.NEWLY_ENVIRONMENT_ID });
|
|
48
|
-
}
|
|
49
|
-
if (url.pathname === "/") {
|
|
50
|
-
return json({
|
|
51
|
-
projectId: env.NEWLY_PROJECT_ID,
|
|
52
|
-
environmentId: env.NEWLY_ENVIRONMENT_ID,
|
|
53
|
-
service: env.NEWLY_SERVICE_NAME,
|
|
54
|
-
databaseConfigured: Boolean(env.DATABASE_URL),
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
return json({ error: "Not found" }, 404);
|
|
58
|
-
},
|
|
59
|
-
};
|
|
60
|
-
`);
|
|
61
|
-
}
|
|
62
|
-
async function waitForProduction(client, environmentId, runtime) {
|
|
63
|
-
const intervalMs = runtime.intervalMs ?? POLL_INTERVAL_MS;
|
|
64
|
-
const deadline = Date.now() + (runtime.timeoutMs ?? POLL_TIMEOUT_MS);
|
|
65
|
-
for (;;) {
|
|
66
|
-
const environment = await client.get(`/environments/${encodeURIComponent(environmentId)}`);
|
|
67
|
-
if (environment.status === 'ready' || environment.status === 'failed')
|
|
68
|
-
return environment;
|
|
69
|
-
if (Date.now() >= deadline) {
|
|
70
|
-
throw new Error(`Timed out provisioning production (${environment.stage}).`);
|
|
71
|
-
}
|
|
72
|
-
await new Promise((done) => setTimeout(done, intervalMs));
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
export async function runCreate(client, dir, opts, runtime = {}) {
|
|
4
|
+
import { requireCredentials, writeProjectLink } from '../config.js';
|
|
5
|
+
import { extractSnapshot } from '../archive.js';
|
|
6
|
+
export async function runCreate(client, dir, opts) {
|
|
76
7
|
if (existsSync(join(dir, 'backend'))) {
|
|
77
8
|
throw new Error('A `backend/` directory already exists here. Run `newly create` in a directory without one.');
|
|
78
9
|
}
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
10
|
+
const { project_id, status } = await client.post('/newly_backend/create_project', {
|
|
11
|
+
name: opts.name,
|
|
12
|
+
provision: opts.provision ?? true,
|
|
13
|
+
});
|
|
82
14
|
writeProjectLink(dir, {
|
|
83
|
-
projectId:
|
|
15
|
+
projectId: project_id,
|
|
84
16
|
pulledSha: null,
|
|
85
17
|
pulledAt: new Date().toISOString(),
|
|
86
18
|
});
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
if (production.status === 'failed') {
|
|
91
|
-
throw new Error(`Production provisioning failed at ${production.stage}: ${production.lastError ?? created.lastError ?? 'unknown error'}`);
|
|
19
|
+
try {
|
|
20
|
+
const tar = await client.getBinary('/newly_backend/backend_template');
|
|
21
|
+
await extractSnapshot(tar, dir);
|
|
92
22
|
}
|
|
93
|
-
|
|
94
|
-
|
|
23
|
+
catch (e) {
|
|
24
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
25
|
+
throw new Error(`Project ${project_id} was created and linked, but scaffolding the backend template failed: ${msg}. The project exists server-side.`);
|
|
95
26
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
projectId: created.id,
|
|
100
|
-
});
|
|
101
|
-
console.log(`Using production (${production.id}) in this workspace.`);
|
|
102
|
-
if (opts.deploy ?? true) {
|
|
103
|
-
console.log('Deploying the starter backend to production…');
|
|
104
|
-
await (runtime.deploy ?? runEnvironmentDeploy)(created.id, production.id, client, dir);
|
|
105
|
-
console.log(`Created ${name}; production database and backend are ready.`);
|
|
27
|
+
if (status === 'provisioning') {
|
|
28
|
+
console.log(`Created backend project ${project_id} and scaffolded backend/ here. ` +
|
|
29
|
+
`Provisioning the dev backend… check with \`newly status\`. Edit, then \`newly deploy\`.`);
|
|
106
30
|
}
|
|
107
31
|
else {
|
|
108
|
-
console.log(`Created ${
|
|
32
|
+
console.log(`Created backend project ${project_id} and scaffolded backend/ here ` +
|
|
33
|
+
`(not provisioned — run \`newly deploy\` when ready).`);
|
|
109
34
|
}
|
|
110
|
-
return
|
|
35
|
+
return project_id;
|
|
111
36
|
}
|
|
112
37
|
export function registerCreate(program) {
|
|
113
38
|
program
|
|
114
39
|
.command('create')
|
|
115
|
-
.description('Create a
|
|
116
|
-
.argument('[name]', 'project name (
|
|
117
|
-
.option('--no-
|
|
40
|
+
.description('Create a new backend-only Newly project here (scaffold + provision)')
|
|
41
|
+
.argument('[name]', 'project name (generated if omitted)')
|
|
42
|
+
.option('--no-provision', 'create without provisioning the backend')
|
|
118
43
|
.action(async (name, opts) => {
|
|
119
44
|
try {
|
|
120
45
|
if (existsSync(join(process.cwd(), '.newly', 'project.json'))) {
|
|
121
46
|
throw new Error('This directory is already linked to a project. Use a fresh directory for `newly create`.');
|
|
122
47
|
}
|
|
123
48
|
const creds = requireCredentials();
|
|
124
|
-
|
|
49
|
+
const client = new ApiClient(creds.apiUrl, creds.token);
|
|
50
|
+
await runCreate(client, process.cwd(), {
|
|
125
51
|
name,
|
|
126
|
-
|
|
52
|
+
provision: opts.provision,
|
|
127
53
|
});
|
|
128
54
|
}
|
|
129
|
-
catch (
|
|
130
|
-
console.error(
|
|
131
|
-
? friendlyApiError(error)
|
|
132
|
-
: String(error instanceof Error ? error.message : error));
|
|
55
|
+
catch (e) {
|
|
56
|
+
console.error(e instanceof ApiError ? friendlyApiError(e) : String(e instanceof Error ? e.message : e));
|
|
133
57
|
process.exitCode = 1;
|
|
134
58
|
}
|
|
135
59
|
});
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { createInterface } from 'node:readline/promises';
|
|
2
2
|
import { ApiClient, friendlyApiError } from '../api.js';
|
|
3
3
|
import { runEnvironmentDatabaseShell } from '../shellSession.js';
|
|
4
|
-
import { clearEnvironmentSelection, readEnvironmentSelection, requireCredentials, requireEnvironmentSelection, resolveProject, writeEnvironmentSelection, } from '../config.js';
|
|
4
|
+
import { clearEnvironmentSelection, readEnvironmentSelection, controlPlaneUrl, requireCredentials, requireEnvironmentSelection, resolveProject, writeEnvironmentSelection, } from '../config.js';
|
|
5
5
|
const POLL_INTERVAL_MS = 2_000;
|
|
6
6
|
const POLL_TIMEOUT_MS = 10 * 60 * 1_000;
|
|
7
7
|
function client() {
|
|
8
8
|
const credentials = requireCredentials();
|
|
9
|
-
return new ApiClient(credentials
|
|
9
|
+
return new ApiClient(controlPlaneUrl(credentials), credentials.token);
|
|
10
10
|
}
|
|
11
11
|
async function projectEnvironments(api, projectId) {
|
|
12
12
|
const response = await api.get(`/projects/${encodeURIComponent(projectId)}/environments`);
|
|
@@ -248,7 +248,7 @@ export function registerEnvironment(program) {
|
|
|
248
248
|
const projectId = resolveProject(process.cwd(), opts.project);
|
|
249
249
|
const selected = requireEnvironmentSelection(process.cwd(), projectId);
|
|
250
250
|
const credentials = requireCredentials();
|
|
251
|
-
const environment = await new ApiClient(credentials
|
|
251
|
+
const environment = await new ApiClient(controlPlaneUrl(credentials), credentials.token).get(`/environments/${encodeURIComponent(selected.id)}`);
|
|
252
252
|
const resource = resolveNamedResource(environment.databases, value, 'database');
|
|
253
253
|
if (resource.status !== 'ready')
|
|
254
254
|
throw new Error(`Database is not ready (${resource.status}).`);
|
package/dist/config.js
CHANGED
|
@@ -33,6 +33,9 @@ export function loginCommandName() {
|
|
|
33
33
|
export function normalizeApiUrl(url) {
|
|
34
34
|
return url.replace(/\/+$/, '');
|
|
35
35
|
}
|
|
36
|
+
export function controlPlaneUrl(creds) {
|
|
37
|
+
return normalizeApiUrl(creds.authApiUrl ?? creds.apiUrl);
|
|
38
|
+
}
|
|
36
39
|
export function saveCredentials(creds) {
|
|
37
40
|
mkdirSync(credentialsHome(), { recursive: true, mode: 0o700 });
|
|
38
41
|
const normalized = { ...creds, apiUrl: normalizeApiUrl(creds.apiUrl) };
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const CLI_VERSION = '0.1.
|
|
1
|
+
export const CLI_VERSION = '0.1.101';
|