tag2upload_service_manager/
gitlab.rs
use crate::prelude::*;
use webhook::*;
#[derive(Deserialize, Eq, PartialEq)]
#[derive(derive_more::Display, derive_more::FromStr)]
#[serde(transparent)]
pub struct ProjectId(pub u64);
#[derive(Deserialize)]
pub struct Payload {
object_kind: String,
after: GitObjectIdOrNull,
#[serde(rename = "ref")]
tag_ref_name: String,
project: Project,
message: String,
}
#[derive(Deserialize)]
struct Project {
git_http_url: String,
id: ProjectId,
}
impl SomeWebhookPayload for Payload {
type Forge = Forge1;
}
pub struct DbData {
project_id: ProjectId,
}
impl TryFrom<Payload> for RawWebhookPayloadData<DbData> {
type Error = WebError;
fn try_from(p: Payload) -> Result<RawWebhookPayloadData<DbData>,
WebError> {
let Payload { object_kind, after, tag_ref_name, project, message } = p;
let Project { git_http_url, id: project_id } = project;
let tag_objectid = after.try_into().map_err(
|UnexpectedNullGitObjectId| NFR::TagIsBeingDeleted
)?;
if object_kind != "tag_push" {
return Err(WE::MisconfiguredWebhook(anyhow!(
"unexpected event {:?}", object_kind
)));
}
let tag_name = tag_ref_name.strip_prefix("refs/tags/")
.ok_or_else(|| WE::MalfunctioningWebhook(anyhow!(
"tag ref name doesn't start refs/tags/"
)))?
.to_owned();
let tag_meta = t2umeta::Parsed::from_tag_message(&message)?;
let repo_git_url = git_http_url;
let forge_data = DbData { project_id };
Ok(RawWebhookPayloadData {
repo_git_url,
tag_objectid,
tag_name,
tag_meta,
forge_data,
})
}
}
impl FromStr for DbData {
type Err = IE;
fn from_str(s: &str) -> Result<Self, IE> {
let project_id = s.parse().into_internal("parse project id")?;
Ok(DbData {
project_id,
})
}
}
impl Display for DbData {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let DbData {
project_id,
} = self;
Display::fmt(project_id, f)?;
Ok(())
}
}
#[derive(Default, Debug)]
pub struct Forge1;
#[derive(Deserialize)]
struct TagResponse {
#[serde(rename = "target")]
tag_objid: GitObjectId,
created_at: humantime_serde::Serde<SystemTime>,
}
#[async_trait]
impl SomeForge for Forge1 {
type DbData = DbData where Self: Sized;
fn kind_name(&self) -> &'static str { "gitlab" }
fn namever_str(&self) -> &str { "gitlab-1" }
async fn make_progress(
&self,
host: &Hostname,
task_tmpdir: &str,
) -> Result<(), QuitTask> {
let globals = globals();
let mut job = JobInWorkflow::start_for_forge(
&host,
self.namever_str(),
).await?;
let jid = job.jid;
let db_data = job.forge_db_data(self)?;
trace!(%host, %jid, "fetching tag");
let url = (|| {
let mut url: Url = format!("https://{}", &job.data.forge_host)
.parse().context("parse initial forge_host https url")?;
url.path_segments_mut()
.map_err(|()| internal!("path no segments?"))?
.extend([
"api", "v4", "projects",
&db_data.project_id.to_string(),
"repository", "tags",
&job.data.tag_name,
]);
Ok::<_, AE>(url)
})()
.into_internal("construct tag API url")?;
trace!(%jid, %url, "gitab tag info");
test_hook_url!(url);
let outcome = async {
let TagResponse {
created_at,
tag_objid: confirmed_tag,
} = globals.http_fetch_json(url.clone())
.await
.context("fetch tag info")
.map_err(PE::Forge)?;
trace!(%jid, ?created_at, "gitab tag date");
MismatchError::check(
"tag object id",
&job.data.tag_objectid,
&confirmed_tag,
)?;
let _is_recent_enough = globals.check_tag_recency(*created_at)?;
gitclone::fetch_tags_via_clone(
&mut job,
&task_tmpdir,
).await
}.await;
fetcher::record_fetch_outcome(
job,
outcome,
)?;
Ok(())
}
}