Skip to content

Commit f97c1a3

Browse files
committed
Allow absolute paths for skill dirs
1 parent 608ce9c commit f97c1a3

File tree

2 files changed

+9
-44
lines changed

2 files changed

+9
-44
lines changed

ballerina-interpreter/skills.bal

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,11 @@ type SkillFrontmatter record {
4444

4545
function discoverSkills(SkillSource[] sources, string afmFileDir) returns map<SkillInfo>|error {
4646
map<SkillInfo> skills = {};
47-
string normalizedAfmDir = check file:normalizePath(check file:getAbsolutePath(afmFileDir), file:CLEAN);
4847

4948
foreach SkillSource 'source in sources {
50-
if check file:isAbsolutePath('source.path) {
51-
return error(string `Skill source path must be relative, but got: ${'source.path}`);
52-
}
53-
string resolvedPath = check file:joinPath(afmFileDir, 'source.path);
54-
string normalizedPath = check file:normalizePath(check file:getAbsolutePath(resolvedPath), file:CLEAN);
55-
if normalizedPath != normalizedAfmDir && !normalizedPath.startsWith(normalizedAfmDir + file:pathSeparator) {
56-
return error(string `Skill source path '${'source.path}' resolves outside the AFM file directory`);
57-
}
49+
string resolvedPath = check file:isAbsolutePath('source.path)
50+
? 'source.path
51+
: check file:joinPath(afmFileDir, 'source.path);
5852
map<SkillInfo> localSkills = check discoverLocalSkills(resolvedPath);
5953
foreach [string, SkillInfo] [name, info] in localSkills.entries() {
6054
if skills.hasKey(name) {

ballerina-interpreter/tests/skills_test.bal

Lines changed: 6 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -325,41 +325,12 @@ function testDiscoverLocalSkillsInvalidSkill() returns error? {
325325
}
326326

327327
@test:Config
328-
function testDiscoverSkillsAbsolutePathRejected() returns error? {
329-
string testDir = check file:getAbsolutePath("tests/skills");
330-
SkillSource[] sources = [{path: "/absolute/path/to/skills"}];
331-
map<SkillInfo>|error result = discoverSkills(sources, testDir);
332-
if result is map<SkillInfo> {
333-
test:assertFail("Expected error for absolute path");
334-
}
335-
test:assertTrue(result.message().includes("must be relative"));
336-
}
337-
338-
@test:Config
339-
function testDiscoverSkillsPathOutsideAfmDirRejected() returns error? {
340-
string testDir = check file:getAbsolutePath("tests/skills");
341-
SkillSource[] sources = [{path: "../../outside"}];
342-
map<SkillInfo>|error result = discoverSkills(sources, testDir);
343-
if result is map<SkillInfo> {
344-
test:assertFail("Expected error for path outside AFM directory");
345-
}
346-
test:assertTrue(result.message().includes("resolves outside"));
347-
}
348-
349-
@test:Config
350-
function testDiscoverSkillsSiblingPrefixPathRejected() returns error? {
351-
// A sibling directory sharing a name prefix must not pass validation.
352-
// E.g. if afm_dir is '/a/skills', a path resolving to '/a/skills-evil'
353-
// should be rejected even though it starts with the same string prefix.
354-
string testDir = check file:getAbsolutePath("tests/skills/single_skill");
355-
// "../single_skill_extra" would resolve to tests/skills/single_skill_extra
356-
// which shares the prefix "tests/skills/single_skill" but is outside the dir
357-
SkillSource[] sources = [{path: "../single_skill_extra"}];
358-
map<SkillInfo>|error result = discoverSkills(sources, testDir);
359-
if result is map<SkillInfo> {
360-
test:assertFail("Expected error for sibling prefix path");
361-
}
362-
test:assertTrue(result.message().includes("resolves outside"));
328+
function testDiscoverSkillsAbsolutePathAccepted() returns error? {
329+
string absPath = check file:getAbsolutePath("tests/skills/single_skill");
330+
SkillSource[] sources = [{path: absPath}];
331+
map<SkillInfo> skills = check discoverSkills(sources, "some/other/dir");
332+
test:assertEquals(skills.length(), 1);
333+
test:assertTrue(skills.hasKey("test-gen"));
363334
}
364335

365336
@test:Config

0 commit comments

Comments
 (0)