Search code examples
typescriptamazon-s3github-actionsamazon-cloudfrontaws-cdk

Cannot find asset at /home/runner/CDK_Test/frontend/frontendapp/build when running CDK deploy on GitHub Action


It could not find the build from frontendapp when I tried to run cdk deploy on GitHub Actions. I checked the path for build and it seems to be no issues. I tried running cdk deploy --all locally and it works when I open the website that shows the content from the frontendapp/build.

Error message:

Run cdk deploy --all
/home/runner/work/CDK_Test/CDK_Test/node_modules/aws-cdk-lib/core/lib/asset-staging.js:1
"use strict";var _a;Object.defineProperty(exports,"__esModule",{value:!0}),exports.AssetStaging=void 0;var jsiiDeprecationWarnings=()=>{var tmp=require("../../.warnings.jsii.js");return jsiiDeprecationWarnings=()=>tmp,tmp};const JSII_RTTI_SYMBOL_1=Symbol.for("jsii.rtti");var crypto=()=>{var tmp=require("crypto");return crypto=()=>tmp,tmp},path=()=>{var tmp=require("path");return path=()=>tmp,tmp},constructs_1=()=>{var tmp=require("constructs");return constructs_1=()=>tmp,tmp},fs=()=>{var tmp=require("fs-extra");return fs=()=>tmp,tmp},assets_1=()=>{var tmp=require("./assets");return assets_1=()=>tmp,tmp},bundling_1=()=>{var tmp=require("./bundling");return bundling_1=()=>tmp,tmp},fs_1=()=>{var tmp=require("./fs");return fs_1=()=>tmp,tmp},fingerprint_1=()=>{var tmp=require("./fs/fingerprint");return fingerprint_1=()=>tmp,tmp},names_1=()=>{var tmp=require("./names");return names_1=()=>tmp,tmp},asset_staging_1=()=>{var tmp=require("./private/asset-staging");return asset_staging_1=()=>tmp,tmp},cache_1=()=>{var tmp=require("./private/cache");return cache_1=()=>tmp,tmp},stack_1=()=>{var tmp=require("./stack");return stack_1=()=>tmp,tmp},stage_1=()=>{var tmp=require("./stage");return stage_1=()=>tmp,tmp},cxapi=()=>{var tmp=require("../../cx-api");return cxapi=()=>tmp,tmp};const ARCHIVE_EXTENSIONS=[".tar.gz",".zip",".jar",".tar",".tgz"],ASSET_SALT_CONTEXT_KEY="@aws-cdk/core:assetHashSalt";class AssetStaging extends constructs_1().Construct{static clearAssetHashCache(){this.assetCache.clear(),(0,fingerprint_1().clearLargeFileFingerprintCache)()}constructor(scope,id,props){super(scope,id);try{jsiiDeprecationWarnings().aws_cdk_lib_AssetStagingProps(props)}catch(error){throw process.env.JSII_DEBUG!=="1"&&error.name==="DeprecationError"&&Error.captureStackTrace(error,AssetStaging),error}const salt=this.node.tryGetContext(ASSET_SALT_CONTEXT_KEY);if(this.sourcePath=path().resolve(props.sourcePath),this.fingerprintOptions={...props,exclude:[".is_custom_resource",...props.exclude??[]],extraHash:props.extraHash||salt?`${props.extraHash??""}${salt??""}`:void 0},!fs().existsSync(this.sourcePath))throw new Error(`Cannot find asset at ${this.sourcePath}`);this.sourceStats=fs().statSync(this.sourcePath);const outdir=stage_1().Stage.of(this)?.assetOutdir;if(!outdir)throw new Error('unable to determine cloud assembly asset output directory. Assets must be defined indirectly within a "Stage" or an "App" scope');this.assetOutdir=outdir,this.customSourceFingerprint=props.assetHash,this.hashType=determineHashType(props.assetHashType,this.customSourceFingerprint);let stageThisAsset,skip=!1;if(props.bundling){skip=!stack_1().Stack.of(this).bundlingRequired;const bundling=props.bundling;stageThisAsset=()=>this.stageByBundling(bundling,skip)}else stageThisAsset=()=>this.stageByCopying();this.cacheKey=calculateCacheKey({outdir:this.assetOutdir,sourcePath:path().resolve(props.sourcePath),bundling:props.bundling,assetHashType:this.hashType,customFingerprint:this.customSourceFingerprint,extraHash:props.extraHash,exclude:props.exclude,ignoreMode:props.ignoreMode,skip});const staged=AssetStaging.assetCache.obtain(this.cacheKey,stageThisAsset);this.stagedPath=staged.stagedPath,this.absoluteStagedPath=staged.stagedPath,this.assetHash=staged.assetHash,this.packaging=staged.packaging,this.isArchive=staged.isArchive}get sourceHash(){return this.assetHash}relativeStagedPath(stack){try{jsiiDeprecationWarnings().aws_cdk_lib_Stack(stack)}catch(error){throw process.env.JSII_DEBUG!=="1"&&error.name==="DeprecationError"&&Error.captureStackTrace(error,this.relativeStagedPath),error}const asmManifestDir=stage_1().Stage.of(stack)?.outdir;return asmManifestDir?path().relative(this.assetOutdir,this.stagedPath).startsWith("..")||this.stagingDisabled?this.stagedPath:path().relative(asmManifestDir,this.stagedPath):this.stagedPath}stageByCopying(){const assetHash=this.calculateHash(this.hashType),stagedPath=this.stagingDisabled?this.sourcePath:path().resolve(this.assetOutdir,renderAssetFilename(assetHash,getExtension(this.sourcePath)));if(!this.sourceStats.isDirectory()&&!this.sourceStats.isFile())throw new Error(`Asset ${this.sourcePath} is expected to be either a directory or a regular file`);return this.stageAsset(this.sourcePath,stagedPath,"copy"),{assetHash,stagedPath,packaging:this.sourceStats.isDirectory()?assets_1().FileAssetPackaging.ZIP_DIRECTORY:assets_1().FileAssetPackaging.FILE,isArchive:this.sourceStats.isDirectory()||ARCHIVE_EXTENSIONS.includes(getExtension(this.sourcePath).toLowerCase())}}stageByBundling(bundling,skip){if(!this.sourceStats.isDirectory())throw new Error(`Asset ${this.sourcePath} is expected to be a directory when bundling`);if(skip){let hashType=this.hashType;return(hashType===assets_1().AssetHashType.OUTPUT||hashType===assets_1().AssetHashType.BUNDLE)&&(this.customSourceFingerprint=names_1().Names.uniqueId(this),hashType=assets_1().AssetHashType.CUSTOM),{assetHash:this.calculateHash(hashType,bundling),stagedPath:this.sourcePath,packaging:assets_1().FileAssetPackaging.ZIP_DIRECTORY,isArchive:!0}}let assetHash=this.hashType===assets_1().AssetHashType.SOURCE||this.hashType===assets_1().AssetHashType.CUSTOM?this.calculateHash(this.hashType,bundling):void 0;const bundleDir=this.determineBundleDir(this.assetOutdir,assetHash);this.bundle(bundling,bundleDir);const bundlingOutputType=bundling.outputType??bundling_1().BundlingOutput.AUTO_DISCOVER,bundledAsset=determineBundledAsset(bundleDir,bundlingOutputType);assetHash=assetHash??this.calculateHash(this.hashType,bundling,bundledAsset.path);const stagedPath=path().resolve(this.assetOutdir,renderAssetFilename(assetHash,bundledAsset.extension));return this.stageAsset(bundledAsset.path,stagedPath,"move"),bundledAsset.packaging===assets_1().FileAssetPackaging.FILE&&(this.hashType===assets_1().AssetHashType.OUTPUT||this.hashType===assets_1().AssetHashType.BUNDLE?fs().removeSync(path().dirname(bundledAsset.path)):fs().closeSync(fs().openSync(bundledAsset.path,"w"))),{assetHash,stagedPath,packaging:bundledAsset.packaging,isArchive:bundlingOutputType!==bundling_1().BundlingOutput.SINGLE_FILE}}get stagingDisabled(){return!!this.node.tryGetContext(cxapi().DISABLE_ASSET_STAGING_CONTEXT)}stageAsset(sourcePath,targetPath,style){if(fs().existsSync(targetPath)){style==="move"&&sourcePath!==targetPath&&fs().removeSync(sourcePath);return}if(style=="move"){fs().renameSync(sourcePath,targetPath);return}if(this.sourceStats.isFile())fs().copyFileSync(sourcePath,targetPath);else if(this.sourceStats.isDirectory())fs().mkdirSync(targetPath),fs_1().FileSystem.copyDirectory(sourcePath,targetPath,this.fingerprintOptions);else throw new Error(`Unknown file type: ${sourcePath}`)}determineBundleDir(outdir,sourceHash){return sourceHash?path().resolve(outdir,renderAssetFilename(sourceHash)):path().resolve(outdir,`bundling-temp-${this.cacheKey}`)}bundle(options,bundleDir){if(fs().existsSync(bundleDir))return;fs().ensureDirSync(bundleDir),fs().chmodSync(bundleDir,511);let localBundling;try{if(process.stderr.write(`Bundling asset ${this.node.path}...
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      ^
Error: Cannot find asset at /home/runner/work/CDK_Test/CDK_Test/frontend/frontendapp/build
    at new AssetStaging (/home/runner/work/CDK_Test/CDK_Test/node_modules/aws-cdk-lib/core/lib/asset-staging.js:1:2119)
    at new Asset (/home/runner/work/CDK_Test/CDK_Test/node_modules/aws-cdk-lib/aws-s3-assets/lib/asset.js:1:1141)
    at Object.bind (/home/runner/work/CDK_Test/CDK_Test/node_modules/aws-cdk-lib/aws-s3-deployment/lib/source.js:1:1460)
    at /home/runner/work/CDK_Test/CDK_Test/node_modules/aws-cdk-lib/aws-s3-deployment/lib/bucket-deployment.js:1:3768
    at Array.map (<anonymous>)
    at new BucketDeployment (/home/runner/work/CDK_Test/CDK_Test/node_modules/aws-cdk-lib/aws-s3-deployment/lib/bucket-deployment.js:1:3749)
    at new FrontendStack (/home/runner/work/CDK_Test/CDK_Test/cdk/lib/cdk_test-frontend-stack.ts:70:3)
    at new AppStack (/home/runner/work/CDK_Test/CDK_Test/cdk/lib/cdk_test-app-stack.ts:11:27)
    at Object.<anonymous> (/home/runner/work/CDK_Test/CDK_Test/cdk/lib/cdk_test-app-stack.ts:32:1)
    at Module._compile (node:internal/modules/cjs/loader:1358:14)

Subprocess exited with error 1
Error: Process completed with exit code 1.

.github/workflows/deploy.yml:

name: Deploy to AWS

on:
  push:
    branches:
      - main

permissions:
  id-token: write
  contents: read

env:
  AWS_REGION: 'us-east-1'

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repository
        uses: actions/checkout@v2

      - name: Set up Node.js
        uses: actions/setup-node@v2
        with:
          node-version: '20'

      - name: Install dependencies
        run: npm install

      - name: Install AWS CDK
        run: npm install -g aws-cdk

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v2
        with:
          role-to-assume: ${{secrets.ROLE_TO_ASSUME}}
          aws-region: ${{ env.AWS_REGION }}
          role-session-name: GitHubActions

      - name: CDK deploy
        run: cdk deploy --all

      - name: Run Sequelize commands
        env:
          DB_USERNAME: ${{ secrets.USERNAME }}
          DB_PASSWORD: ${{ secrets.PASSWORD }}
          DB_NAME: ${{ secrets.DATABASE }}
          DB_HOST: ${{ secrets.HOST }}
        run: |
          npx sequelize db:migrate

cdk/lib/cdk_test-frontend-stack.ts snippet codes:

const distribution = new cloudfront.CloudFrontWebDistribution(
    this,
    "cloudfront",
    {
      originConfigs: [
        {
          s3OriginSource: {
            s3BucketSource: websiteBucket,
            originAccessIdentity: identity,
          },
          behaviors: [
            {
              viewerProtocolPolicy:
                cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
              allowedMethods: cloudfront.CloudFrontAllowedMethods.GET_HEAD,
              compress: true,
              isDefaultBehavior: true,
            },
          ],
        },
      ],
      defaultRootObject: "./frontend/frontendapp/build/index.html",
    }
  );
  
  new s3deploy.BucketDeployment(this, "DeployWebsite", {
    sources: [s3deploy.Source.asset("./frontend/frontendapp/build")],
    destinationBucket: websiteBucket,
    distribution,
    });

frontend/frontendapp/build - listing the files and directory under build:

- static (dir)
- asset-manifest.json
- favicon.ico
- index.html
- logo512.png
- manifest.json
- robots.txt

I have tried running npm run build inside frontend/frontendapp before pushing to GitHub, where it automates deployment with CDK. However, the deployment failed. Do you have any suggestions or solutions for this? Thank you in advance.


Solution

  • I believe your problem will be solved if you:

    1. Ensure that you have separate packages for frontend and cdk. Each directory should have its own package.json and package-lock.json, or the equivalent files if you're using something other than npm.
    2. Add working-directory: cdk to your Actions steps named Install dependencies and CDK deploy.
    3. Add frontend install, build, and test steps to your Actions file before your CDK deploy step, like
      - name: Install frontend
        working-directory: frontend
        run: npm install
      - name: Build frontend
        working-directory: frontend
        run: npm run build
      - name: Test frontend
        working-directory: frontend
        run: npm test
      
      I think this is the most significant missing piece. Without npm run build happening for the frontend code on your Actions runner, the build/ directory isn't populated. The build/ artifact is probably ignored by Git, because its contents are created from other files and so should not be in source control. So even if you run npm run build before pushing, the files aren't available within Actions.
    4. Change sources: [s3deploy.Source.asset("./frontend/frontendapp/build")], to sources: [s3deploy.Source.asset("../frontend/frontendapp/build")], changing the path prefix from . to ...

    You may need some minor adjustments, for example if you have your package.json in frontend/frontendapp/ rather than in frontend/, but I believe it will all work if you do the steps above.

    I find it better to have separate package.jsons for CDK code and frontend code because:

    1. They are separate packages. The dependencies of one are not dependencies of the other.
    2. It avoids the confusion of trying to make sure that npm commands target the right project, and that build artifacts include the right files.

    I have one of my projects set up very similarly to yours, using the recommendations above, and its deployment works successfully. The project is at https://github.com/douglasnaphas/anagrampoems.

    You could probably ignore all my recommendations except for adding an npm run build GitHub Actions step for the frontend code, as long as you make sure it's happening in the right directory.