@elmundi/ship-cli 0.8.1 → 0.12.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/README.md +651 -25
- package/bin/shipctl.mjs +168 -0
- package/lib/adapters/_fs.mjs +165 -0
- package/lib/adapters/agents/index.mjs +26 -0
- package/lib/adapters/ci/azure-pipelines.mjs +23 -0
- package/lib/adapters/ci/buildkite.mjs +24 -0
- package/lib/adapters/ci/circleci.mjs +23 -0
- package/lib/adapters/ci/gh-actions.mjs +29 -0
- package/lib/adapters/ci/gitlab-ci.mjs +23 -0
- package/lib/adapters/ci/jenkins.mjs +23 -0
- package/lib/adapters/ci/manual.mjs +18 -0
- package/lib/adapters/index.mjs +122 -0
- package/lib/adapters/language/dart.mjs +23 -0
- package/lib/adapters/language/go.mjs +23 -0
- package/lib/adapters/language/java.mjs +27 -0
- package/lib/adapters/language/js.mjs +32 -0
- package/lib/adapters/language/kotlin.mjs +48 -0
- package/lib/adapters/language/py.mjs +34 -0
- package/lib/adapters/language/rust.mjs +23 -0
- package/lib/adapters/language/swift.mjs +37 -0
- package/lib/adapters/language/ts.mjs +35 -0
- package/lib/adapters/trackers/azure-boards.mjs +49 -0
- package/lib/adapters/trackers/clickup.mjs +43 -0
- package/lib/adapters/trackers/github-issues.mjs +52 -0
- package/lib/adapters/trackers/jira.mjs +72 -0
- package/lib/adapters/trackers/linear.mjs +62 -0
- package/lib/adapters/trackers/none.mjs +18 -0
- package/lib/adapters/trackers/spreadsheet.mjs +28 -0
- package/lib/artifacts/fs-index.mjs +230 -0
- package/lib/bootstrap/render.mjs +422 -0
- package/lib/cache/store.mjs +422 -0
- package/lib/commands/bootstrap.mjs +4 -0
- package/lib/commands/callback.mjs +742 -0
- package/lib/commands/config.mjs +257 -0
- package/lib/commands/docs.mjs +4 -4
- package/lib/commands/doctor.mjs +583 -0
- package/lib/commands/feedback.mjs +355 -0
- package/lib/commands/help.mjs +159 -24
- package/lib/commands/init.mjs +830 -158
- package/lib/commands/kickoff.mjs +192 -0
- package/lib/commands/knowledge.mjs +562 -0
- package/lib/commands/lanes.mjs +527 -0
- package/lib/commands/manifest-catalog.mjs +106 -42
- package/lib/commands/migrate.mjs +204 -0
- package/lib/commands/new.mjs +452 -0
- package/lib/commands/patterns.mjs +14 -48
- package/lib/commands/run.mjs +857 -0
- package/lib/commands/search.mjs +2 -2
- package/lib/commands/sync.mjs +824 -0
- package/lib/commands/telemetry.mjs +390 -0
- package/lib/commands/trigger.mjs +196 -0
- package/lib/commands/verify.mjs +187 -0
- package/lib/config/io.mjs +232 -0
- package/lib/config/migrate.mjs +223 -0
- package/lib/config/schema.mjs +901 -0
- package/lib/detect.mjs +162 -19
- package/lib/feedback/drafts.mjs +129 -0
- package/lib/find-ship-root.mjs +16 -10
- package/lib/http.mjs +237 -11
- package/lib/state/idempotency.mjs +183 -0
- package/lib/state/lockfile.mjs +180 -0
- package/lib/telemetry/outbox.mjs +224 -0
- package/lib/templates.mjs +53 -65
- package/lib/verify/checks/agents-on-disk.mjs +58 -0
- package/lib/verify/checks/api-reachable.mjs +39 -0
- package/lib/verify/checks/artifacts-up-to-date.mjs +78 -0
- package/lib/verify/checks/bootstrap-files.mjs +67 -0
- package/lib/verify/checks/cache-integrity.mjs +51 -0
- package/lib/verify/checks/ci-secrets.mjs +86 -0
- package/lib/verify/checks/config-present.mjs +39 -0
- package/lib/verify/checks/gitignore-cache.mjs +51 -0
- package/lib/verify/checks/rules-markers.mjs +135 -0
- package/lib/verify/checks/stack-enums.mjs +33 -0
- package/lib/verify/checks/tracker-labels.mjs +91 -0
- package/lib/verify/registry.mjs +120 -0
- package/lib/version.mjs +34 -0
- package/package.json +10 -3
- package/bin/ship.mjs +0 -68
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { isFile } from "../_fs.mjs";
|
|
2
|
+
|
|
3
|
+
export const id = "java";
|
|
4
|
+
export const kind = "language";
|
|
5
|
+
|
|
6
|
+
export async function detect(cwd) {
|
|
7
|
+
const evidence = [];
|
|
8
|
+
if (isFile(cwd, "pom.xml")) {
|
|
9
|
+
evidence.push({ type: "file", where: "pom.xml", match: "present" });
|
|
10
|
+
}
|
|
11
|
+
if (isFile(cwd, "build.gradle")) {
|
|
12
|
+
evidence.push({ type: "file", where: "build.gradle", match: "present" });
|
|
13
|
+
}
|
|
14
|
+
if (isFile(cwd, "build.gradle.kts")) {
|
|
15
|
+
evidence.push({ type: "file", where: "build.gradle.kts", match: "present" });
|
|
16
|
+
}
|
|
17
|
+
if (!evidence.length) return { present: false, confidence: 0, evidence };
|
|
18
|
+
return { present: true, confidence: 1, evidence };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function bootstrap() {
|
|
22
|
+
return { todo: true };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function verify() {
|
|
26
|
+
return { todo: true };
|
|
27
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { isFile, pkgDeps, readJson } from "../_fs.mjs";
|
|
2
|
+
|
|
3
|
+
export const id = "js";
|
|
4
|
+
export const kind = "language";
|
|
5
|
+
|
|
6
|
+
export async function detect(cwd) {
|
|
7
|
+
if (!isFile(cwd, "package.json")) {
|
|
8
|
+
return { present: false, confidence: 0, evidence: [] };
|
|
9
|
+
}
|
|
10
|
+
const pkg = readJson(cwd, "package.json");
|
|
11
|
+
const deps = pkgDeps(pkg);
|
|
12
|
+
if (deps.typescript || isFile(cwd, "tsconfig.json")) {
|
|
13
|
+
return {
|
|
14
|
+
present: false,
|
|
15
|
+
confidence: 0,
|
|
16
|
+
evidence: [{ type: "file", where: "package.json", match: "typescript present → js skipped" }],
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
present: true,
|
|
21
|
+
confidence: 0.9,
|
|
22
|
+
evidence: [{ type: "file", where: "package.json", match: "present (no typescript)" }],
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function bootstrap() {
|
|
27
|
+
return { todo: true };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function verify() {
|
|
31
|
+
return { todo: true };
|
|
32
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { isFile, readText, walk } from "../_fs.mjs";
|
|
2
|
+
|
|
3
|
+
export const id = "kotlin";
|
|
4
|
+
export const kind = "language";
|
|
5
|
+
|
|
6
|
+
export async function detect(cwd) {
|
|
7
|
+
const evidence = [];
|
|
8
|
+
let strong = false;
|
|
9
|
+
|
|
10
|
+
if (isFile(cwd, "build.gradle.kts")) {
|
|
11
|
+
const body = readText(cwd, "build.gradle.kts") || "";
|
|
12
|
+
evidence.push({ type: "file", where: "build.gradle.kts", match: "present" });
|
|
13
|
+
if (/\bkotlin\b/i.test(body)) {
|
|
14
|
+
strong = true;
|
|
15
|
+
evidence.push({
|
|
16
|
+
type: "file",
|
|
17
|
+
where: "build.gradle.kts",
|
|
18
|
+
match: "kotlin plugin referenced",
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const hasGradle =
|
|
24
|
+
isFile(cwd, "build.gradle") ||
|
|
25
|
+
isFile(cwd, "build.gradle.kts") ||
|
|
26
|
+
isFile(cwd, "settings.gradle") ||
|
|
27
|
+
isFile(cwd, "settings.gradle.kts");
|
|
28
|
+
if (hasGradle) {
|
|
29
|
+
const files = walk(cwd, { maxDepth: 4, maxFiles: 400 });
|
|
30
|
+
const kt = files.find((f) => f.endsWith(".kt"));
|
|
31
|
+
if (kt) evidence.push({ type: "file", where: kt, match: "*.kt present" });
|
|
32
|
+
if (kt && !strong) {
|
|
33
|
+
return { present: true, confidence: 0.7, evidence };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (strong) return { present: true, confidence: 1, evidence };
|
|
38
|
+
if (evidence.length) return { present: true, confidence: 0.6, evidence };
|
|
39
|
+
return { present: false, confidence: 0, evidence: [] };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function bootstrap() {
|
|
43
|
+
return { todo: true };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function verify() {
|
|
47
|
+
return { todo: true };
|
|
48
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { isFile, listDir } from "../_fs.mjs";
|
|
2
|
+
|
|
3
|
+
export const id = "py";
|
|
4
|
+
export const kind = "language";
|
|
5
|
+
|
|
6
|
+
export async function detect(cwd) {
|
|
7
|
+
const evidence = [];
|
|
8
|
+
let hit = false;
|
|
9
|
+
|
|
10
|
+
if (isFile(cwd, "pyproject.toml")) {
|
|
11
|
+
hit = true;
|
|
12
|
+
evidence.push({ type: "file", where: "pyproject.toml", match: "present" });
|
|
13
|
+
}
|
|
14
|
+
if (isFile(cwd, "setup.py")) {
|
|
15
|
+
hit = true;
|
|
16
|
+
evidence.push({ type: "file", where: "setup.py", match: "present" });
|
|
17
|
+
}
|
|
18
|
+
for (const name of listDir(cwd, ".")) {
|
|
19
|
+
if (/^requirements.*\.txt$/.test(name) && isFile(cwd, name)) {
|
|
20
|
+
hit = true;
|
|
21
|
+
evidence.push({ type: "file", where: name, match: "present" });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return { present: hit, confidence: hit ? 1 : 0, evidence };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function bootstrap() {
|
|
29
|
+
return { todo: true };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function verify() {
|
|
33
|
+
return { todo: true };
|
|
34
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { isFile } from "../_fs.mjs";
|
|
2
|
+
|
|
3
|
+
export const id = "rust";
|
|
4
|
+
export const kind = "language";
|
|
5
|
+
|
|
6
|
+
export async function detect(cwd) {
|
|
7
|
+
if (isFile(cwd, "Cargo.toml")) {
|
|
8
|
+
return {
|
|
9
|
+
present: true,
|
|
10
|
+
confidence: 1,
|
|
11
|
+
evidence: [{ type: "file", where: "Cargo.toml", match: "present" }],
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
return { present: false, confidence: 0, evidence: [] };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function bootstrap() {
|
|
18
|
+
return { todo: true };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function verify() {
|
|
22
|
+
return { todo: true };
|
|
23
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { isFile, listDir, readText } from "../_fs.mjs";
|
|
2
|
+
|
|
3
|
+
export const id = "swift";
|
|
4
|
+
export const kind = "language";
|
|
5
|
+
|
|
6
|
+
export async function detect(cwd) {
|
|
7
|
+
const evidence = [];
|
|
8
|
+
|
|
9
|
+
if (isFile(cwd, "Package.swift")) {
|
|
10
|
+
evidence.push({ type: "file", where: "Package.swift", match: "present" });
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
for (const name of listDir(cwd, ".")) {
|
|
14
|
+
if (name.endsWith(".xcodeproj")) {
|
|
15
|
+
evidence.push({ type: "dir", where: name, match: "xcodeproj" });
|
|
16
|
+
break;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (isFile(cwd, "Podfile")) {
|
|
21
|
+
const body = readText(cwd, "Podfile") || "";
|
|
22
|
+
if (/\bswift\b/i.test(body) || /:swift/i.test(body)) {
|
|
23
|
+
evidence.push({ type: "file", where: "Podfile", match: "swift pods" });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (!evidence.length) return { present: false, confidence: 0, evidence };
|
|
28
|
+
return { present: true, confidence: 1, evidence };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function bootstrap() {
|
|
32
|
+
return { todo: true };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export async function verify() {
|
|
36
|
+
return { todo: true };
|
|
37
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { isFile, pkgDeps, readJson } from "../_fs.mjs";
|
|
2
|
+
|
|
3
|
+
export const id = "ts";
|
|
4
|
+
export const kind = "language";
|
|
5
|
+
|
|
6
|
+
export async function detect(cwd) {
|
|
7
|
+
const evidence = [];
|
|
8
|
+
let hit = false;
|
|
9
|
+
|
|
10
|
+
if (isFile(cwd, "tsconfig.json")) {
|
|
11
|
+
hit = true;
|
|
12
|
+
evidence.push({ type: "file", where: "tsconfig.json", match: "present" });
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const pkg = readJson(cwd, "package.json");
|
|
16
|
+
const deps = pkgDeps(pkg);
|
|
17
|
+
if (deps.typescript) {
|
|
18
|
+
hit = true;
|
|
19
|
+
evidence.push({
|
|
20
|
+
type: "package",
|
|
21
|
+
where: "package.json",
|
|
22
|
+
match: `typescript@${deps.typescript}`,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return { present: hit, confidence: hit ? 1 : 0, evidence };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function bootstrap() {
|
|
30
|
+
return { todo: true };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function verify() {
|
|
34
|
+
return { todo: true };
|
|
35
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { exists, readEnvFiles, readGithubWorkflows } from "../_fs.mjs";
|
|
2
|
+
|
|
3
|
+
export const id = "azure-boards";
|
|
4
|
+
export const kind = "tracker";
|
|
5
|
+
|
|
6
|
+
const ENV_KEYS = /\b(AZURE_DEVOPS_PAT|AZURE_DEVOPS_ORG|AZURE_DEVOPS_EXT_PAT)\b/;
|
|
7
|
+
|
|
8
|
+
export async function detect(cwd) {
|
|
9
|
+
const evidence = [];
|
|
10
|
+
let envHit = false;
|
|
11
|
+
let ciHit = false;
|
|
12
|
+
|
|
13
|
+
for (const { file, content } of readEnvFiles(cwd)) {
|
|
14
|
+
const m = content.match(ENV_KEYS);
|
|
15
|
+
if (m) {
|
|
16
|
+
envHit = true;
|
|
17
|
+
evidence.push({ type: "env", where: file, match: m[1] });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (exists(cwd, ".vsts-ci.yml")) {
|
|
22
|
+
ciHit = true;
|
|
23
|
+
evidence.push({ type: "file", where: ".vsts-ci.yml", match: "present" });
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
for (const { file, content } of readGithubWorkflows(cwd)) {
|
|
27
|
+
if (/azure[-_ ]?boards/i.test(content) || /dev\.azure\.com/i.test(content)) {
|
|
28
|
+
ciHit = true;
|
|
29
|
+
evidence.push({ type: "workflow", where: file, match: "azure-boards reference" });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const present = envHit || ciHit;
|
|
34
|
+
let confidence = 0;
|
|
35
|
+
if (present) {
|
|
36
|
+
confidence = 0.7;
|
|
37
|
+
if (envHit && ciHit) confidence = 0.95;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return { present, confidence, evidence };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function bootstrap() {
|
|
44
|
+
return { todo: true };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export async function verify() {
|
|
48
|
+
return { todo: true };
|
|
49
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { pkgDeps, readEnvFiles, readJson } from "../_fs.mjs";
|
|
2
|
+
|
|
3
|
+
export const id = "clickup";
|
|
4
|
+
export const kind = "tracker";
|
|
5
|
+
|
|
6
|
+
export async function detect(cwd) {
|
|
7
|
+
const evidence = [];
|
|
8
|
+
let envHit = false;
|
|
9
|
+
let packageHit = false;
|
|
10
|
+
|
|
11
|
+
for (const { file, content } of readEnvFiles(cwd)) {
|
|
12
|
+
if (/\bCLICKUP_API_TOKEN\b/.test(content)) {
|
|
13
|
+
envHit = true;
|
|
14
|
+
evidence.push({ type: "env", where: file, match: "CLICKUP_API_TOKEN" });
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const pkg = readJson(cwd, "package.json");
|
|
19
|
+
const deps = pkgDeps(pkg);
|
|
20
|
+
for (const name of Object.keys(deps)) {
|
|
21
|
+
if (name.startsWith("@clickup/")) {
|
|
22
|
+
packageHit = true;
|
|
23
|
+
evidence.push({ type: "package", where: "package.json", match: name });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const present = envHit || packageHit;
|
|
28
|
+
let confidence = 0;
|
|
29
|
+
if (present) {
|
|
30
|
+
confidence = 0.7;
|
|
31
|
+
if (envHit && packageHit) confidence = 0.95;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return { present, confidence, evidence };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function bootstrap() {
|
|
38
|
+
return { todo: true };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function verify() {
|
|
42
|
+
return { todo: true };
|
|
43
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { isDir, listDir, readEnvFiles, readGithubWorkflows } from "../_fs.mjs";
|
|
2
|
+
|
|
3
|
+
export const id = "github-issues";
|
|
4
|
+
export const kind = "tracker";
|
|
5
|
+
|
|
6
|
+
export async function detect(cwd) {
|
|
7
|
+
const evidence = [];
|
|
8
|
+
let templateHit = false;
|
|
9
|
+
let tokenHit = false;
|
|
10
|
+
|
|
11
|
+
if (isDir(cwd, ".github", "ISSUE_TEMPLATE")) {
|
|
12
|
+
const entries = listDir(cwd, ".github/ISSUE_TEMPLATE").filter((n) => !n.startsWith("."));
|
|
13
|
+
templateHit = true;
|
|
14
|
+
evidence.push({
|
|
15
|
+
type: "dir",
|
|
16
|
+
where: ".github/ISSUE_TEMPLATE/",
|
|
17
|
+
match: `${entries.length} template(s)`,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
for (const { file, content } of readEnvFiles(cwd)) {
|
|
22
|
+
if (/\bGITHUB_TOKEN\b/.test(content)) {
|
|
23
|
+
tokenHit = true;
|
|
24
|
+
evidence.push({ type: "env", where: file, match: "GITHUB_TOKEN" });
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if (!tokenHit) {
|
|
29
|
+
for (const { file, content } of readGithubWorkflows(cwd)) {
|
|
30
|
+
if (/\bGITHUB_TOKEN\b/.test(content)) {
|
|
31
|
+
tokenHit = true;
|
|
32
|
+
evidence.push({ type: "workflow", where: file, match: "GITHUB_TOKEN" });
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const present = templateHit || tokenHit;
|
|
39
|
+
let confidence = 0;
|
|
40
|
+
if (templateHit) confidence = 0.8;
|
|
41
|
+
else if (tokenHit) confidence = 0.3;
|
|
42
|
+
|
|
43
|
+
return { present, confidence, evidence };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function bootstrap() {
|
|
47
|
+
return { todo: true };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export async function verify() {
|
|
51
|
+
return { todo: true };
|
|
52
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import {
|
|
2
|
+
exists,
|
|
3
|
+
readEnvFiles,
|
|
4
|
+
readGithubWorkflows,
|
|
5
|
+
readJson,
|
|
6
|
+
} from "../_fs.mjs";
|
|
7
|
+
|
|
8
|
+
export const id = "jira";
|
|
9
|
+
export const kind = "tracker";
|
|
10
|
+
|
|
11
|
+
const ENV_KEYS = /\b(JIRA_URL|JIRA_API_TOKEN|ATLASSIAN_TOKEN|ATLASSIAN_API_TOKEN)\b/;
|
|
12
|
+
|
|
13
|
+
export async function detect(cwd) {
|
|
14
|
+
const evidence = [];
|
|
15
|
+
let envHit = false;
|
|
16
|
+
let workflowHit = false;
|
|
17
|
+
let fileHit = false;
|
|
18
|
+
|
|
19
|
+
for (const { file, content } of readEnvFiles(cwd)) {
|
|
20
|
+
const m = content.match(ENV_KEYS);
|
|
21
|
+
if (m) {
|
|
22
|
+
envHit = true;
|
|
23
|
+
evidence.push({ type: "env", where: file, match: m[1] });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
for (const { file, content } of readGithubWorkflows(cwd)) {
|
|
28
|
+
if (/\bjira\b/i.test(content) || /atlassian/i.test(content)) {
|
|
29
|
+
workflowHit = true;
|
|
30
|
+
evidence.push({ type: "workflow", where: file, match: "jira/atlassian reference" });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (exists(cwd, ".jira")) {
|
|
35
|
+
fileHit = true;
|
|
36
|
+
evidence.push({ type: "dir", where: ".jira/", match: "present" });
|
|
37
|
+
}
|
|
38
|
+
if (exists(cwd, "atlassian-connect.json")) {
|
|
39
|
+
fileHit = true;
|
|
40
|
+
evidence.push({ type: "file", where: "atlassian-connect.json", match: "present" });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const pkg = readJson(cwd, "package.json");
|
|
44
|
+
const scripts = pkg && typeof pkg === "object" ? pkg.scripts : null;
|
|
45
|
+
if (scripts && typeof scripts === "object") {
|
|
46
|
+
for (const [k, v] of Object.entries(scripts)) {
|
|
47
|
+
if (typeof v === "string" && /\bjira\b/i.test(v)) {
|
|
48
|
+
workflowHit = true;
|
|
49
|
+
evidence.push({ type: "script", where: `package.json:scripts.${k}`, match: "jira reference" });
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const present = envHit || workflowHit || fileHit;
|
|
56
|
+
let confidence = 0;
|
|
57
|
+
if (present) {
|
|
58
|
+
confidence = 0.7;
|
|
59
|
+
if (envHit && (workflowHit || fileHit)) confidence = 0.95;
|
|
60
|
+
if (fileHit && workflowHit) confidence = Math.max(confidence, 0.85);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return { present, confidence, evidence };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export async function bootstrap() {
|
|
67
|
+
return { todo: true };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export async function verify() {
|
|
71
|
+
return { todo: true };
|
|
72
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import {
|
|
2
|
+
exists,
|
|
3
|
+
pkgDeps,
|
|
4
|
+
readEnvFiles,
|
|
5
|
+
readGithubWorkflows,
|
|
6
|
+
readJson,
|
|
7
|
+
} from "../_fs.mjs";
|
|
8
|
+
|
|
9
|
+
export const id = "linear";
|
|
10
|
+
export const kind = "tracker";
|
|
11
|
+
|
|
12
|
+
export async function detect(cwd) {
|
|
13
|
+
const evidence = [];
|
|
14
|
+
let envHit = false;
|
|
15
|
+
let workflowHit = false;
|
|
16
|
+
let packageHit = false;
|
|
17
|
+
|
|
18
|
+
for (const { file, content } of readEnvFiles(cwd)) {
|
|
19
|
+
if (/\bLINEAR_API_KEY\b/.test(content)) {
|
|
20
|
+
envHit = true;
|
|
21
|
+
evidence.push({ type: "env", where: file, match: "LINEAR_API_KEY" });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const pkg = readJson(cwd, "package.json");
|
|
26
|
+
const deps = pkgDeps(pkg);
|
|
27
|
+
for (const name of Object.keys(deps)) {
|
|
28
|
+
if (name.startsWith("@linear/")) {
|
|
29
|
+
packageHit = true;
|
|
30
|
+
evidence.push({ type: "package", where: "package.json", match: name });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
for (const { file, content } of readGithubWorkflows(cwd)) {
|
|
35
|
+
if (/\blinear\b/i.test(content)) {
|
|
36
|
+
workflowHit = true;
|
|
37
|
+
evidence.push({ type: "workflow", where: file, match: "linear reference" });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (exists(cwd, ".linear")) {
|
|
42
|
+
packageHit = true;
|
|
43
|
+
evidence.push({ type: "dir", where: ".linear/", match: "present" });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const present = envHit || workflowHit || packageHit;
|
|
47
|
+
let confidence = 0;
|
|
48
|
+
if (present) {
|
|
49
|
+
confidence = 0.7;
|
|
50
|
+
if (envHit && (workflowHit || packageHit)) confidence = 0.95;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return { present, confidence, evidence };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export async function bootstrap() {
|
|
57
|
+
return { todo: true };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export async function verify() {
|
|
61
|
+
return { todo: true };
|
|
62
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const id = "none";
|
|
2
|
+
export const kind = "tracker";
|
|
3
|
+
|
|
4
|
+
export async function detect() {
|
|
5
|
+
return {
|
|
6
|
+
present: true,
|
|
7
|
+
confidence: 0.05,
|
|
8
|
+
evidence: [{ type: "fallback", where: "-", match: "no tracker detected" }],
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export async function bootstrap() {
|
|
13
|
+
return { todo: true };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function verify() {
|
|
17
|
+
return { todo: true };
|
|
18
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { exists } from "../_fs.mjs";
|
|
2
|
+
|
|
3
|
+
export const id = "spreadsheet";
|
|
4
|
+
export const kind = "tracker";
|
|
5
|
+
|
|
6
|
+
export async function detect(cwd) {
|
|
7
|
+
const evidence = [];
|
|
8
|
+
let hit = false;
|
|
9
|
+
|
|
10
|
+
if (exists(cwd, ".ship", "tracker-sheet.csv")) {
|
|
11
|
+
hit = true;
|
|
12
|
+
evidence.push({ type: "file", where: ".ship/tracker-sheet.csv", match: "present" });
|
|
13
|
+
}
|
|
14
|
+
if (exists(cwd, "tracker.xlsx")) {
|
|
15
|
+
hit = true;
|
|
16
|
+
evidence.push({ type: "file", where: "tracker.xlsx", match: "present" });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return { present: hit, confidence: hit ? 0.9 : 0, evidence };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function bootstrap() {
|
|
23
|
+
return { todo: true };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function verify() {
|
|
27
|
+
return { todo: true };
|
|
28
|
+
}
|