From ba9d16434ace9f8001d2b07105c79f0480448d0a Mon Sep 17 00:00:00 2001 From: xwartz Date: Thu, 19 Jun 2025 12:33:46 +0800 Subject: [PATCH 1/4] chore: update Node.js target version to 18 in tsup.config.ts and enhance pre-release check for required files --- scripts/pre-release-check.ts | 8 +++++++- tsup.config.ts | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/scripts/pre-release-check.ts b/scripts/pre-release-check.ts index 2b9ff0c..580acb6 100644 --- a/scripts/pre-release-check.ts +++ b/scripts/pre-release-check.ts @@ -183,7 +183,13 @@ async function checkPackageContents(): Promise { ] for (const file of requiredFiles) { - if (!packOutput.includes(file)) { + // Check if the file is listed in the npm pack output + // The output format is: "size filename" + const filePattern = new RegExp( + `\\s${file.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$`, + 'm' + ) + if (!filePattern.test(packOutput)) { throw new Error(`Required file missing from package: ${file}`) } } diff --git a/tsup.config.ts b/tsup.config.ts index 39f86f9..337988e 100644 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -1,4 +1,4 @@ -import { defineConfig } from 'tsup'; +import { defineConfig } from 'tsup' export default defineConfig({ entry: ['src/index.ts'], @@ -8,7 +8,7 @@ export default defineConfig({ sourcemap: true, clean: true, minify: false, - target: 'node16', + target: 'node18', outDir: 'dist', platform: 'node', external: ['protobufjs', 'uuid', 'zlib'], From d006b7c0ce3383a675c7ba302ebafce65437287c Mon Sep 17 00:00:00 2001 From: xwartz Date: Thu, 19 Jun 2025 12:42:29 +0800 Subject: [PATCH 2/4] fix: package contents check in pre-release script --- scripts/pre-release-check.ts | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/scripts/pre-release-check.ts b/scripts/pre-release-check.ts index 580acb6..a890dff 100644 --- a/scripts/pre-release-check.ts +++ b/scripts/pre-release-check.ts @@ -171,7 +171,20 @@ async function checkPackageContents(): Promise { try { // Use npm pack to check package contents - const packOutput = execSync('npm pack --dry-run', { encoding: 'utf8' }) + // We need to capture both stdout and stderr since npm notice goes to stderr + const packOutput = execSync('npm pack --dry-run 2>&1', { + encoding: 'utf8', + }) + + // Extract only the npm notice section + const noticeLines = packOutput + .split('\n') + .filter( + line => + line.startsWith('npm notice') && + (line.includes('kB') || line.includes('MB')) + ) + .join('\n') // Check if required files are included const requiredFiles = [ @@ -184,12 +197,15 @@ async function checkPackageContents(): Promise { for (const file of requiredFiles) { // Check if the file is listed in the npm pack output - // The output format is: "size filename" + // The output format is: "npm notice size filename" const filePattern = new RegExp( - `\\s${file.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$`, + `npm notice\\s+[\\d\\.]+\\w+\\s+${file.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$`, 'm' ) - if (!filePattern.test(packOutput)) { + + const found = filePattern.test(noticeLines) + + if (!found) { throw new Error(`Required file missing from package: ${file}`) } } From 0f95c0ea0fdbb8e75f67901799519ea8435e14f3 Mon Sep 17 00:00:00 2001 From: xwartz Date: Thu, 19 Jun 2025 13:00:30 +0800 Subject: [PATCH 3/4] fix: semantic-release GitHub permissions and configuration --- .github/workflows/release.yml | 6 ++ .releaserc.json | 18 ++++ package-lock.json | 149 ++++++++++++++++++++++++++++++++++ package.json | 2 + 4 files changed, 175 insertions(+) create mode 100644 .releaserc.json diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a8896c5..0c4133c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,12 +12,18 @@ jobs: release: runs-on: ubuntu-latest if: ${{ !contains(github.event.head_commit.message, 'skip ci') }} + permissions: + contents: write + issues: write + pull-requests: write + id-token: write steps: - uses: actions/checkout@v4 with: fetch-depth: 0 token: ${{ secrets.GITHUB_TOKEN }} + persist-credentials: true - name: Use Node.js 22 uses: actions/setup-node@v4 diff --git a/.releaserc.json b/.releaserc.json new file mode 100644 index 0000000..d8abe0f --- /dev/null +++ b/.releaserc.json @@ -0,0 +1,18 @@ +{ + "branches": ["main"], + "repositoryUrl": "https://github.com/xwartz/cursor-api.git", + "plugins": [ + "@semantic-release/commit-analyzer", + "@semantic-release/release-notes-generator", + "@semantic-release/changelog", + "@semantic-release/npm", + "@semantic-release/github", + [ + "@semantic-release/git", + { + "assets": ["package.json", "CHANGELOG.md"], + "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" + } + ] + ] +} diff --git a/package-lock.json b/package-lock.json index 7768781..bba87eb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,8 @@ "uuid": "^11.0.3" }, "devDependencies": { + "@semantic-release/changelog": "^6.0.3", + "@semantic-release/git": "^10.0.1", "@types/jest": "^29.5.0", "@types/node": "^22.0.0", "@types/semver": "^7.5.0", @@ -2258,6 +2260,69 @@ "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", "dev": true }, + "node_modules/@semantic-release/changelog": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@semantic-release/changelog/-/changelog-6.0.3.tgz", + "integrity": "sha512-dZuR5qByyfe3Y03TpmCvAxCyTnp7r5XwtHRf/8vD9EAn4ZWbavUX8adMtXYzE86EVh0gyLA7lm5yW4IV30XUag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@semantic-release/error": "^3.0.0", + "aggregate-error": "^3.0.0", + "fs-extra": "^11.0.0", + "lodash": "^4.17.4" + }, + "engines": { + "node": ">=14.17" + }, + "peerDependencies": { + "semantic-release": ">=18.0.0" + } + }, + "node_modules/@semantic-release/changelog/node_modules/@semantic-release/error": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-3.0.0.tgz", + "integrity": "sha512-5hiM4Un+tpl4cKw3lV4UgzJj+SmfNIDCLLw0TepzQxz9ZGV5ixnqkzIVF+3tp0ZHgcMKE+VNGHJjEeyFG2dcSw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@semantic-release/changelog/node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@semantic-release/changelog/node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@semantic-release/changelog/node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/@semantic-release/commit-analyzer": { "version": "13.0.1", "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-13.0.1.tgz", @@ -2289,6 +2354,83 @@ "node": ">=18" } }, + "node_modules/@semantic-release/git": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@semantic-release/git/-/git-10.0.1.tgz", + "integrity": "sha512-eWrx5KguUcU2wUPaO6sfvZI0wPafUKAMNC18aXY4EnNcrZL86dEmpNVnC9uMpGZkmZJ9EfCVJBQx4pV4EMGT1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@semantic-release/error": "^3.0.0", + "aggregate-error": "^3.0.0", + "debug": "^4.0.0", + "dir-glob": "^3.0.0", + "execa": "^5.0.0", + "lodash": "^4.17.4", + "micromatch": "^4.0.0", + "p-reduce": "^2.0.0" + }, + "engines": { + "node": ">=14.17" + }, + "peerDependencies": { + "semantic-release": ">=18.0.0" + } + }, + "node_modules/@semantic-release/git/node_modules/@semantic-release/error": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-3.0.0.tgz", + "integrity": "sha512-5hiM4Un+tpl4cKw3lV4UgzJj+SmfNIDCLLw0TepzQxz9ZGV5ixnqkzIVF+3tp0ZHgcMKE+VNGHJjEeyFG2dcSw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@semantic-release/git/node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@semantic-release/git/node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@semantic-release/git/node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@semantic-release/git/node_modules/p-reduce": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-2.1.0.tgz", + "integrity": "sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/@semantic-release/github": { "version": "11.0.3", "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-11.0.3.tgz", @@ -6645,6 +6787,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash-es": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", diff --git a/package.json b/package.json index 6a15f2c..27ad5ce 100644 --- a/package.json +++ b/package.json @@ -78,6 +78,8 @@ "lint-staged": "^15.2.10", "prettier": "^3.4.0", "semantic-release": "^24.0.0", + "@semantic-release/changelog": "^6.0.3", + "@semantic-release/git": "^10.0.1", "ts-jest": "^29.4.0", "ts-proto": "^2.0.0", "tsup": "^8.3.0", From 7fb9f699e150da32c238a417a26027dd36f1742c Mon Sep 17 00:00:00 2001 From: xwartz Date: Thu, 19 Jun 2025 13:10:34 +0800 Subject: [PATCH 4/4] fix: update GitHub Actions workflow to improve CI/CD process and enhance permissions for semantic-release --- .github/workflows/auto-label.yml | 214 +++++++++++++++++++++++++++++ .github/workflows/setup-labels.yml | 106 ++++++++++++++ 2 files changed, 320 insertions(+) create mode 100644 .github/workflows/auto-label.yml create mode 100644 .github/workflows/setup-labels.yml diff --git a/.github/workflows/auto-label.yml b/.github/workflows/auto-label.yml new file mode 100644 index 0000000..ad2a1e8 --- /dev/null +++ b/.github/workflows/auto-label.yml @@ -0,0 +1,214 @@ +name: Auto Label PR + +on: + pull_request: + types: [opened, edited, synchronize] + pull_request_target: + types: [opened, edited, synchronize] + +permissions: + contents: read + pull-requests: write + issues: write + +jobs: + auto-label: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Auto Label PR + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const { owner, repo, number } = context.issue; + + // Get PR details + const pr = await github.rest.pulls.get({ + owner, + repo, + pull_number: number + }); + + const title = pr.data.title.toLowerCase(); + const body = pr.data.body ? pr.data.body.toLowerCase() : ''; + const branch = pr.data.head.ref.toLowerCase(); + + // Get commits for additional context + const commits = await github.rest.pulls.listCommits({ + owner, + repo, + pull_number: number + }); + + const commitMessages = commits.data + .map(commit => commit.commit.message.toLowerCase()) + .join(' '); + + const allText = `${title} ${body} ${branch} ${commitMessages}`; + + // Define label rules + const labelRules = [ + // Type labels + { + labels: ['๐Ÿ› bug'], + patterns: [/\b(bug|fix|bugfix|hotfix|patch|error|issue)\b/] + }, + { + labels: ['โœจ feature'], + patterns: [/\b(feat|feature|enhance)\b/] + }, + { + labels: ['๐Ÿ“š docs'], + patterns: [/\b(doc|docs|documentation|readme|guide)\b/] + }, + { + labels: ['๐Ÿงน chore'], + patterns: [/\b(chore|cleanup|refactor|style|format)\b/] + }, + { + labels: ['๐Ÿงช test'], + patterns: [/\b(test|testing|spec|jest|coverage)\b/] + }, + { + labels: ['๐Ÿ”ง ci/cd'], + patterns: [/\b(ci|cd|workflow|action|build|deploy|release)\b/] + }, + { + labels: ['โšก performance'], + patterns: [/\b(perf|performance|optimize|speed|fast)\b/] + }, + { + labels: ['๐Ÿ”’ security'], + patterns: [/\b(security|vulnerability|auth|token|credential)\b/] + }, + + // Priority/Impact labels + { + labels: ['๐Ÿšจ breaking change'], + patterns: [/\b(breaking|major|BREAKING CHANGE)\b/] + }, + { + labels: ['๐Ÿ”ฅ critical'], + patterns: [/\b(critical|urgent|hotfix|emergency)\b/] + }, + { + labels: ['๐Ÿ“ฆ dependencies'], + patterns: [/\b(dep|deps|dependency|dependencies|package|npm|yarn)\b/] + }, + + // Component labels + { + labels: ['๐ŸŽฏ api'], + patterns: [/\b(api|endpoint|route|server|backend)\b/] + }, + { + labels: ['๐ŸŽจ ui/ux'], + patterns: [/\b(ui|ux|interface|frontend|design|style)\b/] + }, + { + labels: ['๐Ÿ—„๏ธ database'], + patterns: [/\b(db|database|sql|mongo|redis|migration)\b/] + }, + { + labels: ['๐Ÿ”ง config'], + patterns: [/\b(config|configuration|settings|env|environment)\b/] + }, + + // Size labels based on file changes + { + labels: ['๐Ÿ“ small'], + condition: () => pr.data.additions + pr.data.deletions < 50 + }, + { + labels: ['๐Ÿ“„ medium'], + condition: () => { + const total = pr.data.additions + pr.data.deletions; + return total >= 50 && total < 200; + } + }, + { + labels: ['๐Ÿ“š large'], + condition: () => pr.data.additions + pr.data.deletions >= 200 + } + ]; + + // Collect labels to add + const labelsToAdd = new Set(); + + for (const rule of labelRules) { + let shouldAddLabel = false; + + // Check patterns + if (rule.patterns) { + shouldAddLabel = rule.patterns.some(pattern => pattern.test(allText)); + } + + // Check custom conditions + if (rule.condition) { + shouldAddLabel = rule.condition(); + } + + if (shouldAddLabel) { + rule.labels.forEach(label => labelsToAdd.add(label)); + } + } + + // Special handling for conventional commits + const conventionalPatterns = { + 'feat': ['โœจ feature'], + 'fix': ['๐Ÿ› bug'], + 'docs': ['๐Ÿ“š docs'], + 'style': ['๐Ÿงน chore'], + 'refactor': ['๐Ÿงน chore'], + 'perf': ['โšก performance'], + 'test': ['๐Ÿงช test'], + 'chore': ['๐Ÿงน chore'], + 'ci': ['๐Ÿ”ง ci/cd'], + 'build': ['๐Ÿ”ง ci/cd'] + }; + + for (const [prefix, labels] of Object.entries(conventionalPatterns)) { + const pattern = new RegExp(`\\b${prefix}(\\([^)]+\\))?:\\s`, 'i'); + if (pattern.test(allText)) { + labels.forEach(label => labelsToAdd.add(label)); + } + } + + // Get current labels + const currentLabels = await github.rest.issues.listLabelsOnIssue({ + owner, + repo, + issue_number: number + }); + + const currentLabelNames = new Set(currentLabels.data.map(label => label.name)); + + // Only add labels that don't already exist + const newLabels = Array.from(labelsToAdd).filter(label => !currentLabelNames.has(label)); + + if (newLabels.length > 0) { + console.log(`Adding labels: ${newLabels.join(', ')}`); + + await github.rest.issues.addLabels({ + owner, + repo, + issue_number: number, + labels: newLabels + }); + + // Comment on the PR about auto-labeling + const labelList = newLabels.map(label => `\`${label}\``).join(', '); + await github.rest.issues.createComment({ + owner, + repo, + issue_number: number, + body: `๐Ÿค– **Auto-labeled this PR with:** ${labelList}\n\n_This was done automatically based on the PR title, description, and commits._` + }); + } else { + console.log('No new labels to add'); + } diff --git a/.github/workflows/setup-labels.yml b/.github/workflows/setup-labels.yml new file mode 100644 index 0000000..c669c13 --- /dev/null +++ b/.github/workflows/setup-labels.yml @@ -0,0 +1,106 @@ +name: Setup Repository Labels + +on: + workflow_dispatch: # Manual trigger + push: + branches: + - main + paths: + - '.github/workflows/setup-labels.yml' + +permissions: + issues: write + +jobs: + setup-labels: + runs-on: ubuntu-latest + steps: + - name: Setup Labels + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const { owner, repo } = context.repo; + + // Define all labels with colors + const labels = [ + // Type labels + { name: '๐Ÿ› bug', color: 'd73a4a', description: 'Something isn\'t working' }, + { name: 'โœจ feature', color: 'a2eeef', description: 'New feature or request' }, + { name: '๐Ÿ“š docs', color: '0075ca', description: 'Improvements or additions to documentation' }, + { name: '๐Ÿงน chore', color: 'fef2c0', description: 'Maintenance tasks' }, + { name: '๐Ÿงช test', color: '1d76db', description: 'Testing related' }, + { name: '๐Ÿ”ง ci/cd', color: '0052cc', description: 'CI/CD and build system' }, + { name: 'โšก performance', color: 'fbca04', description: 'Performance improvements' }, + { name: '๐Ÿ”’ security', color: 'b60205', description: 'Security related' }, + + // Priority/Impact labels + { name: '๐Ÿšจ breaking change', color: 'b60205', description: 'Breaking changes' }, + { name: '๐Ÿ”ฅ critical', color: 'b60205', description: 'Critical priority' }, + { name: '๐Ÿ“ฆ dependencies', color: '0366d6', description: 'Dependencies updates' }, + + // Component labels + { name: '๐ŸŽฏ api', color: '7057ff', description: 'API related' }, + { name: '๐ŸŽจ ui/ux', color: 'e99695', description: 'UI/UX related' }, + { name: '๐Ÿ—„๏ธ database', color: '1d76db', description: 'Database related' }, + { name: '๐Ÿ”ง config', color: 'fbca04', description: 'Configuration related' }, + + // Size labels + { name: '๐Ÿ“ small', color: 'c2e0c6', description: 'Small change (< 50 lines)' }, + { name: '๐Ÿ“„ medium', color: 'f9d0c4', description: 'Medium change (50-200 lines)' }, + { name: '๐Ÿ“š large', color: 'f85149', description: 'Large change (> 200 lines)' }, + + // Status labels + { name: '๐Ÿ‘€ needs review', color: 'fbca04', description: 'Needs review' }, + { name: 'โœ… ready to merge', color: '0e8a16', description: 'Ready to merge' }, + { name: '๐Ÿšง work in progress', color: 'ff6b6b', description: 'Work in progress' }, + { name: 'โŒ needs changes', color: 'd93f0b', description: 'Needs changes' }, + { name: '๐Ÿ”„ review requested', color: 'fbca04', description: 'Review requested' }, + + // Special labels + { name: 'good first issue', color: '7057ff', description: 'Good for newcomers' }, + { name: 'help wanted', color: '006b75', description: 'Extra attention is needed' }, + { name: 'question', color: 'd876e3', description: 'Further information is requested' }, + { name: 'wontfix', color: 'ffffff', description: 'This will not be worked on' }, + { name: 'duplicate', color: 'cfd3d7', description: 'This issue or pull request already exists' }, + { name: 'invalid', color: 'e4e669', description: 'This doesn\'t seem right' } + ]; + + // Get existing labels + const existingLabels = await github.rest.issues.listLabelsForRepo({ + owner, + repo + }); + + const existingLabelNames = new Set(existingLabels.data.map(label => label.name)); + + // Create or update labels + for (const label of labels) { + try { + if (existingLabelNames.has(label.name)) { + // Update existing label + await github.rest.issues.updateLabel({ + owner, + repo, + name: label.name, + color: label.color, + description: label.description + }); + console.log(`Updated label: ${label.name}`); + } else { + // Create new label + await github.rest.issues.createLabel({ + owner, + repo, + name: label.name, + color: label.color, + description: label.description + }); + console.log(`Created label: ${label.name}`); + } + } catch (error) { + console.error(`Error processing label ${label.name}:`, error.message); + } + } + + console.log('Label setup completed!');