@suffolklitlab/alkiln 5.12.0 → 5.13.0-logs2

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.
Files changed (38) hide show
  1. package/.env.example +29 -0
  2. package/.github/workflows/github_server.yml +59 -12
  3. package/.github/workflows/playground.yml +11 -4
  4. package/CHANGELOG.md +41 -1
  5. package/CONTRIBUTING.md +98 -83
  6. package/README.md +1 -1
  7. package/action.yml +69 -29
  8. package/action_for_github_server/action.yml +65 -31
  9. package/docs/decisions/006_which_log_level_to_pick_2024_09_5.md +46 -0
  10. package/docs/decisions/adopt_architectural_decision_records.md +58 -0
  11. package/docs/decisions/detect_sign_in.md +124 -0
  12. package/docs/decisions/pdfjs_version_2024_08_08.md +18 -0
  13. package/docs/decisions/reports_and_logs_2024_08_11.md +141 -0
  14. package/docs/decisions/update_puppeteer_2024_07_28.md +62 -0
  15. package/docs/story_tables.md +1 -1
  16. package/lib/docassemble/docassemble_api_REST.js +67 -1
  17. package/lib/docassemble/docassemble_api_interface.js +225 -18
  18. package/lib/docassemble/server_install.js +23 -9
  19. package/lib/docassemble/setup.js +33 -20
  20. package/lib/docassemble/takedown.js +27 -7
  21. package/lib/run_cucumber.js +83 -33
  22. package/lib/scope.js +222 -82
  23. package/lib/steps.js +206 -62
  24. package/lib/utils/files.js +8 -6
  25. package/lib/utils/log.js +477 -84
  26. package/lib/utils/reports.js +14 -5
  27. package/lib/utils/session_vars.js +25 -18
  28. package/lib/utils/time.js +44 -0
  29. package/npm-shrinkwrap.json +635 -193
  30. package/package.json +8 -8
  31. package/setup.py +1 -1
  32. package/tests/log_codes/check_codes.sh +163 -108
  33. package/tests/log_codes/log_code_expected_instances.txt +213 -0
  34. package/tests/unit_tests/getAllFields.test.js +5 -0
  35. package/tests/unit_tests/getMatchingRows.test.js +5 -0
  36. package/tests/unit_tests/log.test.js +20 -0
  37. package/tests/unit_tests/normalizeTable.test.js +5 -0
  38. package/tests/log_codes/deleted_codes.json +0 -8
@@ -1,5 +1,5 @@
1
1
  const _da_REST = require('./docassemble_api_REST');
2
- const log = require('../utils/log');
2
+ const Log = require('../utils/log');
3
3
  const session_vars = require('../utils/session_vars' );
4
4
  const time = require('../utils/time');
5
5
 
@@ -15,6 +15,9 @@ const time = require('../utils/time');
15
15
  // to specific functions, which can run in a very small
16
16
  // window.
17
17
 
