Skip to content

Commit

Permalink
Create an apply function and change generate
Browse files Browse the repository at this point in the history
BestieTemplate.generate only works for new folders now.
BestieTemplate.apply was created to handle existing folders.

Closes #301

Breaking change: generate stops working for existing packages and apply
should be used instead.
  • Loading branch information
abelsiqueira committed Jul 8, 2024
1 parent 56ef2bb commit cc943e7
Show file tree
Hide file tree
Showing 4 changed files with 190 additions and 41 deletions.
1 change: 1 addition & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ CondaPkg = "992eb4ea-22a4-4c89-a5bb-47a3300528ab"
PythonCall = "6099a3de-0909-46bc-b1f4-468b9a2dfc0d"
TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76"
UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
YAML = "ddb6d928-2868-570f-bddf-ab3f9cf99eb6"

[compat]
CondaPkg = "0.2"
Expand Down
14 changes: 1 addition & 13 deletions copier.yml
Original file line number Diff line number Diff line change
Expand Up @@ -130,16 +130,4 @@ _skip_if_exists:
_subdirectory: template

_message_after_copy: |
Your package {{ PackageName }}.jl has been created successfully! 🎉
Next steps: Create git repository and push to Github.
$ cd <path>
$ git init
$ git add .
$ pre-commit run -a # Try to fix possible pre-commit issues (failures are expected)
$ git add .
$ git commit -m "First commit"
$ pre-commit install # Future commits can't be directly to main unless you use -n
Create a repo on GitHub and push your code to it.
Read the full guide: https://abelsiqueira.com/BestieTemplate.jl/stable/10-full-guide
All went well on copier's side. Going back to BestieTemplate.
129 changes: 114 additions & 15 deletions src/BestieTemplate.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,40 @@
This package defines a copier template for Julia packages and a basic user interface aroud copier
to use it from Julia.
The main functions are: [`generate`](@ref) and [`update`](@ref).
The main functions are: [`generate`](@ref), [`apply`](@ref), and [`update`](@ref).
"""
module BestieTemplate

include("Copier.jl")

using TOML: TOML
using YAML: YAML

"""
_copy(src_path, dst_path, data; kwargs...)
Internal function to run common code for new or existing packages.
"""
function _copy(src_path, dst_path, data; kwargs...)
# If the PackageName was not given or guessed from the Project.toml, use the sanitized path
if !haskey(data, "PackageName")
package_name = _sanitize_package_name(dst_path)
if package_name != ""
@info "Using sanitized path $package_name as package name"
data["PackageName"] = package_name
end
end
Copier.copy(src_path, dst_path, data; kwargs...)
end

"""
generate(dst_path[, data]; kwargs...)
generate(src_path, dst_path[, data]; true, kwargs...)
Generates a new project at the path `dst_path` using the template.
If the `dst_path` already exists, this will throw an error, unless `dst_path = "."`.
For existing packages, use `BestieTemplate.apply` instead.
Runs the `copy` command of [copier](https://github.com/copier-org/copier) with the BestieTemplate template.
If `src_path` is not informed, the GitHub URL of BestieTemplate.jl is used.
Expand All @@ -27,7 +49,67 @@ The `data` argument is a dictionary of answers (values) to questions (keys) that
The other keyword arguments are passed directly to the internal [`Copier.copy`](@ref).
"""
function generate(src_path, dst_path, data::Dict = Dict(); warn_existing_pkg = true, kwargs...)
function generate(src_path, dst_path, data::Dict = Dict(); kwargs...)
if dst_path != "." && isdir(dst_path) && length(readdir(dst_path)) > 0
error("$dst_path already exists. For existing packages, use `BestieTemplate.apply` instead.")
end

_copy(src_path, dst_path, data; kwargs...)

data = YAML.load_file(joinpath(dst_path, ".copier-answers.yml"))
package_name = data["PackageName"]
bestie_version = data["_commit"]

println("""Your package $package_name.jl has been created successfully! 🎉
Next steps: Create git repository and push to Github.
\$ cd <path>
\$ git init
\$ git add .
\$ pre-commit run -a # Try to fix possible pre-commit issues (failures are expected)
\$ git add .
\$ git commit -m "Generate repo with BestieTemplate $bestie_version"
\$ pre-commit install # Future commits can't be directly to main unless you use -n
Create a repo on GitHub and push your code to it.
Read the full guide: https://abelsiqueira.com/BestieTemplate.jl/stable/10-full-guide
""")

return nothing
end

function generate(dst_path, data::Dict = Dict(); kwargs...)
generate("https://github.com/abelsiqueira/BestieTemplate.jl", dst_path, data; kwargs...)
end

"""
apply(dst_path[, data]; kwargs...)
apply(src_path, dst_path[, data]; true, kwargs...)
Applies the template to an existing project at path ``dst_path``.
If the `dst_path` does not exist, this will throw an error.
For new packages, use `BestieTemplate.generate` instead.
Runs the `copy` command of [copier](https://github.com/copier-org/copier) with the BestieTemplate template.
If `src_path` is not informed, the GitHub URL of BestieTemplate.jl is used.
The `data` argument is a dictionary of answers (values) to questions (keys) that can be used to bypass some of the interactive questions.
## Keyword arguments
- `warn_existing_pkg::Boolean = true`: Whether to check if you actually meant `update`. If you run `apply` and the `dst_path` contains a `.copier-answers.yml`, it means that the copy was already made, so you might have means `update` instead. When `true`, a warning is shown and execution is stopped.
The other keyword arguments are passed directly to the internal [`Copier.copy`](@ref).
"""
function apply(src_path, dst_path, data::Dict = Dict(); warn_existing_pkg = true, kwargs...)
if !isdir(dst_path)
error("$dst_path does not exist. For new packages, use `BestieTemplate.generate` instead.")

Check warning on line 106 in src/BestieTemplate.jl

View check run for this annotation

Codecov / codecov/patch

src/BestieTemplate.jl#L106

Added line #L106 was not covered by tests
end
if !isdir(joinpath(dst_path, ".git"))
error("""No folder $dst_path/.git found. Are you using git on the project?

Check warning on line 109 in src/BestieTemplate.jl

View check run for this annotation

Codecov / codecov/patch

src/BestieTemplate.jl#L109

Added line #L109 was not covered by tests
To apply to existing packages, git is required to avoid data loss.""")
end

if warn_existing_pkg && isfile(joinpath(dst_path, ".copier-answers.yml"))
@warn """There already exists a `.copier-answers.yml` file in the destination path.
You might have meant to use `BestieTemplate.update` instead, which only fetches the non-applying updates.
Expand All @@ -37,8 +119,8 @@ function generate(src_path, dst_path, data::Dict = Dict(); warn_existing_pkg = t
return nothing
end

# If there are answers in the destionation path, skip guessing the answers
if !isfile(joinpath(dst_path, ".copier-answers")) && isdir(dst_path)
# If there are answers in the destination path, skip guessing the answers
if !isfile(joinpath(dst_path, ".copier-answers"))
existing_data = _read_data_from_existing_path(dst_path)
for (key, value) in existing_data
@info "Inferred $key=$value from destination path"
Expand All @@ -48,21 +130,38 @@ function generate(src_path, dst_path, data::Dict = Dict(); warn_existing_pkg = t
end
data = merge(existing_data, data)
end
# If the PackageName was not given or guessed from the Project.toml, use the sanitized path
if !haskey(data, "PackageName")
package_name = _sanitize_package_name(dst_path)
if package_name != ""
@info "Using sanitized path $package_name as package name"
data["PackageName"] = package_name
end
end
Copier.copy(src_path, dst_path, data; kwargs...)

_copy(src_path, dst_path, data; kwargs...)

data = YAML.load_file(joinpath(dst_path, ".copier-answers.yml"))
package_name = data["PackageName"]
bestie_version = data["_commit"]

println("""BestieTemplate was applied to $package_name.jl! 🎉
Next steps:
Review the modifications.
In particular README.md and docs/src/index.md tend to be heavily edited.
\$ git switch -c apply-bestie # If you haven't created a branch
\$ git add .
\$ pre-commit run -a # Try to fix possible pre-commit issues (failures are expected)
\$ pre-commit run -a # Again. Now failures should not happen
\$ gid add .
\$ git commit -m "Apply BestieTemplate $bestie_version"
\$ pre-commit install
\$ git push -u origin apply-bestie
Go to GitHub and create a Pull Request from apply-bestie to main.
Continue on the full guide: https://abelsiqueira.com/BestieTemplate.jl/stable/10-full-guide
""")

return nothing
end

function generate(dst_path, data::Dict = Dict(); kwargs...)
generate("https://github.com/abelsiqueira/BestieTemplate.jl", dst_path, data; kwargs...)
function apply(dst_path, data::Dict = Dict(); kwargs...)
apply("https://github.com/abelsiqueira/BestieTemplate.jl", dst_path, data; kwargs...)
end

"""
Expand Down
87 changes: 74 additions & 13 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ template_options = Dict(
"AddCopierCI" => false,
)

function _git_setup()
run(`git init`)
run(`git add .`)
run(`git config user.name "Test"`)
run(`git config user.email "[email protected]"`)
run(`git commit -q -m "First commit"`)
end

function test_diff_dir(dir1, dir2)
ignore(line) = startswith("_commit")(line) || startswith("_src_path")(line)
@testset "$(basename(dir1)) vs $(basename(dir2))" begin
Expand Down Expand Up @@ -115,22 +123,14 @@ end
mktempdir(TMPDIR; prefix = "cli_") do dir_copier_cli
run(`copier copy --defaults --quiet $min_bash_args $template_url $dir_copier_cli`)
cd(dir_copier_cli) do
run(`git init`)
run(`git add .`)
run(`git config user.name "Test"`)
run(`git config user.email "[email protected]"`)
run(`git commit -q -m "First commit"`)
_git_setup()
end
run(`copier update --defaults --quiet $bash_args $dir_copier_cli`)

mktempdir(TMPDIR; prefix = "update_") do tmpdir
BestieTemplate.generate(tmpdir, template_minimum_options; defaults = true, quiet = true)
cd(tmpdir) do
run(`git init`)
run(`git add .`)
run(`git config user.name "Test"`)
run(`git config user.email "[email protected]"`)
run(`git commit -q -m "First commit"`)
_git_setup()
BestieTemplate.update(template_options; defaults = true, quiet = true)
end

Expand All @@ -142,8 +142,51 @@ end
@testset "Test that BestieTemplate.generate warns and exits for existing copy" begin
mktempdir(TMPDIR; prefix = "cli_") do dir_copier_cli
run(`copier copy --vcs-ref HEAD --quiet $bash_args $template_url $dir_copier_cli`)
cd(dir_copier_cli) do
_git_setup()
end

@test_logs (:warn,) BestieTemplate.generate(dir_copier_cli; quiet = true)
@test_logs (:warn,) BestieTemplate.apply(dir_copier_cli; quiet = true)
end
end

@testset "Test that generate fails for existing non-empty paths" begin
mktempdir(TMPDIR) do dir
cd(dir) do
@testset "It fails if the dst_path exists and is non-empty" begin
mkdir("some_folder1")
open(joinpath("some_folder1", "README.md"), "w") do io
println(io, "Hi")
end
@test_throws Exception BestieTemplate.generate("some_folder1")
end

@testset "It works if the dst_path is ." begin
mkdir("some_folder2")
cd("some_folder2") do
# Should not throw
BestieTemplate.generate(
template_path,
".",
template_options;
quiet = true,
vcs_ref = "HEAD",
)
end
end

@testset "It works if the dst_path exists but is empty" begin
mkdir("some_folder3")
# Should not throw
BestieTemplate.generate(
template_path,
"some_folder3",
template_options;
quiet = true,
vcs_ref = "HEAD",
)
end
end
end
end

Expand Down Expand Up @@ -195,11 +238,14 @@ end
end
end

@testset "Test generating the template on an existing project" begin
@testset "Test applying the template on an existing project" begin
mktempdir(TMPDIR; prefix = "existing_") do dir_existing
cd(dir_existing) do
Pkg.generate("NewPkg")
BestieTemplate.generate(
cd("NewPkg") do
_git_setup()
end
BestieTemplate.apply(
template_path,
"NewPkg/",
Dict("AuthorName" => "T. Esther", "PackageOwner" => "test");
Expand Down Expand Up @@ -241,4 +287,19 @@ end
end
end
end

@testset "Test input validation of apply" begin
mktempdir(TMPDIR) do dir
cd(dir) do
@testset "It fails if the dst_path does not exist" begin
@test_throws Exception BestieTemplate.generate("some_folder1")
end

@testset "It fails if the dst_path exists but does not contains .git" begin
mkdir("some_folder2")
@test_throws Exception BestieTemplate.generate("some_folder2")
end
end
end
end
end

0 comments on commit cc943e7

Please sign in to comment.