From 0490905766834ee2adffaa9716c99a5526cc0d85 Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Thu, 20 Aug 2026 14:19:16 -0600 Subject: [PATCH 1/4] ADFA-5186 docs(r2): add ADR for publishing plugin .cgp to Cloudflare R2 --- docs/R2/ADR-5186-plugins-cgp-to-r2.md | 37 +++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 docs/R2/ADR-5186-plugins-cgp-to-r2.md diff --git a/docs/R2/ADR-5186-plugins-cgp-to-r2.md b/docs/R2/ADR-5186-plugins-cgp-to-r2.md new file mode 100644 index 00000000..8784a477 --- /dev/null +++ b/docs/R2/ADR-5186-plugins-cgp-to-r2.md @@ -0,0 +1,37 @@ +# ADR-5186 — Publish plugin `.cgp` to Cloudflare R2 + +**Status:** Proposed · **Ticket:** ADFA-5186 +**Scope:** the storage steps of `update-libs.yml`. Not the Gallery, not dev-assets, not `build-plugins.yml`. + +## Context + +`update-libs.yml` builds each plugin's `.cgp` and then keeps a copy on GitHub in two places (an inter-job artifact and GitHub Release assets) while also shipping it to GreenGeeks by scp. An R2 upload step already exists in the deploy job — additive and guarded, so it skips cleanly until the credentials are set. The remaining work is to make R2 the store of record and stop keeping the `.cgp` on GitHub, without a destructive one-step cutover. + +## Decision — staged, additive first + +1. **Now (additive).** After the existing R2 upload, verify each object landed at the expected size — the R2 equivalent of the GreenGeeks MD5 check the workflow already runs. Nothing else changes; both backends keep publishing, so a run today behaves exactly as it does now. +2. **After R2 is validated.** Drop the `.cgp` assets from the GitHub Release (the tag and note stay for traceability). This is the step that actually stops the GitHub-side storage. +3. **Later, and dependent.** The GreenGeeks scp/MD5 steps retire only once the consumers — the website `/flags/plugins/` links and the app — point at R2. That repoint is outside this ticket, so scp stays for now. + +Config is reused as-is: bucket `apk-repo`, prefix `plugins/`, the existing `CLOUDFLARE_*` variables and secret. No new credentials. No `.cgt` is built in CI today; the same prefix serves it if template publishing arrives later. + +## Consequences + +- The published `.cgp` gains a verified store on R2, and GitHub stops being a distribution store on the publish path once step 2 lands. +- The change is reversible per step: R2 stays additive until it verifies, and each retirement is its own small PR. +- The headline artifact-quota problem is **not** solved here — it lives in `build-plugins.yml`, which this ticket leaves untouched (see below). This is a deliberate, recorded trade-off. + +## Alternatives considered + +- **Move `build-plugins.yml` to R2 too.** Rejected for now: it changes the run-summary download UX and mixes ad-hoc dev builds into the published store. +- **Keep `.cgp` on the GitHub Release as the store.** Rejected as the end state, but kept transitionally until R2 is validated. + +## Out of scope + +- `build-plugins.yml` — unchanged. It is the larger quota consumer (each `.cgp` is uploaded twice: a bundle plus a per-plugin artifact). If quota becomes the binding constraint, the follow-up is to stop that double upload — a separate decision, not this ticket. +- dev-assets R2 migration (belongs to the GreenGeeks-retirement effort); the CoGo Gallery; the CoGo APK path (already on R2). + +## Open questions + +- Dedicated R2 bucket vs. a prefix inside `apk-repo` (blast radius / lifecycle). +- Reconciling the `R2_*` vs `CLOUDFLARE_*` variable names (same values, different names). From e6af55d98d5ee98069b10bb11bc545a8ffd3de18 Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Mon, 24 Aug 2026 13:33:51 -0600 Subject: [PATCH 2/4] =?UTF-8?q?ADFA-5186=20docs(r2):=20revise=20ADR=20per?= =?UTF-8?q?=20review=20=E2=80=94=20R2-only=20cutover,=20build-plugins=20in?= =?UTF-8?q?=20scope?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Applies Hal's review on PR #74: - R2 (bucket "addons") is the only plugin store; done in one PR, not staged. - build-plugins.yml is in scope and changes in sync (its double .cgp upload is the artifact-quota consumer). - Retire the GreenGeeks scp publish; keep the GitHub Release (separate quota). - Storage layout: bucket "addons", prefix plugins/, public URL https://addons.appdevforall.org/plugins/.cgp. - Close both open questions (bucket decided; drop the R2_/CLOUDFLARE_ naming Q). --- docs/R2/ADR-5186-plugins-cgp-to-r2.md | 42 +++++++++++++++------------ 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/docs/R2/ADR-5186-plugins-cgp-to-r2.md b/docs/R2/ADR-5186-plugins-cgp-to-r2.md index 8784a477..4c0f01a6 100644 --- a/docs/R2/ADR-5186-plugins-cgp-to-r2.md +++ b/docs/R2/ADR-5186-plugins-cgp-to-r2.md @@ -1,37 +1,41 @@ # ADR-5186 — Publish plugin `.cgp` to Cloudflare R2 **Status:** Proposed · **Ticket:** ADFA-5186 -**Scope:** the storage steps of `update-libs.yml`. Not the Gallery, not dev-assets, not `build-plugins.yml`. +**Scope:** the storage steps of `update-libs.yml` **and** `build-plugins.yml`. Not the CoGo Gallery, not dev-assets, not the CoGo APK path (already on R2). ## Context -`update-libs.yml` builds each plugin's `.cgp` and then keeps a copy on GitHub in two places (an inter-job artifact and GitHub Release assets) while also shipping it to GreenGeeks by scp. An R2 upload step already exists in the deploy job — additive and guarded, so it skips cleanly until the credentials are set. The remaining work is to make R2 the store of record and stop keeping the `.cgp` on GitHub, without a destructive one-step cutover. +Two workflows put a plugin's `.cgp` on GitHub. `update-libs.yml` builds each `.cgp`, keeps it in a GitHub Release and an inter-job artifact, and also ships it to GreenGeeks by scp. `build-plugins.yml` uploads each `.cgp` twice as run artifacts (a bundle plus a per-plugin artifact). A guarded R2 upload step already exists in `update-libs.yml`'s deploy job, so R2 is reachable today — it just is not yet the store of record. -## Decision — staged, additive first +The binding problem is the GitHub **artifact** quota: a couple of very large plugins nearly exhaust it in a single run, and `build-plugins.yml`'s double upload is the largest consumer. The GitHub **Release** draws on a separate quota and is not the problem. -1. **Now (additive).** After the existing R2 upload, verify each object landed at the expected size — the R2 equivalent of the GreenGeeks MD5 check the workflow already runs. Nothing else changes; both backends keep publishing, so a run today behaves exactly as it does now. -2. **After R2 is validated.** Drop the `.cgp` assets from the GitHub Release (the tag and note stay for traceability). This is the step that actually stops the GitHub-side storage. -3. **Later, and dependent.** The GreenGeeks scp/MD5 steps retire only once the consumers — the website `/flags/plugins/` links and the app — point at R2. That repoint is outside this ticket, so scp stays for now. +## Decision — one cutover to R2 as the only plugin store -Config is reused as-is: bucket `apk-repo`, prefix `plugins/`, the existing `CLOUDFLARE_*` variables and secret. No new credentials. No `.cgt` is built in CI today; the same prefix serves it if template publishing arrives later. +Cloudflare R2 becomes the single distribution store for plugin `.cgp`, in one change — no stacked PRs, no staged "additive-only" run: + +1. **R2 is the store of record.** Upload each `.cgp` to R2 and verify each object landed at the expected size (the R2 equivalent of the GreenGeeks MD5 check the workflow runs today). +2. **Stop the GitHub artifact uploads** that consume the quota — the inter-job artifact in `update-libs.yml` and the double `.cgp` upload in `build-plugins.yml`. This is why `build-plugins.yml` is in scope: it must change in sync, or the quota problem remains. +3. **Retire the GreenGeeks scp/MD5 publish.** R2 is the only backend; consumers are pointed at the R2 URL in the same change (below), so there is nothing to wait for. +4. **Keep the GitHub Release.** Its `.cgp` assets stay for traceability — a different quota from artifacts, so it is not the constraint. + +## Storage layout + +- Dedicated bucket **`addons`** (not `apk-repo`). +- Prefix **`plugins/`** now; the same bucket later gains `templates/`, `snippets/`, and `code-actions/` (a prefix is a folder within the bucket). +- Public retrieval URL: `https://addons.appdevforall.org/plugins/.cgp`. +- Reuses the existing Cloudflare credentials and secret — no new credentials. ## Consequences -- The published `.cgp` gains a verified store on R2, and GitHub stops being a distribution store on the publish path once step 2 lands. -- The change is reversible per step: R2 stays additive until it verifies, and each retirement is its own small PR. -- The headline artifact-quota problem is **not** solved here — it lives in `build-plugins.yml`, which this ticket leaves untouched (see below). This is a deliberate, recorded trade-off. +- Plugin `.cgp` has one verified store (R2 `addons`) at a stable public URL; GitHub and GreenGeeks stop being distribution stores on the publish path. +- The artifact-quota problem is actually solved, because `build-plugins.yml`'s double upload is removed in the same change. +- Consumers — the website `/flags/plugins/` links and the app — are switched to the `addons.appdevforall.org` URL in sync with this change. The repoint happens now, not later. +- The GitHub Release remains for traceability; a rollback means re-enabling the previous publish steps. ## Alternatives considered -- **Move `build-plugins.yml` to R2 too.** Rejected for now: it changes the run-summary download UX and mixes ad-hoc dev builds into the published store. -- **Keep `.cgp` on the GitHub Release as the store.** Rejected as the end state, but kept transitionally until R2 is validated. +- **Stage it (keep both backends, retire GitHub/GreenGeeks in later PRs).** Rejected: the intended behavior is R2-only, and the team wants that now in one PR rather than a sequence of transitional states. ## Out of scope -- `build-plugins.yml` — unchanged. It is the larger quota consumer (each `.cgp` is uploaded twice: a bundle plus a per-plugin artifact). If quota becomes the binding constraint, the follow-up is to stop that double upload — a separate decision, not this ticket. -- dev-assets R2 migration (belongs to the GreenGeeks-retirement effort); the CoGo Gallery; the CoGo APK path (already on R2). - -## Open questions - -- Dedicated R2 bucket vs. a prefix inside `apk-repo` (blast radius / lifecycle). -- Reconciling the `R2_*` vs `CLOUDFLARE_*` variable names (same values, different names). +- The CoGo Gallery, the dev-assets R2 migration, and the CoGo APK path (this last one already on R2). From 582b7aec8974d5870cc7f81902240d582e5b319e Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Mon, 24 Aug 2026 14:19:57 -0600 Subject: [PATCH 3/4] ADFA-5186 docs(r2): drop phantom 'guarded R2' + apk-repo references (review) --- docs/R2/ADR-5186-plugins-cgp-to-r2.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/R2/ADR-5186-plugins-cgp-to-r2.md b/docs/R2/ADR-5186-plugins-cgp-to-r2.md index 4c0f01a6..d639867d 100644 --- a/docs/R2/ADR-5186-plugins-cgp-to-r2.md +++ b/docs/R2/ADR-5186-plugins-cgp-to-r2.md @@ -5,7 +5,7 @@ ## Context -Two workflows put a plugin's `.cgp` on GitHub. `update-libs.yml` builds each `.cgp`, keeps it in a GitHub Release and an inter-job artifact, and also ships it to GreenGeeks by scp. `build-plugins.yml` uploads each `.cgp` twice as run artifacts (a bundle plus a per-plugin artifact). A guarded R2 upload step already exists in `update-libs.yml`'s deploy job, so R2 is reachable today — it just is not yet the store of record. +Two workflows put a plugin's `.cgp` on GitHub. `update-libs.yml` builds each `.cgp`, keeps it in a GitHub Release and an inter-job artifact, and also ships it to GreenGeeks by scp. `build-plugins.yml` uploads each `.cgp` twice as run artifacts (a bundle plus a per-plugin artifact). The binding problem is the GitHub **artifact** quota: a couple of very large plugins nearly exhaust it in a single run, and `build-plugins.yml`'s double upload is the largest consumer. The GitHub **Release** draws on a separate quota and is not the problem. @@ -20,7 +20,7 @@ Cloudflare R2 becomes the single distribution store for plugin `.cgp`, in one ch ## Storage layout -- Dedicated bucket **`addons`** (not `apk-repo`). +- Dedicated bucket **`addons`**. - Prefix **`plugins/`** now; the same bucket later gains `templates/`, `snippets/`, and `code-actions/` (a prefix is a folder within the bucket). - Public retrieval URL: `https://addons.appdevforall.org/plugins/.cgp`. - Reuses the existing Cloudflare credentials and secret — no new credentials. From 5355d5a93715d840c6eeef8250c0a32fae0b55db Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Mon, 24 Aug 2026 17:18:32 -0600 Subject: [PATCH 4/4] ADFA-5186 docs(r2): record open decisions from reviewing build output --- docs/R2/ADR-5186-plugins-cgp-to-r2.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/docs/R2/ADR-5186-plugins-cgp-to-r2.md b/docs/R2/ADR-5186-plugins-cgp-to-r2.md index d639867d..190bd4c4 100644 --- a/docs/R2/ADR-5186-plugins-cgp-to-r2.md +++ b/docs/R2/ADR-5186-plugins-cgp-to-r2.md @@ -13,8 +13,8 @@ The binding problem is the GitHub **artifact** quota: a couple of very large plu Cloudflare R2 becomes the single distribution store for plugin `.cgp`, in one change — no stacked PRs, no staged "additive-only" run: -1. **R2 is the store of record.** Upload each `.cgp` to R2 and verify each object landed at the expected size (the R2 equivalent of the GreenGeeks MD5 check the workflow runs today). -2. **Stop the GitHub artifact uploads** that consume the quota — the inter-job artifact in `update-libs.yml` and the double `.cgp` upload in `build-plugins.yml`. This is why `build-plugins.yml` is in scope: it must change in sync, or the quota problem remains. +1. **R2 is the store of record.** Upload each `.cgp` to R2 and verify each object landed at the expected size (the R2 equivalent of the GreenGeeks MD5 check the workflow runs today). Because R2 is now the only backend, a missing R2 credential **fails** the run rather than skipping silently. +2. **Stop the GitHub artifact uploads** that consume the quota — the inter-job artifact in `update-libs.yml` and the double `.cgp` upload in `build-plugins.yml`. This is why `build-plugins.yml` is in scope: it must change in sync, or the quota problem remains. Defining how it leaves the quota without stepping on production builds is the open decision below. 3. **Retire the GreenGeeks scp/MD5 publish.** R2 is the only backend; consumers are pointed at the R2 URL in the same change (below), so there is nothing to wait for. 4. **Keep the GitHub Release.** Its `.cgp` assets stay for traceability — a different quota from artifacts, so it is not the constraint. @@ -23,12 +23,14 @@ Cloudflare R2 becomes the single distribution store for plugin `.cgp`, in one ch - Dedicated bucket **`addons`**. - Prefix **`plugins/`** now; the same bucket later gains `templates/`, `snippets/`, and `code-actions/` (a prefix is a folder within the bucket). - Public retrieval URL: `https://addons.appdevforall.org/plugins/.cgp`. +- **Object key** = the plugin's canonical website filename (the name `update-libs.yml` already normalizes to); that is the `` above. +- Publishing **overwrites** the key — latest wins, one current object per plugin. R2 keeps no version history; the versioned history lives in the GitHub Release (useful if necessary). - Reuses the existing Cloudflare credentials and secret — no new credentials. ## Consequences - Plugin `.cgp` has one verified store (R2 `addons`) at a stable public URL; GitHub and GreenGeeks stop being distribution stores on the publish path. -- The artifact-quota problem is actually solved, because `build-plugins.yml`'s double upload is removed in the same change. +- The artifact-quota problem is actually solved: neither workflow keeps `.cgp` in GitHub artifact storage any more. - Consumers — the website `/flags/plugins/` links and the app — are switched to the `addons.appdevforall.org` URL in sync with this change. The repoint happens now, not later. - The GitHub Release remains for traceability; a rollback means re-enabling the previous publish steps. @@ -36,6 +38,13 @@ Cloudflare R2 becomes the single distribution store for plugin `.cgp`, in one ch - **Stage it (keep both backends, retire GitHub/GreenGeeks in later PRs).** Rejected: the intended behavior is R2-only, and the team wants that now in one PR rather than a sequence of transitional states. +## Open decision — build-plugins presentation and cleanup + +Getting `build-plugins.yml`'s `.cgp` off GitHub artifact storage is settled; it uploads to R2 like `update-libs.yml`. It builds arbitrary CodeOnTheGo refs (a single plugin or all) and publishes nothing to users, so two things remain to decide: + +- **Presentation.** Preview builds must not overwrite the production `plugins/` keys — a separate prefix (e.g. `plugins-preview/`). +- **Cleanup (optional).** A new build overwrites the previous one, so active plugins do not pile up. Stale objects can be removed by date — an R2 lifecycle rule, or a scheduled workflow. Do it in a workflow, or leave it out of scope. + ## Out of scope - The CoGo Gallery, the dev-assets R2 migration, and the CoGo APK path (this last one already on R2).