diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..4ecc0a4 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,35 @@ +name: CI + +on: + push: + branches: + - main + pull_request: + branches: + - main + workflow_dispatch: {} + +env: + python_version: "3.13" + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checks-out the repository + uses: actions/checkout@v4 + - name: Lints Markdown files + uses: DavidAnson/markdownlint-cli2-action@v20 + with: + globs: '**/*.md' + - name: Set up Python ${{ env.python_version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ env.python_version }} + - name: Installs Python packages + run: | + python -m pip install --upgrade pip + pip install yamllint + - name: Lint YAML files + run: | + yamllint . diff --git a/.markdownlint.yml b/.markdownlint.yml new file mode 100644 index 0000000..4572cb2 --- /dev/null +++ b/.markdownlint.yml @@ -0,0 +1,4 @@ +# ref. https://github.com/DavidAnson/markdownlint +default: true +MD013: # Line length + line_length: 240 diff --git a/.yamllint.yaml b/.yamllint.yaml new file mode 100644 index 0000000..26e0a4e --- /dev/null +++ b/.yamllint.yaml @@ -0,0 +1,10 @@ +# ref. https://yamllint.readthedocs.io/en/stable/configuration.html + +extends: default + +rules: + document-start: disable + line-length: + level: warning + max: 120 + truthy: disable diff --git a/README.md b/README.md index 4abbcc2..1923984 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,12 @@ # GitHub workflow parts Repository of workflow parts to be used in GitHub Actions. + +## Actions + +Name | Detail +-------------------------------------------------------------- | --------------------------------------------------------------------------- +[Docker > Build & Push](docker/build-push/action.yml) | Build a new container image with Docker and push it to a container registry +[Docker > Build & Scan](docker/build-scan/action.yml) | Build a new container image with Docker and scan it +[.NET > Build, lint & test](dotnet/build-lint-test/action.yml) | Build .NET code, lint it and run tests +[MongoDB > Start](mongodb/start/action.yml) | Start a local MongoDB database diff --git a/docker/build-push/action.yml b/docker/build-push/action.yml index e160c7f..57bf33c 100644 --- a/docker/build-push/action.yml +++ b/docker/build-push/action.yml @@ -1,51 +1,51 @@ -name: Build and push a container image -description: Builds a new container image with Docker and pushes it to a registry - -inputs: - container_registry: - description: Container registry - required: true - container_username: - description: Container username - required: true - container_password: - description: Container password - required: true - docker_file: - description: Path to the Dockerfile - required: true - image_path: - description: Image tag - required: true - image_name: - description: Image name - required: true - image_tag: - description: Image tag - required: true - create_latest: - description: Create latest tag? - required: false - default: 'false' - -runs: - using: "composite" - steps: - - name: Login to container registry - uses: docker/login-action@v2 - with: - registry: ${{ inputs.container_registry }} - username: ${{ inputs.container_username }} - password: ${{ inputs.container_password }} - - name: Build container image - run: docker build . --file ${{inputs.docker_file}} --tag ${{inputs.image_path}}/${{inputs.image_name}}:${{inputs.image_tag}} - shell: bash - - name: Push image to container registry - run: docker push ${{inputs.image_path}}/${{inputs.image_name}}:${{inputs.image_tag}} - shell: bash - - name: Push latest tag to container registry - if: ${{ inputs.create_latest == 'true' }} - run: | - docker tag ${{inputs.image_path}}/${{inputs.image_name}}:${{inputs.image_tag}} ${{inputs.image_path}}/${{inputs.image_name}}:latest - docker push ${{inputs.image_path}}/${{inputs.image_name}}:latest - shell: bash +name: Build and push a container image +description: Builds a new container image with Docker and pushes it to a registry + +inputs: + container_registry: + description: Container registry + required: true + container_username: + description: Container username + required: true + container_password: + description: Container password + required: true + docker_file: + description: Path to the Dockerfile + required: true + image_path: + description: Image tag + required: true + image_name: + description: Image name + required: true + image_tag: + description: Image tag + required: true + create_latest: + description: Create latest tag? + required: false + default: 'false' + +runs: + using: "composite" + steps: + - name: Login to container registry + uses: docker/login-action@v3 + with: + registry: ${{ inputs.container_registry }} + username: ${{ inputs.container_username }} + password: ${{ inputs.container_password }} + - name: Build container image + run: docker build . --file ${{inputs.docker_file}} --tag ${{inputs.image_path}}/${{inputs.image_name}}:${{inputs.image_tag}} + shell: bash + - name: Push image to container registry + run: docker push ${{inputs.image_path}}/${{inputs.image_name}}:${{inputs.image_tag}} + shell: bash + - name: Push latest tag to container registry + if: ${{ inputs.create_latest == 'true' }} + run: | + docker tag ${{inputs.image_path}}/${{inputs.image_name}}:${{inputs.image_tag}} ${{inputs.image_path}}/${{inputs.image_name}}:latest + docker push ${{inputs.image_path}}/${{inputs.image_name}}:latest + shell: bash diff --git a/docker/build-scan/action.yml b/docker/build-scan/action.yml index 43af536..fbc1a5d 100644 --- a/docker/build-scan/action.yml +++ b/docker/build-scan/action.yml @@ -1,57 +1,57 @@ -name: Scan container image -description: Builds a new container image with Docker and scans it - -inputs: - docker_file: - description: Path to the Dockerfile - required: true - image_tag: - description: Image tag - required: true - image_path: - description: Image tag - required: true - image_name: - description: Image name - required: true - neuvector_enabled: - description: Use NeuVector to scan the image? - required: false - default: 'true' - trivy_enabled: - description: Use Trivy to scan the image? - required: false - default: 'true' - max_high_cves: - description: Maximum number of high CVE authorized - required: false - default: '1' - max_medium_cves: - description: Maximum number of medium CVE authorized - required: false - default: '1' - -runs: - using: "composite" - steps: - - name: Build container image - run: docker build . --file ${{inputs.docker_file}} --tag ${{inputs.image_path}}/${{inputs.image_name}}:${{inputs.image_tag}} - shell: bash - - name: Scan container image with NeuVector - if: ${{ inputs.neuvector_enabled == 'true' }} - uses: neuvector/scan-action@main - with: - image-repository: ${{inputs.image_path}}/${{inputs.image_name}} - image-tag: ${{inputs.image_tag}} - min-high-cves-to-fail: '${{inputs.max_high_cves}}' - min-medium-cves-to-fail: '${{inputs.max_medium_cves}}' - - name: Scan container image with Trivy - if: ${{ inputs.trivy_enabled == 'true' }} - uses: aquasecurity/trivy-action@master - with: - image-ref: '${{inputs.image_path}}/${{inputs.image_name}}:${{inputs.image_tag}}' - format: 'table' - exit-code: '1' - ignore-unfixed: true - vuln-type: 'os,library' - severity: 'CRITICAL,HIGH' +name: Scan container image +description: Builds a new container image with Docker and scans it + +inputs: + docker_file: + description: Path to the Dockerfile + required: true + image_tag: + description: Image tag + required: true + image_path: + description: Image tag + required: true + image_name: + description: Image name + required: true + neuvector_enabled: + description: Use NeuVector to scan the image? + required: false + default: 'true' + trivy_enabled: + description: Use Trivy to scan the image? + required: false + default: 'true' + max_high_cves: + description: Maximum number of high CVE authorized + required: false + default: '1' + max_medium_cves: + description: Maximum number of medium CVE authorized + required: false + default: '1' + +runs: + using: "composite" + steps: + - name: Build container image + run: docker build . --file ${{inputs.docker_file}} --tag ${{inputs.image_path}}/${{inputs.image_name}}:${{inputs.image_tag}} + shell: bash + - name: Scan container image with NeuVector + if: ${{ inputs.neuvector_enabled == 'true' }} + uses: neuvector/scan-action@main + with: + image-repository: ${{inputs.image_path}}/${{inputs.image_name}} + image-tag: ${{inputs.image_tag}} + min-high-cves-to-fail: '${{inputs.max_high_cves}}' + min-medium-cves-to-fail: '${{inputs.max_medium_cves}}' + - name: Scan container image with Trivy + if: ${{ inputs.trivy_enabled == 'true' }} + uses: aquasecurity/trivy-action@master + with: + image-ref: '${{inputs.image_path}}/${{inputs.image_name}}:${{inputs.image_tag}}' + format: 'table' + exit-code: '1' + ignore-unfixed: true + vuln-type: 'os,library' + severity: 'CRITICAL,HIGH' diff --git a/dotnet/build-lint-test/action.yml b/dotnet/build-lint-test/action.yml index 1319aca..4a4fe46 100644 --- a/dotnet/build-lint-test/action.yml +++ b/dotnet/build-lint-test/action.yml @@ -5,7 +5,7 @@ inputs: dotnet_version: description: .NET SDK version to be used required: false - default: '7.0.x' + default: '8.0.x' sonar_enabled: description: Enable code scan by Sonar required: false @@ -30,19 +30,23 @@ inputs: description: Sonar token for login required: false default: '' + report_folder: + description: Folder where report files will be generated + required: false + default: report runs: using: "composite" steps: - name: Install .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: dotnet-version: ${{inputs.dotnet_version}} - - name: Set up JDK 11 for Sonar + - name: Set up JDK for Sonar if: ${{ inputs.sonar_enabled == 'true' }} - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: - java-version: 11 + java-version: 21 distribution: 'zulu' - name: Install .NET linters if: ${{ inputs.dotnet_version == '7.0.x' }} @@ -57,11 +61,11 @@ runs: run: dotnet restore shell: bash - name: Lint .NET code - run: dotnet-format --verify-no-changes --severity warn --verbosity:diagnostic + run: dotnet format --verify-no-changes --severity warn --verbosity:diagnostic shell: bash - name: Cache Sonar packages if: ${{ inputs.sonar_enabled == 'true' }} - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/sonar/cache key: ${{ runner.os }}-sonar @@ -69,7 +73,7 @@ runs: - name: Cache Sonar scanner if: ${{ inputs.sonar_enabled == 'true' }} id: cache-sonar-scanner - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ./.sonar/scanner key: ${{ runner.os }}-sonar-scanner @@ -82,7 +86,7 @@ runs: shell: bash - name: Start code analysis if: ${{ inputs.sonar_enabled == 'true' }} - run: ./.sonar/scanner/dotnet-sonarscanner begin /k:"${{inputs.sonar_project_key}}" /o:"${{inputs.sonar_organization}}" /n:"${{inputs.sonar_project_name}}" /d:sonar.login="${{inputs.sonar_token}}" /d:sonar.host.url="${{inputs.sonar_host_url}}" /d:sonar.cpd.exclusions=**/*Generated*.cs /d:sonar.coverageReportPaths=./sonarqubecoverage/SonarQube.xml + run: ./.sonar/scanner/dotnet-sonarscanner begin /k:"${{inputs.sonar_project_key}}" /o:"${{inputs.sonar_organization}}" /n:"${{inputs.sonar_project_name}}" /d:sonar.token="${{inputs.sonar_token}}" /d:sonar.host.url="${{inputs.sonar_host_url}}" /d:sonar.cpd.exclusions="**/*Generated*.cs,${{inputs.report_folder}}/**" /d:sonar.exclusions="${{inputs.report_folder}}/**/*" /d:sonar.coverageReportPaths="${{inputs.report_folder}}/SonarQube.xml" shell: bash - name: Build .NET solution run: dotnet build --no-restore @@ -94,16 +98,18 @@ runs: ASPNETCORE_ENVIRONMENT: Development Application__IsHttpsRedirectionEnabled: "false" - name: Generate test report - run: reportgenerator "-reports:./test/*/TestResults/*/coverage.cobertura.xml" -targetdir:sonarqubecoverage -reporttypes:SonarQube + run: reportgenerator "-reports:./test/*/TestResults/*/coverage.cobertura.xml" "-targetdir:${{inputs.report_folder}}" "-reporttypes:Cobertura;Html;TextSummary;SonarQube" shell: bash - name: Complete code analysis if: ${{ inputs.sonar_enabled == 'true' }} - run: ./.sonar/scanner/dotnet-sonarscanner end /d:sonar.login="${{inputs.sonar_token}}" + run: ./.sonar/scanner/dotnet-sonarscanner end /d:sonar.token="${{inputs.sonar_token}}" shell: bash - name: Archive test results - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: dotnet-test-results path: | ./**/*test-result.xml ./test/*/TestResults/*/coverage.cobertura.xml + ./**/SonarQube.xml + ./**/Summary.txt diff --git a/mongodb/start/action.yml b/mongodb/start/action.yml index 6d66422..1ac0792 100644 --- a/mongodb/start/action.yml +++ b/mongodb/start/action.yml @@ -1,18 +1,24 @@ -name: Start MongoDB -description: Starts a local MongoDB database - -runs: - using: "composite" - steps: - # see https://www.mongodb.com/docs/manual/tutorial/install-mongodb-on-ubuntu/ - - name: Install & start MongoDB - run: | - curl -fsSL https://pgp.mongodb.com/server-6.0.asc | \ - sudo gpg -o /usr/share/keyrings/mongodb-server-6.0.gpg \ - --dearmor - echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-6.0.gpg ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/6.0 multiverse" | \ - sudo tee /etc/apt/sources.list.d/mongodb-org-6.0.list - sudo apt-get update - sudo apt-get install -y mongodb-org - sudo systemctl start mongod - shell: bash +name: Start MongoDB +description: Starts a local MongoDB database + +runs: + using: "composite" + steps: + # ref. https://www.mongodb.com/docs/manual/tutorial/install-mongodb-on-ubuntu/ + - name: Install & start MongoDB + run: | + curl -fsSL https://www.mongodb.org/static/pgp/server-8.0.asc | \ + sudo gpg -o /usr/share/keyrings/mongodb-server-8.0.gpg \ + --dearmor + echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-8.0.gpg ] https://repo.mongodb.org/apt/ubuntu noble/mongodb-org/8.0 multiverse" | \ + sudo tee /etc/apt/sources.list.d/mongodb-org-8.0.list + sudo apt-get update + sudo apt-get install -y mongodb-org + sudo systemctl start mongod + timeout 30 bash -c " + until mongosh --host localhost:27017 --eval 'db.runCommand({ ping: 1 })' >/dev/null 2>&1; do + echo 'MongoDB not ready yet, retrying in 1 second...' + sleep 1 + done + " + shell: bash