From f565f12f9f8c3abdb780e494f49bfb9a1bd09e0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Raimondas=20Rimkevi=C4=8Dius?= Date: Wed, 22 Apr 2026 23:32:27 +0300 Subject: [PATCH 1/7] infra: add GitHub Actions extraction workflow and CI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit extract.yml — permanent workflow dispatched by the Cloudflare Worker for every extraction job. Accepts run_id, source_repo, source_path, target_repo, target_branch, and target_path as inputs. Secrets are named GE_SRC_{run_id} and GE_TGT_{run_id} so concurrent runs never collide. Supports any HTTPS-accessible repo (GitHub or GitLab). ci.yml — runs on every PR and push to main: install, lint, build for both workspaces. Steps use continue-on-error until workspaces are fully scaffolded. Closes #2 --- .github/workflows/ci.yml | 33 +++++++++++++++++++++ .github/workflows/extract.yml | 56 +++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/extract.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..541793a --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,33 @@ +name: CI + +on: + pull_request: + branches: [main] + push: + branches: [main] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Install dependencies + run: npm install + + - name: Lint frontend + run: npm run lint -w frontend + continue-on-error: true + + - name: Build frontend + run: npm run build -w frontend + continue-on-error: true + + - name: Build worker + run: npm run build -w worker + continue-on-error: true diff --git a/.github/workflows/extract.yml b/.github/workflows/extract.yml new file mode 100644 index 0000000..63bcf02 --- /dev/null +++ b/.github/workflows/extract.yml @@ -0,0 +1,56 @@ +name: git-extract + +on: + workflow_dispatch: + inputs: + run_id: + required: true + description: Unique ID used to name per-run secrets (avoids concurrent collision) + source_repo: + required: true + description: Full HTTPS clone URL (GitHub or GitLab, any host) + source_path: + required: true + description: Subdirectory to extract, e.g. packages/ui + target_repo: + required: true + description: Full HTTPS push URL (GitHub or GitLab, any host) + target_branch: + required: true + default: main + description: Branch to force-push the filtered history onto + target_path: + required: true + default: src + description: Subdirectory in target repo to place the extracted history under + +jobs: + extract: + runs-on: ubuntu-latest + steps: + - name: Install git-filter-repo + run: pip install git-filter-repo + + - name: Clone source repo + env: + TOKEN: ${{ secrets[format('GE_SRC_{0}', inputs.run_id)] }} + run: | + HOST=$(echo "${{ inputs.source_repo }}" | sed 's|https://||') + git clone "https://oauth2:${TOKEN}@${HOST}" /tmp/source + + - name: Filter history + run: | + cd /tmp/source + git filter-repo \ + --subdirectory-filter "${{ inputs.source_path }}" \ + --to-subdirectory-filter "${{ inputs.target_path }}" \ + --force + + - name: Push to target repo + env: + TOKEN: ${{ secrets[format('GE_TGT_{0}', inputs.run_id)] }} + run: | + HOST=$(echo "${{ inputs.target_repo }}" | sed 's|https://||') + cd /tmp/source + git remote set-url origin "https://oauth2:${TOKEN}@${HOST}" + git push origin "HEAD:${{ inputs.target_branch }}" --force From d65c58007887293236e5c3a608feec58e5020121 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Raimondas=20Rimkevi=C4=8Dius?= Date: Wed, 22 Apr 2026 23:35:10 +0300 Subject: [PATCH 2/7] infra: rename ci.yml to lint-build.yml --- .github/workflows/{ci.yml => lint-build.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{ci.yml => lint-build.yml} (100%) diff --git a/.github/workflows/ci.yml b/.github/workflows/lint-build.yml similarity index 100% rename from .github/workflows/ci.yml rename to .github/workflows/lint-build.yml From a3713ad1c6188fb4f0eddc7696f9ac05800a4400 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Raimondas=20Rimkevi=C4=8Dius?= Date: Thu, 23 Apr 2026 02:48:32 +0300 Subject: [PATCH 3/7] infra: use BFG + git-filter-repo hybrid in extract workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BFG mirror-clones the source repo and deletes all top-level dirs not on the source path prefix — fast blob-level operation that strips the bulk of irrelevant history before any path rewriting happens. git-filter-repo then runs on the already-lean working clone to do the subdirectory extraction and rename, which is much faster now that the repo contains only the relevant subtree. BFG requires a mirror clone and a gc pass after deletion. git-filter-repo handles the precise --subdirectory-filter + --to-subdirectory-filter rename that BFG cannot do. --- .github/workflows/extract.yml | 36 ++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/.github/workflows/extract.yml b/.github/workflows/extract.yml index 63bcf02..4824721 100644 --- a/.github/workflows/extract.yml +++ b/.github/workflows/extract.yml @@ -28,17 +28,47 @@ jobs: extract: runs-on: ubuntu-latest steps: + - name: Download BFG + run: | + wget -q https://repo1.maven.org/maven2/com/madgag/bfg/1.14.0/bfg-1.14.0.jar \ + -O /tmp/bfg.jar + - name: Install git-filter-repo run: pip install git-filter-repo - - name: Clone source repo + - name: Mirror clone source repo env: TOKEN: ${{ secrets[format('GE_SRC_{0}', inputs.run_id)] }} run: | HOST=$(echo "${{ inputs.source_repo }}" | sed 's|https://||') - git clone "https://oauth2:${TOKEN}@${HOST}" /tmp/source + git clone --mirror "https://oauth2:${TOKEN}@${HOST}" /tmp/source.git + + - name: BFG — strip top-level dirs outside source path + run: | + SOURCE_ROOT=$(echo "${{ inputs.source_path }}" | cut -d/ -f1) + cd /tmp/source.git + + # Collect all top-level tree dirs except the one we need + UNWANTED=$( + git ls-tree HEAD \ + | awk '$2 == "tree" {print $4}' \ + | grep -v "^${SOURCE_ROOT}$" \ + | paste -sd, + ) + + if [ -n "$UNWANTED" ]; then + java -jar /tmp/bfg.jar \ + --delete-folders "{${UNWANTED}}" \ + --no-blob-protection \ + /tmp/source.git + git reflog expire --expire=now --all + git gc --prune=now --aggressive + fi + + - name: Clone working copy from stripped mirror + run: git clone /tmp/source.git /tmp/source - - name: Filter history + - name: Rename path with git-filter-repo run: | cd /tmp/source git filter-repo \ From dd0ee14758ebeb1123baac6a749097908605fb33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Raimondas=20Rimkevi=C4=8Dius?= Date: Thu, 23 Apr 2026 02:57:07 +0300 Subject: [PATCH 4/7] infra: drop git-filter-repo, rename paths via fast-export/fast-import MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BFG strips irrelevant top-level dirs from the mirror clone (fast, blob-level). The path rename and deeper-level filtering are handled entirely with git fast-export piped through awk into git fast-import — native git, no extra tools. awk rewrites M/D lines whose path matches source_path (or is prefixed by source_path/) and drops all others, eliminating sibling dirs at deeper levels that BFG cannot safely remove by name. --- .github/workflows/extract.yml | 44 +++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/.github/workflows/extract.yml b/.github/workflows/extract.yml index 4824721..2c35b02 100644 --- a/.github/workflows/extract.yml +++ b/.github/workflows/extract.yml @@ -33,9 +33,6 @@ jobs: wget -q https://repo1.maven.org/maven2/com/madgag/bfg/1.14.0/bfg-1.14.0.jar \ -O /tmp/bfg.jar - - name: Install git-filter-repo - run: pip install git-filter-repo - - name: Mirror clone source repo env: TOKEN: ${{ secrets[format('GE_SRC_{0}', inputs.run_id)] }} @@ -43,12 +40,11 @@ jobs: HOST=$(echo "${{ inputs.source_repo }}" | sed 's|https://||') git clone --mirror "https://oauth2:${TOKEN}@${HOST}" /tmp/source.git - - name: BFG — strip top-level dirs outside source path + - name: BFG — strip top-level dirs outside source path prefix run: | SOURCE_ROOT=$(echo "${{ inputs.source_path }}" | cut -d/ -f1) cd /tmp/source.git - # Collect all top-level tree dirs except the one we need UNWANTED=$( git ls-tree HEAD \ | awk '$2 == "tree" {print $4}' \ @@ -68,13 +64,41 @@ jobs: - name: Clone working copy from stripped mirror run: git clone /tmp/source.git /tmp/source - - name: Rename path with git-filter-repo + - name: Rewrite paths with fast-export/fast-import run: | + SRC="${{ inputs.source_path }}" + TGT="${{ inputs.target_path }}" cd /tmp/source - git filter-repo \ - --subdirectory-filter "${{ inputs.source_path }}" \ - --to-subdirectory-filter "${{ inputs.target_path }}" \ - --force + + git fast-export --all | awk \ + -v src="$SRC" \ + -v tgt="$TGT" ' + # Rewrite or drop file modify/delete lines based on path + /^[MD] / { + # Extract path (last field, may contain spaces — use sub to split at correct point) + line = $0 + if (line ~ "^M ") { + # M + match(line, /^M [0-9]+ [^ ]+ /) + prefix = substr(line, 1, RLENGTH) + path = substr(line, RLENGTH + 1) + } else { + # D + prefix = "D " + path = substr(line, 3) + } + + if (path == src || index(path, src "/") == 1) { + sub(src, tgt, path) + print prefix path + } + # Drop lines for paths outside source_path (siblings left by BFG at deeper levels) + next + } + { print } + ' | git fast-import --force --quiet + + git checkout HEAD -- . - name: Push to target repo env: From 9df68fff3bfb1394d1cf77a98534d2fd21411ed1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Raimondas=20Rimkevi=C4=8Dius?= Date: Thu, 23 Apr 2026 02:58:34 +0300 Subject: [PATCH 5/7] infra: remove redundant clone, operate directly on bare mirror fast-export, fast-import and git push all work on bare repos. No need to clone /tmp/source.git to /tmp/source. --- .github/workflows/extract.yml | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/.github/workflows/extract.yml b/.github/workflows/extract.yml index 2c35b02..086a21c 100644 --- a/.github/workflows/extract.yml +++ b/.github/workflows/extract.yml @@ -61,29 +61,22 @@ jobs: git gc --prune=now --aggressive fi - - name: Clone working copy from stripped mirror - run: git clone /tmp/source.git /tmp/source - - name: Rewrite paths with fast-export/fast-import run: | SRC="${{ inputs.source_path }}" TGT="${{ inputs.target_path }}" - cd /tmp/source + cd /tmp/source.git git fast-export --all | awk \ -v src="$SRC" \ -v tgt="$TGT" ' - # Rewrite or drop file modify/delete lines based on path /^[MD] / { - # Extract path (last field, may contain spaces — use sub to split at correct point) line = $0 if (line ~ "^M ") { - # M match(line, /^M [0-9]+ [^ ]+ /) prefix = substr(line, 1, RLENGTH) path = substr(line, RLENGTH + 1) } else { - # D prefix = "D " path = substr(line, 3) } @@ -92,19 +85,16 @@ jobs: sub(src, tgt, path) print prefix path } - # Drop lines for paths outside source_path (siblings left by BFG at deeper levels) next } { print } ' | git fast-import --force --quiet - git checkout HEAD -- . - - name: Push to target repo env: TOKEN: ${{ secrets[format('GE_TGT_{0}', inputs.run_id)] }} run: | HOST=$(echo "${{ inputs.target_repo }}" | sed 's|https://||') - cd /tmp/source + cd /tmp/source.git git remote set-url origin "https://oauth2:${TOKEN}@${HOST}" git push origin "HEAD:${{ inputs.target_branch }}" --force From 046bacb3c63f730dc0915b5f9e95389d0ae00f20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Raimondas=20Rimkevi=C4=8Dius?= Date: Thu, 23 Apr 2026 02:59:55 +0300 Subject: [PATCH 6/7] infra: read node version from package.json engines field --- .github/workflows/lint-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint-build.yml b/.github/workflows/lint-build.yml index 541793a..12cc440 100644 --- a/.github/workflows/lint-build.yml +++ b/.github/workflows/lint-build.yml @@ -14,7 +14,7 @@ jobs: - uses: actions/setup-node@v4 with: - node-version: 20 + node-version-file: package.json cache: npm - name: Install dependencies From 8629c0f8d6470ff5e06d29b66131e03c221a564b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Raimondas=20Rimkevi=C4=8Dius?= Date: Thu, 23 Apr 2026 03:01:01 +0300 Subject: [PATCH 7/7] infra: drop npm cache until lock file exists --- .github/workflows/lint-build.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/lint-build.yml b/.github/workflows/lint-build.yml index 12cc440..2392f4a 100644 --- a/.github/workflows/lint-build.yml +++ b/.github/workflows/lint-build.yml @@ -15,7 +15,6 @@ jobs: - uses: actions/setup-node@v4 with: node-version-file: package.json - cache: npm - name: Install dependencies run: npm install