swarm-controller: read a repo's review rules from its default branch
`GET /api/review-classifier/rules?repo=owner/name` reads `.hyperhive/review-classifier.yml` or `.json` from the repo's default branch with the controller's forge token, on every request, and answers the classifications in the try-out POST's shape, plus the file, the branch and the commit it was read at. The swarm UI's try-out page loads them into its editor (#5134). The file is one `classifications` list of the try-out's `Classification`. A missing file, both files, a parse error, any unknown key, an empty list or more than ten entries is a problem+json error, never a pass. The checks the POST makes on its classifications move into one function both paths call. The doc names the file and its schema, replacing the placeholders, and documents the route. Part of #5096.
This commit is contained in:
parent
c5b66dc5a0
commit
16c7c249ef
6 changed files with 624 additions and 55 deletions
|
|
@ -25,8 +25,8 @@ use forgejo_api::structs::{
|
|||
CreateHookOption, CreateHookOptionConfig, CreateHookOptionType, CreateRepoOption,
|
||||
CreateUserOption, EditUserOption, IssueListIssuesQuery, IssueListIssuesQueryState,
|
||||
IssueListIssuesQueryType, RepoDownloadPullDiffOrPatchQuery, RepoGetContentsQuery,
|
||||
RepoGetPullRequestCommitsQuery, RepoListPullRequestsQuery, RepoListPullRequestsQueryState,
|
||||
RepoSearchQuery, StateType,
|
||||
RepoGetPullRequestCommitsQuery, RepoGetRawFileQuery, RepoListPullRequestsQuery,
|
||||
RepoListPullRequestsQueryState, RepoSearchQuery, StateType,
|
||||
};
|
||||
use forgejo_api::{ApiErrorKind, Auth, Forgejo, ForgejoError};
|
||||
use futures_util::{StreamExt as _, TryStreamExt as _};
|
||||
|
|
@ -65,6 +65,18 @@ pub struct PullForReview {
|
|||
pub diff: String,
|
||||
}
|
||||
|
||||
/// Files on a repo's default branch, as [`Client::default_branch_files`] reads
|
||||
/// them.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct DefaultBranchFiles {
|
||||
pub branch: String,
|
||||
/// The commit the files were read at.
|
||||
pub commit: String,
|
||||
/// Each asked-for path that exists there, with its bytes, in the order
|
||||
/// asked.
|
||||
pub files: Vec<(String, Vec<u8>)>,
|
||||
}
|
||||
|
||||
/// One row of [`Client::issue_report`] — an open issue plus the two facts
|
||||
/// derived from its dependency graph that swarm-ui's issue-report page
|
||||
/// sorts/filters on. See that function's doc comment for how both are
|
||||
|
|
@ -1102,6 +1114,59 @@ impl Client {
|
|||
})
|
||||
}
|
||||
|
||||
/// The `paths` of `owner/repo` that exist on its default branch, all
|
||||
/// read at the branch's head commit. `None` for a repo with no commit.
|
||||
///
|
||||
/// # Errors
|
||||
/// A forge failure, with the [`ForgejoError`] underneath; a 404 on the
|
||||
/// first call means the repo does not exist ([`is_repo_not_found`]).
|
||||
pub async fn default_branch_files(
|
||||
&self,
|
||||
owner: &str,
|
||||
repo: &str,
|
||||
paths: &[&str],
|
||||
) -> Result<Option<DefaultBranchFiles>> {
|
||||
let info = self
|
||||
.api
|
||||
.repo_get(owner, repo)
|
||||
.await
|
||||
.with_context(|| format!("get repo {owner}/{repo}"))?;
|
||||
if info.empty.unwrap_or(false) {
|
||||
return Ok(None);
|
||||
}
|
||||
let branch = info
|
||||
.default_branch
|
||||
.with_context(|| format!("{owner}/{repo} names no default branch"))?;
|
||||
let commit = self
|
||||
.api
|
||||
.repo_get_branch(owner, repo, &branch)
|
||||
.await
|
||||
.with_context(|| format!("read branch {branch} of {owner}/{repo}"))?
|
||||
.commit
|
||||
.and_then(|c| c.id)
|
||||
.with_context(|| format!("branch {branch} of {owner}/{repo} names no commit"))?;
|
||||
let mut files = Vec::new();
|
||||
for path in paths {
|
||||
let query = RepoGetRawFileQuery {
|
||||
r#ref: Some(commit.clone()),
|
||||
};
|
||||
match self.api.repo_get_raw_file(owner, repo, path, query).await {
|
||||
Ok(bytes) => files.push(((*path).to_owned(), bytes.to_vec())),
|
||||
Err(e) if is_repo_not_found(&e) => {}
|
||||
Err(e) => {
|
||||
return Err(e).with_context(|| {
|
||||
format!("read {path} of {owner}/{repo} at {branch} ({commit})")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Some(DefaultBranchFiles {
|
||||
branch,
|
||||
commit,
|
||||
files,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Register the swarm-wide hooks against this controller, so a real
|
||||
/// forge event reaches [`crate::webhook`] instead of the endpoint only
|
||||
/// being reachable by hand.
|
||||
|
|
|
|||
Loading…
Reference in a new issue