rlls 0.0.30

Cut a version, tag it, and publish a GitHub Release with raw git notes
use crate::{git, ui};
use anyhow::{anyhow, Result};

fn looks_like_release_commit(s: &str) -> bool {
    let ls = s.to_ascii_lowercase();
    ls.contains("bump")
        || ls.contains("release")
        || ls.starts_with("docs(changelog):")
}

pub async fn run(owner_repo: &str, remote: bool) -> Result<()> {
    ui::section("rollback");

    let tag = git::tag_pointing_at_head();
    if let Some(t) = &tag {
        ui::info(&format!("tag at head: {}", t));
    } else {
        ui::info("no tag at head");
    }

    let last = git::last_commit_subject().unwrap_or_default();
    ui::info(&format!("last commit: {}", last));

    if remote {
        if let Some(t) = &tag {
            let _ = git::delete_github_release(t, owner_repo);
            let _ = git::delete_remote_tag(t);
            ui::ok("remote release and tag removed if they existed");
        }
    }

    if let Some(t) = &tag {
        let _ = git::delete_local_tag(t);
        ui::ok(&format!("deleted local tag {}", t));
    }

    let mut popped = 0usize;
    if looks_like_release_commit(&last) {
        git::reset_hard("HEAD^")?;
        popped += 1;
        ui::ok("reverted last release commit");
        let second = git::last_commit_subject().unwrap_or_default();
        if looks_like_release_commit(&second) {
            git::reset_hard("HEAD^")?;
            popped += 1;
            ui::ok("reverted secondary release commit");
        }
    } else {
        ui::warn("last commit does not look like a release commit; no reset done");
    }

    if remote && popped > 0 {
        if let Some(_br) = git::current_branch() {
            git::push_force_with_lease()?;
            ui::ok("forced branch update to remote");
        } else {
            return Err(anyhow!(
                "failed to detect current branch for remote push"
            ));
        }
    }

    ui::ok("rollback completed");
    Ok(())
}