From d53fb0b1191c19ca71851c309deded3da59d8516 Mon Sep 17 00:00:00 2001 From: Chris de Graaf Date: Tue, 24 Sep 2019 15:34:24 +0700 Subject: [PATCH] More docs! --- docs/src/developer.md | 112 +++++++++++++++++++++++++++++++++++------- docs/src/index.md | 2 + src/plugins/git.jl | 5 +- 3 files changed, 98 insertions(+), 21 deletions(-) diff --git a/docs/src/developer.md b/docs/src/developer.md index ca3fbaa..04b0b6a 100644 --- a/docs/src/developer.md +++ b/docs/src/developer.md @@ -17,10 +17,47 @@ Plugin BasicPlugin ``` +## Package Generation Pipeline + +The package generation process looks basically like this: + +``` +- create directory for the package +- for each plugin, ordered by priority: + - run plugin prehook +- for each plugin, ordered by priority: + - run plugin hook +- for each plugin, ordered by priority: + - run plugin posthook +``` + +That's it! +As you can tell, plugins play a central role in setting up a package. + +The three main entrypoints for plugins to do work are the [`prehook`](@ref), the [`hook`](@ref), and the [`posthook`](@ref). +As the names might imply, they basically mean "before the main stage", "the main stage", and "after the main stage", respectively. + +Each stage is basically identical, since the functions take the exact same arguments. +However, the multiple stages allow us to depend on artifacts of the previous stages. +For example, the [`Git`](@ref) plugin uses [`posthook`](@ref) to commit all generated files, but it wouldn't make sense to do that before the files are generated. + +But what about dependencies within the same stage? +In this case, we have [`priority`](@ref) to define which plugins go when. +The [`Git`](@ref) plugin also uses this function to lower its priority, so that even if other plugins generate files in their posthooks, they still get committed. + +```@docs +prehook +hook +posthook +priority +``` + ## `Plugin` Walkthrough Concrete types that subtype [`Plugin`](@ref) directly are free to do almost anything. -To understand how they're implemented, let's look at a simplified version of [`Documenter`](@ref): +To understand how they're implemented, let's look at simplified versions of two plugins: [`Documenter`](@ref) to explore templating, and [`Git`](@ref) to further clarify the multi-stage pipeline. + +### Example: `Documenter` ```julia @with_kw_noshow struct Documenter <: Plugin @@ -85,6 +122,9 @@ badges Badge ``` +These two functions, [`gitignore`](@ref) and [`badges`](@ref), are currently the only "special" functions for cross-plugin interactions. +In other cases, you can still access the [`Template`](@ref)'s plugins to depend on the presence/properties of other plugins, although that's less powerful. + Third, we implement [`view`](@ref), which is used to fill placeholders in badges and rendered files. ```@docs @@ -92,17 +132,7 @@ view ``` Finally, we implement [`hook`](@ref), which is the real workhorse for the plugin. - -TODO prehook and posthook in examples -TODO priority - -```@docs -prehook -hook -posthook -``` - -Inside of this function, we call a few more functions, which help us with text templating. +Inside of this function, we generate a couple of files with the help of a few more text templating functions. ```@docs render_file @@ -112,10 +142,51 @@ combined_view tags ``` -TODO more +For more information on text templating, see the [`BasicPlugin` Walkthrough](@ref) and the section on [Custom Template Files](@ref). + +### Example: `Git` + +``` +struct Git <: Plugin end + +priority(::Git, ::typeof(posthook)) = 5 + +function prehook(::Git, t::Template, pkg_dir::AbstractString) + LibGit2.with(LibGit2.init(pkg_dir)) do repo + LibGit2.commit(repo, "Initial commit") + pkg = basename(pkg_dir) + url = "https://$(t.host)/$(t.user)/$pkg.jl" + close(GitRemote(repo, "origin", url)) + end +end + +function hook(::Git, t::Template, pkg_dir::AbstractString) + ignore = mapreduce(gitignore, append!, t.plugins) + unique!(sort!(ignore)) + gen_file(joinpath(pkg_dir, ".gitignore"), join(ignore, "\n")) +end + +function posthook(::Git, ::Template, pkg_dir::AbstractString) + LibGit2.with(GitRepo(pkg_dir)) do repo + LibGit2.add!(repo, ".") + LibGit2.commit(repo, "Files generated by PkgTemplates") + end +end +``` + +As previously mentioned, we use [`priority`](@ref) to make sure that we commit all generated files. + +Then, all three hooks are implemented: + +- [`prehook`](@ref) creates the Git repository for the package +- [`hook`](@ref) generates the `.gitignore` file, using the special [`gitignore`](@ref) function +- [`posthook`](@ref) adds and commits all generated files + +Hopefully, this demonstrates the level of control you have over the package generation process when developing plugins. ## `BasicPlugin` Walkthrough +Most of the time, you don't really need all of the control that we showed off above. Plugins that subtype [`BasicPlugin`](@ref) perform a much more limited task. In general, they just generate one templated file. @@ -156,13 +227,13 @@ We briefly saw this function earlier, but in this case it's necessary to change To see why, it might help to see the template file in its entirety: ``` -@misc{<>.jl, - author = {<>}, - title = {<>.jl}, - url = {<>}, +@misc{<<&PKG>>.jl, + author = {<<&AUTHORS>>}, + title = {<<&PKG>>.jl}, + url = {<<&URL>>}, version = {v0.1.0}, - year = {<>}, - month = {<>} + year = {<<&YEAR>>}, + month = {<<&MONTH>>} } ``` @@ -215,6 +286,9 @@ function hook(p::Tests, t::Template, pkg_dir::AbstractString) end ``` +There is also a default [`prehook`](@ref) implementation for [`BasicPlugin`](@ref)s, which checks that the plugin's [`source`](@ref) file exists, and throws an `ArgumentError` otherwise. +If you want to extend the prehook but keep the file existence check, use the `invoke` method as described above. + For more examples, see the plugins in the [Continuous Integration (CI)](@ref) and [Code Coverage](@ref) sections. ## Miscellaneous Tips diff --git a/docs/src/index.md b/docs/src/index.md index 9b99699..54238d6 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -13,6 +13,8 @@ But it also refers to "template files" and "text templating", which are plaintex These concepts should be familiar if you've used [Jinja](https://palletsprojects.com/p/jinja) or [Mustache](https://mustache.github.io) (Mustache is the particular flavour used by PkgTemplates, via [Mustache.jl](https://github.com/jverzani/Mustache.jl)). +Please keep the difference between these two things in mind when reading this documentation! + ### Documentation If you're looking to **create new packages**, see the [User Guide](user.md). diff --git a/src/plugins/git.jl b/src/plugins/git.jl index d9d93e8..ba1b3e2 100644 --- a/src/plugins/git.jl +++ b/src/plugins/git.jl @@ -59,8 +59,8 @@ function hook(p::Git, t::Template, pkg_dir::AbstractString) gen_file(joinpath(pkg_dir, ".gitignore"), join(ignore, "\n")) end -# Commit the files -function posthook(p::Git, t::Template, pkg_dir::AbstractString) +# Commit the files. +function posthook(p::Git, ::Template, pkg_dir::AbstractString) # Ensure that the manifest exists if it's going to be committed. manifest = joinpath(pkg_dir, "Manifest.toml") if p.manifest && !isfile(manifest) @@ -71,6 +71,7 @@ function posthook(p::Git, t::Template, pkg_dir::AbstractString) LibGit2.with(GitRepo(pkg_dir)) do repo LibGit2.add!(repo, ".") msg = "Files generated by PkgTemplates" + # TODO: Newer versions of Julia will not have Pkg.installed. installed = Pkg.installed() if haskey(installed, "PkgTemplates") ver = string(installed["PkgTemplates"])