mirror of
				https://github.com/TheLartians/ModernCppStarter.git
				synced 2025-10-31 02:01:33 +01:00 
			
		
		
		
	Merge d36cb2e388 into 12cf5de1a8
				
					
				
			This commit is contained in:
		
						commit
						e4d34b3edc
					
				
					 8 changed files with 256 additions and 49 deletions
				
			
		
							
								
								
									
										6
									
								
								.dockerignore
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.dockerignore
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,6 @@ | |||
| /build* | ||||
| /.vscode | ||||
| /cpm_modules | ||||
| .DS_Store | ||||
| /.github | ||||
| /standalone/Dockerfile | ||||
							
								
								
									
										94
									
								
								.github/workflows/docker-cd.yml
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								.github/workflows/docker-cd.yml
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,94 @@ | |||
| name: run Docker CD | ||||
| 
 | ||||
| on: | ||||
|   push: | ||||
|     tags: | ||||
|       - "v*.*.*" | ||||
|   workflow_dispatch: | ||||
|     inputs: | ||||
|       tag: | ||||
|         description: "Test scenario tag" | ||||
|         required: true | ||||
|         default: "test" | ||||
|       set-latest-tag: | ||||
|         description: "Also set the 'latest' tag with this run? (y/n)" | ||||
|         required: true | ||||
|         default: "n" | ||||
| 
 | ||||
| env: | ||||
|   CPM_SOURCE_CACHE: ${{ github.workspace }}/cpm_modules | ||||
| 
 | ||||
| jobs: | ||||
|   build: | ||||
|     name: CD build docker and push to DockerHub | ||||
|     runs-on: ubuntu-latest | ||||
| 
 | ||||
|     steps: | ||||
|       - uses: actions/checkout@v2 | ||||
| 
 | ||||
|       - name: handle manual workflow start and prepare docker image tag(s) | ||||
|         id: docker-tags | ||||
|         shell: bash | ||||
|         run: | | ||||
|           if [[ "x${{ github.event.inputs.tag }}" != "x" ]]; then | ||||
|             echo "Workflow started via workflow_dispatch! Parameters: tag=${{ github.event.inputs.tag }}, set-latest-tag=${{ github.event.inputs.set-latest-tag }}" | ||||
|             tag="${{ github.event.inputs.tag }}" | ||||
|           else | ||||
|             echo "Workflow started via push with tag! Complete tag: ${GITHUB_REF:10}" | ||||
|             tag="${GITHUB_REF:11}" | ||||
|           fi | ||||
| 
 | ||||
|           if [[ "x${{ github.event.inputs.set-latest-tag }}" == "xy" || "x${{ github.event.inputs.tag }}" == "x" ]]; then | ||||
|             tags="${{ secrets.DOCKERHUB_USERNAME }}/greeter-webapi:$tag, ${{ secrets.DOCKERHUB_USERNAME }}/greeter-webapi:latest" | ||||
|             echo "Docker image release tags: $tags" | ||||
|           else | ||||
|             tags="${{ secrets.DOCKERHUB_USERNAME }}/greeter-webapi:$tag" | ||||
|             echo "Docker image release tag: $tags" | ||||
|           fi | ||||
| 
 | ||||
|           echo ::set-output name=tags::$tags | ||||
| 
 | ||||
|       # | ||||
|       # configure and build in GitHub CI as smoke test | ||||
| 
 | ||||
|       # speed up configure step by installing boost as system lib, also use Ninja for faster builds | ||||
|       - name: speed up configure and build | ||||
|         shell: bash | ||||
|         run: sudo apt-get update && sudo apt-get install -y libboost-all-dev ninja-build | ||||
| 
 | ||||
|       # use GitHub cache to cache dependencies | ||||
|       - uses: actions/cache@v2 | ||||
|         with: | ||||
|           path: "**/cpm_modules" | ||||
|           key: ${{ github.workflow }}-cpm-modules-${{ hashFiles('**/CMakeLists.txt', '**/*.cmake') }} | ||||
| 
 | ||||
|       - name: configure | ||||
|         run: cmake -S standalone -B build -G Ninja -D CMAKE_BUILD_TYPE=Release | ||||
| 
 | ||||
|       - name: build | ||||
|         run: cmake --build build | ||||
| 
 | ||||
|       # | ||||
|       # end GitHub CI local build | ||||
| 
 | ||||
|       - name: set up Docker Buildx for multi-platform support | ||||
|         uses: docker/setup-buildx-action@v1 | ||||
| 
 | ||||
|       - name: set up QEMU for multi-platform support | ||||
|         uses: docker/setup-qemu-action@v1 | ||||
| 
 | ||||
|       - name: login to DockerHub | ||||
|         uses: docker/login-action@v1 | ||||
|         with: | ||||
|           username: ${{ secrets.DOCKERHUB_USERNAME }} | ||||
|           password: ${{ secrets.DOCKERHUB_TOKEN }} | ||||
| 
 | ||||
|       - name: build Docker image and push to DockerHub | ||||
|         uses: docker/build-push-action@v2 | ||||
|         with: | ||||
|           file: ./standalone/Dockerfile | ||||
|           platforms: linux/amd64,linux/arm64 | ||||
|           push: true | ||||
|           tags: ${{ steps.docker-tags.outputs.tags }} | ||||
|           cache-from: type=gha | ||||
|           cache-to: type=gha,mode=max | ||||
							
								
								
									
										63
									
								
								.github/workflows/docker-ci.yml
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								.github/workflows/docker-ci.yml
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,63 @@ | |||
| name: run Docker CI | ||||
| 
 | ||||
| on: | ||||
|   push: | ||||
|     branches: | ||||
|       - main | ||||
|       - master | ||||
|       - add-docker-build | ||||
|   pull_request: | ||||
|     branches: | ||||
|       - main | ||||
|       - master | ||||
| 
 | ||||
| env: | ||||
|   CPM_SOURCE_CACHE: ${{ github.workspace }}/cpm_modules | ||||
| 
 | ||||
| jobs: | ||||
|   build: | ||||
|     name: CI build docker | ||||
|     runs-on: ubuntu-latest | ||||
| 
 | ||||
|     steps: | ||||
|       - uses: actions/checkout@v2 | ||||
| 
 | ||||
|       # | ||||
|       # configure and build in GitHub CI as smoke test | ||||
| 
 | ||||
|       # speed up configure step by installing boost as system lib, also use Ninja for faster builds | ||||
|       - name: speed up configure and build | ||||
|         shell: bash | ||||
|         run: sudo apt-get update && sudo apt-get install -y libboost-all-dev ninja-build | ||||
| 
 | ||||
|       # use GitHub cache to cache dependencies | ||||
|       - uses: actions/cache@v2 | ||||
|         with: | ||||
|           path: "**/cpm_modules" | ||||
|           key: ${{ github.workflow }}-cpm-modules-${{ hashFiles('**/CMakeLists.txt', '**/*.cmake') }} | ||||
| 
 | ||||
|       - name: configure | ||||
|         run: cmake -S standalone -B build -G Ninja -D CMAKE_BUILD_TYPE=Release | ||||
| 
 | ||||
|       - name: build | ||||
|         run: cmake --build build | ||||
| 
 | ||||
|       # | ||||
|       # end GitHub CI local build | ||||
| 
 | ||||
|       - name: set up Docker Buildx for multi-platform support | ||||
|         uses: docker/setup-buildx-action@v1 | ||||
| 
 | ||||
|       - name: set up QEMU for multi-platform support | ||||
|         uses: docker/setup-qemu-action@v1 | ||||
| 
 | ||||
|       # build image but do NOT push to DockerHub | ||||
|       - name: build Docker image | ||||
|         uses: docker/build-push-action@v2 | ||||
|         with: | ||||
|           file: ./standalone/Dockerfile | ||||
|           platforms: linux/amd64,linux/arm64 | ||||
|           push: false | ||||
|           tags: ${{ secrets.DOCKERHUB_USERNAME }}/greeter-webapi:ci | ||||
|           cache-from: type=gha | ||||
|           cache-to: type=gha,mode=max | ||||
							
								
								
									
										2
									
								
								.github/workflows/standalone.yml
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/standalone.yml
									
										
									
									
										vendored
									
									
								
							|  | @ -32,4 +32,4 @@ jobs: | |||
|         run: cmake --build build -j4 | ||||
| 
 | ||||
|       - name: run | ||||
|         run: ./build/Greeter | ||||
|         run: ./build/GreeterStandalone | ||||
|  |  | |||
							
								
								
									
										2
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -1,4 +1,4 @@ | |||
| /build* | ||||
| /.vscode | ||||
| /cpm_modules | ||||
| .DS_Store | ||||
| .DS_Store | ||||
|  |  | |||
|  | @ -1,6 +1,10 @@ | |||
| cmake_minimum_required(VERSION 3.14...3.22) | ||||
| 
 | ||||
