Commit 3d4d0ea
committed
clean up and future proof
Excellent questions! Let me address both concerns:
## 1. Single Binary Releases with .NET 9 AOT
Yes! .NET 9 with AOT (Ahead-of-Time compilation) can produce single, self-contained native binaries that don't require .NET runtime or Docker. Here's an updated GitHub Actions workflow that creates binary releases:
```yaml
name: Build, Test, and Release
on:
push:
branches: [ master, main ]
tags: [ 'v*' ]
pull_request:
branches: [ master, main ]
workflow_dispatch:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build and Test Docker
run: |
docker build -t tsp-solver:latest .
echo -e "2\n5\n5\n" | timeout 10s docker run --rm -i tsp-solver:latest || true
build-binaries:
strategy:
matrix:
include:
# Windows
- os: windows-latest
rid: win-x64
artifact: TSP-win-x64.exe
build_cmd: dotnet publish -c Release -r win-x64 --self-contained -p:PublishAot=true -p:PublishSingleFile=true
# Linux x64
- os: ubuntu-latest
rid: linux-x64
artifact: TSP-linux-x64
build_cmd: dotnet publish -c Release -r linux-x64 --self-contained -p:PublishAot=true -p:PublishSingleFile=true
# Linux ARM64
- os: ubuntu-latest
rid: linux-arm64
artifact: TSP-linux-arm64
build_cmd: |
sudo apt-get update
sudo apt-get install -y gcc-aarch64-linux-gnu
dotnet publish -c Release -r linux-arm64 --self-contained -p:PublishAot=true -p:PublishSingleFile=true
# macOS x64
- os: macos-13 # Intel Mac
rid: osx-x64
artifact: TSP-macos-x64
build_cmd: dotnet publish -c Release -r osx-x64 --self-contained -p:PublishAot=true -p:PublishSingleFile=true
# macOS ARM64 (Apple Silicon)
- os: macos-14 # M1/M2 Mac
rid: osx-arm64
artifact: TSP-macos-arm64
build_cmd: dotnet publish -c Release -r osx-arm64 --self-contained -p:PublishAot=true -p:PublishSingleFile=true
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.0.x'
- name: Install dependencies
run: dotnet restore TSP.sln
- name: Build AOT Binary
shell: bash
run: |
cd TravelingSalesman.ConsoleApp
${{ matrix.build_cmd }}
- name: Prepare artifact
shell: bash
run: |
if [[ "${{ matrix.os }}" == "windows-latest" ]]; then
mv TravelingSalesman.ConsoleApp/bin/Release/net9.0/${{ matrix.rid }}/publish/TravelingSalesman.ConsoleApp.exe ${{ matrix.artifact }}
else
mv TravelingSalesman.ConsoleApp/bin/Release/net9.0/${{ matrix.rid }}/publish/TravelingSalesman.ConsoleApp ${{ matrix.artifact }}
chmod +x ${{ matrix.artifact }}
fi
# Create size info
ls -lh ${{ matrix.artifact }} > size-info-${{ matrix.rid }}.txt
- name: Upload binary
uses: actions/upload-artifact@v4
with:
name: binary-${{ matrix.rid }}
path: |
${{ matrix.artifact }}
size-info-${{ matrix.rid }}.txt
create-release:
if: startsWith(github.ref, 'refs/tags/v')
needs: [test, build-binaries]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: ./artifacts
- name: Display structure
run: ls -la ./artifacts/*/
- name: Create Release
uses: softprops/action-gh-release@v1
with:
files: |
./artifacts/binary-*/*
body: |
## TSP Solver Release
### 🚀 Single Binary Executables
No .NET runtime or Docker required! Just download and run.
### 📦 Available Platforms:
- **Windows x64**: `TSP-win-x64.exe`
- **Linux x64**: `TSP-linux-x64`
- **Linux ARM64**: `TSP-linux-arm64`
- **macOS x64 (Intel)**: `TSP-macos-x64`
- **macOS ARM64 (M1/M2)**: `TSP-macos-arm64`
### 🏃 Usage:
```bash
# Linux/macOS
chmod +x TSP-linux-x64
./TSP-linux-x64
# Windows
TSP-win-x64.exe
```
draft: false
prerelease: false
```
To trigger a release, create a git tag:
```powershell
git tag v1.0.0
git push origin v1.0.0
```
## 2. .NET Version Management - Avoiding Annual Ceremony
You're absolutely right about the version churn. Here's a better approach using MSBuild properties to centralize version management:
### Create a `Directory.Build.props` file in your repository root:
```xml
<Project>
<PropertyGroup>
<!-- Centralized .NET version management -->
<TargetFramework>net9.0</TargetFramework>
<DotNetVersion>9.0</DotNetVersion>
<DotNetChannel>9.0</DotNetChannel>
<!-- When .NET 10 releases, just update these three lines -->
<!-- <TargetFramework>net10.0</TargetFramework> -->
<!-- <DotNetVersion>10.0</DotNetVersion> -->
<!-- <DotNetChannel>10.0</DotNetChannel> -->
<!-- Shared project settings -->
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<AnalysisLevel>latest</AnalysisLevel>
<!-- AOT settings for console apps -->
<PublishAot Condition="'$(OutputType)' == 'Exe'">true</PublishAot>
<InvariantGlobalization Condition="'$(PublishAot)' == 'true'">true</InvariantGlobalization>
</PropertyGroup>
</Project>
```
### Simplified `TravelingSalesman.ConsoleApp.csproj`:
```xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<!-- TargetFramework inherited from Directory.Build.props -->
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\TravelingSalesman.Core\TravelingSalesman.Core.csproj" />
</ItemGroup>
</Project>
```
### Simplified `TravelingSalesman.Core.csproj`:
```xml
<Project Sdk="Microsoft.NET.Sdk">
<!-- Everything inherited from Directory.Build.props -->
</Project>
```
### Multi-version Dockerfile using build arguments:
```dockerfile
# Default to .NET 9, but allow override
ARG DOTNET_VERSION=9.0
FROM mcr.microsoft.com/dotnet/runtime:${DOTNET_VERSION} AS base
USER $APP_UID
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION} AS build
RUN apt-get update && apt-get install -y --no-install-recommends clang zlib1g-dev
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["*.sln", "./"]
COPY ["Directory.Build.props", "./"]
COPY ["TravelingSalesman.ConsoleApp/*.csproj", "TravelingSalesman.ConsoleApp/"]
COPY ["TravelingSalesman.Core/*.csproj", "TravelingSalesman.Core/"]
RUN dotnet restore
COPY . .
WORKDIR "/src/TravelingSalesman.ConsoleApp"
RUN dotnet build -c $BUILD_CONFIGURATION -o /app/build
FROM build AS publish
RUN dotnet publish -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=true
FROM mcr.microsoft.com/dotnet/runtime-deps:${DOTNET_VERSION} AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["./TravelingSalesman.ConsoleApp"]
```
### Future-proof GitHub Actions with matrix builds:
```yaml
name: Multi-Version Build
on:
push:
workflow_dispatch:
jobs:
build:
strategy:
matrix:
dotnet: ['9.0', '10.0'] # Easy to add versions
continue-on-error: true # Don't fail if 10.0 isn't out yet
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET ${{ matrix.dotnet }}
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ matrix.dotnet }}.x
continue-on-error: true
- name: Build with .NET ${{ matrix.dotnet }}
run: |
dotnet --version
dotnet build TSP.sln
continue-on-error: ${{ matrix.dotnet == '10.0' }}
```
## Summary of Benefits:
1. **Single Binary Releases**: Yes, AOT produces true single-file executables (~10-30MB) that work without any runtime
2. **Version Management**: With `Directory.Build.props`, updating to .NET 10 requires changing just 3 lines
3. **Future-Proof CI/CD**: Matrix builds can test multiple .NET versions simultaneously
4. **Cross-Platform**: Automatic builds for all major platforms
The AOT binaries are perfect for distribution - users just download and run, no installation needed!1 parent 3792624 commit 3d4d0ea
File tree
3 files changed
+405
-1492
lines changed- .github/workflows
- scripts
3 files changed
+405
-1492
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
0 commit comments