Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/patch-add-label-command-trigger.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

41 changes: 25 additions & 16 deletions .github/workflows/cloclo.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions .github/workflows/cloclo.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
on:
slash_command:
name: cloclo
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean simplification — replacing the explicit issues: types: [labeled] + names: block with a single label_command: cloclo line is much more readable. The intent is immediately clear.

issues:
types: [labeled]
names: [cloclo]
label_command: cloclo
status-comment: true
permissions:
contents: read
Expand Down
46 changes: 24 additions & 22 deletions .github/workflows/smoke-copilot.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions .github/workflows/smoke-copilot.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ description: Smoke Copilot
on:
schedule: every 12h
workflow_dispatch:
pull_request:
types: [labeled]
names: ["smoke"]
label_command:
name: smoke
events: [pull_request]
reaction: "eyes"
status-comment: true
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
Expand Down
139 changes: 139 additions & 0 deletions actions/setup/js/remove_trigger_label.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
// @ts-check
/// <reference types="@actions/github-script" />

const { ERR_API, ERR_CONFIG } = require("./error_codes.cjs");

/**
* Remove the label that triggered this workflow from the issue, pull request, or discussion.
* This allows the same label to be applied again later to re-trigger the workflow.
*
* Supported events: issues (labeled), pull_request (labeled), discussion (labeled).
* For workflow_dispatch, the step emits an empty label_name output and exits without error.
*/
async function main() {
const labelNamesJSON = process.env.GH_AW_LABEL_NAMES;

const { getErrorMessage } = require("./error_helpers.cjs");

if (!labelNamesJSON) {
core.setFailed(`${ERR_CONFIG}: Configuration error: GH_AW_LABEL_NAMES not specified.`);
return;
}

let labelNames = [];
try {
labelNames = JSON.parse(labelNamesJSON);
if (!Array.isArray(labelNames)) {
core.setFailed(`${ERR_CONFIG}: Configuration error: GH_AW_LABEL_NAMES must be a JSON array.`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The GH_AW_LABEL_NAMES env var is parsed but only the context.payload?.label?.name is used for removal. If the labeled event fires for a label that is NOT in labelNames, the step still removes it and sets label_name to the triggering label. Consider validating that triggerLabel is included in labelNames before removing, to avoid accidentally removing unrelated labels.

return;
}
} catch (error) {
core.setFailed(`${ERR_CONFIG}: Configuration error: Failed to parse GH_AW_LABEL_NAMES: ${getErrorMessage(error)}`);
return;
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor nit: labelNames is declared with let but never reassigned after the JSON.parse. Consider using const here for clarity.

const eventName = context.eventName;

// For workflow_dispatch and other non-labeled events, nothing to remove.
if (eventName === "workflow_dispatch") {
core.info("Event is workflow_dispatch – skipping label removal.");
core.setOutput("label_name", "");
return;
}

// Retrieve the label that was added from the event payload.
const triggerLabel = context.payload?.label?.name;
if (!triggerLabel) {
core.info(`Event ${eventName} has no label payload – skipping label removal.`);
core.setOutput("label_name", "");
return;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good defensive check here — verifying triggerLabel exists before proceeding prevents cryptic downstream failures. The early return with empty output is the right pattern for non-fatal skips.


// Confirm that this label is one of the configured command labels.
if (!labelNames.includes(triggerLabel)) {
core.info(`Trigger label '${triggerLabel}' is not in the configured label-command list [${labelNames.join(", ")}] – skipping removal.`);
core.setOutput("label_name", triggerLabel);
return;
}

core.info(`Removing trigger label '${triggerLabel}' (event: ${eventName})`);

const owner = context.repo?.owner;
const repo = context.repo?.repo;
if (!owner || !repo) {
core.setFailed(`${ERR_CONFIG}: Configuration error: Unable to determine repository owner/name from context.`);
return;
}

try {
if (eventName === "issues") {
const issueNumber = context.payload?.issue?.number;
if (!issueNumber) {
core.warning("No issue number found in payload – skipping label removal.");
core.setOutput("label_name", triggerLabel);
return;
}
await github.rest.issues.removeLabel({
owner,
repo,
issue_number: issueNumber,
name: triggerLabel,
});
core.info(`✓ Removed label '${triggerLabel}' from issue #${issueNumber}`);
} else if (eventName === "pull_request") {
// Pull requests share the issues API for labels.
const prNumber = context.payload?.pull_request?.number;
if (!prNumber) {
core.warning("No pull request number found in payload – skipping label removal.");
core.setOutput("label_name", triggerLabel);
return;
}
await github.rest.issues.removeLabel({
owner,
repo,
issue_number: prNumber,
name: triggerLabel,
});
core.info(`✓ Removed label '${triggerLabel}' from pull request #${prNumber}`);
} else if (eventName === "discussion") {
// Discussions require the GraphQL API for label management.
const discussionNodeId = context.payload?.discussion?.node_id;
const labelNodeId = context.payload?.label?.node_id;
if (!discussionNodeId || !labelNodeId) {
core.warning("No discussion or label node_id found in payload – skipping label removal.");
core.setOutput("label_name", triggerLabel);
return;
}
await github.graphql(
`
mutation RemoveLabelFromDiscussion($labelableId: ID!, $labelIds: [ID!]!) {
removeLabelsFromLabelable(input: { labelableId: $labelableId, labelIds: $labelIds }) {
clientMutationId
}
}
`,
{
labelableId: discussionNodeId,
labelIds: [labelNodeId],
}
);
core.info(`✓ Removed label '${triggerLabel}' from discussion`);
} else {
core.info(`Event '${eventName}' does not support label removal – skipping.`);
}
} catch (error) {
// Non-fatal: log a warning but do not fail the step.
// A 404 status means the label is no longer present on the item (e.g., another concurrent
// workflow run already removed it), which is an expected outcome in multi-workflow setups.
const status = /** @type {any} */ error?.status;
if (status === 404) {
core.info(`Label '${triggerLabel}' is no longer present on the item – already removed by another run.`);
} else {
core.warning(`${ERR_API}: Failed to remove label '${triggerLabel}': ${getErrorMessage(error)}`);
}
}

core.setOutput("label_name", triggerLabel);
}

module.exports = { main };
Loading
Loading