| project(GreeterStandalone LANGUAGES CXX) | ||||
| project( | ||||
|   GreeterStandalone | ||||
|   DESCRIPTION "A standalone minimal webapi application using the Crow framework" | ||||
|   LANGUAGES CXX | ||||
| ) | ||||
| 
 | ||||
| # --- Import tools ---- | ||||
| 
 | ||||
|  | @ -10,12 +14,38 @@ include(../cmake/tools.cmake) | |||
| 
 | ||||
| include(../cmake/CPM.cmake) | ||||
| 
 | ||||
| # Crow needs Boost 1.64 and does not provide CPM.cmake integration itself, so we have to get Boost | ||||
| # first | ||||
| find_package(Boost 1.64 QUIET) | ||||
| if(NOT Boost_FOUND) | ||||
|   # Use CPM.cmake to get Boost from the official repo if not provided as system lib | ||||
|   message(STATUS "GreeterStandalone: Boost system lib NOT found") | ||||
|   CPMAddPackage( | ||||
|     NAME Boost | ||||
|     GITHUB_REPOSITORY boostorg/boost | ||||
|     GIT_TAG boost-1.78.0 | ||||
|     VERSION 1.78.0 | ||||
|   ) | ||||
|   # Ugly workaround: Boost cmake support is still experimental, the Boost::boost target is not | ||||
|   # provided if downloaded via FetchContent_declare / CPM.cmake. Crow uses it for asio, so we fake | ||||
|   # the Boost:boost target as asio | ||||
|   if(NOT TARGET Boost::boost) | ||||
|     add_library(Boost::boost INTERFACE IMPORTED) | ||||
|     target_link_libraries(Boost::boost INTERFACE Boost::asio) | ||||
|   endif() | ||||
| else() | ||||
|   message(STATUS "GreeterStandalone: Boost system lib found") | ||||
| endif() | ||||
| # add Crow | ||||
| CPMAddPackage( | ||||
|   GITHUB_REPOSITORY jarro2783/cxxopts | ||||
|   VERSION 3.0.0 | ||||
|   OPTIONS "CXXOPTS_BUILD_EXAMPLES NO" "CXXOPTS_BUILD_TESTS NO" "CXXOPTS_ENABLE_INSTALL YES" | ||||
|   NAME Crow | ||||
|   GITHUB_REPOSITORY CrowCpp/Crow | ||||
|   GIT_TAG v1.0+1 | ||||
|   VERSION 1.0.1 | ||||
|   OPTIONS "CROW_INSTALL OFF" | ||||
| ) | ||||
| 
 | ||||
| # get the Greeter lib | ||||
| CPMAddPackage(NAME Greeter SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/..) | ||||
| 
 | ||||
| # ---- Create standalone executable ---- | ||||
|  | @ -23,7 +53,5 @@ CPMAddPackage(NAME Greeter SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/..) | |||
| file(GLOB sources CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp) | ||||
| 
 | ||||
| add_executable(${PROJECT_NAME} ${sources}) | ||||
| 
 | ||||
| set_target_properties(${PROJECT_NAME} PROPERTIES CXX_STANDARD 17 OUTPUT_NAME "Greeter") | ||||
| 
 | ||||
| target_link_libraries(${PROJECT_NAME} Greeter::Greeter cxxopts) | ||||
| target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_11) | ||||
| target_link_libraries(${PROJECT_NAME} PRIVATE Greeter::Greeter Crow::Crow) | ||||
|  |  | |||
							
								
								
									
										21
									
								
								standalone/Dockerfile
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								standalone/Dockerfile
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | |||
| # build | ||||
| FROM buildpack-deps:bullseye as webapp-build | ||||
| RUN set -eux; \ | ||||
|     apt-get update; \ | ||||
|     apt-get install -y --no-install-recommends \ | ||||
|     cmake \ | ||||
|     ninja-build \ | ||||
|     # configure/build with Boost as system lib - this should be orders of magnitude faster to configure than | ||||
|     # downloading via CPM.cmake while Boost's CMake support is still experimental | ||||
|     libboost-all-dev \ | ||||
|     ; | ||||
| COPY . . | ||||
| RUN cmake -S standalone -B build -G Ninja -D CMAKE_BUILD_TYPE=Release | ||||
| RUN cmake --build build | ||||
| 
 | ||||
| 
 | ||||
