@suffolklitlab/alkiln 5.13.3 → 5.13.4

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 (37) hide show
  1. package/.github/workflows/github_server.yml +7 -7
  2. package/.github/workflows/playground.yml +1 -1
  3. package/CHANGELOG.md +24 -0
  4. package/action.yml +70 -28
  5. package/action_for_github_server/action.yml +61 -29
  6. package/docs/4_environments.md +243 -0
  7. package/docs/artifacts_folder_name.md +135 -0
  8. package/docs/constrained_random_tests.md +320 -0
  9. package/docs/decisions/006_which_log_level_to_pick_2024_09_5.md +70 -0
  10. package/docs/decisions/007_getting_and_storing_logs.md +179 -0
  11. package/docs/decisions/move_runtime_config_to_sources_folder.md +3 -1
  12. package/docs/decisions/reports_and_logs_2024_08_11.md +161 -0
  13. package/docs/kinds_of_errors.md +26 -0
  14. package/lib/docassemble/docassemble_api_interface.js +77 -51
  15. package/lib/docassemble/server_install.js +32 -9
  16. package/lib/docassemble/setup.js +40 -25
  17. package/lib/docassemble/takedown.js +33 -10
  18. package/lib/parse.js +36 -0
  19. package/lib/run_cucumber.js +115 -36
  20. package/lib/scope.js +60 -75
  21. package/lib/steps.js +70 -42
  22. package/lib/utils/Log.js +982 -0
  23. package/lib/utils/files.js +18 -11
  24. package/lib/utils/get_sources_paths.js +162 -0
  25. package/lib/utils/parse.js +1 -0
  26. package/lib/utils/proccess_command_line_args.js +267 -0
  27. package/lib/utils/reports.js +64 -17
  28. package/lib/utils/session_vars.js +34 -21
  29. package/lib/utils/time.js +44 -0
  30. package/package.json +1 -1
  31. package/tests/log_codes/check_codes.sh +2 -1
  32. package/tests/log_codes/log_code_expected_instances.txt +33 -9
  33. package/tests/unit_tests/getAllFields.test.js +5 -0
  34. package/tests/unit_tests/getMatchingRows.test.js +5 -0
  35. package/tests/unit_tests/log.test.js +581 -0
  36. package/tests/unit_tests/normalizeTable.test.js +5 -0
  37. package/lib/utils/log.js +0 -96
