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

Support .net6 runtime assemblies, new Build system, and Legacy Fake Codebase Separation #2609

Conversation

yazeedobaid
Copy link
Collaborator

@yazeedobaid yazeedobaid commented Oct 11, 2021

Description

Introduction

This PR introduce the following modifications to Fake codebase and the build system:

  1. .Net6 Runtime Assemblies Support
  2. Legacy Fake Codebase Separation
  3. Using GitHub Actions for Build and Test
  4. Modifications to build.fsx File

Each point is explained in the following sections.

.Net6 Runtime Assemblies Support

This PR adds support for .NET 6 runtime assemblies in Fake runner, reported in issue #2588. As pointed out in the issue discussion, Fake runner pins its execution to NETSTANDARD2.0, so adding a dependency that supports .NET5 or .NET6 only will fail. The move from NETSTANDARD2.0 to .NET6 is not straightforward, since in NETSTANDARD2.0 runtime assemblies that are needed by the script are installed from a NuGet package called NETStandard.Library. However, in .NET5 and .NET6 it is not the same.

To support new runtime assemblies, this PR make changes to Fake.Runtime project, the changes introduce a pinned framework in which Fake runner will use always in resolving dependencies and framework restrictions. Since .NET6 is the next LTS release, it was used instead of Netstandard2.0 which was used previously. Secondly, it will try to resolve the runtime assemblies as follows:

  1. It first try to find a global.json file from script directory. If it find one it will checks the SDK version in it.
  2. If SDK version found is .NET6, then it will try to locate the installed .NET6 runtime assemblies from installed SDKs.
  3. Otherwise, it will default to current behavior of using NetStandard2.0 assemblies obtained from NETStandard.Library NuGet package.

So, to use .NET6 runtime assemblies, a global.json file must exists with SDK version set to .NET6. Otherwise, it will default to using Netstandard2.0 assemblies.

For the case reported in 2588 issue, the following gist provide a simple script that demonstrate how to use .NET6 runtime assemblies by including a global.json file with .NET6 SDK specified.

The benefits of this solution is the switch between runtime assemblies will now be much easier. For example, when .NET6 is officially released and adopted more. We can drop .Netstandard2.0 and use by default .NET6 - for that Fake runner will require .NET6 as a requirement - and provide a switch for next release of .NET. For example when .NET7 is released, runtime assemblies for it can be activated with a global.json file.

The cons of this solution as pointed out in the issue is that, Fake runner now depends on an installed SDK.

In addition to runtime assemblies reference changes. A change was also introduce to the target profile argument in FSI. In FSI, the target profile for running an interactive script is different between .NET and .Netstandard. So a switch was included to handle that by resolved assemblies version in CompileRunner.fs.

I added two tests in integration tests project to test running Fake runner with .Netstandard2.0 and .NET6 runtime assemblies in Fake.DotNet.RuntimeReferenceAssemblies.fs

→ This handle issue #2588

Legacy Fake Codebase Separation

During the implementation, I faced an issue with dependencies. I wasn't able to detect what was happening and the solution was to remove all the references in the main group in paket.dependecies file which as I found is for the legacy Fake build. I also disabled the build and release for legacy Fake in the Azure pipeline using the environment variable to disable legacy build. My argument for this is we need to move on with the Fake 5 implementation.

Also, while doing that, I moved the current paket.dependencies and paket.lock files to legacy directory and I marked them as ignored for reference. In addition, I moved other files/directories to legacy directory as a form of separation between Fake 5 codebase and legacy Fake codebase, which are:

  1. paket.dependenciessrc/legacy/[ignored]paket.dependencies
  2. paket.locksrc/legacy/[ignored]paket.lock
  3. legacy-build.fsxsrc/legacy/legacy-build.fsx
  4. .gitlab-ci.ymlsrc/legacy/[ignored].gitlab-ci.yml
  5. .travis.ymlsrc/legacy/[ignored].travis.yml
  6. appveyor.ymlsrc/legacy/[ignored]appveyor.yml
  7. Legacy-FAKE.slnsrc/legacy/Legacy-FAKE.sln
  8. Legacy-FAKE.sln.DotSettingssrc/legacy/Legacy-FAKE.sln.DotSettings
  9. Samples/src/legacy/Samples/
  10. Vagrantfilesrc/legacy/Vagrantfile
  11. build-web-bundles.cmdsrc/legacy/[ignored]build-web-bundles.cmd
  12. build-web-bundles.fsxsrc/legacy/[ignored]build-web-bundles.fsx

