refactor(web): batch submissions into single workflow dispatch

Updated triggerAddIconWorkflow to accept comma-separated submission IDs.
Simplified triggerBulkAddIconWorkflow to join IDs and dispatch once
instead of triggering multiple separate workflows.

This works in tandem with the updated add-icon.yml workflow to process
all submissions sequentially in a single run.
This commit is contained in:
Thomas Camlong
2025-12-29 11:26:50 +01:00
parent bcd8a55118
commit e059b0f3ea
2 changed files with 31 additions and 114 deletions

View File

@@ -51,15 +51,15 @@ async function verifyAdmin(
}
/**
* Trigger the "Add Icon to Collection" GitHub workflow for a submission
* Trigger the "Add Icon to Collection" GitHub workflow for one or more submissions
* Only admins can trigger this action
* @param authToken - The PocketBase auth token from the client
* @param submissionId - The ID of the submission to add
* @param submissionIds - Single ID or comma-separated IDs of submissions to add
* @param dryRun - If true, skip actual writes (for testing)
*/
export async function triggerAddIconWorkflow(
authToken: string,
submissionId: string,
submissionIds: string,
dryRun = false,
): Promise<TriggerWorkflowResult> {
// Verify admin status using the provided token
@@ -88,7 +88,7 @@ export async function triggerAddIconWorkflow(
body: JSON.stringify({
ref: "main",
inputs: {
submissionId: submissionId,
submissionIds: submissionIds,
dryRun: dryRun.toString(),
},
}),
@@ -122,21 +122,16 @@ export async function triggerAddIconWorkflow(
}
}
interface BulkTriggerResult {
submissionId: string;
success: boolean;
error?: string;
}
interface BulkTriggerWorkflowResult {
success: boolean;
results: BulkTriggerResult[];
error?: string;
workflowUrl?: string;
submissionCount: number;
}
/**
* Trigger the "Add Icon to Collection" GitHub workflow for multiple submissions
* Workflows are triggered sequentially with a small delay to avoid rate limiting
* All submissions are processed in a single workflow run to avoid concurrency issues
* @param authToken - The PocketBase auth token from the client
* @param submissionIds - Array of submission IDs to add
* @param dryRun - If true, skip actual writes (for testing)
@@ -146,88 +141,22 @@ export async function triggerBulkAddIconWorkflow(
submissionIds: string[],
dryRun = false,
): Promise<BulkTriggerWorkflowResult> {
const { isAdmin, error: authError } = await verifyAdmin(authToken);
if (!isAdmin) {
if (submissionIds.length === 0) {
return {
success: false,
results: submissionIds.map((id) => ({
submissionId: id,
success: false,
error: authError || "Unauthorized",
})),
error: "No submission IDs provided",
submissionCount: 0,
};
}
const githubToken = process.env.GITHUB_TOKEN;
if (!githubToken) {
return {
success: false,
results: submissionIds.map((id) => ({
submissionId: id,
success: false,
error: "GitHub token not configured",
})),
};
}
const results: BulkTriggerResult[] = [];
for (const submissionId of submissionIds) {
try {
const response = await fetch(
`https://api.github.com/repos/${GITHUB_OWNER}/${GITHUB_REPO}/actions/workflows/${WORKFLOW_FILE}/dispatches`,
{
method: "POST",
headers: {
Accept: "application/vnd.github+json",
Authorization: `Bearer ${githubToken}`,
"X-GitHub-Api-Version": "2022-11-28",
},
body: JSON.stringify({
ref: "main",
inputs: {
submissionId: submissionId,
dryRun: dryRun.toString(),
},
}),
},
);
if (!response.ok) {
const errorText = await response.text();
console.error(
`GitHub API error for ${submissionId}:`,
response.status,
errorText,
);
results.push({
submissionId,
success: false,
error: `GitHub API error: ${response.status}`,
});
} else {
results.push({ submissionId, success: true });
}
// Small delay between requests to avoid rate limiting
await new Promise((resolve) => setTimeout(resolve, 500));
} catch (error) {
console.error(`Error triggering workflow for ${submissionId}:`, error);
results.push({
submissionId,
success: false,
error:
error instanceof Error ? error.message : "Failed to trigger workflow",
});
}
}
const allSucceeded = results.every((r) => r.success);
const workflowUrl = `https://github.com/${GITHUB_OWNER}/${GITHUB_REPO}/actions/workflows/${WORKFLOW_FILE}`;
// Join all IDs with commas and trigger a single workflow
const commaSeparatedIds = submissionIds.join(",");
const result = await triggerAddIconWorkflow(authToken, commaSeparatedIds, dryRun);
return {
success: allSucceeded,
results,
workflowUrl,
success: result.success,
error: result.error,
workflowUrl: result.workflowUrl,
submissionCount: submissionIds.length,
};
}

View File

@@ -224,37 +224,25 @@ export function useBulkTriggerWorkflow() {
}
const result = await triggerBulkAddIconWorkflow(authToken, submissionIds, dryRun)
if (!result.success) {
throw new Error(result.error || "Failed to trigger workflow")
}
return result
},
onSuccess: (data) => {
const successCount = data.results.filter((r) => r.success).length
const failCount = data.results.filter((r) => !r.success).length
if (failCount === 0) {
toast.success(`Triggered ${successCount} workflow(s)`, {
description: "All icon workflows have been queued. They will run sequentially.",
action: data.workflowUrl
? {
label: "View on GitHub",
onClick: () => window.open(data.workflowUrl, "_blank"),
}
: undefined,
})
} else {
toast.warning(`Triggered ${successCount} of ${data.results.length} workflows`, {
description: `${failCount} workflow(s) failed to trigger`,
action: data.workflowUrl
? {
label: "View on GitHub",
onClick: () => window.open(data.workflowUrl, "_blank"),
}
: undefined,
})
}
toast.success(`Workflow triggered for ${data.submissionCount} icon(s)`, {
description: "All icons will be processed sequentially in a single workflow run.",
action: data.workflowUrl
? {
label: "View on GitHub",
onClick: () => window.open(data.workflowUrl, "_blank"),
}
: undefined,
})
},
onError: (error: Error) => {
console.error("Error triggering bulk workflows:", error)
toast.error("Failed to trigger workflows", {
console.error("Error triggering bulk workflow:", error)
toast.error("Failed to trigger workflow", {
description: error.message || "An error occurred",
})
},