Skip to content

Commit 422d504

Browse files
mbaumanmortenpi
andauthored
.juliabundleignore should apply to top-level files, too (#99)
Co-authored-by: Morten Piibeleht <morten.piibeleht@juliahub.com>
1 parent 33e3ba0 commit 422d504

File tree

8 files changed

+126
-13
lines changed

8 files changed

+126
-13
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
44

5+
## Unreleased
6+
7+
### Fixed
8+
9+
* Top-level `.juliabundleignore` files are now correctly handled when using `JuliaHub.appbundle`. ([#99], [#100])
10+
511
## Version [v0.1.14] - 2025-06-11
612

713
### Added
@@ -186,3 +192,5 @@ Initial package release.
186192
[#92]: https://github.com/JuliaComputing/JuliaHub.jl/issues/92
187193
[#94]: https://github.com/JuliaComputing/JuliaHub.jl/issues/94
188194
[#96]: https://github.com/JuliaComputing/JuliaHub.jl/issues/96
195+
[#99]: https://github.com/JuliaComputing/JuliaHub.jl/issues/99
196+
[#100]: https://github.com/JuliaComputing/JuliaHub.jl/issues/100

src/PackageBundler/utils.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,15 +77,15 @@ function get_bundleignore(file, top)
7777
dir = dirname(file)
7878
patterns = Set{Any}()
7979
try
80-
while dir != top
80+
while true
8181
if isfile(joinpath(dir, ".juliabundleignore"))
8282
union!(
8383
patterns,
8484
Glob.FilenameMatch.(strip.(readlines(joinpath(dir, ".juliabundleignore")))),
8585
)
8686
return patterns, dir
8787
end
88-
if dir == dirname(dir)
88+
if dir == dirname(dir) || dir == top
8989
break
9090
end
9191
dir = dirname(dir)

src/utils.jl

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -304,22 +304,55 @@ function Base.show(io::IO, filehash::FileHash)
304304
end
305305

306306
# Estimates the size of the bundle directory
307-
function _max_appbundle_dir_size(dir; maxsize=100 * 1024 * 1024)
307+
function _max_appbundle_dir_size(dir::AbstractString; maxsize=100 * 1024 * 1024)
308308
sz = 0
309+
_walk_appbundle_files(dir) do filepath
310+
if sz > maxsize
311+
return _WalkFilesReturnEarly()
312+
end
313+
sz += filesize(filepath)
314+
end
315+
return sz, sz <= maxsize
316+
end
317+
318+
function _walk_appbundle_files(f::Base.Callable, dir::AbstractString)
319+
# Note: even if if `path_filterer` says that directory `foo/bar`
320+
# should not be included, it will still likely return `true` for
321+
# any files in there, like `foo/bar/baz`. So we need to make sure
322+
# we stop recursing into subdirectories.
309323
pred = _PackageBundler.path_filterer(dir)
310-
for (root, _, files) in walkdir(dir)
311-
for file in files
312-
file = joinpath(root, file)
313-
if !pred(file)
314-
@debug "ignoring $file in dir size measurement"
315-
continue
316-
end
324+
_walkfiles(dir; descend=pred) do filepath
325+
if !pred(filepath)
326+
@debug "ignoring file in _walk_appbundle_files: $(filepath)"
327+
return nothing
328+
end
329+
return f(filepath)
330+
end
331+
end
332+
333+
struct _WalkFilesReturnEarly end
317334

318-
sz > maxsize && return sz, false
319-
sz += filesize(file)
335+
# Calls `f` on any non-directory in `root`.
336+
# `descend` gets called on any directory, and if it returns false,
337+
function _walkfiles(f::Base.Callable, root::AbstractString; descend::Base.Callable)
338+
if !isdir(root)
339+
error("Not a directory: $(root)")
340+
end
341+
directories = String[root]
342+
while !isempty(directories)
343+
dir = popfirst!(directories)
344+
for subpath in readdir(dir; join=true)
345+
if isdir(subpath)
346+
if descend(subpath)::Bool
347+
push!(directories, subpath)
348+
end
349+
else
350+
if f(subpath) === _WalkFilesReturnEarly()
351+
break
352+
end
353+
end
320354
end
321355
end
322-
return sz, sz < maxsize
323356
end
324357

325358
function _json_get(d::Dict, key, ::Type{T}; var::AbstractString, parse=false) where {T}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
000000000000000000000000000000000000000000000000
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
000000000000000000000000000000000000000000000000
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
000000000000000000000000000000000000000000000000

test/packagebundler.jl

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,3 +225,44 @@ end
225225
end
226226
@test isfile(out)
227227
end
228+
229+
@testset "path_filterer" begin
230+
@testset "subdirectory" begin
231+
dir = joinpath(@__DIR__, "fixtures", "ignorefiles")
232+
pred = JuliaHub._PackageBundler.path_filterer(dir)
233+
@test pred(joinpath(dir, "Pkg3", "README.md"))
234+
235+
@test pred(joinpath(dir, "Pkg3", "src", "bar"))
236+
@test !pred(joinpath(dir, "Pkg3", "src", "foo"))
237+
@test pred(joinpath(dir, "Pkg3", "src", "fooo"))
238+
239+
@test !pred(joinpath(dir, "Pkg3", "test", "bar"))
240+
@test !pred(joinpath(dir, "Pkg3", "test", "foo"))
241+
@test pred(joinpath(dir, "Pkg3", "test", "fooo", "test"))
242+
243+
# Note: even though the test/foo and test/bar directories are
244+
# excluded, the predicate function does not return false if you
245+
# check for file within the directories.
246+
@test pred(joinpath(dir, "Pkg3", "test", "bar", "test"))
247+
@test pred(joinpath(dir, "Pkg3", "test", "foo", "test"))
248+
end
249+
@testset "toplevel" begin
250+
dir = joinpath(@__DIR__, "fixtures", "ignorefiles", "Pkg3")
251+
pred = JuliaHub._PackageBundler.path_filterer(dir)
252+
@test pred(joinpath(dir, "README.md"))
253+
254+
@test pred(joinpath(dir, "src", "bar"))
255+
@test !pred(joinpath(dir, "src", "foo"))
256+
@test pred(joinpath(dir, "src", "fooo"))
257+
258+
@test !pred(joinpath(dir, "test", "bar"))
259+
@test !pred(joinpath(dir, "test", "foo"))
260+
@test pred(joinpath(dir, "test", "fooo", "test"))
261+
262+
# Note: even though the test/foo and test/bar directories are
263+
# excluded, the predicate function does not return false if you
264+
# check for file within the directories.
265+
@test pred(joinpath(dir, "test", "bar", "test"))
266+
@test pred(joinpath(dir, "test", "foo", "test"))
267+
end
268+
end

test/utils.jl

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,3 +101,31 @@ end
101101
Dict("_id_missing" => "123e4567-e89b-12d3-a456-426614174000"), "id", UUIDs.UUID
102102
)
103103
end
104+
105+
@testset "_max_appbundle_dir_size" begin
106+
# We check here that the `.juliabundleignore` is honored by making
107+
# sure that the calculated total file size of the Pkg3/ directory is
108+
dir = joinpath(@__DIR__, "fixtures", "ignorefiles", "Pkg3")
109+
110+
appbundle_files = String[]
111+
JuliaHub._walk_appbundle_files(dir) do filepath
112+
push!(appbundle_files, relpath(filepath, dir))
113+
end
114+
@test sort(appbundle_files) == [
115+
".gitignore", ".juliabundleignore", "Project.toml", "README.md",
116+
joinpath("src", "Pkg3.jl"), joinpath("src", "bar"), joinpath("src", "fooo"),
117+
joinpath("test", "fooo", "test"), joinpath("test", "runtests.jl"),
118+
]
119+
120+
# The files that are not meant to be included in the /Pkg3/ bundle here are
121+
# all 50 byte files. Should they should show up in the total size here.
122+
#
123+
# Note: on windows, the files may be checked out with different line endings,
124+
# so the total file size may be slightly different.
125+
sz, sz_is_low = JuliaHub._max_appbundle_dir_size(dir)
126+
@test sz_is_low
127+
@test sz in (Sys.iswindows() ? (405, 432) : (405,))
128+
129+
_, sz_low = JuliaHub._max_appbundle_dir_size(dir; maxsize=200)
130+
@test !sz_low
131+
end

0 commit comments

Comments
 (0)