| # deploy | ||||
| FROM debian:bullseye-slim as webapp-run | ||||
| WORKDIR /app | ||||
| COPY --from=webapp-build /build/GreeterStandalone . | ||||
| CMD ["./GreeterStandalone"] | ||||
|  | @ -1,53 +1,48 @@ | |||
| #include <crow.h> | ||||
| #include <greeter/greeter.h> | ||||
| #include <greeter/version.h> | ||||
| 
 | ||||
| #include <cxxopts.hpp> | ||||
| #include <iostream> | ||||
| #include <string> | ||||
| #include <unordered_map> | ||||
| 
 | ||||
| auto main(int argc, char** argv) -> int { | ||||
|   const std::unordered_map<std::string, greeter::LanguageCode> languages{ | ||||
|       {"en", greeter::LanguageCode::EN}, | ||||
|       {"de", greeter::LanguageCode::DE}, | ||||
|       {"es", greeter::LanguageCode::ES}, | ||||
|       {"fr", greeter::LanguageCode::FR}, | ||||
|   }; | ||||
| int main() { | ||||
|   crow::SimpleApp app; | ||||
| 
 | ||||
|   cxxopts::Options options(*argv, "A program to welcome the world!"); | ||||
|   CROW_ROUTE(app, "/hello") | ||||
|   ([](const crow::request& req) { | ||||
|     // check params
 | ||||
|     std::cout << "Params: " << req.url_params << "\n"; | ||||
|     std::cout << "The key 'language' was " | ||||
|               << (req.url_params.get("language") == nullptr ? "not " : "") << "found.\n"; | ||||
| 
 | ||||
|   std::string language; | ||||
|   std::string name; | ||||
|     if (req.url_params.get("language") == nullptr) { | ||||
|       // return bad request
 | ||||
|       return crow::response(400, "please provide a 'language' argument"); | ||||
|     } | ||||
|     const auto language = req.url_params.get("language"); | ||||
| 
 | ||||
|   // clang-format off
 | ||||
|   options.add_options() | ||||
|     ("h,help", "Show help") | ||||
|     ("v,version", "Print the current version number") | ||||
|     ("n,name", "Name to greet", cxxopts::value(name)->default_value("World")) | ||||
|     ("l,lang", "Language code to use", cxxopts::value(language)->default_value("en")) | ||||
|   ; | ||||
|   // clang-format on
 | ||||
|     // see if langauge was found
 | ||||
|     const std::unordered_map<std::string, greeter::LanguageCode> languages{ | ||||
|         {"en", greeter::LanguageCode::EN}, | ||||
|         {"de", greeter::LanguageCode::DE}, | ||||
|         {"es", greeter::LanguageCode::ES}, | ||||
|         {"fr", greeter::LanguageCode::FR}, | ||||
|     }; | ||||
|     const auto langIt = languages.find(language); | ||||
|     if (langIt == languages.end()) { | ||||
|       // return bad request
 | ||||
|       std::cout << "Greeting for language '" << language << "' is not available\n"; | ||||
|       return crow::response(400, "language not recognized"); | ||||
|     } | ||||
| 
 | ||||
|   auto result = options.parse(argc, argv); | ||||
|     const greeter::Greeter greeter("Crow & Greeter"); | ||||
|     std::cout << "Greeting for language '" << language << "' is available, returning message\n"; | ||||
|     const auto message = greeter.greet(langIt->second); | ||||
| 
 | ||||
|   if (result["help"].as<bool>()) { | ||||
|     std::cout << options.help() << std::endl; | ||||
|     return 0; | ||||
|   } | ||||
|     crow::json::wvalue ret({{"answer", message}}); | ||||
|     return crow::response(200, ret); | ||||
|   }); | ||||
| 
 | ||||
|   if (result["version"].as<bool>()) { | ||||
|     std::cout << "Greeter, version " << GREETER_VERSION << std::endl; | ||||
|     return 0; | ||||
|   } | ||||
| 
 | ||||
|   auto langIt = languages.find(language); | ||||
|   if (langIt == languages.end()) { | ||||
|     std::cerr << "unknown language code: " << language << std::endl; | ||||
|     return 1; | ||||
|   } | ||||
| 
 | ||||
|   greeter::Greeter greeter(name); | ||||
|   std::cout << greeter.greet(langIt->second) << std::endl; | ||||
| 
 | ||||
|   return 0; | ||||
|   app.port(3080).multithreaded().run(); | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue