Repository navigation
Publish @aimlapi/openclaw-provider v1.2.1 to ClawHub #2
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: iOS Periphery Dead Code | |
| on: | |
| pull_request: | |
| types: [opened, synchronize, reopened, ready_for_review, converted_to_draft] | |
| workflow_dispatch: | |
| concurrency: | |
| group: ios-periphery-${{ github.workflow }}-${{ 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 iOS 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.0.3 | |
| with: | |
| fetch-depth: 2 | |
| fetch-tags: false | |
| persist-credentials: false | |
| submodules: false | |
| - 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; | |
| } | |
| // pull_request checks out GitHub's synthetic merge commit. Its first | |
| // parent is the exact base tree used to produce the tested result. | |
| const result = await exec.getExecOutput("git", [ | |
| "diff", | |
| "--quiet", | |
| "HEAD^1", | |
| "HEAD", | |
| "--", | |
| "apps/ios/", | |
| "apps/shared/OpenClawKit/Sources/", | |
| "apps/swabble/Sources/SwabbleKit/", | |
| ".github/workflows/ios-periphery.yml", | |
| ".github/workflows/ios-periphery-comment.yml", | |
| "config/swiftformat", | |
| "config/swiftlint.yml", | |
| "scripts/check-swift-tools.sh", | |
| "scripts/format-swift.sh", | |
| "scripts/install-swift-tools.sh", | |
| "scripts/ios-write-swift-filelist.mjs", | |
| "scripts/ios-write-swift-filelist.mts", | |
| "scripts/lint-swift.sh", | |
| ], { 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: | |
| name: Scan iOS dead code | |
| needs: scope | |
| if: ${{ needs.scope.outputs.should-scan == 'true' }} | |
| runs-on: ${{ (github.event_name == 'workflow_dispatch' || github.run_attempt > 1) && 'macos-26' || (github.repository == 'openclaw/openclaw' && 'blacksmith-12vcpu-macos-26' || 'macos-26') }} | |
| timeout-minutes: 45 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 | |
| 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 Swift tooling | |
| run: | | |
| brew update | |
| brew install periphery | |
| swift_tools_dir="$RUNNER_TEMP/openclaw-swift-tools" | |
| # Homebrew's xcodegen bottle can arrive corrupted on runner images; | |
| # use the repo's pinned, checksummed release installer like ci.yml. | |
| ./scripts/install-xcodegen.sh "$swift_tools_dir" | |
| "$swift_tools_dir/xcodegen" --version | |
| ./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: Run Periphery | |
| run: | | |
| set -euo pipefail | |
| output_dir="$RUNNER_TEMP/ios-periphery" | |
| mkdir -p "$output_dir" | |
| cd apps/ios | |
| set +e | |
| periphery scan \ | |
| --config .periphery.yml \ | |
| --strict \ | |
| --format json \ | |
| --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: Build Periphery report | |
| run: | | |
| set -euo pipefail | |
| node <<'NODE' | |
| const fs = require("node:fs"); | |
| const path = require("node:path"); | |
| const outputDir = path.join(process.env.RUNNER_TEMP, "ios-periphery"); | |
| const read = (name) => { | |
| const file = path.join(outputDir, name); | |
| return fs.existsSync(file) ? fs.readFileSync(file, "utf8") : ""; | |
| }; | |
| const status = Number(read("periphery.status").trim() || "1"); | |
| let findings = null; | |
| for (const name of ["periphery.json", "periphery.stdout.json"]) { | |
| try { | |
| const parsed = JSON.parse(read(name)); | |
| if (Array.isArray(parsed)) { | |
| findings = parsed; | |
| break; | |
| } | |
| } catch {} | |
| } | |
| const escapeCommandData = (value) => | |
| String(value ?? "") | |
| .replaceAll("%", "%25") | |
| .replaceAll("\r", "%0D") | |
| .replaceAll("\n", "%0A"); | |
| const escapeCommandProperty = (value) => | |
| escapeCommandData(value) | |
| .replaceAll(":", "%3A") | |
| .replaceAll(",", "%2C"); | |
| const rows = (findings ?? []).map((finding) => { | |
| const location = String(finding.location ?? ""); | |
| const [file, line] = location.split(":"); | |
| const repoFile = file ? `apps/ios/${file}` : ""; | |
| return { | |
| file: repoFile, | |
| line: line || "", | |
| kind: String(finding.kind ?? ""), | |
| name: String(finding.name ?? ""), | |
| }; | |
| }); | |
| for (const row of rows) { | |
| if (!row.file) continue; | |
| const line = row.line ? `,line=${escapeCommandProperty(row.line)}` : ""; | |
| const title = `${row.kind || "Unused code"} ${row.name}`.trim(); | |
| console.log(`::error file=${escapeCommandProperty(row.file)}${line},title=Dead Swift code::${escapeCommandData(title)}`); | |
| } | |
| let shouldFail = "1"; | |
| let summary = ""; | |
| if (findings === null) { | |
| summary = [ | |
| "### iOS Periphery", | |
| "", | |
| "Periphery did not complete. Check the workflow artifact for stdout/stderr.", | |
| ].join("\n"); | |
| } else if (rows.length === 0 && status === 0) { | |
| shouldFail = "0"; | |
| summary = [ | |
| "### iOS Periphery", | |
| "", | |
| "No dead Swift code found.", | |
| ].join("\n"); | |
| } else if (rows.length > 0) { | |
| summary = [ | |
| "### iOS Periphery", | |
| "", | |
| `Found ${rows.length} dead Swift code ${rows.length === 1 ? "symbol" : "symbols"}. See the PR comment or workflow artifact for details.`, | |
| ].join("\n"); | |
| } else { | |
| summary = [ | |
| "### iOS Periphery", | |
| "", | |
| "Periphery exited with a non-zero status before producing findings. Check the workflow artifact for stdout/stderr.", | |
| ].join("\n"); | |
| } | |
| fs.writeFileSync(path.join(outputDir, "should-fail.txt"), `${shouldFail}\n`); | |
| fs.appendFileSync(process.env.GITHUB_STEP_SUMMARY, `${summary.trim()}\n`); | |
| NODE | |
| - name: Upload Periphery report | |
| if: always() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: ios-periphery-dead-code-${{ github.run_id }}-${{ github.run_attempt }} | |
| path: ${{ runner.temp }}/ios-periphery | |
| if-no-files-found: error | |
| retention-days: 14 | |
| - name: Fail on dead code | |
| run: | | |
| set -euo pipefail | |
| test "$(cat "$RUNNER_TEMP/ios-periphery/should-fail.txt")" = "0" |