Skip to content

Commit

Permalink
Turn the dependency on Git into a package extension.
Browse files Browse the repository at this point in the history
  • Loading branch information
GunnarFarneback committed Mar 16, 2024
1 parent 31fb698 commit 8893836
Show file tree
Hide file tree
Showing 11 changed files with 129 additions and 57 deletions.
12 changes: 9 additions & 3 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,30 @@ authors = ["Gunnar Farnebäck <[email protected]>"]
version = "0.6.0"

[deps]
Git = "d7ba0133-e1db-5d97-8f8c-041e4b3a1eb2"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
RegistryInstances = "2792f1a3-b283-48e8-9a74-f99dce5104f3"
RegistryTools = "d1eb7eb1-105f-429d-abf5-b0f65cb9e2c4"
TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76"
UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"

[weakdeps]
Git = "d7ba0133-e1db-5d97-8f8c-041e4b3a1eb2"

[extensions]
GitExt = ["Git"]

[compat]
CodecZlib = "0.5, 0.6, 0.7"
Git = "1.3"
Git = "1.3.1"
RegistryInstances = "0.1"
RegistryTools = "2.2"
julia = "1.6"

[extras]
CodecZlib = "944b1d66-785c-5afd-91f1-9de20f533193"
Git = "d7ba0133-e1db-5d97-8f8c-041e4b3a1eb2"
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["CodecZlib", "Pkg", "Test"]
test = ["CodecZlib", "Git", "Pkg", "Test"]
11 changes: 9 additions & 2 deletions docs/create_registry.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
The full set of arguments to `create_registry` is
```
create_registry(name_or_path, repository_url;
description, push, branch, git_config)
description, push, branch, gitconfig, custom_git)
```

## Positional Arguments
Expand Down Expand Up @@ -76,8 +76,15 @@ What branch to use for the registry in the repository. Defaults to

* Otherwise the default branch for the local `git` is used.

`git_config::Dict{<:AbstractString, <:AbstractString}`:
`gitconfig::Dict{<:AbstractString, <:AbstractString}`:

Optional configuration parameters for the `git` command. For
interactive use you most likely do not need this. Defaults to
an empty dictionary, i.e. no configuration.

`custom_git::Union{Nothing, AbstractString, Cmd}`:

By default LocalRegistry uses an external `git` command, unless the
`Git` package has been loaded, in which case a bundled `git` is used.
This keyword argument can be used for [further
customization](custom_git.md).
53 changes: 45 additions & 8 deletions docs/custom_git.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,56 @@
# Using a Custom Git

By default LocalRegistry uses the `git` binary provided as an artifact
by the Git package. If this is not working satisfactorily you can
replace it with a system or custom git by specifying the
`external_git` keyword argument.
By default LocalRegistry uses an external `git` binary available in
the system PATH.

If you do not have an external `git` installation, or if it is very
old or not working for some other reason, `LocalRegistry` provides a
package extension to obtain a `git` binary from the `Git` package.

## Bundled Git

To enable the package extension you need to install the `Git` package
in the same environment you use for running `LocalRegistry`.
```
using Pkg
Pkg.install("Git")
```

Then you also need to load (with `import` or `using`) the `Git`
package together with `LocalRegistry`. E.g.
```
using LocalRegistry, Git
```

## Further Customization

If this is not working satisfactorily you can customize the `git`
command to be used by specifying the `custom_git` keyword argument to
`create_registry` or `register`. This can either specify a path to
`git` or a `Cmd` object.

Examples:

```
register(; external_git = "git")
register(; external_git = "/usr/bin/git")
register(; external_git = `git`)
register(; external_git = `cmd /c git`)
# Use the git found in the system PATH, even if the Git package is loaded.
register(; custom_git = "git")
# Specify an absolute path.
register(; custom_git = "/usr/bin/git")
# Same as the first example but using a CMD object.
register(; custom_git = `git`)
# Call a system git via a Windows cmd wrapper.
register(; custom_git = `cmd /c git`)
# Call a bundled git via a Windows cmd wrapper.
import Git
register(; custom_git = `cmd /c $(Git.git())`)
```

## Custom Git Configuration

You can also add git configuration with the `gitconfig` keyword
argument. E.g.

Expand Down
15 changes: 11 additions & 4 deletions docs/register.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ The full set of arguments to `register` is
```
register(package = nothing;
registry, commit, push, repo, ignore_reregistration,
gitconfig, create_gitlab_mr)
gitconfig, custom_git, create_gitlab_mr)
```

although in many cases a no-argument `register()` call is sufficient.
Expand Down Expand Up @@ -121,7 +121,7 @@ informational message.

**Note: The default will likely be changed to `true` in a future update.**

`git_config::Dict{<:AbstractString, <:AbstractString}`:
`gitconfig::Dict{<:AbstractString, <:AbstractString}`:

Optional configuration parameters for the `git` command. For
interactive use you most likely do not need this. Defaults to
Expand All @@ -130,10 +130,17 @@ an empty dictionary, i.e. no configuration.
For CI purposes it can be useful, e.g. as used in the LocalRegistry
tests:
```
git_config = Dict("user.name" => "LocalRegistryTests",
"user.email" => "[email protected]")
gitconfig = Dict("user.name" => "LocalRegistryTests",
"user.email" => "[email protected]")
```

`custom_git::Union{Nothing, AbstractString, Cmd}`:

By default LocalRegistry uses an external `git` command, unless the
`Git` package has been loaded, in which case a bundled `git` is used.
This keyword argument can be used for [further
customization](custom_git.md).

`create_gitlab_mr`:

If `true`, send git push options to create a GitLab merge
Expand Down
48 changes: 27 additions & 21 deletions src/LocalRegistry.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
Create and maintain local registries for Julia packages. This package
is intended to provide a simple but manual workflow for maintaining
small local registries (private or public) without making any
assumptions about how the registry or the packages are hosted.
local registries (private or public) without making any assumptions
about how the registry or the packages are hosted.
Registry creation is done by the function `create_registry`.
Expand All @@ -18,7 +18,6 @@ using RegistryTools: RegistryTools, Compress,
using RegistryInstances: RegistryInstance, reachable_registries
using UUIDs: uuid4
import TOML
import Git

export create_registry, register