@@ -0,0 +1,161 @@
1
+ # Shape and behavior of the report and log objects
2
+
3
+ <!-- TODO: turn this into a architecture design doc? -->
4
+ <!-- TODO: Rename to just "logs" -->
5
+
6
+ ## Context and scope
7
+
8
+ We show logs to the user in the console and as test suite artifacts. We have logs we use for development. cucumberjs also has logs we want to include in the above until we are confident we are handling all cases ourselves.
9
+
10
+ Right now, the system for logging is fractured, scattered, and duplicated. Logs that are not from the report don't get saved anywhere. Some logs only get shown in debug mode in the console, which can be frustrating and unhelpful. We lack verbose output information for tests that authors run in the wild.
11
+
12
+ This document excludes discussion of report architecture, but it is useful to note that at the moment they are completely separate from logs and yet save to some of the same files, so they influence our current choices.
13
+
14
+ ## Goals
15
+
16
+ - Make log information persistent and visible
17
+ - Add additional meaningful information to logs (e.g. metadata)
18
+ - Make sure logs are clear without needing to read the surrounding code
19
+ - Unify and clarify log flow
20
+ - Make log behavior consistent
21
+
22
+ ## Non/anti-goals
23
+
24
+ - Disrupt the signals we're giving authors about their tests with the noise of internal ALKiln details
25
+ - Create a complex and heavy system to try to abstract absolutely everything
26
+ - Let the complexity of the relationship with report behavior too heavily influencing our log design goals
27
+
28
+ ## The actual design
29
+
30
+ ### `Log` internal errors
31
+
32
+ Logs are informational and not related to the behavior of authors' tests. Because of that, these logs should avoid adding noise to what an author sees. That is, internal warnings or errors should look different than authors' test errors. To help with this:
33
+
34
+ - All log methods` arguments have default values.
35
+ - We use try/catch very aggressively in the `Log` class.
36
+
37
+ Read more of the thoughts behind this choice in the decision doc on which log level to pick and the design doc on types of errors.
38
+
39
+ ### Purposes of methods
40
+
41
+ Some of these notes have to do with reports because logs and reports are using some of the same files at the moment and yet are not integrated. We'll write a doc for reports when we integrate reports more fully with logs.
42
+
43
+ 1. `Log`: Messages or errors from wrapping functionality, like setup and takedown messages.
44
+ 2. `Log`: Test progress (a compact way to see what's happening/happened in each test):
45
+
46
+ Test1: .............F--.
47
+
48
+ Test 2: ....
49
+ 3. Report: A pretty report at the end with details about each test. It has sections, like "failed" and "passed", headings, etc.
50
+ 4. Report: cucumberjs's various console logs.
51
+ 5. `Log`: Lots of other informational logs for internal development and debugging purposes.
52
+
53
+ Different logs belong in different places:
54
+
55
+ - ALKiln always logs items 1, 2, 3, and 4 to the console.
56
+ - Ideally save all items in a verbose debug file sequentially as the tests run.
57
+ - During the tests, save items 2, 3, and 4 to a running report file sequentially as they come. Include metadata. Suggested filenames: `report_log.txt`/`running_report.txt`/`report_run.txt`. If we have to stop the tests early because of an infinite loop or something, this will at least show the test author some information.
58
+ - At the end of the tests, format items 1, 2, 3, and 4 nicely in a file called `report.txt`. This will mostly exclude the metadata.
59
+ - When there is a warning-level or error-level log, save 1, 3, 4, and 5 in an unexpected results file too.
60
+ - In future, discuss the usefulness of having a debug mode where ALKiln logs all items to the console while the tests are running.
61
+
62
+ We may also want copies of various files that have different formats for different environments (see docs on the different environments in which ALKiln runs).
63
+
64
+ - Console logs with colors.
65
+ - Plain text versions that exclude those color codes.
66
+ - HTML versions to show with, for example ALKilnInThePlayground. Maybe in GitHub as well in the future.
67
+
68
+ ### Anatomy and behavior of logging methods
69
+
70
+ Most of a `Log`'s logging methods add additional metadata to a log:
71
+
72
+ 1. The level of the log (success, info, warning, error, and custom values)
73
+ 2. An icon showing the level of a log
74
+ 3. A log code, like (e.g. ALK0105)
75
+ 4. Contextual info about where the log originated (e.g. "setup", "takedown", "invalid answer")
76
+ 9. A timestamp
77
+
78
+ To be able to show that metadata, most methods will accept custom values for parts of that metadata. Most methods offer:
79
+
80
+ - Log code
81
+ - Contextual info
82
+ - The error throwing information
83
+
84
+ For the debug method, everything other than the timestamp can be customized.
85
+
86
+ Callers can influence the content and formatting of most parts of the log message. For example, they can call `.warn` to show a warning icon and level metadata. The one exception would usually be the part before the metadata. Customizing that could be useful for things like creating a visual separator before ALKiln starts evaluating the fields of a web page. For that reason, the caller can add a `before` property to the metadata that a `Log` object will add before the metadata.
87
+
88
+ A `Log` object should be able to throw errors so that it can log metadata and any additional styling and context along with the error. It can also make sure that the error will be an actual `Error` object (as opposed to a string or other object). Otherwise everywhere we needed to throw an error, we would need duplicate handling bad data (like objects with circular references), using `Log` to save to files (etc.), throwing the error, and other possible complications. For that, `Logs` also need to accept this additional information:
89
+
90
+ - Whether to throw an error
91
+ - The error to throw
92
+
93
+ Those methods should also accept multiple individual log arguments the same way `console.log()` does. This lets the caller include strings separately from objects they want to log, including `Error` objects, so that a `Log` object can handle them safely and style them consistently. We could have required a list of logs instead, but the extra syntax was frustrating.
94
+
95
+ Right now, the only way we differentiate methods that just store data vs. methods that also log to the console is with documentation. This seems less than ideal. Using the name `record` could be confusing because developers might think they need to log to the console separately from saving the information to debug files.
96
+
97
+ ## APIs
98
+
99
+ Proposed external methods: `success`, `info`, `warn`, `throw`, `stdout`, `debug`. See the `Log` class and its tests for the most up-to-date API.
100
+
101
+ <!-- Discuss: better name for `stdout`? -->
102
+
103
+ ## Data storage
104
+
105
+ - `report.txt` - final pretty output for the user with headings, etc.
106
+ - Each Scenario's `report.txt` - pretty output in each test's folder.
107
+ - `report_log.txt` (name?) - ugly, but still sparse, giving the user some info about what happened during the tests without overwhelming them.
108
+ - `debug_log.txt` (name? `debug`?) - very ugly and full of all the information from everywhere else and more. Useful for internal development and troubleshooting. Includes setup, takedown, etc.
109
+ - `temp_unexpected_results_debug_log.txt` - A file containing messages about unexpected behavior, like test warnings and errors, as well as internal ALKiln warnings. We don't output this in our GitHub actions - the file itself is temporary, we just add its contents to the final unexpected results file. All its information exists in the debug file too.
110
+ - `unexpected_results.txt` - Logs for failures and other unexpected behavior. It has information about test warnings and errors, as well as internal ALKiln warnings. At first we only save report information there. When the tests finish, we add the contents of the specific debug file that contains only unexpected results.
111
+
112
+ ## Degree of constraint
113
+
114
+ - Right now we're a bit limited by report functionality staying separate. Other than that, we can design as we want. We should avoid the current behavior of reports substantially influencing our design decisions here. We do have to keep in mind how reports might integrate with this in the future, but we will also need to change the design of the report behavior significantly in the future, so we should keep an open mind.
115
+ - We have not yet approached saving logs from GitHub actions, though this should be fairly simple. It may add some complication.
116
+ - We have not yet solved getting all logs from the console (e.g. cucumberjs logs).
117
+
118
+ ## Alternatives considered
119
+
120
+ - Avoid the complexity of storing setup and takedown output in the verbose/debug file. In GitHub, they're in the GitHub console. Locally, we see the results immediately. On the other hand, storing them ourselves lets us look for information in just one place.
121
+ - Include GitHub action output in the debug files. Out of scope for the current iteration.
122
+ - Catch cucumberjs's actual console output. Out of scope for the current iteration.
123
+
124
+ ### System-context diagram
125
+
126
+ Current design before integration of reports, GitHub action logs, and other non-ALKiln console logs.
127
+
128
+ ```
129
+ ## `Log`
130
+
131
+ console methods -> ._console() -> .debug() -> log to the console
132
+ .stdout() -> .debug() -> sometimes write to stdout
133
+
134
+ .debug() -> debug_log.txt with metadata
135
+ -> sometimes temp_unexpected_results_debug_log.txt with metadata
136
+
137
+ ## `Report`
138
+ report and its methods -> report_log.txt (plain text)
139
+ -> debug_log.txt (plain text)
140
+ -> sometimes unexpected_results.txt (plain text)
141
+ -> report summary at end of tests
142
+ -> final report.txt (plain text summary)
143
+ -> final console log (summary with console colors)
144
+ -> individual Scenario reports at end of tests
145
+ -> final individual report summary file in each Scenario's folder
146
+
147
+ ## takedown
148
+ Add temp_unexpected_results_debug_log.txt to the end of unexpected_results.txt
149
+ ```
150
+
151
+ ## Cross-cutting concerns
152
+
153
+ _Security, privacy, observability, etc._
154
+
155
+ Code that calls `Log` methods should avoid passing sensitive information. A `Log` object doesn't protect against exposing information.
156
+
157
+ ## Related documentation
158
+
159
+ Decision doc about which log level to use.
160
+ Architecture design doc about types of unexpected and expected "errors".
161
+ Decision doc about getting and storing logs.
@@ -0,0 +1,26 @@
1
+ # Kinds of Errors
2
+
3
+ An error, or even an `Error` instance, can mean a lot of different things in the context of this testing framework. Throwing errors for failing tests is what we are most familiar with, but it is just one kind of error.
4
+
5
+ ## Interview `Error`s
6
+
7
+ 1. **Interview bugs**: For example, the interview says ALKiln's answer is invalid. The author expected that answer to be valid. ALKiln saves an error message to the report and throws an error to tell cucumber to mark the test as failed. Currently, these logs errors come with the 🤕 icon. When all the tests are done, the test suite will show as failing in the console, the report, and the various logs. In future, ALKiln will use a method like `report.throw()` to save to the various log files with the `error` level and then throw the error to let cucumber know the test failed.
8
+ 1. **Interview expected errors**: For example, the interview says ALKiln's answer is invalid. The author was testing their interview's field validation rules. The test passes.
9
+
10
+ ## Test file `Error`s
11
+
12
+ 1. **Test file syntax error**: For example, a row in a table in a test file has too many columns. cucumberjs throws an error and no tests are run. ALKiln should show the tests as failed. ALKiln is unable to get the output of that error into a log, but there should be an error in the console.
13
+ 1. **Test Step typo**: For example, `And I ta p to contcontinue`. cucumberjs will cause this test to fail, giving an "undefined" Step as the reason. The test suite will show as failing in the console, the report, and the various logs.
14
+ 1. **Test bugs**: For example, a test has old values that need to be updated. The test fails. This will look like an interview bug, as this document describes.
15
+
16
+ ## ALKiln `Error`s
17
+
18
+ 1. **ALKiln un-handled bugs**: For example, we have forgotten to define a variable and none of our tests has yet triggered that code. These are unexpected errors that force processes to abort or force individual tests to fail in uninformative ways. To an author that runs into that error, it looks like their test has failed, but the fault is ours and we need to troubleshoot it. We should make that clear.
19
+ 1. **ALKiln silent bugs**: For example, a debug log has an object that causes an error when ALKiln tries to turn it into a string. ALKiln should record the failure somewhere, but should avoid throwing it. We can troubleshoot those if we see them and add new internal tests. See decisions docs on when to use which kind of log level.
20
+ 1. **ALKiln expected errors**: For example, one promise won a `Promise.race()`, so ALKiln aborted the other promises. That causes those aborted promises to error. That is why ALKiln saves the "informational" Error to the debug files and it has a level of "info" or "debug".
21
+
22
+ ## ALKiln's internal tests
23
+
24
+ 1. **ALKiln internal success-handling test failures**: For example, the test fails to fill out radio buttons correctly.
25
+ 1. **ALKiln internal failure-handling test successes**: For example, we test that the summary report shows a specific log code when an interview fails to load and the log code shows up correctly. The "test" fails - the interview fails to load - but *our* test passes - the right log code was in the summary report.
26
+ 1. **ALKiln internal failure-handling test failures**: For example, we test that the report shows a specific log code when an interview fails to load, but the log code is missing. The "test" fails and *our* test fails.
@@ -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
 
@@ -61,6 +61,10 @@ const time = require('../utils/time');
61
61
  let da_i = {};
62
62
  module.exports = da_i;
63
63
 
64
+ const context = `da api`;
65
+ const artifacts_path = session_vars.get_artifacts_path_name();
66
+ const log = new Log({ path: artifacts_path, context });
67
+
64
68
 
65
69
  // Constants
66
70
  const DEFAULT_MAX_REQUEST_MS = session_vars.get_setup_timeout_ms();
@@ -92,7 +96,9 @@ da_i.throw_an_error_if_server_is_not_responding = async function ( options ) {
92
96
 
93
97
  // Defaults
94
98
  let { timeout } = options || { timeout: DEFAULT_MAX_REQUEST_MS };
95
- 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, },
100
+ `Waiting for the docassemble server to respond. Will wait for ${ timeout/1000 } seconds at most.`
101
+ );
96
102
 
97
103
  try {
98
104
  let start_time = Date.now()
@@ -100,7 +106,9 @@ da_i.throw_an_error_if_server_is_not_responding = async function ( options ) {
100
106
  let response = await _da_REST.get_dev_id( timeout );
101
107
  let end_time = Date.now()
102
108
  if ( response && response.data && response.data.id ) {
103
- 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, },
110
+ `The docassemble server responded after ${ (end_time - start_time)/1000 } seconds.`
111
+ );
104
112
  return false;
105
113
  }
106
114
 
@@ -109,17 +117,16 @@ da_i.throw_an_error_if_server_is_not_responding = async function ( options ) {
109
117
  if ( is_timeout_error( error ) ) {
110
118
  // Anything that's waiting for the server to not be busy does not want
111
119
  // to move forward without a free server.
112
- log.info({ type: `da api`, code: `ALK0168`, pre: `The docassemble server was still busy after ${ timeout/1000 } seconds.` });
120
+ log.info({ code: `ALK0168`, context, },
121
+ `The docassemble server was still busy after ${ timeout/1000 } seconds.`
122
+ );
113
123
  } else {
114
124
  // Not sure what to do for other errors. It could be a server
115
125
  // error, like an 'Access Denied' error, which should be handled.
116
- log.error({
117
- type: `da api`,
118
- code: `ALK0169`,
119
- pre: `Tried to check if the docassemble server was busy, but `
120
- + `ran into an unexpected error. The error didn't indicate a busy server, though. Error message:\n`
121
- + error
122
- });
126
+ log.warning({ code: `ALK0169`, context, },
127
+ `Checked if the server was busy and ran into a different error`,
128
+ error
129
+ );
123
130
  }
