Skip to content

New plugins symlinks #1510

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

Merged
merged 3 commits into from
May 20, 2025
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
4 changes: 2 additions & 2 deletions Bourreau/config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ class Application < Rails::Application
config.eager_load_paths += Dir["#{config.root}/lib"]
config.eager_load_paths += Dir["#{config.root}/lib/cbrain_task_generators"]

# CBRAIN Plugins load paths: add directories for each Userfile model
config.eager_load_paths += Dir[ "#{config.root}/cbrain_plugins/installed-plugins/userfiles/*" ]
# CBRAIN Plugins load paths: where userfiles defined by plugins are located (as links)
config.eager_load_paths += Dir[ "#{config.root}/cbrain_plugins/installed-plugins/userfiles" ]

# CBRAIN Plugins load paths: add lib directory for standalone Ruby files
config.eager_load_paths += Dir["#{config.root}/cbrain_plugins/installed-plugins/lib"]
Expand Down
2 changes: 0 additions & 2 deletions Bourreau/lib/bourreau_system_checks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,8 @@ def self.puts(*args) #:nodoc:
def self.a000_ensure_models_are_preloaded #:nodoc:
# There's a piece of code at the end of each of these models
# which forces the pre-load of all their subclasses.
Userfile.nil?
ClusterTask.nil? # not PortalTask, which is only on the BrainPortal rails app
BackgroundActivity.nil?
Userfile.preload_subclasses
ClusterTask.preload_subclasses
BackgroundActivity.preload_subclasses
end
Expand Down
24 changes: 1 addition & 23 deletions BrainPortal/app/models/userfile.rb
Original file line number Diff line number Diff line change
Expand Up @@ -860,7 +860,7 @@ def public_path(public_file=nil)
#
# Returns a Pathname object.
def self.view_path(partial_name=nil)
base = Pathname.new(CBRAIN::UserfilesPlugins_Dir) + self.to_s.underscore + "views"
base = Pathname.new(CBRAIN::ViewsPlugins_Dir) + self.to_s.underscore
return base if partial_name.to_s.blank?
partial_name = Pathname.new(partial_name.to_s).cleanpath
raise "View partial path outside of userfile plugin." if partial_name.absolute? || partial_name.to_s =~ /\A\.\./
Expand Down Expand Up @@ -1188,27 +1188,5 @@ def check_exceeded_quota!
true
end



##################################################################
# BOOT-TIME Support
##################################################################

public

# Patch: pre-load all model files for the subclasses
def self.preload_subclasses
Dir.chdir(CBRAIN::UserfilesPlugins_Dir) do
Dir.glob("*").select { |dir| File.directory?(dir) }.each do |model_dir|
model_file = "#{model_dir}/#{model_dir}.rb" # e.g. "mp3_file/mp3_file.rb" for class Mp3File
next unless File.file?(model_file)
model = model_dir.classify
next if Object.const_defined? model # already loaded? Skip.
#puts_blue "Loading Userfile subclass #{model} from #{model_file} ..."
require_dependency "#{CBRAIN::UserfilesPlugins_Dir}/#{model_file}"
end
end
end

end

2 changes: 2 additions & 0 deletions BrainPortal/cbrain_plugins/installed-plugins/views/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*
!.gitignore
4 changes: 2 additions & 2 deletions BrainPortal/config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ class Application < Rails::Application
config.eager_load_paths += Dir["#{config.root}/lib"]
config.eager_load_paths += Dir["#{config.root}/lib/cbrain_task_generators"]

# CBRAIN Plugins load paths: add directories for each Userfile model
config.eager_load_paths += Dir[ "#{config.root}/cbrain_plugins/installed-plugins/userfiles/*" ]
# CBRAIN Plugins load paths: where userfiles defined by plugins are located (as links)
config.eager_load_paths += Dir[ "#{config.root}/cbrain_plugins/installed-plugins/userfiles" ]

# CBRAIN Plugins load paths: add lib directory for standalone Ruby files
config.eager_load_paths += Dir["#{config.root}/cbrain_plugins/installed-plugins/lib"]
Expand Down
1 change: 1 addition & 0 deletions BrainPortal/config/initializers/cbrain.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class CBRAIN
Plugins_Dir = "#{Rails.root.to_s}/cbrain_plugins"
UserfilesPlugins_Dir = "#{Plugins_Dir}/installed-plugins/userfiles"
TasksPlugins_Dir = "#{Plugins_Dir}/installed-plugins/cbrain_task" # singular; historical
ViewsPlugins_Dir = "#{Plugins_Dir}/installed-plugins/views"

