Capistrano 3 / SSHKit write to a file in custom task - ruby

I want mark the current deployed directory with my release number.
I tried this approach:
Get locally the app version, store it into a variable, and on the remote host, store it in a file.
namespace :deploy do
desc "Set a release number as the app version"
task :mark_release do
release_number = `git describe`
on roles(:web) do
execute("echo #{release_number} > #{current_path}/RELEASE")
end
end
end
The problem is, when I run this via:
cap deploy:mark_release
the command look like this:
echo v9.3.0-254-g178d1f8; > /foo/bar/current/RELEASE
The semicolon is making trouble. and my RELEASE file is of course empty.
I think it is due to some escaping made by SSHKit.
Any clues ?

I managed it:
1) I took the release number from the repo directory on the machine
2) I wrote it with a stream to a file via the upload! method
namespace :deploy do
desc "Set a release number as the app version"
task :mark_release do
on roles(:web) do
within "/foo/bar/repo/" do
upload! StringIO.new(capture(:git, "describe")), "#{current_path}/RELEASE"
end
end
end
end

Here is the solution that I came up with which doesn't require uploading a local file. It goes to the repo path to execute the git command to extract the version and then redirects the output to file. The file can then be read by the Rails app. The execute requires the different parameters to be passed in separately. https://github.com/capistrano/sshkit#the-command-map has more info about the command map and why it's needed, due to the problem of escaping and whitespace.
namespace :deploy do
before :restart, :add_revision_file
task :add_revision_file do
on roles(:app) do
within repo_path do
execute(:git, :'rev-parse', :'--short', :'HEAD', ">#{release_path}/REVISION")
end
end
end
end

Use SSHKit::Command
SSHKit::Command.new("echo #{release_number} > #{current_path}/RELEASE")

Related

Rake task selectivly ignoring new code

In a rake task I'm writing some puts statements show changes while others don't. For instance changing
puts model+" | "+id
into
puts model+" * "+id
doesn't change in the output of the script. However in some places changing
puts "Connecting to "+site
into
puts "Connecting to ----"+site
shows the changes that where made.
In the places where any changes to the line doesn't change the output, adding a new puts statement before or after don't show up when the task is run. Commenting out lines of code around the unchanging puts statements that do the actual work cause the script to not execute those lines, just as it should, but changing or adding puts statements there do not change the output of the script.
Removing all other tasks and emacs backup files from the lib/tasks folder doesn't help. I've been bitten before by having a backup copy of a task with the same namespace and task name running instead of the one I was working on.
This is being run with Ruby 2.4.3 on OpenBSD 6.3-stable on a fx-8350. I would post the whole script but the company I'm working for won't allow it.
How about
puts "#{model} +/*/whatever #{site}"
It shouldn't matter to what sounds like a filesystem update issue (reboot), but it's probably better form to put the variables in the string like that instead of + "" them.

Capistrano Ruby: Refactoring Reused String Values

I have a couple tasks within my deploy.rb file that execute within my theme folder.
I currently define them as follows:
namespace :deploy do
desc 'NPM Build Production'
task :npm_build_production do
on roles(:app) do
within "#{release_path}/web/app/themes/example" do
execute :npm, "install --silent --no-progress"
execute :npm, "run build:production"
end;
end
end
end
before 'deploy:updated', 'deploy:npm_build_production'
Everything is working correctly with this implementation, but since there are multiple instances where I use this path, I'd like to extract it to a symbol or variable. I am new to Ruby and am running into some issues.
I have tried using the code below, but for some reason the path ends up being wrong when executed in my tasks.
set :theme_path, -> { "#{release_path}/web/app/themes/example" }
These variables are set on different stages of implementation. for example the release_path variable is not defined in the setting variables section. Move your variable to your execution block for tasking and it will work . for example this snipped will work
namespace :deploy do
desc 'NPM Build Production'
task :npm_build_production do
on roles(:app) do
theme_path = "#{release_path}/web/app/themes/example"
within theme_path do
execute :npm, "install --silent --no-progress"
execute :npm, "run build:production"
end;
end
end
end
also you will be able to fetch some variables using this way while setting other variables like fetch(:varible_name) but I am not sure it is feasible for the release_path since it is a scoped variable
here is how you could do it with a function
def theme_path(release_path)
"#{release_path}/web/app/themes/example"
end
just call this function with your release_path variable and you will have the themepath wherever you want. inside your deployment logic.

How to pass command line parameters to capistrano

I am using Capistrano v2.9.0.
I run this command:
cap deploy:tryout -S testvar=thing
and my deploy.rb contains this:
namespace :deploy do
task :tryout do
if defined? testvar
puts "param: #{testvar}\n"
else
puts "no branch!\n"
end
end
end
The output is "no branch!". How do I pass values from the command line? I tried looking into the code, and I can see options.rb where it adds the passed parameter to options[:pre_vars], but that seems to be an instance variable, and I can't figure out how to access it from my deploy script.
Solution:
The options can be accessed via #parent.variables hash, so if the command line string is testvar=thing, then #parent.variables[:testvar] has the value string.
This seems really ugly and hacky, but it works.
Edit:
Turns out it is also available locally via variables[:testvar]

Why does my system call work in the irb but not in a rake task?

I try to run system "./do_backup.py" within my rake task, where do_backup.py is a python program in the same directory, but the command fails silently. I can run it using system "./do_backup.py", however.
I called system "whoami" from both within my rake task and the IRB, and the users are the same.
EDIT
Here's the rakefile:
desc "Back up a file onto S3"
task :backup do
S3ID = "AKIAJM3NRWxxx"
S3KEY = "0A5kuzV+E1dkaPjxxx"
SRCBUCKET = "rose-test-4"
NUM_BACKUP_BUCKETS = 2
system "whoami"
system "./do_backup.py #{S3ID} #{S3KEY} #{SRCBUCKET} #{NUM_BACKUP_BUCKETS}"
end
As discussed in the comments, this was being run from the wrong directory. You can change directories with Dir.chdir(). For example:
...
Dir.chdir("lib/tasks")
system "./do_backup.py #{S3ID} #{S3KEY} #{SRCBUCKET} #{NUM_BACKUP_BUCKETS}"
end
Of course, as #muistooshort points out, the simpler thing is to just run it from where it is:
system "#{Rails.root}/lib/tasks/do_backup.py #{S3ID} #{S3KEY} #{SRCBUCKET} #{NUM_BACKUP_BUCKETS}"

Running files in a directory recursively using ruby

I'm working on script right now which has to run each ruby script in a directory and its subfolders.
e.g.
run-all.rb
- scripts
- folder1
- script1.rb
- script2.rb
- folder2
- script3.rb
- script4.rb
As the server is a Windows server I would normally use a batch file but the head dev insists everything must be done in ruby as some members have Macs and may not understand Windows Batch Files.
As the question may have given away, my knowledge of Ruby is very basic.
Depends what you mean by "run". To just execute the code that is in each script within the same ruby process, this will do the trick:
Dir["scripts/**/*.rb"].each{|s| load s }
But it you want to run each script in it's own ruby process, then try this:
Dir["scripts/**/*.rb"].each{|s| puts `ruby #{s}` }
Just put the either of these in the contents of run-all.rb and the run ruby run-all.rb form the command line.
Something like this should probably work:
def process_directory(basedir)
puts basedir
Find.find(basedir.chomp) do |path|
if FileTest.directory?(path)
if File.basename(path)[0] == ?.
Find.prune # Don't look any further into this directory.
else
next
end
else
puts path
end
end

Resources