mirror of
https://github.com/openclaw/openclaw.git
synced 2026-09-28 14:12:28 +08:00
Add manually triggered iOS Store Release and Android Store Release workflows with the same local release commands. Derive store versions and OpenAI release notes at build time, retain recovery artifacts, and avoid generated metadata commits. Pin queued releases to their dispatch commit, qualify native iOS behavior before signing, and improve Android screenshots and interrupted artifact recovery. Validation includes focused release/planner tests, workflow and type checks, native iOS qualification, Android screenshot capture, and historical release-note exercises. CI failure attribution and remaining hosted release verification are documented in the PR. Closes #158806
246 lines
8.8 KiB
YAML
246 lines
8.8 KiB
YAML
name: macOS Periphery Dead Code
|
|
# Job conclusions cannot distinguish passive draft events from scope loss.
|
|
# Record source admission for the commenter before either job runs.
|
|
run-name: ${{ github.workflow }} [${{ github.event_name == 'workflow_dispatch' && 'manual' || github.event.pull_request.draft == false && 'report' || github.event.action == 'converted_to_draft' && 'draft' || 'passive' }}]
|
|
|
|
on:
|
|
pull_request:
|
|
types: [opened, synchronize, reopened, ready_for_review, converted_to_draft]
|
|
workflow_dispatch:
|
|
|
|
concurrency:
|
|
# Admission precedes scope skips: isolate passive drafts before they can cancel useful work.
|
|
# Keep conversion events in the PR group as an intentional cancellation signal.
|
|
group: >-
|
|
${{ github.event_name == 'pull_request' && github.event.pull_request.draft && github.event.action != 'converted_to_draft'
|
|
&& format('{0}-passive-draft-{1}', github.workflow, github.run_id)
|
|
|| format('macos-periphery-{0}-{1}', 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 macOS 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@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
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/macos/",
|
|
".github/workflows/macos-periphery.yml",
|
|
".github/workflows/ios-periphery-comment.yml",
|
|
"apps/macos/.periphery.yml",
|
|
"scripts/install-periphery.sh",
|
|
"scripts/lib/swift-toolchain.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 macOS dead code
|
|
needs: scope
|
|
if: ${{ needs.scope.outputs.should-scan == 'true' }}
|
|
runs-on: xcode-27
|
|
timeout-minutes: 45
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
fetch-depth: 1
|
|
fetch-tags: false
|
|
persist-credentials: false
|
|
submodules: false
|
|
|
|
- name: Verify Xcode
|
|
run: |
|
|
set -euo pipefail
|
|
source scripts/lib/swift-toolchain.sh
|
|
select_xcode_toolchain
|
|
|
|
- name: Setup Node environment
|
|
uses: ./.github/actions/setup-node-env
|
|
with:
|
|
cache-mode: restore
|
|
install-bun: "false"
|
|
install-deps: "false"
|
|
|
|
- name: Install Mermaid renderer dependencies
|
|
env:
|
|
CI: "true"
|
|
run: >-
|
|
pnpm install --frozen-lockfile --prefer-offline --optional
|
|
--filter '@openclaw/mermaid-renderer...'
|
|
--config.ignore-scripts=false
|
|
--config.engine-strict=false
|
|
--config.enable-pre-post-scripts=true
|
|
--config.side-effects-cache=true
|
|
|
|
- name: Prepare Apple Mermaid assets
|
|
run: node scripts/prepare-apple-mermaid.mjs
|
|
|
|
- name: Install Periphery
|
|
run: |
|
|
periphery_dir="$RUNNER_TEMP/openclaw-periphery"
|
|
./scripts/install-periphery.sh "$periphery_dir"
|
|
echo "$periphery_dir" >> "$GITHUB_PATH"
|
|
|
|
- name: Run Periphery
|
|
run: |
|
|
set -euo pipefail
|
|
output_dir="$RUNNER_TEMP/macos-periphery"
|
|
mkdir -p "$output_dir"
|
|
cd apps/macos
|
|
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, "macos-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/macos/${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 = [
|
|
"### macOS Periphery",
|
|
"",
|
|
"Periphery did not complete. Check the workflow artifact for stdout/stderr.",
|
|
].join("\n");
|
|
} else if (rows.length === 0 && status === 0) {
|
|
shouldFail = "0";
|
|
summary = [
|
|
"### macOS Periphery",
|
|
"",
|
|
"No dead Swift code found.",
|
|
].join("\n");
|
|
} else if (rows.length > 0) {
|
|
summary = [
|
|
"### macOS 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 = [
|
|
"### macOS 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: macos-periphery-dead-code-${{ github.run_id }}-${{ github.run_attempt }}
|
|
path: ${{ runner.temp }}/macos-periphery
|
|
if-no-files-found: error
|
|
retention-days: 14
|
|
|
|
- name: Fail on dead code
|
|
run: |
|
|
set -euo pipefail
|
|
test "$(cat "$RUNNER_TEMP/macos-periphery/should-fail.txt")" = "0"
|