diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index c5f13ca..3f3cffa 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -1,35 +1,134 @@ --- + name: Docker Image CI on: # yamllint disable-line rule:truthy push: - branches: [main, develop, feature/*] + branches: ["main", "develop", "feature/*"] pull_request: - branches: [main] + branches: ["main"] -permissions: read-all +env: + IMAGE_NAME: algorithm-exercises-csharp + ARTIFACT_NAME: algorithm-exercises-csharp_${{ github.sha }} jobs: + build: - name: Build & Test in Docker + name: "Build Docker images" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: "LINT: Build and push" + uses: docker/build-push-action@v6 + with: + context: . + target: lint + outputs: | + type=docker,dest=/tmp/${{ env.ARTIFACT_NAME }}_lint.tar + tags: | + ${{ env.IMAGE_NAME }}:lint + - name: "LINT: Upload artifact" + uses: actions/upload-artifact@v4 + with: + name: ${{ env.ARTIFACT_NAME }}_lint + path: /tmp/${{ env.ARTIFACT_NAME }}_lint.tar + + - name: "TEST: Build and push" + uses: docker/build-push-action@v6 + with: + context: . + target: testing + outputs: | + type=docker,dest=/tmp/${{ env.ARTIFACT_NAME }}_test.tar + tags: | + ${{ env.IMAGE_NAME }}:test + - name: "TEST: Upload artifact" + uses: actions/upload-artifact@v4 + with: + name: ${{ env.ARTIFACT_NAME }}_test + path: /tmp/${{ env.ARTIFACT_NAME }}_test.tar + + - name: "PRODUCTION: Build and push" + uses: docker/build-push-action@v6 + with: + context: . + target: production + outputs: | + type=docker,dest=/tmp/${{ env.ARTIFACT_NAME }}_prod.tar + tags: | + ${{ env.IMAGE_NAME }}:latest + ${{ env.IMAGE_NAME }}:${{ github.sha }} + - name: "PRODUCTION: Upload artifact" + uses: actions/upload-artifact@v4 + with: + name: ${{ env.ARTIFACT_NAME }}_prod + path: /tmp/${{ env.ARTIFACT_NAME }}_prod.tar + + lint: + name: "Run in docker: LINT" runs-on: ubuntu-latest + needs: build + steps: + - name: Download artifact + uses: actions/download-artifact@v4 + with: + name: ${{ env.ARTIFACT_NAME }}_lint + path: /tmp/ + + - name: Load image + run: | + docker load --input /tmp/${{ env.ARTIFACT_NAME }}_lint.tar + docker image ls -a + + - name: Run lint + run: | + docker run --rm ${{ env.IMAGE_NAME }}:lint make lint + test: + name: "Run in docker: TEST" + runs-on: ubuntu-latest + needs: build + steps: + - name: Download artifact + uses: actions/download-artifact@v4 + with: + name: ${{ env.ARTIFACT_NAME }}_test + path: /tmp/ + + - name: Load image + run: | + docker load --input /tmp/${{ env.ARTIFACT_NAME }}_test.tar + docker image ls -a + + - name: Run test + run: | + docker run --rm ${{ env.IMAGE_NAME }}:test make test + + security: + name: "Snyk Container" + runs-on: ubuntu-latest + needs: build + permissions: + actions: read + contents: read + security-events: write steps: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 - - name: Build the Docker image - run: make compose/rebuild - - name: Lint in Docker image - run: make compose/lint - - name: Test in Docker image - run: make compose/test - - name: Run in Docker image - run: make compose/run - - name: Tag Docker image - run: > - docker tag - algorithm-exercises-csharp:latest - algorithm-exercises-csharp:${{ github.sha }} + - name: Download artifact + uses: actions/download-artifact@v4 + with: + name: ${{ env.ARTIFACT_NAME }}_prod + path: /tmp/ + + - name: Load image + run: | + docker load --input /tmp/${{ env.ARTIFACT_NAME }}_prod.tar + docker image ls -a - name: Run Snyk to check Docker image for vulnerabilities # Snyk can be used to break the build when it detects vulnerabilities. @@ -44,11 +143,47 @@ jobs: # yamllint enable rule:line-length SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} with: - image: algorithm-exercises-csharp:latest + image: ${{ env.IMAGE_NAME }}:${{ github.sha }} args: --file=Dockerfile - # yamllint disable rule:comments-indentation - # - name: Upload result to GitHub Code Scanning - # uses: github/codeql-action/upload-sarif@v2 - # with: - # sarif_file: snyk.sarif - # yamllint enable rule:comments-indentation + # yamllint disable rule:line-length + # https://github.com/github/codeql-action/issues/2187#issuecomment-2043220400 + - name: Replace security-severity undefined for license-related findings + run: | + sed -i 's/"security-severity": "undefined"/"security-severity": "0"/g' snyk.sarif + sed -i 's/"security-severity": "null"/"security-severity": "0"/g' snyk.sarif + # yamllint enable rule:line-length + - name: Upload result to GitHub Code Scanning + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: 'snyk.sarif' + scan: + name: "Trivy" + runs-on: ubuntu-latest + needs: build + permissions: + actions: read + contents: read + security-events: write + steps: + - name: Download artifact + uses: actions/download-artifact@v4 + with: + name: ${{ env.ARTIFACT_NAME }}_prod + path: /tmp/ + + - name: Load image + run: | + docker load --input /tmp/${{ env.ARTIFACT_NAME }}_prod.tar + docker image ls -a + + - name: Run Trivy vulnerability scanner + uses: aquasecurity/trivy-action@0.24.0 + with: + image-ref: ${{ env.IMAGE_NAME }}:${{ github.sha }} + format: 'sarif' + output: 'trivy-results.sarif' + + - name: Upload Trivy scan results to GitHub Security tab + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: 'trivy-results.sarif' diff --git a/Dockerfile b/Dockerfile index b7e2387..9fca5ae 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,25 @@ ############################################################################### -FROM mcr.microsoft.com/dotnet/sdk:8.0.302-1-alpine3.19-amd64 AS base +FROM mcr.microsoft.com/dotnet/sdk:8.0.302-1-alpine3.19-amd64 AS init ENV WORKDIR=/app WORKDIR ${WORKDIR} RUN apk add --update --no-cache make +############################################################################### +FROM init AS base + +ENV WORKDIR=/app +WORKDIR ${WORKDIR} + +COPY ./Makefile ${WORKDIR}/ +COPY ./algorithm-exercises-csharp.sln ${WORKDIR}/algorithm-exercises-csharp.sln +COPY ./algorithm-exercises-csharp/algorithm-exercises-csharp.csproj ${WORKDIR}/algorithm-exercises-csharp/algorithm-exercises-csharp.csproj +COPY ./algorithm-exercises-csharp-base/algorithm-exercises-csharp-base.csproj ${WORKDIR}/algorithm-exercises-csharp-base/algorithm-exercises-csharp-base.csproj +COPY ./algorithm-exercises-csharp-test/algorithm-exercises-csharp-test.csproj ${WORKDIR}/algorithm-exercises-csharp-test/algorithm-exercises-csharp-test.csproj + +RUN make dependencies + ############################################################################### FROM base AS lint @@ -35,7 +49,6 @@ COPY ./algorithm-exercises-csharp ${WORKDIR}/algorithm-exercises-csharp COPY ./algorithm-exercises-csharp-base ${WORKDIR}/algorithm-exercises-csharp-base COPY ./algorithm-exercises-csharp-test ${WORKDIR}/algorithm-exercises-csharp-test COPY ./algorithm-exercises-csharp.sln ${WORKDIR}/algorithm-exercises-csharp.sln -COPY ./Makefile ${WORKDIR}/ # code linting conf COPY ./.editorconfig ${WORKDIR}/ @@ -56,7 +69,6 @@ COPY ./algorithm-exercises-csharp ${WORKDIR}/algorithm-exercises-csharp COPY ./algorithm-exercises-csharp-base ${WORKDIR}/algorithm-exercises-csharp-base COPY ./algorithm-exercises-csharp-test ${WORKDIR}/algorithm-exercises-csharp-test COPY ./algorithm-exercises-csharp.sln ${WORKDIR}/algorithm-exercises-csharp.sln -COPY ./Makefile ${WORKDIR}/ RUN make build RUN ls -alh