はじめに
.NET10 Preview4からfile-based appsという.csprojなしで.csを実行できる機能が追加されました。
手軽に利用できるようになったのは勿論のこと、特にCI/CDで.csを利用する手法がより広がっていくのではと思っています。前にProcessX(Zx) + ConsoleAppFrameworkの組み合わせならCI/CDでbash/zsh/pwshの代わりに使っても良いのではと書いたことがあるのですが、より現実味を帯びてきたと思います。
あとActionsのカスタムシェルを使ってshell: dotnet run {0}とかできたらいいなと思うのですが、調べた感じランナーにインストールされている.NETのバージョンのこともありますし少なくとも現段階では無理そうな気がしています。(追記にコメントしました)
GitHub Actions のワークフロー構文 - GitHub Docs
背景を説明すると、一応.csxとかもあったのですが、基本的に.NETでは.csprojがないと実行をすることができませんでした。
$ tree . └── ConsoleApp ├── ConsoleApp.csproj └── Program.cs
しかし今回のfile-based appsと呼ばれる機能を用いれば、.csprojがいらなくなります。つまり、Program.csのような単一ファイルだけで実行までできるというわけです。
$ chmod +x ./Program.cs $ ./Program.cs
導入〜使い方までを一通り紹介したいと思います。
導入方法
VSCodeで開発環境を整える
Riderだとどうなのか確認できていないのですが、VS Codeであれば既にfile-based appsに対応しています。VS CodeにC# Dev Kitという拡張機能があるのでpre-release(私はv1.20.33)をインストールします。
またC#という拡張機能でも、pre-releaseのv 2.79.8以上にしてください。

基本的な使い方
まずは実際に利用する方法を紹介します。
任意の場所に以下の内容の.csを作成してください。
#!/usr/bin/env dotnet run Console.WriteLine("Hello World!");
Shebangがあるので、実行権限を与えて実行すればOKです。
$ chmod +x ./sample.cs $ ./sample.cs Hello World!
使い方
NuGet Packageを参照する
#:packageの後ろに、NuGetのパッケージ名とバージョンを記述します。
#!/usr/bin/env dotnet run #:package MessagePack@3.1.3 using MessagePack;
パッケージ名が分からない方は、nuget.orgを確認すると良いでしょう。
www.nuget.org
デフォルトのSDKと変更方法
デフォルトのSDKはMicrosoft.NET.Sdkです。WebAPIを作成したいなどでMicrosoft.NET.Sdk.Webといった別のSDKを利用したい場合は#:sdkを記述します。
learn.microsoft.com
#!/usr/bin/env dotnet run #:sdk Microsoft.NET.Sdk.Web var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); app.MapGet("/", () => "Hello World!"); app.Run("http://localhost:8080");
MSBuildのpropertyを設定する
MSBuildのPropertyを記述するためには、#:propertyを用います。
<!-- csprojでの記述例 --> <PropertyGroup> <LangVersion>preview</LangVersion> </PropertyGroup>
#!/usr/bin/env dotnet run #:property LangVersion preview
Shebang
先ほどからしれっとずっと使っていましたが、ちゃんとShebangも用意してくれています。
#!/usr/bin/env dotnet run
$ chmod +x ./sample.cs $ ./sample.cs
file-based appをやめる
複雑になってきたら、.csprojを活用した通常のproject-based appsに変換することも可能です。コマンドはdotnet project convert .csへのパスです。
$ tree . └── sample.cs # migrate $ dotnet project convert sample.cs # .csprojが作成されていることが確認できる $ tree . └── sample ├── sample.cs └── sample.csproj
ProcessX + ConsoleAppFrameworkの組み合わせ
ConsoleAppFramework v5とProcessX(Zx)を組み合わせたオススメ構成を紹介して終わりたいと思います。
GitHub - Cysharp/ProcessX: Simplify call an external process with the async streams in C# 8.0.
GitHub - Cysharp/ConsoleAppFramework: Zero Dependency, Zero Overhead, Zero Reflection, Zero Allocation, AOT Safe CLI Framework powered by C# Source Generator.
#!/usr/bin/env dotnet run #:package ProcessX@1.5.6 #:package ConsoleAppFramework@5.4.1 using ConsoleAppFramework; using Zx; using static Zx.Env; verbose = false; var app = ConsoleApp.Create(); // root command app.Add("", async () => { var tools = await "dotnet tool list -g"; log(string.Join(",", tools)); }); // 例: ./sample.cs echo --msg "Hello, World!" app.Add("echo", (string msg) => log(msg)); // 例: ./sample.cs sum 5 10 app.Add("sum", ([Argument] int x, [Argument] int y) => log(x + y)); app.Run(args);
$ ./sample.cs Package Id Version Commands ------------------------------------------------------------------------- coverlet.console 6.0.4 coverlet $ ./sample.cs sum 1 2 3 $ ./sample.cs echo --msg "hello, world!!!" hello, world!!!
追記
色々調べてみて、GitHub Actionsでshell: dotnet run {0}を利用できるようにするPRをrunnerに出してみてます!
github.com