Skip to content

Instantly share code, notes, and snippets.

@flavio
Last active July 23, 2025 16:36
Show Gist options
  • Select an option

  • Save flavio/f13ab7ebd9b421a4602a7569a9100a50 to your computer and use it in GitHub Desktop.

Select an option

Save flavio/f13ab7ebd9b421a4602a7569a9100a50 to your computer and use it in GitHub Desktop.
Some quick helper scripts to save and restore the labels associated to PRs and issues of GitHub repositories
require 'octokit'
# Usage:
# ruby org-backup.rb save org_name
# ruby org-backup.rb restore org_name
# (expects token in .github_token file in current directory or HOME)
def read_token
paths = [
File.join(Dir.pwd, ".github_token"),
File.join(Dir.home, ".github_token")
]
paths.each do |path|
return File.read(path).strip if File.exist?(path)
end
puts "GitHub token not found. Please create a .github_token file in the current directory or your home directory."
exit 1
end
if ARGV.length != 2 || !%w[save restore].include?(ARGV[0])
puts "Usage:"
puts " ruby org-backup.rb save org_name"
puts " ruby org-backup.rb restore org_name"
exit 1
end
mode, org = ARGV
token = read_token
client = Octokit::Client.new(access_token: token)
client.auto_paginate = true
repos = client.org_repos(org, type: 'all').map(&:full_name)
total = repos.size
repos.each_with_index do |repo, idx|
file = "#{repo.gsub('/', '__')}.json"
progress = "#{idx + 1}/#{total}"
if mode == "save"
puts "[#{progress}] Saving labels for #{repo}..."
system("ruby repo-backup.rb save #{repo} #{file}")
else
if File.exist?(file)
puts "[#{progress}] Restoring labels for #{repo}..."
system("ruby repo-backup.rb restore #{repo} #{file}")
else
puts "[#{progress}] Backup file #{file} not found for #{repo}, skipping."
end
end
end
require 'octokit'
require 'json'
# Usage:
# ruby repo-backup.rb save owner/repo output.json
# ruby repo-backup.rb restore owner/repo input.json
# (expects token in .github_token file in the current directory or HOME)
def read_token
paths = [
File.join(Dir.pwd, ".github_token"),
File.join(Dir.home, ".github_token")
]
paths.each do |path|
return File.read(path).strip if File.exist?(path)
end
puts "GitHub token not found. Please create a .github_token file in the current directory or your home directory."
exit 1
end
if ARGV.length != 3 || !%w[save restore].include?(ARGV[0])
puts "Usage:"
puts " ruby repo-backup.rb save owner/repo output.json"
puts " ruby repo-backup.rb restore owner/repo input.json"
puts " (expects token in .github_token file in current directory or HOME)"
exit 1
end
mode, repo, file = ARGV
token = read_token
client = Octokit::Client.new(access_token: token)
client.auto_paginate = true
def fetch_labels_for_items(client, repo, type)
items = []
page = 1
loop do
batch = if type == :issue
client.issues(repo, state: 'all', per_page: 100, page: page)
else
client.pull_requests(repo, state: 'all', per_page: 100, page: page)
end
break if batch.empty?
items.concat(batch)
page += 1
end
items.map do |item|
{
id: item.id,
number: item.number,
title: item.title,
labels: item.labels.map { |l| l.name }
}
end
end
if mode == "save"
puts "Fetching issues..."
issues = fetch_labels_for_items(client, repo, :issue)
puts "Fetching pull requests..."
prs = fetch_labels_for_items(client, repo, :pr)
result = {
issues: issues,
pull_requests: prs
}
File.write(file, JSON.pretty_generate(result))
puts "Saved associations to #{file}"
elsif mode == "restore"
data = JSON.parse(File.read(file))
puts "Restoring issue labels..."
data["issues"].each do |issue|
begin
client.update_issue(repo, issue["number"], labels: issue["labels"])
puts "Restored labels for issue ##{issue["number"]}"
rescue => e
puts "Failed to restore labels for issue ##{issue["number"]}: #{e}"
end
end
puts "Restoring pull request labels..."
data["pull_requests"].each do |pr|
begin
client.update_issue(repo, pr["number"], labels: pr["labels"])
puts "Restored labels for pull request ##{pr["number"]}"
rescue => e
puts "Failed to restore labels for pull request ##{pr["number"]}: #{e}"
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment