tag2upload_service_manager/
gitlab.rs1
2use crate::prelude::*;
3use webhook::*;
4
5#[derive(Deserialize, Eq, PartialEq)]
6#[derive(derive_more::Display, derive_more::FromStr)]
7#[serde(transparent)]
8pub struct ProjectId(pub u64);
9
10#[derive(Deserialize)]
11pub struct Payload {
12 object_kind: String,
13 after: GitObjectIdOrNull,
14 #[serde(rename = "ref")]
15 tag_ref_name: String,
16 project: Project,
17 message: String,
18}
19
20#[derive(Deserialize)]
21struct Project {
22 git_http_url: String,
23 id: ProjectId,
24}
25
26pub struct DbData {
27 project_id: ProjectId,
28}
29
30impl ForgeKind for Forge1 {
31 type Payload = Payload;
32
33 fn analyse_payload(&self, payload: Self::Payload)
34 -> Result<AnalysedPayload<Self>, WebError>
35 {
36 let Payload { object_kind, after, tag_ref_name, project, message }
37 = payload;
38
39 let Project { git_http_url, id } = project;
40
41 let tag_objectid = after.try_into().map_err(
42 |UnexpectedNullGitObjectId| NFR::TagIsBeingDeleted
43 )?;
44
45 if object_kind != "tag_push" {
46 return Err(WE::MisconfiguredWebhook(anyhow!(
47 "unexpected event {:?}", object_kind
48 )));
49 }
50 let tag_name = tag_ref_name.strip_prefix("refs/tags/")
51 .ok_or_else(|| WE::MisconfiguredWebhook(anyhow!(
52 "tag ref name doesn't start refs/tags/"
53 )))?
54 .to_owned();
55
56 let repo_git_url = git_http_url;
57 let forge_data = DbData { project_id: id };
58
59 Ok(AnalysedPayload {
60 repo_git_url,
61 tag_objectid,
62 tag_name,
63 tag_message: message,
64 forge_data,
65 })
66 }
67}
68
69impl FromStr for DbData {
70 type Err = IE;
71
72 fn from_str(s: &str) -> Result<Self, IE> {
73 let project_id = s.parse().into_internal("parse project id")?;
74 Ok(DbData {
75 project_id,
76 })
77 }
78}
79impl Display for DbData {
80 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
81 let DbData {
82 project_id,
83 } = self;
84 Display::fmt(project_id, f)?;
85 Ok(())
86 }
87}
88
89#[derive(Default, Debug)]
90pub struct Forge1;
91
92#[derive(Deserialize)]
93struct TagResponse {
94 #[serde(rename = "target")]
95 tag_objectid: GitObjectId,
96
97 created_at: humantime_serde::Serde<SystemTime>,
98}
99
100#[async_trait]
101impl ForgeKindDataVersion for Forge1 {
102 type DbData = DbData where Self: Sized;
103
104 fn kind_name(&self) -> &'static str { "gitlab" }
105
106 fn namever_str(&self) -> &str { "gitlab-1" }
107
108 async fn make_progress(
109 &self,
110 host: &Hostname,
111 task_tmpdir: &str,
112 ) -> Result<(), QuitTask> {
113 let globals = globals();
114
115 let mut job = JobInWorkflow::start_for_forge(
116 &host,
117 self.namever_str(),
118 ).await?;
119
120 let jid = job.jid;
121
122 let db_data = job.forge_db_data(self)?;
123
124 trace!(%host, %jid, "fetching tag");
125
126 test_hook(|| format!("fetch gitlab {jid}")).await;
127
128 let url = (|| {
137 let mut url: Url = format!("https://{}", &job.data.forge_host)
138 .parse().context("parse initial forge_host https url")?;
140 url.path_segments_mut()
141 .map_err(|()| internal!("path no segments?"))?
142 .extend([
143 "api", "v4", "projects",
144 &db_data.project_id.to_string(),
145 "repository", "tags",
146 &job.data.tag_name,
147 ]);
148 Ok::<_, AE>(url)
149 })()
150 .into_internal("construct tag API url")?;
151
152 trace!(%jid, %url, "gitab tag info");
153
154 test_hook_url!(url);
155
156 let outcome = async {
157 let TagResponse {
158 created_at,
159 tag_objectid: confirmed_tag,
160 } = globals.http_fetch_json(url.clone())
161 .await
162 .context("fetch tag info")
163 .map_err(PE::Forge)?;
164
165 trace!(%jid, ?created_at, "gitab tag date");
166
167 MismatchError::check(
168 "tag object id",
169 &job.data.tag_objectid,
170 &confirmed_tag,
171 )?;
172
173 let _is_recent_enough = globals.check_tag_recency(*created_at)?;
174
175 gitclone::fetch_tags_via_clone(
176 &mut job,
177 &task_tmpdir,
178 ).await
179 }.await;
180
181 fetcher::record_fetch_outcome(
182 job,
183 outcome,
184 )?;
185
186 Ok(())
187 }
188}