Reusable workflows and composite actions maintained for Cysharp repositories.
This README reflects the current public definitions under .github/workflows and .github/actions.
Test and maintenance workflows prefixed with _ are intentionally omitted here.
| Workflow | Purpose | Key inputs / notes |
|---|---|---|
actions-timeline |
Post workflow timing information using Kesin11/actions-timeline. |
Requires secrets.github-token. |
benchmark-loader |
Authorize benchmark requests and generate a benchmark execution matrix from a loader config. | Inputs: benchmark-name-prefix, benchmark-config-path. Outputs: is-benchmarkable, matrix. |
benchmark-execute |
Provision benchmark infrastructure, execute benchmark matrix entries, and update PR/issue progress comments. | Inputs include benchmark-name, benchmark-config-path, branch. Requires the benchmark environment and 1Password/Azure secrets. |
benchmark-cleanup |
Clean benchmark environments on schedule or on demand. | Inputs: state, try-redeploy, no-delete. Scheduled hourly in this repo. |
clean-packagejson-branch |
Delete a temporary branch created by release/update automation. | Only deletes non-default branches created by github-actions[bot]. Input: branch. |
create-release |
Validate a tag, create a GitHub release and optionally upload release assets. | Inputs include commit-id, tag, dry-run, release-upload, release-asset-path, download-run-id. |
dd-event-post |
Post an event to Datadog, typically for PR merge notifications. | Inputs include title, text, event, additional-tags, alert-type. |
increment-version |
Increment a semantic version string and expose the computed version. | Inputs: tag, type, optional prefix, suffix, ref. Output: version. |
prevent-github-change |
Fail PRs from forks when they modify .github/**/*.yml or .github/**/*.yaml. |
Intended for policy enforcement around GitHub configuration changes. |
pr-harness |
Run protected .github/ checks, dependency review, and forbidden Unicode checks. |
Trigger on edited as well as code-changing PR events so title/body edits are rescanned. |
stale-issue |
Mark and close stale issues and PRs using actions/stale. |
Current defaults: stale after 180 days, close 30 days later. |
update-packagejson |
Normalize a release tag, update version-bearing files, optionally run project-specific dotnet run -- --version {tag}, and push the result. |
Supports package.json, plugin.cfg, and Directory.Build.props. Outputs: branch-name, is-branch-created, sha. |
validate-release |
Validate Git tag syntax, NuGet version syntax and release ordering before version updates or builds. | Input: tag. Outputs: original tag and version with the leading v removed. Requires only contents: read. |
jobs:
timeline:
uses: Cysharp/Actions/.github/workflows/actions-timeline.yaml@main
secrets:
# actions-timeline.yaml requires this exact secret name.
github-token: ${{ secrets.GITHUB_TOKEN }}jobs:
loader:
uses: Cysharp/Actions/.github/workflows/benchmark-loader.yaml@main
with:
# Prefix is used to construct benchmark environment names.
# MagicOnion uses issue/run context to avoid name collisions.
benchmark-name-prefix: myrepo-pr-${{ github.event.number }}
# Loader config path consumed by benchmark-loader2matrix.
benchmark-config-path: .github/benchmark-loader.yaml
benchmark:
needs: [loader]
# loader output is a string ('true'/'false'), compare explicitly.
if: ${{ needs.loader.outputs.is-benchmarkable == 'true' }}
strategy:
fail-fast: false
# Matrix JSON is produced by benchmark-loader output.
matrix: ${{ fromJson(needs.loader.outputs.matrix) }}
uses: Cysharp/Actions/.github/workflows/benchmark-execute.yaml@main
with:
# Key names come from your loader config output schema.
benchmark-name: ${{ matrix.benchmark-name }}
benchmark-config-path: ${{ matrix.benchmark-config-path }}
branch: ${{ matrix.branch }}
# benchmark-execute needs Azure/1Password-related secrets.
secrets: inheritjobs:
cleanup:
uses: Cysharp/Actions/.github/workflows/benchmark-cleanup.yaml@main
with:
# Failed/Succeeded/All depending on your cleanup policy.
state: Failed
# Scheduled runs can optionally redeploy before cleanup.
try-redeploy: false
# Keep false for normal cleanup (true means dry-maintenance mode).
no-delete: falsejobs:
cleanup:
permissions:
# Required because the workflow deletes remote branches.
contents: write
uses: Cysharp/Actions/.github/workflows/clean-packagejson-branch.yaml@main
with:
# Usually pass update-packagejson output branch-name.
branch: test-release/1.2.3jobs:
create-release:
permissions:
contents: write
uses: Cysharp/Actions/.github/workflows/create-release.yaml@main
with:
# Empty means current checked out commit.
commit-id: ""
# Raw tag like 1.2.3 (workflow validates/normalizes internally).
tag: ${{ inputs.tag }}
# true keeps dry-run behavior and cleanup path.
dry-run: ${{ inputs.dry-run }}
# Guard to prevent accidentally releasing older tags.
require-validation: true
# If true, release-asset-path must be provided.
release-upload: true
release-asset-path: |
./MyUnityPackage/MyUnityPackage.unitypackage
# v{0} -> v1.2.3, {0} -> 1.2.3.
release-format: v{0}
# Empty means download artifacts from current run.
download-run-id: ""commit-id must be the SHA used by the build jobs (the output SHA of update-packagejson when used). Existing lightweight and annotated tags must resolve to that SHA. An existing release causes failure. The workflow calls prepare-release using the existing CLI resolution (the downloaded action binary, or dotnet run as a fallback) and records resource ownership outside the artifact directory; dry runs and failures invoke cleanup-release using that journal. Cleanup preserves pre-existing tags and refuses to delete changed tags or published releases. Dry runs still create a draft and upload requested assets, then clean up immediately. Do not manually modify or publish a draft while its creating job is running.
jobs:
post-dd-event:
# Typical trigger pattern: only post when PR was actually merged.
if: ${{ github.event.pull_request.merged == true }}
uses: Cysharp/Actions/.github/workflows/dd-event-post.yaml@main
with:
# Keep consistent with dashboard aggregation keys/tags.
event: pr-merged
alert-type: info
# Required because workflow loads DD_API_KEY via 1Password.
secrets: inheritjobs:
new-version:
uses: Cysharp/Actions/.github/workflows/increment-version.yaml@main
with:
# Use default branch when you want to continue development after release.
ref: ${{ github.event.repository.default_branch }}
# Released tag to increment from.
tag: 1.2.3
# major | minor | patch
type: patch
prefix: ""
# Common post-release convention.
suffix: -devname: PR Harness
on:
pull_request:
# `edited` is required to rescan PR title/body changes.
types: [opened, synchronize, reopened, edited]
jobs:
pr-harness:
permissions:
contents: read
pull-requests: read
uses: Cysharp/Actions/.github/workflows/pr-harness.yaml@mainThe Unicode check is implemented by the CysharpActions scan-pr-unicode CLI command and invoked directly from pr-harness; there is no standalone Unicode workflow or composite action. The checked-in Linux binary is updated only by the release workflow. It scans the PR title, PR body, changed file names, and the complete contents of changed .cs and .csx files in the checked-out working tree. Changed paths are determined by diffing GitHub's test merge commit (refs/pull/<number>/merge) against its first parent, the current base branch tip, so only the merge commit and its two parents (fetch-depth: 2) are required even after the base branch advances. The checkout must be that merge commit, and its second parent must match the event's PR head. File contents are read from the checked-out merge result. C# source rejects symbolic links, raw Unicode format/default-ignorable characters, controls, forbidden \uXXXX / \UXXXXXXXX escapes, and non-ASCII spaces regardless of whether they occur in code, comments, strings, or test data. In addition, every tracked .cs and .csx path is checked for Git symbolic-link mode, including paths not changed by the PR. Other file contents are not scanned. Tests that intentionally need these values should construct them numerically, for example with char.ConvertFromUtf32(0x200B).
jobs:
update-packagejson:
permissions:
actions: read
# Required because this workflow can commit/push version updates.
contents: write
uses: Cysharp/Actions/.github/workflows/update-packagejson.yaml@main
with:
# Keep checkout target explicit (MagicOnion uses github.ref/default branch).
ref: ${{ github.ref }}
file-path: |
# Supported: package.json / plugin.cfg / Directory.Build.props
./src/MyUnityProject/package.json
./addons/MyPlugin/plugin.cfg
./Directory.Build.props
# Release tag to propagate into version files.
tag: ${{ inputs.tag }}
require-validation: true
# true if your default branch requires GitHub App authentication.
use-bot-token: false
# true writes to test-release/{tag} branch instead of target ref.
dry-run: false
dotnet-run-path: |
# Optional hook; workflow always passes: -- --version {tag}
./tools/VersionOutput/VersionOutput.csproj
additional-commit-path: |
# Explicit allow-list for files generated outside file-path by dotnet-run-path.
# Files outside file-path and this list are never committed.
./tools/VersionOutput/version.txtjobs:
cleanup:
# Branch cleanup is only needed when dry-run created a temp branch.
if: ${{ needs.update-packagejson.outputs.is-branch-created == 'true' }}
needs: [update-packagejson]
permissions:
contents: write
uses: Cysharp/Actions/.github/workflows/clean-packagejson-branch.yaml@main
with:
# Output of update-packagejson workflow.
branch: ${{ needs.update-packagejson.outputs.branch-name }}jobs:
validate-release:
permissions:
contents: read
uses: Cysharp/Actions/.github/workflows/validate-release.yaml@main
with:
tag: ${{ inputs.tag }}
build:
needs: [validate-release]
permissions:
contents: read
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
- uses: Cysharp/Actions/.github/actions/checkout@main
- uses: Cysharp/Actions/.github/actions/setup-dotnet@main
- run: dotnet build -c Release -p:Version="$VERSION"
env:
VERSION: ${{ needs.validate-release.outputs.version }}This workflow calls the validate-release CLI, running the current source in Cysharp/Actions and the distributed binary for callers. It exposes tag unchanged and version with one leading v removed (for example, v1.2.3 becomes 1.2.3). It queries releases in the calling repository with its GITHUB_TOKEN; no inherited secrets or write permissions are needed. Validation failures fail the job and block dependent jobs.
It validates Git ref syntax and NuGet version syntax and rejects versions older than the latest release using NuGet version ordering. MagicOnion skips only the ordering check to support multiple release lines. Tag-to-build SHA equality is checked later by create-release, after version updates and builds.
| Action | Purpose | Key inputs / notes |
|---|---|---|
benchmark-progress-comment |
Post or update a benchmark progress comment on an issue or PR. | Inputs: comment, state, title, update. No-op when the event has no issue number. |
benchmark-runnable |
Authorize whether a GitHub user may trigger benchmark execution. | Input: username. Output: authorized. Current allowlist is maintained in the action itself. |
check-metas |
Fail or report when untracked Unity .meta files exist. |
Inputs: directory, optional exit-on-error. Output: meta-exists. |
checkout |
SHA-pinned wrapper around actions/checkout. |
Mirrors most actions/checkout inputs while centralizing the pinned version. |
download-artifact |
SHA-pinned wrapper around actions/download-artifact. |
Supports name, path, pattern, merge-multiple, github-token, repository, run-id. |
publish-nuget |
Download, validate and publish NuGet packages using OIDC in the caller's job. | Required: artifact-name, user, version. Linux X64/ARM64. Optional: dotnet-version (9.0.x), include-symbols (true), skip-duplicate (true). |
publish-crates |
Validate the crate version and publish using OIDC. | Required: manifest-path, version. Caller provides the release checkout, Cargo, jq and id-token: write. |
setup-dotnet |
Install one or more .NET SDKs and configure CI-friendly environment variables. | Defaults to .NET 6.0.x through 10.0.x. Optional dotnet-quality, skip-env. |
unity-builder |
SHA-pinned wrapper around game-ci/unity-builder. |
Inputs include projectPath, unityVersion, targetPlatform, buildMethod, customParameters, versioning. |
upload-artifact |
SHA-pinned wrapper around actions/upload-artifact. |
Default if-no-files-found is error, not warn. |
steps:
# This action posts comments only for issue/PR events with issue.number.
- uses: Cysharp/Actions/.github/actions/benchmark-progress-comment@main
with:
comment: "Benchmark started"
state: running
title: "Benchmark"
# "true" edits latest benchmark comment; "false" appends a new one.
update: "true"steps:
- id: auth
uses: Cysharp/Actions/.github/actions/benchmark-runnable@main
with:
# Usually github.actor
username: ${{ github.actor }}
# authorized output is 'true' or 'false'.
- run: echo "authorized=${{ steps.auth.outputs.authorized }}"steps:
- id: co
uses: Cysharp/Actions/.github/actions/checkout@main
with:
repository: ${{ github.repository }}
ref: ${{ github.ref_name }}
# 0 to fetch full history/tags when versioning logic needs it.
fetch-depth: 0
# Wrapper exposes the same useful outputs as actions/checkout.
- run: echo "checked out ${{ steps.co.outputs.ref }} @ ${{ steps.co.outputs.commit }}"steps:
- uses: Cysharp/Actions/.github/actions/check-metas@main
with:
directory: ./sandbox/Sandbox.Unity
# Keep true in CI to fail immediately on untracked .meta files.
exit-on-error: "true"Run this composite after create-release succeeds, using the same checkout SHA
as the builds. The manifest must identify one package, not a virtual workspace.
steps:
# Check out the release commit before this step.
- uses: Cysharp/Actions/.github/actions/publish-crates@main
with:
manifest-path: csbindgen/Cargo.toml
version: ${{ needs.validate-release.outputs.version }}The composite checks the manifest version with cargo metadata, obtains a crates.io OIDC token, and runs cargo publish --manifest-path. It does not add registry queries, checksum comparisons, toolchain installation, or lock-file management. Cargo publication errors, including an already published version,
fail the step.
Configure trusted publishing for the calling workflow and give its job contents: read and id-token: write. Skip the entire job during dry runs.
Publication across crates.io and NuGet is not atomic. Do not blindly retry a combined publication job after the crate has already been published.
Add this job after validate-release, the builds, and create-release. If update-packagejson changes sources, use its output SHA for every build and for create-release.
jobs:
publish-package:
needs: [validate-release, create-release]
if: ${{ !inputs.dry-run }}
permissions:
contents: read
id-token: write
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
- uses: Cysharp/Actions/.github/actions/publish-nuget@main
with:
artifact-name: nuget
user: ${{ secrets.NUGET_USER }}
version: ${{ needs.validate-release.outputs.version }}Configure NuGet trusted publishing for the calling repository's release workflow. The composite installs the publishing SDK and the .NET 9 runtime required by the distributed CLI; no caller checkout is required. It downloads only the named artifact from the current run to an isolated directory. Put .nupkg and optional .snupkg files at the artifact root and retain the artifact long enough for retries (for example, seven days).
Before authentication, the CLI requires at least one .nupkg and checks the nuspec version in every package, including symbols, using NuGet version equality without build metadata. Missing, corrupt or mismatched packages fail the job. Symbols are optional, but invalid symbol packages must be removed during the build. include-symbols: 'false' disables symbol publication, including automatic sibling symbol upload. skip-duplicate: 'true' permits already published versions; it does not prove their contents match. The temporary NuGet API key is used within the composite and is not exposed as an output. Skip the entire caller job during dry runs so OIDC authentication is also skipped.
steps:
- uses: Cysharp/Actions/.github/actions/setup-dotnet@main
with:
dotnet-version: |
10.0.x
# false also sets CI-friendly env vars (telemetry off, etc.).
skip-env: "false"steps:
- uses: Cysharp/Actions/.github/actions/upload-artifact@main
with:
name: my-artifact
path: ./artifacts/**
# Default is already error, but explicit value is clearer in docs.
if-no-files-found: error
- uses: Cysharp/Actions/.github/actions/download-artifact@main
with:
name: my-artifact
path: ./downloaded
# false keeps each artifact in its own directory.
merge-multiple: "false"steps:
# UNITY_* env vars must be set from secrets before this step.
- uses: Cysharp/Actions/.github/actions/unity-builder@main
with:
projectPath: ./sandbox/Sandbox.Unity
unityVersion: "2022.3.62f1"
targetPlatform: StandaloneLinux64
buildMethod: PackageExporter.Export
customParameters: ""
# Pass-through to game-ci/unity-builder versioning option.
versioning: Nonesecure-checkoutandsecure-setup-dotnetdirectories currently exist but do not contain action definitions.validate-tagis implemented by theCysharpActionsCLI and used internally by reusable workflows such ascreate-releaseandupdate-packagejson; it is not exposed as a standalone reusable workflow.- The repo also contains internal test and maintenance workflows such as
_test-*,_toc-generator.yaml, and_update-actions-binaries.yaml.