Skip to content

Commit b02a4b4

Browse files
fix: allow init in existing directories (#17)
1 parent a5d4a00 commit b02a4b4

3 files changed

Lines changed: 205 additions & 5 deletions

File tree

src/PkgREPL.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,9 @@ function _define_specs()
5454
5555
Initialize a new Pumas product installation at the provided path.
5656
Use `.` for the current path. The path cannot contain a
57-
`Project.toml` or `Manifest.toml` file. When no path is provided
58-
then a global environment is created.
57+
`Project.toml`, `Manifest.toml`, or `PackageBundler.toml` file
58+
(but can contain other files, which will be preserved). When no
59+
path is provided then a global environment is created.
5960
6061
After running `init` you can then start using the product via the
6162
custom `juliaup` channel that is added, for example:

src/PumasProductManager.jl

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,11 @@ function init(product::String, path::Union{String,Nothing} = nothing)
131131
error("path `$path` already contains a `Project.toml` file.")
132132
isfile(joinpath(path, "Manifest.toml")) &&
133133
error("path `$path` already contains a `Manifest.toml` file.")
134+
isfile(joinpath(path, "PackageBundler.toml")) &&
135+
error("path `$path` already contains a `PackageBundler.toml` file.")
136+
if !isempty(readdir(path))
137+
@info "initializing in existing directory with files. Project.toml, Manifest.toml, and PackageBundler.toml will be created."
138+
end
134139
else
135140
mkpath(path)
136141
end
@@ -144,7 +149,6 @@ function install(env::AbstractString, dst::AbstractString; force::Bool = false)
144149
rm(dst; force = true, recursive = true)
145150
end
146151
mkpath(dst)
147-
isempty(readdir(dst)) || error("directory must be empty")
148152

149153
# Find the repo paths to all the bundled deps required by this environment.
150154
# They are later passed to a `Pkg.add` call run in the environment's
@@ -184,7 +188,17 @@ function install(env::AbstractString, dst::AbstractString; force::Bool = false)
184188
_link_juliaup_channel(env, juliaup_config, channel)
185189

186190
@info "finalizing product initialization."
187-
cp(dir, dst; force = true)
191+
# Copy individual files to preserve existing files in the directory
192+
for (root, _, files) in walkdir(dir)
193+
for file in files
194+
src = joinpath(root, file)
195+
content = read(src, String)
196+
relfile = relpath(src, dir)
197+
_dst = joinpath(dst, relfile)
198+
mkpath(dirname(_dst))
199+
write(_dst, content)
200+
end
201+
end
188202
end
189203
end
190204

test/runtests.jl

Lines changed: 186 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,191 @@ using Test
2121
end
2222
end
2323

24+
@testset "Non-empty directory initialization" begin
25+
mktempdir() do dir
26+
cd(dir) do
27+
# Test initialization in directory with existing files
28+
test_version = "Pumas@2.7.0"
29+
test_dir = "test_non_empty"
30+
31+
# Create directory with existing files
32+
mkpath(test_dir)
33+
existing_file = joinpath(test_dir, "existing_file.txt")
34+
write(existing_file, "This file should be preserved")
35+
existing_data = joinpath(test_dir, "data.csv")
36+
write(existing_data, "col1,col2\n1,2\n3,4")
37+
38+
# Create subdirectory with files
39+
mkpath(joinpath(test_dir, "subdir"))
40+
subdir_file = joinpath(test_dir, "subdir", "nested.jl")
41+
write(subdir_file, "# Nested Julia file")
42+
43+
# Initialize Pumas in non-empty directory
44+
withenv("JULIA_PKG_PRECOMPILE_AUTO" => "0") do
45+
PumasProductManager.init(test_version, test_dir)
46+
end
47+
48+
# Verify Pumas files were created
49+
@test isfile(joinpath(test_dir, "Project.toml"))
50+
@test isfile(joinpath(test_dir, "Manifest.toml"))
51+
@test isfile(joinpath(test_dir, "PackageBundler.toml"))
52+
53+
# Verify existing files are preserved
54+
@test isfile(existing_file)
55+
@test read(existing_file, String) == "This file should be preserved"
56+
@test isfile(existing_data)
57+
@test read(existing_data, String) == "col1,col2\n1,2\n3,4"
58+
@test isfile(subdir_file)
59+
@test read(subdir_file, String) == "# Nested Julia file"
60+
61+
# Clean up
62+
rm(test_dir; recursive=true, force=true)
63+
success(`juliaup rm $test_version`)
64+
end
65+
end
66+
end
67+
68+
@testset "Error cases for existing Project/Manifest files" begin
69+
mktempdir() do dir
70+
cd(dir) do
71+
test_version = "Pumas@2.7.0"
72+
73+
@testset "Error on existing Project.toml" begin
74+
test_dir = "test_existing_project"
75+
mkpath(test_dir)
76+
77+
# Create existing Project.toml and other files
78+
project_file = joinpath(test_dir, "Project.toml")
79+
project_content = "[deps]\nDataFrames = \"a93c6f00-e57d-5684-b7b6-d8193f3e46c0\""
80+
write(project_file, project_content)
81+
82+
# Add another file to verify it's preserved
83+
other_file = joinpath(test_dir, "existing.txt")
84+
write(other_file, "should remain")
85+
86+
# Record initial state
87+
initial_files = Set(readdir(test_dir))
88+
89+
# Should throw error
90+
@test_throws ErrorException PumasProductManager.init(test_version, test_dir)
91+
92+
# Verify no new files were created
93+
final_files = Set(readdir(test_dir))
94+
@test initial_files == final_files
95+
96+
# Verify existing files are unchanged
97+
@test read(project_file, String) == project_content
98+
@test read(other_file, String) == "should remain"
99+
100+
# Verify Pumas files were NOT created
101+
@test !isfile(joinpath(test_dir, "Manifest.toml"))
102+
@test !isfile(joinpath(test_dir, "PackageBundler.toml"))
103+
104+
# Clean up
105+
rm(test_dir; recursive=true, force=true)
106+
end
107+
108+
@testset "Error on existing Manifest.toml" begin
109+
test_dir = "test_existing_manifest"
110+
mkpath(test_dir)
111+
112+
# Create existing Manifest.toml and other files
113+
manifest_file = joinpath(test_dir, "Manifest.toml")
114+
manifest_content = "# This file is machine-generated"
115+
write(manifest_file, manifest_content)
116+
117+
# Add another file to verify it's preserved
118+
data_file = joinpath(test_dir, "data.csv")
119+
write(data_file, "a,b,c\n1,2,3")
120+
121+
# Record initial state
122+
initial_files = Set(readdir(test_dir))
123+
124+
# Should throw error
125+
@test_throws ErrorException PumasProductManager.init(test_version, test_dir)
126+
127+
# Verify no new files were created
128+
final_files = Set(readdir(test_dir))
129+
@test initial_files == final_files
130+
131+
# Verify existing files are unchanged
132+
@test read(manifest_file, String) == manifest_content
133+
@test read(data_file, String) == "a,b,c\n1,2,3"
134+
135+
# Verify Pumas files were NOT created
136+
@test !isfile(joinpath(test_dir, "Project.toml"))
137+
@test !isfile(joinpath(test_dir, "PackageBundler.toml"))
138+
139+
# Clean up
140+
rm(test_dir; recursive=true, force=true)
141+
end
142+
143+
@testset "Error on existing PackageBundler.toml" begin
144+
test_dir = "test_existing_bundler"
145+
mkpath(test_dir)
146+
147+
# Create existing PackageBundler.toml and other files
148+
bundler_file = joinpath(test_dir, "PackageBundler.toml")
149+
bundler_content = "[packages]\nstrip = [\"SomePackage\"]"
150+
write(bundler_file, bundler_content)
151+
152+
# Add another file to verify it's preserved
153+
readme_file = joinpath(test_dir, "README.md")
154+
write(readme_file, "# My Project")
155+
156+
# Record initial state
157+
initial_files = Set(readdir(test_dir))
158+
159+
# Should throw error
160+
@test_throws ErrorException PumasProductManager.init(test_version, test_dir)
161+
162+
# Verify no new files were created
163+
final_files = Set(readdir(test_dir))
164+
@test initial_files == final_files
165+
166+
# Verify existing files are unchanged
167+
@test read(bundler_file, String) == bundler_content
168+
@test read(readme_file, String) == "# My Project"
169+
170+
# Verify Pumas files were NOT created
171+
@test !isfile(joinpath(test_dir, "Project.toml"))
172+
@test !isfile(joinpath(test_dir, "Manifest.toml"))
173+
174+
# Clean up
175+
rm(test_dir; recursive=true, force=true)
176+
end
177+
178+
@testset "Error when path is a file" begin
179+
test_file = "test_file.txt"
180+
file_content = "original content"
181+
write(test_file, file_content)
182+
183+
# Record initial directory state
184+
initial_files = Set(readdir("."))
185+
186+
# Should throw error
187+
@test_throws ErrorException PumasProductManager.init(test_version, test_file)
188+
189+
# Verify no new files were created in current directory
190+
final_files = Set(readdir("."))
191+
@test initial_files == final_files
192+
193+
# Verify the file is unchanged
194+
@test isfile(test_file)
195+
@test read(test_file, String) == file_content
196+
197+
# Verify no Pumas files were created in current directory
198+
@test !isfile("Project.toml")
199+
@test !isfile("Manifest.toml")
200+
@test !isfile("PackageBundler.toml")
201+
202+
# Clean up
203+
rm(test_file; force=true)
204+
end
205+
end
206+
end
207+
end
208+
24209
try
25210
mktempdir() do dir
26211
@testset "Installation" begin
@@ -49,7 +234,7 @@ using Test
49234
end
50235

51236
for each in expected_versions
52-
product, _ = split(each, "@"; limit = 2)
237+
product, _ = split(each, "@"; limit=2)
53238
file = joinpath(@__DIR__, "$product.jl")
54239
channel = "+$each"
55240
cmd = `julia $channel $file`

0 commit comments

Comments
 (0)