For the CI/CD files, I moved them and consider them as legacy since this PR also introduce the usage of GitHub actions for build, test, and preparation for a release as pointed out in the section below.

Also, the following Paket files have been marked as ignored to not picked up by Paket:

  1. src/legacy/FAKE/paket.referencessrc/legacy/FAKE/[ignored]paket.references
  2. src/legacy/Fake.Experimental/ignored]paket.references
  3. src/legacy/Fake.FluentMigrator/ignored]paket.references
  4. src/legacy/Fake.Gallio/ignored]paket.references
  5. src/legacy/Fake.IIS/ignored]paket.references
  6. src/legacy/Fake.SQL/ignored]paket.references
  7. src/legacy/FakeLib/ignored]paket.references
  8. src/legacy/FsCheck.Fake/ignored]paket.references
  9. src/legacy/Test.FAKECore/ignored]paket.references
  10. src/legacy/Test.Git/ignored]paket.references

By these changes and changes to build.fsx script, legacy Fake release will be discontinued.

Using GitHub Actions for Build and Test

This PR also adds two GitHub actions for build, test, and preparing for a release. Each one is explained as follows:

  • The build_and_test action builds the repository from PRs and Pushes to release/next branch on three OSs which simulate Appveyor, Travis, and Azure pipelines. It call the build script with the Default target which build the project and run the tests.

    The steps before calling the build script pack the Fake runner and replace the current fake-cli DotNet tool in it with produced version. I did this for two reasons:

    1. Dogfood the action with the latest changes in runner or repository at whole.
    2. Modifying the Paket.Core version between runner versions will not enable build system to succeed. As is happening currently with the Azure pipeline. It fails due to changes in paket.lock file which was produced by Paket with version 6.0.13 which adds target frameworks for .Net6. And previous Fake runner uses older version of Paket which don't understand .Net6 target frameworks.
  • The prepare_release action is intended to be run on pushes on release/next and/or main branches for preparing artifacts for a release using Windows OS which simulates current Azure CI pipeline. It uses similar steps as build_and_test action except it run build script with Release_BuildAndTest target to produce artifacts and collect them.

    Not sure if this actions should be run on release/next or main branches?! If we run it on release/next we will get artifacts published for each change. So this will enable feedback and testing more frequent. On main branch, it will be when a merge from release/next to main is done for releasing, which is less frequent. Currently I set the trigger to pushes on release/next branch.

These two actions can be used as in-house build and test for Fake. For release, we can still use current Azure release pipeline but give it the artifacts from prepare_release action.

The following run has the artifacts produced from prepare_release action - at the bottom of the page.

→ This handles issue #2607

Modifications to build.fsx File

I have tried to organize the build script a little pit and while separating the legacy build. These include:

  1. the #if BOOTSTRAP directive has been removed since GitHub actions uses caching step which caches dependencies. And while working on this PR, changes to build script dependencies didn't effect until I removed it.
  2. The .fake/build.fsx/intellisense.fsx file and call to load it in build script has been removed since it was needed for legacy build.
  3. The publish method now copy build artifacts to a directory to be picked up by release action.
  4. Creation of Debian package command no uses --no-restore option since it seems .NET6 SDK is not supported yet in Debian distribution as pointed out here. Without this option, the dotnet deb command will try to use .NET5 references and will fail since they are not installed in build server and we use .NET6.
  5. Set build version on GitHub actions to use RunId environment variable and commit SHA as a metadata for version.

Other Modifications

  • The dependency Microsoft.DotNet.PlatformAbstractions has been removed since it is deprecated in .NET6 as pointed out here
  • A handle for native assemblies that doesn't include version specific runtime identifier was added to Fake runtime. The issue rises in integration tests that call SQLite. Specifically, the error was Unable to load DLL 'SQLite.Interop.dll': The specified module could not be found. .The SQLite.Interop library uses runtime identifier without a version specific. On windows for example, it was win-x64, runtime graph uses version specific runtime identifier, on my machine it was win10-x64. I referred to this documentation page from Microsoft.

