MRSK hooks

Adds hooks to MRSK. Currently just two hooks, pre-build and post-push.

We could break the build and push into two separate commands if we
found the need for post-build and/or pre-push hooks.

Hooks are stored in `.mrsk/hooks`. Running `mrsk init` will now create
that folder and add sample hook scripts.

Hooks returning non-zero exit codes will abort the current command.

Further potential work here:
- We could replace the audit broadcast command with a
post-deploy/post-rollback hook or similar
- Maybe provide pre-command/post-command hooks that run after every
mrsk invocation
- Also look for hooks in `~/.mrsk/hooks`
This commit is contained in:
Donal McBreen
2023-05-05 15:08:28 +01:00
parent 340ed94fa9
commit 58c1096a90
23 changed files with 292 additions and 51 deletions

View File

@@ -134,5 +134,17 @@ module Mrsk::Cli
MRSK.hold_lock_on_error = false
end
end
def run_hook(hook)
run_locally do
if MRSK.hook.hook_exists?(hook)
begin
MRSK.with_verbosity(:debug) { execute(*MRSK.hook.run(hook)) }
rescue SSHKit::Command::Failed
raise HookError.new("Hook `#{hook}` failed")
end
end
end
end
end
end

View File

@@ -16,6 +16,7 @@ class Mrsk::Cli::Build < Mrsk::Cli::Base
verify_local_dependencies
run_hook("pre-build")
run_locally do
begin
MRSK.with_verbosity(:debug) { execute *MRSK.builder.push }
@@ -31,6 +32,7 @@ class Mrsk::Cli::Build < Mrsk::Cli::Base
end
end
end
run_hook("post-push")
end
end

View File

@@ -132,6 +132,14 @@ class Mrsk::Cli::Main < Mrsk::Cli::Base
puts "Created .env file"
end
unless (hooks_dir = Pathname.new(File.expand_path(".mrsk/hooks"))).exist?
hooks_dir.mkpath
Pathname.new(File.expand_path("templates/sample_hooks", __dir__)).each_child do |sample_hook|
FileUtils.cp sample_hook, hooks_dir
end
puts "Created sample hooks in .mrsk/hooks"
end
if options[:bundle]
if (binstub = Pathname.new(File.expand_path("bin/mrsk"))).exist?
puts "Binstub already exists in bin/mrsk (remove first to create a new one)"

View File

@@ -0,0 +1,20 @@
#!/bin/sh
# A sample post-push hook
#
# Checks:
# 1. We have a clean checkout
#
# These environment variables are available:
# MRSK_RECORDED_AT
# MRSK_PERFORMER
# MRSK_VERSION
# MRSK_DESTINATION (if set)
if [ -n "$(git status --porcelain)" ]; then
echo "Git checkout is not clean, aborting..."
git status --porcelain >&2
exit 1
fi
exit 0

View File

@@ -0,0 +1,49 @@
#!/bin/sh
# A sample pre-build hook
#
# Checks:
# 1. We have a clean checkout
# 2. A remote is configured
# 3. The branch has been pushed to the remote
# 4. The version we are deploying matches the remote
#
# These environment variables are available:
# MRSK_RECORDED_AT
# MRSK_PERFORMER
# MRSK_VERSION
# MRSK_DESTINATION (if set)
if [ -n "$(git status --porcelain)" ]; then
echo "Git checkout is not clean, aborting..." >&2
git status --porcelain >&2
exit 1
fi
first_remote=$(git remote)
if [ -z "$first_remote" ]; then
echo "No git remote set, aborting..." >&2
exit 1
fi
current_branch=$(git branch --show-current)
if [ -z "$current_branch" ]; then
echo "No git remote set, aborting..." >&2
exit 1
fi
remote_head=$(git ls-remote $first_remote --tags $current_branch | cut -f1)
if [ -z "$remote_head" ]; then
echo "Branch not pushed to remote, aborting..." >&2
exit 1
fi
if [ "$MRSK_VERSION" != "$remote_head" ]; then
echo "Version ($MRSK_VERSION) does not match remote HEAD ($remote_head), aborting..." >&2
exit 1
fi
exit 0