Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions src/runtime/cli/publish_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ use bun_libarchive::lib::{Archive, ArchiveIterator, IteratorResult as ArchiveIte
use bun_parsers::json as json_mod;
use bun_paths::resolve_path::{join_abs_string_buf_z, normalize_buf, normalize_buf_z};
use bun_paths::{self as path, PathBuffer};
use bun_resolver::fs::FileSystem;
use bun_sha_hmac as sha;
use bun_simdutf_sys::simdutf;
use bun_sys::dir_iterator as DirIterator;
Expand Down Expand Up @@ -135,14 +134,18 @@ pub(crate) type FromWorkspaceError = pack::PackError<true>;

impl<'a, const DIRECTORY_PUBLISH: bool> Context<'a, DIRECTORY_PUBLISH> {
/// Retrieve information for publishing from a tarball path, `bun publish path/to/tarball.tgz`
///
/// `original_cwd` is the directory the command was run from. `PackageManager::init` has
/// already chdir'd to the package root, which may be an ancestor of it.
Comment thread
robobun marked this conversation as resolved.
Outdated
pub(crate) fn from_tarball_path(
ctx: Command::Context<'a>,
manager: &'a mut PackageManager,
original_cwd: &[u8],
tarball_path: &[u8],
) -> Result<Context<'a, DIRECTORY_PUBLISH>, FromTarballError> {
let mut abs_buf = PathBuffer::uninit();
let abs_tarball_path = join_abs_string_buf_z::<path::platform::Auto>(
FileSystem::instance().top_level_dir,
original_cwd,
&mut abs_buf,
&[tarball_path],
);
Expand Down Expand Up @@ -549,13 +552,13 @@ impl PublishCommand {
Global::crash();
}
};
drop(original_cwd);
let manager_ptr: *mut PackageManager = manager;

if cli.positionals.len() > 1 {
let context = match Context::<false>::from_tarball_path(
ctx,
manager,
&original_cwd,
cli.positionals[1],
) {
Ok(c) => c,
Expand Down
62 changes: 62 additions & 0 deletions test/cli/install/bun-publish.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,68 @@ test("can publish from a tarball", async () => {
await runBunInstall(env, packageDir, { savesLockfile: false });
expect(await file(join(packageDir, "node_modules", "publish-pkg-2", "package.json")).json()).toEqual(json);
});
describe("relative tarball path", () => {
// `bun publish` chdirs to the package root (or the workspace root) before it reads the tarball.
// The path on the command line still has to be resolved against the directory the command ran from.
// --dry-run never talks to the registry, so a bogus one with auth in the url is enough.
const dryRunEnv = { ...env, npm_config_registry: "http://someuser:hunter2@127.0.0.1:1/" };

test.concurrent("is resolved against the cwd, not the package root above it", async () => {
using dir = tempDir("publish-tarball-cwd", {
"package.json": JSON.stringify({ name: "publish-tarball-cwd-root", version: "0.0.0" }),
"stale-src/package.json": JSON.stringify({ name: "publish-tarball-cwd-stale", version: "0.0.1" }),
"dist-src/package.json": JSON.stringify({ name: "publish-tarball-cwd-dist", version: "9.9.9" }),
"dist": {},
});
// a same-named tarball in the package root, and the one we actually mean in ./dist
await pack(join(String(dir), "stale-src"), env, "--filename", join(String(dir), "rel.tgz"));
await pack(join(String(dir), "dist-src"), env, "--filename", join(String(dir), "dist", "rel.tgz"));

const { out, err, exitCode } = await publish(dryRunEnv, join(String(dir), "dist"), "./rel.tgz", "--dry-run");
expect(err).not.toContain("error:");
expect(out).toContain(" + publish-tarball-cwd-dist@9.9.9");
expect(out).not.toContain("publish-tarball-cwd-stale");
expect(exitCode).toBe(0);
});

test.concurrent("is found when the cwd is a subdirectory of the package root", async () => {
using dir = tempDir("publish-tarball-subdir", {
"package.json": JSON.stringify({ name: "publish-tarball-subdir-root", version: "0.0.0" }),
"dist-src/package.json": JSON.stringify({ name: "publish-tarball-subdir-dist", version: "1.0.0" }),
"dist": {},
});
await pack(join(String(dir), "dist-src"), env, "--filename", join(String(dir), "dist", "pkg.tgz"));

const { out, err, exitCode } = await publish(dryRunEnv, join(String(dir), "dist"), "pkg.tgz", "--dry-run");
expect(err).not.toContain("ENOENT");
expect(out).toContain(" + publish-tarball-subdir-dist@1.0.0");
expect(exitCode).toBe(0);
});

test.concurrent("is found when the cwd is a workspace package", async () => {
using dir = tempDir("publish-tarball-workspace", {
"package.json": JSON.stringify({
name: "publish-tarball-workspace-root",
version: "0.0.0",
workspaces: ["packages/*"],
}),
"packages/member/package.json": JSON.stringify({ name: "publish-tarball-workspace-member", version: "1.2.3" }),
});
const memberDir = join(String(dir), "packages", "member");
await pack(memberDir, env);
expect(await exists(join(memberDir, "publish-tarball-workspace-member-1.2.3.tgz"))).toBeTrue();

const { out, err, exitCode } = await publish(
dryRunEnv,
memberDir,
"./publish-tarball-workspace-member-1.2.3.tgz",
"--dry-run",
);
expect(err).not.toContain("ENOENT");
expect(out).toContain(" + publish-tarball-workspace-member@1.2.3");
expect(exitCode).toBe(0);
});
});
test("can publish scoped packages", async () => {
const { packageDir, packageJson } = await registry.createTestDir();
const bunfig = await registry.authBunfig("scoped-pkg");
Expand Down