Skip to content

Commit 3608728

Browse files
authored
Merge pull request stride3d#3240 from xen2/feature/stride-cli
stride CLI tool
2 parents 356df80 + 4f2c392 commit 3608728

37 files changed

Lines changed: 2231 additions & 53 deletions

.github/workflows/dep-gettext.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ jobs:
2929

3030
- uses: actions/setup-dotnet@v4
3131
with:
32-
dotnet-version: '10.0.x'
32+
dotnet-version: 10.0.x
3333

3434
- name: Pack
3535
shell: pwsh

.github/workflows/release-cli.yml

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
name: Release CLI
2+
3+
# Builds, signs, and (optionally) deploys the Stride CLI dotnet tool (Stride.Cli) to NuGet.org.
4+
#
5+
# Versioned independently of the engine: the SemVer in sources/launcher/Stride.Cli/Stride.Cli.csproj
6+
# is the source of truth - bump <Version> by hand before a stable release. A deploy pushes to
7+
# NuGet.org, tags cli/<version> at the built commit, and creates a GitHub Release.
8+
9+
on:
10+
workflow_dispatch:
11+
inputs:
12+
sign:
13+
description: Code sign the package
14+
default: true
15+
type: boolean
16+
deploy:
17+
description: Deploy to NuGet.org and create a GitHub Release
18+
default: false
19+
type: boolean
20+
version-suffix:
21+
description: Prerelease suffix, no leading dash (e.g. "beta1" -> 1.0.0-beta1). Empty = stable.
22+
default: ''
23+
type: string
24+
25+
concurrency:
26+
group: release-cli-${{ github.ref }}
27+
cancel-in-progress: false
28+
29+
jobs:
30+
Validate:
31+
runs-on: ubuntu-latest
32+
steps:
33+
- name: Check deploy requires signing
34+
if: ${{ inputs.deploy && !inputs.sign }}
35+
run: |
36+
echo "::error::Cannot deploy unsigned packages. Enable 'sign' to deploy."
37+
exit 1
38+
39+
- name: Check release permissions
40+
if: ${{ inputs.sign || inputs.deploy }}
41+
env:
42+
GH_TOKEN: ${{ secrets.GH_PAT }}
43+
run: |
44+
ORG=$(echo "${{ github.repository }}" | cut -d/ -f1)
45+
TEAM="stride-release-managers"
46+
USER="${{ github.actor }}"
47+
if ! gh api "orgs/$ORG/teams/$TEAM/memberships/$USER" --silent 2>/dev/null; then
48+
echo "::error::User $USER is not a member of $ORG/$TEAM. Sign/deploy requires stride-release-managers team membership."
49+
exit 1
50+
fi
51+
52+
Package:
53+
name: Package
54+
needs: Validate
55+
runs-on: windows-2025-vs2026
56+
environment: production # signing secrets live here
57+
outputs:
58+
version: ${{ steps.version.outputs.version }}
59+
steps:
60+
- uses: actions/checkout@v4
61+
with:
62+
lfs: true
63+
fetch-depth: 0 # full history + tags for the re-publish guard
64+
fetch-tags: true
65+
66+
- uses: actions/setup-dotnet@v4
67+
with:
68+
dotnet-version: 10.0.x
69+
70+
- name: Build and sign package
71+
run: |
72+
dotnet build build\Stride.build `
73+
-t:PackageCli `
74+
-p:StrideSign=${{ inputs.sign }} `
75+
-p:VersionSuffix=${{ inputs.version-suffix }}
76+
env:
77+
StrideSignTenantId: ${{ inputs.sign && secrets.STRIDE_SIGN_TENANT_ID || '' }}
78+
StrideSignClientId: ${{ inputs.sign && secrets.STRIDE_SIGN_CLIENT_ID || '' }}
79+
StrideSignClientSecret: ${{ inputs.sign && secrets.STRIDE_SIGN_CLIENT_SECRET || '' }}
80+
StrideSignKeyVaultCertificate: ${{ inputs.sign && secrets.STRIDE_SIGN_KEYVAULT_CERTIFICATE || '' }}
81+
StrideSignKeyVaultName: ${{ inputs.sign && secrets.STRIDE_SIGN_KEYVAULT_NAME || '' }}
82+
83+
- name: Detect version
84+
id: version
85+
shell: pwsh
86+
run: |
87+
$pkg = Get-ChildItem -Path bin/cli -Filter "Stride.Cli.*.nupkg" | Select-Object -First 1
88+
if (-not $pkg) {
89+
echo "::error::No Stride.Cli package found"
90+
exit 1
91+
}
92+
$version = $pkg.Name -replace 'Stride\.Cli\.(.*?)\.nupkg','$1'
93+
if ($version -notmatch '^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$') {
94+
echo "::error::Detected package version '$version' is not a valid version - check Stride.Cli.csproj"
95+
exit 1
96+
}
97+
echo "version=$version" >> $env:GITHUB_OUTPUT
98+
echo "::notice::CLI package version: $version"
99+
100+
# Forget-to-bump guard: <Version> is committed and bumped per release. If it wasn't bumped, its
101+
# cli/<version> tag already exists on a previously-released commit, so fail rather than re-publish.
102+
- name: Guard against re-publishing an existing version
103+
if: ${{ inputs.deploy }}
104+
shell: bash
105+
run: |
106+
TAG="cli/${{ steps.version.outputs.version }}"
107+
if git rev-parse "$TAG" >/dev/null 2>&1 && [ -z "$(git tag --points-at HEAD --list "$TAG")" ]; then
108+
echo "::error::CLI version ${{ steps.version.outputs.version }} is already released (tag $TAG on another commit). Bump <Version> in Stride.Cli.csproj."
109+
exit 1
110+
fi
111+
112+
- name: Upload package
113+
uses: actions/upload-artifact@v4
114+
with:
115+
name: cli-package
116+
path: bin/cli/*.nupkg
117+
if-no-files-found: error
118+
119+
# Surface the version in the run UI even when Deploy is skipped.
120+
Version:
121+
name: Packaged CLI ${{ needs.Package.outputs.version }}
122+
needs: Package
123+
if: ${{ always() && needs.Package.result == 'success' }}
124+
runs-on: ubuntu-latest
125+
steps:
126+
- run: echo "::notice title=Packaged CLI::${{ needs.Package.outputs.version }}"
127+
128+
Deploy:
129+
name: Deploy ${{ needs.Package.outputs.version }}
130+
if: ${{ inputs.deploy && inputs.sign }}
131+
needs: Package
132+
runs-on: ubuntu-latest # NuGet push doesn't need Windows
133+
environment: production # requires manual approval in GitHub settings
134+
permissions:
135+
contents: write
136+
steps:
137+
- name: Check release permissions
138+
env:
139+
GH_TOKEN: ${{ secrets.GH_PAT }}
140+
run: |
141+
ORG=$(echo "${{ github.repository }}" | cut -d/ -f1)
142+
TEAM="stride-release-managers"
143+
USER="${{ github.actor }}"
144+
if ! gh api "orgs/$ORG/teams/$TEAM/memberships/$USER" --silent 2>/dev/null; then
145+
echo "::error::User $USER is not a member of $ORG/$TEAM. Deploy requires stride-release-managers team membership."
146+
exit 1
147+
fi
148+
149+
- uses: actions/checkout@v4
150+
with:
151+
fetch-depth: 0
152+
fetch-tags: true
153+
token: ${{ secrets.GH_PAT }} # push the cli/<version> tag
154+
155+
- uses: actions/setup-dotnet@v4
156+
with:
157+
dotnet-version: '10.0.x'
158+
159+
- name: Download package
160+
uses: actions/download-artifact@v4
161+
with:
162+
name: cli-package
163+
path: bin/cli
164+
165+
- name: Push to NuGet.org
166+
shell: bash
167+
run: |
168+
for pkg in bin/cli/*.nupkg; do
169+
echo "Pushing $(basename "$pkg")..."
170+
dotnet nuget push "$pkg" --api-key "$STRIDE_NUGET_API_KEY" --source "https://api.nuget.org/v3/index.json" --timeout 1800 --skip-duplicate
171+
done
172+
env:
173+
STRIDE_NUGET_API_KEY: ${{ secrets.STRIDE_NUGET_API_KEY }}
174+
175+
- name: Tag release
176+
run: |
177+
TAG="cli/${{ needs.Package.outputs.version }}"
178+
if git rev-parse "$TAG" >/dev/null 2>&1; then
179+
echo "Tag $TAG already exists, skipping"
180+
else
181+
git tag "$TAG"
182+
git push origin "$TAG"
183+
fi
184+
185+
- name: Create GitHub Release
186+
env:
187+
GH_TOKEN: ${{ secrets.GH_PAT }}
188+
run: |
189+
TAG="cli/${{ needs.Package.outputs.version }}"
190+
if gh release view "$TAG" >/dev/null 2>&1; then
191+
echo "Release $TAG already exists, skipping"
192+
else
193+
gh release create "$TAG" \
194+
--title "Stride CLI ${{ needs.Package.outputs.version }}" \
195+
--generate-notes \
196+
bin/cli/*.nupkg
197+
fi
198+
199+
- name: Publish summary
200+
if: success()
201+
shell: bash
202+
run: |
203+
V="${{ needs.Package.outputs.version }}"
204+
{
205+
echo "## Published Stride.Cli $V"
206+
echo ""
207+
echo "- [nuget.org](https://www.nuget.org/packages/Stride.Cli/$V)"
208+
echo "- [GitHub Release](${{ github.server_url }}/${{ github.repository }}/releases/tag/cli/$V)"
209+
echo ""
210+
echo "Install: \`dotnet tool install -g Stride.Cli --version $V\`"
211+
} >> "$GITHUB_STEP_SUMMARY"

.github/workflows/release-deploy.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ jobs:
7474

7575
- uses: actions/setup-dotnet@v4
7676
with:
77-
dotnet-version: '10.0.x'
77+
dotnet-version: 10.0.x
7878

7979
- name: Download packages
8080
uses: actions/download-artifact@v4

.github/workflows/release-launcher.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,7 @@ jobs:
6666

6767
- uses: actions/setup-dotnet@v4
6868
with:
69-
dotnet-version: |
70-
6.0.x
71-
10.0.x
69+
dotnet-version: 10.0.x
7270

7371
- uses: microsoft/setup-msbuild@v2
7472

.github/workflows/release-vspackage.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,7 @@ jobs:
6060

6161
- uses: actions/setup-dotnet@v4
6262
with:
63-
dotnet-version: |
64-
6.0.x
65-
10.0.x
63+
dotnet-version: 10.0.x
6664

6765
- name: Build VS Package
6866
run: |

.github/workflows/release.yml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,7 @@ jobs:
9595

9696
- uses: actions/setup-dotnet@v4
9797
with:
98-
# 6.0.x required by deps/Gettext.Net/GNU.Gettext.Msgfmt.exe (see #3113)
99-
dotnet-version: |
100-
6.0.x
101-
10.0.x
98+
dotnet-version: 10.0.x
10299

103100
- name: Install Apple Workloads
104101
# VS provisions the android workload but not ios/macos.

README.md

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,23 +23,30 @@ Stride is an open-source C# game engine designed for realistic rendering and VR.
2323

2424
This README is intended for users who want to build the Stride engine from source or contribute to its development. If your goal is to create games using Stride, we recommend visiting the [Get started with Stride](https://doc.stride3d.net/latest/en/manual/get-started/index.html) guide. There, you'll find detailed instructions on downloading, installing, and getting started with game development in Stride.
2525

26-
## 🚀 Try the CLI templates
26+
## 🚀 Try Stride from the command line
2727

28-
Create a Stride project from the command line — no editor required:
28+
Create and manage Stride projects from the command line — no editor required.
29+
30+
The **Stride CLI tool** installs and manages Stride engine versions, creates projects from templates, and opens Game Studio:
2931

3032
```bash
31-
# Blank starter
32-
dotnet new install Stride.Templates.Games
33-
dotnet new stride-game -n MyGame
34-
cd MyGame && dotnet run --project MyGame.Windows
33+
dotnet tool install -g Stride.Cli
34+
35+
stride sdk install # install the latest Stride engine
36+
stride new fps -n MyShooter # template: game, fps, platformer2d, topdownrpg, vrsandbox, ...
37+
cd MyShooter && dotnet run --project MyShooter.Windows
38+
39+
stride studio # open Game Studio, the visual editor
3540
```
3641

37-
Or start from a genre-specific starter (FPS, 2D platformer, top-down RPG, third-person platformer, VR):
42+
`stride new` with no template lists what's available, `stride sdk` manages installed engine versions (`list`, `install`, `uninstall`, `update`), and `stride upgrade` moves a project to a newer engine. See [`sources/launcher/README.md`](sources/launcher/README.md).
43+
44+
Prefer standard .NET templating? The same templates are available through `dotnet new`:
3845

3946
```bash
40-
dotnet new install Stride.Templates.Games.Starters
41-
dotnet new stride-fps -n MyShooter
42-
cd MyShooter && dotnet run --project MyShooter.Windows
47+
dotnet new install Stride.Templates.Games
48+
dotnet new stride-game -n MyGame
49+
cd MyGame && dotnet run --project MyGame.Windows
4350
```
4451

4552
See [`sources/templates/README.md`](sources/templates/README.md) for the full template catalog (genre starters, 18 feature demos, tutorials) and the local-development workflow.

build/Stride.Launcher.sln

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stride.Core.Translation.Pre
3131
EndProject
3232
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stride.Core.Packages", "..\sources\assets\Stride.Core.Packages\Stride.Core.Packages.csproj", "{1F5FBA04-C334-41C2-895A-ACC4B786F99E}"
3333
EndProject
34-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stride.Core.Presentation", "..\sources\presentation\Stride.Core.Presentation\Stride.Core.Presentation.csproj", "{0C63EF8B-26F9-4511-9FC5-7431DE9657D6}"
34+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stride.Core.Presentation", "..\sources\presentation\Stride.Core.Presentation\Stride.Core.Presentation.csproj", "{0C63EF8B-26F9-4511-9FC5-7431DE9657D6}"
3535
EndProject
36-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stride.Editor.CrashReport", "..\sources\editor\Stride.Editor.CrashReport\Stride.Editor.CrashReport.csproj", "{2880C313-2483-416D-A902-DC2259EDFF64}"
36+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stride.Editor.CrashReport", "..\sources\editor\Stride.Editor.CrashReport\Stride.Editor.CrashReport.csproj", "{2880C313-2483-416D-A902-DC2259EDFF64}"
37+
EndProject
38+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stride.Cli", "..\sources\launcher\Stride.Cli\Stride.Cli.csproj", "{4A321E09-24D6-4446-8E95-2C29404C8B76}"
3739
EndProject
3840
Global
3941
GlobalSection(SharedMSBuildProjectFiles) = preSolution
@@ -46,6 +48,10 @@ Global
4648
Release|Any CPU = Release|Any CPU
4749
EndGlobalSection
4850
GlobalSection(ProjectConfigurationPlatforms) = postSolution
51+
{4A321E09-24D6-4446-8E95-2C29404C8B76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
52+
{4A321E09-24D6-4446-8E95-2C29404C8B76}.Debug|Any CPU.Build.0 = Debug|Any CPU
53+
{4A321E09-24D6-4446-8E95-2C29404C8B76}.Release|Any CPU.ActiveCfg = Release|Any CPU
54+
{4A321E09-24D6-4446-8E95-2C29404C8B76}.Release|Any CPU.Build.0 = Release|Any CPU
4955
{0F8BE30E-C41F-4747-B52B-D2D4E13EC6A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
5056
{0F8BE30E-C41F-4747-B52B-D2D4E13EC6A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
5157
{0F8BE30E-C41F-4747-B52B-D2D4E13EC6A2}.Release|Any CPU.ActiveCfg = Release|Any CPU

build/Stride.build

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ Example: dotnet build build/Stride.build -t:Package
4444
</Target>
4545

4646
<Target Name="Help">
47-
<Message Importance="high" Text="Stride build targets (run: dotnet build build/Stride.build -t:&lt;Target&gt;)%0a%0a Build - build the full Stride solution (Windows)%0a BuildWindows / BuildWindowsDirect3D11 / BuildWindowsDirect3D12 / BuildWindowsVulkan%0a BuildLinux / BuildAndroid / BuildiOS / BuildmacOS - build a platform's runtime%0a BuildRuntime - build the runtime slnf (with tests/packing)%0a Package - build + pack + sign NuGet packages (BuildPackages = build only, no signing)%0a VSIXPlugin - build + pack the Visual Studio extension%0a FullBuildLauncher - build the launcher + installer%0a Publish / PublishVSIX / PublishLauncher - push packages to the NuGet store%0a Clean - delete the Bin output folder%0a%0aCommon properties: -p:StrideSign=false -p:StrideGraphicsApiDependentBuildAll=true" />
47+
<Message Importance="high" Text="Stride build targets (run: dotnet build build/Stride.build -t:&lt;Target&gt;)%0a%0a Build - build the full Stride solution (Windows)%0a BuildWindows / BuildWindowsDirect3D11 / BuildWindowsDirect3D12 / BuildWindowsVulkan%0a BuildLinux / BuildAndroid / BuildiOS / BuildmacOS - build a platform's runtime%0a BuildRuntime - build the runtime slnf (with tests/packing)%0a Package - build + pack + sign NuGet packages (BuildPackages = build only, no signing)%0a VSIXPlugin - build + pack the Visual Studio extension%0a FullBuildLauncher - build the launcher + installer%0a PackageCli - build + pack + sign the Stride CLI dotnet tool (Stride.Cli)%0a Publish / PublishVSIX / PublishLauncher - push packages to the NuGet store%0a Clean - delete the Bin output folder%0a%0aCommon properties: -p:StrideSign=false -p:StrideGraphicsApiDependentBuildAll=true" />
4848
</Target>
4949

5050
<!--
@@ -152,6 +152,37 @@ Example: dotnet build build/Stride.build -t:Package
152152
callers run the two phases separately. -->
153153
<Target Name="Package" DependsOnTargets="BuildPackages;SignPackage"/>
154154

155+
<!--
156+
Stride CLI dotnet tool (Stride.Cli)
157+
Versioned independently of the engine (SemVer in Stride.Cli.csproj); deployed by
158+
.github/workflows/release-cli.yml. BuildPackageCli packs the tool nupkg (secret-free),
159+
SignPackageCli signs it via the shared _SignFiles target, PackageCli does both.
160+
-->
161+
<PropertyGroup>
162+
<StrideCliProject>$(StrideRoot)sources\launcher\Stride.Cli\Stride.Cli.csproj</StrideCliProject>
163+
<StrideCliPackageDir>$(StrideRoot)bin\cli</StrideCliPackageDir>
164+
</PropertyGroup>
165+
166+
<Target Name="BuildPackageCli">
167+
<ItemGroup>
168+
<_StrideCliOldPackages Include="$(StrideCliPackageDir)\*.nupkg" />
169+
</ItemGroup>
170+
<Delete Files="@(_StrideCliOldPackages)" />
171+
<MSBuild Projects="$(StrideCliProject)" Targets="Restore" />
172+
<!-- StrideSkipAutoPack: engine ProjectReferences build but don't auto-pack their own .nupkg into our output. -->
173+
<MSBuild Projects="$(StrideCliProject)" Targets="Pack"
174+
Properties="Configuration=Release;ContinuousIntegrationBuild=true;StrideSkipAutoPack=true;VersionSuffix=$(VersionSuffix);PackageOutputPath=$(StrideCliPackageDir)" />
175+
</Target>
176+
177+
<Target Name="SignPackageCli">
178+
<ItemGroup>
179+
<StrideFilesToSign Include="$(StrideCliPackageDir)\*.nupkg" />
180+
</ItemGroup>
181+
<CallTarget Targets="_SignFiles" />
182+
</Target>
183+
184+
<Target Name="PackageCli" DependsOnTargets="BuildPackageCli;SignPackageCli" />
185+
155186
<!-- In-repo sample version management (SamplesToDevVersion / SamplesToReleaseVersion /
156187
UpgradeSamplesVersion) is standalone in build/Stride.Samples.build - run it directly, e.g.
157188
dotnet msbuild build/Stride.Samples.build -t:UpgradeSamplesVersion. See plans/samples-version-bump.md. -->

docs/build/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,4 @@ Stride auto-selects the native toolchain: MSVC when running under `MSBuild.exe`
7575
- [versioning.md](versioning.md) — versioning &amp; release: engine version, per-checkout `-devN` dev versions, the release flow, and sample/template package versions
7676
- [aot.md](aot.md) — NativeAOT &amp; trimming: publishing games, feature switches for optional subsystems, keeping the engine AOT-clean
7777
- [../../sources/templates/README.md](../../sources/templates/README.md)`dotnet new` template packages (Stride.Templates.Games / .Games.Starters / .Samples): end-user usage, local dev workflow, adding a new sample
78+
- [../../sources/launcher/README.md](../../sources/launcher/README.md) — the `stride` CLI tool and the WPF launcher: usage, building (`PackageCli`), and the independent CLI release flow

0 commit comments

Comments
 (0)