#!/usr/bin/env ruby # This script marks/closes stale issues. It marks an issue as stale if it has not seen any activity in the last 4 months # and is marked with 'help' or 'repro-needed' label, that is, those issues that awaiting a response from the author. If # an issue is marked as stale and it sees activity within the last month, the script removes the 'stale' label. If an # issue is marked as stale and it has not seen any activity in the last month, the script closes the issue with a # comment. require 'time' require 'json' # Usage: stale_issues.rb [dry_run] $repository = ARGV[0] $dry_run = ARGV[1] == 'true' date_format = "%Y-%m-%dT%H:%M:%SZ" closing_comment = "This issue is being closed due to inactivity. \ Please feel free to reopen if you have more information." stale_comment = "This issue has been marked as stale because it has not seen any activity \ in the last 4 months. It is going to be closed soon if it stays inactive." one_month_ago = (Time.now - 30 * 24 * 60 * 60).utc.strftime(date_format) four_months_ago = (Time.now - 4 * 30 * 24 * 60 * 60).utc.strftime(date_format) def run(issue) if $dry_run url = "https://github.com/#{$repository}/issues/#{issue}" case RUBY_PLATFORM when /linux/ system("xdg-open #{url}") when /darwin/ system("open #{url}") else puts url end else yield.each { |cmd| system(cmd) } end end def created_by_maintainer?(comment) comment && (comment['author']['login'] == 'github-actions' || comment['authorAssociation'] == 'COLLABORATOR') end issues_query = "repo:#{$repository} is:issue is:open label:stale,help,repro-needed,resolved" issues = JSON.parse(`gh issue list --json number,comments,labels,updatedAt --search '#{issues_query}'`) for issue in issues do number = issue['number'] comments = issue['comments'] last_comment_by_user = comments.select { |comment| !created_by_maintainer?(comment) }.last last_comment_by_user_date = last_comment_by_user['createdAt'] if last_comment_by_user if !issue['labels'].select { |label| label['name'] == 'stale' }.empty? if last_comment_by_user_date && last_comment_by_user_date > one_month_ago puts "Removing 'stale' label from issue ##{number} ..." run(number) { ["gh issue edit #{number} --remove-label stale"] } elsif issue['updatedAt'] < one_month_ago puts "Closing issue ##{number} ..." run(number) { ["gh issue close #{number} --comment '#{closing_comment}'"] } end elsif created_by_maintainer?(comments.last) && comments.last['createdAt'] < four_months_ago if !issue['labels'].select { |label| !%w(help repro-needed resolved).include?(label['name']) }.empty? next end puts "Adding 'stale' label to issue ##{number} ..." run(number) { [ "gh issue edit #{number} --add-label stale", "gh issue comment #{number} --body '#{stale_comment}'" ] } end end