Skip to content

Commit 4ccab50

Browse files
APT-548 added generate-artifactory-path command to foreman (#84)
This Foreman command generates a path that can be used to upload artifacts to Artifactory. --------- Co-authored-by: Paul Doyle <37384169+ZoteTheMighty@users.noreply.github.com>
1 parent d169fdb commit 4ccab50

File tree

5 files changed

+185
-6
lines changed

5 files changed

+185
-6
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ jobs:
3030

3131
build:
3232
needs: checks
33-
timeout-minutes: 10
33+
timeout-minutes: 15
3434
strategy:
3535
matrix:
3636
os: [windows-latest, ubuntu-latest]

src/artifact_choosing.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
//After updating this file, consider updating artifactory_path.rs to reflect the operating systems and architectures that Foreman recognizes
2+
13
#[cfg(all(target_os = "windows", target_arch = "x86_64"))]
24
static PLATFORM_KEYWORDS: &[&str] = &["win64", "windows-x86_64", "windows"];
35

src/artifactory_path.rs

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
use crate::error::{ForemanError, ForemanResult};
2+
use semver::Version;
3+
use std::io::{Error, ErrorKind};
4+
5+
// Redundant operating systems that Foreman recognizes are not included;
6+
static VALID_OS: &[&str] = &["windows", "macos", "linux"];
7+
static VALID_ARCH: &[&str] = &["x86_64", "arm64", "aarch64", "i686"];
8+
9+
pub fn generate_artifactory_path<S: Into<String>>(
10+
repo: S,
11+
tool_name: S,
12+
version: S,
13+
operating_system: S,
14+
architecture: Option<S>,
15+
) -> ForemanResult<String> {
16+
let repo = repo.into();
17+
let tool_name = tool_name.into();
18+
let version = version.into();
19+
let operating_system = operating_system.into();
20+
21+
check_valid_os(&operating_system)?;
22+
check_valid_version(&version)?;
23+
let mut full_tool_name = format!("{}-{}-{}", tool_name, version, operating_system);
24+
if let Some(architecture) = architecture {
25+
let architecture = architecture.into();
26+
check_valid_arch(&architecture)?;
27+
full_tool_name.push('-');
28+
full_tool_name.push_str(&architecture);
29+
}
30+
31+
full_tool_name.push_str(".zip");
32+
33+
Ok(format!(
34+
"artifactory/{}/{}/{}/{}",
35+
repo, tool_name, version, full_tool_name
36+
))
37+
}
38+
39+
fn check_valid_os(operating_system: &str) -> ForemanResult<()> {
40+
if !VALID_OS.contains(&operating_system) {
41+
return Err(ForemanError::io_error_with_context(
42+
Error::new(ErrorKind::InvalidInput, "Invalid Argument"),
43+
format!(
44+
"Invalid operating system: {}. Please input a valid operating system: {}",
45+
operating_system,
46+
VALID_OS.join(", ")
47+
),
48+
));
49+
} else {
50+
Ok(())
51+
}
52+
}
53+
54+
fn check_valid_arch(architecture: &str) -> ForemanResult<()> {
55+
if !VALID_ARCH.contains(&architecture) {
56+
return Err(ForemanError::io_error_with_context(
57+
Error::new(ErrorKind::InvalidInput, "Invalid Argument"),
58+
format!(
59+
"Invalid architecture: {}. Please input a valid architecture: {}",
60+
architecture,
61+
VALID_ARCH.join(", ")
62+
),
63+
));
64+
} else {
65+
Ok(())
66+
}
67+
}
68+
69+
fn check_valid_version(version: &str) -> ForemanResult<()> {
70+
if !version.starts_with('v') {
71+
return Err(ForemanError::io_error_with_context(
72+
Error::new(ErrorKind::InvalidInput, "Invalid Argument"),
73+
format!("Invalid version: {}. Versions must start with a v", version),
74+
));
75+
}
76+
77+
if let Err(err) = Version::parse(&version[1..]) {
78+
Err(ForemanError::io_error_with_context(
79+
Error::new(ErrorKind::InvalidInput, "Invalid Argument"),
80+
format!("Invalid version: {}. Error: {}", version, err),
81+
))
82+
} else {
83+
Ok(())
84+
}
85+
}
86+
87+
#[cfg(test)]
88+
mod test {
89+
use super::generate_artifactory_path;
90+
91+
#[test]
92+
fn simple_path() {
93+
let path = generate_artifactory_path("repo", "tool_name", "v0.1.0", "macos", None).unwrap();
94+
assert_eq!(
95+
path,
96+
"artifactory/repo/tool_name/v0.1.0/tool_name-v0.1.0-macos.zip"
97+
);
98+
}
99+
100+
#[test]
101+
fn simple_path_with_arch() {
102+
let path = generate_artifactory_path("repo", "tool_name", "v0.1.0", "macos", Some("arm64"))
103+
.unwrap();
104+
assert_eq!(
105+
path,
106+
"artifactory/repo/tool_name/v0.1.0/tool_name-v0.1.0-macos-arm64.zip"
107+
);
108+
}
109+
110+
#[test]
111+
fn invalid_version_no_v() {
112+
let path = generate_artifactory_path("repo", "tool_name", "0.1.0", "macos", Some("arm64"))
113+
.unwrap_err();
114+
assert_eq!(
115+
path.to_string(),
116+
"Invalid version: 0.1.0. Versions must start with a v: Invalid Argument".to_string()
117+
);
118+
}
119+
#[test]
120+
fn invalid_version_incomplete() {
121+
let path = generate_artifactory_path("repo", "tool_name", "v0.1", "macos", Some("arm64"))
122+
.unwrap_err();
123+
assert_eq!(
124+
path.to_string(),
125+
"Invalid version: v0.1. Error: unexpected end of input while parsing minor version number: Invalid Argument".to_string()
126+
);
127+
}
128+
129+
#[test]
130+
fn invalid_operating_system() {
131+
let path =
132+
generate_artifactory_path("repo", "tool_name", "v0.1.0", "fake_os", Some("arm64"))
133+
.unwrap_err();
134+
assert_eq!(
135+
path.to_string(),
136+
"Invalid operating system: fake_os. Please input a valid operating system: windows, macos, linux: Invalid Argument".to_string()
137+
);
138+
}
139+
140+
#[test]
141+
fn invalid_architecture() {
142+
let path =
143+
generate_artifactory_path("repo", "tool_name", "v0.1.0", "macos", Some("fake_arch"))
144+
.unwrap_err();
145+
assert_eq!(
146+
path.to_string(),
147+
"Invalid architecture: fake_arch. Please input a valid architecture: x86_64, arm64, aarch64, i686: Invalid Argument".to_string()
148+
);
149+
}
150+
}

src/main.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
mod aliaser;
22
mod artifact_choosing;
3+
mod artifactory_path;
34
mod auth_store;
45
mod ci_string;
56
mod config;
@@ -151,6 +152,12 @@ enum Subcommand {
151152
/// This token can also be configured by editing ~/.foreman/auth.toml.
152153
#[structopt(name = "gitlab-auth")]
153154
GitLabAuth(GitLabAuthCommand),
155+
156+
/// Create a path to publish to artifactory
157+
///
158+
/// Foreman does not support uploading binaries to artifactory directly, but it can generate the path where it would expect to find a given artifact. Use this command to generate paths that can be input to generic artifactory upload solutions.
159+
#[structopt(name = "generate-artifactory-path")]
160+
GenerateArtifactoryPath(GenerateArtifactoryPathCommand),
154161
}
155162

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

179+
#[derive(Debug, StructOpt)]
180+
struct GenerateArtifactoryPathCommand {
181+
repo: String,
182+
tool_name: String,
183+
version: String,
184+
operating_system: String,
185+
architecture: Option<String>,
186+
}
187+
172188
fn actual_main(paths: ForemanPaths) -> ForemanResult<()> {
173189
let options = Options::from_args();
174190

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

272288
println!("GitLab auth saved successfully.");
273289
}
290+
Subcommand::GenerateArtifactoryPath(subcommand) => {
291+
let artifactory_path = artifactory_path::generate_artifactory_path(
292+
subcommand.repo,
293+
subcommand.tool_name,
294+
subcommand.version,
295+
subcommand.operating_system,
296+
subcommand.architecture,
297+
)?;
298+
println!("{}", artifactory_path);
299+
}
274300
}
275301

276302
Ok(())

tests/snapshots/help_command.snap

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ FLAGS:
1313
-v Logging verbosity. Supply multiple for more verbosity, up to -vvv
1414

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

2223

0 commit comments

Comments
 (0)