Skip to content

Commit

Permalink
Make GitHub token optional (#268)
Browse files Browse the repository at this point in the history
  • Loading branch information
mandrasch authored Oct 28, 2024
1 parent 0631d64 commit 36e9abc
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 69 deletions.
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.ddev/
17 changes: 12 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,6 @@ All commands are run from the root of the project, from a terminal:

### Local Development Setup

1. Run `cp .env.example .env` to create a `.env` file for environment variables. (Don’t check this in!)
2. Create a [classic GitHub access token](https://github.com/settings/tokens) with these scopes: `repo`, `read:org`, `read:user`, and `read:project`.
3. Paste the GitHub token after `.env`’s `GITHUB_TOKEN=`.

#### DDEV setup

DDEV already has all the dependencies included.
Expand All @@ -83,11 +79,22 @@ Check out the project in your favorite Node.js environment, ideally running [`nv

To generate a static copy of the site, run `npm run build`. The contents of the `dist/` folder are exactly what get [deployed to Cloudflare Pages](#build--deployment). You can preview locally by running `npm run preview` or using a tool like [`serve`](https://www.npmjs.com/package/serve).


#### Switching from Without DDEV to with DDEV

Make sure to delete your `node_modules/` directory and run `ddev npm install`. The change in architecture can create odd issues otherwise.

#### GitHub Token

This step is not required if you just want to contribute a blog post to ddev.com.

Contributors, sponsors, releases and more data about DDEV is retrieved dynamically from the GitHub API. To test this, please follow these steps:

1. Run `cp .env.example .env` to create a `.env` file for environment variables. (Don’t check this in!)
2. Create a [classic GitHub access token](https://github.com/settings/tokens) with these scopes: `repo`, `read:org`, `read:user`, and `read:project`.
3. Paste the GitHub token after `.env`’s `GITHUB_TOKEN=`.

There is a local `cache/` to reduce API calls.

## Managing Content

The site’s content lives in either `.astro` components that resemble souped-up HTML, or Markdown files organized into schema-validated [content collections](https://docs.astro.build/en/guides/content-collections/).
Expand Down
2 changes: 1 addition & 1 deletion src/components/RepoCard.astro
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const repoData = await getRepoDetails(name)
className="w-4 h-4 inline-block relative"
style={{ top: "-1px" }}
/>{" "}
{parseInt(repoData.stargazers_count).toLocaleString("en-US")}
{parseInt(repoData.stargazers_count || 0).toLocaleString("en-US")}
</div>
</div>
<p class="mt-2 text-sm opacity-60 px-3 pt-1 pb-4">
Expand Down
169 changes: 107 additions & 62 deletions src/lib/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,33 @@

import dotenv from "dotenv"
import fs2 from "fs"
import path from 'path'
import Slugger from 'github-slugger'
import { Octokit } from "octokit";
import path from "path"
import Slugger from "github-slugger"
import { Octokit } from "octokit"
import { GITHUB_REPO } from "./../const"

dotenv.config()

// Project-root-relative directory for temporary data used in local development
const DEVELOPMENT_CACHE_DIR = 'cache'
let octokitInstance: Octokit;
const DEVELOPMENT_CACHE_DIR = "cache"
let octokitInstance: Octokit

// Define variable if GITHUB_TOKEN is set and not empty
const githubTokenIsSet: boolean = (() => {
if (
process.env.hasOwnProperty("GITHUB_TOKEN") === false ||
process.env.GITHUB_TOKEN === ""
) {
// add warning for production builds
if (import.meta.env.MODE === "production") {
console.warn(
"GITHUB_TOKEN not set or empty. You can ignore this warning for local development."
)
}
return false
}
return true
})()

/**
* Returns an instance of Octokit, which uses the `GITHUB_TOKEN` environment
Expand All @@ -22,14 +39,14 @@ let octokitInstance: Octokit;
*/
const octokit = () => {
if (octokitInstance) {
return octokitInstance;
return octokitInstance
}

octokitInstance = new Octokit({
auth: process.env.GITHUB_TOKEN,
});
return octokitInstance;
})

return octokitInstance
}

/**
Expand All @@ -38,8 +55,8 @@ const octokit = () => {
* @returns string
*/
export function getSlug(value: string) {
const slugger = new Slugger();
return slugger.slug(value);
const slugger = new Slugger()
return slugger.slug(value)
}

/**
Expand All @@ -56,11 +73,15 @@ export function getCategoryUrl(name: string) {
* @returns response data
*/
export async function getSponsors() {
const cacheFilename = 'sponsors.json'
const cachedData = getCache(cacheFilename);
if (!githubTokenIsSet) {
return []
}

const cacheFilename = "sponsors.json"
const cachedData = getCache(cacheFilename)

if (cachedData) {
return cachedData;
return cachedData
}

const response = await octokit().graphql(`
Expand Down Expand Up @@ -106,11 +127,11 @@ export async function getSponsors() {
}
`)

const rfayData = response.user.sponsors.nodes;
const orgData = response.organization.sponsors.nodes;
const data = [ ...rfayData, ...orgData ];
const rfayData = response.user.sponsors.nodes
const orgData = response.organization.sponsors.nodes
const data = [...rfayData, ...orgData]

putCache(cacheFilename, JSON.stringify(data));
putCache(cacheFilename, JSON.stringify(data))

return data
}
Expand All @@ -121,27 +142,34 @@ export async function getSponsors() {
* @returns response data
*/
export async function getContributors(includeAnonymous = false) {
const cacheFilename = 'contributors.json'
const cachedData = getCache(cacheFilename);
if (!githubTokenIsSet) {
return []
}

let data;
const cacheFilename = "contributors.json"
const cachedData = getCache(cacheFilename)

let data

if (cachedData) {
data = cachedData;
data = cachedData
} else {
const response = await octokit().paginate(`GET https://api.github.com/repos/${GITHUB_REPO}/contributors`, {
anon: 1,
per_page: 100,
});

data = response;
putCache(cacheFilename, JSON.stringify(data));
const response = await octokit().paginate(
`GET https://api.github.com/repos/${GITHUB_REPO}/contributors`,
{
anon: 1,
per_page: 100,
}
)

data = response
putCache(cacheFilename, JSON.stringify(data))
}

