Skip to content

Commit 8077a21

Browse files
authored
feat: add setup subcommand to automate prepare-commit-msg githook setup (#58)
* feat: add setup subcommand Automate the setup of prepare-commit-msg githook which will append Co-authored-by trailers to the commit msg Updates typos and mistakes in the CLI help information * test(setup): add integration tests for setup subcommand Also add additional test for prepare-commit-msg local hook * docs(readme): add instructions for automatic setup of githook Extract and update manual setup documentation * refactor(setup): import std::fs::Permissions only in unix Its not used on windows * fix(setup): print paths in setup output correctly * refactor(setup): use home crate instead of dirs to get home dir Make setup integration tests work on windows This is testable on windows because it looks for USERPROFILE env first before invoking the SHGetKnownFolderPath function with FOLDERID_Profile * ci: fix generation of test coverage for setup.rs Use cargo-llvm-cov instead of grcov The trade-off is that this will count unit tests as part of coverage at this moment
1 parent aa903cc commit 8077a21

File tree

17 files changed

+828
-58
lines changed

17 files changed

+828
-58
lines changed

.github/workflows/build.yml

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -86,25 +86,15 @@ jobs:
8686
target/
8787
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
8888

89-
- name: Run cargo test
90-
run: cargo test --locked
91-
env:
92-
CARGO_INCREMENTAL: 0
93-
RUSTFLAGS: "-Cinstrument-coverage"
94-
LLVM_PROFILE_FILE: "git_mob_tool-%p-%m.profraw"
95-
96-
- name: Download grcov
97-
run: |
98-
mkdir -p "${HOME}/.local/bin"
99-
curl -sL https://github.com/mozilla/grcov/releases/download/v0.8.19/grcov-x86_64-unknown-linux-gnu.tar.bz2 | tar jxf - -C "${HOME}/.local/bin"
100-
echo "$HOME/.local/bin" >> $GITHUB_PATH
101-
102-
- name: Generate coverage report
103-
run: grcov . -s . --binary-path ./target/debug/ -t lcov --branch --ignore-not-existing --ignore "/*" --ignore "tests/**/*" --ignore src/lib.rs --excl-br-start "mod tests \{" --excl-start "mod tests \{" -o target/tests.lcov
89+
- name: Install cargo-llvm-cov
90+
uses: taiki-e/install-action@cargo-llvm-cov
91+
92+
- name: Generate code coverage
93+
run: cargo llvm-cov --all-features --workspace --lcov --output-path lcov.info
10494

10595
- name: Upload coverage report to Codecov
10696
uses: codecov/codecov-action@v4
10797
with:
10898
token: ${{ secrets.CODECOV_TOKEN }}
109-
files: ./target/tests.lcov
99+
files: lcov.info
110100
fail_ci_if_error: true

Cargo.lock

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ path = "src/main.rs"
2929

3030
[dependencies]
3131
clap = { version = "4.5.4", features = ["derive"] }
32+
home = "0.5.9"
3233
inquire = "0.6.2"
3334

3435
[dev-dependencies]

README.md

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -44,34 +44,26 @@ This CLI app will help you add them automatically and also help you store and ma
4444
$ cargo install git-mob-tool
4545
```
4646

47-
## Configuration
47+
## Setup & Configuration
4848

49-
- Store your team members' details with keys
49+
- Set up a global [`prepare-commit-msg`](https://git-scm.com/docs/githooks#_prepare_commit_msg) githook which appends the `Co-authored-by` trailers to the commit message.
5050

5151
```console
52-
$ git mob coauthor --add lm "Leo Messi" [email protected]
53-
$ git mob coauthor --add em "Emi Martinez" [email protected]
54-
$ git mob coauthor --add sa "Sergio Aguero" [email protected]
52+
$ git mob setup --global
5553
```
5654

57-
- Set a global [`githooks`](https://git-scm.com/docs/githooks) directory
55+
If a repository overrides `core.hooksPath` git configuration variable (e.g when using husky), then you will additionally need to run `git mob setup --local` for each such repository. This will set up a local (repository-specific) `prepare-commit-msg` githook which invokes the global one.
5856

59-
```console
60-
$ mkdir ~/git
61-
$ git config --global core.hooksPath "~/git"
62-
```
57+
_If you prefer to set this up manually or encounter any issues with the automated setup process, you can follow steps outlined [here.](./docs/manual_setup.md)_
6358

64-
- Download the [`prepare-commit-msg`](./prepare-commit-msg) file into the directory
65-
- Ensure it is set as executable (Linux and macOS)
59+
- Store your team members' details with keys
6660

67-
```console
68-
$ chmod +x ./prepare-commit-msg
61+
```console
62+
$ git mob coauthor --add lm "Leo Messi" [email protected]
63+
$ git mob coauthor --add em "Emi Martinez" [email protected]
64+
$ git mob coauthor --add sa "Sergio Aguero" [email protected]
6965
```
7066

71-
This `githook` will be used to append the `Co-authored-by` trailers to the commit's message.
72-
73-
_This githook also adds a Jira Issue ID as a prefix to the commit message if the branch name starts with a string resembling one. If you don't want want this, comment out [line 12 which calls the function `add_jira_issue_id_prefix`](./prepare-commit-msg#LL12)._
74-
7567
## Usage
7668

7769
- To mob with some team member(s):

docs/manual_setup.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Manual Setup
2+
3+
- Set a global [githooks](https://git-scm.com/docs/githooks) directory
4+
5+
```console
6+
$ mkdir -p ~/.git/hooks
7+
$ git config --global core.hooksPath "~/.git/hooks"
8+
```
9+
10+
- Download the [`prepare-commit-msg`](../src/commands/prepare-commit-msg) file into the directory
11+
- Ensure it is set as executable (Linux and macOS)
12+
13+
```console
14+
$ chmod +x ./prepare-commit-msg
15+
```
16+
17+
This githook will append the `Co-authored-by` trailers to the commit message.
18+
19+
_If you want this githook to add a Jira Issue ID as a prefix to the commit message when the git branch name begins with a string resembling one, uncomment [line 12 to call the function `add_jira_issue_id_prefix`](../src/commands/prepare-commit-msg#LL12)._
20+
21+
## If a repository overrides `core.hooksPath` git configuration variable (e.g when using husky), then you will need to do additional steps for each such repository
22+
23+
- Retrieve the local (repository-specific) hooks directory
24+
25+
```console
26+
$ git config --local core.hooksPath
27+
```
28+
29+
- Download the [`prepare-commit-msg.local`](../src/commands/prepare-commit-msg.local) as `prepare-commit-msg` file into the directory
30+
- Ensure it is set as executable (Linux and macOS)
31+
32+
```console
33+
$ chmod +x ./prepare-commit-msg
34+
```
35+
36+
This githook will invoke the global `prepare-commit-msg` githook that you originally set up.

src/cli.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::coauthor_repo::CoauthorRepo;
2-
use crate::commands::{coauthor::Coauthor, mob::Mob};
2+
use crate::commands::{coauthor::Coauthor, mob::Mob, setup::Setup};
33
use clap::{Parser, Subcommand};
44
use std::error::Error;
55
use std::io::Write;
@@ -26,9 +26,11 @@ use std::str;
2626
///
2727
/// Usage example:
2828
///
29-
/// git mob co-author --add lm "Leo Messi" [email protected]
29+
/// git mob setup --global
3030
///
31-
/// git pair with
31+
/// git mob coauthor --add lm "Leo Messi" [email protected]
32+
///
33+
/// git mob --with lm
3234
struct Cli {
3335
#[command(subcommand)]
3436
command: Option<Commands>,
@@ -38,6 +40,8 @@ struct Cli {
3840

3941
#[derive(Subcommand)]
4042
enum Commands {
43+
/// Create prepare-commit-msg githook which append Co-authored-by trailers to commit message
44+
Setup(Setup),
4145
/// Add/delete/list co-author(s) from co-author repository
4246
///
4347
/// User must store co-author(s) to co-author repository by using keys
@@ -57,6 +61,7 @@ fn run_inner(
5761
) -> Result<(), Box<dyn Error>> {
5862
match &cli.command {
5963
None => cli.mob.handle(coauthor_repo, out)?,
64+
Some(Commands::Setup(setup)) => setup.handle(out)?,
6065
Some(Commands::Coauthor(coauthor)) => coauthor.handle(coauthor_repo, out)?,
6166
}
6267
Ok(())

src/coauthor_repo.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ mod tests {
181181
Ok(CmdOutput {
182182
stdout: stdout.clone(),
183183
stderr: stderr.clone(),
184-
status_code: status_code.clone(),
184+
status_code,
185185
})
186186
});
187187
mock_cmd_runner
@@ -632,7 +632,7 @@ mod tests {
632632
command_runner
633633
.expect_execute()
634634
.once()
635-
.withf(|program, args| program == "git" && args == &["config", "--global", "--get-all", "coauthors-mob.entry"])
635+
.withf(|program, args| program == "git" && args == ["config", "--global", "--get-all", "coauthors-mob.entry"])
636636
.returning(|_, _| {
637637
Ok(CmdOutput {
638638
stdout: b"Leo Messi <[email protected]>\nEmi Martinez <[email protected]>\n".into(),
@@ -645,7 +645,7 @@ mod tests {
645645
.once()
646646
.withf(|program, args| {
647647
program == "git"
648-
&& args == &["config", "--global", "--remove-section", "coauthors-mob"]
648+
&& args == ["config", "--global", "--remove-section", "coauthors-mob"]
649649
})
650650
.returning(|_, _| {
651651
Ok(CmdOutput {
@@ -681,7 +681,7 @@ mod tests {
681681
command_runner
682682
.expect_execute()
683683
.once()
684-
.withf( |program, args| program == "git" && args == &["config", "--global", "--get-all", "coauthors-mob.entry"])
684+
.withf( |program, args| program == "git" && args == ["config", "--global", "--get-all", "coauthors-mob.entry"])
685685
.returning( |_, _| {
686686
Ok(CmdOutput {
687687
stdout: b"Leo Messi <[email protected]>\nEmi Martinez <[email protected]>\n".into(),
@@ -694,7 +694,7 @@ mod tests {
694694
.once()
695695
.withf(|program, args| {
696696
program == "git"
697-
&& args == &["config", "--global", "--remove-section", "coauthors-mob"]
697+
&& args == ["config", "--global", "--remove-section", "coauthors-mob"]
698698
})
699699
.returning(|_, _| {
700700
Ok(CmdOutput {
@@ -719,7 +719,7 @@ mod tests {
719719
command_runner
720720
.expect_execute()
721721
.once()
722-
.withf( |program, args| program == "git" && args == &["config", "--global", "--get-all", "coauthors-mob.entry"])
722+
.withf( |program, args| program == "git" && args == ["config", "--global", "--get-all", "coauthors-mob.entry"])
723723
.returning( |_, _| {
724724
Ok(CmdOutput {
725725
stdout: b"Leo Messi <[email protected]>\nEmi Martinez <[email protected]>\n".into(),
@@ -732,7 +732,7 @@ mod tests {
732732
.once()
733733
.withf(|program, args| {
734734
program == "git"
735-
&& args == &["config", "--global", "--remove-section", "coauthors-mob"]
735+
&& args == ["config", "--global", "--remove-section", "coauthors-mob"]
736736
})
737737
.returning(|_, _| {
738738
Ok(CmdOutput {

src/commands/coauthor.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,17 @@ use std::io::Write;
77
pub(crate) struct Coauthor {
88
/// Adds co-author to co-author repository
99
///
10-
/// Usage example: git mob co-author --add lm "Leo Messi" [email protected]
10+
/// Usage example: git mob coauthor --add lm "Leo Messi" [email protected]
1111
#[arg(short = 'a', long = "add", num_args=3, value_names=["COAUTHOR_KEY", "COAUTHOR_NAME", "COAUTHOR_EMAIL"])]
1212
pub(crate) add: Option<Vec<String>>,
1313
/// Remove co-author from co-author repository
1414
///
15-
/// Usage example: git mob co-author --delete lm
15+
/// Usage example: git mob coauthor --delete lm
1616
#[arg(short = 'd', long = "delete", value_name = "COAUTHOR_KEY")]
1717
pub(crate) delete: Option<String>,
1818
/// Lists co-author(s) with keys(s) from co-author repository
1919
///
20-
/// Usage example: git mob co-author --list
20+
/// Usage example: git mob coauthor --list
2121
#[arg(short = 'l', long = "list")]
2222
pub(crate) list: bool,
2323
}

src/commands/mob.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,8 +257,9 @@ mod tests {
257257
let mut out = Vec::new();
258258
let result = mob_cmd.handle(&mock_coauthor_repo, &mut out);
259259

260-
assert!(result.is_err_and(|err| err.to_string()
261-
== "No co-author(s) found. At least one co-author must be added".to_string()));
260+
assert!(result
261+
.is_err_and(|err| err.to_string()
262+
== *"No co-author(s) found. At least one co-author must be added"));
262263

263264
Ok(())
264265
}

src/commands/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
pub(crate) mod coauthor;
22
pub(crate) mod mob;
3+
pub(crate) mod setup;

0 commit comments

Comments
 (0)