@suffolklitlab/alkiln 5.4.1-feat-github-server-19 → 5.4.2

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/CHANGELOG.md CHANGED
@@ -42,8 +42,14 @@ Format:
42
42
  ### Internal
43
43
  -
44
44
  -->
45
+
45
46
  <!-- ## [Unreleased] -->
46
47
 
48
+ ## [5.4.2] - 2023-10-22
49
+
50
+ ### Changed
51
+ - Downloads are now faster! You won't have to wait for the full duration of a step (30 seconds by default) to download a file anymore. See [this PR](https://github.com/SuffolkLITLab/ALKiln/pull/798) for more details.
52
+
47
53
  ## [5.4.1] - 2023-10-18
48
54
 
49
55
  ### Internal
package/action.yml CHANGED
@@ -22,15 +22,6 @@ inputs:
22
22
  description: "Max amount of time to give the server to reload if it needs to"
23
23
  required: false
24
24
  # no default to let the custom Scenario timeouts create the reload timeout
25
- # INSTALL_LOCATION? Will that get confused with the location passed into the command line?
26
- # Maybe `INSTALL_LOCATION` could be "playground", "server", or a custom path and we could handle all those.
27
- INSTALL_METHOD:
28
- description: "Default is 'playground'. Other option: 'server'. How to install the package onto the server."
29
- required: false
30
- default: "playground"
31
- ALKILN_TAG_EXPRESSION:
32
- description: "The tag expression to use to limit which tests to run. Default is no tag expression."
33
- required: false
34
25
 
35
26
  runs:
36
27
  using: "composite"
@@ -55,43 +46,29 @@ runs:
55
46
  echo -e "\nALKiln version is $ALKILN_VERSION\nRepo is $REPO_URL\nBranch ref is $BRANCH_PATH\nMAX_SECONDS_FOR_SETUP is $MAX_SECONDS_FOR_SETUP\nSERVER_RELOAD_TIMEOUT_SECONDS is $SERVER_RELOAD_TIMEOUT_SECONDS\n"
56
47
  shell: bash
57
48
 
58
- # Install
49
+ # Install node
59
50
  - name: "ALKiln: Install node packages"
60
51
  uses: actions/setup-node@v3
61
52
  with:
62
53
  node-version: "18"
63
54
  - name: Install ALKiln directly from npm
64
55
  run: |
65
- npm install -g @suffolklitlab/alkiln@$ALKILN_VERSION
56
+ npm install -g @suffolklitlab/alkiln
66
57
  shell: bash
67
58
 
68
- # Install on playground
59
+ # Run tests
69
60
  - name: "ALKiln: Create a Project and pull the package from GitHub"
70
- if: ${{ inputs.INSTALL_METHOD == 'playground' }}
71
61
  run: alkiln-setup
72
62
  shell: bash
73
- - run: echo -e "\n\n====\nALKiln could not create a project on your server's testing account or pull your package into it. Check the messages above this line.\n\n"
74
- if: ${{ inputs.INSTALL_METHOD == 'playground' && failure() }}
75
- shell: bash
76
-
77
- # Install on server
78
- - name: "ALKiln: Install the GitHub package onto the server"
79
- if: ${{ inputs.INSTALL_METHOD == 'server' }}
80
- run: alkiln-server-install
81
- shell: bash
82
- - run: echo -e "\n\n====\nALKiln could not install the package on the temporary GitHub docassemble server. Check the steps above this line.\n\n"
83
- if: ${{ inputs.INSTALL_METHOD == 'server' && failure() }}
84
- shell: bash
85
-
86
- # run tests
87
63
  - name: "ALKiln: Run tests"
88
64
  if: ${{ success() }}
89
- run: alkiln-run ${{ inputs.ALKILN_TAG_EXPRESSION || github.event.inputs.tags && format('{0}', github.event.inputs.tags) }}
65
+ run: alkiln-run ${{ github.event.inputs.tags && format('{0}', github.event.inputs.tags) }}
90
66
  shell: bash
91
-
92
- # on playground, delete project
93
- - name: "ALKiln: Try to delete the project from the playground of the docassemble test account."
94
- if: ${{ inputs.INSTALL_METHOD == 'playground' }}
67
+ - run: echo -e "\n\n====ALKiln could not create a project on your server's testing account or pull your package into it. Check the messages above this line.\n\n"
68
+ if: ${{ failure() }}
69
+ shell: bash
70
+ - name: "ALKiln: Delete project from docassemble test account"
71
+ if: ${{ always() }}
95
72
  run: alkiln-takedown
96
73
  shell: bash
97
74
 
@@ -104,7 +81,7 @@ runs:
104
81
  path: ./alkiln-*
105
82
 
106
83
  # ##################################
107
- # # A developer's workflow should look similar to the below.
84
+ # # A developer's workflow should look similar to the below
108
85
  # # In place of `uses: suffolkLITLab/ALKiln@v5`, they
109
86
  # # might use another version.
110
87
  #
@@ -79,21 +79,13 @@ da.pull = async function ( timeout ) {
79
79
  }; // Ends da.pull()
80
80
 
81
81
 
82
- da.has_playground_task_finished = async function ( task_id, timeout ) {
83
- return await da.has_task_finished( "/api/restart_status", task_id, timeout);
84
- }
85
-
86
- da.has_package_task_finished = async function ( task_id, timeout ) {
87
- return await da.has_task_finished( "/api/package_update_status", task_id, timeout);
88
- }
89
-
90
82
  // node -e 'require("./lib/docassemble/docassemble_api_REST.js").has_task_finished()'
91
- da.has_task_finished = async function ( api_endpoint, task_id, timeout ) {
83
+ da.has_task_finished = async function ( task_id, timeout ) {
92
84
  /* Check task status based on `task_id`. */
93
85
  let options = {
94
86
  method: 'GET',
95
87
  headers: {'X-API-Key': session_vars.get_dev_api_key()},
96
- url: `${ session_vars.get_da_server_url() }${ api_endpoint }`,
88
+ url: `${ session_vars.get_da_server_url() }/api/restart_status`,
97
89
  params: {
98
90
  task_id: task_id,
99
91
  },
@@ -329,8 +329,9 @@ da_i.pull = async function ( pull_options ) {
329
329
  }
330
330
  }; // Ends da_i.pull()
331
331
 
332
+
332
333
  // node -e 'require("./lib/docassemble/docassemble_api_interface.js").has_task_finished()'
333
- da_i.has_task_finished = async function ( api_endpoint, task_id, options ) {
334
+ da_i.has_task_finished = async function ( task_id, options ) {
334
335
  /** Check task status if it exists. If not, return true as task did
335
336
  * not require a server restart. See da_i.pull().
336
337
  * See https://docassemble.org/docs/api.html#restart_status. */
@@ -341,7 +342,7 @@ da_i.has_task_finished = async function ( api_endpoint, task_id, options ) {
341
342
  try {
342
343
 
343
344
  // If server did need to restart, check if it's done.
344
- let response = await _da_REST.has_task_finished( api_endpoint, task_id, timeout );
345
+ let response = await _da_REST.has_task_finished( task_id, timeout );
345
346
  log.debug({ pre: `Task status: ${ response.status }. Docassemble status text: ${ response.data.status }. Task id: ${ task_id }` });
346
347
 
347
348
  if ( response.data.status === 'completed' ) {
@@ -352,15 +353,16 @@ da_i.has_task_finished = async function ( api_endpoint, task_id, options ) {
352
353
  return false;
353
354
 
354
355
  } else if ( response.data.status === 'unknown' ) {
355
- // Server is working, but status 'unknown'? Bad design, but this
356
- // does get returned when install packages, so return true
356
+ // Server is working, but status 'unknown'? We shouldn't even
357
+ // get here according to the API docs, but it feels weird to have
358
+ // no default behavior. What to return? Throw?
357
359
  log.info({
358
360
  pre: `\`task_id\` "${ task_id }" has expired. If it's been `
359
361
  + `less than 1 hour since the task started, the task is `
360
362
  + `complete. See https://docassemble.org/docs/api.html#restart_status.`,
361
- data: response.data,
363
+ data: response,
362
364
  });
363
- return true;
365
+ return null;
364
366
  } // ends status type
365
367
 
366
368
  } catch ( error ) {
@@ -376,15 +378,8 @@ da_i.has_task_finished = async function ( api_endpoint, task_id, options ) {
376
378
 
377
379
  }; // Ends da_i.has_task_finished()
378
380
 
379
- da_i.wait_for_playground_to_restart = async ( task_id, options ) => {
380
- return await da_i.wait_for_server_to_restart( "/api/restart_status", task_id, options );
381
- };
382
-
383
- da_i.wait_for_package_to_restart = async ( task_id, options ) => {
384
- return await da_i.wait_for_server_to_restart( "/api/package_update_status", task_id, options );
385
- };
386
381
 
387
- da_i.wait_for_server_to_restart = async ( api_endpoint, task_id, options ) => {
382
+ da_i.wait_for_server_to_restart = async ( task_id, options ) => {
388
383
  /** Wait for the server to finish restarting in case a module needs to load.
389
384
  * This is a polling function because things may be returned
390
385
  * that aren't equivalent to the server being done with a task. */
@@ -415,7 +410,7 @@ da_i.wait_for_server_to_restart = async ( api_endpoint, task_id, options ) => {
415
410
  while ( !done_restarting && num_tries <= max_loops ) {
416
411
  num_tries++;
417
412
  // Wait only a short time for the task to finish, not the full default timeout
418
- done_restarting = await da_i.has_task_finished( api_endpoint, task_id, { timeout: wait_for_response } );
413
+ done_restarting = await da_i.has_task_finished( task_id, { timeout: wait_for_response } );
419
414
  if ( !done_restarting ) {
420
415
  if ( num_tries < max_loops ) {
421
416
  // Show a slightly different message after a number of tries to reassure
@@ -10,20 +10,10 @@ let get_base_interview_url = async function () {
10
10
  * When finishing a test, we go to the sign out page (which is supposed to help even
11
11
  * when the user is not logged in.) We can't use `reset=2` because it caused an
12
12
  * 'Unable to locate interview session' error. */
13
-
14
13
  let server_url = session_vars.get_da_server_url();
15
-
16
- if (session_vars.get_install_method() === `server`) {
17
-
18
- return `${ server_url }/interview?new_session=1&i=docassemble.${session_vars.get_repo_folder_name()}%3Adata/questions/`;
19
-
20
- } else {
21
-
22
- let dev_id = await docassemble_api_interface.get_dev_id();
23
- let project_name = session_vars.get_project_name();
24
- return `${ server_url }/interview?new_session=1&i=docassemble.playground${ dev_id }${ project_name }%3A`;
25
-
26
- }
14
+ let dev_id = await docassemble_api_interface.get_dev_id();
15
+ let project_name = session_vars.get_project_name();
16
+ return `${ server_url }/interview?new_session=1&i=docassemble.playground${ dev_id }${ project_name }%3A`;
27
17
  };
28
18
 
29
19
  module.exports = get_base_interview_url;
@@ -39,7 +39,7 @@ const setup = async () => {
39
39
  let task_id = await da_i.pull();
40
40
  // Task id will be null if server did not need to restart
41
41
  if ( task_id ) {
42
- await da_i.wait_for_playground_to_restart( task_id );
42
+ await da_i.wait_for_server_to_restart( task_id );
43
43
  }
44
44
 
45
45
  // Finish
package/lib/scope.js CHANGED
@@ -46,9 +46,6 @@ let printable_var_value = function(row) {
46
46
  /* `row` might be a row, a source row, or an original row. */
47
47
  let original = get_original_row(row);
48
48
  if ( is_secret_var(original) ) {
49
- if ( session_vars.get_dangerous_debug() ) {
50
- return `alkiln_dangerous ${ row.value }`;
51
- }
52
49
  return original.flags.secret_name;
53
50
  }
54
51
  return row.value;
@@ -2440,10 +2437,6 @@ module.exports = {
2440
2437
  /** Sets a non-story table variable to a "secret" value (i.e. an env var) */
2441
2438
  // Prevent pictures of a screen with a secret.
2442
2439
  scope.disable_error_screenshot = true;
2443
- if ( session_vars.get_dangerous_debug() ) {
2444
- scope.disable_error_screenshot = false;
2445
- }
2446
-
2447
2440
  let var_data = await scope.normalizeTable( scope, {
2448
2441
  var_data: [{
2449
2442
  var: var_name,
@@ -2492,10 +2485,39 @@ module.exports = {
2492
2485
  if ( !elem ) { reports.addToReport(scope, { type: `error`, value: msg }); }
2493
2486
  expect( elem, msg ).to.exist;
2494
2487
 
2495
- scope.toDownload = filename;
2496
- // Should this be using `scope.tapElement()`?
2497
- await elem.evaluate( elem => { return elem.click(); });
2498
- await scope.detectDownloadComplete( scope );
2488
+ let failed_to_download = false;
2489
+ let err_msg = "";
2490
+ try {
2491
+ const binaryStr = await scope.page.evaluate(el => {
2492
+ const url = el.getAttribute("href");
2493
+ return new Promise(async (resolve, reject) => {
2494
+ const response = await fetch(url, {method: "GET"});
2495
+ const reader = new FileReader();
2496
+ reader.readAsBinaryString(await response.blob());
2497
+ reader.onload = () => resolve(reader.result);
2498
+ reader.onerror = () => reject(`Error occurred on page when downloading ${ url }: ${ reader.error }`);
2499
+ });
2500
+ }, elem);
2501
+ if (binaryStr !== '') {
2502
+ const fileData = Buffer.from(binaryStr, 'binary');
2503
+ fs.writeFileSync(`${ scope.paths.scenario }/${ filename }`, fileData);
2504
+ reports.addToReport(scope, { type: `info`, value: `Downloaded ${ filename } (${ fileData.length } bytes) to ${ scope.paths.scenario }`});
2505
+ } else {
2506
+ failed_to_download = true;
2507
+ err_msg = `Couldn't download ${ filename }, binary data for download was empty`;
2508
+ }
2509
+ } catch (error) {
2510
+ failed_to_download = true;
2511
+ err_msg = error;
2512
+ }
2513
+
2514
+ if (failed_to_download) {
2515
+ reports.addToReport(scope, { type: `warning`, value: `Could not download file using fetch (${ err_msg }). ALKiln will now fallback to the click download method` });
2516
+ scope.toDownload = filename;
2517
+ // Should this be using `scope.tapElement()`?
2518
+ await elem.evaluate( elem => { return elem.click(); });
2519
+ await scope.detectDownloadComplete( scope );
2520
+ }
2499
2521
  }, // Ends scope.steps.download()
2500
2522
 
2501
2523
  compare_pdfs: async function (scope, {existing_pdf_path, new_pdf_path}) {
@@ -2613,10 +2635,7 @@ module.exports = {
2613
2635
  */
2614
2636
  // Don't take a picture of a failed login in case one of the inputs is correct
2615
2637
  scope.disable_error_screenshot = true;
2616
- if ( session_vars.get_dangerous_debug() ) {
2617
- scope.disable_error_screenshot = false;
2618
- }
2619
-
2638
+
2620
2639
  // If there is no browser open, start a new one
2621
2640
  if (!scope.browser) {
2622
2641
  scope.browser = await scope.driver.launch({ headless: !session_vars.DEBUG });
@@ -2649,10 +2668,6 @@ module.exports = {
2649
2668
  let email = process.env[ email_secret_name ];
2650
2669
  let password = process.env[ password_secret_name ];
2651
2670
 
2652
- if ( session_vars.get_dangerous_debug() ) {
2653
- log.debug({ type: `unsafe`, data: `login: ${ email }, ${ password }` });
2654
- }
2655
-
2656
2671
  // Give as many useful messages to the user as possible if the env vars
2657
2672
  // aren't defined. Eventually, just provide a link to the docs because
2658
2673
  // this is complex
package/lib/steps.js CHANGED
@@ -203,7 +203,6 @@ Given(/I start the interview at "([^"]+)"/, {timeout: -1}, async (file_name) =>
203
203
  scope.page.on('response', async response => {
204
204
  // Note: We can expect that only one file will be getting downloaded
205
205
  // at any one time since each step completes synchronously
206
-
207
206
  // If response has a file in it that matches the file we are downloading
208
207
  // Sadly, 'content-disposition' isn't working. We're not sure what will.
209
208
  if ( response.headers()['content-disposition'] && (response.headers()['content-disposition']).includes(`attachment;filename=${scope.toDownload}`) ) {
package/lib/utils/log.js CHANGED
@@ -42,7 +42,7 @@ log.error = function ({ type='', pre='', data='', post='' }) {
42
42
 
43
43
  log.debug = function ({ type='', pre='', data='', post='' }) {
44
44
  /* If debugging is turned on, log a message that includes `ALKiln` and `debug` */
45
- if ( session_vars.get_debug() || session_vars.get_dangerous_debug() ) {
45
+ if ( session_vars.get_debug() ) {
46
46
  let start = create_msg_start([ type, 'debug' ], pre );
47
47
  // Log them each on a separate line
48
48
  console.log( `ALKiln ${ type } debug: ${ pre }` );
@@ -24,11 +24,6 @@ module.exports = session_vars;
24
24
  // These default to null
25
25
  // TODO: How to isolate debug elsewhere so this file can use the logger?
26
26
  session_vars.get_debug = function () { return process.env.DEBUG || null; };
27
- session_vars.get_dangerous_debug = function () {
28
- /** Note this will cause failure of tests that reassure the user about
29
- * security precautions we took, like not taking a screenshot. */
30
- return process.env._ALKILN_DANGEROUS_DEBUG || null;
31
- };
32
27
  session_vars.is_dev_env = function () { return process.env.DEV || null; }
33
28
 
34
29
  session_vars.get_dev_api_key = function () { return process.env.DOCASSEMBLE_DEVELOPER_API_KEY || null; };
@@ -178,120 +173,8 @@ session_vars.delete_project_name = function () {
178
173
  const artifacts_path_key = `artifacts_path`;
179
174
  session_vars.save_artifacts_path_name = function ( path_name ) {
180
175
  /* Save a key with the name of the current artifacts folder in case it doesn't exist. */
181
- return session_vars.save_runtime_config_var({ key: artifacts_path_key, value: path_name });
182
-
183
- // // Ensure artifacts path file exists
184
- // if ( !fs.existsSync( runtime_config_path ) ) {
185
- // // If the file doesn't exist, make one because next we need to read it
186
- // fs.writeFileSync( runtime_config_path, JSON.stringify( {}, null, 2 ) );
187
- // }
188
-
189
- // // Get the json, changing it, and re-write the file from scratch
190
- // let json = JSON.parse( fs.readFileSync( runtime_config_path ) );
191
- // json[ artifacts_path_key ] = path_name;
192
- // let file = fs.writeFileSync( runtime_config_path, JSON.stringify( json, null, 2 ) );
193
-
194
- // if ( session_vars.get_debug() ) {
195
- // console.log( `ALKiln debug: Stored name of artifacts directory "${ path_name }" locally.` );
196
- // }
197
- // return file;
198
- }; // Ends save_artifacts_path_name()
199
-
200
- session_vars.get_artifacts_path_name = function () {
201
- /* Get the name of the current artifacts path. */
202
- return session_vars.get_runtime_config_var( artifacts_path_key );
203
- // try {
204
- // let json = JSON.parse( fs.readFileSync( runtime_config_path ));
205
- // let path_name = json[ artifacts_path_key ];
206
-
207
- // if ( session_vars.get_debug() ) { console.log( `ALKiln debug: Artifacts directory is "${ path_name }".` ); }
208
- // return path_name;
209
- // } catch ( error ) {
210
- // return null;
211
- // }
212
- }; // Ends get_artifacts_path_name()
213
-
214
-
215
- const install_method_key = `da_install_method`;
216
- session_vars.save_install_method = function (install_method) {
217
- /* Save a key with the name of the current artifacts folder in case it doesn't exist. */
218
- return session_vars.save_runtime_config_var({ key: install_method_key, value: install_method });
219
-
220
- // // Ensure artifacts path file exists
221
- // if ( !fs.existsSync( runtime_config_path ) ) {
222
- // // If the file doesn't exist, make one because next we need to read it
223
- // fs.writeFileSync( runtime_config_path, JSON.stringify( {}, null, 2 ) );
224
- // }
225
-
226
- // // Get the json, changing it, and re-write the file from scratch
227
- // let json = JSON.parse( fs.readFileSync( runtime_config_path ) );
228
- // json[ install_method_key ] = install_method;
229
- // let file = fs.writeFileSync( runtime_config_path, JSON.stringify( json, null, 2 ) );
230
-
231
- // return file;
232
- }; // Ends save_install_method()
233
-
234
- session_vars.get_install_method = function () {
235
- /* Get the name of the current install method. */
236
- return session_vars.get_runtime_config_var( install_method_key );
237
- // try {
238
- // let json = JSON.parse( fs.readFileSync( runtime_config_path ));
239
- // let install_method = json[ install_method_key ];
240
-
241
- // return install_method;
242
- // } catch ( error ) {
243
- // return null;
244
- // }
245
- }; // Ends get_install_method()
246
-
247
-
248
- const repo_folder_key = `da_repo_folder_name`;
249
- session_vars.save_repo_folder_name = function (repo_folder_name) {
250
- /** Save a key with the name of the main package folder in the repository. */
251
- return session_vars.save_runtime_config_var({ key: repo_folder_key, value: repo_folder_name });
252
-
253
- // // Ensure runtime config file exists
254
- // if ( !fs.existsSync( runtime_config_path ) ) {
255
- // // If the file doesn't exist, make one because next we need to read it
256
- // fs.writeFileSync( runtime_config_path, JSON.stringify( {}, null, 2 ) );
257
- // }
258
-
259
- // // Get the json, changing it, and re-write the file from scratch
260
- // let json = JSON.parse( fs.readFileSync( runtime_config_path ) );
261
- // json[ repo_folder_key ] = repo_folder_name;
262
- // let file = fs.writeFileSync( runtime_config_path, JSON.stringify( json, null, 2 ) );
263
-
264
- // return file;
265
- }; // Ends session_vars.save_repo_folder_name()
266
-
267
- session_vars.get_repo_folder_name = function () {
268
- /** Get the name of the main folder in the repository. */
269
- return session_vars.get_runtime_config_var( repo_folder_key );
270
- // try {
271
- // let json = JSON.parse( fs.readFileSync( runtime_config_path ));
272
- // let name = json[ repo_folder_key ];
273
-
274
- // return name;
275
- // } catch ( error ) {
276
- // return null;
277
- // }
278
- }; // Ends session_vars.get_repo_folder_name()
279
-
280
-
281
- // TODO: Should we replace the other method calls with this one?
282
- // i.e. `save_repo_folder_name`, etc. It would clarify exactly
283
- // what those calls are doing, but would lock in implementation
284
- session_vars.save_runtime_config_var = function ({ key, value }) {
285
- /** Write to the runtime config file by adding a key/value pair to
286
- * the json object there.
287
- *
288
- * @param key {str} Key to use to store `value` in runtime config file
289
- * @param value {any} Value to store in runtime config file
290
- *
291
- * @returns file {binary?} Runtime config file
292
- */
293
176
 
294
- // Ensure runtime config file exists
177
+ // Ensure artifacts path file exists
295
178
  if ( !fs.existsSync( runtime_config_path ) ) {
296
179
  // If the file doesn't exist, make one because next we need to read it
297
180
  fs.writeFileSync( runtime_config_path, JSON.stringify( {}, null, 2 ) );
@@ -299,35 +182,27 @@ session_vars.save_runtime_config_var = function ({ key, value }) {
299
182
 
300
183
  // Get the json, changing it, and re-write the file from scratch
301
184
  let json = JSON.parse( fs.readFileSync( runtime_config_path ) );
302
- json[ key ] = value;
185
+ json[ artifacts_path_key ] = path_name;
303
186
  let file = fs.writeFileSync( runtime_config_path, JSON.stringify( json, null, 2 ) );
304
187
 
305
188
  if ( session_vars.get_debug() ) {
306
- console.log( `ALKiln debug runtime config var: Stored "${key}" locally as "${value}".` );
189
+ console.log( `ALKiln debug: Stored name of artifacts directory "${ path_name }" locally.` );
307
190
  }
308
191
  return file;
309
- }; // Ends session_vars.save_runtime_config_var()
192
+ }; // Ends save_artifacts_path_name()
310
193
 
311
- session_vars.get_runtime_config_var = function ( key ) {
312
- /** Get the value of the given key from the runtime config file.
313
- *
314
- * @param key {str} Key in runtime config file that contains the desired value
315
- * @returns value | null {any} Value of key in runtime config file. If it
316
- * doesn't exist, returns null.
317
- */
194
+ session_vars.get_artifacts_path_name = function () {
195
+ /* Get the name of the current artifacts path. */
318
196
  try {
319
197
  let json = JSON.parse( fs.readFileSync( runtime_config_path ));
320
- let value = json[ key ];
321
- if ( session_vars.get_debug() ) {
322
- console.log( `ALKiln debug runtime config var: "${key}"" is "${value}".` );
323
- }
198
+ let path_name = json[ artifacts_path_key ];
324
199
 
325
- return value;
200
+ if ( session_vars.get_debug() ) { console.log( `ALKiln debug: Artifacts directory is "${ path_name }".` ); }
201
+ return path_name;
326
202
  } catch ( error ) {
327
203
  return null;
328
204
  }
329
- }; // Ends session_vars.get_runtime_config_var()
330
-
205
+ }; // Ends get_artifacts_path_name()
331
206
 
332
207
  session_vars.set_sources_paths = function ( paths ) {
333
208
  /** Returns whether at least one of the given path exists. Store
@@ -360,7 +235,7 @@ session_vars.set_sources_paths = function ( paths ) {
360
235
  // Warn about missing paths
361
236
  if ( missing_paths.length > 0 ) {
362
237
  console.warn(
363
- `ALKiln WARNING: ALKiln can't find folders at "${ paths.join('", "') }".`
238
+ `ALKiln WARNING: ALKiln can't find folders at "${ paths.join(", ") }".`
364
239
  );
365
240
  }
366
241
 
@@ -370,14 +245,10 @@ session_vars.set_sources_paths = function ( paths ) {
370
245
  return false;
371
246
  }
372
247
 
373
- console.info(
374
- `ALKiln info: Did find "sources" folder(s) at "${ existing_paths.join('", "') }".`
375
- )
376
-
377
248
  // Otherwise, store valid paths
378
249
  session_vars._sources_paths = existing_paths;
379
250
  return true;
380
- }; // End session_vars.set_sources_paths()
251
+ };
381
252
 
382
253
  session_vars.get_sources_paths = function () {
383
254
  /** Get the "sources" folder path. Should never be undefined during
@@ -388,7 +259,6 @@ session_vars.get_sources_paths = function () {
388
259
  return session_vars._sources_paths;
389
260
  };
390
261
 
391
-
392
262
  session_vars.validateEnvironment = function() {
393
263
  /** Throw a useful error for the developer if any of the env vars
394
264
  * required by absolutely all tests are missing. Gather all
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suffolklitlab/alkiln",
3
- "version": "5.4.1-feat-github-server-19",
3
+ "version": "5.4.2",
4
4
  "description": "Integrated automated end-to-end testing with docassemble, puppeteer, and cucumber.",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {
@@ -9,7 +9,6 @@
9
9
  "local": "npm run setup && npm run cucumber; npm run takedown",
10
10
  "setup": "./lib/docassemble/setup.js",
11
11
  "takedown": "./lib/docassemble/takedown.js",
12
- "server_install": "./lib/docassemble/server_install.js",
13
12
  "unit": "mocha tests/unit_tests/*.test.*"
14
13
  },
15
14
  "repository": {
@@ -51,7 +50,6 @@
51
50
  "diff": "5.0.0"
52
51
  },
53
52
  "bin": {
54
- "alkiln-server-install": "./lib/docassemble/server_install.js",
55
53
  "alkiln-setup": "./lib/docassemble/setup.js",
56
54
  "alkiln-takedown": "./lib/docassemble/takedown.js",
57
55
  "alkiln-run": "./lib/run_cucumber.js"
package/setup.py CHANGED
@@ -50,10 +50,10 @@ setup(name='docassemble.ALKilnTests',
50
50
  author='plocket',
51
51
  author_email='example@example.com',
52
52
  license='The MIT License (MIT)',
53
- url='https://suffolklitlab.org/docassemble-AssemblyLine-documentation/docs/alkiln',
53
+ url='https://suffolklitlab.org/docassemble-AssemblyLine-documentation/docs/automated_integrated_testing',
54
54
  packages=find_packages(),
55
55
  namespace_packages=['docassemble'],
56
- install_requires=['docassemble.ALToolbox>=0.8.2', 'docassemble.AssemblyLine>=2.24.0'],
56
+ install_requires=['docassemble.ALToolbox>=0.5.1'],
57
57
  zip_safe=False,
58
58
  package_data=find_package_data(where='docassemble/ALKilnTests/', package='docassemble.ALKilnTests'),
59
59
  )
@@ -1,77 +0,0 @@
1
- name: GitHub server tests
2
-
3
- on:
4
- push:
5
- workflow_dispatch:
6
- inputs:
7
- tags:
8
- required: False
9
- description: 'Optional. Use a "tag expression" specify which tagged tests to run (https://cucumber.io/docs/cucumber/api/#tag-expressions)'
10
- enable_tmate:
11
- type: boolean
12
- description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)'
13
- required: false
14
- default: false
15
- # TODO: alkiln_version:
16
-
17
- jobs:
18
-
19
- github-server-tests:
20
- runs-on: ubuntu-latest
21
- strategy:
22
- matrix:
23
- node-version: [18.x]
24
-
25
- env:
26
- # TODO: Must solve - how would a dev make another user/password if they wanted to?
27
- SECRET_VAR1: secret-var1-value
28
- SECRET_VAR2: secret-var2-value
29
- SECRET_FOR_MISSING_FIELD: secret for missing field
30
-
31
- steps:
32
- # Place the root directory in this branch
33
- - uses: actions/checkout@v3
34
-
35
- # Debugging. Maybe add a long sleep after whatever it is we want to check
36
- # to give us time to explore.
37
- - name: Setup tmate session
38
- if: ${{ inputs.enable_tmate }}
39
- uses: mxschmitt/action-tmate@v3
40
- with:
41
- detached: true
42
-
43
- - name: Start the temporary GitHub server
44
- id: github_server
45
- uses: ./github_server
46
- with:
47
- CONFIG_CONTENTS: "${{ secrets.CONFIG_CONTENTS }}"
48
- - run: echo "Finished starting the GitHub server"
49
-
50
- # Debugging on failure
51
- # Trying to catch that intermittent bug mentioned in ./github_server/action.yml
52
- - name: Docker failure tmate session
53
- if: ${{ failure() }}
54
- uses: mxschmitt/action-tmate@v3
55
-
56
- - name: Env vars from server output
57
- run: |
58
- echo "USER1_EMAIL=${{ steps.github_server.outputs.DA_ADMIN_EMAIL }}" >> $GITHUB_ENV
59
- echo "USER1_PASSWORD=${{ steps.github_server.outputs.DA_ADMIN_PASSWORD }}" >> $GITHUB_ENV
60
- ## Example of working with the docker container after it's been started
61
- #- name: Work with docker
62
- # run: |
63
- # container_name=$(docker ps --format '{{.Names}}' | head -n 1)
64
- # echo "container_name is $container_name"
65
-
66
- - name: "Run ALKiln tests"
67
- if: ${{ success() }}
68
- uses: ./
69
- with:
70
- SERVER_URL: "${{ steps.github_server.outputs.SERVER_URL }}"
71
- DOCASSEMBLE_DEVELOPER_API_KEY: "${{ steps.github_server.outputs.DOCASSEMBLE_DEVELOPER_API_KEY }}"
72
- INSTALL_METHOD: "server"
73
- # TODO: See what happens if this is an empty string
74
- ALKILN_VERSION: "server"
75
- #ALKILN_TAG_EXPRESSION: "@al1"
76
- - run: echo "Finished running ALKiln tests"
77
-
@@ -1,126 +0,0 @@
1
- name: "ALKiln/github_server: Isolated docassemble tests"
2
- description: "Download and start up a temporary docassemble server on GitHub's servers, in an isolated environment. Avoids docassemble reload errors and other external factors."
3
-
4
- # If we want to control the version of the docassemble image, we'd have
5
- # to make our own image:
6
- # You can build your own image by doing git clone jhpyle/docassemble and then
7
- # using git checkout to select a particular version, and then using docker build
8
- # to build an image. https://docassemble.org/docs/docker.html#downgrading
9
- # If we just want to use an older version of da (as long as it works with
10
- # the latest image):
11
- # If you have an existing container you can use dainstall from the command
12
- # line to install docassemble.webapp==1.4.68
13
-
14
- inputs:
15
- CONFIG_CONTENTS:
16
- description: "The contents of the config file of the docassemble server"
17
- required: false
18
- MAX_SECONDS_FOR_DOCKER:
19
- description: "Maximum amount of seconds to give the docassemble docker container to serve the site."
20
- required: false
21
- default: 600 # 10 min
22
-
23
- outputs:
24
- DOCASSEMBLE_DEVELOPER_API_KEY:
25
- description: "Use this inside `with:` for the ALKiln step. Temporary admin api key. It will be destroyed after the tests are done."
26
- value: ${{ steps.api_key_output_step.outputs.DOCASSEMBLE_DEVELOPER_API_KEY }}
27
- SERVER_URL:
28
- description: "Use this inside `with:` for the ALKiln step. URL of the temporary docassemble server."
29
- value: "http://localhost"
30
- DA_ADMIN_EMAIL:
31
- value: ${{ steps.api_key_output_step.outputs.DA_ADMIN_EMAIL }}
32
- DA_ADMIN_PASSWORD:
33
- value: ${{ steps.api_key_output_step.outputs.DA_ADMIN_PASSWORD }}
34
-
35
-
36
- runs:
37
- using: "composite"
38
- steps:
39
- - uses: actions/checkout@v3
40
-
41
- - name: "ALKiln github_server: Set environment variables"
42
- run: |
43
- echo "DA_ADMIN_API_KEY=abcd1234abcd1234abcd5678abdc5678" >> $GITHUB_ENV
44
- echo "MAX_SECONDS_FOR_DOCKER=${{ inputs.MAX_SECONDS_FOR_DOCKER }}" >> $GITHUB_ENV
45
- echo "DA_ADMIN_EMAIL=admin@example.com" >> $GITHUB_ENV
46
- # Login will fail if this is "password"
47
- echo "DA_ADMIN_PASSWORD=@123abcdefg" >> $GITHUB_ENV
48
- shell: bash
49
- - id: api_key_output_step
50
- run: |
51
- echo "DOCASSEMBLE_DEVELOPER_API_KEY=$DA_ADMIN_API_KEY" >> "$GITHUB_OUTPUT"
52
- echo "DA_ADMIN_EMAIL=$DA_ADMIN_EMAIL" >> "$GITHUB_OUTPUT"
53
- echo "DA_ADMIN_PASSWORD=$DA_ADMIN_PASSWORD" >> "$GITHUB_OUTPUT"
54
- shell: bash
55
-
56
- - name: "ALKiln github_server: Download and start docker"
57
- run: |
58
- mkdir /tmp/config
59
- echo "${{ inputs.CONFIG_CONTENTS }}" > /tmp/config/myconfig.yml
60
- docker pull jhpyle/docassemble
61
- docker run --name docassemble_container --env DA_ADMIN_EMAIL="${{ env.DA_ADMIN_EMAIL }}" --env DA_ADMIN_PASSWORD="${{ env.DA_ADMIN_PASSWORD }}" --env DA_ADMIN_API_KEY="${{ env.DA_ADMIN_API_KEY }}" --env DA_CONFIG=/tmp/config/myconfig.yml --volume /tmp/config:/tmp/config --cap-add SYS_PTRACE --memory="4gb" -d -p 80:80 jhpyle/docassemble
62
- shell: bash
63
- - name: "ALKiln github_server: Wait for server to start"
64
- run: |
65
- # ALKiln github_server: Wait for server to start
66
-
67
- # Reset the SECONDS variable
68
- SECONDS=0
69
- # Wait 1 minute before pinging the server constantly
70
- sleep 60
71
-
72
- response="start"
73
-
74
- while [[ $response != *"200"* ]]; do
75
- echo "Time elapsed: $SECONDS seconds"
76
-
77
- # Check if max seconds till loading has passed
78
- if [[ $SECONDS -ge $MAX_SECONDS_FOR_DOCKER ]]; then
79
- echo "ALKiln GitHub Server Error: Timed out waiting for docker container to serve the site. If you want to give your docker container more timem to install, set the MAX_SECONDS_FOR_DOCKER input for this action."
80
- exit 1
81
- fi
82
-
83
- # Set the variable to a new string value in each iteration
84
- response=$(curl -o /dev/null -s -w "%{http_code}\n" http://localhost:80/health_check?ready=1)
85
- echo "response is $response"
86
-
87
- # Check if the string includes the desired text
88
- if [[ $response == *"200"* ]]; then
89
- echo "Success! The docker container is ready!"
90
- else
91
- echo "The docker container isn't ready yet. Will check again in 30 seconds."
92
- sleep 30
93
- fi
94
-
95
- done
96
- shell: bash
97
-
98
- # Don't rely on the environment's version of python
99
- - uses: actions/setup-python@v4
100
- with:
101
- python-version: '3.10'
102
- - run: pip install docassemblecli
103
- shell: bash
104
- # Install the current directory onto the docassemble server
105
- - run: dainstall . --apiurl http://localhost --apikey $DA_ADMIN_API_KEY
106
- shell: bash
107
-
108
- # TODO: Upload artifacts that subscribers can download on the Actions summary page
109
- - name: "ALKiln github_server: Docker logs"
110
- if: ${{ always() }}
111
- run: |
112
- # Should this name have a date/time attached? The logs themselves have timestamps
113
- echo "$(docker logs docassemble_container)" > /tmp/alkiln_docker_logs.txt
114
- shell: bash
115
-
116
- - name: "ALKiln github_server: Get folder name with date and time"
117
- if: ${{ always() }}
118
- run: |
119
- echo "GITHUB_SERVER_ARTIFACT_NAME=alkiln-github-server-$(date +'%Y-%m-%d at %Hh%Mm%Ss' -u)UTC" >> $GITHUB_ENV
120
- shell: bash
121
- - name: "ALKiln github_server: Upload artifacts folder"
122
- if: ${{ always() }}
123
- uses: actions/upload-artifact@v3
124
- with:
125
- name: ${{ env.GITHUB_SERVER_ARTIFACT_NAME }}
126
- path: /tmp/alkiln_docker_logs.txt
@@ -1,40 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- const session_vars = require('../utils/session_vars');
4
- const log = require('../utils/log');
5
- const fg = require('fast-glob');
6
-
7
- const server_install = async () => {
8
- session_vars.save_install_method(`server`);
9
-
10
- let current_dir = process.cwd();
11
- let path = `${ current_dir }/docassemble/*`;
12
- let dirs = fg.sync(
13
- [ path ],
14
- {
15
- suppressErrors: true,
16
- onlyDirectories: true
17
- }
18
- );
19
-
20
- let folders = dirs.map(function (dir) { return dir.split('/').pop(); })
21
-
22
- let chosen_folder = folders[0];
23
- if ( folders.length === 0 ) {
24
- let msg = `There are 0 folders at ${path}. Tests won't run.`;
25
- log.error({ type: `setup`, data: msg })
26
- throw new ReferenceError( msg );
27
- }
28
-
29
- if ( folders.length > 1 ) {
30
- log.warn({
31
- type: `setup`,
32
- data: `There are ${folders.length} folders at ${path}: "${folders.join('", "')}". ALKiln chose to use the folder "${chosen_folder}".`
33
- })
34
- }
35
-
36
- console.log(`\n===========\nchosen_folder: ${chosen_folder}\n===========\n`);
37
- session_vars.save_repo_folder_name( chosen_folder );
38
- }; // Ends server_install();
39
-
40
- server_install();