safelaunch 1.0.30 → 1.0.32

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "safelaunch",
3
- "version": "1.0.30",
3
+ "version": "1.0.32",
4
4
  "description": "Backend Reliability Infrastructure - catch what breaks production before it breaks",
5
5
  "main": "index.js",
6
6
  "bin": {
package/src/scan.js CHANGED
@@ -73,6 +73,16 @@ const IMPACTS = {
73
73
  impact: "Known exploits exist for these packages. Shipping them puts your users and infrastructure at risk.",
74
74
  fix: "Run npm audit fix or check npm audit for manual fixes.",
75
75
  }),
76
+ PYTHON_MISMATCH: (expected, actual) => ({
77
+ title: `Python version mismatch (expected: ${expected}, running: ${actual})`,
78
+ impact: "Code that works locally may fail in CI or production if they use the expected version.",
79
+ fix: `Switch to Python ${expected} or update .python-version to match your runtime.`,
80
+ }),
81
+ NODE_ENGINES_MISMATCH: (expected, actual) => ({
82
+ title: `Node version mismatch (package.json engines: ${expected}, running: ${actual})`,
83
+ impact: "Your package.json declares a required Node version that differs from what is running.",
84
+ fix: `Switch to Node ${expected} or update the engines field in package.json.`,
85
+ }),
76
86
  NODE_MISMATCH: (expected, actual) => ({
77
87
  title: `Node version mismatch (.nvmrc: ${expected}, running: ${actual})`,
78
88
  impact: "Code that works locally may fail in CI or production if they use the expected version.",
@@ -216,16 +226,63 @@ function checkNpmAudit(cwd) {
216
226
 
217
227
  function checkNodeVersion(cwd) {
218
228
  const issues = [];
219
- const content = readFileSafe(path.join(cwd, ".nvmrc"));
220
- if (!content) return issues;
221
- const expected = content.trim().replace(/^v/, "");
222
- const actual = process.version.replace(/^v/, "");
223
- if (expected.split(".")[0] !== actual.split(".")[0]) {
224
- issues.push({ severity: "info", ...IMPACTS.NODE_MISMATCH(expected, actual) });
229
+ const actual = process.version.replace(/^v/, "");
230
+
231
+ // Check .nvmrc
232
+ const nvmrc = readFileSafe(path.join(cwd, ".nvmrc"));
233
+ if (nvmrc) {
234
+ const expected = nvmrc.trim().replace(/^v/, "");
235
+ if (expected.split(".")[0] !== actual.split(".")[0]) {
236
+ issues.push({ severity: "warn", ...IMPACTS.NODE_MISMATCH(expected, actual) });
237
+ }
225
238
  }
239
+
240
+ // Check package.json engines.node
241
+ const pkgRaw = readFileSafe(path.join(cwd, "package.json"));
242
+ if (pkgRaw) {
243
+ try {
244
+ const pkg = JSON.parse(pkgRaw);
245
+ const enginesNode = pkg.engines && pkg.engines.node;
246
+ if (enginesNode) {
247
+ const match = enginesNode.match(/(\d+)/);
248
+ if (match && match[1] !== actual.split(".")[0]) {
249
+ issues.push({ severity: "warn", ...IMPACTS.NODE_ENGINES_MISMATCH(enginesNode, actual) });
250
+ }
251
+ }
252
+ } catch {}
253
+ }
254
+
226
255
  return issues;
227
256
  }
228
257
 
258
+ function checkPythonVersion(cwd) {
259
+ const issues = [];
260
+ const actual = exec("python3 --version") || exec("python --version");
261
+ if (!actual) return issues;
262
+ const actualVer = actual.replace(/^Python\s+/i, "").trim();
263
+
264
+ // Check .python-version
265
+ const pyVer = readFileSafe(path.join(cwd, ".python-version"));
266
+ if (pyVer) {
267
+ const expected = pyVer.trim();
268
+ if (expected.split(".")[0] !== actualVer.split(".")[0]) {
269
+ issues.push({ severity: "warn", ...IMPACTS.PYTHON_MISMATCH(expected, actualVer) });
270
+ }
271
+ }
272
+
273
+ // Check runtime.txt (Heroku/Render style)
274
+ const runtimeTxt = readFileSafe(path.join(cwd, "runtime.txt"));
275
+ if (runtimeTxt && runtimeTxt.startsWith("python-")) {
276
+ const expected = runtimeTxt.replace("python-", "").trim();
277
+ if (expected.split(".")[0] !== actualVer.split(".")[0]) {
278
+ issues.push({ severity: "warn", ...IMPACTS.PYTHON_MISMATCH(expected, actualVer) });
279
+ }
280
+ }
281
+
282
+ return issues;
283
+ }
284
+
285
+
229
286
  function renderHookOutput(blockers, warnings, infos, elapsed) {
230
287
  const lines = [];
231
288
  const hr = gray("─".repeat(49));
@@ -325,6 +382,7 @@ async function runScan(options = {}) {
325
382
  ...checkTypeScript(cwd),
326
383
  ...checkNpmAudit(cwd),
327
384
  ...checkNodeVersion(cwd),
385
+ ...checkPythonVersion(cwd),
328
386
  ];
329
387
  const blockers = allIssues.filter((i) => i.severity === "block");
330
388
  const warnings = allIssues.filter((i) => i.severity === "warn");
package/src/telemetry.js CHANGED
@@ -1,22 +1,29 @@
1
1
  const { PostHog } = require('posthog-node');
2
2
 
3
3
  const client = new PostHog('phc_WFm3O5H7wkZK2Ne3kyy5QgUXJR8y86SQbszSwk3BUn0', {
4
- host: 'https://us.i.posthog.com'
4
+ host: 'https://us.i.posthog.com',
5
+ flushAt: 1,
6
+ flushInterval: 0,
5
7
  });
6
8
 
7
9
  function track(event, properties = {}) {
8
- client.capture({
9
- distinctId: 'anonymous',
10
- event,
11
- properties: {
12
- ...properties,
13
- cli_version: require('../../package.json').version,
14
- }
15
- });
10
+ try {
11
+ client.capture({
12
+ distinctId: 'anonymous',
13
+ event,
14
+ properties: {
15
+ ...properties,
16
+ cli_version: require('../package.json').version,
17
+ }
18
+ });
19
+ } catch {}
16
20
  }
17
21
 
18
22
  async function shutdown() {
19
- await client.shutdown();
23
+ try {
24
+ await client.flush();
25
+ await client.shutdown();
26
+ } catch {}
20
27
  }
21
28
 
22
- module.exports = { track, shutdown };
29
+ module.exports = { track, shutdown };