diff --git a/.github/workflows/ce-deploy-publish-docs.yml b/.github/workflows/ce-deploy-publish-docs.yml index df31e516..29161a56 100644 --- a/.github/workflows/ce-deploy-publish-docs.yml +++ b/.github/workflows/ce-deploy-publish-docs.yml @@ -12,6 +12,8 @@ jobs: # Set the job key. The key is displayed as the job name # when a job name is not provided publish-docs: + # Only run the job if it is not coming from a documentation branch + if: ${{ github.event.pull_request.head.ref != 'docs-${{ github.event.pull_request.base.ref }}' }} # Name the Job name: Publish ce-deploy documentation # Set the type of machine to run on @@ -24,30 +26,51 @@ jobs: - ${{ github.workspace }}:/home/controller steps: - - name: Install wiki2pages - run: /usr/bin/su - ce-dev -c "/usr/bin/git clone https://github.com/codeenigma/wikis2pages.git /home/ce-dev/build/wiki2pages" + - uses: actions/checkout@v4 + with: + ref: docs-${{ github.event.pull_request.base.ref }} - - name: Update local applications + # Configure environment + - name: Prepare Git, GitHub CLI and installed CE tools run: | + /usr/bin/git config --global user.email "sysadm@codeenigma.com" + /usr/bin/git config --global user.name "Code Enigma CI" + /usr/bin/git config --global pull.rebase false + /usr/bin/git config --global --add safe.directory /__w/ce-deploy/ce-deploy + (type -p wget >/dev/null || (sudo apt update && sudo apt-get install wget -y)) && sudo mkdir -p -m 755 /etc/apt/keyrings && out=$(mktemp) && wget -nv -O$out https://cli.github.com/packages/githubcli-archive-keyring.gpg && cat $out | sudo tee /etc/apt/keyrings/githubcli-archive-keyring.gpg > /dev/null && sudo chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg && echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null && sudo apt update && sudo apt install gh -y /usr/bin/su - ce-dev -c "cd /home/ce-dev/ce-provision && /usr/bin/git pull origin 2.x" /usr/bin/su - ce-dev -c "cd /home/ce-dev/ce-deploy && /usr/bin/git pull origin 1.x" - - name: Set up Ansible hosts file + # First build and publish the markdown docs + - name: Build and commit table of contents and README files back to the repo run: | - mkdir -p /home/ce-dev/ansible/bin/hosts - echo "wikis2pages-hugo ansible_host=127.0.0.1" > /home/ce-dev/ansible/bin/hosts/hosts + /bin/sh contribute/toc.sh + /usr/bin/find . -name "*.md" | xargs git add + /usr/bin/git diff --quiet && git diff --staged --quiet || git commit -am "GitHub Actions - updating markdown docs - ${{ steps.date.outputs.date }}" + /usr/bin/git push + + # Create docs pull request + - name: Create a documentation pull request + run: | + gh pr create --base ${{ github.event.pull_request.base.ref }} --head docs-${{ github.event.pull_request.base.ref }} --title "Documentation update - ${{ github.event.pull_request.base.ref }}" --body "**Automated pull request** created by GitHub Actions because of a documentation update." || echo "No commits between ${{ github.event.pull_request.base.ref }} and docs-${{ github.event.pull_request.base.ref }} - no PR created!" + gh pr create --base devel --head docs-${{ github.event.pull_request.base.ref }} --title "Documentation update - devel" --body "**Automated pull request** created by GitHub Actions because of a documentation update." || echo "No commits between devel and docs-${{ github.event.pull_request.base.ref }} - no PR created!" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Set up SSH config + - name: Install wiki2pages + run: /usr/bin/su - ce-dev -c "/usr/bin/git clone https://github.com/codeenigma/wikis2pages.git /home/ce-dev/build/wiki2pages" + + - name: Set up Ansible and SSH run: | + mkdir -p /home/ce-dev/ansible/bin/hosts + echo "wikis2pages-hugo ansible_host=127.0.0.1" > /home/ce-dev/ansible/bin/hosts/hosts echo "StrictHostKeyChecking=no" > /home/ce-dev/.ssh/config cat /home/ce-dev/.ssh/id_rsa.pub > /home/ce-dev/.ssh/authorized_keys chown ce-dev:ce-dev /home/ce-dev/.ssh/config chmod 700 /home/ce-dev/.ssh/config chown ce-dev:ce-dev /home/ce-dev/.ssh/authorized_keys chmod 700 /home/ce-dev/.ssh/authorized_keys - - - name: Start SSHD - run: /usr/sbin/sshd& + /usr/sbin/sshd& - name: Initialise wiki2pages for ce-deploy ${{ github.event.pull_request.base.ref }} run: | @@ -55,11 +78,11 @@ jobs: /usr/bin/su - ce-dev -c "cd /home/ce-dev/build/wiki2pages && /home/ce-dev/ansible/bin/ansible-playbook -e 'wiki2pages_build_path=/home/ce-dev/build/wiki2pages' -i /home/ce-dev/ansible/bin/hosts /home/ce-dev/build/wiki2pages/ce-dev/ansible/provision.yml" /usr/bin/su - ce-dev -c "cd /home/ce-dev/build/wiki2pages && /bin/sh set-current.sh --project ce-deploy-${{ github.event.pull_request.base.ref }} --no-ce-dev" /usr/bin/su - ce-dev -c "cd /home/ce-dev/build/wiki2pages && /home/ce-dev/ansible/bin/ansible-playbook -e 'wiki2pages_build_path=/home/ce-dev/build/wiki2pages launch_hugo_server=false' -i /home/ce-dev/ansible/bin/hosts /home/ce-dev/build/wiki2pages/ce-dev/ansible/deploy.yml" + /usr/bin/su - ce-dev -c "cd /home/ce-dev/build/wiki2pages/content/ce-deploy-${{ github.event.pull_request.base.ref }} && /bin/sh contribute/toc_hugo.sh" - name: Run Hugo run: | /usr/bin/su - ce-dev -c "cd /home/ce-dev/build/wiki2pages && hugo" - ls -la /home/ce-dev/build/wiki2pages/public/ce-deploy-${{ github.event.pull_request.base.ref }}/ - name: Publish documentation run: | diff --git a/contribute/toc_hugo.sh b/contribute/toc_hugo.sh new file mode 100755 index 00000000..8dc9a1c8 --- /dev/null +++ b/contribute/toc_hugo.sh @@ -0,0 +1,140 @@ +#!/bin/sh +# shellcheck disable=SC2094 +# shellcheck disable=SC2129 +IFS=$(printf '\n\t') +set -e +OWN_DIR=$(dirname "$0") +cd "$OWN_DIR" || exit 1 +OWN_DIR=$(git rev-parse --show-toplevel) +cd "$OWN_DIR" || exit 1 +OWN_DIR=$(pwd -P) + +# @param +# $1 string filepath +cp_role_page(){ + RELATIVE=$(realpath --relative-to="$OWN_DIR" "$(dirname "$1")") + if [ ! -d "$OWN_DIR/docs/$RELATIVE" ]; then + mkdir -p "$OWN_DIR/docs/$RELATIVE" + fi + cp "$1" "$OWN_DIR/docs/$RELATIVE.md" +} + +# @param +# $1 string folder +cp_single_page(){ + if [ ! -d "$OWN_DIR/docs/$1" ]; then + mkdir "$OWN_DIR/docs/$1" + fi + cp "$OWN_DIR/$1/README.md" "$OWN_DIR/docs/$1.md" +} + +# @param +# $1 (string) filename +parse_role_variables(){ + TMP_MD=$(mktemp) + WRITE=1 + # Ensure we have a trailing line. + echo "" >> "$1" + while read -r LINE; do + case $LINE in + '') + echo "$LINE" >> "$TMP_MD" + generate_role_variables "$1" + WRITE=0 + ;; + '') + echo "$LINE" >> "$TMP_MD" + WRITE=1 + ;; + '') + echo "$LINE" >> "$TMP_MD" + WRITE=0 + ;; + '') + echo "$LINE" >> "$TMP_MD" + WRITE=1 + ;; + *) + if [ $WRITE = 1 ]; then + echo "$LINE" >> "$TMP_MD" + fi + ;; + esac + done < "$1" + printf '%s\n' "$(cat "$TMP_MD")" > "$1" + rm "$TMP_MD" +} + +# @param +# $1 (string) filename +generate_role_variables(){ + VAR_FILE="$(dirname "$1")/defaults/main.yml" + if [ -f "$VAR_FILE" ]; then + echo "## Default variables" >> "$TMP_MD" + echo '```yaml' >> "$TMP_MD" + cat "$VAR_FILE" >> "$TMP_MD" + echo "" >> "$TMP_MD" + echo '```' >> "$TMP_MD" + echo "" >> "$TMP_MD" + fi +} + +generate_roles_toc(){ + TMP_SIDEBAR=$(mktemp) + WRITE="true" + while read -r LINE; do + case $LINE in + " - [Roles](roles)") + echo "$LINE" >> "$TMP_SIDEBAR" + parse_roles_toc roles 2 + WRITE="false" + ;; + " -"*) + WRITE="true" + echo "$LINE" >> "$TMP_SIDEBAR" + ;; + *) + if [ "$WRITE" = "true" ]; then + echo "$LINE" >> "$TMP_SIDEBAR" + fi + ;; + esac + done < "$OWN_DIR/docs/_Sidebar.md" + mv "$TMP_SIDEBAR" "$OWN_DIR/docs/_Sidebar.md" +} + +parse_roles_toc(){ + ROLES=$(find "$OWN_DIR/$1" -mindepth 2 -maxdepth 2 -name "README.md" | sort) + for ROLE in $ROLES; do + WRITE="true" + INDENT=$(printf %$(($2 * 2))s) + RELATIVE=$(realpath --relative-to="$OWN_DIR" "$(dirname "$ROLE")") + while read -r LINE; do + case $LINE in + "# "*) + if [ "$WRITE" = "true" ]; then + TITLE=$(echo "$LINE" | cut -c 3-) + echo "$INDENT"" - [$TITLE]($RELATIVE)" >> "$TMP_SIDEBAR" + WRITE="false" + fi + ;; + esac + done < "$ROLE" + parse_roles_toc "$RELATIVE" $(($2 + 1)) + done +} + +rm -rf "$OWN_DIR/docs/roles" +ROLE_PAGES=$(find "$OWN_DIR/roles" -name "README.md") +for ROLE_PAGE in $ROLE_PAGES; do + parse_role_variables "$ROLE_PAGE" +done +for ROLE_PAGE in $ROLE_PAGES; do + cp_role_page "$ROLE_PAGE" +done +generate_roles_toc + + +cp_single_page install +cp_single_page contribute +cp_single_page scripts diff --git a/docs/_Sidebar.md b/docs/_Sidebar.md index a50bee09..f27bbbbe 100644 --- a/docs/_Sidebar.md +++ b/docs/_Sidebar.md @@ -3,6 +3,15 @@ - [Install](install) - [Usage](scripts) - [Roles](roles) + - [Exit](roles/_exit) + - [Init](roles/_init) + - ["Meta"](roles/_meta) + - [Drupal 10](roles/_meta/deploy-drupal10) + - [Drupal 7](roles/_meta/deploy-drupal7) + - [Drupal 8](roles/_meta/deploy-drupal8) + - [Matomo](roles/_meta/deploy-matomo) + - [Mautic](roles/_meta/deploy-mautic) + - [SimpleSAMLphp](roles/_meta/deploy-simplesamlphp) - [API call](roles/api_call) - [Config](roles/cache_clear) - [Drupal 7](roles/cache_clear/cache_clear-drupal7) @@ -32,17 +41,8 @@ - [MySQL backups](roles/database_backup/database_backup-mysql) - [Deploy](roles/deploy_code) - [Deploy container](roles/deploy_container) - - [Exit](roles/_exit) - - [Init](roles/_init) - [LHCI run](roles/lhci_run) - [Maintenance Mode](roles/maintenance_mode) - - ["Meta"](roles/_meta) - - [Drupal 10](roles/_meta/deploy-drupal10) - - [Drupal 7](roles/_meta/deploy-drupal7) - - [Drupal 8](roles/_meta/deploy-drupal8) - - [Matomo](roles/_meta/deploy-matomo) - - [Mautic](roles/_meta/deploy-mautic) - - [SimpleSAMLphp](roles/_meta/deploy-simplesamlphp) - [NPM](roles/npm) - [Sync roles](roles/sync) - [Database sync](roles/sync/database_sync) diff --git a/docs/roles/cron/cron_drupal7.md b/docs/roles/cron/cron_drupal7.md index 32c2103f..dd46727a 100644 --- a/docs/roles/cron/cron_drupal7.md +++ b/docs/roles/cron/cron_drupal7.md @@ -19,6 +19,8 @@ drupal: # month: job: cron # disabled: true + # mailto: "{{ drupal.cron_mailto | default('') }}" # Each cron can have it's own mailto and can be configured to use different e-mail addresses. + cron_mailto: "" # If the sites are being deployed to an ASG, setting defer to true will create the crontab entry on the deploy server rather than all of the app servers. defer: false # If defer is set to true, the Ansible target must be declared with defer_target. If using a group, include the index. For example, _ce_www_dev[0] diff --git a/docs/roles/cron/cron_drupal8.md b/docs/roles/cron/cron_drupal8.md index bafd49b0..7ff5f24c 100644 --- a/docs/roles/cron/cron_drupal8.md +++ b/docs/roles/cron/cron_drupal8.md @@ -19,6 +19,8 @@ drupal: # month: job: cron # disabled: true + # mailto: "{{ drupal.cron_mailto | default('') }}" # Each cron can have it's own mailto and can be configured to use different e-mail addresses. + cron_mailto: "" # If the sites are being deployed to an ASG, setting defer to true will create the crontab entry on the deploy server rather than all of the app servers. defer: false # If defer is set to true, the Ansible target must be declared with defer_target. If using a group, include the index. For example, _ce_www_dev[0] diff --git a/roles/cron/cron_drupal7/README.md b/roles/cron/cron_drupal7/README.md index 32c2103f..dd46727a 100644 --- a/roles/cron/cron_drupal7/README.md +++ b/roles/cron/cron_drupal7/README.md @@ -19,6 +19,8 @@ drupal: # month: job: cron # disabled: true + # mailto: "{{ drupal.cron_mailto | default('') }}" # Each cron can have it's own mailto and can be configured to use different e-mail addresses. + cron_mailto: "" # If the sites are being deployed to an ASG, setting defer to true will create the crontab entry on the deploy server rather than all of the app servers. defer: false # If defer is set to true, the Ansible target must be declared with defer_target. If using a group, include the index. For example, _ce_www_dev[0] diff --git a/roles/cron/cron_drupal7/defaults/main.yml b/roles/cron/cron_drupal7/defaults/main.yml index 9d4b90f6..636557ca 100644 --- a/roles/cron/cron_drupal7/defaults/main.yml +++ b/roles/cron/cron_drupal7/defaults/main.yml @@ -12,6 +12,8 @@ drupal: # month: job: cron # disabled: true + # mailto: "{{ drupal.cron_mailto | default('') }}" # Each cron can have it's own mailto and can be configured to use different e-mail addresses. + cron_mailto: "" # If the sites are being deployed to an ASG, setting defer to true will create the crontab entry on the deploy server rather than all of the app servers. defer: false # If defer is set to true, the Ansible target must be declared with defer_target. If using a group, include the index. For example, _ce_www_dev[0] diff --git a/roles/cron/cron_drupal7/tasks/job.yml b/roles/cron/cron_drupal7/tasks/job.yml index ae176e11..50194883 100644 --- a/roles/cron/cron_drupal7/tasks/job.yml +++ b/roles/cron/cron_drupal7/tasks/job.yml @@ -1,7 +1,7 @@ --- - name: Define cron job command. ansible.builtin.set_fact: - _cron_job_command: "cd {{ live_symlink_dest }}/{{ webroot }}/sites/{{ site.folder }} && {{ drupal.drush_location }} {{ entry.job }}" + _cron_job_command: "cd {{ live_symlink_dest }}/{{ webroot }}/sites/{{ site.folder }} && {{ drupal.drush_location }} {{ entry.job }} >/dev/null" - name: Define cron job command if deferred (ASG). ansible.builtin.set_fact: @@ -20,6 +20,13 @@ - drupal.defer is defined - drupal.defer +- name: Set global MAILTO for cron jobs (if defined) + community.general.cronvar: + name: MAILTO + value: "{{ drupal.cron_mailto | default(omit) }}" + state: present + when: drupal.cron_mailto is defined and drupal.cron_mailto | length > 0 + - name: Setup Drupal cron tasks on app server. ansible.builtin.cron: name: "{{ project_name }}_{{ site.folder }}_{{ build_type }}_{{ entry.job }}" @@ -28,7 +35,11 @@ day: "{{ entry.day | default(omit) }}" weekday: "{{ entry.weekday | default(omit) }}" month: "{{ entry.month | default(omit) }}" - job: "{{ _cron_job_command }}" + job: | + {% if entry.mailto is defined and entry.mailto | length > 0 %} + MAILTO={{ entry.mailto | trim }} + {% endif %} + {{ _cron_job_command | trim }} state: present disabled: "{{ entry.disabled | default(omit) }}" delegate_to: "{{ 'localhost' if drupal.defer else inventory_hostname }}" diff --git a/roles/cron/cron_drupal8/README.md b/roles/cron/cron_drupal8/README.md index bafd49b0..7ff5f24c 100644 --- a/roles/cron/cron_drupal8/README.md +++ b/roles/cron/cron_drupal8/README.md @@ -19,6 +19,8 @@ drupal: # month: job: cron # disabled: true + # mailto: "{{ drupal.cron_mailto | default('') }}" # Each cron can have it's own mailto and can be configured to use different e-mail addresses. + cron_mailto: "" # If the sites are being deployed to an ASG, setting defer to true will create the crontab entry on the deploy server rather than all of the app servers. defer: false # If defer is set to true, the Ansible target must be declared with defer_target. If using a group, include the index. For example, _ce_www_dev[0] diff --git a/roles/cron/cron_drupal8/defaults/main.yml b/roles/cron/cron_drupal8/defaults/main.yml index 9d4b90f6..636557ca 100644 --- a/roles/cron/cron_drupal8/defaults/main.yml +++ b/roles/cron/cron_drupal8/defaults/main.yml @@ -12,6 +12,8 @@ drupal: # month: job: cron # disabled: true + # mailto: "{{ drupal.cron_mailto | default('') }}" # Each cron can have it's own mailto and can be configured to use different e-mail addresses. + cron_mailto: "" # If the sites are being deployed to an ASG, setting defer to true will create the crontab entry on the deploy server rather than all of the app servers. defer: false # If defer is set to true, the Ansible target must be declared with defer_target. If using a group, include the index. For example, _ce_www_dev[0] diff --git a/roles/cron/cron_drupal8/tasks/job.yml b/roles/cron/cron_drupal8/tasks/job.yml index ffa28b2c..23c7bd5b 100644 --- a/roles/cron/cron_drupal8/tasks/job.yml +++ b/roles/cron/cron_drupal8/tasks/job.yml @@ -1,7 +1,7 @@ --- - name: Define cron job command. ansible.builtin.set_fact: - _cron_job_command: "cd {{ live_symlink_dest }}/{{ webroot }}/sites/{{ site.folder }} && {{ drupal.drush_location }} {{ entry.job }}" + _cron_job_command: "cd {{ live_symlink_dest }}/{{ webroot }}/sites/{{ site.folder }} && {{ drupal.drush_location }} {{ entry.job }} >/dev/null" when: deploy_operation == "deploy" - name: Define cron job command if deferred (ASG). @@ -21,6 +21,13 @@ - drupal.defer is defined - drupal.defer +- name: Set global MAILTO for cron jobs (if defined) + community.general.cronvar: + name: MAILTO + value: "{{ drupal.cron_mailto | default(omit) }}" + state: present + when: drupal.cron_mailto is defined and drupal.cron_mailto | length > 0 + - name: Setup Drupal cron tasks on app server. ansible.builtin.cron: name: "{{ project_name }}_{{ site.folder }}_{{ build_type }}_{{ entry.job }}" @@ -29,7 +36,11 @@ day: "{{ entry.day | default(omit) }}" weekday: "{{ entry.weekday | default(omit) }}" month: "{{ entry.month | default(omit) }}" - job: "{{ _cron_job_command }}" + job: | + {% if entry.mailto is defined and entry.mailto | length > 0 %} + MAILTO={{ entry.mailto | trim }} + {% endif %} + {{ _cron_job_command | trim }} state: present disabled: "{{ entry.disabled | default(omit) }}" delegate_to: "{{ 'localhost' if drupal.defer else inventory_hostname }}"