Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
184 changes: 184 additions & 0 deletions .github/workflows/draftRelease.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
name: Draft Release
on:
workflow_dispatch:
inputs:
version:
description: "Release version, for example 1.26.0"
required: true
type: string
source_branch:
description: "Branch whose build artifacts should be published"
required: false
default: "main"
type: string
run_id:
description: "Build run ID to publish. Defaults to the latest successful build of the source branch."
required: false
default: ""
type: string
dry_run:
description: "Preview the package and release notes without downloading the package or changing releases"
required: false
default: false
type: boolean

jobs:
DraftRelease:
runs-on: ubuntu-latest
permissions:
contents: write
actions: read
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
VERSION: ${{ inputs.version }}
SOURCE_BRANCH: ${{ inputs.source_branch }}
RUN_ID_INPUT: ${{ inputs.run_id }}
DRY_RUN: ${{ inputs.dry_run }}
steps:
- name: Check out repository code
uses: actions/checkout@v4

- name: Validate inputs
run: |
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "::error::Invalid version '$VERSION'. Expected X.Y.Z, for example 1.26.0."
exit 1
fi

- name: Resolve the build to publish
run: |
if [[ -n "$RUN_ID_INPUT" ]]; then
run_id="$RUN_ID_INPUT"
conclusion=$(gh run view "$run_id" --json conclusion --jq .conclusion)
if [[ "$conclusion" != "success" ]]; then
echo "::error::Build run $run_id concluded with '$conclusion'. Only successful builds can be published."
exit 1
fi
else
run_id=$(gh run list --workflow build.yml --branch "$SOURCE_BRANCH" --status success --limit 1 --json databaseId --jq '.[0].databaseId')
fi

if [[ -z "$run_id" || "$run_id" == "null" ]]; then
echo "::error::No successful build.yml run found for branch '$SOURCE_BRANCH'. Wait for CI to pass, or pass an explicit run_id."
exit 1
fi

head_sha=$(gh run view "$run_id" --json headSha --jq .headSha)
echo "Selected package from run $run_id (commit $head_sha, requested branch $SOURCE_BRANCH)."
echo "RUN_ID=$run_id" >> "$GITHUB_ENV"
echo "HEAD_SHA=$head_sha" >> "$GITHUB_ENV"

- name: Inspect artifacts and print the package plan
run: |
artifacts=$(gh api --paginate --slurp "repos/${GH_REPO}/actions/runs/${RUN_ID}/artifacts")
echo "--- artifacts found in run $RUN_ID ---"
jq -r '.[] | .artifacts[] | "\(.name): id=\(.id), size=\(.size_in_bytes) bytes, expired=\(.expired)"' <<< "$artifacts"

matches=$(jq '[.[] | .artifacts[] | select(.name == "Combined Package" and .expired == false)] | length' <<< "$artifacts")
if [[ "$matches" -ne 1 ]]; then
echo "::error::Expected one available artifact named 'Combined Package', found $matches."
exit 1
fi

name=$(jq -r ".name" package.json)
echo "--- package plan: artifact -> release asset ---"
echo "Combined Package -> ${name}-${VERSION}.tgz"

- name: Download and verify the release package
if: ${{ !inputs.dry_run }}
run: |
mkdir -p out
gh run download "$RUN_ID" --name "Combined Package" --dir out

expected="$(jq -r ".name" package.json)-${VERSION}.tgz"
actual=$(find out -maxdepth 1 -name '*.tgz' | wc -l | tr -d ' ')
if [[ "$actual" -ne 1 ]]; then
echo "::error::Expected exactly one .tgz file in the Combined Package artifact, found $actual."
exit 1
fi

found=$(basename "$(find out -maxdepth 1 -name '*.tgz')")
if [[ "$found" != "$expected" ]]; then
echo "::error::Expected package '$expected' but the build produced '$found'. Does package.json's version match the requested release version?"
exit 1
fi

echo "--- SHA256 of the package to be uploaded ---"
sha256sum "out/$found"

- name: Generate the release notes
run: |
if ! changelog=$(node scripts/changelog.js --version "$VERSION" 2> changelog-error.txt); then
reason=$(cat changelog-error.txt)
if [[ "$DRY_RUN" == "true" ]]; then
echo "::warning::Could not extract the changelog for v${VERSION}: ${reason}"
changelog="_Dry run: no changelog section for v${VERSION} was found in CHANGES.md._"
else
echo "::error::Could not extract the changelog for v${VERSION}: ${reason}"
exit 1
fi
fi

CHANGELOG="$changelog" python3 - > notes.md <<'PYTHON'
import os

with open("scripts/release-notes-template.md", encoding="utf-8") as file:
notes = file.read()

notes = notes.replace("{{CHANGELOG}}", os.environ["CHANGELOG"].strip())

