From fcf9748fcd1bbe87b6971d73cd906995c3895415 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Tue, 24 Mar 2026 14:08:11 +0500 Subject: [PATCH 01/91] chore(tests): #14: rename the 'test' directory to 'tests', since it will contain several types of tests: e2e and unit --- CMakeLists.txt | 2 +- {test => tests/unit}/CMakeLists.txt | 0 {test => tests/unit}/addition-operation.cpp | 0 {test => tests/unit}/addition-operation.h | 0 {test => tests/unit}/test_main.cpp | 0 5 files changed, 1 insertion(+), 1 deletion(-) rename {test => tests/unit}/CMakeLists.txt (100%) rename {test => tests/unit}/addition-operation.cpp (100%) rename {test => tests/unit}/addition-operation.h (100%) rename {test => tests/unit}/test_main.cpp (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6f56a1c..b1f0cd9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,4 +52,4 @@ target_link_libraries(${PROJECT_NAME} PUBLIC # Tests enable_testing() -add_subdirectory(test) \ No newline at end of file +add_subdirectory(tests/unit) \ No newline at end of file diff --git a/test/CMakeLists.txt b/tests/unit/CMakeLists.txt similarity index 100% rename from test/CMakeLists.txt rename to tests/unit/CMakeLists.txt diff --git a/test/addition-operation.cpp b/tests/unit/addition-operation.cpp similarity index 100% rename from test/addition-operation.cpp rename to tests/unit/addition-operation.cpp diff --git a/test/addition-operation.h b/tests/unit/addition-operation.h similarity index 100% rename from test/addition-operation.h rename to tests/unit/addition-operation.h diff --git a/test/test_main.cpp b/tests/unit/test_main.cpp similarity index 100% rename from test/test_main.cpp rename to tests/unit/test_main.cpp From f114679b2af42789c2757c22c015af8473f1fde7 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Tue, 24 Mar 2026 14:10:20 +0500 Subject: [PATCH 02/91] ci(tests): #14: add workflows for karate tests; remove of the build workflow, as it has been replaced --- .../.reusable-docker-build-and-push.yml | 197 ++++++++++++++++++ .github/workflows/build.yml | 48 ----- .../workflows/e2e-tests-on-pull-request.yml | 74 +++++++ 3 files changed, 271 insertions(+), 48 deletions(-) create mode 100644 .github/workflows/.reusable-docker-build-and-push.yml delete mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/e2e-tests-on-pull-request.yml diff --git a/.github/workflows/.reusable-docker-build-and-push.yml b/.github/workflows/.reusable-docker-build-and-push.yml new file mode 100644 index 0000000..bb6ff42 --- /dev/null +++ b/.github/workflows/.reusable-docker-build-and-push.yml @@ -0,0 +1,197 @@ +name: Publish Docker image + +# !!! NEVER add on push when there is on workflow_call +# if you do that the workflow can run multiple times +# for instance if you re-use this docker build workflow for prod deployment and for local-env in PR +# it will build the docker image it twice +# if you build => deploy => run e2e against prod it will build the image 3 times! +on: + # to allow to wait for a docker image to be published to proceed in another workflow + workflow_call: + +jobs: + build-amd64: + runs-on: ubuntu-24.04 + steps: + - name: Check out the repo + uses: actions/checkout@v4 + + # this is needed to address this issue according to the comment https://github.com/devcontainers/ci/issues/271#issuecomment-2301764487 + # otherwise our TourmalineCore org name cannot be used in docker image names, only tourmalinecore + - name: Add Registry Image Env Var With Lowercase Organization and Repo Name + run: | + echo "REGISTRY_IMAGE=ghcr.io/${GITHUB_REPOSITORY,,}" >>${GITHUB_ENV} + + - name: Prepare + run: | + platform=linux/amd64 + echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV + + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY_IMAGE }} + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push by digest + id: build + uses: docker/build-push-action@v6 + with: + platforms: linux/amd64 + context: . + file: ./Dockerfile + build-args: | + EXCLUDE_UNIT_TESTS_FROM_BUILD=true + labels: ${{ steps.meta.outputs.labels }} + tags: ${{ env.REGISTRY_IMAGE }} + outputs: type=image,push-by-digest=true,name-canonical=true,push=true + + - name: Export digest + run: | + mkdir -p ${{ runner.temp }}/digests + digest="${{ steps.build.outputs.digest }}" + touch "${{ runner.temp }}/digests/${digest#sha256:}" + + - name: Upload digest + uses: actions/upload-artifact@v4 + with: + name: digests-${{ env.PLATFORM_PAIR }} + path: ${{ runner.temp }}/digests/* + if-no-files-found: error + retention-days: 1 + + build-arm64: + runs-on: ubuntu-24.04-arm + steps: + - name: Check out the repo + uses: actions/checkout@v4 + + # this is needed to address this issue according to the comment https://github.com/devcontainers/ci/issues/271#issuecomment-2301764487 + # otherwise our TourmalineCore org name cannot be used in docker image names, only tourmalinecore + - name: Add Registry Image Env Var With Lowercase Organization and Repo Name + run: | + echo "REGISTRY_IMAGE=ghcr.io/${GITHUB_REPOSITORY,,}" >>${GITHUB_ENV} + + - name: Prepare + run: | + platform=linux/arm64 + echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV + + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY_IMAGE }} + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push by digest + id: build + uses: docker/build-push-action@v6 + with: + platforms: linux/arm64 + context: . + file: ./Dockerfile + build-args: | + EXCLUDE_UNIT_TESTS_FROM_BUILD=true + labels: ${{ steps.meta.outputs.labels }} + tags: ${{ env.REGISTRY_IMAGE }} + outputs: type=image,push-by-digest=true,name-canonical=true,push=true + + - name: Export digest + run: | + mkdir -p ${{ runner.temp }}/digests + digest="${{ steps.build.outputs.digest }}" + touch "${{ runner.temp }}/digests/${digest#sha256:}" + + - name: Upload digest + uses: actions/upload-artifact@v4 + with: + name: digests-${{ env.PLATFORM_PAIR }} + path: ${{ runner.temp }}/digests/* + if-no-files-found: error + retention-days: 1 + merge: + runs-on: ubuntu-24.04 + needs: + - build-amd64 + - build-arm64 + steps: + # this is needed to address this issue according to the comment https://github.com/devcontainers/ci/issues/271#issuecomment-2301764487 + # otherwise our TourmalineCore org name cannot be used in docker image names, only tourmalinecore + - name: Add Registry Image Env Var With Lowercase Organization and Repo Name + run: | + echo "REGISTRY_IMAGE=ghcr.io/${GITHUB_REPOSITORY,,}" >>${GITHUB_ENV} + + - name: Download digests + uses: actions/download-artifact@v4 + with: + path: ${{ runner.temp }}/digests + pattern: digests-* + merge-multiple: true + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Check out the repo + uses: actions/checkout@v4 + + - name: Add SEMVER_VERSION Env Var with Value from __version File + run: | + echo "SEMVER_VERSION=$(cat __version)" >>${GITHUB_ENV} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY_IMAGE }} + tags: | + # minimal (short sha) + type=sha + # full length sha + type=sha,format=long + # SemVer human readable version + type=raw,value=${{ env.SEMVER_VERSION }} + # set latest tag for default branch + # https://github.com/docker/metadata-action/issues/171 explains how to tag latest only on default branch + type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} + env: + # https://github.com/docker/metadata-action/issues/283 + # without this flag it won't tag the image using the commit SHA + # for non push events like pull_request ones it requires this :( + DOCKER_METADATA_PR_HEAD_SHA: true + + - name: Create manifest list and push + working-directory: ${{ runner.temp }}/digests + run: | + docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ + $(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *) + + - name: Inspect image + run: | + docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 571c090..0000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,48 +0,0 @@ -name: Build - -on: - push: - branches: - - feature/* - -jobs: - run-build: - runs-on: ubuntu-24.04 - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Cache Conan packages - id: cache-conan-packages - uses: actions/cache@v4 - env: - cache-name: cache-conan-packages - with: - path: | - .conan2 - build/Debug/generators - key: ${{ env.cache-name }}-${{ hashFiles('conanfile.py') }} - - - name: Install and build Conan dependencies - if: steps.cache-conan-packages.outputs.cache-hit != 'true' - uses: devcontainers/ci@v0.3 - with: - runCmd: | - mkdir -p .conan2/profiles - cp .devcontainer/to-dos-conan-profile.conf .conan2/profiles/default - - export CONAN_HOME="$(pwd)/.conan2" - conan remote add local-recipes ./deps --type=local-recipes-index - conan install . --build=missing - # we don't need to push docker image that was built using our Dev Container - push: never - - - name: Run CMake build - uses: devcontainers/ci@v0.3 - with: - runCmd: | - cd build && cmake .. -DCMAKE_TOOLCHAIN_FILE=Debug/generators/conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Debug -G "Ninja" - ninja - # we don't need to push docker image that was built using our Dev Container - push: never diff --git a/.github/workflows/e2e-tests-on-pull-request.yml b/.github/workflows/e2e-tests-on-pull-request.yml new file mode 100644 index 0000000..9b35bb6 --- /dev/null +++ b/.github/workflows/e2e-tests-on-pull-request.yml @@ -0,0 +1,74 @@ +name: E2E Tests in PR + +on: + pull_request: + types: [opened, synchronize, reopened] + +jobs: + # this is needed to wait for the new docker image to be build and published to the registry + # so that we can use the image to run the service of the needed commit related version as part of local-env + # the idea is taken from here https://stackoverflow.com/a/71489231 + docker-build-and-push: + uses: ./.github/workflows/.reusable-docker-build-and-push.yml + + e2e-test-against-local-env: + runs-on: ubuntu-24.04 + needs: [docker-build-and-push] + steps: + - name: Checkout local-env + uses: actions/checkout@v4 + with: + repository: TourmalineCore/to-dos-local-env + + - name: Deploy Local Env to Kind k8s + uses: devcontainers/ci@v0.3 + with: + # Replace with the necessary image when the image to-dos-api-cpp-devcontainer is created + # cacheFrom: ghcr.io/tourmalinecore/inner-circle-local-env-devcontainer + runCmd: | + # we need to override "latest" image tag of ui inside local-env to run e2e against the current commit ui version and not against latest from master + # We tried to use yq to change the image tag, but in the values files for helmfile we have non-yaml code that yq can`t parse or ignore + # so for that reason we use Stream EDitor which can find needed string using regular expressions and change it to a new value + # The -i flag is needed to write new image tag directly to values file + sed -i "0,/tag:.*/s//tag: \"sha-${{ github.event.pull_request.head.sha }}\"/" deploy/values-to-dos-api.yaml.gotmpl + + # we need to override "latest" ref of service chart inside local-env to run tests against the current commit service chart version and not against latest from master + sed -i "0,/git+https:\/\/github.com\/TourmalineCore\/${{ github.event.repository.name }}.git?ref=.*/s//git+https:\/\/github.com\/TourmalineCore\/${{ github.event.repository.name }}.git?ref=${{ github.event.pull_request.head.sha }}/" deploy/helmfile.yaml + + sed -i "0,/git::https:\/\/github.com\/TourmalineCore\/${{ github.event.repository.name }}.git@\/ci\/values.yaml?ref=.*/s//git::https:\/\/github.com\/TourmalineCore\/${{ github.event.repository.name }}.git@\/ci\/values.yaml?ref=${{ github.event.pull_request.head.sha }}/" deploy/helmfile.yaml + + kind create cluster --name to-dos --config kind-local-config.yaml --kubeconfig ./.to-dos-cluster-kubeconfig + # we need to properly expose KUBECONFIG as an absolute path, pwd prints current working directory path + export KUBECONFIG=$(pwd)/.to-dos-cluster-kubeconfig + + helmfile --environment local --namespace local -f deploy/helmfile.yaml apply + push: never + + - name: Check out the repo + uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + + - name: Download Karate JAR + run: | + curl -L https://github.com/karatelabs/karate/releases/download/v1.5.1/karate-1.5.1.jar -o karate.jar + + - name: Run E2E Tests Against Local Env + run: | + java -jar karate.jar . + env: + "API_ROOT_URL": "http://localhost:30090/api/to-dos-api" + "SHOULD_USE_FAKE_EXTERNAL_DEPENDENCIES": "false" + + # e2e-karate-tests-in-docker-compose: + # runs-on: ubuntu-24.04 + # steps: + # - uses: actions/checkout@v4 + # - name: Run service via docker-compose and run Karate-tests + # run: | + # # The option --exit-code-from ensures that the command's exit code exactly matches the exit code of the specified service. + # docker compose --profile MockForPullRequest up --exit-code-from inner-circle-items-api-karate-tests From 6e18899945e2c9bc47f2f4e3db61b7ed4a8bfd21 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Tue, 24 Mar 2026 14:15:20 +0500 Subject: [PATCH 03/91] ci(tests): #14: add a push event to the workflow so that the workflow can be debugged without opening a pull request --- .github/workflows/e2e-tests-on-pull-request.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/e2e-tests-on-pull-request.yml b/.github/workflows/e2e-tests-on-pull-request.yml index 9b35bb6..51ea94f 100644 --- a/.github/workflows/e2e-tests-on-pull-request.yml +++ b/.github/workflows/e2e-tests-on-pull-request.yml @@ -1,8 +1,13 @@ name: E2E Tests in PR on: - pull_request: - types: [opened, synchronize, reopened] + # pull_request: + # types: [opened, synchronize, reopened] + # The `push` event has been added for debugging purposes. After completing work + # on the workflow, you must revert this change and keep only the `pull_request` event. + push: + branches: + - 'feature/*' jobs: # this is needed to wait for the new docker image to be build and published to the registry From 23639db4488b325fd5c0765952395c16487b85ca Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Tue, 24 Mar 2026 14:36:19 +0500 Subject: [PATCH 04/91] ci(workflows): #14: add the dockerfile required to run the workflow --- Dockerfile | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..b17ce30 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,49 @@ +FROM ubuntu:22.04 AS base + +# use root to be able to use privilidged port 80 +# https://github.com/dotnet/aspnetcore/discussions/53015 +USER root +WORKDIR /app +EXPOSE 80 + +# pip is installed here because it needs to be available in both the build and final stages +RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ + && apt-get -y install --no-install-recommends pip + +FROM base AS build + +WORKDIR /src +COPY . . + +RUN apt-get -y install --no-install-recommends \ + build-essential clang lld make cmake ninja-build gdb \ + && apt-get autoremove -y \ + && apt-get clean -y \ + && rm -rf /var/lib/apt/lists/* + +RUN pip install conan + +# this is necessary so that Conan can see the local dependency recipes +RUN conan remote add local-recipes ./deps --type=local-recipes-index + +RUN conan build . --build=missing \ + --profile:all=.devcontainer/to-dos-conan-profile.conf \ + # This is necessary because, by default, the `build_type` property in the profile is set to `Debug` + --settings:host="build_type=Release" + +FROM base AS final + +# alembic, psycopg2-binary and sqlalchemy-utils are used for performing migrations +RUN pip install alembic psycopg2-binary sqlalchemy-utils + +WORKDIR /app + +# alembic needs this to apply the migrations correctly +COPY --from=build /src/src/data/create_db.py ./alembic/ +COPY --from=build /src/src/data/alembic.ini ./alembic/ +COPY --from=build /src/src/data/migrations/ ./alembic/migrations/ +COPY --from=build /src/src/data/models/ ./alembic/models/ + +COPY --from=build /src/build/Release/to-dos-api . + +ENTRYPOINT ["./to-dos-api"] \ No newline at end of file From 8b9855dfb73c402597a7ac4f4af5a2b381362bdd Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Wed, 25 Mar 2026 14:38:20 +0500 Subject: [PATCH 05/91] ci(tests): #14: add services for karate testing to docker compose --- .../workflows/e2e-tests-on-pull-request.yml | 1 - docker-compose.yml | 61 ++++++++++++++++--- tests/e2e/KarateDockerfile | 11 ++++ 3 files changed, 65 insertions(+), 8 deletions(-) create mode 100644 tests/e2e/KarateDockerfile diff --git a/.github/workflows/e2e-tests-on-pull-request.yml b/.github/workflows/e2e-tests-on-pull-request.yml index 51ea94f..3639c5b 100644 --- a/.github/workflows/e2e-tests-on-pull-request.yml +++ b/.github/workflows/e2e-tests-on-pull-request.yml @@ -67,7 +67,6 @@ jobs: java -jar karate.jar . env: "API_ROOT_URL": "http://localhost:30090/api/to-dos-api" - "SHOULD_USE_FAKE_EXTERNAL_DEPENDENCIES": "false" # e2e-karate-tests-in-docker-compose: # runs-on: ubuntu-24.04 diff --git a/docker-compose.yml b/docker-compose.yml index 37cd437..f8fea5b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,14 +1,61 @@ -version: '3.9' services: - postgresql: - container_name: postgresql + to-dos-api-cpp-db: + container_name: to-dos-api-cpp-db image: postgres:14-alpine + profiles: + - DbOnly + - MockForPullRequest env_file: - .env - volumes: - - postgresql-data:/var/lib/postgresql/data ports: - "5432:5432" + healthcheck: + test: [ "CMD-SHELL", "pg_isready -U postgres" ] + interval: 10s + timeout: 5s + retries: 5 + networks: + - to-dos-api-cpp-network + + to-dos-api-cpp: + container_name: to-dos-api-cpp + profiles: + - MockForPullRequest + depends_on: + inner-circle-items-api-db: + condition: service_healthy + build: + dockerfile: ./Dockerfile + context: . + args: + EXCLUDE_UNIT_TESTS_FROM_BUILD: "true" + env_file: + - .env + ports: + - 6501:80 + networks: + - to-dos-api-cpp-network + + to-dos-api-cpp-karate-tests: + container_name: 'to-dos-api-cpp-karate-tests' + profiles: + - MockForPullRequest + build: + dockerfile: ./KarateDockerfile + context: ./tests/e2e + depends_on: + inner-circle-items-api: + condition: service_started + # restart twice in case of not ready db or network failures + restart: on-failure:2 + command: [ "karate", "/karate" ] + volumes: + # similar to mock-server volumes we need to support both runs: from Dev Container and from OS + - ${LOCAL_WORKSPACE_FOLDER:-.}:/karate + environment: + API_ROOT_URL: "http://to-dos-api-cpp/api" + networks: + - to-dos-api-cpp-network -volumes: - postgresql-data: +networks: + to-dos-api-cpp-network: diff --git a/tests/e2e/KarateDockerfile b/tests/e2e/KarateDockerfile new file mode 100644 index 0000000..8bae7c6 --- /dev/null +++ b/tests/e2e/KarateDockerfile @@ -0,0 +1,11 @@ +FROM eclipse-temurin:17-jre-noble + +RUN apt-get update && apt-get install -y curl + +RUN apt-get install -y unzip + +RUN curl -o /karate.jar -L 'https://github.com/intuit/karate/releases/download/v1.5.1/karate-1.5.1.jar' + +COPY ./js-utils.js . + +ENTRYPOINT ["java", "-jar", "karate.jar"] \ No newline at end of file From 0de63d00126e48d760af53afe8fc5dd9ba088c50 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Wed, 25 Mar 2026 15:57:39 +0500 Subject: [PATCH 06/91] ci(test): #14: return the test execution job within docker compose --- .../workflows/e2e-tests-on-pull-request.yml | 16 +- .../reusable-docker-build-and-push.yml | 201 ------------------ 2 files changed, 8 insertions(+), 209 deletions(-) delete mode 100644 .github/workflows/reusable-docker-build-and-push.yml diff --git a/.github/workflows/e2e-tests-on-pull-request.yml b/.github/workflows/e2e-tests-on-pull-request.yml index 3639c5b..63afc51 100644 --- a/.github/workflows/e2e-tests-on-pull-request.yml +++ b/.github/workflows/e2e-tests-on-pull-request.yml @@ -68,11 +68,11 @@ jobs: env: "API_ROOT_URL": "http://localhost:30090/api/to-dos-api" - # e2e-karate-tests-in-docker-compose: - # runs-on: ubuntu-24.04 - # steps: - # - uses: actions/checkout@v4 - # - name: Run service via docker-compose and run Karate-tests - # run: | - # # The option --exit-code-from ensures that the command's exit code exactly matches the exit code of the specified service. - # docker compose --profile MockForPullRequest up --exit-code-from inner-circle-items-api-karate-tests + e2e-karate-tests-in-docker-compose: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + - name: Run service via docker-compose and run Karate-tests + run: | + # The option --exit-code-from ensures that the command's exit code exactly matches the exit code of the specified service. + docker compose --profile MockForPullRequest up --exit-code-from to-dos-api-cpp-karate-tests diff --git a/.github/workflows/reusable-docker-build-and-push.yml b/.github/workflows/reusable-docker-build-and-push.yml deleted file mode 100644 index 851d641..0000000 --- a/.github/workflows/reusable-docker-build-and-push.yml +++ /dev/null @@ -1,201 +0,0 @@ -name: Publish Docker image - -# !!! NEVER add on push when there is on workflow_call -# if you do that the workflow can run multiple times -# for instance if you re-use this docker build workflow for prod deployment and for local-env in PR -# it will build the docker image it twice -# if you build => deploy => run e2e against prod it will build the image 3 times! -on: - # to allow to wait for a docker image to be published to proceed in another workflow - # workflow_call: - # PR-based activation has been added, as there are no workflows that trigger this. - # After adding a workflow with tests, you must revert the change and leave only the `workflow_call`. - pull_request: - types: [opened, synchronize] - -jobs: - build-amd64: - runs-on: ubuntu-24.04 - steps: - - name: Check out the repo - uses: actions/checkout@v4 - - # this is needed to address this issue according to the comment https://github.com/devcontainers/ci/issues/271#issuecomment-2301764487 - # otherwise our TourmalineCore org name cannot be used in docker image names, only tourmalinecore - - name: Add Registry Image Env Var With Lowercase Organization and Repo Name - run: | - echo "REGISTRY_IMAGE=ghcr.io/${GITHUB_REPOSITORY,,}" >>${GITHUB_ENV} - - - name: Prepare - run: | - platform=linux/amd64 - echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV - - - name: Docker meta - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.REGISTRY_IMAGE }} - - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Build and push by digest - id: build - uses: docker/build-push-action@v6 - with: - platforms: linux/amd64 - context: . - file: ./Dockerfile - build-args: | - EXCLUDE_UNIT_TESTS_FROM_BUILD=true - labels: ${{ steps.meta.outputs.labels }} - tags: ${{ env.REGISTRY_IMAGE }} - outputs: type=image,push-by-digest=true,name-canonical=true,push=true - - - name: Export digest - run: | - mkdir -p ${{ runner.temp }}/digests - digest="${{ steps.build.outputs.digest }}" - touch "${{ runner.temp }}/digests/${digest#sha256:}" - - - name: Upload digest - uses: actions/upload-artifact@v4 - with: - name: digests-${{ env.PLATFORM_PAIR }} - path: ${{ runner.temp }}/digests/* - if-no-files-found: error - retention-days: 1 - - build-arm64: - runs-on: ubuntu-24.04-arm - steps: - - name: Check out the repo - uses: actions/checkout@v4 - - # this is needed to address this issue according to the comment https://github.com/devcontainers/ci/issues/271#issuecomment-2301764487 - # otherwise our TourmalineCore org name cannot be used in docker image names, only tourmalinecore - - name: Add Registry Image Env Var With Lowercase Organization and Repo Name - run: | - echo "REGISTRY_IMAGE=ghcr.io/${GITHUB_REPOSITORY,,}" >>${GITHUB_ENV} - - - name: Prepare - run: | - platform=linux/arm64 - echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV - - - name: Docker meta - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.REGISTRY_IMAGE }} - - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Build and push by digest - id: build - uses: docker/build-push-action@v6 - with: - platforms: linux/arm64 - context: . - file: ./Dockerfile - build-args: | - EXCLUDE_UNIT_TESTS_FROM_BUILD=true - labels: ${{ steps.meta.outputs.labels }} - tags: ${{ env.REGISTRY_IMAGE }} - outputs: type=image,push-by-digest=true,name-canonical=true,push=true - - - name: Export digest - run: | - mkdir -p ${{ runner.temp }}/digests - digest="${{ steps.build.outputs.digest }}" - touch "${{ runner.temp }}/digests/${digest#sha256:}" - - - name: Upload digest - uses: actions/upload-artifact@v4 - with: - name: digests-${{ env.PLATFORM_PAIR }} - path: ${{ runner.temp }}/digests/* - if-no-files-found: error - retention-days: 1 - merge: - runs-on: ubuntu-24.04 - needs: - - build-amd64 - - build-arm64 - steps: - # this is needed to address this issue according to the comment https://github.com/devcontainers/ci/issues/271#issuecomment-2301764487 - # otherwise our TourmalineCore org name cannot be used in docker image names, only tourmalinecore - - name: Add Registry Image Env Var With Lowercase Organization and Repo Name - run: | - echo "REGISTRY_IMAGE=ghcr.io/${GITHUB_REPOSITORY,,}" >>${GITHUB_ENV} - - - name: Download digests - uses: actions/download-artifact@v4 - with: - path: ${{ runner.temp }}/digests - pattern: digests-* - merge-multiple: true - - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Check out the repo - uses: actions/checkout@v4 - - - name: Add SEMVER_VERSION Env Var with Value from __version File - run: | - echo "SEMVER_VERSION=$(cat __version)" >>${GITHUB_ENV} - - - name: Extract metadata (tags, labels) for Docker - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ env.REGISTRY_IMAGE }} - tags: | - # minimal (short sha) - type=sha - # full length sha - type=sha,format=long - # SemVer human readable version - type=raw,value=${{ env.SEMVER_VERSION }} - # set latest tag for default branch - # https://github.com/docker/metadata-action/issues/171 explains how to tag latest only on default branch - type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} - env: - # https://github.com/docker/metadata-action/issues/283 - # without this flag it won't tag the image using the commit SHA - # for non push events like pull_request ones it requires this :( - DOCKER_METADATA_PR_HEAD_SHA: true - - - name: Create manifest list and push - working-directory: ${{ runner.temp }}/digests - run: | - docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ - $(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *) - - - name: Inspect image - run: | - docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }} From 2881d8f0b76f14a8b89d8c6f3454eb7ef46dc30a Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Wed, 25 Mar 2026 16:15:35 +0500 Subject: [PATCH 07/91] fix(tests): #14: correct the service name in the dependencies of the to-dos-api-cpp service in docker compose --- docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index f8fea5b..130bcca 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -22,7 +22,7 @@ services: profiles: - MockForPullRequest depends_on: - inner-circle-items-api-db: + to-dos-api-cpp-db: condition: service_healthy build: dockerfile: ./Dockerfile @@ -44,7 +44,7 @@ services: dockerfile: ./KarateDockerfile context: ./tests/e2e depends_on: - inner-circle-items-api: + to-dos-api-cpp: condition: service_started # restart twice in case of not ready db or network failures restart: on-failure:2 From b052a6d929b70aa8d87d80e9e7d9c2b04445ed03 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Wed, 25 Mar 2026 16:36:32 +0500 Subject: [PATCH 08/91] fix(tests): #14: remove of redundant environment variable definitions from the to-dos-api-cpp service --- docker-compose.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 130bcca..d7fe08c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -29,8 +29,6 @@ services: context: . args: EXCLUDE_UNIT_TESTS_FROM_BUILD: "true" - env_file: - - .env ports: - 6501:80 networks: From ec312001de5e20899054dc185a65b92868657790 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Wed, 25 Mar 2026 16:49:53 +0500 Subject: [PATCH 09/91] fix(tests): #14: add a step to create and populate the .env file so that the services in docker compose can run --- .github/workflows/e2e-tests-on-pull-request.yml | 12 +++++++++++- docker-compose.yml | 2 ++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/e2e-tests-on-pull-request.yml b/.github/workflows/e2e-tests-on-pull-request.yml index 63afc51..573c167 100644 --- a/.github/workflows/e2e-tests-on-pull-request.yml +++ b/.github/workflows/e2e-tests-on-pull-request.yml @@ -71,7 +71,17 @@ jobs: e2e-karate-tests-in-docker-compose: runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - name: Checkout to repo + uses: actions/checkout@v4 + + - name: Create .env file + run: | + echo "POSTGES_HOST=0.0.0.0" >> .env + echo "POSTGRES_PORT=5432" >> .env + echo "POSTGRES_DB=to-dos-api-cpp-db" >> .env + echo "POSTGRES_USER=postgres" >> .env + echo "POSTGRES_PASSWORD=password" >> .env + - name: Run service via docker-compose and run Karate-tests run: | # The option --exit-code-from ensures that the command's exit code exactly matches the exit code of the specified service. diff --git a/docker-compose.yml b/docker-compose.yml index d7fe08c..130bcca 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -29,6 +29,8 @@ services: context: . args: EXCLUDE_UNIT_TESTS_FROM_BUILD: "true" + env_file: + - .env ports: - 6501:80 networks: From 278c16dd8f4d126557cc41d8bccc9db7183d60c2 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Wed, 25 Mar 2026 16:53:31 +0500 Subject: [PATCH 10/91] fix(tests): #14: commenting out a temporarily redundant file --- tests/e2e/KarateDockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/KarateDockerfile b/tests/e2e/KarateDockerfile index 8bae7c6..1752a55 100644 --- a/tests/e2e/KarateDockerfile +++ b/tests/e2e/KarateDockerfile @@ -6,6 +6,6 @@ RUN apt-get install -y unzip RUN curl -o /karate.jar -L 'https://github.com/intuit/karate/releases/download/v1.5.1/karate-1.5.1.jar' -COPY ./js-utils.js . +# COPY ./js-utils.js . ENTRYPOINT ["java", "-jar", "karate.jar"] \ No newline at end of file From accc8b9136df3414e7c4b819184a87217054703d Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Thu, 26 Mar 2026 10:43:58 +0500 Subject: [PATCH 11/91] feat(controllers): #14: change the logic for adding a new task by returning the ID of the created task, to make the API's behavior more explicit and to simplify testing --- src/controllers/app-controller.cpp | 21 +++++++++++---------- src/data/commands/todo-commands.cpp | 2 +- src/services/to-dos.service.cpp | 6 ++++-- src/services/to-dos.service.h | 2 +- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/controllers/app-controller.cpp b/src/controllers/app-controller.cpp index b926954..b9fdf74 100644 --- a/src/controllers/app-controller.cpp +++ b/src/controllers/app-controller.cpp @@ -48,22 +48,23 @@ void AppController::addToDo(const HttpRequestPtr& req, std::functionisMember("name")) { - Json::Value result; - result["status"] = "error"; - result["message"] = "Invalid JSON"; + jsonResponse["status"] = "error"; + jsonResponse["message"] = "Invalid JSON"; - auto resp = HttpResponse::newHttpJsonResponse(result); - resp->setStatusCode(k400BadRequest); - callback(resp); + auto response = HttpResponse::newHttpJsonResponse(jsonResponse); + response->setStatusCode(k400BadRequest); + callback(response); return; } - todo_service_->addToDo(json->get("name", "").asString()); + std::uint64_t todoId = todo_service_->addToDo(json->get("name", "").asString()); - auto resp = HttpResponse::newHttpResponse(); - resp->setStatusCode(k201Created); - callback(resp); + jsonResponse["todoId"] = todoId; + + auto response = HttpResponse::newHttpJsonResponse(jsonResponse); + response->setStatusCode(k201Created); + callback(response); } catch (const std::exception& e) { diff --git a/src/data/commands/todo-commands.cpp b/src/data/commands/todo-commands.cpp index edf2423..0a54c39 100644 --- a/src/data/commands/todo-commands.cpp +++ b/src/data/commands/todo-commands.cpp @@ -3,7 +3,7 @@ #include #include -uint64_t ToDoCommands::create_todo(const std::string& name, std::time_t createdAtUtc) +std::uint64_t ToDoCommands::create_todo(const std::string& name, std::time_t createdAtUtc) { ToDo todo(name, createdAtUtc); diff --git a/src/services/to-dos.service.cpp b/src/services/to-dos.service.cpp index fb292ec..ded2d7f 100644 --- a/src/services/to-dos.service.cpp +++ b/src/services/to-dos.service.cpp @@ -22,10 +22,12 @@ static vector mapToDTOs(const std::shared_ptr>& todos return out; } -void ToDoService::addToDo(const string name) +std::uint64_t ToDoService::addToDo(const string name) { const std::time_t now_utc = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); - (void) _commands.create_todo(name, now_utc); + std::uint64_t todoId = _commands.create_todo(name, now_utc); + + return todoId; } // TODO(https://github.com/TourmalineCore/to-dos-api-cpp/issues/38): add here a check for not found todos diff --git a/src/services/to-dos.service.h b/src/services/to-dos.service.h index 1f836e0..586b054 100644 --- a/src/services/to-dos.service.h +++ b/src/services/to-dos.service.h @@ -20,7 +20,7 @@ class ToDoService _commands(commands) {} - void addToDo(const std::string description); + std::uint64_t addToDo(const std::string description); bool completeToDo(int id); bool deleteToDo(int id); const Json::Value getToDos() const; From 6e864f1e5ca6ed353cdcea10a7c7f63fe3015928 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Thu, 26 Mar 2026 10:45:08 +0500 Subject: [PATCH 12/91] test(e2e): #14: add a test that verifies the functionality for creating a new task --- tests/e2e/item-types-happy-path.feature | 68 +++++++++++++++++++++++++ tests/e2e/js-utils.js | 54 ++++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 tests/e2e/item-types-happy-path.feature create mode 100644 tests/e2e/js-utils.js diff --git a/tests/e2e/item-types-happy-path.feature b/tests/e2e/item-types-happy-path.feature new file mode 100644 index 0000000..4057775 --- /dev/null +++ b/tests/e2e/item-types-happy-path.feature @@ -0,0 +1,68 @@ +Feature: Item Types + # https://github.com/karatelabs/karate/issues/1191 + # https://github.com/karatelabs/karate?tab=readme-ov-file#karate-fork + + Background: + * header Content-Type = 'application/json' + + Scenario: Happy Path + + * def jsUtils = read('./js-utils.js') + * def apiRootUrl = jsUtils().getEnvVariable('API_ROOT_URL') + + // # Authentication + // Given url authApiRootUrl + // And path '/login' + // And request + // """ + // { + // "login": "#(authLogin)", + // "password": "#(authPassword)" + // } + // """ + // And method POST + // Then status 200 + + // * def accessToken = karate.toMap(response.accessToken.value) + + // * configure headers = jsUtils().getAuthHeaders(accessToken) + + # Step 1: Create a new todo + * def randomName = '[API-E2E]-test-todo-' + Math.random() + + Given url apiRootUrl + Given path 'to-dos' + And request + """ + { + "name": "#(randomName)" + } + """ + When method POST + Then status 201 + + * def todoId = response.todoId + + // # Step 2: Verify that item type is in the list with the id and generated name + // Given url apiRootUrl + // Given path 'item-types' + // When method GET + // And match response.itemTypes contains + // """ + // { + // "id": "#(newItemTypeId)", + // "name": "#(randomName)" + // } + // """ + + // # Cleanup: Delete the item type (hard delete) + // Given path 'item-types', newItemTypeId, 'hard-delete' + // When method DELETE + // Then status 200 + // And match response == { isDeleted: true } + + // # Cleanup Verification: Verify that item type was deleted + // Given url apiRootUrl + // Given path 'item-types' + // When method GET + // And assert response.itemTypes.filter(x => x.id == newItemTypeId).length == 0 diff --git a/tests/e2e/js-utils.js b/tests/e2e/js-utils.js new file mode 100644 index 0000000..89eff13 --- /dev/null +++ b/tests/e2e/js-utils.js @@ -0,0 +1,54 @@ +function fn() { + return { + // getAuthHeaders: function (tokenValue) { + // return { + // [this.getAuthHeaderKey()]: this.getAuthHeaderValue(tokenValue) + // } + // }, + + // getAuthHeaderKey: function () { + // return this.shouldUseFakeExternalDependencies() + // ? 'X-DEBUG-TOKEN' + // : 'Authorization'; + // }, + + // getAuthHeaderValue: function (tokenValue) { + // return this.shouldUseFakeExternalDependencies() + // ? tokenValue + // : 'Bearer ' + tokenValue; + // }, + + // shouldUseFakeExternalDependencies: function () { + // return this.getEnvVariable('SHOULD_USE_FAKE_EXTERNAL_DEPENDENCIES') === 'true'; + // }, + + getEnvVariable: function (variable) { + var System = Java.type('java.lang.System'); + + return System.getenv(variable); + }, + + // getEmployeeIdFromToken: function (tokenValue) { + // var decodedString; + + // if (tokenValue.includes('.')) { + // var payload = tokenValue.split('.')[1]; + + // decodedString = decodeString(payload); + // } else { + // decodedString = decodeString(tokenValue); + // } + + // var tokenData = JSON.parse(decodedString); + + // return Number(tokenData.employeeId); + // }, + } + + // function decodeString(string) { + // var Bytes = Java.type('java.util.Base64'); + // var decodedBytes = Bytes.getDecoder().decode(string); + + // return new java.lang.String(decodedBytes); + // } +} \ No newline at end of file From 94d3cd6e58c6d3a597783af865b9fa52a3008239 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Thu, 26 Mar 2026 11:15:14 +0500 Subject: [PATCH 13/91] fix(tests): #14: correct the comments, otherwise the karate parser won't be able to read the test --- tests/e2e/item-types-happy-path.feature | 70 ++++++++++++------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/tests/e2e/item-types-happy-path.feature b/tests/e2e/item-types-happy-path.feature index 4057775..99da0f7 100644 --- a/tests/e2e/item-types-happy-path.feature +++ b/tests/e2e/item-types-happy-path.feature @@ -10,22 +10,22 @@ Feature: Item Types * def jsUtils = read('./js-utils.js') * def apiRootUrl = jsUtils().getEnvVariable('API_ROOT_URL') - // # Authentication - // Given url authApiRootUrl - // And path '/login' - // And request - // """ - // { - // "login": "#(authLogin)", - // "password": "#(authPassword)" - // } - // """ - // And method POST - // Then status 200 + # # Authentication + # Given url authApiRootUrl + # And path '/login' + # And request + # """ + # { + # "login": "#(authLogin)", + # "password": "#(authPassword)" + # } + # """ + # And method POST + # Then status 200 - // * def accessToken = karate.toMap(response.accessToken.value) + # * def accessToken = karate.toMap(response.accessToken.value) - // * configure headers = jsUtils().getAuthHeaders(accessToken) + # * configure headers = jsUtils().getAuthHeaders(accessToken) # Step 1: Create a new todo * def randomName = '[API-E2E]-test-todo-' + Math.random() @@ -43,26 +43,26 @@ Feature: Item Types * def todoId = response.todoId - // # Step 2: Verify that item type is in the list with the id and generated name - // Given url apiRootUrl - // Given path 'item-types' - // When method GET - // And match response.itemTypes contains - // """ - // { - // "id": "#(newItemTypeId)", - // "name": "#(randomName)" - // } - // """ + # # Step 2: Verify that item type is in the list with the id and generated name + # Given url apiRootUrl + # Given path 'item-types' + # When method GET + # And match response.itemTypes contains + # """ + # { + # "id": "#(newItemTypeId)", + # "name": "#(randomName)" + # } + # """ - // # Cleanup: Delete the item type (hard delete) - // Given path 'item-types', newItemTypeId, 'hard-delete' - // When method DELETE - // Then status 200 - // And match response == { isDeleted: true } + # # Cleanup: Delete the item type (hard delete) + # Given path 'item-types', newItemTypeId, 'hard-delete' + # When method DELETE + # Then status 200 + # And match response == { isDeleted: true } - // # Cleanup Verification: Verify that item type was deleted - // Given url apiRootUrl - // Given path 'item-types' - // When method GET - // And assert response.itemTypes.filter(x => x.id == newItemTypeId).length == 0 + # # Cleanup Verification: Verify that item type was deleted + # Given url apiRootUrl + # Given path 'item-types' + # When method GET + # And assert response.itemTypes.filter(x => x.id == newItemTypeId).length == 0 From 70f6f0c31457bd79e0719d7049bc8a1402adb79e Mon Sep 17 00:00:00 2001 From: aasheptunov Date: Thu, 26 Mar 2026 21:12:30 -0700 Subject: [PATCH 14/91] fix(tests): #14: correct the port value for the root path within the cluster --- .github/workflows/e2e-tests-on-pull-request.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/e2e-tests-on-pull-request.yml b/.github/workflows/e2e-tests-on-pull-request.yml index 573c167..9b71532 100644 --- a/.github/workflows/e2e-tests-on-pull-request.yml +++ b/.github/workflows/e2e-tests-on-pull-request.yml @@ -66,7 +66,7 @@ jobs: run: | java -jar karate.jar . env: - "API_ROOT_URL": "http://localhost:30090/api/to-dos-api" + "API_ROOT_URL": "http://localhost:30080/api/to-dos-api" e2e-karate-tests-in-docker-compose: runs-on: ubuntu-24.04 From 21a4b4f1ffa509d0dbe916c664e423484abd89d4 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov Date: Sun, 29 Mar 2026 10:27:03 +0500 Subject: [PATCH 15/91] chore(git): #14: update the .gitignore file so that the .DS_Store Finder's service file isn't included in the commit history; formatting corrections --- .gitignore | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index cdaae0b..4873f56 100644 --- a/.gitignore +++ b/.gitignore @@ -37,11 +37,13 @@ *.out *.app -# debug information files +# Debug information files *.dwo # Build files - /build __pycache__ .env + +# Service files +.DS_Store From 0fafa61870c5c2e7495b567925f93c951f4141e4 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov Date: Sun, 29 Mar 2026 11:39:09 +0500 Subject: [PATCH 16/91] chore(makefile): #14: updating the name of the run-tidy target to something more descriptive --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index b833987..433845d 100644 --- a/Makefile +++ b/Makefile @@ -19,5 +19,5 @@ run: apply-migrations ./build/Debug/to-dos-api # Run clang-tidy static analysis -run-tidy: +run-code-analysis: @run-clang-tidy -p build/Debug From 210436eb8413f6988c9186d4dd5266965ca0da54 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov Date: Sun, 29 Mar 2026 21:21:40 +0500 Subject: [PATCH 17/91] chore(devcontainer): #14: merging duplicate configurations --- .devcontainer/devcontainer.json | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index bb6cda0..43a3f20 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -17,7 +17,9 @@ // This is necessary to avoid having to manually export environment variables from // the .env file in every terminal session. "--env-file", - "${localWorkspaceFolder}/.env" + "${localWorkspaceFolder}/.env", + // This is necessary to ensure that Docker Compose containers are available inside the devcontainer + "--network=host" ], "customizations": { "vscode": { @@ -27,10 +29,6 @@ ] } }, - // This is necessary to ensure that Docker Compose containers are available inside the devcontainer - "runArgs": [ - "--network=host" - ], // Features to add to the dev container. More info: https://containers.dev/features. "features": { "ghcr.io/devcontainers/features/docker-outside-of-docker:1": { From 5a456d72fc8ee8935bd428457c54fd1434ae7c47 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov Date: Sun, 29 Mar 2026 21:24:24 +0500 Subject: [PATCH 18/91] chore(devcontainer): #14: add an environment variable containing the path to the working directory on the host to ensure that Karate tests run correctly in Docker Compose, both inside and outside the devcontainer --- .devcontainer/devcontainer.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 43a3f20..090b3a8 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -13,6 +13,9 @@ // "customizations": {}, // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. "remoteUser": "root", + "remoteEnv": { + "LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}" + }, "runArgs": [ // This is necessary to avoid having to manually export environment variables from // the .env file in every terminal session. From 574b7c308279f75020b531b8bb327ee656324214 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov Date: Mon, 30 Mar 2026 06:39:45 +0500 Subject: [PATCH 19/91] chore(makefile): #14: add the targets up-db, ci-apply-migrations, and ci-run-karate-test-within-docker-compose so that the pipeline instructions are executed --- Makefile | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Makefile b/Makefile index 433845d..dd9a4fa 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,11 @@ include .env export +# Development targets + +up-db: + @docker compose --profile DbOnly up -d + # Generate a new Alembic migration with autogenerate create-migration: up-db @cd ./src/data && \ @@ -21,3 +26,20 @@ run: apply-migrations # Run clang-tidy static analysis run-code-analysis: @run-clang-tidy -p build/Debug + +# TODO: Try to align with the Development targets so as not to split them +# CI/CD targets (safe to run locally too) + +# Apply all pending Alembic migrations +ci-apply-migrations: + @cd alembic/ && \ + alembic upgrade head + +# Running karate tests within Docker Compose +# The option --exit-code-from ensures that the command's exit code exactly matches the exit code of the specified service. +ci-run-karate-test-within-docker-compose: + @if docker pull ghcr.io/tourmalinecore/to-dos-api-cpp:latest 2>/dev/null; then \ + docker compose --profile MockForPullRequest up --no-build --exit-code-from to-dos-api-cpp-karate-tests; \ + else \ + docker compose --profile MockForPullRequest up --build --exit-code-from to-dos-api-cpp-karate-tests; \ + fi \ No newline at end of file From 7b137ca29e63cb7b52cbca3290d03d1bf8785922 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov Date: Mon, 30 Mar 2026 06:42:25 +0500 Subject: [PATCH 20/91] ci(workflow): #14: replace native commands with Makefile targets so that additional checks are performed during execution --- .github/workflows/e2e-tests-on-pull-request.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/e2e-tests-on-pull-request.yml b/.github/workflows/e2e-tests-on-pull-request.yml index 9b71532..32d3035 100644 --- a/.github/workflows/e2e-tests-on-pull-request.yml +++ b/.github/workflows/e2e-tests-on-pull-request.yml @@ -74,6 +74,8 @@ jobs: - name: Checkout to repo uses: actions/checkout@v4 + # If you don't create a .env file, the services in Docker Compose + # won't be able to start because they use this file. - name: Create .env file run: | echo "POSTGES_HOST=0.0.0.0" >> .env @@ -83,6 +85,4 @@ jobs: echo "POSTGRES_PASSWORD=password" >> .env - name: Run service via docker-compose and run Karate-tests - run: | - # The option --exit-code-from ensures that the command's exit code exactly matches the exit code of the specified service. - docker compose --profile MockForPullRequest up --exit-code-from to-dos-api-cpp-karate-tests + run: make ci-run-karate-test-within-docker-compose From a8fa243443496c5ec185bfd3ef48cfd1bb4fc95d Mon Sep 17 00:00:00 2001 From: Artem Sheptunov Date: Mon, 30 Mar 2026 06:43:27 +0500 Subject: [PATCH 21/91] test(tests): #14: add test cases for the other endpoints --- tests/e2e/item-types-happy-path.feature | 90 ++++++++++++------------- 1 file changed, 43 insertions(+), 47 deletions(-) diff --git a/tests/e2e/item-types-happy-path.feature b/tests/e2e/item-types-happy-path.feature index 99da0f7..49cd00f 100644 --- a/tests/e2e/item-types-happy-path.feature +++ b/tests/e2e/item-types-happy-path.feature @@ -1,6 +1,6 @@ Feature: Item Types - # https://github.com/karatelabs/karate/issues/1191 - # https://github.com/karatelabs/karate?tab=readme-ov-file#karate-fork + # https://github.com/karatelabs/karate/issues/1191 + # https://github.com/karatelabs/karate?tab=readme-ov-file#karate-fork Background: * header Content-Type = 'application/json' @@ -9,60 +9,56 @@ Feature: Item Types * def jsUtils = read('./js-utils.js') * def apiRootUrl = jsUtils().getEnvVariable('API_ROOT_URL') - - # # Authentication - # Given url authApiRootUrl - # And path '/login' - # And request - # """ - # { - # "login": "#(authLogin)", - # "password": "#(authPassword)" - # } - # """ - # And method POST - # Then status 200 - - # * def accessToken = karate.toMap(response.accessToken.value) - - # * configure headers = jsUtils().getAuthHeaders(accessToken) # Step 1: Create a new todo * def randomName = '[API-E2E]-test-todo-' + Math.random() - + Given url apiRootUrl - Given path 'to-dos' + And path 'to-dos' And request - """ - { + """ + { "name": "#(randomName)" - } - """ + } + """ When method POST Then status 201 * def todoId = response.todoId - # # Step 2: Verify that item type is in the list with the id and generated name - # Given url apiRootUrl - # Given path 'item-types' - # When method GET - # And match response.itemTypes contains - # """ - # { - # "id": "#(newItemTypeId)", - # "name": "#(randomName)" - # } - # """ + # Step 2: Verify that todo is in the list with the id and generated name + Given url apiRootUrl + And path 'to-dos' + When method GET + Then match response.toDos contains + """ + { + "id": "#(todoId)", + "name": "#(randomName)" + } + """ + + # Step 3: Complete the todo with the id (soft delete) + Given url apiRootUrl + And path 'to-dos/complete' + And request + """ + { + "toDoIds": [#(todoId)] + } + """ + When method POST + Then status 200 - # # Cleanup: Delete the item type (hard delete) - # Given path 'item-types', newItemTypeId, 'hard-delete' - # When method DELETE - # Then status 200 - # And match response == { isDeleted: true } - - # # Cleanup Verification: Verify that item type was deleted - # Given url apiRootUrl - # Given path 'item-types' - # When method GET - # And assert response.itemTypes.filter(x => x.id == newItemTypeId).length == 0 + # Step 4: Delete the todo with the id (hard delete) + Given url apiRootUrl + And path 'to-dos' + And param toDo = todoId + When method DELETE + Then status 200 + + # Step 5: Verify that todo is in the list with the id and generated name has been deleted + Given url apiRootUrl + And path 'to-dos' + When method GET + Then match response.toDos !contains { "name": "#(randomName)" } From 38d8a2f6dfc5ca84f2eecdb91e5ece30c5b58890 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov Date: Mon, 30 Mar 2026 06:46:11 +0500 Subject: [PATCH 22/91] feat(tests): #14: add pgAdmin to enable database viewing; add API status checks and use Makefile targets to execute multiple instructions at startup --- Dockerfile | 6 ++-- docker-compose.yml | 51 ++++++++++++++++++++++++++---- pgAdmin.json | 22 +++++++++++++ src/controllers/app-controller.cpp | 8 +++++ src/controllers/app-controller.h | 3 ++ 5 files changed, 82 insertions(+), 8 deletions(-) create mode 100644 pgAdmin.json diff --git a/Dockerfile b/Dockerfile index 3d19c67..a9b7779 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,7 @@ EXPOSE 80 # pip is installed here because it needs to be available in both the build and final stages RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ - && apt-get -y install --no-install-recommends pip + && apt-get -y install --no-install-recommends pip make FROM base AS build @@ -16,7 +16,7 @@ WORKDIR /src COPY . . RUN apt-get -y install --no-install-recommends \ - build-essential clang lld make cmake ninja-build gdb \ + build-essential clang lld cmake ninja-build gdb \ && apt-get autoremove -y \ && apt-get clean -y \ && rm -rf /var/lib/apt/lists/* @@ -43,6 +43,8 @@ COPY --from=build /src/src/data/alembic.ini ./alembic/ COPY --from=build /src/src/data/migrations/ ./alembic/migrations/ COPY --from=build /src/src/data/models/ ./alembic/models/ +COPY --from=build ./Makefile . + COPY --from=build /src/build/Release/to-dos-api . ENTRYPOINT ["./to-dos-api"] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 130bcca..4cd0404 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,7 +8,7 @@ services: env_file: - .env ports: - - "5432:5432" + - "7501:5432" healthcheck: test: [ "CMD-SHELL", "pg_isready -U postgres" ] interval: 10s @@ -17,6 +17,40 @@ services: networks: - to-dos-api-cpp-network + # https://event-driven.io/en/automatically_connect_pgadmin_to_database/ + to-dos-api-cpp-pgadmin: + image: dpage/pgadmin4 + container_name: to-dos-api-cpp-pgadmin + profiles: + - DbOnly + depends_on: + to-dos-api-cpp-db: + condition: service_healthy + environment: + # TODO: We need to think of a way to synchronize the .env file used for the database service. + # Perhaps we could transfer the pgAdmin environment variables to the .env file. + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_DB: to-dos-api-cpp-db + PGADMIN_DEFAULT_EMAIL: admin@admin.org + PGADMIN_DEFAULT_PASSWORD: postgres + PGADMIN_CONFIG_SERVER_MODE: 'False' + PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED: 'False' + # these 2 variables PGADMIN_CONFIG_PROXY_X_HOST_COUNT and PGADMIN_CONFIG_PROXY_X_PREFIX_COUNT + # are needed to enable PgAdmin in Codespaces scenario when it is behind a reverse proxy + # https://github.com/orgs/community/discussions/17918 + PGADMIN_CONFIG_PROXY_X_HOST_COUNT: 1 + PGADMIN_CONFIG_PROXY_X_PREFIX_COUNT: 1 + ports: + - 9501:80 + networks: + - to-dos-api-cpp-network + volumes: + # this is needed so that we can spin it up from within Dev Container where LOCAL_WORKSPACE_FOLDER is defined and from a simple OS terminal of the repo root + # list of available config properties can be found here in the source code https://github.com/pgadmin-org/pgadmin4/blob/00dbe58125f0304186f1af5374c9c43bc22d0410/web/pgadmin/utils/__init__.py#L505 + # Password is no longer a part of this list as it seems to be when the linked article was written + - ${LOCAL_WORKSPACE_FOLDER:-.}/pgAdmin.json:/pgadmin4/servers.json + to-dos-api-cpp: container_name: to-dos-api-cpp profiles: @@ -24,18 +58,25 @@ services: depends_on: to-dos-api-cpp-db: condition: service_healthy + image: ghcr.io/tourmalinecore/to-dos-api-cpp:latest build: dockerfile: ./Dockerfile context: . args: EXCLUDE_UNIT_TESTS_FROM_BUILD: "true" + entrypoint: [ "/bin/bash", "-c" ] + command: "make ci-apply-migrations && ./to-dos-api" + healthcheck: + test: [ "CMD-SHELL", "curl -f http://localhost:80/health || exit 1" ] + interval: 5s + timeout: 3s + retries: 5 env_file: - .env ports: - - 6501:80 + - 80:80 networks: - to-dos-api-cpp-network - to-dos-api-cpp-karate-tests: container_name: 'to-dos-api-cpp-karate-tests' profiles: @@ -45,9 +86,7 @@ services: context: ./tests/e2e depends_on: to-dos-api-cpp: - condition: service_started - # restart twice in case of not ready db or network failures - restart: on-failure:2 + condition: service_healthy command: [ "karate", "/karate" ] volumes: # similar to mock-server volumes we need to support both runs: from Dev Container and from OS diff --git a/pgAdmin.json b/pgAdmin.json new file mode 100644 index 0000000..5867b69 --- /dev/null +++ b/pgAdmin.json @@ -0,0 +1,22 @@ +{ + "Servers": { + "1": { + "Group": "Servers", + "Name": "to-dos-api-cpp-db (Windows, macOS)", + "Host": "host.docker.internal", + "Port": 7501, + "MaintenanceDB": "to-dos-api-cpp-db", + "Username": "postgres", + "SSLMode": "prefer" + }, + "2": { + "Group": "Servers", + "Name": "to-dos-api-cpp-db (Ubuntu, Codespaces)", + "Host": "172.17.0.1", + "Port": 7501, + "MaintenanceDB": "to-dos-api-cpp-db", + "Username": "postgres", + "SSLMode": "prefer" + } + } +} \ No newline at end of file diff --git a/src/controllers/app-controller.cpp b/src/controllers/app-controller.cpp index b9fdf74..dbd1842 100644 --- a/src/controllers/app-controller.cpp +++ b/src/controllers/app-controller.cpp @@ -130,3 +130,11 @@ void AppController::deleteToDo(const HttpRequestPtr& req, std::function&& callback) +{ + auto response = HttpResponse::newHttpResponse(); + response->setStatusCode(k200OK); + callback(response); +} diff --git a/src/controllers/app-controller.h b/src/controllers/app-controller.h index c9dc4a0..52ff68c 100644 --- a/src/controllers/app-controller.h +++ b/src/controllers/app-controller.h @@ -18,6 +18,7 @@ class AppController : public drogon::HttpController ADD_METHOD_TO(AppController::addToDo, "/api/to-dos", Post); // Adding a new task ADD_METHOD_TO(AppController::completeToDos, "/api/to-dos/complete", Post); // Executing (deleting) a task list ADD_METHOD_TO(AppController::deleteToDo, "/api/to-dos", Delete); // Deleting a specific task + ADD_METHOD_TO(AppController::apiHealthCheck, "/health", Get); // Health endpoint METHOD_LIST_END HttpResponsePtr createInternalServerErrorResponse(const std::string& error) const; @@ -30,6 +31,8 @@ class AppController : public drogon::HttpController void deleteToDo(const HttpRequestPtr& req, std::function&& callback); + void apiHealthCheck(const HttpRequestPtr& req, std::function&& callback); + private: std::shared_ptr db_; std::unique_ptr queries_; From 0eba13a782dc2fd00393bc621b28498381720a60 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Mon, 30 Mar 2026 08:34:40 +0500 Subject: [PATCH 23/91] fix(dockerfile): #14: fix an error related to a path that does not include the working directory --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index a9b7779..7db5e45 100644 --- a/Dockerfile +++ b/Dockerfile @@ -43,7 +43,7 @@ COPY --from=build /src/src/data/alembic.ini ./alembic/ COPY --from=build /src/src/data/migrations/ ./alembic/migrations/ COPY --from=build /src/src/data/models/ ./alembic/models/ -COPY --from=build ./Makefile . +COPY --from=build /src/Makefile . COPY --from=build /src/build/Release/to-dos-api . From ab9514a2d7b24c16a94de9fc81eb21661842f8c4 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Mon, 30 Mar 2026 10:13:28 +0500 Subject: [PATCH 24/91] test(tests): #14: change the data type of a field referenced in a test case --- tests/e2e/item-types-happy-path.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/item-types-happy-path.feature b/tests/e2e/item-types-happy-path.feature index 49cd00f..8915a75 100644 --- a/tests/e2e/item-types-happy-path.feature +++ b/tests/e2e/item-types-happy-path.feature @@ -33,7 +33,7 @@ Feature: Item Types Then match response.toDos contains """ { - "id": "#(todoId)", + "id": #(todoId), "name": "#(randomName)" } """ From 312c730bdb4d9063ae100afaeab7cd1d07c2c127 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Mon, 30 Mar 2026 10:14:19 +0500 Subject: [PATCH 25/91] ci(tests): #14: correct the repository link in the workflow so that the required repository is used --- .github/workflows/e2e-tests-on-pull-request.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/e2e-tests-on-pull-request.yml b/.github/workflows/e2e-tests-on-pull-request.yml index 32d3035..87ddb52 100644 --- a/.github/workflows/e2e-tests-on-pull-request.yml +++ b/.github/workflows/e2e-tests-on-pull-request.yml @@ -31,11 +31,11 @@ jobs: # Replace with the necessary image when the image to-dos-api-cpp-devcontainer is created # cacheFrom: ghcr.io/tourmalinecore/inner-circle-local-env-devcontainer runCmd: | - # we need to override "latest" image tag of ui inside local-env to run e2e against the current commit ui version and not against latest from master + # we need to override "latest" image tag of ui inside local-env to run e2e against the current commit api version and not against latest from master # We tried to use yq to change the image tag, but in the values files for helmfile we have non-yaml code that yq can`t parse or ignore # so for that reason we use Stream EDitor which can find needed string using regular expressions and change it to a new value # The -i flag is needed to write new image tag directly to values file - sed -i "0,/tag:.*/s//tag: \"sha-${{ github.event.pull_request.head.sha }}\"/" deploy/values-to-dos-api.yaml.gotmpl + sed -i "0,/tag:.*/s//tag: \"sha-${{ github.event.pull_request.head.sha }}\"/" deploy/values-to-dos-api-cpp.yaml.gotmpl # we need to override "latest" ref of service chart inside local-env to run tests against the current commit service chart version and not against latest from master sed -i "0,/git+https:\/\/github.com\/TourmalineCore\/${{ github.event.repository.name }}.git?ref=.*/s//git+https:\/\/github.com\/TourmalineCore\/${{ github.event.repository.name }}.git?ref=${{ github.event.pull_request.head.sha }}/" deploy/helmfile.yaml From 0d4bef3809955c8d73e0178265ffa129b9b01b5e Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Mon, 30 Mar 2026 10:20:23 +0500 Subject: [PATCH 26/91] test(tests): #14: add the server response output to the test case for debugging --- tests/e2e/item-types-happy-path.feature | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/e2e/item-types-happy-path.feature b/tests/e2e/item-types-happy-path.feature index 8915a75..a0752c7 100644 --- a/tests/e2e/item-types-happy-path.feature +++ b/tests/e2e/item-types-happy-path.feature @@ -24,6 +24,7 @@ Feature: Item Types When method POST Then status 201 + * print response * def todoId = response.todoId # Step 2: Verify that todo is in the list with the id and generated name From dd8d9708f510d630bda4a1ddb0b8753b3da9d30e Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Mon, 30 Mar 2026 11:17:46 +0500 Subject: [PATCH 27/91] ci(tests): #14: update the branch for the to-dos-local-env repository to use the correct API release --- .github/workflows/e2e-tests-on-pull-request.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/e2e-tests-on-pull-request.yml b/.github/workflows/e2e-tests-on-pull-request.yml index 87ddb52..c8c712e 100644 --- a/.github/workflows/e2e-tests-on-pull-request.yml +++ b/.github/workflows/e2e-tests-on-pull-request.yml @@ -24,6 +24,8 @@ jobs: uses: actions/checkout@v4 with: repository: TourmalineCore/to-dos-local-env + ref: experiment/making-the-helmfile-more-universal + - name: Deploy Local Env to Kind k8s uses: devcontainers/ci@v0.3 @@ -46,6 +48,8 @@ jobs: # we need to properly expose KUBECONFIG as an absolute path, pwd prints current working directory path export KUBECONFIG=$(pwd)/.to-dos-cluster-kubeconfig + export TO_DOS_API_REPO=to-dos-api-cpp + helmfile --environment local --namespace local -f deploy/helmfile.yaml apply push: never From d2b0eb95a81a64a5619ccc84cdbf43b33916b925 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Mon, 30 Mar 2026 11:51:49 +0500 Subject: [PATCH 28/91] chore(makefile): #14: change the condition for including the .env file to optional to avoid errors if the file is missing --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index dd9a4fa..2a772ac 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # This is necessary so that environment variables from .env # are visible when Makefile commands are executed -include .env +-include .env export # Development targets From c007557d573de07c50fcbcdf6fef568bffd5f12f Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Mon, 30 Mar 2026 11:54:06 +0500 Subject: [PATCH 29/91] ci(workflows): #14: add a workflow trigger condition to a PR for debugging --- .github/workflows/.reusable-docker-build-and-push.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/.reusable-docker-build-and-push.yml b/.github/workflows/.reusable-docker-build-and-push.yml index bb6ff42..d4ec696 100644 --- a/.github/workflows/.reusable-docker-build-and-push.yml +++ b/.github/workflows/.reusable-docker-build-and-push.yml @@ -8,6 +8,8 @@ name: Publish Docker image on: # to allow to wait for a docker image to be published to proceed in another workflow workflow_call: + pull_request: + types: [opened, synchronize, reopened] jobs: build-amd64: From 9dd4ae00d66c02ffe50a43875304362820359e64 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Mon, 30 Mar 2026 13:14:40 +0500 Subject: [PATCH 30/91] chore(dockerfile): #14: experiment with accepting migrations without using Make --- Dockerfile | 2 +- docker-compose.yml | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 7db5e45..b74cc61 100644 --- a/Dockerfile +++ b/Dockerfile @@ -43,7 +43,7 @@ COPY --from=build /src/src/data/alembic.ini ./alembic/ COPY --from=build /src/src/data/migrations/ ./alembic/migrations/ COPY --from=build /src/src/data/models/ ./alembic/models/ -COPY --from=build /src/Makefile . +# COPY --from=build /src/Makefile . COPY --from=build /src/build/Release/to-dos-api . diff --git a/docker-compose.yml b/docker-compose.yml index 4cd0404..db8e77d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -65,7 +65,8 @@ services: args: EXCLUDE_UNIT_TESTS_FROM_BUILD: "true" entrypoint: [ "/bin/bash", "-c" ] - command: "make ci-apply-migrations && ./to-dos-api" + # command: "make ci-apply-migrations && ./to-dos-api" + command: "cd /app/alembic && alembic upgrade head && cd .. && ./to-dos-api" healthcheck: test: [ "CMD-SHELL", "curl -f http://localhost:80/health || exit 1" ] interval: 5s From ee367fe12fd7fdb276ec750655a310d4b3bab9c5 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Mon, 30 Mar 2026 13:15:56 +0500 Subject: [PATCH 31/91] fix(tests): #14: correct the name of the helmfile --- .github/workflows/e2e-tests-on-pull-request.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/e2e-tests-on-pull-request.yml b/.github/workflows/e2e-tests-on-pull-request.yml index c8c712e..1c8ba07 100644 --- a/.github/workflows/e2e-tests-on-pull-request.yml +++ b/.github/workflows/e2e-tests-on-pull-request.yml @@ -50,7 +50,7 @@ jobs: export TO_DOS_API_REPO=to-dos-api-cpp - helmfile --environment local --namespace local -f deploy/helmfile.yaml apply + helmfile --environment local --namespace local -f deploy/helmfile.yaml.gotmpl apply push: never - name: Check out the repo From e85a787184f18f321c7c624609af89092c2ffe30 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Mon, 30 Mar 2026 13:54:19 +0500 Subject: [PATCH 32/91] ci(tests): #14: modify the command being called to bypass the use of `make` --- .github/workflows/e2e-tests-on-pull-request.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/e2e-tests-on-pull-request.yml b/.github/workflows/e2e-tests-on-pull-request.yml index 1c8ba07..b1b4942 100644 --- a/.github/workflows/e2e-tests-on-pull-request.yml +++ b/.github/workflows/e2e-tests-on-pull-request.yml @@ -89,4 +89,5 @@ jobs: echo "POSTGRES_PASSWORD=password" >> .env - name: Run service via docker-compose and run Karate-tests - run: make ci-run-karate-test-within-docker-compose + # run: make ci-run-karate-test-within-docker-compose + run: docker compose --profile MockForPullRequest up --build --exit-code-from to-dos-api-cpp-karate-tests From d4ec4e097b1e9abe49c4f3c8a91eb89227014628 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Mon, 30 Mar 2026 14:31:55 +0500 Subject: [PATCH 33/91] ci(tests): #14: add caching when building digests so that dependencies aren't rebuilt every time --- .github/workflows/.reusable-docker-build-and-push.yml | 4 ++++ Dockerfile | 11 ++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/.github/workflows/.reusable-docker-build-and-push.yml b/.github/workflows/.reusable-docker-build-and-push.yml index d4ec696..0c82c31 100644 --- a/.github/workflows/.reusable-docker-build-and-push.yml +++ b/.github/workflows/.reusable-docker-build-and-push.yml @@ -57,6 +57,8 @@ jobs: labels: ${{ steps.meta.outputs.labels }} tags: ${{ env.REGISTRY_IMAGE }} outputs: type=image,push-by-digest=true,name-canonical=true,push=true + cache-from: type=registry,ref=ghcr.io/tourmalinecore/to-dos-api-cpp:cache-amd64 + cache-to: type=registry,ref=ghcr.io/tourmalinecore/to-dos-api-cpp:cache-amd64,mode=max - name: Export digest run: | @@ -117,6 +119,8 @@ jobs: labels: ${{ steps.meta.outputs.labels }} tags: ${{ env.REGISTRY_IMAGE }} outputs: type=image,push-by-digest=true,name-canonical=true,push=true + cache-from: type=registry,ref=ghcr.io/tourmalinecore/to-dos-api-cpp:cache-arm64 + cache-to: type=registry,ref=ghcr.io/tourmalinecore/to-dos-api-cpp:cache-arm64,mode=max - name: Export digest run: | diff --git a/Dockerfile b/Dockerfile index b74cc61..005a13a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,7 +13,6 @@ RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ FROM base AS build WORKDIR /src -COPY . . RUN apt-get -y install --no-install-recommends \ build-essential clang lld cmake ninja-build gdb \ @@ -23,6 +22,16 @@ RUN apt-get -y install --no-install-recommends \ RUN pip install conan +COPY conanfile.py /src/ +COPY .devcontainer/to-dos-conan-profile.conf /src/.devcontainer/ + +RUN conan install . --build=missing \ + --profile:all=.devcontainer/to-dos-conan-profile.conf \ + # This is necessary because, by default, the `build_type` property in the profile is set to `Debug` + --settings:host="build_type=Release" + +COPY . . + # this is necessary so that Conan can see the local dependency recipes RUN conan remote add local-recipes ./deps --type=local-recipes-index From aea1677ef44affed7a91d69bcded2c6e45ea906b Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Mon, 30 Mar 2026 14:36:05 +0500 Subject: [PATCH 34/91] ci(tests): #14: add dependency copying before the installation and build steps --- Dockerfile | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 005a13a..08e96d0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,6 +24,10 @@ RUN pip install conan COPY conanfile.py /src/ COPY .devcontainer/to-dos-conan-profile.conf /src/.devcontainer/ +COPY deps/* /src/deps/ + +# this is necessary so that Conan can see the local dependency recipes +RUN conan remote add local-recipes ./deps --type=local-recipes-index RUN conan install . --build=missing \ --profile:all=.devcontainer/to-dos-conan-profile.conf \ @@ -32,9 +36,6 @@ RUN conan install . --build=missing \ COPY . . -# this is necessary so that Conan can see the local dependency recipes -RUN conan remote add local-recipes ./deps --type=local-recipes-index - RUN conan build . --build=missing \ --profile:all=.devcontainer/to-dos-conan-profile.conf \ # This is necessary because, by default, the `build_type` property in the profile is set to `Debug` From 2e86af48cf2bc447486ee5cd2ce60a40e65bbc80 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Mon, 30 Mar 2026 14:41:01 +0500 Subject: [PATCH 35/91] ci(tests): #14: add a debug message --- Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 08e96d0..d54b2de 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,12 +26,13 @@ COPY conanfile.py /src/ COPY .devcontainer/to-dos-conan-profile.conf /src/.devcontainer/ COPY deps/* /src/deps/ +RUN cd /src/deps && ls + # this is necessary so that Conan can see the local dependency recipes RUN conan remote add local-recipes ./deps --type=local-recipes-index RUN conan install . --build=missing \ --profile:all=.devcontainer/to-dos-conan-profile.conf \ - # This is necessary because, by default, the `build_type` property in the profile is set to `Debug` --settings:host="build_type=Release" COPY . . From 5f017eb0140115e0c01fd3422642c26a7ce07f57 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Mon, 30 Mar 2026 14:43:29 +0500 Subject: [PATCH 36/91] ci(tests): #14: add a debug message --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index d54b2de..9c3354e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,7 +26,7 @@ COPY conanfile.py /src/ COPY .devcontainer/to-dos-conan-profile.conf /src/.devcontainer/ COPY deps/* /src/deps/ -RUN cd /src/deps && ls +RUN echo "test" && cd /src/deps && ls # this is necessary so that Conan can see the local dependency recipes RUN conan remote add local-recipes ./deps --type=local-recipes-index From 224aacb6ed14d802a8be613d2853f1d0ec2f6ba6 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Mon, 30 Mar 2026 14:46:46 +0500 Subject: [PATCH 37/91] ci(tests): #14: add a debug message --- Dockerfile | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 9c3354e..be4fef8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,9 +24,7 @@ RUN pip install conan COPY conanfile.py /src/ COPY .devcontainer/to-dos-conan-profile.conf /src/.devcontainer/ -COPY deps/* /src/deps/ - -RUN echo "test" && cd /src/deps && ls +COPY deps/ /src/deps/ # this is necessary so that Conan can see the local dependency recipes RUN conan remote add local-recipes ./deps --type=local-recipes-index From 0a473e7483b7444108b18d6571ba534ee148e614 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Mon, 30 Mar 2026 15:10:11 +0500 Subject: [PATCH 38/91] ci(tests): #14: changes required to restart the workflow --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 0ec184c..0b2e8db 100644 --- a/README.md +++ b/README.md @@ -105,3 +105,5 @@ The alembic tool is used to work with migrations. To work with it, you need to m Alternatively, you can use make targets. - To create a migration, run the command `make create-migration name=`, where `name` is the name of the migration. You can also use `make create-migration`, in which case the migration will be named after the current date and time. - To apply the migrations, run the `make apply-migrations` command. + +[Test] \ No newline at end of file From 3281c437419cc4fcb9f9033a189e8cc503d80235 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Tue, 31 Mar 2026 14:07:46 +0500 Subject: [PATCH 39/91] ci(workflows): #14: add branch-based cache partitioning based on where the workflow is called from, so that the cache for different branches is overwritten --- .../.reusable-docker-build-and-push.yml | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/.github/workflows/.reusable-docker-build-and-push.yml b/.github/workflows/.reusable-docker-build-and-push.yml index 0c82c31..f87fa2b 100644 --- a/.github/workflows/.reusable-docker-build-and-push.yml +++ b/.github/workflows/.reusable-docker-build-and-push.yml @@ -23,6 +23,11 @@ jobs: - name: Add Registry Image Env Var With Lowercase Organization and Repo Name run: | echo "REGISTRY_IMAGE=ghcr.io/${GITHUB_REPOSITORY,,}" >>${GITHUB_ENV} + + - name: Add branch name env var for image tag + run: | + BRANCH_NAME="${GITHUB_REF_NAME//[^a-zA-Z0-9._-]/-}" + echo "BRANCH_NAME=${BRANCH_NAME}" >> $GITHUB_ENV - name: Prepare run: | @@ -57,8 +62,8 @@ jobs: labels: ${{ steps.meta.outputs.labels }} tags: ${{ env.REGISTRY_IMAGE }} outputs: type=image,push-by-digest=true,name-canonical=true,push=true - cache-from: type=registry,ref=ghcr.io/tourmalinecore/to-dos-api-cpp:cache-amd64 - cache-to: type=registry,ref=ghcr.io/tourmalinecore/to-dos-api-cpp:cache-amd64,mode=max + cache-from: type=registry,ref=${{ env.REGISTRY_IMAGE }}:cache-amd64-${{ env.BRANCH_NAME }} + cache-to: type=registry,ref=${{ env.REGISTRY_IMAGE }}:cache-amd64-${{ env.BRANCH_NAME }},mode=max - name: Export digest run: | @@ -85,6 +90,11 @@ jobs: - name: Add Registry Image Env Var With Lowercase Organization and Repo Name run: | echo "REGISTRY_IMAGE=ghcr.io/${GITHUB_REPOSITORY,,}" >>${GITHUB_ENV} + + - name: Add branch name env var for image tag + run: | + BRANCH_NAME="${GITHUB_REF_NAME//[^a-zA-Z0-9._-]/-}" + echo "BRANCH_NAME=${BRANCH_NAME}" >> $GITHUB_ENV - name: Prepare run: | @@ -119,8 +129,8 @@ jobs: labels: ${{ steps.meta.outputs.labels }} tags: ${{ env.REGISTRY_IMAGE }} outputs: type=image,push-by-digest=true,name-canonical=true,push=true - cache-from: type=registry,ref=ghcr.io/tourmalinecore/to-dos-api-cpp:cache-arm64 - cache-to: type=registry,ref=ghcr.io/tourmalinecore/to-dos-api-cpp:cache-arm64,mode=max + cache-from: type=registry,ref=${{ env.REGISTRY_IMAGE }}:cache-arm64-${{ env.BRANCH_NAME }} + cache-to: type=registry,ref=${{ env.REGISTRY_IMAGE }}:cache-arm64-${{ env.BRANCH_NAME }},mode=max - name: Export digest run: | From f8efa2ebb021ab8b61f843478bb0a2591bcc1b71 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Tue, 31 Mar 2026 14:08:27 +0500 Subject: [PATCH 40/91] docs: #14: add clarifying comments to the Dockerfile --- Dockerfile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index be4fef8..95dc485 100644 --- a/Dockerfile +++ b/Dockerfile @@ -22,6 +22,7 @@ RUN apt-get -y install --no-install-recommends \ RUN pip install conan +# this is necessary so that Conan can install the dependencies COPY conanfile.py /src/ COPY .devcontainer/to-dos-conan-profile.conf /src/.devcontainer/ COPY deps/ /src/deps/ @@ -31,13 +32,16 @@ RUN conan remote add local-recipes ./deps --type=local-recipes-index RUN conan install . --build=missing \ --profile:all=.devcontainer/to-dos-conan-profile.conf \ + # this is necessary because, by default, the `build_type` property in the profile is set to `Debug` --settings:host="build_type=Release" +# We cannot copy all the content before running `conan install`, because +# otherwise the image layers cannot be cached in the pipeline COPY . . RUN conan build . --build=missing \ --profile:all=.devcontainer/to-dos-conan-profile.conf \ - # This is necessary because, by default, the `build_type` property in the profile is set to `Debug` + # this is necessary because, by default, the `build_type` property in the profile is set to `Debug` --settings:host="build_type=Release" FROM base AS final From 77331b7d197deb450cedf35cbc254e91b42798f2 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Tue, 31 Mar 2026 14:30:15 +0500 Subject: [PATCH 41/91] ci(tests): #14: update the to-dos-local-env repository branch; update the paths to helmfile.yaml.gotmpl; add the use of Makefile targets --- .github/workflows/e2e-tests-on-pull-request.yml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/.github/workflows/e2e-tests-on-pull-request.yml b/.github/workflows/e2e-tests-on-pull-request.yml index b1b4942..3e27c21 100644 --- a/.github/workflows/e2e-tests-on-pull-request.yml +++ b/.github/workflows/e2e-tests-on-pull-request.yml @@ -24,14 +24,12 @@ jobs: uses: actions/checkout@v4 with: repository: TourmalineCore/to-dos-local-env - ref: experiment/making-the-helmfile-more-universal + ref: feature/add-to-dos-api-cpp-release - name: Deploy Local Env to Kind k8s uses: devcontainers/ci@v0.3 with: - # Replace with the necessary image when the image to-dos-api-cpp-devcontainer is created - # cacheFrom: ghcr.io/tourmalinecore/inner-circle-local-env-devcontainer runCmd: | # we need to override "latest" image tag of ui inside local-env to run e2e against the current commit api version and not against latest from master # We tried to use yq to change the image tag, but in the values files for helmfile we have non-yaml code that yq can`t parse or ignore @@ -40,17 +38,15 @@ jobs: sed -i "0,/tag:.*/s//tag: \"sha-${{ github.event.pull_request.head.sha }}\"/" deploy/values-to-dos-api-cpp.yaml.gotmpl # we need to override "latest" ref of service chart inside local-env to run tests against the current commit service chart version and not against latest from master - sed -i "0,/git+https:\/\/github.com\/TourmalineCore\/${{ github.event.repository.name }}.git?ref=.*/s//git+https:\/\/github.com\/TourmalineCore\/${{ github.event.repository.name }}.git?ref=${{ github.event.pull_request.head.sha }}/" deploy/helmfile.yaml + sed -i "0,/git+https:\/\/github.com\/TourmalineCore\/${{ github.event.repository.name }}.git?ref=.*/s//git+https:\/\/github.com\/TourmalineCore\/${{ github.event.repository.name }}.git?ref=${{ github.event.pull_request.head.sha }}/" deploy/helmfile.yaml.gotmpl - sed -i "0,/git::https:\/\/github.com\/TourmalineCore\/${{ github.event.repository.name }}.git@\/ci\/values.yaml?ref=.*/s//git::https:\/\/github.com\/TourmalineCore\/${{ github.event.repository.name }}.git@\/ci\/values.yaml?ref=${{ github.event.pull_request.head.sha }}/" deploy/helmfile.yaml + sed -i "0,/git::https:\/\/github.com\/TourmalineCore\/${{ github.event.repository.name }}.git@\/ci\/values.yaml?ref=.*/s//git::https:\/\/github.com\/TourmalineCore\/${{ github.event.repository.name }}.git@\/ci\/values.yaml?ref=${{ github.event.pull_request.head.sha }}/" deploy/helmfile.yaml.gotmpl kind create cluster --name to-dos --config kind-local-config.yaml --kubeconfig ./.to-dos-cluster-kubeconfig # we need to properly expose KUBECONFIG as an absolute path, pwd prints current working directory path export KUBECONFIG=$(pwd)/.to-dos-cluster-kubeconfig - export TO_DOS_API_REPO=to-dos-api-cpp - - helmfile --environment local --namespace local -f deploy/helmfile.yaml.gotmpl apply + make apply-releases-with-cpp-api push: never - name: Check out the repo From 11a8c8004ef85d6c952e93b737977ebfb2c0a38d Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Tue, 31 Mar 2026 14:50:47 +0500 Subject: [PATCH 42/91] ci(tests): #14: restore the workflow activation conditions on the PR --- .github/workflows/e2e-tests-on-pull-request.yml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/workflows/e2e-tests-on-pull-request.yml b/.github/workflows/e2e-tests-on-pull-request.yml index 3e27c21..cd20ab7 100644 --- a/.github/workflows/e2e-tests-on-pull-request.yml +++ b/.github/workflows/e2e-tests-on-pull-request.yml @@ -1,13 +1,8 @@ name: E2E Tests in PR on: - # pull_request: - # types: [opened, synchronize, reopened] - # The `push` event has been added for debugging purposes. After completing work - # on the workflow, you must revert this change and keep only the `pull_request` event. - push: - branches: - - 'feature/*' + pull_request: + types: [opened, synchronize, reopened] jobs: # this is needed to wait for the new docker image to be build and published to the registry From ea3c8a17851cff8981e7be0ccfe1bc4909211b54 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Tue, 31 Mar 2026 14:52:55 +0500 Subject: [PATCH 43/91] ci(workflows): #14: remove the debug call for image building from the PR --- .github/workflows/.reusable-docker-build-and-push.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/.reusable-docker-build-and-push.yml b/.github/workflows/.reusable-docker-build-and-push.yml index f87fa2b..3ba5b24 100644 --- a/.github/workflows/.reusable-docker-build-and-push.yml +++ b/.github/workflows/.reusable-docker-build-and-push.yml @@ -8,8 +8,6 @@ name: Publish Docker image on: # to allow to wait for a docker image to be published to proceed in another workflow workflow_call: - pull_request: - types: [opened, synchronize, reopened] jobs: build-amd64: From 606a8ad5c6359fef7ee0d6ca631a5f3351db4afd Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Tue, 31 Mar 2026 15:31:31 +0500 Subject: [PATCH 44/91] fix(tests): #14: correct the parameter name, as the previous one did not exist --- .../workflows/e2e-tests-on-pull-request.yml | 10 +++-- Dockerfile | 2 - Makefile | 28 ++----------- docker-compose.yml | 39 +++++++++---------- src/controllers/app-controller.cpp | 1 + tests/e2e/item-types-happy-path.feature | 2 +- 6 files changed, 31 insertions(+), 51 deletions(-) diff --git a/.github/workflows/e2e-tests-on-pull-request.yml b/.github/workflows/e2e-tests-on-pull-request.yml index cd20ab7..6a14872 100644 --- a/.github/workflows/e2e-tests-on-pull-request.yml +++ b/.github/workflows/e2e-tests-on-pull-request.yml @@ -65,6 +65,7 @@ jobs: e2e-karate-tests-in-docker-compose: runs-on: ubuntu-24.04 + needs: [docker-build-and-push] steps: - name: Checkout to repo uses: actions/checkout@v4 @@ -73,12 +74,15 @@ jobs: # won't be able to start because they use this file. - name: Create .env file run: | - echo "POSTGES_HOST=0.0.0.0" >> .env + echo "API_HOST=0.0.0.0" >> .env + echo "API_PORT=80" >> .env + echo "API_LOG_LEVEL=INFO" >> .env + echo "API_NUMBER_OF_THREADS=1" >> .env + echo "POSTGRES_HOST=to-dos-api-cpp-db" >> .env echo "POSTGRES_PORT=5432" >> .env echo "POSTGRES_DB=to-dos-api-cpp-db" >> .env echo "POSTGRES_USER=postgres" >> .env echo "POSTGRES_PASSWORD=password" >> .env - name: Run service via docker-compose and run Karate-tests - # run: make ci-run-karate-test-within-docker-compose - run: docker compose --profile MockForPullRequest up --build --exit-code-from to-dos-api-cpp-karate-tests + run: docker compose --profile MockForPullRequest up --exit-code-from to-dos-api-cpp-karate-tests diff --git a/Dockerfile b/Dockerfile index 95dc485..2a0d533 100644 --- a/Dockerfile +++ b/Dockerfile @@ -56,8 +56,6 @@ COPY --from=build /src/src/data/alembic.ini ./alembic/ COPY --from=build /src/src/data/migrations/ ./alembic/migrations/ COPY --from=build /src/src/data/models/ ./alembic/models/ -# COPY --from=build /src/Makefile . - COPY --from=build /src/build/Release/to-dos-api . ENTRYPOINT ["./to-dos-api"] \ No newline at end of file diff --git a/Makefile b/Makefile index 2a772ac..a141f5a 100644 --- a/Makefile +++ b/Makefile @@ -3,18 +3,13 @@ -include .env export -# Development targets - -up-db: - @docker compose --profile DbOnly up -d - # Generate a new Alembic migration with autogenerate -create-migration: up-db +create-migration: @cd ./src/data && \ alembic revision --autogenerate -m $(name) # Apply all pending Alembic migrations -apply-migrations: up-db +apply-migrations: @cd ./src/data && \ alembic upgrade head @@ -25,21 +20,4 @@ run: apply-migrations # Run clang-tidy static analysis run-code-analysis: - @run-clang-tidy -p build/Debug - -# TODO: Try to align with the Development targets so as not to split them -# CI/CD targets (safe to run locally too) - -# Apply all pending Alembic migrations -ci-apply-migrations: - @cd alembic/ && \ - alembic upgrade head - -# Running karate tests within Docker Compose -# The option --exit-code-from ensures that the command's exit code exactly matches the exit code of the specified service. -ci-run-karate-test-within-docker-compose: - @if docker pull ghcr.io/tourmalinecore/to-dos-api-cpp:latest 2>/dev/null; then \ - docker compose --profile MockForPullRequest up --no-build --exit-code-from to-dos-api-cpp-karate-tests; \ - else \ - docker compose --profile MockForPullRequest up --build --exit-code-from to-dos-api-cpp-karate-tests; \ - fi \ No newline at end of file + @run-clang-tidy -p build/Debug \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index db8e77d..7c7bc18 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,12 +5,16 @@ services: profiles: - DbOnly - MockForPullRequest - env_file: - - .env + environment: + - POSTGRES_HOST=0.0.0.0 + - POSTGRES_PORT=5432 + - POSTGRES_DB=to-dos-api-cpp-db + - POSTGRES_USER=postgres + - POSTGRES_PASSWORD=password ports: - "7501:5432" healthcheck: - test: [ "CMD-SHELL", "pg_isready -U postgres" ] + test: [ "CMD", "/bin/bash", "-c", "pg_isready -U postgres" ] interval: 10s timeout: 5s retries: 5 @@ -30,7 +34,7 @@ services: # TODO: We need to think of a way to synchronize the .env file used for the database service. # Perhaps we could transfer the pgAdmin environment variables to the .env file. POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres + POSTGRES_PASSWORD: password POSTGRES_DB: to-dos-api-cpp-db PGADMIN_DEFAULT_EMAIL: admin@admin.org PGADMIN_DEFAULT_PASSWORD: postgres @@ -58,26 +62,21 @@ services: depends_on: to-dos-api-cpp-db: condition: service_healthy - image: ghcr.io/tourmalinecore/to-dos-api-cpp:latest - build: - dockerfile: ./Dockerfile - context: . - args: - EXCLUDE_UNIT_TESTS_FROM_BUILD: "true" - entrypoint: [ "/bin/bash", "-c" ] - # command: "make ci-apply-migrations && ./to-dos-api" - command: "cd /app/alembic && alembic upgrade head && cd .. && ./to-dos-api" - healthcheck: - test: [ "CMD-SHELL", "curl -f http://localhost:80/health || exit 1" ] - interval: 5s - timeout: 3s - retries: 5 + image: ghcr.io/tourmalinecore/to-dos-api-cpp:sha-cf87c64af41a92fa5fc21bed4b6a074f6f5001df + # We need to override the entrypoint to apply the migrations to the database before the API starts + entrypoint: [ "/bin/bash", "-c", "alembic -c ./alembic/alembic.ini upgrade head && ./to-dos-api" ] + # healthcheck: + # test: [ "CMD", "/bin/bash", "-c", "curl -f http://localhost:80/health || exit 1" ] + # interval: 1s + # timeout: 3s + # retries: 2 env_file: - .env ports: - - 80:80 + - 6501:80 networks: - to-dos-api-cpp-network + to-dos-api-cpp-karate-tests: container_name: 'to-dos-api-cpp-karate-tests' profiles: @@ -87,7 +86,7 @@ services: context: ./tests/e2e depends_on: to-dos-api-cpp: - condition: service_healthy + condition: service_started command: [ "karate", "/karate" ] volumes: # similar to mock-server volumes we need to support both runs: from Dev Container and from OS diff --git a/src/controllers/app-controller.cpp b/src/controllers/app-controller.cpp index dbd1842..d8c0e7c 100644 --- a/src/controllers/app-controller.cpp +++ b/src/controllers/app-controller.cpp @@ -116,6 +116,7 @@ void AppController::deleteToDo(const HttpRequestPtr& req, std::functiongetParameter("toDoId"); int toDoId = std::stoi(toDoIdStr); diff --git a/tests/e2e/item-types-happy-path.feature b/tests/e2e/item-types-happy-path.feature index a0752c7..6ca7d92 100644 --- a/tests/e2e/item-types-happy-path.feature +++ b/tests/e2e/item-types-happy-path.feature @@ -54,7 +54,7 @@ Feature: Item Types # Step 4: Delete the todo with the id (hard delete) Given url apiRootUrl And path 'to-dos' - And param toDo = todoId + And param toDoId = todoId When method DELETE Then status 200 From 3098d944f74f94a1a9a212354289042388c8b66a Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Wed, 1 Apr 2026 10:43:13 +0500 Subject: [PATCH 45/91] refactor(controllers): #14: remove the health check since it is redundant --- docker-compose.yml | 5 ----- src/controllers/app-controller.cpp | 8 -------- src/controllers/app-controller.h | 1 - 3 files changed, 14 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 7c7bc18..39b74f6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -65,11 +65,6 @@ services: image: ghcr.io/tourmalinecore/to-dos-api-cpp:sha-cf87c64af41a92fa5fc21bed4b6a074f6f5001df # We need to override the entrypoint to apply the migrations to the database before the API starts entrypoint: [ "/bin/bash", "-c", "alembic -c ./alembic/alembic.ini upgrade head && ./to-dos-api" ] - # healthcheck: - # test: [ "CMD", "/bin/bash", "-c", "curl -f http://localhost:80/health || exit 1" ] - # interval: 1s - # timeout: 3s - # retries: 2 env_file: - .env ports: diff --git a/src/controllers/app-controller.cpp b/src/controllers/app-controller.cpp index d8c0e7c..3adaf63 100644 --- a/src/controllers/app-controller.cpp +++ b/src/controllers/app-controller.cpp @@ -131,11 +131,3 @@ void AppController::deleteToDo(const HttpRequestPtr& req, std::function&& callback) -{ - auto response = HttpResponse::newHttpResponse(); - response->setStatusCode(k200OK); - callback(response); -} diff --git a/src/controllers/app-controller.h b/src/controllers/app-controller.h index 52ff68c..a2efbd5 100644 --- a/src/controllers/app-controller.h +++ b/src/controllers/app-controller.h @@ -18,7 +18,6 @@ class AppController : public drogon::HttpController ADD_METHOD_TO(AppController::addToDo, "/api/to-dos", Post); // Adding a new task ADD_METHOD_TO(AppController::completeToDos, "/api/to-dos/complete", Post); // Executing (deleting) a task list ADD_METHOD_TO(AppController::deleteToDo, "/api/to-dos", Delete); // Deleting a specific task - ADD_METHOD_TO(AppController::apiHealthCheck, "/health", Get); // Health endpoint METHOD_LIST_END HttpResponsePtr createInternalServerErrorResponse(const std::string& error) const; From 8c9219dde0cafb7b177ad59fbc7f323cd96f2309 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Wed, 1 Apr 2026 10:50:44 +0500 Subject: [PATCH 46/91] ci(tests): #14: add a makefile target to run karate tests inside docker compose --- .github/workflows/e2e-tests-on-pull-request.yml | 2 +- Dockerfile | 2 ++ Makefile | 15 ++++++++++++++- docker-compose.yml | 7 ++++++- 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/.github/workflows/e2e-tests-on-pull-request.yml b/.github/workflows/e2e-tests-on-pull-request.yml index 6a14872..99c6ca1 100644 --- a/.github/workflows/e2e-tests-on-pull-request.yml +++ b/.github/workflows/e2e-tests-on-pull-request.yml @@ -85,4 +85,4 @@ jobs: echo "POSTGRES_PASSWORD=password" >> .env - name: Run service via docker-compose and run Karate-tests - run: docker compose --profile MockForPullRequest up --exit-code-from to-dos-api-cpp-karate-tests + run: make run-e2e-tests-within-docker-compose diff --git a/Dockerfile b/Dockerfile index 2a0d533..28042bc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -56,6 +56,8 @@ COPY --from=build /src/src/data/alembic.ini ./alembic/ COPY --from=build /src/src/data/migrations/ ./alembic/migrations/ COPY --from=build /src/src/data/models/ ./alembic/models/ +COPY --from=build /src/Makefile . + COPY --from=build /src/build/Release/to-dos-api . ENTRYPOINT ["./to-dos-api"] \ No newline at end of file diff --git a/Makefile b/Makefile index a141f5a..bea02cd 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,11 @@ -include .env export +# By default, Make treats all targets as files; unless you specify that targets +# are commands, the instructions will be skipped if a file with the target’s name exists. +# More info: https://www.gnu.org/software/make/manual/make.html#Phony-Targets +.PHONY: create-migration apply-migrations run run-code-analysis run-e2e-tests-within-docker-compose + # Generate a new Alembic migration with autogenerate create-migration: @cd ./src/data && \ @@ -20,4 +25,12 @@ run: apply-migrations # Run clang-tidy static analysis run-code-analysis: - @run-clang-tidy -p build/Debug \ No newline at end of file + @run-clang-tidy -p build/Debug + +# Running Karate tests using Docker Compose +run-e2e-tests-within-docker-compose: + @if docker pull ghcr.io/tourmalinecore/to-dos-api-cpp:latest 2>/dev/null; then \ + docker compose --profile MockForPullRequest up --no-build --exit-code-from to-dos-api-cpp-karate-tests; \ + else \ + docker compose --profile MockForPullRequest up --build --exit-code-from to-dos-api-cpp-karate-tests; \ + fi \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 39b74f6..25c360e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -62,7 +62,12 @@ services: depends_on: to-dos-api-cpp-db: condition: service_healthy - image: ghcr.io/tourmalinecore/to-dos-api-cpp:sha-cf87c64af41a92fa5fc21bed4b6a074f6f5001df + image: ghcr.io/tourmalinecore/to-dos-api-cpp:latest + build: + dockerfile: ./Dockerfile + context: . + args: + EXCLUDE_UNIT_TESTS_FROM_BUILD: "true" # We need to override the entrypoint to apply the migrations to the database before the API starts entrypoint: [ "/bin/bash", "-c", "alembic -c ./alembic/alembic.ini upgrade head && ./to-dos-api" ] env_file: From 73693498db1d9b9aa045b7041e8918d860c73a17 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Wed, 1 Apr 2026 11:00:07 +0500 Subject: [PATCH 47/91] ci(workflows): #14: add a condition to trigger the workflow on a push to the master branch; modify the condition for publishing the latest image for debugging purposes --- .github/workflows/.reusable-docker-build-and-push.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/.reusable-docker-build-and-push.yml b/.github/workflows/.reusable-docker-build-and-push.yml index 3ba5b24..d653c54 100644 --- a/.github/workflows/.reusable-docker-build-and-push.yml +++ b/.github/workflows/.reusable-docker-build-and-push.yml @@ -8,6 +8,11 @@ name: Publish Docker image on: # to allow to wait for a docker image to be published to proceed in another workflow workflow_call: + # This event has been added for debugging purposes; there is currently no workflow + # that triggers when changes are made to the master branch. + push: + branches: + - master jobs: build-amd64: @@ -193,7 +198,8 @@ jobs: type=raw,value=${{ env.SEMVER_VERSION }} # set latest tag for default branch # https://github.com/docker/metadata-action/issues/171 explains how to tag latest only on default branch - type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} + # type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} + type=raw,value=latest,enable=true env: # https://github.com/docker/metadata-action/issues/283 # without this flag it won't tag the image using the commit SHA From dbb1c4296edfc03aaec212c276bcee9aa6ef97b6 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Wed, 1 Apr 2026 11:06:26 +0500 Subject: [PATCH 48/91] ci(tests): #14: remove the makefile target for karate tests, as using the latest image is not compatible with testing --- .github/workflows/e2e-tests-on-pull-request.yml | 3 +-- Dockerfile | 2 -- Makefile | 10 +--------- docker-compose.yml | 1 - 4 files changed, 2 insertions(+), 14 deletions(-) diff --git a/.github/workflows/e2e-tests-on-pull-request.yml b/.github/workflows/e2e-tests-on-pull-request.yml index 99c6ca1..0825fe0 100644 --- a/.github/workflows/e2e-tests-on-pull-request.yml +++ b/.github/workflows/e2e-tests-on-pull-request.yml @@ -65,7 +65,6 @@ jobs: e2e-karate-tests-in-docker-compose: runs-on: ubuntu-24.04 - needs: [docker-build-and-push] steps: - name: Checkout to repo uses: actions/checkout@v4 @@ -85,4 +84,4 @@ jobs: echo "POSTGRES_PASSWORD=password" >> .env - name: Run service via docker-compose and run Karate-tests - run: make run-e2e-tests-within-docker-compose + run: docker compose --profile MockForPullRequest up --exit-code-from to-dos-api-cpp-karate-tests diff --git a/Dockerfile b/Dockerfile index 28042bc..2a0d533 100644 --- a/Dockerfile +++ b/Dockerfile @@ -56,8 +56,6 @@ COPY --from=build /src/src/data/alembic.ini ./alembic/ COPY --from=build /src/src/data/migrations/ ./alembic/migrations/ COPY --from=build /src/src/data/models/ ./alembic/models/ -COPY --from=build /src/Makefile . - COPY --from=build /src/build/Release/to-dos-api . ENTRYPOINT ["./to-dos-api"] \ No newline at end of file diff --git a/Makefile b/Makefile index bea02cd..a900125 100644 --- a/Makefile +++ b/Makefile @@ -25,12 +25,4 @@ run: apply-migrations # Run clang-tidy static analysis run-code-analysis: - @run-clang-tidy -p build/Debug - -# Running Karate tests using Docker Compose -run-e2e-tests-within-docker-compose: - @if docker pull ghcr.io/tourmalinecore/to-dos-api-cpp:latest 2>/dev/null; then \ - docker compose --profile MockForPullRequest up --no-build --exit-code-from to-dos-api-cpp-karate-tests; \ - else \ - docker compose --profile MockForPullRequest up --build --exit-code-from to-dos-api-cpp-karate-tests; \ - fi \ No newline at end of file + @run-clang-tidy -p build/Debug \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 25c360e..12e0137 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -62,7 +62,6 @@ services: depends_on: to-dos-api-cpp-db: condition: service_healthy - image: ghcr.io/tourmalinecore/to-dos-api-cpp:latest build: dockerfile: ./Dockerfile context: . From 2cff03719061324d2fe273d7d312a78a58fe488f Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Wed, 1 Apr 2026 11:07:33 +0500 Subject: [PATCH 49/91] ci(workflows): #14: remove of the condition requiring the latest tag for the API image --- .github/workflows/.reusable-docker-build-and-push.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/.reusable-docker-build-and-push.yml b/.github/workflows/.reusable-docker-build-and-push.yml index d653c54..f776207 100644 --- a/.github/workflows/.reusable-docker-build-and-push.yml +++ b/.github/workflows/.reusable-docker-build-and-push.yml @@ -198,8 +198,7 @@ jobs: type=raw,value=${{ env.SEMVER_VERSION }} # set latest tag for default branch # https://github.com/docker/metadata-action/issues/171 explains how to tag latest only on default branch - # type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} - type=raw,value=latest,enable=true + type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} env: # https://github.com/docker/metadata-action/issues/283 # without this flag it won't tag the image using the commit SHA From 9dda99c893fc7c910f505764dd08dddc1614d61d Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Wed, 1 Apr 2026 11:58:07 +0500 Subject: [PATCH 50/91] ci(tests): #14: add image caching to the docker compose API to speed up service image builds --- .github/workflows/e2e-tests-on-pull-request.yml | 2 +- docker-compose.yml | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/e2e-tests-on-pull-request.yml b/.github/workflows/e2e-tests-on-pull-request.yml index 0825fe0..33c5257 100644 --- a/.github/workflows/e2e-tests-on-pull-request.yml +++ b/.github/workflows/e2e-tests-on-pull-request.yml @@ -84,4 +84,4 @@ jobs: echo "POSTGRES_PASSWORD=password" >> .env - name: Run service via docker-compose and run Karate-tests - run: docker compose --profile MockForPullRequest up --exit-code-from to-dos-api-cpp-karate-tests + run: BRANCH_NAME=${GITHUB_REF_NAME} docker compose --profile MockForPullRequest up --exit-code-from to-dos-api-cpp-karate-tests diff --git a/docker-compose.yml b/docker-compose.yml index 12e0137..949f190 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -67,6 +67,8 @@ services: context: . args: EXCLUDE_UNIT_TESTS_FROM_BUILD: "true" + cache-from: type=registry,ref=ghcr.io/tourmalinecore/to-dos-api-cpp:cache-karate-${BRANCH_NAME:-local} + cache-to: type=registry,ref=ghcr.io/tourmalinecore/to-dos-api-cpp:cache-karate-${BRANCH_NAME:-local},mode=max # We need to override the entrypoint to apply the migrations to the database before the API starts entrypoint: [ "/bin/bash", "-c", "alembic -c ./alembic/alembic.ini upgrade head && ./to-dos-api" ] env_file: From 0b102f7d11922e5fab5b139ff0b508f4dd2939d8 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Wed, 1 Apr 2026 12:00:33 +0500 Subject: [PATCH 51/91] ci(tests): #14: adda syntax version parameter to docker compose to allow the use of the cache-to and cache-from parameters --- docker-compose.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index 949f190..3d78ce5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,3 +1,5 @@ +version: "3.8" + services: to-dos-api-cpp-db: container_name: to-dos-api-cpp-db From 1452dcb553af035bec2995fc5d99235e71eb3649 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Wed, 1 Apr 2026 12:02:10 +0500 Subject: [PATCH 52/91] ci(tests): #14: add a step to output the Docker Compose version --- .github/workflows/e2e-tests-on-pull-request.yml | 4 +++- docker-compose.yml | 2 -- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/e2e-tests-on-pull-request.yml b/.github/workflows/e2e-tests-on-pull-request.yml index 33c5257..188ed5d 100644 --- a/.github/workflows/e2e-tests-on-pull-request.yml +++ b/.github/workflows/e2e-tests-on-pull-request.yml @@ -21,7 +21,6 @@ jobs: repository: TourmalineCore/to-dos-local-env ref: feature/add-to-dos-api-cpp-release - - name: Deploy Local Env to Kind k8s uses: devcontainers/ci@v0.3 with: @@ -82,6 +81,9 @@ jobs: echo "POSTGRES_DB=to-dos-api-cpp-db" >> .env echo "POSTGRES_USER=postgres" >> .env echo "POSTGRES_PASSWORD=password" >> .env + + - name: Check compose version + run: docker compose version - name: Run service via docker-compose and run Karate-tests run: BRANCH_NAME=${GITHUB_REF_NAME} docker compose --profile MockForPullRequest up --exit-code-from to-dos-api-cpp-karate-tests diff --git a/docker-compose.yml b/docker-compose.yml index 3d78ce5..949f190 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3.8" - services: to-dos-api-cpp-db: container_name: to-dos-api-cpp-db From 1264aa2053c633132cb4a0ad19e5a06caa6a1ea8 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Wed, 1 Apr 2026 13:02:37 +0500 Subject: [PATCH 53/91] ci(workflows): #14: add the `docker/setup-buildx-action@v3` installation step to enable caching --- .github/workflows/e2e-tests-on-pull-request.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/e2e-tests-on-pull-request.yml b/.github/workflows/e2e-tests-on-pull-request.yml index 188ed5d..f412c0d 100644 --- a/.github/workflows/e2e-tests-on-pull-request.yml +++ b/.github/workflows/e2e-tests-on-pull-request.yml @@ -82,8 +82,8 @@ jobs: echo "POSTGRES_USER=postgres" >> .env echo "POSTGRES_PASSWORD=password" >> .env - - name: Check compose version - run: docker compose version + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 - name: Run service via docker-compose and run Karate-tests run: BRANCH_NAME=${GITHUB_REF_NAME} docker compose --profile MockForPullRequest up --exit-code-from to-dos-api-cpp-karate-tests From 0f10448e9fef1b43f1c0e3f34f22fd741d0a0c65 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Wed, 1 Apr 2026 13:06:36 +0500 Subject: [PATCH 54/91] ci(workflows): #14: change the caching method to gha for debugging --- .github/workflows/e2e-tests-on-pull-request.yml | 3 --- docker-compose.yml | 7 ++++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/e2e-tests-on-pull-request.yml b/.github/workflows/e2e-tests-on-pull-request.yml index f412c0d..f9a8a86 100644 --- a/.github/workflows/e2e-tests-on-pull-request.yml +++ b/.github/workflows/e2e-tests-on-pull-request.yml @@ -81,9 +81,6 @@ jobs: echo "POSTGRES_DB=to-dos-api-cpp-db" >> .env echo "POSTGRES_USER=postgres" >> .env echo "POSTGRES_PASSWORD=password" >> .env - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - name: Run service via docker-compose and run Karate-tests run: BRANCH_NAME=${GITHUB_REF_NAME} docker compose --profile MockForPullRequest up --exit-code-from to-dos-api-cpp-karate-tests diff --git a/docker-compose.yml b/docker-compose.yml index 949f190..109e07f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -67,9 +67,10 @@ services: context: . args: EXCLUDE_UNIT_TESTS_FROM_BUILD: "true" - cache-from: type=registry,ref=ghcr.io/tourmalinecore/to-dos-api-cpp:cache-karate-${BRANCH_NAME:-local} - cache-to: type=registry,ref=ghcr.io/tourmalinecore/to-dos-api-cpp:cache-karate-${BRANCH_NAME:-local},mode=max - # We need to override the entrypoint to apply the migrations to the database before the API starts + # cache-from: type=registry,ref=ghcr.io/tourmalinecore/to-dos-api-cpp:cache-karate-${BRANCH_NAME:-local} + cache-from: type=gha + # cache-to: type=registry,ref=ghcr.io/tourmalinecore/to-dos-api-cpp:cache-karate-${BRANCH_NAME:-local},mode=max + # We need to override the entrypoint to apply the migrations to the database before the API starts entrypoint: [ "/bin/bash", "-c", "alembic -c ./alembic/alembic.ini upgrade head && ./to-dos-api" ] env_file: - .env From 6467d3fe7e184d0eee08462d2b7170a6d5d443b9 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Wed, 1 Apr 2026 13:25:39 +0500 Subject: [PATCH 55/91] fix(tests): #14: fix docker compose validation error --- docker-compose.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 109e07f..408548c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -67,9 +67,10 @@ services: context: . args: EXCLUDE_UNIT_TESTS_FROM_BUILD: "true" - # cache-from: type=registry,ref=ghcr.io/tourmalinecore/to-dos-api-cpp:cache-karate-${BRANCH_NAME:-local} - cache-from: type=gha - # cache-to: type=registry,ref=ghcr.io/tourmalinecore/to-dos-api-cpp:cache-karate-${BRANCH_NAME:-local},mode=max + cache_from: + - type=registry,ref=ghcr.io/tourmalinecore/to-dos-api-cpp:cache-karate-${BRANCH_NAME} + cache_to: + - type=registry,ref=ghcr.io/tourmalinecore/to-dos-api-cpp:cache-karate-${BRANCH_NAME},mode=max # We need to override the entrypoint to apply the migrations to the database before the API starts entrypoint: [ "/bin/bash", "-c", "alembic -c ./alembic/alembic.ini upgrade head && ./to-dos-api" ] env_file: From f7164d8e5a210c9380968796dc54b21091bd7fad Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Wed, 1 Apr 2026 14:03:34 +0500 Subject: [PATCH 56/91] fix(tests): #14: remove the cache save step so that no error occurs when running locally --- docker-compose.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 408548c..2a8a777 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -68,9 +68,7 @@ services: args: EXCLUDE_UNIT_TESTS_FROM_BUILD: "true" cache_from: - - type=registry,ref=ghcr.io/tourmalinecore/to-dos-api-cpp:cache-karate-${BRANCH_NAME} - cache_to: - - type=registry,ref=ghcr.io/tourmalinecore/to-dos-api-cpp:cache-karate-${BRANCH_NAME},mode=max + - type=registry,ref=ghcr.io/tourmalinecore/to-dos-api-cpp:cache-amd64-${BRANCH_NAME:-default} # We need to override the entrypoint to apply the migrations to the database before the API starts entrypoint: [ "/bin/bash", "-c", "alembic -c ./alembic/alembic.ini upgrade head && ./to-dos-api" ] env_file: From ee3728b8fe3690ba44469e32c336cb2354de7ace Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Wed, 1 Apr 2026 14:35:49 +0500 Subject: [PATCH 57/91] fix(controllers): #14: correctthe field name in the JSON returned by the endpoint --- Dockerfile | 2 +- docker-compose.yml | 4 ++-- src/api/features/to-dos/to-dos-controller.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 744b7e5..9b835e7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -52,7 +52,7 @@ RUN pip install alembic psycopg2-binary sqlalchemy-utils WORKDIR /app # alembic needs this to apply the migrations correctly -COPY --from=build /src/alembic/* ./alembic/ +COPY --from=build /src/alembic/ ./alembic/ COPY --from=build /src/build/Release/to-dos-api . diff --git a/docker-compose.yml b/docker-compose.yml index 2a8a777..e64a740 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -67,8 +67,8 @@ services: context: . args: EXCLUDE_UNIT_TESTS_FROM_BUILD: "true" - cache_from: - - type=registry,ref=ghcr.io/tourmalinecore/to-dos-api-cpp:cache-amd64-${BRANCH_NAME:-default} + # cache_from: + # - type=registry,ref=ghcr.io/tourmalinecore/to-dos-api-cpp:cache-amd64-${BRANCH_NAME:-default} # We need to override the entrypoint to apply the migrations to the database before the API starts entrypoint: [ "/bin/bash", "-c", "alembic -c ./alembic/alembic.ini upgrade head && ./to-dos-api" ] env_file: diff --git a/src/api/features/to-dos/to-dos-controller.cpp b/src/api/features/to-dos/to-dos-controller.cpp index 97005e4..8f2f2bd 100644 --- a/src/api/features/to-dos/to-dos-controller.cpp +++ b/src/api/features/to-dos/to-dos-controller.cpp @@ -75,7 +75,7 @@ void ToDosController::addToDo(const HttpRequestPtr& req, std::functionget("name", "").asString() }; auto handlerResponse = createToDoHandler_->handle(request); - jsonResponse["id"] = handlerResponse.id; + jsonResponse["todoId"] = handlerResponse.id; auto resp = HttpResponse::newHttpJsonResponse(jsonResponse); resp->setStatusCode(k201Created); From 41745a74efe8d3f1336d942dfc2491feda360de0 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Thu, 2 Apr 2026 10:15:01 +0500 Subject: [PATCH 58/91] chore(tests): #14: change the location of the test directory; update the docker-compose.yaml configuration to run Karate tests in the pipeline --- .github/workflows/e2e-tests-on-pull-request.yml | 2 +- CMakeLists.txt | 2 +- docker-compose.yml | 16 +++++++--------- {tests/e2e => e2e}/KarateDockerfile | 0 {tests/e2e => e2e}/item-types-happy-path.feature | 0 {tests/e2e => e2e}/js-utils.js | 0 {tests/unit => unit}/CMakeLists.txt | 0 {tests/unit => unit}/addition-operation.cpp | 0 {tests/unit => unit}/addition-operation.h | 0 {tests/unit => unit}/test_main.cpp | 0 10 files changed, 9 insertions(+), 11 deletions(-) rename {tests/e2e => e2e}/KarateDockerfile (100%) rename {tests/e2e => e2e}/item-types-happy-path.feature (100%) rename {tests/e2e => e2e}/js-utils.js (100%) rename {tests/unit => unit}/CMakeLists.txt (100%) rename {tests/unit => unit}/addition-operation.cpp (100%) rename {tests/unit => unit}/addition-operation.h (100%) rename {tests/unit => unit}/test_main.cpp (100%) diff --git a/.github/workflows/e2e-tests-on-pull-request.yml b/.github/workflows/e2e-tests-on-pull-request.yml index f9a8a86..6488077 100644 --- a/.github/workflows/e2e-tests-on-pull-request.yml +++ b/.github/workflows/e2e-tests-on-pull-request.yml @@ -83,4 +83,4 @@ jobs: echo "POSTGRES_PASSWORD=password" >> .env - name: Run service via docker-compose and run Karate-tests - run: BRANCH_NAME=${GITHUB_REF_NAME} docker compose --profile MockForPullRequest up --exit-code-from to-dos-api-cpp-karate-tests + run: docker compose --profile MockForPullRequest up --exit-code-from to-dos-api-cpp-karate-tests diff --git a/CMakeLists.txt b/CMakeLists.txt index eb1b774..0786d89 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,5 +40,5 @@ if (ENV{EXCLUDE_UNIT_TESTS_FROM_BUILD} STREQUAL "true") else() message(STATUS "UNIT TEST ENABLED") enable_testing() - add_subdirectory(tests/unit) + add_subdirectory(unit) endif() diff --git a/docker-compose.yml b/docker-compose.yml index e64a740..5b5b677 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,12 +5,8 @@ services: profiles: - DbOnly - MockForPullRequest - environment: - - POSTGRES_HOST=0.0.0.0 - - POSTGRES_PORT=5432 - - POSTGRES_DB=to-dos-api-cpp-db - - POSTGRES_USER=postgres - - POSTGRES_PASSWORD=password + env_file: + - .env ports: - "7501:5432" healthcheck: @@ -67,12 +63,14 @@ services: context: . args: EXCLUDE_UNIT_TESTS_FROM_BUILD: "true" - # cache_from: - # - type=registry,ref=ghcr.io/tourmalinecore/to-dos-api-cpp:cache-amd64-${BRANCH_NAME:-default} # We need to override the entrypoint to apply the migrations to the database before the API starts entrypoint: [ "/bin/bash", "-c", "alembic -c ./alembic/alembic.ini upgrade head && ./to-dos-api" ] env_file: - .env + # Unless you override the host to the service name, the API will + # not be able to locate the database container on the network + environment: + - POSTGRES_HOST=to-dos-api-cpp-db ports: - 6501:80 networks: @@ -84,7 +82,7 @@ services: - MockForPullRequest build: dockerfile: ./KarateDockerfile - context: ./tests/e2e + context: ./e2e depends_on: to-dos-api-cpp: condition: service_started diff --git a/tests/e2e/KarateDockerfile b/e2e/KarateDockerfile similarity index 100% rename from tests/e2e/KarateDockerfile rename to e2e/KarateDockerfile diff --git a/tests/e2e/item-types-happy-path.feature b/e2e/item-types-happy-path.feature similarity index 100% rename from tests/e2e/item-types-happy-path.feature rename to e2e/item-types-happy-path.feature diff --git a/tests/e2e/js-utils.js b/e2e/js-utils.js similarity index 100% rename from tests/e2e/js-utils.js rename to e2e/js-utils.js diff --git a/tests/unit/CMakeLists.txt b/unit/CMakeLists.txt similarity index 100% rename from tests/unit/CMakeLists.txt rename to unit/CMakeLists.txt diff --git a/tests/unit/addition-operation.cpp b/unit/addition-operation.cpp similarity index 100% rename from tests/unit/addition-operation.cpp rename to unit/addition-operation.cpp diff --git a/tests/unit/addition-operation.h b/unit/addition-operation.h similarity index 100% rename from tests/unit/addition-operation.h rename to unit/addition-operation.h diff --git a/tests/unit/test_main.cpp b/unit/test_main.cpp similarity index 100% rename from tests/unit/test_main.cpp rename to unit/test_main.cpp From 42ca394245553b15d89ac5e96bde4a3e37f90e7a Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Thu, 2 Apr 2026 10:16:07 +0500 Subject: [PATCH 59/91] fix(tests): #14: fix the issue with mounting the .conan2 directory during container build, as well as defining the local recipe storage directory --- .devcontainer/devcontainer.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 84c23bc..82b43b9 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -8,7 +8,7 @@ // Use 'forwardPorts' to make a list of ports inside the container available locally. // "forwardPorts": [], // Use 'postCreateCommand' to run commands after the container is created. - "postCreateCommand": "conan remote add local-recipes ./deps --type=local-recipes-index", // Add a local recipes store for odb libraries + "postCreateCommand": "conan remote add local-recipes ./deps --type=local-recipes-index --force", // Add a local recipes store for odb libraries. Need force flag for redefine while second time container is up // Configure tool-specific properties. // "customizations": {}, // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. @@ -27,10 +27,10 @@ "mounts": [ // This mounts increase future building on devcontainer in case any reseting of container volumes // Mount caching of build folder - "source=dev-build-cache,target=${containerWorkspaceFolder}/build,type=volume", + "source=dev-build-cache,target=${containerWorkspaceFolder}/build,type=volume", // Mount caching of conan dependencies builds - "source=dev-conan-cache,target=~/.conan2,type=volume" - ], + "source=dev-conan-cache,target=/root/.conan2,type=volume" + ], "customizations": { "vscode": { "extensions": [ From 34c24b8121ef9128c1f627072aa3458534b3b2ba Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Thu, 2 Apr 2026 10:26:01 +0500 Subject: [PATCH 60/91] ci(dockerfile): #14: removethe step to install make from the build stage, as it is not required in the final image --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 9b835e7..2174774 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,14 +8,14 @@ EXPOSE 80 # pip is installed here because it needs to be available in both the build and final stages RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ - && apt-get -y install --no-install-recommends pip make + && apt-get -y install --no-install-recommends pip FROM base AS build WORKDIR /src RUN apt-get -y install --no-install-recommends \ - build-essential clang lld cmake ninja-build gdb \ + build-essential clang lld make cmake ninja-build gdb \ && apt-get autoremove -y \ && apt-get clean -y \ && rm -rf /var/lib/apt/lists/* From c21e3841b272f5fd060f5a912922cafe80b7a4a1 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Thu, 2 Apr 2026 10:27:16 +0500 Subject: [PATCH 61/91] fix(workflows): #14: change the host address when specifying the .env file in the pipeline so that the database container initializes correctly --- .github/workflows/e2e-tests-on-pull-request.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/e2e-tests-on-pull-request.yml b/.github/workflows/e2e-tests-on-pull-request.yml index 6488077..2fc9983 100644 --- a/.github/workflows/e2e-tests-on-pull-request.yml +++ b/.github/workflows/e2e-tests-on-pull-request.yml @@ -76,7 +76,7 @@ jobs: echo "API_PORT=80" >> .env echo "API_LOG_LEVEL=INFO" >> .env echo "API_NUMBER_OF_THREADS=1" >> .env - echo "POSTGRES_HOST=to-dos-api-cpp-db" >> .env + echo "POSTGRES_HOST=0.0.0.0" >> .env echo "POSTGRES_PORT=5432" >> .env echo "POSTGRES_DB=to-dos-api-cpp-db" >> .env echo "POSTGRES_USER=postgres" >> .env From f2596ff0dde9ca122db8ae61efe8233082c0b63b Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Thu, 2 Apr 2026 10:27:44 +0500 Subject: [PATCH 62/91] docs: remove of redundant comments --- README.md | 4 +--- e2e/js-utils.js | 45 --------------------------------------------- 2 files changed, 1 insertion(+), 48 deletions(-) diff --git a/README.md b/README.md index 0b2e8db..7f7790f 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,4 @@ The alembic tool is used to work with migrations. To work with it, you need to m Alternatively, you can use make targets. - To create a migration, run the command `make create-migration name=`, where `name` is the name of the migration. You can also use `make create-migration`, in which case the migration will be named after the current date and time. -- To apply the migrations, run the `make apply-migrations` command. - -[Test] \ No newline at end of file +- To apply the migrations, run the `make apply-migrations` command. \ No newline at end of file diff --git a/e2e/js-utils.js b/e2e/js-utils.js index 89eff13..cc615a2 100644 --- a/e2e/js-utils.js +++ b/e2e/js-utils.js @@ -1,54 +1,9 @@ function fn() { return { - // getAuthHeaders: function (tokenValue) { - // return { - // [this.getAuthHeaderKey()]: this.getAuthHeaderValue(tokenValue) - // } - // }, - - // getAuthHeaderKey: function () { - // return this.shouldUseFakeExternalDependencies() - // ? 'X-DEBUG-TOKEN' - // : 'Authorization'; - // }, - - // getAuthHeaderValue: function (tokenValue) { - // return this.shouldUseFakeExternalDependencies() - // ? tokenValue - // : 'Bearer ' + tokenValue; - // }, - - // shouldUseFakeExternalDependencies: function () { - // return this.getEnvVariable('SHOULD_USE_FAKE_EXTERNAL_DEPENDENCIES') === 'true'; - // }, - getEnvVariable: function (variable) { var System = Java.type('java.lang.System'); return System.getenv(variable); }, - - // getEmployeeIdFromToken: function (tokenValue) { - // var decodedString; - - // if (tokenValue.includes('.')) { - // var payload = tokenValue.split('.')[1]; - - // decodedString = decodeString(payload); - // } else { - // decodedString = decodeString(tokenValue); - // } - - // var tokenData = JSON.parse(decodedString); - - // return Number(tokenData.employeeId); - // }, } - - // function decodeString(string) { - // var Bytes = Java.type('java.util.Base64'); - // var decodedBytes = Bytes.getDecoder().decode(string); - - // return new java.lang.String(decodedBytes); - // } } \ No newline at end of file From b7e6af9087a1099bbe9e9895fa00ec9e671e8fd0 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Thu, 2 Apr 2026 10:34:07 +0500 Subject: [PATCH 63/91] ci(dockerfile): #14: add clarifying comments to the Dockerfile; remove the sudo package, since it is already pre-installed in the image --- .devcontainer/Dockerfile | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index ea5c8c2..6cf3cf8 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,16 +1,25 @@ -FROM mcr.microsoft.com/devcontainers/base:ubuntu-22.04 AS base +FROM mcr.microsoft.com/devcontainers/base:ubuntu-22.04 -RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ - && apt-get -y install --no-install-recommends \ - build-essential clang lld make gdb cmake ninja-build \ +# This is necessary to ensure that the installation proceeds in the non-interactive mode. +# If this option is not specified, apt may request confirmation during the installation. +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get -y install --no-install-recommends \ + build-essential clang lld gdb cmake ninja-build \ crossbuild-essential-arm64 \ - git curl wget unzip sudo \ + git curl wget unzip \ pip \ clang-format clang-tidy \ && apt-get autoremove -y \ && apt-get clean -y \ && rm -rf /var/lib/apt/lists/* +# If the local recipes are not specified in Conan, Conan will not be able +# to find them when installing and building dependencies. RUN pip install conan alembic psycopg2-binary sqlalchemy-utils -COPY to-dos-conan-profile.conf /root/.conan2/profiles/default \ No newline at end of file +RUN conan remote add local-recipes ./deps --type=local-recipes-index + +# If this profile is not copied, the profile must be specified via an argument +# when calling `conan`. +COPY .devcontainer/to-dos-conan-profile.conf /root/.conan2/profiles/default \ No newline at end of file From 2a4541c58f495b5eb7c3c3c96072a3daeecf2aa6 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Thu, 2 Apr 2026 10:44:22 +0500 Subject: [PATCH 64/91] ci(dockerfile): #14: add clarifying comments to the Dockerfile; remove the sudo package, since it is already pre-installed in the image --- .devcontainer/Dockerfile | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 6cf3cf8..e2ecae9 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -14,12 +14,8 @@ RUN apt-get update && apt-get -y install --no-install-recommends \ && apt-get clean -y \ && rm -rf /var/lib/apt/lists/* -# If the local recipes are not specified in Conan, Conan will not be able -# to find them when installing and building dependencies. RUN pip install conan alembic psycopg2-binary sqlalchemy-utils -RUN conan remote add local-recipes ./deps --type=local-recipes-index - # If this profile is not copied, the profile must be specified via an argument # when calling `conan`. -COPY .devcontainer/to-dos-conan-profile.conf /root/.conan2/profiles/default \ No newline at end of file +COPY ./to-dos-conan-profile.conf /root/.conan2/profiles/default \ No newline at end of file From eba391135fdbb7c6247b943d105e60cbe26564d4 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Thu, 2 Apr 2026 10:45:35 +0500 Subject: [PATCH 65/91] ci(dockerignore): #14: add a dockerignore file so that files not required for the build are included in the final image --- .dockerignore | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .dockerignore diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..e9ea01b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +.github/ +.vscode/ +ci/ +docs/ +README.md \ No newline at end of file From 7b9398fa8fa5523499acf44983e568f641338a6b Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Thu, 2 Apr 2026 11:14:36 +0500 Subject: [PATCH 66/91] ci(dockerfile): #14: remove the redundant environment variable definition; restore the make and sudo package settings --- .devcontainer/Dockerfile | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index e2ecae9..b91c673 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,13 +1,9 @@ FROM mcr.microsoft.com/devcontainers/base:ubuntu-22.04 -# This is necessary to ensure that the installation proceeds in the non-interactive mode. -# If this option is not specified, apt may request confirmation during the installation. -ENV DEBIAN_FRONTEND=noninteractive - RUN apt-get update && apt-get -y install --no-install-recommends \ - build-essential clang lld gdb cmake ninja-build \ + build-essential clang lld gdb make cmake ninja-build \ crossbuild-essential-arm64 \ - git curl wget unzip \ + git curl wget unzip sudo \ pip \ clang-format clang-tidy \ && apt-get autoremove -y \ From b725fbb5011555eb3f80c72133975c9ff4c055ce Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Thu, 2 Apr 2026 11:26:58 +0500 Subject: [PATCH 67/91] ci(dockerfile): #14: add additional paths to .dockerignore so that files not needed for the build are not included in the image --- .dockerignore | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.dockerignore b/.dockerignore index e9ea01b..bf4899d 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,5 +1,19 @@ .github/ .vscode/ +build/ ci/ +scripts/ +e2e/ docs/ +.clang-format +.clang-tidy +.env +.env.example +.gitattributes +.gitignore +CMakeUserPresets.json +docker-compose.yml +Dockerfile +LICENSE +pgAdmin.json README.md \ No newline at end of file From 7dc1b1c0fb29ea92a664c9a27e89b19fbbec35c7 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Thu, 2 Apr 2026 11:48:55 +0500 Subject: [PATCH 68/91] ci(dockerfile): #14: remove unnecessary environment variable from the dockerfile --- Dockerfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 2174774..81e6adc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,8 +7,7 @@ WORKDIR /app EXPOSE 80 # pip is installed here because it needs to be available in both the build and final stages -RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ - && apt-get -y install --no-install-recommends pip +RUN apt-get update && apt-get -y install --no-install-recommends pip FROM base AS build From 61c5c85740b19f2bf72f8bee99e986d5d64bc7c4 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Thu, 2 Apr 2026 13:03:21 +0500 Subject: [PATCH 69/91] ci(tests): #14: add a hash check for values in to-dos-api-cpp within the local-env repository so that the file is checked for changes during workflow execution --- .github/workflows/e2e-tests-on-pull-request.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/e2e-tests-on-pull-request.yml b/.github/workflows/e2e-tests-on-pull-request.yml index 2fc9983..23b1d2f 100644 --- a/.github/workflows/e2e-tests-on-pull-request.yml +++ b/.github/workflows/e2e-tests-on-pull-request.yml @@ -20,6 +20,16 @@ jobs: with: repository: TourmalineCore/to-dos-local-env ref: feature/add-to-dos-api-cpp-release + + - name: Verify values file checksum + run: | + ACTUAL=$(sha256sum deploy/values-to-dos-api-cpp.yaml.gotmpl | awk '{ print $1 }') + if [ "$ACTUAL" != "${{ vars.LOCAL_ENV_REPO_VALUES_TO_DOS_API_CPP_SHA256 }}" ]; then + echo "ERROR: checksum mismatch" + echo "Expected: ${{ vars.LOCAL_ENV_REPO_VALUES_TO_DOS_API_CPP_SHA256 }}" + echo "Actual: $ACTUAL" + exit 1 + fi - name: Deploy Local Env to Kind k8s uses: devcontainers/ci@v0.3 From 25671f079ff2bb4dfd6b1ee6d233bb421df68ff0 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Thu, 2 Apr 2026 13:08:19 +0500 Subject: [PATCH 70/91] ci(workflows): #14: add a command to update the commit sha for the initContainer so that the image inside the initContainer is also updated --- .github/workflows/e2e-tests-on-pull-request.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/e2e-tests-on-pull-request.yml b/.github/workflows/e2e-tests-on-pull-request.yml index 23b1d2f..b939295 100644 --- a/.github/workflows/e2e-tests-on-pull-request.yml +++ b/.github/workflows/e2e-tests-on-pull-request.yml @@ -40,6 +40,9 @@ jobs: # so for that reason we use Stream EDitor which can find needed string using regular expressions and change it to a new value # The -i flag is needed to write new image tag directly to values file sed -i "0,/tag:.*/s//tag: \"sha-${{ github.event.pull_request.head.sha }}\"/" deploy/values-to-dos-api-cpp.yaml.gotmpl + + # we also need to update the commit sha in the initContainer so that the latest image is used inside it + sed -i "0,/image:.*/s//image: \"ghcr.io/tourmalinecore/to-dos-api-cpp:sha-${{ github.event.pull_request.head.sha }}\"/" deploy/values-to-dos-api-cpp.yaml.gotmpl # we need to override "latest" ref of service chart inside local-env to run tests against the current commit service chart version and not against latest from master sed -i "0,/git+https:\/\/github.com\/TourmalineCore\/${{ github.event.repository.name }}.git?ref=.*/s//git+https:\/\/github.com\/TourmalineCore\/${{ github.event.repository.name }}.git?ref=${{ github.event.pull_request.head.sha }}/" deploy/helmfile.yaml.gotmpl From 932c28aa469dcbcb182da782592298f28cadfe4e Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Thu, 2 Apr 2026 13:55:36 +0500 Subject: [PATCH 71/91] ci(workflows): #14: update the reference hash; add a clarifying comment --- .github/workflows/e2e-tests-on-pull-request.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/e2e-tests-on-pull-request.yml b/.github/workflows/e2e-tests-on-pull-request.yml index b939295..ad40846 100644 --- a/.github/workflows/e2e-tests-on-pull-request.yml +++ b/.github/workflows/e2e-tests-on-pull-request.yml @@ -21,13 +21,15 @@ jobs: repository: TourmalineCore/to-dos-local-env ref: feature/add-to-dos-api-cpp-release + # We need to make sure that the values-to-dos-api-cpp.yaml.gotmpl file hasn't been modified + # in the `to-dos-local-env` repository, to ensure that the test runs as expected. - name: Verify values file checksum run: | - ACTUAL=$(sha256sum deploy/values-to-dos-api-cpp.yaml.gotmpl | awk '{ print $1 }') - if [ "$ACTUAL" != "${{ vars.LOCAL_ENV_REPO_VALUES_TO_DOS_API_CPP_SHA256 }}" ]; then + VALUES_HASH=$(sha256sum deploy/values-to-dos-api-cpp.yaml.gotmpl | awk '{ print $1 }') + if [ "$VALUES_HASH" != "88d14f527d1c39d9af93ddbe2f41462f01b523bdc340b4c62cb72c31a4c46d1f" ]; then echo "ERROR: checksum mismatch" - echo "Expected: ${{ vars.LOCAL_ENV_REPO_VALUES_TO_DOS_API_CPP_SHA256 }}" - echo "Actual: $ACTUAL" + echo "Expected: 88d14f527d1c39d9af93ddbe2f41462f01b523bdc340b4c62cb72c31a4c46d1f" + echo "Actual: $VALUES_HASH" exit 1 fi From 9e0aae4a97261a94abafe190c41a338dd7b5c2e8 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Thu, 2 Apr 2026 13:56:32 +0500 Subject: [PATCH 72/91] ci(workflows): #14: merge two stages into one so that all the prep work happens in a single step --- .../.reusable-docker-build-and-push.yml | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/.github/workflows/.reusable-docker-build-and-push.yml b/.github/workflows/.reusable-docker-build-and-push.yml index f776207..3cc741f 100644 --- a/.github/workflows/.reusable-docker-build-and-push.yml +++ b/.github/workflows/.reusable-docker-build-and-push.yml @@ -18,7 +18,7 @@ jobs: build-amd64: runs-on: ubuntu-24.04 steps: - - name: Check out the repo + - name: Checkout the repo uses: actions/checkout@v4 # this is needed to address this issue according to the comment https://github.com/devcontainers/ci/issues/271#issuecomment-2301764487 @@ -26,16 +26,15 @@ jobs: - name: Add Registry Image Env Var With Lowercase Organization and Repo Name run: | echo "REGISTRY_IMAGE=ghcr.io/${GITHUB_REPOSITORY,,}" >>${GITHUB_ENV} - - - name: Add branch name env var for image tag - run: | - BRANCH_NAME="${GITHUB_REF_NAME//[^a-zA-Z0-9._-]/-}" - echo "BRANCH_NAME=${BRANCH_NAME}" >> $GITHUB_ENV + # we need to remove the / characters from the branch name, + # as they will cause an error when saving the cache - name: Prepare run: | platform=linux/amd64 echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV + BRANCH_NAME="${GITHUB_REF_NAME//[^a-zA-Z0-9._-]/-}" + echo "BRANCH_NAME=${BRANCH_NAME}" >> $GITHUB_ENV - name: Docker meta id: meta @@ -93,16 +92,15 @@ jobs: - name: Add Registry Image Env Var With Lowercase Organization and Repo Name run: | echo "REGISTRY_IMAGE=ghcr.io/${GITHUB_REPOSITORY,,}" >>${GITHUB_ENV} - - - name: Add branch name env var for image tag - run: | - BRANCH_NAME="${GITHUB_REF_NAME//[^a-zA-Z0-9._-]/-}" - echo "BRANCH_NAME=${BRANCH_NAME}" >> $GITHUB_ENV + # we need to remove the / characters from the branch name, + # as they will cause an error when saving the cache - name: Prepare run: | platform=linux/arm64 echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV + BRANCH_NAME="${GITHUB_REF_NAME//[^a-zA-Z0-9._-]/-}" + echo "BRANCH_NAME=${BRANCH_NAME}" >> $GITHUB_ENV - name: Docker meta id: meta From 6d43ea1ca7252c48cc3810d2e2ce18292f8ebccd Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Thu, 2 Apr 2026 14:02:49 +0500 Subject: [PATCH 73/91] docs: add clarifying comments --- .github/workflows/e2e-tests-on-pull-request.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/e2e-tests-on-pull-request.yml b/.github/workflows/e2e-tests-on-pull-request.yml index ad40846..9968e5f 100644 --- a/.github/workflows/e2e-tests-on-pull-request.yml +++ b/.github/workflows/e2e-tests-on-pull-request.yml @@ -21,10 +21,11 @@ jobs: repository: TourmalineCore/to-dos-local-env ref: feature/add-to-dos-api-cpp-release - # We need to make sure that the values-to-dos-api-cpp.yaml.gotmpl file hasn't been modified + # we need to make sure that the values-to-dos-api-cpp.yaml.gotmpl file hasn't been modified # in the `to-dos-local-env` repository, to ensure that the test runs as expected. - name: Verify values file checksum run: | + # the awk command is required to extract the target hash from the output of the sha256sum command VALUES_HASH=$(sha256sum deploy/values-to-dos-api-cpp.yaml.gotmpl | awk '{ print $1 }') if [ "$VALUES_HASH" != "88d14f527d1c39d9af93ddbe2f41462f01b523bdc340b4c62cb72c31a4c46d1f" ]; then echo "ERROR: checksum mismatch" From f88031868b1549ccc1ef4d84587c89c931d3f172 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Thu, 2 Apr 2026 14:16:31 +0500 Subject: [PATCH 74/91] ci(workflows): #14: comment out the step to retrieve the version from __version, since the functionality to update the version in __version is not yet available --- .github/workflows/.reusable-docker-build-and-push.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/.reusable-docker-build-and-push.yml b/.github/workflows/.reusable-docker-build-and-push.yml index 3cc741f..127d6a0 100644 --- a/.github/workflows/.reusable-docker-build-and-push.yml +++ b/.github/workflows/.reusable-docker-build-and-push.yml @@ -178,9 +178,10 @@ jobs: - name: Check out the repo uses: actions/checkout@v4 - - name: Add SEMVER_VERSION Env Var with Value from __version File - run: | - echo "SEMVER_VERSION=$(cat __version)" >>${GITHUB_ENV} + # TODO: This needs to be reverted once the semantic versioning workflow is added + # - name: Add SEMVER_VERSION Env Var with Value from __version File + # run: | + # echo "SEMVER_VERSION=$(cat __version)" >>${GITHUB_ENV} - name: Extract metadata (tags, labels) for Docker id: meta @@ -193,7 +194,8 @@ jobs: # full length sha type=sha,format=long # SemVer human readable version - type=raw,value=${{ env.SEMVER_VERSION }} + # TODO: This needs to be reverted once the semantic versioning workflow is added + # type=raw,value=${{ env.SEMVER_VERSION }} # set latest tag for default branch # https://github.com/docker/metadata-action/issues/171 explains how to tag latest only on default branch type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} From e6534936cfdada8d770365500f52f80dcfdb53cd Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Tue, 7 Apr 2026 08:48:48 +0500 Subject: [PATCH 75/91] docs(workflows): #14: add a comment to the GitHub workflow that clarifies how caching works --- .github/workflows/.reusable-docker-build-and-push.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/.reusable-docker-build-and-push.yml b/.github/workflows/.reusable-docker-build-and-push.yml index 127d6a0..dec87e5 100644 --- a/.github/workflows/.reusable-docker-build-and-push.yml +++ b/.github/workflows/.reusable-docker-build-and-push.yml @@ -64,6 +64,13 @@ jobs: labels: ${{ steps.meta.outputs.labels }} tags: ${{ env.REGISTRY_IMAGE }} outputs: type=image,push-by-digest=true,name-canonical=true,push=true + # The `cache-from` and `cache-to` instructions allow you to load and unload the image cache on ghcr.io, + # respectively. The `mode=max` option allows each instruction in the Dockerfile to be cached separately, + # regardless of the build stage (in the case of a multi-stage build), which speeds up image builds + # significantly if there have been no changes to the instruction. + # More info: + # - https://github.com/docker/build-push-action/ + # - https://docs.docker.com/build/cache/backends/#cache-mode cache-from: type=registry,ref=${{ env.REGISTRY_IMAGE }}:cache-amd64-${{ env.BRANCH_NAME }} cache-to: type=registry,ref=${{ env.REGISTRY_IMAGE }}:cache-amd64-${{ env.BRANCH_NAME }},mode=max From ed9da393f49e4f8283ab9c44601de477b9f12904 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Tue, 7 Apr 2026 09:04:23 +0500 Subject: [PATCH 76/91] refactor(app-config): #14: remove the default value from app-config to prevent undefined behavior if the required environment variables are missing --- src/app-config.cpp | 77 +++++++++++++++++++++------------------------- src/app-config.h | 25 ++++++++------- 2 files changed, 47 insertions(+), 55 deletions(-) diff --git a/src/app-config.cpp b/src/app-config.cpp index fc147d0..2a5b6a5 100644 --- a/src/app-config.cpp +++ b/src/app-config.cpp @@ -2,61 +2,54 @@ AppConfig::AppConfig() { - setApiHost(getEnv("API_HOST", "0.0.0.0")); - setApiPort(getEnvInt("API_PORT", 80)); - setApiNumThreads(getEnvInt("API_NUMBER_OF_THREADS", 1)); - setDatabaseHost(getEnv("POSTGRES_HOST", "0.0.0.0")); - setDatabasePort(getEnv("POSTGRES_PORT", "5432")); - setDatabaseName(getEnv("POSTGRES_DB", "to-dos-api-cpp-db")); - setDatabaseUser(getEnv("POSTGRES_USER", "postgres")); - setDatabasePassword(getEnv("POSTGRES_PASSWORD", "password")); + setApiHost(getEnv("API_HOST")); + setApiPort(getEnvInt("API_PORT")); + setApiNumThreads(getEnvInt("API_NUMBER_OF_THREADS")); + setDatabaseHost(getEnv("POSTGRES_HOST")); + setDatabasePort(getEnv("POSTGRES_PORT")); + setDatabaseName(getEnv("POSTGRES_DB")); + setDatabaseUser(getEnv("POSTGRES_USER")); + setDatabasePassword(getEnv("POSTGRES_PASSWORD")); } AppConfig& AppConfig::GetInstance() { - static AppConfig instance; + static AppConfig instance; return instance; } -std::string AppConfig::getEnv(std::string name, std::string defaultValue) +std::string AppConfig::getEnv(std::string name) { char* value = std::getenv(name.c_str()); - if (value) - { - return std::string(value); - } - else - { - LOG_WARN << "Failed to get the value of the " << name << " environment variable; the default value will be used: " << defaultValue; - return defaultValue; + if (!value) { + throw std::invalid_argument( + "error: failed to extract the " + name + " environment variable value" + ); } + + return std::string(value); } -uint32_t AppConfig::getEnvInt(std::string name, uint32_t defaultValue) +std::uint32_t AppConfig::getEnvInt(std::string name) { char* value = std::getenv(name.c_str()); - if (!value) - return defaultValue; + if (!value) { + throw std::invalid_argument( + "error: failed to extract the " + name + " environment variable value" + ); + } - try - { - auto result = std::stoi(value); - auto limit = std::numeric_limits::max(); + auto result = std::stoi(value); + auto limit = std::numeric_limits::max(); - if (result < 0 || result > limit) - throw std::overflow_error( - "error: an attempt to write a " + name + " value which is larger than what can be stored in a " + std::to_string(limit) - ); + if (result < 0 || result > limit) + throw std::overflow_error( + "error: an attempt to write a " + name + " value which is larger than what can be stored in a " + std::to_string(limit) + ); - return static_cast(result); - } - catch (const std::exception& e) - { - LOG_ERROR << e.what(); - return defaultValue; - } + return static_cast(result); } trantor::Logger::LogLevel AppConfig::parseLogLevel(const std::string& level) @@ -80,15 +73,15 @@ void AppConfig::setApiHost(std::string apiHost) LOG_DEBUG << "Update value: apiHost_=" << apiHost_; }; -void AppConfig::setApiPort(uint32_t apiPort) +void AppConfig::setApiPort(std::uint32_t apiPort) { apiPort_ = apiPort; LOG_DEBUG << "Update value: apiPort_=" << apiPort_; }; -void AppConfig::setApiNumThreads(uint32_t apiNumThreads) +void AppConfig::setApiNumThreads(std::uint32_t apiNumThreads) { - uint32_t numberOfThreads = apiNumThreads; + std::uint32_t numberOfThreads = apiNumThreads; if (numberOfThreads > 0) { @@ -136,14 +129,14 @@ void AppConfig::setDatabasePassword(std::string databasePassword) const std::string& AppConfig::getApiHost() const { return apiHost_; } -const uint32_t& AppConfig::getApiPort() const +const std::uint32_t& AppConfig::getApiPort() const { return apiPort_; } -const uint32_t& AppConfig::getApiNumThreads() const +const std::uint32_t& AppConfig::getApiNumThreads() const { return apiNumThreads_; } const trantor::Logger::LogLevel AppConfig::getApiLogLevel() -{ return parseLogLevel(getEnv("API_LOG_LEVEL", "INFO")); } +{ return parseLogLevel(getEnv("API_LOG_LEVEL")); } const std::string& AppConfig::getDatabaseHost() const { return databaseHost_; } diff --git a/src/app-config.h b/src/app-config.h index d54d4c7..c0c99c7 100644 --- a/src/app-config.h +++ b/src/app-config.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -19,8 +20,8 @@ class AppConfig AppConfig& operator=(const AppConfig&) = delete; std::string apiHost_; - uint32_t apiPort_; - uint32_t apiNumThreads_; + std::uint32_t apiPort_; + std::uint32_t apiNumThreads_; std::string databaseHost_; std::string databasePort_; @@ -40,14 +41,14 @@ class AppConfig * @param apiPort is port on which the API will be run * @return void */ - void setApiPort(uint32_t apiPort); + void setApiPort(std::uint32_t apiPort); /** * @brief Setter for the `apiNumThreads_` variable * @param apiNumThreads is number of threads on which the API will run * @return void */ - void setApiNumThreads(uint32_t apiNumThreads); + void setApiNumThreads(std::uint32_t apiNumThreads); /** * @brief Setter for the `databaseHost_` variable @@ -94,18 +95,16 @@ class AppConfig /** * @brief Function for retrieving the value of an environment variable * @param name is the name of the environment variable - * @param defaultValue is the value that will be used if the environment variable doesn't exist * @return std::string */ - static std::string getEnv(std::string name, std::string defaultValue); + static std::string getEnv(std::string name); /** * @brief Function that retrieves a value from an environment variable and converts it to an integer type * @param name is the name of the environment variable - * @param defaultValue is the value that will be used if the environment variable doesn't exist - * @return uint32_t + * @return std::uint32_t */ - static uint32_t getEnvInt(std::string name, uint32_t defaultValue); + static std::uint32_t getEnvInt(std::string name); /** * @brief Function for converting a string representing a logging level to the `trantor` type. Default is `trantor::Logger::kError` @@ -121,15 +120,15 @@ class AppConfig /** * @brief Getter for the `apiPort_` variable - * @return const uint32_t + * @return const std::uint32_t */ - const uint32_t& getApiPort() const; + const std::uint32_t& getApiPort() const; /** * @brief Getter for the `apiNumThreads_` variable - * @return const uint32_t + * @return const std::uint32_t */ - const uint32_t& getApiNumThreads() const; + const std::uint32_t& getApiNumThreads() const; /** * @brief Getter for the `apiLogLevel_` variable From 96db180ac3f52c4661bad12dbac2687d11bae1f6 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Tue, 7 Apr 2026 11:25:16 +0500 Subject: [PATCH 77/91] chore(devcontainer): #14: add the forwardPorts and portsAttributes configurations to explicitly configure the ports --- .devcontainer/devcontainer.json | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 82b43b9..ebb279d 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -6,7 +6,26 @@ "dockerfile": "Dockerfile" }, // Use 'forwardPorts' to make a list of ports inside the container available locally. - // "forwardPorts": [], + "forwardPorts": [ + 4501, + 7501, + 9501 + ], + // More information about 'portsAttributes' here: https://containers.dev/implementors/json_reference/#port-attributes + "portsAttributes": { + "4501": { + "label": "VSCode Dev Container API Running Port", + "requireLocalPort": true + }, + "7501": { + "label": "VSCode Dev Container DB Running Port", + "requireLocalPort": true + }, + "9501": { + "label": "VSCode Dev Container PgAdmin Running Port", + "requireLocalPort": true + } + }, // Use 'postCreateCommand' to run commands after the container is created. "postCreateCommand": "conan remote add local-recipes ./deps --type=local-recipes-index --force", // Add a local recipes store for odb libraries. Need force flag for redefine while second time container is up // Configure tool-specific properties. From bc728ce2bf676632e0d40951ed73026cd38b53f9 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Tue, 7 Apr 2026 11:29:16 +0500 Subject: [PATCH 78/91] docs(readme): #14: add an Allocated Ports & Services section to the README to explicitly list the ports used by the services --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index 7f7790f..7fd1a93 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,18 @@ To launch the executable, click Launch in the CMake extension. Alternatively, you can use make targets. - To run the application, use the `make run` command. When the command is executed, the project's dependencies will be checked and, if necessary, installed and compiled. +## Allocated Ports & Services + +| Service Name | Api in Dev Container/Codespaces | Api in IDE | Api in Docker Compose | Db in Docker Compose | Reserved for MockServer in Docker Compose | PgAdmin in Docker Compose | +| :------------------------- | :-----------------------------: | :--------: | :-------------------: | :-------------------: | :-------------------------: | :-------------------------: | +| to-dos-api-cpp | 4501 | 5501 | 6501 | 7501 | 8501 | 9501 | + +Full docs about the allocated ports, reasoning, and the other services bindings in this infrastructre setup are available [here](https://github.com/TourmalineCore/inner-circle-documentation/blob/master/code-style/api-code-style.md#ports). + +You can go to `Ports` tab in the `Terminal` parent panel to find available services. + +The most useful is `PgAdmin` http://localhost:9501 (password is `postgres`). + ## Linters The project includes the `clang-tidy` code analyzer and the `clang-format` formatter. Configuration files are located in the project root: `.clang-tidy` and `.clang-format`, respectively. From 39daf9d8b10ed9e2e72dd8a9ef080176bdf143df Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Tue, 7 Apr 2026 11:41:41 +0500 Subject: [PATCH 79/91] feat(app-config): #14: add error messages for incorrectly defined environment variable values --- src/app-config.cpp | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/app-config.cpp b/src/app-config.cpp index 2a5b6a5..940b258 100644 --- a/src/app-config.cpp +++ b/src/app-config.cpp @@ -22,10 +22,9 @@ std::string AppConfig::getEnv(std::string name) { char* value = std::getenv(name.c_str()); - if (!value) { - throw std::invalid_argument( - "error: failed to extract the " + name + " environment variable value" - ); + if (!value) + { + throw std::invalid_argument("error: failed to extract the " + name + " environment variable value"); } return std::string(value); @@ -35,19 +34,18 @@ std::uint32_t AppConfig::getEnvInt(std::string name) { char* value = std::getenv(name.c_str()); - if (!value) { - throw std::invalid_argument( - "error: failed to extract the " + name + " environment variable value" - ); + if (!value) + { + throw std::invalid_argument("error: failed to extract the " + name + " environment variable value"); } auto result = std::stoi(value); auto limit = std::numeric_limits::max(); if (result < 0 || result > limit) - throw std::overflow_error( - "error: an attempt to write a " + name + " value which is larger than what can be stored in a " + std::to_string(limit) - ); + { + throw std::overflow_error("error: an attempt to write a " + name + " value which is less than 0 or larger than " + std::to_string(limit)); + } return static_cast(result); } From 6e0feef9cfd017cf7a2dae7ced564054dfeaad3b Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Tue, 7 Apr 2026 12:01:30 +0500 Subject: [PATCH 80/91] fix(tests): #14: correct the name of the Makefile target as it had previously been changed; add a clarifying comment --- .github/workflows/e2e-tests-on-pull-request.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/e2e-tests-on-pull-request.yml b/.github/workflows/e2e-tests-on-pull-request.yml index 9968e5f..4fa0c71 100644 --- a/.github/workflows/e2e-tests-on-pull-request.yml +++ b/.github/workflows/e2e-tests-on-pull-request.yml @@ -56,7 +56,9 @@ jobs: # we need to properly expose KUBECONFIG as an absolute path, pwd prints current working directory path export KUBECONFIG=$(pwd)/.to-dos-cluster-kubeconfig - make apply-releases-with-cpp-api + # When called, Makefile targets execute a sequence of commands. + # The targets are defined in the Makefile located in the root of the to-dos-local-env repository. + make deploy-with-cpp-api push: never - name: Check out the repo From b6a59b67b99938d7ed3381889d2f44462d3b523c Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Tue, 7 Apr 2026 13:11:02 +0500 Subject: [PATCH 81/91] refactor(tests): #14: remove the link to the branch in the to-dos-local-env repository --- .github/workflows/e2e-tests-on-pull-request.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/e2e-tests-on-pull-request.yml b/.github/workflows/e2e-tests-on-pull-request.yml index 4fa0c71..1ffa5ad 100644 --- a/.github/workflows/e2e-tests-on-pull-request.yml +++ b/.github/workflows/e2e-tests-on-pull-request.yml @@ -19,7 +19,6 @@ jobs: uses: actions/checkout@v4 with: repository: TourmalineCore/to-dos-local-env - ref: feature/add-to-dos-api-cpp-release # we need to make sure that the values-to-dos-api-cpp.yaml.gotmpl file hasn't been modified # in the `to-dos-local-env` repository, to ensure that the test runs as expected. From e0474c1af980af9096875ebce094e40a73516430 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Tue, 21 Apr 2026 09:00:12 +0500 Subject: [PATCH 82/91] chore: #14: rename it to match the test --- e2e/{item-types-happy-path.feature => to-dos-happy-path.feature} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename e2e/{item-types-happy-path.feature => to-dos-happy-path.feature} (100%) diff --git a/e2e/item-types-happy-path.feature b/e2e/to-dos-happy-path.feature similarity index 100% rename from e2e/item-types-happy-path.feature rename to e2e/to-dos-happy-path.feature From d2df2688f84b2bbc40a3a97002a0e619d2ba5368 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Tue, 21 Apr 2026 09:01:20 +0500 Subject: [PATCH 83/91] ci: #14: merge the dependency loading layers in a single layer so as not to increase the image size --- e2e/KarateDockerfile | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/e2e/KarateDockerfile b/e2e/KarateDockerfile index 1752a55..ffb3f0f 100644 --- a/e2e/KarateDockerfile +++ b/e2e/KarateDockerfile @@ -1,11 +1,9 @@ FROM eclipse-temurin:17-jre-noble -RUN apt-get update && apt-get install -y curl - -RUN apt-get install -y unzip +RUN apt-get update && apt-get install -y curl unzip && apt-get autoremove -y \ + && apt-get clean -y \ + && rm -rf /var/lib/apt/lists/* RUN curl -o /karate.jar -L 'https://github.com/intuit/karate/releases/download/v1.5.1/karate-1.5.1.jar' -# COPY ./js-utils.js . - ENTRYPOINT ["java", "-jar", "karate.jar"] \ No newline at end of file From e8d96e9c89b4fd151d35d555c7707a34fd7c24c5 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Tue, 21 Apr 2026 09:02:58 +0500 Subject: [PATCH 84/91] feat: #14: add clarifying logs; add error handling; remove redundant code; format --- src/app-config.cpp | 74 +++++++++++++++++++++++++++++++++++----------- src/app-config.h | 18 +++++------ 2 files changed, 65 insertions(+), 27 deletions(-) diff --git a/src/app-config.cpp b/src/app-config.cpp index 940b258..450d4cf 100644 --- a/src/app-config.cpp +++ b/src/app-config.cpp @@ -18,7 +18,7 @@ AppConfig& AppConfig::GetInstance() return instance; } -std::string AppConfig::getEnv(std::string name) +std::string AppConfig::getEnv(std::string& name) { char* value = std::getenv(name.c_str()); @@ -30,7 +30,7 @@ std::string AppConfig::getEnv(std::string name) return std::string(value); } -std::uint32_t AppConfig::getEnvInt(std::string name) +std::uint64_t AppConfig::getEnvInt(std::string& name) { char* value = std::getenv(name.c_str()); @@ -39,30 +39,49 @@ std::uint32_t AppConfig::getEnvInt(std::string name) throw std::invalid_argument("error: failed to extract the " + name + " environment variable value"); } - auto result = std::stoi(value); - auto limit = std::numeric_limits::max(); + int result; - if (result < 0 || result > limit) + try { - throw std::overflow_error("error: an attempt to write a " + name + " value which is less than 0 or larger than " + std::to_string(limit)); + result = static_cast(std::stoul(value)); + } + catch (const std::exception& e) + { + throw std::invalid_argument("error: failed to parse " + name + ": " + e.what()); + } + + if (result < 0) + { + throw std::overflow_error("error: an attempt to write a " + name + " value which is less than 0"); } - return static_cast(result); + return result; } trantor::Logger::LogLevel AppConfig::parseLogLevel(const std::string& level) { // kTrace < kDebug < kInfo < kWarn < kError if (level == "TRACE") + { return trantor::Logger::kTrace; + } else if (level == "DEBUG") + { return trantor::Logger::kDebug; + } else if (level == "INFO") + { return trantor::Logger::kInfo; + } else if (level == "WARN") + { return trantor::Logger::kWarn; + } else + { + LOG_WARN << "Unknown log level value: " << level << ", defaulting to kError"; return trantor::Logger::kError; + } } void AppConfig::setApiHost(std::string apiHost) @@ -125,28 +144,47 @@ void AppConfig::setDatabasePassword(std::string databasePassword) }; const std::string& AppConfig::getApiHost() const -{ return apiHost_; } +{ + return apiHost_; +} -const std::uint32_t& AppConfig::getApiPort() const -{ return apiPort_; } +const std::uint64_t& AppConfig::getApiPort() const +{ + return apiPort_; +} -const std::uint32_t& AppConfig::getApiNumThreads() const -{ return apiNumThreads_; } +const std::uint64_t& AppConfig::getApiNumThreads() const +{ + return apiNumThreads_; +} const trantor::Logger::LogLevel AppConfig::getApiLogLevel() -{ return parseLogLevel(getEnv("API_LOG_LEVEL")); } +{ + // The log level may change at runtime, so caching during initialization is not recommended + return parseLogLevel(getEnv("API_LOG_LEVEL")); +} const std::string& AppConfig::getDatabaseHost() const -{ return databaseHost_; } +{ + return databaseHost_; +} const std::string& AppConfig::getDatabasePort() const -{ return databasePort_; } +{ + return databasePort_; +} const std::string& AppConfig::getDatabaseName() const -{ return databaseName_; } +{ + return databaseName_; +} const std::string& AppConfig::getDatabaseUser() const -{ return databaseUser_; } +{ + return databaseUser_; +} const std::string& AppConfig::getDatabasePassword() const -{ return databasePassword_; } \ No newline at end of file +{ + return databasePassword_; +} \ No newline at end of file diff --git a/src/app-config.h b/src/app-config.h index c0c99c7..2403642 100644 --- a/src/app-config.h +++ b/src/app-config.h @@ -1,8 +1,8 @@ #pragma once #include -#include #include +#include #include #include @@ -20,8 +20,8 @@ class AppConfig AppConfig& operator=(const AppConfig&) = delete; std::string apiHost_; - std::uint32_t apiPort_; - std::uint32_t apiNumThreads_; + std::uint64_t apiPort_; + std::uint64_t apiNumThreads_; std::string databaseHost_; std::string databasePort_; @@ -102,9 +102,9 @@ class AppConfig /** * @brief Function that retrieves a value from an environment variable and converts it to an integer type * @param name is the name of the environment variable - * @return std::uint32_t + * @return std::uint64_t */ - static std::uint32_t getEnvInt(std::string name); + static std::uint64_t getEnvInt(std::string name); /** * @brief Function for converting a string representing a logging level to the `trantor` type. Default is `trantor::Logger::kError` @@ -120,15 +120,15 @@ class AppConfig /** * @brief Getter for the `apiPort_` variable - * @return const std::uint32_t + * @return const std::uint64_t */ - const std::uint32_t& getApiPort() const; + const std::uint64_t& getApiPort() const; /** * @brief Getter for the `apiNumThreads_` variable - * @return const std::uint32_t + * @return const std::uint64_t */ - const std::uint32_t& getApiNumThreads() const; + const std::uint64_t& getApiNumThreads() const; /** * @brief Getter for the `apiLogLevel_` variable From 27e3c7dd656c7c9d3d03d1c4bdd1830ef2b6c1eb Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Tue, 21 Apr 2026 09:46:39 +0500 Subject: [PATCH 85/91] test: #14: rename it to match the test --- e2e/to-dos-happy-path.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/to-dos-happy-path.feature b/e2e/to-dos-happy-path.feature index 6ca7d92..7bc5ad6 100644 --- a/e2e/to-dos-happy-path.feature +++ b/e2e/to-dos-happy-path.feature @@ -1,4 +1,4 @@ -Feature: Item Types +Feature: To-Dos # https://github.com/karatelabs/karate/issues/1191 # https://github.com/karatelabs/karate?tab=readme-ov-file#karate-fork From e572bc340d043789bea22fbde1674126fd19323f Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Mon, 8 Jun 2026 15:58:26 +0500 Subject: [PATCH 86/91] refactor: #14: formatting changes --- docker-compose.yml | 2 +- src/app-config.cpp | 36 ++++++++++-------------------------- 2 files changed, 11 insertions(+), 27 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 5b5b677..7768bda 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -63,8 +63,8 @@ services: context: . args: EXCLUDE_UNIT_TESTS_FROM_BUILD: "true" - # We need to override the entrypoint to apply the migrations to the database before the API starts entrypoint: [ "/bin/bash", "-c", "alembic -c ./alembic/alembic.ini upgrade head && ./to-dos-api" ] + # We need to override the entrypoint to apply the migrations to the database before the API starts env_file: - .env # Unless you override the host to the service name, the API will diff --git a/src/app-config.cpp b/src/app-config.cpp index 450d4cf..21eef29 100644 --- a/src/app-config.cpp +++ b/src/app-config.cpp @@ -18,7 +18,7 @@ AppConfig& AppConfig::GetInstance() return instance; } -std::string AppConfig::getEnv(std::string& name) +std::string AppConfig::getEnv(std::string name) { char* value = std::getenv(name.c_str()); @@ -30,7 +30,7 @@ std::string AppConfig::getEnv(std::string& name) return std::string(value); } -std::uint64_t AppConfig::getEnvInt(std::string& name) +std::uint64_t AppConfig::getEnvInt(std::string name) { char* value = std::getenv(name.c_str()); @@ -144,19 +144,13 @@ void AppConfig::setDatabasePassword(std::string databasePassword) }; const std::string& AppConfig::getApiHost() const -{ - return apiHost_; -} +{ return apiHost_; } const std::uint64_t& AppConfig::getApiPort() const -{ - return apiPort_; -} +{ return apiPort_; } const std::uint64_t& AppConfig::getApiNumThreads() const -{ - return apiNumThreads_; -} +{ return apiNumThreads_; } const trantor::Logger::LogLevel AppConfig::getApiLogLevel() { @@ -165,26 +159,16 @@ const trantor::Logger::LogLevel AppConfig::getApiLogLevel() } const std::string& AppConfig::getDatabaseHost() const -{ - return databaseHost_; -} +{ return databaseHost_; } const std::string& AppConfig::getDatabasePort() const -{ - return databasePort_; -} +{ return databasePort_; } const std::string& AppConfig::getDatabaseName() const -{ - return databaseName_; -} +{ return databaseName_; } const std::string& AppConfig::getDatabaseUser() const -{ - return databaseUser_; -} +{ return databaseUser_; } const std::string& AppConfig::getDatabasePassword() const -{ - return databasePassword_; -} \ No newline at end of file +{ return databasePassword_; } \ No newline at end of file From 6b890d92e39bd6ce37221ca6282d34c6f686b7af Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Tue, 9 Jun 2026 09:13:31 +0500 Subject: [PATCH 87/91] fix: #14: update database service credentials for to-dos-api-cpp service so that connection is made using correct credentials within network --- docker-compose.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 7768bda..5232274 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -63,14 +63,20 @@ services: context: . args: EXCLUDE_UNIT_TESTS_FROM_BUILD: "true" - entrypoint: [ "/bin/bash", "-c", "alembic -c ./alembic/alembic.ini upgrade head && ./to-dos-api" ] # We need to override the entrypoint to apply the migrations to the database before the API starts + entrypoint: [ "/bin/bash", "-c", "alembic -c ./alembic/alembic.ini upgrade head && ./to-dos-api" ] env_file: - .env - # Unless you override the host to the service name, the API will + # Unless you override the host to the service name, the API will # not be able to locate the database container on the network environment: + # Without redefining the host to 0.0.0.0 (all network interfaces), + # connections from outside the container will not be possible + - API_HOST=0.0.0.0 + # To connect to the database service on the internal network, + # the internal network database credentials must be used. - POSTGRES_HOST=to-dos-api-cpp-db + - POSTGRES_PORT=5432 ports: - 6501:80 networks: From 12a03c1ce357df70119d47f41f6425bf6c1fd260 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Tue, 9 Jun 2026 13:18:28 +0500 Subject: [PATCH 88/91] fix: #14: add character required for correct execution to conditions for including unit tests in build --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0786d89..2b209b9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,7 +33,7 @@ target_link_libraries(${PROJECT_NAME} ) # Tests -if (ENV{EXCLUDE_UNIT_TESTS_FROM_BUILD} STREQUAL "true") +if ("$ENV{EXCLUDE_UNIT_TESTS_FROM_BUILD}" STREQUAL "true") # Disable test here needed for uncompatible builds, bc `enable_testing` is run executable tests file for collecting tests. # E.g. if executable tests are builded for another OS or CPU arch. message(STATUS "UNIT TEST DISABLED") From 9de6aa113ed409138ad23f3ba403cfff6ef6353d Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Thu, 11 Jun 2026 15:44:22 +0500 Subject: [PATCH 89/91] infra: #14: change to-dos-api-cpp helmfile hash to github repo actions variable so that make workflow more flexible --- .github/workflows/e2e-tests-on-pull-request.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/e2e-tests-on-pull-request.yml b/.github/workflows/e2e-tests-on-pull-request.yml index 1ffa5ad..d98b936 100644 --- a/.github/workflows/e2e-tests-on-pull-request.yml +++ b/.github/workflows/e2e-tests-on-pull-request.yml @@ -26,7 +26,7 @@ jobs: run: | # the awk command is required to extract the target hash from the output of the sha256sum command VALUES_HASH=$(sha256sum deploy/values-to-dos-api-cpp.yaml.gotmpl | awk '{ print $1 }') - if [ "$VALUES_HASH" != "88d14f527d1c39d9af93ddbe2f41462f01b523bdc340b4c62cb72c31a4c46d1f" ]; then + if [ "$VALUES_HASH" != ${{ vars.TO_DOS_API_CPP_LOCAL_ENV_HELMFILE_HASH }} ]; then echo "ERROR: checksum mismatch" echo "Expected: 88d14f527d1c39d9af93ddbe2f41462f01b523bdc340b4c62cb72c31a4c46d1f" echo "Actual: $VALUES_HASH" From fa6e104b6bd16497c71246047582017589d4820b Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Thu, 11 Jun 2026 15:46:36 +0500 Subject: [PATCH 90/91] infra: #14: change condition for to-dos-api-cpp service in to-dos-api-cpp-karate-tests service in docker compose --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 5232274..4c1fe33 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -91,7 +91,7 @@ services: context: ./e2e depends_on: to-dos-api-cpp: - condition: service_started + condition: service_healthy command: [ "karate", "/karate" ] volumes: # similar to mock-server volumes we need to support both runs: from Dev Container and from OS From b00e44b60141fe9e8b4cfaffe16565809703bab5 Mon Sep 17 00:00:00 2001 From: Artem Sheptunov <106321977+Infindery@users.noreply.github.com> Date: Thu, 11 Jun 2026 15:53:07 +0500 Subject: [PATCH 91/91] build: #14: remove redundant slash in curl parameter so that prevent undefined behaivor when workdir was redifined --- e2e/KarateDockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/KarateDockerfile b/e2e/KarateDockerfile index ffb3f0f..ff828ed 100644 --- a/e2e/KarateDockerfile +++ b/e2e/KarateDockerfile @@ -4,6 +4,6 @@ RUN apt-get update && apt-get install -y curl unzip && apt-get autoremove -y \ && apt-get clean -y \ && rm -rf /var/lib/apt/lists/* -RUN curl -o /karate.jar -L 'https://github.com/intuit/karate/releases/download/v1.5.1/karate-1.5.1.jar' +RUN curl -o karate.jar -L 'https://github.com/intuit/karate/releases/download/v1.5.1/karate-1.5.1.jar' ENTRYPOINT ["java", "-jar", "karate.jar"] \ No newline at end of file