diff --git a/.github/workflows/add-good-first-issue-labels.yml b/.github/workflows/add-good-first-issue-labels.yml new file mode 100644 index 0000000..2ae3a05 --- /dev/null +++ b/.github/workflows/add-good-first-issue-labels.yml @@ -0,0 +1,71 @@ +# This workflow is centrally managed in https://github.com/asyncapi/.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo + +# Purpose of this workflow is to enable anyone to label issue with 'Good First Issue' and 'area/*' with a single command. +name: Add 'Good First Issue' and 'area/*' labels # if proper comment added + +on: + issue_comment: + types: + - created + +jobs: + add-labels: + if: ${{(!github.event.issue.pull_request && github.event.issue.state != 'closed' && github.actor != 'asyncapi-bot') && (contains(github.event.comment.body, '/good-first-issue') || contains(github.event.comment.body, '/gfi' ))}} + runs-on: ubuntu-latest + steps: + - name: Add label + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GH_TOKEN }} + script: | + const areas = ['javascript', 'typescript', 'java' , 'go', 'docs', 'ci-cd', 'design']; + const words = context.payload.comment.body.trim().split(" "); + const areaIndex = words.findIndex((word)=> word === '/gfi' || word === '/good-first-issue') + 1 + let area = words[areaIndex]; + switch(area){ + case 'ts': + area = 'typescript'; + break; + case 'js': + area = 'javascript'; + break; + case 'markdown': + area = 'docs'; + break; + } + if(!areas.includes(area)){ + const message = `Hey @${context.payload.sender.login}, your message doesn't follow the requirements, you can try \`/help\`.` + + await github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: message + }) + } else { + + // remove area if there is any before adding new labels. + const currentLabels = (await github.rest.issues.listLabelsOnIssue({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + })).data.map(label => label.name); + + const shouldBeRemoved = currentLabels.filter(label => (label.startsWith('area/') && !label.endsWith(area))); + shouldBeRemoved.forEach(label => { + github.rest.issues.deleteLabel({ + owner: context.repo.owner, + repo: context.repo.repo, + name: label, + }); + }); + + // Add new labels. + github.rest.issues.addLabels({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + labels: ['good first issue', `area/${area}`] + }); + } diff --git a/.github/workflows/automerge-for-humans-add-ready-to-merge-or-do-not-merge-label.yml b/.github/workflows/automerge-for-humans-add-ready-to-merge-or-do-not-merge-label.yml new file mode 100644 index 0000000..02d71a7 --- /dev/null +++ b/.github/workflows/automerge-for-humans-add-ready-to-merge-or-do-not-merge-label.yml @@ -0,0 +1,112 @@ +# This workflow is centrally managed in https://github.com/asyncapi/.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo + +# Purpose of this workflow is to enable anyone to label PR with the following labels: +# `ready-to-merge` and `do-not-merge` labels to get stuff merged or blocked from merging +# `autoupdate` to keep a branch up-to-date with the target branch + +name: Label PRs # if proper comment added + +on: + issue_comment: + types: + - created + +jobs: + add-ready-to-merge-label: + if: > + github.event.issue.pull_request && + github.event.issue.state != 'closed' && + github.actor != 'asyncapi-bot' && + ( + contains(github.event.comment.body, '/ready-to-merge') || + contains(github.event.comment.body, '/rtm' ) + ) + + runs-on: ubuntu-latest + steps: + - name: Add ready-to-merge label + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GH_TOKEN }} + script: | + const prDetailsUrl = context.payload.issue.pull_request.url; + const { data: pull } = await github.request(prDetailsUrl); + const { draft: isDraft} = pull; + if(!isDraft) { + console.log('adding ready-to-merge label...'); + github.rest.issues.addLabels({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + labels: ['ready-to-merge'] + }) + } + + const { data: comparison } = + await github.rest.repos.compareCommitsWithBasehead({ + owner: pull.head.repo.owner.login, + repo: pull.head.repo.name, + basehead: `${pull.base.label}...${pull.head.label}`, + }); + if (comparison.behind_by !== 0 && pull.mergeable_state === 'behind') { + console.log(`This branch is behind the target by ${comparison.behind_by} commits`) + console.log('adding out-of-date comment...'); + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `Hello, @${{ github.actor }}! πŸ‘‹πŸΌ + This PR is not up to date with the base branch and can't be merged. + Please update your branch manually with the latest version of the base branch. + PRO-TIP: To request an update from the upstream branch, simply comment \`/u\` or \`/update\` and our bot will handle the update operation promptly. + + The only requirement for this to work is to enable [Allow edits from maintainers](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork) option in your PR. Also the update will not work if your fork is located in an organization, not under your personal profile. + Thanks πŸ˜„` + }) + } + + add-do-not-merge-label: + if: > + github.event.issue.pull_request && + github.event.issue.state != 'closed' && + github.actor != 'asyncapi-bot' && + ( + contains(github.event.comment.body, '/do-not-merge') || + contains(github.event.comment.body, '/dnm' ) + ) + runs-on: ubuntu-latest + steps: + - name: Add do-not-merge label + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GH_TOKEN }} + script: | + github.rest.issues.addLabels({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + labels: ['do-not-merge'] + }) + add-autoupdate-label: + if: > + github.event.issue.pull_request && + github.event.issue.state != 'closed' && + github.actor != 'asyncapi-bot' && + ( + contains(github.event.comment.body, '/autoupdate') || + contains(github.event.comment.body, '/au' ) + ) + runs-on: ubuntu-latest + steps: + - name: Add autoupdate label + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GH_TOKEN }} + script: | + github.rest.issues.addLabels({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + labels: ['autoupdate'] + }) diff --git a/.github/workflows/automerge-for-humans-merging.yml b/.github/workflows/automerge-for-humans-merging.yml new file mode 100644 index 0000000..9ba0be9 --- /dev/null +++ b/.github/workflows/automerge-for-humans-merging.yml @@ -0,0 +1,55 @@ +# This workflow is centrally managed in https://github.com/asyncapi/.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo + +# Purpose of this workflow is to allow people to merge PR without a need of maintainer doing it. If all checks are in place (including maintainers approval) - JUST MERGE IT! +name: Automerge For Humans + +on: + pull_request_target: + types: + - labeled + - unlabeled + - synchronize + - opened + - edited + - ready_for_review + - reopened + - unlocked + +jobs: + automerge-for-humans: + if: github.event.pull_request.draft == false && (github.event.pull_request.user.login != 'asyncapi-bot' || github.event.pull_request.user.login != 'dependabot[bot]' || github.event.pull_request.user.login != 'dependabot-preview[bot]') #it runs only if PR actor is not a bot, at least not a bot that we know + runs-on: ubuntu-latest + steps: + - name: Get list of authors + uses: sergeysova/jq-action@v2 + id: authors + with: + # This cmd does following (line by line): + # 1. CURL querying the list of commits of the current PR via GH API. Why? Because the current event payload does not carry info about the commits. + # 2. Iterates over the previous returned payload, and creates an array with the filtered results (see below) so we can work wit it later. An example of payload can be found in https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#webhook-payload-example-34. + # 3. Grabs the data we need for adding the `Co-authored-by: ...` lines later and puts it into objects to be used later on. + # 4. Filters the results by excluding the current PR sender. We don't need to add it as co-author since is the PR creator and it will become by default the main author. + # 5. Removes repeated authors (authors can have more than one commit in the PR). + # 6. Builds the `Co-authored-by: ...` lines with actual info. + # 7. Transforms the array into plain text. Thanks to this, the actual stdout of this step can be used by the next Workflow step (wich is basically the automerge). + cmd: | + curl -H "Accept: application/vnd.github+json" -H "Authorization: Bearer ${{ secrets.GH_TOKEN }}" "${{github.event.pull_request._links.commits.href}}?per_page=100" | + jq -r '[.[] + | {name: .commit.author.name, email: .commit.author.email, login: .author.login}] + | map(select(.login != "${{github.event.pull_request.user.login}}")) + | unique + | map("Co-authored-by: " + .name + " <" + .email + ">") + | join("\n")' + multiline: true + - name: Automerge PR + uses: pascalgn/automerge-action@22948e0bc22f0aa673800da838595a3e7347e584 #v0.15.6 https://github.com/pascalgn/automerge-action/releases/tag/v0.15.6 + env: + GITHUB_TOKEN: "${{ secrets.GH_TOKEN }}" + MERGE_LABELS: "!do-not-merge,ready-to-merge" + MERGE_METHOD: "squash" + # Using the output of the previous step (`Co-authored-by: ...` lines) as commit description. + # Important to keep 2 empty lines as https://docs.github.com/en/pull-requests/committing-changes-to-your-project/creating-and-editing-commits/creating-a-commit-with-multiple-authors#creating-co-authored-commits-on-the-command-line mentions + MERGE_COMMIT_MESSAGE: "{pullRequest.title} (#{pullRequest.number})\n\n\n${{ steps.authors.outputs.value }}" + MERGE_RETRIES: "20" + MERGE_RETRY_SLEEP: "30000" diff --git a/.github/workflows/automerge-for-humans-remove-ready-to-merge-label-on-edit.yml b/.github/workflows/automerge-for-humans-remove-ready-to-merge-label-on-edit.yml new file mode 100644 index 0000000..00e7f99 --- /dev/null +++ b/.github/workflows/automerge-for-humans-remove-ready-to-merge-label-on-edit.yml @@ -0,0 +1,32 @@ +# This workflow is centrally managed in https://github.com/asyncapi/.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo + +# Defence from evil contributor that after adding `ready-to-merge` all suddenly makes evil commit or evil change in PR title +# Label is removed once above action is detected +name: Remove ready-to-merge label + +on: + pull_request_target: + types: + - synchronize + - edited + +jobs: + remove-ready-label: + runs-on: ubuntu-latest + steps: + - name: Remove label + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GH_TOKEN }} + script: | + const labelToRemove = 'ready-to-merge'; + const labels = context.payload.pull_request.labels; + const isLabelPresent = labels.some(label => label.name === labelToRemove) + if(!isLabelPresent) return; + github.rest.issues.removeLabel({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + name: labelToRemove + }) diff --git a/.github/workflows/automerge-orphans.yml b/.github/workflows/automerge-orphans.yml new file mode 100644 index 0000000..a177685 --- /dev/null +++ b/.github/workflows/automerge-orphans.yml @@ -0,0 +1,66 @@ +# This action is centrally managed in https://github.com/asyncapi/.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo + +name: 'Notify on failing automerge' + +on: + schedule: + - cron: "0 0 * * *" + +jobs: + identify-orphans: + if: startsWith(github.repository, 'asyncapi/') + name: Find orphans and notify + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Get list of orphans + uses: actions/github-script@v6 + id: orphans + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const query = `query($owner:String!, $name:String!) { + repository(owner:$owner, name:$name){ + pullRequests(first: 100, states: OPEN){ + nodes{ + title + url + author { + resourcePath + } + } + } + } + }`; + const variables = { + owner: context.repo.owner, + name: context.repo.repo + }; + const { repository: { pullRequests: { nodes } } } = await github.graphql(query, variables); + + let orphans = nodes.filter( (pr) => pr.author.resourcePath === '/asyncapi-bot' || pr.author.resourcePath === '/apps/dependabot') + + if (orphans.length) { + core.setOutput('found', 'true'); + //Yes, this is very naive approach to assume there is just one PR causing issues, there can be a case that more PRs are affected the same day + //The thing is that handling multiple PRs will increase a complexity in this PR that in my opinion we should avoid + //The other PRs will be reported the next day the action runs, or person that checks first url will notice the other ones + core.setOutput('url', orphans[0].url); + core.setOutput('title', orphans[0].title); + } + - if: steps.orphans.outputs.found == 'true' + name: Convert markdown to slack markdown + uses: asyncapi/.github/.github/actions/slackify-markdown@master + id: issuemarkdown + with: + markdown: "-> [${{steps.orphans.outputs.title}}](${{steps.orphans.outputs.url}})" + - if: steps.orphans.outputs.found == 'true' + name: Send info about orphan to slack + uses: rtCamp/action-slack-notify@v2 + env: + SLACK_WEBHOOK: ${{secrets.SLACK_CI_FAIL_NOTIFY}} + SLACK_TITLE: 🚨 Not merged PR that should be automerged 🚨 + SLACK_MESSAGE: ${{steps.issuemarkdown.outputs.text}} + MSG_MINIMAL: true \ No newline at end of file diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml new file mode 100644 index 0000000..116b806 --- /dev/null +++ b/.github/workflows/automerge.yml @@ -0,0 +1,52 @@ +# This action is centrally managed in https://github.com/asyncapi/.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo. + +name: Automerge PRs from bots + +on: + pull_request_target: + types: + - opened + - synchronize + +jobs: + autoapprove-for-bot: + name: Autoapprove PR comming from a bot + if: > + contains(fromJson('["asyncapi-bot", "dependabot[bot]", "dependabot-preview[bot]"]'), github.event.pull_request.user.login) && + contains(fromJson('["asyncapi-bot", "dependabot[bot]", "dependabot-preview[bot]"]'), github.actor) && + !contains(github.event.pull_request.labels.*.name, 'released') + runs-on: ubuntu-latest + steps: + - name: Autoapproving + uses: hmarr/auto-approve-action@44888193675f29a83e04faf4002fa8c0b537b1e4 # v3.2.1 is used https://github.com/hmarr/auto-approve-action/releases/tag/v3.2.1 + with: + github-token: "${{ secrets.GH_TOKEN_BOT_EVE }}" + + - name: Label autoapproved + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GH_TOKEN }} + script: | + github.rest.issues.addLabels({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + labels: ['autoapproved', 'autoupdate'] + }) + + automerge-for-bot: + name: Automerge PR autoapproved by a bot + needs: [autoapprove-for-bot] + runs-on: ubuntu-latest + steps: + - name: Automerging + uses: pascalgn/automerge-action@22948e0bc22f0aa673800da838595a3e7347e584 #v0.15.6 https://github.com/pascalgn/automerge-action/releases/tag/v0.15.6 + env: + GITHUB_TOKEN: "${{ secrets.GH_TOKEN }}" + GITHUB_LOGIN: asyncapi-bot + MERGE_LABELS: "!do-not-merge" + MERGE_METHOD: "squash" + MERGE_COMMIT_MESSAGE: "{pullRequest.title} (#{pullRequest.number})" + MERGE_RETRIES: "20" + MERGE_RETRY_SLEEP: "30000" diff --git a/.github/workflows/autoupdate.yml b/.github/workflows/autoupdate.yml new file mode 100644 index 0000000..eeb77a4 --- /dev/null +++ b/.github/workflows/autoupdate.yml @@ -0,0 +1,34 @@ +# This action is centrally managed in https://github.com/asyncapi/.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo + +# This workflow is designed to work with: +# - autoapprove and automerge workflows for dependabot and asyncapibot. +# - special release branches that we from time to time create in upstream repos. If we open up PRs for them from the very beginning of the release, the release branch will constantly update with new things from the destination branch they are opened against + +# It uses GitHub Action that auto-updates pull requests branches, whenever changes are pushed to their destination branch. +# Autoupdating to latest destination branch works only in the context of upstream repo and not forks + +name: autoupdate + +on: + push: + branches-ignore: + - 'version-bump/**' + - 'dependabot/**' + - 'bot/**' + - 'all-contributors/**' + +jobs: + autoupdate-for-bot: + if: startsWith(github.repository, 'asyncapi/') + name: Autoupdate autoapproved PR created in the upstream + runs-on: ubuntu-latest + steps: + - name: Autoupdating + uses: docker://chinthakagodawita/autoupdate-action:v1 + env: + GITHUB_TOKEN: '${{ secrets.GH_TOKEN_BOT_EVE }}' + PR_FILTER: "labelled" + PR_LABELS: "autoupdate" + PR_READY_STATE: "ready_for_review" + MERGE_CONFLICT_ACTION: "ignore" diff --git a/.github/workflows/bounty-program-commands.yml b/.github/workflows/bounty-program-commands.yml new file mode 100644 index 0000000..645e0c9 --- /dev/null +++ b/.github/workflows/bounty-program-commands.yml @@ -0,0 +1,126 @@ +# This workflow is centrally managed at https://github.com/asyncapi/.github/ +# Don't make changes to this file in this repository, as they will be overwritten with +# changes made to the same file in the abovementioned repository. + +# The purpose of this workflow is to allow Bounty Team members +# (https://github.com/orgs/asyncapi/teams/bounty_team) to issue commands to the +# organization's global AsyncAPI bot related to the Bounty Program, while at the +# same time preventing unauthorized users from misusing them. + +name: Bounty Program commands + +on: + issue_comment: + types: + - created + +env: + BOUNTY_PROGRAM_LABELS_JSON: | + [ + {"name": "bounty", "color": "0e8a16", "description": "Participation in the Bounty Program"} + ] + +jobs: + guard-against-unauthorized-use: + if: > + github.actor != ('aeworxet' || 'thulieblack') && + ( + startsWith(github.event.comment.body, '/bounty' ) + ) + + runs-on: ubuntu-latest + + steps: + - name: ❌ @${{github.actor}} made an unauthorized attempt to use a Bounty Program's command + uses: actions/github-script@v6 + + with: + github-token: ${{ secrets.GH_TOKEN }} + script: | + const commentText = `❌ @${{github.actor}} is not authorized to use the Bounty Program's commands. + These commands can only be used by members of the [Bounty Team](https://github.com/orgs/asyncapi/teams/bounty_team).`; + + console.log(`❌ @${{github.actor}} made an unauthorized attempt to use a Bounty Program's command.`); + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: commentText + }) + + add-label-bounty: + if: > + github.actor == ('aeworxet' || 'thulieblack') && + ( + startsWith(github.event.comment.body, '/bounty' ) + ) + + runs-on: ubuntu-latest + + steps: + - name: Add label `bounty` + uses: actions/github-script@v6 + + with: + github-token: ${{ secrets.GH_TOKEN }} + script: | + const BOUNTY_PROGRAM_LABELS = JSON.parse(process.env.BOUNTY_PROGRAM_LABELS_JSON); + let LIST_OF_LABELS_FOR_REPO = await github.rest.issues.listLabelsForRepo({ + owner: context.repo.owner, + repo: context.repo.repo, + }); + + LIST_OF_LABELS_FOR_REPO = LIST_OF_LABELS_FOR_REPO.data.map(key => key.name); + + if (!LIST_OF_LABELS_FOR_REPO.includes(BOUNTY_PROGRAM_LABELS[0].name)) { + await github.rest.issues.createLabel({ + owner: context.repo.owner, + repo: context.repo.repo, + name: BOUNTY_PROGRAM_LABELS[0].name, + color: BOUNTY_PROGRAM_LABELS[0].color, + description: BOUNTY_PROGRAM_LABELS[0].description + }); + } + + console.log('Adding label `bounty`...'); + github.rest.issues.addLabels({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + labels: [BOUNTY_PROGRAM_LABELS[0].name] + }) + + remove-label-bounty: + if: > + github.actor == ('aeworxet' || 'thulieblack') && + ( + startsWith(github.event.comment.body, '/unbounty' ) + ) + + runs-on: ubuntu-latest + + steps: + - name: Remove label `bounty` + uses: actions/github-script@v6 + + with: + github-token: ${{ secrets.GH_TOKEN }} + script: | + const BOUNTY_PROGRAM_LABELS = JSON.parse(process.env.BOUNTY_PROGRAM_LABELS_JSON); + let LIST_OF_LABELS_FOR_ISSUE = await github.rest.issues.listLabelsOnIssue({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + }); + + LIST_OF_LABELS_FOR_ISSUE = LIST_OF_LABELS_FOR_ISSUE.data.map(key => key.name); + + if (LIST_OF_LABELS_FOR_ISSUE.includes(BOUNTY_PROGRAM_LABELS[0].name)) { + console.log('Removing label `bounty`...'); + github.rest.issues.removeLabel({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + name: [BOUNTY_PROGRAM_LABELS[0].name] + }) + } diff --git a/.github/workflows/help-command.yml b/.github/workflows/help-command.yml new file mode 100644 index 0000000..3f4dcbc --- /dev/null +++ b/.github/workflows/help-command.yml @@ -0,0 +1,62 @@ +# This workflow is centrally managed in https://github.com/asyncapi/.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo + +name: Create help comment + +on: + issue_comment: + types: + - created + +jobs: + create_help_comment_pr: + if: ${{ github.event.issue.pull_request && contains(github.event.comment.body, '/help') && github.actor != 'asyncapi-bot' }} + runs-on: ubuntu-latest + steps: + - name: Add comment to PR + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GH_TOKEN }} + script: | + //Yes to add comment to PR the same endpoint is use that we use to create a comment in issue + //For more details http://developer.github.com/v3/issues/comments/ + //Also proved by this action https://github.com/actions-ecosystem/action-create-comment/blob/main/src/main.ts + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `Hello, @${{ github.actor }}! πŸ‘‹πŸΌ + + I'm 🧞🧞🧞 Genie 🧞🧞🧞 from the magic lamp. Looks like somebody needs a hand! + + At the moment the following comments are supported in pull requests: + + - \`/please-take-a-look\` or \`/ptal\` - This comment will add a comment to the PR asking for attention from the reviewrs who have not reviewed the PR yet. + - \`/ready-to-merge\` or \`/rtm\` - This comment will trigger automerge of PR in case all required checks are green, approvals in place and do-not-merge label is not added + - \`/do-not-merge\` or \`/dnm\` - This comment will block automerging even if all conditions are met and ready-to-merge label is added + - \`/autoupdate\` or \`/au\` - This comment will add \`autoupdate\` label to the PR and keeps your PR up-to-date to the target branch's future changes. Unless there is a merge conflict or it is a draft PR. (Currently only works for upstream branches.) + - \`/update\` or \`/u\` - This comment will update the PR with the latest changes from the target branch. Unless there is a merge conflict or it is a draft PR. NOTE: this only updates the PR once, so if you need to update again, you need to call the command again.` + }) + + create_help_comment_issue: + if: ${{ !github.event.issue.pull_request && contains(github.event.comment.body, '/help') && github.actor != 'asyncapi-bot' }} + runs-on: ubuntu-latest + steps: + - name: Add comment to Issue + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GH_TOKEN }} + script: | + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `Hello, @${{ github.actor }}! πŸ‘‹πŸΌ + + I'm 🧞🧞🧞 Genie 🧞🧞🧞 from the magic lamp. Looks like somebody needs a hand! + + At the moment the following comments are supported in issues: + + - \`/good-first-issue {js | ts | java | go | docs | design | ci-cd}\` or \`/gfi {js | ts | java | go | docs | design | ci-cd}\` - label an issue as a \`good first issue\`. + example: \`/gfi js\` or \`/good-first-issue ci-cd\`` + }) \ No newline at end of file diff --git a/.github/workflows/issues-prs-notifications.yml b/.github/workflows/issues-prs-notifications.yml new file mode 100644 index 0000000..b8b20c6 --- /dev/null +++ b/.github/workflows/issues-prs-notifications.yml @@ -0,0 +1,70 @@ +# This action is centrally managed in https://github.com/asyncapi/.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo + +# This action notifies community on slack whenever there is a new issue, PR or discussion started in given repository +name: Notify slack + +on: + issues: + types: [opened, reopened] + + pull_request_target: + types: [opened, reopened, ready_for_review] + + discussion: + types: [created] + +jobs: + issue: + if: github.event_name == 'issues' && github.actor != 'asyncapi-bot' && github.actor != 'dependabot[bot]' && github.actor != 'dependabot-preview[bot]' + name: Notify slack on every new issue + runs-on: ubuntu-latest + steps: + - name: Convert markdown to slack markdown for issue + uses: asyncapi/.github/.github/actions/slackify-markdown@master + id: issuemarkdown + with: + markdown: "[${{github.event.issue.title}}](${{github.event.issue.html_url}}) \n ${{github.event.issue.body}}" + - name: Send info about issue + uses: rtCamp/action-slack-notify@v2 + env: + SLACK_WEBHOOK: ${{secrets.SLACK_GITHUB_NEWISSUEPR}} + SLACK_TITLE: πŸ› New Issue in ${{github.repository}} πŸ› + SLACK_MESSAGE: ${{steps.issuemarkdown.outputs.text}} + MSG_MINIMAL: true + + pull_request: + if: github.event_name == 'pull_request_target' && github.actor != 'asyncapi-bot' && github.actor != 'dependabot[bot]' && github.actor != 'dependabot-preview[bot]' + name: Notify slack on every new pull request + runs-on: ubuntu-latest + steps: + - name: Convert markdown to slack markdown for pull request + uses: asyncapi/.github/.github/actions/slackify-markdown@master + id: prmarkdown + with: + markdown: "[${{github.event.pull_request.title}}](${{github.event.pull_request.html_url}}) \n ${{github.event.pull_request.body}}" + - name: Send info about pull request + uses: rtCamp/action-slack-notify@v2 + env: + SLACK_WEBHOOK: ${{secrets.SLACK_GITHUB_NEWISSUEPR}} + SLACK_TITLE: πŸ’ͺ New Pull Request in ${{github.repository}} πŸ’ͺ + SLACK_MESSAGE: ${{steps.prmarkdown.outputs.text}} + MSG_MINIMAL: true + + discussion: + if: github.event_name == 'discussion' && github.actor != 'asyncapi-bot' && github.actor != 'dependabot[bot]' && github.actor != 'dependabot-preview[bot]' + name: Notify slack on every new pull request + runs-on: ubuntu-latest + steps: + - name: Convert markdown to slack markdown for pull request + uses: asyncapi/.github/.github/actions/slackify-markdown@master + id: discussionmarkdown + with: + markdown: "[${{github.event.discussion.title}}](${{github.event.discussion.html_url}}) \n ${{github.event.discussion.body}}" + - name: Send info about pull request + uses: rtCamp/action-slack-notify@v2 + env: + SLACK_WEBHOOK: ${{secrets.SLACK_GITHUB_NEWISSUEPR}} + SLACK_TITLE: πŸ’¬ New Discussion in ${{github.repository}} πŸ’¬ + SLACK_MESSAGE: ${{steps.discussionmarkdown.outputs.text}} + MSG_MINIMAL: true diff --git a/.github/workflows/lint-pr-title.yml b/.github/workflows/lint-pr-title.yml new file mode 100644 index 0000000..77aa1c6 --- /dev/null +++ b/.github/workflows/lint-pr-title.yml @@ -0,0 +1,47 @@ +# This action is centrally managed in https://github.com/asyncapi/.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo + +name: Lint PR title + +on: + pull_request_target: + types: [opened, reopened, synchronize, edited, ready_for_review] + +jobs: + lint-pr-title: + name: Lint PR title + runs-on: ubuntu-latest + steps: + # Since this workflow is REQUIRED for a PR to be mergable, we have to have this 'if' statement in step level instead of job level. + - if: ${{ !contains(fromJson('["asyncapi-bot", "dependabot[bot]", "dependabot-preview[bot]", "allcontributors[bot]"]'), github.actor) }} + uses: amannn/action-semantic-pull-request@c3cd5d1ea3580753008872425915e343e351ab54 #version 5.2.0 https://github.com/amannn/action-semantic-pull-request/releases/tag/v5.2.0 + id: lint_pr_title + env: + GITHUB_TOKEN: ${{ secrets.GH_TOKEN}} + with: + subjectPattern: ^(?![A-Z]).+$ + subjectPatternError: | + The subject "{subject}" found in the pull request title "{title}" should start with a lowercase character. + + # Comments the error message from the above lint_pr_title action + - if: ${{ always() && steps.lint_pr_title.outputs.error_message != null && !contains(fromJson('["asyncapi-bot", "dependabot[bot]", "dependabot-preview[bot]", "allcontributors[bot]"]'), github.actor)}} + name: Comment on PR + uses: marocchino/sticky-pull-request-comment@3d60a5b2dae89d44e0c6ddc69dd7536aec2071cd #use 2.5.0 https://github.com/marocchino/sticky-pull-request-comment/releases/tag/v2.5.0 + with: + header: pr-title-lint-error + GITHUB_TOKEN: ${{ secrets.GH_TOKEN}} + message: | + + We require all PRs to follow [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/). + More details πŸ‘‡πŸΌ + ``` + ${{ steps.lint_pr_title.outputs.error_message}} + ``` + # deletes the error comment if the title is correct + - if: ${{ steps.lint_pr_title.outputs.error_message == null }} + name: delete the comment + uses: marocchino/sticky-pull-request-comment@3d60a5b2dae89d44e0c6ddc69dd7536aec2071cd #use 2.5.0 https://github.com/marocchino/sticky-pull-request-comment/releases/tag/v2.5.0 + with: + header: pr-title-lint-error + delete: true + GITHUB_TOKEN: ${{ secrets.GH_TOKEN}} diff --git a/.github/workflows/notify-tsc-members-mention.yml b/.github/workflows/notify-tsc-members-mention.yml new file mode 100644 index 0000000..d72fd85 --- /dev/null +++ b/.github/workflows/notify-tsc-members-mention.yml @@ -0,0 +1,297 @@ +# This action is centrally managed in https://github.com/asyncapi/.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo + +# This action notifies community on slack whenever there is a new issue, PR or discussion started in given repository +name: Notify slack and email subscribers whenever TSC members are mentioned in GitHub + +on: + issue_comment: + types: + - created + + discussion_comment: + types: + - created + + issues: + types: + - opened + + pull_request_target: + types: + - opened + + discussion: + types: + - created + +jobs: + issue: + if: github.event_name == 'issues' && contains(github.event.issue.body, '@asyncapi/tsc_members') + name: TSC notification on every new issue + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: 16 + cache: 'npm' + cache-dependency-path: '**/package-lock.json' + ######### + # Handling Slack notifications + ######### + - name: Convert markdown to slack markdown + uses: asyncapi/.github/.github/actions/slackify-markdown@master + id: issuemarkdown + with: + markdown: "[${{github.event.issue.title}}](${{github.event.issue.html_url}}) \n ${{github.event.issue.body}}" + - name: Send info about issue + uses: rtCamp/action-slack-notify@v2 + env: + SLACK_WEBHOOK: ${{secrets.SLACK_TSC_MEMBERS_NOTIFY}} + SLACK_TITLE: πŸ†˜ New issue that requires TSC Members attention πŸ†˜ + SLACK_MESSAGE: ${{steps.issuemarkdown.outputs.text}} + MSG_MINIMAL: true + ######### + # Handling Mailchimp notifications + ######### + - name: Install deps + run: npm install + working-directory: ./.github/workflows/scripts/mailchimp + - name: Send email with MailChimp + uses: actions/github-script@v6 + env: + CALENDAR_ID: ${{ secrets.CALENDAR_ID }} + CALENDAR_SERVICE_ACCOUNT: ${{ secrets.CALENDAR_SERVICE_ACCOUNT }} + MAILCHIMP_API_KEY: ${{ secrets.MAILCHIMP_API_KEY }} + with: + script: | + const sendEmail = require('./.github/workflows/scripts/mailchimp/index.js'); + sendEmail('${{github.event.issue.html_url}}', '${{github.event.issue.title}}'); + + pull_request: + if: github.event_name == 'pull_request_target' && contains(github.event.pull_request.body, '@asyncapi/tsc_members') + name: TSC notification on every new pull request + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: 16 + cache: 'npm' + cache-dependency-path: '**/package-lock.json' + ######### + # Handling Slack notifications + ######### + - name: Convert markdown to slack markdown + uses: asyncapi/.github/.github/actions/slackify-markdown@master + id: prmarkdown + with: + markdown: "[${{github.event.pull_request.title}}](${{github.event.pull_request.html_url}}) \n ${{github.event.pull_request.body}}" + - name: Send info about pull request + uses: rtCamp/action-slack-notify@v2 + env: + SLACK_WEBHOOK: ${{secrets.SLACK_TSC_MEMBERS_NOTIFY}} + SLACK_TITLE: πŸ†˜ New PR that requires TSC Members attention πŸ†˜ + SLACK_MESSAGE: ${{steps.prmarkdown.outputs.text}} + MSG_MINIMAL: true + ######### + # Handling Mailchimp notifications + ######### + - name: Install deps + run: npm install + working-directory: ./.github/workflows/scripts/mailchimp + - name: Send email with MailChimp + uses: actions/github-script@v6 + env: + CALENDAR_ID: ${{ secrets.CALENDAR_ID }} + CALENDAR_SERVICE_ACCOUNT: ${{ secrets.CALENDAR_SERVICE_ACCOUNT }} + MAILCHIMP_API_KEY: ${{ secrets.MAILCHIMP_API_KEY }} + with: + script: | + const sendEmail = require('./.github/workflows/scripts/mailchimp/index.js'); + sendEmail('${{github.event.pull_request.html_url}}', '${{github.event.pull_request.title}}'); + + discussion: + if: github.event_name == 'discussion' && contains(github.event.discussion.body, '@asyncapi/tsc_members') + name: TSC notification on every new discussion + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: 16 + cache: 'npm' + cache-dependency-path: '**/package-lock.json' + ######### + # Handling Slack notifications + ######### + - name: Convert markdown to slack markdown + uses: asyncapi/.github/.github/actions/slackify-markdown@master + id: discussionmarkdown + with: + markdown: "[${{github.event.discussion.title}}](${{github.event.discussion.html_url}}) \n ${{github.event.discussion.body}}" + - name: Send info about pull request + uses: rtCamp/action-slack-notify@v2 + env: + SLACK_WEBHOOK: ${{secrets.SLACK_TSC_MEMBERS_NOTIFY}} + SLACK_TITLE: πŸ†˜ New discussion that requires TSC Members attention πŸ†˜ + SLACK_MESSAGE: ${{steps.discussionmarkdown.outputs.text}} + MSG_MINIMAL: true + ######### + # Handling Mailchimp notifications + ######### + - name: Install deps + run: npm install + working-directory: ./.github/workflows/scripts/mailchimp + - name: Send email with MailChimp + uses: actions/github-script@v6 + env: + CALENDAR_ID: ${{ secrets.CALENDAR_ID }} + CALENDAR_SERVICE_ACCOUNT: ${{ secrets.CALENDAR_SERVICE_ACCOUNT }} + MAILCHIMP_API_KEY: ${{ secrets.MAILCHIMP_API_KEY }} + with: + script: | + const sendEmail = require('./.github/workflows/scripts/mailchimp/index.js'); + sendEmail('${{github.event.discussion.html_url}}', '${{github.event.discussion.title}}'); + + issue_comment: + if: ${{ github.event_name == 'issue_comment' && !github.event.issue.pull_request && contains(github.event.comment.body, '@asyncapi/tsc_members') }} + name: TSC notification on every new comment in issue + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: 16 + cache: 'npm' + cache-dependency-path: '**/package-lock.json' + ######### + # Handling Slack notifications + ######### + - name: Convert markdown to slack markdown + uses: asyncapi/.github/.github/actions/slackify-markdown@master + id: issuemarkdown + with: + markdown: "[${{github.event.issue.title}}](${{github.event.comment.html_url}}) \n ${{github.event.comment.body}}" + - name: Send info about issue comment + uses: rtCamp/action-slack-notify@v2 + env: + SLACK_WEBHOOK: ${{secrets.SLACK_TSC_MEMBERS_NOTIFY}} + SLACK_TITLE: πŸ†˜ New comment under existing issue that requires TSC Members attention πŸ†˜ + SLACK_MESSAGE: ${{steps.issuemarkdown.outputs.text}} + MSG_MINIMAL: true + ######### + # Handling Mailchimp notifications + ######### + - name: Install deps + run: npm install + working-directory: ./.github/workflows/scripts/mailchimp + - name: Send email with MailChimp + uses: actions/github-script@v6 + env: + CALENDAR_ID: ${{ secrets.CALENDAR_ID }} + CALENDAR_SERVICE_ACCOUNT: ${{ secrets.CALENDAR_SERVICE_ACCOUNT }} + MAILCHIMP_API_KEY: ${{ secrets.MAILCHIMP_API_KEY }} + with: + script: | + const sendEmail = require('./.github/workflows/scripts/mailchimp/index.js'); + sendEmail('${{github.event.comment.html_url}}', '${{github.event.issue.title}}'); + + pr_comment: + if: github.event_name == 'issue_comment' && github.event.issue.pull_request && contains(github.event.comment.body, '@asyncapi/tsc_members') + name: TSC notification on every new comment in pr + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: 16 + cache: 'npm' + cache-dependency-path: '**/package-lock.json' + ######### + # Handling Slack notifications + ######### + - name: Convert markdown to slack markdown + uses: asyncapi/.github/.github/actions/slackify-markdown@master + id: prmarkdown + with: + markdown: "[${{github.event.issue.title}}](${{github.event.comment.html_url}}) \n ${{github.event.comment.body}}" + - name: Send info about PR comment + uses: rtCamp/action-slack-notify@v2 + env: + SLACK_WEBHOOK: ${{secrets.SLACK_TSC_MEMBERS_NOTIFY}} + SLACK_TITLE: πŸ†˜ New comment under existing PR that requires TSC Members attention πŸ†˜ + SLACK_MESSAGE: ${{steps.prmarkdown.outputs.text}} + MSG_MINIMAL: true + ######### + # Handling Mailchimp notifications + ######### + - name: Install deps + run: npm install + working-directory: ./.github/workflows/scripts/mailchimp + - name: Send email with MailChimp + uses: actions/github-script@v6 + env: + CALENDAR_ID: ${{ secrets.CALENDAR_ID }} + CALENDAR_SERVICE_ACCOUNT: ${{ secrets.CALENDAR_SERVICE_ACCOUNT }} + MAILCHIMP_API_KEY: ${{ secrets.MAILCHIMP_API_KEY }} + with: + script: | + const sendEmail = require('./.github/workflows/scripts/mailchimp/index.js'); + sendEmail('${{github.event.comment.html_url}}', '${{github.event.issue.title}}'); + + discussion_comment: + if: github.event_name == 'discussion_comment' && contains(github.event.comment.body, '@asyncapi/tsc_members') + name: TSC notification on every new comment in discussion + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: 16 + cache: 'npm' + cache-dependency-path: '**/package-lock.json' + ######### + # Handling Slack notifications + ######### + - name: Convert markdown to slack markdown + uses: asyncapi/.github/.github/actions/slackify-markdown@master + id: discussionmarkdown + with: + markdown: "[${{github.event.discussion.title}}](${{github.event.comment.html_url}}) \n ${{github.event.comment.body}}" + - name: Send info about discussion comment + uses: rtCamp/action-slack-notify@v2 + env: + SLACK_WEBHOOK: ${{secrets.SLACK_TSC_MEMBERS_NOTIFY}} + SLACK_TITLE: πŸ†˜ New comment under existing discussion that requires TSC Members attention πŸ†˜ + SLACK_MESSAGE: ${{steps.discussionmarkdown.outputs.text}} + MSG_MINIMAL: true + ######### + # Handling Mailchimp notifications + ######### + - name: Install deps + run: npm install + working-directory: ./.github/workflows/scripts/mailchimp + - name: Send email with MailChimp + uses: actions/github-script@v6 + env: + CALENDAR_ID: ${{ secrets.CALENDAR_ID }} + CALENDAR_SERVICE_ACCOUNT: ${{ secrets.CALENDAR_SERVICE_ACCOUNT }} + MAILCHIMP_API_KEY: ${{ secrets.MAILCHIMP_API_KEY }} + with: + script: | + const sendEmail = require('./.github/workflows/scripts/mailchimp/index.js'); + sendEmail('${{github.event.comment.html_url}}', '${{github.event.discussion.title}}'); diff --git a/.github/workflows/please-take-a-look-command.yml b/.github/workflows/please-take-a-look-command.yml new file mode 100644 index 0000000..b26cbc4 --- /dev/null +++ b/.github/workflows/please-take-a-look-command.yml @@ -0,0 +1,54 @@ +# This action is centrally managed in https://github.com/asyncapi/.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo + +# It uses Github actions to listen for comments on issues and pull requests and +# if the comment contains /please-take-a-look or /ptal it will add a comment pinging +# the code-owners who are reviewers for PR + +name: Please take a Look + +on: + issue_comment: + types: [created] + +jobs: + ping-for-attention: + if: > + github.event.issue.pull_request && + github.event.issue.state != 'closed' && + github.actor != 'asyncapi-bot' && + ( + contains(github.event.comment.body, '/please-take-a-look') || + contains(github.event.comment.body, '/ptal') || + contains(github.event.comment.body, '/PTAL') + ) + runs-on: ubuntu-latest + steps: + - name: Check for Please Take a Look Command + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GH_TOKEN }} + script: | + const prDetailsUrl = context.payload.issue.pull_request.url; + const { data: pull } = await github.request(prDetailsUrl); + const reviewers = pull.requested_reviewers.map(reviewer => reviewer.login); + + const { data: reviews } = await github.rest.pulls.listReviews({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.issue.number + }); + + const reviewersWhoHaveReviewed = reviews.map(review => review.user.login); + + const reviewersWhoHaveNotReviewed = reviewers.filter(reviewer => !reviewersWhoHaveReviewed.includes(reviewer)); + + if (reviewersWhoHaveNotReviewed.length > 0) { + const comment = reviewersWhoHaveNotReviewed.filter(reviewer => reviewer !== 'asyncapi-bot-eve' ).map(reviewer => `@${reviewer}`).join(' '); + await github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `${comment} Please take a look at this PR. Thanks! :wave:` + }); + } diff --git a/.github/workflows/release-announcements.yml b/.github/workflows/release-announcements.yml new file mode 100644 index 0000000..9587cac --- /dev/null +++ b/.github/workflows/release-announcements.yml @@ -0,0 +1,79 @@ +# This action is centrally managed in https://github.com/asyncapi/.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo + +name: 'Announce releases in different channels' + +on: + release: + types: + - published + +jobs: + + slack-announce: + name: Slack - notify on every release + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Convert markdown to slack markdown for issue + uses: asyncapi/.github/.github/actions/slackify-markdown@master + id: markdown + with: + markdown: "[${{github.event.release.tag_name}}](${{github.event.release.html_url}}) \n ${{ github.event.release.body }}" + - name: Send info about release to Slack + uses: rtCamp/action-slack-notify@v2 + env: + SLACK_WEBHOOK: ${{ secrets.SLACK_RELEASES }} + SLACK_TITLE: Release ${{github.event.release.tag_name}} for ${{github.repository}} is out in the wild 😱πŸ’ͺπŸΎπŸŽ‚ + SLACK_MESSAGE: ${{steps.markdown.outputs.text}} + MSG_MINIMAL: true + + twitter-announce: + name: Twitter - notify on minor and major releases + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v3 + - name: Get version of last and previous release + uses: actions/github-script@v6 + id: versions + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const query = `query($owner:String!, $name:String!) { + repository(owner:$owner, name:$name){ + releases(first: 2, orderBy: {field: CREATED_AT, direction: DESC}) { + nodes { + name + } + } + } + }`; + const variables = { + owner: context.repo.owner, + name: context.repo.repo + }; + const { repository: { releases: { nodes } } } = await github.graphql(query, variables); + core.setOutput('lastver', nodes[0].name); + // In case of first release in the package, there is no such thing as previous error, so we set info about previous version only once we have it + // We should tweet about the release no matter of the type as it is initial release + if (nodes.length != 1) core.setOutput('previousver', nodes[1].name); + - name: Identify release type + id: releasetype + # if previousver is not provided then this steps just logs information about missing version, no errors + run: echo "type=$(npx -q -p semver-diff-cli semver-diff ${{steps.versions.outputs.previousver}} ${{steps.versions.outputs.lastver}})" >> $GITHUB_OUTPUT + - name: Get name of the person that is behind the newly released version + id: author + run: echo "name=$(git log -1 --pretty=format:'%an')" >> $GITHUB_OUTPUT + - name: Publish information about the release to Twitter # tweet only if detected version change is not a patch + # tweet goes out even if the type is not major or minor but "You need provide version number to compare." + # it is ok, it just means we did not identify previous version as we are tweeting out information about the release for the first time + if: steps.releasetype.outputs.type != 'null' && steps.releasetype.outputs.type != 'patch' # null means that versions are the same + uses: m1ner79/Github-Twittction@d1e508b6c2170145127138f93c49b7c46c6ff3a7 # using 2.0.0 https://github.com/m1ner79/Github-Twittction/releases/tag/v2.0.0 + with: + twitter_status: "Release ${{github.event.release.tag_name}} for ${{github.repository}} is out in the wild 😱πŸ’ͺπŸΎπŸŽ‚\n\nThank you for the contribution ${{ steps.author.outputs.name }} ${{github.event.release.html_url}}" + twitter_consumer_key: ${{ secrets.TWITTER_CONSUMER_KEY }} + twitter_consumer_secret: ${{ secrets.TWITTER_CONSUMER_SECRET }} + twitter_access_token_key: ${{ secrets.TWITTER_ACCESS_TOKEN_KEY }} + twitter_access_token_secret: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }} \ No newline at end of file diff --git a/.github/workflows/scripts/README.md b/.github/workflows/scripts/README.md new file mode 100644 index 0000000..ba97dca --- /dev/null +++ b/.github/workflows/scripts/README.md @@ -0,0 +1 @@ +The entire `scripts` directory is centrally managed in [.github](https://github.com/asyncapi/.github/) repository. Any changes in this folder should be done in central repository. \ No newline at end of file diff --git a/.github/workflows/scripts/mailchimp/htmlContent.js b/.github/workflows/scripts/mailchimp/htmlContent.js new file mode 100644 index 0000000..d132c72 --- /dev/null +++ b/.github/workflows/scripts/mailchimp/htmlContent.js @@ -0,0 +1,495 @@ +/** + * This code is centrally managed in https://github.com/asyncapi/.github/ + * Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo + */ +module.exports = (link, title) => { + + return ` + + + + + + + + *|MC:SUBJECT|* + + + + + + +
+ + + + +
+ + + + + + + + + + + + +
+ + + + + +
+ + + + + + + + +
+ + Hey *|FNAME|*,
+
+There is a new topic at AsyncAPI Initiative that requires Technical Steering Committee attention. +
+Please have a look if it is just something you need to be aware of, or maybe your vote is needed. +
+Topic: ${ title }. +
+ + + +
+ + + + + + + +
+ + + + + + + + +
+ + Cheers,
+AsyncAPI Initiative +
+ + + +
+ + + + + +
+ + + + + + + + +
+ + Want to change how you receive these emails?
+You can update your preferences or unsubscribe from this list.
+  +
+ + + +
+ + +
+
+ + +` +} \ No newline at end of file diff --git a/.github/workflows/scripts/mailchimp/index.js b/.github/workflows/scripts/mailchimp/index.js new file mode 100644 index 0000000..4a200c6 --- /dev/null +++ b/.github/workflows/scripts/mailchimp/index.js @@ -0,0 +1,79 @@ +/** + * This code is centrally managed in https://github.com/asyncapi/.github/ + * Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo + */ +const mailchimp = require('@mailchimp/mailchimp_marketing'); +const core = require('@actions/core'); +const htmlContent = require('./htmlContent.js'); + +/** + * Sending API request to mailchimp to schedule email to subscribers + * Input is the URL to issue/discussion or other resource + */ +module.exports = async (link, title) => { + + let newCampaign; + + mailchimp.setConfig({ + apiKey: process.env.MAILCHIMP_API_KEY, + server: 'us12' + }); + + /* + * First we create campaign + */ + try { + newCampaign = await mailchimp.campaigns.create({ + type: 'regular', + recipients: { + list_id: '6e3e437abe', + segment_opts: { + match: 'any', + conditions: [{ + condition_type: 'Interests', + field: 'interests-2801e38b9f', + op: 'interestcontains', + value: ['f7204f9b90'] + }] + } + }, + settings: { + subject_line: `TSC attention required: ${ title }`, + preview_text: 'Check out the latest topic that TSC members have to be aware of', + title: `New topic info - ${ new Date(Date.now()).toUTCString()}`, + from_name: 'AsyncAPI Initiative', + reply_to: 'info@asyncapi.io', + } + }); + } catch (error) { + return core.setFailed(`Failed creating campaign: ${ JSON.stringify(error) }`); + } + + /* + * Content of the email is added separately after campaign creation + */ + try { + await mailchimp.campaigns.setContent(newCampaign.id, { html: htmlContent(link, title) }); + } catch (error) { + return core.setFailed(`Failed adding content to campaign: ${ JSON.stringify(error) }`); + } + + /* + * We schedule an email to send it immediately + */ + try { + //schedule for next hour + //so if this code was created by new issue creation at 9:46, the email is scheduled for 10:00 + //is it like this as schedule has limitiations and you cannot schedule email for 9:48 + const scheduleDate = new Date(Date.parse(new Date(Date.now()).toISOString()) + 1 * 1 * 60 * 60 * 1000); + scheduleDate.setUTCMinutes(00); + + await mailchimp.campaigns.schedule(newCampaign.id, { + schedule_time: scheduleDate.toISOString(), + }); + } catch (error) { + return core.setFailed(`Failed scheduling email: ${ JSON.stringify(error) }`); + } + + core.info(`New email campaign created`); +} \ No newline at end of file diff --git a/.github/workflows/scripts/mailchimp/package-lock.json b/.github/workflows/scripts/mailchimp/package-lock.json new file mode 100644 index 0000000..7ee7d84 --- /dev/null +++ b/.github/workflows/scripts/mailchimp/package-lock.json @@ -0,0 +1,597 @@ +{ + "name": "schedule-email", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "schedule-email", + "license": "Apache 2.0", + "dependencies": { + "@actions/core": "1.6.0", + "@mailchimp/mailchimp_marketing": "3.0.74" + } + }, + "node_modules/@actions/core": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.6.0.tgz", + "integrity": "sha512-NB1UAZomZlCV/LmJqkLhNTqtKfFXJZAUPcfl/zqG7EfsQdeUJtaWO98SGbuQ3pydJ3fHl2CvI/51OKYlCYYcaw==", + "dependencies": { + "@actions/http-client": "^1.0.11" + } + }, + "node_modules/@actions/http-client": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.11.tgz", + "integrity": "sha512-VRYHGQV1rqnROJqdMvGUbY/Kn8vriQe/F9HR2AlYHzmKuM/p3kjNuXhmdBfcVgsvRWTz5C5XW5xvndZrVBuAYg==", + "dependencies": { + "tunnel": "0.0.6" + } + }, + "node_modules/@mailchimp/mailchimp_marketing": { + "version": "3.0.74", + "resolved": "https://registry.npmjs.org/@mailchimp/mailchimp_marketing/-/mailchimp_marketing-3.0.74.tgz", + "integrity": "sha512-039iu4GRr7wpXqweBLuS05wvOBtPxSa31cjxgftBYSt7031f0sHEi8Up2DicfbSuQK0AynPDeVyuxrb31Lx+yQ==", + "dependencies": { + "dotenv": "^8.2.0", + "superagent": "3.8.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + }, + "node_modules/cookiejar": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz", + "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dotenv": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", + "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==", + "engines": { + "node": ">=10" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "node_modules/form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/formidable": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz", + "integrity": "sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==", + "deprecated": "Please upgrade to latest, formidable@v2 or formidable@v3! Check these notes: https://bit.ly/2ZEqIau", + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/object-inspect": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", + "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/qs": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/superagent": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.1.tgz", + "integrity": "sha512-VMBFLYgFuRdfeNQSMLbxGSLfmXL/xc+OO+BZp41Za/NRDBet/BNbkRJrYzCUu0u4GU0i/ml2dtT8b9qgkw9z6Q==", + "deprecated": "Please upgrade to v7.0.2+ of superagent. We have fixed numerous issues with streams, form-data, attach(), filesystem errors not bubbling up (ENOENT on attach()), and all tests are now passing. See the releases tab for more information at .", + "dependencies": { + "component-emitter": "^1.2.0", + "cookiejar": "^2.1.0", + "debug": "^3.1.0", + "extend": "^3.0.0", + "form-data": "^2.3.1", + "formidable": "^1.1.1", + "methods": "^1.1.1", + "mime": "^1.4.1", + "qs": "^6.5.1", + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/superagent/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + } + }, + "dependencies": { + "@actions/core": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.6.0.tgz", + "integrity": "sha512-NB1UAZomZlCV/LmJqkLhNTqtKfFXJZAUPcfl/zqG7EfsQdeUJtaWO98SGbuQ3pydJ3fHl2CvI/51OKYlCYYcaw==", + "requires": { + "@actions/http-client": "^1.0.11" + } + }, + "@actions/http-client": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.11.tgz", + "integrity": "sha512-VRYHGQV1rqnROJqdMvGUbY/Kn8vriQe/F9HR2AlYHzmKuM/p3kjNuXhmdBfcVgsvRWTz5C5XW5xvndZrVBuAYg==", + "requires": { + "tunnel": "0.0.6" + } + }, + "@mailchimp/mailchimp_marketing": { + "version": "3.0.74", + "resolved": "https://registry.npmjs.org/@mailchimp/mailchimp_marketing/-/mailchimp_marketing-3.0.74.tgz", + "integrity": "sha512-039iu4GRr7wpXqweBLuS05wvOBtPxSa31cjxgftBYSt7031f0sHEi8Up2DicfbSuQK0AynPDeVyuxrb31Lx+yQ==", + "requires": { + "dotenv": "^8.2.0", + "superagent": "3.8.1" + } + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + }, + "cookiejar": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz", + "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==" + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "dotenv": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", + "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==" + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "formidable": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz", + "integrity": "sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==" + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "object-inspect": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", + "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==" + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "qs": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "requires": { + "side-channel": "^1.0.4" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "superagent": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.1.tgz", + "integrity": "sha512-VMBFLYgFuRdfeNQSMLbxGSLfmXL/xc+OO+BZp41Za/NRDBet/BNbkRJrYzCUu0u4GU0i/ml2dtT8b9qgkw9z6Q==", + "requires": { + "component-emitter": "^1.2.0", + "cookiejar": "^2.1.0", + "debug": "^3.1.0", + "extend": "^3.0.0", + "form-data": "^2.3.1", + "formidable": "^1.1.1", + "methods": "^1.1.1", + "mime": "^1.4.1", + "qs": "^6.5.1", + "readable-stream": "^2.0.5" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + } + } +} \ No newline at end of file diff --git a/.github/workflows/scripts/mailchimp/package.json b/.github/workflows/scripts/mailchimp/package.json new file mode 100644 index 0000000..cc50e43 --- /dev/null +++ b/.github/workflows/scripts/mailchimp/package.json @@ -0,0 +1,9 @@ +{ + "name": "schedule-email", + "description": "This code is responsible for scheduling an email campaign. This file is centrally managed in https://github.com/asyncapi/.github/", + "license": "Apache 2.0", + "dependencies": { + "@actions/core": "1.6.0", + "@mailchimp/mailchimp_marketing": "3.0.74" + } +} \ No newline at end of file diff --git a/.github/workflows/stale-issues-prs.yml b/.github/workflows/stale-issues-prs.yml new file mode 100644 index 0000000..ed1fcf1 --- /dev/null +++ b/.github/workflows/stale-issues-prs.yml @@ -0,0 +1,45 @@ +# This action is centrally managed in https://github.com/asyncapi/.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo + +name: Manage stale issues and PRs + +on: + schedule: + - cron: "0 0 * * *" + +jobs: + stale: + if: startsWith(github.repository, 'asyncapi/') + name: Mark issue or PR as stale + runs-on: ubuntu-latest + steps: + - uses: actions/stale@99b6c709598e2b0d0841cd037aaf1ba07a4410bd #v5.2.0 but pointing to commit for security reasons + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + stale-issue-message: | + This issue has been automatically marked as stale because it has not had recent activity :sleeping: + + It will be closed in 120 days if no further activity occurs. To unstale this issue, add a comment with a detailed explanation. + + There can be many reasons why some specific issue has no activity. The most probable cause is lack of time, not lack of interest. AsyncAPI Initiative is a Linux Foundation project not owned by a single for-profit company. It is a community-driven initiative ruled under [open governance model](https://github.com/asyncapi/community/blob/master/CHARTER.md). + + Let us figure out together how to push this issue forward. Connect with us through [one of many communication channels](https://github.com/asyncapi/community/issues/1) we established here. + + Thank you for your patience :heart: + stale-pr-message: | + This pull request has been automatically marked as stale because it has not had recent activity :sleeping: + + It will be closed in 120 days if no further activity occurs. To unstale this pull request, add a comment with detailed explanation. + + There can be many reasons why some specific pull request has no activity. The most probable cause is lack of time, not lack of interest. AsyncAPI Initiative is a Linux Foundation project not owned by a single for-profit company. It is a community-driven initiative ruled under [open governance model](https://github.com/asyncapi/community/blob/master/CHARTER.md). + + Let us figure out together how to push this pull request forward. Connect with us through [one of many communication channels](https://github.com/asyncapi/community/issues/1) we established here. + + Thank you for your patience :heart: + days-before-stale: 120 + days-before-close: 120 + stale-issue-label: stale + stale-pr-label: stale + exempt-issue-labels: keep-open + exempt-pr-labels: keep-open + close-issue-reason: not_planned diff --git a/.github/workflows/update-maintainers-trigger.yaml b/.github/workflows/update-maintainers-trigger.yaml new file mode 100644 index 0000000..12fc4ab --- /dev/null +++ b/.github/workflows/update-maintainers-trigger.yaml @@ -0,0 +1,28 @@ +# This action is centrally managed in https://github.com/asyncapi/.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo + +name: Trigger MAINTAINERS.yaml file update + +on: + push: + branches: [ master ] + paths: + # Check all valid CODEOWNERS locations: + # https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners#codeowners-file-location + - 'CODEOWNERS' + - '.github/CODEOWNERS' + - '.docs/CODEOWNERS' + +jobs: + trigger-maintainers-update: + name: Trigger updating MAINTAINERS.yaml because of CODEOWNERS change + runs-on: ubuntu-latest + + steps: + - name: Repository Dispatch + uses: peter-evans/repository-dispatch@ff45666b9427631e3450c54a1bcbee4d9ff4d7c0 # https://github.com/peter-evans/repository-dispatch/releases/tag/v3.0.0 + with: + # The PAT with the 'public_repo' scope is required + token: ${{ secrets.GH_TOKEN }} + repository: ${{ github.repository_owner }}/community + event-type: trigger-maintainers-update diff --git a/.github/workflows/update-pr.yml b/.github/workflows/update-pr.yml new file mode 100644 index 0000000..2fa19b0 --- /dev/null +++ b/.github/workflows/update-pr.yml @@ -0,0 +1,102 @@ +# This workflow is centrally managed in https://github.com/asyncapi/.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo + +# This workflow will run on every comment with /update or /u. And will create merge-commits for the PR. +# This also works with forks, not only with branches in the same repository/organization. +# Currently, does not work with forks in different organizations. + +# This workflow will be distributed to all repositories in the AsyncAPI organization + +name: Update PR branches from fork + +on: + issue_comment: + types: [created] + +jobs: + update-pr: + if: > + startsWith(github.repository, 'asyncapi/') && + github.event.issue.pull_request && + github.event.issue.state != 'closed' && ( + contains(github.event.comment.body, '/update') || + contains(github.event.comment.body, '/u') + ) + runs-on: ubuntu-latest + steps: + - name: Get Pull Request Details + id: pr + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }} + previews: 'merge-info-preview' # https://docs.github.com/en/graphql/overview/schema-previews#merge-info-preview-more-detailed-information-about-a-pull-requests-merge-state-preview + script: | + const prNumber = context.payload.issue.number; + core.debug(`PR Number: ${prNumber}`); + const { data: pr } = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: prNumber + }); + + // If the PR has conflicts, we don't want to update it + const updateable = ['behind', 'blocked', 'unknown', 'draft', 'clean'].includes(pr.mergeable_state); + console.log(`PR #${prNumber} is ${pr.mergeable_state} and is ${updateable ? 'updateable' : 'not updateable'}`); + core.setOutput('updateable', updateable); + + core.debug(`Updating PR #${prNumber} with head ${pr.head.sha}`); + + return { + id: pr.node_id, + number: prNumber, + head: pr.head.sha, + } + - name: Update the Pull Request + if: steps.pr.outputs.updateable == 'true' + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const mutation = `mutation update($input: UpdatePullRequestBranchInput!) { + updatePullRequestBranch(input: $input) { + pullRequest { + mergeable + } + } + }`; + + const pr_details = ${{ steps.pr.outputs.result }}; + + try { + const { data } = await github.graphql(mutation, { + input: { + pullRequestId: pr_details.id, + expectedHeadOid: pr_details.head, + } + }); + } catch (GraphQLError) { + core.debug(GraphQLError); + if ( + GraphQLError.name === 'GraphqlResponseError' && + GraphQLError.errors.some( + error => error.type === 'FORBIDDEN' || error.type === 'UNAUTHORIZED' + ) + ) { + // Add comment to PR if the bot doesn't have permissions to update the PR + const comment = `Hi @${context.actor}. Update of PR has failed. It can be due to one of the following reasons: + - I don't have permissions to update this PR. To update your fork with upstream using bot you need to enable [Allow edits from maintainers](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork) option in the PR. + - The fork is located in an organization, not under your personal profile. No solution for that. You are on your own with manual update. + - There may be a conflict in the PR. Please resolve the conflict and try again.`; + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body: comment + }); + + core.setFailed('Bot does not have permissions to update the PR'); + } else { + core.setFailed(GraphQLError.message); + } + } diff --git a/.github/workflows/welcome-first-time-contrib.yml b/.github/workflows/welcome-first-time-contrib.yml new file mode 100644 index 0000000..cbc23ce --- /dev/null +++ b/.github/workflows/welcome-first-time-contrib.yml @@ -0,0 +1,85 @@ +# This action is centrally managed in https://github.com/asyncapi/.github/ +# Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in above mentioned repo + +name: Welcome first time contributors + +on: + pull_request_target: + types: + - opened + issues: + types: + - opened + +jobs: + welcome: + name: Post welcome message + if: ${{ !contains(fromJson('["asyncapi-bot", "dependabot[bot]", "dependabot-preview[bot]", "allcontributors[bot]"]'), github.actor) }} + runs-on: ubuntu-latest + steps: + - uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const issueMessage = `Welcome to AsyncAPI. Thanks a lot for reporting your first issue. Please check out our [contributors guide](https://github.com/asyncapi/community/blob/master/CONTRIBUTING.md) and the instructions about a [basic recommended setup](https://github.com/asyncapi/community/blob/master/git-workflow.md) useful for opening a pull request.
Keep in mind there are also other channels you can use to interact with AsyncAPI community. For more details check out [this issue](https://github.com/asyncapi/asyncapi/issues/115).`; + const prMessage = `Welcome to AsyncAPI. Thanks a lot for creating your first pull request. Please check out our [contributors guide](https://github.com/asyncapi/community/blob/master/CONTRIBUTING.md) useful for opening a pull request.
Keep in mind there are also other channels you can use to interact with AsyncAPI community. For more details check out [this issue](https://github.com/asyncapi/asyncapi/issues/115).`; + if (!issueMessage && !prMessage) { + throw new Error('Action must have at least one of issue-message or pr-message set'); + } + const isIssue = !!context.payload.issue; + let isFirstContribution; + if (isIssue) { + const query = `query($owner:String!, $name:String!, $contributer:String!) { + repository(owner:$owner, name:$name){ + issues(first: 1, filterBy: {createdBy:$contributer}){ + totalCount + } + } + }`; + const variables = { + owner: context.repo.owner, + name: context.repo.repo, + contributer: context.payload.sender.login + }; + const { repository: { issues: { totalCount } } } = await github.graphql(query, variables); + isFirstContribution = totalCount === 1; + } else { + const query = `query($qstr: String!) { + search(query: $qstr, type: ISSUE, first: 1) { + issueCount + } + }`; + const variables = { + "qstr": `repo:${context.repo.owner}/${context.repo.repo} type:pr author:${context.payload.sender.login}`, + }; + const { search: { issueCount } } = await github.graphql(query, variables); + isFirstContribution = issueCount === 1; + } + + if (!isFirstContribution) { + console.log(`Not the users first contribution.`); + return; + } + const message = isIssue ? issueMessage : prMessage; + // Add a comment to the appropriate place + if (isIssue) { + const issueNumber = context.payload.issue.number; + console.log(`Adding message: ${message} to issue #${issueNumber}`); + await github.rest.issues.createComment({ + owner: context.payload.repository.owner.login, + repo: context.payload.repository.name, + issue_number: issueNumber, + body: message + }); + } + else { + const pullNumber = context.payload.pull_request.number; + console.log(`Adding message: ${message} to pull request #${pullNumber}`); + await github.rest.pulls.createReview({ + owner: context.payload.repository.owner.login, + repo: context.payload.repository.name, + pull_number: pullNumber, + body: message, + event: 'COMMENT' + }); + } diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..638f733 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +- The use of sexualized language or imagery and unwelcome sexual attention or advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or electronic address, without explicit permission +- Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at fmvilas@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 72a3be9..1334921 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,34 +1,79 @@ -# Contributing +# Contributing to AsyncAPI +We love your input! We want to make contributing to this project as easy and transparent as possible. -The kotlin-asyncapi project is open and grateful for contributions. You can help us by reporting/fixing issues and adding new features. +## Contribution recogniton -Please consider our vision for this project when suggesting/adding new features. We want to provide code-first tools for Kotlin microservices that make it easy for developers to document event-based APIs. +We use [All Contributors](https://allcontributors.org/docs/en/specification) specification to handle recognitions. For more details read [this](https://github.com/asyncapi/community/blob/master/recognize-contributors.md) document. -Familiarize yourself with the [project architecture](./ARCHITECTURE.md) and design your contribution accordingly. +## Summary of the contribution flow -## Code Style +The following is a summary of the ideal contribution flow. Please, note that Pull Requests can also be rejected by the maintainers when appropriate. -Please follow the [Kotlin coding conventions](https://kotlinlang.org/docs/coding-conventions.html). +``` + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ β”‚ + β”‚ Open an issue β”‚ + β”‚ (a bug report or a β”‚ + β”‚ feature request) β”‚ + β”‚ β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + ⇩ + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ β”‚ + β”‚ Open a Pull Request β”‚ + β”‚ (only after issue β”‚ + β”‚ is approved) β”‚ + β”‚ β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + ⇩ + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ β”‚ + β”‚ Your changes will β”‚ + β”‚ be merged and β”‚ + β”‚ published on the next β”‚ + β”‚ release β”‚ + β”‚ β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` -## Commit Messages +## Code of Conduct +AsyncAPI has adopted a Code of Conduct that we expect project participants to adhere to. Please [read the full text](./CODE_OF_CONDUCT.md) so that you can understand what sort of behaviour is expected. -Please follow [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/). +## Our Development Process +We use Github to host code, to track issues and feature requests, as well as accept pull requests. -## Testing +## Issues +[Open an issue](https://github.com/asyncapi/asyncapi/issues/new) **only** if you want to report a bug or a feature. Don't open issues for questions or support, instead join our [Slack workspace](https://www.asyncapi.com/slack-invite) and ask there. Don't forget to follow our [Slack Etiquette](https://github.com/asyncapi/community/blob/master/slack-etiquette.md) while interacting with community members! It's more likely you'll get help, and much faster! -Please update and add new tests to reflect your code changes. Pull requests will not be accepted if they are failing on GitHub actions. +## Bug Reports and Feature Requests -## Documentation +Please use our issues templates that provide you with hints on what information we need from you to help you out. -Please update the [docs](README.md) accordingly so that there are no discrepancies between the API and the documentation. +## Pull Requests -## Developing +**Please, make sure you open an issue before starting with a Pull Request, unless it's a typo or a really obvious error.** Pull requests are the best way to propose changes to the specification. Get familiar with our document that explains [Git workflow](https://github.com/asyncapi/community/blob/master/git-workflow.md) used in our repositories. -- fork the project -- create a new branch from `dev` (pattern `chore/feat/docs/refactor.../` e.g. `feat/add-annotation-processing`) -- open a PR with `dev` as base branch -- fill out the PR template -- provide a descriptive PR title (pattern `chore/feat/docs/refactor...:` e.g. `feat: Add annotation processing`) -- wait for 1 CODEOWNER + 1 MAINTAINER review -- squash and merge your PR -- your commit will be part of the next release +## Conventional commits + +Our repositories follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/#summary) specification. Releasing to GitHub and NPM is done with the support of [semantic-release](https://semantic-release.gitbook.io/semantic-release/). + +Pull requests should have a title that follows the specification, otherwise, merging is blocked. If you are not familiar with the specification simply ask maintainers to modify. You can also use this cheatsheet if you want: + +- `fix: ` prefix in the title indicates that PR is a bug fix and PATCH release must be triggered. +- `feat: ` prefix in the title indicates that PR is a feature and MINOR release must be triggered. +- `docs: ` prefix in the title indicates that PR is only related to the documentation and there is no need to trigger release. +- `chore: ` prefix in the title indicates that PR is only related to cleanup in the project and there is no need to trigger release. +- `test: ` prefix in the title indicates that PR is only related to tests and there is no need to trigger release. +- `refactor: ` prefix in the title indicates that PR is only related to refactoring and there is no need to trigger release. + +What about MAJOR release? just add `!` to the prefix, like `fix!: ` or `refactor!: ` + +Prefix that follows specification is not enough though. Remember that the title must be clear and descriptive with usage of [imperative mood](https://chris.beams.io/posts/git-commit/#imperative). + +Happy contributing :heart: + +## License +When you submit changes, your submissions are understood to be under the same [Apache 2.0 License](https://github.com/asyncapi/asyncapi/blob/master/LICENSE) that covers the project. Feel free to [contact the maintainers](https://www.asyncapi.com/slack-invite) if that's a concern. + +## References +This document was adapted from the open-source contribution guidelines for [Facebook's Draft](https://github.com/facebook/draft-js/blob/master/CONTRIBUTING.md). \ No newline at end of file