Moving rails apps to coolify
Freitag, 03. Januar 2025, 08:46 Uhr | roberto@vasquez-angel.de |Use Dockerfiles as Nixpacks bug out after install ruby (2025-01-03).
My assets don’t get served
Add the env var RAILS_SERVE_STATIC_FILES=true
Use Dockerfiles as Nixpacks bug out after install ruby (2025-01-03).
Add the env var RAILS_SERVE_STATIC_FILES=true
If webpack build fails with ERR_OSSL_EVP_UNSUPPORTED on node > 16 try exporting following variable before running rails:
export NODE_OPTIONS=--openssl-legacy-provider
Go to the s3 storage, locate the dump and download it. Let’s assume the file is called dump.tgz.gpg
Decrypt the dump using the passphrase/key you have safely stored (i.e. in a password manager):
#> gpg --pinentry-mode=loopback --passphrase "<passphrase>" -d -o dump.tgz dump.tgz.gpg
Create a directory as unpacking target:
#> mkdir dump
Unpack the dump:
#> tar zxvf dump.tgz -C dump
Drop the current database and recreate it, so it is empty. If you are using rails you can use following commands:
#> rails db:drop && rails db:create
Restore the dump to your local postgres database:
#> pg_restore -U <username> -d <database_name> --no-owner < dump/backup/export
Resque | ActiveJob | |
Object | PORO | Inherits from ActiveJob::Base |
Entry method | def self.perform | def perform |
Invocation | MyJob.perform | MyJob.perform_now |
Queueing | @queue = :critical | queue_as :critical |
ActiveJob uses the instance method #perform as entry point for the job. So when you are migrating from resque and your job reference other methods in the job, you have to change them from def self.foo to def foo. You now have the advantage that you are working on an instance. So you can have instance methods, variables, etc.
Assume you have built a bunch of services or any other activemodel/activerecord descendant classes. You decide to add translations to your app. Then you realize that you will have to add the translations to you yaml files. First thing that comes into your mind: I’ll make a list of all attributes of those services. The idea is good, but not perfect. You might validate getter methods and not only attributes. So simply listing the attributes of all services will not cut it. What you want is to list all validated attributes/methods in all of your services. In our special case we narrowed it down to presence validations.
Following admittedly long line will list all of your Rao::Service::Base descendants that are in the namespace Blorgh” with all presences validated attributes as yaml, sorted, ready to cut and paste into your locale yaml files:
irb> y Rao::Service::Base.descendants.collect { |d| d.name.start_with?("Blorgh") ? d : nil }.compact.sort { |c, o| c.name <=> o.name }.each_with_object({}) { |k,m| m[k.name.underscore] = k.validators.collect { |v| v.class.name.include?("Presence") ? v.attributes : nil }.flatten.compact.sort.each_with_object({}) { |a,m| m[a] = nil } }
Do not forget to enable eager loading in your development env before doing this or your list might be incomplete.
sudo apt-get update
# Install rvm
sudo apt install curl -y
sudo apt install gnupg2 -y
gpg2 --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
\curl -sSL https://get.rvm.io | bash -s stable
# Install ruby
rvm install ruby-3.0.0
# Install zsh/oh-my-zsh
Install zsh from this tutorial: https://kifarunix.com/install-and-setup-zsh-and-oh-my-zsh-on-ubuntu-20-04/
# Install vscode
Install vscode on windows from here: https://code.visualstudio.com/Download
# Install the wsl extension when vscode prompts you
# Restart to be able to run code from any ubuntu terminal
# Install postgres
sudo apt install postgresql -y
sudo apt-get install libpq-dev -y
sudo service postgresql start
sudo -u postgres createuser -s $USER
# Install redis
sudo apt install redis-server -y
# Install node
sudo apt install nodejs -y
# Install npm
sudo apt install npm -y
# Install nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash
# Install yarn
sudo npm install --global yarn
# Install heroku cli
curl https://cli-assets.heroku.com/install.sh | sh
Rails security guide: https://guides.rubyonrails.org/security.html
Zen rails security checklist: https://github.com/brunofacca/zen-rails-security-checklist
OWASP ruby on rails cheat sheet: https://cheatsheetseries.owasp.org/cheatsheets/Ruby_on_Rails_Cheat_Sheet.html
Bundle audit: https://github.com/rubysec/bundler-audit
ActiveRecord::Base.logger.level = 1
storage_service = ObjectSpace.each_object(ActiveStorage::Service::S3Service).first
(ActiveStorage::Attachment.pluck(:record_type).uniq - ["Em::Compare::Checkouts::PointOfDelivery"]).each do |klass|
model = klass.constantize
puts "#{klass}"
puts "=" * 80
associations = model.reflect_on_all_associations.select { |a| a.class_name == "ActiveStorage::Attachment" }
associations.each do |association|
storage_name = association.name.to_s.split("_").first.to_sym
puts "#{storage_name}"
puts "-" * 80
records = model.joins(association.name).distinct
records.find_each do |record|
puts "#{model}##{record.id}"
puts "-" * 80
blobs = [record.send(storage_name)].flatten.map(&:blob)
blobs.each do |blob|
if storage_service.exist?(blob.key)
puts "Blob key #{blob.key} exists"
else
puts "Blob key #{blob.key} missing"
end
end
end
end
end
ActiveRecord::Base.logger.level = 0
# Create a folder to store the certificates
mkdir config/certs
# Ignore the certificates folder to avoid accidentally commiting the certs to the repo
echo "config/certs/*" >> .gitignore
# generate the certificate:
# Be sure to set the domain name to "lvh.me". Other settings don't matter
openssl req -x509 -sha256 -nodes -newkey rsa:2048 -days 365 -keyout config/certs/lvh.me.key -out config/certs/lvh.me.crt
Rails.application.configure do
config.force_ssl = true if ENV.fetch('SSL', nil) == "true"
end
SSL=true bundle exec rails s -b 'ssl://0.0.0.0:3000?key=./config/certs/lvh.me.key&cert=./config/certs/lvh.me.crt'
https://lvh.me:3000/
SSL error, peer: 127.0.0.1, peer cert: , #<Puma::MiniSSL::SSLError: OpenSSL error: error:141F7065:SSL routines:final_key_share:no suitable key share - 337604709>
Make sure to use puma '>= 4'.
# For the main application
Rails.application.routes.named_routes.send(:routes).each {|name, route| puts "%20s: %s" % [name, route] }; nil
# For a specific engine
Cmor::Blog::Backend::Engine.routes.named_routes.send(:routes).each {|name, route| puts "%20s: %s" % [name, route] }; nil
class SchemaMigration < ActiveRecord::Base; self.primary_key = :version; end
SchemaMigration.where(version: "20200514202233").first
SchemaMigration.where(version: "20200514202233").update_all(version: "20200514142149")
When you are trying to run rake dummy:app from the rails-dummy gem and you get following message:
Could not find "README.md" in any of your source paths. Your current source paths are:
/home/vagrant/.rvm/gems/ruby-2.4.0@cmor/gems/railties-5.2.0/lib/rails/generators/rails/app/templates
You may be using rails 5.2.0. Upgrading to 5.2.3 or later solves the problem.
If you get following message when deploying to heroku (i.e. after upgraping rails):
command webpacker not found
You may need to run following command locally and commit the changes:
rails webpacker:install
I keep forgetting how to test exceptions with rspec2. This is an example:
describe "something" do describe "exceptions" do it "should raise an exception" do expect { # Code that throws an exception }.to raise_error end
When you use capistrano and you need a custom bundler config in your Rails app, you have the problem, that it gets lost after each update.
The solution to this problem is to move the .bundle folder in your rails app, to the shared folder in your capistrano environment. Then you tell capistrano to link the .bundle to the shared folder after each update.
Rails.root/.bundle to ../../shared/.bundle
Rails.root/config/deploy.rb
:namespace(:bundle) do desc "Symlinks your machine specific bundle to your rails app" task :symlink, :roles => :app do run <<-CMD ln -nfs #{shared_path}/.bundle #{release_path}/.bundle CMD end end
Rails.root/config/deploy.rb
:after "deploy:symlink", "bundle:symlink"
Imagine you have been happily writing your controller test and then, you have to add authentication to your application using devise.
Assume you have a posts controller with following test:
require 'spec_helper' describe PostsController do def mock_post(stubs={}) @mock_post ||= mock_model(Post, stubs).as_null_object end describe "GET index" do it "assigns all posts as @posts" do Post.stub(:all) { [mock_post] } get :index assigns(:posts).should eq([mock_post]) end end describe "GET show" do it "assigns the requested post as @post" do Post.stub(:find).with("37") { mock_post } get :show, :id => "37" assigns(:post).should be(mock_post) end end end
First, you’ll have to add the Devise TestHelpers. Add a new file spec/support/devise.rb
:
RSpec.configure do |config| config.include Devise::TestHelpers, :type => :controller end
You’ll need a factory for the admin. I use factory girl. Add the admin factory to Rails.root/spec/factories.rb
:
Factory.define(:admin) do |admin| admin.email "admin@example.com" admin.password "foobar" end
Then you throw all your controller tests into a new describe block, that signins in your admin before each test:
describe PostsController do describe "as a signed in admin" do before(:each) do admin = Factory(:admin) sign_in admin end def mock_post(stubs={}) @mock_post ||= mock_model(Post, stubs).as_null_object end describe "GET index" do it "assigns all posts as @posts" do Post.stub(:all) { [mock_post] } get :index assigns(:posts).should eq([mock_post]) end end describe "GET show" do it "assigns the requested post as @post" do Post.stub(:find).with("37") { mock_post } get :show, :id => "37" assigns(:post).should be(mock_post) end end end end
Customize the request specs. Before:
require 'spec_helper' describe "NewsItems" do describe "GET /news_items" do it "works! (now write some real specs)" do # Run the generator again with the --webrat flag if you want to use webrat methods/matchers get news_items_path response.status.should be(200) end end end
and with devise integration:
require 'spec_helper' describe "NewsItems" do context "as logged in admin" do before(:each) do admin = Factory(:admin) get new_admin_session_path fill_in :email, :with => admin.email fill_in :password, :with => admin.password click_button end describe "GET /news_items" do it "works! (now write some real specs)" do # Run the generator again with the --webrat flag if you want to use webrat methods/matchers get news_items_path response.status.should be(200) end end end end
namespace :devise do desc "Create admin" task :create_admin, :email, :password, :needs => :environment do |t, args| if a = Admin.create(:email => args.email, :password => args.password) puts "Created admin" else puts a.errors end end end
Usage:
$> rake devise:create_admin["user@example.com","password"]
Adding a new role (i.e. “Admin”) is easy:
$> rails generate devise Admin
Don’t forget to migrate:
$> rake db:migrate
Then you can check for the admin in your controllers:
class BackendController < ApplicationController before_filter :authenticate_admin! end
gem 'devise'
$> bundle install
$> rails generate devise:install
This will generate a initialiter in your config/initializers folder. You should check the options in config/initializers/devise.rb
environments/development.rb:
config.action_mailer.default_url_options = { :host => 'localhost:3000' }
environments/production.rb:
config.action_mailer.default_url_options = { :host => 'blog.robotex.de' }
.svnignore Datei im Rails.root erstellen:
.bundle .project *.swp *~ webrat.log db/*.sqlite3 log/* tmp/* doc/api/* doc/app/*
und danach die Datei “scharf” schalten:
svn -R propset svn:ignore -F .svnignore .