Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

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).

## Unreleased

### Fixed

* Top-level `.juliabundleignore` files are now correctly handled when using `JuliaHub.appbundle`. ([#99], [#100])

## Version [v0.1.14] - 2025-06-11

### Added
Expand Down Expand Up @@ -186,3 +192,5 @@ Initial package release.
[#92]: https://github.com/JuliaComputing/JuliaHub.jl/issues/92
[#94]: https://github.com/JuliaComputing/JuliaHub.jl/issues/94
[#96]: https://github.com/JuliaComputing/JuliaHub.jl/issues/96
[#99]: https://github.com/JuliaComputing/JuliaHub.jl/issues/99
[#100]: https://github.com/JuliaComputing/JuliaHub.jl/issues/100
4 changes: 2 additions & 2 deletions src/PackageBundler/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,15 @@ function get_bundleignore(file, top)
dir = dirname(file)
patterns = Set{Any}()
try
while dir != top
while true
if isfile(joinpath(dir, ".juliabundleignore"))
union!(
patterns,
Glob.FilenameMatch.(strip.(readlines(joinpath(dir, ".juliabundleignore")))),
)
return patterns, dir
end
if dir == dirname(dir)
if dir == dirname(dir) || dir == top
break
end
dir = dirname(dir)
Expand Down
55 changes: 44 additions & 11 deletions src/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -304,22 +304,55 @@ function Base.show(io::IO, filehash::FileHash)
end

# Estimates the size of the bundle directory
function _max_appbundle_dir_size(dir; maxsize=100 * 1024 * 1024)
function _max_appbundle_dir_size(dir::AbstractString; maxsize=100 * 1024 * 1024)
sz = 0
_walk_appbundle_files(dir) do filepath
if sz > maxsize
return _WalkFilesReturnEarly()
end
sz += filesize(filepath)
end
return sz, sz <= maxsize
end

function _walk_appbundle_files(f::Base.Callable, dir::AbstractString)
# Note: even if if `path_filterer` says that directory `foo/bar`
# should not be included, it will still likely return `true` for
# any files in there, like `foo/bar/baz`. So we need to make sure
# we stop recursing into subdirectories.
pred = _PackageBundler.path_filterer(dir)
for (root, _, files) in walkdir(dir)
for file in files
file = joinpath(root, file)
if !pred(file)
@debug "ignoring $file in dir size measurement"
continue
end
_walkfiles(dir; descend=pred) do filepath
if !pred(filepath)
@debug "ignoring file in _walk_appbundle_files: $(filepath)"
return nothing
end
return f(filepath)
end
end

struct _WalkFilesReturnEarly end

sz > maxsize && return sz, false
sz += filesize(file)
# Calls `f` on any non-directory in `root`.
# `descend` gets called on any directory, and if it returns false,
function _walkfiles(f::Base.Callable, root::AbstractString; descend::Base.Callable)
if !isdir(root)
error("Not a directory: $(root)")
end
directories = String[root]
while !isempty(directories)
dir = popfirst!(directories)
for subpath in readdir(dir; join=true)
if isdir(subpath)
if descend(subpath)::Bool
push!(directories, subpath)
end
else
if f(subpath) === _WalkFilesReturnEarly()
break
end
end
end
end
return sz, sz < maxsize
end

function _json_get(d::Dict, key, ::Type{T}; var::AbstractString, parse=false) where {T}
Expand Down
1 change: 1 addition & 0 deletions test/fixtures/ignorefiles/Pkg3/src/foo
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
000000000000000000000000000000000000000000000000
1 change: 1 addition & 0 deletions test/fixtures/ignorefiles/Pkg3/test/bar/test
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
000000000000000000000000000000000000000000000000
1 change: 1 addition & 0 deletions test/fixtures/ignorefiles/Pkg3/test/foo/test
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
000000000000000000000000000000000000000000000000
41 changes: 41 additions & 0 deletions test/packagebundler.jl
Original file line number Diff line number Diff line change
Expand Up @@ -225,3 +225,44 @@ end
end
@test isfile(out)
end

@testset "path_filterer" begin
@testset "subdirectory" begin
dir = joinpath(@__DIR__, "fixtures", "ignorefiles")
pred = JuliaHub._PackageBundler.path_filterer(dir)
@test pred(joinpath(dir, "Pkg3", "README.md"))

@test pred(joinpath(dir, "Pkg3", "src", "bar"))
@test !pred(joinpath(dir, "Pkg3", "src", "foo"))
@test pred(joinpath(dir, "Pkg3", "src", "fooo"))

@test !pred(joinpath(dir, "Pkg3", "test", "bar"))
@test !pred(joinpath(dir, "Pkg3", "test", "foo"))
@test pred(joinpath(dir, "Pkg3", "test", "fooo", "test"))

# Note: even though the test/foo and test/bar directories are
# excluded, the predicate function does not return false if you
# check for file within the directories.
@test pred(joinpath(dir, "Pkg3", "test", "bar", "test"))
@test pred(joinpath(dir, "Pkg3", "test", "foo", "test"))
end
@testset "toplevel" begin
dir = joinpath(@__DIR__, "fixtures", "ignorefiles", "Pkg3")
pred = JuliaHub._PackageBundler.path_filterer(dir)
@test pred(joinpath(dir, "README.md"))

@test pred(joinpath(dir, "src", "bar"))
@test !pred(joinpath(dir, "src", "foo"))
@test pred(joinpath(dir, "src", "fooo"))

@test !pred(joinpath(dir, "test", "bar"))
@test !pred(joinpath(dir, "test", "foo"))
@test pred(joinpath(dir, "test", "fooo", "test"))

# Note: even though the test/foo and test/bar directories are
# excluded, the predicate function does not return false if you
# check for file within the directories.
@test pred(joinpath(dir, "test", "bar", "test"))
@test pred(joinpath(dir, "test", "foo", "test"))
end
end
28 changes: 28 additions & 0 deletions test/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,31 @@ end
Dict("_id_missing" => "123e4567-e89b-12d3-a456-426614174000"), "id", UUIDs.UUID
)
end

@testset "_max_appbundle_dir_size" begin
# We check here that the `.juliabundleignore` is honored by making
# sure that the calculated total file size of the Pkg3/ directory is
dir = joinpath(@__DIR__, "fixtures", "ignorefiles", "Pkg3")

appbundle_files = String[]
JuliaHub._walk_appbundle_files(dir) do filepath
push!(appbundle_files, relpath(filepath, dir))
end
@test sort(appbundle_files) == [
".gitignore", ".juliabundleignore", "Project.toml", "README.md",
joinpath("src", "Pkg3.jl"), joinpath("src", "bar"), joinpath("src", "fooo"),
joinpath("test", "fooo", "test"), joinpath("test", "runtests.jl"),
]

# The files that are not meant to be included in the /Pkg3/ bundle here are
# all 50 byte files. Should they should show up in the total size here.
#
# Note: on windows, the files may be checked out with different line endings,
# so the total file size may be slightly different.
sz, sz_is_low = JuliaHub._max_appbundle_dir_size(dir)
@test sz_is_low
@test sz in (Sys.iswindows() ? (405, 432) : (405,))

_, sz_low = JuliaHub._max_appbundle_dir_size(dir; maxsize=200)
@test !sz_low
end