A Ruby gem that provides Rake tasks for managing code deployment workflows with automated versioning, changelog management, and Slack notifications.
Add this line to your application's Gemfile:
gem "discharger"And then execute:
$ bundle installThen run the install generator:
$ rails generate discharger:installOr install it yourself as:
$ gem install dischargerAdd require "discharger/task" to your Rakefile, then configure the discharger task:
require "discharger/task"
Discharger::Task.create do |task|
# Version management
task.version_file = "config/application.rb"
task.version_constant = "MyApp::VERSION"
# Slack integration
task.release_message_channel = "#some-slack-channel"
task.app_name = "My App Name"
# Git integration
task.commit_identifier = -> { `git rev-parse HEAD`.strip }
task.pull_request_url = "https://github.com/SOFware/some-app"
# Changelog management (optional)
task.fragment = "changelog.d" # Directory for changelog fragments
endDischarger supports changelog management through the fragment setting from Reissue.
Document changes directly in your commit messages using git trailers. Set task.fragment = :git to enable this feature:
Discharger::Task.create do |task|
# ... other configuration ...
task.fragment = :git # Enable git commit trailers
endThis keeps changelog data coupled with your code changes in the same commit. See Git Trailers Guide for detailed usage instructions.
Alternatively, you can use file-based fragments by creating individual changelog files in a directory:
Discharger::Task.create do |task|
# ... other configuration ...
task.fragment = "changelog.d" # Enable file-based fragments
endWith this approach, create individual changelog files in the changelog.d/ directory:
changelog.d/
├── 123-fix-login-bug.md
├── 124-add-user-profile.md
└── 125-update-dependencies.md
Each fragment file should contain the changelog entry for a specific change or feature.
Some releases need manual follow-up: run a rake task, backfill data, re-index documents.
Reissue collects those steps from Runbook: commit trailers, and Discharger announces
them to your team in the release thread.
Discharger::Task.create do |task|
# ... other configuration ...
task.runbook_file = "RUNBOOK.md" # Default: nil (disabled)
endAdd steps as you work, in the same commit as the change that requires them:
git commit -m "Add data migration
Fixed: Duplicate user records
Runbook: Run \`rake data:cleanup\` after deploy"rake release:prepare finalizes the runbook alongside the changelog, so the release tag
records exactly which steps that version needs. Discharger posts the checklist to Slack as
a reply in the deploy thread — both on the stage build (rake release:build /
rake release:stage, threaded under "Building…") and after the production release
(threaded under the release announcement, right after the changelog):
*Post-release runbook for 1.2.3* — 2 steps
• Run `rake data:cleanup` (abc1234)
• Re-index search documents (def5678)
Staging shows the steps as a preview so the team sees what production will need before it happens; the production thread repeats them when the release actually lands.
When a release needs no follow-up, the thread says so explicitly rather than staying silent, so nobody has to wonder whether the check was skipped:
*Post-release runbook for 1.2.3*
No runbook tasks for this release.
Leave runbook_file unset and Discharger posts nothing extra. See the
Reissue runbook documentation
for how items are collected and merged.
The gem creates several Rake tasks for managing your deployment workflow:
$ rake -T release
rake release # ---------- STEP 3 ----------
rake release:build # Release the current version to stage
rake release:config # Echo the configuration settings
rake release:prepare # ---------- STEP 1 ----------
rake release:slack[text,channel,emoji] # Send a message to Slack
rake release:stage # ---------- STEP 2 ----------- Prepare (
rake release:prepare): Create a new branch to prepare the release, update the changelog, and bump the version - Stage (
rake release:stage): Update the staging branch and create a PR to production - Release (
rake release): Release the current version to production by tagging and pushing to the production branch
To release a specific working branch to stage instead of the default branch, use the DISCHARGER_BUILD_BRANCH environment variable:
DISCHARGER_BUILD_BRANCH=your-feature-branch rake buildThis will deploy your working branch to the staging environment.
You can configure the branch names in your Rakefile when setting up the discharger task:
require "discharger"
Discharger::Task.new do |task|
task.app_name = "MyApp"
task.working_branch = ENV.fetch("WORKING_BRANCH", "develop")
task.staging_branch = ENV.fetch("STAGING_BRANCH", "stage")
task.production_branch = ENV.fetch("PRODUCTION_BRANCH", "main")
# ... other configuration
endThis allows you to use environment variables to override the default branch names, or set project-specific defaults. The DISCHARGER_BUILD_BRANCH environment variable (shown above) provides a runtime override specifically for the build task.
Discharger includes a setup script that automates your development environment configuration. When you run the install generator, it creates a bin/setup script and a config/setup.yml configuration file.
After installing Discharger, run the setup script to configure your development environment:
$ bin/setupThis script is idempotent - you can run it multiple times safely, and it will ensure your environment is properly configured.
The setup process is configured through config/setup.yml. Here's an example configuration:
app_name: "YourAppName"
database:
port: 5432
name: "db-your-app"
version: "14"
password: "postgres"
# Optional. Controls how the docker step handles a native PostgreSQL
# already listening on the configured port:
# omitted/nil/false - silently skip Docker and use the native instance (legacy default)
# true - always create the Docker container; fail if a native instance holds the port
# "prompt" - ask the developer (interactive shells only; non-interactive runs fall back to native)
prefer_docker: "prompt"
redis:
port: 6379
name: "redis-your-app"
version: "latest"
# Built-in commands to run
steps:
- brew
- asdf
- git
- bundler
- yarn
- config
- docker
- env
- database
# Custom commands for your application
custom_steps:
- description: "Seed application data"
command: "bin/rails db:seed"The pre_steps array defines commands that run before Rails loads. These are for system dependencies and environment setup that must be in place before bundler or Rails can initialize.
Built-in pre_steps:
homebrew- Installs Homebrew if not present (macOS)postgresql_tools- Optionally installs PostgreSQL client tools (pg_dump,psql) for apps that usestructure.sqlor call those tools directly
pre_steps:
- homebrew
# - postgresql_toolsYou can also define custom pre_steps with shell commands:
pre_steps:
- homebrew
- description: "Set up environment variables"
command: "cp .env.example .env"
condition: "!File.exist?('.env')"The steps array specifies which built-in setup commands to run. Available commands include:
brew- Install Homebrew dependenciesasdf- Setup version management with asdfgit- Configure git settingsbundler- Install Ruby gemsyarn- Install JavaScript packagesconfig- Copy configuration filesdocker- Setup Docker containerspg_tools- Create Docker-awarepg_dumpandpsqlwrappers for apps that usestructure.sqlor call those tools directlyenv- Configure environment variablesdatabase- Setup and migrate database
You can customize which steps run by modifying the steps array:
# Only run specific setup steps
steps:
- bundler
- database
- yarnLeave the array empty or omit it entirely to run all available steps.
Add application-specific setup tasks using the custom_steps section:
custom_steps:
# Simple command
- description: "Compile assets"
command: "bin/rails assets:precompile"
# Command with condition
- description: "Setup Elasticsearch"
command: "bin/rails search:setup"
condition: "defined?(Elasticsearch)"
# Command with environment variable condition
- description: "Import production data"
command: "bin/rails db:import"
condition: "ENV['IMPORT_DATA'] == 'true'"Each custom step can include:
description- A description shown during setupcommand- The command to executecondition- An optional Ruby expression that must evaluate to true for the command to run
For more complex setup logic, you can create custom command classes that integrate with Discharger's setup system:
# lib/setup_commands/elasticsearch_command.rb
class ElasticsearchCommand < Discharger::SetupRunner::Commands::BaseCommand
def description
"Configure Elasticsearch"
end
def can_execute?
defined?(Elasticsearch)
end
def execute
with_spinner("Setting up Elasticsearch...") do
# Your setup logic here
system("bin/rails search:setup")
system("bin/rails search:reindex")
end
log "Elasticsearch configured successfully", emoji: "✅"
end
end
# Register the command in an initializer or your setup script
Discharger::SetupRunner.register_command(:elasticsearch, ElasticsearchCommand)Then use it in your configuration:
steps:
- bundler
- database
- elasticsearch # Your custom commandThis gem is managed with Reissue.
Releases are streamlined with a single GitHub Actions workflow using RubyGems Trusted Publishing:
- Go to Actions → "Release gem to RubyGems.org" → Run workflow
- Select version bump type (patch, minor, or major)
- The workflow will automatically:
- Finalize the changelog with the release date
- Build the gem with checksum verification
- Publish to RubyGems.org via Trusted Publishing (no API keys needed)
- Create a git tag for the release
- Bump to the next development version
- Open a PR with the version bump for continued development
Bug reports and pull requests are welcome on GitHub.
The gem is available as open source under the terms of the MIT License.