Skip to content
Merged
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
12 changes: 6 additions & 6 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ pub fn run() -> ExitCode {
let cli = Cli::parse();
let ui = ui::Ui::detect();

let preflight = match env::run_preflight() {
let preflight = match ui.run_step("Checking repository and tools", env::run_preflight) {
Ok(preflight) => preflight,
Err(message) => {
ui.error(&message);
Expand All @@ -60,14 +60,14 @@ pub fn run() -> ExitCode {
};

match cli.command {
Commands::Status => commands::run_status(&preflight),
Commands::New { branch } => commands::run_new(&preflight, &branch),
Commands::Submit { base } => commands::run_submit(&preflight, base.as_deref()),
Commands::Status => commands::run_status(&preflight, ui),
Commands::New { branch } => commands::run_new(&preflight, &branch, ui),
Commands::Submit { base } => commands::run_submit(&preflight, base.as_deref(), ui),
Commands::Sync {
continue_sync,
reset_sync,
} => commands::run_sync(&preflight, continue_sync, reset_sync),
Commands::Push => commands::run_push(&preflight),
} => commands::run_sync(&preflight, continue_sync, reset_sync, ui),
Commands::Push => commands::run_push(&preflight, ui),
}
}

Expand Down
176 changes: 110 additions & 66 deletions src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::stack;
use crate::sync_state::{
self, LastSyncPlan, LocalBranchHead, PushState, RemoteBranchLease, SyncPlanScope, SyncState,
};
use crate::ui;

struct ParentDrift {
parent: String,
Expand Down Expand Up @@ -150,7 +151,7 @@ fn analyze_open_parent_ancestry(
}

/// Print the detected stack, its PR state, and any local follow-up actions.
pub(crate) fn run_status(preflight: &env::PreflightContext) -> ExitCode {
pub(crate) fn run_status(preflight: &env::PreflightContext, ui: ui::Ui) -> ExitCode {
if preflight.current_branch == preflight.default_branch {
println!(
"On default branch ({}). Run `stck new <branch>` to start a new stack.",
Expand All @@ -159,19 +160,20 @@ pub(crate) fn run_status(preflight: &env::PreflightContext) -> ExitCode {
return ExitCode::SUCCESS;
}

if let Err(message) = gitops::fetch_origin() {
if let Err(message) = ui.run_step("Fetching origin", gitops::fetch_origin) {
eprintln!("error: {message}");
return ExitCode::from(1);
}

let stack =
match github::discover_linear_stack(&preflight.current_branch, &preflight.default_branch) {
Ok(stack) => stack,
Err(message) => {
eprintln!("error: {message}");
return ExitCode::from(1);
}
};
let stack = match ui.run_step("Discovering stack", || {
github::discover_linear_stack(&preflight.current_branch, &preflight.default_branch)
}) {
Ok(stack) => stack,
Err(message) => {
eprintln!("error: {message}");
return ExitCode::from(1);
}
};
let mut report = stack::build_status_report(&stack, &preflight.default_branch);
let cached_plan = match sync_state::load_last_sync_plan() {
Ok(plan) => plan,
Expand Down Expand Up @@ -269,7 +271,7 @@ pub(crate) fn run_status(preflight: &env::PreflightContext) -> ExitCode {
}

/// Create the next branch in the stack and bootstrap the current branch PR when needed.
pub(crate) fn run_new(preflight: &env::PreflightContext, new_branch: &str) -> ExitCode {
pub(crate) fn run_new(preflight: &env::PreflightContext, new_branch: &str, ui: ui::Ui) -> ExitCode {
let current_branch = &preflight.current_branch;
let starting_from_default = current_branch == &preflight.default_branch;
let pr_base_branch = if starting_from_default {
Expand Down Expand Up @@ -326,8 +328,10 @@ pub(crate) fn run_new(preflight: &env::PreflightContext, new_branch: &str) -> Ex
};

if !has_upstream {
println!("$ git push -u origin {}", current_branch);
if let Err(message) = gitops::push_set_upstream(current_branch) {
ui.command("git", &format!("push -u origin {current_branch}"));
if let Err(message) = ui.run_step(format!("Publishing {current_branch}"), || {
gitops::push_set_upstream(current_branch)
}) {
eprintln!("error: {message}");
return ExitCode::from(1);
}
Expand All @@ -340,15 +344,19 @@ pub(crate) fn run_new(preflight: &env::PreflightContext, new_branch: &str) -> Ex
}
};
if needs_push {
println!("$ git push origin {}", current_branch);
if let Err(message) = gitops::push_branch(current_branch) {
ui.command("git", &format!("push origin {current_branch}"));
if let Err(message) = ui.run_step(format!("Publishing {current_branch}"), || {
gitops::push_branch(current_branch)
}) {
eprintln!("error: {message}");
return ExitCode::from(1);
}
}
}

let current_has_pr = match github::pr_exists_for_head(current_branch) {
let current_has_pr = match ui.run_step(format!("Checking PR for {current_branch}"), || {
github::pr_exists_for_head(current_branch)
}) {
Ok(exists) => exists,
Err(message) => {
eprintln!("error: {message}");
Expand All @@ -357,35 +365,42 @@ pub(crate) fn run_new(preflight: &env::PreflightContext, new_branch: &str) -> Ex
};

if !current_has_pr {
let bootstrap_base = match discover_parent_base(current_branch) {
let bootstrap_base = match ui
.run_step(format!("Discovering parent for {current_branch}"), || {
discover_parent_base(current_branch)
}) {
Ok(base) => base.unwrap_or_else(|| preflight.default_branch.clone()),
Err(message) => {
eprintln!("error: {message}");
return ExitCode::from(1);
}
};
println!(
"$ gh pr create --base {} --head {} --title {} --body \"<stack context>\"",
bootstrap_base, current_branch, current_branch
ui.command(
"gh",
&format!(
"pr create --base {bootstrap_base} --head {current_branch} --title {current_branch} --body \"<stack context>\""
),
);
let body = github::stack_pr_body(&bootstrap_base, &preflight.default_branch);
if let Err(message) =
if let Err(message) = ui.run_step(format!("Creating PR for {current_branch}"), || {
github::create_pr(&bootstrap_base, current_branch, current_branch, &body)
{
}) {
eprintln!("error: {message}");
return ExitCode::from(1);
}
}
}

println!("$ git checkout -b {}", new_branch);
ui.command("git", &format!("checkout -b {new_branch}"));
if let Err(message) = gitops::checkout_new_branch(new_branch) {
eprintln!("error: {message}");
return ExitCode::from(1);
}

println!("$ git push -u origin {}", new_branch);
if let Err(message) = gitops::push_set_upstream(new_branch) {
ui.command("git", &format!("push -u origin {new_branch}"));
if let Err(message) = ui.run_step(format!("Publishing {new_branch}"), || {
gitops::push_set_upstream(new_branch)
}) {
eprintln!("error: {message}");
return ExitCode::from(1);
}
Expand All @@ -405,12 +420,16 @@ pub(crate) fn run_new(preflight: &env::PreflightContext, new_branch: &str) -> Ex
return ExitCode::SUCCESS;
}

println!(
"$ gh pr create --base {} --head {} --title {} --body \"<stack context>\"",
pr_base_branch, new_branch, new_branch
ui.command(
"gh",
&format!(
"pr create --base {pr_base_branch} --head {new_branch} --title {new_branch} --body \"<stack context>\""
),
);
let body = github::stack_pr_body(pr_base_branch, &preflight.default_branch);
if let Err(message) = github::create_pr(pr_base_branch, new_branch, new_branch, &body) {
if let Err(message) = ui.run_step(format!("Creating PR for {new_branch}"), || {
github::create_pr(pr_base_branch, new_branch, new_branch, &body)
}) {
eprintln!("error: {message}");
return ExitCode::from(1);
}
Expand Down Expand Up @@ -474,6 +493,7 @@ fn discover_parent_base(branch: &str) -> Result<Option<String>, String> {
pub(crate) fn run_submit(
preflight: &env::PreflightContext,
base_override: Option<&str>,
ui: ui::Ui,
) -> ExitCode {
let current_branch = &preflight.current_branch;
if current_branch == &preflight.default_branch {
Expand All @@ -492,8 +512,10 @@ pub(crate) fn run_submit(
}
};
if !has_upstream {
println!("$ git push -u origin {}", current_branch);
if let Err(message) = gitops::push_set_upstream(current_branch) {
ui.command("git", &format!("push -u origin {current_branch}"));
if let Err(message) = ui.run_step(format!("Publishing {current_branch}"), || {
gitops::push_set_upstream(current_branch)
}) {
eprintln!("error: {message}");
return ExitCode::from(1);
}
Expand All @@ -506,15 +528,19 @@ pub(crate) fn run_submit(
}
};
if needs_push {
println!("$ git push origin {}", current_branch);
if let Err(message) = gitops::push_branch(current_branch) {
ui.command("git", &format!("push origin {current_branch}"));
if let Err(message) = ui.run_step(format!("Publishing {current_branch}"), || {
gitops::push_branch(current_branch)
}) {
eprintln!("error: {message}");
return ExitCode::from(1);
}
}
}

let current_has_pr = match github::pr_exists_for_head(current_branch) {
let current_has_pr = match ui.run_step(format!("Checking PR for {current_branch}"), || {
github::pr_exists_for_head(current_branch)
}) {
Ok(exists) => exists,
Err(message) => {
eprintln!("error: {message}");
Expand All @@ -530,7 +556,10 @@ pub(crate) fn run_submit(
let base = if let Some(explicit) = base_override {
explicit
} else {
discovered_base = match discover_parent_base(current_branch) {
discovered_base = match ui
.run_step(format!("Discovering parent for {current_branch}"), || {
discover_parent_base(current_branch)
}) {
Ok(base) => base.unwrap_or_else(|| preflight.default_branch.clone()),
Err(message) => {
eprintln!("error: {message}");
Expand All @@ -551,12 +580,16 @@ pub(crate) fn run_submit(
&discovered_base
};

println!(
"$ gh pr create --base {} --head {} --title {} --body \"<stack context>\"",
base, current_branch, current_branch
ui.command(
"gh",
&format!(
"pr create --base {base} --head {current_branch} --title {current_branch} --body \"<stack context>\""
),
);
let body = github::stack_pr_body(base, &preflight.default_branch);
if let Err(message) = github::create_pr(base, current_branch, current_branch, &body) {
if let Err(message) = ui.run_step(format!("Creating PR for {current_branch}"), || {
github::create_pr(base, current_branch, current_branch, &body)
}) {
eprintln!("error: {message}");
return ExitCode::from(1);
}
Expand Down Expand Up @@ -631,6 +664,7 @@ pub(crate) fn run_sync(
preflight: &env::PreflightContext,
continue_sync: bool,
reset_sync: bool,
ui: ui::Ui,
) -> ExitCode {
let original_branch = preflight.current_branch.clone();

Expand Down Expand Up @@ -684,15 +718,14 @@ pub(crate) fn run_sync(
return ExitCode::from(1);
}

if let Err(message) = gitops::fetch_origin() {
if let Err(message) = ui.run_step("Fetching origin", gitops::fetch_origin) {
eprintln!("error: {message}");
return ExitCode::from(1);
}

let stack = match github::discover_linear_stack(
&preflight.current_branch,
&preflight.default_branch,
) {
let stack = match ui.run_step("Discovering stack", || {
github::discover_linear_stack(&preflight.current_branch, &preflight.default_branch)
}) {
Ok(stack) => stack,
Err(message) => {
eprintln!("error: {message}");
Expand Down Expand Up @@ -930,11 +963,16 @@ pub(crate) fn run_sync(
step.old_base_ref
);
}
println!(
"$ git rebase --onto {} {} {}",
onto_ref, old_base_sha, step.branch
ui.command(
"git",
&format!(
"rebase --onto {} {} {}",
onto_ref, old_base_sha, step.branch
),
);
if let Err(message) = gitops::rebase_onto(&onto_ref, &old_base_sha, &step.branch) {
if let Err(message) = ui.run_step(format!("Rebasing {}", step.branch), || {
gitops::rebase_onto(&onto_ref, &old_base_sha, &step.branch)
}) {
state.failed_step = Some(index);
state.failed_step_branch_head = Some(branch_head);
if let Err(save_error) = sync_state::save_sync(&state) {
Expand Down Expand Up @@ -962,7 +1000,7 @@ pub(crate) fn run_sync(
}
}

println!("$ git checkout {}", original_branch);
ui.command("git", &format!("checkout {original_branch}"));
if let Err(message) = gitops::checkout_branch(&original_branch) {
if let Err(clear_error) = sync_state::clear() {
eprintln!("error: {clear_error}");
Expand Down Expand Up @@ -1123,20 +1161,21 @@ fn first_push_parent_drift(
}

/// Push rewritten stack branches and retarget any affected pull requests.
pub(crate) fn run_push(preflight: &env::PreflightContext) -> ExitCode {
if let Err(message) = gitops::fetch_origin() {
pub(crate) fn run_push(preflight: &env::PreflightContext, ui: ui::Ui) -> ExitCode {
if let Err(message) = ui.run_step("Fetching origin", gitops::fetch_origin) {
eprintln!("error: {message}");
return ExitCode::from(1);
}

let stack =
match github::discover_linear_stack(&preflight.current_branch, &preflight.default_branch) {
Ok(stack) => stack,
Err(message) => {
eprintln!("error: {message}");
return ExitCode::from(1);
}
};
let stack = match ui.run_step("Discovering stack", || {
github::discover_linear_stack(&preflight.current_branch, &preflight.default_branch)
}) {
Ok(stack) => stack,
Err(message) => {
eprintln!("error: {message}");
return ExitCode::from(1);
}
};

let existing_state = match sync_state::load_push() {
Ok(state) => state,
Expand Down Expand Up @@ -1339,10 +1378,10 @@ pub(crate) fn run_push(preflight: &env::PreflightContext) -> ExitCode {
"--force-with-lease=refs/heads/{branch}:{}",
expected_remote_head.as_deref().unwrap_or_default()
);
println!("$ git push {lease} origin {branch}");
if let Err(message) =
ui.command("git", &format!("push {lease} origin {branch}"));
if let Err(message) = ui.run_step(format!("Pushing {branch}"), || {
gitops::push_force_with_lease(&branch, expected_remote_head.as_deref())
{
}) {
if let Err(save_error) = sync_state::save_push(&state) {
eprintln!("error: {save_error}");
return ExitCode::from(1);
Expand Down Expand Up @@ -1370,11 +1409,16 @@ pub(crate) fn run_push(preflight: &env::PreflightContext) -> ExitCode {
retarget.branch,
retarget.new_base_ref
);
println!(
"$ gh pr edit {} --base {}",
retarget.branch, retarget.new_base_ref
ui.command(
"gh",
&format!(
"pr edit {} --base {}",
retarget.branch, retarget.new_base_ref
),
);
if let Err(message) = github::retarget_pr_base(&retarget.branch, &retarget.new_base_ref) {
if let Err(message) = ui.run_step(format!("Retargeting PR for {}", retarget.branch), || {
github::retarget_pr_base(&retarget.branch, &retarget.new_base_ref)
}) {
if let Err(save_error) = sync_state::save_push(&state) {
eprintln!("error: {save_error}");
return ExitCode::from(1);
Expand Down
Loading
Loading