@produtype/core 1.19.0 → 1.20.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.
|
@@ -5,6 +5,7 @@ const detectContext_1 = require("./detectContext");
|
|
|
5
5
|
const textSearch_1 = require("../utils/textSearch");
|
|
6
6
|
const absenceEvidence_1 = require("./absenceEvidence");
|
|
7
7
|
const valuesFromPackage_1 = require("./structural/valuesFromPackage");
|
|
8
|
+
const readTextFileSafe_1 = require("../utils/readTextFileSafe");
|
|
8
9
|
/**
|
|
9
10
|
* Logging packages, which the ecosystem names and the author does not.
|
|
10
11
|
*
|
|
@@ -78,7 +79,42 @@ async function detectObservability(ctx) {
|
|
|
78
79
|
const actuator = (0, detectContext_1.hasAnyGradleDep)(ctx, ['spring-boot-starter-actuator']);
|
|
79
80
|
for (const dep of actuator)
|
|
80
81
|
evidence.push({ type: 'dependency', value: dep, claim: 'health' });
|
|
81
|
-
|
|
82
|
+
/**
|
|
83
|
+
* The probe a platform declares, which is the operator's half of the same answer.
|
|
84
|
+
*
|
|
85
|
+
* immich declares `healthcheck:` for its services in all four of its compose files
|
|
86
|
+
* and was told it has no health endpoint. Something is being probed — that is what
|
|
87
|
+
* the key means — and the endpoint it probes is inside the image, where no text
|
|
88
|
+
* search here will find it.
|
|
89
|
+
*
|
|
90
|
+
* `HEALTHCHECK` is Docker's instruction, `healthcheck:` is compose's key, and
|
|
91
|
+
* `livenessProbe:` and `readinessProbe:` are Kubernetes' field names. None is the
|
|
92
|
+
* author's word, and each one is a statement that this service answers a health
|
|
93
|
+
* question.
|
|
94
|
+
*
|
|
95
|
+
* `disable: true` is the one that says the opposite — compose's way of switching
|
|
96
|
+
* off an image's own check — so a block carrying it is not counted.
|
|
97
|
+
*/
|
|
98
|
+
const deploymentManifests = ctx.files.all.filter((file) => /(^|\/)(docker-compose[\w.-]*\.ya?ml|compose[\w.-]*\.ya?ml|Dockerfile[\w.-]*)$/.test(file)
|
|
99
|
+
|| /(^|\/)(k8s|kubernetes|helm|deploy|charts)\/.*\.ya?ml$/i.test(file)).slice(0, 12);
|
|
100
|
+
const declaredProbes = [];
|
|
101
|
+
for (const file of deploymentManifests) {
|
|
102
|
+
const text = (await (0, readTextFileSafe_1.readTextFileSafe)(ctx.root, file)) ?? '';
|
|
103
|
+
const lines = text.split(/\r?\n/);
|
|
104
|
+
for (let i = 0; i < lines.length; i++) {
|
|
105
|
+
if (!/^\s*(?:HEALTHCHECK\s|healthcheck\s*:|livenessProbe\s*:|readinessProbe\s*:)/.test(lines[i]))
|
|
106
|
+
continue;
|
|
107
|
+
/** compose writes `healthcheck:` then `disable: true` to switch the image's own off. */
|
|
108
|
+
if (lines.slice(i + 1, i + 3).some((line) => /^\s*disable\s*:\s*true/.test(line)))
|
|
109
|
+
continue;
|
|
110
|
+
declaredProbes.push({ type: 'snippet', value: lines[i].trim().slice(0, 200), file, line: i + 1, claim: 'health' });
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
for (const probe of declaredProbes.slice(0, 3))
|
|
115
|
+
evidence.push(probe);
|
|
116
|
+
const hasHealth = declaredProbes.length > 0
|
|
117
|
+
|| healthFiles.length > 0
|
|
82
118
|
|| actuator.length > 0
|
|
83
119
|
|| hits.some((h) => /\/(health|healthz|readyz|livez|alive)\b/i.test(h.snippet));
|
|
84
120
|
const hasReqId = hits.some((h) => /x-request-id|correlation-id/i.test(h.snippet));
|
|
@@ -146,7 +182,7 @@ async function detectObservability(ctx) {
|
|
|
146
182
|
const hasStructuredLogging = logDeps.length > 0 || structuredHits.length > 0;
|
|
147
183
|
const hasAnyLogging = hasStructuredLogging || plainLogging.length > 0;
|
|
148
184
|
if (!hasHealth) {
|
|
149
|
-
evidence.push(...(0, absenceEvidence_1.searchedFor)('a health endpoint', ['/health', '/healthz', '/readyz', 'a health, healthz, readyz, liveness or readiness route file'], 'health'));
|
|
185
|
+
evidence.push(...(0, absenceEvidence_1.searchedFor)('a health endpoint', ['/health', '/healthz', '/readyz', 'a health, healthz, readyz, liveness or readiness route file', 'a HEALTHCHECK, a compose healthcheck or a Kubernetes liveness probe'], 'health'));
|
|
150
186
|
}
|
|
151
187
|
if (!hasAnyLogging) {
|
|
152
188
|
evidence.push(...(0, absenceEvidence_1.searchedFor)('logging', ['winston', 'pino', 'morgan', 'bunyan', 'Monolog', 'slog', 'zap', 'logrus', 'slf4j', 'Rails.logger', 'tracing::', 'logger.info/warn/error/debug', 'Logger.info', 'error_log(', 'JSON.stringify with a level field'], 'logging'));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@produtype/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.20.0",
|
|
4
4
|
"description": "Deterministic CLI and library that analyzes a web application repository and reports how far it is from production-ready for the kind of product it is meant to be.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|