Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions src/selfhost/private-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ function stripReviewKey(mapping: Record<string, unknown>): Record<string, unknow
return rest;
}

function hasReviewKey(mapping: Record<string, unknown>): boolean {
return Object.prototype.hasOwnProperty.call(mapping, "review");
}

function extractReviewMapping(mapping: Record<string, unknown>): Record<string, unknown> | null {
const { review } = mapping;
if (review === undefined || review === null) return null;
Expand Down Expand Up @@ -207,10 +211,10 @@ function combineConfigLayersWithMeta(

let mergedReview: unknown;
for (const layer of parsedLayers) {
const review = extractReviewMapping(layer.mapping);
if (review === null) continue;
if (!hasReviewKey(layer.mapping)) continue;
const { review } = layer.mapping;
mergedReview = mergedReview === undefined ? review : mergeConfigOverlay(mergedReview, review);
if (layer.kind === "shared" && layer.sourcePath) sharedConfigSource = layer.sourcePath;
if (layer.kind === "shared" && extractReviewMapping(layer.mapping) && layer.sourcePath) sharedConfigSource = layer.sourcePath;
}
if (mergedReview !== undefined) mergedBody.review = mergedReview;

Expand Down
27 changes: 27 additions & 0 deletions test/unit/private-config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,33 @@ describe("makeLocalManifestReader — review.shared_config overlay (#2046)", ()
expect(manifest.review.securityFocus).toBe(true);
});

it("lets a higher-priority review null clear an inherited shared review block", async () => {
const dir = mkdtempSync(join(tmpdir(), "gt-repo-config-"));
mkdirSync(join(dir, "_shared"));
writeFileSync(join(dir, "_shared", ".gittensory.yml"), "review:\n footer:\n text: shared footer\n tone: house-tone\n");
mkdirSync(join(dir, "repo"));
writeFileSync(join(dir, "repo", ".gittensory.yml"), "review: null\n");
const loaded = await readLocalManifestLoad(makeLocalManifestReader(dir)!, "owner/repo");
const manifest = parseFocusManifestContent(loaded!.content!);
expect(manifest.review.present).toBe(false);
expect(manifest.review.footerText).toBeNull();
expect(manifest.review.tone).toBeNull();
});

it("lets a higher-priority non-mapping review value replace an inherited shared review block", async () => {
const dir = mkdtempSync(join(tmpdir(), "gt-repo-config-"));
mkdirSync(join(dir, "_shared"));
writeFileSync(join(dir, "_shared", ".gittensory.yml"), "review:\n footer:\n text: shared footer\n tone: house-tone\n");
mkdirSync(join(dir, "repo"));
writeFileSync(join(dir, "repo", ".gittensory.yml"), "review: false\n");
const loaded = await readLocalManifestLoad(makeLocalManifestReader(dir)!, "owner/repo");
const manifest = parseFocusManifestContent(loaded!.content!);
expect(manifest.review.present).toBe(false);
expect(manifest.review.footerText).toBeNull();
expect(manifest.review.tone).toBeNull();
expect(manifest.warnings).toContain('Manifest field "review" must be a mapping; ignoring it.');
});

it("sets sharedConfigSource when only the shared base is present for a repo", async () => {
const dir = mkdtempSync(join(tmpdir(), "gt-repo-config-"));
mkdirSync(join(dir, "_shared"));
Expand Down
Loading