From 5cbc2fad5a72c57ec936177d8899cfea229f67fb Mon Sep 17 00:00:00 2001 From: Mikel Larreategi Date: Thu, 10 Oct 2024 08:18:53 +0200 Subject: [PATCH] Improve GitLab support --- project/cookiecutter.json | 9 + project/hooks/post_gen_project.py | 18 +- .../.gitlab-ci.yml | 164 ++++++++++++++++++ .../devops/README-GITLAB.md | 18 +- .../stacks/{{ cookiecutter.hostname }}.yml | 12 ++ 5 files changed, 215 insertions(+), 6 deletions(-) create mode 100644 project/{{ cookiecutter.__folder_name }}/.gitlab-ci.yml diff --git a/project/cookiecutter.json b/project/cookiecutter.json index 00834bf..5dd4421 100644 --- a/project/cookiecutter.json +++ b/project/cookiecutter.json @@ -29,6 +29,10 @@ "1", "0" ], + "devops_gitlab_deploy": [ + "1", + "0" + ], "__feature_headless": "1", "__npm_package_name": "{{ cookiecutter.frontend_addon_name }}", "__folder_name": "{{ cookiecutter.project_slug }}", @@ -110,6 +114,11 @@ "__prompt__": "Add GitHub Action to Deploy this project?", "1": "Yes", "0": "No" + }, + "devops_gitlab_deploy": { + "__prompt__": "Add GitLab Action to Deploy this project?", + "1": "Yes", + "0": "No" } }, "_copy_without_render": [ diff --git a/project/hooks/post_gen_project.py b/project/hooks/post_gen_project.py index 818e8c4..8bd166e 100644 --- a/project/hooks/post_gen_project.py +++ b/project/hooks/post_gen_project.py @@ -36,6 +36,10 @@ "devops/.env_gha", "devops/README-GHA.md", ], + "gitlab": [ + ".gitlab-ci.yml", + "devops/README-GITLAB.md", + ], } @@ -45,10 +49,15 @@ def handle_devops_ansible(context: OrderedDict, output_dir: Path): def handle_devops_gha_deploy(context: OrderedDict, output_dir: Path): - """Clean up ansible.""" + """Clean up GitHub Actions deploy files but not docker image generation files.""" files.remove_files(output_dir, DEVOPS_TO_REMOVE["gha"]) +def handle_remove_devops_gitlab(context: OrderedDict, output_dir: Path): + """clean up GitLab deployment files""" + files.remove_files(output_dir, DEVOPS_TO_REMOVE["gitlab"]) + + def handle_git_initialization(context: OrderedDict, output_dir: Path): """Initialize a GIT repository for the project codebase.""" git.initialize_repository(output_dir) @@ -115,6 +124,13 @@ def main(): context.get("devops_gha_deploy") ), # {{ cookiecutter.devops_gha_deploy }} ], + [ + handle_remove_devops_gitlab, + "Remove GHA deployment files", + not int( + context.get("devops_gitlab_deploy") + ), # {{ cookiecutter.devops_gha_deploy }} + ], [ handle_git_initialization, "Initialize Git repository", diff --git a/project/{{ cookiecutter.__folder_name }}/.gitlab-ci.yml b/project/{{ cookiecutter.__folder_name }}/.gitlab-ci.yml new file mode 100644 index 0000000..82e4b70 --- /dev/null +++ b/project/{{ cookiecutter.__folder_name }}/.gitlab-ci.yml @@ -0,0 +1,164 @@ +default: + image: docker:24.0.5 + services: + - docker:24.0.5-dind + before_script: + - apk add --no-cache make bash python3 ncurses openssh-client-common + - echo "$CI_REGISTRY_PASSWORD" | docker login $CI_REGISTRY -u $CI_REGISTRY_USER --password-stdin + +stages: + - test + - build + - release + - deploy + +variables: + # Use TLS https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#tls-enabled + # DOCKER_HOST: tcp://docker:2376 + DOCKER_TLS_CERTDIR: "/certs" + CONTAINER_BACKEND_RELEASE_IMAGE: $CI_REGISTRY_IMAGE/backend + CONTAINER_FRONTEND_RELEASE_IMAGE: $CI_REGISTRY_IMAGE/frontend + CONTAINER_VARNISH_RELEASE_IMAGE: $CI_REGISTRY_IMAGE/varnish + +check-backend: + stage: test + before_script: + - apk add --no-cache make bash python3 ncurses openssh-client-common git + script: + - cd backend + - make check + +check-frontend: + image: 22-alpine + stage: test + before_script: + - apk add --no-cache make bash python3 ncurses openssh-client-common git + script: + - cd frontend + - make lint + - make ci-i18n + +test-backend: + stage: test + before_script: + - apk add --no-cache make bash python3 ncurses openssh-client-common git + script: + - cd backend + - make install + - make test + rules: + - if: $CI_PIPELINE_SOURCE == 'merge_request_event' + +test-frontend: + image: 22-alpine + stage: test + before_script: + - apk add --no-cache make bash python3 ncurses openssh-client-common git + script: + - cd frontend + - make install + - make test + rules: + - if: $CI_PIPELINE_SOURCE == 'merge_request_event' + +build-and-release-backend: + stage: build + before_script: + - apk add --no-cache make bash python3 ncurses openssh-client-common + script: + - cd backend + - make build-image + - echo "$CI_REGISTRY_PASSWORD" | docker login $CI_REGISTRY -u $CI_REGISTRY_USER --password-stdin + - docker tag $CONTAINER_BACKEND_RELEASE_IMAGE:latest $CONTAINER_BACKEND_RELEASE_IMAGE:$CI_COMMIT_TAG + - docker push $CONTAINER_BACKEND_RELEASE_IMAGE:latest + - docker push $CONTAINER_BACKEND_RELEASE_IMAGE:$CI_COMMIT_TAG + + only: + - main + - tags + +build-and-release-frontend: + stage: build + before_script: + - apk add --no-cache make bash python3 ncurses openssh-client-common + + script: + - cd frontend + - make build-image + - echo "$CI_REGISTRY_PASSWORD" | docker login $CI_REGISTRY -u $CI_REGISTRY_USER --password-stdin + - docker tag $CONTAINER_FRONTEND_RELEASE_IMAGE:latest $CONTAINER_FRONTEND_RELEASE_IMAGE:$CI_COMMIT_TAG + - docker push $CONTAINER_FRONTEND_RELEASE_IMAGE:latest + - docker push $CONTAINER_FRONTEND_RELEASE_IMAGE:$CI_COMMIT_TAG + only: + - main + - tags + +build-and-release-varnish: + stage: build + before_script: + - apk add --no-cache make bash python3 ncurses openssh-client-common + script: + - cd devops/varnish + - docker build . -t $CONTAINER_VARNISH_RELEASE_IMAGE:latest -t $CONTAINER_VARNISH_RELEASE_IMAGE:$CI_COMMIT_TAG + - echo "$CI_REGISTRY_PASSWORD" | docker login $CI_REGISTRY -u $CI_REGISTRY_USER --password-stdin + - docker push $CONTAINER_VARNISH_RELEASE_IMAGE:latest + - docker push $CONTAINER_VARNISH_RELEASE_IMAGE:$CI_COMMIT_TAG + only: + - main + - tags + +deploy: + stage: deploy + rules: + - if: $CI_COMMIT_TAG + script: + - mkdir -p ~/.ssh/ + - touch ~/.ssh/known_hosts + - echo "$SSH_KNOWN_HOSTS" >> ~/.ssh/known_hosts + - chmod 644 ~/.ssh/known_hosts + - cd devops + - eval `ssh-agent -s` + - echo "${DEPLOY_SSH_PRIVATE_KEY}" | tr -d '\r' | ssh-add - > /dev/null # add ssh key + - export TERM=dumb + - touch .env + - 'echo "DEPLOY_ENV: ${DEPLOY_ENV}" >> .env' + - 'echo "DEPLOY_HOST: ${DEPLOY_HOST}" >> .env' + - 'echo "DEPLOY_PORT: ${DEPLOY_PORT}" >> .env' + - 'echo "DEPLOY_USER: ${DEPLOY_USER}" >> .env' + - 'echo "DOCKER_CONFIG: ${DOCKER_CONFIG}" >> .env' + - 'echo "STACK_NAME: ${STACK_NAME}" >> .env' + - 'echo "STACK_PARAM: ${CI_COMMIT_TAG}" >> .env' + - make docker-setup + - make stack-deploy + environment: production +# deploy-with-auxiliary-docker-image: +# stage: deploy +# variables: +# REGISTRY: ${CI_REGISTRY} +# USERNAME: ${CI_REGISTRY_USER} +# PASSWORD: ${CI_REGISTRY_PASSWORD} +# REMOTE_HOST: ${DEPLOY_HOST} +# REMOTE_PORT: ${DEPLOY_PORT} +# REMOTE_USER: ${DEPLOY_USER} +# REMOTE_PRIVATE_KEY: "${DEPLOY_SSH_PRIVATE_KEY}" +# STACK_FILE: devops/stacks/${DEPLOY_HOST}.yml +# STACK_NAME: ${STACK_NAME} +# DEPLOY_IMAGE: ghcr.io/kitconcept/docker-stack-deploy:latest +# script: +# - docker pull ${DEPLOY_IMAGE} +# - docker run --rm +# -v "$(pwd)":/github/workspace +# -v /var/run/docker.sock:/var/run/docker.sock +# -e REGISTRY=${REGISTRY} +# -e USERNAME=${USERNAME} +# -e PASSWORD=${PASSWORD} +# -e REMOTE_HOST=${REMOTE_HOST} +# -e REMOTE_PORT=${REMOTE_PORT} +# -e REMOTE_USER=${REMOTE_USER} +# -e REMOTE_PRIVATE_KEY="${REMOTE_PRIVATE_KEY}" +# -e STACK_FILE=${STACK_FILE} +# -e STACK_NAME=${STACK_NAME} +# ${DEPLOY_IMAGE} +# only: +# - main +# environment: production diff --git a/project/{{ cookiecutter.__folder_name }}/devops/README-GITLAB.md b/project/{{ cookiecutter.__folder_name }}/devops/README-GITLAB.md index ea13973..6c0be30 100644 --- a/project/{{ cookiecutter.__folder_name }}/devops/README-GITLAB.md +++ b/project/{{ cookiecutter.__folder_name }}/devops/README-GITLAB.md @@ -17,6 +17,7 @@ See [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/). 4. Expand the `Variables`. 5. Add all variables and their corresponding values, copying them from the `.env` file. This file is not commited into the repository, and that's why we need to add them here. + **Note**: do not mark any of the variables as *protected*. ### Step 2: Add deployment's host SSH key as a known host @@ -39,16 +40,23 @@ See [Use SSH keys to communicate with GitLab](https://docs.gitlab.com/ee/user/ss Check the section "Server Setup" in the file `README.md` in the same folder as this file for details. Be aware that the key generated by this template will not be added to your git repository because it is explicitly excluded by the `.gitignore` file. +## Development flow + +The development should be done in a separate branch such as `develop`. + +On each push to such branch, the basic checks (linter) will be executed. + +When opening a merge requests from that branch to `main` tests will be executed. + + ## Automatic deployment 🚀 -The deployment is executed automatically on each commit to `main` branch. +The deployment is executed automatically when a tag is created in the repository. -You may want to adapt your development workflow to that, and use a `develop` branch to develop and create merge requests for deployments. +The images will be built when creating a tag. -## Manual deployment 🚀 +The tag name will be used as the docker image tag and will be used when running the deployment. -If you want to do manual deployments, you can enable the `when: manual` option of the `.gitlab-ci.yml` file. -By default it is commented. ## Deployment with an auxiliary docker image diff --git a/project/{{ cookiecutter.__folder_name }}/devops/stacks/{{ cookiecutter.hostname }}.yml b/project/{{ cookiecutter.__folder_name }}/devops/stacks/{{ cookiecutter.hostname }}.yml index d6e8c00..84eb7da 100644 --- a/project/{{ cookiecutter.__folder_name }}/devops/stacks/{{ cookiecutter.hostname }}.yml +++ b/project/{{ cookiecutter.__folder_name }}/devops/stacks/{{ cookiecutter.hostname }}.yml @@ -98,7 +98,11 @@ services: order: start-first varnish: + {%- if cookiecutter.devops_gitlab_deploy %} + image: {{ cookiecutter.__container_image_prefix }}/varnish:${STACK_PARAM:-latest} + {% else %} image: {{ cookiecutter.__container_image_prefix }}-varnish:${STACK_PARAM:-latest} + {% endif %} command: - '-p' - 'nuke_limit=2000' @@ -134,7 +138,11 @@ services: {%- endif %} frontend: + {%- if cookiecutter.devops_gitlab_deploy %} + image: {{ cookiecutter.__container_image_prefix }}/frontend:${STACK_PARAM:-latest} + {% else %} image: {{ cookiecutter.__container_image_prefix }}-frontend:${STACK_PARAM:-latest} + {% endif %} environment: RAZZLE_INTERNAL_API_PATH: http://backend:8080/Plone RAZZLE_API_PATH: https://{{ cookiecutter.hostname }} @@ -170,7 +178,11 @@ services: {%- endif %} backend: + {%- if cookiecutter.devops_gitlab_deploy %} + image: {{ cookiecutter.__container_image_prefix }}/backend:${STACK_PARAM:-latest} + {% else %} image: {{ cookiecutter.__container_image_prefix }}-backend:${STACK_PARAM:-latest} + {% endif %} environment: RELSTORAGE_DSN: "dbname='${DB_NAME:-plone}' user='${DB_USER:-plone}' host='${DB_HOST:-db}' password='${DB_PASSWORD:-{{ cookiecutter.__devops_db_password }}}' port='${DB_PORT:-5432}'" depends_on: