-
Notifications
You must be signed in to change notification settings - Fork 17
chore(ci): link Unity Cloud builds, test reports, timings, performance and automation from the CI status comment #9713
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from 2 commits
00ea5fd
68875b9
26cb13e
8ee55d7
3b856eb
94e21fc
34aad87
29b7e7b
1c93440
414b0f3
b5186df
4eb8b84
53dc7e3
23e9577
a94f73d
0532ad4
5d2384c
63a0cf4
a9f8e93
536e961
447ab7a
8f4123e
e480a3a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| name: Fetch Unity Cloud Build Links | ||
| description: >- | ||
| Download the unity_build_info_* artifacts of a Unity Cloud Build run and emit | ||
| sanitized markdown linking each build id to its Unity Cloud dashboard page: | ||
| bare table rows for appending to an existing two-column table, and a standalone | ||
| table section for comment bodies that have no table of their own. | ||
|
|
||
| inputs: | ||
| run-id: | ||
| description: Workflow run id of the Unity Cloud Build run whose artifacts to read. | ||
| required: true | ||
| github-token: | ||
| description: Token used to download the run's artifacts. | ||
| required: true | ||
|
|
||
| outputs: | ||
| rows: | ||
| description: >- | ||
| "| Name | Link |"-shaped rows for an existing two-column table; empty when | ||
| no valid build info was found. | ||
| value: ${{ steps.fetch.outputs.rows }} | ||
| section: | ||
| description: >- | ||
| Standalone table (header + rows); empty when no valid build info was found. | ||
| value: ${{ steps.fetch.outputs.section }} | ||
|
|
||
| runs: | ||
| using: composite | ||
| steps: | ||
| - name: Download and sanitize Unity Cloud build info | ||
| id: fetch | ||
| shell: bash | ||
| env: | ||
| GH_TOKEN: ${{ inputs.github-token }} | ||
| RUN_ID: ${{ inputs.run-id }} | ||
| REPO_FULL: ${{ github.repository }} | ||
| run: | | ||
| set -euo pipefail | ||
|
|
||
| # The info files come out of the PR-controlled build workflow, so treat them as | ||
| # untrusted input: accept only a numeric build id and a Unity dashboard URL with | ||
| # a conservative charset before letting them anywhere near a comment body. | ||
| URL_RE='^https://(cloud\.unity\.com|developer\.cloud\.unity3d\.com|dashboard\.unity3d\.com)/[A-Za-z0-9./_%~?=&#-]*$' | ||
| parse_info() { | ||
| local target="$1" | ||
| local dir="ucb_info_${target}" | ||
| REPLY_ID="" | ||
| REPLY_URL="" | ||
| if gh run download "$RUN_ID" \ | ||
| --repo "$REPO_FULL" \ | ||
| --name "unity_build_info_${target}_launcher" \ | ||
| --dir "$dir" 2>"${dir}.err"; then | ||
| REPLY_ID=$(grep -m1 '^BUILD_ID=' "$dir/unity_cloud_build_info.env" | cut -d= -f2- || true) | ||
| REPLY_URL=$(grep -m1 '^DASHBOARD_URL=' "$dir/unity_cloud_build_info.env" | cut -d= -f2- || true) | ||
| [[ "$REPLY_ID" =~ ^[0-9]+$ ]] || REPLY_ID="" | ||
| [[ "$REPLY_URL" =~ $URL_RE ]] || REPLY_URL="" | ||
| else | ||
| # Absence is normal for runs predating the info artifact; still surface the | ||
| # gh error so an auth/permission regression doesn't silently eat the rows. | ||
| echo "note: could not fetch unity_build_info_${target}_launcher: $(tr '\n' ' ' < "${dir}.err")" | ||
| fi | ||
| } | ||
|
|
||
| ROWS="" | ||
| for entry in "windows64:Windows" "macos:Mac"; do | ||
| target="${entry%%:*}" | ||
| label="${entry#*:}" | ||
| parse_info "$target" | ||
| # A URL without a valid id only occurs on a tampered artifact — drop the row | ||
| # rather than render an empty "[#](...)" label. | ||
| if [ -n "$REPLY_ID" ] && [ -n "$REPLY_URL" ]; then | ||
| ROWS+="| Unity Cloud build (${label}) | [#${REPLY_ID}](${REPLY_URL}) |"$'\n' | ||
| elif [ -n "$REPLY_ID" ]; then | ||
| ROWS+="| Unity Cloud build (${label}) | #${REPLY_ID} |"$'\n' | ||
| fi | ||
| done | ||
|
|
||
| SECTION="" | ||
| if [ -n "$ROWS" ]; then | ||
| SECTION="| Name | Link |"$'\n'"| -------- | ----------------------- |"$'\n'"$ROWS" | ||
| fi | ||
|
|
||
| # The payload derives from artifact bytes, so the heredoc delimiter must not be | ||
| # guessable content even though the validation above already forbids newlines. | ||
| DELIM="UCB_EOF_${RANDOM}${RANDOM}_$$" | ||
| { | ||
| echo "rows<<${DELIM}" | ||
| printf '%s' "$ROWS" | ||
| echo "${DELIM}" | ||
| echo "section<<${DELIM}" | ||
| printf '%s' "$SECTION" | ||
| echo "${DELIM}" | ||
| } >> "$GITHUB_OUTPUT" | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -61,7 +61,7 @@ jobs: | |||||
| section: build | ||||||
| github-token: ${{ github.token }} | ||||||
| body: |- | ||||||
|  <img src="https://ui.decentraland.org/decentraland_256x256.png" width="30"> | ||||||
| [](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }}) <img src="https://ui.decentraland.org/decentraland_256x256.png" width="30"> | ||||||
|
|
||||||
| New build in progress, come back later! | ||||||
|
|
||||||
|
|
@@ -71,6 +71,7 @@ jobs: | |||||
| runs-on: ubuntu-latest | ||||||
| outputs: | ||||||
| build-ran: ${{ steps.check.outputs.build-ran }} | ||||||
| player-artifacts: ${{ steps.check.outputs.player-artifacts }} | ||||||
| steps: | ||||||
| - name: Check if build jobs actually ran | ||||||
| id: check | ||||||
|
|
@@ -80,19 +81,32 @@ jobs: | |||||
| REPO: ${{ github.event.repository.name }} | ||||||
| RUN_ID: ${{ github.event.workflow_run.id }} | ||||||
| run: | | ||||||
| # Check if any build artifacts exist (they only exist when Build jobs ran) | ||||||
| ARTIFACT_COUNT=$(gh api "/repos/$OWNER/$REPO/actions/runs/$RUN_ID/artifacts" \ | ||||||
| --jq '[.artifacts[] | select(.name | startswith("Decentraland_"))] | length') | ||||||
| echo "Build artifact count: $ARTIFACT_COUNT" | ||||||
| if [ "$ARTIFACT_COUNT" -gt 0 ]; then | ||||||
| # player-artifacts: Decentraland_* zips exist. comment-success interpolates | ||||||
| # their artifact ids into download URLs, so the success/skipped split must | ||||||
| # keep gating on this and only this. | ||||||
| # build-ran: any evidence a Unity-side build started. unity_build_info_* is | ||||||
| # uploaded as soon as the build id is known, so a build that failed before | ||||||
| # producing player artifacts still posts a failure comment (with the Unity | ||||||
| # Cloud link) instead of leaving the comment stuck on "Pending". | ||||||
| NAMES=$(gh api "/repos/$OWNER/$REPO/actions/runs/$RUN_ID/artifacts" \ | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit, pre-existing but this PR adds two more artifacts per run:
Suggested change
Note |
||||||
| --jq '[.artifacts[].name]') | ||||||
| PLAYER_COUNT=$(jq 'map(select(startswith("Decentraland_"))) | length' <<< "$NAMES") | ||||||
| INFO_COUNT=$(jq 'map(select(startswith("unity_build_info_"))) | length' <<< "$NAMES") | ||||||
| echo "Player artifact count: $PLAYER_COUNT; build info artifact count: $INFO_COUNT" | ||||||
| if [ "$PLAYER_COUNT" -gt 0 ]; then | ||||||
| echo "player-artifacts=true" >> "$GITHUB_OUTPUT" | ||||||
| else | ||||||
| echo "player-artifacts=false" >> "$GITHUB_OUTPUT" | ||||||
| fi | ||||||
| if [ "$PLAYER_COUNT" -gt 0 ] || [ "$INFO_COUNT" -gt 0 ]; then | ||||||
| echo "build-ran=true" >> "$GITHUB_OUTPUT" | ||||||
| else | ||||||
| echo "build-ran=false" >> "$GITHUB_OUTPUT" | ||||||
| fi | ||||||
|
|
||||||
| comment-skipped: | ||||||
| needs: [pre-validation, check-build-ran] | ||||||
| if: github.event.workflow_run.conclusion == 'success' && needs.pre-validation.outputs.pr-number != '' && needs.check-build-ran.outputs.build-ran == 'false' | ||||||
| if: github.event.workflow_run.conclusion == 'success' && needs.pre-validation.outputs.pr-number != '' && needs.check-build-ran.outputs.player-artifacts == 'false' | ||||||
| runs-on: ubuntu-latest | ||||||
| steps: | ||||||
| - name: Checkout CI status action | ||||||
|
|
@@ -109,19 +123,21 @@ jobs: | |||||
| section: build | ||||||
| github-token: ${{ github.token }} | ||||||
| body: |- | ||||||
|  <img src="https://ui.decentraland.org/decentraland_256x256.png" width="30"> | ||||||
| [](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }}) <img src="https://ui.decentraland.org/decentraland_256x256.png" width="30"> | ||||||
|
|
||||||
| Build skipped — no changes detected under `Explorer/`. | ||||||
|
|
||||||
| comment-success: | ||||||
| needs: [pre-validation, check-build-ran] | ||||||
| if: github.event.workflow_run.conclusion == 'success' && needs.pre-validation.outputs.pr-number != '' && needs.check-build-ran.outputs.build-ran == 'true' | ||||||
| if: github.event.workflow_run.conclusion == 'success' && needs.pre-validation.outputs.pr-number != '' && needs.check-build-ran.outputs.player-artifacts == 'true' | ||||||
| runs-on: ubuntu-latest | ||||||
| steps: | ||||||
| - name: Checkout CI status action | ||||||
| uses: actions/checkout@v6 | ||||||
| with: | ||||||
| sparse-checkout: .github/actions/ci-status-comment | ||||||
| sparse-checkout: | | ||||||
| .github/actions/ci-status-comment | ||||||
| .github/actions/ucb-build-links | ||||||
| sparse-checkout-cone-mode: false | ||||||
| persist-credentials: false | ||||||
|
|
||||||
|
|
@@ -230,14 +246,21 @@ jobs: | |||||
| echo "SIZE_REPORT=" >> "$GITHUB_ENV" | ||||||
| fi | ||||||
|
|
||||||
| - name: Fetch Unity Cloud build links | ||||||
| id: ucb | ||||||
| uses: ./.github/actions/ucb-build-links | ||||||
| with: | ||||||
| run-id: ${{ env.PREVIOUS_JOB_ID }} | ||||||
| github-token: ${{ github.token }} | ||||||
|
|
||||||
| - name: Update build section | ||||||
| uses: ./.github/actions/ci-status-comment | ||||||
| with: | ||||||
| pr-number: ${{ needs.pre-validation.outputs.pr-number }} | ||||||
| section: build | ||||||
| github-token: ${{ github.token }} | ||||||
| body: |- | ||||||
|  <img src="https://ui.decentraland.org/decentraland_256x256.png" width="30"> | ||||||
| [](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ env.PREVIOUS_JOB_ID }}) <img src="https://ui.decentraland.org/decentraland_256x256.png" width="30"> | ||||||
|
|
||||||
| Windows and Mac build successful in Unity Cloud! You can find a link to the downloadable artifact below. | ||||||
|
|
||||||
|
|
@@ -250,6 +273,7 @@ jobs: | |||||
| | Download Mac | ${{ github.server_url }}/${{ github.repository }}/suites/${{ env.SUITE_ID }}/artifacts/${{ env.MAC_ARTIFACT_ID }} | | ||||||
| | Download Mac S3 | ${{ format('{0}/{1}/Decentraland_macos.zip', vars.EXPLORER_TEAM_S3_BUCKET_PUBLIC_URL, env.ARTIFACT_S3_DESTINATION_PATH) }} | | ||||||
| | Built on | ${{ env.BUILD_DATE }} | | ||||||
| ${{ steps.ucb.outputs.rows }} | ||||||
|
|
||||||
| ${{ env.SIZE_REPORT }} | ||||||
|
|
||||||
|
|
@@ -286,18 +310,29 @@ jobs: | |||||
| - name: Checkout CI status action | ||||||
| uses: actions/checkout@v6 | ||||||
| with: | ||||||
| sparse-checkout: .github/actions/ci-status-comment | ||||||
| sparse-checkout: | | ||||||
| .github/actions/ci-status-comment | ||||||
| .github/actions/ucb-build-links | ||||||
| sparse-checkout-cone-mode: false | ||||||
| persist-credentials: false | ||||||
|
|
||||||
| - name: Fetch Unity Cloud build links | ||||||
| id: ucb | ||||||
| uses: ./.github/actions/ucb-build-links | ||||||
| with: | ||||||
| run-id: ${{ github.event.workflow_run.id }} | ||||||
| github-token: ${{ github.token }} | ||||||
|
|
||||||
| - name: Update build section | ||||||
| uses: ./.github/actions/ci-status-comment | ||||||
| with: | ||||||
| pr-number: ${{ needs.pre-validation.outputs.pr-number }} | ||||||
| section: build | ||||||
| github-token: ${{ github.token }} | ||||||
| body: |- | ||||||
|  <img src="https://ui.decentraland.org/decentraland_256x256.png" width="30"> | ||||||
| [](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }}) <img src="https://ui.decentraland.org/decentraland_256x256.png" width="30"> | ||||||
|
|
||||||
| Build failed! Check the logs to see what went wrong. | ||||||
| Build failed! Check the [logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }}) to see what went wrong. | ||||||
| If the error repeats please consider the `clean-build` tag. | ||||||
|
|
||||||
| ${{ steps.ucb.outputs.section }} | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The producer now requires
'/builds/' in candidate(build.py:641) but the consumer still accepts any path under the three hosts, and the charset admits?,=,&and%. On the tampered-artifact path that leaves one narrow primitive: a fork can uploadDASHBOARD_URL=https://cloud.unity.com/<something>?next=https%3A%2F%2Fevil.example, which passes validation and renders as[#123](…)in the comment — a maintainer-facing link that reads as first-party. It only goes anywhere if Unity has an open redirect, so this is speculative, not a known bug.Cheap to close by mirroring the producer's own constraint, which also keeps the two validators in sync:
(Worth a quick check against a real dashboard URL first — if the id segment is followed by nothing, the trailing class still matches empty.)