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
4 changes: 2 additions & 2 deletions internal/core/domain/cacheInfo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ func TestNewRepoInfo(t *testing.T) {
t.Errorf("NewRepoInfo().Key = %q, want %q", info.Key, dep.HashName())
}

if info.Name != "horse" {
t.Errorf("NewRepoInfo().Name = %q, want %q", info.Name, "horse")
if info.Name != "github_com_hashload_horse" {
t.Errorf("NewRepoInfo().Name = %q, want %q", info.Name, "github_com_hashload_horse")
}

if len(info.Versions) != 3 {
Expand Down
26 changes: 23 additions & 3 deletions internal/core/domain/dependency.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ var (
reHasHTTPS = regexp.MustCompile(`(?m)^https?:\/\/`)
reVersionMajorMinor = regexp.MustCompile(`(?m)^(.|)(\d+)\.(\d+)$`)
reVersionMajor = regexp.MustCompile(`(?m)^(.|)(\d+)$`)
reDepName = regexp.MustCompile(`[^/]+(:?/$|$)`)
)

// Dependency represents a package dependency.
Expand Down Expand Up @@ -124,9 +123,30 @@ func GetDependenciesNames(deps []Dependency) []string {
return dependencies
}

// Name returns the name of the dependency extracted from the repository URL.
// Name returns the unique, collision-free name of the dependency based on its repository URL.
func (p *Dependency) Name() string {
return reDepName.FindString(p.Repository)
repo := p.Repository
// Trim protocol and credentials
repo = strings.TrimPrefix(repo, "https://")
repo = strings.TrimPrefix(repo, "http://")
if idx := strings.Index(repo, "@"); idx != -1 {
repo = repo[idx+1:]
}
// Replace colon with slash (for SSH format like git@github.com:owner/repo)
repo = strings.ReplaceAll(repo, ":", "/")
// Trim trailing .git and slashes
repo = strings.TrimSuffix(repo, ".git")
repo = strings.TrimSuffix(repo, "/")

parts := strings.Split(repo, "/")
if len(parts) <= 1 {
return repo
}

// Join with underscore and replace dots in host/domain to get a clean, safe folder name
res := strings.Join(parts, "_")
res = strings.ReplaceAll(res, ".", "_")
return res
}

// GetKey returns the normalized key for the dependency (lowercase repository).
Expand Down
12 changes: 6 additions & 6 deletions internal/core/domain/dependency_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,27 +15,27 @@ func TestDependency_Name(t *testing.T) {
{
name: "github repository",
repository: "github.com/hashload/boss",
expected: "boss",
expected: "github_com_hashload_boss",
},
{
name: "gitlab repository",
repository: "gitlab.com/user/project",
expected: "project",
expected: "gitlab_com_user_project",
},
{
name: "bitbucket repository",
repository: "bitbucket.org/team/repo",
expected: "repo",
expected: "bitbucket_org_team_repo",
},
{
name: "nested path repository",
repository: "github.com/org/group/subgroup/repo",
expected: "repo",
expected: "github_com_org_group_subgroup_repo",
},
{
name: "repository with trailing slash",
repository: "github.com/hashload/boss/",
expected: "boss/",
expected: "github_com_hashload_boss",
},
{
name: "simple name",
Expand Down Expand Up @@ -263,7 +263,7 @@ func TestGetDependenciesNames(t *testing.T) {
t.Errorf("GetDependenciesNames() returned %d names, want 3", len(names))
}

expectedNames := []string{"boss", "horse", "repo"}
expectedNames := []string{"github_com_hashload_boss", "github_com_hashload_horse", "github_com_user_repo"}
for i, expected := range expectedNames {
if names[i] != expected {
t.Errorf("GetDependenciesNames()[%d] = %q, want %q", i, names[i], expected)
Expand Down
4 changes: 2 additions & 2 deletions internal/core/services/cache/cache_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,8 @@ func TestService_SaveAndLoadRepositoryDetails(t *testing.T) {
t.Fatalf("LoadRepositoryData() error = %v", err)
}

if info.Name != "horse" {
t.Errorf("LoadRepositoryData().Name = %q, want %q", info.Name, "horse")
if info.Name != "github_com_hashload_horse" {
t.Errorf("LoadRepositoryData().Name = %q, want %q", info.Name, "github_com_hashload_horse")
}

if len(info.Versions) != 3 {
Expand Down
4 changes: 3 additions & 1 deletion internal/core/services/installer/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,9 @@ func (ic *installContext) reportInstallResult(depName, warning string) {
}

func (ic *installContext) shouldSkipDependency(dep domain.Dependency) bool {
if utils.Contains(ic.options.ForceUpdate, dep.Name()) {
if utils.Contains(ic.options.ForceUpdate, dep.Repository) ||
utils.Contains(ic.options.ForceUpdate, dep.Name()) ||
utils.Contains(ic.options.ForceUpdate, ParseDependency(dep.Name())) {
return false
}

Expand Down
10 changes: 5 additions & 5 deletions internal/core/services/paths/paths_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,21 +94,21 @@ func TestEnsureCleanModulesDir_RemovesOldDependencies(t *testing.T) {
t.Fatalf("Failed to create modules dir: %v", err)
}

// Define current dependencies
dep := domain.ParseDependency("github.com/hashload/horse", "^1.0.0")
deps := []domain.Dependency{dep}

// Create an old dependency directory that should be removed
oldDepDir := filepath.Join(modulesDir, "old-dependency")
if err := os.MkdirAll(oldDepDir, 0755); err != nil {
t.Fatalf("Failed to create old dependency dir: %v", err)
}

// Create a current dependency directory that should be kept
currentDepDir := filepath.Join(modulesDir, "horse")
currentDepDir := filepath.Join(modulesDir, dep.Name())
if err := os.MkdirAll(currentDepDir, 0755); err != nil {
t.Fatalf("Failed to create current dependency dir: %v", err)
}

// Define current dependencies
dep := domain.ParseDependency("github.com/hashload/horse", "^1.0.0")
deps := []domain.Dependency{dep}
lock := domain.PackageLock{
Installed: map[string]domain.LockedDependency{},
}
Expand Down
52 changes: 35 additions & 17 deletions utils/librarypath/dproj_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,23 @@ var (

// updateDprojLibraryPath updates the library path in the project file.
func updateDprojLibraryPath(pkg *domain.Package) {
var isLazarus = isLazarus()
var projectNames = GetProjectNames(pkg)
for _, projectName := range projectNames {
if isLazarus {
if isLazarusFile(projectName) {
updateOtherUnitFilesProject(projectName)
} else {
updateLibraryPathProject(projectName)
}
}
}

// isLazarusFile checks if a specific project file is a Lazarus project or package.
func isLazarusFile(filename string) bool {
lower := strings.ToLower(filename)
return strings.HasSuffix(lower, consts.FileExtensionLpi) ||
strings.HasSuffix(lower, consts.FileExtensionLpk)
}

// updateOtherUnitFilesProject updates the other unit files in the project file.
func updateOtherUnitFilesProject(lpiName string) {
doc := etree.NewDocument()
Expand Down Expand Up @@ -114,10 +120,9 @@ func createTagOtherUnitFiles(node *etree.Element) *etree.Element {

// updateGlobalBrowsingPath updates the global browsing path.
func updateGlobalBrowsingPath(pkg *domain.Package) {
var isLazarus = isLazarus()
var projectNames = GetProjectNames(pkg)
for i, projectName := range projectNames {
if !isLazarus {
if !isLazarusFile(projectName) {
updateGlobalBrowsingByProject(projectName, i == 0)
}
}
Expand Down Expand Up @@ -171,24 +176,37 @@ func createTagLibraryPath(node *etree.Element) *etree.Element {

// GetProjectNames returns the project names.
func GetProjectNames(pkg *domain.Package) []string {
var result []string

if len(pkg.Projects) > 0 {
result = pkg.Projects
} else {
files, err := os.ReadDir(env.GetCurrentDir())
if err != nil {
msg.Err("❌ Failed to read directory: %v", err)
return result
}
return getExplicitProjectNames(pkg.Projects)
}
return discoverProjectNames()
}

for _, file := range files {
if reProjectFile.MatchString(file.Name()) {
result = append(result, filepath.Join(env.GetCurrentDir(), file.Name()))
}
func getExplicitProjectNames(projects []string) []string {
result := make([]string, 0, len(projects))
for _, project := range projects {
if filepath.IsAbs(project) {
result = append(result, project)
} else {
result = append(result, filepath.Join(env.GetCurrentDir(), project))
}
}
return result
}

func discoverProjectNames() []string {
files, err := os.ReadDir(env.GetCurrentDir())
if err != nil {
msg.Err("❌ Failed to read directory: %v", err)
return nil
}

var result []string
for _, file := range files {
if reProjectFile.MatchString(file.Name()) {
result = append(result, filepath.Join(env.GetCurrentDir(), file.Name()))
}
}
return result
}

Expand Down
Loading