Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
10 changes: 10 additions & 0 deletions .github/workflows/cbrain_ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,16 @@ jobs:
with:
ruby-version: 2.7.2

###########################################################
- name: Setup python
uses: actions/setup-python@v1

###########################################################
- name: Setup python package Boutiques to install 'bosh'
uses: BSFishy/pip-action@v1
with:
packages: boutiques

###########################################################
- name: Setup BrainPortal And Bourreau Names
run: |
Expand Down
9 changes: 0 additions & 9 deletions Bourreau/config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,6 @@ class Application < Rails::Application
# properly set up all tasks installed from plugins (and the defaults tasks).
config.autoload_paths += Dir["#{config.root}/cbrain_plugins/installed-plugins/cbrain_task"]

# CBRAIN Plugins load paths: add directory for descriptor-based CbrainTask
# models. This directory, similarly to the one above, contains symbolic
# links to a special loader code which will call a task generator to
# generate the requested CbrainTask subclass on the fly.
#
# The rake task cbrain:plugins:install:all also takes care of creating the
# symlinks for this location.
config.autoload_paths += Dir["#{config.root}/cbrain_plugins/installed-plugins/cbrain_task_descriptors"]

# Only load the plugins named here, in the order given (default is alphabetical).
# :all can be used as a placeholder for all plugins not explicitly named.
# config.plugins = [ :exception_notification, :ssl_requirement, :all ]
Expand Down
1 change: 1 addition & 0 deletions Bourreau/lib/boutiques.schema.json
1 change: 0 additions & 1 deletion Bourreau/lib/cbrain_task_generators

This file was deleted.

443 changes: 169 additions & 274 deletions Bourreau/spec/boutiques/boutiques_tester_spec.rb

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Bourreau/spec/rails_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
# DATABASE_URL=mysql2://prioux:mypassword@localhost/prioux_test
#
require 'yaml'
require 'uri'
1.times do # a block to encapsulate local variables and not pollute anything
env = ENV['RAILS_ENV']
dbconfig_file = "../BrainPortal/config/database.yml"
Expand Down
36 changes: 31 additions & 5 deletions BrainPortal/app/models/boutiques_cluster_task.rb
Original file line number Diff line number Diff line change
Expand Up @@ -118,22 +118,33 @@ def cluster_commands #:nodoc:
descriptor = self.descriptor_for_cluster_commands
invoke_struct = self.invoke_params.dup # as it is in the task's params

# Used when building absolute paths
workdir = Pathname.new(self.full_cluster_workdir)

# Replace userfile IDs for file basenames in the invoke struct
descriptor.file_inputs.each do |input|
userfile_id = invoke_params[input.id]
next if userfile_id.blank? # that happens when it's an optional file
userfile = Userfile.find(userfile_id)
userfiles = Array(Userfile.find(userfile_id)) # can be one or many
jsonfnames = userfiles.map(&:name) # by default, the basename
jsonfnames = jsonfnames.map { |base| (workdir + base).to_s } if input.uses_absolute_path

# Most common situation
if ! input.list || ! userfile.is_a?(CbrainFileList)
invoke_struct[input.id] = (input.list ? [ userfile.name ] : userfile.name)
if ! input.list || ! userfiles.first.is_a?(CbrainFileList)
invoke_struct[input.id] = (input.list ? jsonfnames : jsonfnames.first )
next
end

# In case the input is a list and is assigned a CbrainFileList
userfile = userfiles.first
cb_error "Expected a CbrainFileList as a single entry for input '#{input.name}'" if
userfiles.size > 1 || ! userfile.is_a?(CbrainFileList)
userfile.sync_to_cache
userfile_list = userfile.userfiles_accessible_by_user!(user, nil, nil, file_access_symbol)
subnames = userfile_list.compact.map(&:name) # [ 'userfilename1', 'userfilename2' ... ]
if input.uses_absolute_path
subnames = subnames.map { |base| (workdir + base).to_s }
end
invoke_struct[input.id] = subnames
end

Expand Down Expand Up @@ -174,10 +185,25 @@ def cluster_commands #:nodoc:
end
simul_status = $? # a Process::Status object
if ! simul_status.success?
cb_error "The 'bosh exec simulate' command failed with return code #{simul_status.exitstatus}"
bosh_message = simulout.split("\n").detect { |line| line =~ /ERROR/ }
bosh_message &&= ": #{bosh_message}"
cb_error "The 'bosh exec simulate' command failed with return code #{simul_status.exitstatus}#{bosh_message}"
end
simulout.sub!(/^Generated.*\n/,"") # header junk from simulate
commands = <<-COMMANDS

commands = ""
if descriptor.environment_variables.present?
commands = "# Environment variables defined by Boutiques descriptor\n"
descriptor.environment_variables.each do |struct|
ename = (struct['name'] || struct[:name] ).to_s
evalue = (struct['value'] || struct[:value]).to_s
next if ename.blank? || ! ename.match(/\A[a-zA-Z]\w+\z/)
commands += "export #{ename.strip}=#{evalue.bash_escape}\n"
end
commands += "\n"
end

commands += <<-COMMANDS
# Main tool command, generated with bosh exec simulate
#{simulout.strip}
status=$?
Expand Down
39 changes: 23 additions & 16 deletions BrainPortal/app/models/boutiques_portal_task.rb
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,12 @@ def before_form

# return "Warning: you selected more files than this task requires, so you won't be able to assign them all."
# Not available in case of descriptor qualified to launch multiple task
if !descriptor.qualified_to_launch_multiple_tasks? && (num_in_files < num_needed_inputs || num_in_files > num_needed_inputs+num_opt_inputs)
message = "This task requires #{num_needed_inputs} mandatory file(s) and #{num_opt_inputs} optional file(s)\n" +
input_infos
cb_error message
if num_in_files < num_needed_inputs || num_in_files > num_needed_inputs+num_opt_inputs
if (! descriptor.qualified_to_launch_multiple_tasks? || num_in_files == 0)
message = "This task requires #{num_needed_inputs} mandatory file(s) and #{num_opt_inputs} optional file(s)\n" +
input_infos
cb_error message
end
end

""
Expand Down Expand Up @@ -299,6 +301,7 @@ def final_task_list #:nodoc:
# --------------------------------------
if descriptor.file_inputs.size == 1 || descriptor.qualified_to_launch_multiple_tasks?
input = descriptor.file_inputs.first
input = descriptor.sole_mandatory_file_input if descriptor.qualified_to_launch_multiple_tasks?

fillTask = lambda do |userfile,tsk,extra_params=nil|
tsk.params[:interface_userfile_ids] |= [ userfile.id.to_s ]
Expand Down Expand Up @@ -445,6 +448,8 @@ def cbcsv_files(descriptor = self.descriptor_for_after_form)
next if isInactive(input)
userfile_id = invoke_params[input.id]
next if userfile_id.blank?
next if userfile_id.is_a?(Array) && userfile_id.size > 1 # list = true
userfile_id = userfile_id.first if userfile_id.is_a?(Array)
userfile = Userfile.find_accessible_by_user(userfile_id, self.user, :access_requested => file_access_symbol())
next unless ( userfile.is_a?(CbrainFileList) || (userfile.suggested_file_type || Object) <= CbrainFileList )
[ input, userfile ]
Expand Down Expand Up @@ -576,16 +581,18 @@ def sanitize_param(input)

# Nothing special required for strings, bar for symbols being acceptable strings.
when :string
value = value.to_s if value.is_a?(Symbol)
params_errors.add(invokename, " is not a string (#{value})") unless value.is_a?(String)
value.strip! if value.is_a?(String)
params_errors.add(invokename, " is blank") if value.blank? && !empty_string_allowed
# The following three checks are to prevent cases when
# a string param is used as a path
params_errors.add(invokename, " cannot contain newlines") if value =~ /[\n\r]/
params_errors.add(invokename, " cannot start with this character") if value =~ /^[\.\/]+/
params_errors.add(invokename, " cannot move up dirs") if value.include? "/../"
if value.present? && input.value_choices.blank?
value = value.to_s if value.is_a?(Symbol)
params_errors.add(invokename, " is not a string") unless value.is_a?(String)
value = value.to_s.strip # now force it
params_errors.add(invokename, " is blank") if value.blank? && !empty_string_allowed
# The following checks are to prevent cases when a string param is used as a path
if value.present?
params_errors.add(invokename, " cannot contain newlines") if value =~ /[\n\r]/
params_errors.add(invokename, " cannot start with this character") if value =~ /^[\.\/]+/
params_errors.add(invokename, " cannot move up dirs") if value.include? "/../"
end
# Finally, check allowed characters
if value.present? && input.value_choices.blank? # valid value choices are checked elsewhere
params_errors.add(invokename, " contains invalid characters") unless value.match?(charset_regex) # we can use a string in the match method
end

Expand Down Expand Up @@ -628,7 +635,7 @@ def sanitize_param(input)
end

def check_enum_param(input)
value = invoke_params[input.id]
value = invoke_params[input.id] || input.default_value
string_values = Array(value).map(&:to_s)
allowed_values = input.value_choices.map(&:to_s)
return if (string_values - allowed_values).empty? # I hope that comparing the sets as strings is OK
Expand Down Expand Up @@ -714,7 +721,7 @@ def check_allornone_group(group, descriptor = self.descriptor_for_after_form)
# MAYBE IN COMMON

def invoke_params
self.params[:invoke] ||= {}
self.params[:invoke] ||= {}.with_indifferent_access
end

# In the case of a misconfiguration of the portal, or if the file for
Expand Down
5 changes: 1 addition & 4 deletions BrainPortal/app/models/cbrain_task.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1109,12 +1109,9 @@ def struct_runtime_info(runtime_textfile=self.runtime_info) #:nodoc:

# Patch: pre-load all model files for the subclasses
def self.preload_subclasses
[ CBRAIN::TasksPlugins_Dir, CBRAIN::TaskDescriptorsPlugins_Dir ].each do |dir|
[ CBRAIN::TasksPlugins_Dir ].each do |dir|
Dir.chdir(dir) do
Dir.glob("*.rb").each do |rubyfile|
next if rubyfile == 'cbrain_task_class_loader.rb' # skip that
next if rubyfile == 'cbrain_task_descriptor_loader.rb' # skip that

model = rubyfile.sub(/.rb\z/, '')
require_dependency "#{dir}/#{model}.rb" unless
[ model.classify, model.camelize ].any? { |m| CbrainTask.const_defined?(m) rescue nil }
Expand Down
15 changes: 2 additions & 13 deletions BrainPortal/app/views/tasks/_params.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,10 @@

<%
locals = { :params => @task.params, :form => form }
raw_partial = lambda do |partial|
@task.raw_partial(partial) if @task.respond_to?(:raw_partial)
end
%>

<h2>Task Parameters
<% if raw_partial.(:edit_help) %>
<%= overlay_content_link "(Help)", :class => "task_help_link", :enclosing_element => "span" do %>
<%= render :inline => raw_partial.(:edit_help), :locals => locals %>
<% end %>
<% elsif (public_path = @task.public_path("edit_params_help.html")) %>
<% if (public_path = @task.public_path("edit_params_help.html")) %>
<%= overlay_ajax_link "(Help)", public_path.to_s, :class => "task_help_link" %>
<% elsif (edit_help_partial = task_partial(:edit_help) rescue nil) %>
<%= overlay_content_link "(Help)", :class => "task_help_link", :enclosing_element => "span" do %>
Expand All @@ -46,9 +39,5 @@

<div class="generalbox">
<%= error_messages_for(@task) %>
<% if raw_partial.(:task_params) %>
<%= render :inline => raw_partial.(:task_params), :locals => locals %>
<% else %>
<%= render :partial => task_partial('task_params'), :locals => locals %>
<% end %>
<%= render :partial => task_partial('task_params'), :locals => locals %>
</div>
9 changes: 1 addition & 8 deletions BrainPortal/app/views/tasks/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -203,15 +203,8 @@
<% begin %>
<%
locals = { :task => @task, :params => @task.params }
raw_partial = lambda do |partial|
@task.raw_partial(partial) if @task.respond_to?(:raw_partial)
end
%>
<% if raw_partial.(:show_params) %>
<%= render :inline => raw_partial.(:show_params), :locals => locals %>
<% else %>
<%= render :partial => task_partial(:show_params), :locals => locals %>
<% end %>
<%= render :partial => task_partial(:show_params), :locals => locals %>
<% rescue ActionView::MissingTemplate %>
<pre>Problem loading summary view (no template provided by task author).</pre>
<% rescue => ex %>
Expand Down

This file was deleted.

Loading
Loading