Skip to content

Commit 6e1731a

Browse files
authored
Merge pull request #163 from mean-weasel/fix/issue-162-local-self-hosting-config
fix: avoid tracked local self-hosting config edits
2 parents aa6f1da + 557fb40 commit 6e1731a

29 files changed

Lines changed: 436 additions & 97 deletions

.dev.vars.example

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ YOUR_PRIVATE_KEY_CONTENT_HERE
2929

3030
# Your GitHub App's URL slug (the name in lowercase with hyphens)
3131
# Find it at: https://github.com/apps/YOUR_APP_NAME
32+
# Local development should override the committed wrangler.toml default here instead of editing wrangler.toml.
3233
# GITHUB_APP_NAME=my-bugdrop-app
3334

3435
# Allowed origins for CORS (comma-separated, or "*" for all)
@@ -37,3 +38,6 @@ YOUR_PRIVATE_KEY_CONTENT_HERE
3738

3839
# Maximum screenshot size in MB
3940
# MAX_SCREENSHOT_SIZE_MB=5
41+
42+
# RATE_LIMIT is intentionally not configured here.
43+
# Local development runs with ENVIRONMENT=development and can work without a KV namespace.

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ public/versions.json
1313
# Local secrets (NEVER commit)
1414
.dev.vars
1515

16+
# Local self-hosting test page override
17+
public/test/local-config.js
18+
1619
# Private keys (NEVER commit)
1720
*.pem
1821
private-key*.pem
@@ -29,6 +32,7 @@ private-key*.pem
2932
.plans/
3033
.superpowers/
3134
.playwright-mcp/
35+
docs/superpowers/
3236

3337
# OS files
3438
.DS_Store

SELF_HOSTING.md

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -43,24 +43,32 @@ GITHUB_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
4343
-----END RSA PRIVATE KEY-----"
4444
```
4545

46-
Next, update `wrangler.toml` with your app name and remove the upstream KV namespace IDs:
46+
Next, keep local configuration in ignored files so your fork does not accumulate noisy diffs.
4747

48-
```toml
49-
[vars]
50-
GITHUB_APP_NAME = "your-app-name" # Must match your GitHub App's URL slug
48+
In `.dev.vars`, uncomment and set your GitHub App slug:
5149

52-
# Remove or replace the [[kv_namespaces]] section (and the
53-
# [[env.preview.kv_namespaces]] section) — the existing IDs belong
54-
# to the upstream deployment. See Step 5 to create your own,
55-
# or delete both sections entirely to disable rate limiting.
50+
```bash
51+
GITHUB_APP_NAME=your-app-name
5652
```
5753

58-
> **Note:** The test pages under `public/test/` have `data-repo` set to `mean-weasel/bugdrop-widget-test`. Change this in all test HTML files to a repo where your GitHub App is installed:
59-
>
60-
> ```bash
61-
> # macOS / BSD sed
62-
> find public/test -name '*.html' -exec sed -i '' 's/mean-weasel\/bugdrop-widget-test/your-org\/your-repo/g' {} +
63-
> ```
54+
The committed `wrangler.toml` contains the upstream development defaults used by this repository. For local development, prefer `.dev.vars` overrides instead of editing `wrangler.toml`.
55+
56+
If you want the local test pages under `/test/` to submit to a repository where your GitHub App is installed, copy the local test config example:
57+
58+
```bash
59+
cp public/test/local-config.example.js public/test/local-config.js
60+
```
61+
62+
Then edit `public/test/local-config.js`:
63+
64+
```js
65+
window.BugDropTestConfig = {
66+
...(window.BugDropTestConfig || {}),
67+
repo: 'your-org/your-repo',
68+
};
69+
```
70+
71+
`public/test/local-config.js` is ignored by git. Without it, test pages keep using the upstream test repository `mean-weasel/bugdrop-widget-test`, which is what CI expects.
6472

6573
## 3. Build the Widget
6674

@@ -83,15 +91,19 @@ Visit http://localhost:8787/test/ to try the widget.
8391

8492
Rate limiting prevents spam and protects GitHub API quotas. It uses Cloudflare KV for distributed storage.
8593

86-
> **Important:** The `[[kv_namespaces]]` IDs in `wrangler.toml` belong to the upstream deployment and will not work for your account. You must create your own namespaces below, or remove the section to disable rate limiting.
94+
> **Important:** The `[[kv_namespaces]]` IDs in `wrangler.toml` belong to the upstream deployment and will not work for your account.
95+
96+
For local development, you can leave rate limiting unconfigured. BugDrop disables rate limiting when no `RATE_LIMIT` binding is present.
97+
98+
For production, create your own KV namespaces and update your deployment configuration with the IDs from Wrangler. Do not reuse the upstream IDs committed in this repository.
8799

