Yii2 – closingtags </>
Categories
Linux Server Yii2

Starting Systemd Services with Vagrant machines

I recently ran into a minor inconvenience with a configuration on one of my Vagrant machines. You see, I’m implementing a queueing service on an application and I needed that service to be started whenever the machine starts up. Normally, this is quite simple and is done by creating a file named myservice@service in /etc/systemd/system with content like so:
[Unit]
Description=My Queue Worker %I
After=network.target
[Service]
User=www-data
Group=www-data
ExecStart=/usr/bin/php /var/www/my_project/my_script –verbose
Restart=on-failure
[Install]
WantedBy=multi-user.target
Then run systemctl daemon-reload and systemctl enable myservice@1 myservice@2 to start two workers on system boot. Reboot your system, run systemctl status myservice@* and you should see both of those services running.
The problem with doing this on Vagrant occurs when the file your service is attempting to run is located in the shared folder that doesn’t end up getting mounted until the system has already started all services. But Vagrant has that handy file for provisioning and it can do all sorts of neat things like run shell commands after provisioning. To get these very same services to start up in a Vagrant VM, you simply need to add this bit to your Vagrantfile:
Vagrant.configure(“2”) do | config|
config.vm.provision “shell”, run: “always”,
inline: “systemctl start myservice@1 myservice@2”
end
Because this is a change to the Vagrantfile, you’ll have to re-provision the VM with vagrant reload –provision. This will shutdown the currently running VM, and re-run all provisions. Normally, these provisions are only run during the provisioning stage of Vagrant but because we added the run: “always” flag, this snippet will be run every time the machine is started. Now, once you’ve booted your VM with vagrant up, ssh into it with vagrant ssh and you should be able to run systemctl status myservice@* to see all of your services running.

Categories
PHP Security WordPress Yii2

Deployments w/Capistrano

For so long, developers have been moving code with FTP/SFTP. It’s time consuming, and comes with its faults. One false click, and you could easily overwrite a file that you haven’t downloaded yet. And that’s not good. Especially if there’s no version control in place. I know; it’s 2015 and if that happens to you, you kind of deserve it. But the fact of the matter is that a lot of developers at smaller organizations are still doing things this way.
This is where Capistrano comes in. Capistrano is a Ruby gem that makes deployment a one command process. Seriously.
cap production deploy
That command is all you need to push code to your production server.
Capistrano lets you use a git repo to launch your application. Instead of opening up FileZilla, finding a directory, and uploading everything manually, Capistrano will go to your repository, and move that code via a secured connection (SSH) to your server. The Capistrano website has everything you need to get setup so I won’t go into that here, but I will drop in my configuration to show how I’m using it to deploy a Yii2 app.
config/deploy.rb
set :application, ‘PROJECT_NAME_HERE’
set :repo_url, ‘https://GIT_REPO_HERE.git’

# Default deploy_to directory is /var/www/my_app_name
set :deploy_to, ‘DIR_TO_DEPLOY_TO_ON_SERVER’

# Default value for linked_dirs is []
set :linked_dirs, fetch(:linked_dirs, []).push(‘web/uploads’, ‘vendor’, ‘runtime’)

# Default value for default_env is {}
# set :default_env, { path: “/opt/ruby/bin:$PATH” }

# Default value for keep_releases is 5
# set :keep_releases, 5

Rake::Task[”deploy:symlink:linked_dirs”].clear
Rake::Task[”deploy:symlink:linked_files”].clear
Rake::Task[”deploy:symlink:release”].clear

namespace :deploy do

after :restart, :clear_cache do
on roles(:app), in: :groups, limit: 3, wait: 10 do
# # Here we can do anything such as:
# within release_path do
# execute :rake, ‘cache:clear’
# end
end
end

namespace :symlink do
desc ‘Symlink release to current’
task :release do
on release_roles :all do
tmp_current_path = release_path.parent.join(current_path.basename)
execute :ln, ‘-s’, release_path.relative_path_from(current_path.dirname), tmp_current_path
execute :mv, tmp_current_path, current_path.parent
end
end

