32 Commits

Author SHA1 Message Date
27ef86c1b4 forge: Use "origin" as the default remote name
Close #33
2022-06-15 20:49:15 +02:00
9fc34e6989 just: Add clean target 2022-06-15 20:39:54 +02:00
4b79b6dd1d just: Update targets for static builds 2022-06-15 20:39:54 +02:00
d0cbc2f985 forge: Add option to specify remote name
Close #32
2022-06-15 20:39:54 +02:00
d53e28668b Cargo.lock: Updating http v0.2.7 -> v0.2.8 2022-06-15 20:39:54 +02:00
0b8896d11d Cargo.lock: Updating getrandom v0.2.6 -> v0.2.7 2022-06-15 20:39:54 +02:00
8c0c3ad169 dependencies: Update clap to 3.2.5 2022-06-15 20:39:54 +02:00
aebed5639d Add Max to contributors 2022-06-14 09:37:51 +02:00
4514de9ff5 Add release script 2022-06-14 00:35:03 +02:00
31b9757ef3 Merge branch 'develop' 2022-06-14 00:32:08 +02:00
defb3d1b7d Release v0.7.2 2022-06-14 00:32:08 +02:00
e6b654e990 Cargo.lock: Updating libz-sys v1.1.6 -> v1.1.8 2022-06-14 00:15:15 +02:00
29ddc647e3 dependencies: Update comfy-table to 6.0.0 2022-06-14 00:15:15 +02:00
67c3e40108 just: Update check target to be pre-commit ready 2022-06-14 00:15:15 +02:00
7363ed48b4 Add clippy suggestions 2022-06-14 00:15:15 +02:00
96943c1483 Use new cargo fmt 2022-06-14 00:15:15 +02:00
9f7195282f Enable output in rust unit tests 2022-06-14 00:15:15 +02:00
30480fb568 Update handling of branches on worktree setup 2022-06-14 00:15:15 +02:00
c3aaea3332 Quote branch name on output 2022-06-14 00:15:15 +02:00
fad6f71876 Improve default branch guessing 2022-06-14 00:15:15 +02:00
73158e3d47 Print ok-ish stuff to stdout 2022-06-14 00:15:15 +02:00
6f4ae88260 Add some comments about repo syncing 2022-06-14 00:15:15 +02:00
a8f8803a92 Do not fail on empty clone target 2022-06-14 00:15:15 +02:00
581a513ebd Initialize local branches on clone 2022-06-14 00:15:15 +02:00
f1e212ead9 Add function to get all remote branches 2022-06-14 00:15:15 +02:00
bc3001a4e6 Add function to get basename of branch 2022-06-14 00:15:15 +02:00
c4fd1d0452 Refactor default_branch() for readability 2022-06-14 00:15:15 +02:00
1a65a163a1 Use opaque type for auth token
So we cannot accidentially output it, as it does not implement
`Display`.
2022-06-14 00:15:15 +02:00
4f68a563c6 providers: Use references for field access 2022-06-14 00:15:15 +02:00
e04e8ceeeb Use opaque type for auth token
So we cannot accidentially output it, as it does not implement
`Display`.
2022-06-14 00:15:15 +02:00
Max Volk
b2542b341e Reword some of the documentation and spelling fixes 2022-06-14 00:15:15 +02:00
d402c1f8ce Remove accidentially added file 2022-05-28 22:06:52 +02:00
14 changed files with 288 additions and 206 deletions

View File

@@ -1 +1,2 @@
nonnominandus nonnominandus
Maximilian Volk

99
Cargo.lock generated
View File

