diff --git a/Cargo.lock b/Cargo.lock index 81342b7..465a59d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -524,6 +524,7 @@ dependencies = [ "tracing", "tracing-error", "tracing-subscriber", + "url", "uuid", ] @@ -2709,6 +2710,7 @@ dependencies = [ "form_urlencoded", "idna", "percent-encoding", + "serde", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 364f8b3..84c7657 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ spdx = "0.10.2" uuid = { version = "1.4.0", features = ["serde", "v7", "rand", "std"] } semver = { version = "1.0.18", features = ["serde"] } thiserror = "1.0.56" +url = { version = "2.5.0", features = ["serde"] } [profile.release] strip = true # Automatically strip symbols from the binary. diff --git a/src/cli/mod.rs b/src/cli/mod.rs index baddb80..7549ae7 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -2,13 +2,17 @@ mod instrumentation; use color_eyre::eyre::{eyre, WrapErr}; use std::{ + collections::HashSet, path::{Path, PathBuf}, process::ExitCode, }; use crate::{ build_http_client, - github::{get_actions_id_bearer_token, graphql::GithubGraphqlDataQuery}, + github::{ + get_actions_id_bearer_token, + graphql::{GithubGraphqlDataQuery, MAX_LABEL_LENGTH, MAX_NUM_TOTAL_LABELS}, + }, push::push_new_release, release_metadata::RevisionInfo, }; @@ -21,7 +25,7 @@ pub(crate) struct FlakeHubPushCli { env = "FLAKEHUB_PUSH_HOST", default_value = "https://api.flakehub.com" )] - pub(crate) host: String, + pub(crate) host: url::Url, #[clap(long, env = "FLAKEHUB_PUSH_VISIBLITY")] pub(crate) visibility: crate::Visibility, // Will also detect `GITHUB_REF_NAME` @@ -56,7 +60,7 @@ pub(crate) struct FlakeHubPushCli { #[clap(long, env = "FLAKEHUB_PUSH_JWT_ISSUER_URI", value_parser = StringToNoneParser, default_value = "")] pub(crate) jwt_issuer_uri: OptionString, - /// User-supplied labels beyond those associated with the GitHub repository. + /// User-supplied labels, merged with any associated with GitHub repository (if possible) #[clap( long, short = 'l', @@ -76,7 +80,7 @@ pub(crate) struct FlakeHubPushCli { )] pub(crate) extra_tags: Vec, - /// An SPDX expression that overrides that which is returned from GitHub. + /// An SPDX identifier from https://spdx.org/licenses/, inferred from GitHub (if possible) #[clap( long, env = "FLAKEHUB_PUSH_SPDX_EXPRESSION", @@ -244,12 +248,66 @@ impl clap::builder::TypedValueParser for U64ToNoneParser { } impl FlakeHubPushCli { + #[tracing::instrument(skip_all)] + pub(crate) fn populate_missing_from_github(&mut self) { + if self.git_root.0.is_none() { + let env_key = "GITHUB_WORKSPACE"; + if let Ok(env_val) = std::env::var(env_key) { + tracing::debug!(git_root = %env_val, "Set via `${env_key}`"); + self.git_root.0 = Some(PathBuf::from(env_val)); + } + } + + if self.repository.0.is_none() { + let env_key = "GITHUB_REPOSITORY"; + if let Ok(env_val) = std::env::var(env_key) { + tracing::debug!(repository = %env_val, "Set via `${env_key}`"); + self.repository.0 = Some(env_val); + } + } + + if self.tag.0.is_none() { + let env_key = "GITHUB_REF_NAME"; + if let Ok(env_val) = std::env::var(env_key) { + tracing::debug!(repository = %env_val, "Set via `${env_key}`"); + self.tag.0 = Some(env_val); + } + } + } + #[tracing::instrument( name = "flakehub_push" skip_all, + fields( + host = %self.host, + visibility = ?self.visibility, + name = self.name.0, + tag = tracing::field::Empty, + rolling_minor = tracing::field::Empty, + rolling = self.rolling, + directory = tracing::field::Empty, + repository = tracing::field::Empty, + git_root = tracing::field::Empty, + commit_count = tracing::field::Empty, + mirror = self.mirror, + jwt_issuer_uri = tracing::field::Empty, + extra_labels = self.extra_labels.join(","), + spdx_expression = tracing::field::Empty, + error_on_conflict = self.error_on_conflict, + include_output_paths = self.include_output_paths, + ) )] - pub(crate) async fn execute(self) -> color_eyre::Result { - tracing::trace!(?self, "Executing"); + pub(crate) async fn execute(mut self) -> color_eyre::Result { + let span = tracing::Span::current(); + tracing::trace!("Executing"); + + let is_github_actions = + self.github_token.0.is_some() || std::env::var("GITHUB_ACTION").ok().is_some(); + if is_github_actions { + tracing::debug!("Running inside Github Actions, will enrich with GitHub API data and push with authorized Github bearer token"); + self.populate_missing_from_github() + } + let Self { host, visibility, @@ -271,10 +329,9 @@ impl FlakeHubPushCli { include_output_paths, } = self; - let mut extra_labels: Vec<_> = extra_labels.into_iter().filter(|v| !v.is_empty()).collect(); - let extra_tags: Vec<_> = extra_tags.into_iter().filter(|v| !v.is_empty()).collect(); + let mut labels: HashSet<_> = extra_labels.into_iter().filter(|v| !v.is_empty()).collect(); + let extra_tags: HashSet<_> = extra_tags.into_iter().filter(|v| !v.is_empty()).collect(); - let is_github_actions = std::env::var("GITHUB_ACTION").ok().is_some(); if !extra_tags.is_empty() { let message = "`extra-tags` is deprecated and will be removed in the future. Please use `extra-labels` instead."; tracing::warn!("{message}"); @@ -283,8 +340,8 @@ impl FlakeHubPushCli { println!("::warning::{message}"); } - if extra_labels.is_empty() { - extra_labels = extra_tags; + if labels.is_empty() { + labels = extra_tags; } else { let message = "Both `extra-tags` and `extra-labels` were set; `extra-tags` will be ignored."; @@ -296,20 +353,10 @@ impl FlakeHubPushCli { } } - let github_token = if let Some(github_token) = &github_token.0 { - github_token.clone() - } else { - std::env::var("GITHUB_TOKEN") - .wrap_err("Could not determine Github token, pass `--github-token`, or set either `FLAKEHUB_PUSH_GITHUB_TOKEN` or `GITHUB_TOKEN`")? - }; - - let git_root = if let Some(git_root) = &git_root.0 { + let git_root = if let Some(git_root) = git_root.0 { git_root.clone() - } else if let Ok(github_workspace) = std::env::var("GITHUB_WORKSPACE") { - tracing::trace!(%github_workspace, "Got `GITHUB_WORKSPACE`"); - PathBuf::from(github_workspace) } else { - std::env::current_dir().map(PathBuf::from).wrap_err("Could not determine current git_root. Pass `--git-root` or set `FLAKEHUB_PUSH_GIT_ROOT`")? + std::env::current_dir().map(PathBuf::from).wrap_err("Could not determine current `git_root`. Pass `--git-root` or set `FLAKEHUB_PUSH_GIT_ROOT`, or run `flakehub-push` with the git root as the current working directory")? }; let git_root = git_root @@ -338,17 +385,8 @@ impl FlakeHubPushCli { PathBuf::new() }; - let repository = if let Some(repository) = &repository.0 { - tracing::trace!(%repository, "Got `--repository` argument"); - repository.clone() - } else if let Ok(github_repository) = std::env::var("GITHUB_REPOSITORY") { - tracing::trace!( - %github_repository, - "Got `GITHUB_REPOSITORY` environment" - ); - github_repository - } else { - return Err(eyre!("Could not determine repository name, pass `--repository` or the `GITHUB_REPOSITORY` formatted like `determinatesystems/flakehub-push`")); + let Some(repository) = repository.0 else { + return Err(eyre!("Could not determine repository name, pass `--repository` formatted like `determinatesystems/flakehub-push`")); }; // If the upload name is supplied by the user, ensure that it contains exactly @@ -361,7 +399,7 @@ impl FlakeHubPushCli { || !name.is_ascii() || name.contains(char::is_whitespace) { - return Err(eyre!("The `upload-name` must be in the format of `owner-name/repo-name` and cannot contain whitespace or other special characters")); + return Err(eyre!("The argument `--name` must be in the format of `owner-name/repo-name` and cannot contain whitespace or other special characters")); } else { name } @@ -369,69 +407,144 @@ impl FlakeHubPushCli { repository.clone() }; - let tag = if let Some(tag) = &tag.0 { - Some(tag.clone()) - } else { - std::env::var("GITHUB_REF_NAME").ok() - }; - let mut repository_split = repository.split('/'); let project_owner = repository_split .next() - .ok_or_else(|| eyre!("Could not determine owner, pass `--repository` or the `GITHUB_REPOSITORY` formatted like `determinatesystems/flakehub-push`"))? + .ok_or_else(|| eyre!("Could not determine owner, pass `--repository` formatted like `determinatesystems/flakehub-push`"))? .to_string(); let project_name = repository_split.next() - .ok_or_else(|| eyre!("Could not determine project, pass `--repository` or `GITHUB_REPOSITORY` formatted like `determinatesystems/flakehub-push`"))? + .ok_or_else(|| eyre!("Could not determine project, pass `--repository` formatted like `determinatesystems/flakehub-push`"))? .to_string(); if repository_split.next().is_some() { - Err(eyre!("Could not determine the owner/project, pass `--repository` or `GITHUB_REPOSITORY` formatted like `determinatesystems/flakehub-push`. The passed value has too many slashes (/) to be a valid repository"))?; + Err(eyre!("Could not determine the owner/project, pass `--repository` formatted like `determinatesystems/flakehub-push`. The passed value has too many slashes (/) to be a valid repository"))?; } - let github_api_client = build_http_client().build()?; + let mut spdx_expression = spdx_expression.0; - let revision_info = RevisionInfo::from_git_root(&git_root)?; - let github_graphql_data_result = GithubGraphqlDataQuery::get( - &github_api_client, - &github_token, - &project_owner, - &project_name, - &revision_info.revision, - ) - .await?; + #[allow(unused_assignments)] + // Since we return an error outside github actions right now, `commit_count` throws an unused warning since we don't actually use it. + let RevisionInfo { + mut commit_count, + revision, + } = RevisionInfo::from_git_root(&git_root)?; - let upload_bearer_token = match jwt_issuer_uri.0 { - None => get_actions_id_bearer_token() - .await - .wrap_err("Getting upload bearer token from GitHub")?, - - Some(jwt_issuer_uri) => { - let client = build_http_client().build()?; - let mut claims = github_actions_oidc_claims::Claims::make_dummy(); - // FIXME: we should probably fill in more of these claims. - claims.aud = "flakehub-localhost".to_string(); - claims.iss = "flakehub-push-dev".to_string(); - claims.repository = repository.clone(); - claims.repository_owner = project_owner.to_string(); - claims.repository_id = github_graphql_data_result.project_id.to_string(); - claims.repository_owner_id = github_graphql_data_result.owner_id.to_string(); - - let response = client - .post(jwt_issuer_uri) - .header("Content-Type", "application/json") - .json(&claims) - .send() - .await - .wrap_err("Sending request to JWT issuer")?; - #[derive(serde::Deserialize)] - struct Response { - token: String, + let upload_bearer_token = if is_github_actions { + let github_token = if let Some(github_token) = &github_token.0 { + github_token.clone() + } else { + std::env::var("GITHUB_TOKEN") + .wrap_err("Could not determine Github token, pass `--github-token`")? + }; + let github_api_client = build_http_client().build()?; + + // Take the opportunity to be able to populate/encrich data from the GitHub API since we need it for project/owner_id anywys + let github_graphql_data_result = GithubGraphqlDataQuery::get( + &github_api_client, + &github_token, + &project_owner, + &project_name, + &revision, + ) + .await?; + + // On GitHub Actions, typically shallow clones are used which would report 1 for the commit count. Override it with the result from the API. + // Since users can't set this as a command line flag, that's fine. + tracing::trace!( + "Updating `commit_count` from {} to {} via GitHub API", + commit_count + .map(|v| v.to_string()) + .unwrap_or_else(|| "".into()), + github_graphql_data_result.rev_count as usize + ); + commit_count = Some(github_graphql_data_result.rev_count as usize); + + // If the user didn't pass `--spdx-expression` from command line, enrich it with Github's data. + spdx_expression = if spdx_expression.is_none() { + if let Some(spdx_string) = &github_graphql_data_result.spdx_identifier { + tracing::debug!("Recieved SPDX identifier `{}` from GitHub API", spdx_string); + let parsed = spdx::Expression::parse(spdx_string) + .wrap_err("Invalid SPDX license identifier reported from the GitHub API, either you are using a non-standard license or GitHub has returned a value that cannot be validated")?; + span.record("spdx_expression", tracing::field::display(&parsed)); + Some(parsed) + } else { + None + } + } else { + // Provide the user notice if the SPDX expression passed differs from the one detected on GitHub -- It's probably something they care about. + if github_graphql_data_result.spdx_identifier + != spdx_expression.as_ref().map(|v| v.to_string()) + { + tracing::warn!( + "SPDX identifier `{}` was passed via argument, but GitHub's API suggests it may be `{}`", + spdx_expression.as_ref().map(|v| v.to_string()).unwrap_or_else(|| "None".to_string()), + github_graphql_data_result.spdx_identifier.unwrap_or_else(|| "None".to_string()), + ) } - let response_deserialized: Response = response - .json() + spdx_expression + }; + + // Extend the labels provided by the user with those from GitHub. + labels = labels + .into_iter() + .chain(github_graphql_data_result.topics.into_iter()) + .collect::>(); + + match jwt_issuer_uri.0 { + None => get_actions_id_bearer_token(&host) .await - .wrap_err("Getting token from JWT issuer's response")?; - response_deserialized.token + .wrap_err("Getting upload bearer token from GitHub")?, + + Some(jwt_issuer_uri) => { + let client = build_http_client().build()?; + let mut claims = github_actions_oidc_claims::Claims::make_dummy(); + // FIXME: we should probably fill in more of these claims. + claims.aud = "flakehub-localhost".to_string(); + claims.iss = "flakehub-push-dev".to_string(); + claims.repository = repository.clone(); + claims.repository_owner = project_owner.to_string(); + claims.repository_id = github_graphql_data_result.project_id.to_string(); + claims.repository_owner_id = github_graphql_data_result.owner_id.to_string(); + + let response = client + .post(jwt_issuer_uri) + .header("Content-Type", "application/json") + .json(&claims) + .send() + .await + .wrap_err("Sending request to JWT issuer")?; + #[derive(serde::Deserialize)] + struct Response { + token: String, + } + let response_deserialized: Response = response + .json() + .await + .wrap_err("Getting token from JWT issuer's response")?; + response_deserialized.token + } } + } else { + return Err(eyre!( + "`flakehub-push` currently only runs inside Github Actions" + )); + }; + + // Here we merge explicitly user-supplied labels and the labels ("topics") + // associated with the repo. Duplicates are excluded and all + // are converted to lower case. + let labels: Vec = labels + .into_iter() + .take(MAX_NUM_TOTAL_LABELS) + .map(|s| s.trim().to_lowercase()) + .filter(|t: &String| { + !t.is_empty() + && t.len() <= MAX_LABEL_LENGTH + && t.chars().all(|c| c.is_alphanumeric() || c == '-') + }) + .collect(); + + let Some(commit_count) = commit_count else { + return Err(eyre!("Could not determine commit count, this is normally determined via the `--git-root` argument or via the GitHub API")); }; push_new_release( @@ -439,17 +552,16 @@ impl FlakeHubPushCli { &upload_bearer_token, &git_root, &subdir, - revision_info, - &repository, + revision, + commit_count, upload_name, mirror, visibility, - tag, + tag.0, rolling, rolling_minor.0, - github_graphql_data_result, - extra_labels, - spdx_expression.0, + labels, + spdx_expression, error_on_conflict, include_output_paths, ) diff --git a/src/github/mod.rs b/src/github/mod.rs index d8c5f98..e4d23bd 100644 --- a/src/github/mod.rs +++ b/src/github/mod.rs @@ -4,8 +4,12 @@ use color_eyre::eyre::{eyre, WrapErr}; use crate::build_http_client; -#[tracing::instrument(skip_all)] -pub(crate) async fn get_actions_id_bearer_token() -> color_eyre::Result { +#[tracing::instrument(skip_all, fields(audience = tracing::field::Empty))] +pub(crate) async fn get_actions_id_bearer_token(host: &url::Url) -> color_eyre::Result { + let span = tracing::Span::current(); + let audience = host.host_str().ok_or_else(|| eyre!("`--host` must contain a valid host (eg `https://api.flakehub.com` contains `api.flakehub.com`)"))?; + span.record("audience", audience); + let actions_id_token_request_token = std::env::var("ACTIONS_ID_TOKEN_REQUEST_TOKEN") // We do want to preserve the whitespace here .wrap_err("\ @@ -26,7 +30,7 @@ jobs: let actions_id_token_client = build_http_client().build()?; let response = actions_id_token_client .get(format!( - "{actions_id_token_request_url}&audience=api.flakehub.com" + "{actions_id_token_request_url}&audience={audience}" )) .bearer_auth(actions_id_token_request_token) .send() diff --git a/src/push.rs b/src/push.rs index d584a0b..c840751 100644 --- a/src/push.rs +++ b/src/push.rs @@ -11,8 +11,7 @@ use crate::{ build_http_client, error::Error, flake_info::{check_flake_evaluates, get_flake_metadata, get_flake_outputs, get_flake_tarball}, - github::graphql::GithubGraphqlDataResult, - release_metadata::{ReleaseMetadata, RevisionInfo}, + release_metadata::ReleaseMetadata, Visibility, }; @@ -21,30 +20,42 @@ const DEFAULT_ROLLING_PREFIX: &str = "0.1"; #[tracing::instrument( skip_all, fields( - repository = %repository, - upload_name = tracing::field::Empty, - mirror = %mirror, - tag = tracing::field::Empty, - source = tracing::field::Empty, - mirrored = tracing::field::Empty, + %host, + flake_root, + subdir, + revision, + revision_count, + repository, + upload_name, + mirror, + %visibility, + tag, + rolling, + rolling_minor, + labels = labels.join(","), + mirror, + spdx_expression, + error_if_release_conflicts, + include_output_paths, + project_id, + owner_id, ) )] #[allow(clippy::too_many_arguments)] pub(crate) async fn push_new_release( - host: &str, + host: &url::Url, upload_bearer_token: &str, flake_root: &Path, subdir: &Path, - revision_info: RevisionInfo, - repository: &str, + revision: String, + revision_count: usize, upload_name: String, mirror: bool, visibility: Visibility, tag: Option, rolling: bool, rolling_minor: Option, - github_graphql_data_result: GithubGraphqlDataResult, - extra_labels: Vec, + labels: Vec, spdx_expression: Option, error_if_release_conflicts: bool, include_output_paths: bool, @@ -202,14 +213,14 @@ pub(crate) async fn push_new_release( let release_metadata = ReleaseMetadata::build( &source, subdir, - revision_info, + revision, + revision_count, flake_metadata, flake_outputs, upload_name.clone(), mirror, visibility, - github_graphql_data_result, - extra_labels, + labels, spdx_expression, ) .await @@ -226,11 +237,9 @@ pub(crate) async fn push_new_release( rolling_prefix_or_tag.to_string() // This will always be the tag since `self.rolling_prefix` was empty. }; - let release_metadata_post_url = format!( - "{host}/upload/{upload_name}/{rolling_minor_with_postfix_or_tag}/{flake_tarball_len}/{flake_tarball_hash_base64}" - ); + let release_metadata_post_url = host.join(&format!("upload/{upload_name}/{rolling_minor_with_postfix_or_tag}/{flake_tarball_len}/{flake_tarball_hash_base64}"))?; tracing::debug!( - url = release_metadata_post_url, + url = %release_metadata_post_url, "Computed release metadata POST URL" ); @@ -344,8 +353,8 @@ pub(crate) async fn push_new_release( } // Make the release we just uploaded visible. - let publish_post_url = format!("{host}/publish/{}", release_metadata_post_result.uuid); - tracing::debug!(url = publish_post_url, "Computed publish POST URL"); + let publish_post_url = host.join(&format!("publish/{}", release_metadata_post_result.uuid))?; + tracing::debug!(url = %publish_post_url, "Computed publish POST URL"); let publish_response = flakehub_client .post(publish_post_url) diff --git a/src/release_metadata.rs b/src/release_metadata.rs index 7f39f28..ce90559 100644 --- a/src/release_metadata.rs +++ b/src/release_metadata.rs @@ -1,16 +1,13 @@ use color_eyre::eyre::{eyre, WrapErr}; -use std::{collections::HashSet, path::Path}; +use std::path::{Path, PathBuf}; -use crate::{ - github::graphql::{GithubGraphqlDataResult, MAX_LABEL_LENGTH, MAX_NUM_TOTAL_LABELS}, - Visibility, -}; +use crate::Visibility; const README_FILENAME_LOWERCASE: &str = "readme.md"; #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub(crate) struct ReleaseMetadata { - pub(crate) commit_count: i64, + pub(crate) commit_count: usize, pub(crate) description: Option, pub(crate) outputs: serde_json::Value, pub(crate) raw_flake_metadata: serde_json::Value, @@ -20,8 +17,6 @@ pub(crate) struct ReleaseMetadata { pub(crate) visibility: Visibility, pub(crate) mirrored: bool, pub(crate) source_subdirectory: Option, - pub(crate) project_id: i64, - pub(crate) owner_id: i64, #[serde( deserialize_with = "option_string_to_spdx", @@ -36,7 +31,7 @@ pub(crate) struct ReleaseMetadata { #[derive(Clone)] pub(crate) struct RevisionInfo { - pub(crate) local_revision_count: Option, + pub(crate) commit_count: Option, pub(crate) revision: String, } @@ -68,7 +63,7 @@ impl RevisionInfo { } }; - let local_revision_count = gix_repository + let commit_count = gix_repository .rev_walk([revision]) .all() .map(|rev_iter| rev_iter.count()) @@ -76,7 +71,7 @@ impl RevisionInfo { let revision = revision.to_hex().to_string(); Ok(Self { - local_revision_count, + commit_count, revision, }) } @@ -89,93 +84,63 @@ impl ReleaseMetadata { flake_store_path = %flake_store_path.display(), subdir = %subdir.display(), description = tracing::field::Empty, - readme_path = tracing::field::Empty, - revision = tracing::field::Empty, - revision_count = tracing::field::Empty, - commit_count = tracing::field::Empty, + readme = tracing::field::Empty, + %revision, + %commit_count, spdx_identifier = tracing::field::Empty, visibility = ?visibility, ))] pub(crate) async fn build( flake_store_path: &Path, subdir: &Path, - revision_info: RevisionInfo, + revision: String, + commit_count: usize, flake_metadata: serde_json::Value, flake_outputs: serde_json::Value, upload_name: String, mirror: bool, visibility: Visibility, - github_graphql_data_result: GithubGraphqlDataResult, - extra_labels: Vec, - spdx_expression: Option, + labels: Vec, + spdx_identifier: Option, ) -> color_eyre::Result { let span = tracing::Span::current(); - span.record("revision_string", &revision_info.revision); + if let Some(spdx_identifier) = &spdx_identifier { + span.record("spdx_identifier", tracing::field::display(spdx_identifier)); + } assert!(subdir.is_relative()); - let revision_count = match revision_info.local_revision_count { - Some(n) => n as i64, - None => { - tracing::debug!( - "Getting revision count locally failed, using data from github instead" - ); - github_graphql_data_result.rev_count - } - }; - span.record("revision_count", revision_count); - let description = if let Some(description) = flake_metadata.get("description") { - Some(description + let description_value = description .as_str() .ok_or_else(|| { eyre!("`nix flake metadata --json` does not have a string `description` field") })? - .to_string()) + .to_string(); + span.record("description", tracing::field::display(&description_value)); + Some(description_value) } else { None }; - let readme = get_readme(flake_store_path).await?; - - let spdx_identifier = if spdx_expression.is_some() { - spdx_expression - } else if let Some(spdx_string) = github_graphql_data_result.spdx_identifier { - let parsed = spdx::Expression::parse(&spdx_string) - .wrap_err("Invalid SPDX license identifier reported from the GitHub API, either you are using a non-standard license or GitHub has returned a value that cannot be validated")?; - span.record("spdx_identifier", tracing::field::display(&parsed)); - Some(parsed) + let readme_path = get_readme(flake_store_path).await?; + let readme = if let Some(readme_path) = readme_path { + span.record("readme", tracing::field::display(readme_path.display())); + Some(tokio::fs::read_to_string(&readme_path).await?) } else { None }; tracing::trace!("Collected ReleaseMetadata information"); - // Here we merge explicitly user-supplied labels and the labels ("topics") - // associated with the repo. Duplicates are excluded and all - // are converted to lower case. - let labels: Vec = extra_labels - .into_iter() - .chain(github_graphql_data_result.topics.into_iter()) - .collect::>() - .into_iter() - .take(MAX_NUM_TOTAL_LABELS) - .map(|s| s.trim().to_lowercase()) - .filter(|t: &String| { - !t.is_empty() - && t.len() <= MAX_LABEL_LENGTH - && t.chars().all(|c| c.is_alphanumeric() || c == '-') - }) - .collect(); - Ok(ReleaseMetadata { description, repo: upload_name.to_string(), raw_flake_metadata: flake_metadata.clone(), readme, - revision: revision_info.revision, - commit_count: github_graphql_data_result.rev_count, + revision, + commit_count, visibility, outputs: flake_outputs, source_subdirectory: Some( @@ -186,8 +151,6 @@ impl ReleaseMetadata { ), mirrored: mirror, spdx_identifier, - project_id: github_graphql_data_result.project_id, - owner_id: github_graphql_data_result.owner_id, labels, }) } @@ -223,12 +186,13 @@ where } } -async fn get_readme(readme_dir: &Path) -> color_eyre::Result> { +#[tracing::instrument(skip_all, fields(readme_dir))] +async fn get_readme(readme_dir: &Path) -> color_eyre::Result> { let mut read_dir = tokio::fs::read_dir(readme_dir).await?; while let Some(entry) = read_dir.next_entry().await? { if entry.file_name().to_ascii_lowercase() == README_FILENAME_LOWERCASE { - return Ok(Some(tokio::fs::read_to_string(entry.path()).await?)); + return Ok(Some(entry.path())); } }