Expand All @@ -41,7 +40,7 @@ for example by being a bare repository.
create_registry(...; description = nothing, push = false,
branch = nothing, gitconfig = Dict(),
external_git = nothing)
custom_git = nothing)
* `description`: Optional description of the purpose of the registry.
* `push`: If `false`, the registry will only be prepared locally.
Expand All @@ -53,11 +52,12 @@ for example by being a bare repository.
use the upstream branch if `push` is `true` and otherwise the default
branch name configured for `git init`.
* `gitconfig`: Optional configuration parameters for the `git` command.
* `external_git`: Command or path for an external git. Defaults to nothing,
using the Git Package.
* `custom_git`: Command or path for a custom git command. Defaults to
nothing, which uses a bundled git if the Git Package has been loaded
and otherwise calls out to an external git.
"""
function create_registry(name_or_path, repo; description = nothing,
gitconfig::Dict = Dict(), external_git = nothing,
gitconfig::Dict = Dict(), custom_git = nothing,
uuid = nothing, push = false, branch = nothing)
if length(splitpath(name_or_path)) > 1
path = abspath(expanduser(name_or_path))
Expand All @@ -75,7 +75,7 @@ function create_registry(name_or_path, repo; description = nothing,
end
mkpath(path)

git = gitcmd(path, gitconfig, external_git)
git = gitcmd(path; custom_git, gitconfig)
git_repo_cloned = false

if push
Expand Down Expand Up @@ -169,7 +169,7 @@ will be used to perform the registration. In this case `push` must be
register(package; registry = nothing, commit = true, push = true,
branch = nothing, repo = nothing, ignore_reregistration = false,
gitconfig = Dict(), external_git = nothing,
gitconfig = Dict(), custom_git = nothing,
create_gitlab_mr = false)
* `registry`: Name, path, or URL of registry.
Expand All @@ -179,7 +179,9 @@ will be used to perform the registration. In this case `push` must be
* `repo`: Specify the package repository explicitly. Otherwise looked up as the `git remote` of the package the first time it is registered.
* `ignore_reregistration`: If `true`, do not raise an error if a version has already been registered (with different content), only an informational message. Defaults to `false` but may be changed to `true` in the future.
* `gitconfig`: Optional configuration parameters for the `git` command.
* `external_git`: Command or path for an external git. Defaults to nothing, using the Git package.
* `custom_git`: Command or path for a custom git command. Defaults to
nothing, which uses a bundled git if the Git Package has been loaded
and otherwise calls out to an external git.
* `create_gitlab_mr`: If `true` sends git push options to create a GitLab merge request. Requires `commit` and `push` to be true.
"""
function register(package::Union{Nothing, Module, AbstractString} = nothing;
Expand All @@ -195,7 +197,7 @@ end
function do_register(package, registry;
commit = true, push = true, branch = nothing,
repo = nothing, ignore_reregistration = false,
gitconfig::Dict = Dict(), external_git = nothing,
gitconfig::Dict = Dict(), custom_git = nothing,
create_gitlab_mr = false)
# Find and read the `Project.toml` for the package. First look for
# the alternative `JuliaProject.toml`.
Expand Down Expand Up @@ -223,14 +225,14 @@ function do_register(package, registry;

# If the package directory is dirty, a different version could be
# present in Project.toml.
package_git = gitcmd(package_path, gitconfig, external_git)
package_git = gitcmd(package_path; custom_git, gitconfig)
if is_dirty(package_git)
error("Package directory is dirty. Stash or commit files.")
end

registry_path_or_url = find_registry_path(registry, pkg)
registry_path, registry_git, is_temporary =
check_git_registry(registry_path_or_url, gitconfig, external_git)
check_git_registry(registry_path_or_url, gitconfig, custom_git)
if is_temporary && (!commit || !push)
error("Need to use a temporary git clone of the registry, but commit or push is set to false.")
end
Expand Down Expand Up @@ -496,7 +498,7 @@ end
#
# Either way, LocalRegistry must have a git clone to work with, so if
# the registry is not in that form, make a temporary git clone.
function check_git_registry(registry_path_or_url, gitconfig, external_git)
function check_git_registry(registry_path_or_url, gitconfig, custom_git)
temporary_repo = true
if !ispath(registry_path_or_url)
# URL given. Use this to make a git clone.
Expand All @@ -523,7 +525,7 @@ function check_git_registry(registry_path_or_url, gitconfig, external_git)
path = registry_path_or_url
end

git = gitcmd(path, gitconfig, external_git)
git = gitcmd(path; custom_git, gitconfig)

if temporary_repo
try
Expand Down Expand Up @@ -629,17 +631,21 @@ function gitlab(branch, pkg, new_package, repo, commit)
return branch, push_options
end

function gitcmd(path::AbstractString, gitconfig::Dict, external_git = nothing)
args = ["-C", path]
function gitcmd(path::AbstractString = ""; custom_git = nothing,
gitconfig::Dict = Dict{String, String}())
args = String[]
isempty(path) || push!(args, "-C", path)
for (k, v) in gitconfig
push!(args, "-c")
push!(args, "$k=$v")
push!(args, "-c", "$k=$v")
end
git = _gitcmd(external_git)
git = _gitcmd(custom_git)
return `$git $args`
end

_gitcmd(::Nothing) = Git.git()
# A specialized method `_gitcmd(::Nothing, ::Val{:git})` is defined
# by the Git package extension.
_gitcmd(::Nothing) = _gitcmd(nothing, Val(:git))
_gitcmd(::Nothing, ::Any) = `git`
_gitcmd(path::AbstractString) = `$path`
_gitcmd(cmd::Cmd) = cmd

Expand Down
4 changes: 2 additions & 2 deletions test/check_git_registry.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ with_testdir() do testdir
@test !is_temp

mkpath(upstream_dir)
upstream_git = gitcmd(upstream_dir, TEST_GITCONFIG)
upstream_git = gitcmd(upstream_dir, gitconfig = TEST_GITCONFIG)
run(`$(upstream_git) clone -q --bare $(registry_dir) .`)

reg_path, reg_git, is_temp = check_git_registry(upstream_url,
Expand All @@ -24,7 +24,7 @@ with_testdir() do testdir
@test is_temp

# Emulate a registry downloaded from a package server.
downstream_git = gitcmd(registry_dir, TEST_GITCONFIG)
downstream_git = gitcmd(registry_dir, gitconfig = TEST_GITCONFIG)
tree_hash = readchomp(`$(downstream_git) rev-parse HEAD:`)
rm(joinpath(registry_dir, ".git"), recursive = true)
write(joinpath(registry_dir, ".tree_info.toml"),
Expand Down
6 changes: 3 additions & 3 deletions test/create_registry.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
with_testdir() do testdir
upstream_dir = joinpath(testdir, "upstream_registry")
mkpath(upstream_dir)
upstream_git = gitcmd(upstream_dir, TEST_GITCONFIG)
upstream_git = gitcmd(upstream_dir, gitconfig = TEST_GITCONFIG)
# With a sufficiently new git (probably 2.28) this test could be
# done much more easily, basically just
# run(`$upstream_git init -q --bare --initial_branch=some_unusual_branch_name`)
Expand All @@ -16,7 +16,7 @@ with_testdir() do testdir
write(joinpath(upstream_dir, "HEAD"), "ref: refs/heads/some_unusual_branch_name")
tmp_downstream_dir = joinpath(testdir, "tmp_downstream")
mkpath(tmp_downstream_dir)
tmp_git = gitcmd(tmp_downstream_dir, TEST_GITCONFIG)
tmp_git = gitcmd(tmp_downstream_dir, gitconfig = TEST_GITCONFIG)
run(`$tmp_git clone file://$(upstream_dir) .`)
run(`$tmp_git checkout -b some_unusual_branch_name`)
write(joinpath(tmp_downstream_dir, ".gitignore"), "Manifest.toml")
Expand All @@ -40,7 +40,7 @@ end
with_testdir() do testdir
upstream_dir = joinpath(testdir, "upstream_registry")
mkpath(upstream_dir)
upstream_git = gitcmd(upstream_dir, TEST_GITCONFIG)
upstream_git = gitcmd(upstream_dir, gitconfig = TEST_GITCONFIG)
run(`$upstream_git init -q --bare`)
downstream_dir = joinpath(testdir, "downstream_registry")
create_registry(downstream_dir, "file://$(upstream_dir)", push = true,
Expand Down
15 changes: 8 additions & 7 deletions test/gitcmd.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,29 @@ artifact_exec_path = readchomp(`$(Git.git()) --exec-path`)
# `gitcmd` - that is covered well enough by all the other tests.
#
# The focus here is only to verify that the artifact git (from the Git
# package) is functional and that the external git option works as
# package) is functional and that the custom git option works as
# intended.
mktempdir() do tmp_dir
git = gitcmd(tmp_dir, Dict(), nothing)
git = gitcmd(tmp_dir, custom_git = nothing, gitconfig = Dict())
@test readchomp(`$git --exec-path`) == artifact_exec_path
git = gitcmd(tmp_dir, Dict(), Git.git())
git = gitcmd(tmp_dir, custom_git = Git.git(), gitconfig = Dict())
@test readchomp(`$git --exec-path`) == artifact_exec_path
# We have to skip the rest of the tests if there's no external git
# available.
if external_git_available
external_exec_path = readchomp(`git --exec-path`)
git = gitcmd(tmp_dir, Dict(), "git")
git = gitcmd(tmp_dir, custom_git = "git", gitconfig = Dict())
@test readchomp(`$git --exec-path`) == external_exec_path
git = gitcmd(tmp_dir, Dict(), `git`)
git = gitcmd(tmp_dir, custom_git = `git`, gitconfig = Dict())
@test readchomp(`$git --exec-path`) == external_exec_path
if Sys.iswindows()
# See https://github.com/GunnarFarneback/LocalRegistry.jl/issues/76
# for an explanation why we want this to work.
git = gitcmd(tmp_dir, Dict(), `cmd /c git`)
git = gitcmd(tmp_dir, custom_git = `cmd /c git`, gitconfig = Dict())
@test readchomp(`$git --exec-path`) == external_exec_path
else
git = gitcmd(tmp_dir, Dict(), readchomp(`which git`))
git = gitcmd(tmp_dir, custom_git = readchomp(`which git`),
gitconfig = Dict())
@test readchomp(`$git --exec-path`) == external_exec_path
end
end
Expand Down
Loading

0 comments on commit 8893836

Please sign in to comment.