CZEMacLeod / MSBuild.SDK.SystemWeb

This MSBuild SDK is designed to allow for the easy creation and use of SDK (shortform) projects targeting ASP.NET 4.x using System.Web.
MIT License
151 stars 8 forks source link

Support Publish to IIS from withing Visual Studio using the GUI #21

Open Kation opened 2 years ago

Kation commented 2 years ago
<Project>
  <Import Sdk="Microsoft.NET.Sdk.Publish" Project="Sdk.props" />

  <ItemGroup>
    <ProjectCapability Include="DotNetCoreWeb" />
    <ProjectCapability Include="Web" />
  </ItemGroup>
  <Import Sdk="Microsoft.NET.Sdk.Publish" Project="Sdk.targets" />
</Project>

Add this to enable publish to IIS feature

CZEMacLeod commented 2 years ago

@Kation Publish already works from the command line or build server. Is this to enable the publish command in visual studio? I was aware of the Microsoft.NET.Sdk.Publish SDK type and it appears to include some of the logic and DLLs that are installed by visual studio and are currently pulled in by

<Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" />

I feel that adding the DotNetCoreWeb project capability is going to cause the razor editor and other ASP.NET Core features to enable and break working with ASP.NET 4 technologies.

Your example does not include the MSBuild.SDK.SystemWeb so will miss out a lot of the other features and requirements for ASPNET 4.

Have you tried the following?

<Project>
  <Import Sdk="MSBuild.SDK.SystemWeb/4.0.33" Project="Sdk.props" />
  <Import Sdk="Microsoft.NET.Sdk.Publish" Project="Sdk.props" />

  <ItemGroup>
    <ProjectCapability Include="DotNetCoreWeb" />
    <ProjectCapability Include="Web" />
  </ItemGroup>
  <Import Sdk="MSBuild.SDK.SystemWeb/4.0.33" Project="Sdk.targets" />
  <Import Sdk="Microsoft.NET.Sdk.Publish" Project="Sdk.targets" />
</Project>

This will add the MSBuild.SDK.SystemWeb but 'overwrite' the publish targets from Microsoft.NET.Sdk.Publish. If you build an example repository/example which works fully and doesn't break any ASPNET 4 features, we can look at whether this could be added.

Kation commented 2 years ago

@CZEMacLeod Yes. This allow us publish to IIS in Visual Studio. Of course I use MSBuild.SDK.SystemWeb at the top of Project. I have been tested with asp.net mvc but not webform. I found that IIS publish target appear if add project capability only.

christianbumann commented 1 year ago

@CZEMacLeod and @Kation Thank you for the workaround. With the lines above I get the additional publish targets (e.g. IIS) back. But I currently have the issue that if I publish to a folder no files are published. Even if I do a publish from your example projects https://github.com/CZEMacLeod/MSBuild.SDK.SystemWeb/tree/main/samples nothing is published to the publishing folder. If I build from command line with arguments (DeployOnBuild, WebPublishMethod, PackageLocation) publishing is working.

CZEMacLeod commented 1 year ago

@christianbumann This was never added to the main SDK as it didn't feel like it worked as intended and caused side effects. The fact that it works from the command line means it is easy to integrate this with any CI/CD system, and other than for small/test projects, which wouldn't really benefit from this SDK, it seems like an unlikely use case to be publishing from VS directly. I use the publish mechanism (without any additional settings beyond the SDK), from Azure DevOps for most of my main (ASP.NET4) projects now and it works very well. I'm sure it would also work from GitHub actions too. You could easily set it to publish to a folder and then pick up the folder as artifacts for output, if you want to do it that way. I use MSDeploy and then a release job to actually push the site to our servers.

christianbumann commented 1 year ago

@CZEMacLeod Thank you for your quick answer. Publishing local from Visual Studio would get a faster feedback how the final package content looks like. Deploying from the pipeline is in plan as the build artefact (deployment package) itself already exists.... Our developers can take the build artefact and extract it if required - or build the solution locally through our Cake-scripts and then the package is also available local.... So we don't really need the local publishing in Visual Studio - I just wanted to ask if there is a quick and easy solution to get this working. But anyway, your answer was helpful for me - and your SDK is great. Have a nice time, Christian

CZEMacLeod commented 1 year ago

@christianbumann Ah I see. The only thing I can suggest here is to create a batch file in the solution directory and add it to the solution. That way you can execute the batch file from within VS and do the publish command and then investigate the output files etc.

leusbj commented 1 year ago

@christianbumann Maybe try one of the following

@CZEMacLeod I think there might be a conflict between the "order of operations" in terms of how Visual Studio runs the publish vs running a build. It was something I was investigating but never finished looking into.

I was trying to document the differences between (and the interactions around) the "MvcBuildViews" and the "PreCompileBeforePublish" and how this changed order a little bit when in a ".net core build" as opposed to the original "build", and this as far as I made it when I stopped.

MvcBuildViews target

PreCompileBeforePublish

I think the conflict comes into play because of when CleanupForBuildMvcViews deletes files and when the "Publish" creates the files (or potentially goes looking for the files to bundle them up)

The oldest VS I currently have installed is 2017, so I can't tell for sure, but by VS 2017 it seems like web project templates

christianbumann commented 1 year ago

@leusbj Thank you for your suggestions. I tried it but without success. I also combined <MvcBuildViews>false</MvcBuildViews> with <PreCompileBeforePublish>true</PreCompileBeforePublish> and I tried each possible true-false combination without success. Don't investigate extra time for this, as we have a workaround with a *.cmd file building the project with the required arguments, or using the artefact from the build server...

MalcolmEllis commented 1 year ago

@CZEMacLeod Is there any chance you could add a 'For Dummies' sample showing how to get publish to work from the command line? So far if I call msbuild using a simple build script with a VS 2022 cmd prompt I get the website bin directory contents copied to the specified PublishDir path, but not the expected Asp.Net folder structure with a web.config, global.asax, Views, bin dir etc. that I get doing the same kind of build with the old csproj style (output created in a _PublishedWebsites folder). I also tried using 'dotnet build' but that fails due to your Sdk.targets getting the wrong MSBuildExtensionsPath32. I'm just trying this with the basic site from the template here, should it work with the dotnet CLI, I used that to install the template?

chucker commented 1 year ago

if I call msbuild using a simple build script with a VS 2022 cmd prompt I get the website bin directory contents copied to the specified PublishDir path, but not the expected Asp.Net folder structure with a web.config, global.asax, Views, bin dir etc.

