use crate::cmd::run_cmd;
use crate::git::{does_git_tag_exist, fetch_git_tags, make_and_push_git_tag};
use crate::package::Package;
use anyhow::{Context, Result};
use cargo_metadata::{Metadata, MetadataCommand};
use crates_index::SparseIndex;
use std::env;
use std::process::Command;
pub fn release_packages(packages: &[Package]) -> Result<()> {
let commit_sha = get_commit_sha()?;
fetch_git_tags()?;
let local_metadata = get_local_package_metadata()?;
let mut index = SparseIndex::new_cargo_default()?;
for package in packages {
auto_release_package(package, &local_metadata, &mut index, &commit_sha)?;
}
Ok(())
}
pub fn auto_release_package(
package: &Package,
local_metadata: &Metadata,
index: &mut SparseIndex,
commit_sha: &str,
) -> Result<()> {
let local_version = get_local_package_version(package, local_metadata)?;
println!("local version of {} is {local_version}", package.name());
if does_crates_io_release_exist(package, &local_version, index)? {
println!(
"{}-{local_version} has already been published",
package.name()
);
} else {
publish_package(package)?;
}
let tag = package.get_git_tag_name(&local_version);
if does_git_tag_exist(&tag)? {
println!("git tag {tag} already exists");
} else {
make_and_push_git_tag(&tag, commit_sha)?;
}
Ok(())
}
pub fn get_commit_sha() -> Result<String> {
let commit_var_name = "GITHUB_SHA";
env::var(commit_var_name).context(format!("failed to get env var {commit_var_name}"))
}
pub fn get_local_package_metadata() -> Result<Metadata> {
let mut cmd = MetadataCommand::new();
cmd.no_deps();
Ok(cmd.exec()?)
}
#[must_use]
pub struct RemoteCrateExists(pub bool);
pub fn update_index(index: &mut SparseIndex, package: &Package) -> Result<RemoteCrateExists> {
let crate_name = package.name();
println!("fetching updates for {}", package.name());
let request: ureq::Request = index.make_cache_request(crate_name).unwrap().into();
match request.call() {
Ok(response) => {
index.parse_cache_response(crate_name, response.into(), true)?;
Ok(RemoteCrateExists(true))
}
Err(ureq::Error::Status(404, _)) => {
println!("packages {} does not exist yet", package.name());
Ok(RemoteCrateExists(false))
}
Err(err) => Err(err.into()),
}
}
pub fn does_crates_io_release_exist(
package: &Package,
local_version: &str,
index: &mut SparseIndex,
) -> Result<bool> {
let remote_versions = get_remote_package_versions(package, index)?;
if remote_versions.contains(&local_version.to_string()) {
return Ok(true);
}
Ok(false)
}
pub fn get_local_package_version(package: &Package, local_metadata: &Metadata) -> Result<String> {
let metadata = local_metadata
.packages
.iter()
.find(|pm| pm.name == package.name())
.context(format!(
"failed to find {} in local metadata",
package.name()
))?;
Ok(metadata.version.to_string())
}
pub fn get_remote_package_versions(
package: &Package,
index: &mut SparseIndex,
) -> Result<Vec<String>> {
let exists = update_index(index, package)?;
if !exists.0 {
return Ok(Vec::new());
}
let cr = index.crate_from_cache(package.name())?;
Ok(cr
.versions()
.iter()
.map(|v| v.version().to_string())
.collect())
}
pub fn publish_package(package: &Package) -> Result<()> {
let mut cmd = Command::new("cargo");
cmd.args(["publish", "--package", package.name()]);
run_cmd(cmd)
}