-
Notifications
You must be signed in to change notification settings - Fork 646
/
manifest.ts
86 lines (71 loc) · 2.85 KB
/
manifest.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import { MetadataGroup } from '@ionic/cli-framework';
import { prettyPath } from '@ionic/cli-framework/utils/format';
import { map } from '@ionic/utils-array';
import { readdirp, stat, writeFile } from '@ionic/utils-fs';
import * as crypto from 'crypto';
import * as fs from 'fs';
import * as path from 'path';
import { CommandMetadata } from '../../definitions';
import { input } from '../../lib/color';
import { FatalException } from '../../lib/errors';
import { DeployCoreCommand } from './core';
interface DeployManifestItem {
href: string;
size: number;
integrity: string;
}
export class DeployManifestCommand extends DeployCoreCommand {
async getMetadata(): Promise<CommandMetadata> {
return {
name: 'manifest',
type: 'project',
summary: 'Generates a manifest file for the deploy service from a built app directory',
groups: [MetadataGroup.HIDDEN],
};
}
async run(): Promise<void> {
if (!this.project) {
throw new FatalException(`Cannot run ${input('ionic deploy manifest')} outside a project directory.`);
}
await this.requireNativeIntegration();
const buildDir = await this.project.getDistDir();
const manifest = await this.getFilesAndSizesAndHashesForGlobPattern(buildDir);
const manifestPath = path.resolve(buildDir, 'pro-manifest.json');
await writeFile(manifestPath, JSON.stringify(manifest, undefined, 2), { encoding: 'utf8' });
this.env.log.ok(`Appflow Deploy manifest written to ${input(prettyPath(manifestPath))}!`);
}
private async getFilesAndSizesAndHashesForGlobPattern(buildDir: string): Promise<DeployManifestItem[]> {
const contents = await readdirp(buildDir, { filter: item => !/(css|js)\.map$/.test(item.path) });
const stats = await map(contents, async (f): Promise<[string, fs.Stats]> => [f, await stat(f)]);
const files = stats.filter(([ , s ]) => !s.isDirectory());
const items = await Promise.all(files.map(([f, s]) => this.getFileAndSizeAndHashForFile(buildDir, f, s)));
return items.filter(item => item.href !== 'pro-manifest.json');
}
private async getFileAndSizeAndHashForFile(buildDir: string, file: string, s: fs.Stats): Promise<DeployManifestItem> {
const buffer = await this.readFile(file);
return {
href: path.relative(buildDir, file),
size: s.size,
integrity: this.getIntegrity(buffer),
};
}
private async readFile(file: string): Promise<Buffer> {
return new Promise<Buffer>((resolve, reject) => {
fs.readFile(file, (err, buffer) => {
if (err) {
return reject(err);
}
resolve(buffer);
});
});
}
private getIntegrity(data: Buffer) {
return ['sha256', 'sha384', 'sha512']
.map(algorithm => {
const hash = crypto.createHash(algorithm);
hash.update(data);
return algorithm + '-' + hash.digest('base64');
})
.join(' ');
}
}