Files
openclaw/.github/workflows/shared-openclawkit-periphery.yml
Peter Steinberger d38fc3e2f0 fix(ci): keep Periphery scope checks off shared API quota (#106090)
* fix(ci): avoid API quota in Periphery scope checks

* fix(ci): preserve Periphery scope contracts
2026-07-13 01:47:43 -07:00

280 lines
10 KiB
YAML

name: Shared OpenClawKit Periphery
on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review, converted_to_draft]
workflow_dispatch:
concurrency:
group: shared-openclawkit-periphery-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
permissions:
contents: read
jobs:
scope:
name: Detect shared OpenClawKit scan scope
runs-on: ubuntu-24.04
outputs:
should-scan: ${{ steps.scope.outputs.should-scan }}
steps:
- name: Checkout
if: github.event_name == 'pull_request' && github.event.pull_request.draft == false
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
fetch-depth: 1
fetch-tags: false
persist-credentials: false
submodules: false
- name: Ensure base commit
if: github.event_name == 'pull_request' && github.event.pull_request.draft == false
uses: ./.github/actions/ensure-base-commit
with:
base-sha: ${{ github.event.pull_request.base.sha }}
fetch-ref: ${{ github.event.pull_request.base.ref }}
- name: Detect changed paths
id: scope
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
with:
script: |
if (context.eventName === "workflow_dispatch") {
core.setOutput("should-scan", "true");
return;
}
if (context.payload.pull_request?.draft) {
core.setOutput("should-scan", "false");
return;
}
const baseSha = context.payload.pull_request?.base?.sha;
if (!baseSha) {
throw new Error("missing pull request base SHA");
}
const result = await exec.getExecOutput("git", [
"diff",
"--quiet",
baseSha,
"HEAD",
"--",
"apps/ios/",
"apps/macos/",
"apps/shared/OpenClawKit/",
".github/workflows/shared-openclawkit-periphery.yml",
"scripts/periphery-intersection.mjs",
"scripts/ios-configure-signing.sh",
"scripts/ios-write-swift-filelist.mjs",
"scripts/ios-write-version-xcconfig.sh",
"test/scripts/periphery-intersection.test.ts",
], { ignoreReturnCode: true, silent: true });
if (result.exitCode !== 0 && result.exitCode !== 1) {
throw new Error(`git diff failed with exit code ${result.exitCode}`);
}
core.setOutput("should-scan", String(result.exitCode === 1));
scan-ios:
name: Scan shared kit from iOS
needs: scope
if: ${{ needs.scope.outputs.should-scan == 'true' }}
runs-on: ${{ github.event_name == 'workflow_dispatch' && 'macos-26' || (github.repository == 'openclaw/openclaw' && 'blacksmith-12vcpu-macos-26' || 'macos-26') }}
timeout-minutes: 45
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
fetch-depth: 1
fetch-tags: false
persist-credentials: false
submodules: false
- name: Verify Xcode
run: |
set -euo pipefail
for xcode_app in /Applications/Xcode_26.5.app /Applications/Xcode-26.5.0.app; do
if [ -d "$xcode_app/Contents/Developer" ]; then
sudo xcode-select -s "$xcode_app/Contents/Developer"
break
fi
done
xcodebuild -version
xcode_version="$(xcodebuild -version | awk 'NR == 1 { print $2 }')"
if [[ "$xcode_version" != 26.* ]]; then
echo "error: expected Xcode 26.x, got $xcode_version" >&2
exit 1
fi
swift --version
- name: Setup Node environment
uses: ./.github/actions/setup-node-env
with:
install-bun: "false"
- name: Install iOS scan tooling
run: |
brew update
brew install xcodegen periphery
swift_tools_dir="$RUNNER_TEMP/openclaw-swift-tools"
./scripts/install-swift-tools.sh "$swift_tools_dir"
echo "$swift_tools_dir" >> "$GITHUB_PATH"
"$swift_tools_dir/swiftformat" --version
"$swift_tools_dir/swiftlint" version
- name: Generate iOS project
run: |
set -euo pipefail
./scripts/ios-configure-signing.sh
./scripts/ios-write-version-xcconfig.sh
node scripts/ios-write-swift-filelist.mjs
cd apps/ios
xcodegen generate
- name: Scan shared kit
run: |
set -euo pipefail
output_dir="$RUNNER_TEMP/shared-periphery-ios"
mkdir -p "$output_dir"
cd apps/ios
set +e
periphery scan \
--config .periphery.yml \
--clean-build \
--format json \
--report-include '../shared/OpenClawKit/Sources/**' \
--retain-files '../shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift' \
--write-results "$output_dir/periphery.json" \
>"$output_dir/periphery.stdout.json" \
2>"$output_dir/periphery.stderr.log"
periphery_status="$?"
set -e
printf '%s\n' "$periphery_status" >"$output_dir/periphery.status"
if [ ! -s "$output_dir/periphery.json" ]; then
cp "$output_dir/periphery.stdout.json" "$output_dir/periphery.json"
fi
- name: Upload iOS consumer report
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: shared-periphery-ios-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ runner.temp }}/shared-periphery-ios
if-no-files-found: error
retention-days: 14
scan-macos:
name: Scan shared kit from macOS
needs: scope
if: ${{ needs.scope.outputs.should-scan == 'true' }}
runs-on: ${{ github.event_name == 'workflow_dispatch' && 'macos-26' || (github.repository == 'openclaw/openclaw' && 'blacksmith-12vcpu-macos-26' || 'macos-26') }}
timeout-minutes: 45
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
fetch-depth: 1
fetch-tags: false
persist-credentials: false
submodules: false
- name: Verify Xcode
run: |
set -euo pipefail
for xcode_app in /Applications/Xcode_26.5.app /Applications/Xcode-26.5.0.app; do
if [ -d "$xcode_app/Contents/Developer" ]; then
sudo xcode-select -s "$xcode_app/Contents/Developer"
break
fi
done
xcodebuild -version
xcode_version="$(xcodebuild -version | awk 'NR == 1 { print $2 }')"
if [[ "$xcode_version" != 26.* ]]; then
echo "error: expected Xcode 26.x, got $xcode_version" >&2
exit 1
fi
swift --version
- name: Install Periphery
run: |
brew update
brew install periphery
- name: Scan shared kit
run: |
set -euo pipefail
output_dir="$RUNNER_TEMP/shared-periphery-macos"
mkdir -p "$output_dir"
cd apps/macos
set +e
periphery scan \
--config .periphery.yml \
--clean-build \
--format json \
--report-include '../shared/OpenClawKit/Sources/**' \
--retain-files '../shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift' \
--write-results "$output_dir/periphery.json" \
>"$output_dir/periphery.stdout.json" \
2>"$output_dir/periphery.stderr.log"
periphery_status="$?"
set -e
printf '%s\n' "$periphery_status" >"$output_dir/periphery.status"
if [ ! -s "$output_dir/periphery.json" ]; then
cp "$output_dir/periphery.stdout.json" "$output_dir/periphery.json"
fi
- name: Upload macOS consumer report
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: shared-periphery-macos-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ runner.temp }}/shared-periphery-macos
if-no-files-found: error
retention-days: 14
intersect:
name: Intersect shared OpenClawKit dead code
needs: [scope, scan-ios, scan-macos]
if: ${{ always() && needs.scope.outputs.should-scan == 'true' && needs.scan-ios.result != 'cancelled' && needs.scan-macos.result != 'cancelled' }}
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
fetch-depth: 1
fetch-tags: false
persist-credentials: false
submodules: false
- name: Download iOS consumer report
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: shared-periphery-ios-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ runner.temp }}/shared-periphery-ios
- name: Download macOS consumer report
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: shared-periphery-macos-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ runner.temp }}/shared-periphery-macos
- name: Intersect exact Swift identities
run: |
node scripts/periphery-intersection.mjs \
--ios-results "$RUNNER_TEMP/shared-periphery-ios/periphery.json" \
--ios-status "$RUNNER_TEMP/shared-periphery-ios/periphery.status" \
--macos-results "$RUNNER_TEMP/shared-periphery-macos/periphery.json" \
--macos-status "$RUNNER_TEMP/shared-periphery-macos/periphery.status" \
--output "$RUNNER_TEMP/shared-periphery-intersection/periphery.json"
- name: Upload shared intersection
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: shared-periphery-intersection-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ runner.temp }}/shared-periphery-intersection
if-no-files-found: warn
retention-days: 14