This tool is a utility for managing releases in Contentful. It streamlines the process of branching, releasing, and integrating with CI/CD pipelines such as GitLab or GitHub, by leveraging the use of Environments and Environment aliases.
โจ Features ยท ๐ก Installation ยท ๐ Example ยท ๐น Usage ยท ๐ Release ยท ๐ ToDo ยท ๐พ Contributors ยท ๐ฉ Acknowledgments ยท ๐ Collection ยท ๐ License
To use this helper library, you must have Node.js ๐ and npm ๐ installed.
To install it, simply run:
npm install contentful-cli-release --save
Or, if using yarn ๐:
yarn add contentful-cli-release
Similarly, if you are using Bun ๐, just run:
bun add contentful-cli-release
node
>= 18.20.0npm
>= 10.5.0contentful-management
>= 11.31.7To get the most out of the Contentful CLI Release tool, proper setup is crucial. Here's a step-by-step guide to help you get started:
Environment Variables:
The tool uses environment variables to simplify repetitive tasks and ensure security. Instead of passing sensitive
data as command line arguments every time, you can set them once in your environment. We recommend using a .env
file for this purpose. Here's a sample configuration:
CMS_MANAGEMENT_TOKEN=placeholder-management-token
CMS_SPACE_ID=placeholder-space-id
CMS_RELEASE_MAX_SCHEDULED_ACTIONS=500
CMS_RELEASE_ENVIRONMENT_PROTECTED=dev,staging,master
CMS_RELEASE_ENVIRONMENT_REGEX=release-[0-9]+[\\.]*[0-9]*[\\.]*[0-9]*
Create the .env
or .env.local
files in your project to override the default configuration. But ensure to
replace the placeholders (e.g., placeholder-management-token
and placeholder-space-id
) with your actual data.
Max Scheduled Actions:
The CMS_RELEASE_MAX_SCHEDULED_ACTIONS
parameter sets the number of scheduled actions (in total) to be retrieved.
Default and maximum value is 500
.
Protected Environments:
The CMS_RELEASE_ENVIRONMENT_PROTECTED
parameter lists environments that should not be modified by the tool,
ensuring safety for crucial stages like dev
, staging
, and master
.
Environment Naming Convention:
Using the CMS_RELEASE_ENVIRONMENT_REGEX
, you can specify a regex pattern to match the naming convention of your
release environments. The default pattern matches names like release-1
, release-1.1
, and release-1.1.1
.
With these steps, you should have a fully configured environment ready to utilize the Contentful CLI Release tool effectively. Always refer back to the tool's documentation if you need further assistance.
Duplicates an existing environment to a new environment, and it pings to see when the new environment is available. It skips the duplication if the destination environment already exists.
Usage:
npx contentful-cli-release --duplicate --from SOURCE_ENV --to DEST_ENV (--update-api-key)
Arguments:
--from
: The name of the source environment to duplicate from. Ie: 'master'.--to
: The name of the destination environment to duplicate to. Ie: 'release-1.7.4' or 'staging'.--update-api-key
: It will enable, for the duplicated environment, the CDA API Key that has the same name of theSee the section ๐น Usage for details on the command line options.
$ npx contentful-cli-release --duplicate --from master --to release-1.7.4 --update-api-key
##/INFO: Duplicating environment 'master' for space 'xxxxxxxxx'
##/DEBUG: Creating new environment: 'release-1.7.4'
##/INFO: Environment 'release-1.7.4' successfully created
##/INFO: CDA 'master' Key assigned to environment: release-1.7.4
##/DEBUG: Waiting to retrieve the newly created environment: release-1.7.4
##/DEBUG: Waiting to retrieve the newly created environment: release-1.7.4
##/DEBUG: Waiting to retrieve the newly created environment: release-1.7.4
...
##/INFO: release-1.7.4 successfully duplicated from: master
$ npx contentful-cli-release --duplicate --from master --to release-1.7.4 --update-api-key
##/INFO: Duplicating environment 'master' for space 'xxxxxxxxx'
##/INFO: An environment with this name already exists: 'release-1.7.4'. Skipping creation
##/INFO: CDA 'master' Key assigned to environment: release-1.7.4
##/DEBUG: Waiting to retrieve the newly created environment: release-1.7.4
##/DEBUG: Waiting to retrieve the newly created environment: release-1.7.4
##/DEBUG: Waiting to retrieve the newly created environment: release-1.7.4
...
##/INFO: release-1.7.4 successfully duplicated from: master
$ npx contentful-cli-release --duplicate --from myenvironment --to release-1.7.4
@@/ERROR: The source environment does not exist!
$ npx contentful-cli-release --duplicate --from master
@@/ERROR: You should specify both a '--from' and a '--to' option.
Synchronize scheduled actions between two Environments, because the actions are not copied when duplicating an Environment. Ideally the source is the 'old' master and the destination is the newly created release environment.
Usage:
npx contentful-cli-release --sync-schedule --from SOURCE_ENV --to DEST_ENV (--force-yes)
Arguments:
--from
: The name of the source environment where the existing scheduled actions are. Ie: 'master'.--to
: The name of the destination environment to copy the scheduled actions to. Ie: 'release-1.4.5'.--force-yes
: When the destination environment is protected, this will allow to perform the action.See the section ๐น Usage for details on the command line options.
$ npx contentful-cli-release --sync-schedule --from master --to release-1.4.5 --force-yes
##/INFO: Source Environment: 'master'
##/INFO: Destination Environment: 'release-1.4.5'
##/INFO: Total Scheduled Actions: 2
##/DEBUG: Imported scheduled action: Publish for Entry-Id: '5krek3qkuRtWxRyIqM012a' for the: 2023-10-29 19:00
##/DEBUG: Imported scheduled action: Unpublish for Entry-Id: 'GKfodiofTQFS8oXjJp65Yb' for the: 2023-10-29 20:00
$ npx contentful-cli-release --sync-schedule --from master --to release-1.4.5 --force-yes
##/INFO: Source Environment: 'master'
##/INFO: Destination Environment: 'release-1.4.5'
##/INFO: Total Scheduled Actions: 2
##/DEBUG: Scheduled action already exists - Action-Id: 6jTzhTAOPs5LsbpjRZKkF3
##/DEBUG: Scheduled action already exists - Action-Id: 2HVGh3wJRSp8P6ZW18YI92
$ npx contentful-cli-release --sync-schedule --from master --to staging
@@/ERROR: The destination environment is either empty or reserved!
$ npx contentful-cli-release --sync-schedule --from master
@@/ERROR: You should specify both a '--from' and a '--to' option.
It links an existing alias from one Environment to another one. This is used during a Release to move, for example, the 'master' alias from the old release branch to the new one
Usage:
npx contentful-cli-release --link --alias ALIAS --to TARGET_ENV (--prune-old-releases)
Arguments:
--alias
: The existing alias that needs to be updated. Ie: 'master'.--to
: The target Environment-id to which the alias will point to. Ie: 'release-1.4.5'.--prune-old-releases
: Using the release regular expression, it will delete all the older releases, exceptSee the section ๐น Usage for details on the command line options.
$ npx contentful-cli-release --link --alias master --to release-1.4.5 --prune-old-releases
##/INFO: Linking Environment 'release-1.4.5' to Alias 'master'
##/INFO: Alias 'master' updated to 'release-1.4.5' Environment.
##INFO: Deleting old Release Environments
##/INFO: Processing the list of all environments
##/INFO: This environment will NOT be deleted: dev
##/INFO: This environment will NOT be deleted: staging
##/INFO: This environment will NOT be deleted: release-1.4.5 aliased by master
##/INFO: List of Release environments that will be kept:
- release-1.4.5
- release-1.4.4
##/INFO: List of Release environments that will be deleted:
- release-1.4.3
##/DEBUG: Environment 'release-1.4.3' is going to be deleted!
##/INFO: Deleting environment 'release-1.4.3'.
##/DEBUG: Environment 'release-1.4.3' was deleted.
$ npx contentful-cli-release --link --alias master --to release-1.4.5
##/INFO: Linking Environment 'release-1.4.5' to Alias 'master'
##/INFO: Alias 'master' updated to 'release-1.4.5' Environment.
$ npx contentful-cli-release --link --to release-1.4.5
@@/ERROR: You should specify an '--alias' option when using '--link'
$ npx contentful-cli-release --link --alias master
@@/ERROR: You should specify an '--environment-id' option when using '--delete' or '--link'
This function allows to delete an Environment via the CLI tool. It automatically forbids to delete the configured protected environments, unless we use the option '--force-yes'.
Usage:
npx contentful-cli-release --delete --environment-id TARGET_ENV (--force-yes)
Arguments:
--environment-id
: The name of the environment to be deleted.--force-yes
: When the destination environment is protected, this will allow to perform the action.See the section ๐น Usage for details on the command line options.
$ npx contentful-cli-release --delete --environment-id test
##/INFO: Deleting environment 'test'.
##/DEBUG: Environment 'test' was deleted.
$ npx contentful-cli-release --delete --environment-id staging
@@/ERROR: Environment 'staging' is protected and cannot be deleted.
@@/ERROR: No action chosen or Returned an error. Inspect the logs and try again
$ npx contentful-cli-release --delete --environment-id staging --force-yes
##/INFO: Deleting environment 'staging'.
##/DEBUG: Environment 'staging' was deleted.
$ npx contentful-cli-release --delete
@@/ERROR: You should specify an '--environment-id' option when using '--delete' or '--link'
This script can be used from the command line and accepts various arguments for customization:
--space-id
: The Contentful space id. It will override the env value CMS_SPACE_ID
.--management-token
or --mt
: The Contentful Management Token. It will override the env value CMS_MANAGEMENT_TOKEN
.--from
: The source Environment-id when performing a duplication or a sync-schedule.--alias
: Mandatory only for the link alias option. It represents the alias that we want to associate with another--to
or --environment-id
: The target Environment-id.--update-api-key
: When performing a duplication. It enables a CDA API Key also for the new environment. The only--prune-old-releases
: When running a --link
alias operation, it will keep the latest two releases and delete the--force-yes
: It forces the operation when the target Environment-id is considered protected. The protectedCMS_RELEASE_ENVIRONMENT_PROTECTED
or overridden with the following option.--protected-environments
: It sets a list (separated by comma) of protected environments. It overrides the envCMS_RELEASE_ENVIRONMENT_PROTECTED
. This prevents accidentally deleting or performing operations on an--release-regex
: It overrides the env value CMS_RELEASE_ENVIRONMENT_REGEX
. It identifies a regular expressionrelease-
and then the numberingx.y.z
. The regular expression can be modified, however the script will use the numbering part--prune-old-releases
option.--max-scheduled-actions
: It overrides the env value CMS_RELEASE_MAX_SCHEDULED_ACTIONS
and it represents theIt has been said that Contentful Environments are equivalent to GIT Branches. And meanwhile this is practically true from the user point of view, it does not transform automatically to a simple release strategy. In addition, although there are different functionalities that helps with releasing content (Contentful Workflow and Launch work very good for that) or keeping two Environments in sync (this is what the new Contentful Merge does), for many developers is missing the possibility to easily integrate Contentful into their release strategy.
This is what this tool is for, together with our two other scripts:
The idea behind is to use these tools via command line (without having to install them) to integrate them inside a
CI/CD of your liking. By using one .env
file, that can be injected in your CI/CD, or by passing the right parameters
via command line options, these tools are self-sufficient in running even the more complex tasks during a release.
In particular, actions like duplicating an Environment, or changing the Environment alias to that new duplicate, are
all things that can be done seamlessly with one centralized logic. We will run through few examples and to show you
how efficient your release strategy can be.
We will take into consideration the most common release strategies and scenarios, but obviously you will want (or need) to tailor the actions to your specific use-case. Feel free to reach out and suggest some we haven't thought about, and we will promptly add them to this list.
For most of the Release processes presented, it's good to always use Environment aliases, and one or two Environments
using a release naming strategy (in our examples release-x.y.z
). The reason behind this kind of choice is to
be able to connect a 'code' release to a specific Environment, by using Semantic Versioning
for both.
A common way of releasing is to promote a staging Environment to production, by duplicating staging into the new
master. Let's see how we could use contentful-cli-release
and the other tools to do a release, starting from these
environments:
- Environment 'master'
- Environment 'master-backup'
- Environment 'staging'
- Environment 'dev'
What we wil simply do is to back up the involved Environments first, and then delete the oldest one to make room for
the staging
that is going to be promoted (by being duplicated) to the new master
.
$ npx contentful-cli-export --from "staging" --compress
$ npx contentful-cli-export --from "master" --compress
$ npx contentful-cli-export --from "master-backup" --compress
$ npx contentful-cli-release --delete --environment-id "master-backup"
$ npx contentful-cli-release --duplicate --from "master" --to "master-backup"
$ npx contentful-cli-release --delete --environment-id "master" --force-yes
$ npx contentful-cli-release --duplicate --from "staging" --to "master"
This release process is pretty straightforward and easy to implement, however it doesn't take into account few things:
To notice that running the
contentful-cli-export
command inside the CI/CD will generate an artifact. This artifact can be saved for a specified amount of time, so that a backup of the Contentful Environment is always present in case of a rollback operation.
As many developers that start with a free tier of Contentful, it is possible to use the Releases process also with
'only' 3 Environments. In this case we need to use 2 real Environments (dev
and staging
) and a release Environment
that is aliased by master
.
Let's assume this is the starting scenario:
- Environment `release-1.4.5` aliased by 'master'
- Environment 'staging'
- Environment 'dev'
In this case, the release process is already more structured and reliable. It diminishes downtime, and it allows
to bring the new release Environment at the same state of content, content-types and scheduled actions as the 'old'
master, ensuring high reliability of operations. The usage of the --link
alias function also help reduce downtime.
$ npx contentful-cli-export --from "staging" --compress
$ npx contentful-cli-export --from "master" --compress
$ npx contentful-cli-release --delete --environment-id "staging" --force-yes
$ npx contentful-cli-release --duplicate --from "master" --to "release-1.4.6" --update-api-key
$ npx contentful-cli-release --sync-schedule --from "master" --to "release-1.4.6"
$ npx contentful-cli-migrations --to "release-1.4.6" --force-yes
$ npx contentful-cli-release --link --alias "master" --to "release-1.4.6"
$ npx contentful-cli-release --delete --environment-id "release-1.4.5"
$ npx contentful-cli-release --duplicate --from "master" --to "staging"
Let's see in details:
staging
(downloadable as an artifact in the CI/CD).master
(downloadable as an artifact in the CI/CD).--delete
option of the script. We pass --force-yes
since by default 'staging' is a--update-api-key
to ensure that the new release Environment will becontentful-cli-migrations
from our--force-yes
option will automatically apply all the missing migrations to the new--get-master-release
to obtain the environment linked by master,--update-api-key
option, because it should existThis is somehow the most complex, but also the most reliable way of performing a release.
The starting scenario is something like this
- Environment 'release-1.4.5' aliased by 'master'
- Environment 'release-1.4.4' for rollbacks
- Environment 'staging'
- Environment 'dev'
As we can notice, it does not seem that much different from the previous one, but it is for the following reasons:
release-1.4.4
is the previous release environment and is kept (unlinked by aliases) for two main reasons. One isstaging
is detached from the master
release. This ensures that the development and testing flow can proceedstaging
itself could be anotherIn addition, during a release of this type is probably beneficial to split all the operations on the new release
Environment from the --link --alias
command, since the 'code' release itself has to probably take place in between,
to guarantee practically no downtime.
Before the code release
$ npx contentful-cli-export --from "master" --compress
$ npx contentful-cli-release --duplicate --from "master" --to "release-1.4.6" --update-api-key
$ npx contentful-cli-migrations --to "release-1.4.6" --force-yes
After this stage the Environments will look like:
- Environment 'release-1.4.6'
- Environment 'release-1.4.5' aliased by 'master'
- Environment 'release-1.4.4'
- Environment 'staging'
- Environment 'dev'
After the code release
$ npx contentful-cli-release --link --alias "master" --to "release-1.4.6" --prune-old-releases
$ npx contentful-cli-release --sync-schedule --from "release-1.4.5" --to "master" --force-yes
The linking master to the new release will also delete old releases than the latest two, making the final Environments look like:
- Environment 'release-1.4.6' aliased by 'master'
- Environment 'release-1.4.5' for rollbacks
- Environment 'staging'
- Environment 'dev'
You can also have a look at the official Contentful article Deploying changes with environment aliases
The following are examples of how you could integrate what we said in your GitLab or GitHub CI/CD pipeline. The examples are not fully working without the context of your application and set-ups, but are a good start.
To make this work you will need at least two thins:
.env.local
file has been set up for the Contentful scripts. This can be done by using deployment.node:
image: node:18.15.0-bullseye #This is a Docker Image from Docker Hub
contentful-pre-release:
extends:
- .node
script:
- LATEST_RELEASE=$(git describe --abbrev=0 --tags)
- npm install
- npx contentful-cli-export --from "master" --compress
- npx contentful-cli-release --duplicate --from "master" --to "release-$LATEST_RELEASE" --update-api-key
- sleep 10
- npx contentful-cli-migrations --to "release-$LATEST_RELEASE" --force-yes
contentful-post-release:
extends:
- .node
script:
- LATEST_RELEASE=$(git describe --abbrev=0 --tags)
- PREVIOUS_RELEASE=$(git tag --sort=-committerdate -l|tail -n +2|head -1)
- npm install
- npx contentful-cli-release --link --alias "master" --to "release-$LATEST_RELEASE" --prune-old-releases
- npx contentful-cli-release --sync-schedule --from "release-$PREVIOUS_RELEASE" --to "master" --force-yes
Note:
release-[0-9]*
and use Linux timestamp as unique--get-master-release
to get the current Environment to whichcross-env
: https://www.npmjs.com/package/cross-env
Add to your package.json:
"scripts": {
"build": "next build && npm run contentful-release",
"contentful-release": "cross-env DATE=\"$(date +%s)\" OLD_RELEASE=\"$(npx contentful-cli-release --get-master-release)\" npm run contentful-deploy-commands",
"contentful-deploy-commands": "cross-env-shell \"npm run contentful-delete-staging && npm run contentful-duplicate-master && npm run contentful-sync-schedule && npm run contentful-run-migrations && npm run contentful-link-alias && npm run contentful-delete-old-release && npm run contentful-duplicate-staging\"",
"contentful-delete-staging": "npx contentful-cli-release --delete --environment-id staging --force-yes",
"contentful-duplicate-master": "cross-env-shell \"npx contentful-cli-release --duplicate --from master --to release-$DATE --update-api-key\"",
"contentful-sync-schedule": "cross-env-shell \"npx contentful-cli-release --sync-schedule --from master --to release-$DATE\"",
"contentful-run-migrations": "cross-env-shell \"npx contentful-cli-migrations --to release-$DATE --force-yes\"",
"contentful-link-alias": "cross-env-shell \"npx contentful-cli-release --link --alias master --to release-$DATE\"",
"contentful-delete-old-release": "cross-env-shell \"npx contentful-cli-release --delete --environment-id $OLD_RELEASE\"",
"contentful-duplicate-staging": "npx contentful-cli-release --duplicate --from master --to staging",
},
This is still experimental, but your .github/workflows/contentful.yml
file will look something like:
name: Node.js CI
on:
push:
branches: ['main']
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.15.0]
steps:
- uses: actions/checkout@v3
with:
fetch-depth: '1'
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm install
- run: npx contentful-cli-export --from "master" --compress
- run: LATEST_RELEASE=$(git describe --abbrev=0 --tags) && npx contentful-cli-release --duplicate --from "master" --to "release-$LATEST_RELEASE" --update-api-key
- run: LATEST_RELEASE=$(git describe --abbrev=0 --tags) && npx contentful-cli-migrations --to "release-$LATEST_RELEASE" --force-yes
- run: LATEST_RELEASE=$(git describe --abbrev=0 --tags) && npx contentful-cli-release --link --alias "master" --to "release-$LATEST_RELEASE" --prune-old-releases
- run: PREVIOUS_RELEASE=$(git tag --sort=-committerdate -l|tail -n +2|head -1) && npx contentful-cli-release --sync-schedule --from "release-$PREVIOUS_RELEASE" --to "master" --force-yes
--sync-entries
option to sync entries between two release Environments.--help
command to describe the available command line options.Feel free to open issues or pull requests in our GitHub Repository if you have suggestions or improvements to propose.
I would like to express my gratitude to the following parties:
Thank you to everyone involved!
We produce a bunch of interesting packages for Contentful. You might want to check them out:
This project is licensed under the MIT License