From 6686034f13e47a575982ebc3e2e2d4acfabd4d7e Mon Sep 17 00:00:00 2001 From: Peter Date: Fri, 3 Jul 2026 16:13:50 +0800 Subject: [PATCH 1/5] ci(release): auto-sync core/wren lock after wren-core-py publish When wren-core-py is published to PyPI, core/wren still pins the previous engine in its pyproject floor and uv.lock. Add a sync-wren-core-py-lock reusable workflow, called from release-please after publish-wren-core-py succeeds, that bumps the wren-core-py floor, relocks core/wren, verifies the resolved version, and opens a PR for review. Automates the manual downstream bump previously done by hand (e.g. #2385). --- .github/workflows/release-please.yml | 13 +++ .github/workflows/sync-wren-core-py-lock.yml | 100 +++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 .github/workflows/sync-wren-core-py-lock.yml diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index b067384f6f..3f39710ee1 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -46,6 +46,19 @@ jobs: contents: read id-token: write + # After wren-core-py is published, relock the downstream core/wren SDK against + # the new engine binding and open a PR. Runs only if publish-wren-core-py + # succeeded, so we never bump to a version that failed to publish. + sync-wren-core-py-lock: + needs: [release-please, publish-wren-core-py] + if: needs.release-please.outputs['wren-core-py--release_created'] == 'true' + uses: ./.github/workflows/sync-wren-core-py-lock.yml + with: + version: ${{ needs.release-please.outputs['wren-core-py--version'] }} + permissions: + contents: write + pull-requests: write + publish-wren: needs: release-please if: needs.release-please.outputs['wren--release_created'] == 'true' diff --git a/.github/workflows/sync-wren-core-py-lock.yml b/.github/workflows/sync-wren-core-py-lock.yml new file mode 100644 index 0000000000..17483e8c5a --- /dev/null +++ b/.github/workflows/sync-wren-core-py-lock.yml @@ -0,0 +1,100 @@ +name: Sync core/wren lock after wren-core-py release + +# After wren-core-py publishes to PyPI, core/wren still pins the old engine in +# its pyproject floor and uv.lock (release-please extra-files only touch a +# component's own dir). Relock core/wren against the release and open a PR. +# Called after publish-wren-core-py succeeds, so a failed publish is never bumped. + +on: + workflow_call: + inputs: + version: + description: "Released wren-core-py version (e.g. 0.7.1)" + required: true + type: string + +permissions: + contents: write + pull-requests: write + +jobs: + sync-lock: + if: ${{ github.repository == 'Canner/WrenAI' }} + runs-on: ubuntu-latest + env: + VERSION: ${{ inputs.version }} + steps: + - name: Validate version + run: | + # Guard a malformed version out of the dep edit, branch name, and PR. + if [[ ! "${VERSION}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(rc[0-9]+)?$ ]]; then + echo "::error::Unsupported version format: ${VERSION}. Expected X.Y.Z or X.Y.ZrcN." + exit 1 + fi + + - name: Checkout main + uses: actions/checkout@v4 + with: + # publish takes minutes; main may have moved. Base the PR on main tip. + ref: main + + - uses: astral-sh/setup-uv@v4 + + - name: Bump floor and relock core/wren against published wren-core-py + working-directory: core/wren + run: | + # uv add rewrites the pyproject floor in place and relocks in one step; + # --no-sync skips the heavy env. Retry for PyPI index lag after publish. + for attempt in 1 2 3 4 5; do + if uv add --no-sync "wren-core-py>=${VERSION}"; then + exit 0 + fi + echo "uv add attempt ${attempt} failed; retrying after PyPI propagation delay" + sleep 20 + done + echo "::error::uv add did not succeed after retries" + exit 1 + + - name: Verify lock resolved to the released version + run: | + locked=$(awk -F'"' '/^name = "wren-core-py"$/{getline; print $2}' core/wren/uv.lock) + if [[ "${locked}" != "${VERSION}" ]]; then + echo "::error::core/wren/uv.lock resolved wren-core-py ${locked:-}, expected ${VERSION}" + exit 1 + fi + echo "core/wren/uv.lock resolved wren-core-py ${locked}" + + - name: Validate lockfile is consistent + working-directory: core/wren + run: uv lock --check + + - name: Detect changes + id: diff + run: | + if git diff --quiet -- core/wren/pyproject.toml core/wren/uv.lock; then + echo "changed=false" >> "$GITHUB_OUTPUT" + else + echo "changed=true" >> "$GITHUB_OUTPUT" + fi + + - name: Open sync PR + if: steps.diff.outputs.changed == 'true' + env: + GH_TOKEN: ${{ github.token }} + run: | + BRANCH="chore/sync-wren-core-py-${VERSION}" + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git checkout -b "${BRANCH}" + git add core/wren/pyproject.toml core/wren/uv.lock + git commit -m "chore(wren): bump wren-core-py to ${VERSION}" + git push --force origin "${BRANCH}" + if gh pr view "${BRANCH}" --json number >/dev/null 2>&1; then + echo "PR for ${BRANCH} already exists; branch updated." + else + gh pr create \ + --base main \ + --head "${BRANCH}" \ + --title "chore(wren): bump wren-core-py to ${VERSION}" \ + --body "Automated follow-up after \`wren-core-py\` ${VERSION} was published to PyPI. Relocks \`core/wren\` against the new engine binding. Please review and merge." + fi From 862a2852eb384f327f5f930f8a0814d0ed9774f7 Mon Sep 17 00:00:00 2001 From: Peter Date: Fri, 3 Jul 2026 16:47:11 +0800 Subject: [PATCH 2/5] ci(release): add manual trigger to the wren-core-py lock sync Add a workflow_dispatch trigger so the sync can be re-run by hand when the automatic run after publish fails or is skipped. The version input is optional: left blank it falls back to the wren-core-py version tracked in .release-please-manifest.json, so a manual run needs no typing. This mirrors rc-release.yml, where the version override is optional and auto-derived. --- .github/workflows/sync-wren-core-py-lock.yml | 33 ++++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/.github/workflows/sync-wren-core-py-lock.yml b/.github/workflows/sync-wren-core-py-lock.yml index 17483e8c5a..2124831d70 100644 --- a/.github/workflows/sync-wren-core-py-lock.yml +++ b/.github/workflows/sync-wren-core-py-lock.yml @@ -12,6 +12,14 @@ on: description: "Released wren-core-py version (e.g. 0.7.1)" required: true type: string + # Manual fallback: re-run the sync if the automatic run after publish failed + # or was skipped. Leave version blank to use the release tracked in the repo. + workflow_dispatch: + inputs: + version: + description: "wren-core-py version to sync to; blank = .release-please-manifest.json" + required: false + type: string permissions: contents: write @@ -21,23 +29,28 @@ jobs: sync-lock: if: ${{ github.repository == 'Canner/WrenAI' }} runs-on: ubuntu-latest - env: - VERSION: ${{ inputs.version }} steps: - - name: Validate version - run: | - # Guard a malformed version out of the dep edit, branch name, and PR. - if [[ ! "${VERSION}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(rc[0-9]+)?$ ]]; then - echo "::error::Unsupported version format: ${VERSION}. Expected X.Y.Z or X.Y.ZrcN." - exit 1 - fi - - name: Checkout main uses: actions/checkout@v4 with: # publish takes minutes; main may have moved. Base the PR on main tip. ref: main + - name: Resolve and validate version + env: + INPUT_VERSION: ${{ inputs.version }} + run: | + # workflow_call always passes the released version; manual runs may + # omit it and fall back to the release tracked in the repo. + version="${INPUT_VERSION:-$(jq -r '."core/wren-core-py"' .release-please-manifest.json)}" + # Guard a malformed version out of the dep edit, branch name, and PR. + if [[ ! "${version}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(rc[0-9]+)?$ ]]; then + echo "::error::Unsupported version: ${version}. Expected X.Y.Z or X.Y.ZrcN." + exit 1 + fi + echo "Syncing core/wren to wren-core-py ${version}" + echo "VERSION=${version}" >> "$GITHUB_ENV" + - uses: astral-sh/setup-uv@v4 - name: Bump floor and relock core/wren against published wren-core-py From 1a96edab2ccca040427f07782b05784a76923ee2 Mon Sep 17 00:00:00 2001 From: Peter Date: Fri, 3 Jul 2026 17:21:47 +0800 Subject: [PATCH 3/5] ci(release): harden the wren-core-py lock sync workflow Add a concurrency group, skip when a sync PR is already open so --force can't clobber a review, drop the redundant exact-version lock check, and skip the final retry sleep. --- .github/workflows/sync-wren-core-py-lock.yml | 41 ++++++++++---------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/.github/workflows/sync-wren-core-py-lock.yml b/.github/workflows/sync-wren-core-py-lock.yml index 2124831d70..448e031e3d 100644 --- a/.github/workflows/sync-wren-core-py-lock.yml +++ b/.github/workflows/sync-wren-core-py-lock.yml @@ -25,6 +25,12 @@ permissions: contents: write pull-requests: write +# Serialize runs for the same version so an automatic run and a manual re-run +# can't both push chore/sync-wren-core-py-. Don't cancel mid-push. +concurrency: + group: sync-wren-core-py-lock-${{ inputs.version }} + cancel-in-progress: false + jobs: sync-lock: if: ${{ github.repository == 'Canner/WrenAI' }} @@ -62,21 +68,14 @@ jobs: if uv add --no-sync "wren-core-py>=${VERSION}"; then exit 0 fi - echo "uv add attempt ${attempt} failed; retrying after PyPI propagation delay" - sleep 20 + if [ "${attempt}" -lt 5 ]; then + echo "uv add attempt ${attempt} failed; retrying after PyPI propagation delay" + sleep 20 + fi done echo "::error::uv add did not succeed after retries" exit 1 - - name: Verify lock resolved to the released version - run: | - locked=$(awk -F'"' '/^name = "wren-core-py"$/{getline; print $2}' core/wren/uv.lock) - if [[ "${locked}" != "${VERSION}" ]]; then - echo "::error::core/wren/uv.lock resolved wren-core-py ${locked:-}, expected ${VERSION}" - exit 1 - fi - echo "core/wren/uv.lock resolved wren-core-py ${locked}" - - name: Validate lockfile is consistent working-directory: core/wren run: uv lock --check @@ -96,18 +95,20 @@ jobs: GH_TOKEN: ${{ github.token }} run: | BRANCH="chore/sync-wren-core-py-${VERSION}" + # If a PR is already open for this bump, leave it untouched. + if [ "$(gh pr list --state open --head "${BRANCH}" --json number --jq 'length')" != "0" ]; then + echo "Open PR for ${BRANCH} already exists; leaving it untouched." + exit 0 + fi git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" git checkout -b "${BRANCH}" git add core/wren/pyproject.toml core/wren/uv.lock git commit -m "chore(wren): bump wren-core-py to ${VERSION}" + # No open PR: --force only overwrites a leftover branch from a closed PR. git push --force origin "${BRANCH}" - if gh pr view "${BRANCH}" --json number >/dev/null 2>&1; then - echo "PR for ${BRANCH} already exists; branch updated." - else - gh pr create \ - --base main \ - --head "${BRANCH}" \ - --title "chore(wren): bump wren-core-py to ${VERSION}" \ - --body "Automated follow-up after \`wren-core-py\` ${VERSION} was published to PyPI. Relocks \`core/wren\` against the new engine binding. Please review and merge." - fi + gh pr create \ + --base main \ + --head "${BRANCH}" \ + --title "chore(wren): bump wren-core-py to ${VERSION}" \ + --body "Automated follow-up after \`wren-core-py\` ${VERSION} was published to PyPI. Relocks \`core/wren\` against the new engine binding. Please review and merge." From df887b91504849d8264db4e52accd213a6b63bb4 Mon Sep 17 00:00:00 2001 From: Peter Date: Fri, 3 Jul 2026 17:40:23 +0800 Subject: [PATCH 4/5] ci(release): add a stable concurrency group --- .github/workflows/sync-wren-core-py-lock.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/sync-wren-core-py-lock.yml b/.github/workflows/sync-wren-core-py-lock.yml index 448e031e3d..bf3b4e1fdd 100644 --- a/.github/workflows/sync-wren-core-py-lock.yml +++ b/.github/workflows/sync-wren-core-py-lock.yml @@ -25,10 +25,10 @@ permissions: contents: write pull-requests: write -# Serialize runs for the same version so an automatic run and a manual re-run -# can't both push chore/sync-wren-core-py-. Don't cancel mid-push. +# Serialize sync runs so two can't push the same branch at once. Constant key +# (not per-version) because a blank-dispatch version is only known at run time. concurrency: - group: sync-wren-core-py-lock-${{ inputs.version }} + group: sync-wren-core-py-lock cancel-in-progress: false jobs: From e6aa14831810f18f5e50243a0f648dadb626a4da Mon Sep 17 00:00:00 2001 From: Peter Date: Fri, 3 Jul 2026 21:06:36 +0800 Subject: [PATCH 5/5] ci(release): drop persisted credentials in the lock sync persist-credentials: false keeps the write token out of .git/config while uv builds dependency sdists during resolution; the push re-auths explicitly. --- .github/workflows/sync-wren-core-py-lock.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/sync-wren-core-py-lock.yml b/.github/workflows/sync-wren-core-py-lock.yml index bf3b4e1fdd..88dc817eaa 100644 --- a/.github/workflows/sync-wren-core-py-lock.yml +++ b/.github/workflows/sync-wren-core-py-lock.yml @@ -41,6 +41,9 @@ jobs: with: # publish takes minutes; main may have moved. Base the PR on main tip. ref: main + # Keep the write token out of .git/config so dependency code uv may + # build during resolution can't read it (supply-chain); push re-auths. + persist-credentials: false - name: Resolve and validate version env: @@ -96,7 +99,7 @@ jobs: run: | BRANCH="chore/sync-wren-core-py-${VERSION}" # If a PR is already open for this bump, leave it untouched. - if [ "$(gh pr list --state open --head "${BRANCH}" --json number --jq 'length')" != "0" ]; then + if [ "$(gh pr list --repo "${GITHUB_REPOSITORY}" --state open --head "${BRANCH}" --json number --jq 'length')" != "0" ]; then echo "Open PR for ${BRANCH} already exists; leaving it untouched." exit 0 fi @@ -106,8 +109,12 @@ jobs: git add core/wren/pyproject.toml core/wren/uv.lock git commit -m "chore(wren): bump wren-core-py to ${VERSION}" # No open PR: --force only overwrites a leftover branch from a closed PR. - git push --force origin "${BRANCH}" + # Auth the push explicitly since credentials aren't persisted (masked in logs). + git push --force \ + "https://x-access-token:${GH_TOKEN}@${GITHUB_SERVER_URL#https://}/${GITHUB_REPOSITORY}.git" \ + "${BRANCH}" gh pr create \ + --repo "${GITHUB_REPOSITORY}" \ --base main \ --head "${BRANCH}" \ --title "chore(wren): bump wren-core-py to ${VERSION}" \