Files
fleet/.github/scripts/stale-eng-issues.js
T
Victor Lyuboslavsky 1e9f3807a1 Add author mention to stale issue bots (#46787)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #46790

Example live run off this branch:
https://github.com/fleetdm/fleet/actions/runs/27005120262
Example stale issue with comment:
https://github.com/fleetdm/fleet/issues/18421

- Added `@author` mention when marking issue as stale
- Refactored so that both Fleetie and eng-initiated stale issue bots use
the same core JS code
- Run Fleetie-initiated workflow on a schedule

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Automated stale-issue workflows for engineering-initiated and Fleetie
issues with configurable dry-run, max-operations, manual triggers, and
scheduled runs.

* **Tests**
* Added shared test helpers and expanded, tightened test suites covering
staleness, closing, unstale, and error/boundary behaviors.

* **Refactor**
* Introduced a shared stale-issue engine used by thin,
author/label-based wrappers for consistent behavior and messaging.

* **Chores**
* Updated workflow triggers, permissions, and CI test matrix to include
the new core and wrappers.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-05 20:39:02 +01:00

67 lines
2.7 KiB
JavaScript

// Marks open engineering-initiated issues (label `~engineering-initiated`) as stale after 1y of
// inactivity, and closes them after 14 more days. Invoked by `actions/github-script` from
// `.github/workflows/close-stale-eng-initiated-issues.yml`.
//
// This is a thin wrapper over `stale-issues-core.js`: it provides label-based eligibility and the
// eng-initiated thresholds and wording. The scanning, labeling, closing, and un-staling logic all
// live in the core. The sibling `stale-fleetie-issues.js` wraps the same core with author-based
// eligibility.
//
// Why this replaced `actions/stale`: that action posts a single static stale comment with no way to
// template the issue author, so it could not @-mention the author. The core lets us build the
// comment per issue. Behavior otherwise matches the previous `actions/stale` config (365d to stale,
// 14d to close, remove-stale-when-updated).
//
// Inputs (env):
// DRY_RUN 'true' to log candidates without writing (read by the core).
// MAX_OPERATIONS Cap on API write operations per run. Default 400. `0` disables writes / dry-runs (read by the core).
//
// Exports: `async function run({ github, context, core })`. Returns a summary object for tests.
"use strict";
const core_run = require("./stale-issues-core.js");
const STALE_DAYS = 365;
const CLOSE_DAYS = 14;
const STALE_LABEL = "stale";
const ELIGIBLE_LABEL = "~engineering-initiated";
const staleMessage = (author) =>
`@${author} this issue is stale because it has been open for 365 days with no activity. ` +
"Please update the issue if it is still relevant; otherwise it will be closed in 14 days.";
const CLOSE_MSG =
"This issue was closed because it has been inactive for 14 days since being marked as stale.";
async function run({ github, context, core }) {
const result = await core_run({
github,
context,
core,
config: {
title: "Eng-initiated stale-issue closer",
staleDays: STALE_DAYS,
closeDays: CLOSE_DAYS,
staleLabel: STALE_LABEL,
isEligible: (issue) =>
(issue.labels || []).some(
(l) =>
(typeof l === "string" ? l : (l && l.name) || "").toLowerCase() ===
ELIGIBLE_LABEL.toLowerCase()
),
staleMessage,
closeMessage: () => CLOSE_MSG,
ineligibleSummaryLabel: `Skipped (no ${ELIGIBLE_LABEL} label)`,
},
});
return result;
}
module.exports = run;
// Exported for test boundary assertions so a future policy change surfaces in the boundary tests
// instead of silently passing on a hardcoded old value.
module.exports.STALE_DAYS = STALE_DAYS;
module.exports.CLOSE_DAYS = CLOSE_DAYS;
module.exports.ELIGIBLE_LABEL = ELIGIBLE_LABEL;
module.exports.SELF_ACTIVITY_EPSILON_MS = core_run.SELF_ACTIVITY_EPSILON_MS;