18
+ // TODO: Move get_dev_id() and such into here instead of in
19
+ // docassemble_REST - args should be the same vals as the request expects
20
+
18
21
  /** Notes:
19
22
  * - Checking for a busy server is vulnerable to race conditions, but
20
23
  * there's nothing we can do about that.
@@ -58,6 +61,10 @@ const time = require('../utils/time');
58
61
  let da_i = {};
59
62
  module.exports = da_i;
60
63
 
64
+ const artifacts_path = session_vars.get_artifacts_path_name();
65
+ const log = new Log({ path: artifacts_path, context: `api` });
66
+ const context = `da api`;
67
+
61
68
 
62
69
  // Constants
63
70
  const DEFAULT_MAX_REQUEST_MS = session_vars.get_setup_timeout_ms();
@@ -89,7 +96,9 @@ da_i.throw_an_error_if_server_is_not_responding = async function ( options ) {
89
96
 
90
97
  // Defaults
91
98
  let { timeout } = options || { timeout: DEFAULT_MAX_REQUEST_MS };
92
- log.info({ type: `da api`, code: `ALK0166`, pre: `Waiting for the docassemble server to respond. Will wait for ${ timeout/1000 } seconds at most.` });
99
+ log.info({ code: `ALK0166`, context: `da api`, },
100
+ `Waiting for the docassemble server to respond. Will wait for ${ timeout/1000 } seconds at most.`
101
+ );
93
102
 
94
103
  try {
95
104
  let start_time = Date.now()
@@ -97,7 +106,9 @@ da_i.throw_an_error_if_server_is_not_responding = async function ( options ) {
97
106
  let response = await _da_REST.get_dev_id( timeout );
98
107
  let end_time = Date.now()
99
108
  if ( response && response.data && response.data.id ) {
100
- log.info({ type: `da api`, code: `ALK0167`, pre: `The docassemble server responded after ${ (end_time - start_time)/1000 } seconds.` });
109
+ log.info({ code: `ALK0167`, context: `da api`, },
110
+ `The docassemble server responded after ${ (end_time - start_time)/1000 } seconds.`
111
+ );
101
112
  return false;
102
113
  }
103
114
 
@@ -106,17 +117,16 @@ da_i.throw_an_error_if_server_is_not_responding = async function ( options ) {
106
117
  if ( is_timeout_error( error ) ) {
107
118
  // Anything that's waiting for the server to not be busy does not want
108
119
  // to move forward without a free server.
109
- log.info({ type: `da api`, code: `ALK0168`, pre: `The docassemble server was still busy after ${ timeout/1000 } seconds.` });
120
+ log.info({ code: `ALK0168`, context: `da api`, },
121
+ `The docassemble server was still busy after ${ timeout/1000 } seconds.`
122
+ );
110
123
  } else {
111
124
  // Not sure what to do for other errors. It could be a server
112
125
  // error, like an 'Access Denied' error, which should be handled.
113
- log.error({
114
- type: `da api`,
115
- code: `ALK0169`,
116
- pre: `Tried to check if the docassemble server was busy, but `
117
- + `ran into an unexpected error. The error didn't indicate a busy server, though. Error message:\n`
118
- + error
119
- });
126
+ log.warning({ code: `ALK0169`, context: `da api` },
127
+ `Checked if the server was busy and ran into a different error`,
128
+ error
129
+ );
120
130
  }
121
131
 
122
132
  throw error;
@@ -174,25 +184,222 @@ da_i.get_dev_id = async function ( dev_id_options ) {
174
184
  return dev_id;
175
185
  }; // Ends da_i.get_dev_id();
176
186
 
187
+ da_i.get_api_key_access_status = async function ({ timeout = null, api_key = null }) {
188
+ /** Check if the API key has minimal authorization on the server.
189
+ * See https://docassemble.org/docs/api.html#api_user_api_get.
190
+ *
191
+ * @param options {obj} Options for getting authorization of the API key
192
+ * @param options.api_key {str | null} Required. The API key to test.
193
+ * @param options.timeout {int | null} Optional. Max MS
194
+ * to allow for server request.
195
+ */
196
+
197
+ let auth_data = {
198
+ codes: [ 1 ],
199
+ has_auth: null,
200
+ messages: [`API key authorization is not yet determined.`],
201
+ error: null
202
+ }
203
+
204
+ // Skip this for tests run from ALKilnInThePlayground
205
+ if ( session_vars.get_origin() === 'playground' ) {
206
+ auth_data.codes.push( 2 );
207
+ auth_data.messages.push( `API keys are unnecessary for tests run in the Playground.` );
208
+ }
209
+
210
+ if ( typeof api_key !== `string` ) {
211
+ auth_data.codes.push( 3 );
212
+ auth_data.messages.push( `The given API key is not a string. Type: ${ typeof api_key }`);
213
+ }
214
+
215
+ if ( auth_data.codes.length === 1 ) {
216
+ // Default timeouts
217
+ timeout = timeout || DEFAULT_MAX_REQUEST_MS;
218
+ try {
219
+ let response = await _da_REST.get_api_key_info( timeout, {
220
+ api_key,
221
+ server_url: session_vars.get_da_server_url(),
222
+ });
223
+ auth_data.has_auth = true;
224
+ auth_data.codes = [ 6 ];
225
+ auth_data.messages = [ `This API key has at least basic authorization on this server.` ];
226
+
227
+ } catch ( error ) {
228
+ auth_data.error = error;
229
+ if ( error.status === 403 ) {
230
+ auth_data.has_auth = false;
231
+ auth_data.codes = [ 4 ];
232
+ auth_data.messages = [ `This API is unverified on this server. It is probably missing or has been deleted.` ];
233
+ } else {
234
+ // Note on 404, non-existent key: 404 == 403 - Since we're using the
235
+ // key itself to get its own info, if it doesn't exist, we'll get 403
236
+ auth_data.codes = [ 5 ];
237
+ auth_data.messages = [ `This API key threw a non-403 error.` ];
238
+ } // ends error status
239
+ } // ends try
240
+ } // ends auth_data.codes only has the first code
241
+
242
+ // log.debug({ type: `da api info`, code: `ALKx0202`,
243
+ // pre: `API key authentication info`, data: auth_data });
244
+ log.debug({ code: `ALK0202`, context: `da api`, },
245
+ `API key authentication info`,
246
+ auth_data
247
+ );
248
+
249
+ return auth_data;
250
+ }; // Ends da_i.get_api_key_access_status()
251
+
177
252
  // node -e 'require("./lib/docassemble/docassemble_api_interface.js").delete_project( "create1" )'
