feat: add contributor author #14
@@ -0,0 +1,63 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const specAuthorEnv = "GO2SPEC_AUTHOR"
|
||||
|
||||
type specAuthor struct {
|
||||
Name string
|
||||
Email string
|
||||
}
|
||||
|
||||
func (a specAuthor) IsZero() bool {
|
||||
return a.Name == "" && a.Email == ""
|
||||
}
|
||||
|
||||
func (a specAuthor) String() string {
|
||||
return fmt.Sprintf("%s <%s>", a.Name, a.Email)
|
||||
}
|
||||
|
||||
func parseSpecAuthor(raw string) (specAuthor, error) {
|
||||
raw = strings.TrimSpace(raw)
|
||||
if raw == "" {
|
||||
return specAuthor{}, fmt.Errorf("author is empty")
|
||||
}
|
||||
if strings.ContainsAny(raw, "\r\n") {
|
||||
return specAuthor{}, fmt.Errorf("author must be a single line")
|
||||
}
|
||||
if !strings.HasSuffix(raw, ">") {
|
||||
return specAuthor{}, fmt.Errorf("author must use Name <email> format")
|
||||
}
|
||||
|
||||
start := strings.LastIndex(raw, "<")
|
||||
if start < 0 {
|
||||
return specAuthor{}, fmt.Errorf("author must use Name <email> format")
|
||||
}
|
||||
|
||||
name := strings.TrimSpace(raw[:start])
|
||||
email := strings.TrimSpace(strings.TrimSuffix(raw[start+1:], ">"))
|
||||
if name == "" || email == "" {
|
||||
return specAuthor{}, fmt.Errorf("author must include both name and email")
|
||||
}
|
||||
if strings.ContainsAny(email, "<>") {
|
||||
return specAuthor{}, fmt.Errorf("author email must not contain angle brackets")
|
||||
}
|
||||
|
||||
return specAuthor{Name: name, Email: email}, nil
|
||||
}
|
||||
|
||||
func resolveSpecAuthor(raw string) (specAuthor, error) {
|
||||
if strings.TrimSpace(raw) != "" {
|
||||
return parseSpecAuthor(raw)
|
||||
}
|
||||
|
||||
if envAuthor, ok := os.LookupEnv(specAuthorEnv); ok && strings.TrimSpace(envAuthor) != "" {
|
||||
return parseSpecAuthor(envAuthor)
|
||||
}
|
||||
|
||||
return specAuthor{}, nil
|
||||
}
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseSpecAuthor(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
raw string
|
||||
want specAuthor
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid author",
|
||||
raw: "Test Author <author@example.invalid>",
|
||||
want: specAuthor{Name: "Test Author", Email: "author@example.invalid"},
|
||||
},
|
||||
{
|
||||
name: "missing email",
|
||||
raw: "Test Author",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "missing name",
|
||||
raw: "<author@example.invalid>",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "empty email",
|
||||
raw: "Test Author <>",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "multiline author",
|
||||
raw: "Test Author <author@example.invalid>\nOther <other@example.invalid>",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := parseSpecAuthor(tt.raw)
|
||||
if tt.wantErr {
|
||||
if err == nil {
|
||||
t.Fatalf("parseSpecAuthor() succeeded, want error")
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("parseSpecAuthor(): %v", err)
|
||||
}
|
||||
if got != tt.want {
|
||||
t.Fatalf("parseSpecAuthor() = %#v, want %#v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveSpecAuthorUsesExplicitAuthor(t *testing.T) {
|
||||
t.Setenv(specAuthorEnv, "Env Author <env@example.invalid>")
|
||||
|
||||
got, err := resolveSpecAuthor("Test Author <author@example.invalid>")
|
||||
if err != nil {
|
||||
t.Fatalf("resolveSpecAuthor(): %v", err)
|
||||
}
|
||||
want := specAuthor{Name: "Test Author", Email: "author@example.invalid"}
|
||||
if got != want {
|
||||
t.Fatalf("resolveSpecAuthor() = %#v, want %#v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveSpecAuthorUsesEnvAuthor(t *testing.T) {
|
||||
t.Setenv(specAuthorEnv, "Test Author <author@example.invalid>")
|
||||
|
||||
got, err := resolveSpecAuthor("")
|
||||
if err != nil {
|
||||
t.Fatalf("resolveSpecAuthor(): %v", err)
|
||||
}
|
||||
want := specAuthor{Name: "Test Author", Email: "author@example.invalid"}
|
||||
if got != want {
|
||||
t.Fatalf("resolveSpecAuthor() = %#v, want %#v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveSpecAuthorAllowsMissingAuthor(t *testing.T) {
|
||||
t.Setenv(specAuthorEnv, "")
|
||||
|
||||
got, err := resolveSpecAuthor("")
|
||||
if err != nil {
|
||||
t.Fatalf("resolveSpecAuthor(): %v", err)
|
||||
}
|
||||
if !got.IsZero() {
|
||||
t.Fatalf("resolveSpecAuthor() = %#v, want zero author", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveSpecAuthorRejectsInvalidEnvAuthor(t *testing.T) {
|
||||
t.Setenv(specAuthorEnv, "Test Author")
|
||||
|
||||
if _, err := resolveSpecAuthor(""); err == nil {
|
||||
t.Fatalf("resolveSpecAuthor() succeeded with invalid env author, want error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteSPDXHeaderIncludesContributor(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
writeSPDXHeader(&buf, specAuthor{Name: "Test Author", Email: "author@example.invalid"})
|
||||
|
||||
want := "# SPDX-FileContributor: Test Author <author@example.invalid>\n"
|
||||
if !strings.Contains(buf.String(), want) {
|
||||
t.Fatalf("writeSPDXHeader() missing %q in:\n%s", want, buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteSPDXHeaderOmitsEmptyContributor(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
writeSPDXHeader(&buf, specAuthor{})
|
||||
|
||||
if strings.Contains(buf.String(), "SPDX-FileContributor") {
|
||||
t.Fatalf("writeSPDXHeader() included empty contributor:\n%s", buf.String())
|
||||
}
|
||||
}
|
||||
@@ -1000,6 +1000,12 @@ func mainPack(args []string, usage func()) {
|
||||
"Override the program package name, and the source package name too\n"+
|
||||
"when appropriate, e.g. to name github.com/cli/cli as \"gh\"")
|
||||
|
||||
var authorString string
|
||||
flagSet.StringVar(&authorString,
|
||||
"author",
|
||||
"",
|
||||
"Set optional SPDX-FileContributor author in Name <email> format, defaulting to GO2SPEC_AUTHOR when set")
|
||||
|
||||
var allowUnknownHoster bool
|
||||
flagSet.BoolVar(&allowUnknownHoster,
|
||||
"allow_unknown_hoster",
|
||||
@@ -1026,6 +1032,10 @@ func mainPack(args []string, usage func()) {
|
||||
}
|
||||
|
||||
gitRevision = strings.TrimSpace(gitRevision)
|
||||
author, err := resolveSpecAuthor(authorString)
|
||||
if err != nil {
|
||||
log.Fatalf("resolve author: %v", err)
|
||||
}
|
||||
gopkg := flagSet.Arg(0)
|
||||
|
||||
// Remove URL scheme if present (https://, http://, git://, etc.)
|
||||
@@ -1144,7 +1154,7 @@ func mainPack(args []string, usage func()) {
|
||||
}
|
||||
|
||||
if err := writeSpec(dir, gopkg, openRuyiSrc, openRuyiLib, openRuyiProgram, u.version,
|
||||
pkgType, u); err != nil {
|
||||
pkgType, u, author); err != nil {
|
||||
log.Fatalf("Could not create spec file: %v\n", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -71,7 +72,7 @@ func getSpecAssetFilesForGopkg(gopkg string) (specAssetFiles, error) {
|
||||
}
|
||||
|
||||
func writeSpec(dir, gopkg, openRuyiSrc, openRuyiLib, openRuyiProgram, version string,
|
||||
pkgType packageType, u *upstream) error {
|
||||
pkgType packageType, u *upstream, author specAuthor) error {
|
||||
|
||||
f, err := os.Create(filepath.Join(dir, "", openRuyiSrc+".spec"))
|
||||
if err != nil {
|
||||
@@ -111,12 +112,7 @@ func writeSpec(dir, gopkg, openRuyiSrc, openRuyiLib, openRuyiProgram, version st
|
||||
|
||||
// Write the spec file content
|
||||
|
||||
// SPDX header
|
||||
fmt.Fprintf(f, "# SPDX-FileCopyrightText: (C) 2026 Institute of Software, Chinese Academy of Sciences (ISCAS)\n")
|
||||
fmt.Fprintf(f, "# SPDX-FileCopyrightText: (C) 2026 openRuyi Project Contributors\n")
|
||||
fmt.Fprintf(f, "#\n")
|
||||
fmt.Fprintf(f, "# SPDX-License-Identifier: MulanPSL-2.0\n")
|
||||
fmt.Fprintf(f, "\n")
|
||||
writeSPDXHeader(f, author)
|
||||
|
||||
// Macros
|
||||
fmt.Fprintf(f, "%%define _name %s\n", upstreamName)
|
||||
@@ -364,3 +360,14 @@ func convertDependenciesToRPM(goPkgs []string) []string {
|
||||
|
||||
return rpmDeps
|
||||
}
|
||||
|
||||
func writeSPDXHeader(w io.Writer, author specAuthor) {
|
||||
fmt.Fprintf(w, "# SPDX-FileCopyrightText: (C) 2026 Institute of Software, Chinese Academy of Sciences (ISCAS)\n")
|
||||
fmt.Fprintf(w, "# SPDX-FileCopyrightText: (C) 2026 openRuyi Project Contributors\n")
|
||||
if !author.IsZero() {
|
||||
fmt.Fprintf(w, "# SPDX-FileContributor: %s\n", author.String())
|
||||
}
|
||||
fmt.Fprintf(w, "#\n")
|
||||
fmt.Fprintf(w, "# SPDX-License-Identifier: MulanPSL-2.0\n")
|
||||
fmt.Fprintf(w, "\n")
|
||||
}
|
||||
|
||||
@@ -295,7 +295,42 @@ func writeSpecLines(path string, lines []string) error {
|
||||
return w.Flush()
|
||||
}
|
||||
|
||||
func updateSpecVersion(spec *specInfo, newVersion, remoteSHA256 string, updateRemoteAsset bool) error {
|
||||
func ensureSpecContributor(lines []string, author specAuthor) ([]string, error) {
|
||||
if author.IsZero() {
|
||||
return lines, nil
|
||||
}
|
||||
|
||||
contributor := "# SPDX-FileContributor: " + author.String()
|
||||
lastContributorIndex := -1
|
||||
licenseIndex := -1
|
||||
for i, line := range lines {
|
||||
if line == contributor {
|
||||
return lines, nil
|
||||
}
|
||||
if strings.HasPrefix(line, "# SPDX-FileContributor:") {
|
||||
lastContributorIndex = i
|
||||
}
|
||||
if licenseIndex < 0 && strings.HasPrefix(line, "# SPDX-License-Identifier:") {
|
||||
licenseIndex = i
|
||||
}
|
||||
}
|
||||
|
||||
insertIndex := 0
|
||||
if lastContributorIndex >= 0 {
|
||||
insertIndex = lastContributorIndex + 1
|
||||
} else if licenseIndex > 0 && strings.TrimSpace(lines[licenseIndex-1]) == "#" {
|
||||
insertIndex = licenseIndex - 1
|
||||
} else if licenseIndex >= 0 {
|
||||
insertIndex = licenseIndex
|
||||
}
|
||||
|
||||
lines = append(lines, "")
|
||||
copy(lines[insertIndex+1:], lines[insertIndex:])
|
||||
lines[insertIndex] = contributor
|
||||
return lines, nil
|
||||
}
|
||||
|
||||
func updateSpecVersion(spec *specInfo, newVersion, remoteSHA256 string, updateRemoteAsset bool, author specAuthor) error {
|
||||
lines, err := readSpecLines(spec.FilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -329,6 +364,10 @@ func updateSpecVersion(spec *specInfo, newVersion, remoteSHA256 string, updateRe
|
||||
return fmt.Errorf("Version line not found in %s", spec.FilePath)
|
||||
}
|
||||
if !updateRemoteAsset {
|
||||
lines, err = ensureSpecContributor(lines, author)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return writeSpecLines(spec.FilePath, lines)
|
||||
}
|
||||
if source0RemoteAssetIndex >= 0 {
|
||||
@@ -346,6 +385,11 @@ func updateSpecVersion(spec *specInfo, newVersion, remoteSHA256 string, updateRe
|
||||
lines = append(lines[:source0Index], append([]string{insert}, lines[source0Index:]...)...)
|
||||
}
|
||||
|
||||
lines, err = ensureSpecContributor(lines, author)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return writeSpecLines(spec.FilePath, lines)
|
||||
}
|
||||
|
||||
@@ -354,7 +398,7 @@ type downloadResult struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func verifyAndUpdateSpec(ctx context.Context, spec *specInfo, dryRun bool) error {
|
||||
func verifyAndUpdateSpec(ctx context.Context, spec *specInfo, dryRun bool, author specAuthor) error {
|
||||
err := verifyDownload(ctx, spec)
|
||||
if err != nil {
|
||||
return fmt.Errorf("download verification failed: %w", err)
|
||||
@@ -372,7 +416,7 @@ func verifyAndUpdateSpec(ctx context.Context, spec *specInfo, dryRun bool) error
|
||||
return fmt.Errorf("compute remote asset sha256: %w", err)
|
||||
}
|
||||
|
||||
if err := updateSpecVersion(spec, newVer, remoteSHA256, updateRemoteAsset); err != nil {
|
||||
if err := updateSpecVersion(spec, newVer, remoteSHA256, updateRemoteAsset, author); err != nil {
|
||||
return fmt.Errorf("update spec file: %w", err)
|
||||
}
|
||||
}
|
||||
@@ -380,7 +424,7 @@ func verifyAndUpdateSpec(ctx context.Context, spec *specInfo, dryRun bool) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func processUpdate(ctx context.Context, spec *specInfo, dryRun bool) error {
|
||||
func processUpdate(ctx context.Context, spec *specInfo, dryRun bool, author specAuthor) error {
|
||||
err := verifyDownload(ctx, spec)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -398,7 +442,7 @@ func processUpdate(ctx context.Context, spec *specInfo, dryRun bool) error {
|
||||
return fmt.Errorf("compute remote asset sha256: %w", err)
|
||||
}
|
||||
|
||||
if err := updateSpecVersion(spec, newVer, remoteSHA256, updateRemoteAsset); err != nil {
|
||||
if err := updateSpecVersion(spec, newVer, remoteSHA256, updateRemoteAsset, author); err != nil {
|
||||
return fmt.Errorf("update spec file: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
+14
-6
@@ -312,6 +312,7 @@ func mainUpdate(args []string) int {
|
||||
fs.SetOutput(os.Stderr)
|
||||
concurrency := fs.Int("j", 3, "number of concurrent checks")
|
||||
dryRun := fs.Bool("n", false, "dry run: detect updates only, do not modify files")
|
||||
authorString := fs.String("author", "", "Set optional SPDX-FileContributor author in Name <email> format, defaulting to GO2SPEC_AUTHOR when set")
|
||||
fs.Usage = func() {
|
||||
fmt.Fprintf(fs.Output(), "Usage: %s update [options] <SPECS_DIR>\n\n", os.Args[0])
|
||||
fmt.Fprintf(fs.Output(), "Check Go module spec files for updates.\n\n")
|
||||
@@ -389,13 +390,20 @@ func mainUpdate(args []string) int {
|
||||
sp.Error = err.Error()
|
||||
out.print(fmt.Sprintf(" FAIL: %s - %v", sp.GoImportPath, err))
|
||||
atomic.AddInt32(&c.upErrors, 1)
|
||||
} else if err := updateSpecVersion(sp, newVer, remoteSHA256, updateRemoteAsset); err != nil {
|
||||
sp.Error = err.Error()
|
||||
out.print(fmt.Sprintf(" FAIL: %s - %v", sp.GoImportPath, err))
|
||||
atomic.AddInt32(&c.upErrors, 1)
|
||||
} else {
|
||||
atomic.AddInt32(&c.modified, 1)
|
||||
out.print(fmt.Sprintf(" OK: %s updated to %s", sp.GoImportPath, newVer))
|
||||
author, err := resolveSpecAuthor(*authorString)
|
||||
if err != nil {
|
||||
sp.Error = err.Error()
|
||||
out.print(fmt.Sprintf(" FAIL: %s - %v", sp.GoImportPath, err))
|
||||
atomic.AddInt32(&c.upErrors, 1)
|
||||
} else if err := updateSpecVersion(sp, newVer, remoteSHA256, updateRemoteAsset, author); err != nil {
|
||||
sp.Error = err.Error()
|
||||
out.print(fmt.Sprintf(" FAIL: %s - %v", sp.GoImportPath, err))
|
||||
atomic.AddInt32(&c.upErrors, 1)
|
||||
} else {
|
||||
atomic.AddInt32(&c.modified, 1)
|
||||
out.print(fmt.Sprintf(" OK: %s updated to %s", sp.GoImportPath, newVer))
|
||||
}
|
||||
}
|
||||
}
|
||||
atomic.AddInt32(&c.processed, 1)
|
||||
|
||||
+189
-5
@@ -15,6 +15,8 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var testSpecAuthor = specAuthor{Name: "Test Author", Email: "author@example.invalid"}
|
||||
|
||||
func TestUpdateSpecVersionPreservesFormatting(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -32,7 +34,7 @@ func TestUpdateSpecVersionPreservesFormatting(t *testing.T) {
|
||||
}
|
||||
|
||||
spec := &specInfo{FilePath: specPath}
|
||||
if err := updateSpecVersion(spec, "2.0.0", strings.Repeat("a", 64), true); err != nil {
|
||||
if err := updateSpecVersion(spec, "2.0.0", strings.Repeat("a", 64), true, testSpecAuthor); err != nil {
|
||||
t.Fatalf("updateSpecVersion: %v", err)
|
||||
}
|
||||
|
||||
@@ -66,7 +68,7 @@ func TestUpdateSpecVersionReturnsErrorWhenVersionLineMissing(t *testing.T) {
|
||||
}
|
||||
|
||||
spec := &specInfo{FilePath: specPath}
|
||||
if err := updateSpecVersion(spec, "2.0.0", strings.Repeat("a", 64), true); err == nil {
|
||||
if err := updateSpecVersion(spec, "2.0.0", strings.Repeat("a", 64), true, testSpecAuthor); err == nil {
|
||||
t.Fatal("expected updateSpecVersion to fail when Version line is missing")
|
||||
}
|
||||
}
|
||||
@@ -223,7 +225,7 @@ func TestUpdateSpecVersionOnlyUpdatesSource0RemoteAsset(t *testing.T) {
|
||||
}
|
||||
|
||||
spec := &specInfo{FilePath: specPath}
|
||||
if err := updateSpecVersion(spec, "2.0.0", source0Hash, true); err != nil {
|
||||
if err := updateSpecVersion(spec, "2.0.0", source0Hash, true, testSpecAuthor); err != nil {
|
||||
t.Fatalf("updateSpecVersion: %v", err)
|
||||
}
|
||||
|
||||
@@ -258,7 +260,7 @@ func TestUpdateSpecVersionClearsRemoteAssetWhenHashEmpty(t *testing.T) {
|
||||
}
|
||||
|
||||
spec := &specInfo{FilePath: specPath}
|
||||
if err := updateSpecVersion(spec, "2.0.0", "", true); err != nil {
|
||||
if err := updateSpecVersion(spec, "2.0.0", "", true, testSpecAuthor); err != nil {
|
||||
t.Fatalf("updateSpecVersion: %v", err)
|
||||
}
|
||||
|
||||
@@ -296,7 +298,7 @@ func TestUpdateSpecVersionKeepsRemoteAssetWhenUpdateDisabled(t *testing.T) {
|
||||
}
|
||||
|
||||
spec := &specInfo{FilePath: specPath}
|
||||
if err := updateSpecVersion(spec, "2.0.0", "", false); err != nil {
|
||||
if err := updateSpecVersion(spec, "2.0.0", "", false, testSpecAuthor); err != nil {
|
||||
t.Fatalf("updateSpecVersion: %v", err)
|
||||
}
|
||||
|
||||
@@ -314,6 +316,188 @@ func TestUpdateSpecVersionKeepsRemoteAssetWhenUpdateDisabled(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateSpecVersionDoesNotDuplicateExistingContributor(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dir := t.TempDir()
|
||||
specPath := filepath.Join(dir, "test.spec")
|
||||
contributor := "# SPDX-FileContributor: " + testSpecAuthor.String()
|
||||
original := strings.Join([]string{
|
||||
"# SPDX-FileCopyrightText: (C) 2026 openRuyi Project Contributors",
|
||||
contributor,
|
||||
"# SPDX-License-Identifier: MulanPSL-2.0",
|
||||
"Name: golang-test",
|
||||
"Version: 1.0.0",
|
||||
}, "\n")
|
||||
if err := os.WriteFile(specPath, []byte(original), 0o644); err != nil {
|
||||
t.Fatalf("write spec: %v", err)
|
||||
}
|
||||
|
||||
spec := &specInfo{FilePath: specPath}
|
||||
if err := updateSpecVersion(spec, "2.0.0", "", false, testSpecAuthor); err != nil {
|
||||
t.Fatalf("updateSpecVersion: %v", err)
|
||||
}
|
||||
|
||||
updated, err := os.ReadFile(specPath)
|
||||
if err != nil {
|
||||
t.Fatalf("read spec: %v", err)
|
||||
}
|
||||
if count := strings.Count(string(updated), contributor); count != 1 {
|
||||
t.Fatalf("contributor count = %d, want 1:\n%s", count, string(updated))
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateSpecVersionAppendsDifferentContributor(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dir := t.TempDir()
|
||||
specPath := filepath.Join(dir, "test.spec")
|
||||
original := strings.Join([]string{
|
||||
"# SPDX-FileContributor: Existing Author <existing@example.invalid>",
|
||||
"# SPDX-License-Identifier: MulanPSL-2.0",
|
||||
"Name: golang-test",
|
||||
"Version: 1.0.0",
|
||||
}, "\n")
|
||||
if err := os.WriteFile(specPath, []byte(original), 0o644); err != nil {
|
||||
t.Fatalf("write spec: %v", err)
|
||||
}
|
||||
|
||||
spec := &specInfo{FilePath: specPath}
|
||||
if err := updateSpecVersion(spec, "2.0.0", "", false, testSpecAuthor); err != nil {
|
||||
t.Fatalf("updateSpecVersion: %v", err)
|
||||
}
|
||||
|
||||
updated, err := os.ReadFile(specPath)
|
||||
if err != nil {
|
||||
t.Fatalf("read spec: %v", err)
|
||||
}
|
||||
want := "# SPDX-FileContributor: Existing Author <existing@example.invalid>\n# SPDX-FileContributor: " + testSpecAuthor.String() + "\n# SPDX-License-Identifier:"
|
||||
if !strings.Contains(string(updated), want) {
|
||||
t.Fatalf("contributor was not appended after existing contributor:\n%s", string(updated))
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateSpecVersionInsertsContributorBeforeSeparator(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dir := t.TempDir()
|
||||
specPath := filepath.Join(dir, "test.spec")
|
||||
original := strings.Join([]string{
|
||||
"# SPDX-FileCopyrightText: (C) 2026 Institute of Software, Chinese Academy of Sciences (ISCAS)",
|
||||
"# SPDX-FileCopyrightText: (C) 2026 openRuyi Project Contributors",
|
||||
"#",
|
||||
"# SPDX-License-Identifier: MulanPSL-2.0",
|
||||
"Name: golang-test",
|
||||
"Version: 1.0.0",
|
||||
}, "\n")
|
||||
if err := os.WriteFile(specPath, []byte(original), 0o644); err != nil {
|
||||
t.Fatalf("write spec: %v", err)
|
||||
}
|
||||
|
||||
spec := &specInfo{FilePath: specPath}
|
||||
if err := updateSpecVersion(spec, "2.0.0", "", false, testSpecAuthor); err != nil {
|
||||
t.Fatalf("updateSpecVersion: %v", err)
|
||||
}
|
||||
|
||||
updated, err := os.ReadFile(specPath)
|
||||
if err != nil {
|
||||
t.Fatalf("read spec: %v", err)
|
||||
}
|
||||
want := "# SPDX-FileCopyrightText: (C) 2026 openRuyi Project Contributors\n# SPDX-FileContributor: " + testSpecAuthor.String() + "\n#\n# SPDX-License-Identifier:"
|
||||
if !strings.Contains(string(updated), want) {
|
||||
t.Fatalf("contributor was not inserted before separator:\n%s", string(updated))
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateSpecVersionInsertsContributorBeforeLicense(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dir := t.TempDir()
|
||||
specPath := filepath.Join(dir, "test.spec")
|
||||
original := strings.Join([]string{
|
||||
"# SPDX-FileCopyrightText: (C) 2026 openRuyi Project Contributors",
|
||||
"# SPDX-License-Identifier: MulanPSL-2.0",
|
||||
"Name: golang-test",
|
||||
"Version: 1.0.0",
|
||||
}, "\n")
|
||||
if err := os.WriteFile(specPath, []byte(original), 0o644); err != nil {
|
||||
t.Fatalf("write spec: %v", err)
|
||||
}
|
||||
|
||||
spec := &specInfo{FilePath: specPath}
|
||||
if err := updateSpecVersion(spec, "2.0.0", "", false, testSpecAuthor); err != nil {
|
||||
t.Fatalf("updateSpecVersion: %v", err)
|
||||
}
|
||||
|
||||
updated, err := os.ReadFile(specPath)
|
||||
if err != nil {
|
||||
t.Fatalf("read spec: %v", err)
|
||||
}
|
||||
want := "# SPDX-FileContributor: " + testSpecAuthor.String() + "\n# SPDX-License-Identifier:"
|
||||
if !strings.Contains(string(updated), want) {
|
||||
t.Fatalf("contributor was not inserted before license:\n%s", string(updated))
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateSpecVersionInsertsContributorAtStartWithoutHeader(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dir := t.TempDir()
|
||||
specPath := filepath.Join(dir, "test.spec")
|
||||
original := strings.Join([]string{
|
||||
"Name: golang-test",
|
||||
"Version: 1.0.0",
|
||||
}, "\n")
|
||||
if err := os.WriteFile(specPath, []byte(original), 0o644); err != nil {
|
||||
t.Fatalf("write spec: %v", err)
|
||||
}
|
||||
|
||||
spec := &specInfo{FilePath: specPath}
|
||||
if err := updateSpecVersion(spec, "2.0.0", "", false, testSpecAuthor); err != nil {
|
||||
t.Fatalf("updateSpecVersion: %v", err)
|
||||
}
|
||||
|
||||
updated, err := os.ReadFile(specPath)
|
||||
if err != nil {
|
||||
t.Fatalf("read spec: %v", err)
|
||||
}
|
||||
wantPrefix := "# SPDX-FileContributor: " + testSpecAuthor.String() + "\nName:"
|
||||
if !strings.HasPrefix(string(updated), wantPrefix) {
|
||||
t.Fatalf("contributor was not inserted at start:\n%s", string(updated))
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateSpecVersionOmitsMissingContributorAuthor(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dir := t.TempDir()
|
||||
specPath := filepath.Join(dir, "test.spec")
|
||||
original := strings.Join([]string{
|
||||
"Name: golang-test",
|
||||
"Version: 1.0.0",
|
||||
}, "\n")
|
||||
if err := os.WriteFile(specPath, []byte(original), 0o644); err != nil {
|
||||
t.Fatalf("write spec: %v", err)
|
||||
}
|
||||
|
||||
spec := &specInfo{FilePath: specPath}
|
||||
if err := updateSpecVersion(spec, "2.0.0", "", false, specAuthor{}); err != nil {
|
||||
t.Fatalf("updateSpecVersion: %v", err)
|
||||
}
|
||||
|
||||
updated, err := os.ReadFile(specPath)
|
||||
if err != nil {
|
||||
t.Fatalf("read spec: %v", err)
|
||||
}
|
||||
got := string(updated)
|
||||
if !strings.Contains(got, "Version: 2.0.0") {
|
||||
t.Fatalf("version was not updated:\n%s", got)
|
||||
}
|
||||
if strings.Contains(got, "SPDX-FileContributor") {
|
||||
t.Fatalf("empty author should not add contributor:\n%s", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolvedSource0URLSkipsPinnedCommit(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user