# Original integrator
TaskDescriptorsPlugins_Dir = "#{Plugins_Dir}/installed-plugins/cbrain_task_descriptors"
Expand Down
2 changes: 0 additions & 2 deletions BrainPortal/lib/portal_system_checks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,8 @@ def self.puts(*args) #:nodoc:
def self.a000_ensure_models_are_preloaded #:nodoc:
# There's a piece of code at the end of each of these models
# which forces the pre-load of all their subclasses.
Userfile.nil?
PortalTask.nil? # not ClusterTask, which is only on the Bourreau rails app
BackgroundActivity.nil?
Userfile.preload_subclasses
PortalTask.preload_subclasses
BackgroundActivity.preload_subclasses
end
Expand Down
74 changes: 48 additions & 26 deletions BrainPortal/lib/tasks/cbrain_plugins.rake
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@
namespace :cbrain do
namespace :plugins do

verbose = false # TODO make a command-line param?
verbose = ENV['CBRAIN_RAKE_VERBOSE'].present? # TODO make a command-line param?

# Unfortunately we don't have access to cbrain.rb where some useful constants are defined in the
# CBRAIN class, such as CBRAIN::TasksPlugins_Dir ; if we ever change where plugins are stored, we
# have to update this here and the cbrain.rb file too.
plugins_dir = Rails.root + "cbrain_plugins"
installed_plugins_dir = plugins_dir + "installed-plugins"
userfiles_plugins_dir = installed_plugins_dir + "userfiles"
views_plugins_dir = installed_plugins_dir + "views"
tasks_plugins_dir = installed_plugins_dir + "cbrain_task"
descriptors_plugins_dir = installed_plugins_dir + "cbrain_task_descriptors"
boutiques_plugins_dir = installed_plugins_dir + "boutiques_descriptors"
Expand Down Expand Up @@ -83,12 +84,12 @@ namespace :cbrain do
Dir.chdir(package) do

# Setup a single unit (userfiles, tasks or descriptors)
setup = lambda do |glob, name, directory, condition: nil, after: nil|
files = Dir.glob(glob)
files.select!(&condition) if condition
puts "Found #{files.size} #{name}(s) to set up..." if verbose
files.each do |u_slash_f|
plugin = Pathname.new(u_slash_f).basename.to_s
setup = lambda do |glob, name, directory, condition: nil, linkname: nil, after: nil|
entries = Dir.glob(glob)
entries.select!(&condition) if condition
puts "Found #{entries.size} #{name}(s) to set up..." if verbose
entries.each do |u_slash_f|
plugin = linkname ? linkname.(u_slash_f) : Pathname.new(u_slash_f).basename.to_s
symlink_location = directory + plugin
plugin_location = plugins_dir + package + u_slash_f
symlink_value = plugin_location.relative_path_from(symlink_location.parent)
Expand Down Expand Up @@ -132,40 +133,48 @@ namespace :cbrain do
end

# Setup each userfile plugin
setup.('userfiles/*', 'userfile', userfiles_plugins_dir,
condition: lambda { |f| File.directory?(f) }
)
erase_dead_symlinks.('userfile', userfiles_plugins_dir)
setup.('userfiles/*/*.rb', 'userfile', userfiles_plugins_dir,
condition: lambda { |f| File.file?(f) }
)

# Setup the views of each userfile
if Rails.root.to_s =~ /\/BrainPortal$/ # not needed on Bourreaux
erase_dead_symlinks.('views', views_plugins_dir)
setup.('userfiles/*/views', 'views', views_plugins_dir,
linkname: lambda { |f| Pathname.new(f).parent.basename.to_s }
)
end

# Setup each cbrain_task plugin
erase_dead_symlinks.('task', tasks_plugins_dir)
setup.('cbrain_task/*', 'task', tasks_plugins_dir,
condition: lambda { |f| File.directory?(f) },
after: lambda do |symlink_location|
File.symlink "cbrain_task_class_loader.rb", "#{symlink_location}.rb"
end
)
erase_dead_symlinks.('task', tasks_plugins_dir)

# Setup each cbrain_task descriptor plugin
erase_dead_symlinks.('descriptor', descriptors_plugins_dir)
setup.('cbrain_task_descriptors/*', 'descriptor', descriptors_plugins_dir,
condition: lambda { |f| File.extname(f) == '.json' },
after: lambda do |symlink_location|
File.symlink "cbrain_task_descriptor_loader.rb", "#{symlink_location.sub(/.json$/, '.rb')}"
end
)
erase_dead_symlinks.('descriptor', descriptors_plugins_dir)

