Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/publish_cli.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
id-token: write
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
uses: actions/checkout@v6
Comment thread
riderx marked this conversation as resolved.
with:
fetch-depth: 0
filter: blob:none
Expand All @@ -36,7 +36,7 @@ jobs:
run: bun run cli:build
- name: Generate AI changelog
id: changelog
uses: mistricky/ccc@d9358b3eca472c0f41a6c1732bf073c0c54b837e # v0.2.6
uses: mistricky/ccc@v0.2.6
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
github_token: ${{ github.token }}
Expand All @@ -53,7 +53,7 @@ jobs:
run: bun publish --cwd cli --tag next --access public
- name: Create GitHub release
id: create_release
uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2
uses: softprops/action-gh-release@v2
with:
body: |
## 🆕 Changelog
Expand All @@ -64,5 +64,5 @@ jobs:

🔗 **Full Changelog**: https://github.com/${{ github.repository }}/compare/${{ steps.changelog.outputs.from_tag }}...${{ steps.changelog.outputs.to_tag }}
make_latest: false
token: ${{ github.token }}
token: "${{ secrets.PERSONAL_ACCESS_TOKEN }}"
prerelease: ${{ contains(github.ref, '-alpha.') }}
1 change: 1 addition & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ jobs:
shared:
- '.github/workflows/**'
- '.github/scripts/**'
- 'scripts/release-scope.ts'
- 'scripts/setup-bun.sh'
- 'scripts/setup-bun.ps1'
- '.npmrc'
Expand Down
9 changes: 5 additions & 4 deletions .sonarcloud.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#
# We pin workflow actions by commit SHA, which creates noisy GitHub Actions
# hotspot findings in Sonar. Disable that analysis for this repository.
# GitHub Actions workflow files create noisy security hotspot findings in
# Sonar. Keep workflow analysis disabled and exclude CI automation files from
# the general scan for this repository.
sonar.githubactions.activate=false