178
253
  da_i.delete_project = async function ( delete_options ) {
179
254
  /** Delete a project on the user's da server. Do we need to watch
180
255
  * for a timeout? */
181
256
 
182
257
  // Defaults
183
- let { timeout } = delete_options || { timeout: DEFAULT_MAX_REQUEST_MS };
258
+ let { timeout } = delete_options || { timeout: DEFAULT_MAX_REQUEST_MS };
184
259
 
185
260
  let project_name = session_vars.get_project_name()
186
- log.info({ type: `da api`, code: `ALK0170`, pre: `Trying to delete Project ${ project_name }` });
261
+ log.info({ code: `ALK0170`, context: `da api`, },
262
+ `Trying to delete Project ${ project_name }`
263
+ );
187
264
 
188
265
  try {
189
266
  let response = await _da_REST.delete_project( timeout );
190
- log.info({ type: `da api`, code: `ALK0171`, pre: `Deleted Project "${ project_name }"` });
267
+ log.info({ code: `ALK0171`, context: `da api`, },
268
+ `Deleted Project "${ project_name }"`
269
+ );
191
270
  } catch ( error ) {
192
- let msg = `Ran into an error when deleting Project "${ project_name }". See https://docassemble.org/docs/api.html#playground_delete_project.`
193
- log.error({ type: `da api`, code: `ALK0172`, pre: msg });
194
- log.debug({ type: `da api`, code: `ALK0173`, pre: msg });
195
- throw error;
271
+ log.throw({ code: `ALK0172`, context: `da api`, error: error },
272
+ `Ran into an error when deleting Project "${ project_name }". See https://docassemble.org/docs/api.html#playground_delete_project.`
273
+ );
274
+ // throw error;
196
275
  }
197
276
 
198
277
  }; // Ends da_i.delete_project()
278
+
279
+ // node -e 'require("./lib/docassemble/docassemble_api_interface.js").quietly_try_to_delete_interviews(...)'
280
+ da_i.quietly_try_to_delete_interviews = async function ({ interviews = {}, api_keys = [], options = {} }) {
281
+ /** Try to delete the given interviews. Fail silently. Example failure: the test
282
+ * did not log in any users and the testing account's role prevents
283
+ * access to other user's interviews. Do we need to watch for a timeout? */
284
+
285
+ // Defaults
286
+ let { timeout } = options || { timeout: DEFAULT_MAX_REQUEST_MS };
287
+
288
+ let responses = [];
289
+ for ( let id in interviews ) {
290
+ let interview = interviews[ id ];
291
+ if ( interview && interview.uid ) {
292
+ let sub_responses = await da_i.quietly_delete_one_interview( timeout, {
293
+ interview,
294
+ api_keys
295
+ });
296
+ responses.push( sub_responses );
297
+ } else {
298
+ log.debug({ code: `ALK0191`, context: `da api` },
299
+ `There is no interview to delete.`
300
+ );
301
+ }
302
+ }
303
+ return responses;
304
+ }; // Ends da_i.quietly_try_to_delete_interviews()
305
+
306
+ // node -e 'require("./lib/docassemble/docassemble_api_interface.js").quietly_delete_one_interview(...)'
307
+ da_i.quietly_delete_one_interview = async function ( timeout, { interview = {}, api_keys = [] }) {
308
+ /** Try to delete an interview using all available API keys. Fail silently.
309
+ * Example failure: the test did not log in any users and the testing
310
+ * account's role prevents access to other user's interviews. */
311
+
312
+ let responses = [];
313
+ for ( let api_key of api_keys ) {
314
+ try {
315
+ log.debug({ code: `ALK0188`, context: `da api` },
316
+ `Trying to delete the interview with the id ${ interview.uid } and an API key.`
317
+ );
318
+
319
+ // Actual work:
320
+ let response = await _da_REST.delete_interview( timeout, {
321
+ session: interview.uid,
322
+ api_key
323
+ });
324
+ responses.push( response );
325
+
326
+ log.debug({ code: `ALK0189`, context: `da api` },
327
+ `No error when trying to delete interview ${ interview.uid } at ${ interview.i }.`
328
+ );
329
+ } catch ( error ) {
330
+ error.interview = interview;
331
+ log.debug({ code: `ALK0190`, context: `da api` },
332
+ `Error when deleting the interview. See https://docassemble.org/docs/api.html#session_delete.`,
333
+ error
334
+ );
335
+ }
336
+ } // ends for api keys
337
+ return responses;
338
+ }; // Ends da_i.quietly_delete_one_interview()
339
+
340
+ // node -e 'require("./lib/docassemble/docassemble_api_interface.js").get_interviews(...)'
341
+ da_i.get_interviews = async function ({ interviews = {}, api_keys = [], options = {} }) {
342
+ /** Return an object of interviews that exist on the server
343
+ * that match the given interviews. Keys are the session ids. */
344
+
345
+ let fetched_interviews = {};
346
+ if ( interviews ) {
347
+ for ( let id in interviews ) {
348
+ let interview = interviews[ id ];
349
+ let fetched = await da_i.get_interview({ interview, api_keys, options });
350
+ if ( fetched && fetched.session ) {
351
+ fetched_interviews[ fetched.session ] = fetched;
352
+ }
353
+ }
354
+ } // ends if interviews
355
+
356
+ return fetched_interviews;
357
+ }; // Ends da_i.get_interviews()
358
+
359
+ // node -e 'require("./lib/docassemble/docassemble_api_interface.js").get_interview(...)'
360
+ da_i.get_interview = async function ({ interview = {}, api_keys = [], options = {} }) {
361
+ /** Try to get an interview from the server that matches the given interview data. */
362
+
363
+ // Defaults
364
+ let { timeout } = options || { timeout: DEFAULT_MAX_REQUEST_MS };
365
+
366
+ if ( interview && interview.uid ) {
367
+ for ( let api_key of api_keys ) {
368
+ try {
369
+ log.debug({ code: `ALK0192`, context: `da api` },
370
+ `Trying to get the interview with the id ${ interview.uid } and an API key.`
371
+ );
372
+
373
+ // Actual work:
374
+ let response = await _da_REST.get_interview( timeout, {
375
+ session: interview.uid,
376
+ api_key
377
+ });
378
+
379
+ if ( response && response.data && response.data.items && response.data.items[0] && response.data.items[0].session ) {
380
+ log.debug({ code: `ALK0193`, context: `da api` },
381
+ `Status ${ response.status }. Got ${ response.data.items[0].session }.`
382
+ );
383
+ return response.data.items[0];
384
+ } else {
385
+ log.debug({ code: `ALK0194`, context: `da api` },
386
+ `Status ${ response.status }. Cannot find ${ interview.uid }`
387
+ );
388
+ }
389
+
390
+ } catch ( error ) {
391
+ error.interview = interview;
392
+ log.debug({ code: `ALK0195`, context: `da api` },
393
+ `${ error.status || `No error.status.` } ${ error.statusText || error.data || `No error.statusText.` } Ran into an error when getting the interview ${ interview.uid }. See https://docassemble.org/docs/api.html#interviews.`,
394
+ error
395
+ );
396
+ }
397
+ } // ends for api_keys
398
+ } else {
399
+ log.debug({ code: `ALK0196`, context: `da api` },
400
+ `There is no interview to get.`
401
+ );
402
+ }
403
+
404
+ return null;
405
+ }; // Ends da_i.get_interview()
@@ -1,10 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  const session_vars = require('../utils/session_vars');
4
- const log = require('../utils/log');
4
+ const Log = require('../utils/log');
5
5
  const fg = require('fast-glob');
6
6
 
7
7
  const server_install = async () => {
8
+ /** Finds out where in this directory the package's folders are. */
8
9
  session_vars.save_install_method(`server`);
9
10
 
10
11
  // Get path to folders inside repo main "docassemble" folder
@@ -23,20 +24,33 @@ const server_install = async () => {
23
24
  // Get names of all folders in repo "docassemble" folder
24
25
  let chosen_folder = folders[0];
25
26
  if ( folders.length === 0 ) {
26
- let msg = `There are 0 folders at ${path}. Stopping tests.`;
27
- log.error({ type: `setup`, code: `ALK0044`, data: msg })
28
- throw new ReferenceError( msg );
27
+ let msg = `ALKiln cannot find the package's files. There are 0 folders at "${ path }". Stopping tests.`;
28
+ log.throw({ code: `ALK0044`, context: `server_install`,
29
+ error: new ReferenceError( msg ); // no logs
30
+ });
29
31
  }
30
32
 
31
33
  if ( folders.length > 1 ) {
32
- log.warn({
33
- type: `setup`,
34
- code: `ALK0045`,
35
- data: `There are ${folders.length} folders at ${path}: "${folders.join('", "')}". ALKiln chose to use the folder "${chosen_folder}".`
36
- })
34
+ log.warn({ code: `ALK0045`, context: `setup`, },
35
+ `There are ${ folders.length } folders at "${ path }": "${ folders.join('", "') }". ALKiln chose to use the folder "${ chosen_folder }".`
36
+ );
37
+ } else {
38
+ log.info({ code: `ALK0218`, context: `setup`, },
39
+ `ALKiln found the package folder at "${ chosen_folder }".`
40
+ );
37
41
  }
38
42
 
39
43
  session_vars.save_repo_folder_name( chosen_folder );
40
44
  }; // Ends server_install();
41
45
 
46
+ // 1. session_vars - make sure all the required env vars exist
47
+ session_vars.validateEnvironment();
48
+
49
+ // 2. Prepare logs
50
+ // Local devs: This will be separate from your test paths
51
+ // unless you pass a `path` argument into the cucumber run command
52
+ const argv = require(`minimist`)(process.argv.slice(2));
53
+ const log = new Log({ path: argv.path, context: `server_install` });
54
+
55
+ // 3. run
42
56
  server_install();
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- // TODO: Rename to playground_install?
4
-
5
3
  const child_process = require(`child_process`);
6
4
 
7
- const log = require(`../utils/log`);
5
+ const Log = require(`../utils/log`);
8
6
  const session_vars = require(`../utils/session_vars` );
7
+ const files = require(`../utils/files` );
8
+ const time = require(`../utils/time` );
9
9
 
10
10
  const setup = async () => {
11
11
  /** In the docassemble testing account identified by the
@@ -18,9 +18,14 @@ const setup = async () => {
18
18
  let project_name = session_vars.get_project_base_name().replace(/[^A-Za-z0-9]/g, '') + `${Date.now()}`;
19
19
  session_vars.save_project_name( project_name );
20
20
 
21
- log.info({ type: `setup`, code: `ALK0046`, pre: `Starting to upload this package to `
21
+ const starting_msg = `Starting to upload this package to `
22
22
  + `a new Project called ${project_name} in the Playground `
23
- + `of your server's testing account. It may take a couple of minutes.` });
23
+ + `of your server's testing account. It may take a couple of minutes.`;
24
+ log.info({ code: `ALK0046`, context: `setup` },
25
+ starting_msg
26
+ );
27
+
28
+ // TODO: 1. Start listening to the normal console here (for debug logging)
24
29
 
25
30
  // dainstall . --apiurl https://site.org --apikey $API_KEY --playground --project=name
26
31
  // I think this won't run on windows? Why do I think this? Where on the
@@ -34,29 +39,37 @@ const setup = async () => {
34
39
  `--playground`, `--project=${ project_name }`
35
40
  ],
36
41
  // Prints messages from the process to the console while it's running
42
+ // result.stderr, result.stdout, etc. will be `null`
37
43
  { stdio: 'inherit', }
38
44
  );
39
45
 
40
- // 403 is invalid API key, but I'm not sure how to output
41
- // the response to find out the error code
46
+ // TODO: 2. Stop listening to the normal console here
42
47
 
48
+ // 403 is invalid API key, but I'm not sure how to output
49
+ // the response to find out the error code. stderr is `null`.
43
50
  if ( result.status !== 0 ) {
44
- log.error({
45
- type: `setup`,
46
- code: `ALK0047`,
47
- pre: `Status code ${ result.status }. Could not dainstall this package on your server. See above for more details.`
48
- });
49
- throw(result.stderr);
51
+ const install_err_msg = `Status code ${ result.status }. Could not `
52
+ + `dainstall this package on your server. See above for more details.`;
53
+ log.throw({ code: `ALK0047`, context: `setup`,
54
+ before: `☝️ ☝️ ☝️ Error data ☝️ ☝️ ☝️\n`,
55
+ error: `Error while installing project` },
56
+ install_err_msg
57
+ );
50
58
  }
51
59
 
52
- log.success({
53
- type: `setup`,
54
- code: `ALK0048`,
55
- pre: `ALKiln created a Project in your Playground named "${ project_name }"!`
56
- });
60
+ log.success({ code: `ALK0048`, context: `setup`, },
61
+ `ALKiln created a Project in your Playground named "${ project_name }"!`
62
+ );
57
63
  }; // Ends setup();
58
64
 
59
-
60
- // Before everything, make sure all the required env vars exist
65
+ // 1. session_vars - make sure all the required env vars exist
61
66
  session_vars.validateEnvironment();
67
+
68
+ // 2. Prepare logs
69
+ // Local devs: This will be separate from your test paths
70
+ // unless you pass a `path` argument into the cucumber run command
71
+ const argv = require(`minimist`)(process.argv.slice(2));
72
+ const log = new Log({ path: argv.path, context: `setup` });
73
+
74
+ // 3. run
62
75
  setup();
@@ -1,26 +1,46 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  const da_i = require('./docassemble_api_interface');
4
- const log = require('../utils/log');
5
- const session_vars = require('../utils/session_vars' );
4
+ const session_vars = require('../utils/session_vars');
5
+ const Log = require('../utils/log');
6
6
 
7
7
  const takedown = async () => {
8
- /* In the docassemble testing account identified by the
8
+ /** In the docassemble testing account identified by the
9
9
  * DOCASSEMBLE_DEVELOPER_API_KEY secret, delete the
10
10
  * Project created for this test.
11
11
  * For local development, clean up project name file. */
12
- log.info({ type: `takedown`, code: `ALK0177`, pre: `Trying to remove the docassemble Project from the testing account.` });
12
+ log.info({ code: `ALK0177`, context: `takedown`, },
13
+ `Trying to remove the docassemble Project from the testing account.`
14
+ );
13
15
  // Explicit try-catch prevents "Unhandled promise rejection error"
14
16
  // avoids showing confusing node errors to users that they shouldn't have to fix
15
17
  try {
16
18
  await da_i.delete_project();
17
19
  session_vars.delete_project_name();
18
- log.success({ type: `takedown`, code: `ALK0178`, pre: `Test takedown has finished successfully!` });
20
+ log.info({ code: `ALK0178`, context: `takedown`, },
21
+ `Test takedown finished successfully!`
22
+ );
19
23
  } catch (error) {
20
- log.error({type: `takedown`, code: `ALK0179`, pre: `Could not delete project`, data: JSON.stringify(error, null, 4)})
21
- // we don't re-throw the exception, so make sure to exit with 1 to let GitHub Actions know the script failed
24
+ log.throw({ code: `ALK0179`, context: `takedown`, error: error },
25
+ `Unable to delete project`,
26
+ );
27
+ // we don't re-throw the exception, so make sure to exit
28
+ // with 1 to let GitHub Actions know the script failed
22
29
  process.exitCode = 1;
23
30
  }
31
+
32
+ // TODO: ? Build the final setup, run, & takedown verbose log
33
+ // file in here. Add to it in the GitHub action ?
24
34
  };
25
35
 
36
+ // 1. session_vars - make sure all the required env vars exist
37
+ session_vars.validateEnvironment();
38
+
39
+ // 2. Prepare logs
40
+ // Local devs: This will be separate from your test artifacts paths
41
+ // unless you pass a `path` argument into the `cucumber` run command
42
+ const argv = require(`minimist`)( process.argv.slice(2) );
43
+ const log = new Log({ path: argv.path, context: `takedown` });
44
+
45
+ // 3. run
26
46
  takedown();