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 367eb13
Show file tree
Hide file tree
Showing 8 changed files with 201 additions and 48 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning].
- (breaking change) GitHub PR template is now part of the minimal options (#308)
- (breaking change) TestOnPRs.yml is now part of the minimal options (#312)
- (breaking change) 90-contributing.md and 91-developer.md have moved from minimal to recommended. If you use the minimal option, then these files will be removed (#313)
- (breaking change) `generate` does not work on existing folders anymore. The function `apply` was created to handle that case (#301)

### Removed

Expand Down
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
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ then:

```julia-repl
julia> using BestieTemplate
julia> BestieTemplate.generate("YourPackage.jl")
julia> BestieTemplate.generate("path/to/YourNewPackage.jl")
julia> # or BestieTemplate.apply("path/to/YourExistingPackage.jl")
```

please note that `"YourPackage.jl"` can either be a fresh new package or an existing one.
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.
11 changes: 6 additions & 5 deletions docs/src/10-full-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,11 @@ To apply the template to an existing package, you can do the following:

!!! warning "git"
This assumes that you already use git on that package and the your working directory is clean.
It will fail otherwise.

```julia-repl
julia> using BestieTemplate
julia> BestieTemplate.generate("full/path/to/YourPackage.jl")
julia> BestieTemplate.apply("full/path/to/YourPackage.jl")
```

This will look for a file `Project.toml` at the root of the given path and use the information there to guess some of the answers.
Expand All @@ -126,14 +127,14 @@ Currently, we guess the `PackageName`, `PackageUUID` from the `name` and `uuid`
!!! tip "Overwrite"
You will be asked whether to overwrite existing files or not. Since you are using `git`, you can try it out and reset if you don't like the result.

If you don't like the result, or want to override the answers, you can run the `generate` function with additional arguments:
If you don't like the result, or want to override the answers, you can run the `apply` function with additional arguments:

```julia-repl
julia> data = Dict("AuthorName" => "Bob", "AuthorEmail" => "[email protected]")
julia> BestieTemplate.generate("full/path/to/YourPackage.jl", data)
julia> BestieTemplate.apply("full/path/to/YourPackage.jl", data)
```

See the full docstring for [`BestieTemplate.generate`](@ref) for more information.
See the full docstring for [`BestieTemplate.apply`](@ref) for more information.

You will most likely have conflicts when you apply the template.
Whenever a conflict appears, you will need to decide on whether to accept or reject the new changes.
Expand Down Expand Up @@ -268,7 +269,7 @@ The next time that the tests are run, the coverage page will be updated, and the

!!! warning "Copier.yml is work in progress"
This option is not selected by default because it is a work in progress.
If you want to use it, you have to pass the key `"AddCopierCI" => true` to the `data` argument of `generate`, or select "Ask me" when deciding how to answer the optional questions.
If you want to use it, you have to pass the key `"AddCopierCI" => true` to the `data` argument of `generate` or `apply`, or select "Ask me" when deciding how to answer the optional questions.

You can reapply the template in the future. This is normally a manual job, specially because normally there are conflicts.
That being said, we are experimenting with having a workflow that automatically checks whether there are updates to the template and reapplies it.
Expand Down
3 changes: 2 additions & 1 deletion docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ However, if you kinda know what you need to do, this is the TL;DR:

```julia-repl
julia> using BestieTemplate
julia> BestieTemplate.generate("YourPackage.jl")
julia> BestieTemplate.generate("path/to/YourNewPackage.jl")
julia> # or BestieTemplate.apply("path/to/YourExistingPackage.jl")
```

I really recommend checking the [full guide](@ref full_guide), though.
Expand Down
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.")
end
if !isdir(joinpath(dst_path, ".git"))
error("""No folder $dst_path/.git found. Are you using git on the project?
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.apply("some_folder1")
end

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

0 comments on commit 367eb13

Please sign in to comment.