Skip to content

feat: add GitHub App maker to self-hosted Git configuration#3049

Open
HarshMN2345 wants to merge 1 commit into
mainfrom
feat-github-app-maker-self-hosted
Open

feat: add GitHub App maker to self-hosted Git configuration#3049
HarshMN2345 wants to merge 1 commit into
mainfrom
feat-github-app-maker-self-hosted

Conversation

@HarshMN2345
Copy link
Copy Markdown
Member

Self-hosted instances without VCS configured now see an inline GitHub App maker in Project Settings → Git instead of a static docs link.

Uses the GitHub App manifest flow to pre-configure webhooks, permissions, and callback URLs automatically. Includes CSRF protection via sessionStorage, proper hostname validation, and extracts all required env vars (_APP_VCS_GITHUB_APP_NAME, _APP_VCS_GITHUB_PRIVATE_KEY, _APP_VCS_GITHUB_APP_ID, _APP_VCS_GITHUB_CLIENT_ID, _APP_VCS_GITHUB_CLIENT_SECRET, _APP_VCS_GITHUB_WEBHOOK_SECRET) ready to paste into .env.

What does this PR do?

(Provide a description of what this PR does.)

Test Plan

(Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your changes work.)

Related PRs and Issues

(If this PR is related to any other PR or resolves any issue or related to any issue link all related PR and issues here.)

Have you read the Contributing Guidelines on issues?

(Write your answer here.)

Self-hosted instances without VCS configured now see an inline GitHub
App maker in Project Settings → Git instead of a static docs link.

Uses the GitHub App manifest flow to pre-configure webhooks, permissions,
and callback URLs automatically. Includes CSRF protection via sessionStorage,
proper hostname validation, and extracts all required env vars
(_APP_VCS_GITHUB_APP_NAME, _APP_VCS_GITHUB_PRIVATE_KEY, _APP_VCS_GITHUB_APP_ID,
_APP_VCS_GITHUB_CLIENT_ID, _APP_VCS_GITHUB_CLIENT_SECRET,
_APP_VCS_GITHUB_WEBHOOK_SECRET) ready to paste into .env.

The connectGit component (used mid-flow in function/site creation) is
updated to link back to settings instead of external docs, and migrated
to Svelte 5 runes.
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 18, 2026

Greptile Summary

This PR adds a GitHubAppMaker component to the Project Settings → Git page for self-hosted instances, replacing a static docs link with an interactive GitHub App manifest flow that pre-configures webhooks, permissions, and callback URLs, then displays the resulting env vars for the user to paste into their .env file.

  • The new gitHubAppMaker.svelte implements the full manifest flow (form POST → GitHub redirect → code exchange → credential display) with CSRF protection via sessionStorage and hostname validation.
  • A bug in the env-var output causes the PEM private key — which is a multi-line block — to be embedded with literal newlines, producing an invalid .env value that most Docker and dotenv parsers will reject.
  • The component mixes Svelte 5 runes ($state, $derived) with Svelte 4 on:click event directives on the Button component, which can silently drop handlers in Svelte 5 mode.

Confidence Score: 3/5

The change should not be merged until the PEM newline encoding is fixed; users who follow the flow today will copy a broken .env block and fail to start their instance.

The broken PEM encoding is the central output of the new feature — every user who completes the manifest flow and pastes the generated env vars will get a malformed private key entry that Docker cannot parse. The Svelte event syntax issue could cause the reset and submit buttons to stop working in Svelte 5 mode.

src/lib/components/git/gitHubAppMaker.svelte needs the PEM line-encoding fix and the on:click to onclick updates before this is safe to ship.

Security Review

  • Client-side credential exposure (gitHubAppMaker.svelte lines 133–143): The GitHub App manifest code-exchange call is made directly from the browser. The response containing the private key, client_secret, and webhook_secret is visible in the browser network panel and held in reactive component state. Any browser extension or XSS vector present on the page could read these values before the user copies them.

Important Files Changed

Filename Overview
src/lib/components/git/gitHubAppMaker.svelte New component implementing the GitHub App manifest flow; contains a bug where the PEM private key embedded newlines break standard .env file format, and uses mixed Svelte 4/5 event syntax.
src/lib/components/git/connectGit.svelte Migrated from Svelte 4 to Svelte 5 runes; replaces static docs link with a settings-page link — straightforward and correct.
src/routes/(console)/project-[region]-[project]/settings/updateInstallations.svelte Replaces a static Alert with the new GitHubAppMaker component under the existing isSelfHosted && !isVcsEnabled guard; minimal change with correct scoping.
src/lib/components/git/index.ts Adds export for GitHubAppMaker — trivial barrel-file change.

Reviews (1): Last reviewed commit: "feat: add GitHub App maker to self-hoste..." | Re-trigger Greptile


const envVars = $derived.by(() => {
if (!credentials) return '';
const pem = credentials.pem.replace(/\n$/, '');
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 The PEM private key from GitHub contains embedded newlines (it is a multi-line block), so after only stripping the trailing newline the resulting _APP_VCS_GITHUB_PRIVATE_KEY value spans multiple lines. Standard Docker env_file parsing and most dotenv libraries do not support multi-line values in the shell-quoted format, meaning the pasted block would produce a broken .env and the server would fail to load the private key. The newlines inside the PEM should be escaped to literal sequences so the value fits on a single line.

Suggested change
const pem = credentials.pem.replace(/\n$/, '');
const pem = credentials.pem.replace(/\n/g, '\\n').replace(/\\n$/, '');

Comment on lines +218 to +221
<Button secondary on:click={reset}>
<Icon slot="start" icon={IconRefresh} size="s" />
Create another GitHub App
</Button>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The two Button component usages here use Svelte 4's on:click directive while the native <button> on line 210 uses the Svelte 5 onclick attribute. The rest of this component is already written in Svelte 5 runes style ($state, $derived). Using the old event directive on a component that may not forward it via createEventDispatcher can silently drop the handler in Svelte 5 mode.

Suggested change
<Button secondary on:click={reset}>
<Icon slot="start" icon={IconRefresh} size="s" />
Create another GitHub App
</Button>
<Button secondary onclick={reset}>
<Icon slot="start" icon={IconRefresh} size="s" />
Create another GitHub App
</Button>

required />
{/if}
<div>
<Button on:click={submit} disabled={!isValid}>Create GitHub App</Button>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Same on:click / onclick inconsistency as the reset button — this Button instance should also use the Svelte 5 onclick attribute to stay consistent with the runes-based component.

Suggested change
<Button on:click={submit} disabled={!isValid}>Create GitHub App</Button>
<Button onclick={submit} disabled={!isValid}>Create GitHub App</Button>

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +133 to +143
try {
const response = await fetch(
`https://api.github.com/app-manifests/${code}/conversions`,
{ method: 'POST', headers: { Accept: 'application/vnd.github+json' } }
);
if (!response.ok) {
const body = await response.json().catch(() => null);
throw new Error(body?.message ?? `GitHub API returned ${response.status}`);
}
const data = await response.json();
credentials = data as GitHubAppCredentials;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 security Client-side credential exchange exposes secrets in browser DevTools

The call to https://api.github.com/app-manifests/${code}/conversions is made directly from the browser. The response includes the GitHub App private key (pem), client_secret, and webhook_secret. These appear in plain text in the browser's network panel and are stored in Svelte reactive state for the lifetime of the page. For an admin setup page it may be an acceptable tradeoff, but worth an explicit acknowledgment: any browser extension or XSS vector active on this page could read those values before the user copies and discards them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant