mirror of https://github.com/discourse/pups.git
Compare commits
8 Commits
Author | SHA1 | Date |
---|---|---|
|
332921814b | |
|
f91de7519a | |
|
078edb6407 | |
|
5436aec99e | |
|
1276ccd44d | |
|
d51da1fc1d | |
|
e0ff889553 | |
|
3f02d19ad1 |
|
@ -1,12 +1,30 @@
|
|||
name: Tests
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
pull_request: {}
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: "pups lint"
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 5
|
||||
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
ruby: ["3.3"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: ${{ matrix.ruby }}
|
||||
bundler-cache: true
|
||||
- run: bundle exec rubocop
|
||||
test:
|
||||
name: "pups tests"
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
@ -16,10 +34,10 @@ jobs:
|
|||
fail-fast: true
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
ruby: ["2.7"]
|
||||
ruby: ["3.3"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: ${{ matrix.ruby }}
|
||||
|
@ -27,3 +45,18 @@ jobs:
|
|||
- name: Run minitest
|
||||
run: |
|
||||
rake test
|
||||
|
||||
publish:
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||
needs: [test]
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Release Gem
|
||||
uses: discourse/publish-rubygems-action@v2
|
||||
env:
|
||||
RUBYGEMS_API_KEY: ${{ secrets.RUBYGEMS_API_KEY }}
|
||||
GIT_EMAIL: team@discourse.org
|
||||
GIT_NAME: discoursebot
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
name: Lint
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request: {}
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: "pups lint"
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 5
|
||||
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
ruby: ["2.7"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: ${{ matrix.ruby }}
|
||||
bundler-cache: true
|
||||
- run: bundle exec rubocop
|
|
@ -1,3 +1,2 @@
|
|||
inherit_gem:
|
||||
rubocop-discourse: default.yml
|
||||
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
1.3.0 - 07-08-2025
|
||||
|
||||
- Add --params option
|
||||
|
||||
1.2.0 - 10-22-2023
|
||||
|
||||
- Add --tags and --skip-tags options
|
||||
|
||||
1.0.3 - 09-04-2021
|
||||
|
||||
- Started changelog - release to rubygems
|
||||
|
|
48
README.md
48
README.md
|
@ -27,6 +27,8 @@ Usage: pups [options] [FILE|--stdin]
|
|||
--ignore <elements> Ignore specific configuration elements, multiple elements can be provided (comma-delimited).
|
||||
Useful if you want to skip over config in a pups execution.
|
||||
e.g. `--ignore env,params`.
|
||||
--tags <elements> Only run tagged commands.
|
||||
--skip-tags <elements> Run all but listed tagged commands.
|
||||
--gen-docker-run-args Output arguments from the pups configuration for input into a docker run command. All other pups config is ignored.
|
||||
-h, --help
|
||||
```
|
||||
|
@ -48,6 +50,52 @@ Running: `pups somefile.yaml` will execute the shell script resulting in a file
|
|||
|
||||
### Features
|
||||
|
||||
#### Filtering run commands by tags
|
||||
|
||||
The `--tags` and `--skip-tags` argument allows pups to target a subset of commands listed in the somefile.yaml. To use this, you may tag your commands in the runblock. `--tags` will only run commands when commands have a matching tag. `--skip-tags` will skip when commands have a matching tag.
|
||||
|
||||
Note, hooks from tagged commands will be present or absent depending on if the tag is filtered out or not as well. A command filtered out by targeting tag will also filter out the command's `before_` and `after_` hooks.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
# somefile.yaml
|
||||
|
||||
run:
|
||||
- exec:
|
||||
cmd: /bin/bash -c 'echo hello >> hello'
|
||||
tag: sometag
|
||||
- exec:
|
||||
cmd: /bin/bash -c 'echo hi >> hello'
|
||||
tag: anothertag
|
||||
- exec:
|
||||
cmd: /bin/bash -c 'echo goodbye >> hello'
|
||||
tag: thirdtag
|
||||
```
|
||||
Running: `pups --tags="sometag,anothertag" somefile.yaml` will not run the echo goodbye statement.
|
||||
|
||||
Running: `pups --skip-tags="sometag,anothertag" somefile.yaml` will ONLY run the echo goodbye statement.
|
||||
|
||||
#### Parameter overriding
|
||||
|
||||
The `--params` argument allows pups to dynamically override params set within a configuration for the single pups run.
|
||||
|
||||
Note, it is expected to be of the form `key=value`. If it is malformed, a warning will be thrown.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
# somefile.yaml
|
||||
|
||||
params:
|
||||
param1: false_prophet
|
||||
param2: also overridden
|
||||
run:
|
||||
- exec:
|
||||
cmd: /bin/bash -c 'echo $param1 $param2 >> hello'
|
||||
```
|
||||
Running `pups --params="param1=true_value,param2=other_true_value" somefile.yaml` will overwrite param1 and param2 with true_value and other_true_value respectively
|
||||
|
||||
#### Docker run argument generation
|
||||
|
||||
The `--gen-docker-run-args` argument is used to make pups output arguments be in the format of `docker run <arguments output>`. Specifically, pups
|
||||
|
|
26
lib/pups.rb
26
lib/pups.rb
|
@ -1,17 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'logger'
|
||||
require 'yaml'
|
||||
require "logger"
|
||||
require "yaml"
|
||||
|
||||
require 'pups/version'
|
||||
require 'pups/config'
|
||||
require 'pups/command'
|
||||
require 'pups/exec_command'
|
||||
require 'pups/merge_command'
|
||||
require 'pups/replace_command'
|
||||
require 'pups/file_command'
|
||||
require 'pups/docker'
|
||||
require 'pups/runit'
|
||||
require "pups/version"
|
||||
require "pups/config"
|
||||
require "pups/command"
|
||||
require "pups/exec_command"
|
||||
require "pups/merge_command"
|
||||
require "pups/replace_command"
|
||||
require "pups/file_command"
|
||||
require "pups/docker"
|
||||
require "pups/runit"
|
||||
|
||||
module Pups
|
||||
class ExecError < RuntimeError
|
||||
|
@ -28,9 +28,7 @@ module Pups
|
|||
end
|
||||
|
||||
def self.silence
|
||||
if @logger
|
||||
@logger.close
|
||||
end
|
||||
@logger.close if @logger
|
||||
|
||||
@logger = Logger.new(File.open(File::NULL, "w"))
|
||||
end
|
||||
|
|
|
@ -1,17 +1,35 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'optparse'
|
||||
require "optparse"
|
||||
|
||||
module Pups
|
||||
class Cli
|
||||
def self.opts
|
||||
OptionParser.new do |opts|
|
||||
opts.banner = 'Usage: pups [FILE|--stdin]'
|
||||
opts.on('--stdin', 'Read input from stdin.')
|
||||
opts.on('--quiet', "Don't print any logs.")
|
||||
opts.on('--ignore <element(s)>', Array, "Ignore these template configuration elements, multiple elements can be provided (comma-delimited).")
|
||||
opts.on('--gen-docker-run-args', 'Output arguments from the pups configuration for input into a docker run command. All other pups config is ignored.')
|
||||
opts.on('-h', '--help') do
|
||||
opts.banner = "Usage: pups [FILE|--stdin]"
|
||||
opts.on("--stdin", "Read input from stdin.")
|
||||
opts.on("--quiet", "Don't print any logs.")
|
||||
opts.on(
|
||||
"--ignore <element(s)>",
|
||||
Array,
|
||||
"Ignore these template configuration elements, multiple elements can be provided (comma-delimited)."
|
||||
)
|
||||
opts.on(
|
||||
"--gen-docker-run-args",
|
||||
"Output arguments from the pups configuration for input into a docker run command. All other pups config is ignored."
|
||||
)
|
||||
opts.on("--tags <tag(s)>", Array, "Only run tagged commands.")
|
||||
opts.on(
|
||||
"--skip-tags <tag(s)>",
|
||||
Array,
|
||||
"Run all but listed tagged commands."
|
||||
)
|
||||
opts.on(
|
||||
"--params <param(s)>",
|
||||
Array,
|
||||
"Replace params in the config with params listed."
|
||||
)
|
||||
opts.on("-h", "--help") do
|
||||
puts opts
|
||||
exit
|
||||
end
|
||||
|
@ -26,35 +44,48 @@ module Pups
|
|||
|
||||
def self.run(args)
|
||||
options = parse_args(args)
|
||||
input_file = options[:stdin] ? 'stdin' : args.last
|
||||
input_file = options[:stdin] ? "stdin" : args.last
|
||||
unless input_file
|
||||
puts opts.parse!(%w[--help])
|
||||
exit
|
||||
end
|
||||
|
||||
if options[:quiet]
|
||||
Pups.silence
|
||||
end
|
||||
Pups.silence if options[:quiet]
|
||||
|
||||
Pups.log.info("Reading from #{input_file}")
|
||||
|
||||
if options[:stdin]
|
||||
conf = $stdin.readlines.join
|
||||
split = conf.split('_FILE_SEPERATOR_')
|
||||
split = conf.split("_FILE_SEPERATOR_")
|
||||
|
||||
conf = nil
|
||||
split.each do |data|
|
||||
current = YAML.safe_load(data.strip)
|
||||
conf = if conf
|
||||
Pups::MergeCommand.deep_merge(conf, current, :merge_arrays)
|
||||
else
|
||||
current
|
||||
end
|
||||
conf =
|
||||
if conf
|
||||
Pups::MergeCommand.deep_merge(conf, current, :merge_arrays)
|
||||
else
|
||||
current
|
||||
end
|
||||
end
|
||||
|
||||
config = Pups::Config.new(conf, options[:ignore])
|
||||
config =
|
||||
Pups::Config.new(
|
||||
conf,
|
||||
options[:ignore],
|
||||
tags: options[:tags],
|
||||
skip_tags: options[:"skip-tags"],
|
||||
extra_params: options[:params]
|
||||
)
|
||||
else
|
||||
config = Pups::Config.load_file(input_file, options[:ignore])
|
||||
config =
|
||||
Pups::Config.load_file(
|
||||
input_file,
|
||||
options[:ignore],
|
||||
tags: options[:tags],
|
||||
skip_tags: options[:"skip-tags"],
|
||||
extra_params: options[:params]
|
||||
)
|
||||
end
|
||||
|
||||
if options[:"gen-docker-run-args"]
|
||||
|
|
|
@ -4,8 +4,10 @@ module Pups
|
|||
class Command
|
||||
def self.run(command, params)
|
||||
case command
|
||||
when String then from_str(command, params).run
|
||||
when Hash then from_hash(command, params).run
|
||||
when String
|
||||
from_str(command, params).run
|
||||
when Hash
|
||||
from_hash(command, params).run
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -4,33 +4,72 @@ module Pups
|
|||
class Config
|
||||
attr_reader :config, :params
|
||||
|
||||
def initialize(config, ignored = nil)
|
||||
def initialize(
|
||||
config,
|
||||
ignored = nil,
|
||||
tags: nil,
|
||||
skip_tags: nil,
|
||||
extra_params: nil
|
||||
)
|
||||
@config = config
|
||||
|
||||
# remove any ignored config elements prior to any more processing
|
||||
ignored&.each { |e| @config.delete(e) }
|
||||
|
||||
filter_tags(include_tags: tags, exclude_tags: skip_tags)
|
||||
|
||||
# set some defaults to prevent checks in various functions
|
||||
['env_template', 'env', 'labels', 'params'].each { |key| @config[key] = {} unless @config.has_key?(key) }
|
||||
%w[env_template env labels params].each do |key|
|
||||
@config[key] = {} unless @config.has_key?(key)
|
||||
end
|
||||
|
||||
# Order here is important.
|
||||
Pups::Config.combine_template_and_process_env(@config, ENV)
|
||||
Pups::Config.prepare_env_template_vars(@config['env_template'], ENV)
|
||||
Pups::Config.prepare_env_template_vars(@config["env_template"], ENV)
|
||||
|
||||
# Templating is supported in env and label variables.
|
||||
Pups::Config.transform_config_with_templated_vars(@config['env_template'], ENV)
|
||||
Pups::Config.transform_config_with_templated_vars(@config['env_template'], @config['env'])
|
||||
Pups::Config.transform_config_with_templated_vars(@config['env_template'], @config['labels'])
|
||||
Pups::Config.transform_config_with_templated_vars(
|
||||
@config["env_template"],
|
||||
ENV
|
||||
)
|
||||
Pups::Config.transform_config_with_templated_vars(
|
||||
@config["env_template"],
|
||||
@config["env"]
|
||||
)
|
||||
Pups::Config.transform_config_with_templated_vars(
|
||||
@config["env_template"],
|
||||
@config["labels"]
|
||||
)
|
||||
|
||||
@params = @config["params"]
|
||||
ENV.each do |k, v|
|
||||
@params["$ENV_#{k}"] = v
|
||||
if extra_params
|
||||
extra_params.each do |val|
|
||||
key_val = val.split("=", 2)
|
||||
if key_val.length == 2
|
||||
@params[key_val[0]] = key_val[1]
|
||||
else
|
||||
warn "Malformed param #{val}. Expected param to be of the form `key=value`"
|
||||
end
|
||||
end
|
||||
end
|
||||
ENV.each { |k, v| @params["$ENV_#{k}"] = v }
|
||||
inject_hooks
|
||||
end
|
||||
|
||||
def self.load_file(config_file, ignored = nil)
|
||||
Config.new(YAML.load_file(config_file), ignored)
|
||||
def self.load_file(
|
||||
config_file,
|
||||
ignored = nil,
|
||||
tags: nil,
|
||||
skip_tags: nil,
|
||||
extra_params: nil
|
||||
)
|
||||
Config.new(
|
||||
YAML.load_file(config_file),
|
||||
ignored,
|
||||
tags: tags,
|
||||
skip_tags: skip_tags,
|
||||
extra_params: extra_params
|
||||
)
|
||||
rescue Exception
|
||||
warn "Failed to parse #{config_file}"
|
||||
warn "This is probably a formatting error in #{config_file}"
|
||||
|
@ -38,15 +77,27 @@ module Pups
|
|||
raise
|
||||
end
|
||||
|
||||
def self.load_config(config, ignored = nil)
|
||||
Config.new(YAML.safe_load(config), ignored)
|
||||
def self.load_config(
|
||||
config,
|
||||
ignored = nil,
|
||||
tags: nil,
|
||||
skip_tags: nil,
|
||||
extra_params: nil
|
||||
)
|
||||
Config.new(
|
||||
YAML.safe_load(config),
|
||||
ignored,
|
||||
tags: tags,
|
||||
skip_tags: skip_tags,
|
||||
extra_params: extra_params
|
||||
)
|
||||
end
|
||||
|
||||
def self.prepare_env_template_vars(env_template, env)
|
||||
# Merge env_template variables from env and templates.
|
||||
env.each do |k, v|
|
||||
if k.include?('env_template_')
|
||||
key = k.gsub('env_template_', '')
|
||||
if k.include?("env_template_")
|
||||
key = k.gsub("env_template_", "")
|
||||
env_template[key] = v.to_s
|
||||
end
|
||||
end
|
||||
|
@ -70,10 +121,38 @@ module Pups
|
|||
config["env"].each { |k, v| env[k] = v.to_s }
|
||||
end
|
||||
|
||||
def inject_hooks
|
||||
return unless hooks = @config['hooks']
|
||||
# Filter run commands by tag: by default, keep all commands that contain tags.
|
||||
# If skip_tags argument is true, keep all commands that DO NOT contain tags.
|
||||
def filter_tags(include_tags: nil, exclude_tags: nil)
|
||||
if include_tags
|
||||
@config["run"] = @config["run"].select do |row|
|
||||
keep = false
|
||||
command = row.first
|
||||
if command[1].is_a?(Hash)
|
||||
tag = command[1]["tag"]
|
||||
keep = include_tags.include?(tag)
|
||||
end
|
||||
keep
|
||||
end
|
||||
end
|
||||
|
||||
run = @config['run']
|
||||
if exclude_tags
|
||||
@config["run"] = @config["run"].select do |row|
|
||||
keep = true
|
||||
command = row.first
|
||||
if command[1].is_a?(Hash)
|
||||
tag = command[1]["tag"]
|
||||
keep = !exclude_tags.include?(tag)
|
||||
end
|
||||
keep
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def inject_hooks
|
||||
return unless hooks = @config["hooks"]
|
||||
|
||||
run = @config["run"]
|
||||
|
||||
positions = {}
|
||||
run.each do |row|
|
||||
|
@ -81,7 +160,7 @@ module Pups
|
|||
|
||||
command = row.first
|
||||
if command[1].is_a?(Hash)
|
||||
hook = command[1]['hook']
|
||||
hook = command[1]["hook"]
|
||||
positions[hook] = row if hook
|
||||
end
|
||||
end
|
||||
|
@ -112,11 +191,11 @@ module Pups
|
|||
|
||||
def generate_docker_run_arguments
|
||||
output = []
|
||||
output << Pups::Docker.generate_env_arguments(config['env'])
|
||||
output << Pups::Docker.generate_link_arguments(config['links'])
|
||||
output << Pups::Docker.generate_expose_arguments(config['expose'])
|
||||
output << Pups::Docker.generate_volume_arguments(config['volumes'])
|
||||
output << Pups::Docker.generate_label_arguments(config['labels'])
|
||||
output << Pups::Docker.generate_env_arguments(config["env"])
|
||||
output << Pups::Docker.generate_link_arguments(config["links"])
|
||||
output << Pups::Docker.generate_expose_arguments(config["expose"])
|
||||
output << Pups::Docker.generate_volume_arguments(config["volumes"])
|
||||
output << Pups::Docker.generate_label_arguments(config["labels"])
|
||||
output.sort!.join(" ").strip
|
||||
end
|
||||
|
||||
|
@ -128,25 +207,33 @@ module Pups
|
|||
unless exit_code == 77
|
||||
puts
|
||||
puts
|
||||
puts 'FAILED'
|
||||
puts '-' * 20
|
||||
puts "FAILED"
|
||||
puts "-" * 20
|
||||
puts "#{e.class}: #{e}"
|
||||
puts "Location of failure: #{e.backtrace[0]}"
|
||||
puts "#{@last_command[:command]} failed with the params #{@last_command[:params].inspect}" if @last_command
|
||||
if @last_command
|
||||
puts "#{@last_command[:command]} failed with the params #{@last_command[:params].inspect}"
|
||||
end
|
||||
end
|
||||
exit exit_code
|
||||
end
|
||||
|
||||
def run_commands
|
||||
@config['run']&.each do |item|
|
||||
@config["run"]&.each do |item|
|
||||
item.each do |k, v|
|
||||
type = case k
|
||||
when 'exec' then Pups::ExecCommand
|
||||
when 'merge' then Pups::MergeCommand
|
||||
when 'replace' then Pups::ReplaceCommand
|
||||
when 'file' then Pups::FileCommand
|
||||
else raise SyntaxError, "Invalid run command #{k}"
|
||||
end
|
||||
type =
|
||||
case k
|
||||
when "exec"
|
||||
Pups::ExecCommand
|
||||
when "merge"
|
||||
Pups::MergeCommand
|
||||
when "replace"
|
||||
Pups::ReplaceCommand
|
||||
when "file"
|
||||
Pups::FileCommand
|
||||
else
|
||||
raise SyntaxError, "Invalid run command #{k}"
|
||||
end
|
||||
|
||||
@last_command = { command: k, params: v }
|
||||
type.run(v, @params)
|
||||
|
@ -162,9 +249,7 @@ module Pups
|
|||
return unless cmd
|
||||
|
||||
processed = cmd.dup
|
||||
params.each do |k, v|
|
||||
processed.gsub!("$#{k}", v.to_s)
|
||||
end
|
||||
params.each { |k, v| processed.gsub!("$#{k}", v.to_s) }
|
||||
processed
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
require 'shellwords'
|
||||
require "shellwords"
|
||||
|
||||
class Pups::Docker
|
||||
class << self
|
||||
|
@ -16,7 +16,7 @@ class Pups::Docker
|
|||
def generate_link_arguments(config)
|
||||
output = []
|
||||
config&.each do |c|
|
||||
output << "--link #{c['link']['name']}:#{c['link']['alias']}"
|
||||
output << "--link #{c["link"]["name"]}:#{c["link"]["alias"]}"
|
||||
end
|
||||
normalize_output(output)
|
||||
end
|
||||
|
@ -36,7 +36,7 @@ class Pups::Docker
|
|||
def generate_volume_arguments(config)
|
||||
output = []
|
||||
config&.each do |c|
|
||||
output << "--volume #{c['volume']['host']}:#{c['volume']['guest']}"
|
||||
output << "--volume #{c["volume"]["host"]}:#{c["volume"]["guest"]}"
|
||||
end
|
||||
normalize_output(output)
|
||||
end
|
||||
|
@ -50,24 +50,17 @@ class Pups::Docker
|
|||
end
|
||||
|
||||
private
|
||||
|
||||
def escape_user_string_literal(str)
|
||||
# We need to escape the following strings as they are more likely to contain
|
||||
# special characters than any of the other config variables on a Linux system:
|
||||
# - the value side of an environment variable
|
||||
# - the value side of a label.
|
||||
if str.to_s.include?(" ")
|
||||
"\"#{Shellwords.escape(str)}\""
|
||||
else
|
||||
Shellwords.escape(str)
|
||||
end
|
||||
Shellwords.escape(str)
|
||||
end
|
||||
|
||||
def normalize_output(output)
|
||||
if output.empty?
|
||||
""
|
||||
else
|
||||
output.join(" ")
|
||||
end
|
||||
output.empty? ? "" : output.join(" ")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'timeout'
|
||||
require 'English'
|
||||
require "timeout"
|
||||
require "English"
|
||||
|
||||
module Pups
|
||||
class ExecCommand < Pups::Command
|
||||
|
@ -9,12 +9,14 @@ module Pups
|
|||
attr_accessor :background, :raise_on_fail, :stdin, :stop_signal
|
||||
|
||||
def self.terminate_async(opts = {})
|
||||
return unless defined? @@asyncs
|
||||
return unless defined?(@@asyncs)
|
||||
|
||||
Pups.log.info('Terminating async processes')
|
||||
Pups.log.info("Terminating async processes")
|
||||
|
||||
@@asyncs.each do |async|
|
||||
Pups.log.info("Sending #{async[:stop_signal]} to #{async[:command]} pid: #{async[:pid]}")
|
||||
Pups.log.info(
|
||||
"Sending #{async[:stop_signal]} to #{async[:command]} pid: #{async[:pid]}"
|
||||
)
|
||||
begin
|
||||
Process.kill(async[:stop_signal], async[:pid])
|
||||
rescue StandardError
|
||||
|
@ -22,37 +24,43 @@ module Pups
|
|||
end
|
||||
end
|
||||
|
||||
@@asyncs.map do |async|
|
||||
Thread.new do
|
||||
Timeout.timeout(opts[:wait] || 10) do
|
||||
Process.wait(async[:pid])
|
||||
rescue StandardError
|
||||
nil
|
||||
end
|
||||
rescue Timeout::Error
|
||||
Pups.log.info("#{async[:command]} pid:#{async[:pid]} did not terminate cleanly, forcing termination!")
|
||||
begin
|
||||
Process.kill('KILL', async[:pid])
|
||||
Process.wait(async[:pid])
|
||||
rescue Errno::ESRCH
|
||||
rescue Errno::ECHILD
|
||||
@@asyncs
|
||||
.map do |async|
|
||||
Thread.new do
|
||||
Timeout.timeout(opts[:wait] || 10) do
|
||||
Process.wait(async[:pid])
|
||||
rescue StandardError
|
||||
nil
|
||||
end
|
||||
rescue Timeout::Error
|
||||
Pups.log.info(
|
||||
"#{async[:command]} pid:#{async[:pid]} did not terminate cleanly, forcing termination!"
|
||||
)
|
||||
begin
|
||||
Process.kill("KILL", async[:pid])
|
||||
Process.wait(async[:pid])
|
||||
rescue Errno::ESRCH
|
||||
rescue Errno::ECHILD
|
||||
end
|
||||
end
|
||||
end
|
||||
end.each(&:join)
|
||||
.each(&:join)
|
||||
end
|
||||
|
||||
def self.from_hash(hash, params)
|
||||
cmd = new(params, hash['cd'])
|
||||
cmd = new(params, hash["cd"])
|
||||
|
||||
case c = hash['cmd']
|
||||
when String then cmd.add(c)
|
||||
when Array then c.each { |i| cmd.add(i) }
|
||||
case c = hash["cmd"]
|
||||
when String
|
||||
cmd.add(c)
|
||||
when Array
|
||||
c.each { |i| cmd.add(i) }
|
||||
end
|
||||
|
||||
cmd.background = hash['background']
|
||||
cmd.stop_signal = hash['stop_signal'] || 'TERM'
|
||||
cmd.raise_on_fail = hash['raise_on_fail'] if hash.key? 'raise_on_fail'
|
||||
cmd.stdin = interpolate_params(hash['stdin'], params)
|
||||
cmd.background = hash["background"]
|
||||
cmd.stop_signal = hash["stop_signal"] || "TERM"
|
||||
cmd.raise_on_fail = hash["raise_on_fail"] if hash.key? "raise_on_fail"
|
||||
cmd.stdin = interpolate_params(hash["stdin"], params)
|
||||
|
||||
cmd
|
||||
end
|
||||
|
@ -79,7 +87,6 @@ module Pups
|
|||
Pups.log.info("> #{command}")
|
||||
pid = spawn(command)
|
||||
Pups.log.info(@result.readlines.join("\n")) if @result
|
||||
pid
|
||||
end
|
||||
rescue StandardError
|
||||
raise if @raise_on_fail
|
||||
|
@ -88,7 +95,11 @@ module Pups
|
|||
def spawn(command)
|
||||
if background
|
||||
pid = Process.spawn(command)
|
||||
(@@asyncs ||= []) << { pid: pid, command: command, stop_signal: (stop_signal || 'TERM') }
|
||||
(@@asyncs ||= []) << {
|
||||
pid: pid,
|
||||
command: command,
|
||||
stop_signal: (stop_signal || "TERM")
|
||||
}
|
||||
Thread.new do
|
||||
begin
|
||||
Process.wait(pid)
|
||||
|
@ -100,7 +111,7 @@ module Pups
|
|||
return pid
|
||||
end
|
||||
|
||||
IO.popen(command, 'w+') do |f|
|
||||
IO.popen(command, "w+") do |f|
|
||||
if stdin
|
||||
# need a way to get stdout without blocking
|
||||
Pups.log.info(stdin)
|
||||
|
@ -112,7 +123,10 @@ module Pups
|
|||
end
|
||||
|
||||
unless $CHILD_STATUS == 0
|
||||
err = Pups::ExecError.new("#{command} failed with return #{$CHILD_STATUS.inspect}")
|
||||
err =
|
||||
Pups::ExecError.new(
|
||||
"#{command} failed with return #{$CHILD_STATUS.inspect}"
|
||||
)
|
||||
err.exit_code = $CHILD_STATUS.exitstatus
|
||||
raise err
|
||||
end
|
||||
|
|
|
@ -6,10 +6,10 @@ module Pups
|
|||
|
||||
def self.from_hash(hash, params)
|
||||
command = new
|
||||
command.path = hash['path']
|
||||
command.contents = hash['contents']
|
||||
command.chmod = hash['chmod']
|
||||
command.chown = hash['chown']
|
||||
command.path = hash["path"]
|
||||
command.contents = hash["contents"]
|
||||
command.chmod = hash["chmod"]
|
||||
command.chown = hash["chown"]
|
||||
command.params = params
|
||||
|
||||
command
|
||||
|
@ -20,15 +20,11 @@ module Pups
|
|||
@type = :bash
|
||||
end
|
||||
|
||||
attr_writer :params
|
||||
|
||||
def run
|
||||
path = interpolate_params(@path)
|
||||
|
||||
`mkdir -p #{File.dirname(path)}`
|
||||
File.open(path, 'w') do |f|
|
||||
f.write(interpolate_params(contents))
|
||||
end
|
||||
File.open(path, "w") { |f| f.write(interpolate_params(contents)) }
|
||||
`chmod #{@chmod} #{path}` if @chmod
|
||||
`chown #{@chown} #{path}` if @chown
|
||||
Pups.log.info("File > #{path} chmod: #{@chmod} chown: #{@chown}")
|
||||
|
|
|
@ -9,10 +9,12 @@ module Pups
|
|||
end
|
||||
|
||||
def self.parse_command(command)
|
||||
split = command.split(' ')
|
||||
raise ArgumentError, "Invalid merge command #{command}" unless split[-1][0] == '$'
|
||||
split = command.split(" ")
|
||||
unless split[-1][0] == "$"
|
||||
raise ArgumentError, "Invalid merge command #{command}"
|
||||
end
|
||||
|
||||
[split[0..-2].join(' '), split[-1][1..-1]]
|
||||
[split[0..-2].join(" "), split[-1][1..-1]]
|
||||
end
|
||||
|
||||
def initialize(command, params)
|
||||
|
@ -25,7 +27,7 @@ module Pups
|
|||
|
||||
def run
|
||||
merged = self.class.deep_merge(YAML.load_file(@filename), @merge_hash)
|
||||
File.open(@filename, 'w') { |f| f.write(merged.to_yaml) }
|
||||
File.open(@filename, "w") { |f| f.write(merged.to_yaml) }
|
||||
Pups.log.info("Merge: #{@filename} with: \n#{@merge_hash.inspect}")
|
||||
end
|
||||
|
||||
|
@ -33,17 +35,18 @@ module Pups
|
|||
args ||= []
|
||||
merge_arrays = args.include? :merge_arrays
|
||||
|
||||
merger = proc { |_key, v1, v2|
|
||||
if v1.is_a?(Hash) && v2.is_a?(Hash)
|
||||
v1.merge(v2, &merger)
|
||||
elsif v1.is_a?(Array) && v2.is_a?(Array)
|
||||
merge_arrays ? v1 + v2 : v2
|
||||
elsif v2.is_a?(NilClass)
|
||||
v1
|
||||
else
|
||||
v2
|
||||
merger =
|
||||
proc do |_key, v1, v2|
|
||||
if v1.is_a?(Hash) && v2.is_a?(Hash)
|
||||
v1.merge(v2, &merger)
|
||||
elsif v1.is_a?(Array) && v2.is_a?(Array)
|
||||
merge_arrays ? v1 + v2 : v2
|
||||
elsif v2.is_a?(NilClass)
|
||||
v1
|
||||
else
|
||||
v2
|
||||
end
|
||||
end
|
||||
}
|
||||
first.merge(second, &merger)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,18 +6,18 @@ module Pups
|
|||
|
||||
def self.from_hash(hash, params)
|
||||
replacer = new(params)
|
||||
replacer.from = guess_replace_type(hash['from'])
|
||||
replacer.to = guess_replace_type(hash['to'])
|
||||
replacer.text = File.read(hash['filename'])
|
||||
replacer.filename = hash['filename']
|
||||
replacer.direction = hash['direction'].to_sym if hash['direction']
|
||||
replacer.global = hash['global'].to_s == 'true'
|
||||
replacer.from = guess_replace_type(hash["from"])
|
||||
replacer.to = guess_replace_type(hash["to"])
|
||||
replacer.text = File.read(hash["filename"])
|
||||
replacer.filename = hash["filename"]
|
||||
replacer.direction = hash["direction"].to_sym if hash["direction"]
|
||||
replacer.global = hash["global"].to_s == "true"
|
||||
replacer
|
||||
end
|
||||
|
||||
def self.guess_replace_type(item)
|
||||
# evaling to get all the regex flags easily
|
||||
item[0] == '/' ? eval(item) : item
|
||||
item[0] == "/" ? eval(item) : item # rubocop:disable Security/Eval
|
||||
end
|
||||
|
||||
def initialize(params)
|
||||
|
@ -39,7 +39,7 @@ module Pups
|
|||
|
||||
def run
|
||||
Pups.log.info("Replacing #{from} with #{to} in #{filename}")
|
||||
File.open(filename, 'w') { |f| f.write replaced_text }
|
||||
File.open(filename, "w") { |f| f.write replaced_text }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -11,9 +11,7 @@ module Pups
|
|||
def setup
|
||||
`mkdir -p /etc/service/#{name}`
|
||||
run = "/etc/service/#{name}/run"
|
||||
File.open(run, 'w') do |f|
|
||||
f.write(run_script)
|
||||
end
|
||||
File.open(run, "w") { |f| f.write(run_script) }
|
||||
`chmod +x #{run}`
|
||||
end
|
||||
|
||||
|
@ -31,9 +29,7 @@ exec 2>&1
|
|||
end
|
||||
|
||||
def env_script
|
||||
@env&.map do |k, v|
|
||||
"export #{k}=#{v}"
|
||||
end&.join("\n")
|
||||
@env&.map { |k, v| "export #{k}=#{v}" }&.join("\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Pups
|
||||
VERSION = '1.1.0'
|
||||
VERSION = "1.3.0"
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
lib = File.expand_path('lib', __dir__)
|
||||
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
||||
$LOAD_PATH.unshift(lib) if !$LOAD_PATH.include?(lib)
|
||||
require 'pups/version'
|
||||
|
||||
Gem::Specification.new do |spec|
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
require 'tempfile'
|
||||
require 'stringio'
|
||||
require "test_helper"
|
||||
require "tempfile"
|
||||
require "stringio"
|
||||
|
||||
module Pups
|
||||
class CliTest < MiniTest::Test
|
||||
class CliTest < ::Minitest::Test
|
||||
def test_cli_option_parsing_stdin
|
||||
options = Cli.parse_args(['--stdin'])
|
||||
options = Cli.parse_args(["--stdin"])
|
||||
assert_equal(true, options[:stdin])
|
||||
end
|
||||
|
||||
|
@ -18,11 +18,11 @@ module Pups
|
|||
|
||||
def test_cli_read_config_from_file
|
||||
# for testing output
|
||||
f = Tempfile.new('test_output')
|
||||
f = Tempfile.new("test_output")
|
||||
f.close
|
||||
|
||||
# for testing input
|
||||
cf = Tempfile.new('test_config')
|
||||
cf = Tempfile.new("test_config")
|
||||
cf.puts <<~YAML
|
||||
params:
|
||||
run: #{f.path}
|
||||
|
@ -32,16 +32,16 @@ module Pups
|
|||
cf.close
|
||||
|
||||
Cli.run([cf.path])
|
||||
assert_equal('hello world', File.read(f.path).strip)
|
||||
assert_equal("hello world", File.read(f.path).strip)
|
||||
end
|
||||
|
||||
def test_cli_ignore_config_element
|
||||
# for testing output
|
||||
f = Tempfile.new('test_output')
|
||||
f = Tempfile.new("test_output")
|
||||
f.close
|
||||
|
||||
# for testing input
|
||||
cf = Tempfile.new('test_config')
|
||||
cf = Tempfile.new("test_config")
|
||||
cf.puts <<~YAML
|
||||
env:
|
||||
MY_IGNORED_VAR: a_word
|
||||
|
@ -53,7 +53,7 @@ module Pups
|
|||
cf.close
|
||||
|
||||
Cli.run(["--ignore", "env,params", cf.path])
|
||||
assert_equal('repeating and also', File.read(f.path).strip)
|
||||
assert_equal("repeating and also", File.read(f.path).strip)
|
||||
end
|
||||
|
||||
def test_cli_gen_docker_run_args_ignores_other_config
|
||||
|
@ -111,7 +111,82 @@ module Pups
|
|||
expected.sort!
|
||||
|
||||
assert_equal("", File.read(f.path).strip)
|
||||
assert_output(expected.join(" ")) { Cli.run(["--gen-docker-run-args", cf.path]) }
|
||||
assert_output(expected.join(" ")) do
|
||||
Cli.run(["--gen-docker-run-args", cf.path])
|
||||
end
|
||||
end
|
||||
|
||||
def test_cli_tags
|
||||
# for testing output
|
||||
f = Tempfile.new("test_output")
|
||||
f.close
|
||||
|
||||
# for testing input
|
||||
cf = Tempfile.new("test_config")
|
||||
cf.puts <<~YAML
|
||||
run:
|
||||
- exec:
|
||||
tag: '1'
|
||||
cmd: echo 1 >> #{f.path}
|
||||
- exec:
|
||||
tag: '2'
|
||||
cmd: echo 2 >> #{f.path}
|
||||
- exec:
|
||||
tag: '3'
|
||||
cmd: echo 3 >> #{f.path}
|
||||
YAML
|
||||
cf.close
|
||||
|
||||
Cli.run(["--tags", "1,3", cf.path])
|
||||
assert_equal("1\n3", File.read(f.path).strip)
|
||||
end
|
||||
|
||||
def test_cli_skip_tags
|
||||
# for testing output
|
||||
f = Tempfile.new("test_output")
|
||||
f.close
|
||||
|
||||
# for testing input
|
||||
cf = Tempfile.new("test_config")
|
||||
cf.puts <<~YAML
|
||||
run:
|
||||
- exec:
|
||||
tag: '1'
|
||||
cmd: echo 1 >> #{f.path}
|
||||
- exec:
|
||||
tag: '2'
|
||||
cmd: echo 2 >> #{f.path}
|
||||
- exec:
|
||||
tag: '3'
|
||||
cmd: echo 3 >> #{f.path}
|
||||
YAML
|
||||
cf.close
|
||||
|
||||
Cli.run(["--skip-tags", "1,3", cf.path])
|
||||
assert_equal("2", File.read(f.path).strip)
|
||||
end
|
||||
|
||||
def test_cli_params
|
||||
# for testing output
|
||||
f = Tempfile.new("test_output")
|
||||
f.close
|
||||
|
||||
# for testing input
|
||||
cf = Tempfile.new("test_config")
|
||||
cf.puts <<~YAML
|
||||
params:
|
||||
one: 0
|
||||
two: 0
|
||||
run:
|
||||
- exec:
|
||||
cmd: echo $one >> #{f.path}
|
||||
- exec:
|
||||
cmd: echo $two >> #{f.path}
|
||||
YAML
|
||||
cf.close
|
||||
|
||||
Cli.run(["--params", "one=1,two=2", cf.path])
|
||||
assert_equal("1\n2", File.read(f.path).strip)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
require 'tempfile'
|
||||
require "test_helper"
|
||||
require "tempfile"
|
||||
|
||||
module Pups
|
||||
class ConfigTest < MiniTest::Test
|
||||
class ConfigTest < ::Minitest::Test
|
||||
def test_config_from_env
|
||||
ENV['HELLO'] = 'world'
|
||||
ENV["HELLO"] = "world"
|
||||
config = Config.new({})
|
||||
assert_equal('world', config.params['$ENV_HELLO'])
|
||||
assert_equal("world", config.params["$ENV_HELLO"])
|
||||
end
|
||||
|
||||
def test_env_param
|
||||
ENV['FOO'] = 'BAR'
|
||||
ENV["FOO"] = "BAR"
|
||||
config = <<~YAML
|
||||
env:
|
||||
BAR: baz
|
||||
|
@ -22,14 +22,14 @@ module Pups
|
|||
|
||||
config = Config.new(YAML.safe_load(config))
|
||||
%w[BAR hello one].each { |e| ENV.delete(e) }
|
||||
assert_equal('BAR', config.params['$ENV_FOO'])
|
||||
assert_equal('baz', config.params['$ENV_BAR'])
|
||||
assert_equal('WORLD', config.params['$ENV_hello'])
|
||||
assert_equal('1', config.params['$ENV_one'])
|
||||
assert_equal("BAR", config.params["$ENV_FOO"])
|
||||
assert_equal("baz", config.params["$ENV_BAR"])
|
||||
assert_equal("WORLD", config.params["$ENV_hello"])
|
||||
assert_equal("1", config.params["$ENV_one"])
|
||||
end
|
||||
|
||||
def test_env_with_template
|
||||
ENV['FOO'] = 'BAR'
|
||||
ENV["FOO"] = "BAR"
|
||||
config = <<~YAML
|
||||
env:
|
||||
greeting: "{{hello}}, {{planet}}!"
|
||||
|
@ -43,10 +43,10 @@ module Pups
|
|||
|
||||
config = Config.new(config_hash)
|
||||
%w[greeting one other].each { |e| ENV.delete(e) }
|
||||
assert_equal('hola, pluto!', config.params['$ENV_greeting'])
|
||||
assert_equal('1', config.params['$ENV_one'])
|
||||
assert_equal('BAR', config.params['$ENV_FOO'])
|
||||
assert_equal('where are we on pluto?', config.params['$ENV_other'])
|
||||
assert_equal("hola, pluto!", config.params["$ENV_greeting"])
|
||||
assert_equal("1", config.params["$ENV_one"])
|
||||
assert_equal("BAR", config.params["$ENV_FOO"])
|
||||
assert_equal("where are we on pluto?", config.params["$ENV_other"])
|
||||
end
|
||||
|
||||
def test_label_with_template
|
||||
|
@ -67,11 +67,11 @@ module Pups
|
|||
|
||||
config = Config.new(config_hash)
|
||||
%w[greeting one other].each { |e| ENV.delete(e) }
|
||||
assert_equal("various_discourse", config.config['labels']['app_name'])
|
||||
assert_equal("various_discourse", config.config["labels"]["app_name"])
|
||||
end
|
||||
|
||||
def test_env_with_ENV_templated_variable
|
||||
ENV['env_template_config'] = 'my_application'
|
||||
ENV["env_template_config"] = "my_application"
|
||||
config = <<~YAML
|
||||
env:
|
||||
greeting: "{{hello}}, {{planet}}!"
|
||||
|
@ -85,14 +85,14 @@ module Pups
|
|||
|
||||
config = Config.new(config_hash)
|
||||
%w[greeting one other].each { |e| ENV.delete(e) }
|
||||
assert_equal('hola, pluto!', config.params['$ENV_greeting'])
|
||||
assert_equal('1', config.params['$ENV_one'])
|
||||
assert_equal('building my_application', config.params['$ENV_other'])
|
||||
assert_equal("hola, pluto!", config.params["$ENV_greeting"])
|
||||
assert_equal("1", config.params["$ENV_one"])
|
||||
assert_equal("building my_application", config.params["$ENV_other"])
|
||||
ENV["env_template_config"] = nil
|
||||
end
|
||||
|
||||
def test_integration
|
||||
f = Tempfile.new('test')
|
||||
f = Tempfile.new("test")
|
||||
f.close
|
||||
|
||||
config = <<~YAML
|
||||
|
@ -106,8 +106,8 @@ module Pups
|
|||
YAML
|
||||
|
||||
Config.new(YAML.safe_load(config)).run
|
||||
ENV.delete('PLANET')
|
||||
assert_equal('hello world', File.read(f.path).strip)
|
||||
ENV.delete("PLANET")
|
||||
assert_equal("hello world", File.read(f.path).strip)
|
||||
ensure
|
||||
f.unlink
|
||||
end
|
||||
|
@ -128,12 +128,12 @@ module Pups
|
|||
YAML
|
||||
|
||||
config = Config.load_config(yaml).config
|
||||
assert_equal({ 'exec' => 1.9 }, config['run'][1])
|
||||
assert_equal({ 'exec' => 2.1 }, config['run'][3])
|
||||
assert_equal({ "exec" => 1.9 }, config["run"][1])
|
||||
assert_equal({ "exec" => 2.1 }, config["run"][3])
|
||||
end
|
||||
|
||||
def test_ignored_elements
|
||||
f = Tempfile.new('test')
|
||||
f = Tempfile.new("test")
|
||||
f.close
|
||||
|
||||
yaml = <<~YAML
|
||||
|
@ -157,15 +157,21 @@ module Pups
|
|||
|
||||
conf = Config.load_config(yaml, %w[hooks params])
|
||||
config = conf.config
|
||||
assert_equal({ 'exec' => 1 }, config['run'][0])
|
||||
assert_equal({ 'exec' => { 'hook' => 'middle', 'cmd' => 2 } }, config['run'][1])
|
||||
assert_equal({ 'exec' => 3 }, config['run'][2])
|
||||
assert_equal({ 'exec' => "echo $greeting $PLANET >> #{f.path}" }, config['run'][3])
|
||||
assert_equal({ "exec" => 1 }, config["run"][0])
|
||||
assert_equal(
|
||||
{ "exec" => { "hook" => "middle", "cmd" => 2 } },
|
||||
config["run"][1]
|
||||
)
|
||||
assert_equal({ "exec" => 3 }, config["run"][2])
|
||||
assert_equal(
|
||||
{ "exec" => "echo $greeting $PLANET >> #{f.path}" },
|
||||
config["run"][3]
|
||||
)
|
||||
|
||||
# $greet from params will be an empty var as it was ignored
|
||||
conf.run
|
||||
ENV.delete('PLANET')
|
||||
assert_equal('world', File.read(f.path).strip)
|
||||
ENV.delete("PLANET")
|
||||
assert_equal("world", File.read(f.path).strip)
|
||||
end
|
||||
|
||||
def test_generate_docker_run_arguments
|
||||
|
@ -213,5 +219,79 @@ module Pups
|
|||
|
||||
assert_equal(expected.join(" "), args)
|
||||
end
|
||||
|
||||
def test_tag_filtering
|
||||
f = Tempfile.new("test")
|
||||
f.close
|
||||
|
||||
yaml = <<~YAML
|
||||
run:
|
||||
- exec: 1
|
||||
- exec:
|
||||
hook: middle
|
||||
cmd: 2
|
||||
tag: one_tag
|
||||
- exec:
|
||||
cmd: 3
|
||||
tag: two_tag
|
||||
hooks:
|
||||
after_middle:
|
||||
- exec: 2.1
|
||||
before_middle:
|
||||
- exec: 1.9
|
||||
YAML
|
||||
|
||||
# No tagging loads everything
|
||||
conf = Config.load_config(yaml)
|
||||
config = conf.config
|
||||
assert_equal({ "exec" => 1 }, config["run"][0])
|
||||
assert_equal({ "exec" => 1.9 }, config["run"][1])
|
||||
assert_equal(
|
||||
{ "exec" => { "hook" => "middle", "cmd" => 2, "tag" => "one_tag" } },
|
||||
config["run"][2]
|
||||
)
|
||||
assert_equal({ "exec" => 2.1 }, config["run"][3])
|
||||
assert_equal(
|
||||
{ "exec" => { "cmd" => 3, "tag" => "two_tag" } },
|
||||
config["run"][4]
|
||||
)
|
||||
|
||||
# hooks get applied if hook command is not filtered
|
||||
conf = Config.load_config(yaml, tags: ["one_tag"])
|
||||
config = conf.config
|
||||
assert_equal({ "exec" => 1.9 }, config["run"][0])
|
||||
assert_equal(
|
||||
{ "exec" => { "hook" => "middle", "cmd" => 2, "tag" => "one_tag" } },
|
||||
config["run"][1]
|
||||
)
|
||||
assert_equal({ "exec" => 2.1 }, config["run"][2])
|
||||
|
||||
# hooks get filtered out if the main hook command is filtered
|
||||
conf = Config.load_config(yaml, tags: ["two_tag"])
|
||||
config = conf.config
|
||||
assert_equal(
|
||||
{ "exec" => { "cmd" => 3, "tag" => "two_tag" } },
|
||||
config["run"][0]
|
||||
)
|
||||
|
||||
# skip tags filter out commands with tags
|
||||
conf = Config.load_config(yaml, skip_tags: ["one_tag"])
|
||||
config = conf.config
|
||||
assert_equal({ "exec" => 1 }, config["run"][0])
|
||||
assert_equal(
|
||||
{ "exec" => { "cmd" => 3, "tag" => "two_tag" } },
|
||||
config["run"][1]
|
||||
)
|
||||
end
|
||||
|
||||
def test_extra_params
|
||||
config = <<~YAML
|
||||
params:
|
||||
one: 1
|
||||
YAML
|
||||
config = Config.new(YAML.safe_load(config), extra_params: %w[one=2 two=2])
|
||||
assert_equal("2", config.params["one"])
|
||||
assert_equal("2", config.params["two"])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
require 'test_helper'
|
||||
require 'tempfile'
|
||||
require 'shellwords'
|
||||
require "test_helper"
|
||||
require "tempfile"
|
||||
require "shellwords"
|
||||
|
||||
module Pups
|
||||
class DockerTest < MiniTest::Test
|
||||
class DockerTest < ::Minitest::Test
|
||||
def test_gen_env_arguments
|
||||
yaml = <<~YAML
|
||||
env:
|
||||
|
@ -16,7 +16,10 @@ module Pups
|
|||
YAML
|
||||
|
||||
config = Config.load_config(yaml)
|
||||
Config.transform_config_with_templated_vars(config.config['env_template'], config.config["env"])
|
||||
Config.transform_config_with_templated_vars(
|
||||
config.config["env_template"],
|
||||
config.config["env"]
|
||||
)
|
||||
args = Docker.generate_env_arguments(config.config["env"])
|
||||
assert_equal("--env foo=1 --env bar=2 --env baz=hello_eggs", args)
|
||||
end
|
||||
|
@ -30,7 +33,10 @@ module Pups
|
|||
YAML
|
||||
|
||||
config = Config.load_config(yaml)
|
||||
Config.transform_config_with_templated_vars(config.config['env_template'], config.config["env"])
|
||||
Config.transform_config_with_templated_vars(
|
||||
config.config["env_template"],
|
||||
config.config["env"]
|
||||
)
|
||||
args = Docker.generate_env_arguments(config.config["env"])
|
||||
assert_equal("--env foo=1 --env bar=2", args)
|
||||
end
|
||||
|
@ -44,9 +50,15 @@ module Pups
|
|||
YAML
|
||||
|
||||
config = Config.load_config(yaml)
|
||||
Config.transform_config_with_templated_vars(config.config['env_template'], config.config["env"])
|
||||
Config.transform_config_with_templated_vars(
|
||||
config.config["env_template"],
|
||||
config.config["env"]
|
||||
)
|
||||
args = Docker.generate_env_arguments(config.config["env"])
|
||||
assert_equal("--env password=\"#{Shellwords.escape('eggs*`echo`@e$t| = >>$()&list;#')}\"", args)
|
||||
assert_equal(
|
||||
"--env password=#{Shellwords.escape("eggs*`echo`@e$t| = >>$()&list;#")}",
|
||||
args
|
||||
)
|
||||
end
|
||||
|
||||
def test_gen_env_arguments_quoted_with_a_space
|
||||
|
@ -56,9 +68,12 @@ module Pups
|
|||
YAML
|
||||
|
||||
config = Config.load_config(yaml)
|
||||
Config.transform_config_with_templated_vars(config.config['env_template'], config.config["env"])
|
||||
Config.transform_config_with_templated_vars(
|
||||
config.config["env_template"],
|
||||
config.config["env"]
|
||||
)
|
||||
args = Docker.generate_env_arguments(config.config["env"])
|
||||
assert_equal('--env a_variable="here\ is\ a\ sentence"', args)
|
||||
assert_equal('--env a_variable=here\ is\ a\ sentence', args)
|
||||
end
|
||||
|
||||
def test_gen_env_arguments_newline
|
||||
|
@ -75,9 +90,12 @@ this password is
|
|||
YAML
|
||||
|
||||
config = Config.load_config(yaml)
|
||||
Config.transform_config_with_templated_vars(config.config['env_template'], config.config["env"])
|
||||
Config.transform_config_with_templated_vars(
|
||||
config.config["env_template"],
|
||||
config.config["env"]
|
||||
)
|
||||
args = Docker.generate_env_arguments(config.config["env"])
|
||||
assert_equal('--env password="this\ password\ is\ a\ weird\ one\ "', args)
|
||||
assert_equal('--env password=this\ password\ is\ a\ weird\ one\ ', args)
|
||||
end
|
||||
|
||||
def test_gen_expose_arguments
|
||||
|
@ -90,7 +108,10 @@ this password is
|
|||
|
||||
config = Config.load_config(yaml)
|
||||
args = Docker.generate_expose_arguments(config.config["expose"])
|
||||
assert_equal("--publish 2222:22 --publish 127.0.0.1:20080:80 --expose 5555", args)
|
||||
assert_equal(
|
||||
"--publish 2222:22 --publish 127.0.0.1:20080:80 --expose 5555",
|
||||
args
|
||||
)
|
||||
end
|
||||
|
||||
def test_gen_volume_arguments
|
||||
|
@ -106,7 +127,10 @@ this password is
|
|||
|
||||
config = Config.load_config(yaml)
|
||||
args = Docker.generate_volume_arguments(config.config["volumes"])
|
||||
assert_equal("--volume /var/discourse/shared:/shared --volume /bar:/baz", args)
|
||||
assert_equal(
|
||||
"--volume /var/discourse/shared:/shared --volume /bar:/baz",
|
||||
args
|
||||
)
|
||||
end
|
||||
|
||||
def test_gen_link_arguments
|
||||
|
@ -135,9 +159,15 @@ this password is
|
|||
YAML
|
||||
|
||||
config = Config.load_config(yaml)
|
||||
Config.transform_config_with_templated_vars(config.config['env_template'], config.config["labels"])
|
||||
Config.transform_config_with_templated_vars(
|
||||
config.config["env_template"],
|
||||
config.config["labels"]
|
||||
)
|
||||
args = Docker.generate_label_arguments(config.config["labels"])
|
||||
assert_equal("--label monitor=true --label app_name=my_app_discourse", args)
|
||||
assert_equal(
|
||||
"--label monitor=true --label app_name=my_app_discourse",
|
||||
args
|
||||
)
|
||||
end
|
||||
|
||||
def test_gen_label_arguments_escaped
|
||||
|
@ -149,9 +179,15 @@ this password is
|
|||
YAML
|
||||
|
||||
config = Config.load_config(yaml)
|
||||
Config.transform_config_with_templated_vars(config.config['env_template'], config.config["labels"])
|
||||
Config.transform_config_with_templated_vars(
|
||||
config.config["env_template"],
|
||||
config.config["labels"]
|
||||
)
|
||||
args = Docker.generate_label_arguments(config.config["labels"])
|
||||
assert_equal("--label app_name=#{Shellwords.escape("my_app's_di$course")}", args)
|
||||
assert_equal(
|
||||
"--label app_name=#{Shellwords.escape("my_app's_di$course")}",
|
||||
args
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
require 'tempfile'
|
||||
require "test_helper"
|
||||
require "tempfile"
|
||||
|
||||
module Pups
|
||||
class ExecCommandTest < MiniTest::Test
|
||||
class ExecCommandTest < ::Minitest::Test
|
||||
def from_str(str, params = {})
|
||||
ExecCommand.from_str(str, params).commands
|
||||
end
|
||||
|
@ -14,52 +14,49 @@ module Pups
|
|||
end
|
||||
|
||||
def test_simple_str_command
|
||||
assert_equal(['do_something'],
|
||||
from_str('do_something'))
|
||||
assert_equal(["do_something"], from_str("do_something"))
|
||||
end
|
||||
|
||||
def test_simple_str_command_with_param
|
||||
assert_equal(['hello world'],
|
||||
from_str('hello $bob', { 'bob' => 'world' }))
|
||||
assert_equal(
|
||||
["hello world"],
|
||||
from_str("hello $bob", { "bob" => "world" })
|
||||
)
|
||||
end
|
||||
|
||||
def test_nested_command
|
||||
assert_equal(['first'],
|
||||
from_hash('cmd' => 'first'))
|
||||
assert_equal(["first"], from_hash("cmd" => "first"))
|
||||
end
|
||||
|
||||
def test_multi_commands
|
||||
assert_equal(%w[first second],
|
||||
from_hash('cmd' => %w[first second]))
|
||||
assert_equal(%w[first second], from_hash("cmd" => %w[first second]))
|
||||
end
|
||||
|
||||
def test_multi_commands_with_home
|
||||
assert_equal(['cd /home/sam && first',
|
||||
'cd /home/sam && second'],
|
||||
from_hash('cmd' => %w[first second],
|
||||
'cd' => '/home/sam'))
|
||||
assert_equal(
|
||||
["cd /home/sam && first", "cd /home/sam && second"],
|
||||
from_hash("cmd" => %w[first second], "cd" => "/home/sam")
|
||||
)
|
||||
end
|
||||
|
||||
def test_exec_works
|
||||
ExecCommand.from_str('ls', {}).run
|
||||
ExecCommand.from_str("ls", {}).run
|
||||
end
|
||||
|
||||
def test_fails_for_bad_command
|
||||
assert_raises(Errno::ENOENT) do
|
||||
ExecCommand.from_str('boom', {}).run
|
||||
end
|
||||
assert_raises(Errno::ENOENT) { ExecCommand.from_str("boom", {}).run }
|
||||
end
|
||||
|
||||
def test_backgroud_task_do_not_fail
|
||||
cmd = ExecCommand.new({})
|
||||
cmd.background = true
|
||||
cmd.add('sleep 10 && exit 1')
|
||||
cmd.add("sleep 10 && exit 1")
|
||||
cmd.run
|
||||
end
|
||||
|
||||
def test_raise_on_fail
|
||||
cmd = ExecCommand.new({})
|
||||
cmd.add('chgrp -a')
|
||||
cmd.add("chgrp -a")
|
||||
cmd.raise_on_fail = false
|
||||
cmd.run
|
||||
end
|
||||
|
@ -67,29 +64,27 @@ module Pups
|
|||
def test_stdin
|
||||
`touch test_file`
|
||||
cmd = ExecCommand.new({})
|
||||
cmd.add('read test ; echo $test > test_file')
|
||||
cmd.stdin = 'hello'
|
||||
cmd.add("read test ; echo $test > test_file")
|
||||
cmd.stdin = "hello"
|
||||
cmd.run
|
||||
|
||||
assert_equal("hello\n", File.read('test_file'))
|
||||
assert_equal("hello\n", File.read("test_file"))
|
||||
ensure
|
||||
File.delete('test_file')
|
||||
File.delete("test_file")
|
||||
end
|
||||
|
||||
def test_fails_for_non_zero_exit
|
||||
assert_raises(Pups::ExecError) do
|
||||
ExecCommand.from_str('chgrp -a', {}).run
|
||||
ExecCommand.from_str("chgrp -a", {}).run
|
||||
end
|
||||
end
|
||||
|
||||
def test_can_terminate_async
|
||||
cmd = ExecCommand.new({})
|
||||
cmd.background = true
|
||||
pid = cmd.spawn('sleep 10 && exit 1')
|
||||
pid = cmd.spawn("sleep 10 && exit 1")
|
||||
ExecCommand.terminate_async
|
||||
assert_raises(Errno::ECHILD) do
|
||||
Process.waitpid(pid, Process::WNOHANG)
|
||||
end
|
||||
assert_raises(Errno::ECHILD) { Process.waitpid(pid, Process::WNOHANG) }
|
||||
end
|
||||
|
||||
def test_can_terminate_rogues
|
||||
|
@ -101,9 +96,7 @@ module Pups
|
|||
|
||||
ExecCommand.terminate_async(wait: 0.1)
|
||||
|
||||
assert_raises(Errno::ECHILD) do
|
||||
Process.waitpid(pid, Process::WNOHANG)
|
||||
end
|
||||
assert_raises(Errno::ECHILD) { Process.waitpid(pid, Process::WNOHANG) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,23 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
require 'tempfile'
|
||||
require "test_helper"
|
||||
require "tempfile"
|
||||
|
||||
module Pups
|
||||
class FileCommandTest < MiniTest::Test
|
||||
class FileCommandTest < ::Minitest::Test
|
||||
def test_simple_file_creation
|
||||
tmp = Tempfile.new('test')
|
||||
tmp.write('x')
|
||||
tmp = Tempfile.new("test")
|
||||
tmp.write("x")
|
||||
tmp.close
|
||||
|
||||
cmd = FileCommand.new
|
||||
cmd.path = tmp.path
|
||||
cmd.contents = 'hello $world'
|
||||
cmd.params = { 'world' => 'world' }
|
||||
cmd.contents = "hello $world"
|
||||
cmd.params = { "world" => "world" }
|
||||
cmd.run
|
||||
|
||||
assert_equal('hello world',
|
||||
File.read(tmp.path))
|
||||
assert_equal("hello world", File.read(tmp.path))
|
||||
ensure
|
||||
tmp.close
|
||||
tmp.unlink
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
require 'tempfile'
|
||||
require "test_helper"
|
||||
require "tempfile"
|
||||
|
||||
module Pups
|
||||
class MergeCommandTest < MiniTest::Test
|
||||
class MergeCommandTest < ::Minitest::Test
|
||||
def test_deep_merge_arrays
|
||||
a = { a: { a: ['hi', 1] } }
|
||||
b = { a: { a: ['hi', 2] } }
|
||||
a = { a: { a: ["hi", 1] } }
|
||||
b = { a: { a: ["hi", 2] } }
|
||||
c = { a: {} }
|
||||
|
||||
d = Pups::MergeCommand.deep_merge(a, b, :merge_arrays)
|
||||
d = Pups::MergeCommand.deep_merge(d, c, :merge_arrays)
|
||||
|
||||
assert_equal(['hi', 1, 'hi', 2], d[:a][:a])
|
||||
assert_equal(["hi", 1, "hi", 2], d[:a][:a])
|
||||
end
|
||||
|
||||
def test_merges
|
||||
|
@ -23,7 +23,7 @@ module Pups
|
|||
password: "xyz"
|
||||
YAML
|
||||
|
||||
f = Tempfile.new('test')
|
||||
f = Tempfile.new("test")
|
||||
f.write source
|
||||
f.close
|
||||
|
||||
|
@ -32,24 +32,27 @@ module Pups
|
|||
name: "bob2"
|
||||
YAML
|
||||
|
||||
MergeCommand.from_str("#{f.path} $yaml", { 'yaml' => YAML.safe_load(merge) }).run
|
||||
MergeCommand.from_str(
|
||||
"#{f.path} $yaml",
|
||||
{ "yaml" => YAML.safe_load(merge) }
|
||||
).run
|
||||
|
||||
changed = YAML.load_file(f.path)
|
||||
|
||||
assert_equal({ 'user' => {
|
||||
'name' => 'bob2',
|
||||
'password' => 'xyz'
|
||||
} }, changed)
|
||||
assert_equal(
|
||||
{ "user" => { "name" => "bob2", "password" => "xyz" } },
|
||||
changed
|
||||
)
|
||||
|
||||
def test_deep_merge_nil
|
||||
a = { param: { venison: 'yes please' } }
|
||||
a = { param: { venison: "yes please" } }
|
||||
b = { param: nil }
|
||||
|
||||
r1 = Pups::MergeCommand.deep_merge(a, b)
|
||||
r2 = Pups::MergeCommand.deep_merge(b, a)
|
||||
|
||||
assert_equal({ venison: 'yes please' }, r1[:param])
|
||||
assert_equal({ venison: 'yes please' }, r2[:param])
|
||||
assert_equal({ venison: "yes please" }, r1[:param])
|
||||
assert_equal({ venison: "yes please" }, r2[:param])
|
||||
end
|
||||
ensure
|
||||
f.unlink
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'test_helper'
|
||||
require 'tempfile'
|
||||
require "test_helper"
|
||||
require "tempfile"
|
||||
|
||||
module Pups
|
||||
class ReplaceCommandTest < MiniTest::Test
|
||||
class ReplaceCommandTest < ::Minitest::Test
|
||||
def test_simple
|
||||
command = ReplaceCommand.new({})
|
||||
command.text = 'hello world'
|
||||
command.text = "hello world"
|
||||
command.from = /he[^o]+o/
|
||||
command.to = 'world'
|
||||
command.to = "world"
|
||||
|
||||
assert_equal('world world', command.replaced_text)
|
||||
assert_equal("world world", command.replaced_text)
|
||||
end
|
||||
|
||||
def test_reverse
|
||||
|
@ -21,20 +21,23 @@ module Pups
|
|||
1 one thousand 1
|
||||
SCR
|
||||
|
||||
f = Tempfile.new('test')
|
||||
f = Tempfile.new("test")
|
||||
f.write source
|
||||
f.close
|
||||
|
||||
hash = {
|
||||
'filename' => f.path,
|
||||
'from' => '/one t.*d/',
|
||||
'to' => 'hello world',
|
||||
'direction' => 'reverse'
|
||||
"filename" => f.path,
|
||||
"from" => "/one t.*d/",
|
||||
"to" => "hello world",
|
||||
"direction" => "reverse"
|
||||
}
|
||||
|
||||
command = ReplaceCommand.from_hash(hash, {})
|
||||
|
||||
assert_equal("1 one thousand 1\n1 one thousand 1\n1 hello world 1\n", command.replaced_text)
|
||||
assert_equal(
|
||||
"1 one thousand 1\n1 one thousand 1\n1 hello world 1\n",
|
||||
command.replaced_text
|
||||
)
|
||||
ensure
|
||||
f.unlink
|
||||
end
|
||||
|
@ -46,15 +49,15 @@ module Pups
|
|||
one
|
||||
SCR
|
||||
|
||||
f = Tempfile.new('test')
|
||||
f = Tempfile.new("test")
|
||||
f.write source
|
||||
f.close
|
||||
|
||||
hash = {
|
||||
'filename' => f.path,
|
||||
'from' => '/one/',
|
||||
'to' => 'two',
|
||||
'global' => 'true'
|
||||
"filename" => f.path,
|
||||
"from" => "/one/",
|
||||
"to" => "two",
|
||||
"global" => "true"
|
||||
}
|
||||
|
||||
command = ReplaceCommand.from_hash(hash, {})
|
||||
|
@ -65,20 +68,16 @@ module Pups
|
|||
end
|
||||
|
||||
def test_replace_with_env
|
||||
source = '123'
|
||||
source = "123"
|
||||
|
||||
f = Tempfile.new('test')
|
||||
f = Tempfile.new("test")
|
||||
f.write source
|
||||
f.close
|
||||
|
||||
hash = {
|
||||
'filename' => f.path,
|
||||
'from' => '123',
|
||||
'to' => 'hello $hellos'
|
||||
}
|
||||
hash = { "filename" => f.path, "from" => "123", "to" => "hello $hellos" }
|
||||
|
||||
command = ReplaceCommand.from_hash(hash, { 'hello' => 'world' })
|
||||
assert_equal('hello worlds', command.replaced_text)
|
||||
command = ReplaceCommand.from_hash(hash, { "hello" => "world" })
|
||||
assert_equal("hello worlds", command.replaced_text)
|
||||
ensure
|
||||
f.unlink
|
||||
end
|
||||
|
@ -90,19 +89,19 @@ module Pups
|
|||
}
|
||||
SCR
|
||||
|
||||
f = Tempfile.new('test')
|
||||
f = Tempfile.new("test")
|
||||
f.write source
|
||||
f.close
|
||||
|
||||
hash = {
|
||||
'filename' => f.path,
|
||||
'from' => "/this[^\}]+\}/m",
|
||||
'to' => 'hello world'
|
||||
"filename" => f.path,
|
||||
"from" => "/this[^\}]+\}/m",
|
||||
"to" => "hello world"
|
||||
}
|
||||
|
||||
command = ReplaceCommand.from_hash(hash, {})
|
||||
|
||||
assert_equal('hello world', command.replaced_text.strip)
|
||||
assert_equal("hello world", command.replaced_text.strip)
|
||||
ensure
|
||||
f.unlink
|
||||
end
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'pups'
|
||||
require 'pups/cli'
|
||||
require 'minitest/autorun'
|
||||
require 'minitest/pride'
|
||||
require "pups"
|
||||
require "pups/cli"
|
||||
require "minitest/autorun"
|
||||
require "minitest/pride"
|
||||
|
|
Loading…
Reference in New Issue