Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
3852b0d
ci: fork CI pipeline — mirror, aggregate, build-image, enriched + rea…
dnplkndll May 30, 2026
f3ce98b
docs(ledoent): fork roadmap, proof-of-work, upstream-PR feedback temp…
dnplkndll May 30, 2026
571f9b7
ci(aggregate): read merge list from lab repos.yaml (single source)
dnplkndll May 30, 2026
a370579
ci(aggregate): auth lab-repo checkout with AGGREGATE_PUSH_TOKEN
dnplkndll May 30, 2026
0c9abb5
ci(aggregate): use LAB_READ_TOKEN for the private lab-repo checkout
dnplkndll May 30, 2026
c1fca2c
ci(aggregate): persist-credentials false on read-only lab checkout
dnplkndll May 30, 2026
0d3fd82
ci(aggregate): union-merge docsource/modules180-190.rst
dnplkndll May 30, 2026
39b35b8
[CI] test-migration: install google-auth for cloud_storage_google/goo…
dnplkndll Jun 3, 2026
431b538
[CI] mirror-upstream: continue-on-error on drift-issue step (Issues d…
dnplkndll Jun 3, 2026
a0d5112
[CI] aggregate: dedup docsource module rows before force-push (#104)
dnplkndll Jun 3, 2026
cb8c410
[CI] aggregate: trigger build-image via curl instead of gh CLI (#105)
dnplkndll Jun 11, 2026
6af76f2
[CI] test-migration: resolve the google-auth / cloud_storage_google c…
dnplkndll Jun 11, 2026
6c64119
[CI] aggregate: daily scheduled rebuild — catch upstream-merge confli…
dnplkndll Jun 11, 2026
f18d890
[CI] gates: neutralize cloud_storage_google in the plain seed + raise…
dnplkndll Jun 12, 2026
7edc4a6
[CI] test-migration: raise PG lock limit (same _process_end OOM as en…
dnplkndll Jun 14, 2026
30fb5f1
[CI] test-migration: install+createdb+restore in one step (survive pe…
dnplkndll Jun 16, 2026
369029b
[CI] test-migration-enriched: patch l10n_es_edi_verifactu certificate…
dnplkndll Jun 20, 2026
57b048e
[CI] mirror-upstream: tick every 6h instead of daily
dnplkndll Jul 13, 2026
decbc08
ci: run mirror-upstream and aggregate weekly instead of 4x daily
dnplkndll Aug 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
test-requirements.txt merge=union
docsource/modules180-190.rst merge=union
165 changes: 165 additions & 0 deletions .github/workflows/aggregate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
name: Aggregate fork branches

# Builds the throwaway `aggregated` branch = OCA/19.0 + every branch in the
# merge manifest, then force-pushes it. The build image and the lab's
# `make migrate` consume `aggregated`.
#
# SINGLE SOURCE OF TRUTH for the merge list: ledoent/openupgrade-lab `repos.yaml`
# (the `./openupgrade` block). It is NOT duplicated here — this workflow checks
# out the lab repo and reads it. The OpenUpgrade fork's working/migration
# branches stay clean of aggregation config (they are based on pristine
# OCA/19.0 and are upstream-ready). To change which branches aggregate, edit
# `repos.yaml` in the lab repo (and run `make check-fork-model`). See
# `docs/fork-aggregation-model.md` in the lab repo.

on:
push:
branches:
- ledoent
- "19.0-mig-*"
- "19.0-fix-*"
schedule:
# Weekly, Sunday 06:00 UTC — one hour after mirror-upstream, deliberately.
#
# The ordering is the point, not the hour: pushes to clean migration
# branches don't carry this workflow file so they never fire the push
# trigger, and an upstream OCA merge can silently start conflicting with a
# repos.yaml branch (seen with l10n_es / OCA#5646). A scheduled rebuild
# after the mirror surfaces that instead of leaving it for the next manual
# push. Keep this cron strictly later than mirror-upstream's.
#
# Was daily; moved to weekly with the mirror when the fork stopped being an
# active contribution target. The detection window widens from a day to a
# week — acceptable now, and the reason is org-wide CI concurrency rather
# than anything about this workflow. See mirror-upstream.yml.
- cron: "0 6 * * 0"
workflow_dispatch:

permissions:
contents: write

jobs:
aggregate:
runs-on: ubuntu-latest
steps:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.10"

- name: Install git-aggregator
run: pip install git-aggregator==4.1

- name: Configure git identity (gitaggregate needs this before merging)
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"

- name: Checkout lab repo (single source of the merge manifest)
# The aggregate merge list lives ONLY in the lab repo's repos.yaml so the
# OpenUpgrade fork's working/migration branches stay clean of any
# aggregation config (they are based on pristine OCA/19.0). This workflow
# reads that committed repos.yaml — it does not carry its own copy.
uses: actions/checkout@v4
with:
repository: ledoent/openupgrade-lab
ref: main
path: lab
fetch-depth: 1
# openupgrade-lab is private; the default GITHUB_TOKEN is scoped to THIS
# repo only and gets "Repository not found". LAB_READ_TOKEN is a
# fine-grained PAT with READ access to ledoent/openupgrade-lab.
# Create it once:
# gh secret set LAB_READ_TOKEN --repo ledoent/OpenUpgrade
token: ${{ secrets.LAB_READ_TOKEN }}
# persist-credentials defaults to true, which makes checkout configure
# the token for PUSH and fail a read-only PAT with 403 "Write access
# not granted". We only read repos.yaml, so turn it off.
persist-credentials: false

- name: Extract the ./openupgrade block into the aggregate config
# gitaggregate wants a single-repo config; pull just the ./openupgrade
# top-level key out of the lab's multi-repo repos.yaml.
run: |
python3 - <<'PY'
import yaml
full = yaml.safe_load(open("lab/repos.yaml"))
block = full["./openupgrade"]
yaml.safe_dump({"./openupgrade": block}, open("aggregate.yml", "w"),
default_flow_style=False, sort_keys=False)
print(open("aggregate.yml").read())
PY

- name: Run gitaggregate
run: gitaggregate -c aggregate.yml

- name: Dedup docsource module rows
# Each merged migration branch edits docsource/modules180-190.rst to mark
# its modules, so the same module's row recurs once per branch that touches
# it. gitaggregate replays those edits verbatim, leaving duplicate rows that
# make the coverage table unreadable and let a blank row mask a marked one.
# Collapse to first occurrence per module, preferring a marked row over a
# blank, and commit so the force-pushed aggregated tree carries the clean
# table. (Per-branch diffs stay untouched — this lives only on aggregated.)
working-directory: openupgrade
run: |
python3 - <<'PY'
import re
path = "docsource/modules180-190.rst"
row_re = re.compile(r"^\|\s+([a-z][\w.]+)\s+\|([^|]*)\|")
lines = open(path, encoding="utf-8").read().splitlines(keepends=True)
pos, out, dropped = {}, [], 0
for ln in lines:
m = row_re.match(ln)
if not m:
out.append(ln); continue
mod, col2 = m.group(1), m.group(2).strip()
if mod not in pos:
pos[mod] = len(out); out.append(ln)
else:
dropped += 1
if not row_re.match(out[pos[mod]]).group(2).strip() and col2:
out[pos[mod]] = ln
open(path, "w", encoding="utf-8").writelines(out)
print(f"dedup: dropped {dropped} duplicate module row(s)")
PY
if ! git diff --quiet -- docsource/modules180-190.rst; then
git commit -am "[CI] aggregate: dedup docsource module rows"
else
echo "no duplicate rows to collapse"
fi

- name: Force-push aggregated
id: push
working-directory: openupgrade
env:
PUSH_TOKEN: ${{ secrets.AGGREGATE_PUSH_TOKEN || secrets.GITHUB_TOKEN }}
run: |
git remote set-url ledoent "https://x-access-token:${PUSH_TOKEN}@github.com/ledoent/OpenUpgrade.git"
git push --force ledoent HEAD:refs/heads/aggregated
sha=$(git rev-parse HEAD)
echo "Aggregated head: $sha"
echo "sha=$sha" >> "$GITHUB_OUTPUT"

- name: Trigger build-image workflow
# GITHUB_TOKEN-driven branch pushes don't fire downstream workflows
# (loop protection). Use repository_dispatch with a PAT so build-image
# can react. Falls back to no-op if AGGREGATE_PUSH_TOKEN is unset.
# Uses curl, not the gh CLI: the self-hosted runner pool doesn't ship
# gh (the gh api call failed with "command not found"); curl is portable.
env:
DISPATCH_TOKEN: ${{ secrets.AGGREGATE_PUSH_TOKEN }}
SHA: ${{ steps.push.outputs.sha }}
run: |
if [ -z "$DISPATCH_TOKEN" ]; then
echo "AGGREGATE_PUSH_TOKEN not set; skipping repository_dispatch."
echo "Run the build-image workflow manually against the aggregated branch."
exit 0
fi
curl -fsS -X POST \
-H "Authorization: Bearer $DISPATCH_TOKEN" \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"https://api.github.com/repos/${{ github.repository }}/dispatches" \
-d "{\"event_type\":\"aggregated-updated\",\"client_payload\":{\"sha\":\"$SHA\"}}"
echo "Fired repository_dispatch event_type=aggregated-updated"
58 changes: 58 additions & 0 deletions .github/workflows/build-image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Build openupgrade image

# Builds an Odoo 19 image with the aggregated OpenUpgrade tree baked in
# and pushes to the ledoent Zot registry at registry.hz.ledoweb.com.
#
# Auth: basic-auth via the `robot-ci` Zot user.
# Secrets: ZOT_USERNAME (= robot-ci), ZOT_PASSWORD (set on this repo).
#
# Image: registry.hz.ledoweb.com/openupgrade/openupgrade
# Consumed by: openupgrade-lab/docker-compose.yml (odoo-19 service)

on:
push:
branches:
- aggregated
repository_dispatch:
types: [aggregated-updated]
workflow_dispatch:

concurrency:
group: build-image-${{ github.ref }}
cancel-in-progress: true

env:
ZOT_HOST: registry.hz.ledoweb.com
ZOT_IMAGE: registry.hz.ledoweb.com/openupgrade/openupgrade

jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
with:
ref: aggregated
fetch-depth: 1

- uses: docker/login-action@v3
with:
registry: ${{ env.ZOT_HOST }}
username: ${{ secrets.ZOT_USERNAME }}
password: ${{ secrets.ZOT_PASSWORD }}

- uses: docker/setup-buildx-action@v3

- name: Build and push (amd64 only — cluster is amd64; M-series uses Rosetta)
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile.openupgrade
push: true
platforms: linux/amd64
tags: |
${{ env.ZOT_IMAGE }}:latest
${{ env.ZOT_IMAGE }}:${{ github.sha }}
cache-from: type=registry,ref=${{ env.ZOT_IMAGE }}:buildcache
cache-to: type=registry,ref=${{ env.ZOT_IMAGE }}:buildcache,mode=max
5 changes: 5 additions & 0 deletions .github/workflows/documentation-commit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,15 @@ name: Build and commit documentation

on:
push:
branches: ["19.0"]
paths: ["docsource/modules180-190.rst"]

jobs:
documentation-commit:
# Docs publishing only makes sense on OCA upstream (default branch
# publishes to https://oca.github.io/OpenUpgrade/). Forks don't have
# a `documentation` branch — checkout would fail. Restrict to OCA.
if: ${{ github.repository_owner == 'OCA' }}
runs-on: ubuntu-latest
steps:
- name: Check out OpenUpgrade Documentation
Expand Down
80 changes: 80 additions & 0 deletions .github/workflows/mirror-upstream.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
name: Mirror upstream OCA/OpenUpgrade

on:
schedule:
# Weekly, Sunday 05:00 UTC (01:00 US Eastern).
#
# This ran every 6h from 2026-07-13, because Tecnativa-pace upstream
# outran a daily tick and open PRs drifted from their base. That tempo
# was worth it while this fork was an active contribution target. It no
# longer is, and the cost is paid by the whole organisation: mirroring
# force-pushes 19.0, which fires aggregate.yml's push trigger, which
# rebuilds and force-pushes `aggregated`, which runs tests, pre-commit
# and the migration suite. Four ticks a day of that saturates the
# org-wide 20-concurrent-job cap on the Free plan and leaves unrelated
# repositories queueing for tens of minutes.
#
# If contribution restarts, raise this again — but prefer moving the
# heavy jobs onto the self-hosted runners first.
#
# workflow_dispatch below is the escape hatch for a manual catch-up.
- cron: "0 5 * * 0"
workflow_dispatch:

permissions:
contents: write
issues: write

jobs:
mirror:
runs-on: ubuntu-latest
steps:
- name: Checkout fork
uses: actions/checkout@v4
with:
ref: ledoent
fetch-depth: 0
token: ${{ secrets.GIT_PUSH_TOKEN || secrets.GITHUB_TOKEN }}

- name: Fetch upstream
run: |
git remote add upstream https://github.com/OCA/OpenUpgrade.git
git fetch upstream 19.0

- name: Force-push upstream/19.0 to fork's 19.0
run: |
git push origin "upstream/19.0:refs/heads/19.0" --force-with-lease || \
git push origin "upstream/19.0:refs/heads/19.0" --force

- name: Check whether ledoent has drifted from upstream/19.0
id: drift
run: |
# ledoent should be upstream/19.0 + custom-CI commits. If a merge-base
# comparison shows ledoent missing upstream commits, we need a rebase.
if git merge-base --is-ancestor upstream/19.0 ledoent; then
echo "drift=no" >> "$GITHUB_OUTPUT"
echo "ledoent is up to date with upstream/19.0"
else
echo "drift=yes" >> "$GITHUB_OUTPUT"
behind=$(git rev-list --count ledoent..upstream/19.0)
echo "ledoent is behind upstream/19.0 by $behind commits — rebase needed"
fi

- name: Open issue if drift detected
if: steps.drift.outputs.drift == 'yes'
continue-on-error: true
uses: actions/github-script@v7
with:
script: |
const { owner, repo } = context.repo;
const title = 'ledoent branch needs rebase onto upstream/19.0';
const existing = await github.rest.issues.listForRepo({
owner, repo, labels: 'mirror-drift', state: 'open'
});
if (existing.data.length === 0) {
await github.rest.issues.create({
owner, repo, title,
labels: ['mirror-drift'],
body: 'upstream OCA/OpenUpgrade `19.0` has advanced. Rebase `ledoent` onto it and force-push.\n\n```bash\ngit fetch origin\ngit checkout ledoent\ngit rebase origin/19.0\ngit push -f origin ledoent\n```'
});
}
Loading