├── .github ├── README.template.md ├── actions │ ├── eject │ │ └── action.yml │ ├── new-release │ │ └── action.yml │ ├── sync-assets │ │ └── action.yml │ └── trellis-cli │ │ └── action.yml ├── dependabot.yml ├── examples │ ├── dryrun-production.yml │ ├── dryrun-staging.yml │ ├── eject-production.yml │ └── sage10-build-test.yml ├── template.yml └── workflows │ ├── deploy-production.yml │ ├── deploy-staging.yml │ ├── set-trellis-deploy-keys.yml │ ├── sync-content-production-to-staging.yml │ └── update-readme.yml ├── CODE_OF_CONDUCT.md ├── FUNDING.yml ├── LICENSE.md └── README.md /.github/README.template.md: -------------------------------------------------------------------------------- 1 | # example.com 2 | 3 | This [Trellis](https://roots.io/trellis/)-based WordPress project uses GitHub Actions for continuous deployment. 4 | 5 | [![Deploy to staging](https://github.com/MWDelaney/example.com/actions/workflows/deploy-staging.yml/badge.svg?branch=staging)](https://github.com/MWDelaney/example.com/actions/workflows/deploy-staging.yml) [![Deploy to production](https://github.com/MWDelaney/example.com/actions/workflows/deploy-production.yml/badge.svg)](https://github.com/MWDelaney/example.com/actions/workflows/deploy-production.yml) 6 | 7 | ## Deployment 8 | 9 | Deployment occurs automatically from this GitHub repository using [GitHub Actions](https://github.com/features/actions). 10 | 11 | This project deploys to the `staging` and `production` environments when a `pull_request` is `merged` to `staging` or `main` branches respectively. 12 | 13 | GitHub **Deployments** maintain a history of deployments and provide links to the current deployments in each. 14 | 15 | [See the current deployments](https://github.com/MWDelaney/example.com/deployments) 16 | 17 |
18 | Initial Setup 19 | 20 | _Note: these instructions presume your `staging` and `production` environments (servers) and DNS are already configured._ 21 | 22 | ### System Requirements 23 | 24 | * [Trellis CLI](https://github.com/roots/trellis-cli) 25 | * [Github CLI](https://cli.github.com) 26 | 27 | ### 1. Create GitHub Secrets 28 | 29 | Deployment relies on 4 GitHub secrets: 30 | 31 | Modify and run the following to generate these secrets: 32 | 33 | ```bash 34 | trellis key generate && gh secret set ANSIBLE_VAULT_PASSWORD -b $(cat trellis/.vault_pass) && gh secret set TRELLIS_SITE_SLUG -b example.com 35 | ``` 36 | 37 | #### Secrets 38 | 39 | * `TRELLIS_DEPLOY_SSH_PRIVATE_KEY` - A private key used by GitHub to connect to your environments. 40 | * `TRELLIS_DEPLOY_SSH_KNOWN_HOSTS` - `known_hosts` keys for your environments. 41 | * `ANSIBLE_VAULT_PASSWORD` - Your new Trellis project's [vault password](https://roots.io/trellis/docs/vault/). 42 | * `TRELLIS_SITE_SLUG` - The slug from your new Trellis project's `wordpress_sites.yml` file. 43 | 44 | ### 2. Generate Trellis aliases 45 | 46 | From your `site` directory, run: 47 | 48 | ```bash 49 | trellis alias 50 | ``` 51 | 52 | And update `site/wp-cli.yml` as instructed. 53 | 54 | ### 3. Grant workflow permissions 55 | 56 | 1. In this repository, navigate to `Settings` -> `Actions` -> `General` -> `Workflow Permissions`. 57 | 2. Enable "read and write" permissions. 58 | 59 | ### 4. Reprovision your environments to allow GitHub's deploy key 60 | 61 | Run the following: 62 | 63 | ```bash 64 | trellis provision staging && trellis provision production 65 | ``` 66 | 67 |
68 | 69 | ## Development 70 | 71 | 1) Clone this repository locally 72 | 2) In the repository directry, run `trellis init` 73 | 3) In the repository directory, run `trellis up` 74 | -------------------------------------------------------------------------------- /.github/actions/eject/action.yml: -------------------------------------------------------------------------------- 1 | name: 'Eject site for delivery' 2 | description: 'Eject Trellis site for delivery to traditional WordPress environments' 3 | 4 | inputs: 5 | from: 6 | description: 'The environment to sync from' 7 | required: true 8 | 9 | trellis_deploy_ssh_private_key: 10 | description: 'The SSH private key to use for deployment' 11 | required: true 12 | 13 | trellis_deploy_ssh_known_hosts: 14 | description: 'The SSH known hosts to use for deployment' 15 | required: true 16 | 17 | trellis_site_slug: 18 | description: 'The Trellis site slug' 19 | required: true 20 | 21 | github_token: 22 | description: 'The GitHub token to use' 23 | required: true 24 | 25 | working_directory: 26 | description: 'The working directory to use' 27 | required: true 28 | 29 | runs: 30 | using: 'composite' 31 | steps: 32 | 33 | # Get the current date and time in both human-readable and machine-readable formats 34 | - name: Get the date and time 35 | shell: bash 36 | id: get_date 37 | run: | 38 | echo "human_date=$(date +'%Y-%m-%d %H:%M:%S')" >> $GITHUB_OUTPUT 39 | echo "machine_date=$(date +'%Y%m%d%H%M%S')" >> $GITHUB_OUTPUT 40 | 41 | # Set up known hosts 42 | - uses: shimataro/ssh-key-action@v2 43 | with: 44 | key: ${{ inputs.TRELLIS_DEPLOY_SSH_PRIVATE_KEY }} 45 | known_hosts: ${{ inputs.TRELLIS_DEPLOY_SSH_KNOWN_HOSTS }} 46 | 47 | # Set up SSH agent 48 | - uses: webfactory/ssh-agent@v0.8.0 49 | with: 50 | ssh-private-key: ${{ inputs.TRELLIS_DEPLOY_SSH_PRIVATE_KEY }} 51 | 52 | # Install WP-CLI 53 | - name: Setup WP-CLI 54 | uses: godaddy-wordpress/setup-wp-cli@1 55 | 56 | # Use WP CLI to the "FROM" site info 57 | - name: Get "from" site info 58 | shell: bash 59 | id: from_site 60 | working-directory: ${{ inputs.working_directory }} 61 | run: | 62 | echo "url=$(wp @"${{ inputs.from }}" option get home)" >> $GITHUB_OUTPUT 63 | echo "domain=$(wp @${{ inputs.from }} option get home | sed 's/https\?:\/\///')" >> $GITHUB_OUTPUT 64 | echo "dir=/srv/www/${{ inputs.TRELLIS_SITE_SLUG }}/current/web/app" >> $GITHUB_OUTPUT 65 | echo "ssh_user=web" >> $GITHUB_OUTPUT 66 | 67 | # If this is the production environment, export the database using wp-cli and pipe it to a local file. 68 | - name: 🗃️ Export database 69 | shell: bash 70 | working-directory: ${{ inputs.working_directory }} 71 | run: | 72 | wp @"${{ inputs.from }}" search-replace app/uploads wp-content/uploads --export > ./"${{ inputs.from }}-${{ steps.get_date.outputs.machine_date }}".sql 73 | 74 | # Check that the database export file exists 75 | - uses: andstor/file-existence-action@v2 76 | if: ${{ inputs.environment == 'production' }} 77 | id: check_db_file 78 | with: 79 | files: "site/${{ inputs.environment }}-*.sql" 80 | fail: true 81 | 82 | # Download the app directory from the production environment using rsync 83 | - name: 📥 Download production wp-content directory 84 | shell: bash 85 | working-directory: ${{ inputs.working_directory }} 86 | run: | 87 | rsync -Laz --progress --delete -e "ssh -o StrictHostKeyChecking=no" "${{ steps.from_site.outputs.ssh_user }}"@"${{ steps.from_site.outputs.domain }}":"${{ steps.from_site.outputs.dir }}"/ ./wp-content 88 | 89 | # Zip the wp-content directory 90 | - name: 🗜️ Compress wp-content directory 91 | shell: bash 92 | working-directory: ${{ inputs.working_directory }} 93 | run: | 94 | zip -r wp-content.zip wp-content 95 | 96 | # Create a Git tag with the environment name and machine-readable date 97 | - name: 🏷️ Tag version 98 | id: tag_version 99 | uses: mathieudutour/github-tag-action@v6.1 100 | with: 101 | github_token: ${{ inputs.github_token }} 102 | custom_tag: "${{ inputs.from }}-${{ steps.get_date.outputs.machine_date }}-eject" 103 | tag_prefix: '' 104 | 105 | # If this is the production environment, create a release with the database export as an asset 106 | - name: 📦 Create release 107 | uses: ncipollo/release-action@v1 108 | with: 109 | tag: "${{ steps.tag_version.outputs.new_tag }}" 110 | artifacts: "site/*.sql,site/wp-content.zip" 111 | token: ${{ inputs.github_token }} 112 | makeLatest: true 113 | name: "${{ inputs.trellis_site_slug }} - ${{ steps.get_date.outputs.human_date }} eject" 114 | body: | 115 | # ${{ inputs.from }} eject 116 | 117 | An export of the site's database and wp-content directory from the ${{ inputs.from }} environment. 118 | 119 | * 📅 ${{ steps.get_date.outputs.human_date }} 120 | -------------------------------------------------------------------------------- /.github/actions/new-release/action.yml: -------------------------------------------------------------------------------- 1 | name: 'New Release' 2 | description: 'Publish a new release to GitHub' 3 | 4 | inputs: 5 | environment: 6 | description: 'The environment to deploy to' 7 | required: true 8 | 9 | site_url: 10 | description: 'The URL of the site to deploy' 11 | required: true 12 | 13 | ref: 14 | description: 'The Git ref to deploy' 15 | required: true 16 | 17 | github_token: 18 | description: 'The GitHub token to use' 19 | required: true 20 | 21 | trellis_site_slug: 22 | description: 'The Trellis site slug' 23 | required: true 24 | 25 | runs: 26 | using: 'composite' 27 | steps: 28 | 29 | # Get the current date and time in both human-readable and machine-readable formats 30 | - name: Get the date and time 31 | shell: bash 32 | id: get_date 33 | run: | 34 | echo "human_date=$(date +'%Y-%m-%d %H:%M:%S')" >> $GITHUB_OUTPUT 35 | echo "machine_date=$(date +'%Y%m%d%H%M%S')" >> $GITHUB_OUTPUT 36 | 37 | # Use WP CLI to the site info 38 | - name: Get ${{ inputs.environment }} site info 39 | shell: bash 40 | id: from_site 41 | working-directory: site 42 | run: | 43 | echo "url=$(wp @"${{ inputs.environment }}" option get home)" >> $GITHUB_OUTPUT 44 | echo "domain=$(trellis exec ansible-inventory --list | jq -r '.["${{ inputs.environment }}"].hosts[0]')" >> $GITHUB_OUTPUT 45 | echo "dir=/srv/www/${{ inputs.TRELLIS_SITE_SLUG }}/current/web/app/uploads" >> $GITHUB_OUTPUT 46 | echo "ssh_user=web" >> $GITHUB_OUTPUT 47 | 48 | # Create a Git tag with the environment name and machine-readable date 49 | - name: 🏷️ Tag version 50 | id: tag_version 51 | uses: mathieudutour/github-tag-action@v6.1 52 | with: 53 | github_token: ${{ inputs.github_token }} 54 | custom_tag: "${{ inputs.environment }}-${{ steps.get_date.outputs.machine_date }}" 55 | tag_prefix: '' 56 | 57 | # If this is the production environment, export the database to a local file with the name of the environment and the date 58 | - name: 🗃️ Export database 59 | shell: bash 60 | if: ${{ inputs.environment == 'production' }} 61 | working-directory: site 62 | run: | 63 | wp @"${{ inputs.environment }}" db export --default-character-set=utf8mb4 - > ./"${{ inputs.environment }}"-"$(date +'%Y-%m-%d-%H-%M-%S')".sql 64 | 65 | # Download the app/uploads directory from the production environment using rsync 66 | - name: 📥 Download production uploads directory 67 | shell: bash 68 | if: ${{ inputs.environment == 'production' }} 69 | run: | 70 | rsync -Laz --progress --delete -e "ssh -o StrictHostKeyChecking=no" "${{ steps.from_site.outputs.ssh_user }}"@"${{ steps.from_site.outputs.domain }}":"${{ steps.from_site.outputs.dir }}"/ ./uploads 71 | 72 | # Zip the uploads directory 73 | - name: 🗜️ Compress uploads directory 74 | shell: bash 75 | if: ${{ inputs.environment == 'production' }} 76 | run: | 77 | zip -r uploads-"${{ steps.get_date.outputs.machine_date }}".zip ./uploads 78 | 79 | # If this is the production environment, create a release with the database export as an asset 80 | - name: 📦 Create release 81 | if: ${{ inputs.environment == 'production' }} 82 | uses: ncipollo/release-action@v1 83 | with: 84 | tag: "${{ steps.tag_version.outputs.new_tag }}" 85 | artifacts: "site/*.sql,uploads-*.zip" 86 | token: ${{ inputs.github_token }} 87 | makeLatest: true 88 | name: "${{ inputs.trellis_site_slug }} - ${{ steps.get_date.outputs.human_date }}" 89 | body: | 90 | # ${{ inputs.environment }} 91 | 92 | * 🔗 ${{ inputs.site_url }} 93 | * 📅 ${{ steps.get_date.outputs.human_date }} 94 | 95 | ${{ steps.tag_version.outputs.changelog }} 96 | -------------------------------------------------------------------------------- /.github/actions/sync-assets/action.yml: -------------------------------------------------------------------------------- 1 | name: 'Sync assets' 2 | description: 'Synchronize assets between environments' 3 | 4 | inputs: 5 | from: 6 | description: 'The environment to sync from' 7 | required: true 8 | 9 | to: 10 | description: 'The environment to sync to' 11 | required: true 12 | 13 | trellis_deploy_ssh_private_key: 14 | description: 'The SSH private key to use for deployment' 15 | required: true 16 | 17 | trellis_deploy_ssh_known_hosts: 18 | description: 'The SSH known hosts to use for deployment' 19 | required: true 20 | 21 | trellis_site_slug: 22 | description: 'The Trellis site slug' 23 | required: true 24 | 25 | working_directory: 26 | description: 'The working directory to use' 27 | required: true 28 | 29 | github_token: 30 | description: 'The GitHub token to use' 31 | required: true 32 | 33 | ansible_vault_password: 34 | description: 'The Ansible vault password to use' 35 | required: true 36 | 37 | runs: 38 | using: 'composite' 39 | steps: 40 | 41 | # Set up known hosts 42 | - uses: shimataro/ssh-key-action@v2 43 | with: 44 | key: ${{ inputs.TRELLIS_DEPLOY_SSH_PRIVATE_KEY }} 45 | known_hosts: ${{ inputs.TRELLIS_DEPLOY_SSH_KNOWN_HOSTS }} 46 | 47 | # Set up SSH agent 48 | - uses: webfactory/ssh-agent@v0.8.0 49 | with: 50 | ssh-private-key: ${{ inputs.TRELLIS_DEPLOY_SSH_PRIVATE_KEY }} 51 | 52 | # Install Trellis CLI 53 | - uses: roots/setup-trellis-cli@v1 54 | with: 55 | repo-token: ${{ inputs.GITHUB_TOKEN }} 56 | ansible-vault-password: ${{ inputs.ANSIBLE_VAULT_PASSWORD }} 57 | 58 | 59 | # Install WP-CLI 60 | - name: Setup WP-CLI 61 | uses: godaddy-wordpress/setup-wp-cli@1 62 | 63 | # Use WP CLI to the "FROM" site info 64 | - name: Get "from" site info 65 | shell: bash 66 | id: from_site 67 | working-directory: ${{ inputs.WORKING_DIRECTORY }} 68 | run: | 69 | echo "url=$(wp @"${{ inputs.FROM }}" option get home)" >> $GITHUB_OUTPUT 70 | echo "domain=$(trellis exec ansible-inventory --list | jq -r '.["${{ inputs.FROM }}"].hosts[0]')" >> $GITHUB_OUTPUT 71 | echo "dir=/srv/www/${{ inputs.TRELLIS_SITE_SLUG }}/shared/uploads" >> $GITHUB_OUTPUT 72 | echo "ssh_user=web" >> $GITHUB_OUTPUT 73 | 74 | # Use WP CLI to get the "TO" site info 75 | - name: Get "to" site info 76 | shell: bash 77 | id: to_site 78 | working-directory: ${{ inputs.WORKING_DIRECTORY }} 79 | run: | 80 | echo "url=$(wp @"${{ inputs.TO }}" option get home)" >> $GITHUB_OUTPUT 81 | echo "domain=$(trellis exec ansible-inventory --list | jq -r '.["${{ inputs.TO }}"].hosts[0]')" >> $GITHUB_OUTPUT 82 | echo "dir=/srv/www/${{ inputs.TRELLIS_SITE_SLUG }}/shared/uploads" >> $GITHUB_OUTPUT 83 | echo "ssh_user=web" >> $GITHUB_OUTPUT 84 | 85 | # Sync database with WP-CLI 86 | - name: 🗃️ Sync database 87 | working-directory: ${{ inputs.WORKING_DIRECTORY }} 88 | shell: bash 89 | run: | 90 | wp "@${{ inputs.TO }}" db export --default-character-set=utf8mb4 && 91 | wp "@${{ inputs.TO }}" db reset --yes && 92 | wp "@${{ inputs.FROM }}" db export --default-character-set=utf8mb4 - | wp "@${{ inputs.TO }}" db import - && 93 | wp "@${{ inputs.TO }}" search-replace "${{ steps.from_site.outputs.url }}" "${{ steps.to_site.outputs.url }}" --all-tables-with-prefix 94 | 95 | # Sync uploads with ssh and rsync 96 | - name: 🍱 Sync uploads 97 | shell: bash 98 | run: | 99 | ssh -o ForwardAgent=yes ${{ steps.from_site.outputs.ssh_user }}@${{ steps.from_site.outputs.domain }} "rsync -aze 'ssh -o StrictHostKeyChecking=no' --progress ${{ steps.from_site.outputs.dir }} ${{ steps.to_site.outputs.ssh_user }}@${{ steps.to_site.outputs.domain }}:${{ steps.to_site.outputs.dir }}" 100 | 101 | # Report success to the log 102 | - name: 🎉 Sync complete 103 | shell: bash 104 | run: | 105 | echo "::notice::Successfully synchronized assets from ${{ inputs.from }} to ${{ inputs.to }}" 106 | -------------------------------------------------------------------------------- /.github/actions/trellis-cli/action.yml: -------------------------------------------------------------------------------- 1 | name: '🚀 Deploy to Trellis environment' 2 | description: 'Deploys to a Trellis environment using trellis-cli' 3 | 4 | inputs: 5 | extra-vars: 6 | description: 'Extra variables to pass to Ansible' 7 | required: true 8 | default: '""' 9 | 10 | environment: 11 | description: 'The environment to deploy to' 12 | required: true 13 | 14 | github_token: 15 | description: 'The GitHub token to use' 16 | required: true 17 | 18 | trellis_site_slug: 19 | description: 'The Trellis site slug' 20 | required: true 21 | 22 | trellis_deploy_ssh_private_key: 23 | description: 'The SSH private key to use for deployment' 24 | required: true 25 | 26 | trellis_deploy_ssh_known_hosts: 27 | description: 'The SSH known hosts to use for deployment' 28 | required: true 29 | 30 | ansible_vault_password: 31 | description: 'The Ansible vault password to use' 32 | required: true 33 | 34 | dry_run: 35 | description: 'Whether to run a dry-run deployment' 36 | required: false 37 | 38 | runs: 39 | using: 'composite' 40 | steps: 41 | 42 | # If this is a dry run, show a message indicating so 43 | - name: Is this a dry run? 44 | if: ${{ inputs.dry_run }} 45 | shell: bash 46 | run: | 47 | echo "::notice::This is a dry run, this deployment will not be finalized." 48 | 49 | # Start a GitHub "deployment" 50 | - name: Start deployment 51 | uses: bobheadxi/deployments@v1 52 | id: deployment 53 | if: ${{ inputs.dry_run == '' }} 54 | with: 55 | step: start 56 | token: ${{ inputs.GITHUB_TOKEN }} 57 | env: ${{ inputs.environment }} 58 | 59 | # Optional sage build, should be refactored into separate workflow 60 | - uses: actions/setup-node@v3 61 | with: 62 | node-version: '16' 63 | 64 | # Check that the deploy.yml file exists, a reasonable check that this is a Trellis project 65 | - uses: andstor/file-existence-action@v2 66 | id: check_files 67 | with: 68 | files: "trellis/deploy.yml" 69 | 70 | # Set up known hosts 71 | - uses: shimataro/ssh-key-action@v2 72 | with: 73 | key: ${{ inputs.TRELLIS_DEPLOY_SSH_PRIVATE_KEY }} 74 | known_hosts: ${{ inputs.TRELLIS_DEPLOY_SSH_KNOWN_HOSTS }} 75 | 76 | # Set up SSH agent 77 | - uses: webfactory/ssh-agent@v0.8.0 78 | with: 79 | ssh-private-key: ${{ inputs.TRELLIS_DEPLOY_SSH_PRIVATE_KEY }} 80 | 81 | # Install Trellis CLI 82 | - uses: roots/setup-trellis-cli@v1 83 | with: 84 | repo-token: ${{ inputs.GITHUB_TOKEN }} 85 | ansible-vault-password: ${{ inputs.ANSIBLE_VAULT_PASSWORD }} 86 | 87 | # Run Trellis deploy 88 | - name: Deploy 89 | shell: bash 90 | if: steps.check_files.outputs.files_exists == 'true' 91 | run: trellis deploy --extra-vars ${{ inputs.extra-vars }} ${{ inputs.environment }} 92 | 93 | # install WP-CLI 94 | - name: Setup WP-CLI 95 | if: ${{ inputs.dry_run == '' }} 96 | uses: godaddy-wordpress/setup-wp-cli@1 97 | 98 | # Use WP CLI to get the site URL from the deployed site 99 | - name: Get site URL 100 | if: ${{ inputs.dry_run == '' }} 101 | shell: bash 102 | working-directory: site 103 | id: site_url 104 | run: | 105 | echo "url=$(wp @"${{ inputs.environment }}" option get home)" >> $GITHUB_OUTPUT 106 | 107 | # Call the new-release action to create a tag and/or release 108 | - name: Create release 109 | uses: ./.github/actions/new-release 110 | if: ${{ inputs.dry_run == '' }} 111 | with: 112 | environment: ${{ inputs.environment }} 113 | site_url: ${{ steps.site_url.outputs.url }} 114 | ref: ${{ github.event.pull_request.base.ref }} 115 | github_token: ${{ inputs.GITHUB_TOKEN }} 116 | trellis_site_slug: ${{ inputs.TRELLIS_SITE_SLUG }} 117 | 118 | # Finish the GitHub "deployment" 119 | - name: Finalize deployment status 120 | uses: bobheadxi/deployments@v1 121 | if: ${{ inputs.dry_run == '' }} 122 | with: 123 | step: finish 124 | token: ${{ inputs.GITHUB_TOKEN }} 125 | status: ${{ job.status }} 126 | env: ${{ steps.deployment.outputs.env }} 127 | env_url: ${{ steps.site_url.outputs.url }} 128 | deployment_id: ${{ steps.deployment.outputs.deployment_id }} 129 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # ./github/dependabot.yml 2 | ## 3 | # Dependabot configuration for Bedrock 4 | # 5 | # Check for updates to Composer packages weekly 6 | ## 7 | 8 | version: 2 9 | updates: 10 | - package-ecosystem: "composer" # See documentation for possible values 11 | directory: "/site" # Location of package manifests 12 | target-branch: "staging" 13 | schedule: 14 | interval: "weekly" 15 | open-pull-requests-limit: 20 16 | -------------------------------------------------------------------------------- /.github/examples/dryrun-production.yml: -------------------------------------------------------------------------------- 1 | # .github/workflows/dryrun-production.yml 2 | ## 3 | # Dry-run deploy to a temporary directory in the production environment with Trellis 4 | ## 5 | 6 | name: 🧪 Dry-run to production 7 | run-name: dry-run-production 8 | 9 | on: 10 | workflow_dispatch: 11 | pull_request: 12 | branches: [main] 13 | 14 | env: 15 | environment: production 16 | script: | 17 | --- 18 | - name: "This is a dry-run, delete the deploy." 19 | ansible.builtin.file: 20 | state: absent 21 | path: "{{ deploy_helper.new_release_path }}" 22 | 23 | - name: "This is a dry-run, stop here." 24 | meta: end_play 25 | extra-vars: | 26 | { 27 | "deploy_build_after": [ 28 | "{{ playbook_dir }}/roles/deploy/hooks/build-after.yml", 29 | "{{ playbook_dir }}/deploy-hooks/build-after.yml", 30 | "/build-after.yml" 31 | ], 32 | "dry_run": true, 33 | "project_root": "/tmp/trellis" 34 | } 35 | 36 | jobs: 37 | dryrun-production: 38 | runs-on: ubuntu-latest 39 | steps: 40 | 41 | # Checkout the repo 42 | - uses: actions/checkout@v3 43 | with: 44 | ref: ${{ github.event.pull_request.base.ref }} 45 | 46 | # Create a file with the contents of the script environment variable 47 | - name: Create script file 48 | run: echo "${{ env.script }}" > build-after.yml 49 | 50 | # Create a yml file with the contents of the extra-vars 51 | - name: Create extra-vars file 52 | run: | 53 | echo "${{ env.extra-vars }}" > extra-vars.json 54 | 55 | # Deploy site to production 56 | - uses: ./.github/actions/trellis-cli 57 | name: Dry-run deploy to production 58 | if: github.event.pull_request.merged || github.event_name == 'workflow_dispatch' || github.repository != 'MWDelaney/trellis-template' 59 | with: 60 | extra-vars: '"@file:/extra-vars.json"' 61 | environment: ${{ env.environment }} 62 | github_token: ${{ secrets.GITHUB_TOKEN }} 63 | trellis_site_slug: ${{ secrets.TRELLIS_SITE_SLUG }} 64 | trellis_deploy_ssh_private_key: ${{ secrets.TRELLIS_DEPLOY_SSH_PRIVATE_KEY }} 65 | trellis_deploy_ssh_known_hosts: ${{ secrets.TRELLIS_DEPLOY_SSH_KNOWN_HOSTS }} 66 | ansible_vault_password: ${{ secrets.ANSIBLE_VAULT_PASSWORD }} 67 | dry_run: true 68 | -------------------------------------------------------------------------------- /.github/examples/dryrun-staging.yml: -------------------------------------------------------------------------------- 1 | # .github/workflows/dryrun-staging.yml 2 | ## 3 | # Dry-run deploy to a temporary directory in the staging environment with Trellis 4 | ## 5 | 6 | name: 🧪 Dry-run to staging 7 | run-name: dry-run-staging 8 | 9 | on: 10 | workflow_dispatch: 11 | pull_request: 12 | branches: [staging] 13 | 14 | env: 15 | environment: staging 16 | script: | 17 | --- 18 | - name: "This is a dry-run, delete the deploy." 19 | ansible.builtin.file: 20 | state: absent 21 | path: "{{ deploy_helper.new_release_path }}" 22 | 23 | - name: "This is a dry-run, stop here." 24 | meta: end_play 25 | extra-vars: | 26 | { 27 | "deploy_build_after": [ 28 | "{{ playbook_dir }}/roles/deploy/hooks/build-after.yml", 29 | "{{ playbook_dir }}/deploy-hooks/build-after.yml", 30 | "/build-after.yml" 31 | ], 32 | "dry_run": true, 33 | "project_root": "/tmp/trellis" 34 | } 35 | 36 | jobs: 37 | dryrun-staging: 38 | runs-on: ubuntu-latest 39 | steps: 40 | 41 | # Checkout the repo 42 | - uses: actions/checkout@v3 43 | with: 44 | ref: ${{ github.event.pull_request.base.ref }} 45 | 46 | # Create a file with the contents of the script environment variable 47 | - name: Create script file 48 | run: echo "${{ env.script }}" > build-after.yml 49 | 50 | # Create a yml file with the contents of the extra-vars 51 | - name: Create extra-vars file 52 | run: | 53 | echo "${{ env.extra-vars }}" > extra-vars.json 54 | 55 | # Deploy site to staging 56 | - uses: ./.github/actions/trellis-cli 57 | name: Dry-run deploy to staging 58 | if: github.event.pull_request.merged || github.event_name == 'workflow_dispatch' || github.repository != 'MWDelaney/trellis-template' 59 | with: 60 | extra-vars: '"@file:/extra-vars.json"' 61 | environment: ${{ env.environment }} 62 | github_token: ${{ secrets.GITHUB_TOKEN }} 63 | trellis_site_slug: ${{ secrets.TRELLIS_SITE_SLUG }} 64 | trellis_deploy_ssh_private_key: ${{ secrets.TRELLIS_DEPLOY_SSH_PRIVATE_KEY }} 65 | trellis_deploy_ssh_known_hosts: ${{ secrets.TRELLIS_DEPLOY_SSH_KNOWN_HOSTS }} 66 | ansible_vault_password: ${{ secrets.ANSIBLE_VAULT_PASSWORD }} 67 | dry_run: true 68 | -------------------------------------------------------------------------------- /.github/examples/eject-production.yml: -------------------------------------------------------------------------------- 1 | # .github/workflows/eject-production.yml 2 | ## 3 | # Eject production 4 | # 5 | # Export a Trellis-managed site from production 6 | # for delivery to a traditional WordPress environment 7 | # and attach artifacts to the workflow run for download 8 | ## 9 | 10 | name: ⏏️ Eject production for delivery 11 | run-name: eject-production 12 | 13 | on: 14 | workflow_dispatch: 15 | 16 | env: 17 | from: production 18 | 19 | jobs: 20 | eject-production: 21 | runs-on: ubuntu-latest 22 | steps: 23 | 24 | # Checkout the repo 25 | - uses: actions/checkout@v3 26 | with: 27 | ref: ${{ github.event.pull_request.base.ref }} 28 | 29 | # Eject production for delivery 30 | - uses: ./.github/actions/eject 31 | name: Eject production for delivery 32 | with: 33 | from: ${{ env.from }} 34 | trellis_site_slug: ${{ secrets.TRELLIS_SITE_SLUG }} 35 | trellis_deploy_ssh_private_key: ${{ secrets.TRELLIS_DEPLOY_SSH_PRIVATE_KEY }} 36 | trellis_deploy_ssh_known_hosts: ${{ secrets.TRELLIS_DEPLOY_SSH_KNOWN_HOSTS }} 37 | working_directory: ./site 38 | github_token: ${{ secrets.GITHUB_TOKEN }} 39 | -------------------------------------------------------------------------------- /.github/examples/sage10-build-test.yml: -------------------------------------------------------------------------------- 1 | # .github/workflows/sage10-build.yml 2 | ## 3 | # Build a Sage 10-based theme 4 | # 5 | # ⚠️ This example assumes your theme is using Sage 10 6 | # 7 | # Replace `sage` below with your theme folder 8 | # 9 | ## 10 | 11 | name: 🚧 Sage 10 build test 12 | 13 | on: [push] 14 | 15 | env: 16 | theme_slug: sage # Update this with your theme's directory name 17 | node_version: '16' 18 | 19 | jobs: 20 | yarn-build: 21 | 22 | runs-on: ubuntu-latest 23 | defaults: 24 | run: 25 | working-directory: ./site/web/app/themes/${{ env.theme_slug }} 26 | steps: 27 | - uses: actions/checkout@v3 28 | - name: Use Node.js ${{ env.node_version }} 29 | uses: actions/setup-node@v3 30 | with: 31 | node-version: ${{ env.node_version }} 32 | cache: yarn 33 | cache-dependency-path: ./site/web/app/themes/${{ env.theme_slug }}/yarn.lock 34 | 35 | - run: composer install 36 | - run: yarn install 37 | - run: yarn build 38 | -------------------------------------------------------------------------------- /.github/template.yml: -------------------------------------------------------------------------------- 1 | author:mwdelaney 2 | -------------------------------------------------------------------------------- /.github/workflows/deploy-production.yml: -------------------------------------------------------------------------------- 1 | # .github/workflows/deploy-production.yml 2 | ## 3 | # Deploy to production with Trellis 4 | ## 5 | 6 | name: 🚀 Deploy to production 7 | run-name: deploy-production 8 | 9 | on: 10 | workflow_dispatch: 11 | pull_request: 12 | types: [closed] 13 | branches: [main] 14 | 15 | env: 16 | environment: production 17 | 18 | jobs: 19 | deploy-production: 20 | runs-on: ubuntu-latest 21 | if: ${{ github.actor != 'dependabot[bot]' }} 22 | steps: 23 | 24 | # Checkout the repo 25 | - uses: actions/checkout@v3 26 | with: 27 | ref: ${{ github.event.pull_request.base.ref }} 28 | 29 | # Deploy site to production 30 | - uses: ./.github/actions/trellis-cli 31 | name: Deploy to production 32 | if: github.event.pull_request.merged || github.event_name == 'workflow_dispatch' || github.repository != 'MWDelaney/trellis-template' 33 | with: 34 | extra-vars: '""' 35 | environment: ${{ env.environment }} 36 | github_token: ${{ secrets.GITHUB_TOKEN }} 37 | trellis_site_slug: ${{ secrets.TRELLIS_SITE_SLUG }} 38 | trellis_deploy_ssh_private_key: ${{ secrets.TRELLIS_DEPLOY_SSH_PRIVATE_KEY }} 39 | trellis_deploy_ssh_known_hosts: ${{ secrets.TRELLIS_DEPLOY_SSH_KNOWN_HOSTS }} 40 | ansible_vault_password: ${{ secrets.ANSIBLE_VAULT_PASSWORD }} 41 | -------------------------------------------------------------------------------- /.github/workflows/deploy-staging.yml: -------------------------------------------------------------------------------- 1 | # .github/workflows/deploy-staging.yml 2 | ## 3 | # Deploy to staging with Trellis 4 | ## 5 | 6 | name: 🚀 Deploy to staging 7 | run-name: deploy-staging 8 | 9 | on: 10 | workflow_dispatch: 11 | pull_request: 12 | types: [closed] 13 | branches: [staging] 14 | 15 | env: 16 | environment: staging 17 | 18 | jobs: 19 | deploy-staging: 20 | if: ${{ github.actor != 'dependabot[bot]' }} 21 | runs-on: ubuntu-latest 22 | steps: 23 | 24 | # Checkout the repo 25 | - uses: actions/checkout@v3 26 | with: 27 | ref: ${{ github.event.pull_request.base.ref }} 28 | 29 | # Deploy site to staging 30 | - uses: ./.github/actions/trellis-cli 31 | name: Deploy to staging 32 | if: github.event.pull_request.merged || github.event_name == 'workflow_dispatch' || github.repository != 'MWDelaney/trellis-template' 33 | with: 34 | extra-vars: '""' 35 | environment: ${{ env.environment }} 36 | github_token: ${{ secrets.GITHUB_TOKEN }} 37 | trellis_site_slug: ${{ secrets.TRELLIS_SITE_SLUG }} 38 | trellis_deploy_ssh_private_key: ${{ secrets.TRELLIS_DEPLOY_SSH_PRIVATE_KEY }} 39 | trellis_deploy_ssh_known_hosts: ${{ secrets.TRELLIS_DEPLOY_SSH_KNOWN_HOSTS }} 40 | ansible_vault_password: ${{ secrets.ANSIBLE_VAULT_PASSWORD }} 41 | -------------------------------------------------------------------------------- /.github/workflows/set-trellis-deploy-keys.yml: -------------------------------------------------------------------------------- 1 | # .github/workflows/reset-trellis-deploy-keys.yml 2 | ## 3 | # Set or reset SSH deploy keys for a Trellis environment 4 | # 5 | # Set or reset the SSH keys in a Trellis environment to the 6 | # GitHub public key stored in the Trellis project at `trellis/public_keys` 7 | # and optionally add the public keys stored in GitHub secrets at `TRELLIS_DEPLOY_KEYS` 8 | # and the deploy keys configured in Trellis at `trellis/group_vars/all/users.yml`. 9 | ## 10 | 11 | name: 🔑 Set Trellis deploy keys 12 | 13 | on: 14 | workflow_dispatch: 15 | inputs: 16 | environment: 17 | description: 'Enter the Trellis environment to reset and choose at least one source of deploy keys to add' 18 | required: true 19 | default: 'production' 20 | 21 | from_trellis: 22 | type: boolean 23 | description: 'trellis/group_vars/all/users.yml' 24 | default: true 25 | 26 | from_secrets: 27 | type: boolean 28 | description: 'GitHub secret named TRELLIS_DEPLOY_KEYS' 29 | default: false 30 | 31 | new_key: 32 | description: 'Enter a new key' 33 | 34 | jobs: 35 | reset-trellis-deploy-keys: 36 | runs-on: ubuntu-latest 37 | steps: 38 | 39 | # Checkout the repo 40 | - uses: actions/checkout@v3 41 | with: 42 | ref: ${{ github.ref }} 43 | 44 | # Install WP-CLI 45 | - name: Setup WP-CLI 46 | uses: godaddy-wordpress/setup-wp-cli@1 47 | 48 | # Set up known hosts 49 | - uses: shimataro/ssh-key-action@v2 50 | with: 51 | key: ${{ secrets.TRELLIS_DEPLOY_SSH_PRIVATE_KEY }} 52 | known_hosts: ${{ secrets.TRELLIS_DEPLOY_SSH_KNOWN_HOSTS }} 53 | 54 | # Set up SSH agent 55 | - uses: webfactory/ssh-agent@v0.8.0 56 | with: 57 | ssh-private-key: ${{ secrets.TRELLIS_DEPLOY_SSH_PRIVATE_KEY }} 58 | 59 | # Use WP CLI to get information from the specified environment 60 | - name: Get site info 61 | shell: bash 62 | id: environment_site 63 | working-directory: site 64 | run: | 65 | echo "url=$(wp @"${{ inputs.environment }}" option get home)" >> $GITHUB_OUTPUT 66 | echo "domain=$(wp @${{ inputs.environment }} option get home | sed 's/https\?:\/\///')" >> $GITHUB_OUTPUT 67 | echo "dir=/srv/www/${{ secrets.TRELLIS_SITE_SLUG }}/shared/uploads" >> $GITHUB_OUTPUT 68 | echo "ssh_user=web" >> $GITHUB_OUTPUT 69 | 70 | # Get the contents of the ssh keys in the Trellis project at `trellis/public_keys` 71 | - name: Always get GitHub deploy keys from Trellis 72 | id: trellis_deploy_keys 73 | run: | 74 | echo "keys=$(cat trellis/public_keys/*.pub)" >> $GITHUB_OUTPUT 75 | 76 | # Get Trellis configured entries in `trellis/group_vars/all/users.yml` 77 | - name: Maybe get Trellis config keys 78 | uses: mikefarah/yq@v4 79 | id: trellis_config_keys 80 | if: ${{ inputs.from_trellis == true }} 81 | with: 82 | cmd: yq e '.users[] | select(.name == "{{ web_user }}") | .keys[]' trellis/group_vars/all/users.yml | grep -E '^https?://' | xargs -I {} curl -s {} 83 | 84 | 85 | # Get the contents of the ssh keys in the GitHub secrets 86 | - name: Maybe get GitHub secrets keys 87 | id: github_secrets_keys 88 | if: ${{ inputs.from_secrets == true }} 89 | run: | 90 | echo "keys=${{ secrets.TRELLIS_DEPLOY_KEYS }}" >> $GITHUB_OUTPUT 91 | 92 | # If the user has entered a new key, add it to the list of keys 93 | - name: Maybe add new key 94 | id: new_key 95 | if: ${{ inputs.new_key != '' }} 96 | run: | 97 | echo "keys=${{ inputs.new_key }}" >> $GITHUB_OUTPUT 98 | 99 | # Connect to the specififed environment via ssh set the contents of `~/.ssh/authorized_keys` 100 | - name: Replace all authorized keys 101 | shell: bash 102 | run: | 103 | ssh -o ForwardAgent=yes ${{ steps.environment_site.outputs.ssh_user }}@${{ steps.environment_site.outputs.domain }} "echo ${{ steps.trellis_deploy_keys.outputs.keys }}$'\n'${{ steps.trellis_config_keys.outputs.result }}$'\n'${{ steps.github_secrets_keys.outputs.keys }}${{ steps.new_key.outputs.keys }} > ~/.ssh/authorized_keys" 104 | -------------------------------------------------------------------------------- /.github/workflows/sync-content-production-to-staging.yml: -------------------------------------------------------------------------------- 1 | # .github/workflows/sync-content-production-to-staging.yml 2 | ## 3 | # Sync content production->staging 4 | ## 5 | 6 | name: 🔄 Sync content production->staging 7 | run-name: sync-content-production-to-staging 8 | 9 | on: 10 | workflow_dispatch: 11 | 12 | env: 13 | from: production 14 | to: staging 15 | 16 | jobs: 17 | sync-content-production-to-staging: 18 | runs-on: ubuntu-latest 19 | steps: 20 | 21 | # Checkout the repo 22 | - uses: actions/checkout@v3 23 | with: 24 | ref: ${{ github.event.pull_request.base.ref }} 25 | 26 | # Sync content from production to staging 27 | - uses: ./.github/actions/sync-assets 28 | name: Sync assets from production to staging 29 | if: github.event.pull_request.merged || github.event_name == 'workflow_dispatch' || github.repository != 'MWDelaney/trellis-template' 30 | with: 31 | from: ${{ env.from }} 32 | to: ${{ env.to }} 33 | trellis_site_slug: ${{ secrets.TRELLIS_SITE_SLUG }} 34 | trellis_deploy_ssh_private_key: ${{ secrets.TRELLIS_DEPLOY_SSH_PRIVATE_KEY }} 35 | trellis_deploy_ssh_known_hosts: ${{ secrets.TRELLIS_DEPLOY_SSH_KNOWN_HOSTS }} 36 | github_token: ${{ secrets.GITHUB_TOKEN }} 37 | ansible_vault_password: ${{ secrets.ANSIBLE_VAULT_PASSWORD }} 38 | working_directory: ./site 39 | -------------------------------------------------------------------------------- /.github/workflows/update-readme.yml: -------------------------------------------------------------------------------- 1 | # .github/workflows/update-readme.yml 2 | ## 3 | # Update readme to reflect repository name 4 | ## 5 | 6 | name: 📝 Update README 7 | run-name: update-readme 8 | 9 | on: 10 | workflow_dispatch: 11 | 12 | jobs: 13 | update-readme: 14 | runs-on: ubuntu-latest 15 | steps: 16 | 17 | #Checkout the repo 18 | - uses: actions/checkout@v3 19 | 20 | # Replace repository slug 21 | - name: Replace repository slug 22 | uses: jacobtomlinson/gha-find-replace@v2 23 | with: 24 | find: "MWDelaney/example.com" 25 | replace: "${{ github.repository_owner }}/${{ github.event.repository.name }}" 26 | include: ".github/README.template.md" 27 | regex: false 28 | 29 | # Replace repository title 30 | - name: Replace repository title 31 | uses: jacobtomlinson/gha-find-replace@v3 32 | with: 33 | find: "# example.com" 34 | replace: "# ${{ github.event.repository.name }}" 35 | include: ".github/README.template.md" 36 | regex: false 37 | 38 | # Change the filename to .README 39 | - name: Rename README 40 | shell: bash 41 | run: mv .github/README.template.md .github/README.md 42 | 43 | # Commit changes 44 | - name: Commit changes 45 | uses: EndBug/add-and-commit@v9 46 | with: 47 | message: "Update README" 48 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | michael@michaeldelaney.me. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: MWDelaney 4 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Michael W. Delaney 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🚀 Trellis GitHub Deployment 2 | 3 | Automatically deploy your [Trellis](https://roots.io/trellis/)-based WordPress site to `staging` and `production` environments on merge to `staging` and `main` branches respectively, and keep plugins, themes, and WordPress core up to date with Dependabot. 4 | 5 | 🔥 Based on [setup-trellis-cli](https://github.com/roots/setup-trellis-cli). This set of GitHub actions and workflows extend the functionality of that package to more tightly integrate with GitHub's native features. 6 | 7 | ## Features 8 | 9 | - 🚀 **Automatic (or manual) deployment** to `staging` and `production` environments using [GitHub Actions](https://github.com/features/actions) when pull requests are merged to your `staging` and `main` branches respectively. 10 | - 🔄 **On-Demand one-way sync** database and assets from `production` to `staging`. 11 | - 🔗 **Maintains a history of [GitHub Deployments](https://docs.github.com/en/rest/reference/repos#create-a-deployment)** and provides links to the current deployments in each environment. 12 | - 🔑 **Re-set deployment keys on-demand** when you need to change who has access to `staging` or `production`. 13 | - 📦 **WordPress plugin, core, and theme updates** managed with [Dependabot](https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/about-dependabot-version-updates). 14 | - 📝 **Optionally generates README.md** with deployment status badges and setup instructions. 15 | 16 | ### Optional Additional Workflows 17 | 18 |
19 | Click to expand 20 | 21 | - 🌱 **Sage 10 build test** on pull request or on-demand (make sure your theme builds before you deploy it!). 22 | - 🧪 **Dry-run deployments** to `staging` and `production` environments on pull request or on-demand (confirm Trellis can deploy successfully without finalizing the deployment). 23 | - ⏏️ **Eject WordPress site** from Bedrock and Trellis and prepare database and assets for migration to traditional WordPress hosting. 24 | 25 |
26 | 27 | --- 28 | 29 | ## Requirements 30 | 31 | - A [Trellis](https://roots.io/trellis/docs/installing-trellis/)-based WordPress website. 32 | - Provisioned remote servers for `staging` and `production` environments. 33 | 34 | **NOTE**: This project presumes your Trellis-based website and its `staging` and `production` environments are provisioned and deploying successfully. 35 | 36 | ## Installation 37 | 38 | 1. Copy the `.github` directory from this repository to your Trellis-based WordPress site's repository. 39 | 2. Refer to the Initial Setup instructions in `.github/README.md` to configure your GitHub repository for deployment. 40 | 41 | --- 42 | 43 | ## Usage 44 | 45 | ### Default Workflows 46 | 47 | These workflows are included and enabled by default. 48 | 49 |
50 | 🚀 Deploy to production 51 | 52 | ```md 53 | .github/workflows/deploy-production.yml 54 | ``` 55 | 56 | Automatically deploys to the `production` environment when a `pull_request` is `merged` to the `main` branch. This action can also be run manually from the "Actions" tab in GitHub. 57 | 58 | When a deploy to `production` is completed, the following occurs: 59 | 60 | - A new release is created with the current date and time including site's database and uploads attached as artifacts (GitHub release size restrictions apply). 61 | - A GitHub Deployment is created with a link to the environment. 62 | 63 |
64 | 65 |
66 | 🚀 Deploy to staging 67 | 68 | ```md 69 | .github/workflows/deploy-staging.yml 70 | ``` 71 | 72 | Automatically deploys to the `staging` environment when a `pull_request` is `merged` to the `staging` branch. This action can also be run manually from the "Actions" tab in GitHub. 73 | 74 | When a deploy to `staging` is completed, the following occurs: 75 | 76 | - A new tag is created with the current date and time. 77 | - A GitHub Deployment is created with a link to the environment. 78 | 79 |
80 | 81 |
82 | 🔄 Sync content production->staging 83 | 84 | ```md 85 | .github/workflows/sync-content-production-to-staging.yml 86 | ``` 87 | 88 | Copy the database and assets from the `production` environment to the `staging` environment overwriting the staging environment's database and assets. 89 | 90 |
91 | 92 |
93 | 🔑 Set Trellis deploy keys 94 | 95 | ```md 96 | .github/workflows/set-trellis-deploy-keys.yml 97 | ``` 98 | 99 | Updates the ssh keys used by Trellis to deploy to the `staging` or `production` environments. This action can be run manually from the "Actions" tab in GitHub. 100 | 101 | This action replaces the current deploy keys with keys with keys defined in one or more of the following locations: 102 | 103 | - `trellis/group_vars/all/users.yml` 104 | - GitHub secrets named `TRELLIS_DEPLOY_KEYS` 105 | - A new key entered manually when running the action 106 | 107 |
108 |
109 | 📝 Update README 110 | 111 | ```md 112 | .github/workflows/update-readme.yml 113 | ``` 114 | 115 | Updates the README.md with the current deployment status badges. This action can be run manually from the "Actions" tab in GitHub. 116 |
117 | 118 | ### Optional Workflows 119 | 120 | These example workflows are included to expand the functionality of this project. They are not enabled by default. 121 | 122 |
123 | 🧪 Dry-Run to Production 124 | 125 | ```md 126 | .github/examples/dryrun-production.yml 127 | ``` 128 | 129 | ⚠️ **NOTE:** This workflow must be moved to the `.github/workflows` directory to be used. 130 | 131 | Performs a "dry-run" deployment to the `production` environment, testing all aspects of a Trellis deployment without finalizing the deploy. Automatically deploys to the `staging` environment when a `pull_request` is `opened` to the `main` branch. This action can also be run manually from the "Actions" tab in GitHub. 132 |
133 |
134 | 🧪 Dry-Run to Staging 135 | 136 | ```md 137 | .github/examples/dryrun-staging.yml 138 | ``` 139 | 140 | ⚠️ **NOTE:** This workflow must be moved to the `.github/workflows` directory to be used. 141 | 142 | Performs a "dry-run" deployment to the `staging` environment, testing all aspects of a Trellis deployment without finalizing the deploy. Automatically deploys to the `staging` environment when a `pull_request` is `opened` to the `staging` branch. This action can also be run manually from the "Actions" tab in GitHub. 143 |
144 |
145 | 🌱 Sage 10 Build Test 146 | 147 | ```md 148 | .github/examples/sage10-build-test.yml 149 | ``` 150 | 151 | ⚠️ **NOTE:** This workflow must be moved to the `.github/workflows` directory to be used. 152 | 153 | ⚠️ **NOTE:** You must edit this workflow to update the `theme_slug` variable with your theme's slug. 154 | 155 | Builds the Sage 10 theme and runs the theme's tests. Automatically runs when a `push` is made to any branch. 156 |
157 |
158 | ⏏️ Eject Production 159 | 160 | ```md 161 | .github/examples/eject-production.yml 162 | ``` 163 | 164 | ⚠️ **NOTE:** This workflow must be moved to the `.github/workflows` directory to be used. 165 | 166 | Exports a WordPress site built with Trellis from the `production` environment for delivery to a traditional WordPress environment and attach artifacts to the workflow run for download. This action can be run manually from the "Actions" tab in GitHub. 167 |
168 | 169 | ### Dependabot 170 | 171 |
172 | 🤖 WordPress core, plugin, and theme updates 173 | 174 | Dependabot will check for updates to WordPress themes, plugins, and core as defined in `site/composer.json` on a weekly basis and propose updates as pull requests to the `staging` branch. You can change this behavior by editing the `.github/dependabot.yml` file. 175 |
176 | --------------------------------------------------------------------------------