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
9 changes: 8 additions & 1 deletion .config/cspell/cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
"gitignoreRoot": ".",
"ignorePaths": ["**/node_modules/**", "LICENSE"],
"words": [
"aspnet",
"aspnetcore",
"buildx",
"csharpierignore",
"csharpierrc",
"devcontainer",
Expand All @@ -16,8 +19,12 @@
"hasha",
"mktemp",
"msbuild",
"NOLOGO",
"packagejson",
"syncpack",
"unrs"
"unrs",
"muxer",
"tarball",
"optout"
]
}
6 changes: 5 additions & 1 deletion .config/dotnet/Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,9 @@
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup />

<ItemGroup>
<PackageVersion Include="NuGet.Versioning" Version="7.9.0" />
<PackageVersion Include="System.CommandLine" Version="2.0.11" />
</ItemGroup>
</Project>
1 change: 1 addition & 0 deletions .config/dotnet/Project.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<PropertyGroup>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>

<ItemGroup>
Expand Down
1 change: 1 addition & 0 deletions .config/workspaces/pnpm-workspace.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
packages:
- "features/.devcontainer/features/src/*"
- "features/.devcontainer/features/test/*"
- "features"
lockfile: false
confirmModulesPurge: false
update:
Expand Down
200 changes: 200 additions & 0 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
name: "Dotnet"

on:
pull_request:
paths:
- "features/dotnet/config.json"
push:
branches:
- main
paths:
- "features/dotnet/config.json"
workflow_dispatch:

permissions:
contents: read
packages: write

concurrency:
group: "dotnet-${{ github.ref }}"
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
plan:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.plan.outputs.matrix }}
steps:
- name: Checkout (GitHub)
uses: actions/checkout@v7

- name: Login to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ github.token }}

- name: Plan the publishes
id: plan
uses: devcontainers/ci@v0.3
with:
cacheFrom: ghcr.io/${{ github.repository }}/devcontainer
push: never
runCmd: pnpm tsx scripts/tasks/features/dotnet/planPublish.ts --channel "$CHANNEL"
env: |
CHANNEL=${{ github.head_ref || github.ref_name }}
GH_TOKEN=${{ github.token }}

install-binaries:
needs: plan
if: needs.plan.outputs.matrix != ''
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- rid: linux-x64
runner: ubuntu-latest
arm64: false
- rid: linux-arm64
runner: ubuntu-24.04-arm
arm64: true
steps:
- name: Checkout (GitHub)
uses: actions/checkout@v7

- name: Login to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ github.token }}

- name: Pack the install binary
uses: devcontainers/ci@v0.3
with:
imageName: ${{ matrix.arm64 && format('ghcr.io/{0}/devcontainer', github.repository) || '' }}
cacheFrom: ghcr.io/${{ github.repository }}/devcontainer
push: ${{ matrix.arm64 && 'filter' || 'never' }}
refFilterForPush: ${{ matrix.arm64 && 'refs/heads/main' || '' }}
useNativeRunner: ${{ matrix.arm64 }}
platform: ${{ matrix.arm64 && 'linux/arm64' || '' }}
runCmd: pnpm tsx scripts/tasks/features/dotnet/packInstall.ts --rid "$RID" --out .ci/install
env: |
RID=${{ matrix.rid }}

- name: Upload the install binary
uses: actions/upload-artifact@v7
with:
name: install-${{ matrix.rid }}
path: .ci/install/${{ matrix.rid }}/

image:
needs:
- plan
- install-binaries
if: needs.plan.outputs.matrix != ''
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.plan.outputs.matrix) }}
steps:
- name: Checkout (GitHub)
uses: actions/checkout@v7

- name: Login to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ github.token }}

- name: Download the x64 install binary
uses: actions/download-artifact@v7
with:
name: install-linux-x64
path: .ci/install/linux-x64

- name: Download the arm64 install binary
uses: actions/download-artifact@v7
with:
name: install-linux-arm64
path: .ci/install/linux-arm64

- name: Publish the image
uses: devcontainers/ci@v0.3
with:
cacheFrom: ghcr.io/${{ github.repository }}/devcontainer
push: never
runCmd: pnpm tsx scripts/tasks/features/dotnet/publishImage.ts --component ${{ matrix.component }} --version ${{ matrix.version }} --channel "$CHANNEL" --install-dir .ci/install
env: |
CHANNEL=${{ github.head_ref || github.ref_name }}
GH_TOKEN=${{ github.token }}

commit-tags:
needs:
- plan
- image
if: github.event_name == 'push' && github.ref == 'refs/heads/main' && needs.plan.result == 'success' && needs.image.result == 'success' && needs.plan.outputs.matrix != ''
permissions:
contents: write
packages: read
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.plan.outputs.matrix) }}
steps:
- name: Checkout (GitHub)
uses: actions/checkout@v7

- name: Login to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ github.token }}

- name: Tag the published pair
uses: devcontainers/ci@v0.3
with:
cacheFrom: ghcr.io/${{ github.repository }}/devcontainer
push: never
runCmd: pnpm tsx scripts/tasks/features/dotnet/tagPublished.ts --component ${{ matrix.component }} --version ${{ matrix.version }}
env: |
GH_TOKEN=${{ github.token }}

verify:
needs:
- plan
- image
if: ${{ !cancelled() && needs.plan.result == 'success' && (needs.image.result == 'success' || needs.image.result == 'skipped') }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
scenario:
- sdk
- runtime
- aspnet
- multi
steps:
- name: Checkout (GitHub)
uses: actions/checkout@v7

- name: Login to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ github.token }}

- name: Verify the images
uses: devcontainers/ci@v0.3
with:
cacheFrom: ghcr.io/${{ github.repository }}/devcontainer
push: never
runCmd: pnpm verify dotnet --scenario ${{ matrix.scenario }} --images pull --channel "$CHANNEL"
env: |
CHANNEL=${{ github.head_ref || github.ref_name }}
GH_TOKEN=${{ github.token }}
1 change: 1 addition & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
push:
tags:
- "*_[0-9]+.[0-9]+.[0-9]+"
- "!features-*"
workflow_dispatch:

permissions:
Expand Down
35 changes: 35 additions & 0 deletions .github/workflows/renovate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: "Renovate"

on:
schedule:
- cron: "0 0 7,21 * *"
workflow_dispatch:

permissions:
contents: read
packages: read

jobs:
renovate:
runs-on: ubuntu-latest
steps:
- name: Checkout (GitHub)
uses: actions/checkout@v7
with:
fetch-depth: 0

- name: Login to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ github.token }}

- name: Refresh the dotnet config
uses: devcontainers/ci@v0.3
with:
cacheFrom: ghcr.io/${{ github.repository }}/devcontainer
push: never
runCmd: pnpm tsx scripts/tasks/features/dotnet/renovate.ts
env: |
GH_TOKEN=${{ secrets.RENOVATE_TOKEN }}
1 change: 1 addition & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<ProjectRoot>$(MSBuildThisFileDirectory)</ProjectRoot>
<ConfigDirectory>$([MSBuild]::NormalizeDirectory($(ProjectRoot), ".config", "dotnet"))</ConfigDirectory>
<DirectoryPackagesPropsPath>$(ConfigDirectory)Packages.props</DirectoryPackagesPropsPath>
<DefaultTargetFramework>net10.0</DefaultTargetFramework>
</PropertyGroup>

<Import Project="$(ConfigDirectory)Project.props" />
Expand Down
17 changes: 17 additions & 0 deletions features/dotnet/Build/Build.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>$(DefaultTargetFramework)</TargetFramework>
<AssemblyName>build</AssemblyName>
<OutputType>Exe</OutputType>
<RootNamespace>DevcontainerConfig.Dotnet.Build</RootNamespace>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="System.CommandLine" />
</ItemGroup>

<PropertyGroup Condition="'$(RuntimeIdentifier)' != ''">
<SelfContained>true</SelfContained>
<PublishSingleFile>true</PublishSingleFile>
</PropertyGroup>
</Project>
3 changes: 3 additions & 0 deletions features/dotnet/Build/BuildException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace DevcontainerConfig.Dotnet.Build;

sealed class BuildException(string message) : Exception(message);
17 changes: 17 additions & 0 deletions features/dotnet/Build/Checksum.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System.Security.Cryptography;

namespace DevcontainerConfig.Dotnet.Build;

internal static class Checksum
{
internal static async Task VerifyAsync(string tempPath, string sha512, CancellationToken cancellationToken)
{
await using FileStream stream = File.OpenRead(tempPath);
string actual = Convert.ToHexStringLower(await SHA512.HashDataAsync(stream, cancellationToken));

if (!string.Equals(actual, sha512, StringComparison.OrdinalIgnoreCase))
{
throw new BuildException($"sha512 mismatch: expected {sha512}, actual {actual}.");
}
}
}
34 changes: 34 additions & 0 deletions features/dotnet/Build/Config.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System.Text.Json;
using System.Text.Json.Serialization;

namespace DevcontainerConfig.Dotnet.Build;

sealed class Artifact
{
[JsonPropertyName("url")]
public required string Url { get; init; }

[JsonPropertyName("sha512")]
public required string Sha512 { get; init; }
}

sealed class ComponentVersion
{
[JsonPropertyName("aliases")]
public List<string> Aliases { get; init; } = [];

[JsonExtensionData]
public Dictionary<string, JsonElement> Rids { get; init; } = [];
}

sealed class ComponentConfig
{
[JsonPropertyName("versions")]
public required Dictionary<string, ComponentVersion> Versions { get; init; }
}

sealed class FeatureConfig
{
[JsonPropertyName("components")]
public required Dictionary<string, ComponentConfig> Components { get; init; }
}
12 changes: 12 additions & 0 deletions features/dotnet/Build/Http.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace DevcontainerConfig.Dotnet.Build;

internal static class Http
{
internal static async Task DownloadAsync(string tempPath, string url, CancellationToken cancellationToken)
{
using HttpClient client = new();
await using Stream httpStream = await client.GetStreamAsync(url, cancellationToken);
await using FileStream fileStream = File.Create(tempPath);
await httpStream.CopyToAsync(fileStream, cancellationToken);
}
}
Loading
Loading