Last active
July 23, 2025 16:36
-
-
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
This file contains hidden or 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
| 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 |
This file contains hidden or 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
| 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