fix: linting (#77)

## Description

<!-- **Please link some issue here describing what you are trying to achieve.**

In case there is no issue present for your PR, please consider creating one.
At least please give us some description what you are trying to achieve and why your change is needed. -->

relates to #1234

## Checklist

- [ ] Issue was linked above
- [ ] Code format was applied: `make fmt`
- [ ] Examples were added / adjusted (see `examples/` directory)
- [x] Docs are up-to-date: `make generate-docs` (will be checked by CI)
- [ ] Unit tests got implemented or updated
- [ ] Acceptance tests got implemented or updated (see e.g. [here](f5f99d1709/stackit/internal/services/dns/dns_acc_test.go))
- [x] Unit tests are passing: `make test` (will be checked by CI)
- [x] No linter issues: `make lint` (will be checked by CI)

Reviewed-on: #77
Co-authored-by: Andre Harms <andre.harms@stackit.cloud>
Co-committed-by: Andre Harms <andre.harms@stackit.cloud>
This commit is contained in:
Andre_Harms 2026-02-19 08:54:34 +00:00 committed by Marcel_Henselin
parent 36eccc52c3
commit 4a2819787d
Signed by: tf-provider.git.onstackit.cloud
GPG key ID: 6D7E8A1ED8955A9C
74 changed files with 3010 additions and 2432 deletions

View file

@ -60,7 +60,7 @@ func (b *Builder) Build() error {
if !b.PackagesOnly {
slog.Info(" ... Checking needed commands available")
err := checkCommands([]string{"tfplugingen-framework", "tfplugingen-openapi"})
err := checkCommands([]string{})
if err != nil {
return err
}
@ -111,7 +111,7 @@ func (b *Builder) Build() error {
}
slog.Info("Creating OAS dir")
err = os.MkdirAll(path.Join(genDir, "oas"), 0755)
err = os.MkdirAll(path.Join(genDir, "oas"), 0o755) //nolint:gosec // this dir is not sensitive, so we can use 0755
if err != nil {
return err
}
@ -158,7 +158,17 @@ func (b *Builder) Build() error {
if err = cmd.Wait(); err != nil {
var exitErr *exec.ExitError
if errors.As(err, &exitErr) {
slog.Error("cmd.Wait", "code", exitErr.ExitCode(), "error", err, "stdout", stdOut.String(), "stderr", stdErr.String())
slog.Error(
"cmd.Wait",
"code",
exitErr.ExitCode(),
"error",
err,
"stdout",
stdOut.String(),
"stderr",
stdErr.String(),
)
return fmt.Errorf("%s", stdErr.String())
}
if err != nil {
@ -192,7 +202,11 @@ func (b *Builder) Build() error {
}
slog.Info("Rearranging package directories")
err = os.MkdirAll(path.Join(*root, "pkg_gen"), 0755) // noqa:gosec
//nolint:gosec // this dir is not sensitive, so we can use 0755
err = os.MkdirAll(
path.Join(*root, "pkg_gen"),
0o755,
)
if err != nil {
return err
}
@ -202,20 +216,21 @@ func (b *Builder) Build() error {
return err
}
for _, item := range items {
if item.IsDir() {
slog.Info(" -> package", "name", item.Name())
tgtDir := path.Join(*root, "pkg_gen", item.Name())
if fileExists(tgtDir) {
delErr := os.RemoveAll(tgtDir)
if delErr != nil {
return delErr
}
}
err = os.Rename(path.Join(srcDir, item.Name()), tgtDir)
if err != nil {
return err
if !item.IsDir() {
continue
}
slog.Info(" -> package", "name", item.Name())
tgtDir := path.Join(*root, "pkg_gen", item.Name())
if fileExists(tgtDir) {
delErr := os.RemoveAll(tgtDir)
if delErr != nil {
return delErr
}
}
err = os.Rename(path.Join(srcDir, item.Name()), tgtDir)
if err != nil {
return err
}
}
if !b.PackagesOnly {
@ -275,8 +290,8 @@ type templateData struct {
Fields []string
}
func fileExists(path string) bool {
_, err := os.Stat(path)
func fileExists(pathValue string) bool {
_, err := os.Stat(pathValue)
if os.IsNotExist(err) {
return false
}
@ -312,10 +327,22 @@ func createBoilerplate(rootFolder, folder string) error {
resourceName := res.Name()
dsFile := path.Join(folder, svc.Name(), res.Name(), "datasources_gen", fmt.Sprintf("%s_data_source_gen.go", res.Name()))
dsFile := path.Join(
folder,
svc.Name(),
res.Name(),
"datasources_gen",
fmt.Sprintf("%s_data_source_gen.go", res.Name()),
)
handleDS = fileExists(dsFile)
resFile := path.Join(folder, svc.Name(), res.Name(), "resources_gen", fmt.Sprintf("%s_resource_gen.go", res.Name()))
resFile := path.Join(
folder,
svc.Name(),
res.Name(),
"resources_gen",
fmt.Sprintf("%s_resource_gen.go", res.Name()),
)
handleRes = fileExists(resFile)
dsGoFile := path.Join(folder, svc.Name(), res.Name(), "datasource.go")
@ -407,7 +434,6 @@ func createBoilerplate(rootFolder, folder string) error {
if err != nil {
return err
}
}
}
}
@ -416,7 +442,7 @@ func createBoilerplate(rootFolder, folder string) error {
}
func ucfirst(s string) string {
if len(s) == 0 {
if s == "" {
return ""
}
return strings.ToUpper(s[:1]) + s[1:]
@ -451,8 +477,8 @@ func writeTemplateToFile(tplName, tplFile, outFile string, data *templateData) e
}
func generateServiceFiles(rootDir, generatorDir string) error {
// slog.Info("Generating specs folder")
err := os.MkdirAll(path.Join(rootDir, "generated", "specs"), 0755)
//nolint:gosec // this file is not sensitive, so we can use 0755
err := os.MkdirAll(path.Join(rootDir, "generated", "specs"), 0o755)
if err != nil {
return err
}
@ -490,7 +516,6 @@ func generateServiceFiles(rootDir, generatorDir string) error {
continue
}
// slog.Info("Checking spec", "name", spec.Name())
r := regexp.MustCompile(`^(.*)_config.yml$`)
matches := r.FindAllStringSubmatch(specFile.Name(), -1)
if matches != nil {
@ -506,27 +531,44 @@ func generateServiceFiles(rootDir, generatorDir string) error {
resource,
)
oasFile := path.Join(generatorDir, "oas", fmt.Sprintf("%s%s.json", service.Name(), svcVersion.Name()))
oasFile := path.Join(
generatorDir,
"oas",
fmt.Sprintf("%s%s.json", service.Name(), svcVersion.Name()),
)
if _, oasErr := os.Stat(oasFile); os.IsNotExist(oasErr) {
slog.Warn(" could not find matching oas", "svc", service.Name(), "version", svcVersion.Name())
slog.Warn(
" could not find matching oas",
"svc",
service.Name(),
"version",
svcVersion.Name(),
)
continue
}
scName := fmt.Sprintf("%s%s", service.Name(), svcVersion.Name())
scName = strings.ReplaceAll(scName, "-", "")
err = os.MkdirAll(path.Join(rootDir, "generated", "internal", "services", scName, resource), 0755)
//nolint:gosec // this file is not sensitive, so we can use 0755
err = os.MkdirAll(path.Join(rootDir, "generated", "internal", "services", scName, resource), 0o755)
if err != nil {
return err
}
// slog.Info("Generating openapi spec json")
specJsonFile := path.Join(rootDir, "generated", "specs", fmt.Sprintf("%s_%s_spec.json", scName, resource))
specJsonFile := path.Join(
rootDir,
"generated",
"specs",
fmt.Sprintf("%s_%s_spec.json", scName, resource),
)
var stdOut, stdErr bytes.Buffer
// noqa:gosec
// nolint:gosec // #nosec this command is not using any untrusted input, so we can ignore gosec warning
cmd := exec.Command(
"tfplugingen-openapi",
"go",
"run",
"github.com/hashicorp/terraform-plugin-codegen-openapi/cmd/tfplugingen-openapi",
"generate",
"--config",
path.Join(rootDir, "service_specs", service.Name(), svcVersion.Name(), fileName),
@ -553,11 +595,29 @@ func generateServiceFiles(rootDir, generatorDir string) error {
if err = cmd.Wait(); err != nil {
var exitErr *exec.ExitError
if errors.As(err, &exitErr) {
slog.Error("tfplugingen-openapi generate", "code", exitErr.ExitCode(), "error", err, "stdout", stdOut.String(), "stderr", stdErr.String())
slog.Error(
"tfplugingen-openapi generate",
"code",
exitErr.ExitCode(),
"error",
err,
"stdout",
stdOut.String(),
"stderr",
stdErr.String(),
)
return fmt.Errorf("%s", stdErr.String())
}
if err != nil {
slog.Error("tfplugingen-openapi generate", "err", err, "stdout", stdOut.String(), "stderr", stdErr.String())
slog.Error(
"tfplugingen-openapi generate",
"err",
err,
"stdout",
stdOut.String(),
"stderr",
stdErr.String(),
)
return err
}
}
@ -565,18 +625,26 @@ func generateServiceFiles(rootDir, generatorDir string) error {
slog.Warn(" command output", "stdout", stdOut.String(), "stderr", stdErr.String())
}
// slog.Info("Creating terraform svc resource files folder")
tgtFolder := path.Join(rootDir, "generated", "internal", "services", scName, resource, "resources_gen")
err = os.MkdirAll(tgtFolder, 0755)
tgtFolder := path.Join(
rootDir,
"generated",
"internal",
"services",
scName,
resource,
"resources_gen",
)
//nolint:gosec // this file is not sensitive, so we can use 0755
err = os.MkdirAll(tgtFolder, 0o755)
if err != nil {
return err
}
// slog.Info("Generating terraform svc resource files")
// noqa:gosec
// nolint:gosec // #nosec this command is not using any untrusted input, so we can ignore gosec warning
cmd2 := exec.Command(
"tfplugingen-framework",
"go",
"run",
"github.com/hashicorp/terraform-plugin-codegen-framework/cmd/tfplugingen-framework",
"generate",
"resources",
"--input",
@ -597,27 +665,53 @@ func generateServiceFiles(rootDir, generatorDir string) error {
if err = cmd2.Wait(); err != nil {
var exitErr *exec.ExitError
if errors.As(err, &exitErr) {
slog.Error("tfplugingen-framework generate resources", "code", exitErr.ExitCode(), "error", err, "stdout", stdOut.String(), "stderr", stdErr.String())
slog.Error(
"tfplugingen-framework generate resources",
"code",
exitErr.ExitCode(),
"error",
err,
"stdout",
stdOut.String(),
"stderr",
stdErr.String(),
)
return fmt.Errorf("%s", stdErr.String())
}
if err != nil {
slog.Error("tfplugingen-framework generate resources", "err", err, "stdout", stdOut.String(), "stderr", stdErr.String())
slog.Error(
"tfplugingen-framework generate resources",
"err",
err,
"stdout",
stdOut.String(),
"stderr",
stdErr.String(),
)
return err
}
}
// slog.Info("Creating terraform svc datasource files folder")
tgtFolder = path.Join(rootDir, "generated", "internal", "services", scName, resource, "datasources_gen")
err = os.MkdirAll(tgtFolder, 0755)
tgtFolder = path.Join(
rootDir,
"generated",
"internal",
"services",
scName,
resource,
"datasources_gen",
)
//nolint:gosec // this directory is not sensitive, so we can use 0755
err = os.MkdirAll(tgtFolder, 0o755)
if err != nil {
return err
}
// slog.Info("Generating terraform svc resource files")
// noqa:gosec
// nolint:gosec // #nosec this command is not using any untrusted input, so we can ignore gosec warning
cmd3 := exec.Command(
"tfplugingen-framework",
"go",
"run",
"github.com/hashicorp/terraform-plugin-codegen-framework/cmd/tfplugingen-framework",
"generate",
"data-sources",
"--input",
@ -639,11 +733,29 @@ func generateServiceFiles(rootDir, generatorDir string) error {
if err = cmd3.Wait(); err != nil {
var exitErr *exec.ExitError
if errors.As(err, &exitErr) {
slog.Error("tfplugingen-framework generate data-sources", "code", exitErr.ExitCode(), "error", err, "stdout", stdOut.String(), "stderr", stdErr.String())
slog.Error(
"tfplugingen-framework generate data-sources",
"code",
exitErr.ExitCode(),
"error",
err,
"stdout",
stdOut.String(),
"stderr",
stdErr.String(),
)
return fmt.Errorf("%s", stdErr.String())
}
if err != nil {
slog.Error("tfplugingen-framework generate data-sources", "err", err, "stdout", stdOut.String(), "stderr", stdErr.String())
slog.Error(
"tfplugingen-framework generate data-sources",
"err",
err,
"stdout",
stdOut.String(),
"stderr",
stdErr.String(),
)
return err
}
}
@ -674,10 +786,10 @@ func handleTfTagForDatasourceFile(filePath, service, resource string) error {
if err != nil {
return err
}
defer f.Close()
root, err := getRoot()
if err != nil {
//nolint:gocritic // in this case, we want to log the error and exit, as we cannot proceed without the root directory
log.Fatal(err)
}
@ -685,7 +797,6 @@ func handleTfTagForDatasourceFile(filePath, service, resource string) error {
if err != nil {
return err
}
defer tmp.Close()
sc := bufio.NewScanner(f)
for sc.Scan() {
@ -709,6 +820,7 @@ func handleTfTagForDatasourceFile(filePath, service, resource string) error {
return err
}
//nolint:gosec // path traversal is not a concern here
if err := os.Rename(tmp.Name(), filePath); err != nil {
log.Fatal(err)
}
@ -773,13 +885,23 @@ func copyFile(src, dst string) (int64, error) {
if err != nil {
return 0, err
}
defer source.Close()
defer func(source *os.File) {
err := source.Close()
if err != nil {
slog.Error("copyFile", "err", err)
}
}(source)
destination, err := os.Create(dst)
if err != nil {
return 0, err
}
defer destination.Close()
defer func(destination *os.File) {
err := destination.Close()
if err != nil {
slog.Error("copyFile", "err", err)
}
}(destination)
nBytes, err := io.Copy(destination, source)
return nBytes, err
}
@ -790,10 +912,8 @@ func getOnlyLatest(m map[string]version) (map[string]version, error) {
item, ok := tmpMap[k]
if !ok {
tmpMap[k] = v
} else {
if item.major == v.major && item.minor < v.minor {
tmpMap[k] = v
}
} else if item.major == v.major && item.minor < v.minor {
tmpMap[k] = v
}
}
return tmpMap, nil
@ -807,18 +927,19 @@ func getVersions(dir string) (map[string]version, error) {
}
for _, entry := range children {
if entry.IsDir() {
versions, err := os.ReadDir(path.Join(dir, "services", entry.Name()))
if err != nil {
return nil, err
}
m, err2 := extractVersions(entry.Name(), versions)
if err2 != nil {
return m, err2
}
for k, v := range m {
res[k] = v
}
if !entry.IsDir() {
continue
}
versions, err := os.ReadDir(path.Join(dir, "services", entry.Name()))
if err != nil {
return nil, err
}
m, err2 := extractVersions(entry.Name(), versions)
if err2 != nil {
return m, err2
}
for k, v := range m {
res[k] = v
}
}
return res, nil
@ -827,20 +948,21 @@ func getVersions(dir string) (map[string]version, error) {
func extractVersions(service string, versionDirs []os.DirEntry) (map[string]version, error) {
res := make(map[string]version)
for _, vDir := range versionDirs {
if vDir.IsDir() {
r := regexp.MustCompile(`v([0-9]+)([a-z]+)([0-9]*)`)
matches := r.FindAllStringSubmatch(vDir.Name(), -1)
if matches == nil {
continue
}
svc, ver, err := handleVersion(service, matches[0])
if err != nil {
return nil, err
}
if !vDir.IsDir() {
continue
}
r := regexp.MustCompile(`v(\d+)([a-z]+)(\d*)`)
matches := r.FindAllStringSubmatch(vDir.Name(), -1)
if matches == nil {
continue
}
svc, ver, err := handleVersion(service, matches[0])
if err != nil {
return nil, err
}
if svc != nil && ver != nil {
res[*svc] = *ver
}
if svc != nil && ver != nil {
res[*svc] = *ver
}
}
return res, nil
@ -927,30 +1049,25 @@ func getTokens(fileName string) ([]string, error) {
return nil, err
}
ast.Inspect(node, func(n ast.Node) bool {
// Suche nach Typ-Deklarationen (structs)
ts, ok := n.(*ast.TypeSpec)
if ok {
if strings.Contains(ts.Name.Name, "Model") {
// fmt.Printf("found model: %s\n", ts.Name.Name)
ast.Inspect(ts, func(sn ast.Node) bool {
tts, tok := sn.(*ast.Field)
if tok {
// fmt.Printf(" found: %+v\n", tts.Names[0])
// spew.Dump(tts.Type)
result = append(result, tts.Names[0].String())
// fld, fldOk := tts.Type.(*ast.Ident)
//if fldOk {
// fmt.Printf("type: %+v\n", fld)
//}
}
return true
})
ast.Inspect(
node, func(n ast.Node) bool {
// Suche nach Typ-Deklarationen (structs)
ts, ok := n.(*ast.TypeSpec)
if ok {
if strings.Contains(ts.Name.Name, "Model") {
ast.Inspect(
ts, func(sn ast.Node) bool {
tts, tok := sn.(*ast.Field)
if tok {
result = append(result, tts.Names[0].String())
}
return true
},
)
}
}
}
return true
})
return true
},
)
return result, nil
}

View file

@ -3,6 +3,7 @@ package build
import (
"fmt"
"io"
"log/slog"
"os"
"path/filepath"
"syscall"
@ -74,14 +75,24 @@ func Copy(srcFile, dstFile string) error {
return err
}
defer out.Close()
defer func(out *os.File) {
err := out.Close()
if err != nil {
slog.Error("failed to close file", slog.Any("err", err))
}
}(out)
in, err := os.Open(srcFile)
if err != nil {
return err
}
defer in.Close()
defer func(in *os.File) {
err := in.Close()
if err != nil {
slog.Error("error closing destination file", slog.Any("err", err))
}
}(in)
_, err = io.Copy(out, in)
if err != nil {

View file

@ -16,7 +16,7 @@ var buildCmd = &cobra.Command{
Use: "build",
Short: "Build the necessary boilerplate",
Long: `...`,
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, _ []string) error {
b := build.Builder{
SkipClone: skipClone,
SkipCleanup: skipCleanup,
@ -30,7 +30,7 @@ func NewBuildCmd() *cobra.Command {
return buildCmd
}
func init() { // nolint: gochecknoinits
func init() { //nolint:gochecknoinits // This is the standard way to set up Cobra commands
buildCmd.Flags().BoolVarP(&skipCleanup, "skip-clean", "c", false, "Skip cleanup steps")
buildCmd.Flags().BoolVarP(&skipClone, "skip-clone", "g", false, "Skip cloning from git")
buildCmd.Flags().BoolVarP(&packagesOnly, "packages-only", "p", false, "Only generate packages")

View file

@ -12,16 +12,15 @@ var examplesCmd = &cobra.Command{
Use: "examples",
Short: "create examples",
Long: `...`,
RunE: func(cmd *cobra.Command, args []string) error {
//filePathStr := "stackit/internal/services/postgresflexalpha/database/datasources_gen/database_data_source_gen.go"
RunE: func(_ *cobra.Command, _ []string) error {
// filePathStr := "stackit/internal/services/postgresflexalpha/database/datasources_gen/database_data_source_gen.go"
//
//src, err := os.ReadFile(filePathStr)
//if err != nil {
// src, err := os.ReadFile(filePathStr)
// if err != nil {
// return err
//}
//
//i := interp.New(
// i := interp.New(
// interp.Options{
// GoPath: "/home/henselinm/.asdf/installs/golang/1.25.6/packages",
// BuildTags: nil,
@ -34,46 +33,46 @@ var examplesCmd = &cobra.Command{
// Unrestricted: false,
// },
//)
//err = i.Use(i.Symbols("github.com/hashicorp/terraform-plugin-framework-validators"))
//if err != nil {
// err = i.Use(i.Symbols("github.com/hashicorp/terraform-plugin-framework-validators"))
// if err != nil {
// return err
//}
//err = i.Use(stdlib.Symbols)
//if err != nil {
// err = i.Use(stdlib.Symbols)
// if err != nil {
// return err
//}
//_, err = i.Eval(string(src))
//if err != nil {
// _, err = i.Eval(string(src))
// if err != nil {
// return err
//}
//
//v, err := i.Eval("DatabaseDataSourceSchema")
//if err != nil {
// v, err := i.Eval("DatabaseDataSourceSchema")
// if err != nil {
// return err
//}
//
//bar := v.Interface().(func(string) string)
// bar := v.Interface().(func(string) string)
//
//r := bar("Kung")
//println(r)
// r := bar("Kung")
// println(r)
//
//evalPath, err := i.EvalPath(filePathStr)
//if err != nil {
// evalPath, err := i.EvalPath(filePathStr)
// if err != nil {
// return err
//}
//
//fmt.Printf("%+v\n", evalPath)
// fmt.Printf("%+v\n", evalPath)
//_, err = i.Eval(`import "fmt"`)
//if err != nil {
// _, err = i.Eval(`import "fmt"`)
// if err != nil {
// return err
//}
//_, err = i.Eval(`func Hallo() { fmt.Println("Hi!") }`)
//if err != nil {
// _, err = i.Eval(`func Hallo() { fmt.Println("Hi!") }`)
// if err != nil {
// return err
//}
//v = i.Symbols("Hallo")
// v = i.Symbols("Hallo")
// fmt.Println(v)
return workServices()
@ -110,6 +109,6 @@ func NewExamplesCmd() *cobra.Command {
return examplesCmd
}
//func init() { // nolint: gochecknoinits
// func init() { // nolint: gochecknoinits
// examplesCmd.Flags().BoolVarP(&example, "example", "e", false, "example")
//}

View file

@ -24,7 +24,7 @@ var getFieldsCmd = &cobra.Command{
Use: "get-fields",
Short: "get fields from file",
Long: `...`,
PreRunE: func(cmd *cobra.Command, args []string) error {
PreRunE: func(_ *cobra.Command, _ []string) error {
typeStr := "data_source"
if resType != "resource" && resType != "datasource" {
return fmt.Errorf("--type can only be resource or datasource")
@ -76,13 +76,13 @@ var getFieldsCmd = &cobra.Command{
//// Enum check
// switch format {
//case "json", "yaml":
// case "json", "yaml":
//default:
// return fmt.Errorf("invalid --format: %s (want json|yaml)", format)
//}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, _ []string) error {
return getFields(filePath)
},
}
@ -107,31 +107,26 @@ func getTokens(fileName string) ([]string, error) {
return nil, err
}
ast.Inspect(node, func(n ast.Node) bool {
// Suche nach Typ-Deklarationen (structs)
ts, ok := n.(*ast.TypeSpec)
if ok {
if strings.Contains(ts.Name.Name, "Model") {
// fmt.Printf("found model: %s\n", ts.Name.Name)
ast.Inspect(ts, func(sn ast.Node) bool {
tts, tok := sn.(*ast.Field)
if tok {
// fmt.Printf(" found: %+v\n", tts.Names[0])
// spew.Dump(tts.Type)
result = append(result, tts.Names[0].String())
// fld, fldOk := tts.Type.(*ast.Ident)
//if fldOk {
// fmt.Printf("type: %+v\n", fld)
//}
}
return true
})
ast.Inspect(
node, func(n ast.Node) bool {
// Suche nach Typ-Deklarationen (structs)
ts, ok := n.(*ast.TypeSpec)
if ok {
if strings.Contains(ts.Name.Name, "Model") {
ast.Inspect(
ts, func(sn ast.Node) bool {
tts, tok := sn.(*ast.Field)
if tok {
result = append(result, tts.Names[0].String())
}
return true
},
)
}
}
}
return true
})
return true
},
)
return result, nil
}
@ -139,9 +134,15 @@ func NewGetFieldsCmd() *cobra.Command {
return getFieldsCmd
}
func init() { // nolint: gochecknoinits
func init() { //nolint:gochecknoinits //this is the only way to add the command to the rootCmd
getFieldsCmd.Flags().StringVarP(&inFile, "infile", "i", "", "input filename incl path")
getFieldsCmd.Flags().StringVarP(&svcName, "service", "s", "", "service name")
getFieldsCmd.Flags().StringVarP(&resName, "resource", "r", "", "resource name")
getFieldsCmd.Flags().StringVarP(&resType, "type", "t", "resource", "resource type (data-source or resource [default])")
getFieldsCmd.Flags().StringVarP(
&resType,
"type",
"t",
"resource",
"resource type (data-source or resource [default])",
)
}

View file

@ -35,36 +35,27 @@ type GpgPublicKey struct {
}
func (p *Provider) CreateArchitectureFiles() error {
// var namespace, provider, distPath, repoName, version, gpgFingerprint, gpgPubKeyFile, domain string
log.Println("* Creating architecture files in target directories")
// filename = terraform-provider-[provider]_0.0.1_darwin_amd64.zip - provider_name + version + target + architecture + .zip
// prefix := fmt.Sprintf("v1/providers/%s/%s/%s/", namespace, provider, version)
prefix := path.Join("v1", "providers", p.Namespace, p.Provider, p.Version)
// pathPrefix := fmt.Sprintf("release/%s", prefix)
pathPrefix := path.Join("release", prefix)
// urlPrefix := fmt.Sprintf("https://%s/%s", domain, prefix)
urlPrefix, err := url.JoinPath("https://", p.Domain, prefix)
if err != nil {
return fmt.Errorf("error creating base url: %w", err)
}
// download url = https://example.com/v1/providers/namespace/provider/0.0.1/download/terraform-provider_0.0.1_darwin_amd64.zip
downloadUrlPrefix, err := url.JoinPath(urlPrefix, "download")
if err != nil {
return fmt.Errorf("error crearting download url: %w", err)
}
downloadPathPrefix := path.Join(pathPrefix, "download")
// shasums url = https://example.com/v1/providers/namespace/provider/0.0.1/terraform-provider_0.0.1_SHA256SUMS
shasumsUrl, err := url.JoinPath(urlPrefix, fmt.Sprintf("%s_%s_SHA256SUMS", p.RepoName, p.Version))
if err != nil {
return fmt.Errorf("error creating shasums url: %w", err)
}
// shasums_signature_url = https://example.com/v1/providers/namespace/provider/0.0.1/terraform-provider_0.0.1_SHA256SUMS.sig
shasumsSigUrl := shasumsUrl + ".sig"
gpgAsciiPub, err := p.ReadGpgFile()
@ -116,33 +107,6 @@ func (p *Provider) CreateArchitectureFiles() error {
},
},
}
// var architectureTemplate = []byte(fmt.Sprintf(`
//{
// "protocols": [
// "4.0",
// "5.1",
// "6.0"
// ],
// "os": "%s",
// "arch": "%s",
// "filename": "%s",
// "download_url": "%s",
// "shasums_url": "%s",
// "shasums_signature_url": "%s",
// "shasum": "%s",
// "signing_keys": {
// "gpg_public_keys": [
// {
// "key_id": "%s",
// "ascii_armor": "%s",
// "trust_signature": "",
// "source": "",
// "source_url": ""
// }
// ]
// }
//}
// `, target, arch, fileName, downloadUrl, shasumsUrl, shasumsSigUrl, shasum, gpgFingerprint, gpgAsciiPub))
log.Printf(" - Arch file: %s", archFileName)
@ -160,8 +124,12 @@ func WriteArchitectureFile(filePath string, arch Architecture) error {
if err != nil {
return fmt.Errorf("error encoding data: %w", err)
}
err = os.WriteFile(filePath, jsonString, os.ModePerm)
//nolint:gosec // this file is not sensitive, so we can use os.ModePerm
err = os.WriteFile(
filePath,
jsonString,
os.ModePerm,
)
if err != nil {
return fmt.Errorf("error writing data: %w", err)
}

View file

@ -161,10 +161,12 @@ func (p *Provider) createVersionsFile() error {
target := fileNameSplit[2]
arch := fileNameSplit[3]
version.Platforms = append(version.Platforms, Platform{
OS: target,
Arch: arch,
})
version.Platforms = append(
version.Platforms, Platform{
OS: target,
Arch: arch,
},
)
}
data := Data{}
@ -206,16 +208,19 @@ func (p *Provider) CreateWellKnown() error {
log.Println("* Creating .well-known directory")
pathString := path.Join(p.RootPath, "release", ".well-known")
//nolint:gosec // this file is not sensitive, so we can use ModePerm
err := os.MkdirAll(pathString, os.ModePerm)
if err != nil && !errors.Is(err, fs.ErrExist) {
return fmt.Errorf("error creating '%s' dir: %w", pathString, err)
}
log.Println(" - Writing to .well-known/terraform.json file")
//nolint:gosec // this file is not sensitive, so we can use 0644
err = os.WriteFile(
fmt.Sprintf("%s/terraform.json", pathString),
[]byte(`{"providers.v1": "/v1/providers/"}`),
0644,
0o644,
)
if err != nil {
return err
@ -224,9 +229,10 @@ func (p *Provider) CreateWellKnown() error {
return nil
}
func CreateDir(path string) error {
log.Printf("* Creating %s directory", path)
err := os.MkdirAll(path, os.ModePerm)
func CreateDir(pathValue string) error {
log.Printf("* Creating %s directory", pathValue)
//nolint:gosec // this file is not sensitive, so we can use ModePerm
err := os.MkdirAll(pathValue, os.ModePerm)
if errors.Is(err, fs.ErrExist) {
return nil
}
@ -269,13 +275,23 @@ func CopyFile(src, dst string) (int64, error) {
if err != nil {
return 0, err
}
defer source.Close()
defer func(source *os.File) {
err := source.Close()
if err != nil {
slog.Error("error closing source file", slog.Any("err", err))
}
}(source)
destination, err := os.Create(dst)
if err != nil {
return 0, err
}
defer destination.Close()
defer func(destination *os.File) {
err := destination.Close()
if err != nil {
slog.Error("error closing destination file", slog.Any("err", err))
}
}(destination)
nBytes, err := io.Copy(destination, source)
return nBytes, err
}

View file

@ -35,7 +35,12 @@ func (d *Data) WriteToFile(filePath string) error {
return fmt.Errorf("error encoding data: %w", err)
}
err = os.WriteFile(filePath, jsonString, os.ModePerm)
//nolint:gosec // this file is not sensitive, so we can use os.ModePerm
err = os.WriteFile(
filePath,
jsonString,
os.ModePerm,
)
if err != nil {
return fmt.Errorf("error writing data: %w", err)
}
@ -86,7 +91,13 @@ func (d *Data) LoadFromUrl(uri string) error {
if err != nil {
return err
}
defer os.Remove(file.Name()) // Clean up
defer func(name string) {
//nolint:gosec // The file path is generated by os.CreateTemp and is not user-controllable
err := os.Remove(name)
if err != nil {
slog.Error("failed to remove temporary file", slog.Any("err", err))
}
}(file.Name()) // Clean up
err = DownloadFile(
u.String(),
@ -123,20 +134,30 @@ func (v *Version) AddProtocol(p string) error {
// DownloadFile will download a url and store it in local filepath.
// It writes to the destination file as it downloads it, without
// loading the entire file into memory.
func DownloadFile(url string, filepath string) error {
func DownloadFile(urlValue, filepath string) error {
// Create the file
//nolint:gosec // path traversal is not a concern here, as the filepath is generated by us and not user input
out, err := os.Create(filepath)
if err != nil {
return err
}
defer out.Close()
defer func(out *os.File) {
err := out.Close()
if err != nil {
slog.Error("failed to close file", slog.Any("err", err))
}
}(out)
// Get the data
resp, err := http.Get(url)
//nolint:gosec,bodyclose // this is a controlled URL, not user input
resp, err := http.Get(urlValue)
if err != nil {
return err
}
defer resp.Body.Close()
defer func(Body io.ReadCloser) {
_ = Body.Close()
}(resp.Body)
// Write the body to file
_, err = io.Copy(out, resp.Body)

View file

@ -29,20 +29,32 @@ var publishCmd = &cobra.Command{
Use: "publish",
Short: "Publish terraform provider",
Long: `...`,
RunE: func(_ *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, _ []string) error {
return publish()
},
}
func init() { // nolint: gochecknoinits
func init() { //nolint:gochecknoinits //this is the standard way to set up cobra commands
publishCmd.Flags().StringVarP(&namespace, "namespace", "n", "", "Namespace for the Terraform registry.")
publishCmd.Flags().StringVarP(&domain, "domain", "d", "", "Domain for the Terraform registry.")
publishCmd.Flags().StringVarP(&providerName, "providerName", "p", "", "ProviderName for the Terraform registry.")
publishCmd.Flags().StringVarP(&distPath, "distPath", "x", "dist", "Dist Path for the Terraform registry.")
publishCmd.Flags().StringVarP(&repoName, "repoName", "r", "", "RepoName for the Terraform registry.")
publishCmd.Flags().StringVarP(&version, "version", "v", "", "Version for the Terraform registry.")
publishCmd.Flags().StringVarP(&gpgFingerprint, "gpgFingerprint", "f", "", "GPG Fingerprint for the Terraform registry.")
publishCmd.Flags().StringVarP(&gpgPubKeyFile, "gpgPubKeyFile", "k", "", "GPG PubKey file name for the Terraform registry.")
publishCmd.Flags().StringVarP(
&gpgFingerprint,
"gpgFingerprint",
"f",
"",
"GPG Fingerprint for the Terraform registry.",
)
publishCmd.Flags().StringVarP(
&gpgPubKeyFile,
"gpgPubKeyFile",
"k",
"",
"GPG PubKey file name for the Terraform registry.",
)
err := publishCmd.MarkFlagRequired("namespace")
if err != nil {
@ -105,6 +117,7 @@ func publish() error {
// Create release dir - only the contents of this need to be uploaded to S3
log.Printf("* Creating release directory")
//nolint:gosec // this directory is not sensitive, so we can use 0750
err = os.MkdirAll(path.Join(p.RootPath, "release"), os.ModePerm)
if err != nil && !errors.Is(err, fs.ErrExist) {
return fmt.Errorf("error creating '%s' dir: %w", path.Join(p.RootPath, "release"), err)