feat(server/forgejo): verify branches exist in repo
This commit is contained in:
parent
b093c002d4
commit
ca37045e3a
3 changed files with 125 additions and 30 deletions
|
@ -11,7 +11,7 @@ use super::{LoadedConfig, RepoActor};
|
||||||
|
|
||||||
pub async fn load(details: RepoDetails, addr: Addr<RepoActor>, net: Network) {
|
pub async fn load(details: RepoDetails, addr: Addr<RepoActor>, net: Network) {
|
||||||
let config = match details.forge.forge_type {
|
let config = match details.forge.forge_type {
|
||||||
ForgeType::ForgeJo => forge::forgejo::config::load(details, &net).await,
|
ForgeType::ForgeJo => forge::forgejo::config::load(&details, &net).await,
|
||||||
};
|
};
|
||||||
match config {
|
match config {
|
||||||
Ok(config) => addr.do_send(LoadedConfig(config)),
|
Ok(config) => addr.do_send(LoadedConfig(config)),
|
||||||
|
|
|
@ -34,6 +34,10 @@ impl RepoConfig {
|
||||||
pub(crate) fn load(toml: &str) -> Result<Self, OneOf<(toml::de::Error,)>> {
|
pub(crate) fn load(toml: &str) -> Result<Self, OneOf<(toml::de::Error,)>> {
|
||||||
toml::from_str(toml).map_err(OneOf::new)
|
toml::from_str(toml).map_err(OneOf::new)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) const fn branches(&self) -> &RepoBranches {
|
||||||
|
&self.branches
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl Display for RepoConfig {
|
impl Display for RepoConfig {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
@ -46,6 +50,19 @@ pub struct RepoBranches {
|
||||||
next: String,
|
next: String,
|
||||||
dev: String,
|
dev: String,
|
||||||
}
|
}
|
||||||
|
impl RepoBranches {
|
||||||
|
pub fn main(&self) -> BranchName {
|
||||||
|
BranchName(self.main.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next(&self) -> BranchName {
|
||||||
|
BranchName(self.next.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dev(&self) -> BranchName {
|
||||||
|
BranchName(self.dev.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
impl Display for RepoBranches {
|
impl Display for RepoBranches {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "{:?}", self)
|
write!(f, "{:?}", self)
|
||||||
|
|
|
@ -3,26 +3,37 @@ use kxio::network::{self, Network};
|
||||||
use terrors::OneOf;
|
use terrors::OneOf;
|
||||||
use tracing::{error, info};
|
use tracing::{error, info};
|
||||||
|
|
||||||
use crate::server::config::{RepoConfig, RepoDetails};
|
use crate::server::config::{BranchName, RepoConfig, RepoDetails};
|
||||||
|
|
||||||
pub async fn load(
|
#[derive(Debug)]
|
||||||
details: RepoDetails,
|
pub struct RepoConfigFileNotFound;
|
||||||
net: &Network,
|
#[derive(Debug)]
|
||||||
) -> Result<
|
pub struct RepoConfigIsNotFile;
|
||||||
RepoConfig,
|
#[derive(Debug)]
|
||||||
OneOf<(
|
pub struct RepoConfigDecodeError;
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct RepoConfigParseError;
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct RepoConfigUnknownError(pub network::StatusCode);
|
||||||
|
|
||||||
|
type RepoConfigLoadErrors = (
|
||||||
RepoConfigFileNotFound,
|
RepoConfigFileNotFound,
|
||||||
RepoConfigIsNotFile,
|
RepoConfigIsNotFile,
|
||||||
RepoConfigDecodeError,
|
RepoConfigDecodeError,
|
||||||
RepoConfigParseError,
|
RepoConfigParseError,
|
||||||
RepoConfigUnknownError,
|
RepoConfigUnknownError,
|
||||||
)>,
|
RepoConfigBranchNotFound,
|
||||||
> {
|
);
|
||||||
let hostname = details.forge.hostname;
|
|
||||||
let path = details.repo;
|
pub async fn load(
|
||||||
|
details: &RepoDetails,
|
||||||
|
net: &Network,
|
||||||
|
) -> Result<RepoConfig, OneOf<RepoConfigLoadErrors>> {
|
||||||
|
let hostname = &details.forge.hostname;
|
||||||
|
let path = &details.repo;
|
||||||
let filepath = ".git-next.toml";
|
let filepath = ".git-next.toml";
|
||||||
let branch = details.branch;
|
let branch = &details.branch;
|
||||||
let token = details.forge.token;
|
let token = &details.forge.token;
|
||||||
let url = network::NetUrl::new(format!(
|
let url = network::NetUrl::new(format!(
|
||||||
"https://{hostname}/api/v1/repos/{path}/contents/{filepath}?ref={branch}&token={token}"
|
"https://{hostname}/api/v1/repos/{path}/contents/{filepath}?ref={branch}&token={token}"
|
||||||
));
|
));
|
||||||
|
@ -49,7 +60,7 @@ pub async fn load(
|
||||||
)),
|
)),
|
||||||
})?;
|
})?;
|
||||||
let status = response.status_code();
|
let status = response.status_code();
|
||||||
match response.response_body() {
|
let config = match response.response_body() {
|
||||||
Some(body) => {
|
Some(body) => {
|
||||||
// we need to decode (see encoding field) the value of 'content' from the response
|
// we need to decode (see encoding field) the value of 'content' from the response
|
||||||
match body.content_type {
|
match body.content_type {
|
||||||
|
@ -61,7 +72,11 @@ pub async fn load(
|
||||||
error!(%status, "Failed to fetch repo config file");
|
error!(%status, "Failed to fetch repo config file");
|
||||||
Err(OneOf::new(RepoConfigUnknownError(status)))
|
Err(OneOf::new(RepoConfigUnknownError(status)))
|
||||||
}
|
}
|
||||||
}
|
}?;
|
||||||
|
let config = validate(details, config, net)
|
||||||
|
.await
|
||||||
|
.map_err(OneOf::broaden)?;
|
||||||
|
Ok(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_config(
|
fn decode_config(
|
||||||
|
@ -81,17 +96,6 @@ fn decode_config(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct RepoConfigFileNotFound;
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct RepoConfigIsNotFile;
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct RepoConfigDecodeError;
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct RepoConfigParseError;
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct RepoConfigUnknownError(pub network::StatusCode);
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize)]
|
#[derive(Clone, Debug, serde::Deserialize)]
|
||||||
struct ForgeContentsResponse {
|
struct ForgeContentsResponse {
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
|
@ -110,3 +114,77 @@ enum ForgeContentsType {
|
||||||
#[serde(rename = "submodule")]
|
#[serde(rename = "submodule")]
|
||||||
Submodule,
|
Submodule,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct RepoConfigBranchNotFound(pub BranchName);
|
||||||
|
|
||||||
|
type RepoConfigValidateErrors = (RepoConfigBranchNotFound, RepoConfigUnknownError);
|
||||||
|
|
||||||
|
pub async fn validate(
|
||||||
|
details: &RepoDetails,
|
||||||
|
config: RepoConfig,
|
||||||
|
net: &Network,
|
||||||
|
) -> Result<RepoConfig, OneOf<RepoConfigValidateErrors>> {
|
||||||
|
let hostname = &details.forge.hostname;
|
||||||
|
let path = &details.repo;
|
||||||
|
let token = &details.forge.token;
|
||||||
|
let url = network::NetUrl::new(format!(
|
||||||
|
"https://{hostname}/api/v1/repos/{path}/branches?token={token}"
|
||||||
|
));
|
||||||
|
|
||||||
|
info!(%url, "Listing branches");
|
||||||
|
let request = network::NetRequest::new(
|
||||||
|
network::RequestMethod::Get,
|
||||||
|
url,
|
||||||
|
network::NetRequestHeaders::new(),
|
||||||
|
network::RequestBody::None,
|
||||||
|
network::ResponseType::Json,
|
||||||
|
None,
|
||||||
|
network::NetRequestLogging::Both,
|
||||||
|
);
|
||||||
|
let result = net.get::<BranchList>(request).await;
|
||||||
|
let response = result.map_err(|e| {
|
||||||
|
error!(?e, "Failed to list branches");
|
||||||
|
OneOf::new(RepoConfigUnknownError(
|
||||||
|
network::StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
let branches = response.response_body().unwrap_or_default();
|
||||||
|
if !branches
|
||||||
|
.iter()
|
||||||
|
.any(|branch| branch.name() == config.branches().main())
|
||||||
|
{
|
||||||
|
return Err(OneOf::new(RepoConfigBranchNotFound(
|
||||||
|
config.branches().main(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
if !branches
|
||||||
|
.iter()
|
||||||
|
.any(|branch| branch.name() == config.branches().next())
|
||||||
|
{
|
||||||
|
return Err(OneOf::new(RepoConfigBranchNotFound(
|
||||||
|
config.branches().next(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
if !branches
|
||||||
|
.iter()
|
||||||
|
.any(|branch| branch.name() == config.branches().dev())
|
||||||
|
{
|
||||||
|
return Err(OneOf::new(RepoConfigBranchNotFound(
|
||||||
|
config.branches().dev(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
Ok(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
type BranchList = Vec<Branch>;
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Deserialize)]
|
||||||
|
struct Branch {
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
impl Branch {
|
||||||
|
fn name(&self) -> BranchName {
|
||||||
|
BranchName(self.name.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue