Skip to content
Commits on Source (39)
include:
- template: Security/Secret-Detection.gitlab-ci.yml
- local: '.gitlab/stages/.check_stage.yml'
- local: '.gitlab/stages/.build_stage.yml'
# - local: '.gitlab/stages/.test_stage.yml'
- local: '.gitlab/stages/.release_stage.yml'
- local: '.gitlab/stages/.docker_build_stage.yml'
workflow:
auto_cancel:
on_new_commit: interruptible
on_job_failure: all
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_COMMIT_TAG == null'
name: "${CI_COMMIT_REF_NAME} [${CI_MERGE_REQUEST_SOURCE_BRANCH_NAME} -> ${CI_MERGE_REQUEST_TARGET_BRANCH_NAME}]"
stages:
- check
- build
- deploy
# - test
- release
- docker
variables:
DOCKER_IMAGE_NAME: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
## IMAGES
DOTNET_IMAGE: mcr.microsoft.com/dotnet/sdk:9.0-alpine
NODE_JS_IMAGE: node:24-bookworm-slim
DOCKER_IMAGE: docker:28.0.0-alpine3.21
DOCKER_DIND_SERVICE: docker:28.0.0-dind-alpine3.21
## BRANCHE NAME
BRANCH_DEVELOP: "develop"
BRANCH_TEST: "testing"
BRANCH_RELEASE: "main"
docker_build:
# Use the official docker image.
image: docker:latest
stage: build
default:
tags:
- lexis
services:
- docker:dind
variables:
DOCKER_IMAGE_NAME: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
before_script:
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
# All branches are tagged with $DOCKER_IMAGE_NAME (defaults to commit ref slug)
# Default branch is also tagged with `latest`
script:
- docker build --load -f ./src/Api/Dockerfile -t $DOCKER_IMAGE_NAME .
- docker push $DOCKER_IMAGE_NAME
- |
if [[ "$CI_COMMIT_BRANCH" == "$CI_DEFAULT_BRANCH" ]]; then
docker tag "$DOCKER_IMAGE_NAME" "$CI_REGISTRY_IMAGE:latest"
docker push "$CI_REGISTRY_IMAGE:latest"
fi
deploy_devel:
stage: deploy
trigger:
project: lexis-platform/deployment/docker-compose
branch: main
only:
- develop
emailsender:
image: ${DOTNET_IMAGE}
stage: build
script:
- dotnet restore ./src/Api/Api.csproj
- dotnet build ./src/Api/Api.csproj -c 'Release' -clp:ErrorsOnly -v m
artifacts:
paths:
- artifacts/obj/Api/Api.json
expire_in: 1 hour
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- ~/.nuget/packages
secret_detection:
extends: .secret-analyzer
stage: check
allow_failure: false
after_script:
- |
if [ -f gl-secret-detection-report.json ]; then
if grep -q '"vulnerabilities": \[' gl-secret-detection-report.json; then
echo "Vulnerabilities found!";
exit 1;
else
echo "No vulnerabilities found.";
fi
else
echo "No report found.";
fi
artifacts:
reports:
secret_detection: gl-secret-detection-report.json
paths:
- gl-secret-detection-report.json
rules:
- if: '$CI_PIPELINE_SOURCE == "push" || $CI_PIPELINE_SOURCE == "merge_request_event"'
when: on_success
- when: never
nuget_outdated:
image: ${DOTNET_IMAGE}
stage: check
script:
- export PATH="$DOTNET_ROOT:$PATH"
- dotnet tool install --tool-path . dotnet-outdated-tool
- ./dotnet-outdated --version-lock Major -o outdated_packages.json
- |
if [ -f outdated_packages.json ] && grep -q -i '"UpgradeSeverity": "Patch"\|"UpgradeSeverity": "Minor"' outdated_packages.json; then
exit 1
fi
allow_failure: true
artifacts:
when: on_failure
paths:
- outdated_packages.json
\ No newline at end of file
build_and_push_api:
stage: docker
image: docker:28.0.0
services:
- docker:dind
before_script:
- |
if [ "$PREV_JOB_EXIT_CODE" = "222" ] && [ "$FORCE_IMAGE_PUBLISH" != "true" ]; then echo "Nothing to change, skipping this job."; exit 222; fi
needs:
- job: unit_tests
optional: true
- job: release_version
artifacts: true
allow_failure:
exit_codes: [222]
script:
- |
if [ -f version.env ]; then
source version.env
echo "Using version: $VERSION"
fi
- docker login -u "$CI_REGISTRY_USER" -p "$CI_JOB_TOKEN" "$CI_REGISTRY"
- docker build --no-cache --pull -f ./src/Api/Dockerfile -t "$CI_REGISTRY_IMAGE:$VERSION" --build-arg VERSION=$VERSION .
- docker push "$CI_REGISTRY_IMAGE:$VERSION"
rules:
- if: '($CI_PIPELINE_SOURCE == "push" || $CI_PIPELINE_SOURCE == "web") && ($CI_COMMIT_REF_NAME == $BRANCH_RELEASE || $CI_COMMIT_REF_NAME == $BRANCH_TEST || $CI_COMMIT_REF_NAME == $BRANCH_DEVELOP)'
when: on_success
- when: never
\ No newline at end of file
release_version:
image: ${NODE_JS_IMAGE}
stage: release
before_script:
- apt-get update && apt-get install -y --no-install-recommends git-core ca-certificates sed
- npm install -g semantic-release @semantic-release/gitlab @semantic-release/exec @semantic-release/commit-analyzer @semantic-release/git @semantic-release/release-notes-generator @semantic-release/changelog conventional-changelog-conventionalcommits
script:
- semantic-release
- |
if [ ! -f release.env ]; then
# Get the latest tag on the current branch (if it exists)
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null | sed 's/^v//' || echo "unknown")
echo "VERSION=$LAST_TAG" > release.env
echo "VERSION_CHANGED=false" >> release.env
fi
source release.env
if [ "$VERSION_CHANGED" != "true" ]; then
echo "Version did not change, skipped."
echo "PREV_JOB_EXIT_CODE=222" >> release.env
exit 222
else
echo "PREV_JOB_EXIT_CODE=0" >> release.env
fi
allow_failure:
exit_codes: [222]
rules:
- if: '($CI_PIPELINE_SOURCE == "push" || $CI_PIPELINE_SOURCE == "web") && ($CI_COMMIT_REF_NAME == $BRANCH_RELEASE || $CI_COMMIT_REF_NAME == $BRANCH_TEST || $CI_COMMIT_REF_NAME == $BRANCH_DEVELOP)'
when: on_success
- when: never
needs:
- job: emailsender
- job: unit_tests
optional: true
artifacts: true
artifacts:
paths:
- release.env
reports:
dotenv: release.env
when: always
\ No newline at end of file
## TEST STAGE
# unit_tests:
# image: ${DOTNET_IMAGE}
# stage: test
# before_script:
# - dotnet tool install -g dotnet-reportgenerator-globaltool
# - dotnet tool install -g CodeCoverageExtractor
# script:
# - dotnet test ${UNIT_TEST_PROJECT_PATHS} --results-directory $CI_PROJECT_DIR/cobertura --collect:"XPlat Code Coverage" --test-adapter-path:. --logger:"junit;LogFilePath=../artifacts/Core.Tests-test.xml;MethodFormat=Class;FailureBodyFormat=Verbose"
# - cp $CI_PROJECT_DIR/cobertura/*/coverage.cobertura.xml $CI_PROJECT_DIR/cobertura/coverage.cobertura.xml
# - /root/.dotnet/tools/reportgenerator -reports:"$CI_PROJECT_DIR/cobertura/coverage.cobertura.xml" -targetdir:"coveragereport" -reporttypes:"Html"
# - tar -cvf coveragereport.tar coveragereport/
# artifacts:
# when: always
# expire_in: 1 week
# paths:
# - ./**/*test.xml
# - $CI_PROJECT_DIR/cobertura/coverage.cobertura.xml
# - coveragereport.tar
# reports:
# junit:
# - ./**/*test.xml
# coverage_report:
# coverage_format: cobertura
# path: $CI_PROJECT_DIR/cobertura/coverage.cobertura.xml
# needs:
# - job: expirio
# artifacts: true
# coverage: /total_coverage=(\d+.?\d?)/
# rules:
# - if: '$CI_PIPELINE_SOURCE == "push" || $CI_PIPELINE_SOURCE == "web" || $CI_PIPELINE_SOURCE == "merge_request_event"'
# when: on_success
# - when: never
# integration_tests:
# image: ${DOTNET_IMAGE}
# stage: test
# allow_failure: true
# before_script:
# - dotnet tool install -g dotnet-reportgenerator-globaltool
# - dotnet workload install aspire
# - dotnet restore --runtime linux-x64 tests/Api.Integration.Tests/Api.Integration.Tests.csproj
# - dotnet build tests/Api.Integration.Tests/Api.Integration.Tests.csproj
# script:
# - mkdir -p coverage
# - dotnet test tests/Api.Integration.Tests/Api.Integration.Tests.csproj --results-directory $CI_PROJECT_DIR/cobertura --collect:"XPlat Code Coverage" --test-adapter-path:. --logger:"junit;LogFilePath=../artifacts/Api.Integration.Tests-test.xml;MethodFormat=Class;FailureBodyFormat=Verbose"
# - cp $CI_PROJECT_DIR/cobertura/*/coverage.cobertura.xml $CI_PROJECT_DIR/cobertura/coverage.cobertura.xml
# - /root/.dotnet/tools/reportgenerator -reports:"$CI_PROJECT_DIR/cobertura/coverage.cobertura.xml" -targetdir:"coverage/integration" -reporttypes:"Cobertura;Html"
# - cp coverage/integration/Cobertura.xml coverage/coverage.xml
# - tar -cvf coveragereport_integration.tar coverage/integration/
# artifacts:
# when: always
# expire_in: 1 week
# paths:
# - ./**/*test.xml
# - $CI_PROJECT_DIR/cobertura/coverage.cobertura.xml
# - coveragereport_integration.tar
# - coverage/
# reports:
# junit:
# - ./**/*test.xml
# coverage_report:
# coverage_format: cobertura
# path: coverage/coverage.xml
# needs:
# - job: expirio
# artifacts: true
# coverage: /total_coverage=(\d+.?\d?)/
module.exports = {
branches: [
{ name: 'main', channel: 'latest', prerelease: false },
{ name: 'testing', prerelease: 'rc' },
{ name: 'develop', prerelease: 'dev' }
],
plugins: [
[
'@semantic-release/commit-analyzer',
{
preset: 'conventionalcommits',
releaseRules: [
{ breaking: true, release: 'major' },
{ type: 'build', release: 'patch' },
{ type: 'chore', release: false },
{ type: 'ci', release: false },
{ type: 'dep', release: 'patch' },
{ type: 'docs', release: false },
{ type: 'feat', release: 'minor' },
{ type: 'fix', release: 'patch' },
{ type: 'perf', release: 'patch' },
{ type: 'refactor', release: false },
{ type: 'revert', release: 'patch' },
{ type: 'test', release: false }
]
}
],
[
'@semantic-release/release-notes-generator',
{
preset: 'conventionalcommits',
writerOpts: {
groupBy: 'type',
commitGroupsSort: 'title',
commitsSort: 'header'
},
presetConfig: {
issuePrefixes: ['#'],
referencesActions: ['close', 'closes', 'fix', 'fixes', 'resolve', 'resolves'],
issueUrlFormat: process.env.CI_SERVER_URL + '/' + process.env.CI_PROJECT_PATH + '/-/issues/{{id}}',
types: [
{ type: 'build', section: '**👷 Build**' },
{ type: 'chore', section: '**🧹 Chores**' },
{ type: 'ci', section: '**🚦 CI/CD**' },
{ type: 'dep', section: '**👾 Dependencies**' },
{ type: 'docs', section: '**📚 Docs**' },
{ type: 'feat', section: '**🚀 Features**' },
{ type: 'fix', section: '**🛠️ Fixes**' },
{ type: 'perf', section: '**⏩ Performance**' },
{ type: 'refactor', section: '**🔨 Refactor**' },
{ type: 'revert', section: '**🙅‍♂️ Reverts**' },
{ type: 'test', section: '**🚥 Tests**' }
]
}
}
],
// Changelog a commit CHANGELOG.md pouze na main branch
...((process.env.CI_COMMIT_REF_NAME === 'main' || process.env.GITHUB_REF_NAME === 'main')
? [
['@semantic-release/changelog'],
[
'@semantic-release/git',
{
assets: ['CHANGELOG.md'],
message: 'chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}'
}
],
[
'@semantic-release/gitlab',
{
gitlabUrl: process.env.CI_SERVER_URL || 'https://gitlab.com',
assets: [
{
path: 'migrations/sql/migration.sql',
label: 'Migration SQL Script',
type: 'application/sql'
}
]
}
]
]
: [
[
'@semantic-release/gitlab',
{
gitlabUrl: process.env.CI_SERVER_URL || 'https://gitlab.com',
publish: false,
assets: [
{
path: 'migrations/sql/migration.sql',
label: 'Migration SQL Script',
type: 'application/sql'
}
]
}
]
]),
[
'@semantic-release/exec',
{
prepareCmd: "echo 'Simulating build process'",
publishCmd: "echo 'Simulating publish' && echo VERSION=${nextRelease.version} >> release.env && echo VERSION_CHANGED=true >> release.env",
failCmd: "echo VERSION=${lastRelease.version} >> release.env && echo VERSION_CHANGED=false >> release.env"
}
]
]
};
## [2.1.1](https://opencode.it4i.eu/lexis-platform/backend/emailsender/compare/v2.1.0...v2.1.1) (2025-06-20)
### **🧹 Chores**
* remove unused before_script for docker image pruning ([03708ac](https://opencode.it4i.eu/lexis-platform/backend/emailsender/commit/03708ac379488390ee245cb6a780d1d2356e3592))
* update CI/CD configuration and dependencies ([d9c9b97](https://opencode.it4i.eu/lexis-platform/backend/emailsender/commit/d9c9b9796847b90389078c679838d10ef2ab8cd5))
### **🛠️ Fixes**
* add missing job dependency for emailsender in release stage ([644be5d](https://opencode.it4i.eu/lexis-platform/backend/emailsender/commit/644be5d8ac4ad8c89960ab3300533953d52ce9c4))
* correct file path in build script for dotnet restore and build ([5af67f0](https://opencode.it4i.eu/lexis-platform/backend/emailsender/commit/5af67f0905c9a56ed701c0a0a6182e09b54e2d00))
......@@ -4,7 +4,7 @@
<Authors>Lukas Drabek</Authors>
<Description></Description>
<Nullable>enable</Nullable>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://opencode.it4i.eu/lexis-platform/backend/emailsender.git</RepositoryUrl>
......
......@@ -17,23 +17,23 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AspNetCore.HealthChecks.Sqlite" Version="8.0.0" />
<PackageReference Include="AspNetCore.HealthChecks.UI" Version="8.0.0" />
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="8.0.0" />
<PackageReference Include="AspNetCore.HealthChecks.UI.InMemory.Storage" Version="8.0.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" Version="8.0.1" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.6" />
<PackageReference Include="Serilog" Version="3.1.1" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.1" />
<PackageReference Include="AspNetCore.HealthChecks.Sqlite" Version="9.0.0" />
<PackageReference Include="AspNetCore.HealthChecks.UI" Version="9.0.0" />
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="9.0.0" />
<PackageReference Include="AspNetCore.HealthChecks.UI.InMemory.Storage" Version="9.0.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.6" />
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="9.0.6" />
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" Version="9.0.6" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.2" />
<PackageReference Include="Serilog" Version="4.3.0" />
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageReference Include="Serilog.Enrichers.AspNetCore" Version="1.0.0" />
<PackageReference Include="Serilog.Enrichers.AspNetCore.HttpContext" Version="1.0.1" />
<PackageReference Include="Serilog.Formatting.Compact" Version="2.0.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="8.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.1-dev-00947" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="Serilog.Formatting.Compact" Version="3.0.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="9.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.1" />
</ItemGroup>
<ItemGroup>
......@@ -41,7 +41,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Update="SonarAnalyzer.CSharp" Version="9.19.0.84025" />
<PackageReference Update="SonarAnalyzer.CSharp" Version="10.12.0.118525" />
</ItemGroup>
</Project>
#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
ARG VERSION=dev
WORKDIR /app
EXPOSE 8080
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /src
COPY ["Directory.Build.props", "."]
......@@ -32,4 +33,6 @@ RUN chmod 777 /app/emailTemplates
RUN apt update && apt install -y curl && apt install -y net-tools
COPY --from=publish /app/publish .
ENV APP_VERSION=${VERSION}
LABEL org.opencontainers.image.version=${VERSION}
ENTRYPOINT ["dotnet", "Api.dll"]
\ No newline at end of file
using System.Reflection;
using Api;
using Application;
......@@ -6,6 +8,7 @@ using Application.Email;
using HealthChecks.UI.Client;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Rewrite;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.OpenApi.Models;
......@@ -105,6 +108,17 @@ try
app.UseStatusCodePages();
app.MapEndpoints();
app.MapGet("/version", ([FromServices] IConfiguration configuration) =>
{
var version = Assembly.GetExecutingAssembly()
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion
?? Assembly.GetExecutingAssembly().GetName().Version?.ToString()
?? "unknown";
return Results.Content(version, "text/plain");
})
.WithName("GetVersion")
.Produces<string>(StatusCodes.Status200OK);
app.UseSwagger(swagger =>
{
swagger.PreSerializeFilters.Add((swaggerDoc, httpReq) =>
......
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<PackageReference Include="MailKit" Version="4.3.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Analyzers" Version="8.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.1">
<PackageReference Include="MailKit" Version="4.12.1" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Analyzers" Version="9.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.6">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.1">
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.6">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
<PackageReference Include="MimeKit" Version="4.3.0" />
<PackageReference Include="Serilog" Version="3.1.1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.6" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.6" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.6" />
<PackageReference Include="MimeKit" Version="4.12.0" />
<PackageReference Include="Serilog" Version="4.3.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.1" />
</ItemGroup>
<ItemGroup>
<PackageReference Update="SonarAnalyzer.CSharp" Version="9.19.0.84025" />
<PackageReference Update="SonarAnalyzer.CSharp" Version="10.12.0.118525" />
</ItemGroup>
</Project>
\ No newline at end of file
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
......