From 565ce020e15a766400475078c78df9b76c652083 Mon Sep 17 00:00:00 2001
From: Matthias Andrasch <777278+mandrasch@users.noreply.github.com>
Date: Fri, 25 Oct 2024 17:15:41 +0200
Subject: [PATCH 1/5] update readme to indicate GITHUB_TOKEN is optional for
local dev / PRs
---
README.md | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/README.md b/README.md
index e2f2a365..bdc632d5 100644
--- a/README.md
+++ b/README.md
@@ -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.
@@ -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/).
From 766980edc802b7074607f7a0e78cc142b0d6eda6 Mon Sep 17 00:00:00 2001
From: Matthias Andrasch <777278+mandrasch@users.noreply.github.com>
Date: Fri, 25 Oct 2024 17:15:51 +0200
Subject: [PATCH 2/5] add githubTokenIsSet as const to api.ts
---
src/lib/api.ts | 37 +++++++++++++++++++++++++++++++++++++
1 file changed, 37 insertions(+)
diff --git a/src/lib/api.ts b/src/lib/api.ts
index 571d03d6..fc6cb0ed 100644
--- a/src/lib/api.ts
+++ b/src/lib/api.ts
@@ -15,6 +15,18 @@ dotenv.config()
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
* variable for authentication.
@@ -56,6 +68,11 @@ export function getCategoryUrl(name: string) {
* @returns response data
*/
export async function getSponsors() {
+
+ if(!githubTokenIsSet){
+ return [];
+ }
+
const cacheFilename = 'sponsors.json'
const cachedData = getCache(cacheFilename);
@@ -121,6 +138,11 @@ export async function getSponsors() {
* @returns response data
*/
export async function getContributors(includeAnonymous = false) {
+
+ if(!githubTokenIsSet){
+ return [];
+ }
+
const cacheFilename = 'contributors.json'
const cachedData = getCache(cacheFilename);
@@ -155,6 +177,11 @@ export async function getContributors(includeAnonymous = false) {
* @returns response data
*/
export async function getRepoDetails(name: string) {
+
+ if(!githubTokenIsSet){
+ return [];
+ }
+
const slug = name.replace('/', '-')
const cacheFilename = `repository-${slug}.json`;
const cachedData = getCache(cacheFilename);
@@ -178,6 +205,11 @@ export async function getRepoDetails(name: string) {
* @returns tag name
*/
export async function getLatestReleaseVersion(stable = true) {
+
+ if(!githubTokenIsSet){
+ return [];
+ }
+
let data = await getReleases()
if (stable) {
@@ -190,6 +222,11 @@ export async function getLatestReleaseVersion(stable = true) {
}
export async function getReleases() {
+
+ if(!githubTokenIsSet){
+ return [];
+ }
+
const cacheFilename = 'releases.json'
const cachedData = getCache(cacheFilename);
From f569383e7bd3aef6864c10fdabbb5744603ae88e Mon Sep 17 00:00:00 2001
From: Matthias Andrasch <777278+mandrasch@users.noreply.github.com>
Date: Fri, 25 Oct 2024 18:01:20 +0200
Subject: [PATCH 3/5] fix NaN if GITHUB_TOKEN is not set
---
src/components/RepoCard.astro | 2 +-
src/pages/about.astro | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/components/RepoCard.astro b/src/components/RepoCard.astro
index dc92670f..ae2075cb 100644
--- a/src/components/RepoCard.astro
+++ b/src/components/RepoCard.astro
@@ -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")}
diff --git a/src/pages/about.astro b/src/pages/about.astro
index 5b3f50fd..5a73a432 100644
--- a/src/pages/about.astro
+++ b/src/pages/about.astro
@@ -96,7 +96,7 @@ const releaseData = await getReleases()
{
- parseInt(repoData.stargazers_count).toLocaleString("en-US")
+ parseInt(repoData.stargazers_count || 0).toLocaleString("en-US")
}
GitHub stars
From 2015163268895fe6da54f14acf6c818deb63b276 Mon Sep 17 00:00:00 2001
From: Matthias Andrasch <777278+mandrasch@users.noreply.github.com>
Date: Fri, 25 Oct 2024 19:08:05 +0200
Subject: [PATCH 4/5] re-format with prettier
---
src/lib/api.ts | 174 ++++++++++++++++++++++++++-----------------------
1 file changed, 91 insertions(+), 83 deletions(-)
diff --git a/src/lib/api.ts b/src/lib/api.ts
index fc6cb0ed..7c2d75ba 100644
--- a/src/lib/api.ts
+++ b/src/lib/api.ts
@@ -4,28 +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 === ''){
+ 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.');
+ 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 false
}
- return true;
-})();
+ return true
+})()
/**
* Returns an instance of Octokit, which uses the `GITHUB_TOKEN` environment
@@ -34,14 +39,14 @@ const githubTokenIsSet: boolean = (() => {
*/
const octokit = () => {
if (octokitInstance) {
- return octokitInstance;
+ return octokitInstance
}
octokitInstance = new Octokit({
auth: process.env.GITHUB_TOKEN,
- });
-
- return octokitInstance;
+ })
+
+ return octokitInstance
}
/**
@@ -50,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)
}
/**
@@ -68,16 +73,15 @@ export function getCategoryUrl(name: string) {
* @returns response data
*/
export async function getSponsors() {
-
- if(!githubTokenIsSet){
- return [];
+ if (!githubTokenIsSet) {
+ return []
}
- const cacheFilename = 'sponsors.json'
- const cachedData = getCache(cacheFilename);
+ const cacheFilename = "sponsors.json"
+ const cachedData = getCache(cacheFilename)
if (cachedData) {
- return cachedData;
+ return cachedData
}
const response = await octokit().graphql(`
@@ -123,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
}
@@ -138,32 +142,34 @@ export async function getSponsors() {
* @returns response data
*/
export async function getContributors(includeAnonymous = false) {
-
- if(!githubTokenIsSet){
- return [];
+ if (!githubTokenIsSet) {
+ return []
}
- const cacheFilename = 'contributors.json'
- const cachedData = getCache(cacheFilename);
+ const cacheFilename = "contributors.json"
+ const cachedData = getCache(cacheFilename)
- let data;
+ 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 ?? []
@@ -172,30 +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) {
-
- if(!githubTokenIsSet){
- return [];
+ if (!githubTokenIsSet) {
+ return []
}
- const slug = name.replace('/', '-')
- const cacheFilename = `repository-${slug}.json`;
- const cachedData = getCache(cacheFilename);
+ 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
}
/**
@@ -205,40 +212,41 @@ export async function getRepoDetails(name: string) {
* @returns tag name
*/
export async function getLatestReleaseVersion(stable = true) {
-
- if(!githubTokenIsSet){
- return [];
+ 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() {
-
- if(!githubTokenIsSet){
- return [];
+ if (!githubTokenIsSet) {
+ return []
}
- const cacheFilename = 'releases.json'
- const cachedData = getCache(cacheFilename);
+ 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 ?? []
}
@@ -249,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
@@ -266,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)
@@ -286,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)
From 8e243248308ee97bcad047e00623f3ad3ec34366 Mon Sep 17 00:00:00 2001
From: Matthias Andrasch <777278+mandrasch@users.noreply.github.com>
Date: Fri, 25 Oct 2024 19:08:15 +0200
Subject: [PATCH 5/5] add .ddev/ to prettierignore
---
.prettierignore | 1 +
1 file changed, 1 insertion(+)
create mode 100644 .prettierignore
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 00000000..3a4f5ccf
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1 @@
+.ddev/