diff --git a/.github/pr-title-breaking-change-label-config.json b/.github/pr-title-breaking-change-label-config.json deleted file mode 100644 index c1178adbb43d..000000000000 --- a/.github/pr-title-breaking-change-label-config.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "LABEL": { - "name": "breaking change", - "color": "D93F0B" - }, - "CHECKS": { - "regexp": "^(?:(?!!:).)*$", - "ignoreLabels": [ - "ignore-title" - ], - "alwaysPassCI": true - } -} diff --git a/.github/pr-title-checker-config.json b/.github/pr-title-checker-config.json deleted file mode 100644 index e2276e3c1cc4..000000000000 --- a/.github/pr-title-checker-config.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "LABEL": { - "name": "Invalid PR Title", - "color": "B60205" - }, - "CHECKS": { - "regexp": "^(feat|fix|test|refactor|chore|style|docs|perf|build|ci|revert)(\\(.*\\))?\\!?:.*", - "ignoreLabels": [ - "ignore-title" - ] - } -} diff --git a/.github/workflows/pr-title-checker.yml b/.github/workflows/pr-title-checker.yml deleted file mode 100644 index 50723f9137f5..000000000000 --- a/.github/workflows/pr-title-checker.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: "PR Title Checker" -on: - pull_request_target: - types: - - opened - - edited - - synchronize - - labeled - - unlabeled - -jobs: - check: - runs-on: ubuntu-20.04 - timeout-minutes: 10 - steps: - - uses: thehanimo/pr-title-checker@v1.4.2 - with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - pass_on_octokit_error: false - configuration_path: ".github/pr-title-checker-config.json" - breaking: - runs-on: ubuntu-20.04 - timeout-minutes: 10 - steps: - - uses: thehanimo/pr-title-checker@v1.4.2 - with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - pass_on_octokit_error: false - configuration_path: ".github/pr-title-breaking-change-label-config.json" diff --git a/.github/workflows/schedule.yml b/.github/workflows/schedule.yml index 3e6886735a60..b8b2dfe418e2 100644 --- a/.github/workflows/schedule.yml +++ b/.github/workflows/schedule.yml @@ -13,6 +13,7 @@ jobs: maintenance: name: Periodic Maintenance runs-on: ubuntu-latest + if: ${{ github.repository == 'GreptimeTeam/greptimedb' }} steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 diff --git a/.github/workflows/semantic-pull-request.yml b/.github/workflows/semantic-pull-request.yml new file mode 100644 index 000000000000..f67fb1d4072a --- /dev/null +++ b/.github/workflows/semantic-pull-request.yml @@ -0,0 +1,30 @@ +name: "Semantic Pull Request" + +on: + pull_request_target: + types: + - opened + - reopened + - edited + +jobs: + check: + runs-on: ubuntu-20.04 + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 22 + - uses: pnpm/action-setup@v3 + with: + package_json_file: 'cyborg/package.json' + run_install: true + - name: Describe the Environment + working-directory: cyborg + run: pnpm tsx -v + - name: Check Pull Request + working-directory: cyborg + run: pnpm tsx bin/check-pull-request.ts + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/cyborg/bin/check-pull-request.ts b/cyborg/bin/check-pull-request.ts new file mode 100644 index 000000000000..b7ab79d7a7f0 --- /dev/null +++ b/cyborg/bin/check-pull-request.ts @@ -0,0 +1,79 @@ +/* + * Copyright 2023 Greptime Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as core from '@actions/core' +import {handleError, obtainClient} from "@/common"; +import {context} from "@actions/github"; +import {PullRequestEvent} from "@octokit/webhooks-types"; +import {Options, sync as conventionalCommitsParser} from 'conventional-commits-parser'; +import conventionalCommitTypes from 'conventional-commit-types'; +import _ from "lodash"; + +const defaultTypes = Object.keys(conventionalCommitTypes.types) +const breakingChangeLabel = "breaking-change" + +// These options are copied from [1]. +// [1] https://github.com/conventional-changelog/conventional-changelog/blob/3f60b464/packages/conventional-changelog-conventionalcommits/src/parser.js +export const parserOpts: Options = { + headerPattern: /^(\w*)(?:\((.*)\))?!?: (.*)$/, + breakingHeaderPattern: /^(\w*)(?:\((.*)\))?!: (.*)$/, + headerCorrespondence: [ + 'type', + 'scope', + 'subject' + ], + noteKeywords: ['BREAKING CHANGE', 'BREAKING-CHANGE'], + revertPattern: /^(?:Revert|revert:)\s"?([\s\S]+?)"?\s*This reverts commit (\w*)\./i, + revertCorrespondence: ['header', 'hash'], + issuePrefixes: ['#'] +} + +async function main() { + if (!context.payload.pull_request) { + throw new Error(`Only pull request event supported. ${context.eventName} is unsupported.`) + } + + const client = obtainClient("GITHUB_TOKEN") + const payload = context.payload as PullRequestEvent + const { owner, repo, number } = { + owner: payload.pull_request.base.user.login, + repo: payload.pull_request.base.repo.name, + number: payload.pull_request.number, + } + const { data: pull_request } = await client.rest.pulls.get({ + owner, repo, pull_number: number, + }) + + const commit = conventionalCommitsParser(pull_request.title, parserOpts) + core.info(`Receive commit: ${JSON.stringify(commit)}`) + + if (!commit.type) { + throw Error(`Malformed commit: ${JSON.stringify(commit)}`) + } + + if (!defaultTypes.includes(commit.type)) { + throw Error(`Unexpected type ${JSON.stringify(commit.type)} of commit: ${JSON.stringify(commit)}`) + } + + const breakingChanges = _.filter(commit.notes, _.matches({ title: 'BREAKING CHANGE'})) + if (breakingChanges.length > 0) { + await client.rest.issues.addLabels({ + owner, repo, issue_number: number, labels: [breakingChangeLabel] + }) + } +} + +main().catch(handleError) diff --git a/cyborg/bin/schedule.ts b/cyborg/bin/schedule.ts index 44151dae4a5a..3287956f750d 100644 --- a/cyborg/bin/schedule.ts +++ b/cyborg/bin/schedule.ts @@ -1,11 +1,27 @@ +/* + * Copyright 2023 Greptime Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import * as core from '@actions/core' import {GitHub} from "@actions/github/lib/utils" import _ from "lodash"; import dayjs from "dayjs"; -import {handleError, obtainClient} from "@/."; +import {handleError, obtainClient} from "@/common"; async function main() { - const client = obtainClient() + const client = obtainClient("GITHUB_TOKEN") await unassign(client) } diff --git a/cyborg/package.json b/cyborg/package.json index 7654f49903f1..d340a41d3573 100644 --- a/cyborg/package.json +++ b/cyborg/package.json @@ -8,11 +8,14 @@ "@actions/core": "^1.10.1", "@actions/github": "^6.0.0", "@octokit/webhooks-types": "^7.5.1", + "conventional-commit-types": "^3.0.0", + "conventional-commits-parser": "^5.0.0", "dayjs": "^1.11.11", "dotenv": "^16.4.5", "lodash": "^4.17.21" }, "devDependencies": { + "@types/conventional-commits-parser": "^5.0.0", "@types/lodash": "^4.17.0", "@types/node": "^20.12.7", "tsconfig-paths": "^4.2.0", diff --git a/cyborg/pnpm-lock.yaml b/cyborg/pnpm-lock.yaml index d04d0d1f345e..651a3bcbd3d8 100644 --- a/cyborg/pnpm-lock.yaml +++ b/cyborg/pnpm-lock.yaml @@ -14,6 +14,12 @@ dependencies: '@octokit/webhooks-types': specifier: ^7.5.1 version: 7.5.1 + conventional-commit-types: + specifier: ^3.0.0 + version: 3.0.0 + conventional-commits-parser: + specifier: ^5.0.0 + version: 5.0.0 dayjs: specifier: ^1.11.11 version: 1.11.11 @@ -25,6 +31,9 @@ dependencies: version: 4.17.21 devDependencies: + '@types/conventional-commits-parser': + specifier: ^5.0.0 + version: 5.0.0 '@types/lodash': specifier: ^4.17.0 version: 4.17.0 @@ -376,6 +385,12 @@ packages: resolution: {integrity: sha512-1dozxWEP8lKGbtEu7HkRbK1F/nIPuJXNfT0gd96y6d3LcHZTtRtlf8xz3nicSJfesADxJyDh+mWBOsdLkqgzYw==} dev: false + /@types/conventional-commits-parser@5.0.0: + resolution: {integrity: sha512-loB369iXNmAZglwWATL+WRe+CRMmmBPtpolYzIebFaX4YA3x+BEfLqhUAV9WanycKI3TG1IMr5bMJDajDKLlUQ==} + dependencies: + '@types/node': 20.12.7 + dev: true + /@types/lodash@4.17.0: resolution: {integrity: sha512-t7dhREVv6dbNj0q17X12j7yDG4bD/DHYX7o5/DbDxobP0HnGPgpRz2Ej77aL7TZT3DSw13fqUTj8J4mMnqa7WA==} dev: true @@ -386,10 +401,33 @@ packages: undici-types: 5.26.5 dev: true + /JSONStream@1.3.5: + resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} + hasBin: true + dependencies: + jsonparse: 1.3.1 + through: 2.3.8 + dev: false + /before-after-hook@2.2.3: resolution: {integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==} dev: false + /conventional-commit-types@3.0.0: + resolution: {integrity: sha512-SmmCYnOniSsAa9GqWOeLqc179lfr5TRu5b4QFDkbsrJ5TZjPJx85wtOr3zn+1dbeNiXDKGPbZ72IKbPhLXh/Lg==} + dev: false + + /conventional-commits-parser@5.0.0: + resolution: {integrity: sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==} + engines: {node: '>=16'} + hasBin: true + dependencies: + JSONStream: 1.3.5 + is-text-path: 2.0.0 + meow: 12.1.1 + split2: 4.2.0 + dev: false + /dayjs@1.11.11: resolution: {integrity: sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==} dev: false @@ -448,16 +486,33 @@ packages: resolve-pkg-maps: 1.0.0 dev: true + /is-text-path@2.0.0: + resolution: {integrity: sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==} + engines: {node: '>=8'} + dependencies: + text-extensions: 2.4.0 + dev: false + /json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} hasBin: true dev: true + /jsonparse@1.3.1: + resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} + engines: {'0': node >= 0.2.0} + dev: false + /lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} dev: false + /meow@12.1.1: + resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} + engines: {node: '>=16.10'} + dev: false + /minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} dev: true @@ -472,11 +527,25 @@ packages: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} dev: true + /split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + dev: false + /strip-bom@3.0.0: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} dev: true + /text-extensions@2.4.0: + resolution: {integrity: sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==} + engines: {node: '>=8'} + dev: false + + /through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + dev: false + /tsconfig-paths@4.2.0: resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} engines: {node: '>=6'} diff --git a/cyborg/src/common.ts b/cyborg/src/common.ts new file mode 100644 index 000000000000..ec605920d8e7 --- /dev/null +++ b/cyborg/src/common.ts @@ -0,0 +1,30 @@ +/* + * Copyright 2023 Greptime Team + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as core from "@actions/core"; +import {config} from "dotenv"; +import {getOctokit} from "@actions/github"; +import {GitHub} from "@actions/github/lib/utils"; + +export function handleError(err: any): void { + console.error(err) + core.setFailed(`Unhandled error: ${err}`) +} + +export function obtainClient(token: string): InstanceType { + config() + return getOctokit(process.env[token]) +} diff --git a/cyborg/src/index.ts b/cyborg/src/index.ts deleted file mode 100644 index 6b481f97c063..000000000000 --- a/cyborg/src/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -import * as core from "@actions/core"; -import {config} from "dotenv"; -import {getOctokit} from "@actions/github"; -import {GitHub} from "@actions/github/lib/utils"; - -export function handleError(err: any): void { - core.error(err) - core.setFailed(`Unhandled error: ${err}`) -} - -export function obtainClient(): InstanceType { - config() - return getOctokit(process.env["GITHUB_TOKEN"]) -} diff --git a/cyborg/tsconfig.json b/cyborg/tsconfig.json index 94d44e75c44b..4d531e4dae6f 100644 --- a/cyborg/tsconfig.json +++ b/cyborg/tsconfig.json @@ -8,6 +8,7 @@ "target": "ES6", "paths": { "@/*": ["./src/*"] - } + }, + "resolveJsonModule": true, } } diff --git a/licenserc.toml b/licenserc.toml index 4ecb0de7d446..f18ed6b2eac2 100644 --- a/licenserc.toml +++ b/licenserc.toml @@ -17,6 +17,7 @@ headerPath = "Apache-2.0.txt" includes = [ "*.rs", "*.py", + "*.ts", ] excludes = [