if (!includeAnonymous) {
return data.filter((contributor) => {
return contributor.type !== 'Anonymous'
});
return contributor.type !== "Anonymous"
})
}

return data ?? []
Expand All @@ -150,25 +178,31 @@ export async function getContributors(includeAnonymous = false) {
/**
* Gets repository details from GitHub.
* https://docs.github.com/en/rest/repos/repos?apiVersion=2022-11-28#get-a-repository
*
*
* @param name The name of the repository, like `ddev/ddev`.
* @returns response data
*/
export async function getRepoDetails(name: string) {
const slug = name.replace('/', '-')
const cacheFilename = `repository-${slug}.json`;
const cachedData = getCache(cacheFilename);
if (!githubTokenIsSet) {
return []
}

const slug = name.replace("/", "-")
const cacheFilename = `repository-${slug}.json`
const cachedData = getCache(cacheFilename)

if (cachedData) {
return cachedData;
return cachedData
}

const response = await octokit().request(`GET https://api.github.com/repos/${name}`)
const data = response.data;
const response = await octokit().request(
`GET https://api.github.com/repos/${name}`
)
const data = response.data

putCache(cacheFilename, JSON.stringify(data));
putCache(cacheFilename, JSON.stringify(data))

return data;
return data
}

/**
Expand All @@ -178,30 +212,41 @@ export async function getRepoDetails(name: string) {
* @returns tag name
*/
export async function getLatestReleaseVersion(stable = true) {
if (!githubTokenIsSet) {
return []
}

let data = await getReleases()

if (stable) {
data = data.filter((release) => {
return !release.draft && !release.prerelease;
return !release.draft && !release.prerelease
})
}

return data[0].tag_name;
return data[0].tag_name
}

export async function getReleases() {
const cacheFilename = 'releases.json'
const cachedData = getCache(cacheFilename);
if (!githubTokenIsSet) {
return []
}

const cacheFilename = "releases.json"
const cachedData = getCache(cacheFilename)

if (cachedData) {
return cachedData;
return cachedData
}

const response = await octokit().paginate(`GET https://api.github.com/repos/${GITHUB_REPO}/releases`, {
per_page: 100,
});
const response = await octokit().paginate(
`GET https://api.github.com/repos/${GITHUB_REPO}/releases`,
{
per_page: 100,
}
)

putCache(cacheFilename, JSON.stringify(response));
putCache(cacheFilename, JSON.stringify(response))

return response ?? []
}
Expand All @@ -212,12 +257,12 @@ export async function getReleases() {
* @returns file contents or null
*/
const getCache = (filename: string) => {
const dir = path.resolve('./' + DEVELOPMENT_CACHE_DIR)
const filePath = dir + '/' + filename
const dir = path.resolve("./" + DEVELOPMENT_CACHE_DIR)
const filePath = dir + "/" + filename

if (fs2.existsSync(filePath)) {
const contents = fs2.readFileSync(filePath);
return JSON.parse(contents);
const contents = fs2.readFileSync(filePath)
return JSON.parse(contents)
}

return
Expand All @@ -229,11 +274,11 @@ const getCache = (filename: string) => {
* @param contents Contents of the file.
*/
const putCache = (filename: string, contents: string) => {
const dir = path.resolve('./' + DEVELOPMENT_CACHE_DIR)
const filePath = dir + '/' + filename
const dir = path.resolve("./" + DEVELOPMENT_CACHE_DIR)
const filePath = dir + "/" + filename

if (!fs2.existsSync(dir)) {
fs2.mkdirSync(dir);
fs2.mkdirSync(dir)
}

fs2.writeFileSync(filePath, contents)
Expand All @@ -249,15 +294,15 @@ const putCache = (filename: string, contents: string) => {
export const formatDate = (date: string, customOptions?: object) => {
const pubDate = new Date(date)
const defaultOptions = {
timeZone: "UTC",
month: "short",
day: "numeric",
year: "numeric",
timeZone: "UTC",
month: "short",
day: "numeric",
year: "numeric",
}

const options = {
...defaultOptions,
...customOptions
...customOptions,
}

return new Intl.DateTimeFormat("en-US", options).format(pubDate)
Expand Down
2 changes: 1 addition & 1 deletion src/pages/about.astro
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ const releaseData = await getReleases()
<li class="p-6 lg:p-0">
<span class="text-5xl font-thin"
>{
parseInt(repoData.stargazers_count).toLocaleString("en-US")
parseInt(repoData.stargazers_count || 0).toLocaleString("en-US")
}</span
>
<span class="block font-mono text-sm">GitHub stars</span>
Expand Down

0 comments on commit 36e9abc

Please sign in to comment.