sonar.exclusions=cli/**,scripts/**,tests/**,scriptable/**,.cursor/**,playwright/**,formkit.theme.ts,sql/**,supabase/seed.sql,supabase/tests/**,src/components/comp_def.ts,supabase/functions/_backend/utils/supabase.types.ts,src/types/supabase.types.ts
sonar.cpd.exclusions=cli/**,scripts/**,tests/**,scriptable/**,.cursor/**,playwright/**,formkit.theme.ts,sql/**,supabase/seed.sql,supabase/migrations/**,supabase/functions/_backend/utils/conversion.ts,supabase/tests/**,supabase/functions/_backend/plugins/updates_lite.ts,supabase/functions/_backend/utils/pg_lite.ts,supabase/functions/_backend/utils/supabase.types.ts,src/types/supabase.types.ts,src/components/comp_def.ts,src/services/conversion.ts
sonar.exclusions=.github/workflows/**,.github/scripts/**,cli/**,scripts/**,tests/**,scriptable/**,.cursor/**,playwright/**,formkit.theme.ts,sql/**,supabase/seed.sql,supabase/tests/**,src/components/comp_def.ts,supabase/functions/_backend/utils/supabase.types.ts,src/types/supabase.types.ts
sonar.cpd.exclusions=.github/workflows/**,.github/scripts/**,cli/**,scripts/**,tests/**,scriptable/**,.cursor/**,playwright/**,formkit.theme.ts,sql/**,supabase/seed.sql,supabase/migrations/**,supabase/functions/_backend/utils/conversion.ts,supabase/tests/**,supabase/functions/_backend/plugins/updates_lite.ts,supabase/functions/_backend/utils/pg_lite.ts,supabase/functions/_backend/utils/supabase.types.ts,src/types/supabase.types.ts,src/components/comp_def.ts,src/services/conversion.ts
Comment thread
riderx marked this conversation as resolved.
91 changes: 60 additions & 31 deletions scripts/release-scope.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,30 @@
import { execFileSync } from 'node:child_process'

type Component = 'capgo' | 'cli'
type ReleaseAs = 'patch' | 'minor' | 'major'

const componentMatchers: Record<Component, RegExp[]> = {
export type Component = 'capgo' | 'cli'
export type ReleaseAs = 'patch' | 'minor' | 'major'

const sharedMatchers = [
/^\.github\/workflows\/bump_version\.yml$/,
/^\.github\/workflows\/tests\.yml$/,
/^\.github\/scripts\//,
/^scripts\/release-scope\.ts$/,
/^scripts\/setup-bun\.sh$/,
/^scripts\/setup-bun\.ps1$/,
/^package\.json$/,
/^bun\.lock$/,
/^\.npmrc$/,
/^\.typos\.toml$/,
/^bunfig\.toml$/,
/^tsconfig\.json$/,
/^vitest\.config\.ts$/,
/^vitest\.config\.cloudflare\.ts$/,
/^vitest\.config\.cloudflare-plugin\.ts$/,
]

export const componentMatchers: Record<Component, RegExp[]> = {
capgo: [
/^package\.json$/,
/^bun\.lock$/,
...sharedMatchers,
/^\.github\/workflows\/build_and_deploy\.yml$/,
/^aliproxy\//,
/^android\//,
/^assets\//,
Expand Down Expand Up @@ -35,7 +53,8 @@ const componentMatchers: Record<Component, RegExp[]> = {
/^wrangler\.jsonc$/,
],
cli: [
/^bun\.lock$/,
...sharedMatchers,
/^\.github\/workflows\/publish_cli\.yml$/,
/^cli\/src\//,
/^cli\/skills\/[^/]+\/SKILL\.md$/,
/^cli\/skills\/(?!.*\.(md|mdx)$)/,
Expand Down Expand Up @@ -82,11 +101,11 @@ function getCommitMessage(commit: string): { subject: string, body: string } {
}
}

function matchesComponent(component: Component, files: string[]): boolean {
export function matchesComponent(component: Component, files: string[]): boolean {
return files.some(file => componentMatchers[component].some(pattern => pattern.test(file)))
}

function getSeverity(subject: string, body: string): number {
export function getSeverity(subject: string, body: string): number {
const conventionalMatch = subject.match(/^([a-z]+)(\([^)]+\))?(!)?:/)
const hasBreakingChange = body.includes('BREAKING CHANGE:') || conventionalMatch?.[3] === '!'

Expand All @@ -101,7 +120,7 @@ function getSeverity(subject: string, body: string): number {
return 1
}

function toReleaseAs(severity: number): ReleaseAs {
export function toReleaseAs(severity: number): ReleaseAs {
if (severity >= 3) {
return 'major'
}
Expand All @@ -113,31 +132,41 @@ function toReleaseAs(severity: number): ReleaseAs {
return 'patch'
}

const componentArg = process.argv[2]
const before = process.argv[3] ?? ''
const after = process.argv[4] ?? 'HEAD'

if (componentArg !== 'capgo' && componentArg !== 'cli') {
console.error('Usage: bun scripts/release-scope.ts <capgo|cli> [before] [after]')
process.exit(1)
}
export function resolveReleaseScope(component: Component, before: string, after: string) {
const commits = getCommitShas(before, after)

const component = componentArg as Component
const commits = getCommitShas(before, after)
let shouldRelease = false
let highestSeverity = 0

let shouldRelease = false
let highestSeverity = 0
for (const commit of commits) {
const files = getChangedFiles(commit)
if (!matchesComponent(component, files)) {
continue
}

for (const commit of commits) {
const files = getChangedFiles(commit)
if (!matchesComponent(component, files)) {
continue
shouldRelease = true
const message = getCommitMessage(commit)
highestSeverity = Math.max(highestSeverity, getSeverity(message.subject, message.body))
}

shouldRelease = true
const message = getCommitMessage(commit)
highestSeverity = Math.max(highestSeverity, getSeverity(message.subject, message.body))
return {
shouldRelease,
releaseAs: shouldRelease ? toReleaseAs(highestSeverity) : 'patch',
}
}

console.log(`should_release=${shouldRelease}`)
console.log(`release_as=${shouldRelease ? toReleaseAs(highestSeverity) : 'patch'}`)
if (import.meta.main) {
const componentArg = process.argv[2]
const before = process.argv[3] ?? ''
const after = process.argv[4] ?? 'HEAD'

if (componentArg !== 'capgo' && componentArg !== 'cli') {
console.error('Usage: bun scripts/release-scope.ts <capgo|cli> [before] [after]')
process.exit(1)
}

const scope = resolveReleaseScope(componentArg, before, after)

console.log(`should_release=${scope.shouldRelease}`)
console.log(`release_as=${scope.releaseAs}`)
}
45 changes: 45 additions & 0 deletions tests/release-scope.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { describe, expect, it } from 'vitest'
import { matchesComponent } from '../scripts/release-scope.ts'

describe('release scope matching', () => {
it.concurrent('treats shared release infrastructure as affecting both components', () => {
const files = [
'.github/workflows/tests.yml',
'.github/workflows/bump_version.yml',
'.github/scripts/start-background-service.sh',
'scripts/setup-bun.sh',
'scripts/release-scope.ts',
]

expect(matchesComponent('capgo', files)).toBe(true)
expect(matchesComponent('cli', files)).toBe(true)
})

it.concurrent('treats capgo deploy workflow changes as capgo-only releases', () => {
const files = ['.github/workflows/build_and_deploy.yml']

expect(matchesComponent('capgo', files)).toBe(true)
expect(matchesComponent('cli', files)).toBe(false)
})

it.concurrent('treats cli publish workflow changes as cli-only releases', () => {
const files = ['.github/workflows/publish_cli.yml']

expect(matchesComponent('capgo', files)).toBe(false)
expect(matchesComponent('cli', files)).toBe(true)
})

it.concurrent('keeps runtime code scoped to the matching component', () => {
expect(matchesComponent('capgo', ['src/pages/index.vue'])).toBe(true)
expect(matchesComponent('cli', ['src/pages/index.vue'])).toBe(false)
expect(matchesComponent('capgo', ['cli/src/index.ts'])).toBe(false)
expect(matchesComponent('cli', ['cli/src/index.ts'])).toBe(true)
})

it.concurrent('does not release on unrelated changes', () => {
const files = ['README.md']

expect(matchesComponent('capgo', files)).toBe(false)
expect(matchesComponent('cli', files)).toBe(false)
})
})
Loading