ソースを参照

feat(action): implement kernel repository sync with manifest download and parallel archive handling

Co-authored-by: Copilot <copilot@github.com>
TheWildJames 4 ヶ月 前
コミット
2cf6b4d2ad
1 ファイル変更104 行追加13 行削除
  1. 104 13
      .github/actions/download-kernel/action.yml

+ 104 - 13
.github/actions/download-kernel/action.yml

@@ -20,20 +20,111 @@ outputs:
 runs:
   using: composite
   steps:
-    - name: Initialize and Sync Kernel Repository
-      id: sync
+    # - name: Initialize and Sync Kernel Repository
+    #   id: sync
+    #   shell: bash
+    #   working-directory: ${{ github.workspace }}/kernel
+    #   run: |
+    #     FORMATTED_BRANCH="${{ inputs.android_version }}-${{ inputs.kernel_version }}-${{ inputs.os_patch_level }}"
+    #       repo init -u https://android.googlesource.com/kernel/manifest -b common-${FORMATTED_BRANCH} --depth=1 --partial-clone --clone-filter=blob:limit=10M
+    #       REMOTE_BRANCH=$(git ls-remote https://android.googlesource.com/kernel/common ${FORMATTED_BRANCH})
+    #     DEFAULT_MANIFEST_PATH=.repo/manifests/default.xml
+    #     DEPRECATED=false
+    #     if grep -q deprecated <<< $REMOTE_BRANCH; then
+    #       sed -i "s/\"${FORMATTED_BRANCH}\"/\"deprecated\/${FORMATTED_BRANCH}\"/g" $DEFAULT_MANIFEST_PATH
+    #       echo "⚠ Note: Branch ${FORMATTED_BRANCH} is considered deprecated."
+    #       DEPRECATED=true
+    #     fi
+    #     echo "deprecated=$DEPRECATED" >> $GITHUB_OUTPUT
+    #     repo sync -c -j$(nproc --all) --fail-fast --no-tags --force-sync --no-clone-bundle
+
+    - name: Download manifest.xml for branch
       shell: bash
       working-directory: ${{ github.workspace }}/kernel
       run: |
         FORMATTED_BRANCH="${{ inputs.android_version }}-${{ inputs.kernel_version }}-${{ inputs.os_patch_level }}"
-          repo init -u https://android.googlesource.com/kernel/manifest -b common-${FORMATTED_BRANCH} --depth=1 --partial-clone --clone-filter=blob:limit=10M
-          REMOTE_BRANCH=$(git ls-remote https://android.googlesource.com/kernel/common ${FORMATTED_BRANCH})
-        DEFAULT_MANIFEST_PATH=.repo/manifests/default.xml
-        DEPRECATED=false
-        if grep -q deprecated <<< $REMOTE_BRANCH; then
-          sed -i "s/\"${FORMATTED_BRANCH}\"/\"deprecated\/${FORMATTED_BRANCH}\"/g" $DEFAULT_MANIFEST_PATH
-          echo "⚠ Note: Branch ${FORMATTED_BRANCH} is considered deprecated."
-          DEPRECATED=true
-        fi
-        echo "deprecated=$DEPRECATED" >> $GITHUB_OUTPUT
-        repo sync -c -j$(nproc --all) --fail-fast --no-tags --force-sync --no-clone-bundle
+        MANIFEST_URL="https://android.googlesource.com/kernel/manifest/+/refs/heads/common-${FORMATTED_BRANCH}/default.xml?format=TEXT"
+        echo "Fetching manifest from $MANIFEST_URL"
+        curl -fsSL "$MANIFEST_URL" | base64 -d > manifest.xml
+
+    - name: Fast Parallel Archive Download
+      shell: python
+      working-directory: ${{ github.workspace }}/kernel
+      run: |
+        import xml.etree.ElementTree as ET
+        import subprocess
+        import os, glob, shutil
+        from concurrent.futures import ThreadPoolExecutor
+
+        MAX_WORKERS = (os.cpu_count() or 2) * 4
+        NPROC = int(subprocess.check_output("nproc", shell=True).strip())
+
+        def sync_project(task):
+        name, path, url, strip, rev, linkfiles, copyfiles = task
+        if path not in ["./", "."]:
+          os.makedirs(path, exist_ok=True)
+        print(f"Syncing: {name} -> {path}")
+        try:
+          aria_cmd = f"aria2c -x16 -s16 -k1M -j5 --file-allocation=none -o {name}.tar.gz '{url}'"
+          subprocess.run(aria_cmd, shell=True, check=True)
+          tar_cmd = f"tar -I 'pigz -p {NPROC} -b 256' -x --record-size=1M -C {path} {strip} -f {name}.tar.gz"
+          subprocess.run(tar_cmd, shell=True, check=True)
+          os.remove(f"{name}.tar.gz")
+          # Handle linkfiles and copyfiles
+          top_dir = os.getcwd()
+          for src_rel, dest_rel in linkfiles:
+            src_path = os.path.join(top_dir, path, src_rel)
+            dest_path = os.path.join(top_dir, dest_rel)
+            os.makedirs(os.path.dirname(dest_path), exist_ok=True)
+            if os.path.lexists(dest_path): os.remove(dest_path)
+            rel_target = os.path.relpath(src_path, os.path.dirname(dest_path))
+            os.symlink(rel_target, dest_path)
+            print(f"  [Link] {dest_rel} -> {src_rel}")
+          for src_rel, dest_rel in copyfiles:
+            src_path = os.path.join(top_dir, path, src_rel)
+            dest_path = os.path.join(top_dir, dest_rel)
+            os.makedirs(os.path.dirname(dest_path), exist_ok=True)
+            shutil.copy2(src_path, dest_path)
+            print(f"  [Copy] {dest_rel} from {src_rel}")
+          print(f"Synced {name} successfully!")
+          return True
+        except Exception as e:
+          print(f"Failed to sync {name}: {e}")
+          return False
+
+        with open('manifest.xml', 'r') as f:
+          manifest_content = f.read()
+        root = ET.fromstring(manifest_content)
+        remotes = {r.get('name'): r.get('fetch').rstrip('/') for r in root.findall('remote')}
+        default = root.find('default')
+        def_remote = default.get('remote') if default is not None else None
+        def_rev = default.get('revision') if default is not None else None
+
+        sync_tasks = []
+        for project in root.findall('project'):
+        name = project.get('name')
+        path = project.get('path', name)
+        remote_name = project.get('remote', def_remote)
+        rev = project.get('revision', def_rev)
+        base_url = remotes.get(remote_name)
+        if not base_url: continue
+        if "github.com" in base_url:
+          url = f"{base_url}/{name}/archive/{rev}.tar.gz"
+          strip = "--strip-components=1"
+        elif "googlesource.com" in base_url:
+          url = f"{base_url}/{name}/+archive/{rev}.tar.gz"
+          strip = ""
+        elif "git.codelinaro.org" in base_url:
+          url = f"{base_url}/{name}/-/archive/{rev}.tar.gz"
+          strip = "--strip-components=1"
+        else:
+          continue
+        linkfiles = [(lf.get('src'), lf.get('dest')) for lf in project.findall('linkfile')]
+        copyfiles = [(cf.get('src'), cf.get('dest')) for cf in project.findall('copyfile')]
+        sync_tasks.append((name, path, url, strip, rev, linkfiles, copyfiles))
+
+        with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
+          results = list(executor.map(sync_project, sync_tasks))
+        if not all(results):
+        print("One or more projects failed to sync!")
+        exit(1)