@@ -80,16 +80,16 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "clap" name = "clap"
version = "3.1.18" version = "3.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b" checksum = "d53da17d37dba964b9b3ecb5c5a1f193a2762c700e6829201e645b9381c99dc7"
dependencies = [ dependencies = [
"atty", "atty",
"bitflags", "bitflags",
"clap_derive", "clap_derive",
"clap_lex", "clap_lex",
"indexmap", "indexmap",
"lazy_static", "once_cell",
"strsim", "strsim",
"termcolor", "termcolor",
"textwrap", "textwrap",
@@ -97,11 +97,11 @@ dependencies = [
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "3.1.18" version = "3.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25320346e922cffe59c0bbc5410c8d8784509efb321488971081313cb1e1a33c" checksum = "c11d40217d16aee8508cc8e5fde8b4ff24639758608e5374e731b53f85749fb9"
dependencies = [ dependencies = [
"heck 0.4.0", "heck",
"proc-macro-error", "proc-macro-error",
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -110,18 +110,18 @@ dependencies = [
[[package]] [[package]]
name = "clap_lex" name = "clap_lex"
version = "0.2.0" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213" checksum = "5538cd660450ebeb4234cfecf8f2284b844ffc4c50531e66d584ad5b91293613"
dependencies = [ dependencies = [
"os_str_bytes", "os_str_bytes",
] ]
[[package]] [[package]]
name = "comfy-table" name = "comfy-table"
version = "5.0.1" version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b103d85ca6e209388771bfb7aa6b68a7aeec4afbf6f0a0264bfbf50360e5212e" checksum = "121d8a5b0346092c18a4b2fd6f620d7a06f0eb7ac0a45860939a0884bc579c56"
dependencies = [ dependencies = [
"crossterm", "crossterm",
"strum", "strum",
@@ -321,18 +321,18 @@ dependencies = [
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.6" version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"wasi 0.10.2+wasi-snapshot-preview1", "wasi",
] ]
[[package]] [[package]]
name = "git-repo-manager" name = "git-repo-manager"
version = "0.7.1" version = "0.7.2"
dependencies = [ dependencies = [
"clap", "clap",
"comfy-table", "comfy-table",
@@ -371,15 +371,6 @@ version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
[[package]]
name = "heck"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
dependencies = [
"unicode-segmentation",
]
[[package]] [[package]]
name = "heck" name = "heck"
version = "0.4.0" version = "0.4.0"
@@ -397,9 +388,9 @@ dependencies = [
[[package]] [[package]]
name = "http" name = "http"
version = "0.2.7" version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff8670570af52249509a86f5e3e18a08c60b177071826898fde8997cf5f6bfbb" checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399"
dependencies = [ dependencies = [
"bytes", "bytes",
"fnv", "fnv",
@@ -419,9 +410,9 @@ dependencies = [
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "1.8.1" version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" checksum = "e6012d540c5baa3589337a98ce73408de9b5a25ec9fc2c6fd6be8f0d39e0ca5a"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"hashbrown", "hashbrown",
@@ -532,9 +523,9 @@ dependencies = [
[[package]] [[package]]
name = "libz-sys" name = "libz-sys"
version = "1.1.6" version = "1.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92e7e15d7610cce1d9752e137625f14e61a28cd45929b6e12e47b50fe154ee2e" checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf"
dependencies = [ dependencies = [
"cc", "cc",
"libc", "libc",
@@ -593,7 +584,7 @@ checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799"
dependencies = [ dependencies = [
"libc", "libc",
"log", "log",
"wasi 0.11.0+wasi-snapshot-preview1", "wasi",
"windows-sys", "windows-sys",
] ]
@@ -620,9 +611,9 @@ dependencies = [
[[package]] [[package]]
name = "openssl-sys" name = "openssl-sys"
version = "0.9.73" version = "0.9.74"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d5fd19fb3e0a8191c1e34935718976a3e70c112ab9a24af6d7cadccd9d90bc0" checksum = "835363342df5fba8354c5b453325b110ffd54044e588c539cf2f20a8014e4cb1"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"cc", "cc",
@@ -646,9 +637,9 @@ checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72"
[[package]] [[package]]
name = "parking_lot" name = "parking_lot"
version = "0.12.0" version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [ dependencies = [
"lock_api", "lock_api",
"parking_lot_core", "parking_lot_core",
@@ -1005,17 +996,17 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]] [[package]]
name = "strum" name = "strum"
version = "0.23.0" version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cae14b91c7d11c9a851d3fbc80a963198998c2a64eec840477fa92d8ce9b70bb" checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
[[package]] [[package]]
name = "strum_macros" name = "strum_macros"
version = "0.23.1" version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bb0dc7ee9c15cea6199cde9a127fa16a4c5819af85395457ad72d68edc85a38" checksum = "9550962e7cf70d9980392878dfaf1dcc3ece024f4cf3bf3c46b978d0bad61d6c"
dependencies = [ dependencies = [
"heck 0.3.3", "heck",
"proc-macro2", "proc-macro2",
"quote", "quote",
"rustversion", "rustversion",
@@ -1024,9 +1015,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.95" version = "1.0.96"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942" checksum = "0748dd251e24453cb8717f0354206b91557e4ec8703673a4b30208f2abaf1ebf"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -1114,9 +1105,9 @@ dependencies = [
[[package]] [[package]]
name = "tracing" name = "tracing"
version = "0.1.34" version = "0.1.35"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09" checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"log", "log",
@@ -1138,11 +1129,11 @@ dependencies = [
[[package]] [[package]]
name = "tracing-core" name = "tracing-core"
version = "0.1.26" version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f54c8ca710e81886d498c2fd3331b56c93aa248d49de2222ad2742247c60072f" checksum = "7709595b8878a4965ce5e87ebf880a7d39c9afc6837721b21a5a816a8117d921"
dependencies = [ dependencies = [
"lazy_static", "once_cell",
] ]
[[package]] [[package]]
@@ -1163,9 +1154,9 @@ checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.0" version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"
[[package]] [[package]]
name = "unicode-normalization" name = "unicode-normalization"
@@ -1176,12 +1167,6 @@ dependencies = [
"tinyvec", "tinyvec",
] ]
[[package]]
name = "unicode-segmentation"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
[[package]] [[package]]
name = "unicode-width" name = "unicode-width"
version = "0.1.9" version = "0.1.9"
@@ -1227,12 +1212,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca"
[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.11.0+wasi-snapshot-preview1" version = "0.11.0+wasi-snapshot-preview1"

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "git-repo-manager" name = "git-repo-manager"
version = "0.7.1" version = "0.7.2"
edition = "2021" edition = "2021"
authors = [ authors = [
@@ -54,7 +54,7 @@ version = "=0.14.4"
version = "=2.1.0" version = "=2.1.0"
[dependencies.clap] [dependencies.clap]
version = "=3.1.18" version = "=3.2.5"
features = ["derive", "cargo"] features = ["derive", "cargo"]
[dependencies.console] [dependencies.console]
@@ -64,7 +64,7 @@ version = "=0.15.0"
version = "=1.5.6" version = "=1.5.6"
[dependencies.comfy-table] [dependencies.comfy-table]
version = "=5.0.1" version = "=6.0.0"
[dependencies.serde_yaml] [dependencies.serde_yaml]
version = "=0.8.24" version = "=0.8.24"

View File

@@ -1,42 +1,53 @@
set positional-arguments set positional-arguments
target := "x86_64-unknown-linux-musl" static_target := "x86_64-unknown-linux-musl"
check: test check: fmt-check lint test
cargo check cargo check
cargo fmt --check cargo fmt --check
cargo clippy --no-deps -- -Dwarnings cargo clippy --no-deps -- -Dwarnings
clean:
cargo clean
git clean -f -d -X
fmt: fmt:
cargo fmt cargo fmt
git ls-files | grep '\.py$' | xargs black git ls-files | grep '\.py$' | xargs black
fmt-check:
cargo fmt --check
git ls-files | grep '\.py$' | xargs black --check
lint: lint:
cargo clippy --no-deps cargo clippy --no-deps -- -Dwarnings
lint-fix: lint-fix:
cargo clippy --no-deps --fix cargo clippy --no-deps --fix
release: release:
cargo build --release --target {{target}} cargo build --release
release-static:
cargo build --release --target {{static_target}} --features=static-build
test-binary: test-binary:
env \ env \
GITHUB_API_BASEURL=http://rest:5000/github \ GITHUB_API_BASEURL=http://rest:5000/github \
GITLAB_API_BASEURL=http://rest:5000/gitlab \ GITLAB_API_BASEURL=http://rest:5000/gitlab \
cargo build --target {{target}} --profile e2e-tests --features=static-build cargo build --target {{static_target}} --profile e2e-tests --features=static-build
install: install:
cargo install --path . cargo install --path .
install-static: install-static:
cargo install --target {{target}} --features=static-build --path . cargo install --target {{static_target}} --features=static-build --path .
build: build:
cargo build cargo build
build-static: build-static:
cargo build --target {{target}} --features=static-build cargo build --target {{static_target}} --features=static-build
test: test-unit test-integration test-e2e test: test-unit test-integration test-e2e
@@ -52,7 +63,7 @@ test-e2e +tests=".": test-binary
&& docker-compose build \ && docker-compose build \
&& docker-compose run \ && docker-compose run \
--rm \ --rm \
-v $PWD/../target/{{target}}/e2e-tests/grm:/grm \ -v $PWD/../target/{{static_target}}/e2e-tests/grm:/grm \
pytest \ pytest \
"GRM_BINARY=/grm ALTERNATE_DOMAIN=alternate-rest python3 -m pytest -p no:cacheprovider --color=yes "$@"" \ "GRM_BINARY=/grm ALTERNATE_DOMAIN=alternate-rest python3 -m pytest -p no:cacheprovider --color=yes "$@"" \
&& docker-compose rm --stop -f && docker-compose rm --stop -f

View File

@@ -248,6 +248,7 @@ def test_repos_find_remote_user_empty(
@pytest.mark.parametrize("force_ssh", [True, False]) @pytest.mark.parametrize("force_ssh", [True, False])
@pytest.mark.parametrize("use_alternate_endpoint", [True, False]) @pytest.mark.parametrize("use_alternate_endpoint", [True, False])
@pytest.mark.parametrize("use_config", [True, False]) @pytest.mark.parametrize("use_config", [True, False])
@pytest.mark.parametrize("override_remote_name", [True, False])
def test_repos_find_remote_user( def test_repos_find_remote_user(
provider, provider,
configtype, configtype,
@@ -258,6 +259,7 @@ def test_repos_find_remote_user(
force_ssh, force_ssh,
use_alternate_endpoint, use_alternate_endpoint,
use_config, use_config,
override_remote_name,
): ):
if use_config: if use_config:
with tempfile.NamedTemporaryFile() as config: with tempfile.NamedTemporaryFile() as config:
@@ -274,6 +276,8 @@ def test_repos_find_remote_user(
cfg += f"worktree = {str(worktree).lower()}\n" cfg += f"worktree = {str(worktree).lower()}\n"
if force_ssh: if force_ssh:
cfg += f"force_ssh = true\n" cfg += f"force_ssh = true\n"
if override_remote_name:
cfg += f'remote_name = "otherremote"\n'
if use_owner: if use_owner:
cfg += """ cfg += """
[filters] [filters]
@@ -310,6 +314,8 @@ def test_repos_find_remote_user(
args += ["--user", "myuser1"] args += ["--user", "myuser1"]
if force_ssh: if force_ssh:
args += ["--force-ssh"] args += ["--force-ssh"]
if override_remote_name:
args += ["--remote-name", "otherremote"]
if not worktree_default: if not worktree_default:
args += ["--worktree", str(worktree).lower()] args += ["--worktree", str(worktree).lower()]
if use_alternate_endpoint: if use_alternate_endpoint:
@@ -350,7 +356,10 @@ def test_repos_find_remote_user(
assert repo["worktree_setup"] is (not worktree_default and worktree) assert repo["worktree_setup"] is (not worktree_default and worktree)
assert isinstance(repo["remotes"], list) assert isinstance(repo["remotes"], list)
assert len(repo["remotes"]) == 1 assert len(repo["remotes"]) == 1
assert repo["remotes"][0]["name"] == provider if override_remote_name:
assert repo["remotes"][0]["name"] == "otherremote"
else:
assert repo["remotes"][0]["name"] == "origin"
if force_ssh or i == 1: if force_ssh or i == 1:
assert ( assert (
repo["remotes"][0]["url"] repo["remotes"][0]["url"]
@@ -535,14 +544,14 @@ def test_repos_find_remote_group(
assert isinstance(repo["remotes"], list) assert isinstance(repo["remotes"], list)
assert len(repo["remotes"]) == 1 assert len(repo["remotes"]) == 1
if force_ssh or i == 1: if force_ssh or i == 1:
assert repo["remotes"][0]["name"] == provider assert repo["remotes"][0]["name"] == "origin"
assert ( assert (
repo["remotes"][0]["url"] repo["remotes"][0]["url"]
== f"ssh://git@example.com/mygroup1/myproject{i}.git" == f"ssh://git@example.com/mygroup1/myproject{i}.git"
) )
assert repo["remotes"][0]["type"] == "ssh" assert repo["remotes"][0]["type"] == "ssh"
else: else:
assert repo["remotes"][0]["name"] == provider assert repo["remotes"][0]["name"] == "origin"
assert ( assert (
repo["remotes"][0]["url"] repo["remotes"][0]["url"]
== f"https://example.com/mygroup1/myproject{i}.git" == f"https://example.com/mygroup1/myproject{i}.git"
@@ -659,7 +668,7 @@ def test_repos_find_remote_user_and_group(
assert repo["worktree_setup"] is (not worktree_default and worktree) assert repo["worktree_setup"] is (not worktree_default and worktree)
assert isinstance(repo["remotes"], list) assert isinstance(repo["remotes"], list)
assert len(repo["remotes"]) == 1 assert len(repo["remotes"]) == 1
assert repo["remotes"][0]["name"] == provider assert repo["remotes"][0]["name"] == "origin"
if force_ssh or i == 1: if force_ssh or i == 1:
assert ( assert (
repo["remotes"][0]["url"] repo["remotes"][0]["url"]
@@ -684,7 +693,7 @@ def test_repos_find_remote_user_and_group(
assert repo["worktree_setup"] is (not worktree_default and worktree) assert repo["worktree_setup"] is (not worktree_default and worktree)
assert isinstance(repo["remotes"], list) assert isinstance(repo["remotes"], list)
assert len(repo["remotes"]) == 1 assert len(repo["remotes"]) == 1
assert repo["remotes"][0]["name"] == provider assert repo["remotes"][0]["name"] == "origin"
if force_ssh or i == 1: if force_ssh or i == 1:
assert ( assert (
repo["remotes"][0]["url"] repo["remotes"][0]["url"]
@@ -814,7 +823,7 @@ def test_repos_find_remote_owner(
assert repo["worktree_setup"] is (not worktree_default and worktree) assert repo["worktree_setup"] is (not worktree_default and worktree)
assert isinstance(repo["remotes"], list) assert isinstance(repo["remotes"], list)
assert len(repo["remotes"]) == 1 assert len(repo["remotes"]) == 1
assert repo["remotes"][0]["name"] == provider assert repo["remotes"][0]["name"] == "origin"
if force_ssh or i == 1: if force_ssh or i == 1:
assert ( assert (
repo["remotes"][0]["url"] repo["remotes"][0]["url"]
@@ -837,7 +846,7 @@ def test_repos_find_remote_owner(
assert repo["worktree_setup"] is (not worktree_default and worktree) assert repo["worktree_setup"] is (not worktree_default and worktree)
assert isinstance(repo["remotes"], list) assert isinstance(repo["remotes"], list)
assert len(repo["remotes"]) == 1 assert len(repo["remotes"]) == 1
assert repo["remotes"][0]["name"] == provider assert repo["remotes"][0]["name"] == "origin"
if force_ssh or i == 1: if force_ssh or i == 1:
assert ( assert (
repo["remotes"][0]["url"] repo["remotes"][0]["url"]
@@ -861,7 +870,7 @@ def test_repos_find_remote_owner(
assert repo["worktree_setup"] is (not worktree_default and worktree) assert repo["worktree_setup"] is (not worktree_default and worktree)
assert isinstance(repo["remotes"], list) assert isinstance(repo["remotes"], list)
assert len(repo["remotes"]) == 1 assert len(repo["remotes"]) == 1
assert repo["remotes"][0]["name"] == provider assert repo["remotes"][0]["name"] == "origin"
if force_ssh: if force_ssh:
assert ( assert (
repo["remotes"][0]["url"] == f"ssh://git@example.com/myuser2/myproject3.git" repo["remotes"][0]["url"] == f"ssh://git@example.com/myuser2/myproject3.git"
@@ -890,7 +899,7 @@ def test_repos_find_remote_owner(
assert repo["worktree_setup"] is (not worktree_default and worktree) assert repo["worktree_setup"] is (not worktree_default and worktree)
assert isinstance(repo["remotes"], list) assert isinstance(repo["remotes"], list)
assert len(repo["remotes"]) == 1 assert len(repo["remotes"]) == 1
assert repo["remotes"][0]["name"] == provider assert repo["remotes"][0]["name"] == "origin"
if force_ssh or i == 1: if force_ssh or i == 1:
assert ( assert (
repo["remotes"][0]["url"] repo["remotes"][0]["url"]
@@ -910,7 +919,7 @@ def test_repos_find_remote_owner(
assert repo["worktree_setup"] is (not worktree_default and worktree) assert repo["worktree_setup"] is (not worktree_default and worktree)
assert isinstance(repo["remotes"], list) assert isinstance(repo["remotes"], list)
assert len(repo["remotes"]) == 1 assert len(repo["remotes"]) == 1
assert repo["remotes"][0]["name"] == provider assert repo["remotes"][0]["name"] == "origin"
if force_ssh: if force_ssh:
assert ( assert (
repo["remotes"][0]["url"] repo["remotes"][0]["url"]
@@ -936,7 +945,7 @@ def test_repos_find_remote_owner(
assert repo["worktree_setup"] is (not worktree_default and worktree) assert repo["worktree_setup"] is (not worktree_default and worktree)
assert isinstance(repo["remotes"], list) assert isinstance(repo["remotes"], list)
assert len(repo["remotes"]) == 1 assert len(repo["remotes"]) == 1
assert repo["remotes"][0]["name"] == provider assert repo["remotes"][0]["name"] == "origin"
if force_ssh: if force_ssh:
assert ( assert (
repo["remotes"][0]["url"] repo["remotes"][0]["url"]

153
release.sh Executable file
View File

@@ -0,0 +1,153 @@
#!/usr/bin/env bash
set -o nounset
set -o errexit
set -o pipefail
usage() {
printf '%s\n' "usage: $0 (master|minor|patch)" >&2
}
if (( $# != 1 )) ; then
usage
exit 1
fi
current_version="$(grep '^version \?=' Cargo.toml | head -1 | cut -d '=' -f 2 | tr -d " '"'"')"
major="$(printf '%s' "${current_version}" | grep -oP '^\d+')"
minor="$(printf '%s' "${current_version}" | grep -oP '\.\d+\.' | tr -d '.')"
patch="$(printf '%s' "${current_version}" | grep -oP '\d+$' | tr -d '.')"
case "$1" in
major)
(( major++ )) || true
minor=0
patch=0
;;
minor)
(( minor++ )) || true
patch=0
;;
patch)
(( patch++ )) || true
;;
*)
usage
exit 1
;;
esac
new_version="${major}.${minor}.${patch}"
if ! [[ "${new_version}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] ; then
printf '%s\n' 'Version has to a complete semver' >&2
exit 1
fi
if [[ $(git rev-parse --abbrev-ref HEAD) != "develop" ]] ; then
printf '%s\n' 'You need to be on develop' >&2
exit 1
fi
if [[ -n "$(git status --porcelain)" ]] ; then
printf '%s\n' 'There are uncommitted changes' >&2
exit 1
fi
if git tag --list "v${new_version}" | grep -q . ; then
printf 'Tag %s already exists\n' "v${new_version}" >&2
exit 1
fi
for remote in $(git remote) ; do
if git ls-remote --tags "${remote}" | grep -q "refs/tags/v${new_version}$" ; then
printf 'Tag %s already exists on %s' "v${new_version}" "${remote}" >&2
exit 1
fi
done
git fetch --all
for remote in $(git remote) ; do
for branch in master develop ; do
if ! git diff --quiet "${remote}/${branch}..${branch}" ; then
printf 'Remote branch %s/%s not up to date, synchronize first!\n' "${remote}" "${branch}" >&2
exit 1
fi
done
done
if ! git merge-base --is-ancestor master develop ; then
printf '%s\n' 'Develop is not a straight descendant of master, rebase!' >&2
exit 1
fi
if (( $(git log --oneline master..develop | wc -l) == 0 )) ; then
printf '%s\n' 'No changes between master and develop?' >&2
exit 1
fi
just update-dependencies
just check
sed -i "0,/^version/{s/^version.*$/version = \"${new_version}\"/}" Cargo.toml
cargo update --package git-repo-manager --precise "${new_version}"
diff="$(git diff --numstat)"
if (( $(printf '%s\n' "${diff}" | wc -l) != 2 )) ; then
printf '%s\n' 'Weird changes detected, bailing' >&2
exit 1
fi
if ! printf '%s\n' "${diff}" | grep -Pq '^1\s+1\s+Cargo.lock$' ; then
printf '%s\n' 'Weird changes detected, bailing' >&2
exit 1
fi
if ! printf '%s\n' "${diff}" | grep -Pq '^1\s+1\s+Cargo.toml$' ; then
printf '%s\n' 'Weird changes detected, bailing' >&2
exit 1
fi
git add Cargo.lock Cargo.toml
git commit -m "Release v${new_version}"
git switch master 2>/dev/null || { [[ -d "../master" ]] && cd "../master" ; } || { printf '%s\n' 'Could not change to master' >&2 ; exit 1 ; }
if [[ $(git rev-parse --abbrev-ref HEAD) != "master" ]] ; then
printf '%s\n' 'Looks like branch switching to master did not work' >&2
exit 1
fi
git merge --no-ff --no-edit develop
git tag "v${new_version}"
for remote in $(git remote) ; do
while ! git push "${remote}" "v${new_version}" master ; do
:
done
done
git switch develop 2>/dev/null || { [[ -d "../develop" ]] && cd "../develop" ; } || { printf '%s\n' 'Could not change to develop' >&2 ; exit 1 ; }
if [[ $(git rev-parse --abbrev-ref HEAD) != "develop" ]] ; then
printf '%s\n' 'Looks like branch switching to develop did not work' >&2
exit 1
fi
git merge --ff-only master
for remote in $(git remote) ; do
while ! git push "${remote}" develop ; do
:
done
done
cargo publish
printf 'Published %s successfully\n' "${new_version}"
exit 0

View File

@@ -53,6 +53,8 @@ pub struct ConfigProvider {
pub worktree: Option<bool>, pub worktree: Option<bool>,
pub init_worktree: Option<bool>, pub init_worktree: Option<bool>,
pub remote_name: Option<String>,
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
@@ -192,6 +194,7 @@ impl Config {
.get_repos( .get_repos(
config.worktree.unwrap_or(false), config.worktree.unwrap_or(false),
config.force_ssh.unwrap_or(false), config.force_ssh.unwrap_or(false),
config.remote_name,
)? )?
} }
RemoteProvider::Gitlab => { RemoteProvider::Gitlab => {
@@ -205,6 +208,7 @@ impl Config {
.get_repos( .get_repos(
config.worktree.unwrap_or(false), config.worktree.unwrap_or(false),
config.force_ssh.unwrap_or(false), config.force_ssh.unwrap_or(false),
config.remote_name,
)? )?
} }
}; };

View File

@@ -103,6 +103,9 @@ pub struct FindRemoteArgs {
#[clap(arg_enum, short, long, help = "Remote provider to use")] #[clap(arg_enum, short, long, help = "Remote provider to use")]
pub provider: RemoteProvider, pub provider: RemoteProvider,
#[clap(short, long, help = "Name of the remote to use")]
pub remote_name: Option<String>,
#[clap( #[clap(
multiple_occurrences = true, multiple_occurrences = true,
name = "user", name = "user",
@@ -189,6 +192,9 @@ pub struct SyncRemoteArgs {
#[clap(arg_enum, short, long, help = "Remote provider to use")] #[clap(arg_enum, short, long, help = "Remote provider to use")]
pub provider: RemoteProvider, pub provider: RemoteProvider,
#[clap(short, long, help = "Name of the remote to use")]
pub remote_name: Option<String>,
#[clap( #[clap(
multiple_occurrences = true, multiple_occurrences = true,
name = "user", name = "user",

View File

@@ -64,7 +64,11 @@ fn main() {
process::exit(1); process::exit(1);
} }
} }
.get_repos(worktree, args.force_ssh) .get_repos(
worktree,
args.force_ssh,
args.remote_name,
)
} }
cmd::RemoteProvider::Gitlab => { cmd::RemoteProvider::Gitlab => {
match provider::Gitlab::new(filter, token, args.api_url) { match provider::Gitlab::new(filter, token, args.api_url) {
@@ -74,7 +78,11 @@ fn main() {
process::exit(1); process::exit(1);
} }
} }
.get_repos(worktree, args.force_ssh) .get_repos(
worktree,
args.force_ssh,
args.remote_name,
)
} }
}; };
@@ -280,6 +288,7 @@ fn main() {
.get_repos( .get_repos(
config.worktree.unwrap_or(false), config.worktree.unwrap_or(false),
config.force_ssh.unwrap_or(false), config.force_ssh.unwrap_or(false),
config.remote_name,
) { ) {
Ok(provider) => provider, Ok(provider) => provider,
Err(error) => { Err(error) => {
@@ -299,6 +308,7 @@ fn main() {
.get_repos( .get_repos(
config.worktree.unwrap_or(false), config.worktree.unwrap_or(false),
config.force_ssh.unwrap_or(false), config.force_ssh.unwrap_or(false),
config.remote_name,
) { ) {
Ok(provider) => provider, Ok(provider) => provider,
Err(error) => { Err(error) => {
@@ -382,7 +392,11 @@ fn main() {
process::exit(1); process::exit(1);
} }
} }
.get_repos(worktree, args.force_ssh) .get_repos(
worktree,
args.force_ssh,
args.remote_name,
)
} }
cmd::RemoteProvider::Gitlab => { cmd::RemoteProvider::Gitlab => {
match provider::Gitlab::new(filter, token, args.api_url) { match provider::Gitlab::new(filter, token, args.api_url) {
@@ -392,7 +406,11 @@ fn main() {
process::exit(1); process::exit(1);
} }
} }
.get_repos(worktree, args.force_ssh) .get_repos(
worktree,
args.force_ssh,
args.remote_name,
)
} }
}; };

View File

@@ -8,7 +8,6 @@ use super::JsonError;
use super::Project; use super::Project;
use super::Provider; use super::Provider;
const PROVIDER_NAME: &str = "github";
const ACCEPT_HEADER_JSON: &str = "application/vnd.github.v3+json"; const ACCEPT_HEADER_JSON: &str = "application/vnd.github.v3+json";
const GITHUB_API_BASEURL: &str = const GITHUB_API_BASEURL: &str =
option_env!("GITHUB_API_BASEURL").unwrap_or("https://api.github.com"); option_env!("GITHUB_API_BASEURL").unwrap_or("https://api.github.com");
@@ -88,10 +87,6 @@ impl Provider for Github {
}) })
} }
fn name(&self) -> &str {
PROVIDER_NAME
}
fn filter(&self) -> &Filter { fn filter(&self) -> &Filter {
&self.filter &self.filter
} }
@@ -136,8 +131,8 @@ impl Provider for Github {
fn get_current_user(&self) -> Result<String, ApiErrorResponse<GithubApiErrorResponse>> { fn get_current_user(&self) -> Result<String, ApiErrorResponse<GithubApiErrorResponse>> {
Ok(super::call::<GithubUser, GithubApiErrorResponse>( Ok(super::call::<GithubUser, GithubApiErrorResponse>(
&format!("{GITHUB_API_BASEURL}/user"), &format!("{GITHUB_API_BASEURL}/user"),
&Self::auth_header_key(), Self::auth_header_key(),
&self.secret_token(), self.secret_token(),
Some(ACCEPT_HEADER_JSON), Some(ACCEPT_HEADER_JSON),
)? )?
.username) .username)

View File

@@ -8,7 +8,6 @@ use super::JsonError;
use super::Project; use super::Project;
use super::Provider; use super::Provider;
const PROVIDER_NAME: &str = "gitlab";
const ACCEPT_HEADER_JSON: &str = "application/json"; const ACCEPT_HEADER_JSON: &str = "application/json";
const GITLAB_API_BASEURL: &str = option_env!("GITLAB_API_BASEURL").unwrap_or("https://gitlab.com"); const GITLAB_API_BASEURL: &str = option_env!("GITLAB_API_BASEURL").unwrap_or("https://gitlab.com");
@@ -105,10 +104,6 @@ impl Provider for Gitlab {
}) })
} }
fn name(&self) -> &str {
PROVIDER_NAME
}
fn filter(&self) -> &Filter { fn filter(&self) -> &Filter {
&self.filter &self.filter
} }
@@ -157,8 +152,8 @@ impl Provider for Gitlab {
fn get_current_user(&self) -> Result<String, ApiErrorResponse<GitlabApiErrorResponse>> { fn get_current_user(&self) -> Result<String, ApiErrorResponse<GitlabApiErrorResponse>> {
Ok(super::call::<GitlabUser, GitlabApiErrorResponse>( Ok(super::call::<GitlabUser, GitlabApiErrorResponse>(
&format!("{}/api/v4/user", self.api_url()), &format!("{}/api/v4/user", self.api_url()),
&Self::auth_header_key(), Self::auth_header_key(),
&self.secret_token(), self.secret_token(),
Some(ACCEPT_HEADER_JSON), Some(ACCEPT_HEADER_JSON),
)? )?
.username) .username)

View File

@@ -14,6 +14,8 @@ use super::repo;
use std::collections::HashMap; use std::collections::HashMap;
const DEFAULT_REMOTE_NAME: &str = "origin";
#[derive(Debug, Deserialize, Serialize, clap::ArgEnum, Clone)] #[derive(Debug, Deserialize, Serialize, clap::ArgEnum, Clone)]
pub enum RemoteProvider { pub enum RemoteProvider {
#[serde(alias = "github", alias = "GitHub")] #[serde(alias = "github", alias = "GitHub")]
@@ -122,7 +124,6 @@ pub trait Provider {
where where
Self: Sized; Self: Sized;
fn name(&self) -> &str;
fn filter(&self) -> &Filter; fn filter(&self) -> &Filter;
fn secret_token(&self) -> &auth::AuthToken; fn secret_token(&self) -> &auth::AuthToken;
fn auth_header_key() -> &'static str; fn auth_header_key() -> &'static str;
@@ -213,6 +214,7 @@ pub trait Provider {
&self, &self,
worktree_setup: bool, worktree_setup: bool,
force_ssh: bool, force_ssh: bool,
remote_name: Option<String>,
) -> Result<HashMap<Option<String>, Vec<repo::Repo>>, String> { ) -> Result<HashMap<Option<String>, Vec<repo::Repo>>, String> {
let mut repos = vec![]; let mut repos = vec![];
@@ -292,10 +294,12 @@ pub trait Provider {
let mut ret: HashMap<Option<String>, Vec<repo::Repo>> = HashMap::new(); let mut ret: HashMap<Option<String>, Vec<repo::Repo>> = HashMap::new();
let remote_name = remote_name.unwrap_or_else(|| DEFAULT_REMOTE_NAME.to_string());
for repo in repos { for repo in repos {
let namespace = repo.namespace(); let namespace = repo.namespace();
let mut repo = repo.into_repo_config(&self.name(), worktree_setup, force_ssh); let mut repo = repo.into_repo_config(&remote_name, worktree_setup, force_ssh);
// Namespace is already part of the hashmap key. I'm not too happy // Namespace is already part of the hashmap key. I'm not too happy
// about the data exchange format here. // about the data exchange format here.

View File

@@ -1056,12 +1056,12 @@ impl RepoHandle {
// Note that <remote>/HEAD only exists after a normal clone, there is no way to get the // Note that <remote>/HEAD only exists after a normal clone, there is no way to get the
// remote HEAD afterwards. So this is a "best effort" approach. // remote HEAD afterwards. So this is a "best effort" approach.
if let Ok(remote_head) = self.find_remote_branch(&remote_name, "HEAD") { if let Ok(remote_head) = self.find_remote_branch(remote_name, "HEAD") {
if let Some(pointer_name) = remote_head.as_reference().symbolic_target() { if let Some(pointer_name) = remote_head.as_reference().symbolic_target() {
if let Some(local_branch_name) = if let Some(local_branch_name) =
pointer_name.strip_prefix(&format!("refs/remotes/{}/", remote_name)) pointer_name.strip_prefix(&format!("refs/remotes/{}/", remote_name))
{ {
return Ok(Some(self.find_local_branch(&local_branch_name)?)); return Ok(Some(self.find_local_branch(local_branch_name)?));
} else { } else {
eprintln!("Remote HEAD ({}) pointer is invalid", pointer_name); eprintln!("Remote HEAD ({}) pointer is invalid", pointer_name);
} }
@@ -1088,7 +1088,7 @@ impl RepoHandle {
if remotes.len() == 1 { if remotes.len() == 1 {
let remote_name = &remotes[0]; let remote_name = &remotes[0];
if let Some(default_branch) = self.get_remote_default_branch(&remote_name)? { if let Some(default_branch) = self.get_remote_default_branch(remote_name)? {
return Ok(default_branch); return Ok(default_branch);
} }
} else { } else {
@@ -1099,19 +1099,15 @@ impl RepoHandle {
} }
} }
if !default_branches.is_empty() { if !default_branches.is_empty()
if default_branches.len() == 1 { && (default_branches.len() == 1
return Ok(default_branches.remove(0)); || default_branches
} else {
if default_branches
.windows(2) .windows(2)
.all(|w| w[0].name() == w[1].name()) .all(|w| w[0].name() == w[1].name()))
{ {
return Ok(default_branches.remove(0)); return Ok(default_branches.remove(0));
} }
} }
}
}
for branch_name in &vec!["main", "master"] { for branch_name in &vec!["main", "master"] {
if let Ok(branch) = self.0.find_branch(branch_name, git2::BranchType::Local) { if let Ok(branch) = self.0.find_branch(branch_name, git2::BranchType::Local) {

View File

@@ -5,95 +5,6 @@ use super::repo;
pub const GIT_MAIN_WORKTREE_DIRECTORY: &str = ".git-main-working-tree"; pub const GIT_MAIN_WORKTREE_DIRECTORY: &str = ".git-main-working-tree";
#[cfg(test)]
mod tests {
use super::*;
use std::path::PathBuf;
use std::process::Command;
use tempdir::TempDir;
fn init_empty_repo() -> PathBuf {
let directory = TempDir::new("grm-worktree").unwrap();
Command::new("git")
.args(["init", "."])
.current_dir(directory.path())
.output()
.unwrap();
Command::new("touch")
.args(["test"])
.current_dir(directory.path())
.output()
.unwrap();
Command::new("git")
.args(["add", "test"])
.current_dir(directory.path())
.output()
.unwrap();
Command::new("git")
.args(["commit", "-m", "Initial commit"])
.current_dir(directory.path())
.output()
.unwrap();
Command::new("bash")
.args(["-c", "git ls-files | xargs rm -rf"])
.current_dir(directory.path())
.output()
.unwrap();
Command::new("mv")
.args([".git", GIT_MAIN_WORKTREE_DIRECTORY])
.current_dir(directory.path())
.output()
.unwrap();
Command::new("git")
.args([
"--git-dir",
GIT_MAIN_WORKTREE_DIRECTORY,
"config",
"core.bare",
"true",
])
.current_dir(directory.path())
.output()
.unwrap();
directory.into_path()
}
#[test]
fn add_new_worktree() {
let repo_path = init_empty_repo();
let out = add_worktree(&repo_path, "test", None, None, true);
assert!(out.is_ok());
let repo = git2::Repository::open(repo_path.join("test")).unwrap();
assert_eq!(repo.head().unwrap().shorthand().unwrap(), "test");
assert!(repo
.find_branch("test", git2::BranchType::Local)
.unwrap()
.upstream()
.is_err());
}
#[test]
fn add_new_worktree_with_remote_track() {
let repo_path = init_empty_repo();
let out = add_worktree(&repo_path, "test", None, Some(("origin", "test")), true);
assert!(out.is_ok());
let repo = git2::Repository::open(repo_path.join("test")).unwrap();
assert_eq!(repo.head().unwrap().shorthand().unwrap(), "test");
assert_eq!(
repo.find_branch("test", git2::BranchType::Local)
.unwrap()
.upstream()
.unwrap()
.name()
.unwrap()
.unwrap(),
"test"
);
}
}
// The logic about the base branch and the tracking branch is as follows: // The logic about the base branch and the tracking branch is as follows:
// //
// * If a branch with the same name does not exist and no track is given, use the default // * If a branch with the same name does not exist and no track is given, use the default
@@ -144,9 +55,9 @@ pub fn add_worktree(
if let Ok(local_upstream_branch) = branchref.upstream() { if let Ok(local_upstream_branch) = branchref.upstream() {
if remote_branch.name()? != local_upstream_branch.name()? { if remote_branch.name()? != local_upstream_branch.name()? {
print_warning(&format!( print_warning(&format!(
"You specified a tracking branch ({}) for an existing branch ({}), but \ "You specified a tracking branch ({}/{}) for an existing branch ({}), but \
it differs from the current upstream ({}). Will keep current upstream" it differs from the current upstream ({}). Will keep current upstream"
, format!("{}/{}", remote_name, remote_branch_name), branchref.name()?, local_upstream_branch.name()?)) , remote_name, remote_branch_name, branchref.name()?, local_upstream_branch.name()?))
} }
} }
} }