Merge branch 'main' into v3.7.0

This commit is contained in:
Mic 2025-09-18 22:00:45 +02:00 committed by GitHub
commit 9dabcf4eb1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 192 additions and 1 deletions

117
.github/workflows/auto-mark-released.yml vendored Normal file
View file

@ -0,0 +1,117 @@
name: Mark released issues on close
on:
issues:
types: [closed]
permissions:
contents: read
issues: write
jobs:
mark-released:
# Run only if closed as completed AND has at least one "status:" label
if: ${{ github.event.issue.state_reason == 'completed' && contains(toJSON(github.event.issue.labels), 'status:') }}
runs-on: ubuntu-latest
steps:
- name: Set "released" label when closed completed and was ready for release
uses: actions/github-script@v7
with:
script: |
// Helpers
const owner = context.repo.owner;
const repo = context.repo.repo;
const issue = context.payload.issue;
if (!issue) {
core.info('No issue payload found, skipping.');
return;
}
// Only proceed if closed as "completed" (not "not_planned")
const isCompleted = issue.state_reason === 'completed';
if (!isCompleted) {
core.info('Issue state_reason is not "completed"; skipping.');
return;
}
const getName = (l) => typeof l === 'string' ? l : l.name;
const labels = (issue.labels || []).map(getName);
// Status detection (robust to case/spacing)
const isStatusLabel = (name) => /^status:\s*/i.test(String(name || ''));
const hasAnyStatus = labels.some(isStatusLabel);
if (!hasAnyStatus) {
core.info('Issue has no "status:*" label; skipping.');
return;
}
const READY_REGEX = /^status:\s*ready\s*for\s*release$/i;
const hasReady = labels.some((n) => READY_REGEX.test(String(n || '')));
if (!hasReady) {
core.info('Issue is not "status: ready for release"; nothing to do.');
return;
}
const releasedLabel = 'status: released';
// Ensure "released" label exists
try {
await github.rest.issues.getLabel({ owner, repo, name: releasedLabel });
} catch (e) {
if (e.status === 404) {
core.info(`Label "${releasedLabel}" not found. Creating it.`);
await github.rest.issues.createLabel({
owner,
repo,
name: releasedLabel,
color: '184738', // deep green
description: 'Feature released.'
});
} else {
throw e;
}
}
// Remove all status:* labels, add "status: released"
const filtered = labels.filter((name) => !isStatusLabel(name));
const newLabels = [...new Set([...filtered, releasedLabel])];
await github.rest.issues.update({
owner,
repo,
issue_number: issue.number,
labels: newLabels
});
core.info(`Issue #${issue.number} relabeled as "${releasedLabel}".`);
// === NEW: add release comment with milestone if present ===
const milestone = issue.milestone;
if (!milestone || !milestone.title) {
core.info('Issue has no milestone; no release comment added.');
return;
}
const commentBody = `Released in version ${milestone.title}.`;
// Avoid duplicate comment if the action reruns
const existingComments = await github.paginate(
github.rest.issues.listComments,
{ owner, repo, issue_number: issue.number, per_page: 100 }
);
const alreadyCommented = existingComments.some(c =>
typeof c.body === 'string' && c.body.trim() === commentBody
);
if (alreadyCommented) {
core.info('Release comment already present; skipping comment creation.');
} else {
await github.rest.issues.createComment({
owner,
repo,
issue_number: issue.number,
body: commentBody
});
core.info('Release comment added.');
}

View file

@ -0,0 +1,75 @@
name: Issues Validate Integration Field
on:
issues:
types: [opened, edited]
permissions:
issues: write
jobs:
check-integration:
runs-on: ubuntu-latest
steps:
- name: Validate dropdown selection (only for bug report form)
uses: actions/github-script@v7
with:
script: |
const issue = context.payload.issue;
const bodyText = issue.body || "";
// ---- Guard: only act if this looks like a Bug Report form ----
if (!bodyText.includes("### The problem")) {
core.info("Skipped: This issue does not contain '### The problem', so it is not a Bug Report.");
return;
}
core.info("Detected a Bug Report issue. Proceeding with validation...");
// Extract selection under "Which integration are you using?"
const header = "### Which integration are you using?";
const nextHeaderRegex = /\n### /;
let selection = "";
const startIdx = bodyText.indexOf(header);
if (startIdx !== -1) {
const afterHeader = bodyText.slice(startIdx + header.length).replace(/^\r?\n+/, "");
const nextHeaderIdx = afterHeader.search(nextHeaderRegex);
selection = (nextHeaderIdx === -1 ? afterHeader : afterHeader.slice(0, nextHeaderIdx)).trim();
}
core.info(`Integration field extracted: "${selection}"`);
const isInvalid = !selection || selection.startsWith("-- Select an option");
if (isInvalid) {
core.info("Integration field is invalid or not selected.");
} else {
core.info("Integration field is valid. Nothing to do.");
}
const owner = context.repo.owner;
const repo = context.repo.repo;
const issue_number = issue.number;
// Avoid duplicate comments
const marker = "please specify **Which integration are you using?**";
const { data: comments } = await github.rest.issues.listComments({ owner, repo, issue_number });
const alreadyCommented = comments.some(c => c.body && c.body.includes(marker));
if (isInvalid) {
if (!alreadyCommented) {
core.info("No existing reminder found. Posting a new comment...");
await github.rest.issues.createComment({
owner, repo, issue_number,
body: [
`👋 Hello @${issue.user.login}!`,
`To complete your bug report, please specify which integration you are using.`,
"",
"Right now the field `**Which integration are you using?**` is set to `-- Select an option --`.",
"",
"Click **Edit** at the top right of the issue, choose a valid option from the dropdown, and save.",
].join("\n")
});
} else {
core.info("Reminder comment already exists. Skipping new comment.");
}
}

View file

@ -49,7 +49,6 @@ jobs:
const milestoneTitle = parsed.milestoneTitle; // es. "3.7.0" const milestoneTitle = parsed.milestoneTitle; // es. "3.7.0"
const prereleaseTagDisplay = tagName.startsWith("v") ? tagName : `v${tagName}`; const prereleaseTagDisplay = tagName.startsWith("v") ? tagName : `v${tagName}`;
const commentBody = `This feature is available for testing in the pre-release version [${prereleaseTagDisplay}](https://github.com/micz/ThunderAI/releases).`; const commentBody = `This feature is available for testing in the pre-release version [${prereleaseTagDisplay}](https://github.com/micz/ThunderAI/releases).`;
core.info(`(MAIN) Looking for milestone '${milestoneTitle}' in ${owner}/${repo}`); core.info(`(MAIN) Looking for milestone '${milestoneTitle}' in ${owner}/${repo}`);
// Find milestone (open or closed) // Find milestone (open or closed)