ship18ion 1.1.3 → 1.2.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.
@@ -0,0 +1,60 @@
1
+ # Contributing to ship18ion
2
+
3
+ First off, thanks for taking the time to contribute! ❤️
4
+
5
+ All types of contributions are encouraged and valued. See the [Table of Contents](#table-of-contents) for different ways to help and details about how this project handles them. Please make sure to read the relevant section before making your contribution. It will make it a lot easier for us maintainers and smooth out the experience for all involved. The community looks forward to your contributions.
6
+
7
+ ## Table of Contents
8
+
9
+ - [I Have a Question](#i-have-a-question)
10
+ - [I Want To Contribute](#i-want-to-contribute)
11
+ - [Reporting Bugs](#reporting-bugs)
12
+ - [Suggesting Enhancements](#suggesting-enhancements)
13
+ - [Your First Code Contribution](#your-first-code-contribution)
14
+
15
+ ## I Have a Question
16
+
17
+ > If you want to ask a question, we assume that you have read the available [Documentation](README.md).
18
+
19
+ Before you ask a question, it is best to search for existing [Issues](/issues) that might help you. In case you have found a suitable issue and still need clarification, you can write your question in this issue. It is also advisable to search the internet for answers first.
20
+
21
+ ## I Want To Contribute
22
+
23
+ ### Reporting Bugs
24
+
25
+ - Make sure that you are using the latest version.
26
+ - Read the documentation carefully and find out if the functionality is already covered, maybe by an individual configuration.
27
+ - Perform a search to see if the problem has already been reported. If it has, add a comment to the existing issue instead of opening a new one.
28
+
29
+ ### Suggesting Enhancements
30
+
31
+ - Open a new issue and use the **Feature Request** template.
32
+ - Explain why this enhancement would be useful to most users.
33
+
34
+ ## Styleguides
35
+
36
+ ### Commit Messages
37
+
38
+ - Use [Conventional Commits](https://www.conventionalcommits.org/).
39
+
40
+ ### Code Style
41
+
42
+ - Keep code clean and use the existing ESLint/Prettier configs (if available).
43
+ - Add tests for new features.
44
+
45
+ ## Development Workflow
46
+
47
+ To test your changes locally on another project without publishing:
48
+
49
+ 1. **Build and Link**:
50
+ ```bash
51
+ npm run build
52
+ npm link
53
+ ```
54
+ 2. **Use in Target Project**:
55
+ ```bash
56
+ npm link ship18ion
57
+ npx ship18ion
58
+ ```
59
+
60
+ Thank you!
package/README.md CHANGED
@@ -31,6 +31,17 @@ Think of it as `eslint` but for **deployability**.
31
31
  - Ensures `.env` files are not bundled into build output directories.
32
32
  - Checks for dev dependencies (like `eslint`) accidentally listed in `dependencies`.
33
33
 
34
+ - **🧹 Code Hygiene (New)**:
35
+ - Warns on leftover `console.log()` calls.
36
+ - Flags `FIXME` comments that need resolution.
37
+
38
+ - **📦 Dependencies (New)**:
39
+ - Checks for duplicate packages (listed in both `dependencies` and `devDependencies`).
40
+
41
+ - **🐙 Git Safety (New)**:
42
+ - Ensures critical files (`node_modules`, `.env`) and framework artifacts (`.next`) are git-ignored.
43
+ - **Security**: alerts if dangerous keys (e.g. `serviceAccountKey.json`) exist but are *not* ignored.
44
+
34
45
  - **👷 CI/CD Ready**:
35
46
  - Zero config by default.
36
47
  - Returns exit code `1` on failure to block bad builds.
@@ -95,6 +106,11 @@ npx ship18ion check --ci
95
106
  | **Next.js** | `nextjs-public-secret` | High-entropy string found in `NEXT_PUBLIC_` variable. |
96
107
  | **Security** | `security-cors` | Detects wildcard `Access-Control-Allow-Origin`. |
97
108
  | **Git** | `git-dirty` | Warns if deploying with uncommitted changes. |
109
+ | **Git** | `git-ignore-missing` | Warns if `.gitignore` is missing critical entries (`node_modules`, `.env`). |
110
+ | **Git** | `git-ignore-auth` | **Critical**: Fails if `serviceAccountKey.json` etc are not ignored. |
111
+ | **Hygiene** | `hygiene-console-log` | Warns on `console.log` in production code. |
112
+ | **Hygiene** | `hygiene-fixme` | Warns on leftover `FIXME` comments. |
113
+ | **Package** | `package-duplicate` | Warns if a package is in both dependency lists. |
98
114
 
99
115
  ## 🤝 Contributing
100
116
 
@@ -6,6 +6,8 @@ const env_1 = require("../rules/env");
6
6
  const secrets_1 = require("../rules/secrets");
7
7
  const security_1 = require("../rules/security");
8
8
  const build_1 = require("../rules/build");
9
+ const hygiene_1 = require("../rules/hygiene");
10
+ const packages_1 = require("../rules/packages");
9
11
  const nextjs_1 = require("../rules/frameworks/nextjs");
10
12
  const git_1 = require("../rules/git");
11
13
  const detector_1 = require("./detector");
@@ -33,6 +35,13 @@ async function runChecks(config, cwd, onProgress) {
33
35
  if (onProgress)
34
36
  onProgress('Inspecting build artifacts...');
35
37
  results.push(...await (0, build_1.checkBuild)(ctx));
38
+ // New Rules
39
+ if (onProgress)
40
+ onProgress('Checking code hygiene...');
41
+ results.push(...await (0, hygiene_1.checkHygiene)(ctx));
42
+ if (onProgress)
43
+ onProgress('Validating packages...');
44
+ results.push(...await (0, packages_1.checkPackages)(ctx));
36
45
  // Framework specific checks
37
46
  if (framework === 'nextjs') {
38
47
  if (onProgress)
@@ -12,6 +12,9 @@ const CATEGORIES = {
12
12
  'security': { icon: '⚠️', label: 'Security' },
13
13
  'dep': { icon: '📦', label: 'Dependency & Build' },
14
14
  'build': { icon: '📦', label: 'Dependency & Build' },
15
+ 'git': { icon: '🐙', label: 'Git & Repo' },
16
+ 'hygiene': { icon: '🧹', label: 'Code Hygiene' },
17
+ 'package': { icon: '📦', label: 'Packages' },
15
18
  };
16
19
  function getCategory(ruleId) {
17
20
  const prefix = ruleId.split('-')[0];
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.checkHygiene = checkHygiene;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ async function checkHygiene(ctx) {
9
+ const results = [];
10
+ const codeFiles = ctx.files.filter(f => f.match(/\.(js|ts|jsx|tsx)$/) &&
11
+ !f.includes('.test.') &&
12
+ !f.includes('.spec.'));
13
+ for (const file of codeFiles) {
14
+ const content = fs_1.default.readFileSync(file, 'utf-8');
15
+ const lines = content.split('\n');
16
+ lines.forEach((line, index) => {
17
+ const lineNum = index + 1;
18
+ // 1. Console Log Check
19
+ // Allow console.error and console.warn, but warn on console.log
20
+ if (line.includes('console.log(')) {
21
+ // Ignore if commented out
22
+ if (!line.trim().startsWith('//') && !line.trim().startsWith('*')) {
23
+ results.push({
24
+ status: 'warn',
25
+ message: 'Leftover console.log() call detected.',
26
+ ruleId: 'hygiene-console-log',
27
+ file,
28
+ line: lineNum
29
+ });
30
+ }
31
+ }
32
+ // 2. TODO / FIXME Check
33
+ if (line.match(/\/\/\s*(TODO|FIXME):/i)) {
34
+ if (line.match(/FIXME/i)) {
35
+ results.push({
36
+ status: 'warn',
37
+ message: 'FIXME comment found. Resolve before shipping.',
38
+ ruleId: 'hygiene-fixme',
39
+ file,
40
+ line: lineNum
41
+ });
42
+ }
43
+ }
44
+ });
45
+ }
46
+ return results;
47
+ }
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.checkPackages = checkPackages;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ async function checkPackages(ctx) {
9
+ const results = [];
10
+ const packageJsons = ctx.files.filter(f => f.endsWith('package.json') && !f.includes('node_modules'));
11
+ for (const pkgFile of packageJsons) {
12
+ try {
13
+ const content = JSON.parse(fs_1.default.readFileSync(pkgFile, 'utf-8'));
14
+ const deps = Object.keys(content.dependencies || {});
15
+ const devDeps = Object.keys(content.devDependencies || {});
16
+ // Find intersection
17
+ const duplicates = deps.filter(d => devDeps.includes(d));
18
+ for (const dup of duplicates) {
19
+ results.push({
20
+ status: 'warn',
21
+ message: `Package '${dup}' is listed in both 'dependencies' and 'devDependencies'.`,
22
+ ruleId: 'package-duplicate',
23
+ file: pkgFile
24
+ });
25
+ }
26
+ }
27
+ catch (e) {
28
+ // ignore malformed json
29
+ }
30
+ }
31
+ return results;
32
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ship18ion",
3
- "version": "1.1.3",
3
+ "version": "1.2.0",
4
4
  "description": "",
5
5
  "main": "dist/cli/index.js",
6
6
  "bin": {
@@ -5,6 +5,8 @@ import { checkEnvVars } from '../rules/env';
5
5
  import { checkSecrets } from '../rules/secrets';
6
6
  import { checkSecurity } from '../rules/security';
7
7
  import { checkDependencies, checkBuild } from '../rules/build';
8
+ import { checkHygiene } from '../rules/hygiene';
9
+ import { checkPackages } from '../rules/packages';
8
10
  import { checkNextJs } from '../rules/frameworks/nextjs';
9
11
  import { checkGit } from '../rules/git';
10
12
 
@@ -40,6 +42,13 @@ export async function runChecks(
40
42
  if (onProgress) onProgress('Inspecting build artifacts...');
41
43
  results.push(...await checkBuild(ctx));
42
44
 
45
+ // New Rules
46
+ if (onProgress) onProgress('Checking code hygiene...');
47
+ results.push(...await checkHygiene(ctx));
48
+
49
+ if (onProgress) onProgress('Validating packages...');
50
+ results.push(...await checkPackages(ctx));
51
+
43
52
  // Framework specific checks
44
53
  if (framework === 'nextjs') {
45
54
  if (onProgress) onProgress('Running Next.js specific checks...');
@@ -8,6 +8,9 @@ const CATEGORIES: Record<string, { icon: string; label: string }> = {
8
8
  'security': { icon: '⚠️', label: 'Security' },
9
9
  'dep': { icon: '📦', label: 'Dependency & Build' },
10
10
  'build': { icon: '📦', label: 'Dependency & Build' },
11
+ 'git': { icon: '🐙', label: 'Git & Repo' },
12
+ 'hygiene': { icon: '🧹', label: 'Code Hygiene' },
13
+ 'package': { icon: '📦', label: 'Packages' },
11
14
  };
12
15
 
13
16
  function getCategory(ruleId: string) {
@@ -0,0 +1,52 @@
1
+ import fs from 'fs';
2
+ import { RuleContext, RuleResult } from '../engine/types';
3
+
4
+ export async function checkHygiene(ctx: RuleContext): Promise<RuleResult[]> {
5
+ const results: RuleResult[] = [];
6
+
7
+ const codeFiles = ctx.files.filter(f =>
8
+ f.match(/\.(js|ts|jsx|tsx)$/) &&
9
+ !f.includes('.test.') &&
10
+ !f.includes('.spec.')
11
+ );
12
+
13
+ for (const file of codeFiles) {
14
+ const content = fs.readFileSync(file, 'utf-8');
15
+ const lines = content.split('\n');
16
+
17
+ lines.forEach((line, index) => {
18
+ const lineNum = index + 1;
19
+
20
+ // 1. Console Log Check
21
+ // Allow console.error and console.warn, but warn on console.log
22
+ if (line.includes('console.log(')) {
23
+ // Ignore if commented out
24
+ if (!line.trim().startsWith('//') && !line.trim().startsWith('*')) {
25
+ results.push({
26
+ status: 'warn',
27
+ message: 'Leftover console.log() call detected.',
28
+ ruleId: 'hygiene-console-log',
29
+ file,
30
+ line: lineNum
31
+ });
32
+ }
33
+ }
34
+
35
+ // 2. TODO / FIXME Check
36
+ if (line.match(/\/\/\s*(TODO|FIXME):/i)) {
37
+
38
+ if (line.match(/FIXME/i)) {
39
+ results.push({
40
+ status: 'warn',
41
+ message: 'FIXME comment found. Resolve before shipping.',
42
+ ruleId: 'hygiene-fixme',
43
+ file,
44
+ line: lineNum
45
+ });
46
+ }
47
+ }
48
+ });
49
+ }
50
+
51
+ return results;
52
+ }
@@ -0,0 +1,33 @@
1
+ import fs from 'fs';
2
+ import { RuleContext, RuleResult } from '../engine/types';
3
+
4
+ export async function checkPackages(ctx: RuleContext): Promise<RuleResult[]> {
5
+ const results: RuleResult[] = [];
6
+
7
+ const packageJsons = ctx.files.filter(f => f.endsWith('package.json') && !f.includes('node_modules'));
8
+
9
+ for (const pkgFile of packageJsons) {
10
+ try {
11
+ const content = JSON.parse(fs.readFileSync(pkgFile, 'utf-8'));
12
+ const deps = Object.keys(content.dependencies || {});
13
+ const devDeps = Object.keys(content.devDependencies || {});
14
+
15
+ // Find intersection
16
+ const duplicates = deps.filter(d => devDeps.includes(d));
17
+
18
+ for (const dup of duplicates) {
19
+ results.push({
20
+ status: 'warn',
21
+ message: `Package '${dup}' is listed in both 'dependencies' and 'devDependencies'.`,
22
+ ruleId: 'package-duplicate',
23
+ file: pkgFile
24
+ });
25
+ }
26
+
27
+ } catch (e) {
28
+ // ignore malformed json
29
+ }
30
+ }
31
+
32
+ return results;
33
+ }
package/SHIPPING.md DELETED
@@ -1,57 +0,0 @@
1
- # Shipping ship18ion
2
-
3
- ## 1. Local Testing (Link)
4
- To test `ship18ion` on your other local projects without publishing, use `npm link`.
5
-
6
- 1. **In this directory (ship18ion):**
7
- ```bash
8
- npm run build
9
- npm link
10
- ```
11
- This creates a global symlink to your local version.
12
-
13
- 2. **In your target project directory:**
14
- ```bash
15
- npm link ship18ion
16
- ```
17
- Now you can run:
18
- ```bash
19
- npx ship18ion check
20
- ```
21
-
22
- 3. **To unlink:**
23
- ```bash
24
- # In target project
25
- npm unlink -g ship18ion
26
-
27
- # In ship18ion directory
28
- npm unlink -g
29
- ```
30
-
31
- ## 2. Publishing to NPM
32
- To share this tool with the world.
33
-
34
- 1. **Login to NPM:**
35
- ```bash
36
- npm login
37
- ```
38
-
39
- 2. **Prepare:**
40
- - Ensure `package.json` has the correct version.
41
- - Run tests: `npm test` (or verifying script).
42
-
43
- 3. **Publish:**
44
- ```bash
45
- npm publish
46
- ```
47
-
48
- If you have a scoped package (e.g. `@yourname/ship18ion`), use:
49
- ```bash
50
- npm publish --access public
51
- ```
52
-
53
- ## 3. Usage for Users
54
- Once published, anyone can use it without installation:
55
- ```bash
56
- npx ship18ion@latest
57
- ```
package/walkthrough.md DELETED
@@ -1,51 +0,0 @@
1
- ship18ion - Production Readiness Inspector
2
- I have successfully built ship18ion, a CLI tool to check for production readiness.
3
-
4
- Features Implemented
5
- 1. Environment Variable Safety
6
- Unused Variable Detection: Scans
7
-
8
- .env
9
- files and code to find variables defined but never used.
10
- Missing Variable Detection: Identifies process.env.VAR usages that lack a corresponding definition in
11
-
12
- .env
13
- (or config).
14
- Format Support: Supports
15
-
16
- .env
17
- , .env.production files.
18
- Robust AST Parsing: Correctly detects process.env.VAR, import.meta.env.VAR (Vite), and process.env["VAR"].
19
- 2. Secrets Detection
20
- Pattern Matching: Detects AWS Keys, Google API Keys, Stripe Keys, and generic private keys.
21
- Entropy Heuristics: Detects potential high-entropy strings assigned to "secret" or "key" variables.
22
- 3. Framework & Security Checks
23
- Next.js Safety: Scans for NEXT_PUBLIC_ variables that appear to contain secrets (e.g. NEXT_PUBLIC_SECRET_KEY).
24
- Git Safety: Warns if deploying from a dirty working directory or a non-production branch.
25
- Debug Mode: Alerts on debug: true.
26
- CORS Wildcards: Fails if origin: '*' is detected.
27
- Database Credentials: Detects hardcoded connection strings.
28
- 4. Dependency & Build Safety
29
- Dev Dependencies: Warns if eslint or other dev tools are in dependencies.
30
- Build Artifacts: Alerts if source maps (.map) or
31
-
32
- .env
33
- files are found in build directories.
34
- Usage
35
- # In your project root
36
- npx ship18ion check
37
- # CI Mode (minimal output)
38
- npx ship18ion check --ci
39
- How to Ship & Share
40
- See
41
-
42
- SHIPPING.md
43
- for detailed instructions on:
44
-
45
- Local Testing: Using npm link to test on your other projects instantly.
46
- Publishing: Pushing to NPM so anyone can use npx ship18ion.
47
- Architecture
48
- CLI: Built with commander.
49
- Engine: TypeScript-based rule engine.
50
- Parsing: Babel-based AST parsing.
51
- Config: ship18ion.config.json support.