diff --git a/.github/workflows/build-container-images.yaml b/.github/workflows/build-container-images.yaml index d7155504..cd662ce8 100644 --- a/.github/workflows/build-container-images.yaml +++ b/.github/workflows/build-container-images.yaml @@ -1,222 +1,94 @@ -name: Container image building +name: Release container images on: push: tags: - 'v*.*.*' - pull_request: - branches: - - main - jobs: setup: - if: github.event_name != 'pull_request' || github.event.pull_request.draft == false # Prevents running if the PR is a draft runs-on: ubuntu-latest - permissions: - contents: read - packages: write outputs: - SHA_COMMIT: ${{ steps.vars.outputs.SHA_COMMIT }} GIT_TAG: ${{ steps.vars.outputs.GIT_TAG }} - BRANCH: ${{ steps.vars.outputs.BRANCH }} - LATEST_TAG: ${{ steps.vars.outputs.LATEST_TAG }} + SHA_COMMIT: ${{ steps.vars.outputs.SHA_COMMIT }} steps: - - name: Checkout repository + - name: Checkout uses: actions/checkout@v5 with: - fetch-depth: 0 # Needed for getting Git Tags + fetch-depth: 0 - - name: Determine Image Tags + - name: Determine image tags id: vars run: | - # Checking if the event comes from PR or Branch - if [[ "${{ github.event_name }}" == "pull_request" ]]; then - # PR values - SHA_COMMIT=$(echo ${{ github.event.pull_request.head.sha }}) - BRANCH_NAME=${{ github.event.pull_request.head.ref }} - echo "Changes comming from PR: $BRANCH_NAME/$SHA_COMMIT" - else - # Push values - SHA_COMMIT=$(echo ${{ github.sha }}) - BRANCH_NAME=${GITHUB_REF##*/} - echo "Changes comming from Push: $BRANCH_NAME/$SHA_COMMIT" - fi - echo "SHA_COMMIT=$SHA_COMMIT" >> $GITHUB_OUTPUT - echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_OUTPUT - # Getting LATEST_TAG - if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then - LATEST_TAG="latest" - fi - echo "LATEST_TAG=$LATEST_TAG" >> $GITHUB_OUTPUT - # Getting version tag if available - GIT_TAG=$(git tag --points-at HEAD | head -n 1) - if [[ -n "$GIT_TAG" ]]; then - echo "Detected Git Tag: $GIT_TAG" - fi + GIT_TAG=${GITHUB_REF##*/} + SHA_COMMIT=$(echo "${{ github.sha }}" | cut -c1-7) echo "GIT_TAG=$GIT_TAG" >> $GITHUB_OUTPUT - echo "Building Tags:" - echo " * SHA_COMMIT: ${SHA_COMMIT}" - echo " * BRANCH_NAME: ${BRANCH_NAME}" - echo " * GIT TAG: ${GIT_TAG}" - if [[ $LATEST_TAG == "latest" ]]; then - echo " * Including 'latest' tag" - fi - api: - runs-on: ubuntu-latest - needs: setup - permissions: - contents: read - packages: write - steps: - - name: Checkout repository - uses: actions/checkout@v5 - - - name: Login to Quay.io - run: | - echo "${{ secrets.QUAY_PASSWORD }}" | podman login quay.io -u "${{ secrets.QUAY_USERNAME }}" --password-stdin - - - name: Container image building - run: | - echo "Building ClusterIQ API (${{ needs.setup.outputs.BRANCH }}/${{ needs.setup.outputs.SHA_COMMIT }})" - podman build \ - --platform linux/amd64 \ - --build-arg VERSION=${{ needs.setup.outputs.GIT_TAG }} \ - --build-arg COMMIT=${{ needs.setup.outputs.SHA_COMMIT }} \ - -t quay.io/${{ secrets.QUAY_ORG_NAME }}/cluster-iq-api:${{ needs.setup.outputs.SHA_COMMIT }} \ - -f ./deployments/containerfiles/Containerfile-api . - - - name: Pushing Hash based image - run: | - podman push quay.io/${{ secrets.QUAY_ORG_NAME }}/cluster-iq-api:${{ needs.setup.outputs.SHA_COMMIT }} - - - name: Tagging and Pushing Latest Image - if: ${{ needs.setup.outputs.LATEST_TAG != '' && needs.setup.outputs.LATEST_TAG != null }} - run: | - podman tag \ - quay.io/${{ secrets.QUAY_ORG_NAME }}/cluster-iq-api:${{ needs.setup.outputs.SHA_COMMIT }} \ - quay.io/${{ secrets.QUAY_ORG_NAME }}/cluster-iq-api:${{ needs.setup.outputs.LATEST_TAG }} - podman push quay.io/${{ secrets.QUAY_ORG_NAME }}/cluster-iq-api:${{ needs.setup.outputs.LATEST_TAG }} - - - name: Tagging and Puhsing GitTag based image - if: ${{ needs.setup.outputs.GIT_TAG != '' && needs.setup.outputs.GIT_TAG != null }} - run: | - echo "Building Tagged version image: ${{ needs.setup.outputs.GIT_TAG }}" - podman tag \ - quay.io/${{ secrets.QUAY_ORG_NAME }}/cluster-iq-api:${{ needs.setup.outputs.SHA_COMMIT }} \ - quay.io/${{ secrets.QUAY_ORG_NAME }}/cluster-iq-api:${{ needs.setup.outputs.GIT_TAG }} - podman push quay.io/${{ secrets.QUAY_ORG_NAME }}/cluster-iq-api:${{ needs.setup.outputs.GIT_TAG }} - - - name: Logout from Quay.io - run: | - podman logout quay.io - agent: - runs-on: ubuntu-latest - needs: setup - permissions: - contents: read - packages: write - steps: - - name: Checkout repository - uses: actions/checkout@v5 - - - name: Login to Quay.io - run: | - echo "${{ secrets.QUAY_PASSWORD }}" | podman login quay.io -u "${{ secrets.QUAY_USERNAME }}" --password-stdin - - - name: Container image building - run: | - echo "Building ClusterIQ Agent (${{ needs.setup.outputs.BRANCH }}/${{ needs.setup.outputs.SHA_COMMIT }})" - podman build \ - --platform linux/amd64 \ - --build-arg VERSION=${{ needs.setup.outputs.GIT_TAG }} \ - --build-arg COMMIT=${{ needs.setup.outputs.SHA_COMMIT }} \ - -t quay.io/${{ secrets.QUAY_ORG_NAME }}/cluster-iq-agent:${{ needs.setup.outputs.SHA_COMMIT }} \ - -f ./deployments/containerfiles/Containerfile-agent . - - - name: Pushing Hash based image - run: | - podman push quay.io/${{ secrets.QUAY_ORG_NAME }}/cluster-iq-agent:${{ needs.setup.outputs.SHA_COMMIT }} - - - name: Tagging and Pushing Latest Image - if: ${{ needs.setup.outputs.LATEST_TAG != '' && needs.setup.outputs.LATEST_TAG != null }} - run: | - podman tag \ - quay.io/${{ secrets.QUAY_ORG_NAME }}/cluster-iq-agent:${{ needs.setup.outputs.SHA_COMMIT }} \ - quay.io/${{ secrets.QUAY_ORG_NAME }}/cluster-iq-agent:${{ needs.setup.outputs.LATEST_TAG }} - podman push quay.io/${{ secrets.QUAY_ORG_NAME }}/cluster-iq-agent:${{ needs.setup.outputs.LATEST_TAG }} - - - name: Tagging and Puhsing GitTag based image - if: ${{ needs.setup.outputs.GIT_TAG != '' && needs.setup.outputs.GIT_TAG != null }} - run: | - echo "Building Tagged version image: ${{ needs.setup.outputs.GIT_TAG }}" - podman tag \ - quay.io/${{ secrets.QUAY_ORG_NAME }}/cluster-iq-agent:${{ needs.setup.outputs.SHA_COMMIT }} \ - quay.io/${{ secrets.QUAY_ORG_NAME }}/cluster-iq-agent:${{ needs.setup.outputs.GIT_TAG }} - podman push quay.io/${{ secrets.QUAY_ORG_NAME }}/cluster-iq-agent:${{ needs.setup.outputs.GIT_TAG }} + echo "SHA_COMMIT=$SHA_COMMIT" >> $GITHUB_OUTPUT + echo "Release: $GIT_TAG ($SHA_COMMIT)" - - name: Logout from Quay.io - run: | - podman logout quay.io - scanner: + build: + name: Build ${{ matrix.component }} runs-on: ubuntu-latest needs: setup permissions: contents: read packages: write + strategy: + matrix: + include: + - component: api + containerfile: deployments/containerfiles/Containerfile-api + - component: agent + containerfile: deployments/containerfiles/Containerfile-agent + - component: scanner + containerfile: deployments/containerfiles/Containerfile-scanner steps: - - name: Checkout repository + - name: Checkout uses: actions/checkout@v5 - name: Login to Quay.io run: | echo "${{ secrets.QUAY_PASSWORD }}" | podman login quay.io -u "${{ secrets.QUAY_USERNAME }}" --password-stdin - - name: Container image building + - name: Build image run: | - echo "Building ClusterIQ Scanner (${{ needs.setup.outputs.BRANCH }}/${{ needs.setup.outputs.SHA_COMMIT }})" podman build \ --platform linux/amd64 \ --build-arg VERSION=${{ needs.setup.outputs.GIT_TAG }} \ --build-arg COMMIT=${{ needs.setup.outputs.SHA_COMMIT }} \ - -t quay.io/${{ secrets.QUAY_ORG_NAME }}/cluster-iq-scanner:${{ needs.setup.outputs.SHA_COMMIT }} \ - -f ./deployments/containerfiles/Containerfile-scanner . - - - name: Pushing Hash based image - run: | - podman push quay.io/${{ secrets.QUAY_ORG_NAME }}/cluster-iq-scanner:${{ needs.setup.outputs.SHA_COMMIT }} + -t quay.io/${{ secrets.QUAY_ORG_NAME }}/cluster-iq-${{ matrix.component }}:${{ needs.setup.outputs.GIT_TAG }} \ + -f ${{ matrix.containerfile }} . - - name: Tagging and Pushing Latest Image - if: ${{ needs.setup.outputs.LATEST_TAG != '' && needs.setup.outputs.LATEST_TAG != null }} + - name: Push version tag run: | - podman tag \ - quay.io/${{ secrets.QUAY_ORG_NAME }}/cluster-iq-scanner:${{ needs.setup.outputs.SHA_COMMIT }} \ - quay.io/${{ secrets.QUAY_ORG_NAME }}/cluster-iq-scanner:${{ needs.setup.outputs.LATEST_TAG }} - podman push quay.io/${{ secrets.QUAY_ORG_NAME }}/cluster-iq-scanner:${{ needs.setup.outputs.LATEST_TAG }} + podman push quay.io/${{ secrets.QUAY_ORG_NAME }}/cluster-iq-${{ matrix.component }}:${{ needs.setup.outputs.GIT_TAG }} - - name: Tagging and Puhsing GitTag based image - if: ${{ needs.setup.outputs.GIT_TAG != '' && needs.setup.outputs.GIT_TAG != null }} + - name: Tag and push latest run: | - echo "Building Tagged version image: ${{ needs.setup.outputs.GIT_TAG }}" podman tag \ - quay.io/${{ secrets.QUAY_ORG_NAME }}/cluster-iq-scanner:${{ needs.setup.outputs.SHA_COMMIT }} \ - quay.io/${{ secrets.QUAY_ORG_NAME }}/cluster-iq-scanner:${{ needs.setup.outputs.GIT_TAG }} - podman push quay.io/${{ secrets.QUAY_ORG_NAME }}/cluster-iq-scanner:${{ needs.setup.outputs.GIT_TAG }} + quay.io/${{ secrets.QUAY_ORG_NAME }}/cluster-iq-${{ matrix.component }}:${{ needs.setup.outputs.GIT_TAG }} \ + quay.io/${{ secrets.QUAY_ORG_NAME }}/cluster-iq-${{ matrix.component }}:latest + podman push quay.io/${{ secrets.QUAY_ORG_NAME }}/cluster-iq-${{ matrix.component }}:latest - name: Logout from Quay.io + if: always() run: | podman logout quay.io - final: + verify: + name: Verify ${{ matrix.component }} runs-on: ubuntu-latest needs: - setup - - api - - agent - - scanner + - build + strategy: + matrix: + include: + - component: api + - component: agent + - component: scanner steps: - - name: Validating + - name: Pull and verify image run: | - podman pull quay.io/${{ secrets.QUAY_ORG_NAME }}/cluster-iq-api:${{ needs.setup.outputs.SHA_COMMIT }} - podman pull quay.io/${{ secrets.QUAY_ORG_NAME }}/cluster-iq-agent:${{ needs.setup.outputs.SHA_COMMIT }} - podman pull quay.io/${{ secrets.QUAY_ORG_NAME }}/cluster-iq-scanner:${{ needs.setup.outputs.SHA_COMMIT }} + podman pull quay.io/${{ secrets.QUAY_ORG_NAME }}/cluster-iq-${{ matrix.component }}:${{ needs.setup.outputs.GIT_TAG }} + podman pull quay.io/${{ secrets.QUAY_ORG_NAME }}/cluster-iq-${{ matrix.component }}:latest diff --git a/.github/workflows/release-images.yaml b/.github/workflows/release-images.yaml new file mode 100644 index 00000000..f435399a --- /dev/null +++ b/.github/workflows/release-images.yaml @@ -0,0 +1,66 @@ +name: Pre-release container images + +on: + push: + branches: + - 'release-*' + +jobs: + setup: + runs-on: ubuntu-latest + outputs: + BRANCH: ${{ steps.vars.outputs.BRANCH }} + SHA_SHORT: ${{ steps.vars.outputs.SHA_SHORT }} + IMAGE_TAG: ${{ steps.vars.outputs.IMAGE_TAG }} + steps: + - name: Determine image tag + id: vars + run: | + BRANCH=${GITHUB_REF##*/} + SHA_SHORT=$(echo "${{ github.sha }}" | cut -c1-7) + IMAGE_TAG="${BRANCH}-${SHA_SHORT}" + echo "BRANCH=$BRANCH" >> $GITHUB_OUTPUT + echo "SHA_SHORT=$SHA_SHORT" >> $GITHUB_OUTPUT + echo "IMAGE_TAG=$IMAGE_TAG" >> $GITHUB_OUTPUT + echo "Image tag: $IMAGE_TAG" + + build: + name: Build ${{ matrix.component }} + runs-on: ubuntu-latest + needs: setup + permissions: + contents: read + packages: write + strategy: + matrix: + include: + - component: api + containerfile: deployments/containerfiles/Containerfile-api + - component: agent + containerfile: deployments/containerfiles/Containerfile-agent + - component: scanner + containerfile: deployments/containerfiles/Containerfile-scanner + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Login to Quay.io + run: | + echo "${{ secrets.QUAY_PASSWORD }}" | podman login quay.io -u "${{ secrets.QUAY_USERNAME }}" --password-stdin + + - name: Build image + run: | + podman build \ + --platform linux/amd64 \ + --build-arg COMMIT=${{ needs.setup.outputs.SHA_SHORT }} \ + -t quay.io/${{ secrets.QUAY_ORG_NAME }}/cluster-iq-${{ matrix.component }}:${{ needs.setup.outputs.IMAGE_TAG }} \ + -f ${{ matrix.containerfile }} . + + - name: Push image + run: | + podman push quay.io/${{ secrets.QUAY_ORG_NAME }}/cluster-iq-${{ matrix.component }}:${{ needs.setup.outputs.IMAGE_TAG }} + + - name: Logout from Quay.io + if: always() + run: | + podman logout quay.io diff --git a/.github/workflows/validate-pr.yaml b/.github/workflows/validate-pr.yaml index 3466a19d..31fc4587 100644 --- a/.github/workflows/validate-pr.yaml +++ b/.github/workflows/validate-pr.yaml @@ -2,6 +2,9 @@ name: Pull Request Validation on: pull_request: + branches: + - main + - 'release-*' permissions: contents: read @@ -29,14 +32,54 @@ jobs: only-new-issues: true args: --whole-files + govulncheck: + name: Go Vulnerability Check + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Set up Go + uses: actions/setup-go@v6 + with: + go-version-file: 'go.mod' + + - name: Run govulncheck + run: | + go install golang.org/x/vuln/cmd/govulncheck@latest + govulncheck ./... + call-unit-tests: name: Go Unit tests - needs: - - golangci uses: "./.github/workflows/unit-tests.yaml" call-integration-tests: name: Integration tests needs: + - golangci - call-unit-tests uses: "./.github/workflows/integration-tests.yaml" + + build-check: + name: Build ${{ matrix.component }} + needs: + - call-integration-tests + runs-on: ubuntu-latest + strategy: + matrix: + include: + - component: api + containerfile: deployments/containerfiles/Containerfile-api + - component: agent + containerfile: deployments/containerfiles/Containerfile-agent + - component: scanner + containerfile: deployments/containerfiles/Containerfile-scanner + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Build image (no push) + run: | + podman build \ + --platform linux/amd64 \ + -f ${{ matrix.containerfile }} . diff --git a/CLAUDE.md b/CLAUDE.md index ddb5d76f..a2ea8b69 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -108,6 +108,24 @@ go tool cover -func=coverage.out | grep -v "100.0%" # Find uncovered go tool cover -html=coverage.out -o coverage.html # Visual ``` +## Branching Model + +**Branch types:** +- `main` — Always stable and fully functional. Tagged with version tags at release merge commits. +- `release-X.Y` — Integration branch for a specific version. All features and fixes targeting that version are merged here, tested together, and then merged to `main` when ready. +- `feature/...`, `bug/...`, `hotfix/...`, etc. — Development branches created in personal forks. + +**Workflow:** +1. Contributors work on **forks**, creating `feature/`, `bug/`, etc. branches. +2. PRs from forks target the corresponding `release-X.Y` branch — **never `main`** directly. +3. Exception: `hotfix/...` branches may target `main` directly for critical production fixes. +4. Once all work in a `release-X.Y` branch is validated, it is merged to `main` and tagged (e.g., `v0.6`). + +**Main branch protection:** +- `main` must **always** be fully functional — every commit on `main` must build, pass tests, and be deployable. +- Never merge incomplete or untested work to `main`. +- All validation (build, lint, tests) must pass on the `release-X.Y` branch before merging to `main`. + ## Development Workflow 1. Make code changes