From 4f345e651165d64de38ff0e1ac6338ca1dff5f6f Mon Sep 17 00:00:00 2001 From: babblebey Date: Sat, 4 Oct 2025 22:18:52 +0100 Subject: [PATCH 1/6] feat: implement GitHub App registration flow with HTML content and environment setup --- dev/setup/index.js | 32 +++ dev/setup/register-github-app/content.html.js | 227 ++++++++++++++++++ dev/setup/register-github-app/index.d.ts | 64 +++++ dev/setup/register-github-app/index.js | 98 ++++++++ 4 files changed, 421 insertions(+) create mode 100644 dev/setup/index.js create mode 100644 dev/setup/register-github-app/content.html.js create mode 100644 dev/setup/register-github-app/index.d.ts create mode 100644 dev/setup/register-github-app/index.js diff --git a/dev/setup/index.js b/dev/setup/index.js new file mode 100644 index 0000000..2a706fd --- /dev/null +++ b/dev/setup/index.js @@ -0,0 +1,32 @@ +import crypto from "node:crypto"; +import fs from "node:fs/promises"; +import registerGitHubApp from "./register-github-app/index.js"; + +// register app and retrieve credentials +const appCredentials = await registerGitHubApp({ + // name of your app + name: "jargons.dev-app-for-", + url: "https://github.com/jargonsdev/jargons.dev/CONTRIBUTING.md", + default_permissions: { + issues: "write", + }, +}); + +// convert private key to pkcs8 format (recommended for better cross plattform support) +const privateKeyPKCS8 = String( + crypto.createPrivateKey(appCredentials.pem).export({ + type: "pkcs8", + format: "pem", + }), +); +const singleLinePrivateKey = privateKeyPKCS8.trim().replace(/\n/g, "\\n"); + +// write credentials into `.env` file +const envFileTemp = await fs.readFile(".env.example", "utf-8"); +const newEnvFileContent = envFileTemp + .replace(/^GITHUB_APP_ID=.*$/m, `GITHUB_APP_ID=${appCredentials.id}`) + .replace( + /^GITHUB_APP_PRIVATE_KEY=.*$/m, + `GITHUB_APP_PRIVATE_KEY="${singleLinePrivateKey}"`, + ); +await fs.writeFile(".env", newEnvFileContent); diff --git a/dev/setup/register-github-app/content.html.js b/dev/setup/register-github-app/content.html.js new file mode 100644 index 0000000..f4d7afa --- /dev/null +++ b/dev/setup/register-github-app/content.html.js @@ -0,0 +1,227 @@ +const headEl = ` + + + + + jargons.dev Contributor Setup + + +` + +/** + * Generate the start page HTML content + * @param {string} registerUrl + * @param {string} manifestJson + * @returns + */ +export function getStartPage(registerUrl, manifestJson) { + return `${headEl} + +
+

Setup

+

Welcome to the jargons.dev Local development setup guide

+
    +
  1. +

    Start by Registering your GitHub App

    +

    Click the button below to submit your app manifest to GitHub and start the registration process.

    +

    You will be directed to the GitHub

    +
    + + +
    +
  2. +
+
+ Need help? See the dev/setup guide. +
+
+ +`; +} + +/** + * Generate the next step page HTML content + * @param {import('../index').AppCredentials} appCredentials + * @returns + */ +export function getNextStepPage(appCredentials) { + return `${headEl} + +
+

Setup

+

Welcome to the jargons.dev Local development setup guide