88100
```bash
89101
# Create KV namespaces
90102
npx wrangler kv:namespace create RATE_LIMIT
91103
npx wrangler kv:namespace create RATE_LIMIT --preview
92104
```
93105

94-
Copy the IDs from the output and update `wrangler.toml`:
106+
Copy the IDs from the output and update your production deployment configuration:
95107

96108
```toml
97109
[[kv_namespaces]]

docs/website/self-hosting.mdx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ You need to create your own GitHub App to replace the hosted BugDrop app. The ap
3535
| **Webhook** | Not required (optional) |
3636
| **Installation scope** | Your repositories |
3737

38-
You will need the App ID, private key, and installation ID to configure the Worker.
38+
You will need the App ID and private key to configure the Worker. BugDrop discovers the
39+
installation for each repository at request time.
3940

4041
## Architecture Overview
4142

@@ -60,15 +61,15 @@ The Cloudflare Worker acts as a secure proxy between the user's browser and the
6061

6162
## Setup Instructions
6263

63-
Full self-hosting instructions, including environment variables, Wrangler configuration, and deployment steps, are available in the repository:
64+
Full self-hosting instructions, including environment variables, ignored local configuration, and deployment steps, are available in the repository:
6465

6566
**[SELF_HOSTING.md on GitHub](https://github.com/mean-weasel/bugdrop/blob/main/SELF_HOSTING.md)**
6667

6768
The guide covers:
6869

6970
1. **Forking the repository** -- Get your own copy of the BugDrop codebase
7071
2. **Creating a GitHub App** -- Step-by-step instructions for creating and configuring the app
71-
3. **Configuring Wrangler** -- Setting up `wrangler.toml` with your account details
72+
3. **Configuring local overrides** -- Using ignored `.dev.vars` and `public/test/local-config.js` files so your fork stays clean
7273
4. **Setting secrets** -- Storing your GitHub App credentials as Worker secrets
7374
5. **Deploying** -- Publishing the Worker to your Cloudflare account
7475
6. **Testing** -- Verifying the deployment works end-to-end

e2e/widget.spec.ts

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { readdir, readFile } from 'node:fs/promises';
2+
import { basename, join } from 'node:path';
13
import { test, expect, type Page } from '@playwright/test';
24

35
/**
@@ -39,6 +41,109 @@ test.describe('Widget Loading', () => {
3941
const button = page.locator('#bugdrop-host').locator('css=.bd-trigger');
4042
await expect(button).toBeVisible();
4143
});
44+
45+
test('missing local test config returns an empty script', async ({ page }) => {
46+
const response = await page.request.get('/test/local-config.js');
47+
48+
expect(response.status()).toBe(200);
49+
expect(response.headers()['content-type']).toContain('application/javascript');
50+
expect(await response.text()).toBe('');
51+
});
52+
53+
test('test pages use upstream repo when no local override is present', async ({ page }) => {
54+
await page.route('**/test/local-config.js', route => {
55+
return route.fulfill({ status: 404 });
56+
});
57+
58+
await page.goto('/test/');
59+
60+
const widgetScript = page.locator('script[src="/widget.js"]');
61+
await expect(widgetScript).toBeAttached();
62+
63+
const repo = await widgetScript.evaluate(script => {
64+
return (script as HTMLScriptElement).dataset.repo;
65+
});
66+
67+
expect(repo).toBe('mean-weasel/bugdrop-widget-test');
68+
});
69+
70+
test('test pages use local repo override when configured', async ({ page }) => {
71+
await page.route('**/test/local-config.js', route => {
72+
return route.fulfill({
73+
contentType: 'application/javascript',
74+
body: `
75+
window.BugDropTestConfig = {
76+
...(window.BugDropTestConfig || {}),
77+
repo: 'local-owner/local-repo',
78+
};
79+
`,
80+
});
81+
});
82+
83+
await page.goto('/test/');
84+
85+
const widgetScript = page.locator('script[src="/widget.js"]');
86+
await expect(widgetScript).toBeAttached();
87+
88+
const repo = await widgetScript.evaluate(script => {
89+
return (script as HTMLScriptElement).dataset.repo;
90+
});
91+
92+
expect(repo).toBe('local-owner/local-repo');
93+
});
94+
95+
test('non-index test pages use local repo override when configured', async ({ page }) => {
96+
await page.route('**/test/local-config.js', route => {
97+
return route.fulfill({
98+
contentType: 'application/javascript',
99+
body: `
100+
window.BugDropTestConfig = {
101+
...(window.BugDropTestConfig || {}),
102+
repo: 'local-owner/local-repo',
103+
};
104+
`,
105+
});
106+
});
107+
108+
await page.goto('/test/color-default.html');
109+
110+
const widgetScript = page.locator('script[src="/widget.js"]');
111+
await expect(widgetScript).toBeAttached();
112+
113+
const repo = await widgetScript.evaluate(script => {
114+
return (script as HTMLScriptElement).dataset.repo;
115+
});
116+
117+
expect(repo).toBe('local-owner/local-repo');
118+
});
119+
120+
test('widget test fixtures use shared local config loader', async () => {
121+
const fixtureDir = join(process.cwd(), 'public', 'test');
122+
const fixtureNames = (await readdir(fixtureDir)).filter(name => name.endsWith('.html'));
123+
const nonWidgetFixtures = new Set(['pull-tab-mock.html']);
124+
125+
for (const fixtureName of fixtureNames) {
126+
const source = await readFile(join(fixtureDir, fixtureName), 'utf8');
127+
const directWidgetScript = /<script\b(?=[^>]*\bsrc=["']\/widget\.js["'])/i.test(source);
128+
129+
expect(
130+
directWidgetScript,
131+
`${fixtureName} should load the widget through load-widget.js`
132+
).toBe(false);
133+
expect(source, `${fixtureName} should not hard-code the upstream test repo`).not.toContain(
134+
'data-repo="mean-weasel/bugdrop-widget-test"'
135+
);
136+
137+
if (!nonWidgetFixtures.has(basename(fixtureName))) {
138+
expect(source, `${fixtureName} should include the shared test loader`).toContain(
139+
'src="/test/load-widget.js"'
140+
);
141+
expect(source, `${fixtureName} should call the shared test loader`).toContain(
142+
'loadBugDropTestWidget'
143+
);
144+
}
145+
}
146+
});
42147
});
43148

44149
test.describe('Widget Interaction', () => {

public/test/annotation-preview-size.html

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,15 @@ <h1>Widget</h1>
6161
<div class="row">Object: example.company/feature/preview</div>
6262
</main>
6363

64-
<script id="bugdrop-script" defer src="/widget.js" data-repo="mean-weasel/bugdrop-widget-test" data-theme="dark" data-color="#ff9e64"></script>
64+
<script src="/test/load-widget.js"></script>
65+
<script>
66+
window.loadBugDropTestWidget({
67+
id: 'bugdrop-script',
68+
dataset: {
69+
theme: 'dark',
70+
color: '#ff9e64',
71+
},
72+
});
73+
</script>
6574
</body>
6675
</html>

public/test/annotation-small-wide-area.html

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,15 @@
3333
<body>
3434
<div id="small-wide-target" class="field-label">Settlement Stage</div>
3535

36-
<script id="bugdrop-script" defer src="/widget.js" data-repo="mean-weasel/bugdrop-widget-test" data-theme="dark" data-color="#ff9e64"></script>
36+
<script src="/test/load-widget.js"></script>
37+
<script>
38+
window.loadBugDropTestWidget({
39+
id: 'bugdrop-script',
40+
dataset: {
41+
theme: 'dark',
42+
color: '#ff9e64',
43+
},
44+
});
45+
</script>
3746
</body>
3847
</html>

public/test/annotation-style.html

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -123,13 +123,15 @@ <h1>Shipmen Overview</h1>
123123
</section>
124124
</main>
125125

126-
<script
127-
id="bugdrop-script"
128-
defer
129-
src="/widget.js"
130-
data-repo="mean-weasel/bugdrop-widget-test"
131-
data-theme="dark"
132-
data-color="#ff9e64"
133-
></script>
126+
<script src="/test/load-widget.js"></script>
127+
<script>
128+
window.loadBugDropTestWidget({
129+
id: 'bugdrop-script',
130+
dataset: {
131+
theme: 'dark',
132+
color: '#ff9e64',
133+
},
134+
});
135+
</script>
134136
</body>
135137
</html>

public/test/api-only.html

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,12 @@ <h2>Usage Example</h2>
197197
</script>
198198

199199
<!-- BugDrop widget in API-only mode (no floating button) -->
200-
<script src="/widget.js" data-repo="test/repo" data-button="false"></script>
200+
<script src="/test/load-widget.js"></script>
201+
<script>
202+
window.loadBugDropTestWidget({
203+
dataset: { button: 'false' },
204+
});
205+
</script>
201206

202207
<script>
203208
// Check if BugDrop already initialized (in case the event fired before our listener)

public/test/color-custom.html

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,14 @@ <h1>Custom Color Test</h1>
3939
</div>
4040

4141
<!-- Load BugDrop WITH custom purple color -->
42-
<script src="/widget.js" data-repo="mean-weasel/bugdrop-widget-test" data-theme="dark" data-color="#9333EA"></script>
42+
<script src="/test/load-widget.js"></script>
43+
<script>
44+
window.loadBugDropTestWidget({
45+
dataset: {
46+
theme: 'dark',
47+
color: '#9333EA',
48+
},
49+
});
50+
</script>
4351
</body>
4452
</html>

0 commit comments

Comments
 (0)