Commit ea121941 authored by Pierre Smeyers's avatar Pierre Smeyers
Browse files

Merge branch '28-no-golang-files-found-by-go-sbom' into 'master'

Resolve "no golang files found by go-sbom"

Closes #28

See merge request to-be-continuous/golang!58
parents dc114ab5 f4213df6
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ The Go template uses some global configuration used throughout all jobs.
### build & test jobs

You can specify if you want the template to build an `application` or `modules` with the `GO_BUILD_MODE` variable. It may have the following values: 

 * `application` will make the build output the binaries (use `-o` build option, won't work if there is no `main.go` file)
 * `modules` won't output the binaries (no use of the `-o` option)
 * `auto` the template will rely on the presence of a `main.go` file to detect if it should output the binaries.
@@ -165,6 +166,15 @@ It is bound to the `test` stage, and uses the following variables:
| `GO_SBOM_IMAGE` | Image of cyclonedx-gomod used for SBOM analysis | `registry.hub.docker.com/cyclonedx/cyclonedx-gomod:latest` |
| `GO_SBOM_OPTS` | [@cyclonedx/cyclonedx-gomod options](https://github.com/CycloneDX/cyclonedx-gomod#usage) used for SBOM analysis | `-main .` |

:warning: if you don't have your main class located at the root of your `GO_PROJECT_DIR`, then you will need to override the `-main` option in `GO_SBOM_OPTS` and define your real main class location.

Example:

```yaml
variables:
  GO_SBOM_OPTS: "-main cmd/my_app"
```

### `go-govulncheck` job

This job enables Vulnerability Management with [Govulncheck](https://go.dev/blog/vuln).
+123 −114
Original line number Diff line number Diff line
@@ -128,125 +128,15 @@ stages:
  set -e

  function log_info() {
      echo -e "[\\e[1;94mINFO\\e[0m] $*"
    >&2 echo -e "[\\e[1;94mINFO\\e[0m] $*"
  }

  function log_warn() {
      echo -e "[\\e[1;93mWARN\\e[0m] $*"
    >&2 echo -e "[\\e[1;93mWARN\\e[0m] $*"
  }

  function log_error() {
      echo -e "[\\e[1;91mERROR\\e[0m] $*"
  }

  function output_coverage() {
    coverage_out=reports/go-coverage.native.out
    if [[ -f "$coverage_out" ]]
    then
      log_info "--- \\e[32mCoverage report(s) found\\e[0m (\\e[33;1m${coverage_out}\\e[0m): output"
      percent=$(go tool cover -func="$coverage_out" | tail -1 | awk -F" " '{print $NF}')
      echo "${percent} covered"

      go get github.com/boumenot/gocover-cobertura
      go run github.com/boumenot/gocover-cobertura < "$coverage_out" > reports/go-coverage.cobertura.xml
    else
      log_info "--- \\e[32mCoverage report(s) not found\\e[0m: skip"
    fi
  }

  function go_build() {
    case "${GO_BUILD_MODE}" in
      application)
        go_build_application
        ;;
      modules)
        go_build_modules
        ;;
      auto)
        go_build_auto
        ;;
      *)
        log_error "invalid \\e[94;1mGO_BUILD_MODE\\e[0m (expected values are \\e[96;1mapplication\\e[0m, \\e[96;1mmodules\\e[0m, \\e[96;1mauto\\e[0m)"
        exit 1
        ;;
    esac
  }

  function go_build_auto() {
    log_info "auto build mode, looking up for a \\e[33;1mmain\\e[0m package..."
    if grep -Rw --include "*.go" "^package main"
    then
      go_build_application
    else
      go_build_modules
    fi
  }

  function go_build_application() {
    log_info "building go application"
    GO_TARGET_OS="${GO_TARGET_OS:-$GOOS}"
    GO_TARGET_ARCH="${GO_TARGET_ARCH:-$GOARCH}"
    target_dir="$GOBIN/$GO_TARGET_OS/$GO_TARGET_ARCH"
    mkdir -p "$target_dir"
    # shellcheck disable=SC2086
    GOOS="$GO_TARGET_OS" GOARCH="$GO_TARGET_ARCH" go build -ldflags="$GO_BUILD_LINKER_FLAGS" $GO_BUILD_FLAGS -o "$target_dir" $GO_BUILD_PACKAGES
  }

  function go_build_modules() {
    log_info "building go modules"
    # shellcheck disable=SC2086
    go build -ldflags="$GO_BUILD_LINKER_FLAGS" $GO_BUILD_FLAGS $GO_BUILD_PACKAGES
  }

  function go_test() {
    mkdir -p -m 777 reports
    local go_text_report="reports/go-test.native.txt"

    set +e
    # shellcheck disable=SC2086
    go test $GO_TEST_FLAGS "-coverprofile=reports/go-coverage.native.out" $GO_TEST_PACKAGES > "$go_text_report"
    test_rc=$?
    set -e

    # dump text report in the console
    cat "$go_text_report"

    # compute and dump code coverage in the console
    output_coverage

    # produce JUnit report (for GitLab)
    install_go_junit_report
    "$GOBIN/go-junit-report" < "$go_text_report" > reports/go-test.xunit.xml

    # produce JSON report (for SonarQube)
    go tool test2json < "$go_text_report" > reports/go-test.native.json

    # maybe fail
    if [[ "$test_rc" != "0" ]]; then exit "$test_rc"; fi
  }

  function install_go_junit_report() {
    cd "$(mktemp -d)"
    go mod init go-junit-report
    go install github.com/jstemmer/go-junit-report@latest
    cd -
  }

  function install_go_mod_outdated() {
    cd "$(mktemp -d)"
    go mod init go-mod-outdated
    go install github.com/psampaz/go-mod-outdated@latest
    cd -
  }

  function install_go_govulncheck() {
    if ! command -v govulncheck  > /dev/null
    then
      cd "$(mktemp -d)"
      go mod init govulncheck
      go install golang.org/x/vuln/cmd/govulncheck@latest
      cd -
    fi
    >&2 echo -e "[\\e[1;91mERROR\\e[0m] $*"
  }

  function install_ca_certs() {
@@ -349,6 +239,123 @@ stages:
    log_info "... done"
  }

  function output_coverage() {
    coverage_out=reports/go-coverage.native.out
    if [[ -f "$coverage_out" ]]
    then
      log_info "--- \\e[32mCoverage report(s) found\\e[0m (\\e[33;1m${coverage_out}\\e[0m): output"
      percent=$(go tool cover -func="$coverage_out" | tail -1 | awk -F" " '{print $NF}')
      echo "${percent} covered"

      go get github.com/boumenot/gocover-cobertura
      go run github.com/boumenot/gocover-cobertura < "$coverage_out" > reports/go-coverage.cobertura.xml
    else
      log_info "--- \\e[32mCoverage report(s) not found\\e[0m: skip"
    fi
  }

  # evaluates Go build mode (manages 'auto' mode)
  function go_build_mode() {
    case "$GO_BUILD_MODE" in
      application|modules)
        echo "$GO_BUILD_MODE"
        ;;
      auto)
        go_main_src=$(find . -name "*.go" -exec grep -wl "^package main" {} \;)
        if [[ "$go_main_src" ]]
        then
          log_info "--- build mode auto-detected: \\e[96;1mapplication\\e[0m (main package found)"
          echo "application"
        else
          log_info "--- build mode auto-detected: \\e[96;1mmodules\\e[0m (no main package found)"
          echo "modules"
        fi
        ;;
      *)
        log_error "--- unsupported \\e[94;1m\$GO_BUILD_MODE\\e[0m value (expected values are \\e[96;1mapplication\\e[0m, \\e[96;1mmodules\\e[0m, \\e[96;1mauto\\e[0m)"
        exit 1
        ;;
    esac
  }

  function go_build() {
    case "$(go_build_mode)" in
      application)
        go_build_application
        ;;
      modules)
        go_build_modules
        ;;
    esac
  }

  function go_build_application() {
    log_info "building go application"
    GO_TARGET_OS="${GO_TARGET_OS:-$GOOS}"
    GO_TARGET_ARCH="${GO_TARGET_ARCH:-$GOARCH}"
    target_dir="$GOBIN/$GO_TARGET_OS/$GO_TARGET_ARCH"
    mkdir -p "$target_dir"
    # shellcheck disable=SC2086
    GOOS="$GO_TARGET_OS" GOARCH="$GO_TARGET_ARCH" go build -ldflags="$GO_BUILD_LINKER_FLAGS" $GO_BUILD_FLAGS -o "$target_dir" $GO_BUILD_PACKAGES
  }

  function go_build_modules() {
    log_info "building go modules"
    # shellcheck disable=SC2086
    go build -ldflags="$GO_BUILD_LINKER_FLAGS" $GO_BUILD_FLAGS $GO_BUILD_PACKAGES
  }

  function go_test() {
    mkdir -p -m 777 reports
    local go_text_report="reports/go-test.native.txt"

    set +e
    # shellcheck disable=SC2086
    go test $GO_TEST_FLAGS "-coverprofile=reports/go-coverage.native.out" $GO_TEST_PACKAGES > "$go_text_report"
    test_rc=$?
    set -e

    # dump text report in the console
    cat "$go_text_report"

    # compute and dump code coverage in the console
    output_coverage

    # produce JUnit report (for GitLab)
    install_go_junit_report
    "$GOBIN/go-junit-report" < "$go_text_report" > reports/go-test.xunit.xml

    # produce JSON report (for SonarQube)
    go tool test2json < "$go_text_report" > reports/go-test.native.json

    # maybe fail
    if [[ "$test_rc" != "0" ]]; then exit "$test_rc"; fi
  }

  function install_go_junit_report() {
    cd "$(mktemp -d)"
    go mod init go-junit-report
    go install github.com/jstemmer/go-junit-report@latest
    cd -
  }

  function install_go_mod_outdated() {
    cd "$(mktemp -d)"
    go mod init go-mod-outdated
    go install github.com/psampaz/go-mod-outdated@latest
    cd -
  }

  function install_go_govulncheck() {
    if ! command -v govulncheck  > /dev/null
    then
      cd "$(mktemp -d)"
      go mod init govulncheck
      go install golang.org/x/vuln/cmd/govulncheck@latest
      cd -
    fi
  }

  unscope_variables

  # ENDSCRIPT
@@ -509,7 +516,9 @@ go-sbom:
  needs: []
  script:
    - mkdir -p -m 777 reports
    - cyclonedx-gomod app -json -output reports/go-sbom.cyclonedx.json $GO_SBOM_OPTS
    - go_mode=$(go_build_mode)
    - |
      cyclonedx-gomod "${go_mode:0:3}" -json -output reports/go-sbom.cyclonedx.json $GO_SBOM_OPTS
    - chmod a+r reports/go-sbom.cyclonedx.json
  artifacts:
    name: "SBOM for golang from $CI_PROJECT_NAME on $CI_COMMIT_REF_SLUG"