diff --git a/.github/scripts/maintainers/.gitignore b/.github/scripts/maintainers/.gitignore
new file mode 100644
index 000000000..60923f546
--- /dev/null
+++ b/.github/scripts/maintainers/.gitignore
@@ -0,0 +1 @@
+github.api.cache.json
diff --git a/.github/scripts/maintainers/README.md b/.github/scripts/maintainers/README.md
new file mode 100644
index 000000000..6d82e01e4
--- /dev/null
+++ b/.github/scripts/maintainers/README.md
@@ -0,0 +1,58 @@
+# Maintainers
+
+The ["Update MAINTAINERS.yaml file"](../../workflows/update-maintainers.yaml) workflow, defined in the `community` repository performs a complete refresh by fetching all public repositories under AsyncAPI and their respective `CODEOWNERS` files.
+
+## Workflow Execution
+
+The "Update MAINTAINERS.yaml file" workflow is executed in the following scenarios:
+
+1. **Weekly Schedule**: The workflow runs automatically every week. It is useful, e.g. when some repositories are archived, renamed, or when a GitHub user account is removed.
+2. **On Change**: When a `CODEOWNERS` file is changed in any repository under the AsyncAPI organization, the related repository triggers the workflow by emitting the `trigger-maintainers-update` event.
+3. **Manual Trigger**: Users can manually trigger the workflow as needed.
+
+### Workflow Steps
+
+1. **Load Cache**: Attempt to read previously cached data from `github.api.cache.json` to optimize API calls.
+2. **List All Repositories**: Retrieve a list of all public repositories under the AsyncAPI organization, skipping any repositories specified in the `IGNORED_REPOSITORIES` environment variable.
+3. **Fetch `CODEOWNERS` Files**: For each repository:
+ - Detect the default branch (e.g., `main`, `master`, or a custom branch).
+ - Check for `CODEOWNERS` files in all valid locations as specified in the [GitHub documentation](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners#codeowners-file-location).
+4. **Process `CODEOWNERS` Files**:
+ 1. Extract GitHub usernames from each `CODEOWNERS` file, excluding emails, team names, and users specified by the `IGNORED_USERS` environment variable.
+ 2. Retrieve profile information for each unique GitHub username.
+ 3. Collect a fresh list of repositories currently owned by each GitHub user.
+5. **Refresh Maintainers List**: Iterate through the existing maintainers list:
+ - Delete the entry if it:
+ - Refers to a deleted GitHub account.
+ - Was not found in any `CODEOWNERS` file across all repositories in the AsyncAPI organization.
+ - Otherwise, update **only** the `repos` property.
+6. **Add New Maintainers**: Append any new maintainers not present in the previous list.
+7. **Changes Summary**: Provide details on why a maintainer was removed or changed directly on the GitHub Action [summary page](https://github.blog/2022-05-09-supercharging-github-actions-with-job-summaries/).
+8. **Save Cache**: Save retrieved data in `github.api.cache.json`.
+
+## Job Details
+
+- **Concurrency**: Ensures the workflow does not run multiple times concurrently to avoid conflicts.
+- **Wait for PRs to be Merged**: The workflow waits for pending pull requests to be merged before execution. If the merged pull request addresses all necessary fixes, it prevents unnecessary executions.
+
+## Handling Conflicts
+
+Since the job performs a full refresh each time, resolving conflicts is straightforward:
+
+1. Close the pull request with conflicts.
+2. Navigate to the "Update MAINTAINERS.yaml file" workflow.
+3. Trigger it manually by clicking "Run workflow".
+
+## Caching Mechanism
+
+Each execution of this action performs a full refresh through the following API calls:
+
+```
+ListRepos(AsyncAPI) # 1 call using GraphQL - not cached.
+ for each Repo
+ GetCodeownersFile(Repo) # N calls using REST API - all are cached. N refers to the number of public repositories under AsyncAPI.
+ for each codeowner
+ GetGitHubProfile(owner) # Y calls using REST API - all are cached. Y refers to unique GitHub users found across all CODEOWNERS files.
+```
+
+To avoid hitting the GitHub API rate limits, [conditional requests](https://docs.github.com/en/rest/using-the-rest-api/best-practices-for-using-the-rest-api?apiVersion=2022-11-28#use-conditional-requests-if-appropriate) are used via `if-modified-since`. The API responses are saved into a `github.api.cache.json` file, which is later uploaded as a GitHub action cache item.
diff --git a/.github/scripts/maintainers/cache.js b/.github/scripts/maintainers/cache.js
new file mode 100644
index 000000000..0a52b4b7e
--- /dev/null
+++ b/.github/scripts/maintainers/cache.js
@@ -0,0 +1,64 @@
+const fs = require("fs");
+
+module.exports = {
+ fetchWithCache,
+ saveCache,
+ loadCache,
+ printAPICallsStats,
+};
+
+const CODEOWNERS_CACHE_PATH = "./.github/scripts/maintainers/github.api.cache.json";
+
+let cacheEntries = {};
+
+let numberOfFullFetches = 0;
+let numberOfCacheHits = 0;
+
+function loadCache(core) {
+ try {
+ cacheEntries = JSON.parse(fs.readFileSync(CODEOWNERS_CACHE_PATH, "utf8"));
+ } catch (error) {
+ core.warning(`Cache was not restored: ${error}`);
+ }
+}
+
+function saveCache() {
+ fs.writeFileSync(CODEOWNERS_CACHE_PATH, JSON.stringify(cacheEntries));
+}
+
+async function fetchWithCache(cacheKey, fetchFn, core) {
+ const cachedResp = cacheEntries[cacheKey];
+
+ try {
+ const { data, headers } = await fetchFn({
+ headers: {
+ "if-modified-since": cachedResp?.lastModified ?? "",
+ },
+ });
+
+ cacheEntries[cacheKey] = {
+ // last modified header is more reliable than etag while executing calls on GitHub Action
+ lastModified: headers["last-modified"],
+ data,
+ };
+
+ numberOfFullFetches++;
+ return data;
+ } catch (error) {
+ if (error.status === 304) {
+ numberOfCacheHits++;
+ core.debug(`Returning cached data for ${cacheKey}`);
+ return cachedResp.data;
+ }
+ throw error;
+ }
+}
+
+function printAPICallsStats(core) {
+ core.startGroup("API calls statistic");
+ core.info(
+ `Number of API calls count against rate limit: ${numberOfFullFetches}`,
+ );
+ core.info(`Number of cache hits: ${numberOfCacheHits}`);
+ core.endGroup();
+}
diff --git a/.github/scripts/maintainers/gh_calls.js b/.github/scripts/maintainers/gh_calls.js
new file mode 100644
index 000000000..f10b5c2eb
--- /dev/null
+++ b/.github/scripts/maintainers/gh_calls.js
@@ -0,0 +1,131 @@
+const { fetchWithCache } = require("./cache");
+
+module.exports = { getGitHubProfile, getAllCodeownersFiles, getRepositories };
+
+async function getRepositories(github, owner, ignoredRepos, core) {
+ core.startGroup(
+ `Getting list of all public, non-archived repositories owned by ${owner}`,
+ );
+
+ const query = `
+ query repos($cursor: String, $owner: String!) {
+ organization(login: $owner) {
+ repositories(first: 100 after: $cursor visibility: PUBLIC isArchived: false orderBy: {field: CREATED_AT, direction: ASC} ) {
+ nodes {
+ name
+ }
+ pageInfo {
+ hasNextPage
+ endCursor
+ }
+ }
+ }
+ }`;
+
+ const repos = [];
+ let cursor = null;
+
+ do {
+ const result = await github.graphql(query, { owner, cursor });
+ const { nodes, pageInfo } = result.organization.repositories;
+ repos.push(...nodes);
+
+ cursor = pageInfo.hasNextPage ? pageInfo.endCursor : null;
+ } while (cursor);
+
+ core.debug(`List of repositories for ${owner}:`);
+ core.debug(JSON.stringify(repos, null, 2));
+ core.endGroup();
+
+ return repos.filter((repo) => !ignoredRepos.includes(repo.name));
+}
+
+async function getGitHubProfile(github, login, core) {
+ try {
+ const profile = await fetchWithCache(
+ `profile:${login}`,
+ async ({ headers }) => {
+ return github.rest.users.getByUsername({
+ username: login,
+ headers,
+ });
+ },
+ core,
+ );
+ return removeNulls({
+ name: profile.name ?? login,
+ github: login,
+ twitter: profile.twitter_username,
+ availableForHire: profile.hireable,
+ isTscMember: false,
+ repos: [],
+ githubID: profile.id,
+ });
+ } catch (error) {
+ if (error.status === 404) {
+ return null;
+ }
+ throw error;
+ }
+}
+
+// Checks for all valid locations according to:
+// https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners#codeowners-file-location
+//
+// Detect the repository default branch automatically.
+async function getCodeownersFile(github, owner, repo, core) {
+ const paths = ["CODEOWNERS", "docs/CODEOWNERS", ".github/CODEOWNERS"];
+
+ for (const path of paths) {
+ try {
+ core.debug(
+ `[repo: ${owner}/${repo}]: Fetching CODEOWNERS file at ${path}`,
+ );
+ return await fetchWithCache(
+ `owners:${owner}/${repo}`,
+ async ({ headers }) => {
+ return github.rest.repos.getContent({
+ owner,
+ repo,
+ path,
+ headers: {
+ Accept: "application/vnd.github.raw+json",
+ ...headers,
+ },
+ });
+ },
+ core,
+ );
+ } catch (error) {
+ core.warning(
+ `[repo: ${owner}/${repo}]: Failed to fetch CODEOWNERS file at ${path}: ${error.message}`,
+ );
+ }
+ }
+
+ core.error(
+ `[repo: ${owner}/${repo}]: CODEOWNERS file not found in any of the expected locations.`,
+ );
+ return null;
+}
+
+async function getAllCodeownersFiles(github, owner, repos, core) {
+ core.startGroup(`Fetching CODEOWNERS files for ${repos.length} repositories`);
+ const files = [];
+ for (const repo of repos) {
+ const data = await getCodeownersFile(github, owner, repo.name, core);
+ if (!data) {
+ continue;
+ }
+ files.push({
+ repo: repo.name,
+ content: data,
+ });
+ }
+ core.endGroup();
+ return files;
+}
+
+function removeNulls(obj) {
+ return Object.fromEntries(Object.entries(obj).filter(([_, v]) => v != null));
+}
diff --git a/.github/scripts/maintainers/index.js b/.github/scripts/maintainers/index.js
new file mode 100644
index 000000000..be32d8da5
--- /dev/null
+++ b/.github/scripts/maintainers/index.js
@@ -0,0 +1,190 @@
+const yaml = require("js-yaml");
+const fs = require("fs");
+const { saveCache, loadCache, printAPICallsStats } = require("./cache");
+const { summarizeChanges } = require("./summary");
+const {
+ getAllCodeownersFiles,
+ getGitHubProfile,
+ getRepositories,
+} = require("./gh_calls");
+
+module.exports = async ({ github, context, core }) => {
+ try {
+ await run(github, context, core);
+ } catch (error) {
+ console.log(error);
+ core.setFailed(`An error occurred: ${error}`);
+ }
+};
+
+const config = {
+ ghToken: process.env.GH_TOKEN,
+ ignoredRepos: getCommaSeparatedInputList(process.env.IGNORED_REPOSITORIES),
+ ignoredUsers: getCommaSeparatedInputList(process.env.IGNORED_USERS),
+ maintainersFilePath: process.env.MAINTAINERS_FILE_PATH,
+};
+
+function getCommaSeparatedInputList(list) {
+ return (
+ list
+ ?.split(",")
+ .map((item) => item.trim())
+ .filter((item) => item !== "") ?? []
+ );
+}
+
+function splitByWhitespace(line) {
+ return line.trim().split(/\s+/);
+}
+
+function extractGitHubUsernames(codeownersContent, core) {
+ if (!codeownersContent) return [];
+
+ const uniqueOwners = new Set();
+
+ for (const line of codeownersContent.split("\n")) {
+ // split by '#' to process comments separately
+ const [ownersLine, comment = ""] = line.split("#");
+
+ // 1. Check AsyncAPI custom owners
+ const triagers = comment.split(/docTriagers:|codeTriagers:/)[1]
+ if (triagers) {
+ const owners = splitByWhitespace(triagers)
+ owners.forEach(owner => uniqueOwners.add(owner))
+ }
+
+ // 2. Check GitHub native codeowners
+ const owners = splitByWhitespace(ownersLine);
+
+ // the 1st element is the file location, we don't need it, so we start with 2nd item
+ for (const owner of owners.slice(1)) {
+ if (!owner.startsWith("@") || owner.includes("/")) {
+ core.warning(`Skipping '${owner}' as emails and teams are not supported yet`);
+ continue;
+ }
+ uniqueOwners.add(owner.slice(1)); // remove the '@'
+ }
+ }
+
+ return uniqueOwners;
+}
+
+async function collectCurrentMaintainers(codeownersFiles, github, core) {
+ core.startGroup(`Fetching GitHub profile information for each codeowner`);
+
+ const currentMaintainers = {};
+ for (const codeowners of codeownersFiles) {
+ const owners = extractGitHubUsernames(codeowners.content, core);
+
+ for (const owner of owners) {
+ if (config.ignoredUsers.includes(owner)) {
+ core.debug(
+ `[repo: ${codeowners.repo}]: The user '${owner}' is on the ignore list. Skipping...`,
+ );
+ continue;
+ }
+ const key = owner.toLowerCase();
+ if (!currentMaintainers[key]) {
+ // Fetching GitHub profile is useful to ensure that all maintainers are valid (e.g., their GitHub accounts haven't been deleted).
+ const profile = await getGitHubProfile(github, owner, core);
+ if (!profile) {
+ core.warning(
+ `[repo: ${codeowners.repo}]: GitHub profile not found for ${owner}.`,
+ );
+ continue;
+ }
+
+ currentMaintainers[key] = { ...profile, repos: [] };
+ }
+
+ currentMaintainers[key].repos.push(codeowners.repo);
+ }
+ }
+
+ core.endGroup();
+ return currentMaintainers;
+}
+
+function refreshPreviousMaintainers(
+ previousMaintainers,
+ currentMaintainers,
+ core,
+) {
+ core.startGroup(`Refreshing previous maintainers list`);
+
+ const updatedMaintainers = [];
+
+ // 1. Iterate over the list of previous maintainers to:
+ // - Remove any maintainers who are not listed in any current CODEOWNERS files.
+ // - Update the repos list, ensuring that other properties (e.g., 'linkedin', 'slack', etc.) remain unchanged.
+ for (const previousEntry of previousMaintainers) {
+ const key = previousEntry.github.toLowerCase();
+ const currentMaintainer = currentMaintainers[key];
+ if (!currentMaintainer) {
+ core.info(
+ `The previous ${previousEntry.github} maintainer was not found in any CODEOWNERS file. Removing...`,
+ );
+ continue;
+ }
+ delete currentMaintainers[key];
+
+ updatedMaintainers.push({
+ ...previousEntry,
+ repos: currentMaintainer.repos,
+ githubID: currentMaintainer.githubID,
+ });
+ }
+
+ // 2. Append new codeowners who are not present in the previous Maintainers file.
+ const newMaintainers = Object.values(currentMaintainers);
+ updatedMaintainers.push(...newMaintainers);
+
+ core.endGroup();
+ return updatedMaintainers;
+}
+
+async function run(github, context, core) {
+ if (!config.maintainersFilePath) {
+ core.setFailed("The MAINTAINERS_FILE_PATH is not defined");
+ return;
+ }
+ loadCache(core);
+
+ const repos = await getRepositories(
+ github,
+ context.repo.owner,
+ config.ignoredRepos,
+ core,
+ );
+ const codeownersFiles = await getAllCodeownersFiles(
+ github,
+ context.repo.owner,
+ repos,
+ core,
+ );
+
+ const previousMaintainers = yaml.load(
+ fs.readFileSync(config.maintainersFilePath, "utf8"),
+ );
+
+ // 1. Collect new maintainers from all current CODEOWNERS files found across all repositories.
+ const currentMaintainers = await collectCurrentMaintainers(
+ codeownersFiles,
+ github,
+ core,
+ );
+
+ // 2. Refresh the repository list for existing maintainers and add any new maintainers to the list.
+ const refreshedMaintainers = refreshPreviousMaintainers(
+ previousMaintainers,
+ currentMaintainers,
+ core,
+ );
+
+ fs.writeFileSync(config.maintainersFilePath, yaml.dump(refreshedMaintainers));
+
+ printAPICallsStats(core);
+
+ await summarizeChanges(previousMaintainers, refreshedMaintainers, core);
+ saveCache();
+}
diff --git a/.github/scripts/maintainers/summary.js b/.github/scripts/maintainers/summary.js
new file mode 100644
index 000000000..e07d03fd4
--- /dev/null
+++ b/.github/scripts/maintainers/summary.js
@@ -0,0 +1,99 @@
+module.exports = { summarizeChanges };
+
+async function summarizeChanges(oldMaintainers, newMaintainers, core) {
+ const outOfSync = [];
+ const noLongerActive = [];
+
+ const newMaintainersByGitHubName = new Map();
+ for (const newMaintainer of newMaintainers) {
+ newMaintainersByGitHubName.set(newMaintainer.github, newMaintainer);
+ }
+
+ for (const oldEntry of oldMaintainers) {
+ const newEntry = newMaintainersByGitHubName.get(oldEntry.github);
+
+ if (!newEntry) {
+ noLongerActive.push([oldEntry.github, repositoriesLinks(oldEntry.repos)]);
+ continue;
+ }
+
+ const { newOwnedRepos, noLongerOwnedRepos } = compareRepos(
+ oldEntry.repos,
+ newEntry.repos,
+ );
+
+ if (newOwnedRepos.length > 0 || noLongerOwnedRepos.length > 0) {
+ outOfSync.push([
+ profileLink(oldEntry.github),
+ repositoriesLinks(newOwnedRepos),
+ repositoriesLinks(noLongerOwnedRepos),
+ ]);
+ }
+ }
+
+ if (outOfSync.length > 0) {
+ core.summary.addHeading("⚠️ Out of Sync Maintainers", "2");
+ core.summary.addTable([
+ [
+ { data: "Name", header: true },
+ { data: "Newly added to CODEOWNERS", header: true },
+ { data: "No longer in CODEOWNERS", header: true },
+ ],
+ ...outOfSync,
+ ]);
+ core.summary.addBreak();
+ }
+
+ if (noLongerActive.length > 0) {
+ core.summary.addHeading(
+ "👻 Inactive Maintainers (not listed in any repositories)",
+ "2",
+ );
+
+ core.summary.addTable([
+ [
+ { data: "Name", header: true },
+ { data: "Previously claimed ownership in repos", header: true },
+ ],
+ ...noLongerActive,
+ ]);
+
+ core.summary.addBreak();
+ }
+
+ await core.summary.write({ overwrite: true });
+}
+
+function compareRepos(oldRepos, newRepos) {
+ const newOwnedRepositories = [];
+ const noLongerOwnedRepositories = [];
+
+ for (const repo of newRepos) {
+ if (!oldRepos.includes(repo)) {
+ newOwnedRepositories.push(repo);
+ }
+ }
+
+ for (const repo of oldRepos) {
+ if (!newRepos.includes(repo)) {
+ noLongerOwnedRepositories.push(repo);
+ }
+ }
+
+ return {
+ newOwnedRepos: newOwnedRepositories,
+ noLongerOwnedRepos: noLongerOwnedRepositories,
+ };
+}
+
+function repositoriesLinks(repos) {
+ return repos
+ .map((repo) => {
+ return `${repo}`;
+ })
+ .join(", ");
+}
+
+function profileLink(login) {
+ return `${login}
`;
+}
diff --git a/.github/workflows/update-maintainers.yaml b/.github/workflows/update-maintainers.yaml
new file mode 100644
index 000000000..858eb02aa
--- /dev/null
+++ b/.github/workflows/update-maintainers.yaml
@@ -0,0 +1,130 @@
+# This action updates the `MAINTAINERS.yaml` file based on `CODEOWNERS` files in all organization repositories.
+# It is triggered when a `CODEOWNERS` file is changed; the related repository triggers this workflow by emitting the `trigger-maintainers-update` event.
+# It can also be triggered manually.
+
+name: Update MAINTAINERS.yaml file
+
+on:
+ push:
+ branches: [ master ]
+ paths:
+ - 'CODEOWNERS'
+ - '.github/scripts/maintainers/**'
+ - '.github/workflows/update-maintainers.yaml'
+
+ schedule:
+ - cron: "0 10 * * SUN" # Runs at 10:00 AM UTC every Sunday.
+
+ workflow_dispatch:
+
+ repository_dispatch:
+ types: [ trigger-maintainers-update ]
+
+concurrency:
+ group: ${{ github.workflow }}
+ cancel-in-progress: false
+
+env:
+ IGNORED_REPOSITORIES: "shape-up-process"
+ IGNORED_USERS: "asyncapi-bot-eve"
+
+ BRANCH_NAME: "bot/update-maintainers-${{ github.run_id }}"
+ PR_TITLE: "docs(maintainers): update MAINTAINERS.yaml file with the latest CODEOWNERS changes"
+
+jobs:
+ update-maintainers:
+ name: Update MAINTAINERS.yaml based on CODEOWNERS files in all organization repositories
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ with:
+ # If an action pushes code using the repository’s GITHUB_TOKEN, a pull request workflow will not run.
+ token: ${{ secrets.GH_TOKEN }}
+
+ - name: Wait for active pull requests to be merged
+ env:
+ GH_TOKEN: ${{ github.token }}
+ TIMEOUT: 300 # Timeout in seconds
+ INTERVAL: 5 # Check interval in seconds
+ run: |
+ check_active_prs() {
+ ACTIVE_PULL_REQUESTS=$(gh -R $GITHUB_REPOSITORY pr list --search "is:pr ${PR_TITLE} in:title" --json id)
+ if [ "$ACTIVE_PULL_REQUESTS" == "[]" ]; then
+ return 1 # No active PRs
+ else
+ return 0 # Active PRs found
+ fi
+ }
+
+ # Loop with timeout
+ elapsed_time=0
+ while [ $elapsed_time -lt $TIMEOUT ]; do
+ if check_active_prs; then
+ echo "There is an active pull request. Waiting for it to be merged..."
+ else
+ echo "There is no active pull request. Proceeding with updating MAINTAINERS file."
+ git pull
+ exit 0
+ fi
+
+ sleep $INTERVAL
+ elapsed_time=$((elapsed_time + INTERVAL))
+ done
+
+ echo "Timeout reached. Proceeding with updating MAINTAINERS.yaml file with active pull request(s) present. It may result in merge conflict."
+ exit 0
+
+ - name: Restore cached GitHub API calls
+ uses: actions/cache/restore@v4
+ with:
+ path: ./.github/scripts/maintainers/github.api.cache.json
+ key: github-api-cache
+ restore-keys: |
+ github-api-cache-
+
+ - name: Installing Module
+ shell: bash
+ run: npm install js-yaml@4 --no-save
+
+ - name: Run script updating MAINTAINERS.yaml
+ uses: actions/github-script@v7
+ env:
+ GH_TOKEN: ${{ github.token }}
+ MAINTAINERS_FILE_PATH: "${{ github.workspace }}/MAINTAINERS.yaml"
+ with:
+ script: |
+ const script = require('./.github/scripts/maintainers/index.js')
+ await script({github, context, core})
+
+ - name: Save cached GitHub API calls
+ uses: actions/cache/save@v4
+ with:
+ path: ./.github/scripts/maintainers/github.api.cache.json
+ # re-evaluate the key, so we update cache when file changes
+ key: github-api-cache-${{ hashfiles('./.github/scripts/maintainers/github.api.cache.json') }}
+
+ - name: Create PR with latest changes
+ uses: peter-evans/create-pull-request@c5a7806660adbe173f04e3e038b0ccdcd758773c # https://github.com/peter-evans/create-pull-request/releases/tag/v6.1.0
+ with:
+ token: ${{ secrets.GH_TOKEN }}
+ commit-message: ${{ env.PR_TITLE }}
+ committer: asyncapi-bot
+ author: asyncapi-bot
+ title: ${{ env.PR_TITLE }}
+ branch: ${{ env.BRANCH_NAME }}
+ body: |
+ **Description**
+ - Update MAINTAINERS.yaml based on CODEOWNERS files across all repositories in the organization.
+
+ For details on why a maintainer was removed or changed, refer to the [Job summary page](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}).
+
+ - name: Report workflow run status to Slack
+ uses: rtCamp/action-slack-notify@4e5fb42d249be6a45a298f3c9543b111b02f7907 # https://github.com/rtCamp/action-slack-notify/releases/tag/v2.3.0
+ if: failure()
+ env:
+ SLACK_WEBHOOK: ${{secrets.SLACK_CI_FAIL_NOTIFY}}
+ SLACK_TITLE: 🚨 Update MAINTAINERS.yaml file Workflow failed 🚨
+ SLACK_MESSAGE: Failed to auto update MAINTAINERS.yaml file.
+ MSG_MINIMAL: true
diff --git a/AMBASSADORS_MEMBERS.json b/AMBASSADORS_MEMBERS.json
index 1eef57473..c86c4cca9 100644
--- a/AMBASSADORS_MEMBERS.json
+++ b/AMBASSADORS_MEMBERS.json
@@ -686,5 +686,45 @@
"link": "https://www.asyncapi.com/casestudies/hdiglobal"
}
]
+ },
+ {
+ "name": "Lorna Mitchell",
+ "bio": "Lorna is based in Yorkshire, UK; she is a technology leader and expert in developer experience, passionate about enhancing APIs and developer tools. In her day job as VP of Developer Experience at Redocly, she works on API and documentation tools for technical teams. Lorna is a published author and a regular speaker at conferences, sharing her insights on a variety of tech-related topics. Lorna serves on the OpenUK board, is on the Technical Steering Committee for OpenAPI specification, and maintains open source projects. To learn more about Lorna's activities, visit her website at https://lornajane.net.",
+ "title": "APIs and Open Source",
+ "img": "https://lornajane.net/wp-content/uploads/2011/08/IMG_9410-smaller.jpg",
+ "github": "lornajane",
+ "twitter": "lornajane",
+ "linkedin": "lornajane",
+ "company": "Redocly",
+ "country": "UK",
+ "contributions": [
+ {
+ "type": "presentation",
+ "title": "API Governance for AsyncAPI",
+ "date": {
+ "year": 2023,
+ "month": "September"
+ },
+ "link": "https://noti.st/lornajane/aOuXwe/api-governance-for-asyncapi"
+ },
+ {
+ "type": "presentation",
+ "title": "AsyncAPI for Apache Kafka",
+ "date": {
+ "year": 2021,
+ "month": "May"
+ },
+ "link": "https://videos.confluent.io/watch/hzjXP8QmLtYRNPukNFUu2D?"
+ },
+ {
+ "type": "article",
+ "title": "Lint AsyncAPI with Redocly CLI",
+ "date": {
+ "year": 2023,
+ "month": "August"
+ },
+ "link": "https://redocly.com/docs/cli/guides/lint-asyncapi"
+ }
+ ]
}
]
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index 76d5ab213..ffd3883db 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -49,7 +49,7 @@ For more details, please see the [CoC Committee and Incident Resolution Procedur
## Enforcement
-Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at condcut@asyncapi.io. All complaints will be reviewed and investigated promptly and fairly.
+Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at conduct@asyncapi.io. All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the reporter of any incident.
diff --git a/MAINTAINERS.yaml b/MAINTAINERS.yaml
index 29ad9ddb3..936d755ef 100644
--- a/MAINTAINERS.yaml
+++ b/MAINTAINERS.yaml
@@ -7,6 +7,9 @@
isTscMember: true
repos:
- website
+ - conference-website
+ - brand
+ githubID: 105395613
- name: Aayush Sahu
github: aayushmau5
linkedin: aayushmau5
@@ -16,6 +19,7 @@
isTscMember: true
repos:
- diff
+ githubID: 54525741
- name: Abir Pal
linkedin: imabp
slack: U01S8EQ9LQ2
@@ -25,6 +29,7 @@
isTscMember: true
repos:
- problem
+ githubID: 53480076
- name: Akshat Nema
github: akshatnema
linkedin: akshat-nema
@@ -34,6 +39,7 @@
isTscMember: true
repos:
- website
+ githubID: 76521428
- name: Ansh Goyal
github: anshgoyalevil
linkedin: thisisanshg
@@ -43,6 +49,7 @@
isTscMember: true
repos:
- website
+ githubID: 94157520
- name: Anand Sunderraman
github: anandsunderraman
linkedin: anand-sunderraman-a6b7a131
@@ -51,6 +58,7 @@
isTscMember: true
repos:
- go-watermill-template
+ githubID: 4500774
- name: Ashish Padhy
github: Shurtu-gal
linkedin: ashish-padhy3023
@@ -59,7 +67,10 @@
availableForHire: true
isTscMember: true
repos:
+ - studio
- github-action-for-cli
+ - cli
+ githubID: 100484401
- name: Cameron Rushton
github: CameronRushton
slack: U01DVKKAV5K
@@ -67,9 +78,11 @@
company: Solace
isTscMember: true
repos:
+ - spec-json-schemas
+ - bindings
- java-spring-cloud-stream-template
- python-paho-template
- - bindings
+ githubID: 32455969
- name: Dale Lane
github: dalelane
linkedin: dalelane
@@ -79,9 +92,12 @@
isTscMember: true
company: IBM
repos:
- - avro-schema-parser
+ - spec
+ - spec-json-schemas
- bindings
+ - avro-schema-parser
- java-template
+ githubID: 1444788
- name: Emiliano Zublena
github: emilianozublena
linkedin: emilianozublena
@@ -89,7 +105,8 @@
availableForHire: false
isTscMember: false
repos:
- - asyncapi-php-template
+ - php-template
+ githubID: 466639
- name: Fran Méndez
github: fmvilas
slack: U34F2JRRS
@@ -97,16 +114,21 @@
linkedin: fmvilas
isTscMember: true
repos:
- - raml-dt-schema-parser
- - avro-schema-parser
- - openapi-schema-parser
- - asyncapi-react
- - glee
- - nodejs-ws-template
- - parser-js
- spec
- spec-json-schemas
+ - asyncapi-react
+ - extensions-catalog
+ - converter-js
- bindings
+ - enterprise-patterns
+ - raml-dt-schema-parser
+ - openapi-schema-parser
+ - html-template
+ - markdown-template
+ - nodejs-ws-template
+ - generator-hooks
+ - brand
+ githubID: 242119
- name: Gerald Loeffler
github: GeraldLoeffler
linkedin: geraldloeffler
@@ -114,7 +136,9 @@
availableForHire: false
isTscMember: false
repos:
+ - spec-json-schemas
- bindings
+ githubID: 1985716
- name: Jonas Lagoni
github: jonaslagoni
linkedin: jonaslagoni
@@ -123,14 +147,16 @@
company: Postman
isTscMember: true
repos:
- - dotnet-nats-template
- - ts-nats-template
- - generator-react-sdk
+ - spec-json-schemas
- generator
- - modelina
- parser-js
- - parser-api
+ - converter-js
+ - generator-react-sdk
+ - modelina
- simulator
+ - parser-api
+ - EDAVisualiser
+ githubID: 13396189
- name: Khuda Dad Nomani
github: KhudaDad414
twitter: KhudaDadNomani
@@ -140,10 +166,12 @@
company: Postman
isTscMember: true
repos:
- - bindings
- - glee
+ - spec-json-schemas
+ - studio
- .github
- optimizer
+ - glee
+ githubID: 32505158
- name: Laurent Broudoux
github: lbroudoux
twitter: lbroudoux
@@ -153,7 +181,9 @@
company: Postman
isTscMember: true
repos:
+ - spec-json-schemas
- bindings
+ githubID: 1538635
- name: Ludovic Dussart
github: M3lkior
linkedin: ludovic-dussart-846a8063
@@ -164,6 +194,7 @@
isTscMember: true
repos:
- avro-schema-parser
+ githubID: 5501911
- name: Lukasz Gornicki
github: derberg
linkedin: lukasz-gornicki-a621914
@@ -173,17 +204,28 @@
company: Postman
isTscMember: true
repos:
- - diff
- - generator-filters
- - generator-hooks
- - github-action-for-generator
+ - spec
+ - website
+ - spec-json-schemas
- generator
+ - extensions-catalog
+ - bindings
+ - enterprise-patterns
+ - html-template
+ - markdown-template
- nodejs-template
- nodejs-ws-template
- - spec
- - spec-json-schemas
+ - java-spring-template
+ - github-action-for-cli
+ - .github
+ - jasyncapi
+ - generator-hooks
+ - vs-asyncapi-preview
- template-for-generator-templates
- - website
+ - community
+ - diff
+ - chatbot
+ githubID: 6995927
- name: Maciej Urbańczyk
github: magicmatatjahu
availableForHire: false
@@ -192,18 +234,27 @@
company: Travelping GmbH
isTscMember: true
repos:
+ - website
+ - generator
- asyncapi-react
+ - parser-go
+ - parser-js
+ - converter-js
- converter-go
- - generator-react-sdk
- - generator
+ - studio
- html-template
- markdown-template
+ - github-action-for-cli
+ - template-for-generator-templates
+ - generator-react-sdk
- modelina
- - parser-js
- - parser-go
- - server-api
- template-for-go-projects
- - website
+ - diff
+ - chatbot
+ - server-api
+ - EDAVisualiser
+ - problem
+ githubID: 20404945
- name: Azeez Elegbede
linkedin: acebuild
github: AceTheCreator
@@ -213,26 +264,21 @@
availableForHire: false
isTscMember: true
repos:
+ - asyncapi-react
+ - conference-website
- chatbot
+ githubID: 40604284
- name: Michael Davis
github: damaru-inc
availableForHire: false
slack: UH3B166TD
isTscMember: false
repos:
+ - spec-json-schemas
+ - bindings
- java-spring-cloud-stream-template
- python-paho-template
- - bindings
-- name: Missy Turco
- github: mcturco
- twitter: missyturco
- slack: U02JVEQ6S9W
- linkedin: missy-turco-a476a6126
- availableForHire: false
- company: Postman
- isTscMember: false
- repos:
- - brand
+ githubID: 3926925
- name: Nektarios Fifes
github: NektariosFifes
linkedin: nektarios-fifes-372740220
@@ -241,6 +287,7 @@
isTscMember: true
repos:
- simulator
+ githubID: 61620751
- name: Pavel Bodiachevskii
github: Pakisan
slack: U0132LQU8C9
@@ -248,7 +295,11 @@
availableForHire: false
isTscMember: true
repos:
+ - spec-json-schemas
+ - tck
- jasyncapi
+ - jasyncapi-idea-plugin
+ githubID: 3388414
- name: Philip Schlesinger
github: theschles
slack: U054UUYBNLF
@@ -257,6 +308,7 @@
isTscMember: true
repos:
- jasyncapi-idea-plugin
+ githubID: 901430
- name: Prince Rajpoot
github: princerajpoot20
linkedin: princerajpoot
@@ -266,6 +318,7 @@
isTscMember: true
repos:
- studio
+ githubID: 44585452
- name: Richard Coppen
github: rcoppen
linkedin: richard-coppen
@@ -274,7 +327,9 @@
company: IBM
isTscMember: true
repos:
+ - spec-json-schemas
- bindings
+ githubID: 30902631
- name: Samir AMZANI
github: Amzani
slack: U01N6AW5V5G
@@ -285,6 +340,8 @@
isTscMember: true
repos:
- studio
+ - cli
+ githubID: 554438
- name: Sergio Moya
github: smoya
linkedin: smoya
@@ -296,17 +353,18 @@
repos:
- spec
- spec-json-schemas
- - bindings
- - parser-api
- - parser-js
- - avro-schema-parser
- - openapi-schema-parser
- - raml-dt-schema-parser
- - server-api
- parser-go
+ - parser-js
- converter-go
+ - bindings
+ - raml-dt-schema-parser
+ - openapi-schema-parser
+ - avro-schema-parser
- go-watermill-template
- template-for-go-projects
+ - parser-api
+ - server-api
+ githubID: 1083296
- name: Souvik De
github: Souvikns
slack: U01SGCZMJKW
@@ -317,8 +375,9 @@
isTscMember: true
repos:
- cli
- - bundler
- glee
+ - bundler
+ githubID: 41781438
- name: Quetzalli Writes
github: quetzalliwrites
twitter: QuetzalliWrites
@@ -329,8 +388,7 @@
isTscMember: true
repos:
- website
- - training
- - community
+ githubID: 19964402
- name: David Pereira
github: BOLT04
twitter: BOLT2938
@@ -341,6 +399,7 @@
isTscMember: true
repos:
- server-api
+ githubID: 18630253
- name: Daniel Raper
github: dan-r
slack: U02FP8WBFQE
@@ -349,6 +408,7 @@
isTscMember: true
repos:
- java-template
+ githubID: 1384852
- name: Kieran Murphy
github: KieranM1999
linkedin: kieran-murphy-175b0412b
@@ -358,6 +418,7 @@
isTscMember: false
repos:
- java-template
+ githubID: 45017928
- name: Tom Jefferson
github: JEFFLUFC
linkedin: t-jefferson
@@ -367,6 +428,7 @@
isTscMember: false
repos:
- java-template
+ githubID: 54025356
- name: Lewis Relph
github: lewis-relph
availableForHire: false
@@ -375,6 +437,7 @@
isTscMember: false
repos:
- java-template
+ githubID: 91530893
- name: Semen Tenishchev
github: Tenischev
linkedin: semen-tenishchev
@@ -383,6 +446,7 @@
isTscMember: true
repos:
- java-spring-template
+ githubID: 4137916
- name: Samridhi Agrawal
github: Samridhi-98
slack: U02T2MY9W5T
@@ -392,16 +456,7 @@
isTscMember: true
repos:
- modelina
-- name: Debajyoti Halder
- github: ron-debajyoti
- twitter: rondebajyoti
- slack: U02UK9RUPGQ
- linkedin: rondebajyoti
- availableForHire: false
- company: Narvar
- isTscMember: false
- repos:
- - modelina
+ githubID: 54466041
- name: Ivan Garcia Sainz-Aja
github: ivangsa
linkedin: ivangarciasainzaja
@@ -411,6 +466,7 @@
isTscMember: true
repos:
- vs-asyncapi-preview
+ githubID: 1246876
- name: Florence Njeri
github: Florence-Njeri
linkedin: florencenjeri
@@ -420,6 +476,7 @@
isTscMember: true
repos:
- generator
+ githubID: 40742916
- name: Jeremy Whitlock
github: whitlockjc
linkedin: whitlockjc
@@ -429,7 +486,9 @@
company: Google
isTscMember: true
repos:
+ - spec-json-schemas
- bindings
+ githubID: 98899
- name: Vladimír Gorej
github: char0n
linkedin: vladimirgorej
@@ -439,18 +498,22 @@
company: SmartBear
isTscMember: false
repos:
- - bindings
- spec
- spec-json-schemas
+ - bindings
+ githubID: 193286
- name: Alexander Wichmann
- github: VisualBean
+ github: VisualBean
linkedin: alexcarlsen
slack: U04C58GB8TF
availableForHire: false
company: The LEGO Group
isTscMember: true
repos:
+ - spec-json-schemas
- bindings
+ - saunter
+ githubID: 5294032
- name: Kenneth Aasan
github: kennethaasan
slack: U037S2HK4TS
@@ -460,6 +523,7 @@
isTscMember: true
repos:
- modelina
+ githubID: 1437394
- name: Heiko Henning
github: GreenRover
slack: U03AC4G51H8
@@ -467,7 +531,11 @@
company: mtrail GmbH
isTscMember: true
repos:
+ - spec
+ - spec-json-schemas
+ - bindings
- protobuf-schema-parser
+ githubID: 512850
- name: connil
github: connil
slack: U03A51H8
@@ -476,6 +544,7 @@
isTscMember: false
repos:
- dotnet-rabbitmq-template
+ githubID: 6583798
- name: mr-nuno
github: mr-nuno
slack: U03A5145
@@ -484,6 +553,7 @@
isTscMember: false
repos:
- dotnet-rabbitmq-template
+ githubID: 1067841
- name: Thulisile Sibanda
github: thulieblack
linkedin: v-thulisile-sibanda
@@ -494,7 +564,9 @@
isTscMember: true
repos:
- website
+ - conference-website
- community
+ githubID: 66913810
- name: Ashmit JaiSarita Gupta
github: devilkiller-ag
linkedin: jaisarita
@@ -503,7 +575,9 @@
availableForHire: true
isTscMember: true
repos:
+ - website
- modelina
+ githubID: 43639341
- name: Sambhav Gupta
github: sambhavgupta0705
linkedin: sambhavgupta0705
@@ -513,11 +587,182 @@
isTscMember: true
repos:
- website
+ githubID: 81870866
- name: Viacheslav Turovskyi
github: aeworxet
slack: U01G3U01SVC
availableForHire: false
isTscMember: false
repos:
- - bundler
- optimizer
+ - bundler
+ githubID: 16149591
+- name: Rohit
+ github: TRohit20
+ twitter: TRRohit20
+ isTscMember: false
+ repos:
+ - website
+ githubID: 108233235
+- name: 'Bhaswati Roy '
+ github: BhaswatiRoy
+ twitter: swiftiebhaswati
+ isTscMember: false
+ repos:
+ - website
+ githubID: 78029145
+- name: 'Vaishnavi '
+ github: VaishnaviNandakumar
+ isTscMember: false
+ repos:
+ - website
+ githubID: 41518119
+- name: Joy Almeida
+ github: J0SAL
+ twitter: _j0sal
+ isTscMember: false
+ repos:
+ - website
+ githubID: 52382282
+- name: Mihael Bosnjak
+ github: mboss37
+ isTscMember: false
+ repos:
+ - spec-json-schemas
+ - bindings
+ githubID: 29606687
+- name: Steve Head
+ github: SrfHead
+ isTscMember: false
+ repos:
+ - spec-json-schemas
+ - bindings
+ githubID: 13767299
+- name: Dec Kolakowski
+ github: dpwdec
+ isTscMember: false
+ repos:
+ - spec-json-schemas
+ - bindings
+ githubID: 51292634
+- name: Ian Cooper
+ github: iancooper
+ twitter: ICooper
+ isTscMember: false
+ repos:
+ - spec-json-schemas
+ - bindings
+ githubID: 45537
+- name: Michael Wildman
+ github: m-wild
+ isTscMember: false
+ repos:
+ - saunter
+ githubID: 3260812
+- name: yurvon-screamo
+ github: yurvon-screamo
+ isTscMember: false
+ repos:
+ - saunter
+ githubID: 109030262
+- name: Jonathan Stoikovitch
+ github: jstoiko
+ twitter: jstoiko
+ isTscMember: false
+ repos:
+ - raml-dt-schema-parser
+ githubID: 9660342
+- name: Rishi
+ github: kaushik-rishi
+ twitter: KaushikRishi07
+ isTscMember: false
+ repos:
+ - nodejs-template
+ githubID: 52498617
+- name: Akshit Gupta
+ github: akkshitgupta
+ twitter: akkshitgupta
+ availableForHire: true
+ isTscMember: false
+ repos:
+ - modelina
+ githubID: 96991785
+- name: Leigh Johnson
+ github: leigh-johnson
+ twitter: grepLeigh
+ isTscMember: false
+ repos:
+ - modelina
+ githubID: 2601819
+- name: Zbigniew Malcherczyk
+ github: ferror
+ isTscMember: false
+ repos:
+ - modelina
+ githubID: 17534504
+- name: artur-ciocanu
+ github: artur-ciocanu
+ isTscMember: false
+ repos:
+ - modelina
+ githubID: 743192
+- name: Vinit Shahdeo
+ github: vinitshahdeo
+ twitter: Vinit_Shahdeo
+ availableForHire: true
+ isTscMember: false
+ repos:
+ - diff
+ githubID: 20594326
+- name: Anubhav Vats
+ github: onbit-uchenik
+ twitter: postmanlabs
+ availableForHire: true
+ isTscMember: false
+ repos:
+ - diff
+ githubID: 46771418
+- name: Akshaya Gurlhosur
+ github: AGurlhosur
+ isTscMember: false
+ repos:
+ - java-template
+ githubID: 91530186
+- name: Philip Schlesinger @ Cryoport
+ github: philCryoport
+ isTscMember: false
+ repos:
+ - jasyncapi-idea-plugin
+ githubID: 28901899
+- name: nathanaelweber
+ github: nathanaelweber
+ isTscMember: false
+ repos:
+ - protobuf-schema-parser
+ githubID: 40006685
+- name: Barbanio González
+ github: Barbanio
+ isTscMember: false
+ repos:
+ - learning-paths
+ githubID: 77982319
+- name: Lorenz Simon
+ github: lorenzsimon
+ isTscMember: false
+ repos:
+ - kotlin-asyncapi
+ githubID: 39913716
+- name: Jonas Süskind
+ github: sueskind
+ isTscMember: false
+ repos:
+ - kotlin-asyncapi
+ githubID: 52210599
+- name: Andrei
+ github: gimlet2
+ twitter: gimlet2
+ availableForHire: true
+ isTscMember: false
+ repos:
+ - kotlin-asyncapi
+ githubID: 758568
diff --git a/mentorship/asyncapi-mentorship/README.md b/mentorship/asyncapi-mentorship/README.md
index e5aabd0e0..f40873eca 100644
--- a/mentorship/asyncapi-mentorship/README.md
+++ b/mentorship/asyncapi-mentorship/README.md
@@ -5,7 +5,7 @@ The AsyncAPI Mentorship makes it easy to sponsor and help train the next generat
## Program Cycles and Archive data
| Year | Term | Status | Announcement | Details |
| ---- | ------ | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- |
-| 2023 | Jan-Nov | In progress | | [Jan-Nov](2023/README.md) |
+| 2023 | Jan-Nov | Completed | | [Jan-Nov](2023/README.md) |
| 2022 | Jan-Nov | Completed | | [Jan-Nov](2022/README.md) |
## Current cycle
@@ -134,4 +134,4 @@ AsyncAPI is not required to pay any stipends to any Participant that violates an
## References
-This document was adapted from the [GSOC Guidlines and Rules](https://summerofcode.withgoogle.com/rules).
\ No newline at end of file
+This document was adapted from the [GSOC Guidelines and Rules](https://summerofcode.withgoogle.com/rules).
diff --git a/tweets/recurring-discuss-ideas/2024-09-01.tweet b/tweets/recurring-discuss-ideas/2024-09-01.tweet
new file mode 100644
index 000000000..92b19fefe
--- /dev/null
+++ b/tweets/recurring-discuss-ideas/2024-09-01.tweet
@@ -0,0 +1,5 @@
+Do you have some nice ideas for #AsyncAPI-related tools? Do you want to validate and share with the AsyncAPI community?
+
+Drop it 👇 and let us have an open discussion 🚀
+
+https://github.com/asyncapi/community/discussions/categories/ideas
\ No newline at end of file
diff --git a/tweets/recurring-discuss-ideas/2024-10-01.tweet b/tweets/recurring-discuss-ideas/2024-10-01.tweet
new file mode 100644
index 000000000..92b19fefe
--- /dev/null
+++ b/tweets/recurring-discuss-ideas/2024-10-01.tweet
@@ -0,0 +1,5 @@
+Do you have some nice ideas for #AsyncAPI-related tools? Do you want to validate and share with the AsyncAPI community?
+
+Drop it 👇 and let us have an open discussion 🚀
+
+https://github.com/asyncapi/community/discussions/categories/ideas
\ No newline at end of file
diff --git a/tweets/recurring-slack-link/2024-08-17.tweet b/tweets/recurring-slack-link/2024-08-17.tweet
new file mode 100644
index 000000000..0bd3e8865
--- /dev/null
+++ b/tweets/recurring-slack-link/2024-08-17.tweet
@@ -0,0 +1,7 @@
+✨ Did you know #AsyncAPI is on Slack? ✨
+
+Join our Slack workspace to chat with anyone from our Open-Source community!
+
+🔗 asyncapi.com/slack-invite
+
+Ask for help and help others too. 💪🏿💪🏽🦾
\ No newline at end of file
diff --git a/tweets/recurring-slack-link/2024-08-24.tweet b/tweets/recurring-slack-link/2024-08-24.tweet
new file mode 100644
index 000000000..0bd3e8865
--- /dev/null
+++ b/tweets/recurring-slack-link/2024-08-24.tweet
@@ -0,0 +1,7 @@
+✨ Did you know #AsyncAPI is on Slack? ✨
+
+Join our Slack workspace to chat with anyone from our Open-Source community!
+
+🔗 asyncapi.com/slack-invite
+
+Ask for help and help others too. 💪🏿💪🏽🦾
\ No newline at end of file
diff --git a/tweets/recurring-slack-link/2024-08-31.tweet b/tweets/recurring-slack-link/2024-08-31.tweet
new file mode 100644
index 000000000..0bd3e8865
--- /dev/null
+++ b/tweets/recurring-slack-link/2024-08-31.tweet
@@ -0,0 +1,7 @@
+✨ Did you know #AsyncAPI is on Slack? ✨
+
+Join our Slack workspace to chat with anyone from our Open-Source community!
+
+🔗 asyncapi.com/slack-invite
+
+Ask for help and help others too. 💪🏿💪🏽🦾
\ No newline at end of file
diff --git a/tweets/recurring-slack-link/2024-09-07.tweet b/tweets/recurring-slack-link/2024-09-07.tweet
new file mode 100644
index 000000000..0bd3e8865
--- /dev/null
+++ b/tweets/recurring-slack-link/2024-09-07.tweet
@@ -0,0 +1,7 @@
+✨ Did you know #AsyncAPI is on Slack? ✨
+
+Join our Slack workspace to chat with anyone from our Open-Source community!
+
+🔗 asyncapi.com/slack-invite
+
+Ask for help and help others too. 💪🏿💪🏽🦾
\ No newline at end of file
diff --git a/tweets/recurring-slack-link/2024-09-14.tweet b/tweets/recurring-slack-link/2024-09-14.tweet
new file mode 100644
index 000000000..0bd3e8865
--- /dev/null
+++ b/tweets/recurring-slack-link/2024-09-14.tweet
@@ -0,0 +1,7 @@
+✨ Did you know #AsyncAPI is on Slack? ✨
+
+Join our Slack workspace to chat with anyone from our Open-Source community!
+
+🔗 asyncapi.com/slack-invite
+
+Ask for help and help others too. 💪🏿💪🏽🦾
\ No newline at end of file
diff --git a/tweets/recurring-slack-link/2024-09-21.tweet b/tweets/recurring-slack-link/2024-09-21.tweet
new file mode 100644
index 000000000..0bd3e8865
--- /dev/null
+++ b/tweets/recurring-slack-link/2024-09-21.tweet
@@ -0,0 +1,7 @@
+✨ Did you know #AsyncAPI is on Slack? ✨
+
+Join our Slack workspace to chat with anyone from our Open-Source community!
+
+🔗 asyncapi.com/slack-invite
+
+Ask for help and help others too. 💪🏿💪🏽🦾
\ No newline at end of file
diff --git a/tweets/recurring-slack-link/2024-09-28.tweet b/tweets/recurring-slack-link/2024-09-28.tweet
new file mode 100644
index 000000000..0bd3e8865
--- /dev/null
+++ b/tweets/recurring-slack-link/2024-09-28.tweet
@@ -0,0 +1,7 @@
+✨ Did you know #AsyncAPI is on Slack? ✨
+
+Join our Slack workspace to chat with anyone from our Open-Source community!
+
+🔗 asyncapi.com/slack-invite
+
+Ask for help and help others too. 💪🏿💪🏽🦾
\ No newline at end of file
diff --git a/tweets/recurring-slack-link/2024-10-05.tweet b/tweets/recurring-slack-link/2024-10-05.tweet
new file mode 100644
index 000000000..0bd3e8865
--- /dev/null
+++ b/tweets/recurring-slack-link/2024-10-05.tweet
@@ -0,0 +1,7 @@
+✨ Did you know #AsyncAPI is on Slack? ✨
+
+Join our Slack workspace to chat with anyone from our Open-Source community!
+
+🔗 asyncapi.com/slack-invite
+
+Ask for help and help others too. 💪🏿💪🏽🦾
\ No newline at end of file
diff --git a/tweets/recurring-slack-link/2024-10-12.tweet b/tweets/recurring-slack-link/2024-10-12.tweet
new file mode 100644
index 000000000..0bd3e8865
--- /dev/null
+++ b/tweets/recurring-slack-link/2024-10-12.tweet
@@ -0,0 +1,7 @@
+✨ Did you know #AsyncAPI is on Slack? ✨
+
+Join our Slack workspace to chat with anyone from our Open-Source community!
+
+🔗 asyncapi.com/slack-invite
+
+Ask for help and help others too. 💪🏿💪🏽🦾
\ No newline at end of file
diff --git a/tweets/recurring-slack-link/2024-10-19.tweet b/tweets/recurring-slack-link/2024-10-19.tweet
new file mode 100644
index 000000000..0bd3e8865
--- /dev/null
+++ b/tweets/recurring-slack-link/2024-10-19.tweet
@@ -0,0 +1,7 @@
+✨ Did you know #AsyncAPI is on Slack? ✨
+
+Join our Slack workspace to chat with anyone from our Open-Source community!
+
+🔗 asyncapi.com/slack-invite
+
+Ask for help and help others too. 💪🏿💪🏽🦾
\ No newline at end of file