124
131
 
125
132
  throw error;
@@ -187,7 +194,7 @@ da_i.get_api_key_access_status = async function ({ timeout = null, api_key = nul
187
194
  * to allow for server request.
188
195
  */
189
196
 
190
- let auth_data = {
197
+ let auth_feedback = {
191
198
  codes: [ 1 ],
192
199
  has_auth: null,
193
200
  messages: [`API key authorization is not yet determined.`],
@@ -196,16 +203,16 @@ da_i.get_api_key_access_status = async function ({ timeout = null, api_key = nul
196
203
 
197
204
  // Skip this for tests run from ALKilnInThePlayground
198
205
  if ( session_vars.get_origin() === 'playground' ) {
199
- auth_data.codes.push( 2 );
200
- auth_data.messages.push( `API keys are unnecessary for tests run in the Playground.` );
206
+ auth_feedback.codes.push( 2 );
207
+ auth_feedback.messages.push( `API keys are unnecessary for tests run in the Playground.` );
201
208
  }
202
209
 
203
210
  if ( typeof api_key !== `string` ) {
204
- auth_data.codes.push( 3 );
205
- auth_data.messages.push( `The given API key is not a string. Type: ${ typeof api_key }`);
211
+ auth_feedback.codes.push( 3 );
212
+ auth_feedback.messages.push( `The given API key is not a string. Type: ${ typeof api_key }`);
206
213
  }
207
214
 
208
- if ( auth_data.codes.length === 1 ) {
215
+ if ( auth_feedback.codes.length === 1 ) {
209
216
  // Default timeouts
210
217
  timeout = timeout || DEFAULT_MAX_REQUEST_MS;
211
218
  try {
@@ -213,29 +220,31 @@ da_i.get_api_key_access_status = async function ({ timeout = null, api_key = nul
213
220
  api_key,
214
221
  server_url: session_vars.get_da_server_url(),
215
222
  });
216
- auth_data.has_auth = true;
217
- auth_data.codes = [ 6 ];
218
- auth_data.messages = [ `This API key has at least basic authorization on this server.` ];
223
+ auth_feedback.has_auth = true;
224
+ auth_feedback.codes = [ 6 ];
225
+ auth_feedback.messages = [ `This API key has at least basic authorization on this server.` ];
219
226
 
220
227
  } catch ( error ) {
221
- auth_data.error = error;
228
+ auth_feedback.error = error;
222
229
  if ( error.status === 403 ) {
223
- auth_data.has_auth = false;
224
- auth_data.codes = [ 4 ];
225
- auth_data.messages = [ `This API is unverified on this server. It is probably missing or has been deleted.` ];
230
+ auth_feedback.has_auth = false;
231
+ auth_feedback.codes = [ 4 ];
232
+ auth_feedback.messages = [ `This API is unverified on this server. It is probably missing or has been deleted.` ];
226
233
  } else {
227
234
  // Note on 404, non-existent key: 404 == 403 - Since we're using the
228
235
  // key itself to get its own info, if it doesn't exist, we'll get 403
229
- auth_data.codes = [ 5 ];
230
- auth_data.messages = [ `This API key threw a non-403 error.` ];
236
+ auth_feedback.codes = [ 5 ];
237
+ auth_feedback.messages = [ `This API key threw a non-403 error.` ];
231
238
  } // ends error status
232
239
  } // ends try
233
- } // ends auth_data.codes only has the first code
240
+ } // ends auth_feedback.codes only has the first code
234
241
 
235
- log.debug({ type: `da api info`, code: `ALK0202`,
236
- pre: `API key authentication info`, data: auth_data });
242
+ log.debug({ code: `ALK0202`, context, },
243
+ `API key - what happened during authentication:`,
244
+ auth_feedback
245
+ );
237
246
 
238
- return auth_data;
247
+ return auth_feedback;
239
248
  }; // Ends da_i.get_api_key_access_status()
240
249
 
241
250
  // node -e 'require("./lib/docassemble/docassemble_api_interface.js").delete_project( "create1" )'
@@ -247,16 +256,19 @@ da_i.delete_project = async function ( delete_options ) {
247
256
  let { timeout } = delete_options || { timeout: DEFAULT_MAX_REQUEST_MS };
248
257
 
249
258
  let project_name = session_vars.get_project_name()
250
- log.info({ type: `da api`, code: `ALK0170`, pre: `Trying to delete Project ${ project_name }` });
259
+ log.info({ code: `ALK0170`, context, },
260
+ `Trying to delete Project ${ project_name }`
261
+ );
251
262
 
252
263
  try {
253
264
  let response = await _da_REST.delete_project( timeout );
254
- log.info({ type: `da api`, code: `ALK0171`, pre: `Deleted Project "${ project_name }"` });
265
+ log.info({ code: `ALK0171`, context, },
266
+ `Deleted Project "${ project_name }"`
267
+ );
255
268
  } catch ( error ) {
256
- let msg = `Ran into an error when deleting Project "${ project_name }". See https://docassemble.org/docs/api.html#playground_delete_project.`
257
- log.error({ type: `da api`, code: `ALK0172`, pre: msg });
258
- log.debug({ type: `da api`, code: `ALK0173`, pre: msg });
259
- throw error;
269
+ log.throw({ code: `ALK0172`, context, error: error },
270
+ `Ran into an error when deleting Project "${ project_name }". See https://docassemble.org/docs/api.html#playground_delete_project.`
271
+ );
260
272
  }
261
273
 
262
274
  }; // Ends da_i.delete_project()
@@ -280,7 +292,9 @@ da_i.quietly_try_to_delete_interviews = async function ({ interviews = {}, api_k
280
292
  });
281
293
  responses.push( sub_responses );
282
294
  } else {
283
- log.debug({ type: `da api info`, code: `ALK0191`, pre: `There is no interview to delete.` });
295
+ log.debug({ code: `ALK0191`, context, },
296
+ `There is no interview to delete.`
297
+ );
284
298
  }
285
299
  }
286
300
  return responses;
@@ -295,9 +309,9 @@ da_i.quietly_delete_one_interview = async function ( timeout, { interview = {},
295
309
  let responses = [];
296
310
  for ( let api_key of api_keys ) {
297
311
  try {
298
- log.debug({ type: `da api info`, code: `ALK0188`,
299
- pre: `Trying to delete the interview with the id ${ interview.uid } and an API key.`
300
- });
312
+ log.debug({ code: `ALK0188`, context, },
313
+ `Trying to delete the interview with the id ${ interview.uid } and an API key.`
314
+ );
301
315
 
302
316
  // Actual work:
303
317
  let response = await _da_REST.delete_interview( timeout, {
@@ -306,13 +320,15 @@ da_i.quietly_delete_one_interview = async function ( timeout, { interview = {},
306
320
  });
307
321
  responses.push( response );
308
322
 
309
- log.debug({ type: `da api info`, code: `ALK0189`,
310
- pre: `No error when trying to delete interview ${ interview.uid } at ${ interview.i }.`
311
- });
323
+ log.debug({ code: `ALK0189`, context, },
324
+ `No error when trying to delete interview ${ interview.uid } at ${ interview.i }.`
325
+ );
312
326
  } catch ( error ) {
313
327
  error.interview = interview;
314
- let msg = `Error when deleting the interview. See https://docassemble.org/docs/api.html#session_delete.`
315
- log.debug({ type: `da api info`, code: `ALK0190`, pre: msg, data: error });
328
+ log.debug({ code: `ALK0190`, context, },
329
+ `Error when deleting the interview. See https://docassemble.org/docs/api.html#session_delete.`,
330
+ error
331
+ );
316
332
  }
317
333
  } // ends for api keys
318
334
  return responses;
@@ -347,7 +363,9 @@ da_i.get_interview = async function ({ interview = {}, api_keys = [], options =
347
363
  if ( interview && interview.uid ) {
348
364
  for ( let api_key of api_keys ) {
349
365
  try {
350
- log.debug({ type: `da api info`, code: `ALK0192`, pre: `Trying to get the interview with the id ${ interview.uid } and an API key.` });
366
+ log.debug({ code: `ALK0192`, context, },
367
+ `Trying to get the interview with the id ${ interview.uid } and an API key.`
368
+ );
351
369
 
352
370
  // Actual work:
353
371
  let response = await _da_REST.get_interview( timeout, {
@@ -356,20 +374,28 @@ da_i.get_interview = async function ({ interview = {}, api_keys = [], options =
356
374
  });
357
375
 
358
376
  if ( response && response.data && response.data.items && response.data.items[0] && response.data.items[0].session ) {
359
- log.debug({ type: `da api info`, code: `ALK0193`, pre: `Status ${ response.status }. Got ${ response.data.items[0].session }.`});
377
+ log.debug({ code: `ALK0193`, context, },
378
+ `Status ${ response.status }. Got ${ response.data.items[0].session }.`
379
+ );
360
380
  return response.data.items[0];
361
381
  } else {
362
- log.debug({ type: `da api info`, code: `ALK0194`, pre: `Status ${ response.status }. Cannot find ${ interview.uid }`});
382
+ log.debug({ code: `ALK0194`, context, },
383
+ `Status ${ response.status }. Cannot find ${ interview.uid }`
384
+ );
363
385
  }
364
386
 
365
387
  } catch ( error ) {
366
388
  error.interview = interview;
367
- let msg = `${ 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.`
368
- log.debug({ type: `da api info`, code: `ALK0195`, pre: msg, data: error });
389
+ log.debug({ code: `ALK0195`, context, },
390
+ `${ 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.`,
391
+ error
392
+ );
369
393
  }
370
394
  } // ends for api_keys
371
395
  } else {
372
- log.debug({ type: `da api info`, code: `ALK0196`, pre: `There is no interview to get.` });
396
+ log.debug({ code: `ALK0196`, context, },
397
+ `There is no interview to get.`
398
+ );
373
399
  }
374
400
 
375
401
  return null;
@@ -1,10 +1,20 @@
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
+ const context = `server_install`;
8
+
7
9
  const server_install = async () => {
10
+ /** For packages we are installing on a temporary GitHub docassemble server.
11
+ *
12
+ * - Store that the package is being installed globally on a server as
13
+ * opposed to on a specific author's account (their Playground).
14
+ * - In the repo, find and store the path to directory that stores the
15
+ * package itself instead of the whole repo. For example, EvictionDefense
16
+ * in the docassemble-EvictionDefense repo.
17
+ * */
8
18
  session_vars.save_install_method(`server`);
9
19
 
10
20
  // Get path to folders inside repo main "docassemble" folder
@@ -23,20 +33,33 @@ const server_install = async () => {
23
33
  // Get names of all folders in repo "docassemble" folder
24
34
  let chosen_folder = folders[0];
25
35
  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 );
36
+ let msg = `ALKiln cannot find the package's files. There are 0 folders at "${ path }". Stopping tests.`;
37
+ log.throw({ code: `ALK0044`, context,
38
+ error: new ReferenceError( msg ) // no logs
39
+ });
29
40
  }
30
41
 
31
42
  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
- })
43
+ log.warn({ code: `ALK0045`, context, },
44
+ `There are ${ folders.length } folders at "${ path }": "${ folders.join('", "') }". ALKiln chose to use the folder "${ chosen_folder }".`
45
+ );
46
+ } else {
47
+ log.info({ code: `ALK0218`, context, },
48
+ `ALKiln found the package folder at "${ chosen_folder }".`
49
+ );
37
50
  }
38
51
 
39
52
  session_vars.save_repo_folder_name( chosen_folder );
40
53
  }; // Ends server_install();
41
54
 
55
+ // 1. session_vars - make sure all the required env vars exist
56
+ session_vars.validateEnvironment();
57
+
58
+ // 2. Prepare logs
59
+ // Local devs: This will be separate from your test paths
60
+ // unless you pass a `path` argument into the cucumber run command
61
+ const argv = require(`minimist`)(process.argv.slice(2));
62
+ const log = new Log({ path: argv.path, context, });
63
+
64
+ // 3. run
42
65
  server_install();
@@ -1,26 +1,33 @@
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
+
10
+ const context = `setup`;
9
11
 
10
12
  const setup = async () => {
11
- /** In the docassemble testing account identified by the
12
- * DOCASSEMBLE_DEVELOPER_API_KEY secret, create a new Project,
13
- * pull code into that project, and, if needed, wait for the
14
- * server to reload.
15
- */
13
+ /** This is for a package being installed in an author's account (Playground)
14
+ * on their docassemble server. Using the author's
15
+ * DOCASSEMBLE_DEVELOPER_API_KEY secret, create a new Project, pull code
16
+ * into that project, and, if needed, wait for the server to reload.
17
+ */
16
18
 
17
19
  // Make and save project name
18
20
  let project_name = session_vars.get_project_base_name().replace(/[^A-Za-z0-9]/g, '') + `${Date.now()}`;
19
21
  session_vars.save_project_name( project_name );
20
22
 
21
- log.info({ type: `setup`, code: `ALK0046`, pre: `Starting to upload this package to `
23
+ const starting_msg = `Starting to upload this package to `
22
24
  + `a new Project called ${project_name} in the Playground `
23
- + `of your server's testing account. It may take a couple of minutes.` });
25
+ + `of your server's testing account. It may take a couple of minutes.`;
26
+ log.info({ code: `ALK0046`, context, },
27
+ starting_msg
28
+ );
29
+
30
+ // TODO: 1. Start listening to the normal console here (for debug logging)
24
31
 
25
32
  // dainstall . --apiurl https://site.org --apikey $API_KEY --playground --project=name
26
33
  // I think this won't run on windows? Why do I think this? Where on the
@@ -34,29 +41,37 @@ const setup = async () => {
34
41
  `--playground`, `--project=${ project_name }`
35
42
  ],
36
43
  // Prints messages from the process to the console while it's running
44
+ // result.stderr, result.stdout, etc. will be `null`
37
45
  { stdio: 'inherit', }
38
46
  );
39
47
 
40
- // 403 is invalid API key, but I'm not sure how to output
41
- // the response to find out the error code
48
+ // TODO: 2. Stop listening to the normal console here
42
49
 
50
+ // 403 is invalid API key, but I'm not sure how to output
51
+ // the response to find out the error code. stderr is `null`.
43
52
  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);
53
+ const install_err_msg = `Status code ${ result.status }. Could not `
54
+ + `dainstall this package on your server. See above for more details.`;
55
+ log.throw({ code: `ALK0047`, context,
56
+ before: `☝️ ☝️ ☝️ Error data ☝️ ☝️ ☝️\n`,
57
+ error: `Error while installing project` },
58
+ install_err_msg
59
+ );
50
60
  }
51
61
 
52
- log.success({
53
- type: `setup`,
54
- code: `ALK0048`,
55
- pre: `ALKiln created a Project in your Playground named "${ project_name }"!`
56
- });
62
+ log.success({ code: `ALK0048`, context, },
63
+ `ALKiln created a Project in your Playground named "${ project_name }"!`
64
+ );
57
65
  }; // Ends setup();
58
66
 
59
-
60
- // Before everything, make sure all the required env vars exist
67
+ // 1. session_vars - make sure all the required env vars exist
61
68
  session_vars.validateEnvironment();
69
+
70
+ // 2. Prepare logs
71
+ // Local devs: This will be separate from your test paths
72
+ // unless you pass a `path` argument into the cucumber run command
73
+ const argv = require(`minimist`)(process.argv.slice(2));
74
+ const log = new Log({ path: argv.path, context, });
75
+
76
+ // 3. run
62
77
  setup();