diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index a35eaf30..58396cd3 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -30,16 +30,23 @@ jobs: - uses: actions/setup-dotnet@v4 with: - # dotnet-version: '8.0.x' + dotnet-version: '8.0.x' + + # Setup .NET SDK (installs .NET 10.0 via global.json) + - uses: actions/setup-dotnet@v4 + with: global-json-file: 'global.json' - - run: dotnet --info + - name: Display .NET info + run: dotnet --info - name: Build PASopa.sln run: dotnet build src/PASopa.sln # Pack so we can validate there are no warnings/errors # We must explicitly set the configuration parameter otherwise it defaults to Release + # Packages will include all target frameworks (net48, net8.0, net10.0 for Persistence projects) + # Note: On Windows, net48 binaries are included; on Ubuntu, net48 build is skipped but pack succeeds - name: Pack - Formulas.Tools run: dotnet pack --no-build -c ${{ env.Configuration }} src/PAModel/Microsoft.PowerPlatform.Formulas.Tools.csproj @@ -49,6 +56,23 @@ jobs: - name: Pack - Persistence.Testing run: dotnet pack --no-build -c ${{ env.Configuration }} src/Persistence.Testing/Microsoft.PowerPlatform.PowerApps.Persistence.Testing.csproj - # Run tests - - name: Test - PASopa.sln + # Run tests for all target frameworks + # On Windows: run all projects and frameworks together + - name: Test - PASopa.sln (Windows - all frameworks) + if: runner.os == 'Windows' run: dotnet test --no-build --solution src/PASopa.sln + + # On non-Windows: run test projects individually, since they don't all support the same TFMs + # We are only testing the main test projects & frameworks on Ubuntu + - name: Test - Persistence.Tests (net10.0) + if: runner.os != 'Windows' + run: dotnet test --no-build --project src/Persistence.Tests/Persistence.Tests.csproj --framework net10.0 + continue-on-error: true + - name: Test - PAModelTests (net10.0) + if: runner.os != 'Windows' + run: dotnet test --no-build --project src/PAModelTests/PAModelTests.csproj --framework net10.0 + continue-on-error: true + - name: Test - YamlValidator.Tests (net8.0) + if: runner.os != 'Windows' + run: dotnet test --no-build --project src/YamlValidator.Tests/YamlValidator.Tests.csproj --framework net8.0 + continue-on-error: true diff --git a/README.md b/README.md index 5678f0e5..ece3f7d1 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ ![alt text](assets/images/yaml-redhat-settings.png) ```json "yaml.schemas": { - "https://raw.githubusercontent.com/microsoft/PowerApps-Tooling/master/docs/pa.yaml-schema.json": "*.pa.yaml" + "https://raw.githubusercontent.com/microsoft/PowerApps-Tooling/master/docs/pa.yaml-schema.json": "*.pa.yaml" } ``` @@ -52,13 +52,15 @@ contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additio For a developer machine (Windows 10, WSL, Linux, macOS), install: - [git](https://git-scm.com/downloads) -- [.NET Core SDK v6.0.x (x64)](https://dotnet.microsoft.com/en-us/download/dotnet/6.0) +- [.NET SDK 10.0.x (x64)](https://dotnet.microsoft.com/en-us/download/dotnet/10.0) (version specified in global.json) - [VS Code](https://code.visualstudio.com/Download) -- if on Windows: [VS2019 or VS2022 (Community edition will do)](https://visualstudio.microsoft.com/downloads/). Select at least the following workload: .NET Core cross-plat +- if on Windows: [VS2022 (Community edition will do)](https://visualstudio.microsoft.com/downloads/). Select at least the following workload: .NET desktop development (for .NET Framework 4.8 support) - recommended VSCode extensions: - [GitLens (eamodio.gitlens)](https://github.com/eamodio/vscode-gitlens) - [C# (ms-vscode.csharp)](https://github.com/OmniSharp/omnisharp-vscode) +**Note:** Some projects target multiple frameworks. Building .NET Framework 4.8 targets requires Windows with the .NET Framework 4.8 Developer Pack installed. + ### Building and running tests After cloning this repo (https://github.com/microsoft/PowerApps-Language-Tooling), open a terminal/cmd/PS prompt with the dotnet executable on the path. Check with: ```dotnet --version ``` diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index 918cbe10..fec7f4c4 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -22,6 +22,7 @@ + diff --git a/src/PAModel/Microsoft.PowerPlatform.Formulas.Tools.csproj b/src/PAModel/Microsoft.PowerPlatform.Formulas.Tools.csproj index 09b991ab..0e9565bf 100644 --- a/src/PAModel/Microsoft.PowerPlatform.Formulas.Tools.csproj +++ b/src/PAModel/Microsoft.PowerPlatform.Formulas.Tools.csproj @@ -1,6 +1,6 @@ - netstandard2.0;net8.0 + net48;net8.0;net10.0 12 true @@ -11,12 +11,11 @@ PowerPlatform Canvas App Tools - Preview Release: This takes a Canvas App (.msapp file) and converts to and from text files that can be checked into source control. + DEPRECATED: This takes a Canvas App (.msapp file) and converts to and from text files that can be checked into source control. Notice: - This package is a preview release - use at your own risk. - This package is a .NET Standard 2.0 project, intended to work with .NET Framework 4.7.2 or later, and .NET 6.0 or later - We have not stabilized on Namespace or Class names with this package as of yet and things will change as we move though the preview. + This package is DEPRECATED - use at your own risk. It is no longer receiving any updates and does not support the latest versions of + Power Apps Canvas Apps. See https://github.com/microsoft/PowerApps-Tooling/releases for the latest release notes. @@ -27,6 +26,7 @@ + diff --git a/src/PAModel/packages.lock.json b/src/PAModel/packages.lock.json index 39830097..2cefdcd7 100644 --- a/src/PAModel/packages.lock.json +++ b/src/PAModel/packages.lock.json @@ -1,22 +1,19 @@ { "version": 2, "dependencies": { - ".NETStandard,Version=v2.0": { - "NETStandard.Library": { - "type": "Direct", - "requested": "[2.0.3, )", - "resolved": "2.0.3", - "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0" - } - }, + ".NETFramework,Version=v4.8": { "Newtonsoft.Json": { "type": "Direct", "requested": "[13.0.3, )", "resolved": "13.0.3", "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" }, + "System.IO.Compression": { + "type": "Direct", + "requested": "[4.3.0, )", + "resolved": "4.3.0", + "contentHash": "YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==" + }, "System.Text.Encodings.Web": { "type": "Direct", "requested": "[8.0.0, )", @@ -39,7 +36,8 @@ "System.Memory": "4.5.5", "System.Runtime.CompilerServices.Unsafe": "6.0.0", "System.Text.Encodings.Web": "8.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" + "System.Threading.Tasks.Extensions": "4.5.4", + "System.ValueTuple": "4.5.0" } }, "YamlDotNet": { @@ -56,11 +54,6 @@ "System.Threading.Tasks.Extensions": "4.5.4" } }, - "Microsoft.NETCore.Platforms": { - "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" - }, "System.Buffers": { "type": "Transitive", "resolved": "4.5.1", @@ -72,14 +65,14 @@ "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", "dependencies": { "System.Buffers": "4.5.1", - "System.Numerics.Vectors": "4.4.0", + "System.Numerics.Vectors": "4.5.0", "System.Runtime.CompilerServices.Unsafe": "4.5.3" } }, "System.Numerics.Vectors": { "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" + "resolved": "4.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", @@ -93,6 +86,37 @@ "dependencies": { "System.Runtime.CompilerServices.Unsafe": "4.5.3" } + }, + "System.ValueTuple": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==" + } + }, + "net10.0": { + "Newtonsoft.Json": { + "type": "Direct", + "requested": "[13.0.3, )", + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" + }, + "System.Text.Encodings.Web": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==" + }, + "System.Text.Json": { + "type": "Direct", + "requested": "[8.0.5, )", + "resolved": "8.0.5", + "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==" + }, + "YamlDotNet": { + "type": "Direct", + "requested": "[15.1.6, )", + "resolved": "15.1.6", + "contentHash": "T/cQEK/KHK96Q8kytJ4iUGDXg1/fj2Qtk6rCQeIlHYU1zTeyGVHW0QNZgREQyxZpygGMDMmrXNWt0sj5TsQnjA==" } }, "net8.0": { diff --git a/src/PAModelTests/DataSourceTests.cs b/src/PAModelTests/DataSourceTests.cs index b7f7d93c..b5651f13 100644 --- a/src/PAModelTests/DataSourceTests.cs +++ b/src/PAModelTests/DataSourceTests.cs @@ -221,7 +221,7 @@ public void TestWhenDataSourcesIsSetToEmptyDictionary(string appName) Assert.IsTrue(File.Exists(pathToMsApp)); var (msApp, errors) = CanvasDocument.LoadFromMsapp(pathToMsApp); - msApp._dataSourceReferences["default.cds"].dataSources = new Dictionary(); + msApp._dataSourceReferences["default.cds"].dataSources = new(); errors.ThrowOnErrors(); using var sourcesTempDir = new TempDir(); @@ -256,7 +256,12 @@ public void TestAllUsedDataSourcesArePreserved(string appName) errors1.ThrowOnErrors(); Assert.HasCount(msApp._dataSourceReferences["default.cds"].dataSources.Count, msApp._dataSourceReferences["default.cds"].dataSources); - foreach (var entry in msApp._dataSourceReferences["default.cds"].dataSources.Keys.OrderBy(key => key).Zip(msApp1._dataSourceReferences["default.cds"].dataSources.Keys.OrderBy(key => key))) + foreach (var entry in msApp._dataSourceReferences["default.cds"].dataSources.Keys.OrderBy(key => key) + .Zip(msApp1._dataSourceReferences["default.cds"].dataSources.Keys.OrderBy(key => key) +#if NET48 // in net48, Zip does not have an overload that has default result selector, so we need to create the tuple manually. + , resultSelector: (k1, k2) => (First: k1, Second: k2) +#endif + )) { Assert.AreEqual(entry.First, entry.Second); } diff --git a/src/PAModelTests/PAModelTests.csproj b/src/PAModelTests/PAModelTests.csproj index 5bb8a6b7..4f7c2ed1 100644 --- a/src/PAModelTests/PAModelTests.csproj +++ b/src/PAModelTests/PAModelTests.csproj @@ -1,8 +1,8 @@ - net8.0 - + net48;net8.0;net10.0 + 10.0 true true diff --git a/src/PAModelTests/SmartMergeTests.cs b/src/PAModelTests/SmartMergeTests.cs index ca08bf7e..08c33052 100644 --- a/src/PAModelTests/SmartMergeTests.cs +++ b/src/PAModelTests/SmartMergeTests.cs @@ -178,20 +178,28 @@ public void ScreenDeletionTest() (var msapp, var errors) = CanvasDocument.LoadFromMsapp(root); Assert.IsFalse(errors.HasErrors); + const string screenName = "Home Screen"; MergeTester( - msapp, - (branchADoc) => - { - // Nothing - }, - (branchBDoc) => - { - branchBDoc._screens.Remove("Home Screen", out var control); - }, - (resultDoc) => - { - Assert.IsFalse(resultDoc._screens.ContainsKey("Home Screen")); - }); + msapp, + (branchADoc) => + { + // Nothing + }, + (branchBDoc) => + { +#if NET48 // in net48, Remove does not have an overload that will return the item removed. + if (branchBDoc._screens.TryGetValue(screenName, out var control)) + { + branchBDoc._screens.Remove(screenName); + } +#else + branchBDoc._screens.Remove(screenName, out var control); +#endif + }, + (resultDoc) => + { + Assert.IsFalse(resultDoc._screens.ContainsKey(screenName)); + }); } [TestMethod] diff --git a/src/PASopa/README.md b/src/PASopa/README.md index 5582e70f..6c1fe5c9 100644 --- a/src/PASopa/README.md +++ b/src/PASopa/README.md @@ -51,7 +51,7 @@ Latest Yaml version is: https://github.com/microsoft/PowerApps-Language-Tooling/ You can also use this functionality stand alone, using our test console app. -Download and install the [.NET Core SDK v6.0.x (x64)](https://dotnet.microsoft.com/en-us/download/dotnet/6.0) in order to build. +Download and install the [.NET SDK 10.0.x (x64)](https://dotnet.microsoft.com/en-us/download/dotnet/10.0) (version specified in global.json) in order to build. Build the test console app by running: `\build.cmd` This will create: `\bin\Debug\PASopa\PASopa.exe` diff --git a/src/Persistence.Testing/Microsoft.PowerPlatform.PowerApps.Persistence.Testing.csproj b/src/Persistence.Testing/Microsoft.PowerPlatform.PowerApps.Persistence.Testing.csproj index 48ff7989..962a3d47 100644 --- a/src/Persistence.Testing/Microsoft.PowerPlatform.PowerApps.Persistence.Testing.csproj +++ b/src/Persistence.Testing/Microsoft.PowerPlatform.PowerApps.Persistence.Testing.csproj @@ -1,7 +1,7 @@  - net8.0 + net8.0;net10.0 enable enable diff --git a/src/Persistence.Tests/Persistence.Tests.csproj b/src/Persistence.Tests/Persistence.Tests.csproj index 435e3e28..e90b80f0 100644 --- a/src/Persistence.Tests/Persistence.Tests.csproj +++ b/src/Persistence.Tests/Persistence.Tests.csproj @@ -1,7 +1,7 @@  - net8.0 + net8.0;net10.0 enable enable diff --git a/src/Persistence/Compression/PaArchiveExtensions.ExtractAsync.cs b/src/Persistence/Compression/PaArchiveExtensions.ExtractAsync.cs index da3e742b..11207c33 100644 --- a/src/Persistence/Compression/PaArchiveExtensions.ExtractAsync.cs +++ b/src/Persistence/Compression/PaArchiveExtensions.ExtractAsync.cs @@ -9,15 +9,19 @@ namespace Microsoft.PowerPlatform.PowerApps.Persistence.Compression; public static partial class PaArchiveExtensions { - public static ValueTask ExtractToFileAsync(this PaArchiveEntry source, string destinationFileName, bool overwrite = false) - { #if NET10_0_OR_GREATER - // When we support .net 10, use ExtractToFileAsync + public static async Task ExtractToFileAsync(this PaArchiveEntry source, string destinationFileName, bool overwrite = false) + { + // .net 10 supports ExtractToFileAsync + await source.ZipEntry.ExtractToFileAsync(destinationFileName, overwrite).ConfigureAwait(false); + } #else + public static ValueTask ExtractToFileAsync(this PaArchiveEntry source, string destinationFileName, bool overwrite = false) + { source.ZipEntry.ExtractToFile(destinationFileName, overwrite); return ValueTask.CompletedTask; -#endif } +#endif /// /// Extracts the archive to a target directory, preserving relative paths. diff --git a/src/Persistence/Microsoft.PowerPlatform.PowerApps.Persistence.csproj b/src/Persistence/Microsoft.PowerPlatform.PowerApps.Persistence.csproj index 087183f5..fe16fd24 100644 --- a/src/Persistence/Microsoft.PowerPlatform.PowerApps.Persistence.csproj +++ b/src/Persistence/Microsoft.PowerPlatform.PowerApps.Persistence.csproj @@ -1,7 +1,7 @@ - net8.0 + net8.0;net10.0 enable enable diff --git a/src/Persistence/packages.lock.json b/src/Persistence/packages.lock.json index 5476ac7c..0ffca935 100644 --- a/src/Persistence/packages.lock.json +++ b/src/Persistence/packages.lock.json @@ -1,6 +1,29 @@ { - "version": 1, + "version": 2, "dependencies": { + "net10.0": { + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Direct", + "requested": "[8.0.2, )", + "resolved": "8.0.2", + "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==" + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Direct", + "requested": "[8.0.3, )", + "resolved": "8.0.3", + "contentHash": "dL0QGToTxggRLMYY4ZYX5AMwBb+byQBd/5dMiZE07Nv73o6I5Are3C7eQTh7K2+A4ct0PVISSr7TZANbiNb2yQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2" + } + }, + "YamlDotNet": { + "type": "Direct", + "requested": "[15.1.6, )", + "resolved": "15.1.6", + "contentHash": "T/cQEK/KHK96Q8kytJ4iUGDXg1/fj2Qtk6rCQeIlHYU1zTeyGVHW0QNZgREQyxZpygGMDMmrXNWt0sj5TsQnjA==" + } + }, "net8.0": { "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Direct", diff --git a/src/YamlValidator.Tests/YamlValidator.Tests.csproj b/src/YamlValidator.Tests/YamlValidator.Tests.csproj index c77db480..7434466f 100644 --- a/src/YamlValidator.Tests/YamlValidator.Tests.csproj +++ b/src/YamlValidator.Tests/YamlValidator.Tests.csproj @@ -1,7 +1,7 @@ - net8.0 + net8.0 Latest enable enable