Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Adolf is a fork/vendored clone of github.com/openclaw/openclaw (v2026.6.11), free to diverge. Tree copied sans upstream .git; upstream remote added for future syncs. Node pinned to 24 (.nvmrc); engines already require >=22.19. Preserves docs/ARCHITECTURE.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
231 lines
8.0 KiB
YAML
231 lines
8.0 KiB
YAML
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
|
|
pull-requests: read
|
|
|
|
jobs:
|
|
scope:
|
|
name: Detect iOS scan scope
|
|
runs-on: ubuntu-24.04
|
|
outputs:
|
|
should-scan: ${{ steps.scope.outputs.should-scan }}
|
|
steps:
|
|
- 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 files = await github.paginate(github.rest.pulls.listFiles, {
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
pull_number: context.payload.pull_request.number,
|
|
per_page: 100,
|
|
});
|
|
const isScanPath = (filename) =>
|
|
typeof filename === "string" && (
|
|
filename.startsWith("apps/ios/") ||
|
|
filename === ".github/workflows/ios-periphery.yml" ||
|
|
filename === ".github/workflows/ios-periphery-comment.yml" ||
|
|
filename === "config/swiftformat" ||
|
|
filename === "config/swiftlint.yml"
|
|
);
|
|
const shouldScan = files.some(
|
|
({ filename, previous_filename: previousFilename }) =>
|
|
isScanPath(filename) || isScanPath(previousFilename)
|
|
);
|
|
core.setOutput("should-scan", String(shouldScan));
|
|
|
|
scan:
|
|
name: Scan iOS dead code
|
|
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 Swift tooling
|
|
run: brew install xcodegen swiftformat swiftlint periphery
|
|
|
|
- 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"
|