mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-21 10:16:44 +00:00
Bumps the actions group with 14 updates in the / directory: | Package | From | To | | --- | --- | --- | | [actions/create-github-app-token](https://github.com/actions/create-github-app-token) | `3.1.1` | `3.2.0` | | [actions/attest](https://github.com/actions/attest) | `4.1.1` | `4.2.0` | | [useblacksmith/setup-docker-builder](https://github.com/useblacksmith/setup-docker-builder) | `1.9.0` | `1.11.0` | | [actions/setup-python](https://github.com/actions/setup-python) | `6.2.0` | `6.3.0` | | [actions/setup-java](https://github.com/actions/setup-java) | `5.2.0` | `5.6.0` | | [github/codeql-action/init](https://github.com/github/codeql-action) | `4.36.2` | `4.37.1` | | [github/codeql-action/analyze](https://github.com/github/codeql-action) | `4.36.2` | `4.37.1` | | [github/codeql-action/upload-sarif](https://github.com/github/codeql-action) | `4.36.2` | `4.37.1` | | [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) | `4.1.0` | `4.2.0` | | [docker/login-action](https://github.com/docker/login-action) | `4.2.0` | `4.4.0` | | [docker/build-push-action](https://github.com/docker/build-push-action) | `7.2.0` | `7.3.0` | | [openai/codex-action](https://github.com/openai/codex-action) | `1.8` | `1.11` | | [actions/labeler](https://github.com/actions/labeler) | `6.1.0` | `6.2.0` | | [actions/stale](https://github.com/actions/stale) | `10.3.0` | `10.4.0` | Updates `actions/create-github-app-token` from 3.1.1 to 3.2.0 - [Release notes](https://github.com/actions/create-github-app-token/releases) - [Changelog](https://github.com/actions/create-github-app-token/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/create-github-app-token/compare/v3.1.1...bcd2ba49218906704ab6c1aa796996da409d3eb1) Updates `actions/attest` from 4.1.1 to 4.2.0 - [Release notes](https://github.com/actions/attest/releases) - [Changelog](https://github.com/actions/attest/blob/main/RELEASE.md) - [Commits](https://github.com/actions/attest/compare/a1948c3f048ba23858d222213b7c278aabede763...f7c74d28b9d84cb8768d0b8ca14a4bac6ef463e6) Updates `useblacksmith/setup-docker-builder` from 1.9.0 to 1.11.0 - [Release notes](https://github.com/useblacksmith/setup-docker-builder/releases) - [Commits](https://github.com/useblacksmith/setup-docker-builder/compare/ab5c1da94f53f5cd75c1038092aa276dddfccbba...6ff44f8e5255f9d8aa31ef22f7e57a2d926b7da0) Updates `actions/setup-python` from 6.2.0 to 6.3.0 - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/a309ff8b426b58ec0e2a45f0f869d46889d02405...ece7cb06caefa5fff74198d8649806c4678c61a1) Updates `actions/setup-java` from 5.2.0 to 5.6.0 - [Release notes](https://github.com/actions/setup-java/releases) - [Commits](https://github.com/actions/setup-java/compare/be666c2fcd27ec809703dec50e508c2fdc7f6654...03ad4de0992f5dab5e18fcb136590ce7c4a0ac95) Updates `github/codeql-action/init` from 4.36.2 to 4.37.1 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/8aad20d150bbac5944a9f9d289da16a4b0d87c1e...7188fc363630916deb702c7fdcf4e481b751f97a) Updates `github/codeql-action/analyze` from 4.36.2 to 4.37.1 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/8aad20d150bbac5944a9f9d289da16a4b0d87c1e...7188fc363630916deb702c7fdcf4e481b751f97a) Updates `github/codeql-action/upload-sarif` from 4.36.2 to 4.37.1 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/8aad20d150bbac5944a9f9d289da16a4b0d87c1e...7188fc363630916deb702c7fdcf4e481b751f97a) Updates `docker/setup-buildx-action` from 4.1.0 to 4.2.0 - [Release notes](https://github.com/docker/setup-buildx-action/releases) - [Commits](https://github.com/docker/setup-buildx-action/compare/d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5...bb05f3f5519dd87d3ba754cc423b652a5edd6d2c) Updates `docker/login-action` from 4.2.0 to 4.4.0 - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/650006c6eb7dba73a995cc03b0b2d7f5ca915bee...af1e73f918a031802d376d3c8bbc3fe56130a9b0) Updates `docker/build-push-action` from 7.2.0 to 7.3.0 - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/f9f3042f7e2789586610d6e8b85c8f03e5195baf...53b7df96c91f9c12dcc8a07bcb9ccacbed38856a) Updates `openai/codex-action` from 1.8 to 1.11 - [Changelog](https://github.com/openai/codex-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/openai/codex-action/compare/v1.8...52fe01ec70a42f454c9d2ebd47598f9fd6893d56) Updates `actions/labeler` from 6.1.0 to 6.2.0 - [Release notes](https://github.com/actions/labeler/releases) - [Commits](https://github.com/actions/labeler/compare/f27b608878404679385c85cfa523b85ccb86e213...b8dd2d9be0f68b860e7dae5dae7d772984eacd6d) Updates `actions/stale` from 10.3.0 to 10.4.0 - [Release notes](https://github.com/actions/stale/releases) - [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/stale/compare/eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899...1e223db275d687790206a7acac4d1a11bd6fe629) --- updated-dependencies: - dependency-name: actions/attest dependency-version: 4.2.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: actions - dependency-name: actions/create-github-app-token dependency-version: 3.2.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: actions - dependency-name: actions/labeler dependency-version: 6.2.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: actions - dependency-name: actions/setup-java dependency-version: 5.6.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: actions - dependency-name: actions/setup-python dependency-version: 6.3.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: actions - dependency-name: actions/stale dependency-version: 10.4.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: actions - dependency-name: docker/build-push-action dependency-version: 7.3.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: actions - dependency-name: docker/login-action dependency-version: 4.4.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: actions - dependency-name: docker/setup-buildx-action dependency-version: 4.2.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: actions - dependency-name: github/codeql-action/analyze dependency-version: 4.37.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: actions - dependency-name: github/codeql-action/init dependency-version: 4.37.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: actions - dependency-name: github/codeql-action/upload-sarif dependency-version: 4.37.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: actions - dependency-name: openai/codex-action dependency-version: '1.11' dependency-type: direct:production update-type: version-update:semver-minor dependency-group: actions - dependency-name: useblacksmith/setup-docker-builder dependency-version: 1.11.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: actions ... Signed-off-by: dependabot[bot] <support@github.com>
432 lines
20 KiB
YAML
432 lines
20 KiB
YAML
name: Android Release
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
tag:
|
|
description: Existing stable OpenClaw release tag to receive the signed Android APK
|
|
required: true
|
|
type: string
|
|
release_publish_run_id:
|
|
description: OpenClaw Release Publish run that approved this release
|
|
required: true
|
|
type: string
|
|
release_publish_branch:
|
|
description: Branch used by the approving OpenClaw Release Publish run
|
|
required: true
|
|
type: string
|
|
release_target_sha:
|
|
description: Exact release tag commit resolved by OpenClaw Release Publish
|
|
required: true
|
|
type: string
|
|
direct_release_recovery:
|
|
description: Allow a completed parent run and published release for explicit backfill or recovery
|
|
required: true
|
|
default: false
|
|
type: boolean
|
|
|
|
permissions:
|
|
actions: read
|
|
attestations: write
|
|
contents: write
|
|
id-token: write
|
|
|
|
concurrency:
|
|
group: android-release-${{ inputs.tag }}
|
|
cancel-in-progress: false
|
|
|
|
env:
|
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
|
NODE_VERSION: "24.15.0"
|
|
|
|
jobs:
|
|
publish_signed_android_apk:
|
|
name: Publish signed Android APK
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 45
|
|
environment: android-release
|
|
steps:
|
|
- name: Checkout release tag
|
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
|
with:
|
|
ref: refs/tags/${{ inputs.tag }}
|
|
fetch-depth: 0
|
|
persist-credentials: false
|
|
|
|
- name: Download parent release approval
|
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
|
|
with:
|
|
name: android-release-approval-${{ inputs.release_publish_run_id }}
|
|
path: ${{ runner.temp }}/android-release-approval
|
|
repository: ${{ github.repository }}
|
|
run-id: ${{ inputs.release_publish_run_id }}
|
|
github-token: ${{ github.token }}
|
|
|
|
- name: Validate release approval and target
|
|
id: release_approval
|
|
env:
|
|
APPROVAL_PATH: ${{ runner.temp }}/android-release-approval/approval.json
|
|
DIRECT_RELEASE_RECOVERY: ${{ inputs.direct_release_recovery && 'true' || 'false' }}
|
|
EXPECTED_WORKFLOW_BRANCH: ${{ inputs.release_publish_branch }}
|
|
GH_TOKEN: ${{ github.token }}
|
|
RELEASE_PUBLISH_RUN_ID: ${{ inputs.release_publish_run_id }}
|
|
RELEASE_TAG: ${{ inputs.tag }}
|
|
RELEASE_TARGET_SHA: ${{ inputs.release_target_sha }}
|
|
run: |
|
|
set -euo pipefail
|
|
if [[ ! "${RELEASE_TAG}" =~ ^v[0-9]{4}\.[1-9][0-9]*\.[1-9][0-9]*(-[1-9][0-9]*)?$ ]]; then
|
|
echo "Android APK publishing requires a final or correction OpenClaw release tag: ${RELEASE_TAG}" >&2
|
|
exit 1
|
|
fi
|
|
if [[ "${EXPECTED_WORKFLOW_BRANCH}" != "main" && ! "${EXPECTED_WORKFLOW_BRANCH}" =~ ^release/[0-9]{4}\.[1-9][0-9]*\.[1-9][0-9]*$ ]]; then
|
|
echo "release_publish_branch must be main or release/YYYY.M.PATCH." >&2
|
|
exit 1
|
|
fi
|
|
|
|
expected_source_ref="refs/tags/${RELEASE_TAG}"
|
|
if [[ "${GITHUB_REF}" != "${expected_source_ref}" ]]; then
|
|
echo "Android publication must run from ${expected_source_ref}, got ${GITHUB_REF}." >&2
|
|
exit 1
|
|
fi
|
|
if [[ ! "${RELEASE_TARGET_SHA}" =~ ^[a-f0-9]{40}$ ]]; then
|
|
echo "release_target_sha must be a full lowercase commit SHA." >&2
|
|
exit 1
|
|
fi
|
|
|
|
gh attestation verify "${APPROVAL_PATH}" \
|
|
--repo "${GITHUB_REPOSITORY}" \
|
|
--signer-workflow "${GITHUB_REPOSITORY}/.github/workflows/openclaw-release-publish.yml" \
|
|
--source-ref "refs/heads/${EXPECTED_WORKFLOW_BRANCH}" \
|
|
--deny-self-hosted-runners
|
|
run_json="$(gh run view "${RELEASE_PUBLISH_RUN_ID}" --repo "${GITHUB_REPOSITORY}" --json workflowName,headBranch,event,status,conclusion,url)"
|
|
printf '%s' "${run_json}" | node scripts/validate-release-publish-approval.mjs
|
|
tag_sha="$(git rev-parse "${RELEASE_TAG}^{commit}")"
|
|
if [[ "${RELEASE_TARGET_SHA}" != "${tag_sha}" ]]; then
|
|
echo "Release target SHA ${RELEASE_TARGET_SHA} does not match ${RELEASE_TAG} (${tag_sha})." >&2
|
|
exit 1
|
|
fi
|
|
|
|
release_json="$(gh release view "${RELEASE_TAG}" --repo "${GITHUB_REPOSITORY}" --json tagName,isDraft,isPrerelease,createdAt,assets,url)"
|
|
if [[ "$(printf '%s' "${release_json}" | jq -r '.tagName')" != "${RELEASE_TAG}" ]]; then
|
|
echo "GitHub release tag does not match ${RELEASE_TAG}." >&2
|
|
exit 1
|
|
fi
|
|
if [[ "$(printf '%s' "${release_json}" | jq -r '.isPrerelease')" == "true" ]]; then
|
|
echo "Android APK publishing requires a stable GitHub release." >&2
|
|
exit 1
|
|
fi
|
|
if [[ "${DIRECT_RELEASE_RECOVERY}" != "true" && "$(printf '%s' "${release_json}" | jq -r '.isDraft')" != "true" ]]; then
|
|
echo "Normal Android promotion requires the target GitHub release to remain a draft." >&2
|
|
exit 1
|
|
fi
|
|
release_created_at="$(printf '%s' "${release_json}" | jq -er '.createdAt')"
|
|
build_timestamp="$(date -u -d "${release_created_at}" +%Y-%m-%dT%H:%M:%SZ)"
|
|
echo "build_timestamp=${build_timestamp}" >> "${GITHUB_OUTPUT}"
|
|
unexpected_assets="$(printf '%s' "${release_json}" | jq -r '[.assets[]? | select(.name | startswith("OpenClaw-Android")) | .name] | join(", ")')"
|
|
if [[ -n "${unexpected_assets}" ]]; then
|
|
echo "Target release already contains Android assets: ${unexpected_assets}. Immutable recovery accepts them only after rebuilding identical bytes." >&2
|
|
fi
|
|
|
|
- name: Verify release source and Android version
|
|
id: release_source
|
|
env:
|
|
RELEASE_TAG: ${{ inputs.tag }}
|
|
run: |
|
|
set -euo pipefail
|
|
release_version="${RELEASE_TAG#v}"
|
|
android_release_version="${release_version%%-*}"
|
|
package_version="$(node -p 'require("./package.json").version')"
|
|
android_version="$(node -p 'require("./apps/android/version.json").version')"
|
|
fallback_base_tag=""
|
|
fallback_base_sha=""
|
|
if [[ "${package_version}" == "${release_version}" ]]; then
|
|
:
|
|
elif [[ "${RELEASE_TAG}" =~ -[1-9][0-9]*$ && "${package_version}" == "${android_release_version}" ]]; then
|
|
fallback_base_tag="v${package_version}"
|
|
fallback_base_sha="$(git rev-parse "${fallback_base_tag}^{commit}")"
|
|
release_sha="$(git rev-parse HEAD)"
|
|
if [[ "${fallback_base_sha}" != "${release_sha}" ]]; then
|
|
echo "Fallback correction ${RELEASE_TAG} must resolve to the same source commit as ${fallback_base_tag}." >&2
|
|
exit 1
|
|
fi
|
|
else
|
|
echo "Release tag version ${release_version} does not match package.json ${package_version} or a same-commit fallback correction." >&2
|
|
exit 1
|
|
fi
|
|
if [[ "${android_version}" != "${android_release_version}" ]]; then
|
|
echo "Android version ${android_version} does not match release train ${android_release_version}." >&2
|
|
exit 1
|
|
fi
|
|
echo "fallback_base_tag=${fallback_base_tag}" >> "${GITHUB_OUTPUT}"
|
|
echo "FALLBACK_ANDROID_BASE_TAG=${fallback_base_tag}" >> "${GITHUB_ENV}"
|
|
echo "FALLBACK_ANDROID_BASE_SHA=${fallback_base_sha}" >> "${GITHUB_ENV}"
|
|
git diff --exit-code
|
|
|
|
- name: Setup Node environment
|
|
uses: ./.github/actions/setup-node-env
|
|
with:
|
|
install-bun: "true"
|
|
install-deps: "false"
|
|
use-actions-cache: "false"
|
|
|
|
- name: Setup Android toolchain
|
|
uses: ./.github/actions/setup-android-toolchain
|
|
|
|
- name: Create apps-signing read token
|
|
if: ${{ steps.release_source.outputs.fallback_base_tag == '' }}
|
|
id: apps-signing-token
|
|
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
|
|
with:
|
|
app-id: "2729701"
|
|
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
|
|
owner: openclaw
|
|
repositories: apps-signing
|
|
permission-contents: read
|
|
|
|
- name: Checkout encrypted Android signing assets
|
|
if: ${{ steps.release_source.outputs.fallback_base_tag == '' }}
|
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
|
with:
|
|
repository: openclaw/apps-signing
|
|
ref: main
|
|
path: apps/android/build/release-signing/apps-signing
|
|
token: ${{ steps.apps-signing-token.outputs.token }}
|
|
persist-credentials: false
|
|
|
|
- name: Materialize Android release signing
|
|
if: ${{ steps.release_source.outputs.fallback_base_tag == '' }}
|
|
env:
|
|
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
|
|
run: |
|
|
set -euo pipefail
|
|
if [[ -z "${MATCH_PASSWORD}" ]]; then
|
|
echo "Android release signing secrets are unavailable." >&2
|
|
exit 1
|
|
fi
|
|
node scripts/android-release-signing.mjs --mode materialize
|
|
|
|
- name: Prepare and verify signed Android APK
|
|
env:
|
|
GIT_COMMIT: ${{ inputs.release_target_sha }}
|
|
GH_TOKEN: ${{ github.token }}
|
|
OPENCLAW_BUILD_TIMESTAMP: ${{ steps.release_approval.outputs.build_timestamp }}
|
|
RELEASE_TAG: ${{ inputs.tag }}
|
|
run: |
|
|
set -euo pipefail
|
|
mkdir -p dist
|
|
if [[ -n "${FALLBACK_ANDROID_BASE_TAG}" ]]; then
|
|
base_dir="${RUNNER_TEMP}/fallback-android-release"
|
|
mkdir -p "${base_dir}"
|
|
gh release download "${FALLBACK_ANDROID_BASE_TAG}" --repo "${GITHUB_REPOSITORY}" \
|
|
--pattern OpenClaw-Android.apk \
|
|
--pattern OpenClaw-Android-SHA256SUMS.txt \
|
|
--dir "${base_dir}"
|
|
(
|
|
cd "${base_dir}"
|
|
sha256sum --strict --check OpenClaw-Android-SHA256SUMS.txt
|
|
)
|
|
gh attestation verify "${base_dir}/OpenClaw-Android.apk" \
|
|
--repo "${GITHUB_REPOSITORY}" \
|
|
--signer-workflow "${GITHUB_REPOSITORY}/.github/workflows/android-release.yml" \
|
|
--source-ref "refs/tags/${FALLBACK_ANDROID_BASE_TAG}" \
|
|
--source-digest "${FALLBACK_ANDROID_BASE_SHA}" \
|
|
--deny-self-hosted-runners
|
|
bun apps/android/scripts/build-release-artifacts.ts \
|
|
--verify-apk "${base_dir}/OpenClaw-Android.apk"
|
|
cp "${base_dir}/OpenClaw-Android.apk" dist/OpenClaw-Android.apk
|
|
cp "${base_dir}/OpenClaw-Android-SHA256SUMS.txt" dist/OpenClaw-Android-SHA256SUMS.txt
|
|
else
|
|
node --input-type=module <<'NODE'
|
|
import { readFileSync } from "node:fs";
|
|
import { spawnSync } from "node:child_process";
|
|
|
|
const path = "apps/android/build/release-signing/gradle.properties";
|
|
const expected = new Set([
|
|
"OPENCLAW_ANDROID_STORE_FILE",
|
|
"OPENCLAW_ANDROID_STORE_PASSWORD",
|
|
"OPENCLAW_ANDROID_KEY_ALIAS",
|
|
"OPENCLAW_ANDROID_KEY_PASSWORD",
|
|
]);
|
|
const properties = new Map();
|
|
for (const rawLine of readFileSync(path, "utf8").split(/\r?\n/u)) {
|
|
const line = rawLine.trim();
|
|
if (!line || line.startsWith("#")) continue;
|
|
const separator = line.indexOf("=");
|
|
if (separator <= 0) throw new Error("Malformed Android signing property.");
|
|
properties.set(line.slice(0, separator).trim(), line.slice(separator + 1).trim());
|
|
}
|
|
for (const name of expected) {
|
|
const value = properties.get(name);
|
|
if (!value) throw new Error(`Missing Android signing property: ${name}`);
|
|
process.env[`ORG_GRADLE_PROJECT_${name}`] = value;
|
|
}
|
|
const result = spawnSync(
|
|
"bun",
|
|
["apps/android/scripts/build-release-artifacts.ts", "--artifact", "third-party"],
|
|
{ env: process.env, stdio: "inherit" },
|
|
);
|
|
process.exit(result.status ?? 1);
|
|
NODE
|
|
|
|
version="$(node -p 'require("./apps/android/version.json").version')"
|
|
source_apk="apps/android/build/release-artifacts/openclaw-${version}-third-party-release.apk"
|
|
source_checksum="${source_apk}.sha256"
|
|
test -s "${source_apk}"
|
|
test -s "${source_checksum}"
|
|
cp "${source_apk}" dist/OpenClaw-Android.apk
|
|
(
|
|
cd dist
|
|
sha256sum OpenClaw-Android.apk > OpenClaw-Android-SHA256SUMS.txt
|
|
)
|
|
fi
|
|
|
|
expected_version_name="${RELEASE_TAG#v}"
|
|
expected_version_name="${expected_version_name%%-*}"
|
|
expected_version_code="$(node -p 'require("./apps/android/version.json").versionCode')"
|
|
actual_app_id="$(apkanalyzer manifest application-id dist/OpenClaw-Android.apk)"
|
|
actual_version_name="$(apkanalyzer manifest version-name dist/OpenClaw-Android.apk)"
|
|
actual_version_code="$(apkanalyzer manifest version-code dist/OpenClaw-Android.apk)"
|
|
if [[ "${actual_app_id}" != "ai.openclaw.app" ]]; then
|
|
echo "Standalone APK has unexpected application ID ${actual_app_id}." >&2
|
|
exit 1
|
|
fi
|
|
if [[ "${actual_version_name}" != "${expected_version_name}" || "${actual_version_code}" != "${expected_version_code}" ]]; then
|
|
echo "Standalone APK version metadata does not match apps/android/version.json." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ -n "${FALLBACK_ANDROID_BASE_TAG}" ]]; then
|
|
echo "Reusing verified Android APK from ${FALLBACK_ANDROID_BASE_TAG} for same-commit fallback correction ${RELEASE_TAG}."
|
|
elif [[ "${RELEASE_TAG}" =~ -([1-9][0-9]*)$ ]]; then
|
|
correction_number="${BASH_REMATCH[1]}"
|
|
releases_json="$(gh api --paginate --slurp "repos/${GITHUB_REPOSITORY}/releases?per_page=100")"
|
|
previous_tag="$(printf '%s' "${releases_json}" | jq -r \
|
|
--arg base_tag "v${expected_version_name}" \
|
|
--argjson correction_number "${correction_number}" '
|
|
[
|
|
.[][]
|
|
| select(.draft == false and .prerelease == false)
|
|
| select(any(.assets[]?; .name == "OpenClaw-Android.apk"))
|
|
| select(any(.assets[]?; .name == "OpenClaw-Android-SHA256SUMS.txt"))
|
|
| if .tag_name == $base_tag then
|
|
{ tag: .tag_name, number: 0 }
|
|
elif (.tag_name | startswith($base_tag + "-")) then
|
|
(.tag_name | ltrimstr($base_tag + "-")) as $suffix
|
|
| select($suffix | test("^[1-9][0-9]*$"))
|
|
| { tag: .tag_name, number: ($suffix | tonumber) }
|
|
else empty end
|
|
| select(.number < $correction_number)
|
|
]
|
|
| sort_by(.number)
|
|
| last
|
|
| .tag // empty
|
|
')"
|
|
if [[ -n "${previous_tag}" ]]; then
|
|
previous_dir="${RUNNER_TEMP}/previous-android-release"
|
|
mkdir -p "${previous_dir}"
|
|
gh release download "${previous_tag}" --repo "${GITHUB_REPOSITORY}" \
|
|
--pattern OpenClaw-Android.apk \
|
|
--pattern OpenClaw-Android-SHA256SUMS.txt \
|
|
--dir "${previous_dir}"
|
|
(
|
|
cd "${previous_dir}"
|
|
sha256sum --strict --check OpenClaw-Android-SHA256SUMS.txt
|
|
)
|
|
gh attestation verify "${previous_dir}/OpenClaw-Android.apk" \
|
|
--repo "${GITHUB_REPOSITORY}" \
|
|
--signer-workflow "${GITHUB_REPOSITORY}/.github/workflows/android-release.yml" \
|
|
--source-ref "refs/tags/${previous_tag}" \
|
|
--deny-self-hosted-runners
|
|
bun apps/android/scripts/build-release-artifacts.ts \
|
|
--verify-apk "${previous_dir}/OpenClaw-Android.apk"
|
|
previous_version_code="$(apkanalyzer manifest version-code "${previous_dir}/OpenClaw-Android.apk")"
|
|
if (( actual_version_code <= previous_version_code )); then
|
|
echo "Android correction versionCode ${actual_version_code} must exceed ${previous_tag} versionCode ${previous_version_code}." >&2
|
|
exit 1
|
|
fi
|
|
else
|
|
echo "No earlier verified standalone APK exists for ${expected_version_name}; accepting this correction as the standalone channel bootstrap."
|
|
fi
|
|
fi
|
|
|
|
- name: Attest Android APK provenance
|
|
uses: actions/attest@f7c74d28b9d84cb8768d0b8ca14a4bac6ef463e6 # v4.2.0
|
|
with:
|
|
subject-path: dist/OpenClaw-Android.apk
|
|
|
|
- name: Upload immutable Android release assets
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
RELEASE_TAG: ${{ inputs.tag }}
|
|
run: |
|
|
set -euo pipefail
|
|
attach_or_verify_asset() {
|
|
local source_path="$1"
|
|
local asset_name="$2"
|
|
local existing_dir="${RUNNER_TEMP}/existing-${asset_name}"
|
|
if gh release view "${RELEASE_TAG}" --repo "${GITHUB_REPOSITORY}" --json assets |
|
|
jq -e --arg name "${asset_name}" 'any(.assets[]?; .name == $name)' >/dev/null; then
|
|
rm -rf "${existing_dir}"
|
|
mkdir -p "${existing_dir}"
|
|
gh release download "${RELEASE_TAG}" --repo "${GITHUB_REPOSITORY}" \
|
|
--pattern "${asset_name}" --dir "${existing_dir}"
|
|
cmp --silent "${source_path}" "${existing_dir}/${asset_name}" || {
|
|
echo "Existing Android release asset ${asset_name} differs from this exact-tag build." >&2
|
|
exit 1
|
|
}
|
|
return
|
|
fi
|
|
gh release upload "${RELEASE_TAG}" "${source_path}#${asset_name}" --repo "${GITHUB_REPOSITORY}"
|
|
}
|
|
|
|
attach_or_verify_asset dist/OpenClaw-Android.apk OpenClaw-Android.apk
|
|
attach_or_verify_asset dist/OpenClaw-Android-SHA256SUMS.txt OpenClaw-Android-SHA256SUMS.txt
|
|
|
|
- name: Verify published Android release assets
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
RELEASE_TAG: ${{ inputs.tag }}
|
|
run: |
|
|
set -euo pipefail
|
|
verify_dir="${RUNNER_TEMP}/verify-android-release"
|
|
mkdir -p "${verify_dir}"
|
|
gh release download "${RELEASE_TAG}" --repo "${GITHUB_REPOSITORY}" \
|
|
--pattern OpenClaw-Android.apk \
|
|
--pattern OpenClaw-Android-SHA256SUMS.txt \
|
|
--dir "${verify_dir}"
|
|
(
|
|
cd "${verify_dir}"
|
|
sha256sum --strict --check OpenClaw-Android-SHA256SUMS.txt
|
|
)
|
|
gh attestation verify "${verify_dir}/OpenClaw-Android.apk" \
|
|
--repo "${GITHUB_REPOSITORY}" \
|
|
--signer-workflow "${GITHUB_REPOSITORY}/.github/workflows/android-release.yml" \
|
|
--source-ref "refs/tags/${RELEASE_TAG}" \
|
|
--deny-self-hosted-runners
|
|
bun apps/android/scripts/build-release-artifacts.ts \
|
|
--verify-apk "${verify_dir}/OpenClaw-Android.apk"
|
|
|
|
release_json="$(gh release view "${RELEASE_TAG}" --repo "${GITHUB_REPOSITORY}" --json assets)"
|
|
expected_names='["OpenClaw-Android-SHA256SUMS.txt","OpenClaw-Android.apk"]'
|
|
actual_names="$(printf '%s' "${release_json}" | jq -c '[.assets[]? | select(.name | startswith("OpenClaw-Android")) | .name] | sort')"
|
|
if [[ "${actual_names}" != "${expected_names}" ]]; then
|
|
echo "Android release asset names do not match the canonical contract." >&2
|
|
exit 1
|
|
fi
|
|
|
|
- name: Remove materialized signing files
|
|
if: ${{ always() }}
|
|
run: rm -rf apps/android/build/release-signing
|
|
|
|
- name: Summary
|
|
env:
|
|
RELEASE_TAG: ${{ inputs.tag }}
|
|
run: |
|
|
{
|
|
echo "## Signed Android APK published"
|
|
echo
|
|
echo "- APK: https://github.com/${GITHUB_REPOSITORY}/releases/download/${RELEASE_TAG}/OpenClaw-Android.apk"
|
|
echo "- Checksums: https://github.com/${GITHUB_REPOSITORY}/releases/download/${RELEASE_TAG}/OpenClaw-Android-SHA256SUMS.txt"
|
|
echo "- Provenance: \`gh attestation verify OpenClaw-Android.apk --repo ${GITHUB_REPOSITORY} --signer-workflow ${GITHUB_REPOSITORY}/.github/workflows/android-release.yml\`"
|
|
} >> "${GITHUB_STEP_SUMMARY}"
|