4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

SUPER STUDIOAdvent Calendar 2023

Day 12

GitHubレポジトリを一括でUnwatchするためにChatGPTに頼んだ

Last updated at Posted at 2023-12-11

何が起きたか

新しい企業に就職・転職したときGitHubアカウントは個人アカウントを使ってね!と言われることは多々あるでしょう。
さらに参加した会社の組織リポジトリのほとんどに書き込み権限が付与されていると、通知の設定を変更していないと恐ろしい数のメールが出社翌日からメールボックスに溜まっていくことでしょう。

以下の記事に書かれている「通知設定の変更」と「送信先メールアドレスの変更」を事前に設定していれば問題ないのですが、気づいたときには大体時すでに遅し、という場合もあるのではないでしょうか。

私もそうでした。頑張って手作業で1つずつリポジトリのUnwatchを頑張ろうと思ったのですが、200以上のリポジトリがあったため早々に諦めました。

対策

1年前の私なら、きっとGoogle検索をしながら頑張ってRubyスクリプトを書いていたことでしょう。しかし、2023年はChatGPTという頭脳があります。もちろん課金済みです。早速聞いてみました。

質問文

・GitHubの組織リポジトリをすべてUnwatchするためのスクリプトを教えてください
 ・Unwatchはペイロード {'subscribed': False, 'ignored': False} を指定する必要があります
・引数でdry-run(削除せず一覧だけ表示するモード)と実行するモードを実装してください
・100リポジトリ以上あるのでループ処理できるようにしてください

「大体のAPI仕様調べてるやん」というツッコミはなしでお願いします!

また、上記で指定しているペイロードはUnwatchのための指定にはなりますが、2023/11現在のGitHubのNotificationsのデフォルト仕様だとこのペイロードを指定してUnwatchしたとしても、通知設定は Participating and @mentionsになります。そのため自分宛てのメンションが付与されていたりAuthorやAssignに指定されていたりすれば通知は飛んできますので安心してください。

生成されたスクリプト

私が一生懸命書くと多分1時間はかかる内容を、ものの20秒で出力してくれました。

app.py
import requests
import sys

def unwatch_repos(org, token, dry_run):
    headers = {'Authorization': f'token {token}'}
    repos_url = f'https://api.github.com/orgs/{org}/repos'
    payload = {'subscribed': False, 'ignored': False}

    while True:
        response = requests.get(repos_url, headers=headers)
        repos = response.json()

        if response.status_code != 200 or not repos:
            print("Error fetching repositories or no repositories found.")
            break

        for repo in repos:
            repo_name = repo['full_name']
            watch_url = f'https://api.github.com/repos/{repo_name}/subscription'
            
            if dry_run:
                print(f'Would unwatch: {repo_name}')
            else:
                unwatch_response = requests.put(watch_url, headers=headers, json=payload)
                if unwatch_response.status_code == 200:
                    print(f'Unwatched: {repo_name}')
                else:
                    print(f'Failed to unwatch: {repo_name}')

        # Check for the 'next' link in the headers
        if 'next' in response.links:
            repos_url = response.links['next']['url']
        else:
            break

if __name__ == '__main__':
    if len(sys.argv) != 4:
        print("Usage: python script.py org_name personal_access_token dry_run")
        sys.exit(1)

    org_name = sys.argv[1]
    access_token = sys.argv[2]
    dry_run = sys.argv[3].lower() == 'true'

    unwatch_repos(org_name, access_token, dry_run)

実行は以下のようにすると良いそうですが、実際は pip install requests が事前に必要です。

python script.py [組織名] [GitHub Personal Token] true

ちなみに↑のコード動かしてないので動くかわかりません(笑)
なお、私が他のChatGPTから得た答えはGistにて公開してます。

Gist

まとめ

便利な世の中になりましたね。これでメールボックスが埋まる問題も解決です。
もはや簡単なスクリプトやシェルを使うようなワンライナーコードは大体ChatGPTに考えてもらえば一発で解決するようになりました。
そして、逆転の発想で生成してもらったスクリプトを自分が理解できるようにChatGPTに聞きながら、別のスクリプトに書き換えたりあるいはクラス化したりすることで、自身のスキルアップにつなげることもできるわけです。

まだChatGPTを含めた生成AI系のサービスを使ったことがないエンジニアのみなさん、是非使ってみてください。あなたが昨日めんどくさいと思って止めた手作業のそれ、もしかしたらChatGPTが解決してくれるかもしれませんよ。

(余談)追加のお願いをChatGPTに聞いてみた

宗教上の理由でPythonが使えないという場合もあると思いますので、Rubyに変換してもらうようにChatGPTに依頼してみました。

結果出力された内容が以下です。

require 'net/http'
require 'json'
require 'uri'

def unwatch_repos(org, token, dry_run)
  base_url = "https://api.github.com/orgs/#{org}/repos"
  headers = {
    "Authorization" => "token #{token}",
    "Content-Type" => "application/json",
    "User-Agent" => "Ruby"
  }

  uri = URI(base_url)
  loop do
    response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
      request = Net::HTTP::Get.new(uri, headers)
      http.request(request)
    end

    break unless response.is_a?(Net::HTTPSuccess)

    repos = JSON.parse(response.body)
    break if repos.empty?

    repos.each do |repo|
      repo_name = repo['full_name']
      watch_url = URI("https://api.github.com/repos/#{repo_name}/subscription")

      if dry_run
        puts "Would unwatch: #{repo_name}"
      else
        Net::HTTP.start(watch_url.hostname, watch_url.port, use_ssl: watch_url.scheme == 'https') do |http|
          request = Net::HTTP::Put.new(watch_url, headers)
          request.body = {'subscribed' => false, 'ignored' => false}.to_json
          response = http.request(request)

          if response.is_a?(Net::HTTPSuccess)
            puts "Unwatched: #{repo_name}"
          else
            puts "Failed to unwatch: #{repo_name}"
          end
        end
      end
    end

    # Next page
    link_header = response['link']
    next_link = link_header&.split(',')&.find { |link| link.include?('rel="next"') }
    break unless next_link

    next_page_url = next_link.match(/<(.*?)>/)[1]
    uri = URI(next_page_url)
  end
end

if ARGV.length != 3
  puts "Usage: ruby script.rb org_name personal_access_token dry_run"
  exit
end

org_name, access_token, dry_run_flag = ARGV
dry_run = dry_run_flag.downcase == 'true'

unwatch_repos(org_name, access_token, dry_run)

すごいですね。ChatGPTで実装をお願いして要した時間は約2分。この記事を書く(30分)より早く終わってしまいました。。。
生産性を上げるためにChatGPTに色々手伝ってもらうのは個人的にめちゃくちゃ肯定派なので、企業の情報セキュリティのポリシーやルールに従いつつ、パーソナルすぎる作業は可能な限り自動化してしまいましょう。

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?