default scanned repo to private

AuthorKonata <konata@posteo.jp>
Date
Commit0e6d7d0120e6ec3e97f6f4e47d5ce27d82c96215
Parentdaabb00
7 files changed, 41 insertions(+), 2 deletions(-)
MREADME.md
@@ -80,6 +80,7 @@ All settings are environment variables:
8080 | `MAX_USER_UPLOAD_BYTES` | `2097152` | Max upload size for non-admin users (2 MB) |
8181 | `INLINE_MAX_BYTES` | `524288` | Max file size rendered inline in the code view (512 KB) |
8282 | `SSH_DISABLED` | `0` | Set to `1` to disable the embedded SSH server |
83+| `SCANNED_REPO_PRIVATE` | `1` | Set to `0` to make auto-scanned repos public by default |
8384 | `TRUSTED_PROXY` | `0` | Trust `X-Forwarded-For` headers \*\* |
8485 | `RATE_LIMIT_DISABLED` | `0` | Set to `1` to disable rate limiting |
8586 | `HIGHLIGHT_WORKERS` | `4` | Syntax highlighting worker threads \* |
Mcompose.yml
@@ -15,6 +15,7 @@ services:
1515 BASE_URL: "http://localhost:3000"
1616 # ADMIN_PASSWORD: change-me
1717 # SSH_DISABLED: 0
18+ # SCANNED_REPO_PRIVATE: 1
1819 # ARCHIVE_ZST_ENABLED: 0
1920 # TRUSTED_PROXY: 1
2021 # REGISTRATION_TYPE: enabled
Msrc/config.ts
@@ -19,6 +19,9 @@ const config = {
1919 TRUSTED_PROXY: !!env.TRUSTED_PROXY,
2020 RATE_LIMIT_DISABLED: !!env.RATE_LIMIT_DISABLED,
2121 SSH_DISABLED: !!env.SSH_DISABLED,
22+ SCANNED_REPO_PRIVATE:
23+ env.SCANNED_REPO_PRIVATE !== "0" &&
24+ env.SCANNED_REPO_PRIVATE !== "false",
2225 PORT: parseInt(env.PORT ?? "", 10) || 3000,
2326 SSH_PORT: parseInt(env.SSH_PORT ?? "", 10) || 2222,
2427 REGISTRATION_TYPE: (env.REGISTRATION_TYPE ?? "enabled") as
Msrc/index.tsx
@@ -10,6 +10,6 @@ for (const cmd of ["git", "ssh-keygen"]) {
1010 }
1111 }
1212
13-if (!config.SSH_DISABLED) await startSshServer();
1413 await createApp(config.PORT);
14+if (!config.SSH_DISABLED) await startSshServer();
1515 console.log(`Hearthforge running at http://localhost:${config.PORT}`);
Msrc/services/repoSync.ts
@@ -103,7 +103,7 @@ export async function ensureRepoRecord(name: string): Promise<RepositoryRow> {
103103 .values({
104104 name,
105105 description: null,
106- is_private: 0,
106+ is_private: config.SCANNED_REPO_PRIVATE ? 1 : 0,
107107 default_branch: branch,
108108 created_at: now,
109109 })
Mtests/e2e.releases.test.ts
@@ -351,6 +351,12 @@ describe('releases', () => {
351351 });
352352
353353 test('source archive tar.zst appears in downloads', async () => {
354+ const zstd = Bun.spawnSync({ cmd: ['which', 'zstd'] });
355+ if (zstd.exitCode !== 0) {
356+ console.log('zstd not available, skipping tar.zst test');
357+ return;
358+ }
359+
354360 const page = await adminCtx.newPage();
355361 try {
356362 await page.goto(srcReleaseUrl);
Mtests/e2e.repos.test.ts
@@ -1,9 +1,14 @@
11 import { describe, test, expect, beforeAll, afterAll } from 'bun:test';
2+import { rmSync } from 'node:fs';
3+import { spawnSync } from 'node:child_process';
24 import { chromium } from 'playwright';
35 import type { Browser, BrowserContext } from 'playwright';
6+import config from '../src/config.ts';
7+import { db } from '../src/db/index.ts';
48 import {
59 BASE,
610 ADMIN_PASS,
11+ DATA_DIR,
712 setupTestEnv,
813 spawnServer,
914 killServer,
@@ -340,4 +345,27 @@ describe('repos', () => {
340345 expect(resp.status()).toBe(200);
341346 expect(await resp.text()).toContain('Invalid repository name');
342347 });
348+
349+ test('auto-scanned repo is private by default', async () => {
350+ const name = 'auto-private-repo';
351+ const dir = `${process.cwd()}/${DATA_DIR}/repos/${name}.git`;
352+
353+ rmSync(dir, { recursive: true, force: true });
354+ const tmp = `/tmp/hf-scan-${Date.now()}`;
355+ try {
356+ spawnSync('git', ['init', '--bare', dir], { stdio: 'ignore' });
357+ spawnSync('git', ['clone', dir, tmp], { stdio: 'ignore' });
358+ spawnSync('git', ['-C', tmp, 'commit', '--allow-empty', '-m', 'init'], { stdio: 'ignore' });
359+ spawnSync('git', ['-C', tmp, 'push', 'origin', 'HEAD:main'], { stdio: 'ignore' });
360+ } finally {
361+ rmSync(tmp, { recursive: true, force: true });
362+ }
363+
364+ const { ensureRepoRecord } = await import('../src/services/repoSync.ts');
365+ const repo = await ensureRepoRecord(name);
366+ expect(repo.is_private).toBe(1);
367+
368+ await db.deleteFrom('repositories').where('name', '=', name).execute();
369+ rmSync(dir, { recursive: true, force: true });
370+ });
343371 });