naro-cli 0.1.0 → 0.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/dist/{chunk-63SYN66G.js → chunk-UNCVFCTH.js} +231 -59
- package/dist/deploy.d.ts +8 -0
- package/dist/deploy.js +3 -1
- package/dist/git-info.d.ts +7 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +12 -79
- package/package.json +1 -1
|
@@ -7,9 +7,9 @@ var __export = (target, all) => {
|
|
|
7
7
|
|
|
8
8
|
// src/deploy.ts
|
|
9
9
|
import { existsSync, readFileSync as readFileSync2 } from "fs";
|
|
10
|
-
import { cp, mkdtemp as mkdtemp2, readFile as
|
|
11
|
-
import { tmpdir as tmpdir2 } from "os";
|
|
12
|
-
import { basename, dirname, join as
|
|
10
|
+
import { cp, mkdtemp as mkdtemp2, readFile as readFile6, rm as rm2 } from "fs/promises";
|
|
11
|
+
import { homedir as homedir3, tmpdir as tmpdir2 } from "os";
|
|
12
|
+
import { basename, dirname, join as join9 } from "path";
|
|
13
13
|
import { fileURLToPath } from "url";
|
|
14
14
|
|
|
15
15
|
// ../../packages/build/src/cache.ts
|
|
@@ -110,7 +110,7 @@ async function saveCache(args) {
|
|
|
110
110
|
// ../../packages/build/src/local-build-executor.ts
|
|
111
111
|
import { mkdtemp, rm } from "fs/promises";
|
|
112
112
|
import { tmpdir } from "os";
|
|
113
|
-
import { join as
|
|
113
|
+
import { join as join7 } from "path";
|
|
114
114
|
|
|
115
115
|
// ../../packages/core/src/errors.ts
|
|
116
116
|
var DeploymentError = class extends Error {
|
|
@@ -14722,10 +14722,46 @@ async function detectPackageManager(root) {
|
|
|
14722
14722
|
);
|
|
14723
14723
|
}
|
|
14724
14724
|
|
|
14725
|
+
// ../../packages/build/src/proxy-to-middleware.ts
|
|
14726
|
+
import { access as access2, readFile as readFile3, rename, writeFile as writeFile2 } from "fs/promises";
|
|
14727
|
+
import { join as join3 } from "path";
|
|
14728
|
+
var EXTENSIONS = ["ts", "tsx", "js", "jsx"];
|
|
14729
|
+
var DIRECTORIES = ["", "src"];
|
|
14730
|
+
async function exists2(path) {
|
|
14731
|
+
try {
|
|
14732
|
+
await access2(path);
|
|
14733
|
+
return true;
|
|
14734
|
+
} catch {
|
|
14735
|
+
return false;
|
|
14736
|
+
}
|
|
14737
|
+
}
|
|
14738
|
+
function renameExport(source) {
|
|
14739
|
+
return source.replace(
|
|
14740
|
+
/(export\s+(?:default\s+)?(?:async\s+)?function\s+)proxy\b/g,
|
|
14741
|
+
"$1middleware"
|
|
14742
|
+
).replace(/(export\s+const\s+)proxy\b/g, "$1middleware");
|
|
14743
|
+
}
|
|
14744
|
+
async function convertProxyToMiddleware(appRoot) {
|
|
14745
|
+
for (const dir of DIRECTORIES) {
|
|
14746
|
+
for (const ext of EXTENSIONS) {
|
|
14747
|
+
const rel = join3(dir, `proxy.${ext}`);
|
|
14748
|
+
const proxyPath = join3(appRoot, rel);
|
|
14749
|
+
if (!await exists2(proxyPath)) continue;
|
|
14750
|
+
const middlewarePath = join3(appRoot, dir, `middleware.${ext}`);
|
|
14751
|
+
if (await exists2(middlewarePath)) return { converted: false };
|
|
14752
|
+
const source = await readFile3(proxyPath, "utf8");
|
|
14753
|
+
await writeFile2(middlewarePath, renameExport(source));
|
|
14754
|
+
await rename(proxyPath, `${proxyPath}.converted`);
|
|
14755
|
+
return { converted: true, file: rel };
|
|
14756
|
+
}
|
|
14757
|
+
}
|
|
14758
|
+
return { converted: false };
|
|
14759
|
+
}
|
|
14760
|
+
|
|
14725
14761
|
// ../../packages/build/src/platform-config.ts
|
|
14726
|
-
import { access as
|
|
14762
|
+
import { access as access3, writeFile as writeFile3 } from "fs/promises";
|
|
14727
14763
|
import { createRequire } from "module";
|
|
14728
|
-
import { join as
|
|
14764
|
+
import { join as join4 } from "path";
|
|
14729
14765
|
var COMPATIBILITY_DATE = "2026-08-19";
|
|
14730
14766
|
var NEXT_CONFIG_FILES = [
|
|
14731
14767
|
"next.config.js",
|
|
@@ -14735,31 +14771,31 @@ var NEXT_CONFIG_FILES = [
|
|
|
14735
14771
|
];
|
|
14736
14772
|
async function pathExists(path) {
|
|
14737
14773
|
try {
|
|
14738
|
-
await
|
|
14774
|
+
await access3(path);
|
|
14739
14775
|
return true;
|
|
14740
14776
|
} catch {
|
|
14741
14777
|
return false;
|
|
14742
14778
|
}
|
|
14743
14779
|
}
|
|
14744
14780
|
async function createPlatformBuildConfig(appRoot, deploymentId, writer = async (path, contents) => {
|
|
14745
|
-
await
|
|
14781
|
+
await writeFile3(path, contents, "utf8");
|
|
14746
14782
|
}, cloudflareModulePath = createRequire(import.meta.url).resolve(
|
|
14747
14783
|
"@opennextjs/cloudflare"
|
|
14748
|
-
),
|
|
14749
|
-
const openNextConfigPath =
|
|
14750
|
-
const wranglerConfigPath =
|
|
14751
|
-
const packageManagerConfigPath =
|
|
14784
|
+
), exists3 = pathExists) {
|
|
14785
|
+
const openNextConfigPath = join4(appRoot, ".next-deploy-open-next.config.mjs");
|
|
14786
|
+
const wranglerConfigPath = join4(appRoot, ".next-deploy-wrangler.json");
|
|
14787
|
+
const packageManagerConfigPath = join4(appRoot, ".next-deploy-user.npmrc");
|
|
14752
14788
|
const safeDeploymentId = deploymentId.replace(/[^a-z0-9-]/gi, "-");
|
|
14753
14789
|
let hasNextConfig = false;
|
|
14754
14790
|
for (const name of NEXT_CONFIG_FILES) {
|
|
14755
|
-
if (await
|
|
14791
|
+
if (await exists3(join4(appRoot, name))) {
|
|
14756
14792
|
hasNextConfig = true;
|
|
14757
14793
|
break;
|
|
14758
14794
|
}
|
|
14759
14795
|
}
|
|
14760
14796
|
if (!hasNextConfig) {
|
|
14761
14797
|
await writer(
|
|
14762
|
-
|
|
14798
|
+
join4(appRoot, "next.config.mjs"),
|
|
14763
14799
|
'export default { output: "standalone" };\n'
|
|
14764
14800
|
);
|
|
14765
14801
|
}
|
|
@@ -14800,8 +14836,8 @@ export default defineCloudflareConfig({});
|
|
|
14800
14836
|
}
|
|
14801
14837
|
|
|
14802
14838
|
// ../../packages/build/src/open-next.ts
|
|
14803
|
-
import { access as
|
|
14804
|
-
import { extname, isAbsolute, join as
|
|
14839
|
+
import { access as access4, readdir, readFile as readFile4 } from "fs/promises";
|
|
14840
|
+
import { extname, isAbsolute, join as join5, relative, resolve, sep } from "path";
|
|
14805
14841
|
function parseJsonc(source) {
|
|
14806
14842
|
let output = "";
|
|
14807
14843
|
let index = 0;
|
|
@@ -14896,7 +14932,7 @@ async function listFiles(root) {
|
|
|
14896
14932
|
for (const entry of entries.sort(
|
|
14897
14933
|
(left, right) => left.name.localeCompare(right.name)
|
|
14898
14934
|
)) {
|
|
14899
|
-
const path =
|
|
14935
|
+
const path = join5(directory, entry.name);
|
|
14900
14936
|
if (entry.isSymbolicLink()) {
|
|
14901
14937
|
throw new DeploymentError(
|
|
14902
14938
|
"ARTIFACT_INVALID",
|
|
@@ -14907,7 +14943,7 @@ async function listFiles(root) {
|
|
|
14907
14943
|
else if (entry.isFile()) {
|
|
14908
14944
|
files.push({
|
|
14909
14945
|
name: relative(root, path).split(sep).join("/"),
|
|
14910
|
-
data: await
|
|
14946
|
+
data: await readFile4(path),
|
|
14911
14947
|
contentType: contentType(path)
|
|
14912
14948
|
});
|
|
14913
14949
|
}
|
|
@@ -14926,13 +14962,13 @@ async function findWranglerConfig(root, configuredPath) {
|
|
|
14926
14962
|
"Wrangler config escapes the application root."
|
|
14927
14963
|
);
|
|
14928
14964
|
}
|
|
14929
|
-
await
|
|
14965
|
+
await access4(resolved);
|
|
14930
14966
|
return resolved;
|
|
14931
14967
|
}
|
|
14932
14968
|
for (const name of ["wrangler.jsonc", "wrangler.json"]) {
|
|
14933
|
-
const path =
|
|
14969
|
+
const path = join5(root, name);
|
|
14934
14970
|
try {
|
|
14935
|
-
await
|
|
14971
|
+
await access4(path);
|
|
14936
14972
|
return path;
|
|
14937
14973
|
} catch {
|
|
14938
14974
|
}
|
|
@@ -14946,7 +14982,7 @@ async function readOpenNextArtifact(appRoot, identity, wranglerConfigPath) {
|
|
|
14946
14982
|
try {
|
|
14947
14983
|
const configPath = await findWranglerConfig(appRoot, wranglerConfigPath);
|
|
14948
14984
|
const metadata = parseJsonc(
|
|
14949
|
-
await
|
|
14985
|
+
await readFile4(configPath, "utf8")
|
|
14950
14986
|
);
|
|
14951
14987
|
if (typeof metadata.main !== "string" || typeof metadata.compatibility_date !== "string") {
|
|
14952
14988
|
throw new DeploymentError(
|
|
@@ -14967,8 +15003,8 @@ async function readOpenNextArtifact(appRoot, identity, wranglerConfigPath) {
|
|
|
14967
15003
|
"Worker entrypoint must be inside .open-next."
|
|
14968
15004
|
);
|
|
14969
15005
|
}
|
|
14970
|
-
await
|
|
14971
|
-
const configuredAssets = typeof metadata.assets?.directory === "string" ? confinedPath(appRoot, metadata.assets.directory, "Assets directory") :
|
|
15006
|
+
await access4(entrypointPath);
|
|
15007
|
+
const configuredAssets = typeof metadata.assets?.directory === "string" ? confinedPath(appRoot, metadata.assets.directory, "Assets directory") : join5(outputRoot, "assets");
|
|
14972
15008
|
const assetsRelation = relative(outputRoot, configuredAssets);
|
|
14973
15009
|
if (assetsRelation === ".." || assetsRelation.startsWith(`..${sep}`) || isAbsolute(assetsRelation)) {
|
|
14974
15010
|
throw new DeploymentError(
|
|
@@ -14976,10 +15012,10 @@ async function readOpenNextArtifact(appRoot, identity, wranglerConfigPath) {
|
|
|
14976
15012
|
"Assets directory must be inside .open-next."
|
|
14977
15013
|
);
|
|
14978
15014
|
}
|
|
14979
|
-
const bundleRoot =
|
|
15015
|
+
const bundleRoot = join5(outputRoot, "worker-bundle");
|
|
14980
15016
|
let bundleFiles = null;
|
|
14981
15017
|
try {
|
|
14982
|
-
await
|
|
15018
|
+
await access4(bundleRoot);
|
|
14983
15019
|
bundleFiles = await listFiles(bundleRoot);
|
|
14984
15020
|
} catch {
|
|
14985
15021
|
}
|
|
@@ -15162,13 +15198,13 @@ function resolveAppRoot(checkout, configuredRoot) {
|
|
|
15162
15198
|
}
|
|
15163
15199
|
|
|
15164
15200
|
// ../../packages/build/src/next-project.ts
|
|
15165
|
-
import { readFile as
|
|
15166
|
-
import { join as
|
|
15201
|
+
import { readFile as readFile5 } from "fs/promises";
|
|
15202
|
+
import { join as join6 } from "path";
|
|
15167
15203
|
async function validateNextProject(root) {
|
|
15168
15204
|
let packageJson;
|
|
15169
15205
|
try {
|
|
15170
15206
|
packageJson = JSON.parse(
|
|
15171
|
-
await
|
|
15207
|
+
await readFile5(join6(root, "package.json"), "utf8")
|
|
15172
15208
|
);
|
|
15173
15209
|
} catch (error51) {
|
|
15174
15210
|
throw new DeploymentError(
|
|
@@ -15197,7 +15233,7 @@ async function validateNextProject(root) {
|
|
|
15197
15233
|
|
|
15198
15234
|
// ../../packages/build/src/local-build-executor.ts
|
|
15199
15235
|
var defaultDependencies = {
|
|
15200
|
-
createWorkspace: () => mkdtemp(
|
|
15236
|
+
createWorkspace: () => mkdtemp(join7(tmpdir(), "next-deploy-build-")),
|
|
15201
15237
|
removeWorkspace: (path) => rm(path, { recursive: true, force: true }),
|
|
15202
15238
|
runCommand,
|
|
15203
15239
|
detectPackageManager,
|
|
@@ -15207,7 +15243,8 @@ var defaultDependencies = {
|
|
|
15207
15243
|
readOpenNextArtifact,
|
|
15208
15244
|
openNextCommand: "opennextjs-cloudflare",
|
|
15209
15245
|
wranglerCommand: "wrangler",
|
|
15210
|
-
cacheFetch: (input, init) => fetch(input, init)
|
|
15246
|
+
cacheFetch: (input, init) => fetch(input, init),
|
|
15247
|
+
convertProxyToMiddleware
|
|
15211
15248
|
};
|
|
15212
15249
|
function sanitizeCacheKey(value) {
|
|
15213
15250
|
return value.replace(/[^A-Za-z0-9._-]/g, "-").slice(0, 128);
|
|
@@ -15247,14 +15284,14 @@ var LocalBuildExecutor = class {
|
|
|
15247
15284
|
}
|
|
15248
15285
|
async build(input, observer, cache) {
|
|
15249
15286
|
const workspace = await this.dependencies.createWorkspace();
|
|
15250
|
-
const checkout =
|
|
15251
|
-
const pmCacheDir =
|
|
15287
|
+
const checkout = join7(workspace, "checkout");
|
|
15288
|
+
const pmCacheDir = join7(workspace, ".pm-store");
|
|
15252
15289
|
let cacheSeq = 0;
|
|
15253
15290
|
const cachePorts = cache ? {
|
|
15254
15291
|
transport: cache,
|
|
15255
15292
|
fetch: this.dependencies.cacheFetch ?? ((url2, init) => fetch(url2, init)),
|
|
15256
15293
|
runCommand: this.dependencies.runCommand,
|
|
15257
|
-
tmpFile: (suffix) =>
|
|
15294
|
+
tmpFile: (suffix) => join7(workspace, `cache-${String(cacheSeq++)}${suffix}`)
|
|
15258
15295
|
} : null;
|
|
15259
15296
|
try {
|
|
15260
15297
|
await notifyStatus(observer, "CLONING");
|
|
@@ -15308,6 +15345,14 @@ var LocalBuildExecutor = class {
|
|
|
15308
15345
|
"CLONING",
|
|
15309
15346
|
`Validated Next.js ${project.nextVersion}`
|
|
15310
15347
|
);
|
|
15348
|
+
const conversion = await (this.dependencies.convertProxyToMiddleware ?? convertProxyToMiddleware)(appRoot);
|
|
15349
|
+
if (conversion.converted) {
|
|
15350
|
+
await observer.onLog({
|
|
15351
|
+
level: "WARN",
|
|
15352
|
+
stage: "CLONING",
|
|
15353
|
+
message: `Converted ${conversion.file ?? "proxy"} to edge middleware. Next.js 16 proxy runs on the Node.js runtime, which Cloudflare does not support; middleware runs on the Edge runtime, so Node.js-only APIs are unavailable.`
|
|
15354
|
+
});
|
|
15355
|
+
}
|
|
15311
15356
|
const platformConfig = await this.dependencies.createPlatformBuildConfig(
|
|
15312
15357
|
appRoot,
|
|
15313
15358
|
input.deploymentId
|
|
@@ -15355,7 +15400,7 @@ var LocalBuildExecutor = class {
|
|
|
15355
15400
|
observer
|
|
15356
15401
|
);
|
|
15357
15402
|
await notifyStatus(observer, "BUILDING");
|
|
15358
|
-
const nextCacheDir =
|
|
15403
|
+
const nextCacheDir = join7(appRoot, ".next", "cache");
|
|
15359
15404
|
const nextKey = sanitizeCacheKey(input.branch);
|
|
15360
15405
|
if (cachePorts) {
|
|
15361
15406
|
await tryRestore("next", nextKey, nextCacheDir, cachePorts, observer);
|
|
@@ -15407,7 +15452,7 @@ var LocalBuildExecutor = class {
|
|
|
15407
15452
|
],
|
|
15408
15453
|
cwd: appRoot,
|
|
15409
15454
|
env: {
|
|
15410
|
-
WRANGLER_LOG_PATH:
|
|
15455
|
+
WRANGLER_LOG_PATH: join7(appRoot, ".open-next", "wrangler.log")
|
|
15411
15456
|
},
|
|
15412
15457
|
timeoutMs: 5 * 6e4,
|
|
15413
15458
|
stage: "BUILDING",
|
|
@@ -15437,15 +15482,49 @@ var LocalBuildExecutor = class {
|
|
|
15437
15482
|
}
|
|
15438
15483
|
};
|
|
15439
15484
|
|
|
15485
|
+
// src/git-info.ts
|
|
15486
|
+
import { execFile } from "child_process";
|
|
15487
|
+
var SUPPORTED_REMOTE = [
|
|
15488
|
+
/^https:\/\/[^\s]+$/,
|
|
15489
|
+
/^ssh:\/\/[^\s]+$/,
|
|
15490
|
+
/^git@[A-Za-z0-9.-]+:[A-Za-z0-9._/-]+(\.git)?$/
|
|
15491
|
+
];
|
|
15492
|
+
var defaultExec = (args, cwd) => new Promise((resolve3, reject) => {
|
|
15493
|
+
execFile("git", args, { cwd }, (error51, stdout) => {
|
|
15494
|
+
if (error51) reject(error51);
|
|
15495
|
+
else resolve3(stdout);
|
|
15496
|
+
});
|
|
15497
|
+
});
|
|
15498
|
+
async function tryExec(exec, args, cwd) {
|
|
15499
|
+
try {
|
|
15500
|
+
const out = (await exec(args, cwd)).trim();
|
|
15501
|
+
return out.length > 0 ? out : null;
|
|
15502
|
+
} catch {
|
|
15503
|
+
return null;
|
|
15504
|
+
}
|
|
15505
|
+
}
|
|
15506
|
+
async function detectGitInfo(dir, exec = defaultExec) {
|
|
15507
|
+
const remote = await tryExec(exec, ["remote", "get-url", "origin"], dir);
|
|
15508
|
+
const branch = await tryExec(exec, ["rev-parse", "--abbrev-ref", "HEAD"], dir);
|
|
15509
|
+
return {
|
|
15510
|
+
repositoryUrl: remote !== null && SUPPORTED_REMOTE.some((re) => re.test(remote)) ? remote : null,
|
|
15511
|
+
branch: branch !== null && branch !== "HEAD" ? branch : null
|
|
15512
|
+
};
|
|
15513
|
+
}
|
|
15514
|
+
|
|
15515
|
+
// src/login.ts
|
|
15516
|
+
import { spawn as spawn2 } from "child_process";
|
|
15517
|
+
import { homedir as homedir2 } from "os";
|
|
15518
|
+
|
|
15440
15519
|
// src/session.ts
|
|
15441
15520
|
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
15442
15521
|
import { homedir } from "os";
|
|
15443
|
-
import { join as
|
|
15522
|
+
import { join as join8 } from "path";
|
|
15444
15523
|
function credentialsDir(home = homedir()) {
|
|
15445
|
-
return
|
|
15524
|
+
return join8(home, ".naro");
|
|
15446
15525
|
}
|
|
15447
15526
|
function credentialsFile(home = homedir()) {
|
|
15448
|
-
return
|
|
15527
|
+
return join8(credentialsDir(home), "credentials.json");
|
|
15449
15528
|
}
|
|
15450
15529
|
function saveSession(session, home = homedir()) {
|
|
15451
15530
|
mkdirSync(credentialsDir(home), { recursive: true, mode: 448 });
|
|
@@ -15610,6 +15689,71 @@ async function login(deps) {
|
|
|
15610
15689
|
}
|
|
15611
15690
|
}
|
|
15612
15691
|
|
|
15692
|
+
// src/login.ts
|
|
15693
|
+
var stdio = {
|
|
15694
|
+
out: (m) => process.stdout.write(`${m}
|
|
15695
|
+
`),
|
|
15696
|
+
err: (m) => process.stderr.write(`${m}
|
|
15697
|
+
`)
|
|
15698
|
+
};
|
|
15699
|
+
function openBrowser(url2) {
|
|
15700
|
+
const platform = process.platform;
|
|
15701
|
+
const command = platform === "darwin" ? "open" : platform === "win32" ? "cmd" : "xdg-open";
|
|
15702
|
+
const args = platform === "win32" ? ["/c", "start", "", url2] : [url2];
|
|
15703
|
+
spawn2(command, args, { stdio: "ignore", detached: true }).unref();
|
|
15704
|
+
}
|
|
15705
|
+
async function runLogin(io = stdio) {
|
|
15706
|
+
io.out("Opening your browser to sign in with GitHub...");
|
|
15707
|
+
const session = await login({
|
|
15708
|
+
openBrowser: (url2) => {
|
|
15709
|
+
io.out(`If your browser did not open, visit:
|
|
15710
|
+
${url2}`);
|
|
15711
|
+
openBrowser(url2);
|
|
15712
|
+
}
|
|
15713
|
+
});
|
|
15714
|
+
saveSession(session);
|
|
15715
|
+
io.out(`\u2705 Logged in as ${session.user.email ?? session.user.id}`);
|
|
15716
|
+
}
|
|
15717
|
+
async function runWhoami(io = stdio, home = homedir2(), refresh = refreshStoredSession) {
|
|
15718
|
+
let session = loadStoredSession(home);
|
|
15719
|
+
if (!session) {
|
|
15720
|
+
io.err("Not logged in. Run `naro login`.");
|
|
15721
|
+
process.exitCode = 1;
|
|
15722
|
+
return;
|
|
15723
|
+
}
|
|
15724
|
+
if (isExpired(session)) {
|
|
15725
|
+
try {
|
|
15726
|
+
session = await refresh(session.refresh_token);
|
|
15727
|
+
saveSession(session, home);
|
|
15728
|
+
} catch {
|
|
15729
|
+
io.err("Session expired. Run `naro login`.");
|
|
15730
|
+
process.exitCode = 1;
|
|
15731
|
+
return;
|
|
15732
|
+
}
|
|
15733
|
+
}
|
|
15734
|
+
const handle = session.user.github ? ` (${session.user.github})` : "";
|
|
15735
|
+
io.out(`Logged in as ${session.user.email ?? session.user.id}${handle}`);
|
|
15736
|
+
}
|
|
15737
|
+
async function defaultSignOut(session) {
|
|
15738
|
+
const client = createAuthClient();
|
|
15739
|
+
await client.auth.setSession({
|
|
15740
|
+
access_token: session.access_token,
|
|
15741
|
+
refresh_token: session.refresh_token
|
|
15742
|
+
});
|
|
15743
|
+
await client.auth.signOut();
|
|
15744
|
+
}
|
|
15745
|
+
async function runLogout(io = stdio, home = homedir2(), signOut = defaultSignOut) {
|
|
15746
|
+
const session = loadStoredSession(home);
|
|
15747
|
+
if (session) {
|
|
15748
|
+
try {
|
|
15749
|
+
await signOut(session);
|
|
15750
|
+
} catch {
|
|
15751
|
+
}
|
|
15752
|
+
}
|
|
15753
|
+
clearSession(home);
|
|
15754
|
+
io.out("Logged out.");
|
|
15755
|
+
}
|
|
15756
|
+
|
|
15613
15757
|
// src/upload.ts
|
|
15614
15758
|
function base(opts) {
|
|
15615
15759
|
return `${opts.controlPlaneUrl.replace(/\/$/, "")}/api/internal/deployments/${opts.deploymentId}`;
|
|
@@ -15678,7 +15822,14 @@ async function startCliDeployment(accessToken, opts) {
|
|
|
15678
15822
|
"Content-Type": "application/json",
|
|
15679
15823
|
Authorization: `Bearer ${accessToken}`
|
|
15680
15824
|
},
|
|
15681
|
-
body: JSON.stringify({
|
|
15825
|
+
body: JSON.stringify({
|
|
15826
|
+
name: opts.name,
|
|
15827
|
+
production: opts.production,
|
|
15828
|
+
...opts.repositoryUrl != null && { repositoryUrl: opts.repositoryUrl },
|
|
15829
|
+
...opts.branch != null && { branch: opts.branch },
|
|
15830
|
+
...opts.rootDirectory != null && { rootDirectory: opts.rootDirectory },
|
|
15831
|
+
...opts.env !== void 0 && { env: opts.env }
|
|
15832
|
+
})
|
|
15682
15833
|
}
|
|
15683
15834
|
);
|
|
15684
15835
|
if (!response.ok) {
|
|
@@ -15693,13 +15844,26 @@ async function startCliDeployment(accessToken, opts) {
|
|
|
15693
15844
|
function controlPlaneUrl(env = process.env) {
|
|
15694
15845
|
return (env.NARO_CONTROL_PLANE_URL ?? "https://naro.sh").replace(/\/$/, "");
|
|
15695
15846
|
}
|
|
15696
|
-
async function
|
|
15697
|
-
|
|
15698
|
-
|
|
15699
|
-
|
|
15700
|
-
|
|
15701
|
-
saveSession(session);
|
|
15847
|
+
async function requireSessionOrLogin(log2, home = homedir3(), loginFlow = () => login({
|
|
15848
|
+
openBrowser: (url2) => {
|
|
15849
|
+
log2(`If your browser did not open, visit:
|
|
15850
|
+
${url2}`);
|
|
15851
|
+
openBrowser(url2);
|
|
15702
15852
|
}
|
|
15853
|
+
})) {
|
|
15854
|
+
const stored = loadStoredSession(home);
|
|
15855
|
+
if (stored) {
|
|
15856
|
+
if (!isExpired(stored)) return stored;
|
|
15857
|
+
try {
|
|
15858
|
+
const refreshed = await refreshStoredSession(stored.refresh_token);
|
|
15859
|
+
saveSession(refreshed, home);
|
|
15860
|
+
return refreshed;
|
|
15861
|
+
} catch {
|
|
15862
|
+
}
|
|
15863
|
+
}
|
|
15864
|
+
log2("Not logged in \u2014 opening your browser to sign in with GitHub\u2026");
|
|
15865
|
+
const session = await loginFlow();
|
|
15866
|
+
saveSession(session, home);
|
|
15703
15867
|
return session;
|
|
15704
15868
|
}
|
|
15705
15869
|
function eventsObserver(cp2, deploymentId, token, fetchImpl = fetch) {
|
|
@@ -15737,7 +15901,7 @@ function parseDotenv(text) {
|
|
|
15737
15901
|
}
|
|
15738
15902
|
function readProjectEnv(dir) {
|
|
15739
15903
|
for (const name of [".env.local", ".env.production", ".env"]) {
|
|
15740
|
-
const path =
|
|
15904
|
+
const path = join9(dir, name);
|
|
15741
15905
|
if (existsSync(path)) return parseDotenv(readFileSync2(path, "utf8"));
|
|
15742
15906
|
}
|
|
15743
15907
|
return {};
|
|
@@ -15749,7 +15913,7 @@ function slugify2(value) {
|
|
|
15749
15913
|
async function projectName(sourceDir) {
|
|
15750
15914
|
try {
|
|
15751
15915
|
const pkg = JSON.parse(
|
|
15752
|
-
await
|
|
15916
|
+
await readFile6(join9(sourceDir, "package.json"), "utf8")
|
|
15753
15917
|
);
|
|
15754
15918
|
if (pkg.name) return pkg.name;
|
|
15755
15919
|
} catch {
|
|
@@ -15760,7 +15924,7 @@ var COPY_SKIP = /(^|[\\/])(node_modules|\.next|\.open-next|\.git|\.wrangler|\.tu
|
|
|
15760
15924
|
function resolveBin(name) {
|
|
15761
15925
|
let dir = dirname(fileURLToPath(import.meta.url));
|
|
15762
15926
|
for (; ; ) {
|
|
15763
|
-
const candidate =
|
|
15927
|
+
const candidate = join9(dir, "node_modules", ".bin", name);
|
|
15764
15928
|
if (existsSync(candidate)) return candidate;
|
|
15765
15929
|
const parent = dirname(dir);
|
|
15766
15930
|
if (parent === dir) return name;
|
|
@@ -15785,7 +15949,7 @@ async function detectPackageManagerLenient(root) {
|
|
|
15785
15949
|
}
|
|
15786
15950
|
function localDependencies(sourceDir) {
|
|
15787
15951
|
return {
|
|
15788
|
-
createWorkspace: () => mkdtemp2(
|
|
15952
|
+
createWorkspace: () => mkdtemp2(join9(tmpdir2(), "naro-build-")),
|
|
15789
15953
|
removeWorkspace: (path) => rm2(path, { recursive: true, force: true }),
|
|
15790
15954
|
async runCommand(spec, observer) {
|
|
15791
15955
|
if (spec.command === "git" && spec.args[0] === "clone") {
|
|
@@ -15813,15 +15977,26 @@ function localDependencies(sourceDir) {
|
|
|
15813
15977
|
}
|
|
15814
15978
|
async function deployDirectory(sourceDir, options, log2) {
|
|
15815
15979
|
const cp2 = controlPlaneUrl();
|
|
15816
|
-
const session = await
|
|
15980
|
+
const session = await requireSessionOrLogin(log2);
|
|
15817
15981
|
const name = slugify2(options.name ?? await projectName(sourceDir));
|
|
15818
15982
|
const production = options.production ?? false;
|
|
15983
|
+
const git = await detectGitInfo(sourceDir);
|
|
15984
|
+
const branch = options.branch ?? git.branch;
|
|
15985
|
+
if (git.repositoryUrl) log2(`\u25B8 linking ${git.repositoryUrl}`);
|
|
15986
|
+
if (options.env && Object.keys(options.env).length > 0)
|
|
15987
|
+
log2(`\u25B8 syncing ${Object.keys(options.env).length} env var(s)`);
|
|
15819
15988
|
log2("\u25B8 starting deployment");
|
|
15820
15989
|
const ctx = await startCliDeployment(session.access_token, {
|
|
15821
15990
|
name,
|
|
15822
15991
|
production,
|
|
15823
|
-
controlPlaneUrl: cp2
|
|
15992
|
+
controlPlaneUrl: cp2,
|
|
15993
|
+
repositoryUrl: git.repositoryUrl,
|
|
15994
|
+
branch,
|
|
15995
|
+
rootDirectory: options.rootDirectory,
|
|
15996
|
+
env: options.env
|
|
15824
15997
|
});
|
|
15998
|
+
if (ctx.envSkipped?.length)
|
|
15999
|
+
log2(`\u26A0 skipped env: ${ctx.envSkipped.join(", ")}`);
|
|
15825
16000
|
const executor = new LocalBuildExecutor(localDependencies(sourceDir));
|
|
15826
16001
|
const observer = eventsObserver(cp2, ctx.deploymentId, ctx.callbackToken);
|
|
15827
16002
|
const result = await executor.build(
|
|
@@ -15847,15 +16022,12 @@ async function deployDirectory(sourceDir, options, log2) {
|
|
|
15847
16022
|
}
|
|
15848
16023
|
|
|
15849
16024
|
export {
|
|
15850
|
-
|
|
15851
|
-
|
|
15852
|
-
|
|
15853
|
-
isExpired,
|
|
15854
|
-
createAuthClient,
|
|
15855
|
-
refreshStoredSession,
|
|
15856
|
-
login,
|
|
16025
|
+
runLogin,
|
|
16026
|
+
runWhoami,
|
|
16027
|
+
runLogout,
|
|
15857
16028
|
startCliDeployment,
|
|
15858
16029
|
controlPlaneUrl,
|
|
16030
|
+
requireSessionOrLogin,
|
|
15859
16031
|
eventsObserver,
|
|
15860
16032
|
parseDotenv,
|
|
15861
16033
|
readProjectEnv,
|
package/dist/deploy.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { BuildObserver } from "./artifact-types";
|
|
2
|
+
import { type StoredSession } from "./session";
|
|
2
3
|
export interface CliDeploymentContext {
|
|
3
4
|
deploymentId: string;
|
|
4
5
|
projectId: string;
|
|
@@ -7,14 +8,20 @@ export interface CliDeploymentContext {
|
|
|
7
8
|
rootDirectory: string | null;
|
|
8
9
|
env: Record<string, string>;
|
|
9
10
|
controlPlaneUrl: string;
|
|
11
|
+
envSkipped?: string[];
|
|
10
12
|
}
|
|
11
13
|
export declare function startCliDeployment(accessToken: string, opts: {
|
|
12
14
|
name: string;
|
|
13
15
|
production: boolean;
|
|
14
16
|
controlPlaneUrl: string;
|
|
17
|
+
repositoryUrl?: string | null | undefined;
|
|
18
|
+
branch?: string | null | undefined;
|
|
19
|
+
rootDirectory?: string | null | undefined;
|
|
20
|
+
env?: Record<string, string> | undefined;
|
|
15
21
|
fetch?: typeof fetch;
|
|
16
22
|
}): Promise<CliDeploymentContext>;
|
|
17
23
|
export declare function controlPlaneUrl(env?: NodeJS.ProcessEnv): string;
|
|
24
|
+
export declare function requireSessionOrLogin(log: (message: string) => void, home?: string, loginFlow?: () => Promise<StoredSession>): Promise<StoredSession>;
|
|
18
25
|
export declare function eventsObserver(cp: string, deploymentId: string, token: string, fetchImpl?: typeof fetch): BuildObserver;
|
|
19
26
|
export declare function parseDotenv(text: string): Record<string, string>;
|
|
20
27
|
export declare function readProjectEnv(dir: string): Record<string, string>;
|
|
@@ -24,6 +31,7 @@ export interface DeployOptions {
|
|
|
24
31
|
rootDirectory?: string | null;
|
|
25
32
|
production?: boolean;
|
|
26
33
|
env?: Record<string, string> | undefined;
|
|
34
|
+
branch?: string | undefined;
|
|
27
35
|
}
|
|
28
36
|
export interface DeployResult {
|
|
29
37
|
workerName: string;
|
package/dist/deploy.js
CHANGED
|
@@ -5,15 +5,17 @@ import {
|
|
|
5
5
|
eventsObserver,
|
|
6
6
|
parseDotenv,
|
|
7
7
|
readProjectEnv,
|
|
8
|
+
requireSessionOrLogin,
|
|
8
9
|
slugify,
|
|
9
10
|
startCliDeployment
|
|
10
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-UNCVFCTH.js";
|
|
11
12
|
export {
|
|
12
13
|
controlPlaneUrl,
|
|
13
14
|
deployDirectory,
|
|
14
15
|
eventsObserver,
|
|
15
16
|
parseDotenv,
|
|
16
17
|
readProjectEnv,
|
|
18
|
+
requireSessionOrLogin,
|
|
17
19
|
slugify,
|
|
18
20
|
startCliDeployment
|
|
19
21
|
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface GitInfo {
|
|
2
|
+
repositoryUrl: string | null;
|
|
3
|
+
branch: string | null;
|
|
4
|
+
}
|
|
5
|
+
export type GitExec = (args: string[], cwd: string) => Promise<string>;
|
|
6
|
+
/** Reads local git metadata for the deploy: origin remote URL + current branch. */
|
|
7
|
+
export declare function detectGitInfo(dir: string, exec?: GitExec): Promise<GitInfo>;
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,82 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
clearSession,
|
|
4
|
-
createAuthClient,
|
|
5
3
|
deployDirectory,
|
|
6
|
-
isExpired,
|
|
7
|
-
loadStoredSession,
|
|
8
|
-
login,
|
|
9
4
|
readProjectEnv,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
// src/login.ts
|
|
15
|
-
import { spawn } from "child_process";
|
|
16
|
-
import { homedir } from "os";
|
|
17
|
-
var stdio = {
|
|
18
|
-
out: (m) => process.stdout.write(`${m}
|
|
19
|
-
`),
|
|
20
|
-
err: (m) => process.stderr.write(`${m}
|
|
21
|
-
`)
|
|
22
|
-
};
|
|
23
|
-
function openBrowser(url) {
|
|
24
|
-
const platform = process.platform;
|
|
25
|
-
const command = platform === "darwin" ? "open" : platform === "win32" ? "cmd" : "xdg-open";
|
|
26
|
-
const args = platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
27
|
-
spawn(command, args, { stdio: "ignore", detached: true }).unref();
|
|
28
|
-
}
|
|
29
|
-
async function runLogin(io = stdio) {
|
|
30
|
-
io.out("Opening your browser to sign in with GitHub...");
|
|
31
|
-
const session = await login({
|
|
32
|
-
openBrowser: (url) => {
|
|
33
|
-
io.out(`If your browser did not open, visit:
|
|
34
|
-
${url}`);
|
|
35
|
-
openBrowser(url);
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
saveSession(session);
|
|
39
|
-
io.out(`\u2705 Logged in as ${session.user.email ?? session.user.id}`);
|
|
40
|
-
}
|
|
41
|
-
async function runWhoami(io = stdio, home = homedir(), refresh = refreshStoredSession) {
|
|
42
|
-
let session = loadStoredSession(home);
|
|
43
|
-
if (!session) {
|
|
44
|
-
io.err("Not logged in. Run `naro login`.");
|
|
45
|
-
process.exitCode = 1;
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
if (isExpired(session)) {
|
|
49
|
-
try {
|
|
50
|
-
session = await refresh(session.refresh_token);
|
|
51
|
-
saveSession(session, home);
|
|
52
|
-
} catch {
|
|
53
|
-
io.err("Session expired. Run `naro login`.");
|
|
54
|
-
process.exitCode = 1;
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
const handle = session.user.github ? ` (${session.user.github})` : "";
|
|
59
|
-
io.out(`Logged in as ${session.user.email ?? session.user.id}${handle}`);
|
|
60
|
-
}
|
|
61
|
-
async function defaultSignOut(session) {
|
|
62
|
-
const client = createAuthClient();
|
|
63
|
-
await client.auth.setSession({
|
|
64
|
-
access_token: session.access_token,
|
|
65
|
-
refresh_token: session.refresh_token
|
|
66
|
-
});
|
|
67
|
-
await client.auth.signOut();
|
|
68
|
-
}
|
|
69
|
-
async function runLogout(io = stdio, home = homedir(), signOut = defaultSignOut) {
|
|
70
|
-
const session = loadStoredSession(home);
|
|
71
|
-
if (session) {
|
|
72
|
-
try {
|
|
73
|
-
await signOut(session);
|
|
74
|
-
} catch {
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
clearSession(home);
|
|
78
|
-
io.out("Logged out.");
|
|
79
|
-
}
|
|
5
|
+
runLogin,
|
|
6
|
+
runLogout,
|
|
7
|
+
runWhoami
|
|
8
|
+
} from "./chunk-UNCVFCTH.js";
|
|
80
9
|
|
|
81
10
|
// src/index.ts
|
|
82
11
|
function parseFlags(argv) {
|
|
@@ -86,6 +15,7 @@ function parseFlags(argv) {
|
|
|
86
15
|
if (arg === "--prod" || arg === "--production") flags.production = true;
|
|
87
16
|
else if (arg === "--name") flags.name = argv[++i];
|
|
88
17
|
else if (arg === "--root") flags.root = argv[++i];
|
|
18
|
+
else if (arg === "--branch") flags.branch = argv[++i];
|
|
89
19
|
}
|
|
90
20
|
return flags;
|
|
91
21
|
}
|
|
@@ -95,15 +25,17 @@ Usage:
|
|
|
95
25
|
naro login
|
|
96
26
|
naro whoami
|
|
97
27
|
naro logout
|
|
98
|
-
naro deploy [--name <name>] [--root <dir>] [--prod]
|
|
28
|
+
naro deploy [--name <name>] [--root <dir>] [--branch <name>] [--prod]
|
|
99
29
|
|
|
100
30
|
Options:
|
|
101
|
-
--name
|
|
102
|
-
--root
|
|
103
|
-
--
|
|
31
|
+
--name Override the deployment name (defaults to package.json name)
|
|
32
|
+
--root Sub-directory that contains the Next.js app (where package.json is)
|
|
33
|
+
--branch Override the detected git branch
|
|
34
|
+
--prod Publish to production ({name}.naro.sh) instead of preview
|
|
104
35
|
|
|
105
36
|
Credentials:
|
|
106
37
|
Run \`naro login\` to authenticate via GitHub.
|
|
38
|
+
\`naro deploy\` signs you in automatically (opens your browser) when needed.
|
|
107
39
|
Set NARO_CONTROL_PLANE_URL to use a custom control plane (defaults to https://naro.sh).
|
|
108
40
|
`;
|
|
109
41
|
async function main() {
|
|
@@ -134,6 +66,7 @@ ${HELP}`);
|
|
|
134
66
|
{
|
|
135
67
|
name: flags.name,
|
|
136
68
|
rootDirectory: flags.root ?? null,
|
|
69
|
+
branch: flags.branch,
|
|
137
70
|
production: flags.production,
|
|
138
71
|
env
|
|
139
72
|
},
|