diff --git a/.agents/skills/update-skia/SKILL.md b/.agents/skills/update-skia/SKILL.md index bdd35ecadda..219326fbba5 100644 --- a/.agents/skills/update-skia/SKILL.md +++ b/.agents/skills/update-skia/SKILL.md @@ -131,7 +131,37 @@ E. Ship (Phase 11) > **Note:** When this phase is pre-computed by the automated workflow, you still need to > add the `upstream` remote and fetch `chrome/m{TARGET}` β€” Phase 5 depends on it. -> πŸ›‘ **GATE**: Confirm current milestone, target milestone, and that upstream branch exists. +5. **Determine the base branch (`main` vs a release line).** Most updates target `main` + (newest, in-development line) with `skiasharp` as the mono/skia base. But when the target + milestone is **older** than `main`'s milestone AND a matching release branch exists, the + update targets that release line instead: + + ```bash + # SkiaSharp uses release/..x (e.g. release/4.148.x for m148) + git ls-remote --heads origin "refs/heads/release/*.{TARGET}.x" + # mono/skia mirrors the SAME branch name + git -C externals/skia ls-remote --heads origin "refs/heads/release/*.{TARGET}.x" + ``` + + | Variable | `main` target | release-line target | + |----------|---------------|---------------------| + | `{BASE_BRANCH}` (SkiaSharp) | `main` | `release/.{TARGET}.x` | + | `{SKIA_BASE_BRANCH}` (mono/skia) | `skiasharp` | `release/.{TARGET}.x` | + | `{HEAD_BRANCH}` (both repos) | `skia-sync/m{TARGET}` | `skia-sync/release-.{TARGET}.x` | + + A release-line update is always a **bug-fix-only sync** (`CURRENT == TARGET`): do NOT bump + the milestone/soname/nuget version in Phase 6 β€” only `cgmanifest.json`'s commit hash changes. + + > πŸ›‘ **The release branch must already exist in BOTH repos.** Branch *creation* is owned by + > the release process (`release-branch` skill), not this skill. If the SkiaSharp + > `release/.{TARGET}.x` branch exists but the mono/skia one does NOT, **STOP and fail** β€” + > do not create it here. Ask a human to cut the mono/skia release branch first. + + Use these `{BASE_BRANCH}` / `{SKIA_BASE_BRANCH}` / `{HEAD_BRANCH}` values everywhere below in + place of the hardcoded `main` / `skiasharp` / `skia-sync/m{TARGET}` defaults. + +> πŸ›‘ **GATE**: Confirm current milestone, target milestone, the base branch (main vs release line, +> with the mono/skia base branch confirmed to exist), and that the upstream branch exists. ### Phase 2: Breaking Change Analysis @@ -197,27 +227,30 @@ milestone numbers and paste your breaking change analysis table. The default exp > ⚠️ **This phase creates BOTH branches before making ANY changes.** You may be on a > workflow branch, a feature branch, or a detached HEAD β€” none of which is the right base. -> You MUST branch from `origin/main` (parent) and `origin/skiasharp` (submodule). +> You MUST branch from `origin/{BASE_BRANCH}` (parent) and `origin/{SKIA_BASE_BRANCH}` +> (submodule). For a `main` update those are `origin/main` and `origin/skiasharp`; for a +> release-line update they are `origin/release/.{TARGET}.x` in both repos +> (see Phase 1 step 5). **Parent repo (SkiaSharp):** -1. **Fetch the latest `main`:** +1. **Fetch the latest base branch:** ```bash - git fetch origin main + git fetch origin {BASE_BRANCH} ``` -2. **Create the feature branch from `origin/main`:** +2. **Create the feature branch from `origin/{BASE_BRANCH}`:** ```bash - git checkout -b skia-sync/m{TARGET} origin/main + git checkout -b {HEAD_BRANCH} origin/{BASE_BRANCH} ``` If the branch already exists on the remote, check it out instead: ```bash - git fetch origin skia-sync/m{TARGET} && git checkout skia-sync/m{TARGET} + git fetch origin {HEAD_BRANCH} && git checkout {HEAD_BRANCH} ``` -3. **Verify you are on the correct branch and it is based on `origin/main`:** +3. **Verify you are on the correct branch and it is based on `origin/{BASE_BRANCH}`:** ```bash - git log --oneline -1 origin/main + git log --oneline -1 origin/{BASE_BRANCH} git log --oneline -1 HEAD ``` These should show the same commit (or HEAD should be ahead by only your own commits). @@ -229,34 +262,35 @@ milestone numbers and paste your breaking change analysis table. The default exp cd externals/skia ``` -5. **Align to the SHA that `origin/main` expects** (the submodule tracks the `skiasharp` - branch in mono/skia, NOT `main`): +5. **Align to the SHA that `origin/{BASE_BRANCH}` expects** (the submodule tracks + `{SKIA_BASE_BRANCH}` in mono/skia β€” `skiasharp` for a `main` update, or the matching + `release/.{TARGET}.x` for a release-line update β€” NOT `main`): ```bash - MAIN_SUB_SHA=$(git -C ../.. ls-tree origin/main -- externals/skia | awk '{print $3}') - git fetch origin skiasharp - git checkout "$MAIN_SUB_SHA" + BASE_SUB_SHA=$(git -C ../.. ls-tree origin/{BASE_BRANCH} -- externals/skia | awk '{print $3}') + git fetch origin {SKIA_BASE_BRANCH} + git checkout "$BASE_SUB_SHA" ``` - Verify this SHA is on `origin/skiasharp`: + Verify this SHA is on `origin/{SKIA_BASE_BRANCH}`: ```bash - git branch -r --contains "$MAIN_SUB_SHA" | grep 'origin/skiasharp' + git branch -r --contains "$BASE_SUB_SHA" | grep 'origin/{SKIA_BASE_BRANCH}' ``` 6. **Create the submodule feature branch:** ```bash - git checkout -b skia-sync/m{TARGET} + git checkout -b {HEAD_BRANCH} ``` > ⚠️ **Do NOT skip the SHA alignment step (step 5).** If the submodule is at a different -> SHA than `origin/main` expects, the merge will produce phantom diffs β€” functions that -> already exist on `main` will appear as new or removed. +> SHA than `origin/{BASE_BRANCH}` expects, the merge will produce phantom diffs β€” functions that +> already exist on the base branch will appear as new or removed. > πŸ›‘ **GATE**: Both branches created. Verify: > ```bash > # In parent repo: -> git rev-parse --abbrev-ref HEAD # β†’ skia-sync/m{TARGET} -> git merge-base HEAD origin/main # β†’ should match origin/main tip +> git rev-parse --abbrev-ref HEAD # β†’ {HEAD_BRANCH} +> git merge-base HEAD origin/{BASE_BRANCH} # β†’ should match origin/{BASE_BRANCH} tip > # In submodule: -> git -C externals/skia rev-parse --abbrev-ref HEAD # β†’ skia-sync/m{TARGET} +> git -C externals/skia rev-parse --abbrev-ref HEAD # β†’ {HEAD_BRANCH} > ``` ### Phase 5: Upstream Merge (mono/skia) @@ -520,22 +554,28 @@ before the update can be considered complete. Do not create PRs with only smoke > **Same-milestone bug-fix syncs:** When `CURRENT == TARGET`, only `cgmanifest.json`'s > `commitHash`/`upstream_merge_commit` change. Use PR titles like > `[skia-sync] Merge upstream chrome/m{TARGET} bug fixes` instead of milestone-bump titles. +> A release-line update is always such a sync. + +> **Release-line targets:** Use the `{BASE_BRANCH}` / `{SKIA_BASE_BRANCH}` / `{HEAD_BRANCH}` +> values resolved in Phase 1 step 5. For a `main` update they are `main` / `skiasharp` / +> `skia-sync/m{TARGET}`; for a release-line update they are `release/.{TARGET}.x` (both +> repos) / `skia-sync/release-.{TARGET}.x`. #### PR 1: mono/skia (submodule) | Field | Value | |-------|-------| -| Branch | `skia-sync/m{TARGET}` | -| Target | `skiasharp` | +| Branch | `{HEAD_BRANCH}` | +| Target | `{SKIA_BASE_BRANCH}` | | Title | `[skia-sync] Merge upstream chrome/m{TARGET}` | #### PR 2: mono/SkiaSharp (parent) | Field | Value | |-------|-------| -| Branch | `skia-sync/m{TARGET}` | -| Target | `main` | -| Title | `[skia-sync] Update skia to milestone {TARGET}` | +| Branch | `{HEAD_BRANCH}` | +| Target | `{BASE_BRANCH}` | +| Title | `[skia-sync] Update skia to milestone {TARGET}` (or `Merge upstream chrome/m{TARGET} bug fixes` when `CURRENT == TARGET`) | **Submodule must point to the mono/skia PR branch.** @@ -548,9 +588,9 @@ Both PRs must reference each other. Before proceeding to merge, verify ALL of these: -- [ ] Branch names follow `skia-sync/m{TARGET}` convention in BOTH repos -- [ ] mono/skia PR targets `skiasharp` branch -- [ ] mono/SkiaSharp PR targets `main` branch +- [ ] Branch names follow `{HEAD_BRANCH}` convention in BOTH repos +- [ ] mono/skia PR targets `{SKIA_BASE_BRANCH}` branch +- [ ] mono/SkiaSharp PR targets `{BASE_BRANCH}` branch - [ ] **SkiaSharp's `externals/skia` submodule points to the mono/skia PR branch** (`git submodule status`) - [ ] `cgmanifest.json` updated with new commit hash, version, and chrome_milestone - [ ] `scripts/VERSIONS.txt` updated (ALL version lines, not just milestone) @@ -562,7 +602,7 @@ Before proceeding to merge, verify ALL of these: #### Merge Sequence (CRITICAL) -1. Merge mono/skia PR first β†’ creates new squashed SHA on `skiasharp` +1. Merge mono/skia PR first β†’ creates new squashed SHA on `{SKIA_BASE_BRANCH}` 2. Fetch new SHA in SkiaSharp's submodule 3. Update submodule pointer, push to SkiaSharp PR branch 4. **Only then** merge SkiaSharp PR @@ -572,12 +612,12 @@ Before proceeding to merge, verify ALL of these: Before proceeding past each step, verify: - [ ] mono/skia PR merged -- [ ] Fetched `skiasharp` branch to get new squashed SHA +- [ ] Fetched `{SKIA_BASE_BRANCH}` branch to get new squashed SHA - [ ] Updated SkiaSharp submodule to new SHA (`cd externals/skia && git fetch origin && git checkout {new-sha}`) - [ ] Pushed submodule update to SkiaSharp PR branch - [ ] CI passes on updated SkiaSharp PR - [ ] SkiaSharp PR merged -- [ ] **Submodule points to a commit on `skiasharp` branch** (not an orphaned branch commit) +- [ ] **Submodule points to a commit on `{SKIA_BASE_BRANCH}` branch** (not an orphaned branch commit) > ❌ **NEVER** merge both PRs without updating the submodule in between. > ❌ **NEVER** assume the submodule reference is correct after squash-merging mono/skia. diff --git a/.github/scripts/skia-sync-align-submodule.sh b/.github/scripts/skia-sync-align-submodule.sh new file mode 100755 index 00000000000..8ea0d9f94a7 --- /dev/null +++ b/.github/scripts/skia-sync-align-submodule.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +# +# skia-sync-align-submodule.sh β€” Point externals/skia at the SHA the base branch +# records, before the agent runs. +# +# The agent job checks out the workflow branch (usually main), so the submodule may +# sit at a different SHA than the base branch (`main` or a release line) expects. The +# submodule tracks `$skia_base_branch` in mono/skia (skiasharp for a main sync, +# release/..x for a release sync), so the base-branch submodule SHA +# should be a commit on that branch. +# +# Requires (export these by sourcing skia-sync-detect.sh's output first): +# base_branch parent base branch (main or release/..x) +# skia_base_branch mono/skia base branch (skiasharp or release/..x) + +set -euo pipefail + +BASE_BRANCH="${base_branch:?base_branch not set β€” source skia-sync-detect.sh first}" +SKIA_BASE_BRANCH="${skia_base_branch:?skia_base_branch not set β€” source skia-sync-detect.sh first}" + +echo "Aligning submodule to origin/${BASE_BRANCH} (mono/skia ${SKIA_BASE_BRANCH})" +git fetch origin "$BASE_BRANCH" 2>&1 || true +BASE_SUB_SHA=$(git ls-tree "origin/${BASE_BRANCH}" -- externals/skia | awk '{print $3}') +echo "origin/${BASE_BRANCH} submodule SHA: $BASE_SUB_SHA" +git -C externals/skia fetch origin "$SKIA_BASE_BRANCH" 2>&1 +git -C externals/skia checkout "$BASE_SUB_SHA" 2>&1 +echo "Verifying SHA is on ${SKIA_BASE_BRANCH} branch:" +git -C externals/skia branch -r --contains "$BASE_SUB_SHA" | grep -q "origin/${SKIA_BASE_BRANCH}" \ + && echo " βœ… SHA is on origin/${SKIA_BASE_BRANCH}" \ + || echo " ⚠️ SHA is NOT on origin/${SKIA_BASE_BRANCH} β€” submodule pointer may be stale" diff --git a/.github/scripts/skia-sync-detect.sh b/.github/scripts/skia-sync-detect.sh new file mode 100755 index 00000000000..d5a6d6858b5 --- /dev/null +++ b/.github/scripts/skia-sync-detect.sh @@ -0,0 +1,167 @@ +#!/usr/bin/env bash +# +# skia-sync-detect.sh β€” Resolve which Skia milestone and branch line the upstream +# sync targets. This is the SINGLE source of truth for branch resolution, shared by: +# +# - the pre_activation "Detect milestone" step β†’ run with --gate; writes the job +# outputs consumed by the prompt and the activation gate. +# - the agent job "Align submodule" step β†’ run without --gate and sourced, +# because that job cannot read pre_activation's outputs and must recompute them. +# +# Contract: +# - `key=value` lines (lowercase, matching the workflow's declared outputs) are +# appended to the file named by $SKIA_SYNC_OUT, defaulting to $GITHUB_OUTPUT. +# - All human-readable logs and ::error::/::notice:: workflow commands go to stdout +# (so GitHub renders annotations and the machine output stays clean for sourcing). +# +# Args: +# --mode Which milestone line to track. The workflow resolves +# this from the dispatch input or the triggering cron +# (the cronβ†’mode mapping lives in the workflow, next to +# where the crons are declared). Defaults to `next`. +# --milestone Exact milestone override; when set, mode becomes +# `explicit` and the target is this number. +# --gate Also run the gating checks (does upstream exist? is it +# already merged?) and emit `skip=true` / exit +# accordingly. Only pre_activation needs this. +# +# Inputs (env): +# GITHUB_REPOSITORY owner/repo of this SkiaSharp checkout (default GitHub env) +# GITHUB_REF ref whose scripts/VERSIONS.txt gives main's milestone +# GH_TOKEN token for `gh api` + +set -euo pipefail + +GATE=false +MODE="" +MILESTONE="" +while [ $# -gt 0 ]; do + case "$1" in + --gate) GATE=true; shift ;; + --mode) + [ $# -ge 2 ] || { echo "::error::skia-sync-detect.sh: --mode requires a value"; exit 2; } + MODE="$2"; shift 2 ;; + --milestone) + [ $# -ge 2 ] || { echo "::error::skia-sync-detect.sh: --milestone requires a value"; exit 2; } + MILESTONE="$2"; shift 2 ;; + *) echo "::error::skia-sync-detect.sh: unknown argument '$1'"; exit 2 ;; + esac +done +MODE="${MODE:-next}" + +OUT="${SKIA_SYNC_OUT:-${GITHUB_OUTPUT:?SKIA_SYNC_OUT or GITHUB_OUTPUT must be set}}" +emit() { printf '%s=%s\n' "$1" "$2" >>"$OUT"; } + +# Milestone number from scripts/VERSIONS.txt at the given ref (remote read, no checkout). +milestone_of() { + gh api "repos/${GITHUB_REPOSITORY}/contents/scripts/VERSIONS.txt?ref=$1" \ + --jq '.content' | base64 -d | grep '^libSkiaSharp.*milestone' | awk '{print $NF}' +} + +# -- Mode ------------------------------------------------------------- +# Resolved by the caller (workflow): the cronβ†’mode mapping lives in the workflow, +# next to where the crons are declared. Here it is simply a value in {current,next,latest}. +MAIN_MS=$(milestone_of "$GITHUB_REF") +NEXT=$((MAIN_MS + 1)) +LATEST=$(git ls-remote --heads https://github.com/google/skia.git 'refs/heads/chrome/m*' \ + | sed -n 's|.*refs/heads/chrome/m\([0-9]*\)$|\1|p' | sort -n | tail -1) + +# -- Target ----------------------------------------------------------- +if [ -n "$MILESTONE" ]; then + TARGET="$MILESTONE"; MODE="explicit" +elif [ "$MODE" = latest ]; then + TARGET="$LATEST" +elif [ "$MODE" = current ]; then + TARGET="$MAIN_MS" +else + TARGET="$NEXT" +fi + +# -- Target line: `main` (newest) vs a release/..x maintenance line. +# A maintenance line lives under the SAME branch name in both mono/SkiaSharp and +# mono/skia. We only look for one when TARGET is strictly OLDER than main's milestone; +# the newest line is always served by `main`. The release branch itself is owned by +# the release process (release-branch skill), NOT this sync. +IS_RELEASE=false +BASE_BRANCH=main +SKIA_BASE_BRANCH=skiasharp +HEAD_BRANCH="skia-sync/m${TARGET}" +RELEASE_BRANCH="" +# `2>/dev/null` swallows the "integer expression expected" noise for a non-numeric +# TARGET, which then falls through to the main line. +if [ "$TARGET" -lt "$MAIN_MS" ] 2>/dev/null; then + RELEASE_BRANCH=$(git ls-remote --heads "https://github.com/${GITHUB_REPOSITORY}.git" \ + "refs/heads/release/*.${TARGET}.x" \ + | sed -n 's|.*refs/heads/\(release/[0-9][0-9.]*\.x\)$|\1|p' | sort -u) + # The glob can match more than one major (e.g. release/4.148.x and a stray + # release/14.148.x). Refuse to guess β€” fail so a human disambiguates. + if [ "$(printf '%s' "$RELEASE_BRANCH" | grep -c . || true)" -gt 1 ]; then + matches=$(echo "$RELEASE_BRANCH" | paste -sd' ' -) + echo "::error::Multiple release branches match milestone ${TARGET}: ${matches} β€” cannot disambiguate." + exit 1 + fi +fi +if [ -n "$RELEASE_BRANCH" ]; then + IS_RELEASE=true + BASE_BRANCH="$RELEASE_BRANCH" + SKIA_BASE_BRANCH="$RELEASE_BRANCH" + HEAD_BRANCH="skia-sync/${RELEASE_BRANCH//\//-}" + # The matching mono/skia release branch MUST already exist. + if [ -z "$(git ls-remote --heads https://github.com/mono/skia.git "refs/heads/${SKIA_BASE_BRANCH}" | awk '{print $1}')" ]; then + echo "::error::mono/skia branch '${SKIA_BASE_BRANCH}' does not exist. Release branches are owned by the release process (release-branch skill) β€” create it before running a release sync for m${TARGET}." + exit 1 + fi +fi + +# `current` = milestone of the BASE branch we sync INTO: +# - main line β†’ main's milestone +# - release β†’ that line's milestone (== TARGET β‡’ a bug-fix-only sync) +if [ "$IS_RELEASE" = true ]; then + CURRENT=$(milestone_of "$BASE_BRANCH") +else + CURRENT="$MAIN_MS" +fi + +emit mode "$MODE" +emit next "$NEXT" +emit latest "$LATEST" +emit target "$TARGET" +emit is_release "$IS_RELEASE" +emit base_branch "$BASE_BRANCH" +emit skia_base_branch "$SKIA_BASE_BRANCH" +emit head_branch "$HEAD_BRANCH" +emit current "$CURRENT" + +echo "Resolved: m${TARGET} β†’ ${BASE_BRANCH} (mode=${MODE}, base milestone=m${CURRENT}, latest=m${LATEST}, head=${HEAD_BRANCH}, release=${IS_RELEASE})" + +# Branch derivation is all the agent job needs; gating is pre_activation-only. +$GATE || exit 0 + +# -- Gate: only spin up the (expensive) agent when there is new upstream work ---- +UPSTREAM_SHA=$(git ls-remote https://github.com/google/skia.git "refs/heads/chrome/m${TARGET}" | awk '{print $1}') +if [ -z "$UPSTREAM_SHA" ]; then + echo "::notice::upstream/chrome/m${TARGET} does not exist yet" + [ "$MODE" = explicit ] && exit 1 + emit skip true + exit 0 +fi + +# Compare upstream HEAD against the existing sync branch if present; otherwise, for a +# release line, against the release base branch. A main milestone bump has no sync +# branch yet and always has work, so it proceeds. +SYNC_SHA=$(git ls-remote https://github.com/mono/skia.git "refs/heads/${HEAD_BRANCH}" | awk '{print $1}') +COMPARE_REF="" +if [ -n "$SYNC_SHA" ]; then + COMPARE_REF="$HEAD_BRANCH" +elif [ "$IS_RELEASE" = true ]; then + COMPARE_REF="$SKIA_BASE_BRANCH" +fi +if [ -n "$COMPARE_REF" ]; then + BEHIND=$(gh api "repos/mono/skia/compare/${UPSTREAM_SHA}...${COMPARE_REF}" --jq '.behind_by' 2>/dev/null || echo unknown) + if [ "$BEHIND" = 0 ]; then + echo "::notice::chrome/m${TARGET} already fully merged into ${COMPARE_REF} (upstream HEAD: ${UPSTREAM_SHA:0:12}) β€” skipping" + emit skip true + exit 0 + fi + echo "${COMPARE_REF} exists but is ${BEHIND} commits behind upstream" +fi diff --git a/.github/scripts/skia-sync-push-prs.sh b/.github/scripts/skia-sync-push-prs.sh index 7bc2a8d6bbb..393ae96a1ce 100755 --- a/.github/scripts/skia-sync-push-prs.sh +++ b/.github/scripts/skia-sync-push-prs.sh @@ -6,7 +6,8 @@ # GH_TOKEN β€” PAT with write access to mono/skia and mono/SkiaSharp # # Expected files (written by the agent): -# /tmp/gh-aw/agent/skia-sync-env.sh β€” TARGET and CURRENT vars +# /tmp/gh-aw/agent/skia-sync-env.sh β€” TARGET, CURRENT, and (optionally) IS_RELEASE, +# BASE_BRANCH, SKIA_BASE_BRANCH, HEAD_BRANCH vars # /tmp/gh-aw/agent/skia-sync-skia-summary.md β€” mono/skia PR body # /tmp/gh-aw/agent/skia-sync-skiasharp-summary.md β€” mono/SkiaSharp PR body # @@ -17,6 +18,8 @@ if [ ! -f /tmp/gh-aw/agent/skia-sync-env.sh ]; then echo "No skia-sync-env.sh β€” agent determined no work needed" exit 0 fi +# Written by the agent at runtime; not resolvable at lint time. +# shellcheck source=/dev/null source /tmp/gh-aw/agent/skia-sync-env.sh if [ -z "${TARGET:-}" ]; then @@ -24,7 +27,15 @@ if [ -z "${TARGET:-}" ]; then exit 0 fi -BRANCH="skia-sync/m${TARGET}" +# Branch targets. For a main sync these default to the historical values; for a +# release-line sync the agent provides explicit HEAD_BRANCH / BASE_BRANCH / +# SKIA_BASE_BRANCH that point at the matching release/..x line. +BRANCH="${HEAD_BRANCH:-skia-sync/m${TARGET}}" +SS_BASE="${BASE_BRANCH:-main}" +SKIA_BASE="${SKIA_BASE_BRANCH:-skiasharp}" +IS_RELEASE="${IS_RELEASE:-false}" +echo "Sync branch: $BRANCH | SkiaSharp base: $SS_BASE | mono/skia base: $SKIA_BASE | release: $IS_RELEASE" + SKIA_SUMMARY="" SS_SUMMARY="" [ -f /tmp/gh-aw/agent/skia-sync-skia-summary.md ] && SKIA_SUMMARY=$(cat /tmp/gh-aw/agent/skia-sync-skia-summary.md) @@ -38,6 +49,9 @@ else SS_TITLE="[skia-sync] Update skia to milestone ${TARGET}" SS_BODY_INTRO="Automated Skia milestone bump from m${CURRENT} to m${TARGET}." fi +if [ "$IS_RELEASE" = "true" ]; then + SS_BODY_INTRO="${SS_BODY_INTRO} Targeting release branch \`${SS_BASE}\` (mono/skia \`${SKIA_BASE}\`)." +fi WORKFLOW_LINK="[skia-upstream-sync](https://github.com/${GITHUB_REPOSITORY:-mono/SkiaSharp}/actions/workflows/auto-skia-sync.lock.yml)" @@ -58,7 +72,7 @@ push_skia() { if [ -z "$pr" ]; then echo "Creating mono/skia PR..." gh pr create --repo mono/skia \ - --head "$BRANCH" --base skiasharp \ + --head "$BRANCH" --base "$SKIA_BASE" \ --title "[skia-sync] Merge upstream chrome/m${TARGET}" \ --draft \ --body "Automated upstream merge of \`chrome/m${TARGET}\`. @@ -98,7 +112,7 @@ push_skiasharp() { if [ -z "$ss_pr" ]; then echo "Creating mono/SkiaSharp PR..." gh pr create --repo mono/SkiaSharp \ - --head "$BRANCH" --base main \ + --head "$BRANCH" --base "$SS_BASE" \ --title "$SS_TITLE" \ --draft \ --body "${SS_BODY_INTRO} diff --git a/.github/workflows/auto-skia-sync.lock.yml b/.github/workflows/auto-skia-sync.lock.yml index f154c8d36fc..688dbab6b71 100644 --- a/.github/workflows/auto-skia-sync.lock.yml +++ b/.github/workflows/auto-skia-sync.lock.yml @@ -1,5 +1,5 @@ -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"6c9eafdff2e1546bd8645fa801fb42c9301e9f2305a09e3d7e27ab9a405be143","compiler_version":"v0.71.5","strict":true,"agent_id":"copilot"} -# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN","SKIASHARP_AUTOBUMP_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"b8068426813005612b960b5ab0b8bd2c27142323","version":"v0.71.5"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.40","digest":"sha256:14ff567e8d9d4c2fbc5e55c973488381c71d7e0fdbe72d30ee7b8a738fd86504","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.25.40@sha256:14ff567e8d9d4c2fbc5e55c973488381c71d7e0fdbe72d30ee7b8a738fd86504"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.40","digest":"sha256:2883ca3e5ae9f330cafdd9345bfd4ae17fc8da36c96d4c9a1f76e922b4c45280","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.40@sha256:2883ca3e5ae9f330cafdd9345bfd4ae17fc8da36c96d4c9a1f76e922b4c45280"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.40","digest":"sha256:b084f4a2c771f584ee68084ced52fa6b3245197a1889645d817462d307d3ac51","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.25.40@sha256:b084f4a2c771f584ee68084ced52fa6b3245197a1889645d817462d307d3ac51"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.6","digest":"sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c"},{"image":"ghcr.io/github/github-mcp-server:v1.0.3","digest":"sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"cd20e5a36642ecbadd9af4a79e85a6349ca1d4e6273785f00f9fca538424015a","compiler_version":"v0.71.5","strict":true,"agent_id":"copilot","agent_model":"claude-opus-4.7"} +# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN","SKIASHARP_AUTOBUMP_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"34e114876b0b11c390a56381ad16ebd13914f8d5","version":"v4"},{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"b8068426813005612b960b5ab0b8bd2c27142323","version":"v0.71.5"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.40","digest":"sha256:14ff567e8d9d4c2fbc5e55c973488381c71d7e0fdbe72d30ee7b8a738fd86504","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.25.40@sha256:14ff567e8d9d4c2fbc5e55c973488381c71d7e0fdbe72d30ee7b8a738fd86504"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.40","digest":"sha256:2883ca3e5ae9f330cafdd9345bfd4ae17fc8da36c96d4c9a1f76e922b4c45280","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.40@sha256:2883ca3e5ae9f330cafdd9345bfd4ae17fc8da36c96d4c9a1f76e922b4c45280"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.40","digest":"sha256:b084f4a2c771f584ee68084ced52fa6b3245197a1889645d817462d307d3ac51","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.25.40@sha256:b084f4a2c771f584ee68084ced52fa6b3245197a1889645d817462d307d3ac51"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.6","digest":"sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c"},{"image":"ghcr.io/github/github-mcp-server:v1.0.3","digest":"sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} # ___ _ _ # / _ \ | | (_) # | |_| | __ _ ___ _ __ | |_ _ ___ @@ -36,6 +36,7 @@ # - SKIASHARP_AUTOBUMP_TOKEN # # Custom actions used: +# - actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 # - actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 # - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 # - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -58,75 +59,21 @@ name: "Skia Upstream Sync" - cron: "0 12 * * *" - cron: "0 17 * * *" # steps: # Steps injected into pre-activation job + # - name: Check out detection scripts + # uses: actions/checkout@v4 + # with: + # sparse-checkout: .github/scripts # - env: # GH_TOKEN: ${{ github.token }} - # INPUT_MILESTONE: ${{ github.event.inputs.milestone }} - # INPUT_MODE: ${{ github.event.inputs.mode }} - # SCHEDULE: ${{ github.event.schedule }} + # MILESTONE: ${{ github.event.inputs.milestone }} + # MODE: |- + # ${{ github.event.inputs.mode + # || (github.event.schedule == '0 7 * * *' && 'current') + # || (github.event.schedule == '0 17 * * *' && 'latest') + # || 'next' }} # id: detect # name: Detect milestone - # run: | - # if [ -n "$INPUT_MODE" ]; then - # MODE="$INPUT_MODE" - # elif [ "$SCHEDULE" = "0 7 * * *" ]; then - # MODE=current - # elif [ "$SCHEDULE" = "0 17 * * *" ]; then - # MODE=latest - # else - # MODE=next - # fi - # echo "mode=$MODE" >> "$GITHUB_OUTPUT" - # - # CURRENT=$(gh api "repos/$GITHUB_REPOSITORY/contents/scripts/VERSIONS.txt?ref=$GITHUB_REF" \ - # --jq '.content' | base64 -d | grep '^libSkiaSharp.*milestone' | awk '{print $NF}') - # echo "current=$CURRENT" >> "$GITHUB_OUTPUT" - # - # NEXT=$((CURRENT + 1)) - # echo "next=$NEXT" >> "$GITHUB_OUTPUT" - # - # LATEST=$(git ls-remote --heads https://github.com/google/skia.git 'refs/heads/chrome/m*' \ - # | sed -n 's|.*refs/heads/chrome/m\([0-9]*\)$|\1|p' \ - # | sort -n | tail -1) - # echo "latest=$LATEST" >> "$GITHUB_OUTPUT" - # - # # Explicit milestone input overrides mode - # if [ -n "$INPUT_MILESTONE" ]; then - # TARGET="$INPUT_MILESTONE" - # MODE="explicit" - # elif [ "$MODE" = "latest" ]; then - # TARGET="$LATEST" - # elif [ "$MODE" = "current" ]; then - # TARGET="$CURRENT" - # else - # TARGET="$NEXT" - # fi - # echo "target=$TARGET" >> "$GITHUB_OUTPUT" - # - # UPSTREAM_SHA=$(git ls-remote https://github.com/google/skia.git "refs/heads/chrome/m${TARGET}" | awk '{print $1}') - # if [ -z "$UPSTREAM_SHA" ]; then - # echo "::notice::upstream/chrome/m${TARGET} does not exist yet" - # if [ "$MODE" = "explicit" ]; then - # exit 1 - # fi - # echo "skip=true" >> "$GITHUB_OUTPUT" - # exit 0 - # fi - # - # # Check if the sync branch already contains all upstream commits. - # # This avoids spinning up the expensive agent job when there's nothing new. - # SYNC_SHA=$(git ls-remote https://github.com/mono/skia.git "refs/heads/skia-sync/m${TARGET}" | awk '{print $1}') - # if [ -n "$SYNC_SHA" ]; then - # BEHIND=$(gh api "repos/mono/skia/compare/${UPSTREAM_SHA}...skia-sync/m${TARGET}" \ - # --jq '.behind_by' 2>/dev/null || echo "unknown") - # if [ "$BEHIND" = "0" ]; then - # echo "::notice::chrome/m${TARGET} already fully merged into skia-sync/m${TARGET} (upstream HEAD: ${UPSTREAM_SHA:0:12}) β€” skipping" - # echo "skip=true" >> "$GITHUB_OUTPUT" - # exit 0 - # fi - # echo "Sync branch exists but is ${BEHIND} commits behind upstream" - # fi - # - # echo "Will process: m${TARGET} (mode=${MODE}, current=m${CURRENT}, latest=m${LATEST})" + # run: bash .github/scripts/skia-sync-detect.sh --gate --mode "$MODE" --milestone "$MILESTONE" workflow_dispatch: inputs: aw_context: @@ -196,7 +143,7 @@ jobs: env: GH_AW_INFO_ENGINE_ID: "copilot" GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI" - GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || 'claude-sonnet-4.6' }} + GH_AW_INFO_MODEL: "claude-opus-4.7" GH_AW_INFO_VERSION: "1.0.40" GH_AW_INFO_AGENT_VERSION: "1.0.40" GH_AW_INFO_CLI_VERSION: "v0.71.5" @@ -277,29 +224,33 @@ jobs: GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_BASE_BRANCH: ${{ needs.pre_activation.outputs.base_branch }} GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_CURRENT: ${{ needs.pre_activation.outputs.current }} + GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_HEAD_BRANCH: ${{ needs.pre_activation.outputs.head_branch }} + GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_IS_RELEASE: ${{ needs.pre_activation.outputs.is_release }} + GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_SKIA_BASE_BRANCH: ${{ needs.pre_activation.outputs.skia_base_branch }} GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_TARGET: ${{ needs.pre_activation.outputs.target }} # poutine:ignore untrusted_checkout_exec run: | bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh" { - cat << 'GH_AW_PROMPT_9a538b67d4f587c5_EOF' + cat << 'GH_AW_PROMPT_8ff49e7cd67c6b58_EOF' - GH_AW_PROMPT_9a538b67d4f587c5_EOF + GH_AW_PROMPT_8ff49e7cd67c6b58_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md" cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" - cat << 'GH_AW_PROMPT_9a538b67d4f587c5_EOF' + cat << 'GH_AW_PROMPT_8ff49e7cd67c6b58_EOF' Tools: create_issue, missing_tool, missing_data, noop - GH_AW_PROMPT_9a538b67d4f587c5_EOF + GH_AW_PROMPT_8ff49e7cd67c6b58_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_auto_create_issue.md" - cat << 'GH_AW_PROMPT_9a538b67d4f587c5_EOF' + cat << 'GH_AW_PROMPT_8ff49e7cd67c6b58_EOF' - GH_AW_PROMPT_9a538b67d4f587c5_EOF + GH_AW_PROMPT_8ff49e7cd67c6b58_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md" - cat << 'GH_AW_PROMPT_9a538b67d4f587c5_EOF' + cat << 'GH_AW_PROMPT_8ff49e7cd67c6b58_EOF' The following GitHub context information is available for this workflow: {{#if __GH_AW_GITHUB_ACTOR__ }} @@ -331,19 +282,23 @@ jobs: - **Note**: If a branch you need is not in the list above and is not listed as an additional fetched ref, it has NOT been checked out. For private repositories you cannot fetch it without proper authentication. If the branch is required and not available, exit with an error and ask the user to add it to the `fetch:` option of the `checkout:` configuration (e.g., `fetch: ["refs/pulls/open/*"]` for all open PR refs, or `fetch: ["main", "feature/my-branch"]` for specific branches). - GH_AW_PROMPT_9a538b67d4f587c5_EOF + GH_AW_PROMPT_8ff49e7cd67c6b58_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" - cat << 'GH_AW_PROMPT_9a538b67d4f587c5_EOF' + cat << 'GH_AW_PROMPT_8ff49e7cd67c6b58_EOF' {{#runtime-import .github/workflows/auto-skia-sync.md}} - GH_AW_PROMPT_9a538b67d4f587c5_EOF + GH_AW_PROMPT_8ff49e7cd67c6b58_EOF } > "$GH_AW_PROMPT" - name: Interpolate variables and render templates uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_ENGINE_ID: "copilot" + GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_BASE_BRANCH: ${{ needs.pre_activation.outputs.base_branch }} GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_CURRENT: ${{ needs.pre_activation.outputs.current }} + GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_HEAD_BRANCH: ${{ needs.pre_activation.outputs.head_branch }} + GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_IS_RELEASE: ${{ needs.pre_activation.outputs.is_release }} + GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_SKIA_BASE_BRANCH: ${{ needs.pre_activation.outputs.skia_base_branch }} GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_TARGET: ${{ needs.pre_activation.outputs.target }} with: script: | @@ -365,7 +320,11 @@ jobs: GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} GH_AW_MCP_CLI_SERVERS_LIST: '- `safeoutputs` β€” run `safeoutputs --help` to see available tools' GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }} + GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_BASE_BRANCH: ${{ needs.pre_activation.outputs.base_branch }} GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_CURRENT: ${{ needs.pre_activation.outputs.current }} + GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_HEAD_BRANCH: ${{ needs.pre_activation.outputs.head_branch }} + GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_IS_RELEASE: ${{ needs.pre_activation.outputs.is_release }} + GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_SKIA_BASE_BRANCH: ${{ needs.pre_activation.outputs.skia_base_branch }} GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_TARGET: ${{ needs.pre_activation.outputs.target }} with: script: | @@ -388,7 +347,11 @@ jobs: GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE, GH_AW_MCP_CLI_SERVERS_LIST: process.env.GH_AW_MCP_CLI_SERVERS_LIST, GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED, + GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_BASE_BRANCH: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_BASE_BRANCH, GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_CURRENT: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_CURRENT, + GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_HEAD_BRANCH: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_HEAD_BRANCH, + GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_IS_RELEASE: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_IS_RELEASE, + GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_SKIA_BASE_BRANCH: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_SKIA_BASE_BRANCH, GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_TARGET: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_TARGET } }); @@ -475,14 +438,50 @@ jobs: run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_gh_for_ghe.sh" env: GH_TOKEN: ${{ github.token }} + - name: Start DIFC Proxy + env: + GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + GITHUB_SERVER_URL: ${{ github.server_url }} + DIFC_PROXY_POLICY: '{"allow-only":{"min-integrity":"none","repos":["mono/skia","mono/skiasharp"]}}' + DIFC_PROXY_IMAGE: 'ghcr.io/github/gh-aw-mcpg:v0.3.6' + run: | + bash "${RUNNER_TEMP}/gh-aw/actions/start_difc_proxy.sh" - name: Set up agent output directory run: | mkdir -p /tmp/gh-aw/agent - - name: Align submodule to origin/main - run: "# The checkout uses the workflow branch, so the submodule may be at a\n# different SHA than what origin/main expects. Fix it before the agent runs.\n# Note: the submodule tracks the `skiasharp` branch in mono/skia, so this\n# SHA should be a commit on origin/skiasharp.\nMAIN_SUB_SHA=$(git ls-tree origin/main -- externals/skia | awk '{print $3}')\necho \"origin/main submodule SHA: $MAIN_SUB_SHA\"\ngit -C externals/skia fetch origin skiasharp 2>&1\ngit -C externals/skia checkout \"$MAIN_SUB_SHA\" 2>&1\necho \"Verifying SHA is on skiasharp branch:\"\ngit -C externals/skia branch -r --contains \"$MAIN_SUB_SHA\" | grep -q 'origin/skiasharp' \\\n && echo \" βœ… SHA is on origin/skiasharp\" \\\n || echo \" ⚠️ SHA is NOT on origin/skiasharp β€” submodule pointer may be stale\"\n" + env: + GH_HOST: localhost:18443 + GH_REPO: ${{ github.repository }} + GITHUB_API_URL: https://localhost:18443/api/v3 + GITHUB_GRAPHQL_URL: https://localhost:18443/api/graphql + NODE_EXTRA_CA_CERTS: /tmp/gh-aw/proxy-logs/proxy-tls/ca.crt + - name: Align submodule to the base branch + run: | + OUT=$(mktemp) + SKIA_SYNC_OUT="$OUT" bash .github/scripts/skia-sync-detect.sh --mode "$MODE" --milestone "$MILESTONE" + set -a; . "$OUT"; set +a + bash .github/scripts/skia-sync-align-submodule.sh + env: + GH_HOST: localhost:18443 + GH_REPO: ${{ github.repository }} + GH_TOKEN: ${{ github.token }} + GITHUB_API_URL: https://localhost:18443/api/v3 + GITHUB_GRAPHQL_URL: https://localhost:18443/api/graphql + MILESTONE: ${{ github.event.inputs.milestone }} + MODE: |- + ${{ github.event.inputs.mode + || (github.event.schedule == '0 7 * * *' && 'current') + || (github.event.schedule == '0 17 * * *' && 'latest') + || 'next' }} + NODE_EXTRA_CA_CERTS: /tmp/gh-aw/proxy-logs/proxy-tls/ca.crt - name: Copy push script for post-step run: cp .github/scripts/skia-sync-push-prs.sh /tmp/gh-aw/skia-sync-push-prs.sh - + env: + GH_HOST: localhost:18443 + GH_REPO: ${{ github.repository }} + GITHUB_API_URL: https://localhost:18443/api/v3 + GITHUB_GRAPHQL_URL: https://localhost:18443/api/graphql + NODE_EXTRA_CA_CERTS: /tmp/gh-aw/proxy-logs/proxy-tls/ca.crt - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} @@ -523,6 +522,10 @@ jobs: GH_AW_TRUSTED_USERS_VAR: ${{ vars.GH_AW_GITHUB_TRUSTED_USERS || '' }} GH_AW_APPROVAL_LABELS_VAR: ${{ vars.GH_AW_GITHUB_APPROVAL_LABELS || '' }} run: bash "${RUNNER_TEMP}/gh-aw/actions/parse_guard_list.sh" + - name: Stop DIFC Proxy + if: always() + continue-on-error: true + run: bash "${RUNNER_TEMP}/gh-aw/actions/stop_difc_proxy.sh" - name: Download activation artifact uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: @@ -539,7 +542,7 @@ jobs: name: Install native build dependencies run: |- sudo apt-get update -qq - sudo apt-get install -y clang fontconfig libfontconfig1-dev ninja-build fonts-dejavu-core ttf-ancient-fonts + sudo apt-get install -y clang libc++-dev libc++abi-dev fontconfig libfontconfig1-dev ninja-build fonts-dejavu-core ttf-ancient-fonts fc-cache -f dotnet workload install android --skip-sign-check @@ -550,9 +553,9 @@ jobs: mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs - cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_270162c4ead1b8eb_EOF' + cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_282d84f8ec931f90_EOF' {"create_issue":{"labels":["auto-skia-sync"],"max":1,"title_prefix":"[auto-skia-sync]"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{}} - GH_AW_SAFE_OUTPUTS_CONFIG_270162c4ead1b8eb_EOF + GH_AW_SAFE_OUTPUTS_CONFIG_282d84f8ec931f90_EOF - name: Generate Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | @@ -750,7 +753,7 @@ jobs: mkdir -p /home/runner/.copilot GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_43a43904c0849692_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_e17cf7bba83b9a58_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { "mcpServers": { "github": { @@ -798,7 +801,7 @@ jobs: "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" } } - GH_AW_MCP_CONFIG_43a43904c0849692_EOF + GH_AW_MCP_CONFIG_e17cf7bba83b9a58_EOF - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true @@ -838,7 +841,7 @@ jobs: COPILOT_AGENT_RUNNER_TYPE: STANDALONE COPILOT_API_KEY: dummy-byok-key-for-offline-mode COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - COPILOT_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || 'claude-sonnet-4.6' }} + COPILOT_MODEL: claude-opus-4.7 GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json GH_AW_PHASE: agent GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt @@ -1299,7 +1302,7 @@ jobs: COPILOT_AGENT_RUNNER_TYPE: STANDALONE COPILOT_API_KEY: dummy-byok-key-for-offline-mode COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - COPILOT_MODEL: ${{ vars.GH_AW_MODEL_DETECTION_COPILOT || 'claude-sonnet-4.6' }} + COPILOT_MODEL: claude-opus-4.7 GH_AW_PHASE: detection GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_VERSION: v0.71.5 @@ -1358,13 +1361,17 @@ jobs: runs-on: ubuntu-slim outputs: activated: ${{ steps.check_membership.outputs.is_team_member == 'true' }} + base_branch: ${{ steps.detect.outputs.base_branch }} current: ${{ steps.detect.outputs.current }} detect_result: ${{ steps.detect.outcome }} + head_branch: ${{ steps.detect.outputs.head_branch }} + is_release: ${{ steps.detect.outputs.is_release }} latest: ${{ steps.detect.outputs.latest }} matched_command: '' mode: ${{ steps.detect.outputs.mode }} next: ${{ steps.detect.outputs.next }} setup-trace-id: ${{ steps.setup.outputs.trace-id }} + skia_base_branch: ${{ steps.detect.outputs.skia_base_branch }} skip: ${{ steps.detect.outputs.skip }} target: ${{ steps.detect.outputs.target }} steps: @@ -1390,75 +1397,21 @@ jobs: setupGlobals(core, github, context, exec, io, getOctokit); const { main } = require('${{ runner.temp }}/gh-aw/actions/check_membership.cjs'); await main(); + - name: Check out detection scripts + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + with: + sparse-checkout: .github/scripts - name: Detect milestone id: detect - run: | - if [ -n "$INPUT_MODE" ]; then - MODE="$INPUT_MODE" - elif [ "$SCHEDULE" = "0 7 * * *" ]; then - MODE=current - elif [ "$SCHEDULE" = "0 17 * * *" ]; then - MODE=latest - else - MODE=next - fi - echo "mode=$MODE" >> "$GITHUB_OUTPUT" - - CURRENT=$(gh api "repos/$GITHUB_REPOSITORY/contents/scripts/VERSIONS.txt?ref=$GITHUB_REF" \ - --jq '.content' | base64 -d | grep '^libSkiaSharp.*milestone' | awk '{print $NF}') - echo "current=$CURRENT" >> "$GITHUB_OUTPUT" - - NEXT=$((CURRENT + 1)) - echo "next=$NEXT" >> "$GITHUB_OUTPUT" - - LATEST=$(git ls-remote --heads https://github.com/google/skia.git 'refs/heads/chrome/m*' \ - | sed -n 's|.*refs/heads/chrome/m\([0-9]*\)$|\1|p' \ - | sort -n | tail -1) - echo "latest=$LATEST" >> "$GITHUB_OUTPUT" - - # Explicit milestone input overrides mode - if [ -n "$INPUT_MILESTONE" ]; then - TARGET="$INPUT_MILESTONE" - MODE="explicit" - elif [ "$MODE" = "latest" ]; then - TARGET="$LATEST" - elif [ "$MODE" = "current" ]; then - TARGET="$CURRENT" - else - TARGET="$NEXT" - fi - echo "target=$TARGET" >> "$GITHUB_OUTPUT" - - UPSTREAM_SHA=$(git ls-remote https://github.com/google/skia.git "refs/heads/chrome/m${TARGET}" | awk '{print $1}') - if [ -z "$UPSTREAM_SHA" ]; then - echo "::notice::upstream/chrome/m${TARGET} does not exist yet" - if [ "$MODE" = "explicit" ]; then - exit 1 - fi - echo "skip=true" >> "$GITHUB_OUTPUT" - exit 0 - fi - - # Check if the sync branch already contains all upstream commits. - # This avoids spinning up the expensive agent job when there's nothing new. - SYNC_SHA=$(git ls-remote https://github.com/mono/skia.git "refs/heads/skia-sync/m${TARGET}" | awk '{print $1}') - if [ -n "$SYNC_SHA" ]; then - BEHIND=$(gh api "repos/mono/skia/compare/${UPSTREAM_SHA}...skia-sync/m${TARGET}" \ - --jq '.behind_by' 2>/dev/null || echo "unknown") - if [ "$BEHIND" = "0" ]; then - echo "::notice::chrome/m${TARGET} already fully merged into skia-sync/m${TARGET} (upstream HEAD: ${UPSTREAM_SHA:0:12}) β€” skipping" - echo "skip=true" >> "$GITHUB_OUTPUT" - exit 0 - fi - echo "Sync branch exists but is ${BEHIND} commits behind upstream" - fi - - echo "Will process: m${TARGET} (mode=${MODE}, current=m${CURRENT}, latest=m${LATEST})" + run: bash .github/scripts/skia-sync-detect.sh --gate --mode "$MODE" --milestone "$MILESTONE" env: GH_TOKEN: ${{ github.token }} - INPUT_MILESTONE: ${{ github.event.inputs.milestone }} - INPUT_MODE: ${{ github.event.inputs.mode }} - SCHEDULE: ${{ github.event.schedule }} + MILESTONE: ${{ github.event.inputs.milestone }} + MODE: |- + ${{ github.event.inputs.mode + || (github.event.schedule == '0 7 * * *' && 'current') + || (github.event.schedule == '0 17 * * *' && 'latest') + || 'next' }} safe_outputs: needs: @@ -1475,7 +1428,7 @@ jobs: GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }} GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens }} GH_AW_ENGINE_ID: "copilot" - GH_AW_ENGINE_MODEL: ${{ needs.agent.outputs.model }} + GH_AW_ENGINE_MODEL: "claude-opus-4.7" GH_AW_ENGINE_VERSION: "1.0.40" GH_AW_SAFE_OUTPUTS_STAGED: "true" GH_AW_WORKFLOW_ID: "auto-skia-sync" diff --git a/.github/workflows/auto-skia-sync.md b/.github/workflows/auto-skia-sync.md index 6da17ce9842..a77e3d32cf7 100644 --- a/.github/workflows/auto-skia-sync.md +++ b/.github/workflows/auto-skia-sync.md @@ -1,6 +1,14 @@ --- description: "Daily upstream Skia milestone sync - merges new commits, resolves conflicts, builds, tests, and creates PRs." +# -- Engine ------------------------------------------------------------ +# Use Claude Opus (instead of the default Sonnet) for this workflow: the +# upstream merge/conflict-resolution and build-fix reasoning is hard and +# benefits from the stronger model, despite the higher AI-credit cost. +engine: + id: copilot + model: claude-opus-4.7 + # -- Triggers ---------------------------------------------------------- # Three daily crons: current (7 AM), next (12 PM), latest (5 PM UTC). # Manual dispatch with mode selector. @@ -23,80 +31,34 @@ on: type: string # -- Pre-activation step ------------------------------------------- - # Runs BEFORE the agent job. Detects the target milestone. - # Exit 1 = hard failure (explicit milestone input doesn't exist). + # Runs BEFORE the agent job. Detects the target milestone + branch line. + # All resolution logic lives in the committed .github/scripts/skia-sync-detect.sh + # (the single source of truth, sparse-checked-out below); --gate adds the + # "is there anything to sync?" check. + # Exit 1 = hard failure (explicit milestone input doesn't exist / branch missing). # skip=true output = nothing to sync (graceful skip, workflow shows green). # Outputs are available in the prompt via ${{ needs.pre_activation.outputs.* }}. steps: + - name: Check out detection scripts + uses: actions/checkout@v4 + with: + sparse-checkout: .github/scripts - name: Detect milestone id: detect + # The cronβ†’mode mapping lives HERE, next to the cron declarations: + # 7 AM β†’ current, 5 PM β†’ latest, everything else (12 PM cron + the + # workflow_dispatch default) β†’ next. Staged into env vars rather than + # interpolated straight into `run:`, so the free-form `milestone` input + # can't inject shell β€” the script consumes them as real --mode/--milestone args. env: - INPUT_MODE: ${{ github.event.inputs.mode }} - INPUT_MILESTONE: ${{ github.event.inputs.milestone }} - SCHEDULE: ${{ github.event.schedule }} + MODE: >- + ${{ github.event.inputs.mode + || (github.event.schedule == '0 7 * * *' && 'current') + || (github.event.schedule == '0 17 * * *' && 'latest') + || 'next' }} + MILESTONE: ${{ github.event.inputs.milestone }} GH_TOKEN: ${{ github.token }} - run: | - if [ -n "$INPUT_MODE" ]; then - MODE="$INPUT_MODE" - elif [ "$SCHEDULE" = "0 7 * * *" ]; then - MODE=current - elif [ "$SCHEDULE" = "0 17 * * *" ]; then - MODE=latest - else - MODE=next - fi - echo "mode=$MODE" >> "$GITHUB_OUTPUT" - - CURRENT=$(gh api "repos/$GITHUB_REPOSITORY/contents/scripts/VERSIONS.txt?ref=$GITHUB_REF" \ - --jq '.content' | base64 -d | grep '^libSkiaSharp.*milestone' | awk '{print $NF}') - echo "current=$CURRENT" >> "$GITHUB_OUTPUT" - - NEXT=$((CURRENT + 1)) - echo "next=$NEXT" >> "$GITHUB_OUTPUT" - - LATEST=$(git ls-remote --heads https://github.com/google/skia.git 'refs/heads/chrome/m*' \ - | sed -n 's|.*refs/heads/chrome/m\([0-9]*\)$|\1|p' \ - | sort -n | tail -1) - echo "latest=$LATEST" >> "$GITHUB_OUTPUT" - - # Explicit milestone input overrides mode - if [ -n "$INPUT_MILESTONE" ]; then - TARGET="$INPUT_MILESTONE" - MODE="explicit" - elif [ "$MODE" = "latest" ]; then - TARGET="$LATEST" - elif [ "$MODE" = "current" ]; then - TARGET="$CURRENT" - else - TARGET="$NEXT" - fi - echo "target=$TARGET" >> "$GITHUB_OUTPUT" - - UPSTREAM_SHA=$(git ls-remote https://github.com/google/skia.git "refs/heads/chrome/m${TARGET}" | awk '{print $1}') - if [ -z "$UPSTREAM_SHA" ]; then - echo "::notice::upstream/chrome/m${TARGET} does not exist yet" - if [ "$MODE" = "explicit" ]; then - exit 1 - fi - echo "skip=true" >> "$GITHUB_OUTPUT" - exit 0 - fi - - # Check if the sync branch already contains all upstream commits. - # This avoids spinning up the expensive agent job when there's nothing new. - SYNC_SHA=$(git ls-remote https://github.com/mono/skia.git "refs/heads/skia-sync/m${TARGET}" | awk '{print $1}') - if [ -n "$SYNC_SHA" ]; then - BEHIND=$(gh api "repos/mono/skia/compare/${UPSTREAM_SHA}...skia-sync/m${TARGET}" \ - --jq '.behind_by' 2>/dev/null || echo "unknown") - if [ "$BEHIND" = "0" ]; then - echo "::notice::chrome/m${TARGET} already fully merged into skia-sync/m${TARGET} (upstream HEAD: ${UPSTREAM_SHA:0:12}) β€” skipping" - echo "skip=true" >> "$GITHUB_OUTPUT" - exit 0 - fi - echo "Sync branch exists but is ${BEHIND} commits behind upstream" - fi - - echo "Will process: m${TARGET} (mode=${MODE}, current=m${CURRENT}, latest=m${LATEST})" + run: bash .github/scripts/skia-sync-detect.sh --gate --mode "$MODE" --milestone "$MILESTONE" # -- Pre-activation outputs ------------------------------------------ # Expose detect step outputs for use in the prompt and other jobs. @@ -110,6 +72,10 @@ jobs: target: ${{ steps.detect.outputs.target }} mode: ${{ steps.detect.outputs.mode }} skip: ${{ steps.detect.outputs.skip }} + is_release: ${{ steps.detect.outputs.is_release }} + base_branch: ${{ steps.detect.outputs.base_branch }} + skia_base_branch: ${{ steps.detect.outputs.skia_base_branch }} + head_branch: ${{ steps.detect.outputs.head_branch }} # -- Agent job gate -------------------------------------------------- # Only run the agent if pre-activation succeeded and there's work to do. @@ -181,20 +147,24 @@ steps: - name: Set up agent output directory run: | mkdir -p /tmp/gh-aw/agent - - name: Align submodule to origin/main + - name: Align submodule to the base branch + # Same cronβ†’mode resolution as the pre_activation detect step (see there). The + # agent job can't read pre_activation's outputs (it only `needs:` activation), so + # re-run the same committed detector to recover base_branch / skia_base_branch, + # then align the submodule. skia-sync-detect.sh is the single source of truth. + env: + MODE: >- + ${{ github.event.inputs.mode + || (github.event.schedule == '0 7 * * *' && 'current') + || (github.event.schedule == '0 17 * * *' && 'latest') + || 'next' }} + MILESTONE: ${{ github.event.inputs.milestone }} + GH_TOKEN: ${{ github.token }} run: | - # The checkout uses the workflow branch, so the submodule may be at a - # different SHA than what origin/main expects. Fix it before the agent runs. - # Note: the submodule tracks the `skiasharp` branch in mono/skia, so this - # SHA should be a commit on origin/skiasharp. - MAIN_SUB_SHA=$(git ls-tree origin/main -- externals/skia | awk '{print $3}') - echo "origin/main submodule SHA: $MAIN_SUB_SHA" - git -C externals/skia fetch origin skiasharp 2>&1 - git -C externals/skia checkout "$MAIN_SUB_SHA" 2>&1 - echo "Verifying SHA is on skiasharp branch:" - git -C externals/skia branch -r --contains "$MAIN_SUB_SHA" | grep -q 'origin/skiasharp' \ - && echo " βœ… SHA is on origin/skiasharp" \ - || echo " ⚠️ SHA is NOT on origin/skiasharp β€” submodule pointer may be stale" + OUT=$(mktemp) + SKIA_SYNC_OUT="$OUT" bash .github/scripts/skia-sync-detect.sh --mode "$MODE" --milestone "$MILESTONE" + set -a; . "$OUT"; set +a + bash .github/scripts/skia-sync-align-submodule.sh - name: Copy push script for post-step run: | cp .github/scripts/skia-sync-push-prs.sh /tmp/gh-aw/skia-sync-push-prs.sh @@ -205,9 +175,19 @@ steps: # carry into the chroot β€” the agent must run it itself. pre-agent-steps: - name: Install native build dependencies + # These run on the HOST and are visible to the agent's AWF chroot via the shared + # filesystem. The agent itself CANNOT apt-install anything (no apt inside the chroot, + # and the firewall blocks the Ubuntu archives), so every native build dependency must + # be installed here. + # + # libc++: native/linux/build.cake builds libSkiaSharp/libHarfBuzzSharp with + # `-stdlib=libc++` (clang's LLVM C++ runtime). On the real CI this comes from the + # .NET cross-compilation image; for this host build we must install libc++-dev + + # libc++abi-dev or the compile fails with "cannot find ". Keep this in + # sync with native/linux/build.cake's extra_cflags/extra_ldflags. run: | sudo apt-get update -qq - sudo apt-get install -y clang fontconfig libfontconfig1-dev ninja-build fonts-dejavu-core ttf-ancient-fonts + sudo apt-get install -y clang libc++-dev libc++abi-dev fontconfig libfontconfig1-dev ninja-build fonts-dejavu-core ttf-ancient-fonts fc-cache -f dotnet workload install android --skip-sign-check env: @@ -225,22 +205,48 @@ post-steps: # Skia Upstream Sync -Current: `m${{ needs.pre_activation.outputs.current }}`. -Target: `m${{ needs.pre_activation.outputs.target }}`. -Branch: `skia-sync/m${{ needs.pre_activation.outputs.target }}`. +Base milestone (current): `m${{ needs.pre_activation.outputs.current }}`. +Target milestone: `m${{ needs.pre_activation.outputs.target }}`. +Base branch (SkiaSharp): `${{ needs.pre_activation.outputs.base_branch }}` β€” mono/skia base: `${{ needs.pre_activation.outputs.skia_base_branch }}`. +Sync (head) branch: `${{ needs.pre_activation.outputs.head_branch }}` (same name in both repos). +Release-line sync: `${{ needs.pre_activation.outputs.is_release }}`. + +> **About the base branch.** Most syncs target `main` (the newest, in-development line) with +> `skiasharp` as the mono/skia base. When an older milestone is requested AND a matching +> `release/..x` branch exists, this is a **release-line sync**: both PRs target +> that release branch instead (mono/skia base = the same `release/..x` branch, +> which the release process guarantees exists). In that case `current == target`, so it is always a +> **bug-fix-only sync** β€” do NOT bump the milestone, soname, or nuget versions; only the upstream +> merge + `cgmanifest.json` commit hash change. Use the base/head branch values above everywhere +> instead of hardcoding `main`/`skiasharp`/`skia-sync/m{target}`. **Read `.agents/skills/update-skia/SKILL.md` and follow Phases 2-10.** Notes specific to this automated workflow: - **Phase 1 is pre-computed** (above). Skip it β€” but you still need to add the `upstream` remote and fetch `chrome/m${{ needs.pre_activation.outputs.target }}` (Phase 1 step 4) since Phase 5 depends on it. - **First thing**: run `dotnet tool restore` (pre-agent-steps can't do this for the chroot). -- **Phase 4**: Before creating a fresh branch, check if `origin/skia-sync/m${{ needs.pre_activation.outputs.target }}` already exists. - If so, check it out β€” the pre-activation step already verified new upstream commits exist. - Even when current == target, there may be new upstream bug-fix commits β€” a matching milestone does NOT mean no work. - **Skip Phase 4 step 5** (submodule SHA alignment) β€” the pre-agent step already aligned it. +- **Phase 4**: The parent base branch is `origin/${{ needs.pre_activation.outputs.base_branch }}` and the + submodule base branch is mono/skia `${{ needs.pre_activation.outputs.skia_base_branch }}` β€” use these in + place of `origin/main` / `skiasharp` for every step. The sync branch in BOTH repos is + `${{ needs.pre_activation.outputs.head_branch }}`. Before creating a fresh branch, check if + `origin/${{ needs.pre_activation.outputs.head_branch }}` already exists; if so, check it out β€” the + pre-activation step already verified new upstream commits exist. Even when current == target there may + be new upstream bug-fix commits β€” a matching milestone does NOT mean no work. + **Skip Phase 4 step 5** (submodule SHA alignment) β€” the pre-agent step already aligned the submodule to + the base branch's pointer (on mono/skia `${{ needs.pre_activation.outputs.skia_base_branch }}`). Branch from the current HEAD when creating the submodule feature branch in step 6. +- **Phase 6 (version files)**: For a release-line sync (`is_release == true`, so `current == target`) this is a + bug-fix-only sync β€” keep the release line's milestone/soname/nuget versions unchanged; the only expected + parent-repo change is `cgmanifest.json`'s commit hash. Do NOT advance the milestone. - **Build platform**: use Linux x64 (`dotnet cake --target=externals-linux --arch=x64`). Clang is pre-configured via env vars. This also applies to Phase 10 if a native rebuild is needed. +- **Native build environment is fully provisioned by the workflow** (clang, `libc++-dev`/`libc++abi-dev`, + fontconfig, ninja). Do NOT modify any native build files (`native/**/build.cake`, `scripts/infra/native/**`) + and do NOT change compiler/linker flags (e.g. `-stdlib=libc++`) to work around a build error. You also CANNOT + install packages (no apt/sudo inside the sandbox, and the firewall blocks OS package mirrors). If a native + build genuinely fails because a dependency is missing from the host, that is a workflow bug: STOP, do not hack + the build, and record it in the mono/SkiaSharp summary under "items needing human attention" so the + `Install native build dependencies` step can be fixed. - **NEVER run `externals-download`** in this workflow β€” not even for debugging or baseline comparison. Build from source only. - **Phase 9 reminder**: a green C# build is NOT sufficient - run the new-function diff check from Phase 9 step 1. - **Phase 11 β€” do NOT execute it.** Replace it entirely with the file writes below. @@ -257,6 +263,10 @@ After Phase 10, write these files: cat > /tmp/gh-aw/agent/skia-sync-env.sh << EOF TARGET=${{ needs.pre_activation.outputs.target }} CURRENT=${{ needs.pre_activation.outputs.current }} + IS_RELEASE=${{ needs.pre_activation.outputs.is_release }} + BASE_BRANCH=${{ needs.pre_activation.outputs.base_branch }} + SKIA_BASE_BRANCH=${{ needs.pre_activation.outputs.skia_base_branch }} + HEAD_BRANCH=${{ needs.pre_activation.outputs.head_branch }} EOF ``` @@ -269,5 +279,5 @@ After Phase 10, write these files: All files written to `/tmp/gh-aw/agent/` are automatically uploaded as workflow artifacts. Write test output there too (`/tmp/gh-aw/agent/test-output.txt`) so failures can be inspected after the run. -Commit submodule changes inside `externals/skia` on `skia-sync/m${{ needs.pre_activation.outputs.target }}`. -Commit parent repo changes on `skia-sync/m${{ needs.pre_activation.outputs.target }}` in the parent. +Commit submodule changes inside `externals/skia` on `${{ needs.pre_activation.outputs.head_branch }}`. +Commit parent repo changes on `${{ needs.pre_activation.outputs.head_branch }}` in the parent.