Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions crates/ticgit/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ use crate::commands;
\x1b[1;36mSync & Setup:\x1b[0m
sync Sync ticket metadata with a Git remote
pull Pull tickets from a fork or remote URL
push Push ticket metadata to a Git remote
init Initialise ticgit on the current repo
setup Configure git-meta remote from .git-meta
update Update ti to the latest release
Expand Down Expand Up @@ -215,6 +216,9 @@ pub enum Command {
/// Pull tickets from a fork or remote URL.
Pull(commands::pull::Args),

/// Push ticket metadata to a Git remote.
Push(commands::push::Args),

/// Initialise ticgit metadata on the current repo (idempotent).
Init,

Expand Down Expand Up @@ -277,6 +281,7 @@ pub fn run(cli: Cli) -> anyhow::Result<()> {
Some(Command::Users(args)) => commands::users::run(args),
Some(Command::Sync(args)) => commands::sync::run_sync(args),
Some(Command::Pull(args)) => commands::pull::run(args),
Some(Command::Push(args)) => commands::push::run(args),
Some(Command::Update(args)) => commands::update::run(args),
}
}
1 change: 1 addition & 0 deletions crates/ticgit/src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub mod next;
pub mod points;
pub mod priority;
pub mod pull;
pub mod push;
pub mod recent;
pub mod review;
pub mod setup;
Expand Down
39 changes: 39 additions & 0 deletions crates/ticgit/src/commands/push.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use anyhow::Result;
use clap::Parser;

use crate::commands::open_store;
use crate::commands::sync::{meta_namespace, remote_url, ssh_project_web_url, sync_remote};

#[derive(Debug, Parser)]
pub struct Args {
/// Remote to push to. Defaults to git-meta's first configured meta remote.
#[arg(short = 'r', long = "remote")]
pub remote: Option<String>,
}

pub fn run(args: Args) -> Result<()> {
let store = open_store()?;
let remote = sync_remote(args.remote.as_deref())?;
let namespace = meta_namespace()?;
let url = remote
.as_deref()
.map(remote_url)
.transpose()?
.unwrap_or_else(|| "(none)".to_string());

if let Some(remote) = &remote {
println!("Remote: {remote}");
}
println!("Ref: refs/{namespace}/main");
println!("URL: {url}");
if let Some(web_url) = ssh_project_web_url(&url) {
println!("Web URL: {web_url}");
}

store.push(args.remote.as_deref())?;

let total = store.list()?.len();
println!("Push: {total} ticket(s) synced.");
println!("Done.");
Ok(())
}
8 changes: 4 additions & 4 deletions crates/ticgit/src/commands/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ pub fn run_sync(args: Args) -> Result<()> {
Ok(())
}

fn sync_remote(explicit: Option<&str>) -> Result<Option<String>> {
pub(crate) fn sync_remote(explicit: Option<&str>) -> Result<Option<String>> {
if let Some(remote) = explicit {
return Ok(Some(remote.to_string()));
}
Expand All @@ -94,15 +94,15 @@ fn sync_remote(explicit: Option<&str>) -> Result<Option<String>> {
Ok(remotes.into_iter().next())
}

fn meta_namespace() -> Result<String> {
pub(crate) fn meta_namespace() -> Result<String> {
Ok(git_config_get("meta.namespace")?.unwrap_or_else(|| "meta".to_string()))
}

fn remote_url(remote: &str) -> Result<String> {
pub(crate) fn remote_url(remote: &str) -> Result<String> {
git_output(&["remote", "get-url", remote])
}

fn ssh_project_web_url(url: &str) -> Option<String> {
pub(crate) fn ssh_project_web_url(url: &str) -> Option<String> {
let (host, path) = if let Some(rest) = url.strip_prefix("git@") {
rest.split_once(':')?
} else if let Some(rest) = url.strip_prefix("ssh://git@") {
Expand Down
26 changes: 24 additions & 2 deletions crates/ticgit/tests/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -546,14 +546,14 @@ fn version_flags_print_cargo_version() {
}

#[test]
fn help_lists_sync_and_pull_but_not_push() {
fn help_lists_sync_pull_and_push() {
let mut cmd = assert_cmd::Command::cargo_bin("ti").expect("ti binary");
cmd.arg("--help")
.assert()
.success()
.stdout(predicate::str::contains("sync"))
.stdout(predicate::str::contains("pull"))
.stdout(predicate::str::contains(" push ").not());
.stdout(predicate::str::contains("push"));
}

#[test]
Expand Down Expand Up @@ -657,6 +657,28 @@ fn sync_prints_remote_url_and_ref() {
.stdout(predicate::str::contains(format!("URL: {remote_url}")));
}

#[test]
fn push_sends_tickets_to_bare_remote() {
let repo = TestRepo::new();
let remote = tempfile::tempdir().expect("bare remote tempdir");
git(remote.path(), &["init", "--bare", "--quiet"]);
let remote_url = remote.path().to_string_lossy().to_string();

git(repo.dir.path(), &["remote", "add", "origin", &remote_url]);
repo.ti().arg("init").assert().success();
create_ticket(&repo, "pushed ticket");

repo.ti()
.arg("push")
.assert()
.success()
.stdout(predicate::str::contains("Remote: origin"))
.stdout(predicate::str::contains("Ref: refs/meta/main"))
.stdout(predicate::str::contains(format!("URL: {remote_url}")))
.stdout(predicate::str::contains("1 ticket(s) synced"))
.stdout(predicate::str::contains("Done."));
}

#[test]
fn new_show_and_list_round_trip() {
let repo = TestRepo::new();
Expand Down