From fad10d0a3bdaf50687926a831bb395b798745812 Mon Sep 17 00:00:00 2001 From: Jesse Stuart Date: Tue, 12 Aug 2025 15:26:43 -0400 Subject: [PATCH] github: support overriding api url --- crates/squawk/src/github.rs | 9 ++++- crates/squawk/src/main.rs | 3 ++ crates/squawk_github/src/actions.rs | 12 ++++++- crates/squawk_github/src/app.rs | 51 ++++++++++++++++++++++------- crates/squawk_github/src/lib.rs | 2 ++ 5 files changed, 63 insertions(+), 14 deletions(-) diff --git a/crates/squawk/src/github.rs b/crates/squawk/src/github.rs index b17fa65b..6eec4f32 100644 --- a/crates/squawk/src/github.rs +++ b/crates/squawk/src/github.rs @@ -33,6 +33,7 @@ fn get_github_private_key( } fn create_gh_app( + github_api_url: Option, github_install_id: Option, github_app_id: Option, github_token: Option, @@ -51,7 +52,11 @@ fn create_gh_app( if let Some(github_token) = github_token { info!("using github actions client"); - return Ok(Box::new(actions::GitHub::new(&github_token))); + let client = match github_api_url { + Some(github_api_url) => actions::GitHub::new_with_url(&github_api_url, &github_token), + None => actions::GitHub::new(&github_token), + }; + return Ok(Box::new(client)); }; bail!( "Missing GitHub credentials: @@ -84,6 +89,7 @@ pub fn check_and_comment_on_pr( paths, fail_on_violations, github_private_key, + github_api_url, github_token, github_app_id, github_install_id, @@ -101,6 +107,7 @@ pub fn check_and_comment_on_pr( }; let github_app = create_gh_app( + github_api_url, github_install_id, github_app_id, github_token, diff --git a/crates/squawk/src/main.rs b/crates/squawk/src/main.rs index 06ee258d..04b84d7d 100644 --- a/crates/squawk/src/main.rs +++ b/crates/squawk/src/main.rs @@ -32,6 +32,9 @@ pub struct UploadToGithubArgs { github_private_key: Option, #[structopt(long, env = "SQUAWK_GITHUB_PRIVATE_KEY_BASE64")] github_private_key_base64: Option, + /// GitHub API url. + #[structopt(long, env = "SQUAWK_GITHUB_API_URL")] + github_api_url: Option, #[structopt(long, env = "SQUAWK_GITHUB_TOKEN")] github_token: Option, /// GitHub App Id. diff --git a/crates/squawk_github/src/actions.rs b/crates/squawk_github/src/actions.rs index 04813368..39b668b5 100644 --- a/crates/squawk_github/src/actions.rs +++ b/crates/squawk_github/src/actions.rs @@ -1,13 +1,20 @@ use crate::app; -use crate::{Comment, GitHubApi, GithubError}; +use crate::{Comment, DEFAULT_GITHUB_API_URL, GitHubApi, GithubError}; pub struct GitHub { + github_api_url: String, github_token: String, } impl GitHub { #[must_use] pub fn new(github_token: &str) -> Self { + Self::new_with_url(DEFAULT_GITHUB_API_URL, github_token) + } + + #[must_use] + pub fn new_with_url(github_api_url: &str, github_token: &str) -> Self { GitHub { + github_api_url: github_api_url.to_string(), github_token: github_token.to_string(), } } @@ -24,6 +31,7 @@ impl GitHubApi for GitHub { body: &str, ) -> Result<(), GithubError> { app::create_comment( + &self.github_api_url, app::CommentArgs { owner: owner.to_string(), repo: repo.to_string(), @@ -40,6 +48,7 @@ impl GitHubApi for GitHub { issue_id: i64, ) -> Result, GithubError> { app::list_comments( + &self.github_api_url, &app::PullRequest { issue: issue_id, owner: owner.to_string(), @@ -56,6 +65,7 @@ impl GitHubApi for GitHub { body: &str, ) -> Result<(), GithubError> { app::update_comment( + &self.github_api_url, owner, repo, comment_id, diff --git a/crates/squawk_github/src/app.rs b/crates/squawk_github/src/app.rs index d41e0ced..a9adfe74 100644 --- a/crates/squawk_github/src/app.rs +++ b/crates/squawk_github/src/app.rs @@ -1,7 +1,7 @@ #![allow(clippy::doc_markdown)] #![allow(clippy::missing_errors_doc)] #![allow(clippy::single_match_else)] -use crate::{Comment, GitHubApi, GithubError}; +use crate::{Comment, DEFAULT_GITHUB_API_URL, GitHubApi, GithubError}; use jsonwebtoken::{Algorithm, EncodingKey, Header}; use log::info; @@ -32,10 +32,14 @@ pub struct GithubAccessToken { pub token: String, } /// https://developer.github.com/v3/apps/#create-an-installation-access-token-for-an-app -fn create_access_token(jwt: &str, install_id: i64) -> Result { +fn create_access_token( + github_api_url: &str, + jwt: &str, + install_id: i64, +) -> Result { Ok(reqwest::Client::new() .post(&format!( - "https://api.github.com/app/installations/{install_id}/access_tokens", + "{github_api_url}/app/installations/{install_id}/access_tokens", )) .header(AUTHORIZATION, format!("Bearer {jwt}")) .header(ACCEPT, "application/vnd.github.machine-man-preview+json") @@ -45,11 +49,15 @@ fn create_access_token(jwt: &str, install_id: i64) -> Result Result<(), GithubError> { +pub(crate) fn create_comment( + github_api_url: &str, + comment: CommentArgs, + secret: &str, +) -> Result<(), GithubError> { let comment_body = CommentBody { body: comment.body }; reqwest::Client::new() .post(&format!( - "https://api.github.com/repos/{owner}/{repo}/issues/{issue_number}/comments", + "{github_api_url}/repos/{owner}/{repo}/issues/{issue_number}/comments", owner = comment.owner, repo = comment.repo, issue_number = comment.issue @@ -68,9 +76,9 @@ pub struct GitHubAppInfo { } /// Get the bot name for finding existing comments on a PR -pub fn get_app_info(jwt: &str) -> Result { +pub fn get_app_info(github_api_url: &str, jwt: &str) -> Result { Ok(reqwest::Client::new() - .get("https://api.github.com/app") + .get(&format!("{github_api_url}/app")) .header(AUTHORIZATION, format!("Bearer {jwt}")) .send()? .error_for_status()? @@ -142,12 +150,16 @@ pub struct PullRequest { } /// https://developer.github.com/v3/issues/comments/#list-issue-comments -pub(crate) fn list_comments(pr: &PullRequest, secret: &str) -> Result, GithubError> { +pub(crate) fn list_comments( + github_api_url: &str, + pr: &PullRequest, + secret: &str, +) -> Result, GithubError> { // TODO(sbdchd): use the next links to get _all_ the comments // see: https://developer.github.com/v3/guides/traversing-with-pagination/ Ok(reqwest::Client::new() .get(&format!( - "https://api.github.com/repos/{owner}/{repo}/issues/{issue_number}/comments", + "{github_api_url}/repos/{owner}/{repo}/issues/{issue_number}/comments", owner = pr.owner, repo = pr.repo, issue_number = pr.issue @@ -161,6 +173,7 @@ pub(crate) fn list_comments(pr: &PullRequest, secret: &str) -> Result Result<(), GithubError> { reqwest::Client::new() .patch(&format!( - "https://api.github.com/repos/{owner}/{repo}/issues/comments/{comment_id}", + "{github_api_url}/repos/{owner}/{repo}/issues/comments/{comment_id}", )) .header(AUTHORIZATION, format!("Bearer {secret}")) .json(&CommentBody { body }) @@ -179,19 +192,30 @@ pub(crate) fn update_comment( } pub struct GitHub { + github_api_url: String, slug_name: String, installation_access_token: String, } impl GitHub { pub fn new(private_key: &str, app_id: i64, installation_id: i64) -> Result { + Self::new_with_url(DEFAULT_GITHUB_API_URL, private_key, app_id, installation_id) + } + + pub fn new_with_url( + github_api_url: &str, + private_key: &str, + app_id: i64, + installation_id: i64, + ) -> Result { info!("generating jwt"); let jwt = generate_jwt(private_key, app_id)?; info!("getting app info"); - let app_info = get_app_info(&jwt)?; - let access_token = create_access_token(&jwt, installation_id)?; + let app_info = get_app_info(github_api_url, &jwt)?; + let access_token = create_access_token(github_api_url, &jwt, installation_id)?; Ok(GitHub { + github_api_url: github_api_url.to_string(), slug_name: format!("{}[bot]", app_info.slug), installation_access_token: access_token.token, }) @@ -210,6 +234,7 @@ impl GitHubApi for GitHub { body: &str, ) -> Result<(), GithubError> { create_comment( + &self.github_api_url, CommentArgs { owner: owner.to_string(), repo: repo.to_string(), @@ -226,6 +251,7 @@ impl GitHubApi for GitHub { issue_id: i64, ) -> Result, GithubError> { list_comments( + &self.github_api_url, &PullRequest { owner: owner.to_string(), repo: repo.to_string(), @@ -242,6 +268,7 @@ impl GitHubApi for GitHub { body: &str, ) -> Result<(), GithubError> { update_comment( + &self.github_api_url, owner, repo, comment_id, diff --git a/crates/squawk_github/src/lib.rs b/crates/squawk_github/src/lib.rs index e5520f98..8edea921 100644 --- a/crates/squawk_github/src/lib.rs +++ b/crates/squawk_github/src/lib.rs @@ -8,6 +8,8 @@ use std::error::Error; use log::info; use serde::{Deserialize, Serialize}; +pub(crate) const DEFAULT_GITHUB_API_URL: &'static str = "https://api.github.com"; + #[derive(Debug)] pub enum GithubError { JsonWebTokenCreation(jsonwebtoken::errors::Error),