Skip to content

Commit 4dd2b42

Browse files
authored
Merge pull request #9573 from decentraland/release/2026-08-03
release: 2026-08-03
2 parents bffb71f + ede02eb commit 4dd2b42

221 files changed

Lines changed: 17040 additions & 3819 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/auto-sync-main-to-dev.yml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,14 @@ jobs:
2222
env:
2323
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
2424
run: |
25+
OPEN_PRS=$(gh pr list --base dev --head chore/sync --state open --json number --jq 'length')
26+
if [ "$OPEN_PRS" -ne 0 ]; then
27+
echo "Sync PR already exists — branch was updated, the open PR now points at the new commits."
28+
exit 0
29+
fi
2530
gh pr create \
2631
--base dev \
2732
--head chore/sync \
2833
--title "chore: sync main to dev" \
29-
--body ":crown: *An automated PR*" \
30-
--label auto-pr \
34+
--body $':crown: *An automated PR*\n\n> [!IMPORTANT]\n> This PR must be merged **by an admin, as a merge commit** ("Merge pull request", bypassing the linear-history rule) — **never squash or rebase it**.\n> Squashing rewrites the synced commits, which makes `main` and `dev` diverge silently and surfaces later as release-merge conflicts.\n> The `enforce-approvals` required check stays red until an admin approves this PR.\n\nThis manual procedure has kept `main` and `dev` in sync for years when followed — see #9566 for the mechanical enforcement plan (ruleset + bot bypass) we will apply if a squash slips through again.' \
35+
--label auto-pr

.github/workflows/enforce-group-approvals.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,20 +35,46 @@ jobs:
3535
REPO_OWNER=$(jq -r '.repository.owner.login' <<< "$EVENT_PAYLOAD")
3636
REPO_NAME=$(jq -r '.repository.name' <<< "$EVENT_PAYLOAD")
3737
HEAD_SHA=$(jq -r '.pull_request.head.sha' <<< "$EVENT_PAYLOAD")
38+
HEAD_REF=$(jq -r '.pull_request.head.ref' <<< "$EVENT_PAYLOAD")
3839
3940
echo "✅ PR Number: $PR_NUMBER"
4041
echo "✅ Repository: $REPO_OWNER/$REPO_NAME"
4142
echo "✅ Head SHA: $HEAD_SHA"
43+
echo "✅ Head Ref: $HEAD_REF"
4244
4345
echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_ENV
4446
echo "REPO_OWNER=$REPO_OWNER" >> $GITHUB_ENV
4547
echo "REPO_NAME=$REPO_NAME" >> $GITHUB_ENV
4648
echo "HEAD_SHA=$HEAD_SHA" >> $GITHUB_ENV
49+
echo "HEAD_REF=$HEAD_REF" >> $GITHUB_ENV
4750
4851
- name: Validate Group Approvals
4952
env:
5053
GH_TOKEN: ${{ secrets.ORG_ACCESS_TOKEN }}
5154
run: |
55+
if [ "$HEAD_REF" == "chore/sync" ]; then
56+
echo "🔍 main→dev sync PR detected — requiring an approval from a repository admin."
57+
58+
APPROVERS=$(gh pr view "$PR_NUMBER" --repo "$REPO_OWNER/$REPO_NAME" --json reviews \
59+
--jq '.reviews[] | select(.state == "APPROVED") | .author.login' 2>/dev/null || echo "")
60+
61+
for reviewer in $APPROVERS; do
62+
PERMISSION=$(gh api "/repos/$REPO_OWNER/$REPO_NAME/collaborators/$reviewer/permission" --jq '.permission' 2>/dev/null || echo "none")
63+
if [ "$PERMISSION" == "admin" ]; then
64+
echo "✅ Admin approval found: $reviewer."
65+
echo "APPROVAL_CHECK=passed" >> $GITHUB_ENV
66+
echo "MISSING_MSG=" >> $GITHUB_ENV
67+
exit 0
68+
fi
69+
echo "🔸 $reviewer approved but is not an admin (permission: $PERMISSION)."
70+
done
71+
72+
echo "❌ The sync PR requires an approval from a repository admin, who must merge it as a merge commit (never squash)."
73+
echo "APPROVAL_CHECK=failed" >> $GITHUB_ENV
74+
echo "MISSING_MSG=Waiting on: admin approval (sync PR)" >> $GITHUB_ENV
75+
exit 1
76+
fi
77+
5278
echo "🔍 Checking PR labels..."
5379
LABELS_JSON=$(gh api "/repos/$REPO_OWNER/$REPO_NAME/issues/$PR_NUMBER/labels" --jq '.')
5480

.github/workflows/pr-comment-artifact-url.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ jobs:
8282
8383
Build skipped — no changes detected under `Explorer/`.
8484
85-
[badge]: https://img.shields.io/badge/Build-Skipped-yellow?logo=github&style=for-the-badge
85+
[badge]: https://img.shields.io/badge/Build-Skipped-yellow?logo=unity&logoColor=white&style=for-the-badge
8686
8787
comment-success:
8888
needs: [pre-validation, check-build-ran]
@@ -231,7 +231,7 @@ jobs:
231231
232232
${{ env.SIZE_REPORT }}
233233
234-
[badge]: https://img.shields.io/badge/Build-Success!-3fb950?logo=github&style=for-the-badge
234+
[badge]: https://img.shields.io/badge/Build-Success!-3fb950?logo=unity&logoColor=white&style=for-the-badge
235235
236236
- name: Find latest release
237237
env:
@@ -278,4 +278,4 @@ jobs:
278278
Build failed! Check the logs to see what went wrong.
279279
If the error repeats please consider the `clean-build` tag.
280280
281-
[badge]: https://img.shields.io/badge/Build-Failed!-ff0000?logo=github&style=for-the-badge
281+
[badge]: https://img.shields.io/badge/Build-Failed!-ff0000?logo=unity&logoColor=white&style=for-the-badge

.github/workflows/pr-comment-test-failures.yml

Lines changed: 72 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ on:
1616
permissions:
1717
contents: read
1818
pull-requests: write
19+
actions: read
1920

2021
jobs:
2122
comment:
@@ -60,43 +61,87 @@ jobs:
6061
- name: Compose comment body
6162
if: steps.pr.outputs.pr-number != '' && (steps.download-editmode.outcome == 'success' || steps.download-playmode.outcome == 'success')
6263
id: body
64+
env:
65+
GITHUB_TOKEN: ${{ github.token }}
66+
REPO: ${{ github.repository }}
67+
WORKFLOW_RUN_ID: ${{ github.event.workflow_run.id }}
68+
WORKFLOW_RUN_URL: ${{ github.event.workflow_run.html_url }}
6369
run: |
70+
set -euo pipefail
71+
72+
# Job list of the originating "Unity Test" run, used to deep-link a
73+
# crashed/timed-out suite straight to its job page in the warning line.
74+
JOBS_JSON=$(gh api "/repos/$REPO/actions/runs/$WORKFLOW_RUN_ID/jobs" 2>/dev/null || echo '{"jobs":[]}')
75+
76+
declare -A DISPLAY=( [editmode]=EditMode [playmode]=PlayMode )
77+
78+
status=passed # passed | failed | incomplete
79+
rows=""
80+
warnings=""
81+
failed_list=""
82+
total_failed=0
83+
84+
for mode in editmode playmode; do
85+
file="failed-tests/$mode/failed-tests-$mode.json"
86+
[ -f "$file" ] || continue
87+
disp=${DISPLAY[$mode]}
88+
89+
# A suite that produced no result XML crashed or timed out before finishing.
90+
# Surface it as its own state instead of silently contributing 0 to a green total.
91+
if [ "$(jq -r '.hasResults' "$file")" != "true" ]; then
92+
status=incomplete
93+
job_url=$(jq -r --arg n "Test ($mode)" '.jobs[]? | select(.name==$n) | .html_url' <<< "$JOBS_JSON" | head -1)
94+
[ -n "$job_url" ] || job_url="$WORKFLOW_RUN_URL"
95+
rows="$rows| $disp | ⚠️ No results | — | — | — |"$'\n'
96+
warnings="$warnings⚠️ **$disp** produced no results — the run likely crashed or timed out before finishing. Check the [\`Unity Test / Test ($mode)\`]($job_url) job."$'\n\n'
97+
continue
98+
fi
99+
100+
# The artifact comes from the untrusted pull_request job - never let a
101+
# non-numeric value reach the arithmetic context.
102+
p=$(jq -r '.passed' "$file"); [[ "$p" =~ ^[0-9]+$ ]] || p=0
103+
t=$(jq -r '.total' "$file"); [[ "$t" =~ ^[0-9]+$ ]] || t=0
104+
f=$(jq -r '.failed | length' "$file")
105+
s=$((t - p - f)); if [ "$s" -lt 0 ]; then s=0; fi
106+
107+
if [ "$f" -gt 0 ]; then
108+
if [ "$status" = "passed" ]; then status=failed; fi
109+
total_failed=$((total_failed + f))
110+
rows="$rows| $disp | ❌ $f failed | $p | $f | $s |"$'\n'
111+
names=$(jq -r --arg mode "$mode" '.failed[] | "- [\($mode)] \(. | gsub("[\r\n]"; " "))"' "$file")
112+
failed_list="$failed_list$names"$'\n'
113+
else
114+
rows="$rows| $disp | ✅ Passed | $p | 0 | $s |"$'\n'
115+
fi
116+
done
117+
118+
case "$status" in
119+
incomplete) badge="https://img.shields.io/badge/Tests-Incomplete-d29922?logo=codecov&logoColor=white&style=for-the-badge"; headline="$warnings" ;;
120+
failed) badge="https://img.shields.io/badge/Tests-Failed!-ff0000?logo=codecov&logoColor=white&style=for-the-badge"; headline="Some Unity tests failed ❌" ;;
121+
*) badge="https://img.shields.io/badge/Tests-Passed!-3fb950?logo=codecov&logoColor=white&style=for-the-badge"; headline="All Unity tests passed ✅" ;;
122+
esac
123+
64124
DELIM="EOF_$(uuidgen)"
65125
{
66126
echo "body<<$DELIM"
67127
echo "<!-- test-failures -->"
68-
69-
passed=0
70-
failed=0
71-
any_results=false
72-
for mode in editmode playmode; do
73-
file="failed-tests/$mode/failed-tests-$mode.json"
74-
[ -f "$file" ] || continue
75-
[ "$(jq -r '.hasResults' "$file")" = "true" ] && any_results=true
76-
# The artifact comes from the untrusted pull_request job - never let a
77-
# non-numeric value reach the arithmetic context.
78-
p=$(jq -r '.passed' "$file"); [[ "$p" =~ ^[0-9]+$ ]] || p=0
79-
passed=$((passed + p))
80-
failed=$((failed + $(jq -r '.failed | length' "$file")))
81-
done
82-
83-
if [ "$any_results" = "false" ]; then
84-
echo "**Test results not found** — the test run likely crashed or timed out before producing results; check the \`Unity Tests\` checks."
85-
elif [ "$failed" -eq 0 ]; then
86-
echo "**Tests: $passed passed, 0 failed** ✅"
87-
else
88-
echo "**Tests: $passed passed, $failed failed**"
128+
echo "![badge]"
129+
echo ""
130+
printf '%s\n' "$headline"
131+
echo ""
132+
echo "| TESTS SUITE | Result | Passed | Failed | Skipped |"
133+
echo "| ----------- | ------ | -----: | -----: | ------: |"
134+
printf '%s' "$rows"
135+
if [ "$total_failed" -gt 0 ]; then
89136
echo ""
90-
echo "<details><summary>Failed tests ($failed)</summary>"
137+
echo "<details><summary>Failed tests ($total_failed)</summary>"
91138
echo ""
92-
for mode in editmode playmode; do
93-
file="failed-tests/$mode/failed-tests-$mode.json"
94-
[ -f "$file" ] || continue
95-
jq -r --arg mode "$mode" '.failed[] | "- [\($mode)] \(. | gsub("[\r\n]"; " "))"' "$file"
96-
done
139+
printf '%s' "$failed_list"
97140
echo ""
98141
echo "</details>"
99142
fi
143+
echo ""
144+
echo "[badge]: $badge"
100145
echo "$DELIM"
101146
} >> "$GITHUB_OUTPUT"
102147

