| // Copyright 2022 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package project |
| |
| import ( |
| "fmt" |
| "path/filepath" |
| "strings" |
| |
| spdx "github.com/spdx/tools-golang/spdx/v2_2" |
| "go.fuchsia.dev/fuchsia/tools/check-licenses/file" |
| "go.fuchsia.dev/fuchsia/tools/check-licenses/project/readme" |
| ) |
| |
| // Project struct follows the format of README.fuchsia files. |
| // For more info, see the following article: |
| // |
| // https://fuchsia.dev/fuchsia-src/development/source_code/third-party-metadata |
| type Project struct { |
| Root string `json:"root"` |
| Name string `json:"name"` |
| URL string |
| LicenseFiles []*file.File `licenseFiles` |
| RegularFiles []*file.File |
| SearchableRegularFiles []*file.File |
| ReadmeFile *readme.Readme |
| |
| // Projects that this project depends on. |
| // Constructed from the GN dependency tree. |
| Children map[string]*Project |
| |
| // SPDX fields |
| Package *spdx.Package "json:'package'" |
| SPDXID string `json:"spdxid"` |
| |
| // Compliance fields. |
| BeingSurfaced bool |
| SourceCodeIncluded bool |
| } |
| |
| // Order implements sort.Interface for []*Project based on the Root field. |
| type Order []*Project |
| |
| func (a Order) Len() int { return len(a) } |
| func (a Order) Swap(i, j int) { a[i], a[j] = a[j], a[i] } |
| func (a Order) Less(i, j int) bool { return a[i].Root < a[j].Root } |
| |
| // NewProject creates a Project object from a README.fuchsia file. |
| func NewProject(r *readme.Readme, projectRootPath string) (*Project, error) { |
| var err error |
| |
| // Make all projectRootPath values relative to Config.FuchsiaDir. |
| if strings.Contains(projectRootPath, Config.FuchsiaDir) { |
| projectRootPath, err = filepath.Rel(Config.FuchsiaDir, projectRootPath) |
| if err != nil { |
| return nil, err |
| } |
| } |
| |
| // See if we've already processed this project. |
| // If so, return the previously created instance. |
| if _, ok := AllProjects[projectRootPath]; ok { |
| plusVal(NumPreviousProjectRetrieved, projectRootPath) |
| return AllProjects[projectRootPath], nil |
| } |
| |
| p := &Project{ |
| Root: projectRootPath, |
| Name: r.Name, |
| URL: r.URL, |
| ReadmeFile: r, |
| LicenseFiles: make([]*file.File, 0), |
| RegularFiles: make([]*file.File, 0), |
| SearchableRegularFiles: make([]*file.File, 0), |
| Children: make(map[string]*Project, 0), |
| } |
| |
| for _, l := range r.Licenses { |
| if l.LicenseFile == "" { |
| continue |
| } |
| if l.LicenseFileFormat == "" { |
| l.LicenseFileFormat = "Single License File" |
| } |
| |
| path := filepath.Join(p.Root, l.LicenseFile) |
| f, err := file.LoadFile(path, file.FileType(l.LicenseFileFormat), r.Name) |
| if err != nil { |
| // Readmes in asset dirs may occasionally go out of sync with |
| // the project they describe. This is to prevent roll breakages. |
| if r.IsAssetDirReadme { |
| continue |
| } |
| |
| return nil, fmt.Errorf("failed to load license file %s: %w\n", path, err) |
| } |
| f.SetURL(l.LicenseFileURL) |
| p.LicenseFiles = append(p.LicenseFiles, f) |
| l.LicenseFileRef = f |
| } |
| |
| AllProjects[p.Root] = p |
| plusVal(NumProjects, p.Root) |
| |
| return p, nil |
| } |
| |
| func (p *Project) AddFile(f *file.File) error { |
| if f.FileType() != file.RegularFile { |
| // This could be a license file. |
| // Make sure this file isn't already in the list. |
| for _, l := range p.LicenseFiles { |
| if l.RelPath() == f.RelPath() { |
| return nil |
| } |
| } |
| |
| p.LicenseFiles = append(p.LicenseFiles, f) |
| |
| relPath, _ := filepath.Rel(p.Root, f.RelPath()) |
| p.ReadmeFile.AddLicense(relPath, f) |
| |
| return nil |
| } |
| |
| p.RegularFiles = append(p.RegularFiles, f) |
| |
| ext := filepath.Ext(f.RelPath()) |
| if _, ok := file.Config.Extensions[ext]; ok { |
| p.SearchableRegularFiles = append(p.SearchableRegularFiles, f) |
| } |
| |
| return nil |
| } |
| |
| func getGitInfo(path string) (string, string, error) { |
| url, err := git.GetURL(ctx, path) |
| if err != nil { |
| return "", "", fmt.Errorf("Failed to get git URL for path %s: %w", path, err) |
| } |
| |
| // Turquoise repos return "sso" urls, so convert them to |
| // http links that a user can view in a browser. |
| url = strings.ReplaceAll(url, "sso://turquoise-internal", "https://turquoise-internal.googlesource.com") |
| |
| // Retrieve the hash URL for the current commit. |
| hash, err := git.GetCommitHash(ctx, path) |
| if err != nil { |
| return "", "", fmt.Errorf("Failed to get git commit hash for path %s: %w", path, err) |
| } |
| |
| return url, hash, nil |
| } |