Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
root = true

[*]
end_of_line = lf
insert_final_newline = true
indent_style = space
root = true

[*]
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true

[md]
trim_trailing_whitespace = false
2 changes: 1 addition & 1 deletion .markdownlint.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# ref. https://github.com/DavidAnson/markdownlint
default: true
MD013: # Line length
MD013:
line_length: 240
4 changes: 1 addition & 3 deletions .yamllint.yaml
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
# ref. https://yamllint.readthedocs.io/en/stable/configuration.html

extends: default

rules:
document-start: disable
line-length:
level: warning
max: 120
max: 180
truthy: disable
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ 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
Technology | Role | Action | Detail
-----------|----------|---------------------------------------------------------|----------------------------------------------------------------------------
Docker | CD | [Build & Push](docker/build-push/action.yml) | Build a new container image with Docker, and push it to a container registry
Docker | CI | [Build & Scan](docker/build-scan/action.yml) | Build a new container image with Docker, and scan it
.NET | CI | [Build, lint & test](dotnet/build-lint-test/action.yml) | Build .NET code, check the code with linter and Sonar, and run tests
MongoDB | Services | [Start](mongodb/start/action.yml) | Start a local MongoDB database
64 changes: 53 additions & 11 deletions docker/build-push/action.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
name: Build and push a container image
description: Builds a new container image with Docker and pushes it to a registry
description: |
Builds a new container image with Docker and pushes it to a registry
Make sure to add (needed by cosign):
```
permissions:
id-token: write
contents: read
```

inputs:
container_registry:
description: Container registry
required: true
container_username:
description: Container username
container_registry_username:
description: Container registry username
required: true
container_password:
description: Container password
container_registry_password:
description: Container registry password
required: true
docker_file:
description: Path to the Dockerfile
Expand All @@ -27,6 +34,10 @@ inputs:
description: Create latest tag?
required: false
default: 'false'
build_arguments:
description: Container build arguments
required: false
default: ''

runs:
using: "composite"
Expand All @@ -35,17 +46,48 @@ runs:
uses: docker/login-action@v3
with:
registry: ${{ inputs.container_registry }}
username: ${{ inputs.container_username }}
password: ${{ inputs.container_password }}
username: ${{ inputs.container_registry_username }}
password: ${{ inputs.container_registry_password }}

- name: Build container image
run: docker build . --file ${{inputs.docker_file}} --tag ${{inputs.image_path}}/${{inputs.image_name}}:${{inputs.image_tag}}
run: docker build . --file ${{ inputs.docker_file }} --tag ${{ inputs.image_path }}/${{ inputs.image_name }}:${{ inputs.image_tag }}${{ inputs.build_arguments }}
shell: bash

- name: Generate SBOM with Syft
uses: anchore/sbom-action@v0
continue-on-error: true
with:
image: ${{ inputs.image_path }}/${{ inputs.image_name }}:${{ inputs.image_tag }}
# format: spdx-json # Or cyclonedx-json
# output-file: sbom.json
# upload-artifact: true # Auto-upload to workflow artifacts

- name: Push image to container registry
run: docker push ${{inputs.image_path}}/${{inputs.image_name}}:${{inputs.image_tag}}
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
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: Install Cosign
uses: sigstore/cosign-installer@v4.0.0
with:
cosign-release: 'v3.0.3'

- name: Get image digest
id: digest
run: |
DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' ${{ inputs.image_path }}/${{ inputs.image_name }}:${{ inputs.image_tag }} | cut -d'@' -f2)
echo "DIGEST=$DIGEST" >> $GITHUB_OUTPUT
shell: bash

- name: Sign image with Cosign
env:
COSIGN_EXPERIMENTAL: 1
run: |
cosign sign --yes ${{ inputs.image_path }}/${{ inputs.image_name }}@${{ steps.digest.outputs.DIGEST }}
shell: bash
58 changes: 46 additions & 12 deletions dotnet/build-lint-test/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,49 +5,57 @@ inputs:
dotnet_version:
description: .NET SDK version to be used
required: false
default: '10.0.x'
default: "10.0.x"
sonar_enabled:
description: Enable code scan by Sonar
required: false
default: 'false'
default: "false"
sonar_organization:
description: Sonar organization
required: false
default: ''
default: ""
sonar_host_url:
description: Sonar host URL
required: false
default: ''
default: ""
sonar_project_name:
description: Sonar project name
required: false
default: ''
default: ""
sonar_project_key:
description: Sonar project key
required: false
default: ''
default: ""
sonar_token:
description: Sonar token for login
required: false
default: ''
default: ""
report_folder:
description: Folder where report files will be generated
required: false
default: report
fossa_enabled:
description: Enable license compliance with FOSSA
required: false
default: "false"
fossa_api_key:
description: FOSSA API KEY
required: false
default: ""

