fledge 1.1.1

Dev lifecycle CLI. One tool for the dev loop, any language.
use anyhow::{Context, Result};
use console::style;
use std::fs;
use std::path::Path;

use super::{validate_plugin, PluginManifest, PLUGINS_PUBLISH_SCHEMA};

pub(crate) fn publish_plugin(
    path: &Path,
    org: Option<&str>,
    private: bool,
    description: Option<&str>,
    yes: bool,
    json: bool,
) -> Result<()> {
    let yes = yes || crate::utils::is_non_interactive() || json;
    let config = crate::config::Config::load()?;
    let token = config.github_token().ok_or_else(|| {
        anyhow::anyhow!(
            "No GitHub token configured. Run: fledge config set github.token <your-token>"
        )
    })?;

    let path = path
        .canonicalize()
        .with_context(|| format!("Directory not found: {}", path.display()))?;

    let manifest_path = path.join("plugin.toml");
    validate_plugin(&path, false, false)?;

    let content = fs::read_to_string(&manifest_path).context("reading plugin.toml")?;
    let manifest: PluginManifest = toml::from_str(&content).context("Invalid plugin.toml")?;

    let repo_name = &manifest.plugin.name;
    let desc = description
        .or(manifest.plugin.description.as_deref())
        .unwrap_or("A fledge plugin");

    let owner = match org {
        Some(o) => o.to_string(),
        None => crate::publish::get_authenticated_user(&token)?,
    };

    if !json {
        println!(
            "{} Publishing plugin {} as {}/{}",
            style("➡️").cyan().bold(),
            style(path.display()).dim(),
            style(&owner).green(),
            style(repo_name).green()
        );
    }

    let sp = if json {
        None
    } else {
        Some(crate::spinner::Spinner::start("Checking repository:"))
    };
    let repo_exists = crate::publish::check_repo_exists(&owner, repo_name, &token)?;
    if let Some(s) = sp {
        s.finish();
    }

    let mut created_repo = false;
    if repo_exists {
        if !yes {
            crate::utils::require_interactive("yes")?;
            let confirm =
                dialoguer::Confirm::with_theme(&dialoguer::theme::ColorfulTheme::default())
                    .with_prompt(format!(
                        "Repository {}/{} already exists. Push update?",
                        owner, repo_name
                    ))
                    .default(false)
                    .interact()?;

            if !confirm {
                if json {
                    let result = serde_json::json!({
                        "schema_version": PLUGINS_PUBLISH_SCHEMA,
                        "action": "publish",
                        "cancelled": true,
                        "repo": {
                            "owner": owner,
                            "name": repo_name,
                            "url": format!("https://github.com/{owner}/{repo_name}"),
                            "created": false,
                            "private": private,
                        },
                        "plugin": {
                            "name": manifest.plugin.name,
                            "version": manifest.plugin.version,
                            "description": desc,
                        },
                        "topic": "fledge-plugin",
                        "install_hint": format!("fledge plugins install {owner}/{repo_name}"),
                    });
                    println!("{}", serde_json::to_string_pretty(&result)?);
                } else {
                    println!("{} Cancelled.", style("*").cyan().bold());
                }
                return Ok(());
            }
        }
    } else {
        let sp = if json {
            None
        } else {
            Some(crate::spinner::Spinner::start("Creating repository:"))
        };
        crate::publish::create_github_repo(repo_name, desc, private, org, &token)?;
        if let Some(s) = sp {
            s.finish();
        }
        created_repo = true;
        if !json {
            println!(
                "  {} Created repository {}/{}",
                style("").green().bold(),
                owner,
                repo_name
            );
        }
    }

    let sp = if json {
        None
    } else {
        Some(crate::spinner::Spinner::start("Setting repository topics:"))
    };
    crate::publish::set_repo_topic(&owner, repo_name, "fledge-plugin", &token)?;
    if let Some(s) = sp {
        s.finish();
    }
    if !json {
        println!(
            "  {} Set {} topic",
            style("").green().bold(),
            style("fledge-plugin").cyan()
        );
    }

    let sp = if json {
        None
    } else {
        Some(crate::spinner::Spinner::start("Pushing plugin files:"))
    };
    crate::publish::push_directory(&path, &owner, repo_name, &token)?;
    if let Some(s) = sp {
        s.finish();
    }

    if json {
        let result = serde_json::json!({
            "schema_version": PLUGINS_PUBLISH_SCHEMA,
            "action": "publish",
            "cancelled": false,
            "repo": {
                "owner": owner,
                "name": repo_name,
                "url": format!("https://github.com/{owner}/{repo_name}"),
                "created": created_repo,
                "private": private,
            },
            "plugin": {
                "name": manifest.plugin.name,
                "version": manifest.plugin.version,
                "description": desc,
            },
            "topic": "fledge-plugin",
            "install_hint": format!("fledge plugins install {owner}/{repo_name}"),
        });
        println!("{}", serde_json::to_string_pretty(&result)?);
    } else {
        println!("  {} Pushed plugin files", style("").green().bold());
        println!(
            "\n{} Published! Install with:\n\n  {}",
            style("").green().bold(),
            style(format!("fledge plugins install {}/{}", owner, repo_name)).cyan()
        );
    }

    Ok(())
}