Skip to content
Open
Show file tree
Hide file tree
Changes from 7 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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,7 @@ Gemfile.lock
Thumbs.db
.vscode
.zed

# C# build artifacts
bin/
obj/
4 changes: 4 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ RUN mise install [email protected] && \
RUN mise install [email protected] && \
mise use --global [email protected]

# .NET version support: https://dotnet.microsoft.com/en-us/platform/support/policy/dotnet-core
RUN mise install dotnet@9 && \
mise use --global dotnet@9

# Install Python tools.
RUN pip install --no-cache-dir uv black
RUN npm install -g nx
Expand Down
10 changes: 10 additions & 0 deletions apps/csharp/App.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../../sdk/csharp/src/Buildkite.Sdk/Buildkite.Sdk.csproj" />
</ItemGroup>
</Project>
20 changes: 20 additions & 0 deletions apps/csharp/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using System.IO;
using Buildkite.Sdk;
using Buildkite.Sdk.Schema;

var pipeline = new Pipeline();

pipeline.AddStep(new CommandStep
{
Label = "some-label",
Command = "echo 'Hello, world!'"
});

Directory.CreateDirectory("../../out/apps/csharp");

var json = pipeline.ToJson();
File.WriteAllText("../../out/apps/csharp/pipeline.json", json);

var yaml = pipeline.ToYaml();
File.WriteAllText("../../out/apps/csharp/pipeline.yaml", yaml);
59 changes: 59 additions & 0 deletions apps/csharp/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
{
"name": "app-csharp",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/csharp",
"projectType": "application",
"tags": [],
"targets": {
"install": {
"executor": "nx:run-commands",
"options": {
"commands": ["dotnet restore"],
"cwd": "apps/csharp"
}
},
"clean": {
"executor": "nx:run-commands",
"options": {
"commands": ["dotnet clean", "rimraf bin", "rimraf obj"],
"cwd": "apps/csharp"
},
"cache": false
},
"format": {
"executor": "nx:run-commands",
"options": {
"commands": ["dotnet format"],
"cwd": "apps/csharp"
},
"cache": false
},
"build": {
"executor": "nx:run-commands",
"outputs": ["{workspaceRoot}/dist/apps/csharp"],
"options": {
"commands": [
"rimraf ../../dist/apps/csharp",
"mkdir -p ../../dist/apps/csharp",
"dotnet build --configuration Release --output ../../dist/apps/csharp"
],
"cwd": "apps/csharp",
"parallel": false
},
"cache": false
},
"run": {
"executor": "nx:run-commands",
"dependsOn": ["build"],
"options": {
"commands": ["dotnet run"],
"cwd": "apps/csharp"
}
},
"test": {
"options": {
"passWithNoTests": true
}
}
}
}
64 changes: 53 additions & 11 deletions ci/src/pipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,14 @@ const languageTargets: Target[] = [
appTarget: "app-ruby",
versions: languageVersions["ruby"],
},
{
icon: ":csharp:",
label: "C#",
key: "csharp",
sdkTarget: "sdk-csharp",
appTarget: "app-csharp",
versions: [], // C# doesn't use mise for version management
},
];

function generateAppCommands(key: string, appTarget: string) {
Expand All @@ -124,6 +132,15 @@ function generateAppCommands(key: string, appTarget: string) {
language = "node";
}

// C# uses dotnet directly, not mise
if (key === "csharp") {
return [
"mise trust",
`nx install ${appTarget}`,
`nx run ${appTarget}:run`,
];
}

let appInstallCommand = `mise exec ${language}@{{matrix}} -- nx install ${appTarget}`;
if (language === "python") {
appInstallCommand = `mise exec ${language}@{{matrix}} -- pip install --no-cache-dir uv black && nx install ${appTarget}`;
Expand Down Expand Up @@ -196,17 +213,42 @@ languageTargets.forEach((target) => {
`nx run ${target.sdkTarget}:docs:build`,
],
},
{
label: ":lab_coat: Apps",
key: `${target.key}-apps`,
depends_on: [`${target.key}-test`, `${target.key}-build`],
plugins: languagePlugins,
commands: generateAppCommands(target.key, target.appTarget),
matrix: target.versions,
env: {
MISE_NODE_VERIFY: false,
},
},
// Only add matrix if there are versions to test
...(target.versions.length > 0
? [
{
label: ":lab_coat: Apps",
key: `${target.key}-apps`,
depends_on: [
`${target.key}-test`,
`${target.key}-build`,
],
plugins: languagePlugins,
commands: generateAppCommands(
target.key,
target.appTarget
),
matrix: target.versions,
env: {
MISE_NODE_VERIFY: false,
},
},
]
: [
{
label: ":lab_coat: Apps",
key: `${target.key}-apps`,
depends_on: [
`${target.key}-test`,
`${target.key}-build`,
],
plugins: languagePlugins,
commands: generateAppCommands(
target.key,
target.appTarget
),
},
]),
],
});
});
Expand Down
13 changes: 11 additions & 2 deletions ci/src/release.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ const plugins = [
"GITHUB_TOKEN",
"NPM_TOKEN",
"PYPI_TOKEN",
"GEM_HOST_API_KEY"
"GEM_HOST_API_KEY",
"NUGET_API_KEY"
]
}},
{ "rubygems-oidc#v0.2.0": { role: "rg_oidc_akr_emf87k6zphtb7x7adyrk" } },
Expand All @@ -22,7 +23,8 @@ const plugins = [
parameters: {
NPM_TOKEN: "/prod/buildkite-sdk/npm-token",
PYPI_TOKEN: "/prod/buildkite-sdk/pypi-token",
GITHUB_TOKEN: "/prod/buildkite-sdk/github-token"
GITHUB_TOKEN: "/prod/buildkite-sdk/github-token",
NUGET_API_KEY: "/prod/buildkite-sdk/nuget-api-key"
}
}}
]
Expand Down Expand Up @@ -79,6 +81,13 @@ const languageTargets = [
key: "ruby",
sdkLabel: "sdk-ruby",
appLabel: "app-ruby"
},
{
icon: ":csharp:",
label: "C#",
key: "csharp",
sdkLabel: "sdk-csharp",
appLabel: "app-csharp"
}
]

Expand Down
128 changes: 128 additions & 0 deletions internal/gen/csharp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package main

import (
"fmt"

"github.com/buildkite/buildkite-sdk/internal/gen/csharp"
"github.com/buildkite/buildkite-sdk/internal/gen/types"
"github.com/buildkite/buildkite-sdk/internal/gen/utils"
)

func generateCSharpTypes(
generator types.PipelineSchemaGenerator,
outDir string,
) error {
// Track written types and their dependencies for ordering
writtenTypes := make(map[string]bool)
allTypes := make(map[string]types.Value)
allDeps := make(map[string][]string)

// First pass: convert all definitions to values
for _, name := range generator.Definitions.Keys() {
prop, err := generator.Definitions.Get(name)
if err != nil {
return err
}

value, deps, err := generator.PropertyDefinitionToValue(name, prop)
if err != nil {
return fmt.Errorf("converting property definition to value: %v", err)
}

allTypes[name] = value
allDeps[name] = deps
}

// Second pass: write types in dependency order
var writeType func(name string) error
writeType = func(name string) error {
if writtenTypes[name] {
return nil
}

// Write dependencies first
for _, dep := range allDeps[name] {
if err := writeType(dep); err != nil {
return err
}
}

value, exists := allTypes[name]
if !exists {
return nil // Reference to external type
}

contents, err := value.CSharp()
if err != nil {
return fmt.Errorf("generating C# for [%s]: %v", name, err)
}

if contents == "" {
writtenTypes[name] = true
return nil
}

fileName := fmt.Sprintf("%s/%s.cs", outDir, utils.ToTitleCase(name))
file := csharp.NewCSharpFile(
"Buildkite.Sdk.Schema",
fileName,
[]string{
"System.Collections.Generic",
"System.Text.Json.Serialization",
},
contents,
)

if err := file.Write(); err != nil {
return fmt.Errorf("writing file [%s]: %v", fileName, err)
}

writtenTypes[name] = true
return nil
}

// Write all types
for _, name := range generator.Definitions.Keys() {
if err := writeType(name); err != nil {
return err
}
}

// Generate the main BuildkitePipeline class
pipelineContents, err := generator.GenerateCSharpPipelineSchema()
if err != nil {
return fmt.Errorf("generating pipeline schema: %v", err)
}

pipelineFile := csharp.NewCSharpFile(
"Buildkite.Sdk.Schema",
fmt.Sprintf("%s/BuildkitePipeline.cs", outDir),
[]string{
"System.Collections.Generic",
"System.Text.Json.Serialization",
},
pipelineContents,
)

if err := pipelineFile.Write(); err != nil {
return fmt.Errorf("writing pipeline file: %v", err)
}

// Generate interfaces file
interfacesContents := csharp.GenerateInterfaces()
interfacesFile := csharp.NewCSharpFile(
"Buildkite.Sdk.Schema",
fmt.Sprintf("%s/Interfaces.cs", outDir),
[]string{
"System.Collections.Generic",
"System.Text.Json.Serialization",
},
interfacesContents,
)

if err := interfacesFile.Write(); err != nil {
return fmt.Errorf("writing interfaces file: %v", err)
}

return nil
}
Loading