@quenty/nevermore-cli 4.32.0 → 4.33.0

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/CHANGELOG.md +13 -0
  2. package/dist/commands/deploy-command/deploy-init-utils.js +1 -1
  3. package/dist/commands/deploy-command/deploy-init-utils.js.map +1 -1
  4. package/dist/commands/deploy-command/deploy-init.js +2 -2
  5. package/dist/commands/deploy-command/deploy-init.js.map +1 -1
  6. package/dist/commands/init-command/init-package-command.d.ts +2 -0
  7. package/dist/commands/init-command/init-package-command.d.ts.map +1 -1
  8. package/dist/commands/init-command/init-package-command.js +36 -2
  9. package/dist/commands/init-command/init-package-command.js.map +1 -1
  10. package/dist/utils/job-context/batch-script-job-context.d.ts.map +1 -1
  11. package/dist/utils/job-context/batch-script-job-context.js +8 -1
  12. package/dist/utils/job-context/batch-script-job-context.js.map +1 -1
  13. package/dist/utils/job-context/cloud-job-context.d.ts.map +1 -1
  14. package/dist/utils/job-context/cloud-job-context.js +17 -1
  15. package/dist/utils/job-context/cloud-job-context.js.map +1 -1
  16. package/dist/utils/job-context/job-context.d.ts +4 -0
  17. package/dist/utils/job-context/job-context.d.ts.map +1 -1
  18. package/dist/utils/open-cloud/open-cloud-client.d.ts +11 -0
  19. package/dist/utils/open-cloud/open-cloud-client.d.ts.map +1 -1
  20. package/dist/utils/open-cloud/open-cloud-client.js.map +1 -1
  21. package/dist/utils/testing/parsers/batch-log-parser.d.ts.map +1 -1
  22. package/dist/utils/testing/parsers/batch-log-parser.js +11 -2
  23. package/dist/utils/testing/parsers/batch-log-parser.js.map +1 -1
  24. package/package.json +2 -2
  25. package/src/commands/deploy-command/deploy-init-utils.ts +1 -1
  26. package/src/commands/deploy-command/deploy-init.ts +2 -2
  27. package/src/commands/init-command/init-package-command.ts +43 -2
  28. package/src/utils/job-context/batch-script-job-context.ts +10 -1
  29. package/src/utils/job-context/cloud-job-context.ts +18 -1
  30. package/src/utils/job-context/job-context.ts +4 -0
  31. package/src/utils/open-cloud/open-cloud-client.ts +4 -0
  32. package/src/utils/testing/parsers/batch-log-parser.ts +16 -2
  33. package/templates/batch-test-runner.luau +8 -8
  34. package/templates/nevermore-library-package-template/package.json +3 -1
  35. package/templates/nevermore-library-package-template/src/jest.config.lua +3 -0
  36. package/templates/nevermore-library-package-template/test/default.project.json +6 -0
  37. package/templates/nevermore-library-package-template/test/scripts/Server/ServerMain.server.lua +13 -0
  38. package/tsconfig.tsbuildinfo +1 -1
