@@ -34,10 +34,16 @@ type ComponentInfo struct {
3434func main () {
3535 log .Println ("Generating license information..." )
3636
37+ repoRoot , err := locateRepoRoot ()
38+ if err != nil {
39+ log .Fatalf ("Error locating repository root: %v" , err )
40+ }
41+ log .Printf ("INFO: Repository root resolved to %s" , repoRoot )
42+
3743 var info ComponentInfo
3844
3945 // Generate backend licenses
40- backendLicenses , err := generateBackendLicenses ()
46+ backendLicenses , err := generateBackendLicenses (repoRoot )
4147 if err != nil {
4248 log .Printf ("Error generating backend licenses: %v" , err )
4349 } else {
@@ -46,7 +52,7 @@ func main() {
4652 }
4753
4854 // Generate frontend licenses
49- frontendLicenses , err := generateFrontendLicenses ()
55+ frontendLicenses , err := generateFrontendLicenses (repoRoot )
5056 if err != nil {
5157 log .Printf ("Error generating frontend licenses: %v" , err )
5258 } else {
@@ -83,7 +89,7 @@ func main() {
8389 compressed .Len (), float64 (compressed .Len ())/ float64 (len (jsonData ))* 100 )
8490
8591 // Write compressed data to file
86- outputPath := "internal/ license/ licenses.xz"
92+ outputPath := filepath . Join ( repoRoot , "internal" , " license" , " licenses.xz")
8793 log .Printf ("INFO: Writing compressed data to %s" , outputPath )
8894 err = os .MkdirAll (filepath .Dir (outputPath ), 0755 )
8995 if err != nil {
@@ -103,29 +109,32 @@ func main() {
103109 log .Printf (" - Output file: %s" , outputPath )
104110}
105111
106- func generateBackendLicenses () ([]License , error ) {
112+ func generateBackendLicenses (repoRoot string ) ([]License , error ) {
107113 var licenses []License
108114
109115 log .Println ("INFO: Collecting backend Go modules..." )
110116
111- // Read go.mod file directly
112- goModPath := "go.mod"
117+ goModPath := filepath .Join (repoRoot , "go.mod" )
118+ if _ , err := os .Stat (goModPath ); err != nil {
119+ return nil , fmt .Errorf ("failed to locate go.mod at %s: %v" , goModPath , err )
120+ }
121+
113122 data , err := os .ReadFile (goModPath )
114123 if err != nil {
115- return nil , fmt .Errorf ("failed to read go.mod: %v" , err )
124+ return nil , fmt .Errorf ("failed to read go.mod at %s : %v" , goModPath , err )
116125 }
117126
118127 // Parse go.mod content to extract dependencies
119128 depMap := make (map [string ]string ) // path -> version
120129 lines := strings .Split (string (data ), "\n " )
121130 inRequireBlock := false
122131 inReplaceBlock := false
123-
132+
124133 replaceMap := make (map [string ]string ) // original -> replacement
125134
126135 for _ , line := range lines {
127136 line = strings .TrimSpace (line )
128-
137+
129138 // Handle require block
130139 if strings .HasPrefix (line , "require (" ) {
131140 inRequireBlock = true
@@ -148,18 +157,18 @@ func generateBackendLicenses() ([]License, error) {
148157 if len (parts ) == 2 {
149158 original := strings .TrimSpace (parts [0 ])
150159 replacement := strings .TrimSpace (parts [1 ])
151-
160+
152161 // Remove "replace " prefix if present
153162 original = strings .TrimPrefix (original , "replace " )
154-
163+
155164 // Extract module path (before version if present)
156165 if strings .Contains (original , " " ) {
157166 original = strings .Fields (original )[0 ]
158167 }
159168 if strings .Contains (replacement , " " ) {
160169 replacement = strings .Fields (replacement )[0 ]
161170 }
162-
171+
163172 replaceMap [original ] = replacement
164173 }
165174 }
@@ -170,13 +179,13 @@ func generateBackendLicenses() ([]License, error) {
170179 if inRequireBlock || strings .HasPrefix (line , "require " ) {
171180 // Remove "require " prefix if present
172181 line = strings .TrimPrefix (line , "require " )
173-
182+
174183 // Remove comments
175184 if idx := strings .Index (line , "//" ); idx != - 1 {
176185 line = line [:idx ]
177186 }
178187 line = strings .TrimSpace (line )
179-
188+
180189 if line == "" {
181190 continue
182191 }
@@ -186,16 +195,16 @@ func generateBackendLicenses() ([]License, error) {
186195 if len (parts ) >= 2 {
187196 path := parts [0 ]
188197 version := parts [1 ]
189-
198+
190199 if path == "" {
191200 continue
192201 }
193-
202+
194203 // Apply replacements if they exist
195204 if replacement , exists := replaceMap [path ]; exists {
196205 path = replacement
197206 }
198-
207+
199208 depMap [path ] = version
200209 }
201210 }
@@ -303,13 +312,13 @@ func generateBackendLicenses() ([]License, error) {
303312 return licenses , nil
304313}
305314
306- func generateFrontendLicenses () ([]License , error ) {
315+ func generateFrontendLicenses (repoRoot string ) ([]License , error ) {
307316 var licenses []License
308317
309318 log .Println ("INFO: Collecting frontend npm packages..." )
310319
311320 // Read package.json
312- packagePath := "app/ package.json"
321+ packagePath := filepath . Join ( repoRoot , "app" , " package.json")
313322 if _ , err := os .Stat (packagePath ); os .IsNotExist (err ) {
314323 return nil , fmt .Errorf ("package.json not found at %s" , packagePath )
315324 }
@@ -760,3 +769,48 @@ func getGoVersion() string {
760769
761770 return "Unknown"
762771}
772+
773+ // locateRepoRoot returns the directory containing go.mod so relative paths stay stable.
774+ func locateRepoRoot () (string , error ) {
775+ goModPath , err := locateGoModPath ()
776+ if err != nil {
777+ return "" , err
778+ }
779+ return filepath .Dir (goModPath ), nil
780+ }
781+
782+ // locateGoModPath finds the module root so the tool works from any working directory.
783+ func locateGoModPath () (string , error ) {
784+ // Prefer go env GOMOD which respects module-aware mode.
785+ cmd := exec .Command ("go" , "env" , "GOMOD" )
786+ output , err := cmd .Output ()
787+ if err == nil {
788+ path := strings .TrimSpace (string (output ))
789+ if path != "" {
790+ if _ , statErr := os .Stat (path ); statErr == nil {
791+ return path , nil
792+ }
793+ }
794+ }
795+
796+ // Fallback: walk upwards from the current working directory.
797+ dir , err := os .Getwd ()
798+ if err != nil {
799+ return "" , err
800+ }
801+
802+ for {
803+ candidate := filepath .Join (dir , "go.mod" )
804+ if info , statErr := os .Stat (candidate ); statErr == nil && ! info .IsDir () {
805+ return candidate , nil
806+ }
807+
808+ parent := filepath .Dir (dir )
809+ if parent == dir {
810+ break
811+ }
812+ dir = parent
813+ }
814+
815+ return "" , fmt .Errorf ("go.mod not found from current directory or parents" )
816+ }
0 commit comments