mirror of
https://github.com/cargo-bins/cargo-binstall.git
synced 2025-04-19 12:08:43 +00:00

* Refactor: Create new crate binstalk-git-repo-api Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Fix CI lint warnings Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Fix `just check`: Rm deleted features from `cargo-hack` check Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Refactor: Extract new mod error Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Ret artifact url in `has_release_artifact` So that we can use it to download from private repositories. Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Move `test_graph_ql_error_type` to mod `error` Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Fix running `cargo test` in `binstalk-git-repo-api`` Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Remove unnecessary import in mod `error::test` Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Rename mod `request`` to `release_artifacts` Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Impl draft version of fetching repo info Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Refactor: Move `HasReleaseArtifacts` failure variants into `GhApiError` Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Refactor: Use `GhRepo` in `GhRelease` Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Fix testing Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Refactor: Return `'static` future Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Refactor: Make sure `'static` Future is returned To make it easier to create generic function Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Add logging to unit testing Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Fix unit testing Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Refactor: Extract new fn `GhApiClient::do_fetch` Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Rm unused `percent_encode_http_url_path` Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Fix `cargo test` run on CI `cargo test` run all tests in one process. As such, `set_global_default` would fail on the second call. Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Optimize `GhApiClient::do_fetch`: Avoid unnecessary restful API call Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Refactor: Rm param `auth_token` for restful API fn which is always set to `None` Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Impl new API `GhApiClient::get_repo_info` Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Fix unit test for `GhApiClient::get_repo_info` Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Refactor testing: Parameter-ize testing Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Parallelise `test_get_repo_info` Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Refactor: Create parameter-ised `test_has_release_artifact` Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Parallelize `test_has_release_artifact` Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Refactor: `gh_api_client::test::create_client` shall not be `async` as there is no `.await` in it. Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Return `Url` in `GhApiClient::has_release_artifact` Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Impl new API `GhApiClient::download_artifact` Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Remove unused deps added to binstalk-git-repo-api Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Fix clippy lints Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Add new API `GhApiClient::remote_client` Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Add `GhApiClient::has_gh_token` Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Add `GhRepo::try_extract_from_url` Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Rename `ReleaseArtifactUrl` to `GhReleaseArtifactUrl` Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Add new fn `Download::with_data_verifier` Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * feature: Support private repository Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Fix clippy lints Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Add e2e-test/private-github-repo Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Fix clippy lints Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Fix `launch_baseline_find_tasks`: Retry on rate limit Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Fix test failure: Retry on rate limit Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Temporarily enable debug output for e2e-test-private-github-repo Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Fix `get_repo_info`: Retry on rate limit Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Improve `debug!` logging Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Add more debug logging Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Add more debugging Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Add more debug logging Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Apply suggestions from code review * Fix compilation Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Fix cargo fmt Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Add crate binstalk-git-repo-api to release-pr.yml * Update crates/binstalk-git-repo-api/Cargo.toml * Apply suggestions from code review * Update crates/binstalk/Cargo.toml --------- Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com>
143 lines
3.9 KiB
Rust
143 lines
3.9 KiB
Rust
use std::{fmt::Debug, future::Future, sync::OnceLock, time::Duration};
|
|
|
|
use binstalk_downloader::remote::{self, Response, Url};
|
|
use compact_str::CompactString;
|
|
use percent_encoding::percent_decode_str;
|
|
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
|
use serde_json::to_string as to_json_string;
|
|
use tracing::debug;
|
|
|
|
use super::{GhApiError, GhGraphQLErrors};
|
|
|
|
pub(super) fn percent_decode_http_url_path(input: &str) -> CompactString {
|
|
if input.contains('%') {
|
|
percent_decode_str(input).decode_utf8_lossy().into()
|
|
} else {
|
|
// No '%', no need to decode.
|
|
CompactString::new(input)
|
|
}
|
|
}
|
|
|
|
pub(super) fn check_http_status_and_header(response: &Response) -> Result<(), GhApiError> {
|
|
let headers = response.headers();
|
|
|
|
match response.status() {
|
|
remote::StatusCode::FORBIDDEN
|
|
if headers
|
|
.get("x-ratelimit-remaining")
|
|
.map(|val| val == "0")
|
|
.unwrap_or(false) =>
|
|
{
|
|
Err(GhApiError::RateLimit {
|
|
retry_after: headers.get("x-ratelimit-reset").and_then(|value| {
|
|
let secs = value.to_str().ok()?.parse().ok()?;
|
|
Some(Duration::from_secs(secs))
|
|
}),
|
|
})
|
|
}
|
|
|
|
remote::StatusCode::UNAUTHORIZED => Err(GhApiError::Unauthorized),
|
|
remote::StatusCode::NOT_FOUND => Err(GhApiError::NotFound),
|
|
|
|
_ => Ok(()),
|
|
}
|
|
}
|
|
|
|
fn get_api_endpoint() -> &'static Url {
|
|
static API_ENDPOINT: OnceLock<Url> = OnceLock::new();
|
|
|
|
API_ENDPOINT.get_or_init(|| {
|
|
Url::parse("https://api.github.com/").expect("Literal provided must be a valid url")
|
|
})
|
|
}
|
|
|
|
pub(super) fn issue_restful_api<T>(
|
|
client: &remote::Client,
|
|
path: &[&str],
|
|
) -> impl Future<Output = Result<T, GhApiError>> + Send + Sync + 'static
|
|
where
|
|
T: DeserializeOwned,
|
|
{
|
|
let mut url = get_api_endpoint().clone();
|
|
|
|
url.path_segments_mut()
|
|
.expect("get_api_endpoint() should return a https url")
|
|
.extend(path);
|
|
|
|
debug!("Getting restful API: {url}");
|
|
|
|
let future = client
|
|
.get(url)
|
|
.header("Accept", "application/vnd.github+json")
|
|
.header("X-GitHub-Api-Version", "2022-11-28")
|
|
.send(false);
|
|
|
|
async move {
|
|
let response = future.await?;
|
|
|
|
check_http_status_and_header(&response)?;
|
|
|
|
Ok(response.json().await?)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
struct GraphQLResponse<T> {
|
|
data: T,
|
|
errors: Option<GhGraphQLErrors>,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
struct GraphQLQuery {
|
|
query: String,
|
|
}
|
|
|
|
fn get_graphql_endpoint() -> Url {
|
|
let mut graphql_endpoint = get_api_endpoint().clone();
|
|
|
|
graphql_endpoint
|
|
.path_segments_mut()
|
|
.expect("get_api_endpoint() should return a https url")
|
|
.push("graphql");
|
|
|
|
graphql_endpoint
|
|
}
|
|
|
|
pub(super) fn issue_graphql_query<T>(
|
|
client: &remote::Client,
|
|
query: String,
|
|
auth_token: &str,
|
|
) -> impl Future<Output = Result<T, GhApiError>> + Send + Sync + 'static
|
|
where
|
|
T: DeserializeOwned + Debug,
|
|
{
|
|
let res = to_json_string(&GraphQLQuery { query })
|
|
.map_err(remote::Error::from)
|
|
.map(|graphql_query| {
|
|
let graphql_endpoint = get_graphql_endpoint();
|
|
|
|
debug!("Sending graphql query to {graphql_endpoint}: '{graphql_query}'");
|
|
|
|
let request_builder = client
|
|
.post(graphql_endpoint, graphql_query)
|
|
.header("Accept", "application/vnd.github+json")
|
|
.bearer_auth(&auth_token);
|
|
|
|
request_builder.send(false)
|
|
});
|
|
|
|
async move {
|
|
let response = res?.await?;
|
|
check_http_status_and_header(&response)?;
|
|
|
|
let mut response: GraphQLResponse<T> = response.json().await?;
|
|
|
|
debug!("response = {response:?}");
|
|
|
|
if let Some(error) = response.errors.take() {
|
|
Err(error.into())
|
|
} else {
|
|
Ok(response.data)
|
|
}
|
|
}
|
|
}
|