ToDos

If this PR is accepted the following ToDos are still needed to be done:

  • Add status badges to README file for GitHub actions.
  • Disable trigger on Azure CI pipeline.
  • Test release pipeline for picking up artifacts from GitHub actually works.
  • Add documentation for .NET6 runtime assembly usage.

Summary

Sorry for making this PR very long and full of modifications, but all issues handled in this PR are related to each other in some way. I tried to document every modification I did in it. Please have a look at the proposal for supporting .NET6 runtime assemblies, new build system, and modification to separate legacy codebase from Fake 5 codebase. Your feedback is highly appreciated.

Thanks

TODO

Feel free to open the PR and ask for help

  • New (API-)documentation for new features exist (Note: API-docs are enough, additional docs are in help/markdown)

  • unit or integration test exists (or short reasoning why it doesn't make sense)

    Note: Consider using the CreateProcess API which can be tested more easily, see https://github.com/fsharp/FAKE/pull/2131/files#diff-4fb4a77e110fbbe8210205dfe022389b for an example (the changes in the DotNet.Testing.NUnit module)

  • boy scout rule: "leave the code behind in a better state than you found it" (fix warnings, obsolete members or code-style in the places you worked in)

  • (if new module) the module has been linked from the "Modules" menu, edit help/templates/template.cshtml, linking to the API-reference is fine.

  • (if new module) the module is in the correct namespace

  • (if new module) the module is added to Fake.sln (dotnet sln Fake.sln add src/app/Fake.*/Fake.*.fsproj)

  • Fake 5 API guideline is honored

@baronfel
Copy link
Contributor

Chiming in to say a giant +1 on deleting the fake4 codebase at this time.

@dsyme
Copy link
Collaborator

dsyme commented Oct 14, 2021

Great to see the work in progress!

@yazeedobaid yazeedobaid force-pushed the net6-runtime-reference-assemblies branch 2 times, most recently from c250b10 to fbadd28 Compare October 25, 2021 11:11
@yazeedobaid yazeedobaid marked this pull request as ready for review October 25, 2021 11:38
@yazeedobaid yazeedobaid changed the title [WIP] Attempt to support .net6 runtime assemblies in Fake runner and fix build Support .net6 runtime assemblies, new Build system, and Legacy Fake Codebase Separation Oct 25, 2021
@yazeedobaid yazeedobaid force-pushed the net6-runtime-reference-assemblies branch from fbadd28 to 5981ba0 Compare November 1, 2021 16:05
@yazeedobaid yazeedobaid force-pushed the net6-runtime-reference-assemblies branch from b5ae18c to 247209c Compare November 1, 2021 16:20
@yazeedobaid yazeedobaid requested a review from matthid November 1, 2021 16:45
matthid
matthid previously approved these changes Nov 3, 2021
Copy link
Member

@matthid matthid left a comment

Choose a reason for hiding this comment

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

Good job, Nice!
A lot less changes than I would have expected. I guess there was nevertheless a bit of pain involved to get it running, sorry for that :)

Comment on lines +173 to +181
// please see: https://docs.microsoft.com/en-us/dotnet/core/rid-catalog
// some native libraries, such as Sqlite.Interop are distributed without version specific.
// THe runtime identifier on windows 10 machine will be win10-x64 however, Sqlite.Interop
// will have an RID of win-x64
let runtimeLibrariesNotVersionSpecific =
installModel.GetRuntimeLibraries graph ridNotVersionSpecific targetProfile
|> Seq.map (fun fi -> DependencyFile.Library { File = fi.Library.Path })
|> Seq.toList

Copy link
Member

@matthid matthid Nov 3, 2021

Choose a reason for hiding this comment

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

