Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Discussion about Solution files, filters and CI #2560

Closed
bruno-garcia opened this issue Aug 20, 2023 · 9 comments · Fixed by #2576, #2586 or #2587
Closed

Discussion about Solution files, filters and CI #2560

bruno-garcia opened this issue Aug 20, 2023 · 9 comments · Fixed by #2576, #2586 or #2587

Comments

@bruno-garcia
Copy link
Member

bruno-garcia commented Aug 20, 2023

I found it hard to get my head around this:
image

Mainly because:

  1. Sentry.sln vs Sentry.Full.sln was not very intuitive to me. Shouldn't Sentry.sln already be "full"?
    In that case, could it be Sentry.sln with everything, and just the filters on top? I understand there's some logic based on the solution file to avoid compiling the mobile targets:

<PropertyGroup Condition="'$(SolutionName)' != '' And '$(SolutionName)' != 'Sentry.Mobile' And '$(SolutionName)' != 'Sentry.Full'">
<NO_MOBILE>true</NO_MOBILE>
</PropertyGroup>

Is there another approach?

  1. Sentry-CI-Pack.slnf: What does give us? Since pack happens on the same workflow where things are built, I imagine this saves from looking what projects need to be backed (<IsPackable>true..)? Samples are all marked as not packable already:

<IsPackable>false</IsPackable>

Same with tests:
<IsPackable>false</IsPackable>

Is the 'speed up' worth having to maintain this additional filter file?

  1. Too many slfn files? SentryNoSamples.slnf, SentryAzureFunctions.slnf, etc. Could this be just Unload project instead or are these used often enough?