.github/workflows/pr-comment-warnings.yml

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,11 @@ jobs:
6161
edit-mode: replace
6262
body: |-
6363
<!-- warning-ratchet -->
64-
![badge] <img src="https://ui.decentraland.org/decentraland_256x256.png" width="30">
64+
![badge]
6565
6666
Lint in progress, come back later!
6767
68-
[badge]: https://img.shields.io/badge/Lint-Pending!-ffff00?logo=github&style=for-the-badge
68+
[badge]: https://img.shields.io/badge/Lint-Pending!-ffff00?logo=jetbrains&logoColor=white&style=for-the-badge
6969
7070
comment:
7171
if: github.event.action == 'completed'
@@ -126,9 +126,16 @@ jobs:
126126
RUN_URL: ${{ github.event.workflow_run.html_url }}
127127
run: |
128128
DELIM="EOF_$(uuidgen)"
129-
LOGO='<img src="https://ui.decentraland.org/decentraland_256x256.png" width="30">'
130-
BADGE_STYLE="?logo=github&style=for-the-badge"
129+
BADGE_STYLE="?logo=jetbrains&logoColor=white&style=for-the-badge"
131130
DETAILS=""
131+
BLOCKED=0
132+
133+
# COUNT/BASELINE come from the artifact produced by the untrusted pull_request job
134+
# (fork-controlled). This step has no `set -e`, and bash's `[` errors out on a
135+
# non-numeric operand and falls through - never let such a value reach a comparison
136+
# or a bash arithmetic context ($(( )) evaluates its operands as expressions).
137+
[[ "$COUNT" =~ ^[0-9]+$ ]] || COUNT=0
138+
[[ "$BASELINE" =~ ^[0-9]+$ ]] || BASELINE=""
132139
133140
if [ "$FOUND" != "true" ]; then
134141
if [ "$CONCLUSION" = "success" ]; then
@@ -152,12 +159,18 @@ jobs:
152159
MSG="**Warnings unchanged: $BASELINE => $COUNT** — allowed on release/hotfix branches."
153160
BADGE="https://img.shields.io/badge/Lint-Passed!-3fb950${BADGE_STYLE}"
154161
else
155-
MSG="**Warnings not reduced: $BASELINE => $COUNT** — remove at least one warning to merge."
162+
need=$((COUNT - BASELINE + 1))
163+
if [ "$need" -eq 1 ]; then noun="warning"; else noun="warnings"; fi
164+
MSG="**Warnings not reduced: $BASELINE => $COUNT** — remove at least $need $noun to merge."
156165
BADGE="https://img.shields.io/badge/Lint-Blocked!-ff0000${BADGE_STYLE}"
166+
BLOCKED=1
157167
fi
158168
159-
# List the warnings/errors in files this PR changed, collapsed by default.
169+
# One collapsible block. Primary: warnings in files this PR changed. Fallback
170+
# (branch clean but still blocked): a general list of easy-to-remove warnings
171+
# from anywhere, so the dev has concrete targets to get under the baseline.
160172
total=$(jq -r '.pr_findings_total // 0' warning-result.json)
173+
[[ "$total" =~ ^[0-9]+$ ]] || total=0
161174
if [ "$total" -gt 0 ]; then
162175
{
163176
echo ""
@@ -175,13 +188,28 @@ jobs:
175188
echo "</details>"
176189
} > details.md
177190
DETAILS="details.md"
191+
elif [ "$BLOCKED" = 1 ]; then
192+
dcount=$(jq -r '.droppable | length' warning-result.json)
193+
if [ "$dcount" -gt 0 ]; then
194+
{
195+
echo ""
196+
echo "<details><summary>No warnings in files changed by this PR — showing general ones you can remove to unblock ($dcount of $COUNT)</summary>"
197+
echo ""
198+
echo '```'
199+
jq -r '.droppable[] | "\(.file):\(.line) \(.rule) \(.message | gsub("[\r\n]"; " "))"' warning-result.json
200+
echo '```'
201+
echo ""
202+
echo "</details>"
203+
} > details.md
204+
DETAILS="details.md"
205+
fi
178206
fi
179207
fi
180208
181209
{
182210
echo "body<<$DELIM"
183211
echo "<!-- warning-ratchet -->"
184-
echo "![badge] $LOGO"
212+
echo "![badge]"
185213
echo ""
186214
echo "$MSG"
187215
[ -n "$DETAILS" ] && cat "$DETAILS"

.github/workflows/test.yml

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,9 @@ jobs:
371371
echo "::warning::Warnings not reduced ($COUNT >= $BASELINE) but 'no-warning-ratchet' label present - bypassing."
372372
exit 0
373373
fi
374-
echo "::error::Compilation warnings ($COUNT) must be strictly LESS than the baseline ($BASELINE). Remove at least one warning to merge (or apply the 'no-warning-ratchet' label)."
374+
need=$((COUNT - BASELINE + 1))
375+
if [ "$need" -eq 1 ]; then noun="warning"; else noun="warnings"; fi
376+
echo "::error::Compilation warnings ($COUNT) must be strictly LESS than the baseline ($BASELINE). Remove at least $need $noun to merge (or apply the 'no-warning-ratchet' label)."
375377
exit 1
376378
377379
# Lower the baseline on merge to dev. Only ever decreases it.
@@ -409,6 +411,7 @@ jobs:
409411
run: |
410412
set -euo pipefail
411413
echo '[]' > pr-findings.json
414+
echo '[]' > droppable.json
412415
echo "total=0" >> "$GITHUB_OUTPUT"
413416
[ -f filtered.json ] || exit 0
414417
@@ -436,6 +439,20 @@ jobs:
436439
echo "total=$total" >> "$GITHUB_OUTPUT"
437440
echo "PR-file findings: $total (listing up to $MAX_FINDINGS)"
438441
442+
# Also surface up to MAX_FINDINGS easy-to-remove warnings from anywhere in the
443+
# codebase, so a blocked PR whose own files are already clean still has concrete
444+
# targets. Redundant/unused findings are listed first (safest mechanical removals).
445+
jq --argjson max "$MAX_FINDINGS" '
446+
[ .[] | {
447+
file: (.locations[0].physicalLocation.artifactLocation.uri // ""),
448+
line: (.locations[0].physicalLocation.region.startLine // 0),
449+
rule: (.ruleId // ""),
450+
message: (.message.text // "")
451+
} ]
452+
| sort_by(if (.rule | test("Redundant|Unused|Unaccessed")) then 0 else 1 end)
453+
| .[:$max]
454+
' filtered.json > droppable.json
455+
439456
# Hand the numbers to the trusted workflow_run companion that posts the PR comment.
440457
# Runs even when the gate above failed (!cancelled), so a failing run still refreshes the comment
441458
# instead of leaving a stale message.
@@ -457,7 +474,8 @@ jobs:
457474
--argjson allow_equal "$IS_RELEASE_OR_HOTFIX" \
458475
--slurpfile findings pr-findings.json \
459476
--argjson findings_total "$FINDINGS_TOTAL" \
460-
'{pr: $pr, count: $count, baseline: (if $baseline == "" then null else ($baseline | tonumber) end), allow_equal: $allow_equal, pr_findings: ($findings[0] // []), pr_findings_total: $findings_total}' \
477+
--slurpfile droppable droppable.json \
478+
'{pr: $pr, count: $count, baseline: (if $baseline == "" then null else ($baseline | tonumber) end), allow_equal: $allow_equal, pr_findings: ($findings[0] // []), pr_findings_total: $findings_total, droppable: ($droppable[0] // [])}' \
461479
> warning-result.json
462480
cat warning-result.json
463481

Explorer/Assets/AddressableAssetsData/AssetGroups/UI.asset

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -840,6 +840,11 @@ MonoBehaviour:
840840
m_ReadOnly: 0
841841
m_SerializedLabels: []
842842
FlaggedDuringContentUpdateRestriction: 0
843+
- m_GUID: ff767aa62c2546829c435addb04026fe
844+
m_Address: DeepLinkParamsWarningScreen
845+
m_ReadOnly: 0
846+
m_SerializedLabels: []
847+
FlaggedDuringContentUpdateRestriction: 0
843848
m_ReadOnly: 0
844849
m_Settings: {fileID: 11400000, guid: fc8a9d2b539788c47a5b305639fa8b34, type: 2}
845850
m_SchemaSet:

Explorer/Assets/DCL/ApplicationGuards/ApplicationDeepLinkGuard.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)