From 2bf77b207e4fbc559b1f99b80d44661999173eea Mon Sep 17 00:00:00 2001 From: Julian Bensch Date: Thu, 12 Jan 2023 14:49:16 +0100 Subject: [PATCH] Add initial stuff --- .golangci.yml | 110 +++++++++++++++++++++++++++++++ dockerfile | 25 +++++++ drone.yml | 35 ++++++++++ main.go | 178 ++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 348 insertions(+) create mode 100644 .golangci.yml create mode 100644 dockerfile create mode 100644 drone.yml create mode 100644 main.go diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..f55b69b --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,110 @@ +timeout: 30m + +output: + format: tab + sort-results: true + +linters: + disable-all: true + enable: + # checks whether HTTP response body is closed successfully + - bodyclose + + # Tool for code clone detection + - dupl + + # Errcheck is a program for checking for unchecked errors in go programs. + # These unchecked errors can be critical bugs in some cases + - errcheck + + # Tool for detection of long functions + - funlen + + # Checks that no init functions are present in Go code + - gochecknoinits + + # Computes and checks the cyclomatic complexity of functions + - gocyclo + + # Gofmt checks whether code was gofmt-ed. + # By default this tool runs with -s option to check for code simplification + - gofmt + + # In addition to fixing imports, goimports also formats your code in the same style as gofmt. + - goimports + + # Detects when assignments to existing variables are not used + - ineffassign + + # Checks Go code for unused constants, variables, functions and types + - unused + + # Inspects source code for security problems + - gosec + +linters-settings: + gocyclo: + # Minimal code complexity to report. + # Default: 30 (but we recommend 10-20) + min-complexity: 40 + + funlen: + # Checks the number of lines in a function. + # If lower than 0, disable the check. + # Default: 60 + lines: 200 + # Checks the number of statements in a function. + # If lower than 0, disable the check. + # Default: 40 + statements: 200 + + gofmt: + # Simplify code: gofmt with `-s` option. + # Default: true + simplify: true + + goconst: + # Minimal length of string constant. + # Default: 3 + min-len: 3 + # Minimum occurrences of constant string count to trigger issue. + # Default: 3 + min-occurrences: 2 + # Ignore test files. + # Default: false + ignore-tests: true + # Look for existing constants matching the values. + # Default: true + match-constant: false + # Search also for duplicated numbers. + # Default: false + numbers: true + # Minimum value, only works with goconst.numbers + # Default: 3 + min: 2 + # Maximum value, only works with goconst.numbers + # Default: 3 + max: 2 + # Ignore when constant is not used as function argument. + # Default: true + ignore-calls: false + + goimports: + # Put imports beginning with prefix after 3rd-party packages. + # It's a comma-separated list of prefixes. + # Default: "" + local-prefixes: "" + +issues: + # Report all issues + max-issues-per-linter: 0 + max-same-issues: 0 + # Disable some + exclude-rules: + - path: main.go|app.go + linters: + - funlen + - path: _test\.go|mock|integration + linters: + - funlen + - goconst diff --git a/dockerfile b/dockerfile new file mode 100644 index 0000000..1d612d7 --- /dev/null +++ b/dockerfile @@ -0,0 +1,25 @@ +FROM golang:alpine AS builder +# Set necessary environmet variables needed for our image +ENV GO111MODULE=on \ + GOOS=linux \ + GOARCH=amd64 \ + CGO_ENABLED=0 +WORKDIR /build +# Add the required build libraries +# Nothing here! +# Copy and download dependency using go mod +ADD ./src/go.* /build/ +RUN go mod download +# Copy sources to build container +ADD ./src /build/ +# Build the application +WORKDIR /build +RUN ls -al +RUN go build -a -tags musl -o /build/app +###################################### +FROM alpine:3 +LABEL AUTHOR="AUTHOR" +#RUN apk --no-cache add curl +USER nobody +COPY --from=builder --chown=nobody /build/app /custom/app +ENTRYPOINT [ "/custom/app" ] \ No newline at end of file diff --git a/drone.yml b/drone.yml new file mode 100644 index 0000000..be7fb6a --- /dev/null +++ b/drone.yml @@ -0,0 +1,35 @@ +--- +kind: pipeline +type: docker +name: default +steps: +# Build docker image and push to docker hub +- name: build + image: golang:1.19-alpine + pull: if-not-exists + commands: + - go build -o cmd/app main.go + +- name: "lint" + image: golangci/golangci-lint:v1.50 + pull: if-not-exists + commands: + - golangci-lint run + +- name: "add binary release to gitea" + image: plugins/gitea-release + api_key: 5353c40cd692fd01f243b1f935af1962ff03bfd6 + base_url: https://gitea.linuxcode.net + files: cmd/* + +- name: "build and publish docker image" + image: plugins/docker + settings: + registry: gitea.linuxcode.net + repo: gitea.linuxcode.net/drblury/oapi-type-definitions-extractor + dockerfile: dockerfile + tags: latest + username: + from_secret: drone_docker_username + password: + from_secret: drone_docker_password \ No newline at end of file diff --git a/main.go b/main.go new file mode 100644 index 0000000..d34cfbb --- /dev/null +++ b/main.go @@ -0,0 +1,178 @@ +package main + +import ( + "bufio" + "fmt" + "io" + "os" + "os/exec" + "path/filepath" + "strings" +) + +func main() { + if len(os.Args) != 3 { + fmt.Println("Usage: go run main.go ") + return + } + + inputDir := os.Args[1] + outputDir := os.Args[2] + files, err := getGoFiles(inputDir) + if err != nil { + fmt.Println(err) + return + } + + for _, file := range files { + typeDecls, importDecls, err := extractTypes(file) + if err != nil { + fmt.Println(err) + continue + } + + // Filter out structs with "Nullable" at the beginning of their names. + typeDecls = filterNullableTypes(typeDecls) + + // Generate the adapted file name by inserting the "_adapted" string before the ".go" extension of the original file name. + adaptedFile := strings.Replace(filepath.Base(file), ".go", "_adapted.go", 1) + adaptedFile = filepath.Join(outputDir, adaptedFile) + // Write the struct declarations and import declarations to the adapted file. + err = writeStruct(typeDecls, importDecls, adaptedFile) + if err != nil { + fmt.Println(err) + continue + } + + // Reformat the adapted file using gofmt. + err = runGoImports(adaptedFile) + if err != nil { + fmt.Println(err) + continue + } + } +} + +// getGoFiles returns a list of .go files in the given directory. +func getGoFiles(dir string) ([]string, error) { + var files []string + + f, err := os.Open(dir) + if err != nil { + return nil, err + } + defer f.Close() + + fi, err := f.Readdir(-1) + if err != nil { + return nil, err + } + + for _, file := range fi { + if !file.IsDir() && strings.HasSuffix(file.Name(), ".go") { + // Concatenate the directory path and the file name to get the full path. + files = append(files, dir+"/"+file.Name()) + } + } + + return files, nil +} + +// filterNullableTypes filters out structs with "Nullable" at the beginning of their names. +func filterNullableTypes(structDecls []string) []string { + var filteredStructDecls []string + for _, s := range structDecls { + // Check if the struct name starts with "Nullable". + if !strings.HasPrefix(s, "type Nullable") { + filteredStructDecls = append(filteredStructDecls, s) + } + } + return filteredStructDecls +} + +// extractTypes reads a .go file and returns a list of type declarations and a list of import declarations. +func extractTypes(file string) ([]string, []string, error) { + var typeDecls []string + var importDecls []string + + f, err := os.Open(file) + if err != nil { + return nil, nil, err + } + defer f.Close() + + scanner := bufio.NewScanner(f) + var typeDecl, importDecl string + inStruct, inImport := false, false + for scanner.Scan() { + line := scanner.Text() + if strings.HasPrefix(line, "import") { + inImport = true + importDecl = line + "\n" + } else if inImport && (line == "" || strings.HasPrefix(line, ")")) { + importDecl += line + "\n" + inImport = false + importDecls = append(importDecls, importDecl) + } else if inImport { + importDecl += line + "\n" + } else if strings.HasPrefix(line, "type") { + inStruct = true + typeDecl = line + "\n" + } else if inStruct && strings.HasPrefix(line, "}") { + typeDecl += line + "\n" + typeDecls = append(typeDecls, typeDecl) + inStruct = false + } else if inStruct { + typeDecl += line + "\n" + } + } + + if err := scanner.Err(); err != nil { + return nil, nil, err + } + + return typeDecls, importDecls, nil +} + +// writeStruct writes the given struct declarations and import declarations to a new file with the given name. +func writeStruct(structDecls []string, importDecls []string, file string) error { + f, err := os.Create(file) + if err != nil { + return err + } + defer f.Close() + + // Add the package dto declaration at the top of the file. + _, err = io.WriteString(f, "package dto\n\n") + if err != nil { + return err + } + + // Write the import declarations to the file. + for _, importDecl := range importDecls { + _, err = io.WriteString(f, importDecl) + if err != nil { + return err + } + } + + // Write the struct declarations to the file. + for _, s := range structDecls { + _, err = io.WriteString(f, s) + if err != nil { + return err + } + } + + return nil +} + +// runimports reformats the given file using the goimports tool. +func runGoImports(file string) error { + cmd := exec.Command("goimports", "-w", file) + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("%s: %s", err, output) + } + return nil +}