diff --git a/.deb.yaml b/.deb.yaml index 493ab0b..bd985c7 100755 --- a/.deb.yaml +++ b/.deb.yaml @@ -1,16 +1,18 @@ -package: deb-builder -source: deb-builder -version: '1:0.5.0' -architecture: - - amd64 - - arm64 -maintainer: The OSSPkg Team -homepage: https://deb.osspkg.com/ -description: - - Debian package builder -section: web -priority: optional -control: - build: devtool build --arch=%arch% -data: - usr/bin/deb-builder: build/deb-builder_%arch% +ver: "2" +packages: + - package: deb-builder + source: deb-builder + version: 'git' + architecture: + - amd64 + - arm64 + maintainer: The OSSPkg Team + homepage: https://deb.osspkg.com/ + description: + - Debian package builder + section: web + priority: optional + control: + build: goppy build --arch=%arch% + data: + usr/bin/deb-builder: build/deb-builder_%arch% diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 07f8108..a66a0cf 100755 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - go: [ '1.23.6' ] + go: [ '1.25' ] steps: - uses: actions/checkout@v3 diff --git a/.golangci.yml b/.golangci.yml index 33af9da..ea46c6b 100755 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,202 +1,64 @@ +version: "2" run: - go: "1.23" - concurrency: 4 + go: "1.25" timeout: 5m tests: false issues-exit-code: 1 modules-download-mode: readonly + allow-parallel-runners: true issues: - exclude-use-default: false - max-issues-per-linter: 100 - max-same-issues: 4 + max-issues-per-linter: 0 + max-same-issues: 0 new: false - exclude-files: - - ".+_test.go" - exclude-dirs: - - "vendor$" + fix: false output: formats: - - format: line-number - sort-results: true + text: + print-linter-name: true + print-issued-lines: true -linters-settings: - govet: - check-shadowing: true - enable: - - asmdecl - - assign - - atomic - - atomicalign - - bools - - buildtag - - cgocall - - composites - - copylocks - - deepequalerrors - - errorsas - - findcall - - framepointer - - httpresponse - - ifaceassert - - loopclosure - - lostcancel - - nilfunc - - nilness - - printf - - reflectvaluecompare - - shadow - - shift - - sigchanyzer - - sortslice - - stdmethods - - stringintconv - - structtag - - testinggoroutine - - tests - - unmarshal - - unreachable - - unsafeptr - - unusedresult - - unusedwrite - disable: - - fieldalignment - gofmt: - simplify: true - errcheck: - check-type-assertions: true - check-blank: true - gocyclo: - min-complexity: 30 - misspell: - locale: US - prealloc: - simple: true - range-loops: true - for-loops: true - unparam: - check-exported: false - gci: - skip-generated: true - custom-order: false - gosec: - includes: - - G101 # Look for hard coded credentials - - G102 # Bind to all interfaces - - G103 # Audit the use of unsafe block - - G104 # Audit errors not checked - - G106 # Audit the use of ssh.InsecureIgnoreHostKey - - G107 # Url provided to HTTP request as taint input - - G108 # Profiling endpoint automatically exposed on /debug/pprof - - G109 # Potential Integer overflow made by strconv.Atoi result conversion to int16/32 - - G110 # Potential DoS vulnerability via decompression bomb - - G111 # Potential directory traversal - - G112 # Potential slowloris attack - - G113 # Usage of Rat.SetString in math/big with an overflow (CVE-2022-23772) - - G114 # Use of net/http serve function that has no support for setting timeouts - - G201 # SQL query construction using format string - - G202 # SQL query construction using string concatenation - - G203 # Use of unescaped data in HTML templates - - G204 # Audit use of command execution - - G301 # Poor file permissions used when creating a directory - - G302 # Poor file permissions used with chmod - - G303 # Creating tempfile using a predictable path - - G304 # File path provided as taint input - - G305 # File traversal when extracting zip/tar archive - - G306 # Poor file permissions used when writing to a new file - - G307 # Deferring a method which returns an error - - G401 # Detect the usage of DES, RC4, MD5 or SHA1 - - G402 # Look for bad TLS connection settings - - G403 # Ensure minimum RSA key length of 2048 bits - - G404 # Insecure random number source (rand) - - G501 # Import blocklist: crypto/md5 - - G502 # Import blocklist: crypto/des - - G503 # Import blocklist: crypto/rc4 - - G504 # Import blocklist: net/http/cgi - - G505 # Import blocklist: crypto/sha1 - - G601 # Implicit memory aliasing of items from a range statement - excludes: - - G101 # Look for hard coded credentials - - G102 # Bind to all interfaces - - G103 # Audit the use of unsafe block - - G104 # Audit errors not checked - - G106 # Audit the use of ssh.InsecureIgnoreHostKey - - G107 # Url provided to HTTP request as taint input - - G108 # Profiling endpoint automatically exposed on /debug/pprof - - G109 # Potential Integer overflow made by strconv.Atoi result conversion to int16/32 - - G110 # Potential DoS vulnerability via decompression bomb - - G111 # Potential directory traversal - - G112 # Potential slowloris attack - - G113 # Usage of Rat.SetString in math/big with an overflow (CVE-2022-23772) - - G114 # Use of net/http serve function that has no support for setting timeouts - - G201 # SQL query construction using format string - - G202 # SQL query construction using string concatenation - - G203 # Use of unescaped data in HTML templates - - G204 # Audit use of command execution - - G301 # Poor file permissions used when creating a directory - - G302 # Poor file permissions used with chmod - - G303 # Creating tempfile using a predictable path - - G304 # File path provided as taint input - - G305 # File traversal when extracting zip/tar archive - - G306 # Poor file permissions used when writing to a new file - - G307 # Deferring a method which returns an error - - G401 # Detect the usage of DES, RC4, MD5 or SHA1 - - G402 # Look for bad TLS connection settings - - G403 # Ensure minimum RSA key length of 2048 bits - - G404 # Insecure random number source (rand) - - G501 # Import blocklist: crypto/md5 - - G502 # Import blocklist: crypto/des - - G503 # Import blocklist: crypto/rc4 - - G504 # Import blocklist: net/http/cgi - - G505 # Import blocklist: crypto/sha1 - - G601 # Implicit memory aliasing of items from a range statement - exclude-generated: true - severity: medium - confidence: medium - concurrency: 12 - config: - global: - nosec: true - "#nosec": "#my-custom-nosec" - show-ignored: true - audit: true - G101: - pattern: "(?i)passwd|pass|password|pwd|secret|token|pw|apiKey|bearer|cred" - ignore_entropy: false - entropy_threshold: "80.0" - per_char_threshold: "3.0" - truncate: "32" - G104: - fmt: - - Fscanf - G111: - pattern: "http\\.Dir\\(\"\\/\"\\)|http\\.Dir\\('\\/'\\)" - G301: "0750" - G302: "0600" - G306: "0600" - - lll: - line-length: 130 - tab-width: 1 - staticcheck: - go: "1.15" - # SAxxxx checks in https://staticcheck.io/docs/configuration/options/#checks - # Default: ["*"] - checks: [ "*", "-SA1019" ] +formatters: + exclusions: + paths: + - vendors/ + enable: + - gofmt + - goimports linters: - disable-all: true + settings: + staticcheck: + checks: + - all + - -S1023 + - -ST1000 + - -ST1003 + - -ST1020 + gosec: + excludes: + - G104 + - G115 + - G204 + - G301 + - G302 + - G304 + - G306 + - G401 + - G501 + - G505 + exclusions: + paths: + - vendors/ + default: none enable: - govet - - gofmt - errcheck - misspell - gocyclo - ineffassign - - goimports - - nakedret - unparam - unused - prealloc @@ -207,5 +69,3 @@ linters: - errorlint - bodyclose - gosec - - lll - fast: false diff --git a/Makefile b/Makefile index 57e6a74..e9ace69 100755 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ pre-commit: license setup lint build tests ci: install setup lint build tests deb: - deb-builder build + deb-builder build --base-dir=./build --tmp-dir=/tmp/deb-build local: build cp ./build/deb-builder_amd64 $(GOPATH)/bin/deb-builder \ No newline at end of file diff --git a/go.mod b/go.mod index 63a2c73..4e5aa2e 100755 --- a/go.mod +++ b/go.mod @@ -1,18 +1,19 @@ module github.com/osspkg/deb-builder -go 1.23.6 +go 1.25.3 require ( - github.com/stretchr/testify v1.8.4 + github.com/stretchr/testify v1.11.1 go.osspkg.com/archives v1.1.0 go.osspkg.com/console v0.3.3 - go.osspkg.com/ioutils v0.5.1 - golang.org/x/crypto v0.37.0 + go.osspkg.com/encrypt v0.5.1 + go.osspkg.com/ioutils v0.7.3 gopkg.in/yaml.v3 v3.0.1 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - go.osspkg.com/errors v0.3.1 // indirect + go.osspkg.com/errors v0.4.0 // indirect + golang.org/x/crypto v0.43.0 // indirect ) diff --git a/go.sum b/go.sum index aad3289..4f4f871 100644 --- a/go.sum +++ b/go.sum @@ -2,18 +2,22 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= go.osspkg.com/archives v1.1.0 h1:oXW46spG1Qh2Tofcokl24hY8Ggsf019jQjLhRcqtPcc= go.osspkg.com/archives v1.1.0/go.mod h1:Yj7p+GBlZzAHkFasjLoo7glfPJ14wO8f3a34ChCI3Bg= +go.osspkg.com/casecheck v0.3.0 h1:x15blEszElbrHrEH5H02JIIhGIg/lGZzIt1kQlD3pwM= +go.osspkg.com/casecheck v0.3.0/go.mod h1:TRFXDMFJEOtnlp3ET2Hix3osbxwPWhvaiT/HfD3+gBA= go.osspkg.com/console v0.3.3 h1:UB/pPoPsgWbyNFix8pEMQHbsXdMv/UK/dgsbRknCH2A= go.osspkg.com/console v0.3.3/go.mod h1:IknBCliH6mX/ogHa6wbycnGDFYixCGH3WuNc5W5tQe8= -go.osspkg.com/errors v0.3.1 h1:F9m/EEd/Ot2jba/TV7tvVRIpWXzIpNLc7vRJKcBD86A= -go.osspkg.com/errors v0.3.1/go.mod h1:dKXe6Rt07nzY7OyKQNZ8HGBicZ2uQ5TKEoVFnVFOK44= -go.osspkg.com/ioutils v0.5.1 h1:qzoOECBxChZUxmp6p72XvQRHjDFsVw1kJ2oaFZycNv8= -go.osspkg.com/ioutils v0.5.1/go.mod h1:XRASOo5GKzVaJMCXXZQ4//ymPWq90iyKm1qTQcBEsyo= -golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= -golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= +go.osspkg.com/encrypt v0.5.1 h1:DaYhos4Si9Mzi1LBW2mkin7TPQGkKvlwI+aq/a8Z6ko= +go.osspkg.com/encrypt v0.5.1/go.mod h1:mGDe5PTd+i6cntpiOaesAaD7498ypqPbbMPbI89PK4c= +go.osspkg.com/errors v0.4.0 h1:E17+WyUzTXEHCTxGm8lOMPOOojzHG1lsOuQtTVGoATQ= +go.osspkg.com/errors v0.4.0/go.mod h1:s75ZovPemYtrCtRPVsbQNq9MgMbmLMK1NEypr+uwjXI= +go.osspkg.com/ioutils v0.7.3 h1:QF+Ra0bHoU3MGMGH5PGdV2lRLq1rWPdv/OB+v5UTjkI= +go.osspkg.com/ioutils v0.7.3/go.mod h1:RO/43IM//Wq8RnLvEzivDAuM37mnLW3eWxTCVmkUaY4= +golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= +golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/internal/commands/build.go b/internal/commands/build.go index 33df178..9d82200 100644 --- a/internal/commands/build.go +++ b/internal/commands/build.go @@ -29,176 +29,179 @@ func Build() console.CommandGetter { return console.NewCommand(func(setter console.CommandSetter) { setter.Setup("build", "Build deb package") setter.Flag(func(flag console.FlagsSetter) { - flag.StringVar("config", config.ConfigFileName, "Config file") + flag.StringVar("config", config.FileName, "Config file") flag.StringVar("base-dir", utils.GetEnv("DEB_STORAGE_BASE_DIR", "./build"), "Deb package base storage") flag.StringVar("tmp-dir", utils.GetEnv("DEB_BUILD_DIR", "/tmp/deb-build"), "Deb package build dir") }) setter.ExecFunc(func(_ []string, debConf, baseDir, tmpDir string) { - conf, err := config.Detect(debConf) + configs, err := config.Detect(debConf) console.FatalIfErr(err, "deb config not found") - buildDir := fmt.Sprintf("%s/%s_%s", tmpDir, conf.Package, conf.Version) - console.FatalIfErr(os.RemoveAll(buildDir), "clearing build directory") - console.FatalIfErr(os.MkdirAll(buildDir, 0755), "creating build directory") + for _, conf := range configs { - storeDir := fmt.Sprintf("%s/%s/%s", baseDir, conf.Package[0:1], conf.Package) - console.FatalIfErr(os.MkdirAll(storeDir, 0755), "creating storage directory") + buildDir := fmt.Sprintf("%s/%s_%s", tmpDir, conf.Package, conf.Version) + console.FatalIfErr(os.RemoveAll(buildDir), "clearing build directory") + console.FatalIfErr(os.MkdirAll(buildDir, 0755), "creating build directory") - exec.Build(conf.Control.Build, conf.Version, conf.Architecture, func(arch string, replacer exec.Replacer) { + storeDir := fmt.Sprintf("%s/%s/%s", baseDir, conf.Package[0:1], conf.Package) + console.FatalIfErr(os.MkdirAll(storeDir, 0755), "creating storage directory") - // check file version + exec.Build(conf.Control.Build, conf.Version, conf.Architecture, func(arch string, replacer exec.Replacer) { - debFile, revision, carch := packages.BuildName(storeDir, conf.Package, conf.Version, arch) + // check file version - // package + debFile, revision, carch := packages.BuildName(storeDir, conf.Package, conf.Version, arch) - cpkg := control.NewControlPkg(conf) + // package - // md5sums + data.tar.gz + cpkg := control.NewControlPkg() - md5sum := control.NewMd5Sums() - dataFile := buildDir + "/data.tar.gz" - tg, err := archive.NewWriter(dataFile) - console.FatalIfErr(err, "create data.tar.gz") + // md5sums + data.tar.gz - for dst, src := range conf.Data { - src = replacer.Replace(src) - var ( - f, h string - err1 error - ) + md5sum := control.NewMd5Sums() + dataFile := buildDir + "/data.tar.gz" + tg, err := archive.NewWriter(dataFile) + console.FatalIfErr(err, "create data.tar.gz") - switch true { - case strings.HasPrefix(src, "+"): - f, h, err1 = tg.WriteData(dst, []byte(src)[1:]) - console.FatalIfErr(err1, "write %s to data.tar.gz", src) - md5sum.Add(f, h) - console.Infof("Add: %s", dst) + for dst, src := range conf.Data { + src = replacer.Replace(src) + var ( + f, h string + err1 error + ) - case strings.HasPrefix(src, "c:"): - f, h, err1 = tg.WriteData(dst, []byte(src)[2:]) - console.FatalIfErr(err1, "write %s to data.tar.gz", src) - md5sum.Add(f, h) - console.Infof("Add: %s", dst) + switch true { + case strings.HasPrefix(src, "+"): + f, h, err1 = tg.WriteData(dst, []byte(src)[1:]) + console.FatalIfErr(err1, "write %s to data.tar.gz", src) + md5sum.Add(f, h) + console.Infof("Add: %s", dst) - case strings.HasPrefix(src, "~"): - fullpath, err0 := filepath.Abs(src[1:]) - console.FatalIfErr(err0, "get full path for %s", src[1:]) + case strings.HasPrefix(src, "c:"): + f, h, err1 = tg.WriteData(dst, []byte(src)[2:]) + console.FatalIfErr(err1, "write %s to data.tar.gz", src) + md5sum.Add(f, h) + console.Infof("Add: %s", dst) - err2 := filepath.Walk(fullpath, func(path string, info iofs.FileInfo, e error) error { - if e != nil { - return e - } - if info.IsDir() { - return nil - } - walkedFile := strings.ReplaceAll(path, fullpath, dst) - ff, hh, ee := tg.WriteFile(path, walkedFile) - console.FatalIfErr(ee, "write %s to data.tar.gz", src) - md5sum.Add(ff, hh) - console.Infof("Add: %s", walkedFile) - return nil - }) - console.FatalIfErr(err2, "write %s to data.tar.gz", src) - - case strings.HasPrefix(src, "d:"): - fullpath, err0 := filepath.Abs(src[2:]) - console.FatalIfErr(err0, "get full path for %s", src[2:]) - - err2 := filepath.Walk(fullpath, func(path string, info iofs.FileInfo, e error) error { - if e != nil { - return e - } - if info.IsDir() { + case strings.HasPrefix(src, "~"): + fullpath, err0 := filepath.Abs(src[1:]) + console.FatalIfErr(err0, "get full path for %s", src[1:]) + + err2 := filepath.Walk(fullpath, func(path string, info iofs.FileInfo, e error) error { + if e != nil { + return e + } + if info.IsDir() { + return nil + } + walkedFile := strings.ReplaceAll(path, fullpath, dst) + ff, hh, ee := tg.WriteFile(path, walkedFile) + console.FatalIfErr(ee, "write %s to data.tar.gz", src) + md5sum.Add(ff, hh) + console.Infof("Add: %s", walkedFile) return nil - } - walkedFile := strings.ReplaceAll(path, fullpath, dst) - ff, hh, ee := tg.WriteFile(path, walkedFile) - console.FatalIfErr(ee, "write %s to data.tar.gz", src) - md5sum.Add(ff, hh) - console.Infof("Add: %s", walkedFile) - return nil - }) - console.FatalIfErr(err2, "write %s to data.tar.gz", src) - - case strings.HasPrefix(src, "e:"): - rex, err0 := regexp.Compile(`(?Us)^` + src[2:] + `$`) - console.FatalIfErr(err0, "build regexp `%s`", src[2:]) - - fullpath := fs.CurrentDir() - err2 := filepath.Walk(fullpath, func(path string, info iofs.FileInfo, e error) error { - if e != nil { - return e - } - if info.IsDir() { + }) + console.FatalIfErr(err2, "write %s to data.tar.gz", src) + + case strings.HasPrefix(src, "d:"): + fullpath, err0 := filepath.Abs(src[2:]) + console.FatalIfErr(err0, "get full path for %s", src[2:]) + + err2 := filepath.Walk(fullpath, func(path string, info iofs.FileInfo, e error) error { + if e != nil { + return e + } + if info.IsDir() { + return nil + } + walkedFile := strings.ReplaceAll(path, fullpath, dst) + ff, hh, ee := tg.WriteFile(path, walkedFile) + console.FatalIfErr(ee, "write %s to data.tar.gz", src) + md5sum.Add(ff, hh) + console.Infof("Add: %s", walkedFile) return nil - } - - if !rex.MatchString(strings.TrimPrefix(path, fullpath)) { + }) + console.FatalIfErr(err2, "write %s to data.tar.gz", src) + + case strings.HasPrefix(src, "e:"): + rex, err0 := regexp.Compile(`(?Us)^` + src[2:] + `$`) + console.FatalIfErr(err0, "build regexp `%s`", src[2:]) + + fullpath := fs.CurrentDir() + err2 := filepath.Walk(fullpath, func(path string, info iofs.FileInfo, e error) error { + if e != nil { + return e + } + if info.IsDir() { + return nil + } + + if !rex.MatchString(strings.TrimPrefix(path, fullpath)) { + return nil + } + + walkedFile := strings.ReplaceAll(path, fullpath, dst) + ff, hh, ee := tg.WriteFile(path, walkedFile) + console.FatalIfErr(ee, "write %s to data.tar.gz", src) + md5sum.Add(ff, hh) + console.Infof("Add: %s", walkedFile) return nil - } - - walkedFile := strings.ReplaceAll(path, fullpath, dst) - ff, hh, ee := tg.WriteFile(path, walkedFile) - console.FatalIfErr(ee, "write %s to data.tar.gz", src) - md5sum.Add(ff, hh) - console.Infof("Add: %s", walkedFile) - return nil - }) - console.FatalIfErr(err2, "write %s to data.tar.gz", src) - - default: - f, h, err1 = tg.WriteFile(src, dst) - console.FatalIfErr(err1, "write %s to data.tar.gz", src) - md5sum.Add(f, h) - console.Infof("Add: %s", dst) + }) + console.FatalIfErr(err2, "write %s to data.tar.gz", src) + + default: + f, h, err1 = tg.WriteFile(src, dst) + console.FatalIfErr(err1, "write %s to data.tar.gz", src) + md5sum.Add(f, h) + console.Infof("Add: %s", dst) + } } - } - console.FatalIfErr(tg.Close(), "close data.tar.gz") + console.FatalIfErr(tg.Close(), "close data.tar.gz") - md5file, err := md5sum.Save(buildDir) - console.FatalIfErr(err, "create md5sums") - cpkg.AddFile(md5file) + md5file, err := md5sum.Save(buildDir) + console.FatalIfErr(err, "create md5sums") + cpkg.AddFile(md5file) - // control + // control - ctrl := control.NewControl(conf) - ctrl.DataSize(tg.Size()) - ctrl.Arch(carch) - ctrlFile, err := ctrl.Save(buildDir, revision) - console.FatalIfErr(err, "create control") - cpkg.AddFile(ctrlFile) + ctrl := control.NewControl(conf) + ctrl.DataSize(tg.Size()) + ctrl.Arch(carch) + ctrlFile, err := ctrl.Save(buildDir, revision) + console.FatalIfErr(err, "create control") + cpkg.AddFile(ctrlFile) - // other control.tar.gz files + // other control.tar.gz files - other := control.NewOther(conf) - console.FatalIfErr(other.WriteTo(buildDir), "prepare other control files") - cpkg.AddFile(other.List()...) + other := control.NewOther(conf) + console.FatalIfErr(other.WriteTo(buildDir), "prepare other control files") + cpkg.AddFile(other.List()...) - // control.tar.gz + // control.tar.gz - controlFile := buildDir + "/control.tar.gz" - tg, err = archive.NewWriter(controlFile) - console.FatalIfErr(err, "create control.tar.gz") - for _, file := range cpkg.List() { - if _, _, err1 := tg.WriteFile(file, filepath.Base(file)); err1 != nil { - console.FatalIfErr(err1, "write %s to control.tar.gz", file) + controlFile := buildDir + "/control.tar.gz" + tg, err = archive.NewWriter(controlFile) + console.FatalIfErr(err, "create control.tar.gz") + for _, file := range cpkg.List() { + if _, _, err1 := tg.WriteFile(file, filepath.Base(file)); err1 != nil { + console.FatalIfErr(err1, "write %s to control.tar.gz", file) + } } - } - console.FatalIfErr(tg.Close(), "close file control.tar.gz") + console.FatalIfErr(tg.Close(), "close file control.tar.gz") - // build deb + // build deb - deb, err := ar.Open(debFile, 0644) - console.FatalIfErr(err, "create %s", debFile) - console.FatalIfErr(deb.Write("debian-binary", []byte("2.0\n"), 0644), "write debian-binary to %s", debFile) - console.FatalIfErr(deb.Import(controlFile, 0644), "write %s to %s", controlFile, debFile) - console.FatalIfErr(deb.Import(dataFile, 0644), "write %s to %s", dataFile, debFile) - console.FatalIfErr(deb.Close(), "close file %s", debFile) + deb, err := ar.Open(debFile, 0644) + console.FatalIfErr(err, "create %s", debFile) + console.FatalIfErr(deb.Write("debian-binary", []byte("2.0\n"), 0644), "write debian-binary to %s", debFile) + console.FatalIfErr(deb.Import(controlFile, 0644), "write %s to %s", controlFile, debFile) + console.FatalIfErr(deb.Import(dataFile, 0644), "write %s to %s", dataFile, debFile) + console.FatalIfErr(deb.Close(), "close file %s", debFile) - console.Infof("Result: %s", debFile) - }) + console.Infof("Result: %s", debFile) + }) + } }) }) } diff --git a/internal/commands/pgp.go b/internal/commands/pgp.go index 08434f1..49d9df3 100644 --- a/internal/commands/pgp.go +++ b/internal/commands/pgp.go @@ -6,10 +6,16 @@ package commands import ( + "os" + "go.osspkg.com/console" + "go.osspkg.com/encrypt/pgp" "go.osspkg.com/ioutils/fs" +) - "github.com/osspkg/deb-builder/pkg/pgp" +const ( + publicFilename = "/public.pgp" + privateFilename = "/private.pgp" ) func CreatePGPCert() console.CommandGetter { @@ -25,7 +31,10 @@ func CreatePGPCert() console.CommandGetter { if len(path) == 0 { path = fs.CurrentDir() } - console.FatalIfErr(pgp.NewPGP().Generate(path, name, comment, email), "generate cert") + crt, err := pgp.NewCertSHA512(pgp.Config{Name: name, Email: email, Comment: comment}) + console.FatalIfErr(err, "generate cert") + console.FatalIfErr(os.WriteFile(path+privateFilename, crt.Private, 0600), "save private key") + console.FatalIfErr(os.WriteFile(path+publicFilename, crt.Public, 0644), "save public cert") }) }) } diff --git a/internal/commands/release.go b/internal/commands/release.go index bf79cb2..5f0791d 100644 --- a/internal/commands/release.go +++ b/internal/commands/release.go @@ -17,12 +17,12 @@ import ( "go.osspkg.com/archives/ar" "go.osspkg.com/console" + "go.osspkg.com/encrypt/pgp" "github.com/osspkg/deb-builder/pkg/archive" "github.com/osspkg/deb-builder/pkg/buffer" "github.com/osspkg/deb-builder/pkg/hash" "github.com/osspkg/deb-builder/pkg/packages" - "github.com/osspkg/deb-builder/pkg/pgp" "github.com/osspkg/deb-builder/pkg/utils" ) @@ -47,17 +47,12 @@ func GenerateRelease() console.CommandGetter { f.StringVar("dist", utils.GetEnv("DEB_DISTRIBUTION", "stable"), "release distribution") f.StringVar("comp", utils.GetEnv("DEB_COMPONENT", "main"), "release component") }) - setter.ExecFunc(func(_ []string, path, tmp, priv, passwd, origin, label, dist, comp string) { + setter.ExecFunc(func(_ []string, path, tmp, privKeyFile, passwd, origin, label, dist, comp string) { /** LOAD PGP */ - pgpStore := pgp.NewPGP() - privKeyFile, err := os.Open(priv) - console.FatalIfErr(err, "open PGP private key") - defer func() { - console.FatalIfErr(privKeyFile.Close(), "close PGP private key") - }() - console.FatalIfErr(pgpStore.LoadPrivateKey(privKeyFile, passwd), "read PGP private key") + pgpStore := pgp.New() + console.FatalIfErr(pgpStore.SetKeyFromFile(privKeyFile, passwd), "read PGP private key") /** Validate dirs @@ -74,7 +69,7 @@ func GenerateRelease() console.CommandGetter { pkgs := make([]*packages.PackegesModel, 0, 1000) pathcomp := fmt.Sprintf(PathComponent, path, comp) - err = filepath.Walk(pathcomp, func(filename string, info fs.FileInfo, err error) error { + err := filepath.Walk(pathcomp, func(filename string, info fs.FileInfo, err error) error { if err != nil { return err } @@ -198,12 +193,12 @@ func GenerateRelease() console.CommandGetter { SHA256: "", } - for _, inr := range inRelease { - inrHash, err1 := hash.CalcMultiHash(inr) - console.FatalIfErr(err1, "calc multi hash: %s", inr) - shortName := strings.Replace(inr, fmt.Sprintf(PathDistribution, path, dist), "", 1) - stats, err3 := os.Stat(inr) - console.FatalIfErr(err3, "file stat: %s", inr) + for _, fileName := range inRelease { + inrHash, err1 := hash.CalcMultiHash(fileName) + console.FatalIfErr(err1, "calc multi hash: %s", fileName) + shortName := strings.Replace(fileName, fmt.Sprintf(PathDistribution, path, dist), "", 1) + stats, err3 := os.Stat(fileName) + console.FatalIfErr(err3, "file stat: %s", fileName) inReleaseModel.MD5Sum += fmt.Sprintf("\n %s %d %s", inrHash.MD5, stats.Size(), shortName) inReleaseModel.SHA1 += fmt.Sprintf("\n %s %d %s", inrHash.SHA1, stats.Size(), shortName) @@ -225,12 +220,12 @@ func GenerateRelease() console.CommandGetter { Copy Release.gpg */ - pubKeyB64, err := pgpStore.GetPublicBase64() + pubKeyB64, err := pgpStore.PublicKeyBase64() console.FatalIfErr(err, "read public key") err = os.WriteFile(fmt.Sprintf(PathDistribution, path, dist)+"Release.gpg", pubKeyB64, 0755) console.FatalIfErr(err, "write Release.gpg") - pubKey, err := pgpStore.GetPublic() + pubKey, err := pgpStore.PublicKey() console.FatalIfErr(err, "read public key") err = os.WriteFile(path+"/key.gpg", pubKey, 0755) console.FatalIfErr(err, "write key.gpg") @@ -241,7 +236,7 @@ func GenerateRelease() console.CommandGetter { curl -fsSL https://[yourdomain]/key.gpg | sudo gpg --dearmor -o /etc/apt/keyrings/[yourdomain].gpg sudo chmod a+r /etc/apt/keyrings/[yourdomain].gpg sudo tee /etc/apt/sources.list.d/[yourdomain].list <<'EOF' -deb [arch=arm64 signed-by=/etc/apt/keyrings/[yourdomain].gpg] https://[yourdomain]/ stable main +deb [arch=amd64 signed-by=/etc/apt/keyrings/[yourdomain].gpg] https://[yourdomain]/ stable main EOF sudo apt update diff --git a/pkg/config/config.go b/pkg/config/config.go index 6959995..64e3a1f 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -18,9 +18,17 @@ import ( "github.com/osspkg/deb-builder/pkg/utils" ) -const ConfigFileName = ".deb.yaml" +const FileName = ".deb.yaml" type ( + Version struct { + Version string `yaml:"ver"` + } + + Multi struct { + Version string `yaml:"ver"` + Packages []Config `yaml:"packages"` + } Config struct { Package string `yaml:"package"` Source string `yaml:"source"` @@ -47,30 +55,53 @@ type ( var versionRegexp = regexp.MustCompile(`\d+:\d+\.\d+\.\d+`) -func Detect(name string) (*Config, error) { +func Detect(name string) ([]Config, error) { dir := fs.CurrentDir() - conf := &Config{} + b, err := os.ReadFile(dir + "/" + name) if err != nil { return nil, err } - if err = yaml.Unmarshal(b, conf); err != nil { + + ver := Version{} + if err = yaml.Unmarshal(b, &ver); err != nil { return nil, err } - if conf.Version == "git" { - conf.Version, err = exec.GitVersion() - if err != nil { - return nil, fmt.Errorf("fail build git version: %w", err) + + var out []Config + switch ver.Version { + case "", "0", "1": + cfg := Config{} + if err = yaml.Unmarshal(b, &cfg); err != nil { + return nil, err + } + out = append(out, cfg) + + case "2": + cfg := Multi{} + if err = yaml.Unmarshal(b, &cfg); err != nil { + return nil, err } - } else if !versionRegexp.MatchString(conf.Version) { - return nil, fmt.Errorf("invalid version format, want format 0:0.0.0") + out = append(out, cfg.Packages...) } - return conf, nil + + for i := 0; i < len(out); i++ { + if out[i].Version == "git" { + out[i].Version, err = exec.GitVersion() + if err != nil { + return nil, fmt.Errorf("fail build git version: %w", err) + } + } else if !versionRegexp.MatchString(out[i].Version) { + return nil, fmt.Errorf("invalid version format, want format 0:0.0.0") + } + } + + return out, nil } func Create() error { dir := fs.CurrentDir() - conf := &Config{ + conf := Config{ Package: filepath.Base(dir), Source: filepath.Base(dir), Version: "1:0.0.1 # or use `git` for build version by git commit", @@ -95,11 +126,17 @@ func Create() error { "var/log/" + filepath.Base(dir) + ".log": "+Write contents of file here after '+'", }, } - b, err := yaml.Marshal(conf) + + cfg := Multi{ + Version: "2", + Packages: []Config{conf}, + } + + b, err := yaml.Marshal(cfg) if err != nil { return err } - if err = os.WriteFile(dir+"/"+ConfigFileName, b, 0755); err != nil { + if err = os.WriteFile(dir+"/"+FileName, b, 0755); err != nil { return err } return nil diff --git a/pkg/control/control.go b/pkg/control/control.go index 359aa10..794d16b 100644 --- a/pkg/control/control.go +++ b/pkg/control/control.go @@ -19,7 +19,7 @@ const descriptionMaxLen = 70 type ( Control struct { - conf *config.Config + conf config.Config size int64 arch string } @@ -38,7 +38,7 @@ type ( } ) -func NewControl(conf *config.Config) *Control { +func NewControl(conf config.Config) *Control { return &Control{ conf: conf, } diff --git a/pkg/control/controlpkg.go b/pkg/control/controlpkg.go index 90a65ab..d8f417a 100644 --- a/pkg/control/controlpkg.go +++ b/pkg/control/controlpkg.go @@ -5,16 +5,12 @@ package control -import "github.com/osspkg/deb-builder/pkg/config" - type Pkg struct { - conf *config.Config files []string } -func NewControlPkg(conf *config.Config) *Pkg { +func NewControlPkg() *Pkg { return &Pkg{ - conf: conf, files: make([]string, 0), } } diff --git a/pkg/control/other.go b/pkg/control/other.go index 5001317..7dbdc28 100644 --- a/pkg/control/other.go +++ b/pkg/control/other.go @@ -18,7 +18,7 @@ import ( type ( Other struct { - conf *config.Config + conf config.Config files []string } copyFile struct { @@ -27,7 +27,7 @@ type ( } ) -func NewOther(conf *config.Config) *Other { +func NewOther(conf config.Config) *Other { return &Other{ conf: conf, files: make([]string, 0), diff --git a/pkg/exec/common.go b/pkg/exec/common.go index 7f83e02..ec8d80b 100644 --- a/pkg/exec/common.go +++ b/pkg/exec/common.go @@ -6,6 +6,7 @@ package exec import ( + "bytes" "fmt" "os" "os/exec" @@ -54,50 +55,81 @@ func execCommand(cmd string, line bool, envs ...string) (string, error) { } c.Dir = fs.CurrentDir() b, err := c.CombinedOutput() - return string(b), err + return string(bytes.TrimSpace(b)), err } func GitVersion() (string, error) { - out, err := execCommand("git status --porcelain", false) + out, err := execCommand(`git status --porcelain 2>/dev/null`, false) if err != nil { - return "", err + return "", fmt.Errorf("check uncommitted changes: %w", err) } - out = strings.TrimSpace(out) - if len(strings.TrimSpace(out)) > 0 { + if len(out) > 0 { return "", fmt.Errorf("has uncommitted changes") } - out, err = execCommand("git for-each-ref --count=1 --sort=\"-committerdate\" "+ - "--format=\"%(refname:lstrip=-1)-build%(committerdate:format:%Y%m%d%H%M%S)-%(objectname:short=12)\" "+ - "refs/tags --merged HEAD~0\n", false) + lastTag, err := execCommand(`git describe --tags --abbrev=0 2>/dev/null`, false) if err != nil { - return "", err + return "", fmt.Errorf("get last git tag: %w", err) + } + if len(lastTag) == 0 { + lastTag = "v0.0.0" + } + + var epoch int64 = 1 + + info := strings.SplitN(lastTag, ".", 2) + major, err := strconv.ParseInt(strings.TrimPrefix(info[0], "v"), 10, 64) + if err != nil { + return "", fmt.Errorf("parse major version: %w", err) + } + if major > 1 { + epoch = major + } + + branch, err := execCommand(`git branch --show-current`, false) + if err != nil { + return "", fmt.Errorf("get branch: %w", err) + } + + revTag := lastTag + ".." + if lastTag == "v0.0.0" { + revTag = "" + } + out, err = execCommand(fmt.Sprintf(`git rev-list --count %sHEAD`, revTag), false) + if err != nil { + return "", fmt.Errorf("get rev list: %w", err) + } + commitCount, err := strconv.ParseInt(out, 10, 64) + if err != nil { + return "", fmt.Errorf("parse commit count: %w", err) } - out = strings.TrimSpace(out) - if len(out) > 0 { - info := strings.SplitN(out, ".", 2) - major, err0 := strconv.ParseInt(strings.TrimPrefix(info[0], "v"), 10, 64) - if err0 != nil { - return "", err0 - } - var epoch int64 = 1 - if major > 0 { - epoch = major - } - return fmt.Sprintf("%d:%d.%s", epoch, major, info[1]), nil + lastHash, err := execCommand(`git rev-parse --short HEAD`, false) + if err != nil { + return "", fmt.Errorf("get last commit: %w", err) } - out, err = execCommand("git -c log.showsignature=false log -1 --format=%H:%ct", false) + out, err = execCommand(`git -c log.showsignature=false log -1 --format=%ct`, false) if err != nil { - return "", err + return "", fmt.Errorf("get last commit date: %w", err) } - info := strings.Split(strings.TrimSpace(out), ":") - hash := info[0][0:12] - secs, err := strconv.ParseInt(info[1], 10, 64) + ts, err := strconv.ParseInt(out, 10, 64) if err != nil { - return "", err + return "", fmt.Errorf("parse timestamp: %w", err) + } + lastHashDate := time.Unix(ts, 0).Format("20060102") + + var buf strings.Builder + fmt.Fprintf(&buf, "%d:%s", epoch, strings.TrimPrefix(lastTag, "v")) + if commitCount > 0 { + switch branch { + case "master", "main": + fmt.Fprintf(&buf, "-%d", commitCount) + default: + fmt.Fprintf(&buf, "~dev.%d", commitCount) + } + fmt.Fprintf(&buf, "-git.%s-%s", lastHashDate, lastHash) } - ts := time.Unix(secs, 0).Format("20060102150405") - return fmt.Sprintf("1:0.0.1-build%s-%s", ts, hash), nil + + return buf.String(), nil } diff --git a/pkg/pgp/pgp.go b/pkg/pgp/pgp.go deleted file mode 100644 index 976dc15..0000000 --- a/pkg/pgp/pgp.go +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (c) 2021-2025 Mikhail Knyazhev . All rights reserved. - * Use of this source code is governed by a BSD-3-Clause license that can be found in the LICENSE file. - */ - -package pgp - -import ( - "bytes" - "crypto" - "fmt" - "io" - - "golang.org/x/crypto/openpgp" - "golang.org/x/crypto/openpgp/armor" - "golang.org/x/crypto/openpgp/clearsign" - "golang.org/x/crypto/openpgp/packet" -) - -const ( - DefaultRSAKeyBits = 4096 -) - -type PGP struct { - key *openpgp.Entity - conf *packet.Config -} - -func NewPGP() *PGP { - return &PGP{ - conf: &packet.Config{ - DefaultHash: crypto.SHA512, - }, - } -} - -func (v *PGP) LoadPrivateKey(r io.ReadSeeker, passwd string) error { - block, err := armor.Decode(r) - if err != nil { - return fmt.Errorf("armor decode: %w", err) - } - if block.Type != openpgp.PrivateKeyType { - return fmt.Errorf("invalid key type: %w", err) - } - if _, err = r.Seek(0, 0); err != nil { - return fmt.Errorf("seek file: %w", err) - } - keys, err := openpgp.ReadArmoredKeyRing(r) - if err != nil { - return fmt.Errorf("read key: %w", err) - } - v.key = keys[0] - if v.key.PrivateKey.Encrypted { - if err = v.key.PrivateKey.Decrypt([]byte(passwd)); err != nil { - return fmt.Errorf("invalid password: %w", err) - } - } - return nil -} - -func (v *PGP) Sign(in io.Reader, out io.Writer) error { - w, err := clearsign.Encode(out, v.key.PrivateKey, v.conf) - if err != nil { - return err - } - - if _, err = io.Copy(w, in); err != nil { - return err - } - - if err = w.Close(); err != nil { - return err - } - // if err := openpgp.ArmoredDetachSignText(out, v.key, in, v.conf); err != nil { - // return err - // } - return nil -} - -func (v *PGP) GetPublic() ([]byte, error) { - buf := &bytes.Buffer{} - if err := v.key.Serialize(buf); err != nil { - return nil, err - } - return buf.Bytes(), nil -} - -func (v *PGP) GetPublicBase64() ([]byte, error) { - buf := &bytes.Buffer{} - enc, err := armor.Encode(buf, openpgp.PublicKeyType, map[string]string{}) - if err != nil { - return nil, err - } - if err = v.key.Serialize(enc); err != nil { - return nil, err - } - if err = enc.Close(); err != nil { - return nil, err - } - return buf.Bytes(), nil -} diff --git a/pkg/pgp/pgp_generate.go b/pkg/pgp/pgp_generate.go deleted file mode 100644 index 26deb3a..0000000 --- a/pkg/pgp/pgp_generate.go +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2021-2025 Mikhail Knyazhev . All rights reserved. - * Use of this source code is governed by a BSD-3-Clause license that can be found in the LICENSE file. - */ - -package pgp - -import ( - "bytes" - "fmt" - "io" - "os" - - "golang.org/x/crypto/openpgp" - "golang.org/x/crypto/openpgp/armor" -) - -const ( - PublicFilename = "public.pgp" - PrivateFilename = "private.pgp" -) - -var ( - keyHeaders = map[string]string{ - "Version": "Golang OpenPGP", - "Generated by": "github.com/osspkg/deb-builder", - } -) - -func (v *PGP) Generate(out string, name, comment, email string) error { - buf := &bytes.Buffer{} - - key, err := openpgp.NewEntity(name, comment, email, nil) - if err != nil { - return fmt.Errorf("generate entity: %w", err) - } - - if err = v.setup(key); err != nil { - return fmt.Errorf("setup entity: %w", err) - } - - if err = v.genPrivateKey(key, buf); err != nil { - return fmt.Errorf("generate private key: %w", err) - } - - if err = os.WriteFile(out+"/"+PrivateFilename, buf.Bytes(), 0644); err != nil { - return fmt.Errorf("write private key: %w", err) - } - - buf.Reset() - - if err = v.genPublicKey(key, buf); err != nil { - return fmt.Errorf("generate public key: %w", err) - } - - if err = os.WriteFile(out+"/"+PublicFilename, buf.Bytes(), 0600); err != nil { - return fmt.Errorf("write public key: %w", err) - } - - return nil -} - -func (v *PGP) genPrivateKey(key *openpgp.Entity, w io.Writer) error { - enc, err := armor.Encode(w, openpgp.PrivateKeyType, keyHeaders) - if err != nil { - return fmt.Errorf("create armor: %w", err) - } - - defer enc.Close() //nolint: errcheck - - if err = key.SerializePrivate(enc, nil); err != nil { - return fmt.Errorf("serialize private key: %w", err) - } - - return nil -} - -func (v *PGP) genPublicKey(key *openpgp.Entity, w io.Writer) error { - enc, err := armor.Encode(w, openpgp.PublicKeyType, keyHeaders) - if err != nil { - return fmt.Errorf("create armor: %w", err) - } - - defer enc.Close() //nolint: errcheck - - if err = key.Serialize(enc); err != nil { - return fmt.Errorf("serialize public key: %w", err) - } - - return nil -} - -func (v *PGP) setup(key *openpgp.Entity) error { - // Sign all the identities - for _, id := range key.Identities { - id.SelfSignature.PreferredCompression = []uint8{1, 2, 3, 0} - id.SelfSignature.PreferredHash = []uint8{2, 8, 10, 1, 3, 9, 11} - id.SelfSignature.PreferredSymmetric = []uint8{9, 8, 7, 3, 2} - - if err := id.SelfSignature.SignUserId(id.UserId.Id, key.PrimaryKey, key.PrivateKey, nil); err != nil { - return err - } - } - - return nil -} diff --git a/pkg/pgp/pgp_test.go b/pkg/pgp/pgp_test.go deleted file mode 100644 index db7bca5..0000000 --- a/pkg/pgp/pgp_test.go +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2021-2025 Mikhail Knyazhev . All rights reserved. - * Use of this source code is governed by a BSD-3-Clause license that can be found in the LICENSE file. - */ - -package pgp_test - -import ( - "bytes" - "os" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/osspkg/deb-builder/pkg/pgp" -) - -func TestPGP(t *testing.T) { - enc := pgp.NewPGP() - - err := enc.Generate("/tmp", "Demo", "", "demo@email.xxx") - require.NoError(t, err) - - keyFile, err := os.Open("/tmp/private.pgp") - require.NoError(t, err) - - err = enc.LoadPrivateKey(keyFile, "") - require.NoError(t, err) - - in := bytes.NewBufferString("Hello world") - out := &bytes.Buffer{} - - err = enc.Sign(in, out) - require.NoError(t, err) - - sign := `-----BEGIN PGP SIGNED MESSAGE----- -Hash: SHA512 - -Hello world ------BEGIN PGP SIGNATURE-----` - - require.Contains(t, out.String(), sign) - - err = os.WriteFile("/tmp/message.dsc", out.Bytes(), 0644) - require.NoError(t, err) -}