@@ -282,9 +282,18 @@ export class BatchScriptJobContext implements JobContext {
282
282
  const rawLogs = await this._inner.getLogsAsync(deployment);
283
283
 
284
284
  if (!result.success) {
285
+ const stateInfo = result.taskState ? ` (state: ${result.taskState})` : '';
285
286
  OutputHelper.warn(
286
- 'Batch execution task did not complete successfully — parsing partial results'
287
+ `Batch execution task did not complete successfully${stateInfo} — parsing partial results`
287
288
  );
289
+ if (result.errorMessage) {
290
+ OutputHelper.error(result.errorMessage);
291
+ }
292
+ if (!rawLogs || rawLogs.trim().length === 0) {
293
+ OutputHelper.warn(
294
+ 'No logs were returned from the execution — the script may not have started'
295
+ );
296
+ }
288
297
  OutputHelper.verbose(`Raw batch logs:\n${rawLogs || '(empty)'}`);
289
298
  }
290
299
 
@@ -112,7 +112,24 @@ export class CloudJobContext extends BaseJobContext {
112
112
  cloudDeployment.taskPath = task.path;
113
113
  cloudDeployment.taskState = completedTask.state;
114
114
 
115
- return { success: completedTask.state === 'COMPLETE' };
115
+ // Surface error details from the Roblox API when the task didn't complete
116
+ let errorMessage: string | undefined;
117
+ if (completedTask.state !== 'COMPLETE') {
118
+ const parts: string[] = [`Task ended with state: ${completedTask.state}`];
119
+ if (completedTask.error?.message) {
120
+ parts.push(`Error: ${completedTask.error.message}`);
121
+ }
122
+ if (completedTask.error?.code) {
123
+ parts.push(`Code: ${completedTask.error.code}`);
124
+ }
125
+ errorMessage = parts.join('. ');
126
+ }
127
+
128
+ return {
129
+ success: completedTask.state === 'COMPLETE',
130
+ taskState: completedTask.state,
131
+ errorMessage,
132
+ };
116
133
  }
117
134
 
118
135
  async getLogsAsync(deployment: Deployment): Promise<string> {
@@ -24,6 +24,10 @@ export interface ScriptRunResult {
24
24
  * wall-clock measurement.
25
25
  */
26
26
  durationMs?: number;
27
+ /** Final task state (e.g. 'COMPLETE', 'FAILED', 'CANCELLED'). */
28
+ taskState?: string;
29
+ /** Error message from the execution backend, if any. */
30
+ errorMessage?: string;
27
31
  }
28
32
 
29
33
  /**
@@ -20,6 +20,10 @@ export interface LuauTask {
20
20
  | 'FAILED';
21
21
  script: string;
22
22
  timeout?: string;
23
+ /** Script return value (populated on COMPLETE). */
24
+ output?: { results?: Array<{ value?: string }> };
25
+ /** Error details (populated on FAILED). */
26
+ error?: { code?: string; message?: string };
23
27
  }
24
28
 
25
29
  export interface OpenCloudClientOptions {
@@ -134,10 +134,24 @@ export function parseBatchTestLogs(
134
134
  }
135
135
  }
136
136
 
137
+ // ── Warn when the batch produced no recognizable output ──
138
+
139
+ const noOutputAtAll = logSections.size === 0 && summaryResults.size === 0;
140
+ if (noOutputAtAll) {
141
+ OutputHelper.warn(
142
+ `[batch-log-parser] No batch markers or summary found in logs (${lines.length} lines, ${rawLogs.length} chars). ` +
143
+ 'The batch script may not have started or produced output.'
144
+ );
145
+ }
146
+
137
147
  // ── Pass 3: Combine log sections with summary results ──
138
148
 
149
+ // When the batch produced zero output, surface the raw error in every
150
+ // package's logs so reporters show the actual failure instead of "(no output)".
151
+ const fallbackLogs = noOutputAtAll ? rawLogs.trim() : '';
152
+
139
153
  for (const [packageName, slug] of slugMap) {
140
- const sectionLogs = logSections.get(slug) ?? '';
154
+ const sectionLogs = logSections.get(slug) ?? fallbackLogs;
141
155
  const summarySuccess = summaryResults.get(slug);
142
156
 
143
157
  // The JSON summary (pcall result) is the primary success signal.
@@ -150,7 +164,7 @@ export function parseBatchTestLogs(
150
164
  }
151
165
  }
152
166
 
153
- if (!success) {
167
+ if (!success && !noOutputAtAll) {
154
168
  console.error(
155
169
  `[batch-log-parser] ${slug}: summarySuccess=${summarySuccess} hasLogs=${
156
170
  sectionLogs.length > 0
@@ -19,7 +19,7 @@ for _, slug in packageSlugs do
19
19
  if inst then
20
20
  scriptSources[slug] = inst.Source
21
21
  else
22
- error(`Failed to find script source for {slug}`)
22
+ warn(`[BatchTest] Failed to find script source for {slug}`)
23
23
  end
24
24
  end
25
25
 
@@ -41,8 +41,8 @@ local results: { Result } = {}
41
41
  for _, slug in packageSlugs do
42
42
  local scriptSource = scriptSources[slug]
43
43
  if not scriptSource then
44
- warn(`[BatchTest] No script for {slug}`)
45
44
  print("===BATCH_TEST_BEGIN " .. slug .. "===")
45
+ warn(`[BatchTest] {slug}: No test script found for {slug}. Is it tagged with _BatchTest_{slug}?`)
46
46
  print("===BATCH_TEST_END " .. slug .. " FAIL 0===")
47
47
  table.insert(results, { slug = slug, success = false, durationMs = 0, error = "No test script found" })
48
48
  continue
@@ -93,14 +93,14 @@ for _, slug in packageSlugs do
93
93
 
94
94
  -- CLEANUP: restore service state
95
95
  allPackages[slug].Parent = nil
96
- for _, c in workspace:GetChildren() do
97
- if not preWorkspace[c] and not c:IsA("Terrain") and not c:IsA("Camera") then
98
- pcall(c.Destroy, c)
96
+ for _, child in workspace:GetChildren() do
97
+ if not preWorkspace[child] and not child:IsA("Terrain") and not child:IsA("Camera") then
98
+ pcall(child.Destroy, child)
99
99
  end
100
100
  end
101
- for _, c in ReplicatedStorage:GetChildren() do
102
- if not preRunService[c] then
103
- pcall(c.Destroy, c)
101
+ for _, child in ReplicatedStorage:GetChildren() do
102
+ if not preRunService[child] then
103
+ pcall(child.Destroy, child)
104
104
  end
105
105
  end
106
106
  -- Destroy injected LoaderLinks (non-archivable, created by LoaderLinkCreator)
@@ -28,7 +28,9 @@
28
28
  "Quenty"
29
29
  ],
30
30
  "dependencies": {
31
- "@quenty/loader": "workspace:*"
31
+ "@quenty/loader": "workspace:*",
32
+ "@quenty/nevermore-test-runner": "workspace:*",
33
+ "@quentystudios/jest-lua": "3.10.0-quenty.2"
32
34
  },
33
35
  "publishConfig": {
34
36
  "access": "public"
@@ -0,0 +1,3 @@
1
+ return {
2
+ testMatch = { "**/*.spec" },
3
+ }
@@ -11,8 +11,14 @@
11
11
  "tree": {
12
12
  "$className": "DataModel",
13
13
  "ServerScriptService": {
14
+ "$properties": {
15
+ "LoadStringEnabled": true
16
+ },
14
17
  "{{packageName}}": {
15
18
  "$path": ".."
19
+ },
20
+ "Script": {
21
+ "$path": "scripts/Server"
16
22
  }
17
23
  }
18
24
  }
@@ -0,0 +1,13 @@
1
+ --[[
2
+ @class ServerMain
3
+ ]]
4
+ local ServerScriptService = game:GetService("ServerScriptService")
5
+
6
+ local root = ServerScriptService.{{packageName}}
7
+ local loader = root:FindFirstChild("LoaderUtils", true).Parent
8
+ local require = require(loader).bootstrapGame(root)
9
+
10
+ local NevermoreTestRunnerUtils = require("NevermoreTestRunnerUtils")
11
+ if NevermoreTestRunnerUtils.runTestsIfNeededAsync(root) then
12
+ return
13
+ end