Skip to content

Commit fb580a0

Browse files
feat: improve publish workflow (#27)
* feat: don't validate publish arguments * feat: use local deno binary if env provided * chore: exclude test from windows
1 parent 49dd483 commit fb580a0

File tree

4 files changed

+134
-81
lines changed

4 files changed

+134
-81
lines changed

src/bin.ts

Lines changed: 93 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,21 @@ ${
7676
"Prepare the package for publishing performing all checks and validations without uploading.",
7777
],
7878
["--allow-slow-types", "Allow publishing with slow types."],
79+
[
80+
"--provenance",
81+
"From CI/CD system, publicly links the package to where it was built and published from.",
82+
],
7983
])
8084
}
8185
8286
Environment variables:
8387
${
8488
prettyPrintRow([
8589
["JSR_URL", "Use a different registry URL for the publish command."],
90+
[
91+
"DENO_BIN_PATH",
92+
"Use specified Deno binary instead of local downloaded one.",
93+
],
8694
])
8795
}
8896
`);
@@ -106,85 +114,98 @@ if (args.length === 0) {
106114
printHelp();
107115
process.exit(0);
108116
} else {
109-
const options = parseArgs({
110-
args,
111-
allowPositionals: true,
112-
options: {
113-
"save-prod": { type: "boolean", default: true, short: "P" },
114-
"save-dev": { type: "boolean", default: false, short: "D" },
115-
"save-optional": { type: "boolean", default: false, short: "O" },
116-
"dry-run": { type: "boolean", default: false },
117-
"allow-slow-types": { type: "boolean", default: false },
118-
token: { type: "string" },
119-
npm: { type: "boolean", default: false },
120-
yarn: { type: "boolean", default: false },
121-
pnpm: { type: "boolean", default: false },
122-
bun: { type: "boolean", default: false },
123-
debug: { type: "boolean", default: false },
124-
help: { type: "boolean", default: false, short: "h" },
125-
version: { type: "boolean", default: false, short: "v" },
126-
},
127-
});
128-
129-
if (options.values.debug || process.env.DEBUG) {
130-
setDebug(true);
131-
}
132-
133-
if (options.values.help) {
134-
printHelp();
135-
process.exit(0);
136-
} else if (options.values.version) {
137-
const version = JSON.parse(
138-
fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf-8"),
139-
).version as string;
140-
console.log(version);
141-
process.exit(0);
142-
} else if (options.positionals.length === 0) {
143-
printHelp();
144-
process.exit(0);
145-
}
146-
147-
const pkgManagerName: PkgManagerName | null = options.values.pnpm
148-
? "pnpm"
149-
: options.values.yarn
150-
? "yarn"
151-
: options.values.bun
152-
? "bun"
153-
: null;
154-
155-
const cmd = options.positionals[0];
156-
if (cmd === "i" || cmd === "install" || cmd === "add") {
157-
run(async () => {
158-
const packages = getPackages(options.positionals);
159-
await install(packages, {
160-
mode: options.values["save-dev"]
161-
? "dev"
162-
: options.values["save-optional"]
163-
? "optional"
164-
: "prod",
165-
pkgManagerName,
166-
});
167-
});
168-
} else if (cmd === "r" || cmd === "uninstall" || cmd === "remove") {
169-
run(async () => {
170-
const packages = getPackages(options.positionals);
171-
await remove(packages, { pkgManagerName });
172-
});
173-
} else if (cmd === "publish") {
117+
const cmd = args[0];
118+
// Bypass cli argument validation for publish command. The underlying
119+
// `deno publish` cli is under active development and args may change
120+
// frequently.
121+
if (
122+
cmd === "publish" &&
123+
!args.some((arg) =>
124+
arg === "-h" || arg === "--help" || arg === "--version" || arg === "-v"
125+
)
126+
) {
174127
const binFolder = path.join(__dirname, "..", ".download");
175128
run(() =>
176129
publish(process.cwd(), {
177130
binFolder,
178-
dryRun: options.values["dry-run"] ?? false,
179-
allowSlowTypes: options.values["allow-slow-types"] ?? false,
180-
token: options.values.token,
131+
publishArgs: args.slice(1),
181132
})
182133
);
183134
} else {
184-
console.error(kl.red(`Unknown command: ${cmd}`));
185-
console.log();
186-
printHelp();
187-
process.exit(1);
135+
const options = parseArgs({
136+
args,
137+
allowPositionals: true,
138+
options: {
139+
"save-prod": { type: "boolean", default: true, short: "P" },
140+
"save-dev": { type: "boolean", default: false, short: "D" },
141+
"save-optional": { type: "boolean", default: false, short: "O" },
142+
"dry-run": { type: "boolean", default: false },
143+
"allow-slow-types": { type: "boolean", default: false },
144+
token: { type: "string" },
145+
config: { type: "string", short: "c" },
146+
"no-config": { type: "boolean" },
147+
check: { type: "string" },
148+
"no-check": { type: "string" },
149+
quiet: { type: "boolean", short: "q" },
150+
npm: { type: "boolean", default: false },
151+
yarn: { type: "boolean", default: false },
152+
pnpm: { type: "boolean", default: false },
153+
bun: { type: "boolean", default: false },
154+
debug: { type: "boolean", default: false },
155+
help: { type: "boolean", default: false, short: "h" },
156+
version: { type: "boolean", default: false, short: "v" },
157+
},
158+
});
159+
160+
if (options.values.debug || process.env.DEBUG) {
161+
setDebug(true);
162+
}
163+
164+
if (options.values.help) {
165+
printHelp();
166+
process.exit(0);
167+
} else if (options.values.version) {
168+
const version = JSON.parse(
169+
fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf-8"),
170+
).version as string;
171+
console.log(version);
172+
process.exit(0);
173+
} else if (options.positionals.length === 0) {
174+
printHelp();
175+
process.exit(0);
176+
}
177+
178+
const pkgManagerName: PkgManagerName | null = options.values.pnpm
179+
? "pnpm"
180+
: options.values.yarn
181+
? "yarn"
182+
: options.values.bun
183+
? "bun"
184+
: null;
185+
186+
if (cmd === "i" || cmd === "install" || cmd === "add") {
187+
run(async () => {
188+
const packages = getPackages(options.positionals);
189+
await install(packages, {
190+
mode: options.values["save-dev"]
191+
? "dev"
192+
: options.values["save-optional"]
193+
? "optional"
194+
: "prod",
195+
pkgManagerName,
196+
});
197+
});
198+
} else if (cmd === "r" || cmd === "uninstall" || cmd === "remove") {
199+
run(async () => {
200+
const packages = getPackages(options.positionals);
201+
await remove(packages, { pkgManagerName });
202+
});
203+
} else {
204+
console.error(kl.red(`Unknown command: ${cmd}`));
205+
console.log();
206+
printHelp();
207+
process.exit(1);
208+
}
188209
}
189210
}
190211

src/commands.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -97,16 +97,14 @@ export async function remove(packages: JsrPackage[], options: BaseOptions) {
9797

9898
export interface PublishOptions {
9999
binFolder: string;
100-
dryRun: boolean;
101-
allowSlowTypes: boolean;
102-
token: string | undefined;
100+
publishArgs: string[];
103101
}
104102

105-
export async function publish(cwd: string, options: PublishOptions) {
103+
async function getOrDownloadBinPath(binFolder: string) {
106104
const info = await getDenoDownloadUrl();
107105

108106
const binPath = path.join(
109-
options.binFolder,
107+
binFolder,
110108
info.version,
111109
// Ensure each binary has their own folder to avoid overwriting it
112110
// in case jsr gets added to a project as a dependency where
@@ -120,7 +118,7 @@ export async function publish(cwd: string, options: PublishOptions) {
120118
// Clear folder first to get rid of old download artifacts
121119
// to avoid taking up lots of disk space.
122120
try {
123-
await fs.promises.rm(options.binFolder, { recursive: true });
121+
await fs.promises.rm(binFolder, { recursive: true });
124122
} catch (err) {
125123
if (!(err instanceof Error) || (err as any).code !== "ENOENT") {
126124
throw err;
@@ -130,14 +128,20 @@ export async function publish(cwd: string, options: PublishOptions) {
130128
await downloadDeno(binPath, info);
131129
}
132130

131+
return binPath;
132+
}
133+
134+
export async function publish(cwd: string, options: PublishOptions) {
135+
const binPath = process.env.DENO_BIN_PATH ??
136+
await getOrDownloadBinPath(options.binFolder);
137+
133138
// Ready to publish now!
134139
const args = [
135140
"publish",
136141
"--unstable-bare-node-builtins",
137142
"--unstable-sloppy-imports",
143+
"--no-check",
144+
...options.publishArgs,
138145
];
139-
if (options.dryRun) args.push("--dry-run");
140-
if (options.allowSlowTypes) args.push("--allow-slow-types");
141-
if (options.token) args.push("--token", options.token);
142146
await exec(binPath, args, cwd);
143147
}

test/commands.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,4 +297,30 @@ describe("publish", () => {
297297
await runJsr(["publish", "--dry-run", "--token", "dummy-token"], dir);
298298
});
299299
}).timeout(600000);
300+
301+
// Windows doesn't support #!/usr/bin/env
302+
if (process.platform !== "win32") {
303+
it("use deno binary from DENO_BIN_PATH when set", async () => {
304+
await runInTempDir(async (dir) => {
305+
await fs.promises.writeFile(
306+
path.join(dir, "mod.ts"),
307+
"export const value = 42;",
308+
"utf-8",
309+
);
310+
311+
// TODO: Change this once deno supports jsr.json
312+
await writeJson<DenoJson>(path.join(dir, "deno.json"), {
313+
name: "@deno/jsr-cli-test",
314+
version: "1.0.0",
315+
exports: {
316+
".": "./mod.ts",
317+
},
318+
});
319+
320+
await runJsr(["publish", "--dry-run", "--non-existant-option"], dir, {
321+
DENO_BIN_PATH: path.join(__dirname, "fixtures", "dummy.js"),
322+
});
323+
});
324+
});
325+
}
300326
});

test/fixtures/dummy.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#!/usr/bin/env node
2+
console.log("it works");

0 commit comments

Comments
 (0)