The biggest problem I see is that everytime we add a new project, we need to deal with 3 solutions and all subsequent slnf because there's no way to use glob patterns, or negate (like everything except /samples, or for the CodeQL filter, just add everything under src, to name a couple).

  1. Sentry.Samples.OpenTelemetry.AspNet classic msbuild project in the repo. Because dotnet build doesn't work here, there was an effort to keep these out, since the creation of this repo. It doesn't seem like this is built in CI (and that's probably good because it'd be slow, and require a separate build script/flow). There was another repo (https://github.com/getsentry/sentry-dotnet-samples and later https://github.com/getsentry/examples/tree/master/dotnet) to keep such samples that are rarely used and don't need to be built in CI on each PR.

  2. The .root situation. I find these Solution Files thing a bit annoying. We have to manually curate a list of files/scripts etc to get into the solution. And when in the editor we only see what's there. So if something new gets added to the repo, we need to 'remember' (my weakest point) to add. These days IDEs are smarter, Rider for example:
    image

Already shows you all the files in the repo overlaying with the soltion:
image
Now I can see everything in the file system, as god intended:
image

So I propose getting rid of all of those solution items copied over in 3 different sln files

  1. How about we stuff all of these filters in some folder?

  2. It's hard to see the side effect of some changes. It builds on your filter. But you're actually breaking some sample or other part of the solution.

Or perhaps I'm overthinking, first time trying to contribute here in a year or so. That's possible. Thoughts?

@jamescrosswell
Copy link
Collaborator

  1. potentially we could rename Sentry.sln -> SentryNoMobile.sln and then rename Sentry.Full -> Sentry.sln
  2. Sounds reasonable. I've been operating on the assumption there's a good reason for most stuff so haven't challenged any of this. The IsPackable build property sounds good instead if that's the only reason we have a separate solution for CI though.
  3. I don't use those personally... happy to delete
  4. Is there an issue having them in the repo? If they're not part of any of the solutions that get built by CI or that we use when developing, I don't think they get in the way... and it sounds like a pain maintaining a separate repo for these. If we ever need to debug anything or look into issues with Classic ASP.NET, the sample project is a good way of quickly bootstrapping things for investigation, so it's nice to have these next to the source...
  5. Maybe worth thinking about the experience for folks who are new to the repo... as long as the key files they'd need (ready, contrib etc.) are obvious and easy to find, I'm happy with another approach. If they need to "know" that you have to show all files in the IDE to see relevant stuff, it starts to become a bit of a gotcha that will trip people up (repeatedly). So maybe there's an argument to keep those things referenced in the solution files.
  6. I don't mind having the filters in the root directory. They're only really convenient if you're using Visual Studio anyway... in Rider you don't have a solution filter drop down to easily apply different filters in the same way.

@bruno-garcia
Copy link
Member Author

bruno-garcia commented Aug 21, 2023

After trying to figure out why a specific test project wasn't running, sadly took me too long to check the build yaml. There's a solution filter for that too that has to be manually changed:

        run: dotnet test Sentry-CI-Test.slnf -c Release --no-build --nologo -l GitHubActions -l "trx;LogFilePrefix=testresults_${{ runner.os }}"

It's quite hard to reproduce CI problems locally due to this complexity.

I strongly believe we should dotnet build and dotnet test on the same target (sln or slnf) and avoid having to maintain two files for these two steps unless there's a strong reason (several minutes gain in CI?) not to

@bitsandfoxes
Copy link
Contributor

I've been operating on the assumption there's a good reason for most stuff so haven't challenged any of this.
I'm in the same boat.

I think there should be as few filters as possible. Mobile vs. No Mobile comes to mind. I have not yet used a single .slnf for local development.

Regarding CI: I'm of the strong opinion that whatever happens in CI should be as close to what we have in our local environment. Ideally, those should be identical. I'm really not buying into speeding up CI at the cost of complexity.

@jamescrosswell
Copy link
Collaborator

jamescrosswell commented Aug 22, 2023

Re getting rid of the solution filters, there's a StackOverflow thread that looks interesting but I haven't quite wrapped my head around it yet:

I was thinking we could define some custom BeforeBuild logic in our Directory.Build.targets file that could check something like the matrix.os from GitHub Actions and use that to skip/exit the build for any projects that weren't relevant to a particular CI build. That way we could map projects to the CI builds in build props rather than solution filters.

However, I think what Mengdi Liang is suggesting would return an exit code of 1, which would stop the project being built but would implicitly break our builds in CI as it would look like an error to GitHub.

Maybe it's easier to keep using the solution filters to determine which projects to include in CI, but we get rid of some of the other unnecessary filters:

Keep Solution/Filter Comments
Sentry.Full.sln Rename to Sentry.sln
SentryNoMobile.slnf If you don't have a Cocoa/Android dev environment
Sentry-CI-Build-Linux.slnf
Sentry-CI-Build-macOS.slnf
Sentry-CI-Build-Windows.slnf
Sentry-CI-CodeQL.slnf Use same slnf as for build
Sentry-CI-Pack.slnf Use same slnf as for build + IsPackable build prop
Sentry-CI-Test.slnf Use same slnf as for build
SentryAspNetCore.slnf Unnecessary
SentryAzureFunctions.slnf Unnecessary
SentryCore.slnf Unnecessary
SentryDiagnosticSource.slnf Unnecessary
SentryNoSamples.slnf Unnecessary
Sentry.Mobile.sln Unnecessary
SentryMobile.slnf Unnecessary
Sentry.sln Replaced by SentryNoMobile.slnf

One note about the above is that if we had both Sentry.Bindings.Cocoa.csproj and Sentry.Samples.Wpf in the same solution file, you wouldn't be able to build that solution anywhere... It wouldn't build on Windows (due to the Cocoa stuff) and it wouldn't build on Mac (due to the WPF stuff)... So we'd have to start doing our regular development using one of the Sentry-CI-matrix.os slnf files. That might not be a bad things, as it brings CI closer to what we're doing day to day. And we could maybe take the -CI- bit out of the name of those *.slnf files.

@bruno-garcia
Copy link
Member Author

Would that work with a single solution? Today we need at least two solutions because of this hack:

<PropertyGroup Condition="'$(SolutionName)' != '' And '$(SolutionName)' != 'Sentry.Mobile' And '$(SolutionName)' != 'Sentry.Full'">
<NO_MOBILE>true</NO_MOBILE>
</PropertyGroup>

We'd need a different approach to avoid building those.

I wonder if a script that generates these slnf are also an approach. Because it's useful to have less projects loaded depending on the work you do, it's just hard to maintain all of those filters/slns

@jamescrosswell
Copy link
Collaborator

jamescrosswell commented Aug 23, 2023

I wonder if a script that generates these slnf are also an approach. Because it's useful to have less projects loaded depending on the work you do, it's just hard to maintain all of those filters/slns

I took a look at rosenbjerg/slnf-gen for this but I think it's more of a job for a script than a program that needs to be compiled and slnf-gen has some limitations. So I put together a powershell script that could do this for us based on a YAML config:
https://github.com/getsentry/sentry-dotnet/pull/2576/files

So far I've tried to do it just for Sentry-CI-Build-Linux.slnf and it identified various files that I suspect should be part of that build on Linux.

The tentative config I have looks like this (all open for debate - I'm not sure I have the most context for which files should be in/out of each slnf):

# Sentry filter configuration

solution: Sentry.Full.sln

filterConfigs:

  # Core filter for CI builds on Linux
  - outputPath: Sentry-CI-Build-Linux.slnf

    # Include all projects
    includes:
      - "**/*.csproj"

    # Exclude iOS/MacCatalyst and .NET Framework projects
    excludes:
      - modules/**/*.csproj
      - "**/*Ios.csproj"
      - "**/*Cocoa*.csproj"
      - "**/*MacCatalyst.csproj"
      - "**/*SourceGen.csproj"
      - "**/*.AspNet.csproj"

@jamescrosswell
Copy link
Collaborator

jamescrosswell commented Aug 23, 2023

Regarding the NO_MOBILE hack, that flag is set for the following solutions/filters:

  • Sentry.sln
  • SentryAspNetCore.slnf
  • SentryAzureFunctions.slnf
  • SentryCore.slnf
  • SentryDiagnosticSource.slnf
  • SentryNoSamples.slnf

None of those are used in CI... essentially it disables mobile target frameworks for developers who are working on any of those solutions/filters, in the following scenarios (I think):

Developing on Windows

Can't target iOS/MacCatalyst

<TargetFrameworks Condition="'$(NO_IOS)' == '' And $([MSBuild]::IsOSPlatform('OSX'))">$(TargetFrameworks);net6.0-ios</TargetFrameworks>
<TargetFrameworks Condition="'$(NO_MACCATALYST)' == '' And $([MSBuild]::IsOSPlatform('OSX'))">$(TargetFrameworks);net6.0-maccatalyst</TargetFrameworks>

Developing on Mac

Don't want to deal with the complexity of setting up the dev environment to support mobile targets

<TargetFrameworks Condition="'$(NO_ANDROID)' == ''">$(TargetFrameworks);net6.0-android</TargetFrameworks>
<TargetFrameworks Condition="'$(NO_IOS)' == '' And $([MSBuild]::IsOSPlatform('OSX'))">$(TargetFrameworks);net6.0-ios</TargetFrameworks>
<TargetFrameworks Condition="'$(NO_MACCATALYST)' == '' And $([MSBuild]::IsOSPlatform('OSX'))">$(TargetFrameworks);net6.0-maccatalyst</TargetFrameworks>

Alternatives

The Mac use case is valid (particularly if we're thinking about non-Sentry contributors)... it took me ages to get my dev envioronment setup for mobile. It was not easy.

I think the Windows one is necessary as you literally can't build the solution on Windows unless you omit those target frameworks. Although, if that's true, I'm wondering how we do this on CI since Sentry-CI-Build-Windows.slnf is based on Sentry.Full.sln (so presumably includes those targets and builds successfully on Windows)?

It looks like we have a different solution for Tizen, and we might be able to leverage this to determine which targets to include, instead of using the Sentry.sln hack:

<!-- Target Tizen only if the Tizen SDK workload pack is installed. -->
<TargetFrameworks Condition="'$(NO_TIZEN)' == '' And Exists('$(MSBuildBinPath)\..\..\packs\Samsung.Tizen.Sdk')">$(TargetFrameworks);net6.0-tizen</TargetFrameworks>

@jamescrosswell
Copy link
Collaborator

I added a feature request for msbuild btw, but I see they've got 1.2k issues on their backlog so I'm guessing we'll need to come up with another plan in the mean time.

I'll dig into the possibility of disabling target frameworks based on whether workloads are installed or not. Not quite the same but gives us maybe most of what we want/need.

@jamescrosswell
Copy link
Collaborator

OK, latest commit here will disable mobile targets when running the build on machines that don't have the appropriate workloads installed:

<!-- Disable mobile targets if workloads aren't installed. -->
<PropertyGroup Condition="'$(AndroidWorkloadVersion)' == ''">
<NO_ANDROID>true</NO_ANDROID>
</PropertyGroup>
<PropertyGroup Condition="'$(IosWorkloadVersion)' == ''">
<NO_IOS>true</NO_IOS>
</PropertyGroup>
<PropertyGroup Condition="'$(MacCatalystWorkloadVersion)' == ''">
<NO_MACCATALYST>true</NO_MACCATALYST>
</PropertyGroup>
<PropertyGroup Condition="'$(MauiWorkloadVersion)' == ''">
<NO_WINDOWS>true</NO_WINDOWS>
</PropertyGroup>
<PropertyGroup Condition="!Exists('$(MSBuildBinPath)\..\..\packs\Samsung.Tizen.Sdk')">
<NO_TIZEN>true</NO_TIZEN>
</PropertyGroup>

That's not exactly the same behaviour we had before... possibly we'd want to ensure the appropriate workloads are installed when running the build on our CI machines.

One possible alternative would be to leave the builds running pretty much as they are but copy Sentry_Full.sln to a SentryNoMobile.sln prior to running the build (overwriting whatever SentryNoMobile.sln exists before we do that). That way we could continue to have some Solution Filters that filter against Sentry_Full.sln and other that filter against SentryNoMobile.sln, and we could continue to use the hack we're currently using (check to see what the solution file is) as a way to determine whether or not mobile targets should be enabled.

Any other ideas?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants