summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2026-01-08 19:08:14 +0700
committerShulhan <ms@kilabit.info>2026-01-08 20:40:27 +0700
commita10b07ddbd21c7725df43f3e37b9d7709fbe66b7 (patch)
tree0d2704626e6b489658a1a068f85a8942c0a07fb6
parent496c812f6e14b77408b71f7a35a3d755b8bbbf36 (diff)
downloadspdxconv-a10b07ddbd21c7725df43f3e37b9d7709fbe66b7.tar.xz
cmd/spdxconv: implement "init" command
The init command create the spdxconv configuration file in the current directory.
-rw-r--r--.gitignore3
-rw-r--r--README.md95
l---------_doc/index.md1
-rw-r--r--cmd/spdxconv/main.go69
-rw-r--r--config.go24
-rw-r--r--spdxconv.go18
-rw-r--r--spdxconv_test.go29
-rw-r--r--testdata/loadConfig/config_exists/spdxconv.cfg11
8 files changed, 208 insertions, 42 deletions
diff --git a/.gitignore b/.gitignore
index 2d50d27..73f1fa2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
diff --git a/README.md b/README.md
index faa6333..61609da 100644
--- a/README.md
+++ b/README.md
@@ -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()
+}
diff --git a/config.go b/config.go
index 1268cb8..879ba7e 100644
--- a/config.go
+++ b/config.go
@@ -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>.*)>.*$"