I'm not sure if this is how it is supposed to work, I'd assume some libraries might still run into issues.
Basically the runtime identifiers are organized like a tree (https://docs.microsoft.com/en-us/dotnet/core/rid-catalog), last time I checked those RIDs where pushed with a NuGet package (you should find the logic in the Paket.Core code). This might have changed by now.
But basically we should know that a win-x64 is compatible on a win10-x64 runtime and take that (disclaimer: I wrote that code in Paket and it might have changed by now or is no longer working)

Of course we can release it as-is and see if someone complains.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

For the native assemblies not sure why it breaks, but the two changes I think affect it are:

  1. Either Paket.Core, in which we updated to a major version, so it may have some breaking changes in that area.
  2. Or the removal of Microsoft.DotNet.PlatformAbstractions since it is now deprecated by Microsoft and using built-in runtime identifier property.

As you pointed out it should resolve the native package since it is a graph and win10-x64 is a child of win-x64. When I debug it, the InstallModel resolved it with win10-x64 but the runtime identifier from SDK was win-x64, and InstallModel didn't return anything in that configuration.

I'm not familiar with this area, if you have any suggestions I'm all open.

Copy link
Member

Choose a reason for hiding this comment

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

If you want to take a look, I'd say that if you debugged it to InstallModel it is most likely a Paket bug, so the easiest would be to add a unit-test there and try to solve it there and then ask @forki to make a release :)
IIRC there are already tests for the runtime identifiers there, so it shouldn't be too hard to add this scenario (but that is always easy to write, I could easily be wrong on that)

@dsyme
Copy link
Collaborator

dsyme commented Nov 4, 2021

@yazeedobaid This is amazing work! Do you need further review before merging?

@dsyme
Copy link
Collaborator

dsyme commented Nov 6, 2021

@yazeedobaid I'd love to see this merged, I think I just hit a need for net6 support. Can we proceed? Thanks

@yazeedobaid
Copy link
Collaborator Author

@matthid Thanks for the review!
I agree with the naming changes you suggested. They are much cleaner and remove the confusion between compiled and runtime assemblies. I got drained in the changes and didn't focus on the naming side, and I picked up runtime assemblies!

So I renamed the class to SdkAssemblyResolver and used SdkVersion instead of pinned Sdk version, which indicates the LTS version of the .NET SDK.

@yazeedobaid
Copy link
Collaborator Author

@yazeedobaid I'd love to see this merged, I think I just hit a need for net6 support. Can we proceed? Thanks

@dsyme Yes, we can! I'm done from my side. I think we can go with an alpha release - 5.20.5-alpha.
Could you or @matthid please merge it, the merge button is not active for me, it seems the branch is protected and I don't have the sufficient permissions on it.
Thanks

@yazeedobaid yazeedobaid force-pushed the net6-runtime-reference-assemblies branch from 6a6e44a to 9f97e0d Compare November 7, 2021 19:21
@matthid
Copy link
Member

matthid commented Nov 7, 2021

@yazeedobaid Please check again, I assigned you the admin role.

@yazeedobaid yazeedobaid merged commit 0051787 into fsprojects:release/next Nov 7, 2021
@yazeedobaid
Copy link
Collaborator Author

Thanks @matthid

@dsyme
Copy link
Collaborator

dsyme commented Nov 7, 2021

You guys rock. Really impressive to see you deliver this

yazeedobaid added a commit that referenced this pull request Nov 11, 2021
…odebase Separation (#2609)

* .net6 runtime assemblies in Fake runner, using GitHub actions, and separation of legacy codebase.

* naming convention changes

* disabiling stale bot
yazeedobaid added a commit that referenced this pull request Nov 11, 2021
…odebase Separation (#2609)

* .net6 runtime assemblies in Fake runner, using GitHub actions, and separation of legacy codebase.

* naming convention changes

* disabiling stale bot
yazeedobaid added a commit that referenced this pull request Nov 11, 2021
…odebase Separation (#2609)

* .net6 runtime assemblies in Fake runner, using GitHub actions, and separation of legacy codebase.

* naming convention changes

* disabiling stale bot
yazeedobaid added a commit to FoothillSolutions/FAKE that referenced this pull request Jan 31, 2022
…odebase Separation (fsprojects#2609)

* .net6 runtime assemblies in Fake runner, using GitHub actions, and separation of legacy codebase.

* naming convention changes

* disabiling stale bot
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants