Skip to content

Commit

Permalink
added generate-artifactory-path command to foreman
Browse files Browse the repository at this point in the history
  • Loading branch information
afujiwara-roblox committed Oct 17, 2023
1 parent d169fdb commit beedab7
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 5 deletions.
2 changes: 2 additions & 0 deletions src/artifact_choosing.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//After updating this file, consider updating artifactory_path.rs to reflect the operating systems and architectures that Foreman recognizes

#[cfg(all(target_os = "windows", target_arch = "x86_64"))]
static PLATFORM_KEYWORDS: &[&str] = &["win64", "windows-x86_64", "windows"];

Expand Down
150 changes: 150 additions & 0 deletions src/artifactory_path.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
use crate::error::{ForemanError, ForemanResult};
use semver::Version;
use std::io::{Error, ErrorKind};

// Redundant operating systems that Foreman recognizes are not included;
static VALID_OS: &[&str] = &["windows", "macos", "linux"];
static VALID_ARCH: &[&str] = &["x86_64", "arm64", "aarch64", "i686"];

pub fn generate_artifactory_path<S: Into<String>>(
repo: S,
tool_name: S,
version: S,
operating_system: S,
architecture: Option<S>,
) -> ForemanResult<String> {
let repo = repo.into();
let tool_name = tool_name.into();
let version = version.into();
let operating_system = operating_system.into();

check_valid_os(&operating_system)?;
check_valid_version(&version)?;
let mut full_tool_name = format!("{}-{}-{}", tool_name, version, operating_system);
if let Some(architecture) = architecture {
let architecture = architecture.into();
check_valid_arch(&architecture)?;
full_tool_name.push('-');
full_tool_name.push_str(&architecture);
}

full_tool_name.push_str(".zip");

Ok(format!(
"artifactory/{}/{}/{}/{}",
repo, tool_name, version, full_tool_name
))
}

fn check_valid_os(operating_system: &str) -> ForemanResult<()> {
if !VALID_OS.contains(&operating_system) {
return Err(ForemanError::io_error_with_context(
Error::new(ErrorKind::InvalidInput, "Invalid Argument"),
format!(
"Invalid operating system: {}. Please input a valid operating system: {}",
operating_system,
VALID_OS.join(", ")
),
));
} else {
Ok(())
}
}

fn check_valid_arch(architecture: &str) -> ForemanResult<()> {
if !VALID_ARCH.contains(&architecture) {
return Err(ForemanError::io_error_with_context(
Error::new(ErrorKind::InvalidInput, "Invalid Argument"),
format!(
"Invalid architecture: {}. Please input a valid architecture: {}",
architecture,
VALID_ARCH.join(", ")
),
));
} else {
Ok(())
}
}

fn check_valid_version(version: &str) -> ForemanResult<()> {
if !version.starts_with('v') {
return Err(ForemanError::io_error_with_context(
Error::new(ErrorKind::InvalidInput, "Invalid Argument"),
format!("Invalid version: {}. Versions must start with a v", version),
));
}

if let Err(err) = Version::parse(&version[1..]) {
Err(ForemanError::io_error_with_context(
Error::new(ErrorKind::InvalidInput, "Invalid Argument"),
format!("Invalid version: {}. Error: {}", version, err),
))
} else {
Ok(())
}
}