+
    +
  1. +

    Registering your GitHub App

    +
    + + + + GitHub App created +
    +
    + + + + .env file created +
    +
    + + + + GitHub App details added to .env file +
    +
  2. +
  3. +

    Create Your Test Repository

    +

    Create a new github repository with name "jargons.dev-test"

    + Create Test Repo +
  4. +
  5. +

    Update Your .env File

    +

    + Copy and paste the repo name in full as value to the PUBLIC_PROJECT_REPO in the .env file created in step 1
    +

    + Example (assuming you chose the suggested name, your .env file should have the line below) +
    PUBLIC_PROJECT_REPO="${appCredentials.owner.login}/jargons.dev-test"
    +
  6. +
  7. +

    Install the GitHub App

    +

    + Follow the link provided after registration to install the app on your test repository. +

    + Install \`${appCredentials.name}\` GitHub App +
  8. +
+
+ Need help? See the dev/setup guide. +
+
+`; +} \ No newline at end of file diff --git a/dev/setup/register-github-app/index.d.ts b/dev/setup/register-github-app/index.d.ts new file mode 100644 index 0000000..9ab6cb9 --- /dev/null +++ b/dev/setup/register-github-app/index.d.ts @@ -0,0 +1,64 @@ +import { request } from "@octokit/request"; +import { components } from "@octokit/openapi-types"; + +export default function registerGitHubApp( + manifest?: Manifest, + metaOptions?: MetaOptions, +): Promise; + +export type Manifest = { + /** Organization login. If not set, the app will be registered on the user's account */ + org?: string; + /** The name of the GitHub App. */ + name?: string; + /** A description of the GitHub App. */ + description?: string; + /** The homepage of your GitHub App. If `org` is set, `homepageUrl` will default to `https://github.com/{org}`, otherwise to `https://github.com` */ + url: string; + /** The configuration of the GitHub App's webhook. */ + hook_attributes?: { + /* + * __Required.__ The URL of the server that will receive the webhook POST requests. + */ + url: string; + /* + * Deliver event details when this hook is triggered, defaults to true. + */ + active?: boolean; + }; + /** The full URL to redirect to after a user initiates the registration of a GitHub App from a manifest. */ + redirect_url?: string; + /** A full URL to redirect to after someone authorizes an installation. You can provide up to 10 callback URLs. */ + callback_urls?: string[]; + /** A full URL to redirect users to after they install your GitHub App if additional setup is required. */ + setup_url?: string; + /** Set to `true` when your GitHub App is available to the public or `false` when it is only accessible to the owner of the app. */ + public?: boolean; + /** The list of events the GitHub App subscribes to. */ + default_events?: string[]; + /** The set of permissions needed by the GitHub App. The format of the object uses the permission name for the key (for example, `issues`) and the access type for the value (for example, `write`). */ + default_permissions?: Record; + /** Set to `true` to request the user to authorize the GitHub App, after the GitHub App is installed. */ + request_oauth_on_install?: boolean; + /** Set to `true` to redirect users to the setup_url after they update your GitHub App installation. */ + setup_on_update?: boolean; +}; + +export type MetaOptions = { + /** Port number for local server. Defaults to a random available port number */ + port?: number; + + /** GitHub website URL. Defaults to `https://github.com` */ + githubUrl?: string; + + /** GitHub API base URL. Defaults to `https://api.github.com` */ + githubApiUrl?: string; + + /** message to be used for logging. Defaults to `console.log` */ + log?: Console["log"]; + + /** custom `octokit.request` method */ + request?: typeof request; +}; + +export type AppCredentials = components["schemas"]["integration"]; diff --git a/dev/setup/register-github-app/index.js b/dev/setup/register-github-app/index.js new file mode 100644 index 0000000..277acc8 --- /dev/null +++ b/dev/setup/register-github-app/index.js @@ -0,0 +1,98 @@ +/** + * Register a GitHub App via a local server and the GitHub App Manifest flow + * @uses https://github.com/gr2m/register-github-app + * + * @ts-check + */ + +import { createServer } from "node:http"; +import { request as octokitRequest } from "@octokit/request"; +import { getStartPage, getNextStepPage } from "./content.html.js"; + +const DEFAULT_MANIFEST = { + url: "https://github.com", +}; +const DEFAULT_META_OPTIONS = { + githubUrl: "https://github.com", + githubApiUrl: "https://api.github.com", + log: console.log.bind(console), +}; + +/** + * @param {import('../index').Manifest} manifest + * @param {import('../index').MetaOptions} metaOptions + * @returns {Promise} + */ +export default async function registerGitHubApp( + { org, ...manifest } = DEFAULT_MANIFEST, + metaOptions = DEFAULT_META_OPTIONS, +) { + // defaults + manifest.url ||= org ? `https://github.com/${org}` : "https://github.com"; + manifest.public ||= false; + manifest.setup_on_update ||= false; + manifest.request_oauth_on_install ||= false; + + metaOptions.githubUrl ||= DEFAULT_META_OPTIONS.githubUrl; + metaOptions.githubApiUrl ||= DEFAULT_META_OPTIONS.githubApiUrl; + + const log = metaOptions.log || DEFAULT_META_OPTIONS.log; + + const manifestRequest = (metaOptions.request || octokitRequest).defaults({ + baseUrl: metaOptions.githubApiUrl, + method: "POST", + url: "/app-manifests/{code}/conversions", + }); + + return new Promise((resolve, reject) => { + // start the server at an available port + const server = createServer(); + + server.listen(metaOptions.port || 0); + + const port = + metaOptions.port || + // @ts-expect-error - I have yet to see a usecase when `server.address()` can be a string + server.address().port; + + log(`Open http://localhost:${port} in the browser to get started`); + + server.on("error", (error) => { + reject(new Error("A server error occured", { cause: error })); + server.close(); + }); + + // Listen to the request event + server.on("request", async (request, response) => { + const url = new URL(request.url, `http://localhost:${port}`); + + const code = url.searchParams.get("code"); + if (code) { + const { data: appCredentials } = await manifestRequest({ + code, + }); + + console.log(appCredentials); + + response.writeHead(200, { "Content-Type": "text/html" }); + response.end(getNextStepPage(appCredentials)); + + resolve(appCredentials); + + server.close(); + return; + } + + const registerUrl = org + ? `${metaOptions.githubUrl}/organizations/${org}/settings/apps/new` + : `${metaOptions.githubUrl}/settings/apps/new`; + const manifestJson = JSON.stringify({ + redirect_url: `http://localhost:${port}`, + ...manifest, + }); + + response.writeHead(200, { "Content-Type": "text/html" }); + response.end(getStartPage(registerUrl, manifestJson)); + }); + }); +} From 406b96660bf0eb95f6a116460186756c45d2496d Mon Sep 17 00:00:00 2001 From: babblebey Date: Sat, 4 Oct 2025 22:19:20 +0100 Subject: [PATCH 2/6] fix: update setup script path in package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d5d235f..beef2fc 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "test:ui": "vitest --ui", "test:watch": "vitest", "test:coverage": "vitest run --coverage", - "setup": "node dev/setup.js", + "setup": "node dev/setup/index.js", "seed:jai": "node --env-file=.env dev/seed-vector-store.js", "ping:qdrant": "node --env-file=.env dev/ping-qdrant-cluster.js", "ping:qdrant:ci": "node dev/ping-qdrant-cluster.js" From baa7f74bade9637f64601730f9db68b9fed73e06 Mon Sep 17 00:00:00 2001 From: babblebey Date: Sat, 4 Oct 2025 22:22:12 +0100 Subject: [PATCH 3/6] refactor: remove GitHub App registration implementation and related setup script --- dev/lib/register-github-app/index.d.ts | 64 ------------ dev/lib/register-github-app/index.js | 139 ------------------------- dev/setup.js | 32 ------ 3 files changed, 235 deletions(-) delete mode 100644 dev/lib/register-github-app/index.d.ts delete mode 100644 dev/lib/register-github-app/index.js delete mode 100644 dev/setup.js diff --git a/dev/lib/register-github-app/index.d.ts b/dev/lib/register-github-app/index.d.ts deleted file mode 100644 index 9ab6cb9..0000000 --- a/dev/lib/register-github-app/index.d.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { request } from "@octokit/request"; -import { components } from "@octokit/openapi-types"; - -export default function registerGitHubApp( - manifest?: Manifest, - metaOptions?: MetaOptions, -): Promise; - -export type Manifest = { - /** Organization login. If not set, the app will be registered on the user's account */ - org?: string; - /** The name of the GitHub App. */ - name?: string; - /** A description of the GitHub App. */ - description?: string; - /** The homepage of your GitHub App. If `org` is set, `homepageUrl` will default to `https://github.com/{org}`, otherwise to `https://github.com` */ - url: string; - /** The configuration of the GitHub App's webhook. */ - hook_attributes?: { - /* - * __Required.__ The URL of the server that will receive the webhook POST requests. - */ - url: string; - /* - * Deliver event details when this hook is triggered, defaults to true. - */ - active?: boolean; - }; - /** The full URL to redirect to after a user initiates the registration of a GitHub App from a manifest. */ - redirect_url?: string; - /** A full URL to redirect to after someone authorizes an installation. You can provide up to 10 callback URLs. */ - callback_urls?: string[]; - /** A full URL to redirect users to after they install your GitHub App if additional setup is required. */ - setup_url?: string; - /** Set to `true` when your GitHub App is available to the public or `false` when it is only accessible to the owner of the app. */ - public?: boolean; - /** The list of events the GitHub App subscribes to. */ - default_events?: string[]; - /** The set of permissions needed by the GitHub App. The format of the object uses the permission name for the key (for example, `issues`) and the access type for the value (for example, `write`). */ - default_permissions?: Record; - /** Set to `true` to request the user to authorize the GitHub App, after the GitHub App is installed. */ - request_oauth_on_install?: boolean; - /** Set to `true` to redirect users to the setup_url after they update your GitHub App installation. */ - setup_on_update?: boolean; -}; - -export type MetaOptions = { - /** Port number for local server. Defaults to a random available port number */ - port?: number; - - /** GitHub website URL. Defaults to `https://github.com` */ - githubUrl?: string; - - /** GitHub API base URL. Defaults to `https://api.github.com` */ - githubApiUrl?: string; - - /** message to be used for logging. Defaults to `console.log` */ - log?: Console["log"]; - - /** custom `octokit.request` method */ - request?: typeof request; -}; - -export type AppCredentials = components["schemas"]["integration"]; diff --git a/dev/lib/register-github-app/index.js b/dev/lib/register-github-app/index.js deleted file mode 100644 index 8aaf92f..0000000 --- a/dev/lib/register-github-app/index.js +++ /dev/null @@ -1,139 +0,0 @@ -/** - * Register a GitHub App using the manifest flow - * @see https://github.com/gr2m/register-github-app - * - * @ts-check - */ - -import { createServer } from "node:http"; - -import { request as octokitRequest } from "@octokit/request"; - -const DEFAULT_MANIFEST = { - url: "https://github.com", -}; -const DEFAULT_META_OPTIONS = { - githubUrl: "https://github.com", - githubApiUrl: "https://api.github.com", - log: console.log.bind(console), -}; - -/** - * @param {import('./index').Manifest} manifest - * @param {import('./index').MetaOptions} metaOptions - * @returns {Promise} - */ -export default async function registerGitHubApp( - { org, ...manifest } = DEFAULT_MANIFEST, - metaOptions = DEFAULT_META_OPTIONS, -) { - // defaults - manifest.url ||= org ? `https://github.com/${org}` : "https://github.com"; - manifest.public ||= false; - manifest.setup_on_update ||= false; - manifest.request_oauth_on_install ||= false; - - metaOptions.githubUrl ||= DEFAULT_META_OPTIONS.githubUrl; - metaOptions.githubApiUrl ||= DEFAULT_META_OPTIONS.githubApiUrl; - - const log = metaOptions.log || DEFAULT_META_OPTIONS.log; - - const manifestRequest = (metaOptions.request || octokitRequest).defaults({ - baseUrl: metaOptions.githubApiUrl, - method: "POST", - url: "/app-manifests/{code}/conversions", - }); - - return new Promise((resolve, reject) => { - // start the server at an available port - const server = createServer(); - - server.listen(metaOptions.port || 0); - - const port = - metaOptions.port || - // @ts-expect-error - I have yet to see a usecase when `server.address()` can be a string - server.address().port; - - log(`Open http://localhost:${port}`); - - server.on("error", (error) => { - reject(new Error("A server error occured", { cause: error })); - server.close(); - }); - - // Listen to the request event - server.on("request", async (request, response) => { - const url = new URL(request.url, `http://localhost:${port}`); - - const code = url.searchParams.get("code"); - if (code) { - const { data: appCredentials } = await manifestRequest({ - code, - }); - - console.log(appCredentials); - - response.writeHead(200, { "Content-Type": "text/html" }); - response.end(` - -

GitHub App registered successfully

-

- Now follow this steps below.. -

    -
  • - Create a new github repository with name "jargons.dev-test" at https://github.com/new -
  • -
  • - Copy and paste the repo name in full as value to the "PUBLIC_PROJECT_REPO" in the newly created .env; -
    - Example: (assuming you chose the suggested name) -
    - - PUBLIC_PROJECT_REPO="${appCredentials.owner.login}/jargons.dev-test" - -
  • -
  • - Then follow this link to install the app on the repo ${appCredentials.html_url}. -
  • -
-

- `); - - resolve(appCredentials); - - server.close(); - return; - } - - const registerUrl = org - ? `${metaOptions.githubUrl}/organizations/${org}/settings/apps/new` - : `${metaOptions.githubUrl}/settings/apps/new`; - const manifestJson = JSON.stringify({ - redirect_url: `http://localhost:${port}`, - ...manifest, - }); - - response.writeHead(200, { "Content-Type": "text/html" }); - response.end(` - -

Registering GitHub App

-
- - -
- -

- You will be redirected automatically … -

- - - `); - }); - }); -} diff --git a/dev/setup.js b/dev/setup.js deleted file mode 100644 index 3beb106..0000000 --- a/dev/setup.js +++ /dev/null @@ -1,32 +0,0 @@ -import crypto from "node:crypto"; -import fs from "node:fs/promises"; -import registerGitHubApp from "./lib/register-github-app/index.js"; - -// register app and retrieve credentials -const appCredentials = await registerGitHubApp({ - // name of your app - name: "jargons.dev-app-for-", - url: "https://github.com/jargonsdev/jargons.dev/CONTRIBUTING.md", - default_permissions: { - issues: "write", - }, -}); - -// convert private key to pkcs8 format (recommended for better cross plattform support) -const privateKeyPKCS8 = String( - crypto.createPrivateKey(appCredentials.pem).export({ - type: "pkcs8", - format: "pem", - }), -); -const singleLinePrivateKey = privateKeyPKCS8.trim().replace(/\n/g, "\\n"); - -// write credentials into `.env` file -const envFileTemp = await fs.readFile(".env.example", "utf-8"); -const newEnvFileContent = envFileTemp - .replace(/^GITHUB_APP_ID=.*$/m, `GITHUB_APP_ID=${appCredentials.id}`) - .replace( - /^GITHUB_APP_PRIVATE_KEY=.*$/m, - `GITHUB_APP_PRIVATE_KEY="${singleLinePrivateKey}"`, - ); -await fs.writeFile(".env", newEnvFileContent); From 94d8742b14fa89a0410c2066aa94c0044399afa4 Mon Sep 17 00:00:00 2001 From: babblebey Date: Sat, 4 Oct 2025 22:23:56 +0100 Subject: [PATCH 4/6] feat: implement GitHub App registration flow with HTML content and modular structure --- dev/setup/index.js | 2 +- dev/setup/{register-github-app => lib}/content.html.js | 0 dev/setup/{register-github-app => lib}/index.d.ts | 0 dev/setup/{register-github-app => lib}/index.js | 0 4 files changed, 1 insertion(+), 1 deletion(-) rename dev/setup/{register-github-app => lib}/content.html.js (100%) rename dev/setup/{register-github-app => lib}/index.d.ts (100%) rename dev/setup/{register-github-app => lib}/index.js (100%) diff --git a/dev/setup/index.js b/dev/setup/index.js index 2a706fd..acac65e 100644 --- a/dev/setup/index.js +++ b/dev/setup/index.js @@ -1,6 +1,6 @@ import crypto from "node:crypto"; import fs from "node:fs/promises"; -import registerGitHubApp from "./register-github-app/index.js"; +import registerGitHubApp from "./lib/index.js"; // register app and retrieve credentials const appCredentials = await registerGitHubApp({ diff --git a/dev/setup/register-github-app/content.html.js b/dev/setup/lib/content.html.js similarity index 100% rename from dev/setup/register-github-app/content.html.js rename to dev/setup/lib/content.html.js diff --git a/dev/setup/register-github-app/index.d.ts b/dev/setup/lib/index.d.ts similarity index 100% rename from dev/setup/register-github-app/index.d.ts rename to dev/setup/lib/index.d.ts diff --git a/dev/setup/register-github-app/index.js b/dev/setup/lib/index.js similarity index 100% rename from dev/setup/register-github-app/index.js rename to dev/setup/lib/index.js From 913b65d948ce2a0c8f5454af868c43fbb4ba8b70 Mon Sep 17 00:00:00 2001 From: babblebey Date: Sat, 4 Oct 2025 22:26:54 +0100 Subject: [PATCH 5/6] fix: update parameter import path and enhance success message in getNextStepPage function --- dev/setup/lib/content.html.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/setup/lib/content.html.js b/dev/setup/lib/content.html.js index f4d7afa..128ca84 100644 --- a/dev/setup/lib/content.html.js +++ b/dev/setup/lib/content.html.js @@ -167,7 +167,7 @@ export function getStartPage(registerUrl, manifestJson) { /** * Generate the next step page HTML content - * @param {import('../index').AppCredentials} appCredentials + * @param {import('./index').AppCredentials} appCredentials * @returns */ export function getNextStepPage(appCredentials) { @@ -183,7 +183,7 @@ export function getNextStepPage(appCredentials) { - GitHub App created + GitHub App \`${appCredentials.name}\` created
From 8416063923ca5438e04d4184b02044aee0e735c7 Mon Sep 17 00:00:00 2001 From: babblebey Date: Sun, 19 Oct 2025 00:41:02 +0100 Subject: [PATCH 6/6] fix: update background color and success message in content.html.js --- dev/setup/lib/content.html.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/setup/lib/content.html.js b/dev/setup/lib/content.html.js index 128ca84..96b2716 100644 --- a/dev/setup/lib/content.html.js +++ b/dev/setup/lib/content.html.js @@ -111,7 +111,7 @@ const headEl = ` background: #444; } code, pre { - background: #222; + background: #7f7f7f; color: #fff; border-radius: 5px; padding: 0.2em 0.5em; @@ -178,7 +178,7 @@ export function getNextStepPage(appCredentials) {

Welcome to the jargons.dev Local development setup guide

  1. -

    Registering your GitHub App

    +

    GitHub App Registered Sucessfully