From 47d5a110ec14840d78cb0c335a10d17622bd1b8f Mon Sep 17 00:00:00 2001 From: Sandeep Vattapparambil <7328658+Sandeepv68@users.noreply.github.com> Date: Fri, 24 Jul 2026 13:20:23 +0530 Subject: [PATCH] env updates and ci cd updates --- .dockerignore | 16 ++++ .github/workflows/cd.yml | 138 ++++++++++++++++++++++++++++++++++ .github/workflows/ci.yml | 18 +---- .github/workflows/publish.yml | 119 +++++++++++++++++++++++++++++ Dockerfile | 37 +++++++++ README.md | 29 +++++++ docker-compose.yml | 29 +++++++ tests/test_behavioral.cpp | 14 +++- 8 files changed, 385 insertions(+), 15 deletions(-) create mode 100644 .dockerignore create mode 100644 .github/workflows/cd.yml create mode 100644 .github/workflows/publish.yml create mode 100644 Dockerfile create mode 100644 docker-compose.yml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..af2faaa --- /dev/null +++ b/.dockerignore @@ -0,0 +1,16 @@ +build/ +out/ +cmake-build-*/ +.git/ +.github/ +.vscode/ +.idea/ +*.swp +*.swo +*~ +vcpkg_installed/ +conan_provider.cmake +conanbuildinfo.* +conaninfo.txt +graph_info.json +InstallationLog.txt diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml new file mode 100644 index 0000000..175bf6a --- /dev/null +++ b/.github/workflows/cd.yml @@ -0,0 +1,138 @@ +name: CD + +on: + push: + tags: [ 'v*' ] + +permissions: + contents: write + +jobs: + build-linux: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Build release (Docker) + run: | + docker compose build + docker compose run build + + - name: Package artifacts + run: | + docker compose run --rm build sh -c " + tar -czf /app/wrapsplash++-linux-x64.tar.gz \ + -C /app/build lib/libwrapsplash++.a \ + -C /app include/wrapsplash + " + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: linux-x64 + path: wrapsplash++-linux-x64.tar.gz + + build-macos: + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + + - name: Install dependencies + run: brew install cmake curl nlohmann-json openssl + + - name: Build + run: | + cmake -B build -DCMAKE_BUILD_TYPE=Release \ + -DWRAPSLASH_BUILD_TESTS=OFF -DWRAPSLASH_BUILD_EXAMPLES=OFF + cmake --build build --config Release + + - name: Package artifacts + run: | + tar -czf wrapsplash++-macos-arm64.tar.gz \ + -C build lib/libwrapsplash++.a \ + -C .. include/wrapsplash + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: macos-arm64 + path: wrapsplash++-macos-arm64.tar.gz + + build-windows: + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup vcpkg + run: | + git clone https://github.com/microsoft/vcpkg.git C:\vcpkg + C:\vcpkg\bootstrap-vcpkg.bat + shell: cmd + + - name: Build + run: | + cmake -B build -DCMAKE_BUILD_TYPE=Release ^ + -DWRAPSLASH_BUILD_TESTS=OFF -DWRAPSLASH_BUILD_EXAMPLES=OFF ^ + -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake + cmake --build build --config Release + shell: cmd + + - name: Package artifacts + run: | + Compress-Archive -Path build\Release\wrapsplash++.lib, include\wrapsplash\* ` + -DestinationPath wrapsplash++-windows-x64.zip + shell: pwsh + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: windows-x64 + path: wrapsplash++-windows-x64.zip + + release: + needs: [build-linux, build-macos, build-windows] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: artifacts + + - name: Generate changelog + id: changelog + run: | + TAG="${GITHUB_REF#refs/tags/}" + PREV_TAG=$(git tag --sort=-v:refname | grep '^v' | sed -n '2p') + if [ -n "$PREV_TAG" ]; then + CHANGELOG=$(git log --pretty=format:"- %s (%h)" "$PREV_TAG".."$TAG") + else + CHANGELOG=$(git log --pretty=format:"- %s (%h)" "$TAG") + fi + echo "changelog<> "$GITHUB_OUTPUT" + echo "$CHANGELOG" >> "$GITHUB_OUTPUT" + echo "EOF" >> "$GITHUB_OUTPUT" + + - name: Create release + uses: softprops/action-gh-release@v2 + with: + name: WrapSplash++ ${{ github.ref_name }} + body: | + ## What's Changed + ${{ steps.changelog.outputs.changelog }} + + ## Downloads + | Platform | Archive | + |----------|---------| + | Linux x64 | `wrapsplash++-linux-x64.tar.gz` | + | macOS ARM64 | `wrapsplash++-macos-arm64.tar.gz` | + | Windows x64 | `wrapsplash++-windows-x64.zip` | + files: | + artifacts/linux-x64/* + artifacts/macos-arm64/* + artifacts/windows-x64/* + draft: false + prerelease: false diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aa09bf2..28c958f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,21 +12,11 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Install dependencies - run: | - sudo apt-get update - sudo apt-get install -y cmake build-essential libcurl4-openssl-dev nlohmann-json3-dev libssl-dev - - - name: Build - run: | - mkdir build && cd build - cmake -DWRAPSLASH_BUILD_TESTS=ON -DWRAPSLASH_BUILD_EXAMPLES=ON .. - cmake --build . - - - name: Test + - name: Build and test (Docker) run: | - cd build - ctest --output-on-failure + docker compose build + docker compose run build + docker compose run test build-macos: runs-on: macos-latest diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..2d38ad7 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,119 @@ +name: Publish + +on: + release: + types: [published] + +permissions: + contents: write + pull-requests: write + +jobs: + publish-conan: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.x' + + - name: Install Conan + run: pip install conan + + - name: Configure Conan + run: conan profile detect --force + + - name: Export package + run: conan export . --version=${{ github.event.release.tag_name }} + + - name: Upload to Conan Center + env: + CONAN_LOGIN_USERNAME: ${{ secrets.CONAN_USERNAME }} + CONAN_PASSWORD: ${{ secrets.CONAN_PASSWORD }} + run: | + conan upload "wrapsplash-cpp/${{ github.event.release.tag_name }}" \ + --remote conancenter --confirm + + publish-vcpkg: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up vcpkg + uses: lukka/run-vcpkg@v11 + with: + vcpkgGitCommitId: ${{ vars.VCPKG_COMMIT_ID }} + + - name: Create vcpkg port + run: | + VERSION="${{ github.event.release.tag_name }}" + VERSION="${VERSION#v}" + PORT_DIR="vcpkg-port" + mkdir -p "$PORT_DIR" + + cat > "$PORT_DIR/vcpkg.json" << EOF + { + "name": "wrapsplash-cpp", + "version": "$VERSION", + "description": "A C++ wrapper for the Unsplash API", + "license": "MIT", + "supports": "!(uwp|arm)", + "dependencies": ["curl", "nlohmann-json", "openssl"] + } + EOF + + cat > "$PORT_DIR/portfile.cmake" << 'PORTFILE' + set(VCPKG_BUILD_TYPE release) + + vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO Sandeepv68/wrapsplash-cpp + REF ${{ github.event.release.tag_name }} + SHA512 0 + HEAD_REF master + ) + + vcpkg_cmake_configure( + SOURCE_PATH "${SOURCE_PATH}" + OPTIONS + -DWRAPSLASH_BUILD_TESTS=OFF + -DWRAPSLASH_BUILD_EXAMPLES=OFF + ) + + vcpkg_cmake_install() + vcpkg_cmake_config_fixup(PACKAGE_NAME wrapsplash++ CONFIG_PATH lib/cmake/wrapsplash++) + file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug") + vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/LICENSE") + PORTFILE + + - name: Submit PR to vcpkg + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + VERSION="${{ github.event.release.tag_name }}" + VERSION="${VERSION#v}" + BRANCH="auto/wrapsplash-cpp-${VERSION}" + + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + git clone https://x-access-token:${GH_TOKEN}@github.com/microsoft/vcpkg.git /tmp/vcpkg + cd /tmp/vcpkg + git checkout -b "$BRANCH" + + mkdir -p "ports/wrapsplash-cpp" + cp "${{ github.workspace }}/vcpkg-port/vcpkg.json" "ports/wrapsplash-cpp/" + cp "${{ github.workspace }}/vcpkg-port/portfile.cmake" "ports/wrapsplash-cpp/" + + git add "ports/wrapsplash-cpp/" + git commit -m "wrapsplash-cpp: update to $VERSION" + git push origin "$BRANCH" + + gh pr create \ + --repo microsoft/vcpkg \ + --title "wrapsplash-cpp: update to $VERSION" \ + --body "Updates wrapsplash-cpp to $VERSION. Release: ${{ github.event.release.html_url }}" \ + --base master \ + --head "$BRANCH" diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..42c23e3 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,37 @@ +FROM ubuntu:24.04 AS base + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + cmake \ + make \ + g++ \ + pkg-config \ + libcurl4-openssl-dev \ + nlohmann-json3-dev \ + libssl-dev \ + libgtest-dev \ + git \ + && rm -rf /var/lib/apt/lists/* + +FROM base AS build + +WORKDIR /app + +COPY CMakeLists.txt ./ +COPY cmake/ cmake/ +COPY include/ include/ +COPY src/ src/ +COPY tests/ tests/ + +RUN cmake -B build -DWRAPSLASH_BUILD_TESTS=ON -DWRAPSLASH_BUILD_EXAMPLES=ON \ + && cmake --build build -j$(nproc) + +FROM base AS test + +WORKDIR /app + +COPY --from=build /app/build build/ + +CMD ["ctest", "--test-dir", "build", "--output-on-failure"] diff --git a/README.md b/README.md index 01da935..d9bdc26 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ Before using the Unsplash API, you need to **register as a developer** and **rea * [Installation](#installation) * [Sample Usage](#sample-usage) * [Development](#development) +* [Docker](#docker) * [New API aliases](#new-api-aliases) * [Dependency](#dependency) * [Changelog](#changelog) @@ -137,6 +138,34 @@ cmake --build . ctest ``` +### Docker + +You can build and test the project entirely inside Docker, which avoids installing dependencies on your host machine. + +**Prerequisites:** [Docker Desktop](https://www.docker.com/products/docker-desktop/) must be running. + +**Build the project:** +```sh +docker compose run build +``` + +**Run the tests:** +```sh +docker compose run test +``` + +**Build and test in one shot:** +```sh +docker compose up build test +``` + +On subsequent runs, Docker caches the dependency layer and preserves compiled objects in a named volume (`build-cache`), so only changed source files are recompiled. + +**How it works:** +- `Dockerfile` installs all build dependencies (cmake, g++, curl, nlohmann-json, openssl, googletest) on Ubuntu 24.04. +- `docker-compose.yml` mounts your source code into the container and uses a `build-cache` volume for the build directory, enabling incremental builds. +- `.dockerignore` keeps the Docker build context small by excluding `build/`, `.git/`, IDE files, etc. + ### New API aliases The library includes more descriptive convenience methods such as `get_photo`, `get_random_photo`, `create_collection`, and `update_collection`. The original method names remain available for backward compatibility. diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..1a59d61 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,29 @@ +services: + build: + build: + context: . + target: base + volumes: + - .:/app + - build-cache:/app/build + working_dir: /app + command: > + sh -c " + cmake -B build -DWRAPSLASH_BUILD_TESTS=ON -DWRAPSLASH_BUILD_EXAMPLES=ON && + cmake --build build -j$$(nproc) + " + + test: + build: + context: . + target: base + volumes: + - .:/app + - build-cache:/app/build + working_dir: /app + command: ctest --test-dir build --output-on-failure + depends_on: + - build + +volumes: + build-cache: diff --git a/tests/test_behavioral.cpp b/tests/test_behavioral.cpp index 42ca390..a55baa4 100644 --- a/tests/test_behavioral.cpp +++ b/tests/test_behavioral.cpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include using json = nlohmann::json; @@ -71,7 +73,17 @@ TEST_F(HttpClientTest, HeadersAreSent) { auto response = client.request("https://postman-echo.com/headers", "GET"); EXPECT_EQ(response.status_code, 200); auto json_response = json::parse(response.body); - EXPECT_EQ(json_response["headers"]["X-Custom-Header"], "test-value"); + bool found = false; + for (auto& [key, value] : json_response["headers"].items()) { + std::string lower_key = key; + std::transform(lower_key.begin(), lower_key.end(), lower_key.begin(), ::tolower); + if (lower_key == "x-custom-header") { + EXPECT_EQ(value.get(), "test-value"); + found = true; + break; + } + } + EXPECT_TRUE(found) << "X-Custom-Header not found in response headers"; } TEST_F(HttpClientTest, InvalidUrlThrows) {