# Setup each boutiques descriptor plugin (new integrator)
erase_dead_symlinks.('boutiques', boutiques_plugins_dir)
setup.('boutiques_descriptors/*', 'boutiques', boutiques_plugins_dir,
condition: lambda { |f| File.extname(f) == '.json' },
)
erase_dead_symlinks.('boutiques', boutiques_plugins_dir)

# Setup each ruby lib file
erase_dead_symlinks.('lib', lib_plugins_dir)
setup.('lib/*', 'lib', lib_plugins_dir,
condition: lambda { |f| File.extname(f) == '.rb' },
)
erase_dead_symlinks.('lib', lib_plugins_dir)

end # chdir package
end # each package
Expand All @@ -190,20 +199,26 @@ namespace :cbrain do
puts "Adjusting paths to public assets for tasks and userfiles..." if verbose

Dir.chdir(public_userfiles) do
userfiles_public_dirs = Dir.glob(userfiles_plugins_dir + "*/views/public")
userfiles_public_dirs = Dir.glob(views_plugins_dir + "*/public")
if userfiles_public_dirs.empty?
puts "No public assets made available by any userfiles." if verbose
else
puts "Found #{userfiles_public_dirs.size} userfile(s) with public assets to set up..." if verbose
end

userfiles_public_dirs.each do |fullpath| # "/a/b/rails/cbrain_plugins/installed-plugins/userfiles/text_file/views/public"
relpath = Pathname.new(fullpath).relative_path_from(public_tasks) # ../(...)/cbrain_plugins/installed-plugins/userfiles/text_file/views/public
filename = relpath.parent.parent.basename # "text_file"
if File.exists?(filename)
puts "-> Assets for userfile already set up: '#{filename}'." if verbose
logger.('AssetSymlinkIsOk','(installed)','userfile',filename) if verbose
next
userfiles_public_dirs.each do |fullpath| # "/a/b/rails/cbrain_plugins/installed-plugins/views/text_file/public"
relpath = Pathname.new(fullpath).relative_path_from(public_userfiles) # ../(...)/cbrain_plugins/installed-plugins/views/text_file/public
filename = relpath.parent.basename # "text_file"
if File.exists?(filename) || File.symlink?(filename)
if File.symlink?(filename) && (File.readlink(filename) == relpath.to_s)
puts "-> Assets for userfile already set up: '#{filename}'." if verbose
logger.('AssetSymlinkIsOk','(installed)','userfile',filename) if verbose
next
else
puts "-> Something is in the way for assets for userfile: '#{filename}'." if verbose
logger.('AssetSymlinkBad','(installed)','userfile',filename) if verbose
File.unlink(filename) rescue true # let's try to cleanup.. if that fails, and exception will happen later during symlink
end
end
puts "-> Creating assets symbolic link for userfile '#{filename}'." if verbose
logger.('MakeAssetSymlink','(installed)','userfile',filename)
Expand All @@ -222,10 +237,16 @@ namespace :cbrain do
tasks_public_dirs.each do |fullpath| # "/a/b/rails/cbrain_plugins/installed-plugins/cbrain_tasks/diagnostics/views/public"
relpath = Pathname.new(fullpath).relative_path_from(public_tasks) # ../(...)/cbrain_plugins/cbrain_tasks/diagnostics/views/public
taskname = relpath.parent.parent.basename # "diagnostics"
if File.exists?(taskname)
puts "-> Assets for task already set up: '#{taskname}'." if verbose
logger.('AssetSymlinkIsOk','(installed)','task',taskname) if verbose
next
if File.exists?(taskname) || File.symlink?(taskname)
if File.symlink?(taskname) && (File.readlink(taskname) == relpath.to_s)
puts "-> Assets for task already set up: '#{taskname}'." if verbose
logger.('AssetSymlinkIsOk','(installed)','task',taskname) if verbose
next
else
puts "-> Something is in the way for assets for tasks: '#{taskname}'." if verbose
logger.('AssetSymlinkBad','(installed)','task',taskname) if verbose
File.unlink(taskname) rescue true # let's try to cleanup.. if that fails, and exception will happen later during symlink
end
end
puts "-> Creating assets symbolic link for task '#{taskname}'." if verbose
logger.('MakeAssetSymlink','(installed)','task',taskname)
Expand Down Expand Up @@ -299,6 +320,7 @@ namespace :cbrain do
end

erase.('userfile', userfiles_plugins_dir)
erase.('views', views_plugins_dir)
erase.('task', tasks_plugins_dir)
erase.('descriptor', descriptors_plugins_dir)
erase.('boutiques', boutiques_plugins_dir)
Expand Down