From d9e05e594a0b96a8a23268fb32264cb1366641eb Mon Sep 17 00:00:00 2001 From: naiba Date: Mon, 16 Feb 2026 13:36:45 +0800 Subject: [PATCH] feat: add AtomGit code and release sync workflows --- .github/sync_atomgit.py | 123 +++++++++++++++++++++ .github/workflows/sync-code-atomgit.yml | 16 +++ .github/workflows/sync-release-atomgit.yml | 17 +++ 3 files changed, 156 insertions(+) create mode 100644 .github/sync_atomgit.py create mode 100644 .github/workflows/sync-code-atomgit.yml create mode 100644 .github/workflows/sync-release-atomgit.yml diff --git a/.github/sync_atomgit.py b/.github/sync_atomgit.py new file mode 100644 index 0000000..fe82c31 --- /dev/null +++ b/.github/sync_atomgit.py @@ -0,0 +1,123 @@ +import os +import time +import requests +from github import Github + +ATOMGIT_API = "https://api.atomgit.com/api/v5" +ATOMGIT_OWNER = "naiba" +ATOMGIT_REPO = "nezha-dashboard" +GITHUB_REPO = "nezhahq/nezha" + + +def get_github_latest_release(): + g = Github() + repo = g.get_repo(GITHUB_REPO) + release = repo.get_latest_release() + if not release: + print("No releases found.") + return + + print(f"Latest release tag is: {release.tag_name}") + print(f"Latest release info is: {release.body}") + files = [] + for asset in release.get_assets(): + url = asset.browser_download_url + name = asset.name + + response = requests.get(url) + if response.status_code == 200: + with open(name, "wb") as f: + f.write(response.content) + print(f"Downloaded {name}") + else: + print(f"Failed to download {name}") + files.append(get_abs_path(name)) + sync_to_atomgit(release.tag_name, release.body, files) + + +def sync_to_atomgit(tag, body, files): + access_token = os.environ["ATOMGIT_PAT"] + release_api_uri = f"{ATOMGIT_API}/repos/{ATOMGIT_OWNER}/{ATOMGIT_REPO}/releases" + + release_data = { + "access_token": access_token, + "tag_name": tag, + "name": tag, + "body": body, + "prerelease": False, + "target_commitish": "master", + } + + release_resp = None + for attempt in range(3): + try: + release_resp = requests.post(release_api_uri, json=release_data, timeout=30) + release_resp.raise_for_status() + break + except requests.exceptions.Timeout: + print( + f"Create release timed out, retrying in 30s... (attempt {attempt + 1})" + ) + time.sleep(30) + except requests.exceptions.RequestException as err: + print(f"Create release failed: {err}") + if release_resp is not None: + print(f"Response: {release_resp.text}") + break + + if release_resp is None or release_resp.status_code not in (200, 201): + print("Failed to create release on AtomGit, aborting.") + return + + print(f"Created release {tag} on AtomGit") + + for file_path in files: + upload_asset(access_token, tag, file_path) + + print("Sync is completed!") + + +def upload_asset(access_token, tag, file_path): + file_name = os.path.basename(file_path) + upload_url_api = ( + f"{ATOMGIT_API}/repos/{ATOMGIT_OWNER}/{ATOMGIT_REPO}" + f"/releases/{tag}/upload_url?file_name={file_name}" + ) + + for attempt in range(3): + try: + resp = requests.get( + upload_url_api, + headers={"Authorization": f"Bearer {access_token}"}, + timeout=30, + ) + resp.raise_for_status() + upload_info = resp.json() + + obs_url = upload_info["url"] + obs_headers = upload_info["headers"] + + with open(file_path, "rb") as f: + put_resp = requests.put( + obs_url, headers=obs_headers, data=f, timeout=120 + ) + + if put_resp.text.strip() == "success" or put_resp.status_code in (200, 201): + print(f"Uploaded {file_name}") + return + else: + print( + f"Upload {file_name} failed: {put_resp.status_code} {put_resp.text}" + ) + except requests.exceptions.RequestException as err: + print(f"Upload {file_name} attempt {attempt + 1} failed: {err}") + time.sleep(10) + + print(f"Failed to upload {file_name} after 3 attempts") + + +def get_abs_path(path): + return os.path.join(os.getcwd(), path) + + +get_github_latest_release() diff --git a/.github/workflows/sync-code-atomgit.yml b/.github/workflows/sync-code-atomgit.yml new file mode 100644 index 0000000..10982ec --- /dev/null +++ b/.github/workflows/sync-code-atomgit.yml @@ -0,0 +1,16 @@ +name: Sync Code to AtomGit + +on: + push: + branches: [master] + +jobs: + sync-code-to-atomgit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: adambirds/sync-github-to-gitlab-action@v1.1.0 + with: + destination_repository: git@atomgit.com:naiba/nezha-dashboard.git + destination_branch_name: master + destination_ssh_key: ${{ secrets.ATOMGIT_SSH_KEY }} diff --git a/.github/workflows/sync-release-atomgit.yml b/.github/workflows/sync-release-atomgit.yml new file mode 100644 index 0000000..cac88db --- /dev/null +++ b/.github/workflows/sync-release-atomgit.yml @@ -0,0 +1,17 @@ +name: Sync Release to AtomGit + +on: + workflow_dispatch: + +jobs: + sync-release-to-atomgit: + runs-on: ubuntu-latest + timeout-minutes: 120 + env: + ATOMGIT_PAT: ${{ secrets.ATOMGIT_PAT }} + steps: + - uses: actions/checkout@v4 + - name: Sync to AtomGit + run: | + pip3 install PyGitHub + python3 .github/sync_atomgit.py