@frankzhang2026/opencode-android-orchestrator 0.9.0 → 0.10.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.
- package/CHANGELOG.md +16 -0
- package/README.md +55 -12
- package/dist/config/commit-message-prefix.d.ts +31 -0
- package/dist/config/commit-message-prefix.d.ts.map +1 -0
- package/dist/config/commit-message-prefix.js +155 -0
- package/dist/config/commit-message-prefix.js.map +1 -0
- package/dist/config/verification-policy.d.ts +5 -3
- package/dist/config/verification-policy.d.ts.map +1 -1
- package/dist/config/verification-policy.js +32 -13
- package/dist/config/verification-policy.js.map +1 -1
- package/dist/doctor/installation.d.ts.map +1 -1
- package/dist/doctor/installation.js +78 -0
- package/dist/doctor/installation.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/installer/adaptive-templates.d.ts +6 -2
- package/dist/installer/adaptive-templates.d.ts.map +1 -1
- package/dist/installer/adaptive-templates.js +8 -0
- package/dist/installer/adaptive-templates.js.map +1 -1
- package/dist/installer/index.d.ts +1 -0
- package/dist/installer/index.d.ts.map +1 -1
- package/dist/installer/index.js +1 -0
- package/dist/installer/index.js.map +1 -1
- package/dist/installer/init.d.ts +4 -1
- package/dist/installer/init.d.ts.map +1 -1
- package/dist/installer/init.js +60 -9
- package/dist/installer/init.js.map +1 -1
- package/dist/installer/opencode-config.d.ts +3 -3
- package/dist/installer/opencode-config.d.ts.map +1 -1
- package/dist/installer/opencode-config.js +1 -1
- package/dist/installer/opencode-config.js.map +1 -1
- package/dist/installer/upgrade.d.ts +5 -1
- package/dist/installer/upgrade.d.ts.map +1 -1
- package/dist/installer/upgrade.js +81 -10
- package/dist/installer/upgrade.js.map +1 -1
- package/docs/MIGRATION.md +27 -14
- package/docs/SECURITY.md +14 -5
- package/docs/TROUBLESHOOTING.md +13 -11
- package/package.json +1 -1
- package/templates/README.md +9 -1
- package/templates/automation/config.json +2 -1
- package/templates/automation/config.schema.json +7 -2
- package/templates/scripts/automation/abort-task.sh +2 -1
- package/templates/scripts/automation/accept-and-integrate.sh +4 -2
- package/templates/scripts/automation/approve-and-run.sh +1 -0
- package/templates/scripts/automation/lib.sh +93 -2
- package/templates/scripts/automation/orchestrate-task.sh +1 -0
- package/templates/scripts/automation/preflight.sh +11 -0
- package/templates/scripts/automation/show-acceptance-review.sh +3 -0
- package/templates/scripts/automation/tests/run-tests.sh +59 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.10.0 - 2026-09-11
|
|
4
|
+
|
|
5
|
+
- Add automation configuration schema v5 with
|
|
6
|
+
`commitMessagePrefixMode`, defaulting to `required`.
|
|
7
|
+
- Create and preserve the human-maintained
|
|
8
|
+
`automation/automation-commit-prefix` sidecar during init and upgrade without
|
|
9
|
+
adding it to the managed installation manifest.
|
|
10
|
+
- Block new orchestrated tasks until the required prefix file contains exactly
|
|
11
|
+
one valid non-comment line, while keeping init, upgrade, doctor, and shadow
|
|
12
|
+
verification usable with the initial comments-only template.
|
|
13
|
+
- Prefix both accepted task commits and abort recovery commits automatically,
|
|
14
|
+
read the latest value from the source worktree, and show the resulting message
|
|
15
|
+
in the human acceptance card without prompting on each commit.
|
|
16
|
+
- Exclude the prefix sidecar from task scope, evidence, cleanliness, automated
|
|
17
|
+
staging, and plugin-created commits, including isolated-worktree execution.
|
|
18
|
+
|
|
3
19
|
## 0.9.0 - 2026-09-09
|
|
4
20
|
|
|
5
21
|
- Make Gradle's evaluated project model authoritative for Android module
|
package/README.md
CHANGED
|
@@ -20,7 +20,9 @@ package, and removes the runtime GitHub/Superpowers plugin dependency. Version
|
|
|
20
20
|
project-specific `AGENTS.md` content outside the managed marker block. Version
|
|
21
21
|
`0.9.0` discovers Android modules from Gradle's evaluated runtime model, so
|
|
22
22
|
dynamic settings logic and company convention plugins no longer leave modules
|
|
23
|
-
or their focused-test tasks out of generated configuration.
|
|
23
|
+
or their focused-test tasks out of generated configuration. Version `0.10.0`
|
|
24
|
+
adds a user-maintained commit-message prefix that is read automatically for
|
|
25
|
+
each task commit and blocks task startup until the required value is filled.
|
|
24
26
|
|
|
25
27
|
## Documentation
|
|
26
28
|
|
|
@@ -55,8 +57,9 @@ project builds retain their configured cache behavior.
|
|
|
55
57
|
## Quick start
|
|
56
58
|
|
|
57
59
|
```sh
|
|
58
|
-
npx @frankzhang2026/opencode-android-orchestrator@0.
|
|
59
|
-
|
|
60
|
+
npx @frankzhang2026/opencode-android-orchestrator@0.10.0 init .
|
|
61
|
+
$EDITOR automation/automation-commit-prefix
|
|
62
|
+
npx @frankzhang2026/opencode-android-orchestrator@0.10.0 doctor .
|
|
60
63
|
opencode --agent scheduled-planner .
|
|
61
64
|
```
|
|
62
65
|
|
|
@@ -66,7 +69,7 @@ a task contract without selecting a primary module. To intentionally restrict
|
|
|
66
69
|
generated contracts to one module, opt into primary-module scope:
|
|
67
70
|
|
|
68
71
|
```sh
|
|
69
|
-
npx @frankzhang2026/opencode-android-orchestrator@0.
|
|
72
|
+
npx @frankzhang2026/opencode-android-orchestrator@0.10.0 init . \
|
|
70
73
|
--module-scope primary \
|
|
71
74
|
--primary-module :mobile
|
|
72
75
|
```
|
|
@@ -94,14 +97,14 @@ For an existing manifest-managed installation whose generated module/task
|
|
|
94
97
|
lists are incomplete, refresh all derived Gradle data in one upgrade:
|
|
95
98
|
|
|
96
99
|
```sh
|
|
97
|
-
npx @frankzhang2026/opencode-android-orchestrator@0.
|
|
100
|
+
npx @frankzhang2026/opencode-android-orchestrator@0.10.0 upgrade . \
|
|
98
101
|
--refresh-gradle-discovery
|
|
99
102
|
```
|
|
100
103
|
|
|
101
104
|
The refresh updates `androidProject.modules`, production/test paths, protected
|
|
102
105
|
build files, and all five Gradle task groups together. Without the flag,
|
|
103
106
|
upgrade preserves an intentionally customized task matrix. The explicit
|
|
104
|
-
refresh is also allowed on an already installed `0.
|
|
107
|
+
refresh is also allowed on an already installed `0.10.0` when the evaluated
|
|
105
108
|
Gradle model has changed; an unchanged refresh remains byte-idempotent.
|
|
106
109
|
|
|
107
110
|
### Bundled workflow skills
|
|
@@ -146,13 +149,47 @@ unchanged. Android lint is disabled by default; setting `lintEnabled` to `true`
|
|
|
146
149
|
runs the discovered `lintTasks` in those gates. Assemble, scope, evidence, and
|
|
147
150
|
required device-test checks are unaffected by either flag.
|
|
148
151
|
|
|
149
|
-
These two booleans are the operator-editable
|
|
150
|
-
managed configuration. Change their values in
|
|
152
|
+
These two booleans and `commitMessagePrefixMode` are the operator-editable
|
|
153
|
+
exceptions in the otherwise managed configuration. Change their values in
|
|
154
|
+
place and commit the file before
|
|
151
155
|
starting orchestration. Doctor accepts these policy changes and a later
|
|
152
156
|
upgrade preserves them. Do not reformat or edit unrelated generated fields.
|
|
153
157
|
Legacy installations without the fields receive `true` for unit tests and
|
|
154
158
|
`false` for lint during upgrade.
|
|
155
159
|
|
|
160
|
+
### Automatic commit-message prefix
|
|
161
|
+
|
|
162
|
+
Automation configuration schema V5 adds this repository policy:
|
|
163
|
+
|
|
164
|
+
```json
|
|
165
|
+
{
|
|
166
|
+
"commitMessagePrefixMode": "required"
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
`required` is the default. During `init` or `upgrade`, the plugin creates
|
|
171
|
+
`automation/automation-commit-prefix` only when it does not already exist. The
|
|
172
|
+
initial file contains comments but no value, so installation or upgrade still
|
|
173
|
+
succeeds while new task startup remains blocked. Edit it once and put the
|
|
174
|
+
current company text on exactly one non-comment line, for example:
|
|
175
|
+
|
|
176
|
+
```text
|
|
177
|
+
本周研发批次
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
The plugin reads that file automatically; it never asks for the value on each
|
|
181
|
+
commit. A successful task commit becomes, for example,
|
|
182
|
+
`本周研发批次 Implement Add profile validation (TASK-123)`, and an abort
|
|
183
|
+
recovery commit receives the same current prefix. Change the one line whenever
|
|
184
|
+
the company text changes; the next plugin-created commit uses the new value.
|
|
185
|
+
|
|
186
|
+
The prefix file is human-owned, excluded from the 47 managed-file hashes and
|
|
187
|
+
from task diffs, evidence, automated staging, and plugin-created commits, and
|
|
188
|
+
preserved by upgrade and uninstall. Set `commitMessagePrefixMode` to `disabled`
|
|
189
|
+
only when repository policy explicitly does not require a prefix. This setting
|
|
190
|
+
governs commits made by the orchestrator; use a repository Git hook if manually
|
|
191
|
+
created commits must also be enforced.
|
|
192
|
+
|
|
156
193
|
### Managed long-command timeout
|
|
157
194
|
|
|
158
195
|
The plugin raises direct managed lifecycle commands such as `claim-task.sh`,
|
|
@@ -220,7 +257,10 @@ The install transaction manages 47 project-local paths:
|
|
|
220
257
|
| Android automation config and task example | 2 | Generate from detected modules. |
|
|
221
258
|
| `AGENTS.md` and OpenCode JSON/JSONC | 2 | Merge bounded content without replacing unrelated settings. |
|
|
222
259
|
|
|
223
|
-
After installation,
|
|
260
|
+
After installation or upgrade, fill the required commit-prefix file before
|
|
261
|
+
starting `scheduled-planner`; preflight rejects a missing, comments-only,
|
|
262
|
+
multi-value, oversized, or symbolic-link prefix file. Then use the planner's
|
|
263
|
+
interactive flow.
|
|
224
264
|
The `/acceptance <TASK-ID>`, `/resume-task <TASK-ID>`,
|
|
225
265
|
`/resume-review <TASK-ID>`, and `/abort-task <TASK-ID>` commands are
|
|
226
266
|
recovery/re-entry points; they do not replace the required fresh approval
|
|
@@ -240,6 +280,8 @@ Cache setting, `0.6.1` adds configurable long-command timeouts plus bounded
|
|
|
240
280
|
baseline-interruption recovery, and `0.7.0` makes unit-test and lint gates
|
|
241
281
|
repository-configurable. Version `0.8.0` moves the five required workflow
|
|
242
282
|
skills into the npm package and removes their external plugin dependency.
|
|
283
|
+
Version `0.9.0` makes Gradle's evaluated module model authoritative, and
|
|
284
|
+
`0.10.0` adds the required user-maintained commit prefix and task-start gate.
|
|
243
285
|
|
|
244
286
|
The cross-version plugin entry, OpenCode version doctor, Android/Gradle project
|
|
245
287
|
discovery, audited V4 OpenCode/configuration resources, and all deterministic
|
|
@@ -304,7 +346,7 @@ Implemented checks include:
|
|
|
304
346
|
Shell executable modes, merges OpenCode JSON/JSONC and one bounded AGENTS
|
|
305
347
|
block, bootstraps the optional human-owned worktree allowlist when missing,
|
|
306
348
|
and is byte-idempotent for an unchanged installed version
|
|
307
|
-
- write-before-complete verification using the
|
|
349
|
+
- write-before-complete verification using the 46-case automation suite and a
|
|
308
350
|
read-only shadow run; any failure restores original files before reporting
|
|
309
351
|
the error
|
|
310
352
|
- an installation-aware, read-only doctor that authenticates the 47-file
|
|
@@ -382,7 +424,8 @@ preparation alone as resource installation;
|
|
|
382
424
|
## Init
|
|
383
425
|
|
|
384
426
|
```sh
|
|
385
|
-
npx @frankzhang2026/opencode-android-orchestrator@0.
|
|
427
|
+
npx @frankzhang2026/opencode-android-orchestrator@0.10.0 init .
|
|
428
|
+
$EDITOR automation/automation-commit-prefix
|
|
386
429
|
opencode --agent scheduled-planner .
|
|
387
430
|
```
|
|
388
431
|
|
|
@@ -503,7 +546,7 @@ the immediately preceding `question` result and bind at least the approval
|
|
|
503
546
|
kind, task ID, session, message, relevant sealed SHA/branch, timestamp, and
|
|
504
547
|
nonce. It must also retain fixed-script authentication, strict state
|
|
505
548
|
preconditions, project/worktree bounds, abort propagation that terminates child
|
|
506
|
-
processes, bounded structured output, exact per-agent permissions, the
|
|
549
|
+
processes, bounded structured output, exact per-agent permissions, the 46-case
|
|
507
550
|
transaction suite, and real `1.14.22`/`1.15.13` integration tests. Internal
|
|
508
551
|
Coder/Reviewer transition scripts remain private orchestration details rather
|
|
509
552
|
than public tools.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export declare const COMMIT_MESSAGE_PREFIX_RELATIVE_PATH = "automation/automation-commit-prefix";
|
|
2
|
+
export declare const INITIAL_COMMIT_MESSAGE_PREFIX_CONTENT: string;
|
|
3
|
+
export declare const COMMIT_MESSAGE_PREFIX_FILE_MAX_BYTES = 4096;
|
|
4
|
+
export declare const COMMIT_MESSAGE_PREFIX_MAX_BYTES = 256;
|
|
5
|
+
export type CommitMessagePrefixMode = "required" | "disabled";
|
|
6
|
+
export declare const DEFAULT_COMMIT_MESSAGE_PREFIX_MODE: CommitMessagePrefixMode;
|
|
7
|
+
export declare function isCommitMessagePrefixMode(value: unknown): value is CommitMessagePrefixMode;
|
|
8
|
+
export type CommitMessagePrefixInspectionStatus = "disabled" | "missing" | "unconfigured" | "configured" | "invalid";
|
|
9
|
+
export interface CommitMessagePrefixInspection {
|
|
10
|
+
path: string;
|
|
11
|
+
mode: CommitMessagePrefixMode;
|
|
12
|
+
status: CommitMessagePrefixInspectionStatus;
|
|
13
|
+
prefix: string | null;
|
|
14
|
+
details: readonly string[];
|
|
15
|
+
}
|
|
16
|
+
export type CommitMessagePrefixInitializationStatus = "disabled" | "created-unconfigured" | "existing-unconfigured" | "existing-configured";
|
|
17
|
+
export interface CommitMessagePrefixInitialization {
|
|
18
|
+
path: string;
|
|
19
|
+
mode: CommitMessagePrefixMode;
|
|
20
|
+
status: CommitMessagePrefixInitializationStatus;
|
|
21
|
+
}
|
|
22
|
+
export type CommitMessagePrefixErrorCode = "COMMIT_MESSAGE_PREFIX_INVALID" | "COMMIT_MESSAGE_PREFIX_ROLLBACK_FAILED" | "COMMIT_MESSAGE_PREFIX_WRITE_FAILED";
|
|
23
|
+
export declare class CommitMessagePrefixError extends Error {
|
|
24
|
+
readonly code: CommitMessagePrefixErrorCode;
|
|
25
|
+
readonly details: readonly string[];
|
|
26
|
+
constructor(code: CommitMessagePrefixErrorCode, message: string, details?: readonly string[]);
|
|
27
|
+
}
|
|
28
|
+
export declare function inspectCommitMessagePrefix(targetDirectory: string, mode: CommitMessagePrefixMode): CommitMessagePrefixInspection;
|
|
29
|
+
export declare function initializeCommitMessagePrefix(targetDirectory: string, mode: CommitMessagePrefixMode): CommitMessagePrefixInitialization;
|
|
30
|
+
export declare function rollbackCommitMessagePrefixInitialization(initialization: CommitMessagePrefixInitialization): void;
|
|
31
|
+
//# sourceMappingURL=commit-message-prefix.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commit-message-prefix.d.ts","sourceRoot":"","sources":["../../src/config/commit-message-prefix.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,mCAAmC,wCACT,CAAC;AACxC,eAAO,MAAM,qCAAqC,QAItC,CAAC;AACb,eAAO,MAAM,oCAAoC,OAAQ,CAAC;AAC1D,eAAO,MAAM,+BAA+B,MAAM,CAAC;AAEnD,MAAM,MAAM,uBAAuB,GAAG,UAAU,GAAG,UAAU,CAAC;AAE9D,eAAO,MAAM,kCAAkC,EAAE,uBACrC,CAAC;AAEb,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,uBAAuB,CAElC;AAED,MAAM,MAAM,mCAAmC,GAC3C,UAAU,GACV,SAAS,GACT,cAAc,GACd,YAAY,GACZ,SAAS,CAAC;AAEd,MAAM,WAAW,6BAA6B;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,uBAAuB,CAAC;IAC9B,MAAM,EAAE,mCAAmC,CAAC;IAC5C,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CAC5B;AAED,MAAM,MAAM,uCAAuC,GAC/C,UAAU,GACV,sBAAsB,GACtB,uBAAuB,GACvB,qBAAqB,CAAC;AAE1B,MAAM,WAAW,iCAAiC;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,uBAAuB,CAAC;IAC9B,MAAM,EAAE,uCAAuC,CAAC;CACjD;AAED,MAAM,MAAM,4BAA4B,GACpC,+BAA+B,GAC/B,uCAAuC,GACvC,oCAAoC,CAAC;AAEzC,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,QAAQ,CAAC,IAAI,EAAE,4BAA4B,CAAC;IAC5C,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;gBAGlC,IAAI,EAAE,4BAA4B,EAClC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,SAAS,MAAM,EAAO;CAOlC;AA4BD,wBAAgB,0BAA0B,CACxC,eAAe,EAAE,MAAM,EACvB,IAAI,EAAE,uBAAuB,GAC5B,6BAA6B,CAqF/B;AAcD,wBAAgB,6BAA6B,CAC3C,eAAe,EAAE,MAAM,EACvB,IAAI,EAAE,uBAAuB,GAC5B,iCAAiC,CAoCnC;AAED,wBAAgB,yCAAyC,CACvD,cAAc,EAAE,iCAAiC,GAChD,IAAI,CA8BN"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { lstatSync, readFileSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { TextDecoder } from "node:util";
|
|
4
|
+
export const COMMIT_MESSAGE_PREFIX_RELATIVE_PATH = "automation/automation-commit-prefix";
|
|
5
|
+
export const INITIAL_COMMIT_MESSAGE_PREFIX_CONTENT = [
|
|
6
|
+
"# 必填:请在下一行填写当前 Git 提交文案前缀",
|
|
7
|
+
"# 示例:XXX",
|
|
8
|
+
"",
|
|
9
|
+
].join("\n");
|
|
10
|
+
export const COMMIT_MESSAGE_PREFIX_FILE_MAX_BYTES = 4_096;
|
|
11
|
+
export const COMMIT_MESSAGE_PREFIX_MAX_BYTES = 256;
|
|
12
|
+
export const DEFAULT_COMMIT_MESSAGE_PREFIX_MODE = "required";
|
|
13
|
+
export function isCommitMessagePrefixMode(value) {
|
|
14
|
+
return value === "required" || value === "disabled";
|
|
15
|
+
}
|
|
16
|
+
export class CommitMessagePrefixError extends Error {
|
|
17
|
+
code;
|
|
18
|
+
details;
|
|
19
|
+
constructor(code, message, details = []) {
|
|
20
|
+
super(message);
|
|
21
|
+
this.name = "CommitMessagePrefixError";
|
|
22
|
+
this.code = code;
|
|
23
|
+
this.details = details;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function filesystemErrorCode(error) {
|
|
27
|
+
if (typeof error === "object" &&
|
|
28
|
+
error !== null &&
|
|
29
|
+
"code" in error &&
|
|
30
|
+
typeof error.code === "string") {
|
|
31
|
+
return error.code;
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
function invalidInspection(path, mode, detail) {
|
|
36
|
+
return {
|
|
37
|
+
path,
|
|
38
|
+
mode,
|
|
39
|
+
status: "invalid",
|
|
40
|
+
prefix: null,
|
|
41
|
+
details: [detail],
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export function inspectCommitMessagePrefix(targetDirectory, mode) {
|
|
45
|
+
const path = join(targetDirectory, ...COMMIT_MESSAGE_PREFIX_RELATIVE_PATH.split("/"));
|
|
46
|
+
if (mode === "disabled") {
|
|
47
|
+
return { path, mode, status: "disabled", prefix: null, details: [] };
|
|
48
|
+
}
|
|
49
|
+
let stats;
|
|
50
|
+
try {
|
|
51
|
+
stats = lstatSync(path);
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
if (filesystemErrorCode(error) === "ENOENT") {
|
|
55
|
+
return { path, mode, status: "missing", prefix: null, details: [] };
|
|
56
|
+
}
|
|
57
|
+
return invalidInspection(path, mode, error instanceof Error ? error.message : String(error));
|
|
58
|
+
}
|
|
59
|
+
if (stats.isSymbolicLink() || !stats.isFile()) {
|
|
60
|
+
return invalidInspection(path, mode, "The commit-message prefix must be a regular file, not a symlink or directory.");
|
|
61
|
+
}
|
|
62
|
+
if (stats.size > COMMIT_MESSAGE_PREFIX_FILE_MAX_BYTES) {
|
|
63
|
+
return invalidInspection(path, mode, `The commit-message prefix file exceeds ${String(COMMIT_MESSAGE_PREFIX_FILE_MAX_BYTES)} bytes.`);
|
|
64
|
+
}
|
|
65
|
+
let content;
|
|
66
|
+
try {
|
|
67
|
+
content = new TextDecoder("utf-8", { fatal: true }).decode(readFileSync(path));
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
return invalidInspection(path, mode, `The commit-message prefix file is unreadable or is not valid UTF-8: ${error instanceof Error ? error.message : String(error)}`);
|
|
71
|
+
}
|
|
72
|
+
const activeLines = content
|
|
73
|
+
.split(/\r?\n/)
|
|
74
|
+
.filter((line) => line.trim().length > 0 && !line.trimStart().startsWith("#"));
|
|
75
|
+
if (activeLines.length === 0) {
|
|
76
|
+
return { path, mode, status: "unconfigured", prefix: null, details: [] };
|
|
77
|
+
}
|
|
78
|
+
if (activeLines.length !== 1) {
|
|
79
|
+
return invalidInspection(path, mode, "The commit-message prefix file must contain exactly one non-comment line.");
|
|
80
|
+
}
|
|
81
|
+
const prefix = activeLines[0];
|
|
82
|
+
if (prefix !== prefix.trim()) {
|
|
83
|
+
return invalidInspection(path, mode, "The commit-message prefix must not have leading or trailing whitespace.");
|
|
84
|
+
}
|
|
85
|
+
if (/[\u0000-\u001f\u007f]/u.test(prefix)) {
|
|
86
|
+
return invalidInspection(path, mode, "The commit-message prefix must not contain control characters.");
|
|
87
|
+
}
|
|
88
|
+
if (Buffer.byteLength(prefix, "utf8") > COMMIT_MESSAGE_PREFIX_MAX_BYTES) {
|
|
89
|
+
return invalidInspection(path, mode, `The commit-message prefix exceeds ${String(COMMIT_MESSAGE_PREFIX_MAX_BYTES)} UTF-8 bytes.`);
|
|
90
|
+
}
|
|
91
|
+
return { path, mode, status: "configured", prefix, details: [] };
|
|
92
|
+
}
|
|
93
|
+
function assertUsableInspection(inspection) {
|
|
94
|
+
if (inspection.status === "invalid") {
|
|
95
|
+
throw new CommitMessagePrefixError("COMMIT_MESSAGE_PREFIX_INVALID", "The commit-message prefix file is invalid.", [inspection.path, ...inspection.details]);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
export function initializeCommitMessagePrefix(targetDirectory, mode) {
|
|
99
|
+
const initial = inspectCommitMessagePrefix(targetDirectory, mode);
|
|
100
|
+
assertUsableInspection(initial);
|
|
101
|
+
if (initial.status === "disabled") {
|
|
102
|
+
return { path: initial.path, mode, status: "disabled" };
|
|
103
|
+
}
|
|
104
|
+
if (initial.status === "configured") {
|
|
105
|
+
return { path: initial.path, mode, status: "existing-configured" };
|
|
106
|
+
}
|
|
107
|
+
if (initial.status === "unconfigured") {
|
|
108
|
+
return { path: initial.path, mode, status: "existing-unconfigured" };
|
|
109
|
+
}
|
|
110
|
+
try {
|
|
111
|
+
writeFileSync(initial.path, INITIAL_COMMIT_MESSAGE_PREFIX_CONTENT, {
|
|
112
|
+
flag: "wx",
|
|
113
|
+
mode: 0o644,
|
|
114
|
+
});
|
|
115
|
+
return { path: initial.path, mode, status: "created-unconfigured" };
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
if (filesystemErrorCode(error) === "EEXIST") {
|
|
119
|
+
const concurrent = inspectCommitMessagePrefix(targetDirectory, mode);
|
|
120
|
+
assertUsableInspection(concurrent);
|
|
121
|
+
if (concurrent.status === "configured") {
|
|
122
|
+
return { path: concurrent.path, mode, status: "existing-configured" };
|
|
123
|
+
}
|
|
124
|
+
if (concurrent.status === "unconfigured") {
|
|
125
|
+
return { path: concurrent.path, mode, status: "existing-unconfigured" };
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
throw new CommitMessagePrefixError("COMMIT_MESSAGE_PREFIX_WRITE_FAILED", `Unable to create the commit-message prefix file: ${initial.path}`, [error instanceof Error ? error.message : String(error)]);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
export function rollbackCommitMessagePrefixInitialization(initialization) {
|
|
132
|
+
if (initialization.status !== "created-unconfigured") {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
try {
|
|
136
|
+
const stats = lstatSync(initialization.path);
|
|
137
|
+
const content = readFileSync(initialization.path, "utf8");
|
|
138
|
+
if (stats.isSymbolicLink() ||
|
|
139
|
+
!stats.isFile() ||
|
|
140
|
+
content !== INITIAL_COMMIT_MESSAGE_PREFIX_CONTENT) {
|
|
141
|
+
throw new Error("The automatically created prefix file changed and was preserved.");
|
|
142
|
+
}
|
|
143
|
+
unlinkSync(initialization.path);
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
if (filesystemErrorCode(error) === "ENOENT") {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
throw new CommitMessagePrefixError("COMMIT_MESSAGE_PREFIX_ROLLBACK_FAILED", "The automatically created commit-message prefix file could not be rolled back safely.", [
|
|
150
|
+
error instanceof Error ? error.message : String(error),
|
|
151
|
+
`Preserved path: ${initialization.path}`,
|
|
152
|
+
]);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=commit-message-prefix.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commit-message-prefix.js","sourceRoot":"","sources":["../../src/config/commit-message-prefix.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { type CommitMessagePrefixMode } from "./commit-message-prefix.js";
|
|
1
2
|
export declare const AUTOMATION_CONFIG_RELATIVE_PATH = "automation/config.json";
|
|
2
3
|
export declare const DEFAULT_LINT_ENABLED = false;
|
|
3
4
|
export declare const DEFAULT_UNIT_TESTS_ENABLED = true;
|
|
4
5
|
export interface VerificationPolicy {
|
|
6
|
+
commitMessagePrefixMode: CommitMessagePrefixMode;
|
|
5
7
|
lintEnabled: boolean;
|
|
6
8
|
unitTestsEnabled: boolean;
|
|
7
9
|
}
|
|
@@ -11,9 +13,9 @@ interface ContentFingerprint {
|
|
|
11
13
|
}
|
|
12
14
|
/**
|
|
13
15
|
* Accept an otherwise byte-identical generated configuration when an operator
|
|
14
|
-
* changed only
|
|
15
|
-
*
|
|
16
|
-
*
|
|
16
|
+
* changed only supported operator policy fields. Enumerating prior states lets
|
|
17
|
+
* upgrades authenticate the remaining managed content against the installed
|
|
18
|
+
* manifest without storing another file copy.
|
|
17
19
|
*/
|
|
18
20
|
export declare function matchesManifestModuloVerificationPolicy(content: Uint8Array, expected: ContentFingerprint): boolean;
|
|
19
21
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verification-policy.d.ts","sourceRoot":"","sources":["../../src/config/verification-policy.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"verification-policy.d.ts","sourceRoot":"","sources":["../../src/config/verification-policy.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,uBAAuB,EAC7B,MAAM,4BAA4B,CAAC;AAEpC,eAAO,MAAM,+BAA+B,2BAA2B,CAAC;AACxE,eAAO,MAAM,oBAAoB,QAAQ,CAAC;AAC1C,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAE/C,MAAM,WAAW,kBAAkB;IACjC,uBAAuB,EAAE,uBAAuB,CAAC;IACjD,WAAW,EAAE,OAAO,CAAC;IACrB,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAED,UAAU,kBAAkB;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAiCD;;;;;GAKG;AACH,wBAAgB,uCAAuC,CACrD,OAAO,EAAE,UAAU,EACnB,QAAQ,EAAE,kBAAkB,GAC3B,OAAO,CAkDT"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
+
import { DEFAULT_COMMIT_MESSAGE_PREFIX_MODE, isCommitMessagePrefixMode, } from "./commit-message-prefix.js";
|
|
2
3
|
export const AUTOMATION_CONFIG_RELATIVE_PATH = "automation/config.json";
|
|
3
4
|
export const DEFAULT_LINT_ENABLED = false;
|
|
4
5
|
export const DEFAULT_UNIT_TESTS_ENABLED = true;
|
|
@@ -16,11 +17,19 @@ function setOptionalBoolean(value, key, configured) {
|
|
|
16
17
|
value[key] = configured;
|
|
17
18
|
}
|
|
18
19
|
}
|
|
20
|
+
function setOptionalCommitMessagePrefixMode(value, configured) {
|
|
21
|
+
if (configured === undefined) {
|
|
22
|
+
delete value.commitMessagePrefixMode;
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
value.commitMessagePrefixMode = configured;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
19
28
|
/**
|
|
20
29
|
* Accept an otherwise byte-identical generated configuration when an operator
|
|
21
|
-
* changed only
|
|
22
|
-
*
|
|
23
|
-
*
|
|
30
|
+
* changed only supported operator policy fields. Enumerating prior states lets
|
|
31
|
+
* upgrades authenticate the remaining managed content against the installed
|
|
32
|
+
* manifest without storing another file copy.
|
|
24
33
|
*/
|
|
25
34
|
export function matchesManifestModuloVerificationPolicy(content, expected) {
|
|
26
35
|
if (content.byteLength === expected.size &&
|
|
@@ -38,19 +47,29 @@ export function matchesManifestModuloVerificationPolicy(content, expected) {
|
|
|
38
47
|
return false;
|
|
39
48
|
}
|
|
40
49
|
if (typeof parsed.lintEnabled !== "boolean" ||
|
|
41
|
-
typeof parsed.unitTestsEnabled !== "boolean"
|
|
50
|
+
typeof parsed.unitTestsEnabled !== "boolean" ||
|
|
51
|
+
(parsed.commitMessagePrefixMode !== undefined &&
|
|
52
|
+
!isCommitMessagePrefixMode(parsed.commitMessagePrefixMode))) {
|
|
42
53
|
return false;
|
|
43
54
|
}
|
|
44
55
|
const states = [undefined, false, true];
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
56
|
+
const prefixModes = [
|
|
57
|
+
undefined,
|
|
58
|
+
DEFAULT_COMMIT_MESSAGE_PREFIX_MODE,
|
|
59
|
+
"disabled",
|
|
60
|
+
];
|
|
61
|
+
for (const commitMessagePrefixMode of prefixModes) {
|
|
62
|
+
for (const unitTestsEnabled of states) {
|
|
63
|
+
for (const lintEnabled of states) {
|
|
64
|
+
const candidate = { ...parsed };
|
|
65
|
+
setOptionalCommitMessagePrefixMode(candidate, commitMessagePrefixMode);
|
|
66
|
+
setOptionalBoolean(candidate, "unitTestsEnabled", unitTestsEnabled);
|
|
67
|
+
setOptionalBoolean(candidate, "lintEnabled", lintEnabled);
|
|
68
|
+
const rendered = Buffer.from(`${JSON.stringify(candidate, null, 2)}\n`);
|
|
69
|
+
if (rendered.byteLength === expected.size &&
|
|
70
|
+
sha256(rendered) === expected.sha256) {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
54
73
|
}
|
|
55
74
|
}
|
|
56
75
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verification-policy.js","sourceRoot":"","sources":["../../src/config/verification-policy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"verification-policy.js","sourceRoot":"","sources":["../../src/config/verification-policy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EACL,kCAAkC,EAClC,yBAAyB,GAE1B,MAAM,4BAA4B,CAAC;AAEpC,MAAM,CAAC,MAAM,+BAA+B,GAAG,wBAAwB,CAAC;AACxE,MAAM,CAAC,MAAM,oBAAoB,GAAG,KAAK,CAAC;AAC1C,MAAM,CAAC,MAAM,0BAA0B,GAAG,IAAI,CAAC;AAa/C,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED,SAAS,MAAM,CAAC,OAAmB;IACjC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC5D,CAAC;AAED,SAAS,kBAAkB,CACzB,KAA8B,EAC9B,GAA6B,EAC7B,UAA+B;IAE/B,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC7B,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC;IACpB,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC;IAC1B,CAAC;AACH,CAAC;AAED,SAAS,kCAAkC,CACzC,KAA8B,EAC9B,UAA+C;IAE/C,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC7B,OAAO,KAAK,CAAC,uBAAuB,CAAC;IACvC,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,uBAAuB,GAAG,UAAU,CAAC;IAC7C,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,uCAAuC,CACrD,OAAmB,EACnB,QAA4B;IAE5B,IACE,OAAO,CAAC,UAAU,KAAK,QAAQ,CAAC,IAAI;QACpC,MAAM,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,MAAM,EACnC,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAY,CAAC;IACxE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QACtB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IACE,OAAO,MAAM,CAAC,WAAW,KAAK,SAAS;QACvC,OAAO,MAAM,CAAC,gBAAgB,KAAK,SAAS;QAC5C,CAAC,MAAM,CAAC,uBAAuB,KAAK,SAAS;YAC3C,CAAC,yBAAyB,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC,EAC7D,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,MAAM,GAAqC,CAAC,SAAS,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;IAC1E,MAAM,WAAW,GAAqD;QACpE,SAAS;QACT,kCAAkC;QAClC,UAAU;KACX,CAAC;IACF,KAAK,MAAM,uBAAuB,IAAI,WAAW,EAAE,CAAC;QAClD,KAAK,MAAM,gBAAgB,IAAI,MAAM,EAAE,CAAC;YACtC,KAAK,MAAM,WAAW,IAAI,MAAM,EAAE,CAAC;gBACjC,MAAM,SAAS,GAAG,EAAE,GAAG,MAAM,EAAE,CAAC;gBAChC,kCAAkC,CAAC,SAAS,EAAE,uBAAuB,CAAC,CAAC;gBACvE,kBAAkB,CAAC,SAAS,EAAE,kBAAkB,EAAE,gBAAgB,CAAC,CAAC;gBACpE,kBAAkB,CAAC,SAAS,EAAE,aAAa,EAAE,WAAW,CAAC,CAAC;gBAC1D,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;gBACxE,IACE,QAAQ,CAAC,UAAU,KAAK,QAAQ,CAAC,IAAI;oBACrC,MAAM,CAAC,QAAQ,CAAC,KAAK,QAAQ,CAAC,MAAM,EACpC,CAAC;oBACD,OAAO,IAAI,CAAC;gBACd,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"installation.d.ts","sourceRoot":"","sources":["../../src/doctor/installation.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"installation.d.ts","sourceRoot":"","sources":["../../src/doctor/installation.ts"],"names":[],"mappings":"AA0CA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAusB9C,wBAAgB,wBAAwB,CACtC,eAAe,EAAE,MAAM,GACtB,SAAS,WAAW,EAAE,CA6DxB"}
|
|
@@ -3,6 +3,7 @@ import { lstatSync, readFileSync, } from "node:fs";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { DEFAULT_LONG_COMMAND_TIMEOUT_MS } from "../config/long-command-timeout.js";
|
|
6
|
+
import { COMMIT_MESSAGE_PREFIX_RELATIVE_PATH, DEFAULT_COMMIT_MESSAGE_PREFIX_MODE, inspectCommitMessagePrefix, isCommitMessagePrefixMode, } from "../config/commit-message-prefix.js";
|
|
6
7
|
import { AUTOMATION_CONFIG_RELATIVE_PATH, DEFAULT_LINT_ENABLED, DEFAULT_UNIT_TESTS_ENABLED, matchesManifestModuloVerificationPolicy, } from "../config/verification-policy.js";
|
|
7
8
|
import { isModuleScope, planAdaptiveProjectTemplates, } from "../installer/adaptive-templates.js";
|
|
8
9
|
import { planAgentsConfigMerge } from "../installer/agents-config.js";
|
|
@@ -257,6 +258,7 @@ function blockedInstallationChecks(reason) {
|
|
|
257
258
|
blockedCheck("managed-permissions", "Managed permissions", reason),
|
|
258
259
|
blockedCheck("installation-backups", "Installation backups", reason),
|
|
259
260
|
blockedCheck("managed-configuration", "Managed configuration", reason),
|
|
261
|
+
blockedCheck("commit-message-prefix", "Commit-message prefix", reason),
|
|
260
262
|
];
|
|
261
263
|
}
|
|
262
264
|
function managedResourceCheck(manifest, inspections) {
|
|
@@ -445,6 +447,17 @@ function managedConfigurationCheck(targetDirectory, manifest, inspections) {
|
|
|
445
447
|
unitTestsEnabled,
|
|
446
448
|
};
|
|
447
449
|
}
|
|
450
|
+
const commitMessagePrefixMode = automationConfig.commitMessagePrefixMode ??
|
|
451
|
+
DEFAULT_COMMIT_MESSAGE_PREFIX_MODE;
|
|
452
|
+
if (!isCommitMessagePrefixMode(commitMessagePrefixMode)) {
|
|
453
|
+
throw new Error("commitMessagePrefixMode must be either required or disabled");
|
|
454
|
+
}
|
|
455
|
+
if (automationConfig.commitMessagePrefixMode === undefined) {
|
|
456
|
+
comparableAutomationConfig = {
|
|
457
|
+
...comparableAutomationConfig,
|
|
458
|
+
commitMessagePrefixMode,
|
|
459
|
+
};
|
|
460
|
+
}
|
|
448
461
|
const longCommandTimeoutMs = automationConfig.longCommandTimeoutMs ?? DEFAULT_LONG_COMMAND_TIMEOUT_MS;
|
|
449
462
|
if (typeof longCommandTimeoutMs !== "number") {
|
|
450
463
|
throw new Error("longCommandTimeoutMs must be a number");
|
|
@@ -461,6 +474,7 @@ function managedConfigurationCheck(targetDirectory, manifest, inspections) {
|
|
|
461
474
|
gradleVerification: automationConfig.gradleVerification,
|
|
462
475
|
lintEnabled,
|
|
463
476
|
unitTestsEnabled,
|
|
477
|
+
commitMessagePrefixMode,
|
|
464
478
|
longCommandTimeoutMs,
|
|
465
479
|
});
|
|
466
480
|
if (!jsonMatches(comparableAutomationConfig, expected.automationConfig)) {
|
|
@@ -471,6 +485,7 @@ function managedConfigurationCheck(targetDirectory, manifest, inspections) {
|
|
|
471
485
|
details.push(`Automation default module: ${primaryModule}`);
|
|
472
486
|
details.push(`Unit-test verification: ${unitTestsEnabled ? "enabled" : "disabled"}`);
|
|
473
487
|
details.push(`Android lint verification: ${lintEnabled ? "enabled" : "disabled"}`);
|
|
488
|
+
details.push(`Commit-message prefix mode: ${commitMessagePrefixMode}`);
|
|
474
489
|
}
|
|
475
490
|
const taskExample = JSON.parse(inspectedText("automation/tasks/TASK-TEMPLATE.json.example", inspections));
|
|
476
491
|
if (!jsonMatches(taskExample, expected.taskContractExample)) {
|
|
@@ -490,6 +505,68 @@ function managedConfigurationCheck(targetDirectory, manifest, inspections) {
|
|
|
490
505
|
details: failures.length === 0 ? details : failures,
|
|
491
506
|
};
|
|
492
507
|
}
|
|
508
|
+
function commitMessagePrefixCheck(targetDirectory, inspections) {
|
|
509
|
+
let mode;
|
|
510
|
+
try {
|
|
511
|
+
const automationConfig = JSON.parse(inspectedText(AUTOMATION_CONFIG_RELATIVE_PATH, inspections));
|
|
512
|
+
if (!isRecord(automationConfig)) {
|
|
513
|
+
throw new Error("automation config must be a JSON object");
|
|
514
|
+
}
|
|
515
|
+
const configuredMode = automationConfig.commitMessagePrefixMode ??
|
|
516
|
+
DEFAULT_COMMIT_MESSAGE_PREFIX_MODE;
|
|
517
|
+
if (!isCommitMessagePrefixMode(configuredMode)) {
|
|
518
|
+
throw new Error("commitMessagePrefixMode must be either required or disabled");
|
|
519
|
+
}
|
|
520
|
+
mode = configuredMode;
|
|
521
|
+
}
|
|
522
|
+
catch (error) {
|
|
523
|
+
return {
|
|
524
|
+
id: "commit-message-prefix",
|
|
525
|
+
label: "Commit-message prefix",
|
|
526
|
+
status: "fail",
|
|
527
|
+
summary: "Commit-message prefix policy could not be inspected.",
|
|
528
|
+
details: errorDetails(error),
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
const inspection = inspectCommitMessagePrefix(targetDirectory, mode);
|
|
532
|
+
if (inspection.status === "disabled") {
|
|
533
|
+
return {
|
|
534
|
+
id: "commit-message-prefix",
|
|
535
|
+
label: "Commit-message prefix",
|
|
536
|
+
status: "pass",
|
|
537
|
+
summary: "Commit-message prefixing is disabled by automation configuration.",
|
|
538
|
+
details: [],
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
if (inspection.status === "configured") {
|
|
542
|
+
return {
|
|
543
|
+
id: "commit-message-prefix",
|
|
544
|
+
label: "Commit-message prefix",
|
|
545
|
+
status: "pass",
|
|
546
|
+
summary: "The required user-maintained commit-message prefix is configured.",
|
|
547
|
+
details: [inspection.path],
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
if (inspection.status === "missing" || inspection.status === "unconfigured") {
|
|
551
|
+
return {
|
|
552
|
+
id: "commit-message-prefix",
|
|
553
|
+
label: "Commit-message prefix",
|
|
554
|
+
status: "warn",
|
|
555
|
+
summary: "New automation tasks are blocked until the commit-message prefix is filled.",
|
|
556
|
+
details: [
|
|
557
|
+
inspection.path,
|
|
558
|
+
`Put exactly one non-comment prefix line in ${COMMIT_MESSAGE_PREFIX_RELATIVE_PATH}.`,
|
|
559
|
+
],
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
return {
|
|
563
|
+
id: "commit-message-prefix",
|
|
564
|
+
label: "Commit-message prefix",
|
|
565
|
+
status: "fail",
|
|
566
|
+
summary: "The required commit-message prefix file is invalid.",
|
|
567
|
+
details: [inspection.path, ...inspection.details],
|
|
568
|
+
};
|
|
569
|
+
}
|
|
493
570
|
export function installationDoctorChecks(targetDirectory) {
|
|
494
571
|
let manifest;
|
|
495
572
|
try {
|
|
@@ -537,6 +614,7 @@ export function installationDoctorChecks(targetDirectory) {
|
|
|
537
614
|
managedPermissionCheck(manifest, inspections),
|
|
538
615
|
installationBackupCheck(targetDirectory, manifest),
|
|
539
616
|
managedConfigurationCheck(targetDirectory, manifest, inspections),
|
|
617
|
+
commitMessagePrefixCheck(targetDirectory, inspections),
|
|
540
618
|
];
|
|
541
619
|
}
|
|
542
620
|
//# sourceMappingURL=installation.js.map
|