Automating Shopify Theme Deployments with GitLab CI/CD
Introduction
Shopify development projects often involve multiple team members working on different features simultaneously. Efficient collaboration and continuous integration are crucial for delivering high-quality Shopify stores. To streamline this process, we've developed an automated deployment process that integrates Shopify themes with a GitLab repo by utilising the GitLab CI/CD.
This is done by defining pipelines in the .gitlab-ci.yml
file that executes a shell script which determines how to deploy the branch to a Shopify theme. This could be anything from a feature branch to a production deployment. In this blog post, we'll delve into how this automation works and how you can implement this on your or your clients Shopify stores.
All files referenced below can be found in this gist.
GitLab CI Script
For Shopify projects, the GitLab CI/CD file manages the deployment workflow for various branches, including feature branches, staging, UAT (User Acceptance Testing), and the main/production branch. Let's break down the key stages of the script:
1. Review Stage
review:
stage: review
script:
- echo "Contents of root directory before actions:"
- ls -la
- echo "Contents of shopify-deployment before actions:"
- ls -la shopify-deployment || true
- shopify version
- mkdir -p ./shopify-deployment
- chmod +x ./shopify-deployment-script.sh; ./shopify-deployment-script.sh
- echo "Contents of shopify-deployment after actions:"
- cat ./shopify-deployment/deployment-data
- DYNAMIC_ENVIRONMENT_URL=$(grep -o '"preview_url":"[^}]*' ./shopify-deployment/deployment-data | grep -Eo '(http|https)://[^"]+')
- echo "DYNAMIC_ENVIRONMENT_URL=$DYNAMIC_ENVIRONMENT_URL" >> deploy.env
artifacts:
reports:
dotenv: deploy.env
environment:
name: review/$CI_COMMIT_REF_NAME
url: $DYNAMIC_ENVIRONMENT_URL
on_stop: stop_review
cache:
<<: *global_cache
only:
- branches
except:
- staging
- uat
- main
The Review stage deploys feature branches to a Shopify development theme. It uses the shopify-deployment-script.sh
to deploy updates to a Shopify development theme if this branch hasn’t been deployed before, or updates an existing Shopify development theme if it has. This shell script is explained in detail in the Shopify Deployment Script section below.
The Shopify CLI is used to enable this automated deployment. This means that you will need to have the Shopify CLI installed on your GitLab Runner. It may be possible to install this directly but we are using a Docker container that has the Shopify CLI and all required packages pre-installed. This image is then saved to a GitLab Container Registry. The Dockerfile used to generate this image is available in this gist.
After the deployment occurs in the shopify-deployment-script.sh
file the review stage then saves the Shopify development theme URL in GitLab as the App URL. It does this by setting the DYNAMIC_ENVIRONMENT_URL
to the preview_url
returned by the Shopify CLI. A grep command is used to extract this preview_url
from the JSON data in the deployment-data
file. Finally, this DYNAMIC_ENVIRONMENT_URL
variable is written to the deploy.env
file.
All of this enables the deployed development theme to be opened directly within a GitLab merge request by clicking the ‘View app” button. Bear in mind that Shopify automatically removes development themes that have been inactive for seven days. Therefore if your review process takes longer than this you will need to rerun the pipeline after seven days to deploy this feature branch to a new Shopify development theme.
2. Staging and UAT Stages
staging:
stage: staging
script:
- echo "Contents of root directory before actions:"
- ls -la
- echo "Contents of shopify-deployment before actions:"
- ls -la shopify-deployment || true
- shopify version
- mkdir -p ./shopify-deployment
- echo "Deploying updates to Shopify Staging theme:"
- shopify theme push --theme $STAGING_THEME_ID --store $SHOPIFY_FLAG_STORE --password $SHOPIFY_CLI_THEME_TOKEN --json > ./shopify-deployment/deployment-data
- echo "Contents of shopify-deployment after actions:"
- cat ./shopify-deployment/deployment-data
- DYNAMIC_ENVIRONMENT_URL=$(grep -o '"preview_url":"[^}]*' ./shopify-deployment/deployment-data | grep -Eo '(http|https)://[^"]+')
- echo "DYNAMIC_ENVIRONMENT_URL=$DYNAMIC_ENVIRONMENT_URL" >> deploy.env
artifacts:
reports:
dotenv: deploy.env
environment:
name: review/$CI_COMMIT_REF_NAME
url: $DYNAMIC_ENVIRONMENT_URL
cache:
<<: *global_cache
only:
- staging
uat:
stage: uat
script:
- echo "Contents of root directory before actions:"
- ls -la
- echo "Contents of shopify-deployment before actions:"
- ls -la shopify-deployment || true
- shopify version
- mkdir -p ./shopify-deployment
- echo "Deploying updates to Shopify UAT theme:"
- shopify theme push --theme $UAT_THEME_ID --store $SHOPIFY_FLAG_STORE --password $SHOPIFY_CLI_THEME_TOKEN --json > ./shopify-deployment/deployment-data
- echo "Contents of shopify-deployment after actions:"
- cat ./shopify-deployment/deployment-data
- DYNAMIC_ENVIRONMENT_URL=$(grep -o '"preview_url":"[^}]*' ./shopify-deployment/deployment-data | grep -Eo '(http|https)://[^"]+')
- echo "DYNAMIC_ENVIRONMENT_URL=$DYNAMIC_ENVIRONMENT_URL" >> deploy.env
artifacts:
reports:
dotenv: deploy.env
environment:
name: review/$CI_COMMIT_REF_NAME
url: $DYNAMIC_ENVIRONMENT_URL
cache:
<<: *global_cache
only:
- uat
Similar to the Review stage, these stages deploy updates to Shopify staging and UAT themes, respectively. As with the Review stage it does this by using the Shopify CLI. Unlike the Review stage, this doesn’t require using the shopify-deployment-script.sh
script though as the staging and UAT Shopify theme are created as permanent themes in the Shopify theme library. This has been implemented this way so that the Staging and UAT themes will not be automatically removed by Shopify after seven days.
The Staging and UAT themes need to be created in Shopify by duplicating the main theme and renaming it. Thereby adding these duplicate themes to the theme library. Then the theme IDs for both of these themes need to be added to the GitLab repo under Settings → CI/CD → Variables. In the example .gitlab-ci.yml
file these variables are STAGING_THEME_ID
and UAT_THEME_ID
. These steps only have to be completed once when first adding this functionality to the project.
3. Generate Version Stage
generate_version:
stage: prep_deploy
image: alpine:latest
rules:
- if: $CI_COMMIT_TAG
when: never
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
script:
- echo "RELEASE_TAG = $(date '+%Y%m%d.%H%M')" >> variables.env
artifacts:
reports:
dotenv: variables.env
expire_in: 1 week
This stage is the first stage in a production deployment. It generates a version tag for deployments of the default branch (e.g. main) if a commit has not already been tagged. This allows for improved version tracking for the live theme.
4. Deploy Stage
deploy:
stage: deploy
script:
- echo "Contents of root directory before actions:"
- ls -la
- echo "Contents of shopify-deployment before actions:"
- ls -la shopify-deployment || true
- shopify version
- mkdir -p ./shopify-deployment
- shopify theme push --store $SHOPIFY_FLAG_STORE --password $SHOPIFY_CLI_THEME_TOKEN --live --allow-live --json > ./shopify-deployment/deployment-data
- echo "Contents of shopify-deployment after actions:"
- cat ./shopify-deployment/deployment-data
- ID=$(grep -o '"id":[^"]*' ./shopify-deployment/deployment-data | grep -o '[^:]*$' | grep -o '^[^,]*')
- PREVIEW_URL=$(grep -o '"preview_url":"[^}]*' ./shopify-deployment/deployment-data | grep -Eo '(http|https)://[^"]+')
- DYNAMIC_ENVIRONMENT_URL=${PREVIEW_URL}?preview_theme_id=${ID}
- echo "DYNAMIC_ENVIRONMENT_URL=$DYNAMIC_ENVIRONMENT_URL" >> deploy.env
artifacts:
reports:
dotenv: deploy.env
environment:
name: live
url: $DYNAMIC_ENVIRONMENT_URL
cache:
<<: *global_cache
only:
- main
The Deploy stage is the second stage in production deployment and handles the actual deployment of the live theme to Shopify. As with the Review, Staging and UAT stages, it pushes updates to the theme using the Shopify CLI.
It is very important to note that any changes made on the live store through the Shopify theme editor or code editor that are not contained in the code base may be overwritten by this deployment stage. It is recommended to always manually run the shopify theme pull
command to pull and commit the latest changes from the live store before merging and pushing your changes to the default (e.g. main) branch.
5. Release Stage
release:
stage: finalise_deploy
image: registry.gitlab.com/gitlab-org/release-cli:latest
rules:
- if: $CI_COMMIT_TAG
when: never
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
script:
- echo "Running release $RELEASE_TAG..."
release:
name: 'Release $RELEASE_TAG'
tag_name: '$RELEASE_TAG'
description: '$RELEASE_TAG'
ref: '$CI_COMMIT_SHA'
artifacts:
expire_in: 1 week
This stage is the third stage in production deployment and is triggered when a new tag is created. It runs the release CLI to finalise the deployment and create a release tag.
6. Stop Review Stage
stop_review:
stage: cleanup
script:
- ./teardown-environment
- dep ci:destroy cd --revision=$CI_COMMIT_SHA
when: manual
environment:
name: review/$CI_COMMIT_REF_NAME
action: stop
only:
- branches
except:
- main
- uat
- staging
This manual stage is used to tear down Review environments for feature branches.
Shopify Deployment Script
# IMPORTANT: Deployment results files (e.g. deployment-data) cannot use the file type '.json' as Shopify will interpret these as being theme files and the theme deployment will fail
# When the deployment-data file already exisits (loaded from the cache) then try to redeploy to this theme
if [ -f ./shopify-deployment/deployment-data ]
then
# Get the theme id from the deployment-data json file
ID="$(grep -o '"id":[^"]*' ./shopify-deployment/deployment-data | grep -o '[^:]*$' | grep -o '^[^,]*')"
echo "Deploying updates to Shopify development theme with id: $ID"
echo "shopify theme push --theme $ID --store $SHOPIFY_FLAG_STORE --password $SHOPIFY_CLI_THEME_TOKEN > ./shopify-deployment/result"
shopify theme push --theme $ID --store $SHOPIFY_FLAG_STORE --password $SHOPIFY_CLI_THEME_TOKEN > ./shopify-deployment/result
# If the redeployment output includes the word 'Error' then deploy a development new theme
ERROR="$(grep -w "Error" ./shopify-deployment/result)"
if [ ! -z "$ERROR" ]
then
echo "WARNING: There was an error with the deployemnt. This is probably due to this Shopify development theme being inactive for more than 7 days. The theme will now be redeployed. This will result in the Shopify development theme having a new id and url."
echo "shopify theme push --development --store $SHOPIFY_FLAG_STORE --password $SHOPIFY_CLI_THEME_TOKEN --json > ./shopify-deployment/deployment-data"
shopify theme push --development --store $SHOPIFY_FLAG_STORE --password $SHOPIFY_CLI_THEME_TOKEN --json > ./shopify-deployment/deployment-data
fi
rm -f ./shopify-deployment/result
# When the deployment-data file doesn't exisit deploy a new development theme
else
echo "Deploying a new Shopify development theme:"
echo "shopify theme push --development --store $SHOPIFY_FLAG_STORE --password $SHOPIFY_CLI_THEME_TOKEN --json > ./shopify-deployment/deployment-data"
shopify theme push --development --store $SHOPIFY_FLAG_STORE --password $SHOPIFY_CLI_THEME_TOKEN --json > ./shopify-deployment/deployment-data
fi
The Shopify deployment shell script (shopify-deployment-script.sh
) complements the .gitlab-ci.yml
file by handling the deployment specifics for feature branches. This script is used by the Review stage and is required as there is more complex logic required for a feature branch deployment than a staging, UAT or live deployment. This more complex logic is slightly easier to write and understand in a shell file than if written directly in the .gitlab-ci.yml
file. Here's a summary of the functionality of this shell script:
If the
deployment-data
file exists, it attempts to redeploy to the existing Shopify development theme using the stored theme ID.If the redeployment fails (e.g. due to the theme having been inactive for over 7 days) or if the
deployment-data
file doesn’t exist, it deploys a new development theme and updates thedeployment-data
file with the theme ID.
As the Shopify CLI returns fairly verbose command line messages, checking if a deployment has failed has been implemented in a fairly unusual way. The deployment response is redirected to a result
file. A grep command is then used to determine if the result
file contains the word "Error". If it does then the script triggers the development of a new theme. This will result in the Shopify development theme having a new theme ID and URL. As mentioned in 1. Review Stage this new URL will be set as the DYNAMIC_ENVIRONMENT_URL
. This means that the ‘View app’ link in the MR is automatically updated to this new development theme URL.
Something you may have noticed when looking at this shell script and the .gitlab-ci.yml
file is that the JSON output of the shopify theme push
commands is not redirected into a file with a file extension of .json
. This is because the Shopify CLI will interpret these as theme files and attempt to deploy these files. As they will contain data that doesn't match the expected format of any Shopify .json
file the theme deployment will fail. To get around this issue these files have not been given any file extension.
Setup of GitLab CI/CD Variables
For the shopify-deployment-script.sh
and .gitlab-ci.yml
functionality to work the following variables need to be added to the GitLab repo under Settings → CI/CD → Variables.
Shopify Store Name
The store name variable is required to tell the Shopify CLI instance that is running in your GitLab Runner which Shopify store it is communicating with. This variable can have any key, but for the example files in this gist use SHOPIFY_FLAG_STORE
.
Theme Access Password
The theme Access password is needed to authorise the Shopify CLI instance that is running on your GitLab Runner to communicate with your store. This is created by:
Adding the Theme Access app to the Shopify store.
Create a password in the Theme Access app.
Adding this theme access password as a GitLab variable. In our example files we have defined the key for this variable as
SHOPIFY_CLI_THEME_TOKEN
.
For security, I recommend masking this variable so that it is not displayed in your pipeline job logs.
Staging and UAT Theme IDs
If your development process requires you to have a Staging and/or UAT store then these themes can be created and deployed by following the steps below.
Go to the admin for your Shopify store. This is at the URL for your Shopify store with
/admin
added at the end.Within the 'Online Store' section, on the live theme click on the ellipses (3 dots) then click 'Duplicate'.
Once this theme has been duplicated click on the ellipses again and rename the theme to either Staging or UAT.
Get the theme IDs for these new themes. This can be done either:
Via the Shopify CLI with the command
shopify theme list
Or by clicking 'Customize' and copying the ID from the URL (It should be a 12-digit number).
In GitLab add a variable with the value set as the theme ID.
In the example files the staging theme ID has the key
STAGING_THEME_ID
and the UAT theme ID has the keyUAT_THEME_ID
Repeat steps 1-3 if a Staging and UAT theme are required.
Summary
Summary
Has this post struck a chord with you? Have you been struggling with manual Shopify theme deployments? We can help! Contact us for a free consultation to discuss how to automate your workflows and streamline your development processes.