Fetch the in-repo config file from the remote repo.

This commit is contained in:
Tom Alexander 2024-09-28 16:51:27 -04:00
parent c32a8650f5
commit 66228f83f2
Signed by: talexander
GPG Key ID: D3A179C9A53C0EDE
6 changed files with 97 additions and 16 deletions

7
list_files_curl.bash Executable file
View File

@ -0,0 +1,7 @@
#!/usr/bin/env bash
#
set -euo pipefail
IFS=$'\n\t'
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
curl -vv -H "Authorization: token $(cat /bridge/git/mrmanager/k8s/lighthouse/secrets/lighthouse/lighthouse/OAUTH_TOKEN)" 'https://code.fizz.buzz/api/v1/repos/talexander/organic/git/trees/841a348dd02f31ee8828f069b2a948712369069d?recursive=true&per_page=10&page=60'

7
run.bash Executable file
View File

@ -0,0 +1,7 @@
#!/usr/bin/env bash
#
set -euo pipefail
IFS=$'\n\t'
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
RUST_LOG=webhook_bridge=DEBUG WEBHOOK_BRIDGE_API_ROOT="https://code.fizz.buzz/api" WEBHOOK_BRIDGE_HMAC_SECRET=$(cat /bridge/git/mrmanager/k8s/lighthouse/secrets/lighthouse/lighthouse/HMAC_TOKEN) WEBHOOK_BRIDGE_OAUTH_TOKEN=$(cat /bridge/git/mrmanager/k8s/lighthouse/secrets/lighthouse/lighthouse/OAUTH_TOKEN) cargo run

24
src/discovery.rs Normal file
View File

@ -0,0 +1,24 @@
use crate::gitea_client::GiteaClient;
use crate::gitea_client::TreeFileReference;
use crate::in_repo_config::InRepoConfig;
pub(crate) async fn discover_webhook_bridge_config<O: AsRef<str>, R: AsRef<str>, C: AsRef<str>>(
gitea: &GiteaClient,
owner: O,
repo: R,
commit: C,
) -> Result<InRepoConfig, Box<dyn std::error::Error>> {
let repo_tree = gitea.get_tree(owner, repo, commit).await?;
let in_repo_config_reference = repo_tree
.files
.iter()
.filter(|file_reference| file_reference.path == ".webhook_bridge/webhook_bridge.toml")
.next()
.ok_or("File not found in remote repo: .webhook_bridge/webhook_bridge.toml.")?;
let in_repo_config_contents =
String::from_utf8(gitea.read_file(in_repo_config_reference).await?)?;
let parsed_in_repo_config = InRepoConfig::from_str(in_repo_config_contents)?;
Ok(parsed_in_repo_config)
}

View File

@ -1,4 +1,6 @@
use base64::{engine::general_purpose, Engine as _};
use serde::Deserialize;
use tracing::debug;
use self::error::GiteaClientError;
pub(crate) mod error;
@ -30,7 +32,7 @@ impl GiteaClient {
let mut num_responses: u64 = 0;
loop {
let url = format!(
"{api_root}/v1/repos/{owner}/{repo}/git/trees/{commit}?recursive=true&per_page=10{page}",
"{api_root}/v1/repos/{owner}/{repo}/git/trees/{commit}?recursive=true&per_page=100{page}",
api_root = self.api_root,
owner = owner.as_ref(),
repo = repo.as_ref(),
@ -70,6 +72,27 @@ impl GiteaClient {
}
Ok(Tree::new(files))
}
pub(crate) async fn read_file(
&self,
file_reference: &TreeFileReference,
) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
let response = self
.http_client
.get(&file_reference.url)
.header("Authorization", format!("token {}", self.token))
.send()
.await?;
let response = response.error_for_status()?;
let body = response.text().await?;
debug!("read_file response: {}", body);
let parsed_body: ResponseReadFile = serde_json::from_str(body.as_str())?;
assert!(
parsed_body.encoding == "base64",
"We currently only support base64 file encoding from gitea."
);
Ok(general_purpose::STANDARD.decode(parsed_body.content)?)
}
}
/// A single API response for GetTree containing only one page.
@ -101,13 +124,13 @@ pub(crate) struct Tree {
#[derive(Debug)]
pub(crate) struct TreeFileReference {
path: String,
url: String,
pub(crate) path: String,
pub(crate) url: String,
}
impl Tree {
pub(crate) fn new(files: Vec<TreeFileReference>) -> Tree {
Tree { files: Vec::new() }
Tree { files }
}
}
@ -119,3 +142,12 @@ impl TreeFileReference {
}
}
}
#[derive(Debug, Deserialize)]
struct ResponseReadFile {
content: String,
encoding: String,
url: String,
sha: String,
size: u64,
}

View File

@ -4,8 +4,7 @@ use serde::Serialize;
/// The webhook_bridge.toml file that lives inside repos that have their CI triggered by webhook_bridge.
#[derive(Serialize, Deserialize, Clone, Debug)]
pub(crate) struct InRepoConfig {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub(crate) version: Option<String>,
pub(crate) version: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub(crate) push: Vec<TriggerPush>,
@ -29,3 +28,16 @@ pub(crate) struct TriggerPush {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub(crate) skip_branches: Vec<String>,
}
impl InRepoConfig {
pub(crate) fn from_str<S: AsRef<str>>(
contents: S,
) -> Result<InRepoConfig, Box<dyn std::error::Error>> {
let parsed_in_repo_config: InRepoConfig = toml::from_str(contents.as_ref())?;
assert!(
parsed_in_repo_config.version == "0.0.1",
"We only support version 0.0.1 currently."
);
Ok(parsed_in_repo_config)
}
}

View File

@ -19,6 +19,7 @@ use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
use self::crd_pipeline_run::PipelineRun;
use self::discovery::discover_webhook_bridge_config;
use self::gitea_client::GiteaClient;
use self::in_repo_config::InRepoConfig;
use self::webhook::hook;
@ -26,13 +27,13 @@ use self::webhook::verify_signature;
use kube::CustomResourceExt;
mod crd_pipeline_run;
mod discovery;
mod gitea_client;
mod hook_push;
mod in_repo_config;
mod webhook;
const EXAMPLE_PIPELINE_RUN: &'static str = include_str!("../example_pipeline_run.json");
const TEST_IN_REPO_CONFIG: &'static str = include_str!("../.webhook_bridge/webhook_bridge.toml");
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
@ -53,15 +54,13 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let gitea_api_token = std::env::var("WEBHOOK_BRIDGE_OAUTH_TOKEN")?;
let gitea = GiteaClient::new(gitea_api_root, gitea_api_token);
// gitea
// .get_tree(
// "talexander",
// "organic",
// "841a348dd02f31ee8828f069b2a948712369069d",
// )
// .await?;
let parsed_in_repo_config: InRepoConfig = toml::from_str(TEST_IN_REPO_CONFIG)?;
discover_webhook_bridge_config(
&gitea,
"talexander",
"webhook_bridge",
"c32a8650f509b5e4bbf6df0c210a3a01ad405eb5",
)
.await?;
// let jobs: Api<PipelineRun> = Api::namespaced(kubernetes_client, "lighthouse");
// let jobs: Api<PipelineRun> = Api::default_namespaced(kubernetes_client);