diff options
| author | Shulhan <ms@kilabit.info> | 2026-01-08 19:08:14 +0700 |
|---|---|---|
| committer | Shulhan <ms@kilabit.info> | 2026-01-08 20:40:27 +0700 |
| commit | a10b07ddbd21c7725df43f3e37b9d7709fbe66b7 (patch) | |
| tree | 0d2704626e6b489658a1a068f85a8942c0a07fb6 | |
| parent | 496c812f6e14b77408b71f7a35a3d755b8bbbf36 (diff) | |
| download | spdxconv-a10b07ddbd21c7725df43f3e37b9d7709fbe66b7.tar.xz | |
cmd/spdxconv: implement "init" command
The init command create the spdxconv configuration file in the current
directory.
| -rw-r--r-- | .gitignore | 3 | ||||
| -rw-r--r-- | README.md | 95 | ||||
| l--------- | _doc/index.md | 1 | ||||
| -rw-r--r-- | cmd/spdxconv/main.go | 69 | ||||
| -rw-r--r-- | config.go | 24 | ||||
| -rw-r--r-- | spdxconv.go | 18 | ||||
| -rw-r--r-- | spdxconv_test.go | 29 | ||||
| -rw-r--r-- | testdata/loadConfig/config_exists/spdxconv.cfg | 11 |
8 files changed, 208 insertions, 42 deletions
@@ -1,7 +1,8 @@ # SPDX-License-Identifier: GPL-3.0-only -# SPDX-FileCopyrightText: 2025 M. Shulhan <ms@kilabit.info> +# SPDX-FileCopyrightText: 2026 M. Shulhan <ms@kilabit.info> _coverage/ +_doc/index.html cover.html cover.out cover.txt @@ -1,23 +1,46 @@ <!-- SPDX-License-Identifier: GPL-3.0-only -SPDX-FileCopyrightText: 2025 M. Shulhan <ms@kilabit.info> +SPDX-FileCopyrightText: 2026 M. Shulhan <ms@kilabit.info> --> # spdxconv -spdxconv is a tool to convert and insert the SPDX license headers. +spdxconv is a tool to convert and insert the SPDX identifiers. ## Background -Converting the license and copyright in the source codes to become compliant +Converting the license and copyright in the project to become compliant with the SPDX headers is very tedious works, especially if we have so many files with different year, copyright, and licenses. -This program help to do that by using pattern-matching, search, replace, and -sometimes deletion. +This program help to do that by using pattern matching, search, replace, and +deletion. ## Usage +Converting to SPDX is trial-and-error tasks. +This tool does not guarantee that the conversion will success in one cycle. +So, to help with it, we provides three commands: `init`, `scan`, and +`apply`. + +The init command create the "spdxconv.cfg" configuration in the current +directory. +The configuration file teach the tool how to scan and apply the license and +copyright. + +The scan command list the files that need to be converted or inserted with +SPDX identifiers into a file named "spdxconv.report". +User then can inspect and modify the report to see and edit which files +needs to proceed or not. + +The apply command read the `spdxconv.report` and apply the license and +copyright as stated. + +User then can repeat edit "spdxconv.cfg", "scan" and "apply" command +multiple times, until they satisfied with the result. + +### init command + The first thing to do is to generate the configuration file using ``` @@ -28,36 +51,50 @@ This will create the `spdxconv.cfg` file in the current directory with the following content, ``` +# SPDX-License-Identifier: CC0-1.0 +# SPDX-FileCopyrightText: 2025 M. Shulhan <ms@kilabit.info> + [default] license_identifier = file_copyright_text = max_line_match = 10 -[match_license] -pattern = ^(//+|#+)*\s+(.*)governed by a BSD-style(.*)$ +[match-license] +pattern = "^(//+|#+)\\s+(.*)governed by a BSD-style(.*)$" license_identifier = BSD-3-Clause delete_match = true -delete_line_pattern = ^(//+|#+)*\s+license that(.*)$ +delete_line_pattern = "^(//+|#+)\\s+license that(.*)$" -[match_copyright] -pattern = ^(//+|#+)*\s+Copyright\s+(?<year>\d{4},?\s+(?<holder>.*)\s+<*(?<email>.*)>.*$ +[match-copyright] +pattern = "^(//+|#+)\\s+Copyright\\s+(?<year>\\d{4}),?\\s+(?<author>.*)\\s+<*(?<contact>.*)>.*$" ``` -The next subsection explain each of this section in the configuration file. +The configuration use the `ini` file format. +You need to modify it by filling the "default" section, and can add another +match-license and match-copyright pattern as required. +For quick references here are several rules that you need to be aware of, + +- The regex value must be enclosed in double quote +- Backslash '\\' character must be escaped. For example, regex for space + "\\s" must be written as "\\\\s". + +The next subsection explain the content of configuration file and how it +affect the program during scan and apply. ### default section -First is the `[default]` section. This section define the default license identifier and copyright text to be -inserted into file if no match_license or match_copyright found on the file. +inserted into file if no `match-license` or `match-copyright` found in the +file. You should fill the `license_identifier` and `file_copyright_text` before continue running the program. The `max_line_match` define the number of lines to be searched at the -top and bottom of file for match_license and match_copyright before the -program insert the default values. +top and bottom of file for `SPDX-*` identifiers, `match-license`, and +`match-copyright` before the program insert the default values. +The default values is 10. -### match_license section +### match-license section <!-- REUSE-IgnoreStart --> @@ -66,17 +103,33 @@ with "SPDX-License-Identifier:". <!-- REUSE-IgnoreEnd --> -If there is a match, at the top or bottom, the scan will stop. +If there is a match, at the top or bottom, the scan will stop and continue +for processing copyright. If there is no match it will search for a line that match with "pattern" regular expression. If there is a line that match with it, the value in -"match_license::license_identifier" will replace the +"match-license::license_identifier" will replace the "default::license_identifier" value. -If the "delete_match" is true, it will delete the line from the file. +If the "delete_match" is true, it will delete the matched line from the +file. If there is "delete_line_pattern" defined, it will search for line that match -with that regular expression and delete it. -The "delete_line_pattern" can be defined zero or more times. +with that pattern and delete it. +The "delete_line_pattern" can be defined zero or multiple times. + +### scan command + +The scan command scan the files that need to be converted or inserted with +SPDX identifiers in the current directory. + +The result of scan is stored inside a file named "spdxconv.report". +User then can inspect and modify the report to see and edit which files needs +to proceed or not. + +### apply command + +The apply command read the "spdxconv.report" and apply the license and +copyright as stated on each file in the report. ## References diff --git a/_doc/index.md b/_doc/index.md new file mode 120000 index 0000000..32d46ee --- /dev/null +++ b/_doc/index.md @@ -0,0 +1 @@ +../README.md
\ No newline at end of file diff --git a/cmd/spdxconv/main.go b/cmd/spdxconv/main.go new file mode 100644 index 0000000..f69fd5d --- /dev/null +++ b/cmd/spdxconv/main.go @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-3.0-only +// SPDX-FileCopyrightText: 2026 M. Shulhan <ms@kilabit.info> + +package main + +import ( + "flag" + "log" + "os" + "path/filepath" + "strings" + + "git.sr.ht/~shulhan/spdxconv" +) + +var programName = filepath.Base(os.Args[0]) + +func main() { + log.SetFlags(0) + flag.Parse() + + cmd := strings.ToLower(flag.Arg(0)) + switch cmd { + case `init`: + err := spdxconv.Init() + if err != nil { + log.Fatal(err) + } + return + + case `help`: + usage() + os.Exit(1) + default: + log.Fatalf(`unknown command, use "%s help" for more information.`, + programName) + } +} + +func usage() { + log.Println(` +spdxconv is a tool to convert and insert the SPDX identifiers. + +Usage: + spdxconv <command> [arguments] + +The commands are: + +init + Create the "spdxconv.cfg" configuration in the current directory. + The configuration file teach the tool how to scan and apply the + license and copyright. + +scan + The scan command scan the files that need to be converted or inserted + with SPDX identifiers in the current directory. + + The result of scan is stored into a file named "spdxconv.report". + User then can inspect and modify the report to see and edit which + files needs to proceed or not. + +apply + The apply command read the "spdxconv.report" and apply the license and + copyright as stated on each file in the report. + +User then can repeat edit "spdxconv.cfg", "scan" and "apply" command multiple +times, until they satisfied with the result.`) + flag.PrintDefaults() +} @@ -1,9 +1,29 @@ // SPDX-License-Identifier: GPL-3.0-only -// SPDX-FileCopyrightText: 2025 M. Shulhan <ms@kilabit.info> +// SPDX-FileCopyrightText: 2026 M. Shulhan <ms@kilabit.info> package spdxconv -import "errors" +import ( + "errors" +) + +var configTemplate string = `# SPDX-License-Identifier: CC0-1.0 +# SPDX-FileCopyrightText: 2026 M. Shulhan <ms@kilabit.info> + +[default] +license_identifier = +file_copyright_text = +max_line_match = 10 + +[match-license] +pattern = "^(//+|#+)\\s+(.*)governed by a BSD-style(.*)$" +license_identifier = BSD-3-Clause +delete_match = true +delete_line_pattern = "^(//+|#+)\\s+license that(.*)$" + +[match-copyright] +pattern = "^(//+|#+)\\s+Copyright\\s+(?<year>\\d{4}),?\\s+(?<author>.*)\\s+<*(?<contact>.*)>.*$" +` type config struct { LicenseIdentifier string `ini:"default::license_identifier"` diff --git a/spdxconv.go b/spdxconv.go index 3aaabf3..25ebec0 100644 --- a/spdxconv.go +++ b/spdxconv.go @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-only -// SPDX-FileCopyrightText: 2025 M. Shulhan <ms@kilabit.info> +// SPDX-FileCopyrightText: 2026 M. Shulhan <ms@kilabit.info> package spdxconv @@ -14,6 +14,9 @@ import ( "git.sr.ht/~shulhan/pakakeh.go/lib/ini" ) +// ConfigFile the file name for configuration file. +const ConfigFile = `spdxconv.cfg` + // SPDXConv the main type for converting files to SPDX format. type SPDXConv struct { scm sourceCodeManagement @@ -64,6 +67,12 @@ func Apply(path string) (err error) { return nil } +// Init create the configuration file [ConfigFile] in the current directory. +func Init() (err error) { + err = os.WriteFile(ConfigFile, []byte(configTemplate), 0600) + return err +} + // New initialize new instance of SPDXConv. func New(path string) (conv *SPDXConv, err error) { var logp = `New` @@ -108,11 +117,10 @@ func New(path string) (conv *SPDXConv, err error) { return conv, nil } -// loadConfig load the program configuration from file `spdxconv.cfg` in the -// current directory. +// loadConfig load the program [ConfigFile] from the current directory. func (conv *SPDXConv) loadConfig(dir string) (err error) { var logp = `loadConfig` - var pathcfg = filepath.Join(dir, `spdxconv.cfg`) + var pathcfg = filepath.Join(dir, ConfigFile) var rawcfg []byte rawcfg, err = os.ReadFile(pathcfg) if err != nil { @@ -182,7 +190,7 @@ func (conv *SPDXConv) scanFiles(listDir []string) (listFile []string, err error) var commonIgnore = map[string]struct{}{ `.git`: struct{}{}, `node_modules`: struct{}{}, - `spdxconv.cfg`: struct{}{}, + ConfigFile: struct{}{}, `vendor`: struct{}{}, } var suffixLicense = `.license` diff --git a/spdxconv_test.go b/spdxconv_test.go index d2c7494..236468d 100644 --- a/spdxconv_test.go +++ b/spdxconv_test.go @@ -1,9 +1,10 @@ // SPDX-License-Identifier: GPL-3.0-only -// SPDX-FileCopyrightText: 2025 M. Shulhan <ms@kilabit.info> +// SPDX-FileCopyrightText: 2026 M. Shulhan <ms@kilabit.info> package spdxconv import ( + "os" "regexp" "testing" @@ -11,6 +12,22 @@ import ( "git.sr.ht/~shulhan/pakakeh.go/lib/test" ) +func TestInit(t *testing.T) { + tempDir := t.TempDir() + t.Chdir(tempDir) + + err := Init() + if err != nil { + t.Fatal(err) + } + + got, err := os.ReadFile(ConfigFile) + if err != nil { + t.Fatal(err) + } + test.Assert(t, `Init`, configTemplate, string(got)) +} + func TestNew(t *testing.T) { type testCase struct { exp *SPDXConv @@ -73,17 +90,15 @@ func TestSPDXConv_loadConfig(t *testing.T) { FileCopyrightText: `Author <author@email.info>`, MaxLineMatch: 10, MatchLicense: []*configMatchLicense{{ - Pattern: `^(//+|#+)*\s+(.*)governed by a BSD-style(.*)$`, + Pattern: `^(//+|#+)\s+(.*)governed by a BSD-style(.*)$`, LicenseIdentifier: `BSD-3-Clause`, DeleteLinePattern: []string{ - `^(//+|#+)*\s*$`, - `^(//+|#+)*\s+license that(.*)$`, + `^(//+|#+)\s+license that(.*)$`, }, DeleteMatch: true, - rePattern: regexp.MustCompile(`^(//+|#+)*\s+(.*)governed by a BSD-style(.*)$`), + rePattern: regexp.MustCompile(`^(//+|#+)\s+(.*)governed by a BSD-style(.*)$`), reDeleteLine: []*regexp.Regexp{ - regexp.MustCompile(`^(//+|#+)*\s*$`), - regexp.MustCompile(`^(//+|#+)*\s+license that(.*)$`), + regexp.MustCompile(`^(//+|#+)\s+license that(.*)$`), }, }}, }, diff --git a/testdata/loadConfig/config_exists/spdxconv.cfg b/testdata/loadConfig/config_exists/spdxconv.cfg index 9af4d1d..c1a11e5 100644 --- a/testdata/loadConfig/config_exists/spdxconv.cfg +++ b/testdata/loadConfig/config_exists/spdxconv.cfg @@ -1,5 +1,5 @@ -# SPDX-License-Identifier: GPL-3.0-only -# SPDX-FileCopyrightText: 2025 M. Shulhan <ms@kilabit.info> +# SPDX-License-Identifier: CC0-1.0 +# SPDX-FileCopyrightText: 2026 M. Shulhan <ms@kilabit.info> [default] license_identifier = GPL-3.0-only @@ -7,11 +7,10 @@ file_copyright_text = Author <author@email.info> max_line_match = 10 [match-license] -pattern = "^(//+|#+)*\\s+(.*)governed by a BSD-style(.*)$" +pattern = "^(//+|#+)\\s+(.*)governed by a BSD-style(.*)$" license_identifier = BSD-3-Clause delete_match = true -delete_line_pattern = "^(//+|#+)*\\s*$" -delete_line_pattern = "^(//+|#+)*\\s+license that(.*)$" +delete_line_pattern = "^(//+|#+)\\s+license that(.*)$" [match-copyright] -pattern = "^(//+|#+)*\\s+Copyright\\s+(?<year>\\d{4}),?\\s+(?<holder>.*)\\s+<*(?<email>.*)>.*$" +pattern = "^(//+|#+)\\s+Copyright\\s+(?<year>\\d{4}),?\\s+(?<author>.*)\\s+<*(?<contact>.*)>.*$" |
