Skip to content

Commit

Permalink
Add GitHub stats (#27)
Browse files Browse the repository at this point in the history
* Replace heroicons with Octicons

Heroicons don't match the octicon look for stars, forks and watchers,
but Octicons have equivalents for all the icons we're using from
heroicons, so we can replace them all.

* Pull stats from GitHub

Shows stars, watchers and forks next to the license text on each app
block. Changes the app sorting to star count.

* Schedule publication to run every night

Updates the stats for each repo.
  • Loading branch information
trond-snekvik authored Nov 10, 2023
1 parent c043cb7 commit c635cdd
Show file tree
Hide file tree
Showing 11 changed files with 86 additions and 77 deletions.
5 changes: 4 additions & 1 deletion .github/workflows/pages-deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ on:

# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
schedule:
# Run every night to get fresh GitHub stats:
- cron: 0 4 * * *

# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
Expand Down Expand Up @@ -80,4 +83,4 @@ jobs:
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v2
uses: actions/deploy-pages@v2
33 changes: 18 additions & 15 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
},
"homepage": "https://github.com/nrfconnect/ncs-app-index#readme",
"dependencies": {
"@heroicons/react": "^2.0.18",
"@octokit/rest": "^20.0.1",
"@primer/octicons-react": "^19.8.0",
"ajv": "^8.12.0",
"ansi-colors": "^4.1.3",
"classnames": "^2.3.2",
Expand Down
4 changes: 4 additions & 0 deletions resources/output_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,9 @@
"watchers": {
"type": "integer"
},
"stars": {
"type": "integer"
},
"forks": {
"type": "integer"
},
Expand All @@ -188,6 +191,7 @@
"tags",
"releases",
"watchers",
"stars",
"forks",
"defaultBranch",
"lastUpdate",
Expand Down
22 changes: 11 additions & 11 deletions scripts/generate-index-json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,26 +43,25 @@ function initialiseGitHubApi() {
return new Octokit({ request: { fetch }, auth: authToken });
}

/** Sort organizations by kind, then name */
function compareOrgs(a: { org: Organization }, b: { org: Organization }) {
const kinds: Organization['kind'][] = ['Nordic Semiconductor', 'Official Partner', 'External'];

return kinds.indexOf(b.org.kind) - kinds.indexOf(a.org.kind) || b.org.name < a.org.name
? -1
: 1;
}

const octokit = initialiseGitHubApi();

async function generateIndex(orgIndices: ParsedOrgFile[]): Promise<AppIndex> {
const appIndex: AppIndex = { orgs: {}, apps: [] };

const data = await Promise.all(orgIndices.map(fetchOrgData));
for (const { org, apps } of data.sort(compareOrgs).filter(notUndefined)) {
for (const { org, apps } of data.filter(notUndefined)) {
appIndex.orgs[org.id] = org;
appIndex.apps.push(...apps);
}

appIndex.apps = appIndex.apps.sort((a, b) => {
if (a.stars === b.stars) {
return a.name < b.name ? -1 : 1;
}

return a.stars > b.stars ? -1 : 1;
});

return appIndex;
}

Expand Down Expand Up @@ -133,12 +132,13 @@ async function fetchRepoData(
name: app.name,
title: app.title,
defaultBranch: repoData.default_branch,
forks: repoData.forks_count,
isTemplate: repoData.is_template ?? false,
kind: app.kind,
lastUpdate: repoData.updated_at,
license: app.license ?? repoData.license?.name ?? undefined,
watchers: repoData.watchers_count,
stars: repoData.stargazers_count,
forks: repoData.forks_count,
releases: releases.data.map((release) => ({
date: release.created_at,
name: release.name ?? release.tag_name,
Expand Down
2 changes: 0 additions & 2 deletions site/src/app/AboutDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
* SPDX-License-Identifier: BSD-3-Clause
*/

import { XMarkIcon } from '@heroicons/react/20/solid';

import { PropsWithChildren } from 'react';
import { DialogTitle } from './Dialog';

Expand Down
66 changes: 32 additions & 34 deletions site/src/app/AppBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@
import formatRelative from 'date-fns/formatRelative';
import classNames from 'classnames';
import Markdown from 'react-markdown';

import {
EnvelopeIcon,
CommandLineIcon,
ArrowTopRightOnSquareIcon,
CheckBadgeIcon,
ScaleIcon,
} from '@heroicons/react/20/solid';
EyeIcon,
LawIcon,
LinkExternalIcon,
MailIcon,
RepoForkedIcon,
StarIcon,
TerminalIcon,
VerifiedIcon,
} from '@primer/octicons-react';

import { NormalisedApp } from '../schema';
import VSCodeButton from './VSCodeButton';
Expand All @@ -25,8 +27,6 @@ interface Props {
}

function AppBlock({ app, setShowingAppId }: Props): JSX.Element {
const smallIconClass = 'w-5 h-5 md:w-4 md:h-4';

return (
<li className="flex w-full max-w-5xl flex-col gap-3 border border-gray-300 bg-white p-3 lg:w-2/3">
<div className="flex gap-3">
Expand All @@ -36,15 +36,10 @@ function AppBlock({ app, setShowingAppId }: Props): JSX.Element {
<div className="flex flex-wrap items-center justify-between">
<div className="flex items-center gap-2">
<div className="flex gap-3 md:block">
<img src={app.owner.avatar} className="h-12 w-12 md:hidden" />
<h1 className="text-xl text-gray-600">{app.title ?? app.name}</h1>
</div>
<a href={app.repo} target="_blank" title="Visit Website">
<ArrowTopRightOnSquareIcon
className="hoverable-icon"
width={20}
height={20}
/>
<LinkExternalIcon className="hoverable-icon" size={20} />
</a>
</div>

Expand All @@ -54,25 +49,21 @@ function AppBlock({ app, setShowingAppId }: Props): JSX.Element {
</div>

<div className="flex items-center gap-1">
<h2 className="text-md text-gray-600">
<h2 className="text-md text-gray-600" title={app.owner.kind}>
<a href={app.owner.urls.support}>{app.owner.name}</a>
</h2>

{app.owner.kind !== 'External' && (
<CheckBadgeIcon
title={app.owner.kind}
className={classNames(smallIconClass, {
<VerifiedIcon
className={classNames({
'text-[#00A9CE]': app.owner.kind === 'Nordic Semiconductor',
})}
/>
)}

{app.owner.urls.email && (
<a href={`mailto:${app.owner.urls.email}`}>
<EnvelopeIcon
title="Send Email"
className={classNames(smallIconClass, 'hoverable-icon')}
/>
<MailIcon className={classNames('hoverable-icon')} />
</a>
)}
</div>
Expand All @@ -94,20 +85,27 @@ function AppBlock({ app, setShowingAppId }: Props): JSX.Element {
className="button bg-[#768692] text-white"
onClick={() => setShowingAppId(app.id)}
>
Instructions <CommandLineIcon width={20} height={20} />
Instructions <TerminalIcon size={20} />
</button>
</div>
<div className="flex justify-between gap-4 text-xs text-gray-600">
<span className="flex gap-1">
{app.license && (
<>
<ScaleIcon className={smallIconClass} /> {app.license}
</>
)}
</span>
<span className="float-right font-thin italic">
<div className="flex w-full justify-between gap-3 text-xs text-gray-600">
<div className="flex items-center gap-1" title={`${app.stars} stars`}>
<StarIcon /> {app.stars}
</div>
<div className="flex items-center gap-1" title={`${app.watchers} watching`}>
<EyeIcon /> {app.watchers}
</div>
<div className="flex items-center gap-1" title={`${app.forks} forks`}>
<RepoForkedIcon /> {app.forks}
</div>
{app.license && (
<div className="flex items-center gap-1">
<LawIcon /> {app.license}
</div>
)}
<div className="flex-1 text-right font-thin italic">
Last updated {formatRelative(new Date(app.lastUpdate), new Date())}
</span>
</div>
</div>
</li>
);
Expand Down
4 changes: 2 additions & 2 deletions site/src/app/CodeBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
* SPDX-License-Identifier: BSD-3-Clause
*/

import { CheckIcon } from '@primer/octicons-react';
import { useState } from 'react';
import { CheckIcon } from '@heroicons/react/20/solid';

interface Props {
text: string;
Expand All @@ -28,7 +28,7 @@ function CodeBlock(props: Props): JSX.Element {
{isTextCopied ? (
<div className="flex items-center gap-1 text-green-800">
<span>Copied</span>
<CheckIcon className="" width={10} height={10} />
<CheckIcon />
</div>
) : (
<span>Copy to clipboard</span>
Expand Down
6 changes: 4 additions & 2 deletions site/src/app/Dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* SPDX-License-Identifier: BSD-3-Clause
*/

import { XMarkIcon } from '@heroicons/react/20/solid';
import { XIcon } from '@primer/octicons-react';
import { ForwardedRef, HTMLProps, PropsWithChildren, forwardRef } from 'react';

function Dialog(
Expand All @@ -27,7 +27,9 @@ export function DialogTitle(props: { title: string; onClose: () => void }) {
return (
<div className="flex items-center justify-between border-b border-gray-300 bg-gray-50 p-3 ">
<h1 className="text-xl text-gray-700">{props.title}</h1>
<XMarkIcon width={25} height={25} className="cursor-pointer" onClick={props.onClose} />
<div onClick={props.onClose}>
<XIcon size={25} className="cursor-pointer" />
</div>
</div>
);
}
Expand Down
17 changes: 8 additions & 9 deletions site/src/app/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
import Image from 'next/image';
import { ChangeEvent, Dispatch } from 'react';
import { FilterAction, Filters } from './filters';
import { QuestionMarkCircleIcon } from '@heroicons/react/24/solid';
import classNames from 'classnames';
import { QuestionIcon } from '@primer/octicons-react';

let contents = `static void gzll_tx_result_handler(struct gzll_tx_result *tx_result)a {
int err;
Expand Down Expand Up @@ -44,13 +44,12 @@ function Header(props: Props): JSX.Element {
}

const aboutIcon = (
<QuestionMarkCircleIcon
title="About"
onClick={props.showAboutDialog}
width={32}
height={32}
className="cursor-pointer text-white text-opacity-95 drop-shadow transition-opacity hover:text-opacity-100"
/>
<div title="About" onClick={props.showAboutDialog}>
<QuestionIcon
size={32}
className="cursor-pointer text-white text-opacity-95 drop-shadow transition-opacity hover:text-opacity-100"
/>
</div>
);

return (
Expand Down Expand Up @@ -101,7 +100,7 @@ function Header(props: Props): JSX.Element {
value={props.filters.textSearch}
onChange={handleTextSearchChange}
aria-label="Filter applications"
className="relative top-5 mx-4 h-14 w-full max-w-5xl p-3 pl-3 drop-shadow-md lg:mx-0 lg:w-2/3 outline-none"
className="relative top-5 mx-4 h-14 w-full max-w-5xl p-3 pl-3 outline-none drop-shadow-md lg:mx-0 lg:w-2/3"
/>
</div>
</div>
Expand Down
2 changes: 2 additions & 0 deletions site/src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ export const appSchema = {
minItems: 1,
},
watchers: { type: 'integer' },
stars: { type: 'integer' },
forks: { type: 'integer' },
defaultBranch: { type: 'string' },
lastUpdate: { type: 'string', format: 'date-time' },
Expand All @@ -183,6 +184,7 @@ export const appSchema = {
'tags',
'releases',
'watchers',
'stars',
'forks',
'defaultBranch',
'lastUpdate',
Expand Down

0 comments on commit c635cdd

Please sign in to comment.