forked from github-starred/komodo
add repo build webhook
This commit is contained in:
@@ -137,6 +137,7 @@ impl Resolve<GetRepoWebhooksEnabled, User> for State {
|
|||||||
managed: false,
|
managed: false,
|
||||||
clone_enabled: false,
|
clone_enabled: false,
|
||||||
pull_enabled: false,
|
pull_enabled: false,
|
||||||
|
build_enabled: false,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -154,6 +155,7 @@ impl Resolve<GetRepoWebhooksEnabled, User> for State {
|
|||||||
managed: false,
|
managed: false,
|
||||||
clone_enabled: false,
|
clone_enabled: false,
|
||||||
pull_enabled: false,
|
pull_enabled: false,
|
||||||
|
build_enabled: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,6 +167,7 @@ impl Resolve<GetRepoWebhooksEnabled, User> for State {
|
|||||||
managed: false,
|
managed: false,
|
||||||
clone_enabled: false,
|
clone_enabled: false,
|
||||||
pull_enabled: false,
|
pull_enabled: false,
|
||||||
|
build_enabled: false,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -190,23 +193,33 @@ impl Resolve<GetRepoWebhooksEnabled, User> for State {
|
|||||||
format!("{host}/listener/github/repo/{}/clone", repo.id);
|
format!("{host}/listener/github/repo/{}/clone", repo.id);
|
||||||
let pull_url =
|
let pull_url =
|
||||||
format!("{host}/listener/github/repo/{}/pull", repo.id);
|
format!("{host}/listener/github/repo/{}/pull", repo.id);
|
||||||
|
let build_url =
|
||||||
|
format!("{host}/listener/github/repo/{}/build", repo.id);
|
||||||
|
|
||||||
let mut clone_enabled = false;
|
let mut clone_enabled = false;
|
||||||
let mut pull_enabled = false;
|
let mut pull_enabled = false;
|
||||||
|
let mut build_enabled = false;
|
||||||
|
|
||||||
for webhook in webhooks {
|
for webhook in webhooks {
|
||||||
if webhook.active && webhook.config.url == clone_url {
|
if !webhook.active {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if webhook.config.url == clone_url {
|
||||||
clone_enabled = true
|
clone_enabled = true
|
||||||
}
|
}
|
||||||
if webhook.active && webhook.config.url == pull_url {
|
if webhook.config.url == pull_url {
|
||||||
pull_enabled = true
|
pull_enabled = true
|
||||||
}
|
}
|
||||||
|
if webhook.config.url == build_url {
|
||||||
|
build_enabled = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(GetRepoWebhooksEnabledResponse {
|
Ok(GetRepoWebhooksEnabledResponse {
|
||||||
managed: true,
|
managed: true,
|
||||||
clone_enabled,
|
clone_enabled,
|
||||||
pull_enabled,
|
pull_enabled,
|
||||||
|
build_enabled,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -225,6 +225,9 @@ impl Resolve<CreateRepoWebhook, User> for State {
|
|||||||
RepoWebhookAction::Pull => {
|
RepoWebhookAction::Pull => {
|
||||||
format!("{host}/listener/github/repo/{}/pull", repo.id)
|
format!("{host}/listener/github/repo/{}/pull", repo.id)
|
||||||
}
|
}
|
||||||
|
RepoWebhookAction::Build => {
|
||||||
|
format!("{host}/listener/github/repo/{}/build", repo.id)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for webhook in webhooks {
|
for webhook in webhooks {
|
||||||
@@ -339,6 +342,9 @@ impl Resolve<DeleteRepoWebhook, User> for State {
|
|||||||
RepoWebhookAction::Pull => {
|
RepoWebhookAction::Pull => {
|
||||||
format!("{host}/listener/github/repo/{}/pull", repo.id)
|
format!("{host}/listener/github/repo/{}/pull", repo.id)
|
||||||
}
|
}
|
||||||
|
RepoWebhookAction::Build => {
|
||||||
|
format!("{host}/listener/github/repo/{}/build", repo.id)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for webhook in webhooks {
|
for webhook in webhooks {
|
||||||
|
|||||||
@@ -89,6 +89,24 @@ pub fn router() -> Router {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
.route(
|
||||||
|
"/repo/:id/build",
|
||||||
|
post(
|
||||||
|
|Path(Id { id }), headers: HeaderMap, body: String| async move {
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let span = info_span!("repo_build_webhook", id);
|
||||||
|
async {
|
||||||
|
let res = repo::handle_repo_build_webhook(id.clone(), headers, body).await;
|
||||||
|
if let Err(e) = res {
|
||||||
|
warn!("failed to run repo build webook for repo {id} | {e:#}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.instrument(span)
|
||||||
|
.await
|
||||||
|
});
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
.route(
|
.route(
|
||||||
"/stack/:id/refresh",
|
"/stack/:id/refresh",
|
||||||
post(
|
post(
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use std::sync::OnceLock;
|
|||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use axum::http::HeaderMap;
|
use axum::http::HeaderMap;
|
||||||
use monitor_client::{
|
use monitor_client::{
|
||||||
api::execute::{CloneRepo, PullRepo},
|
api::execute::{BuildRepo, CloneRepo, PullRepo},
|
||||||
entities::{repo::Repo, user::git_webhook_user},
|
entities::{repo::Repo, user::git_webhook_user},
|
||||||
};
|
};
|
||||||
use resolver_api::Resolve;
|
use resolver_api::Resolve;
|
||||||
@@ -84,3 +84,35 @@ pub async fn handle_repo_pull_webhook(
|
|||||||
State.resolve(req, (user, update)).await?;
|
State.resolve(req, (user, update)).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn handle_repo_build_webhook(
|
||||||
|
repo_id: String,
|
||||||
|
headers: HeaderMap,
|
||||||
|
body: String,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
// Acquire and hold lock to make a task queue for
|
||||||
|
// subsequent listener calls on same resource.
|
||||||
|
// It would fail if we let it go through from action state busy.
|
||||||
|
let lock = repo_locks().get_or_insert_default(&repo_id).await;
|
||||||
|
let _lock = lock.lock().await;
|
||||||
|
|
||||||
|
verify_gh_signature(headers, &body).await?;
|
||||||
|
let request_branch = extract_branch(&body)?;
|
||||||
|
let repo = resource::get::<Repo>(&repo_id).await?;
|
||||||
|
if !repo.config.webhook_enabled {
|
||||||
|
return Err(anyhow!("repo does not have webhook enabled"));
|
||||||
|
}
|
||||||
|
if request_branch != repo.config.branch {
|
||||||
|
return Err(anyhow!("request branch does not match expected"));
|
||||||
|
}
|
||||||
|
let user = git_webhook_user().to_owned();
|
||||||
|
let req = crate::api::execute::ExecuteRequest::BuildRepo(BuildRepo {
|
||||||
|
repo: repo_id,
|
||||||
|
});
|
||||||
|
let update = init_execution_update(&req, &user).await?;
|
||||||
|
let crate::api::execute::ExecuteRequest::BuildRepo(req) = req else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
|
State.resolve(req, (user, update)).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -139,4 +139,6 @@ pub struct GetRepoWebhooksEnabledResponse {
|
|||||||
pub clone_enabled: bool,
|
pub clone_enabled: bool,
|
||||||
/// Whether pushes to branch trigger pull. Will always be false if managed is false.
|
/// Whether pushes to branch trigger pull. Will always be false if managed is false.
|
||||||
pub pull_enabled: bool,
|
pub pull_enabled: bool,
|
||||||
|
/// Whether pushes to branch trigger build. Will always be false if managed is false.
|
||||||
|
pub build_enabled: bool,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ pub struct RefreshRepoCache {
|
|||||||
pub enum RepoWebhookAction {
|
pub enum RepoWebhookAction {
|
||||||
Clone,
|
Clone,
|
||||||
Pull,
|
Pull,
|
||||||
|
Build,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a webhook on the github repo attached to the (monitor) repo
|
/// Create a webhook on the github repo attached to the (monitor) repo
|
||||||
|
|||||||
@@ -3262,6 +3262,8 @@ export interface GetRepoWebhooksEnabledResponse {
|
|||||||
clone_enabled: boolean;
|
clone_enabled: boolean;
|
||||||
/** Whether pushes to branch trigger pull. Will always be false if managed is false. */
|
/** Whether pushes to branch trigger pull. Will always be false if managed is false. */
|
||||||
pull_enabled: boolean;
|
pull_enabled: boolean;
|
||||||
|
/** Whether pushes to branch trigger build. Will always be false if managed is false. */
|
||||||
|
build_enabled: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Find resources matching a common query. Response: [FindResourcesResponse]. */
|
/** Find resources matching a common query. Response: [FindResourcesResponse]. */
|
||||||
@@ -4389,6 +4391,7 @@ export interface RefreshRepoCache {
|
|||||||
export enum RepoWebhookAction {
|
export enum RepoWebhookAction {
|
||||||
Clone = "Clone",
|
Clone = "Clone",
|
||||||
Pull = "Pull",
|
Pull = "Pull",
|
||||||
|
Build = "Build",
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -164,6 +164,11 @@ export const RepoConfig = ({ id }: { id: string }) => {
|
|||||||
<CopyGithubWebhook path={`/repo/${id}/clone`} />
|
<CopyGithubWebhook path={`/repo/${id}/clone`} />
|
||||||
</ConfigItem>
|
</ConfigItem>
|
||||||
),
|
),
|
||||||
|
["build" as any]: () => (
|
||||||
|
<ConfigItem label="Build">
|
||||||
|
<CopyGithubWebhook path={`/repo/${id}/build`} />
|
||||||
|
</ConfigItem>
|
||||||
|
),
|
||||||
webhook_enabled: webhooks !== undefined && !webhooks.managed,
|
webhook_enabled: webhooks !== undefined && !webhooks.managed,
|
||||||
["managed" as any]: () => {
|
["managed" as any]: () => {
|
||||||
const inv = useInvalidate();
|
const inv = useInvalidate();
|
||||||
@@ -247,44 +252,95 @@ export const RepoConfig = ({ id }: { id: string }) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{!webhooks.clone_enabled && !webhooks.pull_enabled && (
|
{webhooks.build_enabled && (
|
||||||
<div className="flex items-center gap-4 flex-wrap">
|
<div className="flex items-center gap-4 flex-wrap">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
Incoming webhook is{" "}
|
Incoming webhook is{" "}
|
||||||
<div
|
<div
|
||||||
className={text_color_class_by_intention(
|
className={text_color_class_by_intention("Good")}
|
||||||
"Critical"
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
DISABLED
|
ENABLED
|
||||||
|
</div>
|
||||||
|
and will trigger
|
||||||
|
<div
|
||||||
|
className={text_color_class_by_intention("Neutral")}
|
||||||
|
>
|
||||||
|
BUILD
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ConfirmButton
|
<ConfirmButton
|
||||||
title="Enable Clone"
|
title="Disable"
|
||||||
icon={<CirclePlus className="w-4 h-4" />}
|
icon={<Ban className="w-4 h-4" />}
|
||||||
|
variant="destructive"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
createWebhook({
|
deleteWebhook({
|
||||||
repo: id,
|
repo: id,
|
||||||
action: Types.RepoWebhookAction.Clone,
|
action: Types.RepoWebhookAction.Build,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
loading={createPending}
|
loading={deletePending}
|
||||||
disabled={disabled || createPending}
|
disabled={disabled || deletePending}
|
||||||
/>
|
|
||||||
<ConfirmButton
|
|
||||||
title="Enable Pull"
|
|
||||||
icon={<CirclePlus className="w-4 h-4" />}
|
|
||||||
onClick={() =>
|
|
||||||
createWebhook({
|
|
||||||
repo: id,
|
|
||||||
action: Types.RepoWebhookAction.Pull,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
loading={createPending}
|
|
||||||
disabled={disabled || createPending}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{!webhooks.clone_enabled &&
|
||||||
|
!webhooks.pull_enabled &&
|
||||||
|
!webhooks.build_enabled && (
|
||||||
|
<div className="flex items-center gap-4 flex-wrap">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
Incoming webhook is{" "}
|
||||||
|
<div
|
||||||
|
className={text_color_class_by_intention(
|
||||||
|
"Critical"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
DISABLED
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{(update.server_id ?? config.server_id) && (
|
||||||
|
<ConfirmButton
|
||||||
|
title="Enable Clone"
|
||||||
|
icon={<CirclePlus className="w-4 h-4" />}
|
||||||
|
onClick={() =>
|
||||||
|
createWebhook({
|
||||||
|
repo: id,
|
||||||
|
action: Types.RepoWebhookAction.Clone,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
loading={createPending}
|
||||||
|
disabled={disabled || createPending}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{(update.server_id ?? config.server_id) && (
|
||||||
|
<ConfirmButton
|
||||||
|
title="Enable Pull"
|
||||||
|
icon={<CirclePlus className="w-4 h-4" />}
|
||||||
|
onClick={() =>
|
||||||
|
createWebhook({
|
||||||
|
repo: id,
|
||||||
|
action: Types.RepoWebhookAction.Pull,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
loading={createPending}
|
||||||
|
disabled={disabled || createPending}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{(update.builder_id ?? config.builder_id) && (
|
||||||
|
<ConfirmButton
|
||||||
|
title="Enable Build"
|
||||||
|
icon={<CirclePlus className="w-4 h-4" />}
|
||||||
|
onClick={() =>
|
||||||
|
createWebhook({
|
||||||
|
repo: id,
|
||||||
|
action: Types.RepoWebhookAction.Build,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
loading={createPending}
|
||||||
|
disabled={disabled || createPending}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</ConfigItem>
|
</ConfigItem>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user