Skip to content

Commit

Permalink
add attribution for contributors (#2030)
Browse files Browse the repository at this point in the history
- load contributor and team data from GitHub API
- add attribution for bots and contributors
- make use of `VPTeamPage` components
  • Loading branch information
JoCa96 authored Nov 20, 2024
1 parent 738c7a1 commit 8636711
Show file tree
Hide file tree
Showing 7 changed files with 218 additions and 81 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ node_modules/
.npmrc
**/.cache
**/.vitepress/cache
**/.vitepress/config.ts.timestamp-*.mjs
*.ts.timestamp*.mjs
*.tsbuildinfo
.turbo
vite.*.timestamp*
Expand Down
1 change: 1 addition & 0 deletions apps/docs/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { defineConfig, devices } from "@playwright/test";
*/
export default defineConfig({
testDir: "./tests",
testMatch: `**/*.ct.tsx?`,
snapshotDir: "./playwright/snapshots",
// custom snapshotPathTemplate to remove the testFileName folder that we don't want
snapshotPathTemplate: "{snapshotDir}/{testFileDir}/{arg}-{projectName}-{platform}{ext}",
Expand Down
62 changes: 62 additions & 0 deletions apps/docs/src/about/team.data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { defineLoader } from "vitepress";
import { executeGitHubRequest } from "../github-api";

/**
* Build-time data for the team page
*/
export type TeamPageData = {
/** All onyx contributors */
contributors: GithubContributor[];
};

declare const data: TeamPageData;
export { data };

/**
* Build-Time data loader to get the github data
* @see https://vitepress.dev/guide/data-loading
*/
export default defineLoader({
async load(): Promise<TeamPageData> {
return {
contributors: (await getContributors()) ?? [],
};
},
});

type GithubContributor = {
login: string;
id: number;
node_id: string;
avatar_url: string;
gravatar_id: string;
url: string;
html_url: string;
followers_url: string;
following_url: string;
gists_url: string;
starred_url: string;
subscriptions_url: string;
organizations_url: string;
repos_url: string;
events_url: string;
received_events_url: string;
type: "User" | "Bot";
user_view_type: string;
site_admin: false;
contributions: number;
};

/**
* Lists all github contributors
*/
const getContributors = async (): Promise<GithubContributor[]> => {
const body = await executeGitHubRequest(`repos/SchwarzIT/onyx/contributors`);

if (typeof body !== "object" && !Array.isArray(body)) {
throw new Error(
`GitHub contributor listing is not an array. Response body: ${JSON.stringify(body)}`,
);
}
return body;
};
161 changes: 117 additions & 44 deletions apps/docs/src/about/team.md
Original file line number Diff line number Diff line change
@@ -1,72 +1,145 @@
---
layout: page
---

<script lang="ts" setup>
import { VPTeamMembers } from 'vitepress/theme'
import { data } from "./team.data";
import {
VPTeamPage,
VPTeamPageTitle,
VPTeamMembers,
VPTeamPageSection
} from 'vitepress/theme';

const sortByContributions = (a, b) => b.contributions - a.contributions;
const sortByOverride = (a, b) => overrides.findIndex(o => o.login === a.login) - overrides.findIndex(o => o.login === b.login);


// https://vitepress.dev/reference/default-theme-team-page#show-team-members-in-a-page
const members = [
// Add your full name here, if you want it to be shown
const overrides = [
{
avatar: 'https://www.github.com/mj-hof.png',
login: "mj-hof",
name: 'Martin Hofmann',
core: true,
title: 'Product Owner',
links: [
{ icon: 'github', link: 'https://github.com/mj-hof' },
]
org: "Schwarz IT",
orgLink: "https://it.schwarz/"
},
{
avatar: 'https://www.github.com/jannick-ux.png',
login: "jannick-ux",
name: 'Jannick Keller',
title: 'Lead Designer',
links: [
{ icon: 'github', link: 'https://github.com/jannick-ux' },
]
core: true,
title: 'Design Lead',
org: "Schwarz IT",
orgLink: "https://it.schwarz/",
},
{
login: "flubnau",
name: 'Florian Lubnau',
core: true,
title: 'Designer',
org: "Schwarz IT",
orgLink: "https://it.schwarz/",
},
{
avatar: 'https://www.github.com/JoCa96.png',
login: "JoCa96",
name: 'Jonathan Leo Carle',
title: 'Lead Developer',
links: [
{ icon: 'github', link: 'https://github.com/JoCa96' },
]
core: true,
title: 'Engineering Lead',
org: "Schwarz IT",
orgLink: "https://it.schwarz/",
},
{
avatar: 'https://www.github.com/BoppLi.png',
login: "BoppLi",
name: 'Linda Bopp',
title: 'Developer',
links: [
{ icon: 'github', link: 'https://github.com/BoppLi' },
]
core: false,
org: "Schwarz IT",
orgLink: "https://it.schwarz/",
desc: "Former Core Member 🫡",
},
{
avatar: 'https://www.github.com/larsrickert.png',
login: "larsrickert",
name: 'Lars Rickert',
title: 'Developer',
links: [
{ icon: 'github', link: 'https://github.com/larsrickert' },
]
core: true,
title: 'Engineer',
org: "Schwarz IT",
orgLink: "https://it.schwarz/",
},
{
avatar: 'https://www.github.com/MajaZarkova.png',
login: "MajaZarkova",
name: 'Maja Zarkova',
title: 'Developer',
links: [
{ icon: 'github', link: 'https://github.com/MajaZarkova' },
]
core: true,
title: 'Engineer',
org: "Schwarz IT",
orgLink: "https://it.schwarz/",
},
{
avatar: 'https://www.github.com/ChristianBusshoff.png',
login: "ChristianBusshoff",
name: 'Christian Bußhoff',
title: 'Developer',
links: [
{ icon: 'github', link: 'https://github.com/ChristianBusshoff' },
]
core: true,
title: 'Engineer',
org: "Schwarz IT",
orgLink: "https://it.schwarz/",
},
]
</script>

# Meet the team 👋
{
login: "rhoggs-bot-test-account",
type: "Bot",
},
{
login: "oemueller",
name: "Oliver Müller",
org: "Schwarz IT",
orgLink: "https://it.schwarz/",
},
{
login: "markbrockhoff",
name: "Mark Brockhoff",
org: "Schwarz IT",
orgLink: "https://it.schwarz/",
}
];

onyx is maintained by [Schwarz IT](https://it.schwarz). Below you will find the members of our Core Team.
const mapped = data.contributors.map((c) => ({
...c,
avatar: c.avatar_url,
name: c.login,
links: [
{ icon: 'github', link: c.html_url },
],
...overrides.find(n => c.login === n.login)
}));

Are you looking for a bug report or feature request? Then please use our [GitHub issues](https://github.com/SchwarzIT/onyx/issues).
For general Q&A, announcements and polls feel free to visit our community space via [GitHub discussions](https://github.com/SchwarzIT/onyx/discussions/categories/q-a).
const coreMembers = mapped.filter(m => m.core).sort(sortByOverride);
const bots = mapped.filter(m => m.type === "Bot").sort(sortByContributions);
const contributors = mapped.filter(m => !bots.includes(m) && !coreMembers.includes(m)).sort(sortByContributions);
</script>

<VPTeamMembers size="small" :members="members" />
<main>
<VPTeamPage style="margin-top: 0;">
<VPTeamPageTitle>
<template #title>Meet the team 👋</template>
<template #lead>
onyx is maintained by a dedicated team at <a href="https://it.schwarz">Schwarz IT</a>. Below you will find the core members of our team.
<br><br>
Are you looking for a bug report or feature request?
<br><br>
Then please use our <a href="https://github.com/SchwarzIT/onyx/issues">GitHub issues</a>.
For general Q&A, announcements and polls feel free to visit our community space via <a href="https://github.com/SchwarzIT/onyx/discussions/categories/q-a">GitHub discussions</a>.
</template>
</VPTeamPageTitle>
<VPTeamMembers size="medium" :members="coreMembers" />
<VPTeamPageSection>
<template #title>Thank you to all contributors 🙏</template>
<template #members>
<VPTeamMembers size="small" :members="contributors" />
</template>
</VPTeamPageSection>
<VPTeamPageSection>
<template #title>Our hardworking bots 🤖</template>
<template #members>
<VPTeamMembers size="small" :members="bots" />
</template>
</VPTeamPageSection>
</VPTeamPage>
</main>
33 changes: 33 additions & 0 deletions apps/docs/src/github-api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* Executes a GET request to the given GitHub API route.
*
* @param apiRoute API route without "https://api.github.com/". Must not start with a trailing slash.
* @throws Error if API request was not successful
* @returns JSON response body.
*/
export const executeGitHubRequest = async (apiRoute: string) => {
// we only want to fetch the data from GitHub / npmjs API on build, not when running locally
// to improve the startup time and prevent rate limits
const skipGitHubFetch = process.env.VITEPRESS_SKIP_GITHUB_FETCH === "false";
if (skipGitHubFetch) {
return;
}

// GitHub token can be used to have a higher rate limit (useful if used in CI)
// see: https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api?apiVersion=2022-11-28#about-primary-rate-limits
const accessToken = process.env.VITEPRESS_GITHUB_ACCESS_TOKEN;

const response = await fetch(`https://api.github.com/${apiRoute}`, {
headers: {
"X-GitHub-Api-Version": "2022-11-28",
Authorization: accessToken ? `Bearer ${accessToken}` : "",
},
});
const body = await response.json();

if (response.status < 200 || response.status >= 300) {
throw new Error(`GitHub request failed. Response body: ${JSON.stringify(body)}`);
}

return body;
};
40 changes: 4 additions & 36 deletions apps/docs/src/index.data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import fs from "node:fs";
import { defineLoader } from "vitepress";
import type { ComponentCardProps } from "./.vitepress/components/ComponentCard.vue";
import { getOnyxNpmPackages } from "./.vitepress/utils";
import { executeGitHubRequest } from "./github-api";

/**
* Build-time data for the home page (components, facts/numbers etc.)
Expand Down Expand Up @@ -49,15 +50,9 @@ export default defineLoader({

const timestamp = new Date();

// we only want to fetch the data from GitHub / npmjs API on build, not when running locally
// to improve the startup time and prevent rate limits
const skipGitHubFetch = process.env.VITEPRESS_SKIP_GITHUB_FETCH === "true";

const downloads = skipGitHubFetch ? 0 : await getNpmDownloadCount(npmPackageNames);
const mergedPRCount = skipGitHubFetch ? 0 : await searchGitHub("issues", "type:pr is:merged");
const closedIssueCount = skipGitHubFetch
? 0
: await searchGitHub("issues", "type:issue is:closed");
const downloads = (await getNpmDownloadCount(npmPackageNames)) ?? 0;
const mergedPRCount = (await searchGitHub("issues", "type:pr is:merged")) ?? 0;
const closedIssueCount = (await searchGitHub("issues", "type:issue is:closed")) ?? 0;

/**
* Checks whether the given component is implemented (meaning a Storybook file exists).
Expand Down Expand Up @@ -305,30 +300,3 @@ const getNpmDownloadCount = async (packages: string[]): Promise<number> => {
const downloads = await Promise.all(promises);
return downloads.reduce((total, downloads) => total + downloads, 0);
};

/**
* Executes a GET request to the given GitHub API route.
*
* @param apiRoute API route without "https://api.github.com/". Must not start with a trailing slash.
* @throws Error if API request was not successful
* @returns JSON response body.
*/
const executeGitHubRequest = async (apiRoute: string) => {
// GitHub token can be used to have a higher rate limit (useful if used in CI)
// see: https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api?apiVersion=2022-11-28#about-primary-rate-limits
const accessToken = process.env.VITEPRESS_GITHUB_ACCESS_TOKEN;

const response = await fetch(`https://api.github.com/${apiRoute}`, {
headers: {
"X-GitHub-Api-Version": "2022-11-28",
Authorization: accessToken ? `Bearer ${accessToken}` : "",
},
});
const body = await response.json();

if (response.status < 200 || response.status >= 300) {
throw new Error(`GitHub request failed. Response body: ${JSON.stringify(body)}`);
}

return body;
};
File renamed without changes.

0 comments on commit 8636711

Please sign in to comment.