Always remove Angular dist folder contents with WebDeploy on Destination
Asked Answered
H

3

7

We've got a single page application on Angular 5 with an ASP.NET backend, and when we compile it, the release contents for Angular are output to a folder "Project\dist".

This works great on local dev machines, but all of the dist files are randomized with different names such as:

  • polyfills.dc7175a7225af84b3c9b.bundle.js
  • styles.dc7175a7225af84b3c9b.bundle.js
  • inline.dc7175a7225af84b3c9b.bundle.js

When we use Web Publishing to deploy to staging or production, everything transfers great and our custom folder in the publish profiles is included and published.

However, on the destination server (staging or production) these old, randomly named files and old (no longer used) folders persist. This results in hundreds and hundreds of old files (from old web deploys) that have accumulated on the staging and production servers. I need a method to automatically delete these every time we push updates with webdeploy.

Ideally, the workflow is:

  • Select publish profile, click Publish
  • Enter my credentials
  • Application builds successfully
  • If app built successfully, we go delete "Project\dist" folder on the destination server. "Project" could be in c:\inetpub\www\project or d:\websites\Project, for example.
  • Updated files are copied
  • Web deploy executes and copies the custom files in dist folder (already working).

Here's a redacted version of our current publish profile:

<?xml version="1.0" encoding="utf-8"?>
<!--
This file is used by the publish/package process of your Web project. You can customize the behavior of this process
by editing this MSBuild file. In order to learn more about this please visit http://go.microsoft.com/fwlink/?LinkID=208121. 
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <WebPublishMethod>MSDeploy</WebPublishMethod>
    <LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
    <LastUsedPlatform>Any CPU</LastUsedPlatform>
    <SiteUrlToLaunchAfterPublish />
    <LaunchSiteAfterPublish>True</LaunchSiteAfterPublish>
    <ExcludeApp_Data>False</ExcludeApp_Data>
    <MSDeployServiceURL>staging.example.com</MSDeployServiceURL>
    <DeployIisAppPath>Project</DeployIisAppPath>
    <RemoteSitePhysicalPath />
    <SkipExtraFilesOnServer>True</SkipExtraFilesOnServer>
    <MSDeployPublishMethod>WMSVC</MSDeployPublishMethod>
    <EnableMSDeployBackup>False</EnableMSDeployBackup>
    <UserName>WebDeployUser</UserName>
    <PublishDatabaseSettings>
      <Objects xmlns="">
      </Objects>
    </PublishDatabaseSettings>
    <ADUsesOwinOrOpenIdConnect>False</ADUsesOwinOrOpenIdConnect>
  </PropertyGroup>
  <Target Name="CustomCollectFiles">
    <ItemGroup>
      <_CustomFiles Include="..\Project\dist\**\*" />

      <FilesForPackagingFromProject  Include="%(_CustomFiles.Identity)">
        <DestinationRelativePath>dist\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
      </FilesForPackagingFromProject>
    </ItemGroup>
  </Target>
  <PropertyGroup>
    <CopyAllFilesToSingleFolderForPackageDependsOn>
      CustomCollectFiles;
      $(CopyAllFilesToSingleFolderForPackageDependsOn);
    </CopyAllFilesToSingleFolderForPackageDependsOn>

    <CopyAllFilesToSingleFolderForMsdeployDependsOn>
      CustomCollectFiles;
      $(CopyAllFilesToSingleFolderForMsdeployDependsOn);
    </CopyAllFilesToSingleFolderForMsdeployDependsOn>
  </PropertyGroup>
</Project>

I've tried a few accepted answer solutions already and can't get this to work:

Any ideas? I have essentially zero knowledge of web deploy aside from setting it up in IIS.

Best, Chris

EDIT I've also tried this: (Based on this: https://mcmap.net/q/454318/-msbuild-how-to-delete-folder-contents-but-not-folder-itself)

  <Target Name="CleanFolder">

    <PropertyGroup>
      <TargetFolder>$(_MSDeployDirPath_FullPath)\dist</TargetFolder>
    </PropertyGroup>

    <ItemGroup>
      <FilesToClean Include="$(TargetFolder)\**\*"/>
      <Directories Include="$([System.IO.Directory]::GetDirectories('$(TargetFolder)', '*', System.IO.SearchOption.AllDirectories))"
                   Exclude="$(TargetFolder)"/>
    </ItemGroup>

    <Delete Files="@(FilesToClean)" ContinueOnError="true"/>
    <RemoveDir Directories="@(Directories)" />
  </Target>

Update This is specifically what we're doing: https://learn.microsoft.com/en-us/aspnet/web-forms/overview/deployment/visual-studio-web-deployment/deploying-extra-files

The first comment from there is the same problem we're experiencing: This comes very handy in deploying Angular distribution files along with ASP.Net backend, whenever both SPA and the backend share the same single virtual application. Unfortunately, due to browser cache busting techniques, the bundle files for Angular deployment will always ship with unique names and, therefore, an msbuild command/attribute or other possibility to wipe the folder clean on the IIS side before sending the updated files would be very welcomed. If anyone has found a way to do that, please share.

"Sync" functionality described here for msdeploy is exactly what we need to be doing but I don't know how to hook into this: https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-R2-and-2008/dd569034(v=ws.10)#sync

