Files
qwen-code/.github/workflows/release-sdk-java.yml
T
5d40c8153c fix(ci): gate Desktop and Java SDK releases on checks of the release commit (#14259)
* fix(ci): gate desktop releases on checks of the release commit

Co-authored-by: Qwen-Coder <[email protected]>

* fix(ci): gate Java SDK releases on the complete Java suite

Co-authored-by: Qwen-Coder <[email protected]>

* fix(ci): update sdk-java MAVEN_ARGS count after checkstyle step addition

Commit 404845a (#14242) added a 'Run Managed Agent Server Checkstyle' step
to the test job in sdk-java.yml, bringing the MAVEN_ARGS count from 6 to 7,
but did not update the corresponding test expectation.

* fix(ci): run desktop shell checks in parallel with the build

The relocated desktop_shell job serialized up to 45 minutes ahead of the
120-minute build on every release and daily dry run, re-running cargo test
and test-release.js that build already runs ungated on all five matrix
entries against the same pinned revision. Keep the job, but gate only
publish and sync-oss on it; build needs prepare alone, so the shell lane
runs in parallel and nothing publishes unchecked.

Also: sdk-java.yml's cancel-in-progress expression named a trigger #14205
removed, so it was permanently false — make that literal. The desktop
qwen_code_ref input descriptions now say the ref must contain the desktop
crate, since a pre-desktop ref hard-fails the gate instead of passing
vacuously.

Co-authored-by: Qwen-Coder <[email protected]>

* fix(ci): restore the runs-on dropped from the desktop build job

Commit 6989b9c replaced the build job's `runs-on: '${{ matrix.os }}'`
with the comment explaining the parallel shell lane, so the key never
came back. actionlint fails the Lint & Static gate on it, and GitHub
rejects the file outright — the push-event Desktop Release run ends
"failed because of a workflow file issue" with zero jobs, which would
break every desktop release and dry run, not just CI.

Restore the line. The build job's 34 steps and its five-entry `os`
matrix are unchanged, so this returns the job to the shape main has.

Co-authored-by: Qwen-Coder <[email protected]>

---------

Co-authored-by: Qwen-Coder <[email protected]>
Co-authored-by: qwen-code-dev-bot <[email protected]>
2026-10-15 19:42:08 +08:00

298 lines
12 KiB
YAML

name: 'Release Java SDK'
on:
workflow_dispatch:
inputs:
dry_run:
description: 'Verify the release without publishing, tagging, or creating a GitHub Release.'
required: true
type: 'boolean'
default: true
concurrency:
group: 'release-sdk-java-0.1.0-alpha'
cancel-in-progress: false
env:
SDK_DIRECTORY: 'packages/sdk-java/qwencode'
SDK_VERSION: '0.1.0-alpha'
RELEASE_TAG: 'sdk-java-v0.1.0-alpha'
jobs:
prepare:
if: "${{ github.repository == 'QwenLM/qwen-code' }}"
runs-on: 'ubuntu-latest'
timeout-minutes: 60
permissions:
contents: 'read'
outputs:
release_sha: '${{ steps.preflight.outputs.release_sha }}'
tag_state: '${{ steps.preflight.outputs.tag_state }}'
artifact_state: '${{ steps.preflight.outputs.artifact_state }}'
steps:
- name: 'Require protected main'
env:
WORKFLOW_REF: '${{ github.ref }}'
run: |-
if [[ "${WORKFLOW_REF}" != "refs/heads/main" ]]; then
echo 'The Java SDK release workflow must run from main.' >&2
exit 1
fi
- name: 'Checkout release candidate'
uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3
with:
ref: '${{ github.sha }}'
fetch-depth: 0
persist-credentials: false
- name: 'Set up Java'
uses: 'actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654' # v5.2.0
with:
distribution: 'temurin'
java-version: '11'
cache: 'maven'
cache-dependency-path: 'packages/sdk-java/qwencode/pom.xml'
- name: 'Validate release version and tag'
id: 'preflight'
env:
CURRENT_SHA: '${{ github.sha }}'
DRY_RUN: '${{ inputs.dry_run }}'
run: |-
set -euo pipefail
checked_out_sha=$(git rev-parse HEAD)
if [[ "${checked_out_sha}" != "${CURRENT_SHA}" ]]; then
echo "Checked-out main ${checked_out_sha} does not match workflow SHA ${CURRENT_SHA}." >&2
exit 1
fi
tag_state='new'
direct_tag=$(git ls-remote --refs --tags origin "refs/tags/${RELEASE_TAG}" | cut -f1)
peeled_tag=$(git ls-remote --tags origin "refs/tags/${RELEASE_TAG}^{}" | cut -f1)
if [[ -n "${direct_tag}" ]]; then
if [[ -z "${peeled_tag}" ]]; then
echo "${RELEASE_TAG} is not an annotated release tag." >&2
exit 1
fi
tag_commit=${peeled_tag}
if ! git merge-base --is-ancestor "${tag_commit}" "${checked_out_sha}"; then
echo "${RELEASE_TAG} does not point to a commit in protected main history." >&2
exit 1
fi
git checkout --detach "${tag_commit}"
tag_state='resume'
fi
actual_version=$(mvn --batch-mode --no-transfer-progress -f "${SDK_DIRECTORY}/pom.xml" help:evaluate -Dexpression=project.version -q -DforceStdout)
if [[ "${actual_version}" != "${SDK_VERSION}" ]]; then
echo "Expected ${SDK_VERSION}, found ${actual_version}." >&2
exit 1
fi
artifact_url="https://repo1.maven.org/maven2/com/alibaba/qwencode-sdk/${SDK_VERSION}/qwencode-sdk-${SDK_VERSION}.pom"
probe_artifact() {
local status=''
for attempt in 1 2 3; do
if status=$(curl --silent --show-error --location --connect-timeout 10 --max-time 30 --output /dev/null --write-out '%{http_code}' "${artifact_url}"); then
if [[ "${status}" == '200' ]]; then
echo 'published'
return 0
fi
if [[ "${status}" == '404' ]]; then
echo 'missing'
return 0
fi
fi
if [[ "${attempt}" -lt 3 ]]; then
sleep 5
fi
done
echo "Maven Central probe was inconclusive (last HTTP status: ${status:-transport_error})." >&2
return 1
}
artifact_state=$(probe_artifact)
if [[ "${tag_state}" == 'new' && "${artifact_state}" == 'published' ]]; then
echo "${SDK_VERSION} is already published without ${RELEASE_TAG}; manual recovery is required." >&2
exit 1
fi
if [[ "${DRY_RUN}" == 'false' && "${tag_state}" == 'resume' && "${artifact_state}" == 'missing' ]]; then
echo 'Matching tag already exists; Maven publication will resume from that immutable source commit.'
elif [[ "${DRY_RUN}" == 'false' && "${tag_state}" == 'resume' ]]; then
echo 'Matching tag and artifact already exist; GitHub Release creation will resume.'
fi
echo "release_sha=$(git rev-parse HEAD)" >> "${GITHUB_OUTPUT}"
echo "tag_state=${tag_state}" >> "${GITHUB_OUTPUT}"
echo "artifact_state=${artifact_state}" >> "${GITHUB_OUTPUT}"
java_checks:
needs: 'prepare'
permissions:
contents: 'read'
uses: './.github/workflows/sdk-java.yml'
with:
ref: '${{ needs.prepare.outputs.release_sha }}'
release:
needs:
- 'prepare'
- 'java_checks'
runs-on: 'ubuntu-latest'
timeout-minutes: 60
environment:
name: "${{ inputs.dry_run && 'sdk-java-dry-run' || 'production-release' }}"
url: '${{ github.server_url }}/${{ github.repository }}/releases'
permissions:
contents: 'write'
steps:
- name: 'Checkout checked release source'
uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3
with:
ref: '${{ needs.prepare.outputs.release_sha }}'
fetch-depth: 0
persist-credentials: false
- name: 'Set up Java'
uses: 'actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654' # v5.2.0
with:
distribution: 'temurin'
java-version: '11'
cache: 'maven'
cache-dependency-path: 'packages/sdk-java/qwencode/pom.xml'
- name: 'Set up Node.js'
uses: 'actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e' # v6.4.0
with:
node-version: '22'
package-manager-cache: false
- name: 'Verify release tag has not changed'
env:
RELEASE_SHA: '${{ needs.prepare.outputs.release_sha }}'
TAG_STATE: '${{ needs.prepare.outputs.tag_state }}'
run: |-
set -euo pipefail
direct_tag=$(git ls-remote --refs --tags origin "refs/tags/${RELEASE_TAG}" | cut -f1)
if [[ "${TAG_STATE}" == 'new' ]]; then
if [[ -n "${direct_tag}" ]]; then
echo 'Release tag appeared during validation; rerun to validate its source.' >&2
exit 1
fi
else
peeled_tag=$(git ls-remote --tags origin "refs/tags/${RELEASE_TAG}^{}" | cut -f1)
if [[ "${peeled_tag}" != "${RELEASE_SHA}" ]]; then
echo 'Release tag no longer points to the checked source commit.' >&2
exit 1
fi
fi
- name: 'Require publishing credentials'
if: "${{ !inputs.dry_run && needs.prepare.outputs.artifact_state == 'missing' }}"
env:
CENTRAL_USERNAME: '${{ secrets.CENTRAL_USERNAME }}'
CENTRAL_PASSWORD: '${{ secrets.CENTRAL_PASSWORD }}'
MAVEN_GPG_PRIVATE_KEY: '${{ secrets.MAVEN_GPG_PRIVATE_KEY }}'
MAVEN_GPG_PASSPHRASE: '${{ secrets.MAVEN_GPG_PASSPHRASE }}'
run: |-
set -euo pipefail
missing=()
[[ -n "${CENTRAL_USERNAME}" ]] || missing+=('CENTRAL_USERNAME')
[[ -n "${CENTRAL_PASSWORD}" ]] || missing+=('CENTRAL_PASSWORD')
[[ -n "${MAVEN_GPG_PRIVATE_KEY}" ]] || missing+=('MAVEN_GPG_PRIVATE_KEY')
[[ -n "${MAVEN_GPG_PASSPHRASE}" ]] || missing+=('MAVEN_GPG_PASSPHRASE')
if (( ${#missing[@]} > 0 )); then
printf 'Missing production-release environment secret: %s\n' "${missing[@]}" >&2
exit 1
fi
- name: 'Verify release build'
working-directory: '${{ env.SDK_DIRECTORY }}'
run: 'mvn --batch-mode --no-transfer-progress clean verify -Dgpg.skip=true'
- name: 'Install Node.js dependencies'
run: 'corepack pnpm install --frozen-lockfile --prefer-offline --reporter=append-only'
- name: 'Build Qwen Code'
run: 'npm run build'
- name: 'Bundle Qwen Code'
run: 'npm run bundle'
- name: 'Run Java daemon E2E'
run: 'npx tsx scripts/run-java-daemon-sdk-e2e.ts'
- name: 'Require an unchanged source tree'
run: |-
if [[ -n "$(git status --porcelain)" ]]; then
git status --short
echo 'Release verification changed or created source files.' >&2
exit 1
fi
- name: 'Create and push release tag'
if: "${{ !inputs.dry_run && needs.prepare.outputs.tag_state == 'new' }}"
env:
GH_TOKEN: '${{ github.token }}'
run: |-
set -euo pipefail
git config user.name 'github-actions[bot]'
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
git tag -a "${RELEASE_TAG}" -m "Java SDK ${SDK_VERSION}"
gh auth setup-git
git push origin "refs/tags/${RELEASE_TAG}"
- name: 'Configure Maven Central publishing'
if: "${{ !inputs.dry_run && needs.prepare.outputs.artifact_state == 'missing' }}"
uses: 'actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654' # v5.2.0
with:
distribution: 'temurin'
java-version: '11'
cache: 'maven'
cache-dependency-path: 'packages/sdk-java/qwencode/pom.xml'
server-id: 'central'
server-username: 'MAVEN_USERNAME'
server-password: 'MAVEN_PASSWORD'
gpg-private-key: '${{ secrets.MAVEN_GPG_PRIVATE_KEY }}'
gpg-passphrase: 'MAVEN_GPG_PASSPHRASE'
- name: 'Publish signed artifacts to Maven Central'
if: "${{ !inputs.dry_run && needs.prepare.outputs.artifact_state == 'missing' }}"
working-directory: '${{ env.SDK_DIRECTORY }}'
env:
MAVEN_GPG_PASSPHRASE: '${{ secrets.MAVEN_GPG_PASSPHRASE }}'
MAVEN_USERNAME: '${{ secrets.CENTRAL_USERNAME }}'
MAVEN_PASSWORD: '${{ secrets.CENTRAL_PASSWORD }}'
run: 'mvn --batch-mode --no-transfer-progress -DskipTests deploy'
- name: 'Verify Maven Central availability'
if: '${{ !inputs.dry_run }}'
run: |-
set -euo pipefail
artifact_url="https://repo1.maven.org/maven2/com/alibaba/qwencode-sdk/${SDK_VERSION}/qwencode-sdk-${SDK_VERSION}.pom"
for attempt in {1..40}; do
if curl --fail --silent --show-error --location --connect-timeout 10 --max-time 10 --output /dev/null "${artifact_url}"; then
exit 0
fi
if [[ "${attempt}" -lt 40 ]]; then
sleep 30
fi
done
echo "Published artifact is not available at ${artifact_url}." >&2
exit 1
- name: 'Create GitHub Release'
if: '${{ !inputs.dry_run }}'
env:
GH_TOKEN: '${{ github.token }}'
run: |-
set -euo pipefail
git fetch origin "refs/tags/${RELEASE_TAG}:refs/tags/${RELEASE_TAG}"
if [[ "$(git rev-parse "${RELEASE_TAG}^{commit}")" != "$(git rev-parse HEAD)" ]]; then
echo 'Release tag does not match the checked source commit.' >&2
exit 1
fi
if gh release view "${RELEASE_TAG}" >/dev/null 2>&1; then
echo "GitHub Release ${RELEASE_TAG} already exists."
exit 0
fi
gh release create "${RELEASE_TAG}" --verify-tag --title "Java SDK ${SDK_VERSION}" --generate-notes