Skip to content

Commit

Permalink
correctly cache packaging type
Browse files Browse the repository at this point in the history
  • Loading branch information
jogold committed Feb 19, 2021
1 parent 7bc2ad8 commit 7b3d829
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 24 deletions.
78 changes: 54 additions & 24 deletions packages/@aws-cdk/core/lib/asset-staging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ interface StagedAsset {
* The hash we used previously
*/
readonly assetHash: string;

/**
* The packaging of the asset
*/
readonly packaging: FileAssetPackaging,

/**
* Whether this asset is an archive
*/
readonly isArchive: boolean;
}

/**
Expand Down Expand Up @@ -126,6 +136,16 @@ export class AssetStaging extends CoreConstruct {
*/
public readonly assetHash: string;

/**
* How this asset should be packaged.
*/
public readonly packaging: FileAssetPackaging;

/**
* Whether this asset is an archive (zip or jar).
*/
public readonly isArchive: boolean;

private readonly fingerprintOptions: FingerprintOptions;

private readonly hashType: AssetHashType;
Expand All @@ -140,15 +160,20 @@ export class AssetStaging extends CoreConstruct {

private readonly cacheKey: string;

private _packaging = FileAssetPackaging.ZIP_DIRECTORY;
private _isArchive = true;
private readonly sourceStats: fs.Stats;

constructor(scope: Construct, id: string, props: AssetStagingProps) {
super(scope, id);

this.sourcePath = path.resolve(props.sourcePath);
this.fingerprintOptions = props;

if (!fs.existsSync(this.sourcePath)) {
throw new Error(`Cannot find asset at ${this.sourcePath}`);
}

this.sourceStats = fs.statSync(this.sourcePath);

const outdir = 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');
Expand Down Expand Up @@ -197,6 +222,8 @@ export class AssetStaging extends CoreConstruct {
this.stagedPath = staged.stagedPath;
this.absoluteStagedPath = staged.stagedPath;
this.assetHash = staged.assetHash;
this.packaging = staged.packaging;
this.isArchive = staged.isArchive;
}

/**
Expand All @@ -208,20 +235,6 @@ export class AssetStaging extends CoreConstruct {
return this.assetHash;
}

/**
* How this asset should be packaged.
*/
public get packaging(): FileAssetPackaging {
return this._packaging;
}

/**
* Whether this asset is an archive (zip or jar).
*/
public get isArchive(): boolean {
return this._isArchive;
}

/**
* Return the path to the staged asset, relative to the Cloud Assembly (manifest) directory of the given stack
*
Expand Down Expand Up @@ -267,8 +280,18 @@ export class AssetStaging extends CoreConstruct {
? this.sourcePath
: path.resolve(this.assetOutdir, renderAssetFilename(assetHash, path.extname(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`);
}

this.stageAsset(this.sourcePath, stagedPath, 'copy');
return { assetHash, stagedPath };

return {
assetHash,
stagedPath,
packaging: this.sourceStats.isDirectory() ? FileAssetPackaging.ZIP_DIRECTORY : FileAssetPackaging.FILE,
isArchive: this.sourceStats.isDirectory() || ARCHIVE_EXTENSIONS.includes(path.extname(this.sourcePath).toLowerCase()),
};
}

/**
Expand All @@ -277,6 +300,10 @@ export class AssetStaging extends CoreConstruct {
* Optionally skip, in which case we pretend we did something but we don't really.
*/
private stageByBundling(bundling: BundlingOptions, skip: boolean): StagedAsset {
if (!this.sourceStats.isDirectory()) {
throw new Error(`Asset ${this.sourcePath} is expected to be a directory when bundling`);
}

if (skip) {
// We should have bundled, but didn't to save time. Still pretend to have a hash.
// If the asset uses OUTPUT or BUNDLE, we use a CUSTOM hash to avoid fingerprinting
Expand All @@ -289,6 +316,8 @@ export class AssetStaging extends CoreConstruct {
return {
assetHash: this.calculateHash(hashType, bundling),
stagedPath: this.sourcePath,
packaging: FileAssetPackaging.ZIP_DIRECTORY,
isArchive: true,
};
}

Expand All @@ -303,14 +332,18 @@ export class AssetStaging extends CoreConstruct {
// Check bundling output content and determine if we will need to archive
const bundlingOutputType = bundling.outputType ?? BundlingOutput.AUTO_DISCOVER;
const bundledAsset = determineBundledAsset(bundleDir, bundlingOutputType);
this._packaging = bundledAsset.packaging;

// Calculate assetHash afterwards if we still must
assetHash = assetHash ?? this.calculateHash(this.hashType, bundling, bundledAsset.path);
const stagedPath = path.resolve(this.assetOutdir, renderAssetFilename(assetHash, bundledAsset.extension));

this.stageAsset(bundledAsset.path, stagedPath, 'move');
return { assetHash, stagedPath };
return {
assetHash,
stagedPath,
packaging: bundledAsset.packaging,
isArchive: true, // bundling always produces an archive
};
}

/**
Expand Down Expand Up @@ -344,12 +377,9 @@ export class AssetStaging extends CoreConstruct {
}

// Copy file/directory to staging directory
const stat = fs.statSync(sourcePath);
if (stat.isFile()) {
if (this.sourceStats.isFile()) {
fs.copyFileSync(sourcePath, targetPath);
this._packaging = FileAssetPackaging.FILE;
this._isArchive = ARCHIVE_EXTENSIONS.includes(path.extname(sourcePath).toLowerCase());
} else if (stat.isDirectory()) {
} else if (this.sourceStats.isDirectory()) {
fs.mkdirSync(targetPath);
FileSystem.copyDirectory(sourcePath, targetPath, this.fingerprintOptions);
} else {
Expand Down
50 changes: 50 additions & 0 deletions packages/@aws-cdk/core/test/staging.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,56 @@ nodeunitShim({
test.done();
},

'asset packaging type is correct when staging is skipped because of memory cache'(test: Test) {
// GIVEN
const stack = new Stack();
const sourcePath = path.join(__dirname, 'archive', 'archive.zip');

// WHEN
const staging1 = new AssetStaging(stack, 's1', { sourcePath });
const staging2 = new AssetStaging(stack, 's2', { sourcePath });

test.deepEqual(staging1.packaging, FileAssetPackaging.FILE);
test.deepEqual(staging1.isArchive, true);
test.deepEqual(staging2.packaging, staging1.packaging);
test.deepEqual(staging2.isArchive, staging1.isArchive);
test.done();
},

'asset packaging type is correct when staging is skipped because of disk cache'(test: Test) {
// GIVEN
const TEST_OUTDIR = path.join(__dirname, 'cdk.out');
if (fs.existsSync(TEST_OUTDIR)) {
fs.removeSync(TEST_OUTDIR);
}

const sourcePath = path.join(__dirname, 'archive', 'archive.zip');

const app1 = new App({ outdir: TEST_OUTDIR });
const stack1 = new Stack(app1, 'Stack');

const app2 = new App({ outdir: TEST_OUTDIR }); // same OUTDIR
const stack2 = new Stack(app2, 'stack');

// WHEN
const staging1 = new AssetStaging(stack1, 'Asset', { sourcePath });

// Now clear asset hash cache to show that during the second staging
// even though the asset is already available on disk it will correctly
// be considered as a FileAssetPackaging.FILE.
AssetStaging.clearAssetHashCache();

const staging2 = new AssetStaging(stack2, 'Asset', { sourcePath });

// THEN
test.deepEqual(staging1.packaging, FileAssetPackaging.FILE);
test.deepEqual(staging1.isArchive, true);
test.deepEqual(staging2.packaging, staging1.packaging);
test.deepEqual(staging2.isArchive, staging1.isArchive);

test.done();
},

'staging of a non-archive file correctly sets packaging and isArchive'(test: Test) {
// GIVEN
const stack = new Stack();
Expand Down

0 comments on commit 7b3d829

Please sign in to comment.