eip-s3-deploy 2.1.2 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/deploy.js +231 -126
- package/deployConf.js +39 -17
- package/deployment.lock +11 -1
- package/package.json +3 -6
- package/test/example.html +117 -0
- package/test/folder1/file1.txt +1 -0
- package/test/deployConf.js +0 -26
- package/test/deployment.lock +0 -10
package/deploy.js
CHANGED
|
@@ -1,176 +1,281 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* --------------
|
|
5
|
-
* DATE: 2020-06-18 @ 11:54
|
|
6
|
-
* AUTHOR: ollie
|
|
7
|
-
* ORIGINAL NAME: /./deploy
|
|
8
|
-
* --------------
|
|
9
|
-
* Created for the olivers-tools
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
const aws = require ( 'aws-sdk' );
|
|
13
3
|
const path = require ( 'path' );
|
|
14
|
-
const
|
|
4
|
+
const { s3 } = require ( 'eip-cloud-services' );
|
|
15
5
|
const fs = require ( 'fs' ).promises;
|
|
16
|
-
const
|
|
17
|
-
const cf = new aws.CloudFront ( { region: 'eu-west-1' } );
|
|
6
|
+
const crypto = require ( 'crypto' );
|
|
18
7
|
const recursive = require ( 'recursive-readdir' );
|
|
19
|
-
const md5File = require ( 'md5-file' );
|
|
20
8
|
const handlebars = require ( 'handlebars' );
|
|
21
9
|
const mime = require ( 'mime' );
|
|
22
10
|
const progress = require ( 'cli-progress' );
|
|
23
|
-
|
|
11
|
+
|
|
12
|
+
const COMMAND_OPTIONS = [ '-f', '--force', '-ff', '--folderFilter', '-e', '--env', '-v', '--verbose' ];
|
|
13
|
+
const forceRedeploy = process.argv.includes ( '-f' ) || process.argv.includes ( '--force' );
|
|
14
|
+
const verboseOutput = process.argv.includes ( '-v' ) || process.argv.includes ( '--verbose' );
|
|
15
|
+
|
|
16
|
+
const getCommandOptions = ( options ) => {
|
|
17
|
+
const optionIndex = process.argv.findIndex ( arg => options.includes ( arg ) );
|
|
18
|
+
if ( optionIndex === -1 ) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const option = process.argv[ optionIndex ];
|
|
23
|
+
const value = process.argv[ optionIndex + 1 ];
|
|
24
|
+
|
|
25
|
+
if ( !value || COMMAND_OPTIONS.includes ( value ) ) {
|
|
26
|
+
usage ( `Option "${option}" requires a value` );
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return value;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const folderFilter = getCommandOptions ( [ '-ff', '--folderFilter' ] );
|
|
33
|
+
const environment = getCommandOptions ( [ '-e', '--env' ] ) || process.argv[ 2 ];
|
|
24
34
|
|
|
25
35
|
const loadDeploymentLock = async () => fs.access ( process.cwd () + '/deployment.lock' )
|
|
26
36
|
.then ( () => fs.readFile ( path.join ( process.cwd (), 'deployment.lock' ), 'utf8' ) )
|
|
27
|
-
.then ( json => JSON.parse ( json )[
|
|
37
|
+
.then ( json => JSON.parse ( json )[ environment ] || ( {} ) )
|
|
28
38
|
.catch ( () => ( {} ) );
|
|
29
39
|
|
|
30
40
|
const writeDeploymentLock = async ( deploymentHash ) => fs.access ( process.cwd () + '/deployment.lock' )
|
|
31
41
|
.then ( () => fs.readFile ( path.join ( process.cwd (), 'deployment.lock' ), 'utf8' ) )
|
|
32
42
|
.then ( json => {
|
|
33
43
|
const previousHash = JSON.parse ( json );
|
|
34
|
-
previousHash[
|
|
44
|
+
previousHash[ environment ] = previousHash[ environment ] ? { ...previousHash[ environment ], ...deploymentHash } : deploymentHash;
|
|
35
45
|
|
|
36
46
|
return fs.writeFile ( path.join ( process.cwd (), 'deployment.lock' ), JSON.stringify ( previousHash, null, 2 ) );
|
|
37
47
|
} )
|
|
38
48
|
.catch ( () => {
|
|
39
|
-
const newHash =
|
|
40
|
-
newHash[
|
|
49
|
+
const newHash = {};
|
|
50
|
+
newHash[ environment ] = deploymentHash;
|
|
41
51
|
|
|
42
52
|
return fs.writeFile ( path.join ( process.cwd (), 'deployment.lock' ), JSON.stringify ( newHash, null, 2 ) );
|
|
43
53
|
} );
|
|
44
54
|
|
|
55
|
+
const calculateMD5 = async ( filePath ) => {
|
|
56
|
+
const content = await fs.readFile ( filePath );
|
|
57
|
+
|
|
58
|
+
return crypto.createHash ( 'md5' ).update ( content ).digest ( 'hex' );
|
|
59
|
+
};
|
|
60
|
+
|
|
45
61
|
const deploy = async () => {
|
|
46
|
-
const files = await fs.readdir ( process.cwd () );
|
|
47
|
-
let deployConfName;
|
|
48
62
|
|
|
49
|
-
if (
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
63
|
+
if ( process.argv.length <= 2 || process.argv.includes ( '--help' ) || process.argv.includes ( '-h' ) || process.argv.includes ( 'help' ) ) {
|
|
64
|
+
showHelp ();
|
|
65
|
+
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
53
68
|
|
|
54
|
-
|
|
55
|
-
|
|
69
|
+
try {
|
|
70
|
+
const files = await fs.readdir ( process.cwd () );
|
|
71
|
+
let deployConfName;
|
|
56
72
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
73
|
+
if ( files.find ( f => f === 'deployConf.cjs' ) )
|
|
74
|
+
deployConfName = 'deployConf.cjs';
|
|
75
|
+
else
|
|
76
|
+
deployConfName = 'deployConf.js';
|
|
77
|
+
|
|
78
|
+
const parentConfig = require ( path.join ( process.cwd (), deployConfName ) );
|
|
79
|
+
const config = require ( path.join ( process.cwd (), deployConfName ) )[ environment ];
|
|
80
|
+
if ( typeof config.bucket === 'string' ) config.bucket = [ config.bucket ];
|
|
81
|
+
if ( typeof config.folder === 'string' ) config.folder = [ config.folder ];
|
|
82
|
+
const promiseChain = {};
|
|
83
|
+
|
|
84
|
+
if ( !config ) {
|
|
85
|
+
process.stdout.write ( '\x1b[38;5;196mThe environment specified cannot be found in the deployConfig.js file.\x1b[0m\n' );
|
|
86
|
+
process.exit ( 0 );
|
|
87
|
+
}
|
|
88
|
+
if ( !parentConfig.version || parentConfig.version !== 2 ) {
|
|
89
|
+
process.stdout.write ( '\x1b[38;5;196mThere is a deployConf.js file however it\'s not using the latest version please update your config to version 2.\x1b[0m\n' );
|
|
90
|
+
process.stdout.write ( '\x1b[38;5;214mHere is an example deployConf.js to assist:\x1b[0m - https://cf.eip.telegraph.co.uk/testFolder/example.html\n' );
|
|
91
|
+
process.exit ( 0 );
|
|
92
|
+
}
|
|
93
|
+
if ( ![ 'bucket', 'folder', 'cache' ].every ( key => Object.keys ( config ).includes ( key ) ) ){
|
|
94
|
+
process.stdout.write ( '\x1b[38;5;214mThere is a deployConf.js file however it\'s not structured correctly, please make sure you\'ve got the correct attributes.\x1b[0m\n' );
|
|
95
|
+
process.stdout.write ( '\x1b[38;5;190mHere is an example deployConf.js to assist:\x1b[0m - https://cf.eip.telegraph.co.uk/testFolder/example.html\n' );
|
|
96
|
+
process.exit ( 0 );
|
|
97
|
+
}
|
|
61
98
|
|
|
62
|
-
process.stdout.write ( '-------------------\n' );
|
|
63
|
-
process.stdout.write ( '\x1b[38;5;214mINITIATING DEPLOYMENT TO:\x1b[0m \x1b[38;5;190m' + args._[ 0 ] + '\n' );
|
|
64
|
-
process.stdout.write ( '\x1b[38;5;214m📁 Copying contents of:\x1b[0m \x1b[38;5;190m' + ( config.targetFolder ? process.cwd () + '/' + config.targetFolder : process.cwd () ) + ( args._[ 1 ] ? '/' + args._[ 1 ] : '' ) + '\x1b[0m\n' );
|
|
65
|
-
process.stdout.write ( '\x1b[38;5;214m🗑 to S3 Bucket/Folder:\x1b[0m \x1b[38;5;190m' + config.Bucket + '/' + ( config.Folder || '' ) + ( args._[ 1 ] ? args._[ 1 ] + '/' : '' ) + '\x1b[0m\n' );
|
|
66
|
-
process.stdout.write ( '\x1b[38;5;214m☁️ CloudFront CDN Invalidation:\x1b[0m ' + ( config.cdnId ? '\x1b[38;5;190mYES (' + config.cdnId + ')' : '\x1b[38;5;9mNO' ) + '\x1b[0m\n' );
|
|
67
|
-
if ( args._[ 1 ] ) process.stdout.write ( '\x1b[38;5;214m📂 Custom Deploy Folder Selected:\x1b[0m \x1b[38;5;190m' + args._[ 1 ] + '\x1b[0m\n' );
|
|
68
|
-
if ( args.f ) {
|
|
69
99
|
process.stdout.write ( '-------------------\n' );
|
|
70
|
-
process.stdout.write ( '\x1b[38;5;
|
|
71
|
-
|
|
72
|
-
|
|
100
|
+
process.stdout.write ( '\x1b[38;5;214mINITIATING FILE DEPLOYMENT TO:\x1b[0m \x1b[38;5;190m' + environment + '\n' );
|
|
101
|
+
process.stdout.write ( '\x1b[38;5;214m📁 Copying contents of:\x1b[0m \x1b[38;5;190m' + ( config.targetFolder ? process.cwd () + '/' + config.targetFolder : process.cwd () ) + ( folderFilter ? '/' + folderFilter : '' ) + '\x1b[0m\n' );
|
|
102
|
+
config.bucket.forEach ( ( bucket, i ) => {
|
|
103
|
+
process.stdout.write ( '\x1b[38;5;214m🗑 to S3 Bucket/Folder:\x1b[0m \x1b[38;5;190m' + bucket + '/' + ( config.folder[ i ] || '' ) + ( folderFilter ? '/' + folderFilter + '/' : '' ) + '\x1b[0m\n' );
|
|
104
|
+
} );
|
|
105
|
+
process.stdout.write ( '\x1b[38;5;214m☁️ CloudFront CDN Invalidation:\x1b[0m ' + ( config.cdnId ? '\x1b[38;5;190mYES (' + config.cdnId + ')' : '\x1b[38;5;9mNO' ) + '\x1b[0m\n' );
|
|
106
|
+
if ( folderFilter ) {
|
|
107
|
+
process.stdout.write ( '-------------------\n' );
|
|
108
|
+
process.stdout.write ( '\x1b[38;5;214m📂 Folder Filter Applied:\x1b[0m \x1b[38;5;190m' + folderFilter + '\x1b[0m\n' );
|
|
109
|
+
}
|
|
110
|
+
if ( forceRedeploy ) {
|
|
111
|
+
process.stdout.write ( '-------------------\n' );
|
|
112
|
+
process.stdout.write ( '\x1b[38;5;196m🔥 FORCING DEPLOYMENT - deployment.lock will be ignored. 🔥\x1b[0m\n' );
|
|
113
|
+
}
|
|
114
|
+
process.stdout.write ( '-------------------\n' );
|
|
73
115
|
|
|
74
|
-
|
|
75
|
-
|
|
116
|
+
const scanDirectory = path.resolve ( config.targetFolder && config.targetFolder !== '' ? `${ process.cwd () }/${ config.targetFolder }` : process.cwd () );
|
|
117
|
+
|
|
118
|
+
let scannedFiles = await recursive ( scanDirectory, [ ...( config.omit || [] ), 'deployment.lock' ] );
|
|
119
|
+
const deploymentLock = await loadDeploymentLock ();
|
|
76
120
|
|
|
77
|
-
|
|
78
|
-
const deploymentLock = await loadDeploymentLock ();
|
|
121
|
+
if ( folderFilter ) scannedFiles = scannedFiles.filter ( filepath => filepath.includes ( `${ scanDirectory }/${ folderFilter }` ) );
|
|
79
122
|
|
|
80
|
-
|
|
81
|
-
|
|
123
|
+
promiseChain.changedFiles = promiseChain.newFiles = 0;
|
|
124
|
+
promiseChain.deploymentHash = {};
|
|
82
125
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
if (
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
126
|
+
const filesToUpload = ( await Promise.all ( scannedFiles.map ( async filename => {
|
|
127
|
+
const md5Hash = await calculateMD5 ( filename );
|
|
128
|
+
promiseChain.deploymentHash[ filename.replace ( process.cwd (), '' ) ] = md5Hash;
|
|
129
|
+
if ( Object.prototype.hasOwnProperty.call ( deploymentLock, filename.replace ( process.cwd (), '' ) ) ) {
|
|
130
|
+
if ( deploymentLock[ filename.replace ( process.cwd (), '' ) ] === md5Hash ) {
|
|
131
|
+
if ( verboseOutput ) process.stdout.write ( '\x1b[38;5;245mNo Change: ' + filename + '\x1b[0m\n' );
|
|
132
|
+
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
if ( verboseOutput ) process.stdout.write ( '\x1b[38;5;172mChange: ' + filename + '\x1b[0m\n' );
|
|
136
|
+
promiseChain.changedFiles++;
|
|
90
137
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
138
|
+
return filename;
|
|
139
|
+
}
|
|
140
|
+
if ( verboseOutput ) process.stdout.write ( '\x1b[38;5;64mChange: ' + filename + '\x1b[0m\n' );
|
|
141
|
+
promiseChain.newFiles++;
|
|
94
142
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
process.stdout.write ( `📗 ${ args.f ? scannedFiles.length : promiseChain.newFiles } new files | 📙 ${ args.f ? 0 : promiseChain.changedFiles } updated files | 📕 ${ args.f ? 0 : scannedFiles.length - filesToUpload.length } unchanged files\n` );
|
|
99
|
-
process.stdout.write ( '-------------------\n' );
|
|
143
|
+
return filename;
|
|
144
|
+
} ) ) ).filter ( Boolean );
|
|
145
|
+
if ( verboseOutput ) process.stdout.write ( '-------------------\n' );
|
|
100
146
|
|
|
101
|
-
|
|
102
|
-
process.stdout.write ( '\x1b[38;5;190mNothing to deploy\x1b[0m (you can force redeploy using the \'-f\' option)\n' );
|
|
147
|
+
process.stdout.write ( `📗 ${ forceRedeploy ? scannedFiles.length : promiseChain.newFiles } new files | 📙 ${ forceRedeploy ? 0 : promiseChain.changedFiles } updated files | 📕 ${ forceRedeploy ? 0 : scannedFiles.length - filesToUpload.length } unchanged files\n` );
|
|
103
148
|
process.stdout.write ( '-------------------\n' );
|
|
149
|
+
|
|
150
|
+
if ( ( forceRedeploy ? scannedFiles.length : filesToUpload.length ) === 0 ) {
|
|
151
|
+
process.stdout.write ( '\x1b[38;5;190mNothing to deploy\x1b[0m (you can force redeploy using the \'-f\' option)\n' );
|
|
152
|
+
process.stdout.write ( '-------------------\n' );
|
|
104
153
|
|
|
105
|
-
|
|
106
|
-
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
107
156
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
157
|
+
const progressBar = new progress.SingleBar ( {
|
|
158
|
+
format: '\x1b[38;5;214m🚀 Deploying [\x1b[38;5;190m{bar}\x1b[0m\x1b[38;5;214m] {percentage}% | ETA: {eta}s | {value}/{total}',
|
|
159
|
+
barCompleteChar: '#',
|
|
160
|
+
barIncompleteChar: '-',
|
|
161
|
+
barGlue: '\x1b[38;5;214m',
|
|
162
|
+
etaBuffer: 10000,
|
|
163
|
+
etaAsynchronousUpdate: true
|
|
164
|
+
} );
|
|
165
|
+
if ( !verboseOutput ) progressBar.start ( ( forceRedeploy ? scannedFiles.length : filesToUpload.length ) * config.bucket.length );
|
|
117
166
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
167
|
+
await Promise.all ( config.bucket.map ( async ( bucket, i ) => {
|
|
168
|
+
await Promise.all ( ( forceRedeploy ? scannedFiles : filesToUpload ).map ( filename => fs.readFile ( filename )
|
|
169
|
+
.then ( file => {
|
|
170
|
+
let body = file;
|
|
121
171
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
172
|
+
try {
|
|
173
|
+
body = config.data ? handlebars.compile ( file.toString ( 'utf8' ) ) ( config.data ) : file;
|
|
174
|
+
}
|
|
175
|
+
catch ( e ) {
|
|
176
|
+
// do nothing
|
|
177
|
+
}
|
|
128
178
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
179
|
+
let key = ( config.folder[ i ] ? `${config.folder[ i ]}` : '' ) +
|
|
180
|
+
( config.targetFolder && config.targetFolder !== '' ? filename.replace ( process.cwd (), '' ).replace ( `${config.targetFolder}/`, '' ) : filename.replace ( process.cwd (), '' ) );
|
|
181
|
+
key = key.replace ( /\\/g, '/' );
|
|
182
|
+
if ( key.startsWith ( '/' ) ) {
|
|
183
|
+
key = key.substring ( 1 );
|
|
184
|
+
}
|
|
185
|
+
if ( key.endsWith ( '/' ) ) {
|
|
186
|
+
key = key.slice ( 0, -1 );
|
|
187
|
+
}
|
|
137
188
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
189
|
+
if ( verboseOutput ) process.stdout.write ( '\x1b[38;5;172mUploading to S3: ' + filename + '\x1b[0m\n' );
|
|
190
|
+
|
|
191
|
+
return s3.set ( key, body, {
|
|
192
|
+
bucket: bucket,
|
|
193
|
+
contentType: mime.getType ( filename ),
|
|
194
|
+
cacheControl: Object.keys ( config.cache )
|
|
195
|
+
.map ( cacheKey => typeof config.cache[ cacheKey ] === 'boolean' ?
|
|
196
|
+
config.cache[ cacheKey ] ? cacheKey : null :
|
|
197
|
+
config.cache[ cacheKey ] ? `${cacheKey}=${config.cache[ cacheKey ]}` : null )
|
|
198
|
+
.filter ( n => n )
|
|
199
|
+
.join ( ', ' )
|
|
200
|
+
} ).then ( () => !verboseOutput && progressBar.increment () );
|
|
201
|
+
} )
|
|
202
|
+
) );
|
|
203
|
+
} ) );
|
|
204
|
+
|
|
205
|
+
if ( !verboseOutput ) progressBar.stop ();
|
|
206
|
+
|
|
207
|
+
if ( config.cdnId ) {
|
|
208
|
+
if ( verboseOutput ) process.stdout.write ( '\x1b[38;5;172mRequesting invalidation: ' + [ config.folder ? '/' + config.folder + '/*' : '/*' ] + '\x1b[0m\n' );
|
|
209
|
+
await fetch ( 'https://tools.eip.telegraph.co.uk/v1/invalidate', {
|
|
210
|
+
method: 'POST',
|
|
211
|
+
headers: {
|
|
212
|
+
'Content-Type': 'application/json'
|
|
213
|
+
},
|
|
214
|
+
body: JSON.stringify ( {
|
|
215
|
+
cdn: config.cdnId,
|
|
216
|
+
key: config.folder.map ( folder => folder ? '/' + folder + '/*' : '/*' ),
|
|
217
|
+
environment: config?.environment || undefined
|
|
218
|
+
} )
|
|
219
|
+
} );
|
|
220
|
+
process.stdout.write ( '-------------------\n' );
|
|
221
|
+
config.folder.forEach ( folder => {
|
|
222
|
+
process.stdout.write ( '📤 \x1b[38;5;190mCloudFront Cache Cleared\x1b[0m | Invalidation: ' + ( folder ? '/' + folder + '/*' : '/*' ) + '\n' );
|
|
223
|
+
} );
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// This should be done after everything is complete.
|
|
227
|
+
await writeDeploymentLock ( promiseChain.deploymentHash );
|
|
228
|
+
|
|
229
|
+
const user = process.env.PAPI_USER || 'Unknown';
|
|
230
|
+
const machine = process.env.PAPI_MACHINE || 'Unknown';
|
|
231
|
+
const deploymentReport = [ {
|
|
232
|
+
project: config.folder,
|
|
233
|
+
context: config.bucket,
|
|
234
|
+
user: `${user} - ${machine}`,
|
|
235
|
+
environment: environment,
|
|
236
|
+
timestamp: Date.now ()
|
|
237
|
+
} ];
|
|
238
|
+
if ( deploymentReport.length > 0 ){
|
|
239
|
+
await fetch ( 'https://tools.eip.telegraph.co.uk/v1/healthcheck/deployment', {
|
|
240
|
+
method: 'POST',
|
|
241
|
+
headers: {
|
|
242
|
+
'Content-Type': 'application/json'
|
|
243
|
+
},
|
|
244
|
+
body: JSON.stringify ( { deployments: deploymentReport } )
|
|
245
|
+
} );
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
process.stdout.write ( '-------------------\n' );
|
|
249
|
+
process.stdout.write ( '✅ \x1b[38;5;190mDeployment completed.\x1b[0m\n' );
|
|
164
250
|
process.stdout.write ( '-------------------\n' );
|
|
165
|
-
process.stdout.write ( '📤 \x1b[38;5;190mCloudFront Cache Cleared\x1b[0m | Invalidation: ' + ( config.Folder ? '/' + config.Folder + '/*' : '/*' ) + '\n' );
|
|
166
251
|
}
|
|
252
|
+
catch ( error ){
|
|
253
|
+
console.log ( error );
|
|
254
|
+
showHelp ();
|
|
255
|
+
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
};
|
|
167
259
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
260
|
+
const showHelp = () => {
|
|
261
|
+
console.log ( 'S3 Deploy Script' );
|
|
262
|
+
console.log ( 'Usage:' );
|
|
263
|
+
console.log ( ' s3-deploy <environment_name> [-f|--force] [-ff|--folderFilter <folder_filter>] [-e|--env <environment_name>]' );
|
|
264
|
+
console.log ( 'Options:' );
|
|
265
|
+
console.log ( ' -f, --force Force redeployment of files' );
|
|
266
|
+
console.log ( ' -ff, --folderFilter Specify a single folder to deploy' );
|
|
267
|
+
console.log ( ' -e, --env Specify the environment for deployment' );
|
|
268
|
+
console.log ( ' -v, --verbose Print more output' );
|
|
269
|
+
console.log ( 'Examples:' );
|
|
270
|
+
console.log ( ' s3-deploy test' );
|
|
271
|
+
console.log ( ' s3-deploy production --force' );
|
|
272
|
+
console.log ( ' s3-deploy test -ff myFolder' );
|
|
273
|
+
console.log ( ' s3-deploy -ff myFolder -e prod -f' );
|
|
274
|
+
console.log ( ' s3-deploy --folderFilter myFolder -e test -f' );
|
|
275
|
+
process.exit ( 0 );
|
|
174
276
|
};
|
|
175
277
|
|
|
176
|
-
deploy ()
|
|
278
|
+
deploy ().catch ( ( error ) => {
|
|
279
|
+
console.error ( `ERROR: ${error}` );
|
|
280
|
+
console.log ( error );
|
|
281
|
+
} );
|
package/deployConf.js
CHANGED
|
@@ -1,30 +1,52 @@
|
|
|
1
1
|
module.exports = {
|
|
2
|
-
|
|
2
|
+
version: 2,
|
|
3
3
|
prod: {
|
|
4
|
-
|
|
5
|
-
cdnId: '
|
|
6
|
-
|
|
7
|
-
targetFolder: 'test',
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
bucket: 's3.eip.telegraph.co.uk', // The location you want to deploy to.
|
|
5
|
+
cdnId: 'cf', // The prefix or identifier for the CDN. e.g. "cf", "cf-particle-html", etc
|
|
6
|
+
folder: 'testFolder', // The remote folder you which to upload to.
|
|
7
|
+
targetFolder: 'test', // The local folder to use as a root for uploading. Remove this to use root folder.
|
|
8
|
+
cache: {
|
|
9
|
+
'max-age': 3600, // Specifies the maximum age of the resource in seconds on a user's device.
|
|
10
|
+
's-maxage': 86400 * 7, // Specifies the maximum age of the resource in shared caches (e.g., CDNs e.g., CloudFront, Google Cloud CDN).
|
|
11
|
+
'public': true, // Indicates that the response can be cached by both public and private caches.
|
|
12
|
+
'private': false, // Indicates that the response is intended for a single user and should not be cached by CDNs, this should be false unless we're generating private information.
|
|
13
|
+
'no-cache': false, // This will still cache the resource, but it will be requested from the server each time its requested again. This should be false.
|
|
14
|
+
'no-store': false, // This is the same as above, except it will not cache the resource at all. (e.g. large files), this should be false.
|
|
15
|
+
'must-revalidate': false, // Requires the cache to revalidate the resource with the server before serving it to subsequent requests. Useful for frequently changing resources such as live data files.
|
|
16
|
+
'proxy-revalidate': false, // Similar to must-revalidate, but specifically applies to CDN caches.
|
|
17
|
+
'immutable': false // Indicates that the resource is considered immutable and should not change. Caches can store immutable resources indefinitely. Use wisely.
|
|
18
|
+
},
|
|
19
|
+
omit: [ // File & folder to ignore from deployment.
|
|
10
20
|
'deployConf.js'
|
|
11
21
|
],
|
|
12
22
|
data: {
|
|
13
|
-
key1: 'production1',
|
|
14
|
-
key2: 'production2'
|
|
23
|
+
key1: 'production1', // Replaces {{key1}} in any text-based files with what you put here
|
|
24
|
+
key2: 'production2' // Replaces {{key2}} in any text-based files with what you put here
|
|
15
25
|
}
|
|
16
26
|
},
|
|
17
27
|
test: {
|
|
18
|
-
|
|
19
|
-
cdnId: '
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
28
|
+
bucket: [ 's3-test.eip.telegraph.co.uk', 's3-test.eip.telegraph.co.uk' ], // The location you want to deploy to.
|
|
29
|
+
cdnId: 'cf', // The prefix or identifier for the CDN. e.g. "cf", "cf-particle-html", etc
|
|
30
|
+
environment: 'test', // The variant of environment.
|
|
31
|
+
folder: [ 'testFolder', 'testFolder2' ], // The remote folder you which to upload to.
|
|
32
|
+
targetFolder: 'test', // The local folder to use as a root for uploading. Remove this to use root folder.
|
|
33
|
+
cache: {
|
|
34
|
+
'max-age': 3600, // Specifies the maximum age of the resource in seconds on a user's device.
|
|
35
|
+
's-maxage': 86400 * 7, // Specifies the maximum age of the resource in shared caches (e.g., CDNs e.g., CloudFront, Google Cloud CDN).
|
|
36
|
+
'public': true, // Indicates that the response can be cached by both public and private caches.
|
|
37
|
+
'private': false, // Indicates that the response is intended for a single user and should not be cached by CDNs, this should be false unless we're generating private information.
|
|
38
|
+
'no-cache': false, // This will still cache the resource, but it will be requested from the server each time its requested again. This should be false.
|
|
39
|
+
'no-store': false, // This is the same as above, except it will not cache the resource at all. (e.g. large files), this should be false.
|
|
40
|
+
'must-revalidate': false, // Requires the cache to revalidate the resource with the server before serving it to subsequent requests. Useful for frequently changing resources such as live data files.
|
|
41
|
+
'proxy-revalidate': false, // Similar to must-revalidate, but specifically applies to CDN caches.
|
|
42
|
+
'immutable': false // Indicates that the resource is considered immutable and should not change. Caches can store immutable resources indefinitely. Use wisely.
|
|
43
|
+
},
|
|
44
|
+
omit: [ // File & folder to ignore from deployment.
|
|
23
45
|
'deployConf.js'
|
|
24
46
|
],
|
|
25
47
|
data: {
|
|
26
|
-
key1: '
|
|
27
|
-
key2: '
|
|
48
|
+
key1: 'production1', // Replaces {{key1}} in any text-based files with what you put here
|
|
49
|
+
key2: 'production2' // Replaces {{key2}} in any text-based files with what you put here
|
|
28
50
|
}
|
|
29
51
|
}
|
|
30
|
-
};
|
|
52
|
+
};
|
package/deployment.lock
CHANGED
|
@@ -6,11 +6,21 @@
|
|
|
6
6
|
"test/folder1/file2.txt": "d41d8cd98f00b204e9800998ecf8427e",
|
|
7
7
|
"test/folder2/file2.txt": "d41d8cd98f00b204e9800998ecf8427e",
|
|
8
8
|
"test/folder2/file1.txt": "d41d8cd98f00b204e9800998ecf8427e",
|
|
9
|
+
"/test/file1.txt": "f5b5ed0c72f4e9984c3e7a8b18c08c2b",
|
|
10
|
+
"/test/file2.txt": "d41d8cd98f00b204e9800998ecf8427e",
|
|
11
|
+
"/test/folder1/file1.txt": "5d41402abc4b2a76b9719d911017c592",
|
|
12
|
+
"/test/folder1/file2.txt": "d41d8cd98f00b204e9800998ecf8427e",
|
|
13
|
+
"/test/folder2/file1.txt": "d41d8cd98f00b204e9800998ecf8427e",
|
|
14
|
+
"/test/folder2/file2.txt": "d41d8cd98f00b204e9800998ecf8427e",
|
|
15
|
+
"/test/example.html": "d3ee67eb6e0f33d59c944fb4786359f3"
|
|
16
|
+
},
|
|
17
|
+
"prod": {
|
|
9
18
|
"/test/file1.txt": "f5b5ed0c72f4e9984c3e7a8b18c08c2b",
|
|
10
19
|
"/test/file2.txt": "d41d8cd98f00b204e9800998ecf8427e",
|
|
11
20
|
"/test/folder1/file1.txt": "d41d8cd98f00b204e9800998ecf8427e",
|
|
12
21
|
"/test/folder1/file2.txt": "d41d8cd98f00b204e9800998ecf8427e",
|
|
13
22
|
"/test/folder2/file1.txt": "d41d8cd98f00b204e9800998ecf8427e",
|
|
14
|
-
"/test/folder2/file2.txt": "d41d8cd98f00b204e9800998ecf8427e"
|
|
23
|
+
"/test/folder2/file2.txt": "d41d8cd98f00b204e9800998ecf8427e",
|
|
24
|
+
"/test/example.html": "d3ee67eb6e0f33d59c944fb4786359f3"
|
|
15
25
|
}
|
|
16
26
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eip-s3-deploy",
|
|
3
|
-
"version": "2.1
|
|
3
|
+
"version": "2.2.1",
|
|
4
4
|
"description": "Deploy static websites to S3 - all files will be public",
|
|
5
5
|
"main": "deploy.js",
|
|
6
6
|
"bin": {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
},
|
|
9
9
|
"preferGlobal": true,
|
|
10
10
|
"scripts": {
|
|
11
|
-
"test": "
|
|
11
|
+
"test": "node ./deploy.js test -f"
|
|
12
12
|
},
|
|
13
13
|
"repository": {
|
|
14
14
|
"type": "git",
|
|
@@ -24,13 +24,10 @@
|
|
|
24
24
|
"author": "Oliver Edgington <oliver.edgington@telegraph.co.uk>",
|
|
25
25
|
"license": "ISC",
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"aws-sdk": "^2.700.0",
|
|
28
27
|
"cli-progress": "^3.8.2",
|
|
28
|
+
"eip-cloud-services": "^1.0.16",
|
|
29
29
|
"handlebars": "^4.7.6",
|
|
30
|
-
"lodash": "^4.17.15",
|
|
31
|
-
"md5-file": "^5.0.0",
|
|
32
30
|
"mime": "^2.4.6",
|
|
33
|
-
"minimist": "^1.2.5",
|
|
34
31
|
"recursive-readdir": "^2.2.2"
|
|
35
32
|
}
|
|
36
33
|
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="UTF-8">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
+
<title>Code Block Example</title>
|
|
8
|
+
<style>
|
|
9
|
+
body {
|
|
10
|
+
display: flex;
|
|
11
|
+
justify-content: center;
|
|
12
|
+
align-items: center;
|
|
13
|
+
height: 100vh;
|
|
14
|
+
margin: 0;
|
|
15
|
+
padding: 0;
|
|
16
|
+
background-color: #2d2d2d;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.code-block {
|
|
20
|
+
background-color: #3f3f3f;
|
|
21
|
+
padding: 10px;
|
|
22
|
+
font-family: Consolas, monospace;
|
|
23
|
+
font-size: 14px;
|
|
24
|
+
line-height: 1.4;
|
|
25
|
+
overflow-x: auto;
|
|
26
|
+
border-radius: 4px;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/* PrismJS 1.29.0
|
|
30
|
+
https://prismjs.com/download.html#themes=prism-tomorrow&languages=markup+css+clike+javascript */
|
|
31
|
+
code[class*=language-],pre[class*=language-]{color:#ccc;background:0 0;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#2d2d2d}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.block-comment,.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#999}.token.punctuation{color:#ccc}.token.attr-name,.token.deleted,.token.namespace,.token.tag{color:#e2777a}.token.function-name{color:#6196cc}.token.boolean,.token.function,.token.number{color:#f08d49}.token.class-name,.token.constant,.token.property,.token.symbol{color:#f8c555}.token.atrule,.token.builtin,.token.important,.token.keyword,.token.selector{color:#cc99cd}.token.attr-value,.token.char,.token.regex,.token.string,.token.variable{color:#7ec699}.token.entity,.token.operator,.token.url{color:#67cdcc}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.token.inserted{color:green}
|
|
32
|
+
|
|
33
|
+
</style>
|
|
34
|
+
</head>
|
|
35
|
+
|
|
36
|
+
<body>
|
|
37
|
+
<div class="code-block language-js">
|
|
38
|
+
<pre>
|
|
39
|
+
<code>
|
|
40
|
+
module.exports = {
|
|
41
|
+
version: 2,
|
|
42
|
+
prod: {
|
|
43
|
+
bucket: 's3.eip.telegraph.co.uk', // The location you want to deploy to.
|
|
44
|
+
cdnId: 'cf', // The prefix or identifier for the CDN. e.g. "cf", "cf-particle-html", etc
|
|
45
|
+
folder: 'testFolder', // The remote folder you which to upload to.
|
|
46
|
+
targetFolder: 'test', // The local folder to use as a root for uploading. Remove this to use root folder.
|
|
47
|
+
cache: {
|
|
48
|
+
'max-age': 3600, // Specifies the maximum age of the resource in seconds on a user's device.
|
|
49
|
+
's-maxage': 86400 * 7, // Specifies the maximum age of the resource in shared caches (e.g., CDNs e.g., CloudFront, Google Cloud CDN).
|
|
50
|
+
'public': true, // Indicates that the response can be cached by both public and private caches.
|
|
51
|
+
'private': false, // Indicates that the response is intended for a single user and should not be cached by CDNs, this should be false unless we're generating private information.
|
|
52
|
+
'no-cache': false, // This will still cache the resource, but it will be requested from the server each time its requested again. This should be false.
|
|
53
|
+
'no-store': false, // This is the same as above, except it will not cache the resource at all. (e.g. large files), this should be false.
|
|
54
|
+
'must-revalidate': false, // Requires the cache to revalidate the resource with the server before serving it to subsequent requests. Useful for frequently changing resources such as live data files.
|
|
55
|
+
'proxy-revalidate': false, // Similar to must-revalidate, but specifically applies to CDN caches.
|
|
56
|
+
'immutable': false // Indicates that the resource is considered immutable and should not change. Caches can store immutable resources indefinitely. Use wisely.
|
|
57
|
+
},
|
|
58
|
+
omit: [ // File & folder to ignore from deployment.
|
|
59
|
+
'deployConf.js'
|
|
60
|
+
],
|
|
61
|
+
data: {
|
|
62
|
+
key1: 'production1', // Replaces {{key1}} in any text-based files with what you put here
|
|
63
|
+
key2: 'production2' // Replaces {{key2}} in any text-based files with what you put here
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
test: {
|
|
67
|
+
bucket: 's3-test.eip.telegraph.co.uk', // The location you want to deploy to.
|
|
68
|
+
cdnId: 'cf', // The prefix or identifier for the CDN. e.g. "cf", "cf-particle-html", etc
|
|
69
|
+
environment: 'test', // The variant of environment.
|
|
70
|
+
folder: 'testFolder', // The remote folder you which to upload to.
|
|
71
|
+
targetFolder: 'test', // The local folder to use as a root for uploading. Remove this to use root folder.
|
|
72
|
+
cache: {
|
|
73
|
+
'max-age': 3600, // Specifies the maximum age of the resource in seconds on a user's device.
|
|
74
|
+
's-maxage': 86400 * 7, // Specifies the maximum age of the resource in shared caches (e.g., CDNs e.g., CloudFront, Google Cloud CDN).
|
|
75
|
+
'public': true, // Indicates that the response can be cached by both public and private caches.
|
|
76
|
+
'private': false, // Indicates that the response is intended for a single user and should not be cached by CDNs, this should be false unless we're generating private information.
|
|
77
|
+
'no-cache': false, // This will still cache the resource, but it will be requested from the server each time its requested again. This should be false.
|
|
78
|
+
'no-store': false, // This is the same as above, except it will not cache the resource at all. (e.g. large files), this should be false.
|
|
79
|
+
'must-revalidate': false, // Requires the cache to revalidate the resource with the server before serving it to subsequent requests. Useful for frequently changing resources such as live data files.
|
|
80
|
+
'proxy-revalidate': false, // Similar to must-revalidate, but specifically applies to CDN caches.
|
|
81
|
+
'immutable': false // Indicates that the resource is considered immutable and should not change. Caches can store immutable resources indefinitely. Use wisely.
|
|
82
|
+
},
|
|
83
|
+
omit: [ // File & folder to ignore from deployment.
|
|
84
|
+
'deployConf.js'
|
|
85
|
+
],
|
|
86
|
+
data: {
|
|
87
|
+
key1: 'production1', // Replaces {{key1}} in any text-based files with what you put here
|
|
88
|
+
key2: 'production2' // Replaces {{key2}} in any text-based files with what you put here
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
</code>
|
|
93
|
+
</pre>
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<script>
|
|
97
|
+
/* PrismJS 1.29.0
|
|
98
|
+
https://prismjs.com/download.html#themes=prism-tomorrow&languages=markup+css+clike+javascript */
|
|
99
|
+
var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(e){var n=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,t=0,r={},a={manual:e.Prism&&e.Prism.manual,disableWorkerMessageHandler:e.Prism&&e.Prism.disableWorkerMessageHandler,util:{encode:function e(n){return n instanceof i?new i(n.type,e(n.content),n.alias):Array.isArray(n)?n.map(e):n.replace(/&/g,"&").replace(/</g,"<").replace(/\u00a0/g," ")},type:function(e){return Object.prototype.toString.call(e).slice(8,-1)},objId:function(e){return e.__id||Object.defineProperty(e,"__id",{value:++t}),e.__id},clone:function e(n,t){var r,i;switch(t=t||{},a.util.type(n)){case"Object":if(i=a.util.objId(n),t[i])return t[i];for(var l in r={},t[i]=r,n)n.hasOwnProperty(l)&&(r[l]=e(n[l],t));return r;case"Array":return i=a.util.objId(n),t[i]?t[i]:(r=[],t[i]=r,n.forEach((function(n,a){r[a]=e(n,t)})),r);default:return n}},getLanguage:function(e){for(;e;){var t=n.exec(e.className);if(t)return t[1].toLowerCase();e=e.parentElement}return"none"},setLanguage:function(e,t){e.className=e.className.replace(RegExp(n,"gi"),""),e.classList.add("language-"+t)},currentScript:function(){if("undefined"==typeof document)return null;if("currentScript"in document)return document.currentScript;try{throw new Error}catch(r){var e=(/at [^(\r\n]*\((.*):[^:]+:[^:]+\)$/i.exec(r.stack)||[])[1];if(e){var n=document.getElementsByTagName("script");for(var t in n)if(n[t].src==e)return n[t]}return null}},isActive:function(e,n,t){for(var r="no-"+n;e;){var a=e.classList;if(a.contains(n))return!0;if(a.contains(r))return!1;e=e.parentElement}return!!t}},languages:{plain:r,plaintext:r,text:r,txt:r,extend:function(e,n){var t=a.util.clone(a.languages[e]);for(var r in n)t[r]=n[r];return t},insertBefore:function(e,n,t,r){var i=(r=r||a.languages)[e],l={};for(var o in i)if(i.hasOwnProperty(o)){if(o==n)for(var s in t)t.hasOwnProperty(s)&&(l[s]=t[s]);t.hasOwnProperty(o)||(l[o]=i[o])}var u=r[e];return r[e]=l,a.languages.DFS(a.languages,(function(n,t){t===u&&n!=e&&(this[n]=l)})),l},DFS:function e(n,t,r,i){i=i||{};var l=a.util.objId;for(var o in n)if(n.hasOwnProperty(o)){t.call(n,o,n[o],r||o);var s=n[o],u=a.util.type(s);"Object"!==u||i[l(s)]?"Array"!==u||i[l(s)]||(i[l(s)]=!0,e(s,t,o,i)):(i[l(s)]=!0,e(s,t,null,i))}}},plugins:{},highlightAll:function(e,n){a.highlightAllUnder(document,e,n)},highlightAllUnder:function(e,n,t){var r={callback:t,container:e,selector:'code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code'};a.hooks.run("before-highlightall",r),r.elements=Array.prototype.slice.apply(r.container.querySelectorAll(r.selector)),a.hooks.run("before-all-elements-highlight",r);for(var i,l=0;i=r.elements[l++];)a.highlightElement(i,!0===n,r.callback)},highlightElement:function(n,t,r){var i=a.util.getLanguage(n),l=a.languages[i];a.util.setLanguage(n,i);var o=n.parentElement;o&&"pre"===o.nodeName.toLowerCase()&&a.util.setLanguage(o,i);var s={element:n,language:i,grammar:l,code:n.textContent};function u(e){s.highlightedCode=e,a.hooks.run("before-insert",s),s.element.innerHTML=s.highlightedCode,a.hooks.run("after-highlight",s),a.hooks.run("complete",s),r&&r.call(s.element)}if(a.hooks.run("before-sanity-check",s),(o=s.element.parentElement)&&"pre"===o.nodeName.toLowerCase()&&!o.hasAttribute("tabindex")&&o.setAttribute("tabindex","0"),!s.code)return a.hooks.run("complete",s),void(r&&r.call(s.element));if(a.hooks.run("before-highlight",s),s.grammar)if(t&&e.Worker){var c=new Worker(a.filename);c.onmessage=function(e){u(e.data)},c.postMessage(JSON.stringify({language:s.language,code:s.code,immediateClose:!0}))}else u(a.highlight(s.code,s.grammar,s.language));else u(a.util.encode(s.code))},highlight:function(e,n,t){var r={code:e,grammar:n,language:t};if(a.hooks.run("before-tokenize",r),!r.grammar)throw new Error('The language "'+r.language+'" has no grammar.');return r.tokens=a.tokenize(r.code,r.grammar),a.hooks.run("after-tokenize",r),i.stringify(a.util.encode(r.tokens),r.language)},tokenize:function(e,n){var t=n.rest;if(t){for(var r in t)n[r]=t[r];delete n.rest}var a=new s;return u(a,a.head,e),o(e,a,n,a.head,0),function(e){for(var n=[],t=e.head.next;t!==e.tail;)n.push(t.value),t=t.next;return n}(a)},hooks:{all:{},add:function(e,n){var t=a.hooks.all;t[e]=t[e]||[],t[e].push(n)},run:function(e,n){var t=a.hooks.all[e];if(t&&t.length)for(var r,i=0;r=t[i++];)r(n)}},Token:i};function i(e,n,t,r){this.type=e,this.content=n,this.alias=t,this.length=0|(r||"").length}function l(e,n,t,r){e.lastIndex=n;var a=e.exec(t);if(a&&r&&a[1]){var i=a[1].length;a.index+=i,a[0]=a[0].slice(i)}return a}function o(e,n,t,r,s,g){for(var f in t)if(t.hasOwnProperty(f)&&t[f]){var h=t[f];h=Array.isArray(h)?h:[h];for(var d=0;d<h.length;++d){if(g&&g.cause==f+","+d)return;var v=h[d],p=v.inside,m=!!v.lookbehind,y=!!v.greedy,k=v.alias;if(y&&!v.pattern.global){var x=v.pattern.toString().match(/[imsuy]*$/)[0];v.pattern=RegExp(v.pattern.source,x+"g")}for(var b=v.pattern||v,w=r.next,A=s;w!==n.tail&&!(g&&A>=g.reach);A+=w.value.length,w=w.next){var E=w.value;if(n.length>e.length)return;if(!(E instanceof i)){var P,L=1;if(y){if(!(P=l(b,A,e,m))||P.index>=e.length)break;var S=P.index,O=P.index+P[0].length,j=A;for(j+=w.value.length;S>=j;)j+=(w=w.next).value.length;if(A=j-=w.value.length,w.value instanceof i)continue;for(var C=w;C!==n.tail&&(j<O||"string"==typeof C.value);C=C.next)L++,j+=C.value.length;L--,E=e.slice(A,j),P.index-=A}else if(!(P=l(b,0,E,m)))continue;S=P.index;var N=P[0],_=E.slice(0,S),M=E.slice(S+N.length),W=A+E.length;g&&W>g.reach&&(g.reach=W);var z=w.prev;if(_&&(z=u(n,z,_),A+=_.length),c(n,z,L),w=u(n,z,new i(f,p?a.tokenize(N,p):N,k,N)),M&&u(n,w,M),L>1){var I={cause:f+","+d,reach:W};o(e,n,t,w.prev,A,I),g&&I.reach>g.reach&&(g.reach=I.reach)}}}}}}function s(){var e={value:null,prev:null,next:null},n={value:null,prev:e,next:null};e.next=n,this.head=e,this.tail=n,this.length=0}function u(e,n,t){var r=n.next,a={value:t,prev:n,next:r};return n.next=a,r.prev=a,e.length++,a}function c(e,n,t){for(var r=n.next,a=0;a<t&&r!==e.tail;a++)r=r.next;n.next=r,r.prev=n,e.length-=a}if(e.Prism=a,i.stringify=function e(n,t){if("string"==typeof n)return n;if(Array.isArray(n)){var r="";return n.forEach((function(n){r+=e(n,t)})),r}var i={type:n.type,content:e(n.content,t),tag:"span",classes:["token",n.type],attributes:{},language:t},l=n.alias;l&&(Array.isArray(l)?Array.prototype.push.apply(i.classes,l):i.classes.push(l)),a.hooks.run("wrap",i);var o="";for(var s in i.attributes)o+=" "+s+'="'+(i.attributes[s]||"").replace(/"/g,""")+'"';return"<"+i.tag+' class="'+i.classes.join(" ")+'"'+o+">"+i.content+"</"+i.tag+">"},!e.document)return e.addEventListener?(a.disableWorkerMessageHandler||e.addEventListener("message",(function(n){var t=JSON.parse(n.data),r=t.language,i=t.code,l=t.immediateClose;e.postMessage(a.highlight(i,a.languages[r],r)),l&&e.close()}),!1),a):a;var g=a.util.currentScript();function f(){a.manual||a.highlightAll()}if(g&&(a.filename=g.src,g.hasAttribute("data-manual")&&(a.manual=!0)),!a.manual){var h=document.readyState;"loading"===h||"interactive"===h&&g&&g.defer?document.addEventListener("DOMContentLoaded",f):window.requestAnimationFrame?window.requestAnimationFrame(f):window.setTimeout(f,16)}return a}(_self);"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism);
|
|
100
|
+
Prism.languages.markup={comment:{pattern:/<!--(?:(?!<!--)[\s\S])*?-->/,greedy:!0},prolog:{pattern:/<\?[\s\S]+?\?>/,greedy:!0},doctype:{pattern:/<!DOCTYPE(?:[^>"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|<!--(?:[^-]|-(?!->))*-->)*\]\s*)?>/i,greedy:!0,inside:{"internal-subset":{pattern:/(^[^\[]*\[)[\s\S]+(?=\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},punctuation:/^<!|>$|[[\]]/,"doctype-tag":/^DOCTYPE/i,name:/[^\s<>'"]+/}},cdata:{pattern:/<!\[CDATA\[[\s\S]*?\]\]>/i,greedy:!0},tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"special-attr":[],"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:"attr-equals"},{pattern:/^(\s*)["']|["']$/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:[{pattern:/&[\da-z]{1,8};/i,alias:"named-entity"},/&#x?[\da-f]{1,8};/i]},Prism.languages.markup.tag.inside["attr-value"].inside.entity=Prism.languages.markup.entity,Prism.languages.markup.doctype.inside["internal-subset"].inside=Prism.languages.markup,Prism.hooks.add("wrap",(function(a){"entity"===a.type&&(a.attributes.title=a.content.replace(/&/,"&"))})),Object.defineProperty(Prism.languages.markup.tag,"addInlined",{value:function(a,e){var s={};s["language-"+e]={pattern:/(^<!\[CDATA\[)[\s\S]+?(?=\]\]>$)/i,lookbehind:!0,inside:Prism.languages[e]},s.cdata=/^<!\[CDATA\[|\]\]>$/i;var t={"included-cdata":{pattern:/<!\[CDATA\[[\s\S]*?\]\]>/i,inside:s}};t["language-"+e]={pattern:/[\s\S]+/,inside:Prism.languages[e]};var n={};n[a]={pattern:RegExp("(<__[^>]*>)(?:<!\\[CDATA\\[(?:[^\\]]|\\](?!\\]>))*\\]\\]>|(?!<!\\[CDATA\\[)[^])*?(?=</__>)".replace(/__/g,(function(){return a})),"i"),lookbehind:!0,greedy:!0,inside:t},Prism.languages.insertBefore("markup","cdata",n)}}),Object.defineProperty(Prism.languages.markup.tag,"addAttribute",{value:function(a,e){Prism.languages.markup.tag.inside["special-attr"].push({pattern:RegExp("(^|[\"'\\s])(?:"+a+")\\s*=\\s*(?:\"[^\"]*\"|'[^']*'|[^\\s'\">=]+(?=[\\s>]))","i"),lookbehind:!0,inside:{"attr-name":/^[^\s=]+/,"attr-value":{pattern:/=[\s\S]+/,inside:{value:{pattern:/(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,lookbehind:!0,alias:[e,"language-"+e],inside:Prism.languages[e]},punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}}}})}}),Prism.languages.html=Prism.languages.markup,Prism.languages.mathml=Prism.languages.markup,Prism.languages.svg=Prism.languages.markup,Prism.languages.xml=Prism.languages.extend("markup",{}),Prism.languages.ssml=Prism.languages.xml,Prism.languages.atom=Prism.languages.xml,Prism.languages.rss=Prism.languages.xml;
|
|
101
|
+
!function(s){var e=/(?:"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n])*')/;s.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:RegExp("@[\\w-](?:[^;{\\s\"']|\\s+(?!\\s)|"+e.source+")*?(?:;|(?=\\s*\\{))"),inside:{rule:/^@[\w-]+/,"selector-function-argument":{pattern:/(\bselector\s*\(\s*(?![\s)]))(?:[^()\s]|\s+(?![\s)])|\((?:[^()]|\([^()]*\))*\))+(?=\s*\))/,lookbehind:!0,alias:"selector"},keyword:{pattern:/(^|[^\w-])(?:and|not|only|or)(?![\w-])/,lookbehind:!0}}},url:{pattern:RegExp("\\burl\\((?:"+e.source+"|(?:[^\\\\\r\n()\"']|\\\\[^])*)\\)","i"),greedy:!0,inside:{function:/^url/i,punctuation:/^\(|\)$/,string:{pattern:RegExp("^"+e.source+"$"),alias:"url"}}},selector:{pattern:RegExp("(^|[{}\\s])[^{}\\s](?:[^{};\"'\\s]|\\s+(?![\\s{])|"+e.source+")*(?=\\s*\\{)"),lookbehind:!0},string:{pattern:e,greedy:!0},property:{pattern:/(^|[^-\w\xA0-\uFFFF])(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*(?=\s*:)/i,lookbehind:!0},important:/!important\b/i,function:{pattern:/(^|[^-a-z0-9])[-a-z0-9]+(?=\()/i,lookbehind:!0},punctuation:/[(){};:,]/},s.languages.css.atrule.inside.rest=s.languages.css;var t=s.languages.markup;t&&(t.tag.addInlined("style","css"),t.tag.addAttribute("style","css"))}(Prism);
|
|
102
|
+
Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|trait)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:break|catch|continue|do|else|finally|for|function|if|in|instanceof|new|null|return|throw|try|while)\b/,boolean:/\b(?:false|true)\b/,function:/\b\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/};
|
|
103
|
+
Prism.languages.javascript=Prism.languages.extend("clike",{"class-name":[Prism.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/,lookbehind:!0}],keyword:[{pattern:/((?:^|\})\s*)catch\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],function:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,number:{pattern:RegExp("(^|[^\\w$])(?:NaN|Infinity|0[bB][01]+(?:_[01]+)*n?|0[oO][0-7]+(?:_[0-7]+)*n?|0[xX][\\dA-Fa-f]+(?:_[\\dA-Fa-f]+)*n?|\\d+(?:_\\d+)*n|(?:\\d+(?:_\\d+)*(?:\\.(?:\\d+(?:_\\d+)*)?)?|\\.\\d+(?:_\\d+)*)(?:[Ee][+-]?\\d+(?:_\\d+)*)?)(?![\\w$])"),lookbehind:!0},operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),Prism.languages.javascript["class-name"][0].pattern=/(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/,Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:RegExp("((?:^|[^$\\w\\xA0-\\uFFFF.\"'\\])\\s]|\\b(?:return|yield))\\s*)/(?:(?:\\[(?:[^\\]\\\\\r\n]|\\\\.)*\\]|\\\\.|[^/\\\\\\[\r\n])+/[dgimyus]{0,7}|(?:\\[(?:[^[\\]\\\\\r\n]|\\\\.|\\[(?:[^[\\]\\\\\r\n]|\\\\.|\\[(?:[^[\\]\\\\\r\n]|\\\\.)*\\])*\\])*\\]|\\\\.|[^/\\\\\\[\r\n])+/[dgimyus]{0,7}v[dgimyus]{0,7})(?=(?:\\s|/\\*(?:[^*]|\\*(?!/))*\\*/)*(?:$|[\r\n,.;:})\\]]|//))"),lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:Prism.languages.regex},"regex-delimiter":/^\/|\/$/,"regex-flags":/^[a-z]+$/}},"function-variable":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,lookbehind:!0,inside:Prism.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,lookbehind:!0,inside:Prism.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),Prism.languages.insertBefore("javascript","string",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:"comment"},"template-string":{pattern:/`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:Prism.languages.javascript}},string:/[\s\S]+/}},"string-property":{pattern:/((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,lookbehind:!0,greedy:!0,alias:"property"}}),Prism.languages.insertBefore("javascript","operator",{"literal-property":{pattern:/((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,lookbehind:!0,alias:"property"}}),Prism.languages.markup&&(Prism.languages.markup.tag.addInlined("script","javascript"),Prism.languages.markup.tag.addAttribute("on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)","javascript")),Prism.languages.js=Prism.languages.javascript;
|
|
104
|
+
|
|
105
|
+
</script>
|
|
106
|
+
<script>
|
|
107
|
+
// Function to highlight code using Prism.js library
|
|
108
|
+
function highlightCode() {
|
|
109
|
+
Prism.highlightAllUnder(document.querySelector(".code-block"));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Call the highlightCode function when the page loads
|
|
113
|
+
window.addEventListener("DOMContentLoaded", highlightCode);
|
|
114
|
+
</script>
|
|
115
|
+
</body>
|
|
116
|
+
|
|
117
|
+
</html>
|
package/test/folder1/file1.txt
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
hello
|
package/test/deployConf.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
prod: {
|
|
3
|
-
Bucket: 's3.eip.telegraph.co.uk',
|
|
4
|
-
Folder: 'testFolder',
|
|
5
|
-
targetFolder: 'test',
|
|
6
|
-
Omit: [
|
|
7
|
-
'deployConf.js'
|
|
8
|
-
],
|
|
9
|
-
data: {
|
|
10
|
-
key1: "production1",
|
|
11
|
-
key2: "production2"
|
|
12
|
-
}
|
|
13
|
-
},
|
|
14
|
-
staging: {
|
|
15
|
-
Bucket: 's3-staging.eip.telegraph.co.uk',
|
|
16
|
-
cdnId: 'E2YWDG5C5WR01M',
|
|
17
|
-
Folder: 'testFolder',
|
|
18
|
-
Omit: [
|
|
19
|
-
'deployConf.js'
|
|
20
|
-
],
|
|
21
|
-
data: {
|
|
22
|
-
key1: "staging1",
|
|
23
|
-
key2: "staging2"
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
};
|
package/test/deployment.lock
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"staging": {
|
|
3
|
-
"/Users/ollie/workspace/olivers-tools/eip-s3-deploy/test/file1.txt": "f5b5ed0c72f4e9984c3e7a8b18c08c2b",
|
|
4
|
-
"/Users/ollie/workspace/olivers-tools/eip-s3-deploy/test/file2.txt": "d41d8cd98f00b204e9800998ecf8427e",
|
|
5
|
-
"/Users/ollie/workspace/olivers-tools/eip-s3-deploy/test/folder1/file1.txt": "d41d8cd98f00b204e9800998ecf8427e",
|
|
6
|
-
"/Users/ollie/workspace/olivers-tools/eip-s3-deploy/test/folder1/file2.txt": "d41d8cd98f00b204e9800998ecf8427e",
|
|
7
|
-
"/Users/ollie/workspace/olivers-tools/eip-s3-deploy/test/folder2/file1.txt": "d41d8cd98f00b204e9800998ecf8427e",
|
|
8
|
-
"/Users/ollie/workspace/olivers-tools/eip-s3-deploy/test/folder2/file2.txt": "d41d8cd98f00b204e9800998ecf8427e"
|
|
9
|
-
}
|
|
10
|
-
}
|