diff --git a/src/bin.ts b/src/bin.ts index 6ff400b..b27126f 100644 --- a/src/bin.ts +++ b/src/bin.ts @@ -76,6 +76,10 @@ ${ "Prepare the package for publishing performing all checks and validations without uploading.", ], ["--allow-slow-types", "Allow publishing with slow types."], + [ + "--provenance", + "From CI/CD system, publicly links the package to where it was built and published from.", + ], ]) } @@ -83,6 +87,10 @@ Environment variables: ${ prettyPrintRow([ ["JSR_URL", "Use a different registry URL for the publish command."], + [ + "DENO_BIN_PATH", + "Use specified Deno binary instead of local downloaded one.", + ], ]) } `); @@ -106,85 +114,98 @@ if (args.length === 0) { printHelp(); process.exit(0); } else { - const options = parseArgs({ - args, - allowPositionals: true, - options: { - "save-prod": { type: "boolean", default: true, short: "P" }, - "save-dev": { type: "boolean", default: false, short: "D" }, - "save-optional": { type: "boolean", default: false, short: "O" }, - "dry-run": { type: "boolean", default: false }, - "allow-slow-types": { type: "boolean", default: false }, - token: { type: "string" }, - npm: { type: "boolean", default: false }, - yarn: { type: "boolean", default: false }, - pnpm: { type: "boolean", default: false }, - bun: { type: "boolean", default: false }, - debug: { type: "boolean", default: false }, - help: { type: "boolean", default: false, short: "h" }, - version: { type: "boolean", default: false, short: "v" }, - }, - }); - - if (options.values.debug || process.env.DEBUG) { - setDebug(true); - } - - if (options.values.help) { - printHelp(); - process.exit(0); - } else if (options.values.version) { - const version = JSON.parse( - fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf-8"), - ).version as string; - console.log(version); - process.exit(0); - } else if (options.positionals.length === 0) { - printHelp(); - process.exit(0); - } - - const pkgManagerName: PkgManagerName | null = options.values.pnpm - ? "pnpm" - : options.values.yarn - ? "yarn" - : options.values.bun - ? "bun" - : null; - - const cmd = options.positionals[0]; - if (cmd === "i" || cmd === "install" || cmd === "add") { - run(async () => { - const packages = getPackages(options.positionals); - await install(packages, { - mode: options.values["save-dev"] - ? "dev" - : options.values["save-optional"] - ? "optional" - : "prod", - pkgManagerName, - }); - }); - } else if (cmd === "r" || cmd === "uninstall" || cmd === "remove") { - run(async () => { - const packages = getPackages(options.positionals); - await remove(packages, { pkgManagerName }); - }); - } else if (cmd === "publish") { + const cmd = args[0]; + // Bypass cli argument validation for publish command. The underlying + // `deno publish` cli is under active development and args may change + // frequently. + if ( + cmd === "publish" && + !args.some((arg) => + arg === "-h" || arg === "--help" || arg === "--version" || arg === "-v" + ) + ) { const binFolder = path.join(__dirname, "..", ".download"); run(() => publish(process.cwd(), { binFolder, - dryRun: options.values["dry-run"] ?? false, - allowSlowTypes: options.values["allow-slow-types"] ?? false, - token: options.values.token, + publishArgs: args.slice(1), }) ); } else { - console.error(kl.red(`Unknown command: ${cmd}`)); - console.log(); - printHelp(); - process.exit(1); + const options = parseArgs({ + args, + allowPositionals: true, + options: { + "save-prod": { type: "boolean", default: true, short: "P" }, + "save-dev": { type: "boolean", default: false, short: "D" }, + "save-optional": { type: "boolean", default: false, short: "O" }, + "dry-run": { type: "boolean", default: false }, + "allow-slow-types": { type: "boolean", default: false }, + token: { type: "string" }, + config: { type: "string", short: "c" }, + "no-config": { type: "boolean" }, + check: { type: "string" }, + "no-check": { type: "string" }, + quiet: { type: "boolean", short: "q" }, + npm: { type: "boolean", default: false }, + yarn: { type: "boolean", default: false }, + pnpm: { type: "boolean", default: false }, + bun: { type: "boolean", default: false }, + debug: { type: "boolean", default: false }, + help: { type: "boolean", default: false, short: "h" }, + version: { type: "boolean", default: false, short: "v" }, + }, + }); + + if (options.values.debug || process.env.DEBUG) { + setDebug(true); + } + + if (options.values.help) { + printHelp(); + process.exit(0); + } else if (options.values.version) { + const version = JSON.parse( + fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf-8"), + ).version as string; + console.log(version); + process.exit(0); + } else if (options.positionals.length === 0) { + printHelp(); + process.exit(0); + } + + const pkgManagerName: PkgManagerName | null = options.values.pnpm + ? "pnpm" + : options.values.yarn + ? "yarn" + : options.values.bun + ? "bun" + : null; + + if (cmd === "i" || cmd === "install" || cmd === "add") { + run(async () => { + const packages = getPackages(options.positionals); + await install(packages, { + mode: options.values["save-dev"] + ? "dev" + : options.values["save-optional"] + ? "optional" + : "prod", + pkgManagerName, + }); + }); + } else if (cmd === "r" || cmd === "uninstall" || cmd === "remove") { + run(async () => { + const packages = getPackages(options.positionals); + await remove(packages, { pkgManagerName }); + }); + } else { + console.error(kl.red(`Unknown command: ${cmd}`)); + console.log(); + printHelp(); + process.exit(1); + } } } diff --git a/src/commands.ts b/src/commands.ts index a5db9f3..e2862af 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -97,16 +97,14 @@ export async function remove(packages: JsrPackage[], options: BaseOptions) { export interface PublishOptions { binFolder: string; - dryRun: boolean; - allowSlowTypes: boolean; - token: string | undefined; + publishArgs: string[]; } -export async function publish(cwd: string, options: PublishOptions) { +async function getOrDownloadBinPath(binFolder: string) { const info = await getDenoDownloadUrl(); const binPath = path.join( - options.binFolder, + binFolder, info.version, // Ensure each binary has their own folder to avoid overwriting it // in case jsr gets added to a project as a dependency where @@ -120,7 +118,7 @@ export async function publish(cwd: string, options: PublishOptions) { // Clear folder first to get rid of old download artifacts // to avoid taking up lots of disk space. try { - await fs.promises.rm(options.binFolder, { recursive: true }); + await fs.promises.rm(binFolder, { recursive: true }); } catch (err) { if (!(err instanceof Error) || (err as any).code !== "ENOENT") { throw err; @@ -130,14 +128,20 @@ export async function publish(cwd: string, options: PublishOptions) { await downloadDeno(binPath, info); } + return binPath; +} + +export async function publish(cwd: string, options: PublishOptions) { + const binPath = process.env.DENO_BIN_PATH ?? + await getOrDownloadBinPath(options.binFolder); + // Ready to publish now! const args = [ "publish", "--unstable-bare-node-builtins", "--unstable-sloppy-imports", + "--no-check", + ...options.publishArgs, ]; - if (options.dryRun) args.push("--dry-run"); - if (options.allowSlowTypes) args.push("--allow-slow-types"); - if (options.token) args.push("--token", options.token); await exec(binPath, args, cwd); } diff --git a/test/commands.test.ts b/test/commands.test.ts index 22914b1..bd260ce 100644 --- a/test/commands.test.ts +++ b/test/commands.test.ts @@ -297,4 +297,30 @@ describe("publish", () => { await runJsr(["publish", "--dry-run", "--token", "dummy-token"], dir); }); }).timeout(600000); + + // Windows doesn't support #!/usr/bin/env + if (process.platform !== "win32") { + it("use deno binary from DENO_BIN_PATH when set", async () => { + await runInTempDir(async (dir) => { + await fs.promises.writeFile( + path.join(dir, "mod.ts"), + "export const value = 42;", + "utf-8", + ); + + // TODO: Change this once deno supports jsr.json + await writeJson(path.join(dir, "deno.json"), { + name: "@deno/jsr-cli-test", + version: "1.0.0", + exports: { + ".": "./mod.ts", + }, + }); + + await runJsr(["publish", "--dry-run", "--non-existant-option"], dir, { + DENO_BIN_PATH: path.join(__dirname, "fixtures", "dummy.js"), + }); + }); + }); + } }); diff --git a/test/fixtures/dummy.js b/test/fixtures/dummy.js new file mode 100755 index 0000000..e73d040 --- /dev/null +++ b/test/fixtures/dummy.js @@ -0,0 +1,2 @@ +#!/usr/bin/env node +console.log("it works");