Compare commits

...

8 Commits
v1.1.0 ... main

Author SHA1 Message Date
Jeff Wong 332921814b
Version bump (#34)
* version bump for 1.3.0 release

* update changelog
2025-07-08 17:19:03 -07:00
Jeff Wong f91de7519a
FEATURE: add a --params option (#33)
* FEATURE: add a --params option

Add a --params option that dynamically adds or modifies params of a pups run

usage:

`pups --params foo=bar,baz=other config.yml`

This would dynamically set or override any params set in config.yml for the run.

DEV: linting, and rubocop
update ruby and checkout actions
2025-07-08 08:07:10 -07:00
Jeff Wong 078edb6407
DEV: fix readme (#31)
tags and skip tags should go under --ignore details
2023-10-24 09:22:05 -07:00
Jeff Wong 5436aec99e
FIX: skip tags (#30)
fix skip-tags options typo
2023-10-23 12:27:42 -07:00
Jeff Wong 1276ccd44d
FIX: github actions, add auto gem publish (#29)
* FIX: github actions, add auto gem publish

adds auto publish to ruby gems on push, update auto push branch to main.

* DEV: move linting to CI workflows, rename workflow from test to CI

* DEV: update readme copy
2023-10-23 08:15:34 -07:00
Jeff Wong d51da1fc1d
FEATURE: add --tags and --skip-tags options (#28)
* FEATURE: add --tags and --skip-tags options

Allow config manifests to be tagged, so a pups run can apply a subset of run commands.

Update to ruby 3.2.
Lots of linting. Added rubocop lint exception for Eval.
Fixing test imports, update MiniTest::Test -> Minitest::Test.
2023-10-22 17:42:24 -07:00
Michael Fitz-Payne e0ff889553 1.1.1: bump new version to include bugfix. 2021-06-17 11:16:21 +10:00
Michael Fitz-Payne 3f02d19ad1
FIX: Remove quotes from escaped strings. (#27)
When strings have been escaped and contain a space, we don't want to
quote them because the escape character is then treated as a literal.

For example:
```
$ export var1="hello there"
$ export var2="hello\ there"
$ export var3=hello\ there
$ printf "$var1\n$var2\n$var3\n"
hello there
hello\ there
hello there
```
2021-06-17 10:43:44 +10:00
25 changed files with 706 additions and 342 deletions

View File

@ -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

View File

@ -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

View File

@ -1,3 +1,2 @@
inherit_gem:
rubocop-discourse: default.yml

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"]

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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}")

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,5 +1,5 @@
# frozen_string_literal: true
module Pups
VERSION = '1.1.0'
VERSION = "1.3.0"
end

View File

@ -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|

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"