Files
odoo-addons/.gitea/workflows/seed-compat.yml
compat-seeder adfdf38fb0
All checks were successful
addon-qualify / qualify (push) Successful in 12s
fix(compat): commit email must match git_admin's Gitea record (gitea@local.domain)
2026-05-10 17:05:11 +03:00

182 lines
8.0 KiB
YAML

# Pillar 2 / Phase 4 — Cold-start compat-matrix seed.
#
# This file is the SOURCE-OF-TRUTH copy. The deployed copy lives in
# the Gitea repo at:
#
# odoo-tower/odoo-addons (branch `compat-bootstrap`)
# └── .gitea/workflows/seed-compat.yml
#
# Why a dedicated branch instead of `main`/`18.0`/`19.0`: the addon
# code lives on per-Odoo-major branches. The cold-start snapshot is
# orthogonal data — keeping it on its own branch lets Tower fetch
# from one stable place and lets the cron commit without touching
# addon source.
#
# Schedule: daily 03:00 UTC. The emitter computes a content-hash
# stampId, so identical results across nights produce no commit
# (git diff is empty → push is skipped). When the catalog drifts
# (a new addon, a fix, a regression), one commit lands and Tower's
# next 24h tick replays it.
#
# Deployment (one-shot, manual until we mass-publish workflows):
#
# git -C /path/to/odoo-addons checkout -b compat-bootstrap origin/18.0
# mkdir -p .gitea/workflows compat-bootstrap
# cp infrastructure/gitea-actions/workflows/seed-compat.yml \
# /path/to/odoo-addons/.gitea/workflows/
# cp scripts/qualify-addon.py scripts/emit-compat-bootstrap.py \
# /path/to/odoo-addons/scripts/
# git -C /path/to/odoo-addons add .gitea scripts compat-bootstrap
# git -C /path/to/odoo-addons commit -m "feat(compat): seed bootstrap workflow"
# git -C /path/to/odoo-addons push -u origin compat-bootstrap
name: Cold-start compat seed
on:
schedule:
- cron: '0 3 * * *' # nightly 03:00 UTC
workflow_dispatch: {} # manual trigger from Gitea UI
jobs:
seed:
runs-on: ubuntu-latest
steps:
- name: Checkout compat-bootstrap branch
uses: actions/checkout@v4
with:
ref: compat-bootstrap
fetch-depth: 1
- name: Materialise per-major addon trees
run: |
set -euo pipefail
# Detached worktrees keep each version branch's tree independent
# so the qualifier sees a clean addon root with no cross-branch
# contamination. Cleaned up in the final step.
for major in 18.0 19.0; do
git fetch --depth=1 origin "$major"
git worktree add --detach "addons-$major" "origin/$major"
ls -1 "addons-$major/addons" 2>/dev/null | head -5 || true
done
- name: Run qualifier + emit bootstrap snapshot per major
run: |
set -euo pipefail
mkdir -p compat-bootstrap/per-major
for major in 18.0 19.0; do
# qualify-addon.py is vendored into each version branch under
# .gitea/qualify-addon.py by Pillar 1; reuse that copy so static-
# lint logic stays in lockstep with what addon-qualify.yml runs
# on push.
python3 scripts/emit-compat-bootstrap.py \
--addons-root "addons-$major/addons" \
--qualifier "addons-$major/.gitea/qualify-addon.py" \
--output "compat-bootstrap/per-major/$major.json" \
--pg-by-major '{"18.0":"16","19.0":"17"}' \
--source "qualify-addon-v1"
done
- name: Merge per-major snapshots into seeded-ci.json
run: |
set -euo pipefail
python3 - <<'PY'
import hashlib, json, pathlib
rows = []
for p in sorted(pathlib.Path('compat-bootstrap/per-major').glob('*.json')):
rows.extend(json.loads(p.read_text())['rows'])
rows.sort(key=lambda r: (r['addonCode'], r['addonVersion'],
r['odooMajor'], r['postgresMajor']))
canonical = json.dumps(rows, sort_keys=True, separators=(',', ':'))
stamp = 'sha256:' + hashlib.sha256(canonical.encode('utf-8')).hexdigest()
out = {
'stampId': stamp,
'schemaVersion': 1,
'source': 'qualify-addon-v1',
'rows': rows,
}
pathlib.Path('compat-bootstrap/seeded-ci.json').write_text(
json.dumps(out, indent=2) + '\n')
print(f'merged {len(rows)} rows; stamp={stamp}')
PY
- name: Install cosign
run: |
set -euo pipefail
if ! command -v cosign >/dev/null 2>&1; then
curl -sSL -o /usr/local/bin/cosign \
https://github.com/sigstore/cosign/releases/download/v2.4.1/cosign-linux-amd64
chmod +x /usr/local/bin/cosign
fi
cosign version | head -3
- name: Sign seeded-ci.json (Phase 4.1)
env:
COSIGN_PRIVATE_KEY: ${{ secrets.COMPAT_SIGNING_KEY }}
COSIGN_PASSWORD: ${{ secrets.COMPAT_SIGNING_PASSWORD }}
run: |
set -euo pipefail
if [ ! -f compat-bootstrap/seeded-ci.json ]; then
echo "ERROR: seeded-ci.json was not produced; nothing to sign"
exit 1
fi
# cosign sign-blob with --key env://VAR reads the private key
# PEM from the named env var; the file/stdout split keeps the
# raw key out of process args (where it'd show up in `ps`).
cosign sign-blob --yes \
--key env://COSIGN_PRIVATE_KEY \
--output-signature compat-bootstrap/seeded-ci.json.sig \
compat-bootstrap/seeded-ci.json
# Sanity: verify with the public copy on disk before we push.
# Catches a key-rotation mismatch while we still can.
# (skips the Sigstore tlog — we're not relying on Rekor)
if [ -f compat-bootstrap/cosign.pub ]; then
cosign verify-blob --insecure-ignore-tlog \
--key compat-bootstrap/cosign.pub \
--signature compat-bootstrap/seeded-ci.json.sig \
compat-bootstrap/seeded-ci.json
fi
echo "signed: $(wc -c < compat-bootstrap/seeded-ci.json.sig) bytes"
- name: Commit + push if content changed
env:
GIT_USER_TOKEN: ${{ secrets.COMPAT_PUSH_TOKEN }}
run: |
set -euo pipefail
# Discard the per-major scratch files + addon worktrees; only
# the consolidated snapshot + sig are part of the canonical state.
for major in 18.0 19.0; do git worktree remove --force "addons-$major" || true; done
rm -rf compat-bootstrap/per-major
# Author identity must match a real Gitea user — the
# branch-protection pre-receive hook calls GetUserByEmail
# against the committer and rejects with "Internal Server
# Error" (uid=-2) when no match exists. The COMPAT_PUSH_TOKEN
# belongs to git_admin, whose canonical Gitea email is
# `gitea@local.domain` (the install default — NOT the address
# in .credentials.md, which never made it onto the user
# record). Commit + push under that identity for a clean
# audit trail.
git config user.email "gitea@local.domain"
git config user.name "git_admin"
if [ ! -f compat-bootstrap/seeded-ci.json ]; then
echo "ERROR: seeded-ci.json was not produced; aborting"
exit 1
fi
# Stage both the JSON and its sig. The .sig changes whenever
# the JSON changes (different SHA256 → different signature),
# so a content-stable run will produce a no-op diff for both.
git add compat-bootstrap/seeded-ci.json compat-bootstrap/seeded-ci.json.sig
if git diff --cached --quiet -- compat-bootstrap/seeded-ci.json compat-bootstrap/seeded-ci.json.sig; then
echo "no content change in seeded-ci.json; nothing to commit"
exit 0
fi
stamp=$(python3 -c "import json,sys; print(json.load(open('compat-bootstrap/seeded-ci.json'))['stampId'])")
git commit -m "chore(compat): refresh cold-start seed (${stamp})"
# Push via token. COMPAT_PUSH_TOKEN belongs to git_admin and
# is whitelisted on the compat-bootstrap branch protection.
remote="https://git_admin:${GIT_USER_TOKEN}@git.odoosky.org/odoo-tower/odoo-addons.git"
git push "$remote" HEAD:compat-bootstrap