GitHubリポジトリ招待の自動化

# GitHub repo.への自動招待スクリプト
## 手作業ベースだと・・・

- コラボレーターのGitHubアカウントリストを作成
- 各リポのsettings/accessを開いて、
- 招待をかける

このような作業をリポの数とユーザーの数分だけ繰り返す。

## 自動化?

APIで行ってもいいと思いますがここはブラウザをシミュレートさせる方向で・・

# Seleniumで自動化できないものか？

:::note info
**わたくしの環境**
- Windows10
- python3をWin側にインストール
- firefox browser/geckodriver
:::

## 事前にインストール

- selenium
- webdriver

## まずこんな感じで始めました

```python
import os
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.firefox.service import Service
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from webdriver_manager.firefox import GeckoDriverManager
import pickle
import time

# Initialize the WebDriver
driver = webdriver.Firefox(service=Service(GeckoDriverManager().install()))
wait = WebDriverWait(driver, 20)  # Explicit wait

# Load cookies if they exist
try:
    cookies = pickle.load(open("cookies.pkl", "rb"))
    driver.get("https://github.com")
    for cookie in cookies:
        driver.add_cookie(cookie)
    print("Cookies loaded successfully.")
except FileNotFoundError:
    print("No cookies found, you will need to log in.")
```

## 次はリストの読み込み

```
# Users to invite
usernames_to_invite = [
]
```

## login

```python
try:
    # Open GitHub login page
    driver.get("https://github.com/login")
    
    # Check if already logged in by verifying the presence of a specific element
    if "login" in driver.current_url:
        # Enter username and password from environment variables
        username_input = wait.until(EC.presence_of_element_located((By.ID, "login_field")))
        username_input.send_keys(os.environ["GITHUB_USERNAME"])

        password_input = wait.until(EC.presence_of_element_located((By.ID, "password")))
        password_input.send_keys(os.environ["GITHUB_PASSWORD"])
        password_input.send_keys(Keys.RETURN)

        # Wait for two-factor authentication if needed
        time.sleep(15)  # Adjust based on your 2FA input

        # Save the session cookies
        cookies = driver.get_cookies()
        pickle.dump(cookies, open("cookies.pkl", "wb"))
        print("Cookies saved successfully.")

```

## リポジトリのユーザーページに移動します

:::note info
`((org))/((repo))`にはご自身の組織名とリポジトリを入力してください
:::

```python
   repo_url = "https://github.com/((org))/((repo))"
   driver.get(f"{repo_url}/settings/access")
```

## ループ部分

```python
    for username in usernames_to_invite:
        # Click the "Add people" button using its ID
        add_people_button = wait.until(EC.element_to_be_clickable((By.ID, "dialog-show-add-access-dialog-user")))
        add_people_button.click()

        # Wait for the modal to open
        time.sleep(2)  # Increase wait time if necessary

        # Use JavaScript to focus on the input field
        invite_input = wait.until(EC.visibility_of_element_located((By.NAME, "member")))
        driver.execute_script("arguments[0].scrollIntoView(true); arguments[0].focus();", invite_input)
        invite_input.clear()
        invite_input.send_keys(username)
        
        # Wait for suggestions to load
        time.sleep(2)

        # Select the first suggestion
        invite_input.send_keys(Keys.DOWN)
        invite_input.send_keys(Keys.RETURN)

        # Wait for role selection to appear
        time.sleep(2)

        # Select the "Write" role
        write_role = wait.until(EC.element_to_be_clickable((By.XPATH, "//span[text()='Write']")))
        write_role.click()

        # Send Tab key twice to focus on the "Add to repository" button, then press Return
        driver.find_element(By.TAG_NAME, "body").send_keys(Keys.TAB)
        driver.find_element(By.TAG_NAME, "body").send_keys(Keys.TAB)
        driver.find_element(By.TAG_NAME, "body").send_keys(Keys.RETURN)

        # Wait a bit before inviting the next user
        time.sleep(2)

    print("Invitations sent successfully!")

except Exception as e:
    print(f"An error occurred: {e}")

finally:
    # Close the WebDriver
    time.sleep(2)
    driver.quit()
```

# 個人情報さらしてないよね

コード内で個人情報さらしているよとかあったら至急ご指摘ください！

# リポジトリが複数なら

こちら側もループ回してください

# 工夫したこと

Users' role selection and "Add to repository" buttonへの遷移がとても苦労しました。前の画面でのキーボードアクションやらせようとしていたみたいで、壮絶な勘違いでした。


# 感想

時間をかけてでも作っておいてよかったと思います。

わりと人の入れ替わりがある組織なので、リポジトリにユーザーを招待するのも
大変といえば大変ですね。

手作業でも大したことないっちゃないけど、精神的に弱っているときとかにこのタスクが来ると 正直うんざりしていたので、これで年度初め（＝4月）がすこし楽になるかもしれません。