Turn it on
1
Run smartcloud on a schedule
The sweep runs only on See Getting started for the whole workflow.
schedule and workflow_dispatch events, never on pushes or pull request events. Make sure your smartcloud workflow has both:.github/workflows/smartcloud.yml
2
Give the job write access to issues and pull requests
Labelling, commenting and closing need
issues: write and pull-requests: write on the job. The workflow token is enough; no app or personal token is needed.3
Add a stale section
The smallest useful section names how many days of silence make an item stale, and which label marks it:Always set
.github/smartcloud.yml
staleComment: it is how smartcloud remembers when it marked an item (see How it works).4
Run it once by hand
Open the Actions tab, pick the smartcloud workflow and choose Run workflow. Check the job summary to see what was marked. Set the action’s
dryRun: true input first if you want to see what would happen without changing anything.Options
staleLabel, abandonedLabel and exempt.labels are label names as GitHub shows them, not keys of the labels section. If you also manage labels with the labels feature, define them there too so they get a colour and description.
To sweep only issues:
Complete example
.github/smartcloud.yml
How it works
Every sweep:- Lists every open issue and pull request, keeps the kinds named in
on, and drops any with anexempt.labelslabel or that passexempt.when. - For each item without the stale label: when it has had no activity for
staleAfterDays, and it is not snoozed, smartcloud postsstaleCommentand then adds the stale label. - For each item with the stale label:
- If it also has the abandoned label, it is left alone.
- If it has had activity since it was marked, or is snoozed, the stale label comes off.
- Otherwise, when it has been quiet for
abandonedAfterDays, smartcloud adds the abandoned label, postsabandonedComment, and closes it whencloseistrue.
updated_at time: any comment, push, edit, label or review. smartcloud’s own label and comment update it too, so abandonedAfterDays counts from the stale mark, not from the last human activity. Changes within ten minutes of the mark are treated as smartcloud’s own and do not count as activity.
The stale comment carries a hidden record of when the item was marked. That record is how a later sweep tells new activity from the mark. Without a staleComment there is no record, and the stale label stays until someone removes it by hand.
smartcloud keeps at most one stale and one abandoned comment per item, and edits an existing one rather than posting again. Only a marker comment written by a bot account or by a login in roles.trustedBots counts, for both editing and reading the mark time, so a person cannot take over the comment or forge when an item was marked. The stale comment is written before the stale label, so if GitHub fails part way the next sweep marks the item again.
Each item is swept on its own. When GitHub fails on one, the sweep records an error naming it and carries on with the rest.
Snoozing
With the commands feature on, anyone with the triage role can comment/stale-snooze [days] on an item. It keeps the item from being marked stale until the snooze ends, and takes off the stale label if it has one. The number of days is 1 to 365; without one, commands.snoozeDays is used (30 by default).
smartcloud records the end of the snooze in one comment of its own on the item, which a later snooze updates. Only a snooze comment written by a bot account or a roles.trustedBots login counts, so nobody can snooze an item by typing the marker.
Exemptions that need extra data
Whenexempt.when needs a pull request’s files, reviews, requested reviewers, commits, mergeability or checks, the sweep loads them for each pull request it considers, which takes more API calls.
A sweep cannot read a pull request’s draft state, branches or size. So if exempt.when uses isDraft, branchMatches, baseBranchMatches or changesSize, the sweep records a warning and skips every pull request rather than act on one that should be exempt. Issues are swept as usual, since those conditions never hold for an issue.
What you will see on GitHub
- On the item: the stale label and your
staleCommentfrom the account smartcloud acts as (for examplegithub-actions[bot]). Later, the abandoned label,abandonedComment, and the item closed ifcloseis on. - Check run:
smartcloud / staleon the commit the run is for, the default branch’s head on a schedule. It concludessuccesswith no problems,neutralwith warnings, andfailurewhen an item could not be swept or the config is wrong. See check runs. - Job summary: every label, comment and close it made, as a list of changes, plus any warnings and errors.
- Notifications: with the notifications feature, items that were marked, abandoned or closed can be sent to Slack, Discord or Linear.
Forks and restricted runs
The sweep only runs onschedule and workflow_dispatch, which never come from a fork, so it always runs with the repository’s own token. It works with the plain workflow token. If a write is still refused, for example because the job lacks issues: write, that write is skipped and listed under Restricted access in the job summary in a restricted run, or recorded as an error for that item otherwise.
Troubleshooting
Nothing happens on pull request or push runs
Nothing happens on pull request or push runs
That is expected. The sweep runs only on
schedule and workflow_dispatch events. Add a schedule trigger to the
workflow, or run it by hand from the Actions tab.Error: stale.staleLabel and stale.abandonedLabel are both ...
Error: stale.staleLabel and stale.abandonedLabel are both ...
The two labels have the same name, ignoring case, so a stale item would look abandoned. The sweep changes nothing
until you give them different names. Remember
abandonedLabel defaults to abandoned, so staleLabel: Abandoned
clashes with it.The stale label never comes off after someone comments
The stale label never comes off after someone comments
smartcloud only knows when it marked an item from its
staleComment. Without a staleComment, or when the comment
was deleted or written by a person rather than a bot, it cannot tell new activity from the mark, and the label
stays. Set staleComment; remove the label by hand for items already marked.Warning: stale.exempt.when uses isDraft, branchMatches, baseBranchMatches or changesSize
Warning: stale.exempt.when uses isDraft, branchMatches, baseBranchMatches or changesSize
A sweep cannot read those for a pull request, so every pull request is skipped to avoid acting on one that should be
exempt. Use a label in
exempt.labels instead, or a condition the sweep can answer, such as titleMatches or
hasLabel.Warning: stale.unmark ... was already gone
Warning: stale.unmark ... was already gone
Someone removed the stale label between the listing and the sweep’s own removal. Nothing is wrong; the item is
unmarked either way.
Error: #123 was not swept: ...
Error: #123 was not swept: ...
GitHub refused or failed a request for that one item. The rest of the sweep carried on. Common causes are a missing
issues: write or pull-requests: write permission, or GitHub rate limits on a large repository; the next sweep
tries again.Abandoned items are labelled but not closed
Abandoned items are labelled but not closed
close is false by default. Set close: true.