Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b9051d5afb | |||
| 6c6295651f | |||
| 8c418ff846 | |||
| 29b3bd3581 | |||
|
|
012c6efb03 | ||
| 241bf473a7 | |||
| 8fd663462e | |||
| 4beacbf65d | |||
| 102f5561a8 | |||
| e04f065d42 | |||
| 941dd50868 | |||
| d20dabc91e | |||
| 0e63a1c6bf | |||
| 9792c09850 | |||
| a1519a6bc5 | |||
| 36535dcaec | |||
| 32f94b1ef5 | |||
| 913df16f28 | |||
| f66a512a83 | |||
| de15e799ac | |||
| a8736ed37f | |||
| 1a45887fb6 | |||
| 9403156edf |
603
Cargo.lock
generated
603
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
22
Cargo.toml
22
Cargo.toml
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "git-repo-manager"
|
name = "git-repo-manager"
|
||||||
version = "0.7.13"
|
version = "0.7.15"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
authors = [
|
authors = [
|
||||||
@@ -23,7 +23,7 @@ repository = "https://github.com/hakoerber/git-repo-manager"
|
|||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
||||||
# Required for `std::path::Path::is_symlink()`. Will be released with 1.57.
|
# Required for `std::path::Path::is_symlink()`. Will be released with 1.57.
|
||||||
rust-version = "1.57"
|
rust-version = "1.58"
|
||||||
|
|
||||||
license = "GPL-3.0-only"
|
license = "GPL-3.0-only"
|
||||||
|
|
||||||
@@ -41,36 +41,36 @@ path = "src/grm/main.rs"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
||||||
[dependencies.toml]
|
[dependencies.toml]
|
||||||
version = "=0.7.3"
|
version = "=0.8.6"
|
||||||
|
|
||||||
[dependencies.serde]
|
[dependencies.serde]
|
||||||
version = "=1.0.162"
|
version = "=1.0.190"
|
||||||
features = ["derive"]
|
features = ["derive"]
|
||||||
|
|
||||||
[dependencies.git2]
|
[dependencies.git2]
|
||||||
version = "=0.17.1"
|
version = "=0.18.1"
|
||||||
|
|
||||||
[dependencies.shellexpand]
|
[dependencies.shellexpand]
|
||||||
version = "=3.1.0"
|
version = "=3.1.0"
|
||||||
|
|
||||||
[dependencies.clap]
|
[dependencies.clap]
|
||||||
version = "=4.2.7"
|
version = "=4.4.7"
|
||||||
features = ["derive", "cargo"]
|
features = ["derive", "cargo"]
|
||||||
|
|
||||||
[dependencies.console]
|
[dependencies.console]
|
||||||
version = "=0.15.5"
|
version = "=0.15.7"
|
||||||
|
|
||||||
[dependencies.regex]
|
[dependencies.regex]
|
||||||
version = "=1.8.1"
|
version = "=1.10.2"
|
||||||
|
|
||||||
[dependencies.comfy-table]
|
[dependencies.comfy-table]
|
||||||
version = "=6.1.4"
|
version = "=7.1.0"
|
||||||
|
|
||||||
[dependencies.serde_yaml]
|
[dependencies.serde_yaml]
|
||||||
version = "=0.9.21"
|
version = "=0.9.27"
|
||||||
|
|
||||||
[dependencies.serde_json]
|
[dependencies.serde_json]
|
||||||
version = "=1.0.96"
|
version = "=1.0.108"
|
||||||
|
|
||||||
[dependencies.isahc]
|
[dependencies.isahc]
|
||||||
version = "=1.7.2"
|
version = "=1.7.2"
|
||||||
|
|||||||
10
Justfile
10
Justfile
@@ -4,7 +4,7 @@ set shell := ["/bin/bash", "-c"]
|
|||||||
|
|
||||||
static_target := "x86_64-unknown-linux-musl"
|
static_target := "x86_64-unknown-linux-musl"
|
||||||
|
|
||||||
cargo := "cargo +nightly"
|
cargo := "cargo"
|
||||||
|
|
||||||
check: fmt-check lint test
|
check: fmt-check lint test
|
||||||
{{cargo}} check
|
{{cargo}} check
|
||||||
@@ -76,14 +76,14 @@ test-integration:
|
|||||||
|
|
||||||
test-e2e +tests=".": test-binary
|
test-e2e +tests=".": test-binary
|
||||||
cd ./e2e_tests \
|
cd ./e2e_tests \
|
||||||
&& docker-compose rm --stop -f \
|
&& docker compose rm --stop -f \
|
||||||
&& docker-compose build \
|
&& docker compose build \
|
||||||
&& docker-compose run \
|
&& docker compose run \
|
||||||
--rm \
|
--rm \
|
||||||
-v $PWD/../target/x86_64-unknown-linux-musl/e2e-tests/grm:/grm \
|
-v $PWD/../target/x86_64-unknown-linux-musl/e2e-tests/grm:/grm \
|
||||||
pytest \
|
pytest \
|
||||||
"GRM_BINARY=/grm ALTERNATE_DOMAIN=alternate-rest python3 -m pytest --exitfirst -p no:cacheprovider --color=yes "$@"" \
|
"GRM_BINARY=/grm ALTERNATE_DOMAIN=alternate-rest python3 -m pytest --exitfirst -p no:cacheprovider --color=yes "$@"" \
|
||||||
&& docker-compose rm --stop -f
|
&& docker compose rm --stop -f
|
||||||
|
|
||||||
update-dependencies: update-cargo-dependencies
|
update-dependencies: update-cargo-dependencies
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ in once place?
|
|||||||
|
|
||||||
This is how GRM came to be. I'm a fan of infrastructure-as-code, and GRM is a bit
|
This is how GRM came to be. I'm a fan of infrastructure-as-code, and GRM is a bit
|
||||||
like Terraform for your local git repositories. Write a config, run the tool, and
|
like Terraform for your local git repositories. Write a config, run the tool, and
|
||||||
your repos are ready. The only thing that is tracked by git it the list of
|
your repos are ready. The only thing that is tracked by git is the list of
|
||||||
repositories itself.
|
repositories itself.
|
||||||
|
|
||||||
# Crates
|
# Crates
|
||||||
|
|||||||
@@ -93,15 +93,7 @@ for tier in ["dependencies", "dev-dependencies"]:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
cmd = subprocess.run(
|
cmd = subprocess.run(
|
||||||
[
|
["cargo", "update", "--offline", "--aggressive", "--package", name],
|
||||||
"cargo",
|
|
||||||
"update",
|
|
||||||
"-Z",
|
|
||||||
"no-index-update",
|
|
||||||
"--aggressive",
|
|
||||||
"--package",
|
|
||||||
name,
|
|
||||||
],
|
|
||||||
check=True,
|
check=True,
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
@@ -135,15 +127,7 @@ while True:
|
|||||||
spec = f"{package['name']}:{package['version']}"
|
spec = f"{package['name']}:{package['version']}"
|
||||||
try:
|
try:
|
||||||
cmd = subprocess.run(
|
cmd = subprocess.run(
|
||||||
[
|
["cargo", "update", "--offline", "--aggressive", "--package", spec],
|
||||||
"cargo",
|
|
||||||
"update",
|
|
||||||
"-Z",
|
|
||||||
"no-index-update",
|
|
||||||
"--aggressive",
|
|
||||||
"--package",
|
|
||||||
spec,
|
|
||||||
],
|
|
||||||
check=True,
|
check=True,
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ Feature branches are not required, there are also changes happening directly on
|
|||||||
|
|
||||||
You will need the following tools:
|
You will need the following tools:
|
||||||
|
|
||||||
* Rust (obviously) (easiest via `rustup`), with the nightly toolchain
|
* Rust (obviously) (easiest via `rustup`)
|
||||||
* Python3
|
* Python3
|
||||||
* [`just`](https://github.com/casey/just), a command runner like `make`. See
|
* [`just`](https://github.com/casey/just), a command runner like `make`. See
|
||||||
[here](https://github.com/casey/just#installation) for installation
|
[here](https://github.com/casey/just#installation) for installation
|
||||||
@@ -52,18 +52,3 @@ mvdan.cc/sh/v3/cmd/shfmt@latest`, depending on your go build environment.
|
|||||||
|
|
||||||
For details about rustup and the toolchains, see [the installation
|
For details about rustup and the toolchains, see [the installation
|
||||||
section](./installation.md).
|
section](./installation.md).
|
||||||
|
|
||||||
## FAQ
|
|
||||||
|
|
||||||
### Why nightly?
|
|
||||||
|
|
||||||
For now, GRM requires the nightly toolchain for two reasons:
|
|
||||||
|
|
||||||
* [`io_error_more`](https://github.com/rust-lang/rust/issues/86442) to get
|
|
||||||
better error messages on IO errors
|
|
||||||
* [`const_option_ext`](https://github.com/rust-lang/rust/issues/91930) to have
|
|
||||||
static variables read from the environment that fall back to hard coded
|
|
||||||
defaults
|
|
||||||
|
|
||||||
Honestly, both of those are not really necessary or can be handled without
|
|
||||||
nightly. It's just that I'm using nightly anyway.
|
|
||||||
|
|||||||
@@ -2,14 +2,14 @@
|
|||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
Building GRM currently requires the nightly Rust toolchain. The easiest way is
|
Building GRM requires the Rust toolchain to be installed. The easiest way is
|
||||||
using [`rustup`](https://rustup.rs/). Make sure that rustup is properly
|
using [`rustup`](https://rustup.rs/). Make sure that rustup is properly
|
||||||
installed.
|
installed.
|
||||||
|
|
||||||
Make sure that the nightly toolchain is installed:
|
Make sure that the stable toolchain is installed:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ rustup toolchain install nightly
|
$ rustup toolchain install stable
|
||||||
```
|
```
|
||||||
|
|
||||||
Then, install the build dependencies:
|
Then, install the build dependencies:
|
||||||
@@ -22,13 +22,13 @@ Then, install the build dependencies:
|
|||||||
Then, it's a simple command to install the latest stable version:
|
Then, it's a simple command to install the latest stable version:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ cargo +nightly install git-repo-manager
|
$ cargo install git-repo-manager
|
||||||
```
|
```
|
||||||
|
|
||||||
If you're brave, you can also run the development build:
|
If you're brave, you can also run the development build:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ cargo +nightly install --git https://github.com/hakoerber/git-repo-manager.git --branch develop
|
$ cargo install --git https://github.com/hakoerber/git-repo-manager.git --branch develop
|
||||||
```
|
```
|
||||||
|
|
||||||
## Static build
|
## Static build
|
||||||
@@ -47,11 +47,11 @@ need `musl` and a few other build dependencies installed installed:
|
|||||||
The, add the musl target via `rustup`:
|
The, add the musl target via `rustup`:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ rustup +nightly target add x86_64-unknown-linux-musl
|
$ rustup target add x86_64-unknown-linux-musl
|
||||||
```
|
```
|
||||||
|
|
||||||
Then, use a modified build command to get a statically linked binary:
|
Then, use a modified build command to get a statically linked binary:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ cargo +nightly install git-repo-manager --target x86_64-unknown-linux-musl --features=static-build
|
$ cargo install git-repo-manager --target x86_64-unknown-linux-musl --features=static-build
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -163,7 +163,6 @@ def test_repos_find(configtype, exclude, default_format):
|
|||||||
@pytest.mark.parametrize("configtype", ["toml", "yaml"])
|
@pytest.mark.parametrize("configtype", ["toml", "yaml"])
|
||||||
def test_repos_find_in_root(configtype, default_format):
|
def test_repos_find_in_root(configtype, default_format):
|
||||||
with TempGitRepository() as repo_dir:
|
with TempGitRepository() as repo_dir:
|
||||||
|
|
||||||
args = ["repos", "find", "local", repo_dir]
|
args = ["repos", "find", "local", repo_dir]
|
||||||
if not default_format:
|
if not default_format:
|
||||||
args += ["--format", configtype]
|
args += ["--format", configtype]
|
||||||
|
|||||||
@@ -43,7 +43,9 @@ def test_repos_find_remote_invalid_provider(use_config):
|
|||||||
assert cmd.returncode != 0
|
assert cmd.returncode != 0
|
||||||
assert len(cmd.stdout) == 0
|
assert len(cmd.stdout) == 0
|
||||||
if not use_config:
|
if not use_config:
|
||||||
assert re.match(".*invalid value 'thisproviderdoesnotexist' for.*provider", cmd.stderr)
|
assert re.match(
|
||||||
|
".*invalid value 'thisproviderdoesnotexist' for.*provider", cmd.stderr
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("provider", PROVIDERS)
|
@pytest.mark.parametrize("provider", PROVIDERS)
|
||||||
|
|||||||
@@ -312,7 +312,7 @@ def test_repos_sync_root_is_file(configtype):
|
|||||||
|
|
||||||
cmd = grm(["repos", "sync", "config", "--config", config.name])
|
cmd = grm(["repos", "sync", "config", "--config", config.name])
|
||||||
assert cmd.returncode != 0
|
assert cmd.returncode != 0
|
||||||
assert "not a directory" in cmd.stderr.lower()
|
assert "notadirectory" in cmd.stderr.lower()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("configtype", ["toml", "yaml"])
|
@pytest.mark.parametrize("configtype", ["toml", "yaml"])
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
[toolchain]
|
[toolchain]
|
||||||
channel = "nightly"
|
channel = "stable"
|
||||||
targets = ["x86_64-unknown-linux-musl"]
|
targets = ["x86_64-unknown-linux-musl"]
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#![feature(io_error_more)]
|
|
||||||
#![forbid(unsafe_code)]
|
#![forbid(unsafe_code)]
|
||||||
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|||||||
@@ -313,7 +313,7 @@ pub trait Provider {
|
|||||||
// about the data exchange format here.
|
// about the data exchange format here.
|
||||||
repo.remove_namespace();
|
repo.remove_namespace();
|
||||||
|
|
||||||
ret.entry(namespace).or_insert(vec![]).push(repo);
|
ret.entry(namespace).or_default().push(repo);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
|
|||||||
17
src/table.rs
17
src/table.rs
@@ -4,6 +4,7 @@ use super::repo;
|
|||||||
|
|
||||||
use comfy_table::{Cell, Table};
|
use comfy_table::{Cell, Table};
|
||||||
|
|
||||||
|
use std::fmt::Write;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
fn add_table_header(table: &mut Table) {
|
fn add_table_header(table: &mut Table) {
|
||||||
@@ -56,9 +57,10 @@ fn add_repo_status(
|
|||||||
repo_status
|
repo_status
|
||||||
.branches
|
.branches
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(branch_name, remote_branch)| {
|
.fold(String::new(), |mut s, (branch_name, remote_branch)| {
|
||||||
format!(
|
writeln!(
|
||||||
"branch: {}{}\n",
|
&mut s,
|
||||||
|
"branch: {}{}",
|
||||||
&branch_name,
|
&branch_name,
|
||||||
&match remote_branch {
|
&match remote_branch {
|
||||||
None => String::from(" <!local>"),
|
None => String::from(" <!local>"),
|
||||||
@@ -78,8 +80,9 @@ fn add_repo_status(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
.unwrap();
|
||||||
|
s
|
||||||
})
|
})
|
||||||
.collect::<String>()
|
|
||||||
.trim(),
|
.trim(),
|
||||||
&match is_worktree {
|
&match is_worktree {
|
||||||
true => String::from(""),
|
true => String::from(""),
|
||||||
@@ -91,8 +94,10 @@ fn add_repo_status(
|
|||||||
repo_status
|
repo_status
|
||||||
.remotes
|
.remotes
|
||||||
.iter()
|
.iter()
|
||||||
.map(|r| format!("{}\n", r))
|
.fold(String::new(), |mut s, r| {
|
||||||
.collect::<String>()
|
writeln!(&mut s, "{r}").unwrap();
|
||||||
|
s
|
||||||
|
})
|
||||||
.trim(),
|
.trim(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|||||||
@@ -128,8 +128,6 @@ pub fn find_repo_paths(path: &Path) -> Result<Vec<PathBuf>, String> {
|
|||||||
"Failed to open \"{}\": {}",
|
"Failed to open \"{}\": {}",
|
||||||
&path.display(),
|
&path.display(),
|
||||||
match e.kind() {
|
match e.kind() {
|
||||||
std::io::ErrorKind::NotADirectory =>
|
|
||||||
String::from("directory expected, but path is not a directory"),
|
|
||||||
std::io::ErrorKind::NotFound => String::from("not found"),
|
std::io::ErrorKind::NotFound => String::from("not found"),
|
||||||
_ => format!("{:?}", e.kind()),
|
_ => format!("{:?}", e.kind()),
|
||||||
}
|
}
|
||||||
@@ -181,7 +179,7 @@ fn sync_repo(root_path: &Path, repo: &repo::Repo, init_worktree: bool) -> Result
|
|||||||
"Repo already exists, but is not using a worktree setup",
|
"Repo already exists, but is not using a worktree setup",
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
} else if matches!(&repo.remotes, None) || repo.remotes.as_ref().unwrap().is_empty() {
|
} else if repo.remotes.is_none() || repo.remotes.as_ref().unwrap().is_empty() {
|
||||||
print_repo_action(
|
print_repo_action(
|
||||||
&repo.name,
|
&repo.name,
|
||||||
"Repository does not have remotes configured, initializing new",
|
"Repository does not have remotes configured, initializing new",
|
||||||
|
|||||||
Reference in New Issue
Block a user