print(notes.strip())
PYTHON

echo "--- release notes ---"
cat notes.md

- name: Create or update the draft release (preview only in dry run)
run: |
tag="v${VERSION}"
title="Cesium for Unity v${VERSION}"
echo "Release title: $title"
echo "Release tag: $tag"
echo "Target commit: $HEAD_SHA"

if gh release view "$tag" > /dev/null 2>&1; then
if [[ "$(gh release view "$tag" --json isDraft --jq .isDraft)" != "true" ]]; then
if [[ "$DRY_RUN" != "true" ]]; then
echo "::error::Release $tag already exists and has been published. Refusing to modify it."
exit 1
fi
echo "::warning::Release $tag already exists and has been published. A full run would stop here and refuse to modify it."
else
echo "Would update the existing draft $tag with the release notes printed above."
if [[ "$DRY_RUN" != "true" ]]; then
gh release edit "$tag" --title "$title" --notes-file notes.md --target "$HEAD_SHA"
fi
fi
else
echo "Would create draft $tag with the release notes printed above."
if [[ "$DRY_RUN" != "true" ]]; then
gh release create "$tag" --draft --title "$title" --notes-file notes.md --target "$HEAD_SHA"
fi
fi

echo "Would upload the planned package, replacing an attachment with a matching name."
if [[ "$DRY_RUN" == "true" ]]; then
echo "Dry run: no package downloaded and no release changes made."
else
gh release upload "$tag" out/*.tgz --clobber
fi

- name: Summarize
run: |
{
echo "### Draft release v${VERSION}"
echo
echo "Selected build: [${RUN_ID}](https://github.com/${GH_REPO}/actions/runs/${RUN_ID}) at commit \`${HEAD_SHA}\`."
echo
if [[ "$DRY_RUN" == "true" ]]; then
echo "**Dry run:** no package downloaded and no release changes made. See the logs for artifact metadata, the package plan, and release notes."
else
echo "Review the draft at https://github.com/${GH_REPO}/releases before publishing."
fi
} >> "$GITHUB_STEP_SUMMARY"
53 changes: 30 additions & 23 deletions Documentation~/release-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,40 +18,47 @@ This is the process we follow when releasing a new version of Cesium for Unity o

Cesium for Unity's release package is fairly large, so it can take awhile to upload it to the GitHub Releases page. For that reason, it is helpful to start the process now, even though we haven't tested the release yet.

1. Go to https://github.com/CesiumGS/cesium-unity/actions.
2. Click the most recent build of the `main` branch (it should be near the top). This should be the build for the commit you pushed in the previous section. If it's still running, wait. If it failed, you'll need to fix it.
3. Scroll down to **Artifacts**.
4. Download the artifact named "Combined Package".
5. Extract the `com.cesium.unity-1.21.0.tgz` (or similar) from the `Combined Package.zip` file. On macOS this is a pain to do, because Finder's unzip insists on also extracting the contents of the .tgz file. The best approach is probably to use `unzip` from the command-line.
6. Create a new release by visiting https://github.com/CesiumGS/cesium-unity/releases/new.
7. Leave the release tag blank for now, as we haven't created it yet.
8. Set `Release Title` to "Cesium for Unity v1.21.0", updating the version number as appropriate.
9. Copy the "release notes" section from a previous release, which you can find by visiting https://github.com/CesiumGS/cesium-unity/releases/latest and clicking the Edit button. Be careful not to change or save the previous release!
10. Update the version numbers as appropriate in the top section. Replace the changelog section with the actual changelog entries from this release. Copy it from `CHANGES.md`.
11. Upload the .tgz file by dragging it into the "Attach binaries" box.
12. Click Save Draft. Be careful not to publish it yet.
13. Visit the [releases page](https://github.com/CesiumGS/cesium-unity/releases) and you'll see the new draft release at the top. Expand the "Assets" section you should see the file you uploaded above.
14. Take a moment to verify that the SHA256 listed for each file is identical to the SHA256 for the same file that was printed at the end of the build by the "Print SHA256 of combined package" job. If they are, this proves the release package hasn't been inadvertently modified while you had a copy of it on your local machine. If they're different, stop the release process immediately!

If the uploaded package is later found to have problems during testing, this step will need to be repeated.
The `Draft Release` GitHub Actions workflow does this for you. It copies the package directly from the build artifacts to a draft release, so it never passes through your local machine.

1. Go to the [Draft Release workflow](https://github.com/CesiumGS/cesium-unity/actions/workflows/draftRelease.yml) and click "Run workflow".
2. Enter the release version, for example `1.26.0`, leave the other inputs at their defaults, and click "Run workflow".
- The workflow publishes the most recent _successful_ build of `main`, so it will fail if CI hasn't passed yet.
- To publish a different build, enter its run ID in the `run_id` input.
3. Wait for the workflow to finish. It will:
- Download the combined package from that build, for example `com.cesium.unity-1.26.0.tgz`, and verify its name matches the requested release version.
- Build the release notes from the `CHANGES.md` section for this version.
- Create a draft release named "Cesium for Unity v1.26.0" with the package attached, tagged `v1.26.0` against the exact commit that was built. The tag itself is not created until you publish the release.
4. Visit the [releases page](https://github.com/CesiumGS/cesium-unity/releases), expand the "Assets" section of the new draft, and confirm that the package is attached and that the release notes look correct.
- To preview the notes before running the workflow, use `npm run release-notes -- --version 1.26.0`.

If the uploaded package is later found to have problems during testing, re-run the workflow. It updates the existing draft in place, and refuses to modify the release once it has been published.

We used to download the package, extract it by hand, and then compare the SHA256 of the uploaded asset against the build artifact, to prove that it hadn't been modified while it was on a local machine. The workflow copies the package straight from the build artifact to the release, so there is no longer a local copy to verify.

## Update the "Cesium for Unity Samples" Project

1. Clone a fresh copy of [cesium-unity-samples](https://github.com/CesiumGS/cesium-unity-samples) to a new directory.
2. Before opening the project, go to the Packages folder and open `Packages/manifest.json` in a text editor. Find the `com.cesium.unity` dependency under the `"dependencies"` object and change the version to the version number of the in-progress release.
3. Open `ProjectSettings/ProjectSettings.asset` and find the `bundleVersion` key (under `PlayerSettings`). Set this to the version number of the in-progress release.
4. Create a new Cesium ion token for this release under the "CesiumJS" account:
1. Create a new Cesium ion token for this release under the "CesiumJS" account:
- Visit https://ion.cesium.com.
- Log in using your own `@cesium.com` email address.
- Log in using your own Bentley email address.
- Click your name in the top-right corner and choose "Switch to CesiumJS". If you don't see this option, ask someone on the ion team to add you to the CesiumJS account.
- Click "Access Tokens".
- Create a new token named "Cesium for Unity Samples vX.Y.Z - Delete on MONTH DAY, YEAR", using real values for the version number and date. The date should be the date of the release two months out, using the month's name rather than its number.
- Enable _only_ the `assets:read` and `geocode` scopes on the new token.
- Set the other properties as follows (these should be the defaults):
- `Allow URLs`: "All Urls"
- `Resources`: "All assets"
5. Open `Assets/CesiumSettings/Resources/CesiumIonServers/ion.cesium.com.asset` in a text editor and change the `defaultIonAccessToken` property to the new token you created above.
6. Commit the above changes but do _not_ push it yet. If you do, anyone trying to use cesium-unity-samples will be broken until the new version is published.
2. Run the `update-unity-samples` script. The ion token is only ever read from the `CESIUM_ION_TOKEN` environment variable, never accepted as a command-line argument, so it doesn't end up in your shell history or process list:
```
CESIUM_ION_TOKEN=<the token you just created> npm run update-unity-samples -- --version 1.26.0 --samples-dir /path/to/cesium-unity-samples
```
If `--samples-dir` doesn't exist yet, the script clones a fresh copy of [cesium-unity-samples](https://github.com/CesiumGS/cesium-unity-samples) there. If it's an existing checkout, the script updates it with `git checkout main && git pull --ff-only`, and refuses to touch it if it has uncommitted changes.

It then updates, in the samples checkout:
- `Packages/manifest.json`: the `com.cesium.unity` dependency version.
- `ProjectSettings/ProjectSettings.asset`: `PlayerSettings.bundleVersion`.
- `Assets/CesiumSettings/Resources/CesiumIonServers/ion.cesium.com.asset`: `defaultIonAccessToken`.

Use `--dry-run` first to preview the planned changes (the token value itself is never printed). The script commits the changes locally but does _not_ push them. Do not push until the new package version is published, or anyone trying to use cesium-unity-samples will be broken in the meantime.

## Test the release candidate

Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
},
"scripts": {
"doxygen": "doxygen ./Documentation~/Doxyfile && cd Documentation~ && node copy-images.js && cd ..",
"preinstall": "echo \"ERROR: This is a Unity package, not an npm package. Do not run npm install.\" && exit 1"
"preinstall": "echo \"ERROR: This is a Unity package, not an npm package. Do not run npm install.\" && exit 1",
"release-notes": "node scripts/changelog.js",
"update-unity-samples": "node scripts/update-unity-samples.js"
},
"changelogUrl": "https://github.com/CesiumGS/cesium-unity/blob/main/CHANGES.md",
"documentationUrl": "https://cesium.com/learn/unity/",
Expand Down
Loading
Loading