Skip to content

feat(csharp): add sln-format config option for legacy .sln solution files#13460

Merged
patrickthornton merged 10 commits intomainfrom
devin/1773340199-csharp-use-sln-format
Mar 13, 2026
Merged

feat(csharp): add sln-format config option for legacy .sln solution files#13460
patrickthornton merged 10 commits intomainfrom
devin/1773340199-csharp-use-sln-format

Conversation

@patrickthornton
Copy link
Contributor

@patrickthornton patrickthornton commented Mar 12, 2026

Description

Refs: Requested by @patrickthornton
Link to Devin Session

Adds a sln-format configuration option to the C# SDK generator. When set to "sln", the generator produces a legacy .sln solution file in addition to the default .slnx format. The default value "slnx" generates only the modern .slnx file. This is useful for teams that need compatibility with older .NET tooling or CI systems that do not yet support .slnx.

generators:
  - name: fernapi/fern-csharp-sdk
    config:
      sln-format: sln

Changes Made

  • Added sln-format enum option ("sln" | "slnx") to CsharpConfigSchema.ts
  • Added corresponding slnFormat setting in generation-info.ts (defaults to "slnx")
  • Modified CsharpProject.ts:
    • createSolutionFile always generates .slnx; when sln-format is "sln", it additionally generates a legacy .sln file
    • dotnetFormat always uses .slnx (present in all configurations)
    • Added generateDeterministicGuid() helper that hashes the project name via MD5 to produce stable GUIDs for the .sln file, ensuring reproducible output across generation runs
  • Updated all seed build/test scripts in seed.yml to find both *.slnx and *.sln solution files
  • Removed **/*.sln from seed/csharp-sdk/.gitignore so generated .sln files are tracked
  • Added a seed test fixture (simple-api/use-sln-format) with generated snapshot (both .sln and .slnx)
  • Added changelog entry in versions.yml (version 2.27.0)

Testing

  • Seed test simple-api:use-sln-format passes (pnpm seed test --generator csharp-sdk --fixture simple-api --outputFolder use-sln-format --skip-scripts)
  • TypeScript compilation succeeds (pnpm turbo run compile --filter @fern-api/fern-csharp-sdk)

⚠️ Human Review Checklist

  1. .sln format validity: The generated .sln uses Visual Studio Solution File Format Version 12.00 with the FAE04EC0-301F-11D3-BF4B-00C04F79EFBC project type GUID. The seed test was run with --skip-scripts so the .sln was not validated with dotnet build. Worth verifying that the generated file opens/builds correctly.
  2. Line endings: The .sln file is generated with \n (Unix) line endings rather than \r\n (Windows). Modern dotnet CLI handles both, but worth noting for any Windows-specific tooling concerns.
  3. Deterministic GUIDs: Uses MD5 hashing of the project name — fine for reproducibility, but verify the output GUIDs look reasonable in the snapshot (SeedSimpleApi.sln).
  4. Seed build script ordering: When both .sln and .slnx exist, the find | head -n 1 in build scripts will pick whichever file is found first. Both should be equivalent, but the ordering is filesystem-dependent.
  5. .gitignore change: Removed **/*.sln from seed/csharp-sdk/.gitignore — needed so the fixture .sln file is tracked. Verify no other fixtures are affected by this.

Open with Devin

…on files

Co-Authored-By: patrick <patrickthornton@college.harvard.edu>
@devin-ai-integration
Copy link
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

devin-ai-integration bot and others added 2 commits March 12, 2026 18:40
Co-Authored-By: patrick <patrickthornton@college.harvard.edu>
Co-Authored-By: patrick <patrickthornton@college.harvard.edu>
Copy link
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 potential issue.

View 4 additional findings in Devin Review.

Open in Devin Review

Comment on lines +97 to +99
- outputFolder: use-sln-format
customConfig:
use-sln-format: true
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Missing .sln file in seed test output for use-sln-format fixture

