diff --git a/config/scopes.yaml b/config/scopes.yaml index 09a4392f..c40dd1d7 100644 --- a/config/scopes.yaml +++ b/config/scopes.yaml @@ -1,21 +1,20 @@ events: - path: /events/{request_id} + path: /events/{event_id} methods: - get - - put + - patch events-search: - path: /events/search + path: /events methods: - get visitors: path: /visitors/{visitor_id} methods: - - get - delete webhook: path: /webhook methods: - - trace + - post related-visitors: path: /related-visitors methods: diff --git a/scripts/lintChangesets.js b/scripts/lintChangesets.js index 5ba51c61..992220e0 100644 --- a/scripts/lintChangesets.js +++ b/scripts/lintChangesets.js @@ -1,7 +1,12 @@ import fs from 'node:fs'; import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import yaml from 'js-yaml'; const DIR = process.argv[2] ?? '.changeset'; +const scriptDir = path.dirname(fileURLToPath(import.meta.url)); +const SCOPES_PATH = path.resolve(scriptDir, '../config/scopes.yaml'); +const validScopes = new Set(Object.keys(yaml.load(fs.readFileSync(SCOPES_PATH, 'utf8')))); const files = fs.readdirSync(DIR).filter((f) => f.endsWith('.md') && f !== 'README.md'); @@ -26,6 +31,15 @@ for (const file of files) { continue; } + const scopeMatch = body.match(/^\*\*([^*]+)\*\*:\s*/); + if (scopeMatch) { + const scope = scopeMatch[1]; + if (!validScopes.has(scope)) { + const allowed = [...validScopes].sort().join(', '); + issues.push(`${full}: invalid scope "${scope}" — use one of: ${allowed} (or omit the scope line)`); + } + } + const withoutScope = body.replace(/^\*\*[^*]+\*\*:\s*/, ''); const firstChar = withoutScope[0]; diff --git a/scripts/lintChangesets.spec.js b/scripts/lintChangesets.spec.js index ec6906af..355c6578 100644 --- a/scripts/lintChangesets.spec.js +++ b/scripts/lintChangesets.spec.js @@ -55,4 +55,26 @@ describe('lintChangesets', () => { expect(status).toBe(1); expect(stderr).toMatch(/should end with a period/); }); + + it('flags a scope that is not in config/scopes.yaml', () => { + const { status, stderr } = run({ + 'bad.md': `${frontmatter}**unknown-scope**: Add \`x\` to \`Event\`\n`, + }); + expect(status).toBe(1); + expect(stderr).toMatch(/invalid scope "unknown-scope"/); + }); + + it('accepts a hyphenated scope from config/scopes.yaml', () => { + const { status } = run({ + 'good.md': `${frontmatter}**events-search**: Add \`q\` query parameter to search\n`, + }); + expect(status).toBe(0); + }); + + it('accepts a changeset with no scope line', () => { + const { status } = run({ + 'good.md': `${frontmatter}Add \`x\` to \`Event\`\n`, + }); + expect(status).toBe(0); + }); });