Hm, that should work. Your deployment command should look something like:

    msbuild /t:Restore,Rebuild /p:DeployOnBuild=true /p:PublishProfile=Dev.pubxml `
    /p:Password=$pass /p:Configuration=MyBuildConfig .\MyProject.csproj

This assumes you've created a Dev.pubxml publish profile in VS, that it already sets the username, and that it resides in the default location Properties/PublishProfiles/. That, in turn, looks something like:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <AllowUntrustedCertificate>True</AllowUntrustedCertificate>
    <WebPublishMethod>MSDeploy</WebPublishMethod>
    <LaunchSiteAfterPublish>True</LaunchSiteAfterPublish>
    <LastUsedBuildConfiguration>Beta</LastUsedBuildConfiguration>
    <LastUsedPlatform>Any CPU</LastUsedPlatform>
    <SiteUrlToLaunchAfterPublish />
    <ExcludeApp_Data>False</ExcludeApp_Data>
    <MSDeployServiceURL>(some host name)</MSDeployServiceURL>
    <DeployIisAppPath>(the web site in IIS)</DeployIisAppPath>
    <RemoteSitePhysicalPath />
    <SkipExtraFilesOnServer>True</SkipExtraFilesOnServer>
    <MSDeployPublishMethod>WMSVC</MSDeployPublishMethod>
    <EnableMSDeployBackup>True</EnableMSDeployBackup>
    <UserName>(your username, on the server)</UserName>
  </PropertyGroup>
</Project>

Maybe it's something about your csproj?

CZEMacLeod commented 1 year ago

@MalcolmEllis This has been covered somewhat in issues #12 and #20 @chucker Thanks for the suggestion on using an existing publish profile.

You cannot use dotnet as, not only does it not pick up the Web.Application build tasks which are only available from MSBuild included in VS - those tasks are not compatible is netcore so cannot run under dotnet.

msbuild /t:Publish /p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingeFile=true /p:PackageLocation="MyPublishDir"
msbuild "path\to\your\solution.sln" /t:rebuild ^
                    /p:Configuration=Release ^
                    /p:DeployOnBuild=True ^
                    /p:DeployDefaultTarget=WebPublish ^
                    /p:WebPublishMethod=FileSystem ^
                    /p:DeleteExistingFiles=True ^
                    /p:publishUrl="path\to\your\output\folder"

should both work.

Note that you should do a nuget.exe restore before the build. I think @chucker's example of doing /t:restore,rebuild should work too, but that is not the way I do it.

If you are using azure pipelines you can use something like (taken from a working commercial implementation)

- task: NuGetToolInstaller@1
  displayName: 'Use NuGet 6.2.1'
  inputs:
    versionSpec: 6.2.1

- task: NuGetCommand@2
  displayName: 'NuGet restore'
  inputs:
    restoreSolution: '$(Parameters.solution)'
    feedsToUse: config
    nugetConfigPath: NuGet.Config

- task: VSBuild@1
  displayName: 'Build solution'
  inputs:
    solution: '$(Parameters.solution)'
    msbuildArgs: '/p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingleFile=true /p:SkipInvalidConfigurations=true /p:PackageLocation="$(build.artifactstagingdirectory)\\"'
    platform: '$(BuildPlatform)'
    configuration: '$(BuildConfiguration)'

- task: PublishSymbols@1
  displayName: 'Publish symbols path'
  inputs:
    SearchPattern: '**\bin\**\*.pdb'
  continueOnError: true

- task: PublishBuildArtifacts@1
  displayName: 'Publish Artifact'
  inputs:
    PathtoPublish: '$(build.artifactstagingdirectory)'
    ArtifactName: '$(Parameters.ArtifactName)'

I'm open to PRs adding to the documentation - especially with tested snippets. I am considering adding an optional .cmd or .bat file to the template projects with a publish command script if that would help. My understanding of the main use case for this SDK is normally that you transfer an existing project to the new format, and that the existing build scripts/publish profiles should pretty much work as is. This issue is specifically about getting it to work from within Visual Studio itself, and I think that is generally not possible (at least not without some side-effects) at the moment.

John0King commented 1 year ago

@CZEMacLeod why not the Sdk.props and Sdk.targets only import what the "WebProject" import ?

CZEMacLeod commented 1 year ago

@John0King What exactly do you mean by that? The SDK handles a lot of functionality that was provided by Visual Studio explicitly for Web Application Projects, such as binding redirects, handling launching IIS/IISExpress and so on. The SDK does import the same built in targets as a legacy WAP file in SDK.props line 31

<Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" />

This specific issue is that VS has custom handling for Publish for legacy projects, which isn't triggered in SDK projects, and trying to reuse the dotnetcore/aspnetcore publish targets doesn't exactly work, due to the differences between dotnetcore/aspnetcore and asp.net 4.x See the last 2 paragraphs of my previous reply.

My understanding of the main use case for this SDK is normally that you transfer an existing project to the new format, and that the existing build scripts/publish profiles should pretty much work as is. This issue is specifically about getting it to work from within Visual Studio itself, and I think that is generally not possible (at least not without some side-effects) at the moment.

John0King commented 1 year ago

@CZEMacLeod

I don't know is there any other issue on Sdk.props and Sdk.targets

here is a project with old format

image

and this is in the Sdk.props

image

it seems that you replace the Microsoft.Common.props with Microsoft.Net.Sdk .

I'm new to the msbuild and the Sdk ralated concept, I'm just curious about why not just use the old vs props

CZEMacLeod commented 1 year ago

@John0King If you want to continue this - we should move it to a discussion as this issue doesn't really relate to what you are talking about. Microsoft.Net.Sdk imports Microsoft.Common.props as basically the first thing it does. It also imports the appropriate language targets and all the newer style PackageReference and so on handing. This is what gives you all the good features of the new style SDK - e.g. your original project file will probably not need any of the content in your original file, except for any additional packages.

The basic new project file is as simple as

<Project Sdk="MSBuild.SDK.SystemWeb/4.0.88">
  <PropertyGroup>
    <TargetFramework>net48</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <None Include="Properties\launchSettings.json" />
  </ItemGroup>
</Project>

I'm pretty sure there is a lot of information on the new style SDK projects out there, including on the MS Learn website. If you have any queries or problems, either create a new discussion, or raise a new issue about your exact problem.

julealgon commented 1 year ago

Can this issue be renamed to make it explicit we are talking about publishing to IIS through Visual Studio specifically?

I was browsing the issues on the repo to see if there was anything I considered major and this issue here immediately jumped to me. After finding out it works just fine via cmdline the criticality of this drops massively for me.

Renaming it to be more precise would potentially save other people from similar confusion.