The use-sln-format seed test fixture is missing the generated .sln solution file. The no-custom-config fixture correctly includes SeedSimpleApi.slnx at the root of the output, but the use-sln-format fixture has no corresponding SeedSimpleApi.sln file. This means the seed build/test scripts (seed/csharp-sdk/seed.yml:13) that search for *.slnx or *.sln files will fail to find a solution file, causing dotnet build and dotnet test to fail for this fixture. The .sln file is generated in generators/csharp/base/src/project/CsharpProject.ts:504 using the same absolutePathToSolutionDirectory as the .slnx path, so it should appear in the seed output but does not.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — the .sln file was being excluded by seed/csharp-sdk/.gitignore which had **/*.sln. Fixed in c6d84b3: removed that rule and added the SeedSimpleApi.sln file to the repo.

… generated file

Co-Authored-By: patrick <patrickthornton@college.harvard.edu>
Comment on lines +475 to +476
const slnContents = [
"",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The .sln file content starts with an empty string, which will produce an invalid solution file that begins with a blank line. Legacy .sln files must start directly with the Visual Studio header without any preceding newlines.

const slnContents = [
    "Microsoft Visual Studio Solution File, Format Version 12.00",  // Remove the "" from line 476
    "# Visual Studio Version 17",
    // ... rest of the array
].join("\n");

This will cause Visual Studio and dotnet CLI tools to potentially reject or misparse the solution file.

Suggested change
const slnContents = [
"",
const slnContents = [
"Microsoft Visual Studio Solution File, Format Version 12.00",

Spotted by Graphite

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin check this out

@fern-support
Copy link
Collaborator

Can we try to have both sln and slnx when someone wants to use use-sln-format: true?
Also, let's use this flag:
sln-format: sln and sln-format: slnx
sln will do both sln and slnx if that doesn't cause any issues!

…d .slnx when sln

Co-Authored-By: patrick <patrickthornton@college.harvard.edu>
@devin-ai-integration
Copy link
Contributor

Done! Updated the config to sln-format: sln | slnx:

  • sln-format: sln → generates both .sln and .slnx files
  • sln-format: slnx (default) → generates only .slnx

Also fixed the leading blank line issue flagged by Graphite. See commit 7eed932.

@devin-ai-integration devin-ai-integration bot changed the title feat(csharp): add use-sln-format config option for legacy .sln solution files feat(csharp): add sln-format config option for legacy .sln solution files Mar 13, 2026
Co-Authored-By: patrick <patrickthornton@college.harvard.edu>
@Swimburger
Copy link
Member

Verify:
.sln format validity: The generated .sln uses Visual Studio Solution File Format Version 12.00 with the FAE04EC0-301F-11D3-BF4B-00C04F79EFBC project type GUID. The seed test was run with --skip-scripts so the .sln was not validated with dotnet build. Worth verifying that the generated file opens/builds correctly.
Line endings: The .sln file is generated with \n (Unix) line endings rather than \r\n (Windows). Modern dotnet CLI handles both, but worth noting for any Windows-specific tooling concerns.
Deterministic GUIDs: Uses MD5 hashing of the project name — fine for reproducibility, but verify the output GUIDs look reasonable in the snapshot (SeedSimpleApi.sln).
Seed build script ordering: When both .sln and .slnx exist, the find | head -n 1 in build scripts will pick whichever file is found first. Both should be equivalent, but the ordering is filesystem-dependent.
.gitignore change: Removed **/*.sln from seed/csharp-sdk/.gitignore — needed so the fixture .sln file is tracked. Verify no other fixtures are affected by this.

@devin-ai-integration
Copy link
Contributor

@Swimburger Thanks for the review! Here's a quick rundown on each point:

  1. .sln format validity — The file starts with Microsoft Visual Studio Solution File, Format Version 12.00 and uses the standard C# project type GUID (FAE04EC0-301F-11D3-BF4B-00C04F79EFBC). The leading blank line issue flagged by Graphite has been fixed. Haven't validated with dotnet build end-to-end though (seed test ran with --skip-scripts), so worth a manual check if you want full confidence.

  2. Line endings — Correct, generated with \n. Modern dotnet CLI handles both fine. If Windows-specific tooling is a concern, we could add \r\n support later, but it shouldn't be needed for most users.

  3. Deterministic GUIDs — The GUIDs in the snapshot look reasonable:

    • SeedSimpleApi{B03A7A2B-3B0F-2ED7-2B2C-5154CF821312}
    • SeedSimpleApi.Test{4EDD4DAE-E1F5-2F72-AB2D-D180CF4C7600}

    These are stable across regeneration runs (MD5 hash of project name, formatted as a GUID).

  4. Seed build script ordering — Both .sln and .slnx reference the same .csproj files, so whichever find | head -n 1 picks will build identically. Not a correctness issue, just a note.

  5. .gitignore change — Only the use-sln-format fixture generates a .sln file (requires sln-format: sln in config). No other fixtures are affected since the default is slnx which only generates .slnx.

@Swimburger
Copy link
Member

merge from main

@patrickthornton patrickthornton merged commit 2efb1de into main Mar 13, 2026
87 checks passed
@patrickthornton patrickthornton deleted the devin/1773340199-csharp-use-sln-format branch March 13, 2026 19:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants