oss-signal 0.1.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 +7 -0
- package/LICENSE +21 -0
- package/README.md +179 -0
- package/docs/assets/terminal-report.svg +22 -0
- package/docs/examples/minimal-repo-report.md +21 -0
- package/docs/outreach/README.md +20 -0
- package/docs/outreach/platformatic-massimo-issue-draft.md +24 -0
- package/docs/outreach/platformatic-massimo-report.md +43 -0
- package/docs/outreach/sammorrisdesign-interactive-feed-issue-draft.md +28 -0
- package/docs/outreach/sammorrisdesign-interactive-feed-report.md +46 -0
- package/docs/outreach/supermarkt-checkjebon-issue-draft.md +26 -0
- package/docs/outreach/supermarkt-checkjebon-report.md +48 -0
- package/docs/rules.md +43 -0
- package/docs/self-audit.md +37 -0
- package/package.json +47 -0
- package/src/cli.js +123 -0
- package/src/index.js +326 -0
package/CHANGELOG.md
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 oss-signal contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# oss-signal
|
|
2
|
+
|
|
3
|
+
[](https://github.com/SalmonPlays/oss-signal/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/oss-signal)
|
|
5
|
+
[](https://www.npmjs.com/package/oss-signal)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
`oss-signal` is a dependency-light CLI for auditing open-source repository maintenance readiness.
|
|
9
|
+
|
|
10
|
+
It checks the files and automation that reduce maintainer load: README, license, contributing guide, security policy, CI, tests, issue templates, pull request templates, Dependabot, and release notes. The output is a score plus concrete next steps in Markdown or JSON.
|
|
11
|
+
|
|
12
|
+

|
|
13
|
+
|
|
14
|
+
## Why
|
|
15
|
+
|
|
16
|
+
Open-source projects often fail quietly because the maintainer workflow is undocumented. `oss-signal` gives maintainers a repeatable checklist they can run locally, in CI, or before asking contributors to help.
|
|
17
|
+
|
|
18
|
+
## Use Cases
|
|
19
|
+
|
|
20
|
+
- Maintainers can run it before publishing a new project.
|
|
21
|
+
- Contributors can attach a report to a cleanup issue or pull request.
|
|
22
|
+
- Teams can gate release readiness with `--fail-under`.
|
|
23
|
+
- Foundations and working groups can compare repository hygiene across many projects.
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install --global oss-signal
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
For local development:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
git clone https://github.com/SalmonPlays/oss-signal.git
|
|
35
|
+
cd oss-signal
|
|
36
|
+
npm install
|
|
37
|
+
npm test
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Usage
|
|
41
|
+
|
|
42
|
+
Audit the current directory:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
oss-signal
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Write a Markdown report:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
oss-signal /path/to/repo --format markdown --output oss-signal-report.md
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Use JSON in automation:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
oss-signal . --format json --fail-under 80
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Generate a report that can be attached to an issue:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
oss-signal . --format markdown --output docs/maintainer-readiness.md
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Checks
|
|
67
|
+
|
|
68
|
+
`oss-signal` currently checks:
|
|
69
|
+
|
|
70
|
+
- Community files: README, license, contributing guide, security policy, code of conduct, changelog, support policy
|
|
71
|
+
- Automation: CI workflows, tests, issue templates, pull request template, Dependabot, CodeQL or similar security workflow
|
|
72
|
+
- Package hygiene: package metadata and lockfile presence
|
|
73
|
+
|
|
74
|
+
See [docs/rules.md](docs/rules.md) for rule details and scoring weights.
|
|
75
|
+
|
|
76
|
+
## Real Output
|
|
77
|
+
|
|
78
|
+
This repository audits itself at **100/100 (A)**:
|
|
79
|
+
|
|
80
|
+
```text
|
|
81
|
+
Score: 100/100 (A)
|
|
82
|
+
|
|
83
|
+
Summary:
|
|
84
|
+
- Passed: 15
|
|
85
|
+
- Failed: 0
|
|
86
|
+
- Total checks: 15
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
See [docs/self-audit.md](docs/self-audit.md) for the full self-audit report.
|
|
90
|
+
|
|
91
|
+
## Field Audits
|
|
92
|
+
|
|
93
|
+
`oss-signal` has been run against public repositories to produce maintainer-readiness reports and respectful issue drafts:
|
|
94
|
+
|
|
95
|
+
- [platformatic/massimo report](docs/outreach/platformatic-massimo-report.md) and [issue #159](https://github.com/platformatic/massimo/issues/159)
|
|
96
|
+
- [supermarkt/checkjebon report](docs/outreach/supermarkt-checkjebon-report.md) and [issue #22](https://github.com/supermarkt/checkjebon/issues/22)
|
|
97
|
+
- [sammorrisdesign/interactive-feed report](docs/outreach/sammorrisdesign-interactive-feed-report.md) and [issue #14](https://github.com/sammorrisdesign/interactive-feed/issues/14)
|
|
98
|
+
|
|
99
|
+
See [docs/outreach](docs/outreach) for the reports and draft issue text. Drafts are not posted automatically; maintainers should only receive specific, useful, and respectful suggestions.
|
|
100
|
+
|
|
101
|
+
## Example Recommendation Output
|
|
102
|
+
|
|
103
|
+
```text
|
|
104
|
+
Score: 86/100 (B)
|
|
105
|
+
|
|
106
|
+
Recommended next steps:
|
|
107
|
+
- Static security analysis: Add a CodeQL or equivalent security scanning workflow.
|
|
108
|
+
- Support policy: Add SUPPORT.md describing where to ask questions.
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
See [docs/examples/minimal-repo-report.md](docs/examples/minimal-repo-report.md) for a small repository example with missing maintainer files.
|
|
112
|
+
|
|
113
|
+
## Exit Codes
|
|
114
|
+
|
|
115
|
+
By default, `oss-signal` exits with `0` after writing a report.
|
|
116
|
+
|
|
117
|
+
When `--fail-under <score>` is provided, it exits with `1` if the score is below the threshold:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
oss-signal . --fail-under 80
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## CI
|
|
124
|
+
|
|
125
|
+
Add this to a GitHub Actions workflow:
|
|
126
|
+
|
|
127
|
+
```yaml
|
|
128
|
+
- run: npx oss-signal . --fail-under 80
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Full workflow example:
|
|
132
|
+
|
|
133
|
+
```yaml
|
|
134
|
+
name: Repository health
|
|
135
|
+
|
|
136
|
+
on:
|
|
137
|
+
pull_request:
|
|
138
|
+
push:
|
|
139
|
+
branches: [main]
|
|
140
|
+
|
|
141
|
+
jobs:
|
|
142
|
+
oss-signal:
|
|
143
|
+
runs-on: ubuntu-latest
|
|
144
|
+
steps:
|
|
145
|
+
- uses: actions/checkout@v4
|
|
146
|
+
- uses: actions/setup-node@v4
|
|
147
|
+
with:
|
|
148
|
+
node-version: 22
|
|
149
|
+
- run: npx oss-signal . --format markdown --output oss-signal-report.md --fail-under 80
|
|
150
|
+
- uses: actions/upload-artifact@v4
|
|
151
|
+
with:
|
|
152
|
+
name: oss-signal-report
|
|
153
|
+
path: oss-signal-report.md
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Current Limitations
|
|
157
|
+
|
|
158
|
+
- It inspects local files only; GitHub URL mode is on the roadmap.
|
|
159
|
+
- It checks deterministic maintenance signals, not code quality.
|
|
160
|
+
- A high score does not prove a project is important. It proves the maintainer workflow is documented and automatable.
|
|
161
|
+
|
|
162
|
+
## Roadmap
|
|
163
|
+
|
|
164
|
+
- GitHub API mode for public repository URLs
|
|
165
|
+
- Ecosystem-specific profiles for Python, Rust, Go, and JavaScript packages
|
|
166
|
+
- SARIF output for code scanning dashboards
|
|
167
|
+
- Rules for release automation and provenance metadata
|
|
168
|
+
|
|
169
|
+
## Contributing
|
|
170
|
+
|
|
171
|
+
Contributions are welcome. Please read [CONTRIBUTING.md](CONTRIBUTING.md) before opening a pull request.
|
|
172
|
+
|
|
173
|
+
## Security
|
|
174
|
+
|
|
175
|
+
Please report security issues privately. See [SECURITY.md](SECURITY.md).
|
|
176
|
+
|
|
177
|
+
## License
|
|
178
|
+
|
|
179
|
+
MIT
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="920" height="520" viewBox="0 0 920 520" role="img" aria-labelledby="title desc">
|
|
2
|
+
<title id="title">oss-signal terminal output</title>
|
|
3
|
+
<desc id="desc">Example terminal output showing an OSS Signal repository health score of 100 out of 100.</desc>
|
|
4
|
+
<rect width="920" height="520" rx="18" fill="#0d1117"/>
|
|
5
|
+
<rect x="0" y="0" width="920" height="48" rx="18" fill="#161b22"/>
|
|
6
|
+
<circle cx="30" cy="24" r="7" fill="#ff5f56"/>
|
|
7
|
+
<circle cx="54" cy="24" r="7" fill="#ffbd2e"/>
|
|
8
|
+
<circle cx="78" cy="24" r="7" fill="#27c93f"/>
|
|
9
|
+
<text x="110" y="31" fill="#8b949e" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" font-size="14">oss-signal</text>
|
|
10
|
+
<text x="34" y="86" fill="#7ee787" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" font-size="16">$ oss-signal . --format markdown</text>
|
|
11
|
+
<text x="34" y="130" fill="#f0f6fc" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" font-size="24" font-weight="700">OSS Signal Report</text>
|
|
12
|
+
<text x="34" y="174" fill="#f0f6fc" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" font-size="18">Score: </text>
|
|
13
|
+
<text x="108" y="174" fill="#7ee787" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" font-size="18" font-weight="700">100/100 (A)</text>
|
|
14
|
+
<text x="34" y="222" fill="#8b949e" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" font-size="15">Summary</text>
|
|
15
|
+
<text x="34" y="252" fill="#f0f6fc" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" font-size="15">PASS README</text>
|
|
16
|
+
<text x="34" y="280" fill="#f0f6fc" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" font-size="15">PASS License</text>
|
|
17
|
+
<text x="34" y="308" fill="#f0f6fc" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" font-size="15">PASS Contributing guide</text>
|
|
18
|
+
<text x="34" y="336" fill="#f0f6fc" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" font-size="15">PASS Security policy</text>
|
|
19
|
+
<text x="34" y="364" fill="#f0f6fc" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" font-size="15">PASS Continuous integration</text>
|
|
20
|
+
<text x="34" y="392" fill="#f0f6fc" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" font-size="15">PASS Tests</text>
|
|
21
|
+
<text x="34" y="442" fill="#7ee787" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" font-size="16">No missing checks. Keep the report current as the repository evolves.</text>
|
|
22
|
+
</svg>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Example Report: Minimal Repository
|
|
2
|
+
|
|
3
|
+
This is representative output for a tiny repository that only has a README.
|
|
4
|
+
|
|
5
|
+
```text
|
|
6
|
+
Score: 12/100 (F)
|
|
7
|
+
|
|
8
|
+
Summary:
|
|
9
|
+
- Passed: 1
|
|
10
|
+
- Failed: 14
|
|
11
|
+
- Total checks: 15
|
|
12
|
+
|
|
13
|
+
Recommended next steps:
|
|
14
|
+
- Continuous integration (12 pts): Add a GitHub Actions workflow that runs linting and tests on pushes and pull requests.
|
|
15
|
+
- License (10 pts): Add an OSI-approved license file such as MIT, Apache-2.0, BSD-3-Clause, or MPL-2.0.
|
|
16
|
+
- Tests (10 pts): Add focused tests for public behavior and document the test command.
|
|
17
|
+
- Contributing guide (9 pts): Add CONTRIBUTING.md with local setup, test commands, review expectations, and release notes guidance.
|
|
18
|
+
- Security policy (9 pts): Add SECURITY.md with supported versions, reporting instructions, and response expectations.
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The goal is not to shame small projects. The goal is to turn implicit maintainer expectations into a short, reviewable checklist.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Field Audits
|
|
2
|
+
|
|
3
|
+
This directory contains public-repository audit reports generated with `oss-signal`.
|
|
4
|
+
|
|
5
|
+
These reports are examples of how maintainers or contributors can use the tool before opening a cleanup issue or pull request. They are based on local repository contents plus a manual check of GitHub community profile signals where needed.
|
|
6
|
+
|
|
7
|
+
Important notes:
|
|
8
|
+
|
|
9
|
+
- These projects are not affiliated with `oss-signal`.
|
|
10
|
+
- A low score does not mean the project is low quality.
|
|
11
|
+
- Missing files may be intentional or handled outside the repository.
|
|
12
|
+
- Any external issue or pull request should be respectful, specific, and easy for the maintainer to close.
|
|
13
|
+
|
|
14
|
+
## Audited Repositories
|
|
15
|
+
|
|
16
|
+
| Repository | Local score | Draft | Posted issue |
|
|
17
|
+
| --- | ---: | --- | --- |
|
|
18
|
+
| `platformatic/massimo` | 58/100 | [issue draft](platformatic-massimo-issue-draft.md) | [#159](https://github.com/platformatic/massimo/issues/159) |
|
|
19
|
+
| `supermarkt/checkjebon` | 21/100 | [issue draft](supermarkt-checkjebon-issue-draft.md) | [#22](https://github.com/supermarkt/checkjebon/issues/22) |
|
|
20
|
+
| `sammorrisdesign/interactive-feed` | 31/100 | [issue draft](sammorrisdesign-interactive-feed-issue-draft.md) | [#14](https://github.com/sammorrisdesign/interactive-feed/issues/14) |
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Issue Draft: platformatic/massimo
|
|
2
|
+
|
|
3
|
+
Posted as [platformatic/massimo#159](https://github.com/platformatic/massimo/issues/159).
|
|
4
|
+
|
|
5
|
+
Title:
|
|
6
|
+
|
|
7
|
+
```text
|
|
8
|
+
Add issue and pull request templates for contributor triage
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Body:
|
|
12
|
+
|
|
13
|
+
```markdown
|
|
14
|
+
Hi maintainers. I ran a local maintainer-readiness audit with `oss-signal` against this repository and noticed two small GitHub workflow files that may help contributor triage:
|
|
15
|
+
|
|
16
|
+
- `.github/ISSUE_TEMPLATE/bug_report.md`
|
|
17
|
+
- `.github/PULL_REQUEST_TEMPLATE.md`
|
|
18
|
+
|
|
19
|
+
GitHub's community profile endpoint shows this repository already has a README, license, contributing guide, and organization-level code of conduct, so this is not a broad cleanup request. The highest-signal next step looks like adding templates that ask for reproduction steps, expected behavior, test coverage, and release-note impact.
|
|
20
|
+
|
|
21
|
+
I am happy to open a small PR with template files if that would be useful. If this is intentionally handled elsewhere, feel free to close.
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Local report: [platformatic-massimo-report.md](platformatic-massimo-report.md)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# OSS Signal Report
|
|
2
|
+
|
|
3
|
+
Repository: `https://github.com/platformatic/massimo`
|
|
4
|
+
Generated: 2026-06-01T05:32:40.366Z
|
|
5
|
+
|
|
6
|
+
Score: **58/100** (F)
|
|
7
|
+
|
|
8
|
+
## Summary
|
|
9
|
+
|
|
10
|
+
- Passed: 7
|
|
11
|
+
- Failed: 8
|
|
12
|
+
- Total checks: 15
|
|
13
|
+
|
|
14
|
+
## Checks
|
|
15
|
+
|
|
16
|
+
| Status | Check | Why it matters |
|
|
17
|
+
| --- | --- | --- |
|
|
18
|
+
| PASS | README | A clear README is the front door for users and contributors. |
|
|
19
|
+
| PASS | License | A license tells downstream users what they may legally do with the code. |
|
|
20
|
+
| PASS | Contributing guide | Maintainers get better issues and pull requests when expectations are documented. |
|
|
21
|
+
| FAIL | Security policy | Responsible disclosure needs a private, documented path. |
|
|
22
|
+
| FAIL | Code of conduct | Community norms reduce ambiguity during difficult interactions. |
|
|
23
|
+
| FAIL | Changelog | Users need a durable place to understand release impact. |
|
|
24
|
+
| FAIL | Support policy | Support boundaries help maintainers avoid turning every request into unpaid consulting. |
|
|
25
|
+
| PASS | Continuous integration | CI catches regressions before maintainers merge changes. |
|
|
26
|
+
| PASS | Tests | Tests make review safer and lower the cost of outside contributions. |
|
|
27
|
+
| FAIL | Issue templates | Issue templates collect the facts maintainers need to reproduce and triage. |
|
|
28
|
+
| FAIL | Pull request template | PR templates nudge contributors to include tests, docs, and review context. |
|
|
29
|
+
| FAIL | Dependency update automation | Automated dependency updates reduce security and compatibility drift. |
|
|
30
|
+
| FAIL | Static security analysis | Static analysis finds common vulnerability patterns before releases. |
|
|
31
|
+
| PASS | Node package metadata | Package metadata makes installation, testing, and release automation discoverable. |
|
|
32
|
+
| PASS | Dependency lockfile | Lockfiles make CI and contributor setup reproducible. |
|
|
33
|
+
|
|
34
|
+
## Recommended Next Steps
|
|
35
|
+
|
|
36
|
+
- **Security policy** (9 pts): Add SECURITY.md with supported versions, reporting instructions, and response expectations.
|
|
37
|
+
- **Code of conduct** (6 pts): Add CODE_OF_CONDUCT.md, for example the Contributor Covenant.
|
|
38
|
+
- **Changelog** (6 pts): Keep CHANGELOG.md with dated release entries and migration notes.
|
|
39
|
+
- **Issue templates** (5 pts): Add bug report and feature request templates under .github/ISSUE_TEMPLATE/.
|
|
40
|
+
- **Pull request template** (5 pts): Add .github/PULL_REQUEST_TEMPLATE.md with a short checklist.
|
|
41
|
+
- **Dependency update automation** (5 pts): Add .github/dependabot.yml for the package ecosystems used in the repository.
|
|
42
|
+
- **Support policy** (4 pts): Add SUPPORT.md describing where to ask questions, what is in scope, and expected response times.
|
|
43
|
+
- **Static security analysis** (4 pts): Add a CodeQL or equivalent security scanning workflow.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Issue Draft: sammorrisdesign/interactive-feed
|
|
2
|
+
|
|
3
|
+
Posted as [sammorrisdesign/interactive-feed#14](https://github.com/sammorrisdesign/interactive-feed/issues/14).
|
|
4
|
+
|
|
5
|
+
Title:
|
|
6
|
+
|
|
7
|
+
```text
|
|
8
|
+
Clarify license and contributor workflow
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Body:
|
|
12
|
+
|
|
13
|
+
```markdown
|
|
14
|
+
Hi maintainers. I ran a local maintainer-readiness audit with `oss-signal` and noticed that GitHub's community profile does not currently detect a license file for this repository.
|
|
15
|
+
|
|
16
|
+
Because license choice has to come from the maintainer, I do not want to guess one in a PR. It may be worth adding an explicit `LICENSE` file if you intend others to reuse, fork, or contribute to the project.
|
|
17
|
+
|
|
18
|
+
The same audit also flagged a few optional maintainer-workflow files that could help if you want outside contributions:
|
|
19
|
+
|
|
20
|
+
- `CONTRIBUTING.md`
|
|
21
|
+
- `SECURITY.md`
|
|
22
|
+
- `.github/ISSUE_TEMPLATE/bug_report.md`
|
|
23
|
+
- `.github/PULL_REQUEST_TEMPLATE.md`
|
|
24
|
+
|
|
25
|
+
I can open a small PR for the non-license templates if that would be useful. If this project is not intended for outside contribution, feel free to close.
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Local report: [sammorrisdesign-interactive-feed-report.md](sammorrisdesign-interactive-feed-report.md)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# OSS Signal Report
|
|
2
|
+
|
|
3
|
+
Repository: `https://github.com/sammorrisdesign/interactive-feed`
|
|
4
|
+
Generated: 2026-06-01T05:32:40.592Z
|
|
5
|
+
|
|
6
|
+
Score: **31/100** (F)
|
|
7
|
+
|
|
8
|
+
## Summary
|
|
9
|
+
|
|
10
|
+
- Passed: 4
|
|
11
|
+
- Failed: 11
|
|
12
|
+
- Total checks: 15
|
|
13
|
+
|
|
14
|
+
## Checks
|
|
15
|
+
|
|
16
|
+
| Status | Check | Why it matters |
|
|
17
|
+
| --- | --- | --- |
|
|
18
|
+
| PASS | README | A clear README is the front door for users and contributors. |
|
|
19
|
+
| FAIL | License | A license tells downstream users what they may legally do with the code. |
|
|
20
|
+
| FAIL | Contributing guide | Maintainers get better issues and pull requests when expectations are documented. |
|
|
21
|
+
| FAIL | Security policy | Responsible disclosure needs a private, documented path. |
|
|
22
|
+
| FAIL | Code of conduct | Community norms reduce ambiguity during difficult interactions. |
|
|
23
|
+
| FAIL | Changelog | Users need a durable place to understand release impact. |
|
|
24
|
+
| FAIL | Support policy | Support boundaries help maintainers avoid turning every request into unpaid consulting. |
|
|
25
|
+
| PASS | Continuous integration | CI catches regressions before maintainers merge changes. |
|
|
26
|
+
| FAIL | Tests | Tests make review safer and lower the cost of outside contributions. |
|
|
27
|
+
| FAIL | Issue templates | Issue templates collect the facts maintainers need to reproduce and triage. |
|
|
28
|
+
| FAIL | Pull request template | PR templates nudge contributors to include tests, docs, and review context. |
|
|
29
|
+
| FAIL | Dependency update automation | Automated dependency updates reduce security and compatibility drift. |
|
|
30
|
+
| FAIL | Static security analysis | Static analysis finds common vulnerability patterns before releases. |
|
|
31
|
+
| PASS | Node package metadata | Package metadata makes installation, testing, and release automation discoverable. |
|
|
32
|
+
| PASS | Dependency lockfile | Lockfiles make CI and contributor setup reproducible. |
|
|
33
|
+
|
|
34
|
+
## Recommended Next Steps
|
|
35
|
+
|
|
36
|
+
- **License** (10 pts): Add an OSI-approved license file such as MIT, Apache-2.0, BSD-3-Clause, or MPL-2.0.
|
|
37
|
+
- **Tests** (10 pts): Add focused tests for public behavior and document the test command.
|
|
38
|
+
- **Contributing guide** (9 pts): Add CONTRIBUTING.md with local setup, test commands, review expectations, and release notes guidance.
|
|
39
|
+
- **Security policy** (9 pts): Add SECURITY.md with supported versions, reporting instructions, and response expectations.
|
|
40
|
+
- **Code of conduct** (6 pts): Add CODE_OF_CONDUCT.md, for example the Contributor Covenant.
|
|
41
|
+
- **Changelog** (6 pts): Keep CHANGELOG.md with dated release entries and migration notes.
|
|
42
|
+
- **Issue templates** (5 pts): Add bug report and feature request templates under .github/ISSUE_TEMPLATE/.
|
|
43
|
+
- **Pull request template** (5 pts): Add .github/PULL_REQUEST_TEMPLATE.md with a short checklist.
|
|
44
|
+
- **Dependency update automation** (5 pts): Add .github/dependabot.yml for the package ecosystems used in the repository.
|
|
45
|
+
- **Support policy** (4 pts): Add SUPPORT.md describing where to ask questions, what is in scope, and expected response times.
|
|
46
|
+
- **Static security analysis** (4 pts): Add a CodeQL or equivalent security scanning workflow.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Issue Draft: supermarkt/checkjebon
|
|
2
|
+
|
|
3
|
+
Posted as [supermarkt/checkjebon#22](https://github.com/supermarkt/checkjebon/issues/22).
|
|
4
|
+
|
|
5
|
+
Title:
|
|
6
|
+
|
|
7
|
+
```text
|
|
8
|
+
Document contributor and security reporting workflow
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Body:
|
|
12
|
+
|
|
13
|
+
```markdown
|
|
14
|
+
Hi maintainers. I ran a local maintainer-readiness audit with `oss-signal` and noticed a few lightweight repository-health files that could make future contributions easier to triage:
|
|
15
|
+
|
|
16
|
+
- `CONTRIBUTING.md` for local setup, tests, and review expectations
|
|
17
|
+
- `SECURITY.md` for private vulnerability reporting
|
|
18
|
+
- `.github/ISSUE_TEMPLATE/bug_report.md` for reproducible bug reports
|
|
19
|
+
- `.github/PULL_REQUEST_TEMPLATE.md` for test and docs checklists
|
|
20
|
+
|
|
21
|
+
GitHub's community profile already detects the README and license, so this would mainly document maintainer workflow rather than change product code.
|
|
22
|
+
|
|
23
|
+
I can open a focused PR with starter templates if that would be welcome. If these files are intentionally omitted, no problem; feel free to close.
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Local report: [supermarkt-checkjebon-report.md](supermarkt-checkjebon-report.md)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# OSS Signal Report
|
|
2
|
+
|
|
3
|
+
Repository: `https://github.com/supermarkt/checkjebon`
|
|
4
|
+
Generated: 2026-06-01T05:32:40.484Z
|
|
5
|
+
|
|
6
|
+
Score: **21/100** (F)
|
|
7
|
+
|
|
8
|
+
## Summary
|
|
9
|
+
|
|
10
|
+
- Passed: 2
|
|
11
|
+
- Failed: 13
|
|
12
|
+
- Total checks: 15
|
|
13
|
+
|
|
14
|
+
## Checks
|
|
15
|
+
|
|
16
|
+
| Status | Check | Why it matters |
|
|
17
|
+
| --- | --- | --- |
|
|
18
|
+
| PASS | README | A clear README is the front door for users and contributors. |
|
|
19
|
+
| PASS | License | A license tells downstream users what they may legally do with the code. |
|
|
20
|
+
| FAIL | Contributing guide | Maintainers get better issues and pull requests when expectations are documented. |
|
|
21
|
+
| FAIL | Security policy | Responsible disclosure needs a private, documented path. |
|
|
22
|
+
| FAIL | Code of conduct | Community norms reduce ambiguity during difficult interactions. |
|
|
23
|
+
| FAIL | Changelog | Users need a durable place to understand release impact. |
|
|
24
|
+
| FAIL | Support policy | Support boundaries help maintainers avoid turning every request into unpaid consulting. |
|
|
25
|
+
| FAIL | Continuous integration | CI catches regressions before maintainers merge changes. |
|
|
26
|
+
| FAIL | Tests | Tests make review safer and lower the cost of outside contributions. |
|
|
27
|
+
| FAIL | Issue templates | Issue templates collect the facts maintainers need to reproduce and triage. |
|
|
28
|
+
| FAIL | Pull request template | PR templates nudge contributors to include tests, docs, and review context. |
|
|
29
|
+
| FAIL | Dependency update automation | Automated dependency updates reduce security and compatibility drift. |
|
|
30
|
+
| FAIL | Static security analysis | Static analysis finds common vulnerability patterns before releases. |
|
|
31
|
+
| FAIL | Node package metadata | Package metadata makes installation, testing, and release automation discoverable. |
|
|
32
|
+
| FAIL | Dependency lockfile | Lockfiles make CI and contributor setup reproducible. |
|
|
33
|
+
|
|
34
|
+
## Recommended Next Steps
|
|
35
|
+
|
|
36
|
+
- **Continuous integration** (12 pts): Add a GitHub Actions workflow that runs linting and tests on pushes and pull requests.
|
|
37
|
+
- **Tests** (10 pts): Add focused tests for public behavior and document the test command.
|
|
38
|
+
- **Contributing guide** (9 pts): Add CONTRIBUTING.md with local setup, test commands, review expectations, and release notes guidance.
|
|
39
|
+
- **Security policy** (9 pts): Add SECURITY.md with supported versions, reporting instructions, and response expectations.
|
|
40
|
+
- **Code of conduct** (6 pts): Add CODE_OF_CONDUCT.md, for example the Contributor Covenant.
|
|
41
|
+
- **Changelog** (6 pts): Keep CHANGELOG.md with dated release entries and migration notes.
|
|
42
|
+
- **Issue templates** (5 pts): Add bug report and feature request templates under .github/ISSUE_TEMPLATE/.
|
|
43
|
+
- **Pull request template** (5 pts): Add .github/PULL_REQUEST_TEMPLATE.md with a short checklist.
|
|
44
|
+
- **Dependency update automation** (5 pts): Add .github/dependabot.yml for the package ecosystems used in the repository.
|
|
45
|
+
- **Node package metadata** (5 pts): Add package.json with name, description, license, scripts, repository, and engines fields.
|
|
46
|
+
- **Support policy** (4 pts): Add SUPPORT.md describing where to ask questions, what is in scope, and expected response times.
|
|
47
|
+
- **Static security analysis** (4 pts): Add a CodeQL or equivalent security scanning workflow.
|
|
48
|
+
- **Dependency lockfile** (4 pts): Commit the lockfile for application-style projects, or document why this library intentionally omits one.
|
package/docs/rules.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Rules
|
|
2
|
+
|
|
3
|
+
`oss-signal` uses weighted checks. Missing high-weight items are the most likely to increase maintainer load or create downstream risk.
|
|
4
|
+
|
|
5
|
+
## Community Files
|
|
6
|
+
|
|
7
|
+
| Rule | Weight | Signal |
|
|
8
|
+
| --- | ---: | --- |
|
|
9
|
+
| README | 12 | `README.md` or `README` |
|
|
10
|
+
| License | 10 | `LICENSE`, `LICENSE.md`, or `COPYING` |
|
|
11
|
+
| Contributing guide | 9 | `CONTRIBUTING.md`, `.github/CONTRIBUTING.md`, or `docs/CONTRIBUTING.md` |
|
|
12
|
+
| Security policy | 9 | `SECURITY.md` or `.github/SECURITY.md` |
|
|
13
|
+
| Code of conduct | 6 | `CODE_OF_CONDUCT.md` or `.github/CODE_OF_CONDUCT.md` |
|
|
14
|
+
| Changelog | 6 | `CHANGELOG.md`, `HISTORY.md`, or `RELEASES.md` |
|
|
15
|
+
| Support policy | 4 | `SUPPORT.md` or `.github/SUPPORT.md` |
|
|
16
|
+
|
|
17
|
+
## Automation
|
|
18
|
+
|
|
19
|
+
| Rule | Weight | Signal |
|
|
20
|
+
| --- | ---: | --- |
|
|
21
|
+
| Continuous integration | 12 | Any YAML workflow under `.github/workflows/` |
|
|
22
|
+
| Tests | 10 | Common test directories or `*.test.*` / `*.spec.*` files |
|
|
23
|
+
| Issue templates | 5 | `.github/ISSUE_TEMPLATE/` or `.github/ISSUE_TEMPLATE.md` |
|
|
24
|
+
| Pull request template | 5 | `.github/PULL_REQUEST_TEMPLATE.md` or `PULL_REQUEST_TEMPLATE.md` |
|
|
25
|
+
| Dependency update automation | 5 | `.github/dependabot.yml` or `.github/dependabot.yaml` |
|
|
26
|
+
| Static security analysis | 4 | Workflow filename containing `codeql` or `security` |
|
|
27
|
+
|
|
28
|
+
## Package Hygiene
|
|
29
|
+
|
|
30
|
+
| Rule | Weight | Signal |
|
|
31
|
+
| --- | ---: | --- |
|
|
32
|
+
| Node package metadata | 5 | `package.json` |
|
|
33
|
+
| Dependency lockfile | 4 | Common lockfiles such as `package-lock.json`, `pnpm-lock.yaml`, `poetry.lock`, `Cargo.lock`, or `go.sum` |
|
|
34
|
+
|
|
35
|
+
## Scoring
|
|
36
|
+
|
|
37
|
+
The score is the percentage of available weighted points that pass. Grades are:
|
|
38
|
+
|
|
39
|
+
- A: 90-100
|
|
40
|
+
- B: 80-89
|
|
41
|
+
- C: 70-79
|
|
42
|
+
- D: 60-69
|
|
43
|
+
- F: below 60
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# OSS Signal Report
|
|
2
|
+
|
|
3
|
+
Repository: `/Users/amon/Documents/Codex/2026-06-01/openai-s/outputs/oss-signal`
|
|
4
|
+
Generated: 2026-06-02T01:40:56.294Z
|
|
5
|
+
|
|
6
|
+
Score: **100/100** (A)
|
|
7
|
+
|
|
8
|
+
## Summary
|
|
9
|
+
|
|
10
|
+
- Passed: 15
|
|
11
|
+
- Failed: 0
|
|
12
|
+
- Total checks: 15
|
|
13
|
+
|
|
14
|
+
## Checks
|
|
15
|
+
|
|
16
|
+
| Status | Check | Why it matters |
|
|
17
|
+
| --- | --- | --- |
|
|
18
|
+
| PASS | README | A clear README is the front door for users and contributors. |
|
|
19
|
+
| PASS | License | A license tells downstream users what they may legally do with the code. |
|
|
20
|
+
| PASS | Contributing guide | Maintainers get better issues and pull requests when expectations are documented. |
|
|
21
|
+
| PASS | Security policy | Responsible disclosure needs a private, documented path. |
|
|
22
|
+
| PASS | Code of conduct | Community norms reduce ambiguity during difficult interactions. |
|
|
23
|
+
| PASS | Changelog | Users need a durable place to understand release impact. |
|
|
24
|
+
| PASS | Support policy | Support boundaries help maintainers avoid turning every request into unpaid consulting. |
|
|
25
|
+
| PASS | Continuous integration | CI catches regressions before maintainers merge changes. |
|
|
26
|
+
| PASS | Tests | Tests make review safer and lower the cost of outside contributions. |
|
|
27
|
+
| PASS | Issue templates | Issue templates collect the facts maintainers need to reproduce and triage. |
|
|
28
|
+
| PASS | Pull request template | PR templates nudge contributors to include tests, docs, and review context. |
|
|
29
|
+
| PASS | Dependency update automation | Automated dependency updates reduce security and compatibility drift. |
|
|
30
|
+
| PASS | Static security analysis | Static analysis finds common vulnerability patterns before releases. |
|
|
31
|
+
| PASS | Node package metadata | Package metadata makes installation, testing, and release automation discoverable. |
|
|
32
|
+
| PASS | Dependency lockfile | Lockfiles make CI and contributor setup reproducible. |
|
|
33
|
+
|
|
34
|
+
## Recommended Next Steps
|
|
35
|
+
|
|
36
|
+
No missing checks. Keep the report current as the repository evolves.
|
|
37
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "oss-signal",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A dependency-light CLI that audits open-source repository maintenance readiness.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"oss-signal": "./src/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/SalmonPlays/oss-signal.git"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/SalmonPlays/oss-signal/issues"
|
|
15
|
+
},
|
|
16
|
+
"homepage": "https://github.com/SalmonPlays/oss-signal#readme",
|
|
17
|
+
"files": [
|
|
18
|
+
"src",
|
|
19
|
+
"docs",
|
|
20
|
+
"README.md",
|
|
21
|
+
"LICENSE",
|
|
22
|
+
"CHANGELOG.md"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"test": "node --test",
|
|
26
|
+
"lint": "node --check src/*.js test/*.test.js",
|
|
27
|
+
"audit:self": "node src/cli.js . --format markdown --output docs/self-audit.md",
|
|
28
|
+
"audit:check": "node src/cli.js . --format json --fail-under 100 > /dev/null",
|
|
29
|
+
"check": "npm run lint && npm test && npm run audit:check",
|
|
30
|
+
"prepublishOnly": "npm run check"
|
|
31
|
+
},
|
|
32
|
+
"keywords": [
|
|
33
|
+
"open-source",
|
|
34
|
+
"maintainer",
|
|
35
|
+
"audit",
|
|
36
|
+
"repository",
|
|
37
|
+
"cli"
|
|
38
|
+
],
|
|
39
|
+
"author": "SalmonPlays",
|
|
40
|
+
"license": "MIT",
|
|
41
|
+
"publishConfig": {
|
|
42
|
+
"access": "public"
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=18"
|
|
46
|
+
}
|
|
47
|
+
}
|
package/src/cli.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { promises as fs } from "node:fs";
|
|
3
|
+
import { auditRepository, renderMarkdown } from "./index.js";
|
|
4
|
+
|
|
5
|
+
const VERSION = "0.1.0";
|
|
6
|
+
|
|
7
|
+
async function main(argv) {
|
|
8
|
+
const options = parseArgs(argv);
|
|
9
|
+
|
|
10
|
+
if (options.help) {
|
|
11
|
+
process.stdout.write(helpText());
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
if (options.version) {
|
|
15
|
+
process.stdout.write(`${VERSION}\n`);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const report = await auditRepository(options.path, { maxFiles: options.maxFiles });
|
|
20
|
+
const body = options.format === "json" ? `${JSON.stringify(report, null, 2)}\n` : renderMarkdown(report);
|
|
21
|
+
|
|
22
|
+
if (options.output) {
|
|
23
|
+
await fs.writeFile(options.output, body, "utf8");
|
|
24
|
+
} else {
|
|
25
|
+
process.stdout.write(body);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (typeof options.failUnder === "number" && report.score < options.failUnder) {
|
|
29
|
+
process.stderr.write(`oss-signal: score ${report.score} is below --fail-under ${options.failUnder}\n`);
|
|
30
|
+
process.exitCode = 1;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function parseArgs(argv) {
|
|
35
|
+
const options = {
|
|
36
|
+
path: ".",
|
|
37
|
+
format: "markdown",
|
|
38
|
+
output: undefined,
|
|
39
|
+
failUnder: undefined,
|
|
40
|
+
maxFiles: 20000,
|
|
41
|
+
help: false,
|
|
42
|
+
version: false
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const positionals = [];
|
|
46
|
+
|
|
47
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
48
|
+
const arg = argv[index];
|
|
49
|
+
if (arg === "--help" || arg === "-h") {
|
|
50
|
+
options.help = true;
|
|
51
|
+
} else if (arg === "--version" || arg === "-v") {
|
|
52
|
+
options.version = true;
|
|
53
|
+
} else if (arg === "--format") {
|
|
54
|
+
options.format = requireValue(argv, ++index, "--format");
|
|
55
|
+
} else if (arg.startsWith("--format=")) {
|
|
56
|
+
options.format = arg.slice("--format=".length);
|
|
57
|
+
} else if (arg === "--output" || arg === "-o") {
|
|
58
|
+
options.output = requireValue(argv, ++index, arg);
|
|
59
|
+
} else if (arg.startsWith("--output=")) {
|
|
60
|
+
options.output = arg.slice("--output=".length);
|
|
61
|
+
} else if (arg === "--fail-under") {
|
|
62
|
+
options.failUnder = parseNumber(requireValue(argv, ++index, "--fail-under"), "--fail-under");
|
|
63
|
+
} else if (arg.startsWith("--fail-under=")) {
|
|
64
|
+
options.failUnder = parseNumber(arg.slice("--fail-under=".length), "--fail-under");
|
|
65
|
+
} else if (arg === "--max-files") {
|
|
66
|
+
options.maxFiles = parseNumber(requireValue(argv, ++index, "--max-files"), "--max-files");
|
|
67
|
+
} else if (arg.startsWith("--max-files=")) {
|
|
68
|
+
options.maxFiles = parseNumber(arg.slice("--max-files=".length), "--max-files");
|
|
69
|
+
} else if (arg.startsWith("-")) {
|
|
70
|
+
throw new Error(`Unknown option: ${arg}`);
|
|
71
|
+
} else {
|
|
72
|
+
positionals.push(arg);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (positionals.length > 1) {
|
|
77
|
+
throw new Error(`Expected at most one repository path, got ${positionals.length}`);
|
|
78
|
+
}
|
|
79
|
+
if (positionals.length === 1) {
|
|
80
|
+
options.path = positionals[0];
|
|
81
|
+
}
|
|
82
|
+
if (!["markdown", "json"].includes(options.format)) {
|
|
83
|
+
throw new Error("--format must be either markdown or json");
|
|
84
|
+
}
|
|
85
|
+
return options;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function requireValue(argv, index, optionName) {
|
|
89
|
+
const value = argv[index];
|
|
90
|
+
if (!value || value.startsWith("-")) {
|
|
91
|
+
throw new Error(`${optionName} requires a value`);
|
|
92
|
+
}
|
|
93
|
+
return value;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function parseNumber(value, optionName) {
|
|
97
|
+
const parsed = Number(value);
|
|
98
|
+
if (!Number.isFinite(parsed)) {
|
|
99
|
+
throw new Error(`${optionName} must be a number`);
|
|
100
|
+
}
|
|
101
|
+
return parsed;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function helpText() {
|
|
105
|
+
return `oss-signal audits open-source repository maintenance readiness.
|
|
106
|
+
|
|
107
|
+
Usage:
|
|
108
|
+
oss-signal [path] [--format markdown|json] [--output file] [--fail-under score]
|
|
109
|
+
|
|
110
|
+
Options:
|
|
111
|
+
--format Output format. Defaults to markdown.
|
|
112
|
+
--output, -o Write the report to a file instead of stdout.
|
|
113
|
+
--fail-under Exit with code 1 when the score is below this value.
|
|
114
|
+
--max-files Maximum files to inspect. Defaults to 20000.
|
|
115
|
+
--version, -v Show the CLI version.
|
|
116
|
+
--help, -h Show this help message.
|
|
117
|
+
`;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
main(process.argv.slice(2)).catch((error) => {
|
|
121
|
+
process.stderr.write(`oss-signal: ${error.message}\n`);
|
|
122
|
+
process.exitCode = 1;
|
|
123
|
+
});
|
package/src/index.js
ADDED
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
const COMMUNITY_FILES = [
|
|
5
|
+
{
|
|
6
|
+
id: "readme",
|
|
7
|
+
label: "README",
|
|
8
|
+
weight: 12,
|
|
9
|
+
paths: ["README.md", "README"],
|
|
10
|
+
why: "A clear README is the front door for users and contributors.",
|
|
11
|
+
fix: "Add setup, usage, contribution, support, and project status sections to README.md."
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
id: "license",
|
|
15
|
+
label: "License",
|
|
16
|
+
weight: 10,
|
|
17
|
+
paths: ["LICENSE", "LICENSE.md", "COPYING"],
|
|
18
|
+
why: "A license tells downstream users what they may legally do with the code.",
|
|
19
|
+
fix: "Add an OSI-approved license file such as MIT, Apache-2.0, BSD-3-Clause, or MPL-2.0."
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: "contributing",
|
|
23
|
+
label: "Contributing guide",
|
|
24
|
+
weight: 9,
|
|
25
|
+
paths: ["CONTRIBUTING.md", ".github/CONTRIBUTING.md", "docs/CONTRIBUTING.md"],
|
|
26
|
+
why: "Maintainers get better issues and pull requests when expectations are documented.",
|
|
27
|
+
fix: "Add CONTRIBUTING.md with local setup, test commands, review expectations, and release notes guidance."
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: "security",
|
|
31
|
+
label: "Security policy",
|
|
32
|
+
weight: 9,
|
|
33
|
+
paths: ["SECURITY.md", ".github/SECURITY.md"],
|
|
34
|
+
why: "Responsible disclosure needs a private, documented path.",
|
|
35
|
+
fix: "Add SECURITY.md with supported versions, reporting instructions, and response expectations."
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: "code-of-conduct",
|
|
39
|
+
label: "Code of conduct",
|
|
40
|
+
weight: 6,
|
|
41
|
+
paths: ["CODE_OF_CONDUCT.md", ".github/CODE_OF_CONDUCT.md"],
|
|
42
|
+
why: "Community norms reduce ambiguity during difficult interactions.",
|
|
43
|
+
fix: "Add CODE_OF_CONDUCT.md, for example the Contributor Covenant."
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
id: "changelog",
|
|
47
|
+
label: "Changelog",
|
|
48
|
+
weight: 6,
|
|
49
|
+
paths: ["CHANGELOG.md", "HISTORY.md", "RELEASES.md"],
|
|
50
|
+
why: "Users need a durable place to understand release impact.",
|
|
51
|
+
fix: "Keep CHANGELOG.md with dated release entries and migration notes."
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: "support",
|
|
55
|
+
label: "Support policy",
|
|
56
|
+
weight: 4,
|
|
57
|
+
paths: ["SUPPORT.md", ".github/SUPPORT.md"],
|
|
58
|
+
why: "Support boundaries help maintainers avoid turning every request into unpaid consulting.",
|
|
59
|
+
fix: "Add SUPPORT.md describing where to ask questions, what is in scope, and expected response times."
|
|
60
|
+
}
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
const AUTOMATION_FILES = [
|
|
64
|
+
{
|
|
65
|
+
id: "ci",
|
|
66
|
+
label: "Continuous integration",
|
|
67
|
+
weight: 12,
|
|
68
|
+
matcher: (tree) => tree.some((file) => file.startsWith(".github/workflows/") && /\.ya?ml$/i.test(file)),
|
|
69
|
+
why: "CI catches regressions before maintainers merge changes.",
|
|
70
|
+
fix: "Add a GitHub Actions workflow that runs linting and tests on pushes and pull requests."
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
id: "tests",
|
|
74
|
+
label: "Tests",
|
|
75
|
+
weight: 10,
|
|
76
|
+
matcher: (tree) => tree.some((file) => /(^|\/)(test|tests|spec|__tests__)\//i.test(file) || /\.(test|spec)\.[cm]?[jt]sx?$/i.test(file)),
|
|
77
|
+
why: "Tests make review safer and lower the cost of outside contributions.",
|
|
78
|
+
fix: "Add focused tests for public behavior and document the test command."
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
id: "issue-templates",
|
|
82
|
+
label: "Issue templates",
|
|
83
|
+
weight: 5,
|
|
84
|
+
matcher: (tree) => tree.some((file) => file.startsWith(".github/ISSUE_TEMPLATE/") || file === ".github/ISSUE_TEMPLATE.md"),
|
|
85
|
+
why: "Issue templates collect the facts maintainers need to reproduce and triage.",
|
|
86
|
+
fix: "Add bug report and feature request templates under .github/ISSUE_TEMPLATE/."
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
id: "pull-request-template",
|
|
90
|
+
label: "Pull request template",
|
|
91
|
+
weight: 5,
|
|
92
|
+
matcher: (tree) => tree.includes(".github/PULL_REQUEST_TEMPLATE.md") || tree.includes("PULL_REQUEST_TEMPLATE.md"),
|
|
93
|
+
why: "PR templates nudge contributors to include tests, docs, and review context.",
|
|
94
|
+
fix: "Add .github/PULL_REQUEST_TEMPLATE.md with a short checklist."
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
id: "dependabot",
|
|
98
|
+
label: "Dependency update automation",
|
|
99
|
+
weight: 5,
|
|
100
|
+
matcher: (tree) => tree.includes(".github/dependabot.yml") || tree.includes(".github/dependabot.yaml"),
|
|
101
|
+
why: "Automated dependency updates reduce security and compatibility drift.",
|
|
102
|
+
fix: "Add .github/dependabot.yml for the package ecosystems used in the repository."
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
id: "codeql",
|
|
106
|
+
label: "Static security analysis",
|
|
107
|
+
weight: 4,
|
|
108
|
+
matcher: (tree) => tree.some((file) => file.startsWith(".github/workflows/") && /codeql|security/i.test(file)),
|
|
109
|
+
why: "Static analysis finds common vulnerability patterns before releases.",
|
|
110
|
+
fix: "Add a CodeQL or equivalent security scanning workflow."
|
|
111
|
+
}
|
|
112
|
+
];
|
|
113
|
+
|
|
114
|
+
const PACKAGE_FILES = [
|
|
115
|
+
{
|
|
116
|
+
id: "package-json",
|
|
117
|
+
label: "Node package metadata",
|
|
118
|
+
weight: 5,
|
|
119
|
+
matcher: (tree) => tree.includes("package.json"),
|
|
120
|
+
why: "Package metadata makes installation, testing, and release automation discoverable.",
|
|
121
|
+
fix: "Add package.json with name, description, license, scripts, repository, and engines fields."
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
id: "lockfile",
|
|
125
|
+
label: "Dependency lockfile",
|
|
126
|
+
weight: 4,
|
|
127
|
+
matcher: (tree) => tree.some((file) => ["package-lock.json", "npm-shrinkwrap.json", "pnpm-lock.yaml", "yarn.lock", "uv.lock", "poetry.lock", "Pipfile.lock", "Cargo.lock", "go.sum"].includes(file)),
|
|
128
|
+
why: "Lockfiles make CI and contributor setup reproducible.",
|
|
129
|
+
fix: "Commit the lockfile for application-style projects, or document why this library intentionally omits one."
|
|
130
|
+
}
|
|
131
|
+
];
|
|
132
|
+
|
|
133
|
+
const DEFAULT_IGNORE_DIRS = new Set([
|
|
134
|
+
".git",
|
|
135
|
+
"node_modules",
|
|
136
|
+
"vendor",
|
|
137
|
+
"dist",
|
|
138
|
+
"build",
|
|
139
|
+
"coverage",
|
|
140
|
+
".next",
|
|
141
|
+
".turbo",
|
|
142
|
+
".venv",
|
|
143
|
+
"__pycache__"
|
|
144
|
+
]);
|
|
145
|
+
|
|
146
|
+
export async function auditRepository(root, options = {}) {
|
|
147
|
+
const absoluteRoot = path.resolve(root ?? ".");
|
|
148
|
+
const tree = await listRepositoryFiles(absoluteRoot, options);
|
|
149
|
+
const fileSet = new Set(tree);
|
|
150
|
+
const checks = [
|
|
151
|
+
...COMMUNITY_FILES.map((rule) => checkPathRule(rule, fileSet)),
|
|
152
|
+
...AUTOMATION_FILES.map((rule) => checkMatcherRule(rule, tree)),
|
|
153
|
+
...PACKAGE_FILES.map((rule) => checkMatcherRule(rule, tree))
|
|
154
|
+
];
|
|
155
|
+
|
|
156
|
+
const totalWeight = checks.reduce((sum, check) => sum + check.weight, 0);
|
|
157
|
+
const earnedWeight = checks.filter((check) => check.passed).reduce((sum, check) => sum + check.weight, 0);
|
|
158
|
+
const score = totalWeight === 0 ? 0 : Math.round((earnedWeight / totalWeight) * 100);
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
tool: "oss-signal",
|
|
162
|
+
version: "0.1.0",
|
|
163
|
+
root: absoluteRoot,
|
|
164
|
+
generatedAt: new Date().toISOString(),
|
|
165
|
+
score,
|
|
166
|
+
grade: gradeForScore(score),
|
|
167
|
+
summary: summarizeChecks(checks),
|
|
168
|
+
checks,
|
|
169
|
+
recommendations: checks
|
|
170
|
+
.filter((check) => !check.passed)
|
|
171
|
+
.sort((a, b) => b.weight - a.weight)
|
|
172
|
+
.map(({ id, label, weight, why, fix }) => ({ id, label, weight, why, fix }))
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function renderMarkdown(report) {
|
|
177
|
+
const lines = [
|
|
178
|
+
"# OSS Signal Report",
|
|
179
|
+
"",
|
|
180
|
+
`Repository: \`${report.root}\``,
|
|
181
|
+
`Generated: ${report.generatedAt}`,
|
|
182
|
+
"",
|
|
183
|
+
`Score: **${report.score}/100** (${report.grade})`,
|
|
184
|
+
"",
|
|
185
|
+
"## Summary",
|
|
186
|
+
"",
|
|
187
|
+
`- Passed: ${report.summary.passed}`,
|
|
188
|
+
`- Failed: ${report.summary.failed}`,
|
|
189
|
+
`- Total checks: ${report.summary.total}`,
|
|
190
|
+
"",
|
|
191
|
+
"## Checks",
|
|
192
|
+
"",
|
|
193
|
+
"| Status | Check | Why it matters |",
|
|
194
|
+
"| --- | --- | --- |"
|
|
195
|
+
];
|
|
196
|
+
|
|
197
|
+
for (const check of report.checks) {
|
|
198
|
+
lines.push(`| ${check.passed ? "PASS" : "FAIL"} | ${escapeTable(check.label)} | ${escapeTable(check.why)} |`);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
lines.push("", "## Recommended Next Steps", "");
|
|
202
|
+
if (report.recommendations.length === 0) {
|
|
203
|
+
lines.push("No missing checks. Keep the report current as the repository evolves.");
|
|
204
|
+
} else {
|
|
205
|
+
for (const recommendation of report.recommendations) {
|
|
206
|
+
lines.push(`- **${recommendation.label}** (${recommendation.weight} pts): ${recommendation.fix}`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
lines.push("");
|
|
211
|
+
return `${lines.join("\n")}\n`;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export async function listRepositoryFiles(root, options = {}) {
|
|
215
|
+
const maxFiles = options.maxFiles ?? 20000;
|
|
216
|
+
const files = [];
|
|
217
|
+
|
|
218
|
+
async function walk(currentDir, relativeDir = "") {
|
|
219
|
+
if (files.length >= maxFiles) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
let entries;
|
|
224
|
+
try {
|
|
225
|
+
entries = await fs.readdir(currentDir, { withFileTypes: true });
|
|
226
|
+
} catch (error) {
|
|
227
|
+
if (error.code === "EACCES" || error.code === "ENOENT") {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
throw error;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
234
|
+
|
|
235
|
+
for (const entry of entries) {
|
|
236
|
+
if (files.length >= maxFiles) {
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
const relativePath = path.posix.join(relativeDir, entry.name);
|
|
240
|
+
const fullPath = path.join(currentDir, entry.name);
|
|
241
|
+
|
|
242
|
+
if (entry.isDirectory()) {
|
|
243
|
+
if (!DEFAULT_IGNORE_DIRS.has(entry.name)) {
|
|
244
|
+
await walk(fullPath, relativePath);
|
|
245
|
+
}
|
|
246
|
+
} else if (entry.isFile()) {
|
|
247
|
+
files.push(relativePath);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
await walk(root);
|
|
253
|
+
return files;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function checkPathRule(rule, fileSet) {
|
|
257
|
+
const matchedPath = rule.paths.find((candidate) => fileSet.has(candidate));
|
|
258
|
+
return {
|
|
259
|
+
id: rule.id,
|
|
260
|
+
label: rule.label,
|
|
261
|
+
weight: rule.weight,
|
|
262
|
+
passed: Boolean(matchedPath),
|
|
263
|
+
evidence: matchedPath ? [matchedPath] : [],
|
|
264
|
+
why: rule.why,
|
|
265
|
+
fix: rule.fix
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function checkMatcherRule(rule, tree) {
|
|
270
|
+
const passed = rule.matcher(tree);
|
|
271
|
+
return {
|
|
272
|
+
id: rule.id,
|
|
273
|
+
label: rule.label,
|
|
274
|
+
weight: rule.weight,
|
|
275
|
+
passed,
|
|
276
|
+
evidence: passed ? findEvidence(rule.id, tree) : [],
|
|
277
|
+
why: rule.why,
|
|
278
|
+
fix: rule.fix
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function findEvidence(id, tree) {
|
|
283
|
+
if (id === "ci" || id === "codeql") {
|
|
284
|
+
return tree.filter((file) => file.startsWith(".github/workflows/")).slice(0, 5);
|
|
285
|
+
}
|
|
286
|
+
if (id === "tests") {
|
|
287
|
+
return tree.filter((file) => /(^|\/)(test|tests|spec|__tests__)\//i.test(file) || /\.(test|spec)\.[cm]?[jt]sx?$/i.test(file)).slice(0, 5);
|
|
288
|
+
}
|
|
289
|
+
if (id === "issue-templates") {
|
|
290
|
+
return tree.filter((file) => file.startsWith(".github/ISSUE_TEMPLATE/") || file === ".github/ISSUE_TEMPLATE.md").slice(0, 5);
|
|
291
|
+
}
|
|
292
|
+
if (id === "pull-request-template") {
|
|
293
|
+
return tree.filter((file) => file === ".github/PULL_REQUEST_TEMPLATE.md" || file === "PULL_REQUEST_TEMPLATE.md").slice(0, 5);
|
|
294
|
+
}
|
|
295
|
+
if (id === "dependabot") {
|
|
296
|
+
return tree.filter((file) => file === ".github/dependabot.yml" || file === ".github/dependabot.yaml").slice(0, 5);
|
|
297
|
+
}
|
|
298
|
+
if (id === "package-json") {
|
|
299
|
+
return tree.includes("package.json") ? ["package.json"] : [];
|
|
300
|
+
}
|
|
301
|
+
if (id === "lockfile") {
|
|
302
|
+
return tree.filter((file) => ["package-lock.json", "npm-shrinkwrap.json", "pnpm-lock.yaml", "yarn.lock", "uv.lock", "poetry.lock", "Pipfile.lock", "Cargo.lock", "go.sum"].includes(file)).slice(0, 5);
|
|
303
|
+
}
|
|
304
|
+
return [];
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function summarizeChecks(checks) {
|
|
308
|
+
const passed = checks.filter((check) => check.passed).length;
|
|
309
|
+
return {
|
|
310
|
+
total: checks.length,
|
|
311
|
+
passed,
|
|
312
|
+
failed: checks.length - passed
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function gradeForScore(score) {
|
|
317
|
+
if (score >= 90) return "A";
|
|
318
|
+
if (score >= 80) return "B";
|
|
319
|
+
if (score >= 70) return "C";
|
|
320
|
+
if (score >= 60) return "D";
|
|
321
|
+
return "F";
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function escapeTable(value) {
|
|
325
|
+
return String(value).replaceAll("|", "\\|");
|
|
326
|
+
}
|