In a sync operation, if the source file or folder does not exist on the destination, the provider creates the folder and any subfolders that have the corresponding files and attributes. If the destination folder already exists, the provider updates only those objects that do not match the source. This means that in some cases only one file or folder will be updated. Files on the destination that do not exist on the source will be deleted. The source and destination folders for contentPath do not have to have the same name. If the name of the destination folder differs from that of the source, the name of the destination folder will remain the same, but the contents of the folder will be updated to those of the source.

Hush answered 10/4, 2018 at 16:35 Comment(6)
How about you build for production without the hashes: ng build --prod --output-hashing none? The only disadvantage I can think of is that a service worker will not cache the app properly.Sociometry
Users could be using an old version, because the browser thinks it is still correct.Feathery
Hi guys thanks for replying! @Sociometry -- Unfortunately we need the cache busting purpose created by output-hashing.Hush
@RobinDijkhof -- issue isn't caching or end users it's that the old files persist on disk because web deploy doesn't overwrite them because the files by virtue of "output-hashing" are named differently. The solution here should be focused on leveraging web deploy to empty a folder upon publish and then publish all of the new local files.Hush
Yes, but using no output-hash would.Feathery
have you tried using a rimraf before build? github.com/isaacs/rimrafUltrasonic
T
3

If I understood you correctly, then resolve this problem is help DeleteExistingFiles property in publish profile.

<DeleteExistingFiles>True</DeleteExistingFiles>

If set to True the output directory (publishUrl) will be purged before output is written to it, it's good to start out with a clean slate.


How can I apply this to a specific folder, ex. only delete the "Project\dist" folder?

As I know result of this property is removing all files. For specify directories to remove you can try verb delete from MSDeploy, that can be wrap in <Exec> task of custom script:

<Exec Command="$(MSDeploy) -verb:delete -dest:&quot;ContentPath=D:\TestDir\Test.txt&quot"/>

/* 
 * $(MSDeploy) is path to MSDeploy binary that you passed to script.                                                                     
 */

This example show removing file on local machine. You should customize own call.


Exec Task | How to create a Web Deploy package when publishing a ClickOnce project (Some snippets for using targets)

Trespass answered 5/5, 2018 at 6:31 Comment(6)
How can I apply this to a specific folder, ex. only delete the "Project\dist" folder?Hush
Can you write the <Exec> task for me? (I don't understand how to turn the msdeploy commands you showed into an exec task)Hush
I think we're close! I added this key in a PropertyGroup: <MSDeployPath>"C:\Program Files (x86)\iis\Microsoft Web Deploy V3\msdeploy.exe"</MSDeployPath> I run the command you specified (fixed the quotes placement): <Exec Command="$(MSDeployPath) -verb:delete -dest:ContentPath=&quot;D:\Website\dist&quot;"/> But I get errors: (too long to post here in the comment) pastebin.com/5QhPvWmw It's basically these errors: EXEC(0,0): Error Code: FileOrFolderNotFound EXEC(0,0): Error : The device is not ready. EXEC(0,0): Error count: 1.Hush
Per the instructions (here: link) I to use this with a hardcoded username and password. (Which isn't ideal but better than nothing I guess). <Exec Command="$(MSDeployPath) -verb:delete -dest:ContentPath=&quot;D:\Website\dist&quot;,computerName=Server,username=Admin,password=Password"/> Any ideas on how to lock that down so I don't have this username/pass in source / plaintext? I tried without a user/pass and got an error ERROR_USER_NOT_ADMIN (401) UnauthorizedHush
Interestingly if I do specify a user other than "Administrator" (even if that user is in the Administrators group and authorized as a Web Deploy user) I end up with the same issue: ERROR_USER_NOT_ADMIN (401) Unauthorized. Any ideas?Hush
If anyone stumbles across this, the built-in Administrator account works, but to get other admin accounts to work we have to create a user group on the machine called MSDepSvcUsers and add the local account to it. Then open Services.msc and restart Web Deployment Agent Service. Sources: https://mcmap.net/q/1626783/-msdeploy-stop-working-getting-error-code-error_user_not_admin forums.iis.net/post/2138489.aspx It doesn't appear there's any way around hard coding the password into the exec command, which for us means this file gets pulled out of source control.Hush
E
0

Try please setting this in publish profile

 <SkipExtraFilesOnServer>False</SkipExtraFilesOnServer>
Eucharist answered 4/5, 2018 at 6:19 Comment(6)
How can I apply this to a specific folder, ex. only delete the "Project\dist" folder?Hush
Are you used TFS to publish frontend app?Eucharist
Nope, we use npm to build a production, AOT set of files. Then I used the Custom Files method here: learn.microsoft.com/en-us/aspnet/web-forms/overview/deployment/… to publish these files with web deploy in Visual StudioHush
I tried placing this inside of a <PropertyGroup> inside of the <Target> for CustomCollectFiles and it just deletes everything on the destination which is undesirable.Hush
Your web server IIS? Can you use ftp to publish frontend app?) We use this approach, because frontend and backend apps different projects.Eucharist
Yep it's IIS; we use MSDeploy with WMSVC publish method. Specifically to your point; splitting the publishing between frontend and backend is undesirable for us.Hush
U
0

in angular with nodejs, we will handle this problem with 'ng build --output-hashing=false'. Maybe you can search in this scope.

Uralian answered 7/5, 2018 at 20:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.