Mutex
An advisory lock service for CI/CD workflows, implemented as a GitHub Action. It helps prevent race conditions and ensures that critical sections of your pipeline are executed by only one job at a time.
How it works
This action uses a PostgreSQL database to manage locks. When a workflow job needs to acquire a lock, it communicates with the lock service. If the lock is available, it's granted, and the job proceeds. If not, the job can wait or fail, depending on your workflow configuration.
Features
- Advisory Locking: Create and manage locks within your GitHub Actions workflows.
- Pull Request Integration: Lock and release events are posted as PR comments.
- Slack Notifications: Choose if you want to be notified in your Slack channels about locking events.
- Easy Disabling: Skip locking for specific pull requests by:
- adding a
SKIP_MUTEX
label - including
SKIP_MUTEX
in the PR's description or comment - or defining
SKIP_MUTEX=1
as an environment variable.
- adding a
Usage Example
Here is an example of how to use the mutex
action in a workflow:
- name: Acquire Lock
uses: releasetools/mutex@v1
permissions:
contents: read
pull-requests: write
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
with:
command: "lock"
id: "my-resource"
slack-channel: "#ci-cd"
Any other workflows or actions using a mutex
on the same lock id
, will not run until the lock is released.
Configuration
Prerequisites
- PostgreSQL database: This action requires access to a PostgreSQL database to store lock information. You can use any standard Postgres provider. If you need a free one for getting started, consider using Neon.
Environment Variables
The action supports the following environment variables (env:
).
DATABASE_URL
Connection string for a PostgreSQL database. The action will create a table named releasetools_mutex
if it doesn't exist. If the role specified in the connection string cannot create tables, ensure such a table exists. You can find the schema definition in database.ts.
GITHUB_TOKEN
The action needs access to the GitHub API. It can be passed via ${{ secrets.GITHUB_TOKEN }}
. The workflow needs additional permissions:
permissions:
contents: read
pull-requests: write
SLACK_BOT_TOKEN
The Slack Bot Token for sending notifications. It requires the chat:write
permission, and the associated bot must be invited to the specified slack-channel
, otherwise it will fail to post.
Action Inputs
The action can be configured using inputs (with:
).
command
Required. The command to execute (e.g., lock
or release
).
id
Required. A unique identifier for the lock.
reason
Optional reason for taking the lock. Useful to provide context regarding which service took the lock and why.
expiration
Lock expiration in seconds from current time. Defaults to 60 seconds in the future.
max-wait
Maximum time in seconds to wait to acquire the lock, before failing.
If not specified, it defaults to -1
which results in using the specified expiration
as a timeout for the current run.
poll-interval
Allows changing the polling interval. Useful for long-duration locks.
auto-release
Used to signal if a lock should be automatically released when the workflow job ends. Defaults to true
.
disable-pr-updates
By default, a comment will be posted on the Pull Request running the action, when locks are acquired or released.
Set it to true
to never post comments on PRs.
slack-channel
Required for Slack notifications. The Slack channel to post updates to (e.g., C12345678
).
The bot that owns the SLACK_BOT_TOKEN
should be a member of this channel.
See Slack API docs for channel ID formats.
Advanced Usage
Multiple Locks
You can use multiple locks in the same workflow:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Acquire Database Lock
uses: releasetools/mutex@v1
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
command: "lock"
id: "database-migration"
reason: "Running database migrations"
- name: Acquire Deployment Lock
uses: releasetools/mutex@v1
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
command: "lock"
id: "production-deployment"
reason: "Deploying to production"
# Your deployment steps here
- name: Release Deployment Lock
uses: releasetools/mutex@v1
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
command: "release"
id: "production-deployment"
- name: Release Database Lock
uses: releasetools/mutex@v1
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
command: "release"
id: "database-migration"
Conditional Locking
Skip locking based on conditions:
- name: Acquire Lock
if: ${{ !contains(github.event.pull_request.labels.*.name, 'SKIP_MUTEX') }}
uses: releasetools/mutex@v1
# ... configuration
Development
All contributions are welcome!
-
Clone the repository:
git clone https://github.com/releasetools/mutex.git
cd mutex -
Install dependencies and pre-commit hooks:
npm install
npm run prepare
The main entry point is main.ts
, which handles 'lock' or 'release' actions. A post-job script in post.ts
handles automatic lock release if enabled.
You can learn about creating GitHub actions in this tutorial.
Releasing
You can use the releasetools cli to create release tags.
Run this command to tag the HEAD commit and also update the v1
tag.
rt git::release --major --sign --force --push v1.0.2
Since mutex
is a Javascript-based action, no other step is needed to make a new release available.
Troubleshooting
Common Issues
Database Connection Errors
- Ensure your
DATABASE_URL
is correct and the database is accessible - Verify that the database user has sufficient permissions to create tables
- Check that the PostgreSQL server is running and accepting connections
Permission Errors
- Make sure your workflow has the required permissions:
permissions:
contents: read
pull-requests: write
Slack Integration Issues
- Verify that the
SLACK_BOT_TOKEN
haschat:write
permissions - Ensure the bot is added to the specified channel
- Check that the channel ID format is correct
Getting Help
If you encounter issues:
- Check the GitHub Issues for similar problems
- Review the action logs in your GitHub workflow
- Verify your configuration against the examples above
- Open a new issue with detailed information about your setup
License
Copyright © 2024 ReleaseTools
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.