runs:
using: "composite"
steps:
- name: Install .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{inputs.dotnet_version}}
dotnet-version: ${{ inputs.dotnet_version }}
- name: Set up JDK for Sonar
if: ${{ inputs.sonar_enabled == 'true' }}
uses: actions/setup-java@v4
with:
java-version: 21
distribution: 'zulu'
distribution: "zulu"
- name: Install .NET linters
if: ${{ inputs.dotnet_version == '7.0.x' }}
run: dotnet tool install -g dotnet-format --version "7.*" --add-source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet7/nuget/v3/index.json
Expand Down Expand Up @@ -86,24 +94,50 @@ 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.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"
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
shell: bash
- name: Run tests
run: dotnet test --no-build --verbosity normal --configuration Debug --logger:"junit;LogFilePath=..\..\artifacts\{assembly}-test-result.xml;MethodFormat=Class;FailureBodyFormat=Verbose" --collect:"XPlat Code Coverage"
run: |
dotnet test --no-build --verbosity normal --configuration Debug \
--logger:"junit;LogFilePath=..\..\artifacts\{assembly}-test-result.xml;MethodFormat=Class;FailureBodyFormat=Verbose" \
--collect:"XPlat Code Coverage"
shell: bash
env:
ASPNETCORE_ENVIRONMENT: Development
Application__IsHttpsRedirectionEnabled: "false"
- name: Generate test report
run: reportgenerator "-reports:./test/*/TestResults/*/coverage.cobertura.xml" "-targetdir:${{inputs.report_folder}}" "-reporttypes:Cobertura;Html;TextSummary;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.token="${{inputs.sonar_token}}"
shell: bash

- name: License Compliance with FOSSA
if: ${{ inputs.fossa_enabled == 'true' }}
uses: fossas/fossa-action@v1
with:
api-key: "${{ inputs.fossa_api_key }}"
run-tests: false

- name: Generate SBOM with Syft
uses: anchore/sbom-action@v0
# with:
# path: . # Or Dockerfile path
# format: spdx-json # Or cyclonedx-json
# output-file: sbom.json
# upload-artifact: true # Auto-upload to workflow artifacts

- name: Archive test results
uses: actions/upload-artifact@v4
with:
Expand Down
52 changes: 52 additions & 0 deletions mongodb-atlas/add-runner-ip/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: Add GitHub Actions runner public IP to MongoDB Atlas
description: Update project IP access list (temporary)

inputs:
atlas_publickey:
description: MongoDB public key
required: true
atlas_privatekey:
description: MongoDB private key
required: true
atlas_groupid:
description: MongoDB group ID
required: true
github_runid:
description: GitHub run ID
required: true

runs:
using: "composite"
steps:
- name: Add runner IP to MongoDB Atlas
shell: bash
run: |
# gets current outbound IP of this runner
RUNNER_IP=$(curl -s https://api.ipify.org || curl -s https://ifconfig.me)
if [ -z "$RUNNER_IP" ]; then
echo "Failed to detect runner IP"
exit 1
fi

echo "Detected runner IP: $RUNNER_IP"

# prepares JSON payload (single /32 entry, temporary delete after 1 hour)
# uses ISO 8601 UTC for deleteAfterDate (current time + 3600s)
DELETE_AFTER=$(date -u -d '+3600 seconds' +"%Y-%m-%dT%H:%M:%SZ")
PAYLOAD="[{\"ipAddress\": \"$RUNNER_IP\", \"comment\": \"GH Actions temp - run ${{ github.run_id }}\", \"deleteAfterDate\": \"$DELETE_AFTER\"}]"

# calls Atlas API v2 to add the entry (uses digest Auth via curl)
RESPONSE=$(curl -s -w "%{http_code}" --user "${{inputs.atlas_publickey}}:${{inputs.atlas_privatekey}}" --digest \
-H "Accept: application/vnd.atlas.2023-01-01+json" \
-H "Content-Type: application/json" \
--data "$PAYLOAD" \
"https://cloud.mongodb.com/api/atlas/v2/groups/${{inputs.atlas_groupid}}/accessList")

HTTP_CODE=${RESPONSE: -3}

if [[ "$HTTP_CODE" != "201" ]]; then
echo "Failed to add IP to Atlas access list (code $HTTP_CODE)"
exit 1
fi

echo "Successfully added temporary IP $RUNNER_IP to Atlas access list (auto-deletes ~1h)"