mirror of
https://github.com/go-gitea/gitea.git
synced 2026-08-09 04:40:49 +00:00
e23fe79e5e
extends the current license detection to support two modes: - legacy which is using classification and was expanded to handle more paths (extensions, different spelling or GNU copying file) - REUSE which avoids classification by relying on the spec dictating that license must be named as SPDX-ID.extension. Newly created repositories will default to REUSE based paths Use of styles at the same time is not allowed by design. Extends the UI to show all the detected licenses and paths to them, deduplicating them per SPDX-ID in database as is in github closes: https://github.com/go-gitea/gitea/issues/28672 --------- Assisted-By: omp:glm5.2 Assisted-By: omp:mimo-v2.5-pro Assisted-By: omp:mimimax-m3 Assisted-By: omp:kimi-k3 Assisted-By: omp:deepseek-v4-flash Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
144 lines
3.9 KiB
Go
144 lines
3.9 KiB
Go
// Copyright 2024 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package repo
|
|
|
|
import (
|
|
"context"
|
|
|
|
"gitea.dev/models/db"
|
|
"gitea.dev/modules/timeutil"
|
|
)
|
|
|
|
func init() {
|
|
db.RegisterModel(new(RepoLicense))
|
|
}
|
|
|
|
type RepoLicense struct { //revive:disable-line:exported
|
|
ID int64 `xorm:"pk autoincr"`
|
|
RepoID int64 `xorm:"UNIQUE(path) NOT NULL"`
|
|
CommitID string
|
|
License string `xorm:"VARCHAR(255) UNIQUE(path) NOT NULL"`
|
|
LicensePath string `xorm:"VARCHAR(255) UNIQUE(path) NOT NULL DEFAULT 'LICENSE'"`
|
|
CreatedUnix timeutil.TimeStamp `xorm:"INDEX CREATED"`
|
|
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX UPDATED"`
|
|
}
|
|
|
|
// RepoLicenseList defines a list of repo licenses
|
|
type RepoLicenseList []*RepoLicense //revive:disable-line:exported
|
|
|
|
func (rll RepoLicenseList) StringList() []string {
|
|
var licenses []string
|
|
for _, rl := range rll {
|
|
licenses = append(licenses, rl.License)
|
|
}
|
|
return licenses
|
|
}
|
|
|
|
type DetectedLicense struct {
|
|
// SPDXID of the license
|
|
SPDXID string
|
|
// LicensePath is in repo path to the license
|
|
LicensePath string
|
|
}
|
|
|
|
// GetRepoLicenses returns the license statistics for a repository
|
|
func GetRepoLicenses(ctx context.Context, repo *Repository) (RepoLicenseList, error) {
|
|
licenses := make(RepoLicenseList, 0)
|
|
if err := db.GetEngine(ctx).Where("`repo_id` = ?", repo.ID).Asc("`license`").Find(&licenses); err != nil {
|
|
return nil, err
|
|
}
|
|
return licenses, nil
|
|
}
|
|
|
|
func GetUniqueRepoLicenses(ctx context.Context, repo *Repository) (RepoLicenseList, error) {
|
|
licenses := make(RepoLicenseList, 0)
|
|
if err := db.GetEngine(ctx).Select("MAX(`id`) AS `id`, `license`, MAX(`license_path`) AS `license_path`, MAX(`commit_id`) AS `commit_id`").
|
|
Where("`repo_id` = ?", repo.ID).
|
|
GroupBy("`license`").
|
|
Find(&licenses); err != nil {
|
|
return nil, err
|
|
}
|
|
return licenses, nil
|
|
}
|
|
|
|
// UpdateRepoLicenses updates the license statistics for repository
|
|
func UpdateRepoLicenses(ctx context.Context, repo *Repository, commitID string, licenses []DetectedLicense) error {
|
|
oldLicenses, err := GetRepoLicenses(ctx, repo)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, license := range licenses {
|
|
upd := false
|
|
for _, o := range oldLicenses {
|
|
// Update already existing license
|
|
if o.License == license.SPDXID {
|
|
o.CommitID = commitID
|
|
o.LicensePath = license.LicensePath
|
|
if _, err := db.GetEngine(ctx).ID(o.ID).Cols("`commit_id`", "`license_path`").Update(o); err != nil {
|
|
return err
|
|
}
|
|
upd = true
|
|
break
|
|
}
|
|
}
|
|
// Insert new license
|
|
if !upd {
|
|
if err := db.Insert(ctx, &RepoLicense{
|
|
RepoID: repo.ID,
|
|
CommitID: commitID,
|
|
License: license.SPDXID,
|
|
LicensePath: license.LicensePath,
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
// Delete old licenses
|
|
licenseToDelete := make([]int64, 0, len(oldLicenses))
|
|
for _, o := range oldLicenses {
|
|
if o.CommitID != commitID {
|
|
licenseToDelete = append(licenseToDelete, o.ID)
|
|
}
|
|
}
|
|
if len(licenseToDelete) > 0 {
|
|
if _, err := db.GetEngine(ctx).In("`id`", licenseToDelete).Delete(&RepoLicense{}); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// CopyLicense Copy originalRepo license information to destRepo (use for forked repo)
|
|
func CopyLicense(ctx context.Context, originalRepo, destRepo *Repository) error {
|
|
repoLicenses, err := GetRepoLicenses(ctx, originalRepo)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(repoLicenses) > 0 {
|
|
newRepoLicenses := make(RepoLicenseList, 0, len(repoLicenses))
|
|
|
|
for _, rl := range repoLicenses {
|
|
newRepoLicense := &RepoLicense{
|
|
RepoID: destRepo.ID,
|
|
CommitID: rl.CommitID,
|
|
License: rl.License,
|
|
LicensePath: rl.LicensePath,
|
|
}
|
|
newRepoLicenses = append(newRepoLicenses, newRepoLicense)
|
|
}
|
|
if err := db.Insert(ctx, &newRepoLicenses); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// CleanRepoLicenses will remove all license record of the repo
|
|
func CleanRepoLicenses(ctx context.Context, repo *Repository) error {
|
|
return db.DeleteBeans(ctx, &RepoLicense{
|
|
RepoID: repo.ID,
|
|
})
|
|
}
|