Skip to content

Commit 1e73600

Browse files
authored
Merge pull request #5880 from continuedev/pe/tag-to-string-fix
fix: use dir hash in `tagToString`
2 parents 3fc3ca9 + 9c2a780 commit 1e73600

File tree

2 files changed

+72
-26
lines changed

2 files changed

+72
-26
lines changed

core/indexing/utils.test.ts

Lines changed: 54 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ test("tagToString returns full tag string when under length limit", () => {
1111
expect(tagToString(tag)).toBe("/normal/path/to/repo::main::12345");
1212
});
1313

14-
test("tagToString truncates beginning of directory when path is too long", () => {
15-
// Create a very long directory path that exceeds MAX_DIR_LENGTH (200)
14+
test("tagToString truncates beginning of directory when path is too long and adds hash for uniqueness", () => {
1615
const longPrefix = "/very/long/path/that/will/be/truncated/";
1716
const importantSuffix = "/user/important-project/src/feature";
1817
const longPath = longPrefix + "x".repeat(200) + importantSuffix;
@@ -25,18 +24,14 @@ test("tagToString truncates beginning of directory when path is too long", () =>
2524

2625
const result = tagToString(tag);
2726

28-
// The result should keep the important suffix part
29-
expect(result).toContain(importantSuffix);
30-
// The result should NOT contain the beginning of the path
31-
expect(result).not.toContain(longPrefix);
32-
// The result should include the branch and artifactId
3327
expect(result).toContain("::feature-branch::67890");
34-
// The result should be within the MAX_TABLE_NAME_LENGTH limit (240)
3528
expect(result.length).toBeLessThanOrEqual(240);
29+
expect(result).toMatch(/^[a-f0-9]{8}_/);
30+
expect(result).toContain(importantSuffix);
3631
});
3732

3833
test("tagToString preserves branch and artifactId exactly, even when truncating", () => {
39-
const longPath = "/a".repeat(300); // Much longer than MAX_DIR_LENGTH
34+
const longPath = "/a".repeat(300);
4035
const tag: IndexTag = {
4136
directory: longPath,
4237
branch: "release-v2.0",
@@ -45,14 +40,56 @@ test("tagToString preserves branch and artifactId exactly, even when truncating"
4540

4641
const result = tagToString(tag);
4742

48-
// Should contain the exact branch and artifactId
4943
expect(result).toContain("::release-v2.0::build-123");
50-
// Should contain the end of the path
51-
expect(result).toContain("/a/a/a");
52-
// Should not contain the full original path (it should be truncated)
53-
expect(result.length).toBeLessThan(
54-
longPath.length + "::release-v2.0::build-123".length,
55-
);
56-
// The result should be within the MAX_TABLE_NAME_LENGTH limit
44+
expect(result).toMatch(/^[a-f0-9]{8}_/);
5745
expect(result.length).toBeLessThanOrEqual(240);
5846
});
47+
48+
test("tagToString ensures uniqueness for different long paths that would otherwise collide", () => {
49+
const basePath = "/very/long/base/path/that/exceeds/limits/";
50+
const commonSuffix = "/same/ending/path";
51+
52+
const tag1: IndexTag = {
53+
directory: basePath + "different1" + "x".repeat(100) + commonSuffix,
54+
branch: "main",
55+
artifactId: "12345",
56+
};
57+
58+
const tag2: IndexTag = {
59+
directory: basePath + "different2" + "y".repeat(100) + commonSuffix,
60+
branch: "main",
61+
artifactId: "12345",
62+
};
63+
64+
const fullString1 = `${tag1.directory}::${tag1.branch}::${tag1.artifactId}`;
65+
const fullString2 = `${tag2.directory}::${tag2.branch}::${tag2.artifactId}`;
66+
67+
const result1 = tagToString(tag1);
68+
const result2 = tagToString(tag2);
69+
70+
expect(result1).not.toBe(result2);
71+
expect(result1.length).toBeLessThanOrEqual(240);
72+
expect(result2.length).toBeLessThanOrEqual(240);
73+
74+
if (fullString1.length > 240) {
75+
expect(result1).toMatch(/^[a-f0-9]{8}_/);
76+
expect(result2).toMatch(/^[a-f0-9]{8}_/);
77+
} else {
78+
expect(result1).toBe(fullString1);
79+
expect(result2).toBe(fullString2);
80+
}
81+
});
82+
83+
test("tagToString produces consistent results for the same input", () => {
84+
const tag: IndexTag = {
85+
directory:
86+
"/some/very/long/path/that/exceeds/the/maximum/length/limit/for/directory/names/in/the/system",
87+
branch: "develop",
88+
artifactId: "test-123",
89+
};
90+
91+
const result1 = tagToString(tag);
92+
const result2 = tagToString(tag);
93+
94+
expect(result1).toBe(result2);
95+
});

core/indexing/utils.ts

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import crypto from "crypto";
12
import { IndexTag } from "..";
23

34
// Maximum length for table names to stay under OS filename limits
@@ -18,8 +19,8 @@ const MAX_DIR_LENGTH = 200;
1819
*
1920
* To handle long paths:
2021
* 1. First tries the full string - most backwards compatible
21-
* 2. If too long, truncates directory from the beginning to maintain uniqueness
22-
* (since final parts of paths are more unique than prefixes)
22+
* 2. If too long, truncates directory from the beginning and adds a hash prefix
23+
* to ensure uniqueness while preserving the more readable end parts
2324
* 3. Finally ensures entire string stays under MAX_TABLE_NAME_LENGTH for OS compatibility
2425
*
2526
* @param tag The tag containing directory, branch, and artifactId
@@ -32,14 +33,22 @@ export function tagToString(tag: IndexTag): string {
3233
return result;
3334
}
3435

36+
// Create a hash of the full directory path to ensure uniqueness
37+
const dirHash = crypto
38+
.createHash("md5")
39+
.update(tag.directory)
40+
.digest("hex")
41+
.slice(0, 8);
42+
43+
// Calculate how much space we have for the directory after accounting for hash, separators, branch, and artifactId
44+
const nonDirLength = `${dirHash}_::${tag.branch}::${tag.artifactId}`.length;
45+
const maxDirForTruncated = MAX_TABLE_NAME_LENGTH - nonDirLength;
46+
3547
// Truncate from the beginning of directory path to preserve the more unique end parts
36-
const dir =
37-
tag.directory.length > MAX_DIR_LENGTH
38-
? tag.directory.slice(tag.directory.length - MAX_DIR_LENGTH)
48+
const truncatedDir =
49+
tag.directory.length > maxDirForTruncated
50+
? tag.directory.slice(tag.directory.length - maxDirForTruncated)
3951
: tag.directory;
4052

41-
return `${dir}::${tag.branch}::${tag.artifactId}`.slice(
42-
0,
43-
MAX_TABLE_NAME_LENGTH,
44-
);
53+
return `${dirHash}_${truncatedDir}::${tag.branch}::${tag.artifactId}`;
4554
}

0 commit comments

Comments
 (0)