Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/source/builder-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ Build the kernel and upload to Hugging Face Hub
Possible values: `model`, `kernel`

* `--create-pr` — Open a pull request with the changes rather than committing them directly (does not require write access to the repository)
* `--output-json <PATH>` — Write a machine-readable JSON summary of the upload to this path
* `-q`, `--quiet` — Suppress progress output


Expand All @@ -178,6 +179,7 @@ Upload kernel build artifacts to the Hugging Face Hub
Possible values: `model`, `kernel`

* `--create-pr` — Open a pull request with the changes rather than committing them directly (does not require write access to the repository)
* `--output-json <PATH>` — Write a machine-readable JSON summary of the upload to this path
* `-q`, `--quiet` — Suppress progress output


Expand Down
6 changes: 6 additions & 0 deletions kernel-builder/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@ enum Commands {
#[arg(long)]
create_pr: bool,

/// Write a machine-readable JSON summary of the upload to this path.
#[arg(long, value_name = "PATH")]
output_json: Option<PathBuf>,

/// Suppress progress output.
#[arg(long, short)]
quiet: bool,
Expand Down Expand Up @@ -366,6 +370,7 @@ fn main() -> Result<()> {
private,
repo_type,
create_pr,
output_json,
quiet,
} => {
run_build(
Expand All @@ -382,6 +387,7 @@ fn main() -> Result<()> {
private,
repo_type,
create_pr,
output_json,
quiet,
})
}
Expand Down
133 changes: 126 additions & 7 deletions kernel-builder/src/upload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use hf_hub::{
};
use indicatif::{ProgressBar, ProgressStyle};
use kernels_data::metadata::Metadata;
use serde::Serialize;
use walkdir::WalkDir;

use crate::{
Expand Down Expand Up @@ -97,11 +98,46 @@ pub struct UploadArgs {
#[arg(long)]
pub create_pr: bool,

/// Write a machine-readable JSON summary of the upload to this path.
#[arg(long, value_name = "PATH")]
pub output_json: Option<PathBuf>,

/// Suppress progress output.
#[arg(long, short)]
pub quiet: bool,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[serde(rename_all = "snake_case")]
enum UploadStatus {
NoChanges,
Uploaded,
PullRequestCreated,
}

#[derive(Debug, Serialize)]
struct PullRequest {
branch: String,
url: String,
}

#[derive(Debug, Serialize)]
struct UploadOutcome {
status: UploadStatus,
repo_id: String,
branch: Option<String>,
url: Option<String>,
pull_requests: Vec<PullRequest>,
}

fn write_output_json(path: &Path, outcome: &UploadOutcome) -> Result<()> {
let mut json =
serde_json::to_string_pretty(outcome).wrap_err("Cannot serialize the upload result")?;
json.push('\n');
fs::write(path, json)
.wrap_err_with(|| format!("Cannot write the upload result to `{}`", path.display()))
}

/// Get repository and branch from the given arguments, or fallback to
/// reading `build.toml` and/or metadata otherwise.
fn get_repo_and_branch(
Expand Down Expand Up @@ -228,7 +264,7 @@ fn run_upload_typed<T: RepoType>(args: UploadArgs) -> Result<()> {

// README goes to main branch, build artifacts go to version branch.
let mut operations_by_branch: BTreeMap<String, Vec<CommitOperation>> = BTreeMap::new();
let mut pr_urls: Vec<String> = Vec::new();
let mut pull_requests: Vec<PullRequest> = Vec::new();

collect_readme_commit_ops(
&build_dir,
Expand Down Expand Up @@ -330,7 +366,10 @@ fn run_upload_typed<T: RepoType>(args: UploadArgs) -> Result<()> {
format!("Cannot determine the pull request opened for branch `{branch}`")
})?;
pr_revision = Some(pr_ref);
pr_urls.push(pr_url);
pull_requests.push(PullRequest {
branch: branch.clone(),
url: pr_url,
});
}
}

Expand All @@ -342,18 +381,52 @@ fn run_upload_typed<T: RepoType>(args: UploadArgs) -> Result<()> {
}

let total_ops: usize = operations_by_branch.values().map(|v| v.len()).sum();
if total_ops == 0 {
eprintln!("No changes to upload.");
let outcome = if total_ops == 0 {
UploadOutcome {
status: UploadStatus::NoChanges,
repo_id,
branch,
url: None,
pull_requests,
}
} else if args.create_pr {
for url in &pr_urls {
println!("Pull request created: {url}");
UploadOutcome {
status: UploadStatus::PullRequestCreated,
repo_id,
branch,
url: None,
pull_requests,
}
} else {
let type_prefix = T::default().url_prefix();
let tree_path = branch
.as_ref()
.map_or(String::new(), |b| format!("/tree/{b}"));
println!("Kernel uploaded: https://hf.co/{type_prefix}{repo_id}{tree_path}");
UploadOutcome {
status: UploadStatus::Uploaded,
url: Some(format!("https://hf.co/{type_prefix}{repo_id}{tree_path}")),
repo_id,
branch,
pull_requests,
}
};

match outcome.status {
UploadStatus::NoChanges => eprintln!("No changes to upload."),
UploadStatus::PullRequestCreated => {
for pr in &outcome.pull_requests {
println!("Pull request created: {}", pr.url);
}
}
UploadStatus::Uploaded => {
if let Some(ref url) = outcome.url {
println!("Kernel uploaded: {url}");
}
}
}

if let Some(ref path) = args.output_json {
write_output_json(path, &outcome)?;
}

Ok(())
Expand Down Expand Up @@ -627,6 +700,52 @@ mod tests {
assert!(guidance.contains("Benchmark"));
}

#[test]
fn test_write_output_json() {
let temp_dir = tempfile::tempdir().unwrap();
let output_path = temp_dir.path().join("upload-result.json");
let outcome = UploadOutcome {
status: UploadStatus::PullRequestCreated,
repo_id: "user/my-kernel".to_owned(),
branch: Some("v3".to_owned()),
url: None,
pull_requests: vec![
PullRequest {
branch: MAIN_BRANCH.to_owned(),
url: "https://hf.co/kernels/user/my-kernel/discussions/1".to_owned(),
},
PullRequest {
branch: "v3".to_owned(),
url: "https://hf.co/kernels/user/my-kernel/discussions/2".to_owned(),
},
],
};

write_output_json(&output_path, &outcome).unwrap();

let contents = fs::read_to_string(output_path).unwrap();
assert!(contents.ends_with('\n'));
assert_eq!(
serde_json::from_str::<serde_json::Value>(&contents).unwrap(),
serde_json::json!({
"status": "pull_request_created",
"repo_id": "user/my-kernel",
"branch": "v3",
"url": null,
"pull_requests": [
{
"branch": "main",
"url": "https://hf.co/kernels/user/my-kernel/discussions/1"
},
{
"branch": "v3",
"url": "https://hf.co/kernels/user/my-kernel/discussions/2"
}
]
})
);
}

#[test]
fn test_collect_readme_commit_ops() {
let temp_dir = tempfile::tempdir().unwrap();
Expand Down
Loading