我构建了一个 CLI,告诉你你的开源项目符合哪些免费福利
Source: Dev.to
TL;DR: 开源维护者因为没有人把所有免费额度和工具聚合起来,导致错失成千上万美元的免费信用和工具。我构建了 OSS Perks – 一个网站 + CLI,列出 15+ 家供应商的福利计划,并只需一条命令即可检查你的仓库是否符合资格。
问题
Vercel、Sentry、JetBrains、Cloudflare——它们都向 OSS 维护者提供免费资源:
- $3,600 的托管积分
- 500 万次错误事件
- 无限的 IDE 许可证
没有人知道其中一半。
所有这些优惠分散在不同的网站上,埋藏在营销页面里,各自有不同的资格规则和申请步骤。我花了一个周末在 Google 上搜索“开源免费工具”,在标签页之间切换,试图弄清楚我的项目符合哪些条件。
随后,我看到了 getfirstcheck.com,作者是 @5harath ——一个为创始人提供免费积分和资助的创业项目目录。看到发布推文后,我恍然大悟:创始人有 firstcheck,但 OSS 维护者却没有类似的东西。
于是我创建了 OSS Perks。
它是什么
两件事:
- 一个网站 – 可搜索的 OSS 福利计划目录,提供 9 种语言。
- 一个 CLI – 在任意仓库运行
ossperks check,它会告诉你符合哪些福利。
Source: …
架构
ossperks/
├── packages/
│ ├── data/ # JSON 程序 + Zod 架构
│ └── cli/ # 检查资格的 CLI
├── docs/ # Next.js 16 + Fumadocs 网站
└── pnpm-workspace.yaml核心理念:@ossperks/data 是 唯一可信来源。每个项目都是一个 JSON 文件,例如:
{
"slug": "vercel",
"name": "Vercel for Open Source",
"provider": "Vercel",
"url": "https://vercel.com/open-source-program",
"category": "hosting",
"description": "Vercel provides platform credits, community support, and an OSS Starter Pack.",
"perks": [
{
"title": "$3,600 Platform Credits",
"description": "$3,600 in Vercel platform credits distributed over 12 months."
},
{
"title": "OSS Starter Pack",
"description": "Credits from third‑party services to boost your project."
}
],
"eligibility": [
"Must be an open-source project that is actively developed and maintained.",
"Must show measurable impact or growth potential.",
"Must follow a Code of Conduct.",
"Credits must be used exclusively for open-source work."
],
"duration": "12 months",
"tags": ["hosting", "deployment", "serverless", "credits"]
}这个单一文件会被用于三件事:
- 网站 – 渲染项目页面。
- CLI – 用于
list、show、search。 - 构建脚本 – 将其转换为 MDX,然后 lingo.dev 将其翻译成另外 8 种语言。
修改 JSON,所有内容都会随之更新。
核心技巧:资格检查
ossperks check 会从 GitHub/GitLab 读取你的仓库元数据,并将每条资格规则通过一系列匹配器进行处理。
const matchRule = (rule: string, ctx: RepoContext): RuleVerdict =>
checkSubjective(rule, ctx) ??
checkProvider(rule, ctx) ??
checkStars(rule, ctx) ??
checkActivity(rule, ctx) ??
checkLicense(rule, ctx) ??
checkRepoAttrs(rule, ctx) ?? { reason: rule, verdict: "unknown" };每个检查器都会对人类可读的资格字符串使用正则表达式,以推断规则类型,然后针对仓库进行验证。示例——许可证检查器:
const checkLicense = (rule: string, ctx: RepoContext): RuleVerdict | null => {
const label = ctx.license ?? "no detected license";
if (/permissive\s+(?:open[\s-]?source\s+)?licen[sc]e/i.test(rule)) {
return isPermissive(ctx.license)
? { verdict: "pass" }
: {
reason: `requires a permissive license (detected: ${label})`,
verdict: "fail",
};
}
if (/open[\s-]?source\s+licen[sc]e|recognized\s+licen[sc]e/i.test(rule)) {
return isOsiApproved(ctx.license)
? { verdict: "pass" }
: {
reason: `requires an OSI‑approved license (detected: ${label})`,
verdict: "fail",
};
}
return null;
};不需要为特定项目编写 if 语句——资格规则以 JSON 中的字符串形式存在,引擎会对意图进行模式匹配并检查仓库。添加新项目只需放入一个新的 JSON 文件;检查器会自动处理。
示例输出:
✔ next.js — MIT · 131,247 stars · last push today
Eligibility across 15 programs — 8 eligible, 5 need review, 2 ineligible
✔ vercel eligible
✔ sentry eligible
✔ github-copilot eligible
✔ jetbrains eligible
⚠ cloudflare needs review
• non‑commercial requirement cannot be auto‑verified
✖ browserstack ineligible
• requires 500+ stars (you have 42)数据管道:JSON → 9 种语言
packages/data/src/programs/*.json (真相唯一来源)
│
▼
docs/scripts/generate-programs-mdx (将 JSON 转换为 MDX)
│
▼
lingo.dev (将 MDX 翻译成另外 8 种语言)该管道确保每个项目页面都能在所有受支持的语言中使用,同步保持唯一的真相来源。
.mjs (JSON → MDX)
│
▼
docs/content/programs/en/*.mdx (英文)
│
▼ lingo.dev
docs/content/programs/{es,fr,de,ja,ko,zh-CN,pt-BR,ru}/*.mdx生成脚本
生成脚本会从每个 JSON 项目构建结构化的 Markdown:
const buildMarkdownBody = (p) => {
const sections = [
buildMetaSection(p),
buildPerksSection(p),
buildEligibilitySection(p),
buildRequirementsSection(p),
buildApplicationProcessSection(p),
buildTagsSection(p),
].filter(Boolean);
return sections.join("\n\n");
};网站上的解析
在网站上,翻译后的 MDX 会被解析回结构化数据以供渲染:
const parsePerks = (
section: string
): { title: string; description: string }[] =>
section
.split("\n")
.filter((l) => /^-\s+\*\*/.test(l.trim()))
.map((line) => {
const match = line.match(/\*\*(.+?)\*\*\s*[::]\s*(.*)/);
return match
? { description: match[2], title: match[1] }
: { description: "", title: line };
});注意: JSON → MDX → 再解析回结构化数据是一次往返过程。实际的决定是让 lingo.dev 翻译 MDX 文件,而不是直接翻译原始 JSON。
为什么选择这个技术栈
方法
| Stack | Reason |
|---|---|
| Static site + JSON API | 没有 i18n,且没有文档框架 |
| Astro | 在大规模时 i18n 生态系统较少 |
| Docusaurus | 较重,仅支持当时的 React 18 |
| Fumadocs + Next.js 16 (chosen) | 免费 i18n、MDX、OG 图片、搜索。开箱即用地提供带语言前缀的路由、语言切换、OG 图片生成以及每种语言的内容源。 |
| Commander | 标准的 CLI 库 |
使用 Zod 进行验证
每个程序在导入时都会经过 Zod 检验。错误的 JSON 会立即报错:
export const programSchema = z.object({
applicationProcess: z.array(z.string()).optional(),
applicationUrl: z.string().url().optional(),
category: categoryEnum,
contact: contactSchema.optional(),
description: z.string(),
duration: z.string().optional(),
eligibility: z.array(z.string()),
name: z.string(),
perks: z.array(perkSchema),
provider: z.string(),
requirements: z.array(z.string()).optional(),
slug: z.string(),
tags: z.array(z.string()).optional(),
url: z.string().url(),
});
export const programs: Program[] = raw.map((p) => programSchema.parse(p));权衡
- MDX round‑trip – JSON → MDX → 解析回来的过程有点尴尬,但这是必要的,因为 lingo.dev 翻译的是 MDX,而不是 JSON。
- Regex eligibility matching – 该方法适用于当前的 15 个项目,但较为脆弱;使用结构化规则会在长期内更为稳健。
- No auth by default – CLI 默认未认证地访问 GitHub API,可能会触发速率限制。设置
GITHUB_TOKEN可避免此问题。 - Limited program count – 目前仅收录了 15 个项目;还有数十个(DigitalOcean、AWS、MongoDB、Datadog 等)只需添加对应的 JSON 文件即可。
试一试
网站
git clone https://github.com/Aniket-508/ossperks.git
cd ossperks
pnpm install
pnpm --filter docs devCLI
npx @ossperks/cli
# Check the current repo
ossperks check
# Check a specific repo
ossperks check --repo vercel/next.js
# List all programs
ossperks list
# Search
ossperks search hosting
# Show program details
ossperks show vercel接下来构建什么
如果你 fork 了此仓库:
- 添加项目 – 创建一个
{slug}.json并提交 PR。就是这么简单。 - 结构化资格规则 – 用类似
{ "type": "min-stars", "value": 500 }的对象替换自由文本,使检查器不依赖正则表达式。 - GitHub Action – 在 CI 中运行
ossperks check并将结果作为 PR 评论发布。 - 过期跟踪 – 许多福利在 12 个月后失效;提醒功能会很有帮助。
- 社区提交 – API 路径 (
/api/submit-program) 已经存在。
如果您维护开源项目,请运行 npx @ossperks/cli check。您可能会惊讶于自己符合哪些福利。
链接
- GitHub:
- Website:
- NPM:
知道缺少的程序吗? 打开一个 issue 或添加一个 JSON 文件。大约需要 5 分钟。
