From 83328b5d76710c6f42cb18d305263e42db3d7e91 Mon Sep 17 00:00:00 2001 From: Ahmed AbouZaid <6760103+aabouzaid@users.noreply.github.com> Date: Wed, 4 Jun 2025 06:18:35 +0200 Subject: [PATCH] ci: automate build and release chores --- .github/workflows/go-ci.yml | 86 +++++++++++++++++++++++ .github/workflows/go-pre-release.yml | 54 ++++++++++++++ .github/workflows/tpl-packaging.yml | 95 +++++++++++++++++++++++++ .golangci.yml | 64 +++++++++++++++++ .goreleaser.yaml | 101 +++++++++++++++++++++++++++ Dockerfile | 8 +++ README.md | 10 ++- 7 files changed, 417 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/go-ci.yml create mode 100644 .github/workflows/go-pre-release.yml create mode 100644 .github/workflows/tpl-packaging.yml create mode 100644 .golangci.yml create mode 100644 .goreleaser.yaml create mode 100644 Dockerfile diff --git a/.github/workflows/go-ci.yml b/.github/workflows/go-ci.yml new file mode 100644 index 0000000..7aaa9d1 --- /dev/null +++ b/.github/workflows/go-ci.yml @@ -0,0 +1,86 @@ +name: Go - CI +on: + push: + branches: + - main + pull_request: + +permissions: + contents: read + +jobs: + lint-and-test: + name: Lint And Test Code + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4 + with: + go-version-file: go.mod + cache: false + - name: Run GolangCI linter + uses: golangci/golangci-lint-action@971e284b6050e8a5849b72094c50ab08da042db8 # v6.1.1 + with: + args: '--timeout=5m' + skip-cache: true + skip-save-cache: true + - name: Run Go test coverage + run: go test -race -coverprofile=coverage.out -covermode=atomic ./... + - name: Upload coverage to Codecov + uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + + build: + name: Build Artifacts + needs: ["lint-and-test"] + permissions: + id-token: write + contents: write + packages: write + pull-requests: write + uses: ./.github/workflows/tpl-packaging.yml + secrets: inherit + with: + goreleaser-args: "release --clean --snapshot" + artifacts-cache: true + artifacts-cache-key: "dist-${{ github.run_id }}" + artifacts-publish-ci: true + artifacts-identifier: "${{ github.event.number == 0 && 'snapshot' || format('pr-{0}', github.event.number) }}" + + upload: + name: Upload Artifacts - ${{ matrix.os.name }} + needs: ["build"] + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + os: + - name: Linux + id: linux + - name: MacOS + id: darwin + - name: Windows + id: windows + steps: + - name: Get Cached Artifacts + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4 + with: + path: dist + key: dist-${{ github.run_id }} + - run: find dist + # The upload-artifact action doesn't support multi upload with different names. + - name: Upload Artifacts - AMD + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: kube-rbac-extractor-${{ matrix.os.id }}-amd + path: | + dist/kube-rbac-extractor_*_${{ matrix.os.id }}_amd* + dist/*checksums.txt* + - name: Upload Artifacts - ARM + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: kube-rbac-extractor-${{ matrix.os.id }}-arm + path: | + dist/kube-rbac-extractor_*_${{ matrix.os.id }}_arm* + dist/*checksums.txt* diff --git a/.github/workflows/go-pre-release.yml b/.github/workflows/go-pre-release.yml new file mode 100644 index 0000000..659bae4 --- /dev/null +++ b/.github/workflows/go-pre-release.yml @@ -0,0 +1,54 @@ +name: Go - Pre Release + +on: + push: + branches: + - main + +permissions: + contents: read + +jobs: + pre-release: + name: Pre Release + runs-on: ubuntu-latest + permissions: + id-token: write + contents: write + packages: write + pull-requests: write + steps: + - name: Run Release Please + uses: GoogleCloudPlatform/release-please-action@db8f2c60ee802b3748b512940dde88eabd7b7e01 # v3 + id: release + with: + release-type: go + package-name: kube-rbac-extractor + changelog-types: | + [ + { + "type": "feat", + "section": "Features", + "hidden": false + }, + { + "type": "refactor", + "section": "Refactors", + "hidden": false + }, + { + "type": "fix", + "section": "Bug Fixes", + "hidden": false + }, + { + "type": "docs", + "section": "Documentation", + "hidden": false + }, + { + "type": "ci", + "section": "Continuous Integration", + "hidden": false + } + ] diff --git a/.github/workflows/tpl-packaging.yml b/.github/workflows/tpl-packaging.yml new file mode 100644 index 0000000..3c5d086 --- /dev/null +++ b/.github/workflows/tpl-packaging.yml @@ -0,0 +1,95 @@ +name: Packaging + +on: + workflow_call: + inputs: + goreleaser-version: + default: '~> v2' + type: string + goreleaser-args: + required: true + type: string + artifacts-cache: + default: false + type: boolean + artifacts-identifier: + default: latest + type: string + artifacts-publish-ci: + default: false + type: boolean + artifacts-cache-key: + default: dist + type: string + artifacts-cache-path: + default: dist + type: string + +permissions: + contents: read + +jobs: + run: + name: Run GoReleaser + permissions: + id-token: write + contents: write + packages: write + pull-requests: write + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4 + with: + go-version-file: go.mod + cache: false + - name: Setup Golang Caches + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4 + with: + path: | + ~/.cache/go-build + ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + - name: Setup Cosign + uses: sigstore/cosign-installer@main + - name: Login to GitHub Container Registry + uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Setup QEMU + uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3 + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@5742e2a039330cbb23ebf35f046f814d4c6ff811 # v5 + with: + version: "${{ inputs.goreleaser-version }}" + args: "${{ inputs.goreleaser-args }}" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # - name: Cleanup CI Docker Image + # if: ${{ inputs.artifacts-publish-ci }} + # uses: actions/delete-package-versions@e5bc658cc4c965c472efe991f8beea3981499c55 # v5.0.0 + # with: + # token: ${{ secrets.GITHUB_TOKEN }} + # package-name: kube-rbac-extractor-ci + # package-type: container + # min-versions-to-keep: 10 + - name: Publish CI Docker Image + if: ${{ inputs.artifacts-publish-ci }} + run: | + for xarch in amd64 arm64; do + docker_image_src="ghcr.io/devopshivehq/kube-rbac-extractor:latest-${xarch}" + docker_image_dst="ghcr.io/devopshivehq/kube-rbac-extractor-ci:${{ inputs.artifacts-identifier }}-${xarch}" + echo "Tag and push image: ${docker_image_dst}" + docker tag ${docker_image_src} ${docker_image_dst} + docker push ${docker_image_dst} + done + - name: Cache Artifacts + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4 + if: ${{ inputs.artifacts-cache }} + with: + path: "${{ inputs.artifacts-cache-path }}" + key: "${{ inputs.artifacts-cache-key }}" diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..7a93c68 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,64 @@ +--- +linters-settings: + funlen: + lines: 100 + statements: 50 + gocyclo: + min-complexity: 15 + goimports: + local-prefixes: github.com/DevOpsHiveHQ/kube-rbac-extractor + govet: + check: true + misspell: + locale: US + nolintlint: + allow-leading-space: false # require machine-readable nolint directives (with no leading space) + allow-unused: false # report any unused nolint directives + require-explanation: true # require an explanation for nolint directives + require-specific: false # don't require nolint directives to be specific about which linter is being skipped + revive: + confidence: 0 + +linters: + disable-all: true + enable: + - bodyclose + - dogsled + - dupl + - errcheck + - copyloopvar + - funlen + - gochecknoinits + - gocritic + - goconst + - gocyclo + - gofumpt + - goimports + - revive + - rowserrcheck + - goprintffuncname + - gosec + - gosimple + - govet + - ineffassign + - misspell + - nakedret + - noctx + - nolintlint + - staticcheck + - stylecheck + - sqlclosecheck + - typecheck + - unconvert + - unparam + - unused + - whitespace + +issues: + # Disable default exclusions. + # https://golangci-lint.run/usage/false-positives/#default-exclusions + exclude-use-default: false + exclude: + - "package-comments: .+" + - "ST1000: package comment should be of the form" + - "G304: Potential file inclusion via variable" diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 0000000..3e1e7c0 --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,101 @@ +--- +version: 2 +before: + hooks: + - go mod download +builds: + - env: + - CGO_ENABLED=0 + goos: + - darwin + - linux + - windows + goarch: + - amd64 + - arm64 +upx: + - enabled: true + compress: "1" +archives: + - format_overrides: + - goos: windows + format: zip +dockers: + - use: buildx + image_templates: + - "ghcr.io/devopshivehq/kube-rbac-extractor:latest-amd64" + - "ghcr.io/devopshivehq/kube-rbac-extractor:{{ .Major }}-amd64" + - "ghcr.io/devopshivehq/kube-rbac-extractor:{{ .Major }}.{{ .Minor }}-amd64" + - "ghcr.io/devopshivehq/kube-rbac-extractor:{{ .Major }}.{{ .Minor }}.{{ .Patch }}-amd64" + build_flag_templates: + - "--pull" + - "--platform=linux/amd64" + # OCI annotations: https://github.com/opencontainers/image-spec/blob/main/annotations.md + - "--label=org.opencontainers.image.created={{ .Date }}" + - "--label=org.opencontainers.image.name={{ .ProjectName }}" + - "--label=org.opencontainers.image.revision={{ .FullCommit }}" + - "--label=org.opencontainers.image.version={{ .Version }}" + - "--label=org.opencontainers.image.source={{ .GitURL }}" + - use: buildx + image_templates: + - "ghcr.io/devopshivehq/kube-rbac-extractor:latest-arm64" + - "ghcr.io/devopshivehq/kube-rbac-extractor:{{ .Major }}-arm64" + - "ghcr.io/devopshivehq/kube-rbac-extractor:{{ .Major }}.{{ .Minor }}-arm64" + - "ghcr.io/devopshivehq/kube-rbac-extractor:{{ .Major }}.{{ .Minor }}.{{ .Patch }}-arm64" + build_flag_templates: + - "--pull" + - "--platform=linux/arm64" + # OCI annotations: https://github.com/opencontainers/image-spec/blob/main/annotations.md + - "--label=org.opencontainers.image.created={{ .Date }}" + - "--label=org.opencontainers.image.name={{ .ProjectName }}" + - "--label=org.opencontainers.image.revision={{ .FullCommit }}" + - "--label=org.opencontainers.image.version={{ .Version }}" + - "--label=org.opencontainers.image.source={{ .GitURL }}" +docker_manifests: + - name_template: "ghcr.io/devopshivehq/kube-rbac-extractor:latest" + image_templates: + - "ghcr.io/devopshivehq/kube-rbac-extractor:latest-amd64" + - "ghcr.io/devopshivehq/kube-rbac-extractor:latest-arm64" + - name_template: "ghcr.io/devopshivehq/kube-rbac-extractor:{{ .Major }}" + image_templates: + - "ghcr.io/devopshivehq/kube-rbac-extractor:{{ .Major }}-amd64" + - "ghcr.io/devopshivehq/kube-rbac-extractor:{{ .Major }}-arm64" + - name_template: "ghcr.io/devopshivehq/kube-rbac-extractor:{{ .Major }}.{{ .Minor }}" + image_templates: + - "ghcr.io/devopshivehq/kube-rbac-extractor:{{ .Major }}.{{ .Minor }}-amd64" + - "ghcr.io/devopshivehq/kube-rbac-extractor:{{ .Major }}.{{ .Minor }}-arm64" + - name_template: "ghcr.io/devopshivehq/kube-rbac-extractor:{{ .Major }}.{{ .Minor }}.{{ .Patch }}" + image_templates: + - "ghcr.io/devopshivehq/kube-rbac-extractor:{{ .Major }}.{{ .Minor }}.{{ .Patch }}-amd64" + - "ghcr.io/devopshivehq/kube-rbac-extractor:{{ .Major }}.{{ .Minor }}.{{ .Patch }}-arm64" +release: + github: {} + name_template: "{{ .Version }}" + prerelease: auto + mode: append +signs: + - cmd: cosign + env: + - COSIGN_EXPERIMENTAL=1 + certificate: '${artifact}.pem' + args: + - sign-blob + - '--yes' + - '--output-certificate=${certificate}' + - '--output-signature=${signature}' + - '${artifact}' + artifacts: checksum + output: true +docker_signs: + - cmd: cosign + env: + - COSIGN_EXPERIMENTAL=1 + args: + - sign + - '--yes' + - '${artifact}' + artifacts: all + output: true +# Changelog is generated by release-please. +changelog: + disable: true diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..8df1d4f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,8 @@ +FROM ubuntu:latest@sha256:04f510bf1f2528604dc2ff46b517dbdbb85c262d62eacc4aa4d3629783036096 AS base +RUN useradd -u 1001 kube-rbac-extractor + +FROM scratch +COPY --from=base /etc/passwd /etc/passwd +COPY kube-rbac-extractor / +USER 1001 +ENTRYPOINT ["/kube-rbac-extractor"] diff --git a/README.md b/README.md index 06c5e3c..62b30cd 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@
A CLI tool generates Kubernetes RBAC Role/ClusterRole from K8s resources (manifests), applying the principle of least privilege (PoLP) for restricted security access.
+ +[](https://github.com/DevOpsHiveHQ/kube-rbac-extractor/actions/workflows/go-ci.yml?query=branch%3Amain) +[](https://goreportcard.com/report/github.com/DevOpsHiveHQ/kube-rbac-extractor) +[](https://github.com/DevOpsHiveHQ/kube-rbac-extractor/releases) +[](https://github.com/DevOpsHiveHQ/kube-rbac-extractor/pkgs/container/kustomize-generator-merger) +[](https://pkg.go.dev/github.com/DevOpsHiveHQ/kube-rbac-extractor) +[](https://github.com/DevOpsHiveHQ/kube-rbac-extractor/pulls) +