{"id":13602415,"url":"https://github.com/old-xebis/infrastructure-template","last_synced_at":"2025-04-11T08:32:35.774Z","repository":{"id":59490848,"uuid":"425300442","full_name":"old-xebis/infrastructure-template","owner":"old-xebis","description":"Template to automate GitOps and IaC in a cloud. GitLab CI manages static and dynamic environments, which are created, updated, and destroyed by Terraform, then set up by cloud-init and Ansible.","archived":false,"fork":false,"pushed_at":"2024-01-06T22:26:40.000Z","size":880,"stargazers_count":10,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-08-02T18:42:43.516Z","etag":null,"topics":["automation","continuous-delivery","continuous-integration","environments","gitlab","gitlab-ci","gitops","hetzner","hetzner-cloud","iac","pull-request","repository-template","terraform","terraform-hetzner-cloud"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/old-xebis.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-11-06T17:03:47.000Z","updated_at":"2024-05-07T05:40:45.000Z","dependencies_parsed_at":"2024-01-01T18:26:27.522Z","dependency_job_id":"d6483106-a65d-4496-bdf7-35d37d284009","html_url":"https://github.com/old-xebis/infrastructure-template","commit_stats":null,"previous_names":["old-xebis/infrastructure-template"],"tags_count":37,"template":true,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/old-xebis%2Finfrastructure-template","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/old-xebis%2Finfrastructure-template/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/old-xebis%2Finfrastructure-template/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/old-xebis%2Finfrastructure-template/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/old-xebis","download_url":"https://codeload.github.com/old-xebis/infrastructure-template/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223464720,"owners_count":17149603,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["automation","continuous-delivery","continuous-integration","environments","gitlab","gitlab-ci","gitops","hetzner","hetzner-cloud","iac","pull-request","repository-template","terraform","terraform-hetzner-cloud"],"created_at":"2024-08-01T18:01:22.596Z","updated_at":"2024-11-07T05:31:29.341Z","avatar_url":"https://github.com/old-xebis.png","language":"Shell","funding_links":[],"categories":["automation"],"sub_categories":[],"readme":"\u003c!-- omit in toc --\u003e\n# Infrastructure Template\n\n\u003c!-- cSpell:ignore Hetzner, IGMP, inet, gitflow, pushd, popd, uninitialize, uninit, jumanjihouse, shellcheckrc, antonbabenko, pluggable, shuaibiyy, hetznercloud --\u003e\n\n![GitHub top language](https://img.shields.io/github/languages/top/xebis/infrastructure-template)\n[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit\u0026logoColor=white)](https://github.com/pre-commit/pre-commit)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg)](https://conventionalcommits.org)\n[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)\n\n![GitHub](https://img.shields.io/github/license/xebis/infrastructure-template)\n![GitHub tag (latest SemVer)](https://img.shields.io/github/v/tag/xebis/infrastructure-template)\n![GitHub issues](https://img.shields.io/github/issues/xebis/infrastructure-template)\n![GitHub last commit](https://img.shields.io/github/last-commit/xebis/infrastructure-template)\n[![pipeline status](https://gitlab.com/xebis/infrastructure-template/badges/main/pipeline.svg?ignore_skipped=true)](https://gitlab.com/xebis/infrastructure-template/-/commits/main)\n\nTemplate to automate GitOps and IaC in a cloud. GitLab CI manages static and dynamic environments, which are created, updated, and destroyed by Terraform, then set up by cloud-init and Ansible.\n\n**The project is under active development.** The project is a fork of [xebis/repository-template](https://github.com/xebis/repository-template).\n\n\u003c!-- omit in toc --\u003e\n## The Goal\n\nThe goal is to have a GitOps repository to automatically handle environments life cycle - its creation, update, configuration, and eventually destroy as well.\n\n\u003e GitOps = IaC + MRs + CI/CD\n\n_[GitLab: What is GitOps?](https://about.gitlab.com/topics/gitops/)_\n\n\u003c!-- omit in toc --\u003e\n## Table of Contents\n\n- [Features](#features)\n  - [Environment](#environment)\n  - [Caveats](#caveats)\n  - [Images](#images)\n- [Installation and Configuration](#installation-and-configuration)\n  - [Set up GitLab CI](#set-up-gitlab-ci)\n  - [Set up Local Usage](#set-up-local-usage)\n- [Usage](#usage)\n  - [GitLab CI](#gitlab-ci)\n  - [Local Usage](#local-usage)\n  - [Terraform Configuration Documentation](#terraform-configuration-documentation)\n- [Contributing](#contributing)\n  - [Testing](#testing)\n    - [Test at Docker Container](#test-at-docker-container)\n- [To-Do list](#to-do-list)\n- [Roadmap](#roadmap)\n- [Credits and Acknowledgments](#credits-and-acknowledgments)\n- [Copyright and Licensing](#copyright-and-licensing)\n- [Changelog and News](#changelog-and-news)\n- [Notes and References](#notes-and-references)\n  - [Dependencies](#dependencies)\n  - [Recommendations](#recommendations)\n  - [Suggestions](#suggestions)\n  - [Further Reading](#further-reading)\n\n## Features\n\nOptimized for [GitHub flow](https://guides.github.com/introduction/flow/), easily adjustable to [GitLab flow](https://docs.gitlab.com/ee/topics/gitlab_flow.html) or any other workflow.\n\n![Example of the full workflow](images/workflow-full.png)\n\nAutomatically checks conventional commits, validates Markdown, YAML, shell scripts, Terraform (HCL), runs static security analysis, terraform-doc, tests, deployments, releases, and so on. See [GitHub - xebis/repository-template: Well-manageable and well-maintainable repository template.](https://github.com/xebis/repository-template) and [Notes And References](#notes-and-references) for full feature list.\n\nEnvironments are managed in stages:\n\n- **_Deploy_**: overarching name for **provision** and **install** stages\n  - **Provision**: environment is provisioned by Terraform at Hetzner Cloud and pre-configured by Cloud-init\n  - **Install**: environment is installed by Ansible over SSH\n- **Destroy** (only dynamic environments): environment is removed by Terraform from Hetzner Cloud\n\n![Deploy and destroy in more detail](images/deploy-and-destroy.png)\n\nAutomatically managed environments:\n\n- On _release_ tag runs **production** environment stages\n- On `main` branch commit runs **staging** environment stages\n  - Releases and creates _release_ tag when a commit starting `feat` or `fix` is present in the history from the previous release\n- On _pre-release_ tag runs **testing/_tag_** environment stages, and plans automatic destroy after 1 week (earlier manual destruction possible)\n- On _non-_`main` branch commit under certain conditions runs **development/_branch_** environment stages, and plans automatic destroy after 1 day (earlier manual destruction possible):\n  - It runs when the environment already exists or existed in the past (when Terraform backend returns HTTP status code `200 OK` for the environment state file)\n  - It runs when the pipeline is run by the _pipelines API_, _GitLab ChatOps_, created by using _trigger token_, created by using the **Run pipeline** _button in the GitLab UI_ or created by using the _GitLab WebIDE_\n  - It runs when the pipeline is by a _`git push` event_ or is _scheduled pipeline_, but only if there's non-empty the environment variable `ENV_CREATE` or `CREATE_ENV`\n  - It doesn't run when the environment variable `ENV_SKIP` or `SKIP_ENV` is present, or the commit message contains `[env skip]` or `[skip env]`, using any capitalization\n\n**Development/_branch_** environment create or not decision:\n\n![Development environment create or not](images/development-environment-create-or-not.png)\n\nManually managed environments:\n\n- All stages must be run manually and locally\n\n### Environment\n\nCreates one machine for development and testing environments, or intentionally zero machines for staging and production environments. Each machine uses [Xebis Ansible Collection](https://github.com/xebis/xebis-ansible-collection) roles:\n\n- `xebis.ansible.system`: Well maintained operating system - updates and upgrades `deb` packages including autoremove and autoclean, reboots the system (when necessary), provides `Reboot machine` handler\n- `xebis.ansible.firewall`: Extensible nftables firewall - installs `nftables` and sets up basic extensible nftables chains and rules, provides `Reload nftables` handler, see [GitHub: xebis/xebis-ansible-collection/README.md](https://github.com/xebis/xebis-ansible-collection/blob/main/README.md) for usage, configuration, and examples\n- `xebis.ansible.fail2ban`: Fail2ban service - installs `fail2ban` and sets it up as a systemd service\n- `xebis.ansible.iam`: IAM - creates user groups and users as regular users or admins, their public SSH keys, disables password remote logins, provides `Restart sshd` handler, see [GitHub: xebis/xebis-ansible-collection/README.md](https://github.com/xebis/xebis-ansible-collection/blob/main/README.md) for usage, configuration, and examples\n- `xebis.ansible.bash`:Extensible Bash - installs `~/.bash_aliases` and sets up basic extensible Bash aliases, see [GitHub: xebis/xebis-ansible-collection/README.md](https://github.com/xebis/xebis-ansible-collection/blob/main/README.md) for usage, configuration, and examples\n- `xebis.ansible.starship`: Starship CLI prompt - Installs `starship` and sets up improved PowerLine configuration\n- `xebis.ansible.admin`: Administration essentials - installs and sets up `at`, `curl`, `htop`, `mc`, `screen`\n\nEach machine uses [LabLabs RKE2 Ansible Role](https://github.com/lablabs/ansible-role-rke2):\n\n- `lablabs.rke2`: Ansible Role to install RKE2 Kubernetes.\n\n### Caveats\n\nOne Hetzner cloud project is used for all environments, which brings a few caveats to keep in one's mind:\n\n- To distinguish machines between environments and to separate them from manually created machines they are named with prefix `env-slug-` and labeled `env=env-slug` by Terraform\n- To use Ansible, inventory file `hcloud.yml` must have replaced `env-slug` with an **environment slug** before any local manual use\n- Use Ansible group `env` instead of groups `all` or `hcloud`, as these groups contain all machines from all environments and eventually manually created machines as well\n\n### Images\n\n- [Git workflow examples \u0026 template](images/workflow.drawio) - inspired by [diagrams.net: Blog - How to create a gitflow diagram](https://www.diagrams.net/blog/gitflow-diagram)\n- [Example of the full workflow](images/workflow-full.png)\n- [Deploy in more detail](images/deploy.png)\n\n## Installation and Configuration\n\nPrepare Hetzner Cloud API token and GitLab CI SSH keys:\n\n- [Hetzner Cloud - referral link with €20 credit](https://hetzner.cloud/?ref=arhwlvW4nCxX)\n  - Hetzner Cloud Console -\u003e Projects -\u003e _Your Project_ -\u003e Security -\u003e API Tokens -\u003e Generate API Token `Read \u0026 Write`\n- Generate GitLab CI SSH keys `ssh-keygen -t rsa` (no passphrase, _to your secret file_, **do not commit it!**), file with `.pub` extension will be generated automatically, put `*.pub` file contents at [`cloud-config.yml`](cloud-config.yml) under section `users:name=gitlab-ci` to the `ssh_authorized_keys` as the first element, and commit it\n\n### Set up GitLab CI\n\n- GitLab -\u003e Settings\n  - General \u003e Visibility, project features, permissions \u003e Operations: **on**\n  - CI/CD \u003e Variables:\n    - Add variable: Key `HCLOUD_TOKEN`, Value `\u003ctoken\u003e`\n    - Add variable: Key `GL_CI_SSH_KEY`, Value _contents of your secret file_ created by `ssh-keygen -t rsa` above\n\n### Set up Local Usage\n\nMake sure **GL_TOKEN**: [GitLab Personal Access Token](https://gitlab.com/-/profile/personal_access_tokens) with scope `api` is present, otherwise `gitlab-ci-linter` is skipped. To run Terraform provisioning Hetzner Cloud you have to set up `TF_HTTP_PASSWORD`, `HCLOUD_TOKEN`, `TF_VAR_ENV_NAME`, and `TF_VAR_ENV_SLUG` required by Terraform configuration.\nTo load secrets you can use shell extension like [direnv](https://direnv.net/), encryption like [SOPS](https://github.com/getsops/sops), or secrets manager [HashiCorp Vault](https://www.vaultproject.io/), **please make sure you won't commit your secrets**.\n\n```shell\nexport GL_TOKEN=\"\u003ctoken\u003e\" # Your GitLab's personal access token with the api scope\nexport TF_HTTP_PASSWORD=\"$GL_TOKEN\" # Set password for Terraform HTTP backend\nexport HCLOUD_TOKEN=\"\u003ctoken\u003e\" # Your Hetzner API token\nexport TF_VAR_ENV_NAME=\"\u003cenvironment\u003e\" # Replace with the environment name\nexport TF_VAR_ENV_SLUG=\"\u003cenv\u003e\" # Replace with the environment slug\n```\n\n- Install repository dependencies by `sudo scripts/bootstrap` script, setup repository by `scripts/setup`, update repository by `scripts/update` script.\n- Set up all admins and users, including public SSH key at [`ansible/group_vars/all.yml`](ansible/group_vars/all.yml) under section `users`, see documentation there. Do not forget to commit it 😀\n\n## Usage\n\n### GitLab CI\n\n- Commit and push to run validations\n- Push a _non-_`main` branch\n  - To create a **development/_branch_** environment you have to create a new pipeline for the branch using API, GitLab ChatOps, trigger token, or by using the **Run pipeline** button in the GitLab UI\n  - Alternatively, you can create a **development/_branch_** environment directly by pushing or scheduling with `ENV_CREATE` or `CREATE_ENV` environment variable present, for example by running `git push -o ci.variable=\"CREATE_ENV=true\"`\n  - Once created, the environment will be updated (or recreated if it was destroyed) with each subsequent pipeline on the branch\n  - Environment deploy is skipped when the environment variable `ENV_SKIP` or `SKIP_ENV` is present, or commit message contains `[env skip]` or `[skip env]`, using any capitalization, or when CI pipeline is skipped altogether, for example using `git push -o ci.skip`\n  - Destroy **development/_branch_** environment manually, or wait until auto-stop (1 day from the last commit in the branch in GitLab, could be overridden in GitLab UI)\n- Create a _pre-release_ tag to create **testing/_tag_** environment\n  - Pre-release tag must match regex `^v(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$`, see \u003chttps://regex101.com/r/G1OFXH/1\u003e\n  - Destroy **testing/_tag_** environment manually, or wait until auto-stop (1 week, could be overridden in GitLab UI)\n- Merge to the `main` branch to create or update the **staging** environment\n- Creates or updates **production** environment when a commit starting `feat` or `fix` is present in the history from the previous release\n  - _Release_ tag must match regex `^v(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$`, see \u003chttps://regex101.com/r/9DFqb3/1\u003e\n\n_Release_ and _pre-release_ tags must follow SemVer string, see [Semantic Versioning 2.0.0: Is there a suggested regular expression (RegEx) to check a SemVer string?](https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string)\n\n### Local Usage\n\nInitialize local workspace if not yet initialized:\n\n```shell\n# Init local workspace\npushd terraform\nterraform init -reconfigure \\\n    -backend-config=\"address=https://gitlab.com/api/v4/projects/31099306/terraform/state/$TF_VAR_ENV_SLUG\" \\\n    -backend-config=\"lock_address=https://gitlab.com/api/v4/projects/31099306/terraform/state/$TF_VAR_ENV_SLUG/lock\" \\\n    -backend-config=\"unlock_address=https://gitlab.com/api/v4/projects/31099306/terraform/state/$TF_VAR_ENV_SLUG/lock\"\n```\n\n- Create or update environment by `terraform apply` or `terraform apply -auto-approve`\n- Get nodes IP addresses by `terraform output nodes_ipv4_addresses`\n- Direct SSH by `ssh user@$(terraform output -raw nodes_ipv4_addresses)`\n- Ansible:\n  - Change to Ansible configuration directory `pushd ../ansible`\n  - First replace `hcloud.yml` string `env-slug` with `$TF_VAR_ENV_SLUG`: `sed -i \"s/env-slug/$TF_VAR_ENV_SLUG/\" hcloud.yml`\n  - List or graph inventory: `ansible-inventory -i hcloud.yml --list # or --graph`\n  - Ping: `ansible -u user -i hcloud.yml env -m ansible.builtin.ping`\n  - Get all facts: `ansible -u user -i hcloud.yml env -m ansible.builtin.setup`\n  - Configure with playbook: `ansible-playbook -u user -i hcloud.yml playbook.yml`\n  - Change back to Terraform configuration directory `popd`\n- Destroy environment by `terraform destroy` or `terraform destroy -auto-approve`, and go back to the repository root directory`popd`\n\nUninitialize local workspace if you wish:\n\n```shell\nrm -rf terraform/.terraform # Uninit local workspace, this step is required if you would like to work with another environment\n```\n\nCommit and push to run validations.\n\n### Terraform Configuration Documentation\n\n- [Terraform Root module: `terraform/README.md`](terraform/README.md)\n  - [Terraform Nodes module: `terraform/modules/nodes/README.md`](terraform/modules/nodes/README.md)\n\n## Contributing\n\nPlease read [CONTRIBUTING](CONTRIBUTING.md) for details on our code of conduct, and the process for submitting merge requests to us.\n\n### Testing\n\n- Git hooks check a lot of things for you, including running automated tests `scripts/test full`\n- Make sure all `scripts/*`, git hooks, and GitLab pipelines work as expected, testing checklist:\n\n- `scripts/*` scripts - covered by unit tests `tests/*`\n  - [ ] [`scripts/bootstrap`](scripts/bootstrap)\n  - [ ] [`scripts/deploy-env`](scripts/deploy-env)\n  - [ ] [`scripts/pre-commit`](scripts/pre-commit)\n  - [ ] [`scripts/pre-push`](scripts/pre-push)\n  - [ ] [`scripts/setup`](scripts/setup)\n  - [ ] [`scripts/test`](scripts/test)\n  - [ ] [`scripts/update`](scripts/update)\n- Local working directory\n  - [ ] `git commit` runs `pre-commit` hook-type `commit-msg` and [`scripts/pre-commit`](scripts/pre-commit)\n  - [ ] `git merge`\n    - [ ] Fast-forward shouldn't run any hooks or scripts\n    - [ ] Automatically resolved `merge commit` runs `pre-commit` hook-type `commit-msg` and [`scripts/pre-commit`](scripts/pre-commit)\n    - [ ] Manually resolved `merge commit` runs `pre-commit` hook-type `commit-msg` and [`scripts/pre-commit`](scripts/pre-commit)\n  - [ ] `git push` runs [`scripts/pre-push`](scripts/pre-push)\n  - Terraform and Ansible\n    - [ ] `terraform init`\n    - [ ] `terraform plan`\n    - [ ] `terraform apply`\n    - [ ] `ansible ... ping`\n    - [ ] `ansible-playbook`\n    - [ ] `terraform destroy`\n- GitLab CI\n  - [ ] Commit on a new _non-_`main` branch runs `validate:lint` and `validate:test-full`\n    - [ ] Without any environment variables, runs `provision:provision-dev`, `install:install-dev`, and prepares `destroy:destroy-dev`\n    - [ ] With non-empty environment variable `ENV_CREATE` or `CREATE_ENV`, runs `provision:provision-dev`, `install:install-dev`, and prepares `destroy:destroy-dev`\n  - [ ] Commit on an existing _non-_`main` branch within 24 hours runs `provision:provision-dev`, `install:install-dev`, and prepares `destroy:destroy-dev`\n  - [ ] Absence of commit on an existing _non-_`main` branch within 24 hours auto-stops **development/_branch_** environment\n  - [ ] _Pre-release_ tag on a _non-_`main` branch commit runs `validate:lint`, `validate:test-full`, `provision:provision-test`, `install:install-test`, and prepares `destroy:destroy-test`\n    - [ ] After a week auto-stops **testing/_tag_** environment\n  - [ ] Merge to the `main` branch runs `validate:lint`, `validate:test-full`, `provision:provision-stage`, `install:install-stage`, and `release:release`\n    - [ ] With a new `feat` or `fix`, commit releases a new version\n    - [ ] _Release_ tag on the `main` branch commit runs `validate:lint`, `validate:test-full`, `provision:provision-prod`, and `install:install-prod`\n    - [ ] Without a new feature or fix commit does not release a new version\n  - [ ] Scheduled (nightly) pipeline runs `validate:lint` and `validate:test-nightly`\n\n#### Test at Docker Container\n\nTo test your changes in a different environment, you might try to run a Docker container and test it from there.\n\nRun a disposal Docker container:\n\n- `sudo docker run -it --rm -v \"$(pwd)\":/infrastructure-template alpine:latest`\n- `sudo docker run -it --rm -v \"$(pwd)\":/infrastructure-template --entrypoint sh hashicorp/terraform:light`\n- `sudo docker run -it --rm -v \"$(pwd)\":/infrastructure-template --entrypoint sh gableroux/ansible:latest`\n- `sudo docker run -it --rm -v \"$(pwd)\":/infrastructure-template --entrypoint sh node:alpine`\n\nIn the container:\n\n```bash\ncd infrastructure-template\n# Set variables GL_TOKEN and GH_TOKEN when needed\n# Put here commands from .gitlab-ci.yml job:before_script and job:script\n# For example job test-full:\napk -U upgrade\napk add bats\nbats tests\n# Result is similar to:\n# 1..1\n# ok 1 dummy test\n```\n\n## To-Do list\n\n- [ ] Fix workaround for pre-commit `jumanjihouse/pre-commit-hooks` hook `script-must-have-extension` - `*.bats` shouldn't be excluded\n- [ ] Fix workaround for pre-commit `local` hook `shellcheck` - shellcheck has duplicated parameters from `.shellcheckrc`, because these are not taken into account\n\n## Roadmap\n\n- [ ] Find a satisfactory way how to manage (list, install, update) dependencies across various distributions and package managers\n- [ ] Add [pre-commit meta hooks](https://pre-commit.com/#meta-hooks)\n- [ ] Add [jumanjihouse/pre-commit-hooks hook protect-first-parent](https://github.com/jumanjihouse/pre-commit-hooks#protect-first-parent)\n- [ ] Speed up CI/CD by preparing a set of Docker images with pre-installed dependencies for each CI/CD stage, or by cache for `apk`, `pip`, and `npm`\n\n## Credits and Acknowledgments\n\n- [Martin Bružina](https://bruzina.cz/) - Author\n\n## Copyright and Licensing\n\n- [MIT License](LICENSE)\n- Copyright © 2021 Martin Bružina\n\n## Changelog and News\n\n- [Changelog](CHANGELOG.md)\n\n## Notes and References\n\n### Dependencies\n\n- [Hetzner Cloud - referral link with €20 credit](https://hetzner.cloud/?ref=arhwlvW4nCxX)\n- [Terraform](https://www.terraform.io/)\n  - [Terraform: Hetzner Cloud Provider](https://registry.terraform.io/providers/hetznercloud/hcloud/latest/docs)\n  - [GitHub - antonbabenko/pre-commit-terraform: pre-commit git hooks to take care of Terraform configurations](https://github.com/antonbabenko/pre-commit-terraform)\n  - [GitHub - terraform-linters/tflint: A Pluggable Terraform Linter](https://github.com/terraform-linters/tflint)\n  - [checkov](https://www.checkov.io/)\n  - [terraform-docs: Generate Terraform modules documentation in various formats](https://terraform-docs.io/)\n- [Ansible](https://www.ansible.com/)\n  - [GitHub - ansible-community/ansible-lint: Best practices checker for Ansible](https://github.com/ansible-community/ansible-lint)\n  - [GitHub - ansible-collections/hetzner.hcloud: A collection containing modules to manage resources on the Hetzner Cloud.](https://github.com/ansible-collections/hetzner.hcloud)\n    - [GitHub - hetznercloud/hcloud-python: hcloud-python is a library for the Hetzner Cloud API.](https://github.com/hetznercloud/hcloud-python)\n  - [Ansible Documentation: Ansible.Posix](https://docs.ansible.com/ansible/latest/collections/ansible/posix/index.html)\n- [cloud-init](https://cloud-init.io/)\n- [Docker Hub - hashicorp/terraform](https://hub.docker.com/r/hashicorp/terraform/)\n- [Docker Hub - gableroux/ansible](https://hub.docker.com/r/gableroux/ansible)\n- [jq: jq is a lightweight and flexible command-line JSON processor](https://stedolan.github.io/jq/)\n- [GitHub - xebis/repository-template: Well-manageable and well-maintainable repository template.](https://github.com/xebis/repository-template) - contains GitLab CI/CD, set of useful scripts, `pre-commit`, `semantic-release`, and `Visual Studio Code` suggested extensions\n- [GitHub - xebis/xebis-ansible-collection: A collection of Xebis shared Ansible roles.](https://github.com/xebis/xebis-ansible-collection)\n- [GitHub - lablabs/ansible-role-rke2: Ansible Role to install RKE2 Kubernetes.](https://github.com/lablabs/ansible-role-rke2) and [Ansible Galaxy - lablabs.rke2](https://galaxy.ansible.com/ui/standalone/roles/lablabs/rke2/)\n\n### Recommendations\n\n- [GitHub - shuaibiyy/awesome-terraform: Curated list of resources on HashiCorp's Terraform](https://github.com/shuaibiyy/awesome-terraform)\n- [GitHub - KeyboardInterrupt/awesome-ansible: Awesome Ansible List](https://github.com/KeyboardInterrupt/awesome-ansible)\n- [GitHub - hetznercloud/awesome-hcloud: A curated list of awesome libraries, tools, and integrations for Hetzner Cloud](https://github.com/hetznercloud/awesome-hcloud)\n\n### Suggestions\n\n- [Visual Studio Code](https://code.visualstudio.com/) with [Extensions for Visual Studio Code](https://marketplace.visualstudio.com/VSCode):\n  - [HashiCorp Terraform](https://marketplace.visualstudio.com/items?itemName=HashiCorp.terraform)\n  - [Ansible](https://marketplace.visualstudio.com/items?itemName=redhat.ansible) - includes potentially unwanted extensions [Python](https://marketplace.visualstudio.com/items?itemName=ms-python.python) (Ansible dependency) and [Pylance](https://marketplace.visualstudio.com/items?itemName=ms-python.vscode-pylance) (could be uninstalled)\n\n### Further Reading\n\n- [GitLab: What is GitOps?](https://about.gitlab.com/topics/gitops/)\n- [Semantic Versioning - Semantic Versioning 2.0.0](https://semver.org/)\n  - [RegEx101: Valid Semantic Versions](https://regex101.com/r/vkijKf/1/)\n  - [RegEx101: Valid Release Semantic Versions](https://regex101.com/r/9DFqb3/1)\n  - [RegEx101: Valid Pre-release Semantic Versions](https://regex101.com/r/G1OFXH/1)\n- [Conventional Commits - Conventional Commits 1.0.0](https://www.conventionalcommits.org/en/v1.0.0/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fold-xebis%2Finfrastructure-template","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fold-xebis%2Finfrastructure-template","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fold-xebis%2Finfrastructure-template/lists"}