vdev/commands/release/
homebrew.rs

1use crate::utils::git;
2use anyhow::Result;
3use sha2::Digest;
4use std::{env, fs, path::Path};
5use tempfile::TempDir;
6
7/// Releases latest version to the vectordotdev homebrew tap
8#[derive(clap::Args, Debug)]
9#[command()]
10pub struct Cli {
11    /// GitHub username for the repository.
12    #[arg(long, default_value = "vectordotdev")]
13    username: String,
14
15    /// Vector version, defaults to $`VECTOR_VERSION` if not specified.
16    #[arg(long, env = "VECTOR_VERSION")]
17    vector_version: String,
18}
19
20impl Cli {
21    pub fn exec(self) -> Result<()> {
22        // Create temporary directory for cloning the homebrew-brew repository
23        let td = TempDir::new()?;
24        env::set_current_dir(td.path())?;
25
26        debug!(
27            "Cloning the homebrew repository for username: {}",
28            self.username
29        );
30        clone_and_setup_git(&self.username)?;
31
32        let vector_version = env::var("VECTOR_VERSION")?;
33        debug!("Updating the vector.rb formula for VECTOR_VERSION={vector_version}.");
34        update_formula("Formula/vector.rb", &vector_version)?;
35
36        debug!("Committing and pushing changes (if any).");
37        commit_and_push_changes(&vector_version)?;
38
39        td.close()?;
40        Ok(())
41    }
42}
43
44/// Clones the repository and sets up Git configuration
45fn clone_and_setup_git(username: &str) -> Result<()> {
46    let github_token = env::var("HOMEBREW_PAT").or_else(|_| env::var("GITHUB_TOKEN"))?;
47    let homebrew_repo =
48        format!("https://{username}:{github_token}@github.com/{username}/homebrew-brew.git");
49
50    git::clone(&homebrew_repo)?;
51    env::set_current_dir("homebrew-brew")?;
52    git::set_config_value("user.name", "vic")?;
53    git::set_config_value("user.email", "vector@datadoghq.com")?;
54    Ok(())
55}
56
57/// Updates the vector.rb formula with new URLs, SHA256s, and version
58fn update_formula<P>(file_path: P, vector_version: &str) -> Result<()>
59where
60    P: AsRef<Path>,
61{
62    // URLs and SHA256s for both architectures
63    let arm_package_url = format!(
64        "https://packages.timber.io/vector/{vector_version}/vector-{vector_version}-arm64-apple-darwin.tar.gz"
65    );
66    let arm_package_sha256 = hex::encode(sha2::Sha256::digest(
67        reqwest::blocking::get(&arm_package_url)?.bytes()?,
68    ));
69
70    let content = fs::read_to_string(&file_path)?;
71
72    // Replace the lines with updated URLs and SHA256s
73    let updated_content = content
74        .lines()
75        .map(|line| {
76            if line.trim_start().starts_with("version \"") {
77                format!("  version \"{vector_version}\"")
78            } else if line.contains("# arm64 url") {
79                format!("      url \"{arm_package_url}\" # arm64 url")
80            } else if line.contains("# arm64 sha256") {
81                format!("      sha256 \"{arm_package_sha256}\" # arm64 sha256")
82            } else {
83                line.to_string()
84            }
85        })
86        .collect::<Vec<_>>()
87        .join("\n");
88
89    fs::write(file_path, updated_content)?;
90    Ok(())
91}
92
93/// Commits and pushes changes if any exist
94fn commit_and_push_changes(vector_version: &str) -> Result<()> {
95    let has_changes = !git::check_git_repository_clean()?;
96    if has_changes {
97        debug!("Modified lines {:?}", git::get_modified_files());
98        let commit_message = format!("Release Vector {vector_version}");
99        git::commit(&commit_message)?;
100        git::push()?;
101    } else {
102        debug!("No changes to push.");
103    }
104    Ok(())
105}