desc ‘Symlink files and directories from shared to release’
task :shared do
invoke ‘deploy:symlink:linked_files’
invoke ‘deploy:symlink:linked_dirs’
end

desc ‘Symlink linked directories’
task :linked_dirs do
next unless any? :linked_dirs
on release_roles :all do
execute :mkdir, ‘-p’, linked_dir_parents(release_path)

fetch(:linked_dirs).each do |dir|
target = release_path.join(dir)
source = shared_path.join(dir)
unless test “[ -L #{target} ]”
if test “[ -d #{target} ]”
execute :rm, ‘-rf’, target
end
execute :ln, ‘-s’, source.relative_path_from(target.dirname), target
end
end
end
end

desc ‘Symlink linked files’
task :linked_files do
next unless any? :linked_files
on release_roles :all do
execute :mkdir, ‘-p’, linked_file_dirs(release_path)

fetch(:linked_files).each do |file|
target = release_path.join(file)
source = shared_path.join(file)
unless test “[ -L #{target} ]”
if test “[ -f #{target} ]”
execute :rm, target
end
execute :ln, ‘-s’, source.relative_path_from(target.dirname), target
end
end
end
end
end

task :composer do
on roles(:app) do
within release_path do
execute “cd #{release_path} &amp;&amp; php-latest ~/composer.phar install”
end
end
end

task :setup do
on roles(:app) do
within release_path do
execute “cd #{release_path} &amp;&amp; php-latest yii.php setup”
# execute :php, “yii setup”
end
end
end

after :updated, “deploy:composer”
after :updated, “deploy:setup”

end
config/deploy/production.rb
server ‘closingtags.com’,
roles: %w{app},
branch: ‘master’,
ssh_options: {
user: ‘SSH_USER_NAME_HERE’,
keys: %w(~/.ssh/id_rsa),
# forward_agent: false,
auth_methods: %w(publickey)
# password: ‘please use keys’
}
Now, I’m no Ruby developer, so if my code could be cleaned up, which I know it can be, let me know. But let’s take a quick walk through with what we have here. Firstly, anything starting with # shows a commented line. I’ve removed most of mine, but left a few I thought might be helpful. You’ll notice the first three uncommented lines of the deployment file are just setting some variables for the configuration.
The fourth line, set :linked_dirs, is doing something really interesting. When Capistrano deploys my app, it creates a git repo on the server and will keep the last 5 versions. The linked_dirs variable is basically creating a symlink on my server, so that it doesn’t recreate those files every time. This was necessary for my uploads directory, which needs to be have the same location each time I deploy. If I created a new directory each time I deployed, users would get 404’d every time I deployed.
The next few segments actually contain a description so there isn’t much to say about those, other than they were necessary to deploy to MediaTemple’s GridServer. Most of the time, this stuff isn’t necessary, but I had a special case with our server.
task :composer installs all of my dependencies and task :setup runs a custom yii2 command that upgrades the database, if necessary.
Now like I said earlier, I’m using this to deploy a Yii2 app, but really, it could be used for anything. It’s mostly used to deploy Ruby applications, but there really isn’t anything stopping you from deploying something built WordPress or Laravel. If you have some suggestions on how I could streamline my process, let me know!

Categories
PHP Yii2

Yii2 Feedback Widget

I needed to get feedback directly from my users to me on a Yii2 web application. Most of the options were fairly expensive. After a lot of looking, I found a JavaScript plugin (https://github.com/ivoviz/feedback) that did some of what I needed but not all. So I developed a Yii2 widget with the intent of bundling¬†it all into one package and maintaining it as just a side project. Well, I’ve basically built the Yii2 wrapper for the ivoviz/feedback JS plugin. It uses all of the same parameters as the original plugin, and currently only has one JS error (yay).
My plugin and all the documentation needed to use it can be found on GitHub! I’ve even included some sample controller code for getting the AJAX response handled.