diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 95296cd..ebccfcd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,12 +2,15 @@ name: CI on: push: - branches: [ master, main ] + branches: [master, main] pull_request: - branches: [ master, main ] + branches: [master, main] permissions: + contents: write + statuses: write checks: write + pull-requests: write jobs: set-version: @@ -29,7 +32,7 @@ jobs: - name: Install GitVersion uses: gittools/actions/gitversion/setup@v3.0.0 with: - versionSpec: '5.x' + versionSpec: "5.x" - name: Set SemVer Version uses: gittools/actions/gitversion/execute@v3.0.0 id: gitversion @@ -38,6 +41,7 @@ jobs: run: | echo "REVISION -> $GITHUB_SHA" echo "VERSION -> $GITVERSION_SEMVER" + test: runs-on: ubuntu-latest needs: set-version @@ -49,36 +53,36 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 1 + - name: Install Eirctl uses: ensono/actions/eirctl-setup@v0.3.1 - with: - version: 0.7.6 + with: + version: 0.9.3 isPrerelease: false - name: Run Lint run: | - eirctl run pipeline lint + eirctl run pipeline lints - name: Run Tests run: | - eirctl run pipeline test - ls -alt .coverage/out - ls -lat .coverage/report-junit.xml + eirctl run pipeline gha:unit:test + - name: Publish Junit style Test Report uses: mikepenz/action-junit-report@v4 if: always() # always run even if the previous step fails with: - report_paths: '**/.coverage/report-junit.xml' + report_paths: "**/.coverage/report-junit.xml" + - name: Analyze with SonarCloud - # You can pin the exact commit or the version. - uses: SonarSource/sonarqube-scan-action@v5.1.0 + uses: SonarSource/sonarqube-scan-action@v6 env: - SEMVER: $SEMVER - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} # Generate a token on Sonarcloud.io, add it to the secrets of this repo with the name SONAR_TOKEN (Settings > Secrets > Actions > add new repository secret) + # Needed to get PR information + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Generate a token on Sonarcloud.io, add it to the secrets of this repo with the name SONAR_TOKEN (Settings > Secrets > Actions > add new repository secret) + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} with: - # Additional arguments for the sonarcloud scanner - # mandatory + projectBaseDir: . args: > -Dsonar.projectVersion=${{ needs.set-version.outputs.semVer }} - -Dsonar.go.coverage.reportPaths=.coverage/out - -Dsonar.go.tests.reportPaths=.coverage/report-junit.xml + -Dsonar.working.directory=.scannerwork + -Dsonar.scm.provider=git diff --git a/.github/workflows/release_container.yml b/.github/workflows/release_container.yml new file mode 100644 index 0000000..9a1c45d --- /dev/null +++ b/.github/workflows/release_container.yml @@ -0,0 +1,66 @@ +name: Publish Container + +on: + workflow_run: + workflows: ['Lint and Test'] + types: + - completed + branches: + - main + +permissions: + contents: write + packages: write + +jobs: + set-version-tag: + if: ${{ github.event.workflow_run.head_branch == 'main' }} + runs-on: ubuntu-24.04 + outputs: + semVer: ${{ steps.gitversion.outputs.semVer }} + steps: + - uses: actions/checkout@v4 + # get version + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Install GitVersion + uses: gittools/actions/gitversion/setup@v3.0 + with: + versionSpec: '5.x' + - name: Set SemVer Version + uses: gittools/actions/gitversion/execute@v3.0 + id: gitversion + + build-and-push: + runs-on: ubuntu-latest + needs: set-version-tag + env: + SEMVER: ${{ needs.set-version-tag.outputs.semVer }} + steps: + - name: Check out repository + uses: actions/checkout@v3 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v2 + with: + registry: ghcr.io + # GITHUB_TOKEN is automatically provided in GitHub Actions + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up QEMU (for multi-arch builds, optional) + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push Docker image + uses: docker/build-push-action@v6 + with: + context: . + push: true + build-args: Version=${{ needs.set-version-tag.outputs.semVer }},Revision=${{ github.sha }} + tags: | + ghcr.io/ensono/eirctl:${{ needs.set-version-tag.outputs.semVer }} + platforms: linux/amd64,linux/arm64 # adjust as needed diff --git a/.golangci.yaml b/.golangci.yaml new file mode 100644 index 0000000..b82b548 --- /dev/null +++ b/.golangci.yaml @@ -0,0 +1,42 @@ +version: "2" +linters: + # Default set of linters. + # The value can be: `standard`, `all`, `none`, or `fast`. + # Default: standard + default: standard + exclusions: + generated: lax + # Log a warning if an exclusion rule is unused. + # Default: false + warn-unused: true + # Predefined exclusion rules. + # Default: [] + presets: + - comments + - std-error-handling + - common-false-positives + - legacy + # Excluding configuration per-path, per-linter, per-text and per-source. + rules: + # Exclude some linters from running on tests files. + - path: _test\.go + linters: + - gocyclo + - errcheck + - dupl + - gosec + - ineffassign + - staticcheck + - unused + - govet + # Exclude some linters from running on examples files. + - path: examples/ + linters: + - gocyclo + - errcheck + - dupl + - gosec + - ineffassign + - staticcheck + - unused + - govet \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..8dd4e1a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +ARG Version +ARG Revision + +FROM docker.io/golang:1-trixie AS builder + +ARG Version=0.0.1 +ARG Revision=beta01 + +WORKDIR /app + +COPY ./ /app +RUN CGO_ENABLED=0 go build -mod=readonly -buildvcs=false \ + -ldflags="-s -w -X \"github.com/DevLabFoundry/configmanager/v2/cmd/configmanager.Version=${Version}\" -X \"github.com/DevLabFoundry/configmanager/v2/cmd/configmanager.Revision=${Revision}\" -extldflags -static" \ + -o bin/configmanager cmd/main.go + +FROM docker.io/alpine:3 + +COPY --from=builder /app/bin/configmanager /usr/bin/configmanager + +ENTRYPOINT ["configmanager"] diff --git a/cmd/configmanager/fromfileinput.go b/cmd/configmanager/fromfileinput.go index 38a254e..4b8e915 100644 --- a/cmd/configmanager/fromfileinput.go +++ b/cmd/configmanager/fromfileinput.go @@ -46,7 +46,7 @@ func newFromStrCmd(rootCmd *Root) { inside a variable to be searched for tokens. If value is a valid path it will open it if not it will accept the string as an input. e.g. -i /some/file or -i $"(cat /som/file)", are both valid input values`) - fromstrCmd.MarkPersistentFlagRequired("input") + _ = fromstrCmd.MarkPersistentFlagRequired("input") fromstrCmd.PersistentFlags().StringVarP(&f.path, "path", "p", "./app.env", `Path where to write out the replaced a config/secret variables. Special value of stdout can be used to return the output to stdout e.g. -p stdout, unix style output only`) diff --git a/cmd/configmanager/insert.go b/cmd/configmanager/insert.go index ffddf6f..3dde42b 100644 --- a/cmd/configmanager/insert.go +++ b/cmd/configmanager/insert.go @@ -29,6 +29,6 @@ func newInsertCmd(rootCmd *Root) { }, } insertCmd.PersistentFlags().StringToStringVarP(&f.insertKv, "config-pair", "", defaultInsertKv, " token=value pair. This can be specified multiple times.") - insertCmd.MarkPersistentFlagRequired("config-pair") + _ = insertCmd.MarkPersistentFlagRequired("config-pair") rootCmd.Cmd.AddCommand(insertCmd) } diff --git a/cmd/configmanager/retrieve.go b/cmd/configmanager/retrieve.go index 9b3289d..6e86522 100644 --- a/cmd/configmanager/retrieve.go +++ b/cmd/configmanager/retrieve.go @@ -35,7 +35,7 @@ func newRetrieveCmd(rootCmd *Root) { }, } retrieveCmd.PersistentFlags().StringArrayVarP(&f.tokens, "token", "t", []string{}, "Token pointing to a config/secret variable. This can be specified multiple times.") - retrieveCmd.MarkPersistentFlagRequired("token") + _ = retrieveCmd.MarkPersistentFlagRequired("token") retrieveCmd.PersistentFlags().StringVarP(&f.path, "path", "p", "./app.env", "Path where to write out the replaced a config/secret variables. Special value of stdout can be used to return the output to stdout e.g. -p stdout, unix style output only") rootCmd.Cmd.AddCommand(retrieveCmd) } diff --git a/eirctl.yaml b/eirctl.yaml index 1d84c68..fd5349c 100644 --- a/eirctl.yaml +++ b/eirctl.yaml @@ -3,88 +3,33 @@ output: prefixed debug: false -contexts: - go1x: - container: - name: golang:1.24.6-bookworm - enable_dind: true - enable_mount: true - envfile: - exclude: - - GO - - CXX - - CGO +import: + - https://raw.githubusercontent.com/Ensono/eirctl/refs/tags/0.9.3/shared/build/go/eirctl.yaml - golint: +contexts: + bash: container: - name: golangci/golangci-lint:v2.1.5-alpine - enable_dind: true - enable_mount: true - envfile: - exclude: - - GO - - CXX - - CGO - - PATH - - HOME - -tasks: - clean: - context: go1x - command: - - rm -rf dist/* - - rm -rf .coverage - - mkdir -p dist .coverage - - run:test: - description: Runs the tests - context: go1x - command: - - | - go test ./... -v -buildvcs=false -mod=readonly -race -coverpkg=./... -coverprofile=.coverage/out | tee .coverage/unit - - install: - description: Install dependencies - command: | - go mod tidy - - lint:vet: - description: Runs lint and vet - context: go1x - command: | - go vet + name: mirror.gcr.io/bash:5.0.18-alpine3.22 - golint: - # in CI it is run - context: golint - description: Runs the linter and go vet and other default static checks - allow_failure: false - command: - # echo "lint ran with exit code: $?" - # pwd && ls -lat - - | - golangci-lint run +pipelines: + gha:unit:test: + - pipeline: test:unit + env: + ROOT_PKG_NAME: github.com/DevLabFoundry + - task: sonar:coverage:prep + depends_on: test:unit - vuln:check: - context: go1x - description: | - Runs a vulnerability scan against the code base - command: - - | - go install golang.org/x/vuln/cmd/govulncheck@latest - govulncheck ./... + show_coverage: + - pipeline: test:unit + - task: show:coverage + depends_on: test:unit - coverage: - description: generate coverage - context: go1x - command: - - | - go install github.com/jstemmer/go-junit-report/v2@latest - go install github.com/axw/gocov/gocov@latest - go install github.com/AlekSi/gocov-xml@latest - go-junit-report -in .coverage/unit > .coverage/report-junit.xml - gocov convert .coverage/out | gocov-xml > .coverage/report-cobertura.xml + build:bin: + - task: clean + - task: go:build:binary + depends_on: clean +tasks: show:coverage: description: Opens the current coverage viewer for the the configmanager utility. command: go tool cover -html=.coverage/out @@ -106,7 +51,8 @@ tasks: Generates all the binaries for the configmanager utility. command: - | - mkdir -p /eirctl/.deps + mkdir -p .deps + unset GOTOOLCHAIN ldflags="-s -w -X \"github.com/DevLabFoundry/configmanager/v2/cmd/configmanager.Version=${VERSION}\" -X \"github.com/DevLabFoundry/configmanager/v2/cmd/configmanager.Revision=${REVISION}\" -extldflags -static" GOPATH=/eirctl/.deps GOOS=${BUILD_GOOS} GOARCH=${BUILD_GOARCH} CGO_ENABLED=0 go build -mod=readonly -buildvcs=false -ldflags="$ldflags" \ -o ./dist/configmanager-${BUILD_GOOS}-${BUILD_GOARCH}${BUILD_SUFFIX} ./cmd @@ -141,6 +87,16 @@ tasks: - VERSION - REVISION + sonar:coverage:prep: + context: bash + command: + - | + sed -i 's|github.com/DevLabFoundry/configmanager/v2/||g' .coverage/out + echo "Coverage file first 20 lines after conversion:" + head -20 .coverage/out + echo "Coverage file line count:" + wc -l .coverage/out + tag: description: | Usage `eirctl tag GIT_TAG=2111dsfsdfa REVISION=as2342432` @@ -153,24 +109,4 @@ tasks: - VERSION - REVISION -pipelines: - test: - - task: clean - - task: run:test - depends_on: clean - - task: coverage - depends_on: run:test - lint: - - task: lint:vet - - task: vuln:check - - show_coverage: - - pipeline: test - - task: show:coverage - depends_on: test - - build:bin: - - task: clean - - task: go:build:binary - depends_on: clean diff --git a/go.mod b/go.mod index 6aa1758..d4718c9 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/DevLabFoundry/configmanager/v2 -go 1.24.6 +go 1.25.1 require ( cloud.google.com/go/secretmanager v1.15.0 diff --git a/internal/config/config.go b/internal/config/config.go index 356eb63..50aecce 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -257,7 +257,7 @@ func (t *ParsedTokenConfig) extractMetadataStr() { metaString := newS[:endIndex] t.metadataStr = metaString // Set Metadataless token - t.metadataLess = strings.Replace(token, startMetaStr+metaString+endMetaStr, "", -1) + t.metadataLess = strings.ReplaceAll(token, startMetaStr+metaString+endMetaStr, "") } // keysLookup returns the keysLookup path and the string without it diff --git a/internal/store/azappconf.go b/internal/store/azappconf.go index db067a3..a37cc8a 100644 --- a/internal/store/azappconf.go +++ b/internal/store/azappconf.go @@ -41,7 +41,10 @@ type AzAppConfConfig struct { // NewAzAppConf func NewAzAppConf(ctx context.Context, token *config.ParsedTokenConfig, logger log.ILogger) (*AzAppConf, error) { storeConf := &AzAppConfConfig{} - token.ParseMetadata(storeConf) + if err := token.ParseMetadata(storeConf); err != nil { + return nil, err + } + backingStore := &AzAppConf{ ctx: ctx, config: storeConf, diff --git a/internal/store/azkeyvault.go b/internal/store/azkeyvault.go index a609f6d..84f1715 100644 --- a/internal/store/azkeyvault.go +++ b/internal/store/azkeyvault.go @@ -36,7 +36,7 @@ type AzKvConfig struct { func NewKvScrtStore(ctx context.Context, token *config.ParsedTokenConfig, logger log.ILogger) (*KvScrtStore, error) { storeConf := &AzKvConfig{} - token.ParseMetadata(storeConf) + _ = token.ParseMetadata(storeConf) backingStore := &KvScrtStore{ ctx: ctx, diff --git a/internal/store/aztablestorage.go b/internal/store/aztablestorage.go index 37ca8d9..539979b 100644 --- a/internal/store/aztablestorage.go +++ b/internal/store/aztablestorage.go @@ -43,7 +43,7 @@ type AzTableStrgConfig struct { func NewAzTableStore(ctx context.Context, token *config.ParsedTokenConfig, logger log.ILogger) (*AzTableStore, error) { storeConf := &AzTableStrgConfig{} - token.ParseMetadata(storeConf) + _ = token.ParseMetadata(storeConf) // initialToken := config.ParseMetadata(token, storeConf) backingStore := &AzTableStore{ ctx: ctx, @@ -100,7 +100,7 @@ func (imp *AzTableStore) Token() (string, error) { if len(s.Value) > 0 { // check for `value` property in entity checkVal := make(map[string]interface{}) - json.Unmarshal(s.Value, &checkVal) + _ = json.Unmarshal(s.Value, &checkVal) if checkVal["value"] != nil { return fmt.Sprintf("%v", checkVal["value"]), nil } diff --git a/internal/store/gcpsecrets.go b/internal/store/gcpsecrets.go index e473b95..1df7199 100644 --- a/internal/store/gcpsecrets.go +++ b/internal/store/gcpsecrets.go @@ -44,14 +44,16 @@ func NewGcpSecrets(ctx context.Context, logger log.ILogger) (*GcpSecrets, error) func (imp *GcpSecrets) SetToken(token *config.ParsedTokenConfig) { storeConf := &GcpSecretsConfig{} - token.ParseMetadata(storeConf) + _ = token.ParseMetadata(storeConf) imp.token = token imp.config = storeConf } func (imp *GcpSecrets) Token() (string, error) { // Close client currently as new one would be created per iteration - defer imp.close() + defer func() { + _ = imp.close() + }() imp.logger.Info("Concrete implementation GcpSecrets") imp.logger.Info("GcpSecrets Token: %s", imp.token.String()) diff --git a/internal/store/hashivault.go b/internal/store/hashivault.go index d979310..048039b 100644 --- a/internal/store/hashivault.go +++ b/internal/store/hashivault.go @@ -43,7 +43,7 @@ type VaultConfig struct { func NewVaultStore(ctx context.Context, token *config.ParsedTokenConfig, logger log.ILogger) (*VaultStore, error) { storeConf := &VaultConfig{} - token.ParseMetadata(storeConf) + _ = token.ParseMetadata(storeConf) imp := &VaultStore{ ctx: ctx, logger: logger, diff --git a/internal/store/paramstore.go b/internal/store/paramstore.go index b20a98e..72b43a8 100644 --- a/internal/store/paramstore.go +++ b/internal/store/paramstore.go @@ -43,7 +43,7 @@ func NewParamStore(ctx context.Context, logger log.ILogger) (*ParamStore, error) func (imp *ParamStore) SetToken(token *config.ParsedTokenConfig) { storeConf := &ParamStrConfig{} - token.ParseMetadata(storeConf) + _ = token.ParseMetadata(storeConf) imp.token = token imp.config = storeConf } diff --git a/pkg/generator/generator.go b/pkg/generator/generator.go index 6ba12fb..4d8bd69 100644 --- a/pkg/generator/generator.go +++ b/pkg/generator/generator.go @@ -10,17 +10,10 @@ import ( "github.com/DevLabFoundry/configmanager/v2/internal/config" "github.com/DevLabFoundry/configmanager/v2/internal/log" - "github.com/DevLabFoundry/configmanager/v2/internal/store" "github.com/DevLabFoundry/configmanager/v2/internal/strategy" "github.com/spyzhov/ajson" ) -type retrieveIface interface { - WithStrategyFuncMap(funcMap strategy.StrategyFuncMap) *strategy.RetrieveStrategy - RetrieveByToken(ctx context.Context, impl store.Strategy, in *config.ParsedTokenConfig) *strategy.TokenResponse - SelectImplementation(ctx context.Context, in *config.ParsedTokenConfig) (store.Strategy, error) -} - // GenVars is the main struct holding the // strategy patterns iface // any initialised config if overridded with withers diff --git a/sonar-project.properties b/sonar-project.properties index 4869d51..b070476 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -14,3 +14,7 @@ sonar.test.exclusions=**/*_generated*.go,**/*_generated/**,**/vendor/** sonar.sourceEncoding=UTF-8 sonar.qualitygate.wait=true + +# go +sonar.go.coverage.reportPaths=.coverage/out +sonar.go.tests.reportPaths=.coverage/report-junit.xml