Last active
October 12, 2022 20:05
-
-
Save mriddle/623bc0114aaa719f7d15f6c9f110091f to your computer and use it in GitHub Desktop.
List of all unarchived repositories within organization using the GitHub GraphQL API. Requires a PAT (Personal Access Token) that has the necessary privileges
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
# List of all unarchived repositories within organization | |
# - group by year last updated | |
# - exclude those updated in the last 12 months | |
# | |
# Requires a GitHub Personal Access Token with the `repo` scope | |
# and it will need be authorized with with SSO where applicable. | |
# | |
# Pass token in via the GITHUB_ACCESS_TOKEN environment variable | |
# | |
# Usage: ./repository_archiving_report.rb | |
# | |
# Example output: | |
# | |
# 3 not updated since 2016 | |
# - https://github.com/org_or_user_name/repo_name | |
# ... | |
# | |
# 7 not updated since 2018 | |
# - https://github.com/org_or_user_name/repo_name | |
# ... | |
require 'bundler/inline' | |
gemfile do | |
source 'https://rubygems.org' | |
gem "graphql-client" | |
gem "pry-byebug" | |
end | |
require "graphql/client" | |
require "graphql/client/http" | |
##################### | |
# GraphQL connection | |
##################### | |
module GitHubGraphAPI | |
# Configure GraphQL endpoint using the basic HTTP network adapter. | |
HTTPAdapter = GraphQL::Client::HTTP.new("https://api.github.com/graphql") do | |
def headers(_) | |
token = ENV.fetch("GITHUB_ACCESS_TOKEN") | |
{ "Authorization" => "Bearer #{token}" } | |
end | |
end | |
# An offline copy of the schema allows queries to be typed checked statically | |
# before even sending a request. | |
unless File.exists? "db/schema.json" | |
# Schema = GraphQL::Client.load_schema(HTTP) | |
Dir.mkdir("db") unless Dir.exists?("db") | |
GraphQL::Client.dump_schema(HTTPAdapter, "db/schema.json") | |
end | |
Schema = GraphQL::Client.load_schema("db/schema.json") | |
Client = GraphQL::Client.new(schema: Schema, execute: HTTPAdapter) | |
end | |
################ | |
# GraphQL query | |
################ | |
RepositoriesQuery = GitHubGraphAPI::Client.parse <<-'GRAPHQL' | |
query($first:Int=null,$after:String=null) { | |
viewer { | |
repositories(first: $first, after: $after, affiliations: [ORGANIZATION_MEMBER], ownerAffiliations: [ORGANIZATION_MEMBER]) { | |
totalCount | |
edges { | |
node { | |
name | |
url | |
owner { | |
login | |
} | |
isArchived | |
isFork | |
isPrivate | |
createdAt | |
updatedAt | |
pushedAt | |
} | |
} | |
pageInfo { | |
endCursor | |
hasNextPage | |
} | |
} | |
} | |
} | |
GRAPHQL | |
######################## | |
# Retrieve repositories | |
######################## | |
all_repositories = [] | |
cursor = nil | |
has_next_page = true | |
current_count = 0 | |
while has_next_page | |
result = GitHubGraphAPI::Client.query(RepositoriesQuery, variables: { | |
first: 100, # 100 is the maximum number of records you can retrieve at a time | |
after: cursor | |
} | |
) | |
repositories = result.data.viewer.repositories.edges.map(&:node) | |
all_repositories.concat(repositories) | |
has_next_page = result.data.viewer.repositories.page_info.has_next_page # set this to false if you want to play around without fetching the entire dataset | |
cursor = result.data.viewer.repositories.page_info.end_cursor | |
total_count = result.data.viewer.repositories.total_count | |
current_count += repositories.size | |
puts "Fetching data (#{current_count} / #{total_count}) from repositories [next:#{cursor}]" | |
end | |
##################################################################### | |
# Report on unarchived repositories not touched in the last 12 months | |
##################################################################### | |
def within_last_12_months?(date_string) | |
return false if date_string.nil? | |
DateTime.parse(date_string) >= DateTime.now.prev_year | |
end | |
puts "-" * 47 | |
puts "List of all unarchived repositories" | |
puts " - grouped by year last updated" | |
puts " - excludes those updated in the last 12 months" | |
puts "-" * 47 | |
binding.pry | |
list_by_year = all_repositories | |
.reject { |repo| repo.is_archived || within_last_12_months?(repo.pushed_at) } | |
# .reject { |repo| repo.is_fork } # Optionally filter on forks | |
# .reject { |repo| repo.is_private } # Optionally filter on private/public repositories | |
# .select { |repo| repo.owner.login == "org_name" } # Optionally filter on repositories under github.com/org_name (ORGANIZATION_MEMBER will include all repositories the org has access to) | |
.group_by { |repo| DateTime.parse(repo.pushed_at).year } | |
list_by_year.keys.sort.each do |year| | |
group = list_by_year[year] | |
puts "#{group.count} not updated since #{year}" | |
group.each { |repo| puts " - " + repo.url } | |
puts "" | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment