Mike Christensen
Mike Christensen

Reputation: 91608

Web app runs fine locally, but has script errors when built by DevOps

I have a fairly simple web application based on the Visual Studio "React" template. When I run MSBuild, it runs an npm install and then calls webpack to create a Javascript bundle. When I build this locally, the app runs fine. When I build this in a DevOps pipeline, the home page is a blank white page with script errors in the console:

enter image description here

I doubt the error really matters much and I wouldn't even begin to know where to start tracking it down anyway. However, after quite a bit of investigation here's what I know so far:

  1. The source code I'm building locally and the source code DevOps is building is exactly the same.
  2. I've verified package.json and package-lock.json are exactly the same.
  3. I've done a diff on the node_modules directory locally and on DevOps. Every single file in every single directory is exactly the same. I'm npm installing the same exact versions of everything.
  4. Both my local machine and DevOps is using node 12.10.0
  5. It's not the web server. If I zip up my locally built files and upload them to to the web server, it works.

Here's my package.json file:

{
  "name": "LimeadeDocs",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "bootstrap": "^3.4.1",
    "core-js": "^3.1.4",
    "js-yaml": "^3.13.1",
    "mobx": "^5.13.0",
    "mobx-react": "^6.1.3",
    "react": "^16.9.0",
    "react-bootstrap": "^0.31.5",
    "react-dom": "^16.9.0",
    "react-is": "^16.9.0",
    "react-router-bootstrap": "^0.24.4",
    "react-router-dom": "^4.2.2",
    "react-scripts": "1.0.17",
    "redoc": "2.0.0-rc.14",
    "rimraf": "^2.6.2",
    "styled-components": "^4.4.0",
    "swagger-diff": "^0.6.0"
  },
  "scripts": {
    "start": "rimraf ./build && react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  }
}

Here's my DevOps build pipeline:

trigger:
- master

pool:
  vmImage: 'windows-latest'

variables:
  buildConfiguration: 'Debug'

steps:
- task: UseNode@1
  displayName: 'Install Node 12.10.0'
  inputs:
    version: '12.10.0'
- task: NuGetCommand@2
  displayName: 'Restore NuGet Packages'
  inputs:
    command: 'restore'
    restoreSolution: './LimeadeDocs.sln'
- task: MSBuild@1
  inputs:
    solution: './LimeadeDocs.sln'
    platform: 'Any CPU'
    configuration: '$(BuildConfiguration)'
    msbuildArguments: '/t:publish'
- task: PublishBuildArtifacts@1
  displayName: 'Publish Build Artifacts'
  inputs:
    PathtoPublish: '$(Build.SourcesDirectory)/LimeadeDocs/bin/$(BuildConfiguration)/netcoreapp2.1/publish/'
    ArtifactName: 'Website'
  condition: succeededOrFailed()

Here's the CSPROJ file it's building:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>netcoreapp2.1</TargetFramework>
    <TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
    <TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
    <IsPackable>false</IsPackable>
    <SpaRoot>ClientApp\</SpaRoot>
    <DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules\**</DefaultItemExcludes>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.App" />
    <PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.1.2" PrivateAssets="All" />
    <PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="2.1.1" />
  </ItemGroup>

  <ItemGroup>
    <!-- Don't publish the SPA source files, but do show them in the project files list -->
    <Content Remove="$(SpaRoot)**" />
    <None Include="$(SpaRoot)**" Exclude="$(SpaRoot)node_modules\**" />
  </ItemGroup>

  <Target Name="DebugEnsureNodeEnv" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('$(SpaRoot)node_modules') ">
    <!-- Ensure Node.js is installed -->
    <Exec Command="node --version" ContinueOnError="true">
      <Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
    </Exec>
    <Error Condition="'$(ErrorCode)' != '0'" Text="Node.js is required to build and run this project. To continue, please install Node.js from https://nodejs.org/, and then restart your command prompt or IDE." />
    <Message Importance="high" Text="Restoring dependencies using 'npm'. This may take several minutes..." />
    <Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
  </Target>

  <Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
    <!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
    <Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
    <Exec WorkingDirectory="$(SpaRoot)" Command="npm run build" />

    <!-- Include the newly-built files in the publish output -->
    <ItemGroup>
      <DistFiles Include="$(SpaRoot)build\**" />
      <ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
        <RelativePath>%(DistFiles.Identity)</RelativePath>
        <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
      </ResolvedFileToPublish>
    </ItemGroup>
  </Target>

</Project>

So, in summary, if I run msbuild .\LimeadeDocs.sln /t:publish from my command line, zip up the contents of \bin\Debug\netcoreapp2.1\publish and drop it on the web server, it works. However, when I deploy the contents of the Website artifact built in DevOps, I get the script error.

I also ran a diff between the bundle.js file produced locally and the one DevOps produces. There's massive amounts of differences, so I'm not quite sure what would account for that. Originally, it looked like different versions of packages were getting installed locally than on DevOps, but I've done everything I can think of to show that isn't the case. So, the bundling mechanism that takes all these packages and builds a giant bundle.js file must be running differently on DevOps as it does locally. However, it's all the same code. I'm pretty stuck as to where to go from here.

Upvotes: 1

Views: 1002

Answers (1)

Mike Christensen
Mike Christensen

Reputation: 91608

So, turns out the answer was to remove the package-lock.json file from the build so that the DevOps build agent has to generate the file itself. No idea what sort of environmental differences there are between my local build machine and the DevOps agent, but it seems the two files are incompatible and thus cannot be checked in to Git.

Hopefully this will help someone in the future who has run across this problem too.

Upvotes: 2

Related Questions