#[cfg(test)]
mod test {
use super::generate_artifactory_path;

#[test]
fn simple_path() {
let path = generate_artifactory_path("repo", "tool_name", "v0.1.0", "macos", None).unwrap();
assert_eq!(
path,
"artifactory/repo/tool_name/v0.1.0/tool_name-v0.1.0-macos.zip"
);
}

#[test]
fn simple_path_with_arch() {
let path = generate_artifactory_path("repo", "tool_name", "v0.1.0", "macos", Some("arm64"))
.unwrap();
assert_eq!(
path,
"artifactory/repo/tool_name/v0.1.0/tool_name-v0.1.0-macos-arm64.zip"
);
}

#[test]
fn invalid_version_no_v() {
let path = generate_artifactory_path("repo", "tool_name", "0.1.0", "macos", Some("arm64"))
.unwrap_err();
assert_eq!(
path.to_string(),
"Invalid version: 0.1.0. Versions must start with a v: Invalid Argument".to_string()
);
}
#[test]
fn invalid_version_incomplete() {
let path = generate_artifactory_path("repo", "tool_name", "v0.1", "macos", Some("arm64"))
.unwrap_err();
assert_eq!(
path.to_string(),
"Invalid version: v0.1. Error: unexpected end of input while parsing minor version number: Invalid Argument".to_string()
);
}

#[test]
fn invalid_operating_system() {
let path =
generate_artifactory_path("repo", "tool_name", "v0.1.0", "fake_os", Some("arm64"))
.unwrap_err();
assert_eq!(
path.to_string(),
"Invalid operating system: fake_os. Please input a valid operating system: windows, macos, linux: Invalid Argument".to_string()
);
}

#[test]
fn invalid_architecture() {
let path =
generate_artifactory_path("repo", "tool_name", "v0.1.0", "macos", Some("fake_arch"))
.unwrap_err();
assert_eq!(
path.to_string(),
"Invalid architecture: fake_arch. Please input a valid architecture: x86_64, arm64, aarch64, i686: Invalid Argument".to_string()
);
}
}
26 changes: 26 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod aliaser;
mod artifact_choosing;
mod artifactory_path;
mod auth_store;
mod ci_string;
mod config;
Expand Down Expand Up @@ -151,6 +152,12 @@ enum Subcommand {
/// This token can also be configured by editing ~/.foreman/auth.toml.
#[structopt(name = "gitlab-auth")]
GitLabAuth(GitLabAuthCommand),

/// Create a path to publish to artifactory
///
/// Foreman does not support uploading the artifactory itself but will generate the path it expects to find artifacts.
#[structopt(name = "generate-artifactory-path")]
GenerateArtifactoryPath(GenerateArtifactoryPathCommand),
}

#[derive(Debug, StructOpt)]
Expand All @@ -169,6 +176,15 @@ struct GitLabAuthCommand {
token: Option<String>,
}

#[derive(Debug, StructOpt)]
struct GenerateArtifactoryPathCommand {
repo: String,
tool_name: String,
version: String,
operating_system: String,
architecture: Option<String>,
}

fn actual_main(paths: ForemanPaths) -> ForemanResult<()> {
let options = Options::from_args();

Expand Down Expand Up @@ -271,6 +287,16 @@ fn actual_main(paths: ForemanPaths) -> ForemanResult<()> {

println!("GitLab auth saved successfully.");
}
Subcommand::GenerateArtifactoryPath(subcommand) => {
let artifactory_path = artifactory_path::generate_artifactory_path(
subcommand.repo,
subcommand.tool_name,
subcommand.version,
subcommand.operating_system,
subcommand.architecture,
)?;
println!("{}", artifactory_path);
}
}

Ok(())
Expand Down
11 changes: 6 additions & 5 deletions tests/snapshots/help_command.snap
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ FLAGS:
-v Logging verbosity. Supply multiple for more verbosity, up to -vvv

SUBCOMMANDS:
github-auth Set the GitHub Personal Access Token that Foreman should use with the GitHub API
gitlab-auth Set the GitLab Personal Access Token that Foreman should use with the GitLab API
help Prints this message or the help of the given subcommand(s)
install Install tools defined by foreman.toml
list List installed tools
generate-artifactory-path Create a path to publish to artifactory
github-auth Set the GitHub Personal Access Token that Foreman should use with the GitHub API
gitlab-auth Set the GitLab Personal Access Token that Foreman should use with the GitLab API
help Prints this message or the help of the given subcommand(s)
install Install tools defined by foreman.toml
list List installed tools


0 comments on commit beedab7

Please sign in to comment.