Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FEATURE: add --tags and --skip-tags options #28

Merged
merged 6 commits into from
Oct 23, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
fail-fast: true
matrix:
os: [ubuntu-latest]
ruby: ["2.7"]
ruby: ["3.2"]

steps:
- uses: actions/checkout@v2
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
fail-fast: true
matrix:
os: [ubuntu-latest]
ruby: ["2.7"]
ruby: ["3.2"]

steps:
- uses: actions/checkout@v2
Expand Down
26 changes: 12 additions & 14 deletions lib/pups.rb
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -28,9 +28,7 @@ def self.log=(logger)
end

def self.silence
if @logger
@logger.close
end
@logger.close if @logger

@logger = Logger.new(File.open(File::NULL, "w"))
end
Expand Down
58 changes: 39 additions & 19 deletions lib/pups/cli.rb
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
# 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, "Filter tagged commands.")
opts.on("--skip-tags <tag(s)>", Array, "Skip tagged commands.")
opts.on("-h", "--help") do
puts opts
exit
end
Expand All @@ -26,35 +35,46 @@ def self.parse_args(args)

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: option[:"skip-tags"]
)
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"]
)
end

if options[:"gen-docker-run-args"]
Expand Down
6 changes: 4 additions & 2 deletions lib/pups/command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
135 changes: 98 additions & 37 deletions lib/pups/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,49 +4,86 @@ module Pups
class Config
attr_reader :config, :params

def initialize(config, ignored = nil)
def initialize(
config,
ignored = nil,
tags: tags = nil,
skip_tags: skip_tags = nil
)
@config = config

# remove any ignored config elements prior to any more processing
ignored&.each { |e| @config.delete(e) }

filter_tags(tags)
filter_tags(skip_tags, true)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sig on this method is a bit odd:

Maybe ...

filter_tags(include_tags: ... , exclude_tags: ... )

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated, it's a bit more verbose now but hopefully a little more readable


# 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
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: tags = nil,
skip_tags: skip_tags = nil
)
Config.new(
YAML.load_file(config_file),
ignored,
tags: tags,
skip_tags: skip_tags
)
rescue Exception
warn "Failed to parse #{config_file}"
warn "This is probably a formatting error in #{config_file}"
warn "Cannot continue. Edit #{config_file} and try again."
raise
end

def self.load_config(config, ignored = nil)
Config.new(YAML.safe_load(config), ignored)
def self.load_config(
config,
ignored = nil,
tags: tags = nil,
skip_tags: skip_tags = nil
)
Config.new(
YAML.safe_load(config),
ignored,
tags: tags,
skip_tags: skip_tags
)
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
Expand All @@ -70,18 +107,36 @@ def self.combine_template_and_process_env(config, env)
config["env"].each { |k, v| env[k] = v.to_s }
end

# 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(tags, skip_tags = false)
return unless tags
run = @config["run"]

@config["run"] = run.select do |row|
keep = false
command = row.first
if command[1].is_a?(Hash)
tag = command[1]["tag"]
keep = tags.include?(tag)
end
keep = !keep if skip_tags #skip_tags keeps everything NOT tagged
keep
end
end

def inject_hooks
return unless hooks = @config['hooks']
return unless hooks = @config["hooks"]

run = @config['run']
run = @config["run"]

positions = {}
run.each do |row|
next unless row.is_a?(Hash)

command = row.first
if command[1].is_a?(Hash)
hook = command[1]['hook']
hook = command[1]["hook"]
positions[hook] = row if hook
end
end
Expand Down Expand Up @@ -112,11 +167,11 @@ def inject_hooks

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

Expand All @@ -128,25 +183,33 @@ def run
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)
Expand All @@ -162,9 +225,7 @@ def self.interpolate_params(cmd, params)
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
Expand Down
Loading
Loading