Sync scripts from ProxmoxVE #24
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Sync scripts from ProxmoxVE | |
| # The application scripts are platform-agnostic and canonical in ProxmoxVE. | |
| # This mirrors them here and rewrites the one line that differs: the bootstrap, | |
| # which must load the engine from core (ProxmoxVE's own misc/build.func is a | |
| # monolith with no Incus backend, so an unmodified copy would not run here). | |
| # | |
| # Only ct/ and install/ are mirrored. vm/ calls qm and pvesm directly, turnkey/ | |
| # is a Proxmox feature, and tools/pve/ is Proxmox host management -- none of | |
| # them are portable. Headers are excluded: they are generated into core, which | |
| # serves them to every script repo. tools/incus/ and json/ are maintained in | |
| # this repository and are never touched by this workflow. | |
| on: | |
| schedule: | |
| - cron: "0 4 * * *" | |
| workflow_dispatch: | |
| inputs: | |
| ref: | |
| description: "ProxmoxVE ref to sync from" | |
| required: false | |
| default: "main" | |
| # Fired by community-scripts/ProxmoxVE on every ct/ or install/ change so the | |
| # mirror tracks upstream within minutes instead of waiting for the daily cron. | |
| repository_dispatch: | |
| types: [proxmoxve-scripts-changed] | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| concurrency: | |
| group: sync-scripts | |
| cancel-in-progress: false | |
| jobs: | |
| sync: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout Incus | |
| uses: actions/checkout@v4 | |
| - name: Checkout ProxmoxVE | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: community-scripts/ProxmoxVE | |
| ref: ${{ github.event.inputs.ref || 'main' }} | |
| path: .upstream | |
| - name: Mirror ct/ and install/ | |
| run: | | |
| set -euo pipefail | |
| rm -rf ct install | |
| cp -r .upstream/ct ct | |
| cp -r .upstream/install install | |
| # Headers live in core, generated for every script repo at once. | |
| rm -rf ct/headers | |
| rm -rf .upstream | |
| - name: Point the bootstrap at core | |
| run: | | |
| set -euo pipefail | |
| python3 - <<'PY' | |
| import glob, re, sys | |
| NEW = '''# Engine comes from community-scripts/core; this repo only ships the scripts. | |
| # A local core checkout wins (COMMUNITY_SCRIPTS_CORE_DIR, else a sibling ../core), | |
| # so a fork or branch of core can be tested without editing this file. | |
| _cs_boot="${COMMUNITY_SCRIPTS_CORE_DIR:-$(dirname "${BASH_SOURCE[0]}")/../../core}/core/build.func" | |
| source "$_cs_boot" 2>/dev/null || source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/core/build.func")''' | |
| # Upstream uses several spellings: raw.githubusercontent or the Gitea | |
| # mirror, curl -fsSL or curl -s. | |
| pat = re.compile(r'source <\(curl -[a-zA-Z]* ?https?://[^)]*/misc/build\.func\)') | |
| # Upstream is migrating to core, so a growing number of scripts already | |
| # carry this bootstrap. Nothing to rewrite there, but they must not be | |
| # reported as unrecognised either -- that failed this job for every | |
| # migrated script. | |
| already = re.compile(r'^_cs_boot=.*core/build\.func', re.M) | |
| # ProxmoxVE pins its own scripts base. Carried over verbatim it would send | |
| # an Incus container to ProxmoxVE for its install/ script -- exactly the | |
| # silent breakage the check below exists to prevent. | |
| pin = re.compile(r'^_CS_DEFAULT_URL=.*\n', re.M) | |
| scripts = sorted(glob.glob("ct/*.sh")) | |
| done, kept, miss = 0, 0, [] | |
| for p in scripts: | |
| s = open(p, encoding="utf-8", newline="").read() | |
| s, dropped = pin.subn("", s) | |
| s2, n = pat.subn(NEW, s, count=1) | |
| if n: | |
| open(p, "w", encoding="utf-8", newline="").write(s2) | |
| done += 1 | |
| elif already.search(s): | |
| if dropped: | |
| open(p, "w", encoding="utf-8", newline="").write(s) | |
| kept += 1 | |
| else: | |
| miss.append(p) | |
| print(f"rewritten {done}, already on core {kept}, of {len(scripts)}") | |
| if miss: | |
| # A script we cannot rewrite would silently load ProxmoxVE's | |
| # monolith and fail on an Incus host. Fail loudly instead. | |
| print("::error::bootstrap not recognised in:", ", ".join(miss)) | |
| sys.exit(1) | |
| PY | |
| - name: Verify no script still loads the ProxmoxVE engine | |
| run: | | |
| set -euo pipefail | |
| if grep -rln "ProxmoxVE.*/misc/build\.func" ct/ install/; then | |
| echo "::error::scripts above still point at the ProxmoxVE engine" | |
| exit 1 | |
| fi | |
| fail=0 | |
| for f in ct/*.sh install/*.sh; do | |
| bash -n "$f" || { echo "::error::syntax error in $f"; fail=1; } | |
| done | |
| exit $fail | |
| - name: Open a pull request | |
| id: cpr | |
| uses: peter-evans/create-pull-request@v6 | |
| with: | |
| branch: sync/proxmoxve-scripts | |
| title: "Sync scripts from ProxmoxVE" | |
| commit-message: | | |
| Sync ct/ and install/ from ProxmoxVE | |
| Mirrored automatically. The bootstrap line is rewritten to load the | |
| engine from core, which is the only difference to upstream. | |
| body: | | |
| Automated mirror of `ct/` and `install/` from | |
| [ProxmoxVE](https://github.com/community-scripts/ProxmoxVE). | |
| The only change applied on top is the bootstrap line, which loads the | |
| engine from [core](https://github.com/community-scripts/core) so the | |
| host backend is detected at runtime. | |
| Application fixes belong upstream — anything committed here by hand is | |
| overwritten by the next run. | |
| delete-branch: true | |
| # A sync PR only ever mirrors upstream, so it is approved and merged | |
| # automatically. A separate app identity does the approval because a PR | |
| # cannot be approved by the identity that opened it. | |
| - name: Generate token for approve + merge | |
| id: merge-token | |
| if: steps.cpr.outputs.pull-request-number | |
| uses: actions/create-github-app-token@v3 | |
| with: | |
| app-id: ${{ secrets.APP_ID_APPROVE_AND_MERGE }} | |
| private-key: ${{ secrets.APP_KEY_APPROVE_AND_MERGE }} | |
| - name: Approve and merge the sync PR | |
| if: steps.cpr.outputs.pull-request-number | |
| env: | |
| GH_TOKEN: ${{ steps.merge-token.outputs.token }} | |
| PR_NUMBER: ${{ steps.cpr.outputs.pull-request-number }} | |
| run: | | |
| set -euo pipefail | |
| gh pr review "$PR_NUMBER" --approve | |
| gh pr merge "$PR_NUMBER" --squash --admin --delete-branch | |
| - name: Ask core to regenerate headers | |
| if: success() | |
| continue-on-error: true | |
| env: | |
| TOKEN: ${{ secrets.CORE_DISPATCH_TOKEN }} | |
| run: | | |
| # Optional: core polls every 6h anyway. With a token set, a new | |
| # script gets its header in seconds instead of hours. | |
| if [ -z "$TOKEN" ]; then | |
| echo "CORE_DISPATCH_TOKEN not set - core picks this up on its next poll" | |
| exit 0 | |
| fi | |
| curl -fsS -X POST -H "Accept: application/vnd.github+json" -H "Authorization: Bearer $TOKEN" https://api.github.com/repos/community-scripts/core/dispatches -d '{"event_type":"scripts-changed"}' |