diff options
author | Divy Srivastava <dj.srivastava23@gmail.com> | 2024-02-23 07:56:34 +0530 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-02-23 07:56:34 +0530 |
commit | f49abcc1ac3de72bf894ccfc0102d83ec19f1d46 (patch) | |
tree | 8a50b03bb1e1d2e9e8831620529ef328a5582041 | |
parent | ae703041b1921affb7fa8a0aa865c6f302c72d6e (diff) |
feat(publish): respect .gitignore during `deno publish` (#22514)
Files from `.gitignore`, global git config, `.git/info/exclude` and
`deno.json`'s `exclude` are ignored.
-rw-r--r-- | Cargo.lock | 51 | ||||
-rw-r--r-- | cli/Cargo.toml | 1 | ||||
-rw-r--r-- | cli/tools/registry/tar.rs | 56 | ||||
-rw-r--r-- | tests/integration/publish_tests.rs | 67 |
4 files changed, 156 insertions, 19 deletions
diff --git a/Cargo.lock b/Cargo.lock index 1cb8c9d48..5e6d1ee4e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -500,6 +500,16 @@ dependencies = [ ] [[package]] +name = "bstr" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "542f33a8835a0884b006a0c3df3dadd99c0c3f296ed26c2fdc8028e01ad6230c" +dependencies = [ + "memchr", + "serde", +] + +[[package]] name = "bumpalo" version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1060,6 +1070,7 @@ dependencies = [ "glibc_version", "glob", "hex", + "ignore", "import_map", "indexmap", "jsonc-parser", @@ -2848,6 +2859,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] +name = "globset" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", +] + +[[package]] name = "glow" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3315,6 +3339,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" [[package]] +name = "ignore" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbe7873dab538a9a44ad79ede1faf5f30d49f9a5c883ddbab48bce81b64b7492" +dependencies = [ + "globset", + "lazy_static", + "log", + "memchr", + "regex", + "same-file", + "thread_local", + "walkdir", + "winapi-util", +] + +[[package]] name = "image" version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -6483,6 +6524,16 @@ dependencies = [ ] [[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] name = "time" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index ae42d3c30..b376f924e 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -106,6 +106,7 @@ flate2.workspace = true fs3.workspace = true glob = "0.3.1" hex.workspace = true +ignore = "0.4" import_map = { version = "=0.18.3", features = ["ext"] } indexmap.workspace = true jsonc-parser = { version = "=0.23.0", features = ["serde"] } diff --git a/cli/tools/registry/tar.rs b/cli/tools/registry/tar.rs index 3dc2616fa..66d15b5a6 100644 --- a/cli/tools/registry/tar.rs +++ b/cli/tools/registry/tar.rs @@ -3,12 +3,14 @@ use bytes::Bytes; use deno_ast::MediaType; use deno_config::glob::FilePatterns; +use deno_config::glob::PathOrPattern; use deno_core::anyhow::Context; use deno_core::error::AnyError; use deno_core::url::Url; +use ignore::overrides::OverrideBuilder; +use ignore::WalkBuilder; use sha2::Digest; use std::collections::HashSet; -use std::ffi::OsStr; use std::fmt::Write as FmtWrite; use std::io::Write; use std::path::Path; @@ -46,27 +48,45 @@ pub fn create_gzipped_tarball( let mut paths = HashSet::new(); - let mut iterator = walkdir::WalkDir::new(dir).follow_links(false).into_iter(); - while let Some(entry) = iterator.next() { + let mut ob = OverrideBuilder::new(dir); + ob.add("!.git")?.add("!node_modules")?.add("!.DS_Store")?; + + for pattern in file_patterns.as_ref().iter().flat_map(|p| p.include.iter()) { + for path_or_pat in pattern.inner() { + match path_or_pat { + PathOrPattern::Path(p) => ob.add(p.to_str().unwrap())?, + PathOrPattern::Pattern(p) => ob.add(p.as_str())?, + PathOrPattern::RemoteUrl(_) => continue, + }; + } + } + + let overrides = ob.build()?; + + let iterator = WalkBuilder::new(dir) + .follow_links(false) + .require_git(false) + .git_ignore(true) + .git_global(true) + .git_exclude(true) + .overrides(overrides) + .filter_entry(move |entry| { + let matches_pattern = file_patterns + .as_ref() + .map(|p| p.matches_path(entry.path())) + .unwrap_or(true); + matches_pattern + }) + .build(); + + for entry in iterator { let entry = entry?; let path = entry.path(); - let file_type = entry.file_type(); - - let matches_pattern = file_patterns - .as_ref() - .map(|p| p.matches_path(path)) - .unwrap_or(true); - if !matches_pattern - || path.file_name() == Some(OsStr::new(".git")) - || path.file_name() == Some(OsStr::new("node_modules")) - || path.file_name() == Some(OsStr::new(".DS_Store")) - { - if file_type.is_dir() { - iterator.skip_current_dir(); - } + let Some(file_type) = entry.file_type() else { + // entry doesn’t have a file type if it corresponds to stdin. continue; - } + }; let Ok(specifier) = Url::from_file_path(path) else { diagnostics_collector diff --git a/tests/integration/publish_tests.rs b/tests/integration/publish_tests.rs index 61cb40fba..71bc838a8 100644 --- a/tests/integration/publish_tests.rs +++ b/tests/integration/publish_tests.rs @@ -216,6 +216,43 @@ itest!(config_flag { }); #[test] +fn ignores_gitignore() { + let context = publish_context_builder().build(); + let temp_dir = context.temp_dir().path(); + temp_dir.join("deno.json").write_json(&json!({ + "name": "@foo/bar", + "version": "1.0.0", + "exports": "./main.ts" + })); + + temp_dir.join("main.ts").write("import './sub_dir/b.ts';"); + + let gitignore = temp_dir.join(".gitignore"); + gitignore.write("ignored.ts\nsub_dir/ignored.wasm"); + + let sub_dir = temp_dir.join("sub_dir"); + sub_dir.create_dir_all(); + sub_dir.join("ignored.wasm").write(""); + sub_dir.join("b.ts").write("export default {}"); + + temp_dir.join("ignored.ts").write(""); + + let output = context + .new_command() + .arg("publish") + .arg("--dry-run") + .arg("--token") + .arg("sadfasdf") + .run(); + output.assert_exit_code(0); + let output = output.combined_output(); + assert_contains!(output, "b.ts"); + assert_contains!(output, "main.ts"); + assert_not_contains!(output, "ignored.ts"); + assert_not_contains!(output, "ignored.wasm"); +} + +#[test] fn ignores_directories() { let context = publish_context_builder().build(); let temp_dir = context.temp_dir().path(); @@ -261,6 +298,35 @@ fn ignores_directories() { } #[test] +fn includes_directories_with_gitignore() { + let context = publish_context_builder().build(); + let temp_dir = context.temp_dir().path(); + temp_dir.join("deno.json").write_json(&json!({ + "name": "@foo/bar", + "version": "1.0.0", + "exports": "./main.ts", + "publish": { + "include": [ "deno.json", "main.ts" ] + } + })); + + temp_dir.join(".gitignore").write("main.ts"); + temp_dir.join("main.ts").write(""); + temp_dir.join("ignored.ts").write(""); + + let output = context + .new_command() + .arg("publish") + .arg("--token") + .arg("sadfasdf") + .run(); + output.assert_exit_code(0); + let output = output.combined_output(); + assert_contains!(output, "main.ts"); + assert_not_contains!(output, "ignored.ts"); +} + +#[test] fn includes_directories() { let context = publish_context_builder().build(); let temp_dir = context.temp_dir().path(); @@ -279,7 +345,6 @@ fn includes_directories() { let output = context .new_command() .arg("publish") - .arg("--log-level=debug") .arg("--token") .arg("sadfasdf") .run(); |