diff --git a/.github/workflows/auto-mark-released.yml b/.github/workflows/auto-mark-released.yml new file mode 100644 index 00000000..08efb3ab --- /dev/null +++ b/.github/workflows/auto-mark-released.yml @@ -0,0 +1,86 @@ +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 from your palette + 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}".`);