diff --git a/.github/workflows/sync_ruyi.yml b/.github/workflows/sync_ruyi.yml new file mode 100644 index 0000000..1aaec98 --- /dev/null +++ b/.github/workflows/sync_ruyi.yml @@ -0,0 +1,43 @@ +name: Sync support matrix Images with Ruyi + +on: + push: + + workflow_dispatch: + +jobs: + sync: + environment: ruyi-sync + runs-on: ubuntu-latest + steps: + - name: Generate a token + id: generate_token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ vars.APP_ID }} # support-matrix Automation: 1044103 + private-key: ${{ secrets.APP_PRIVATE_KEY }} + - name: Checkout + uses: actions/checkout@v2 + - name: Sync Images + env: + GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} + run: | + python assets/renew_ruyi_index.py -c assets/config.toml -p . | tee sync.log + - name: Setup Dependencies + run: | + sudo apt-get update + sudo apt-get install -y ninja-build + sudo pip3 install --upgrade meson + pip install -r assets/requirements_ruyinv.txt + - name: Output logs to summary + run: | + echo "# Support Matrix Image Sync Report" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "[CI #${{ github.run_number }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) powered by [${{ github.repository }}](${{ github.server_url }}/${{ github.repository }}) has successfully sync images." >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "## Sync Log" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY + cat sync.log >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY diff --git a/assets/docs/ruyi_index_updator.md b/assets/docs/ruyi_index_updator.md index 50d99fd..2ae6c5a 100644 --- a/assets/docs/ruyi_index_updator.md +++ b/assets/docs/ruyi_index_updator.md @@ -4,9 +4,15 @@ ### 使用方法 +#### Oauth Token + 你需要设置环境变量 `GITHUB_TOKEN`,并且该 token 需要有对应仓库的读写权限和 pr 权限。或者你可以同时允许 fork 仓库的权限,让工具自动 fork 仓库。 -**注意!**你直接从设置中获取到的 token 都是 personal token,但是向组织仓库进行 pr 时,该 token 是会被拒绝的。你需要使用一个 OAuth app token。最简单的辨别方法是,personal token 的格式是 `ghp_` 开头,OAuth app token 的格式是 `gho` 开头。若你不知道如何获取 OAuth app token,可以通过 `gh` 命令行工具来获取一个。 **请注意不要泄漏** +**注意!**你直接从设置中获取到的 token 都是 personal token,但是向组织仓库进行 pr 时,该 token 是会被拒绝的。你需要使用一个 OAuth app token。最简单的辨别方法是,personal token 的格式是 `ghp` 开头,OAuth app token 的格式是 `gho` 开头。 + +若未设置,会引导你获取一个。 + +--- 默认工具会在临时文件夹中运行,每次可能会下载一大堆镜像文件。你可以设置环境变量 `$CACHE_DIR` 来指定缓存目录,这样工具会将所有的东西都缓存到这个目录中。 diff --git a/assets/src/ruyi_index_updator/index_handler.py b/assets/src/ruyi_index_updator/index_handler.py index 0fb8d6f..540086a 100644 --- a/assets/src/ruyi_index_updator/index_handler.py +++ b/assets/src/ruyi_index_updator/index_handler.py @@ -4,6 +4,8 @@ import os import logging +from typing import NoReturn +import requests import git from github import Github, Auth from github.Repository import Repository @@ -11,9 +13,69 @@ logger = logging.getLogger(__name__) -PACKAGE_INDEX_OWNER = "ruyisdk" +PACKAGE_INDEX_OWNER = "wychlw" PACKAGE_INDEX_REPO = "packages-index" +APP_CLIENT_ID = "Iv23liO0aJcnHy86Y4a6" if os.getenv( + "GITHUB_CLIENT_ID", None) is None else os.getenv("GITHUB_CLIENT_ID") +# Ov23liyfuTo79LSHdp49: Support-matrix_Automation APP_CLIENT_ID +APP_SCOPE = "repo,gist" + + +def create_new_oath_token() -> NoReturn: + """ + Prompt to create a new OAuth token. + """ + accept = "application/json" + logger.error("No Github OAuth token found. Let's create a new one.") + logger.warning("We are going to create a new OAuth token for you.") + req_url = "https://github.com/login/device/code" + req_data = { + "client_id": APP_CLIENT_ID, + "scope": APP_SCOPE + } + res = requests.post(req_url, data=req_data, + headers={"Accept": accept}, timeout=20) + res_json = res.json() + device_code = res_json["device_code"] + user_code = res_json["user_code"] + verification_uri = res_json["verification_uri"] + logger.warning("Please enter the code %s in following link.", user_code) + logger.warning("Open link: %s", verification_uri) + logger.warning("After you enter the code, please press enter to continue.") + input() + req_token_url = "https://github.com/login/oauth/access_token" + req_token_data = { + "client_id": APP_CLIENT_ID, + "device_code": device_code, + "grant_type": "urn:ietf:params:oauth:grant-type:device_code" + } + res_token = requests.post(req_token_url, data=req_token_data, + headers={"Accept": accept}, + timeout=20) + res_token_json = res_token.json() + access_token = res_token_json["access_token"] + logger.warning( + "Following step will display your new OAuth token. Press enter to continue.") + input() + logger.warning("Copy the following token and set it to GITHUB_TOKEN.") + logger.warning("New OAuth token: %s", access_token) + logger.warning("After you set the token, please restart the program.") + exit(1) + + +def try_login_github() -> Github: + """ + Login to github. + """ + + github_token = os.getenv('GITHUB_TOKEN', None) + if github_token is None: + create_new_oath_token() + logger.info("Login with Github OAuth Token") + auth = Auth.Token(github_token) + return Github(auth=auth) + class PrWrapper: """ @@ -98,12 +160,7 @@ def __reset_to_upstream(self): self.local_repo.remote().set_url(self.repo.ssh_url) def __init__(self, repo_dir: str) -> None: - github_token = os.getenv('GITHUB_TOKEN', None) - if github_token is None: - logger.error('GITHUB_TOKEN env is not set') - raise ValueError('GITHUB_TOKEN is not set') - auth = Auth.Token(github_token) - self.github = Github(auth=auth) + self.github = try_login_github() self.user = self.github.get_user() self.upstream = self.github.get_repo(