Skip to content
Draft
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions cookiecutter.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@
"title": "Documentation scaffold for Plone projects",
"description": "Create a new documentation scaffold for Plone projects",
"hidden": false
},
"devops_ansible": {
"path": "./templates/devops/ansible",
"title": "Ansible Playbooks for Plone",
"description": "Ansible setup to manage a Docker Swarm cluster for Plone hosting.",
"hidden": true
}
}
}
1 change: 1 addition & 0 deletions news/+monorepo-calver.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Project monorepo uses calendar versioning. @ericof
1 change: 1 addition & 0 deletions news/+monorepo-gha.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Refactored Github actions for monorepo projects. @ericof
1 change: 1 addition & 0 deletions news/+monorepo.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Refactor monorepo project to use uv and repoplone. @ericof
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also mention the refactor of the CI pipeline

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still a day or two from being done, but I will make sure to add the changelog entries for the CI and for the base Docker images

4 changes: 3 additions & 1 deletion templates/add-ons/backend/cookiecutter.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"plone_version": "{{ cookiecutter.use_prerelease_versions | latest_plone }}",
"python_package_name": "{{ cookiecutter.github_organization|lower }}.{{ cookiecutter.title|replace(' ', '')|replace('-', '_')|replace('.', '')|lower }}",
"__year": "{% now 'local', '%Y' %}",
"initial_version": "1.0.0a0",
"feature_headless": ["1", "0"],
"initialize_documentation": ["1", "0"],
"__project_slug": "{{ cookiecutter.python_package_name }}",
Expand All @@ -28,7 +29,7 @@
"__gha_plone_version": "{{ cookiecutter.plone_version }}",
"__python_version_identifier": "{{ cookiecutter.__supported_versions_python[0] | replace('.', '') }}",
"__profile_language": "en",
"__version_package": "1.0.0a0",
"__version_package": "{{ cookiecutter.initial_version }}",
"__profile_version": "1000",
"__backend_addon_git_initialize": "1",
"__backend_addon_format": "1",
Expand All @@ -41,6 +42,7 @@
"python_package_name": "Python package name",
"author": "Author",
"email": "Author E-mail",
"initial_version": "Package version",
"feature_headless": {
"__prompt__": "Support headless Plone?",
"1": "Yes",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ test = [
"plone.restapi[test]",
"pytest",
"pytest-cov",
"pytest-plone>=0.5.0",
"pytest-plone>=1.0.0a2",
]
release = [
"zest.releaser[recommended]",
Expand Down
6 changes: 4 additions & 2 deletions templates/add-ons/frontend/cookiecutter.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,21 @@
"npm_package_name": "{{ cookiecutter.frontend_addon_name }}",
"use_prerelease_versions": "{{ 'No' | use_prerelease_versions }}",
"volto_version": "{{ cookiecutter.use_prerelease_versions | latest_volto }}",
"initial_version": "1.0.0-alpha.0",
"initialize_documentation": ["1", "0"],
"__project_slug": "{{ cookiecutter.frontend_addon_name }}",
"__repository_url": "https://github.com/{{ cookiecutter.github_organization }}/{{ cookiecutter.__project_slug }}",
"__repository_git": "[email protected]:{{ cookiecutter.github_organization }}/{{ cookiecutter.__project_slug }}",
"__folder_name": "{{ cookiecutter.frontend_addon_name }}",
"__npm_package_name": "{{ cookiecutter.npm_package_name }}",
"__version_frontend_package": "1.0.0-alpha.0",
"__version_frontend_package": "{{ cookiecutter.initial_version }}",
"__version_plone_volto": "{{ cookiecutter.volto_version }}",
"__version_mrs_developer": "^2.2.0",
"__version_pnpm": "9.1.1",
"__version_release_it": "^17.1.1",
"__node_version": "{{ cookiecutter.volto_version | node_version_for_volto }}",
"__gha_version_node": "{{ cookiecutter.__node_version }}",
"__gha_version_checkout": "v4",
"__gha_version_checkout": "v5",
"__gha_version_setup_node": "v4",
"__gha_version_cache": "v4",
"__gha_version_background_action": "v1",
Expand All @@ -37,6 +38,7 @@
"volto_version": "Volto version",
"author": "Author",
"email": "Author E-mail",
"initial_version": "Package version",
"initialize_documentation": {
"__prompt__": "Would you like to add a documentation scaffold to your project?",
"1": "Yes",
Expand Down
1 change: 1 addition & 0 deletions templates/devops/ansible/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ansible
49 changes: 49 additions & 0 deletions templates/devops/ansible/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
SHELL := /bin/bash
CURRENT_DIR:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))


# We like colors
# From: https://coderwall.com/p/izxssa/colored-makefile-for-golang-projects
RED=`tput setaf 1`
GREEN=`tput setaf 2`
RESET=`tput sgr0`
YELLOW=`tput setaf 3`

.PHONY: all
all: build

BASE_FOLDER = $(shell git rev-parse --show-toplevel)
VENV_FOLDER = ${BASE_FOLDER}/.venv
BIN_FOLDER = ${VENV_FOLDER}/bin

TEMPLATE = devops_ansible
DEVOPS_FOLDER_NAME = ansible

.PHONY: clean
clean: ## Clean
rm -rf $(ADDON_NAME)

$(VENV_FOLDER): ## cookieplone installation
$(MAKE) -C $(BASE_FOLDER) sync

.PHONY: format
format: $(VENV_FOLDER)## Format code
@echo "$(GREEN)==> Formatting $(TEMPLATE) codebase $(RESET)"
@uv run ruff format --config $(BASE_FOLDER)/pyproject.toml hooks
@uv run ruff check --select I --fix --config $(BASE_FOLDER)/pyproject.toml hooks

.PHONY: generate
generate: $(VENV_FOLDER) ## Create a sample package
@echo "$(GREEN)==> Creating new test package$(RESET)"
rm -rf $(DEVOPS_FOLDER_NAME)
COOKIEPLONE_REPOSITORY=$(BASE_FOLDER) uv run cookieplone $(TEMPLATE) --no-input

.PHONY: test
test: $(VENV_FOLDER)## Create a sample package and tests it
@echo "$(GREEN)==> Creating new test package$(RESET)"
@uv run pytest $(BASE_FOLDER)/tests/templates/devops/ansible

.PHONY: test-pdb
test-pdb: $(VENV_FOLDER)## Stop on the first failed test
@echo "$(GREEN)==> Test template, stop on first error$(RESET)"
@uv run pytest $(BASE_FOLDER)/tests/templates/devops/ansible -x --pdb
1 change: 1 addition & 0 deletions templates/devops/ansible/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Cookieplone Ansible Playbooks
82 changes: 82 additions & 0 deletions templates/devops/ansible/cookiecutter.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
{
"title": "Ansible",
"description": "Ansible setup to manage a Docker Swarm cluster for Plone hosting",
"folder_name": "{{ cookiecutter.title|replace(' ', '')|replace('-', '_')|replace('.', '')|lower }}",
"hostname": "{{ cookiecutter.folder_name }}.example.com",
"hostname_or_ip": "{{ cookiecutter.hostname }}",
"stack_name": "{{ cookiecutter.hostname|replace('.', '-') }}",
"stack_prefix": "{{ cookiecutter.hostname | extract_host | replace('.', '-') }}",
"stack_location": "etc/stacks/{{ cookiecutter.hostname }}.yml",
"author": "Plone Community",
"email": "[email protected]",
"repository_type": ["github", "gitlab_com", "gitlab"],
"organization": "collective",
"repository_url": "https://github.com/{{ cookiecutter.organization }}/{{ cookiecutter.folder_name }}",
"container_registry": ["github", "docker_hub", "gitlab"],
"initialize_git": ["1", "0"],
"__folder_name": "{{ cookiecutter.folder_name }}",
"__project_slug": "{{ cookiecutter.folder_name }}",
"__devops_host": "{{ cookiecutter.hostname | extract_host }}",
"__devops_stack_name": "{{ cookiecutter.stack_name }}",
"__devops_stack_prefix": "{{ cookiecutter.stack_prefix }}",
"__devops_swarm_public_network": "nw-public",
"__devops_swarm_stack_network": "nw-internal",
"__devops_traefik_version": "v2.11",
"__devops_traefik_stack_include_ui": "no",
"__devops_cronjob_version": "1.14",
"__devops_project_user": "500",
"__repository_url": "{{ cookiecutter.repository_url }}",
"__container_registry_prefix": "{{ cookiecutter.container_registry | image_prefix }}",
"__container_image_prefix": "{{ cookiecutter.__container_registry_prefix }}{{ cookiecutter.organization }}/{{ cookiecutter.__project_slug }}",
"__year": "{% now 'local', '%Y' %}",
"__generator_date_long": "{% now 'utc', '%Y-%m-%d %H:%M:%S' %}",
"__generator_signature": "This was generated by the [cookieplone-templates documentation_starter template](https://github.com/plone/cookieplone-templates/tree/main/documentation_starter) on {{ cookiecutter.__generator_date_long }}",
"__documentation_starter_format": "1",
"__prompts__": {
"title": "Title",
"description": "A short description for this repository",
"folder_name": "Name of the folder to be created",
"hostname": "Public URL of the server",
"hostname_or_ip": "Address of the server (IP or hostname)",
"repository_type": {
"__prompt__": "Repository Type",
"github": "GitHub",
"gitlab_com": "GitLab.com",
"gitlab": "GitLab Self-Hosted"
},
"organization": "GitHub / GitLab Username or Organization",
"repository_url": "Repository URL",
"container_registry": {
"__prompt__": "Container Registry",
"github": "GitHub Container Registry",
"docker_hub": "Docker Hub",
"gitlab": "GitLab"
},
"stack_name": "Docker Swarm Stack Name",
"stack_prefix": "A 5 to 10 character prefix for the stack",
"author": "Author",
"email": "Author E-mail",
"initialize_git": {
"__prompt__": "Initialize Git Repository?",
"1": "Yes",
"0": "No"
}
},
"_copy_without_render": [
"etc/.ssh",
"etc/base",
"etc/docker",
"etc/keys",
"etc/ssh",
"playbooks",
"requirements",
"tasks"
],
"_extensions": [
"cookieplone.filters.pascal_case",
"cookieplone.filters.extract_host",
"cookieplone.filters.image_prefix"
],
"__cookieplone_repository_path": "",
"__cookieplone_template": ""
}
70 changes: 70 additions & 0 deletions templates/devops/ansible/hooks/post_gen_project.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
from collections import OrderedDict
from copy import deepcopy
from pathlib import Path
from random import choice
from string import ascii_letters, digits

from cookieplone.utils import console, git

context: OrderedDict = {{cookiecutter}}


def generate_vaultpass(context: OrderedDict, output_dir: Path):
"""Generate a vault password file for Ansible."""
vault_pass_path = output_dir / ".vault_pass"
value = "".join(choice(ascii_letters + digits) for _ in range(32)) # noQA: S311
if not vault_pass_path.exists():
vault_pass_path.write_text(value)
console.print(f"Vault password file created at {vault_pass_path}")
else:
console.print(f"Vault password file already exists at {vault_pass_path}")


def handle_git_initialization(context: OrderedDict, output_dir: Path):
"""Initialize a Git repository for the documentation codebase."""
git.initialize_repository(output_dir)


def main():
"""Final fixes."""
output_dir = Path().cwd()
initialize_git = bool(int(context.get("initialize_git")))
# Cleanup / Git
actions = [
[
generate_vaultpass,
"Generate Ansible vault password",
True,
],
[
handle_git_initialization,
"Initialize Git repository",
initialize_git,
],
]
for func, title, enabled in actions:
if not int(enabled):
continue
new_context = deepcopy(context)
console.print(f" -> {title}")
func(new_context, output_dir)

msg = """
[bold blue]{{ cookiecutter.title }}[/bold blue]

Now, enter the {{ cookiecutter.folder_name }} folder, start using your
playbooks.

Sorry for the convenience,
The Plone Community.
"""
console.panel(
title="New Ansible setup created",
subtitle="",
msg=msg,
url="https://plone.org/",
)


if __name__ == "__main__":
main()
17 changes: 17 additions & 0 deletions templates/devops/ansible/hooks/pre_gen_project.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"""Pre generation hook."""

from collections import OrderedDict
from pathlib import Path

output_path = Path().resolve()

context: OrderedDict = {{cookiecutter}}


def main():
"""Validate context."""
pass


if __name__ == "__main__":
main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
exclude_paths:
- .cache/ # implicit unless exclude_paths is defined in config
- .github/
- .ansible-lint
- etc/
- keys/
- roles/
- "~/.ansible/roles/"
- "inventory/**/vault.yml"

enable_list:
- fqcn-builtins
- no-log-password # opt-in
- no-same-owner # opt-in

kinds:
- playbook: "./playbooks/**/*.{yml,yaml}"
- tasks: "./tasks/**/*.{yml,yaml}"
- yaml: "./inventory/.{yml,yaml}"

skip_list:
- role-name

# Offline mode disables installation of requirements.yml
offline: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# EditorConfig Configurtaion file, for more details see:
# http://EditorConfig.org
# EditorConfig is a convention description, that could be interpreted
# by multiple editors to enforce common coding conventions for specific
# file types

# top-most EditorConfig file:
# Will ignore other EditorConfig files in Home directory or upper tree level.
root = true


[*] # For All Files
# Unix-style newlines with a newline ending every file
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
# Set default charset
charset = utf-8
# Indent style default
indent_style = space
# Max Line Length - a hard line wrap, should be disabled
max_line_length = off

[*.{py,cfg,ini}]
# 4 space indentation
indent_size = 4

[*.{html,dtml,pt,zpt,xml,zcml,js,json,less,css,yml,yaml,sh}]
# 2 space indentation
indent_size = 2

[{Makefile,.gitmodules}]
# Tab indentation (no size specified, but view as 4 spaces)
indent_style = tab
indent_size = unset
tab_width = unset
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.DS_Store
.env
.venv
.galaxy_install_info
.Python
.vagrant
.vault_pass.txt
*.retry
etc/keys/*
!etc/keys/.gitkeep
!roles/.gitkeep
roles/*
.install.stamp
Loading