diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..c96f5b6 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,33 @@ +name: Build + +on: push + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install buildinfo + run: ./ci/install-buildinfo + + - name: Produce build info + working-directory: ./buildinfo-cli + run: | + buildinfo + cat ./src/buildinfo.json + + - name: Build + run: cargo build --release --verbose + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: buildinfo + path: ./target/release/buildinfo + if-no-files-found: error diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock index a0a1660..4e9cb48 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -94,7 +94,7 @@ dependencies = [ [[package]] name = "buildinfo-cli" -version = "0.1.0" +version = "0.2.0" dependencies = [ "anyhow", "buildinfo", diff --git a/buildinfo-cli/Cargo.toml b/buildinfo-cli/Cargo.toml index a757f49..9ca0792 100644 --- a/buildinfo-cli/Cargo.toml +++ b/buildinfo-cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "buildinfo-cli" -version = "0.1.0" +version = "0.2.0" edition = "2021" [dependencies] diff --git a/buildinfo-cli/src/build.rs b/buildinfo-cli/src/build.rs index ce315da..1b1d66d 100644 --- a/buildinfo-cli/src/build.rs +++ b/buildinfo-cli/src/build.rs @@ -15,6 +15,7 @@ pub struct BuildInfo { pub trigger: String, pub number: u32, pub timestamp: String, + pub as_string: String, } fn build_environment() -> BuildEnvironment { @@ -29,42 +30,74 @@ fn build_environment() -> BuildEnvironment { } } +fn timestamp() -> String { + let timestamp: std::sync::LazyLock> = + std::sync::LazyLock::new(|| chrono::Utc::now()); + + timestamp.to_rfc3339() +} + +fn as_string(number: u32, timestamp: &str, trigger: &str) -> String { + format!("build #{} at {} by {}", number, timestamp, trigger) +} + fn github_build_info() -> Result { + let trigger = var("GITHUB_ACTOR")?; + let number = var("GITHUB_RUN_NUMBER")?.parse()?; + let timestamp = timestamp(); + let as_string = as_string(number, ×tamp, &trigger); + Ok(BuildInfo { environment: BuildEnvironment::GitHub, - trigger: var("GITHUB_ACTOR")?, - number: var("GITHUB_RUN_NUMBER")?.parse()?, - timestamp: var("GITHUB_RUN_AT")?, + trigger, + number, + timestamp, + as_string, }) } fn bitbucket_build_info() -> Result { + let trigger = var("BITBUCKET_BUILD_CREATOR")?; + let number = var("BITBUCKET_BUILD_NUMBER")?.parse()?; + let timestamp = var("BITBUCKET_BUILD_CREATED_ON")?; + let as_string = as_string(number, ×tamp, &trigger); + Ok(BuildInfo { environment: BuildEnvironment::BitBucket, - trigger: var("BITBUCKET_BUILD_CREATOR")?, - number: var("BITBUCKET_BUILD_NUMBER")?.parse()?, - timestamp: var("BITBUCKET_BUILD_CREATED_ON")?, + trigger, + number, + timestamp, + as_string, }) } fn codebuild_build_info() -> Result { + let trigger = var("CODEBUILD_INITIATOR")?; + let number = var("CODEBUILD_BUILD_NUMBER")?.parse()?; + let timestamp = var("CODEBUILD_START_TIME")?; + let as_string = as_string(number, ×tamp, &trigger); + Ok(BuildInfo { environment: BuildEnvironment::CodeBuild, - trigger: var("CODEBUILD_INITIATOR")?, - number: var("CODEBUILD_BUILD_NUMBER")?.parse()?, - timestamp: var("CODEBUILD_START_TIME")?, + trigger, + number, + timestamp, + as_string, }) } fn local_build_environment() -> Result { - static TIMESTAMP: std::sync::LazyLock> = - std::sync::LazyLock::new(|| chrono::Local::now()); + let trigger = whoami::username(); + let number = 1; + let timestamp = timestamp(); + let as_string = as_string(number, ×tamp, &trigger); Ok(BuildInfo { environment: BuildEnvironment::Local, - trigger: whoami::username(), - number: 1, - timestamp: TIMESTAMP.to_rfc3339(), + trigger, + number, + timestamp, + as_string, }) } diff --git a/buildinfo-cli/src/git.rs b/buildinfo-cli/src/git.rs index c77b7da..896ce12 100644 --- a/buildinfo-cli/src/git.rs +++ b/buildinfo-cli/src/git.rs @@ -9,6 +9,13 @@ pub struct GitInfo { pub reference: String, pub repository: String, pub dirty: bool, + pub as_string: String, +} + +fn as_string(commit: &str, dirty: bool, reference: &str) -> String{ + let short_commit = &commit[..7]; + let dirty = if dirty { "#dirty" } else { "" }; + format!("commit: {}{}, ref: {}", short_commit, dirty, reference) } fn local_build_info() -> Result { @@ -25,38 +32,59 @@ fn local_build_info() -> Result { .count() > 0; + let as_string = as_string(&commit, dirty, &reference); + Ok(GitInfo { commit, reference, repository, dirty, + as_string, }) } fn github_build_info() -> Result { + let commit = var("GITHUB_SHA")?; + let reference = var("GITHUB_REF_NAME")?; + let dirty = false; + let as_string = as_string(&commit, dirty, &reference); + Ok(GitInfo { - commit: var("GITHUB_SHA")?, - reference: var("GITHUB_REF_NAME")?, + commit, + reference, repository: var("GITHUB_REPOSITORY")?, - dirty: false, + dirty, + as_string, }) } fn bitbucket_build_info() -> Result { + let commit = var("BITBUCKET_COMMIT")?; + let reference = var("BITBUCKET_BRANCH").or_else(|_| var("BITBUCKET_TAG"))?; + let dirty = false; + let as_string = as_string(&commit, dirty, &reference); + Ok(GitInfo { - commit: var("BITBUCKET_COMMIT")?, - reference: var("BITBUCKET_BRANCH").or_else(|_| var("BITBUCKET_TAG"))?, + commit, + reference, repository: var("BITBUCKET_REPO_FULL_NAME")?, - dirty: false, + dirty, + as_string, }) } fn codebuild_build_info() -> Result { + let commit = var("CODEBUILD_RESOLVED_SOURCE_VERSION")?; + let reference = var("CODEBUILD_SOURCE_VERSION").unwrap_or_else(|_| "undefined".to_string()); + let dirty = false; + let as_string = as_string(&commit, dirty, &reference); + Ok(GitInfo { - commit: var("CODEBUILD_RESOLVED_SOURCE_VERSION")?, - reference: var("CODEBUILD_SOURCE_VERSION").unwrap_or_else(|_| "undefined".to_string()), + commit, + reference, repository: var("CODEBUILD_SOURCE_REPO_URL")?, - dirty: false, + dirty, + as_string, }) } diff --git a/buildinfo-cli/src/main.rs b/buildinfo-cli/src/main.rs index 66754ad..d0a8acb 100644 --- a/buildinfo-cli/src/main.rs +++ b/buildinfo-cli/src/main.rs @@ -25,19 +25,21 @@ mod v1 { git_info: &super::git::GitInfo, project_info: &super::project::ProjectInfo, ) -> BuildInfo { - BuildInfo { - project: ProjectInfo { + let project = ProjectInfo { name: project_info.name.clone(), version: project_info.version.clone(), - }, + as_string: project_info.as_string.clone(), + }; - git: RepoInfo { + let git = RepoInfo { repository: git_info.repository.clone(), reference: git_info.reference.clone(), commit: git_info.commit.clone(), dirty: git_info.dirty, - }, - build: BuilderInfo { + as_string: git_info.as_string.clone(), + }; + + let build = BuilderInfo { timestamp: build_info.timestamp.clone(), number: build_info.number, trigger: build_info.trigger.clone(), @@ -48,8 +50,16 @@ mod v1 { BuildEnvironment::CodeBuild => "codebuild", } .to_string(), - }, + as_string: build_info.as_string.clone(), + }; + let as_string = format!("{} ({}, {})", project.as_string, git.as_string, build.as_string); + + BuildInfo { + project, + git, + build, + as_string, properties: HashMap::new(), } } diff --git a/buildinfo-cli/src/project.rs b/buildinfo-cli/src/project.rs index 47b9ce1..994d261 100644 --- a/buildinfo-cli/src/project.rs +++ b/buildinfo-cli/src/project.rs @@ -17,6 +17,7 @@ pub struct ProjectInfo { pub name: String, pub version: String, pub target_path: PathBuf, + pub as_string: String, } fn project_type() -> ProjectType { @@ -35,6 +36,10 @@ fn project_type() -> ProjectType { } } +fn as_string(name: &str, version: &str) -> String { + format!("{} v{}", name, version) +} + fn rust_project_info() -> Result { let cargo_toml = slurp::read_all_to_string("Cargo.toml")?; let cargo_toml = toml::from_str::(&cargo_toml)?; @@ -42,6 +47,7 @@ fn rust_project_info() -> Result { let package = &cargo_toml["package"]; let name = package["name"].as_str().unwrap_or("unknown").to_string(); let version = package["version"].as_str().unwrap_or("unknown").to_string(); + let as_string = as_string(&name, &version); let target_path = current_dir()?.join("src/buildinfo.json"); @@ -50,6 +56,7 @@ fn rust_project_info() -> Result { name, version, target_path, + as_string, }) } @@ -67,13 +74,18 @@ fn java_project_info() -> Result { let pom_xml = slurp::read_all_to_string("pom.xml")?; let pom_xml = fast_xml::de::from_str::(&pom_xml)?; + let name = format!("{}/{}", pom_xml.group_id, pom_xml.artifact_id); + let version = pom_xml.version; + let as_string = as_string(&name, &version); + let target_path = current_dir()?.join("src/main/resources/META-INF/buildinfo.json"); Ok(ProjectInfo { project_type: ProjectType::Java, - name: format!("{}/{}", pom_xml.group_id, pom_xml.artifact_id), - version: pom_xml.version, + name, + version, target_path, + as_string }) } @@ -85,11 +97,14 @@ fn javascript_project_info() -> Result { .as_str() .unwrap_or("unknown") .to_string(); + let version = project_json["version"] .as_str() .unwrap_or("unknown") .to_string(); + let as_string = as_string(&name, &version); + let target_path = current_dir()?.join("src/buildinfo.json"); Ok(ProjectInfo { @@ -97,6 +112,7 @@ fn javascript_project_info() -> Result { name, version, target_path, + as_string }) } @@ -105,11 +121,18 @@ pub fn project_info() -> Result { ProjectType::Rust => rust_project_info(), ProjectType::Java => java_project_info(), ProjectType::JavaScript => javascript_project_info(), - ProjectType::Other => Ok(ProjectInfo { - project_type: ProjectType::Other, - name: "unknown".to_string(), - version: "unknown".to_string(), - target_path: current_dir()?.join("buildinfo.json"), - }), + ProjectType::Other => { + let name = "unknown".to_string(); + let version = "unknown".to_string(); + let as_string = as_string(&name, &version); + + Ok(ProjectInfo { + project_type: ProjectType::Other, + name, + version, + as_string, + target_path: current_dir()?.join("buildinfo.json"), + }) + }, } } diff --git a/buildinfo/src/lib.rs b/buildinfo/src/lib.rs index f6b8c21..b8f7472 100644 --- a/buildinfo/src/lib.rs +++ b/buildinfo/src/lib.rs @@ -6,6 +6,7 @@ pub mod v1 { pub struct ProjectInfo { pub name: String, pub version: String, + pub as_string: String, } #[derive(Debug, Serialize, Deserialize)] @@ -16,6 +17,8 @@ pub mod v1 { #[serde(skip_serializing_if = "std::ops::Not::not")] pub dirty: bool, + + pub as_string: String, } #[derive(Debug, Serialize, Deserialize)] @@ -24,6 +27,7 @@ pub mod v1 { pub timestamp: String, pub number: u32, pub trigger: String, + pub as_string: String, } #[derive(Debug, Serialize, Deserialize)] @@ -34,6 +38,8 @@ pub mod v1 { #[serde(skip_serializing_if = "HashMap::is_empty")] pub properties: HashMap, + + pub as_string: String, } } diff --git a/ci/install-buildinfo b/ci/install-buildinfo new file mode 100755 index 0000000..7f176a7 --- /dev/null +++ b/ci/install-buildinfo @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -eu + +readonly cmd=~/.local/bin/buildinfo + +mkdir -p "$(dirname "$cmd")" +curl -Lo "$cmd" 'https://github.com/suprematic/buildinfo/releases/download/v0.1.1-alpha/buildinfo' +chmod +x "$cmd"