[PYTHON] I made an appdo command to execute a command in the context of the app

It takes too long to prepare for running rake in a Ruby on Rails application after logging in to the host, so let's combine it into one command, ʻappdo`.

The problem you want to solve

If you put Ruby in rbenv or rvm, you will not be able to find ruby when executing commands with sudo or cron. Well, you should read ~ / .bashrc etc. every time, but doing so complicates the command. If the number of places where such processing is performed increases, correction omissions are likely to occur. It's packed.

When running a Ruby on Rails application, neither bundle exec rake nor bin / rails runner can be executed without further cd $ RAILS_ROOT. As a result, operational commands become longer and longer, making crontab and config / deploy.rb difficult to maintain. It's packed.

** Anyway, the same host prepares the same thing, so I should have such a setting on the host **, so I made such a command.

The solution is ʻappdo`

I created a ʻappdo command to execute a command in the context of an app. You can [pip install`](https://pypi.python.org/pypi/appdo).

pip install appdo

With ʻappdo, the rails c` that used to run like this ...

$ su - web                       #Log in first ...
$ cd my-awesome-app              #There was this directory
$ cd current                     #Oh, it was under the control of capistrano
$ export RAILS_ENV=production    #Set environment variables
$ export RACK_ENV=production     #(Something different)
$ bundle exec rails c            #Hoi

That's all you need to do.

$ sudo -iu web appdo rails c     #rails c within the web user's app

Write the configuration file in $ HOME / .appdo.conf as follows. The format is TOML.

# /home/web/.appdo.conf
#
##If you do sudo, the sudo destination user.appdo.conf applies

[default]
cd = "~/my-awesome-app/current"
source = ["/etc/profile", "~/.bashrc"]
prefix = "bundle exec"

[default.env]
RAILS_ENV = production
RACK_ENV = production

As you can see, it does cd, source, sets environment variables, and then bundle execs the command.

If you do it every time, it's not a job that humans should do. It makes people happier to put it in a configuration file and provision and manage that file.

Other changes

Changes in crontab

When there is no appdo

RAILS_ROOT=/home/web/my-awesome-app/current
BASHRC=/home/web/.bashrc
RAILS_ENV=production

30 3 * * * bash -c "cd $RAILS_ROOT; source $BASHRC; bundle exec rails runner MyAwesomeApp::MyCron.run()"
30 4 * * * bash -c "cd $RAILS_ROOT; source $BASHRC; bundle exec rails runner MyAwesomeApp::MyCron2.run()"
30 5 * * * bash -c "cd $RAILS_ROOT; source $BASHRC; bundle exec rails runner MyAwesomeApp::MyCron3.run()"

When there is appdo

30 3 * * * appdo rails runner MyAwesomeApp::MyCron.run()
30 4 * * * appdo rails runner MyAwesomeApp::MyCron2.run()
30 5 * * * appdo rails runner MyAwesomeApp::MyCron3.run()

Since there is no need to set environment variables (appdo absorbs them), crontab becomes super simple. You can see what command you are typing with appdo. I can see it

Command change in ssh

When there is no appdo

ssh my.server.example.com sudo -u web bash -c "cd /home/web/my-awesome-app/current; source ~/.bashrc; RAILS_ENV=production bundle exec rake assets:precompile"

When there is appdo

ssh my.server.example.com sudo -iu web appdo rake assets:precompile

This is as simple as crontab. Simplifying ssh has the big advantage of simplifying config / deploy.rb. Separation is better because you don't have to put host-specific information (paths, environment variables, etc.) in your app code.

Simplification of procedure manual

Imagine all cd / source move out of the document Wow

Easy host migration

For example, even if the app path is different or ruby is in a mixed environment of rvm / rbenv, the configuration file on each host can absorb this difference. As a result, sequential migration may be much easier, as the deployment script does not need to be modified.

Other usage examples

Server configuration file management

# /root/.appdo.conf

[apache]
cd = "/etc/apache2"

[bind]
cd = "/var/bind/etc/bind"

It may be convenient to specify only the cd destination and share only the management command of the configuration file.

# appdo --app=apache -- ls sites-available
# appdo --app=apache -- ln -s sites-available/99-default sites-enabled
# appdo --app=bind -- co -l master/my.example.com
# appdo --app=bind -- vim master/my.example.com
# appdo --app=bind -- ci -u master/my.example.com

Summary

--Use appdo to execute commands in "app context" --Use appdo to drop the "app context" into your config file --Use appdo to clean up breaks between apps and infrastructure

Muttering at the end

It's a script that I just made myself and isn't bullet proof, but I decided to release it and hit the specs first. We look forward to your bug reports and requests.

Originally, I thought about this ʻappdo command because it was stressful that I couldn't use rubyfromsudo when using rvm / rbenv. The demands placed on infrastructure are changing again these days, but I think it's better to design the host to behave autonomously, whether it's docker, serf / consul, or autoscaling. Even in such a place, if you can create something like a "host layer" by using ʻappdo and absorb various things, I think that you will be happy because you will have less head use.

The Python implementation is the so-called first prototyping guy. I haven't made a package in Python, so I used @ mkouhei's bootstrap-py for implementation. I think it would be better to move to Go when the requirements such as options are fixed.

e? Is Rust better? ...

Recommended Posts

I made an appdo command to execute a command in the context of the app
I made a command to display a colorful calendar in the terminal
I want to leave an arbitrary command in the command history of Shell
I made a program to check the size of a file in Python
I made a command to markdown the table clipboard
A memorandum of how to execute the! Sudo magic command in Jupyter Notebook
I made a command to generate a table comment in Django
I made a function to check the model of DCGAN
I made you to execute a command from a web browser
[Python] I made an app to practice the subtle voice distinction of English words.
I made a program in Python that changes the 1-minute data of FX to an arbitrary time frame (1 hour frame, etc.)
I made an app to find out who the members of the Straw Hat Pirates look like
[Linux] Command to get a list of commands executed in the past
I want to sort a list in the order of other lists
I want to color a part of an Excel string in Python
I made a mistake in fetching the hierarchy with MultiIndex of pandas
I tried to display the altitude value of DTM in a graph
I made a function to see the movement of a two-dimensional array (Python)
I made a tool to estimate the execution time of cron (+ PyPI debut)
How to pass the execution result of a shell command in a list in Python
With LINEBot, I made an app that informs me of the "bus time"
I want to set a life cycle in the task definition of ECS
I want to see a list of WebDAV files in the Requests module
I made a tool to automatically back up the metadata of the Salesforce organization
I want to store the result of% time, %% time, etc. in an object (variable)
I made a script to record the active window using win32gui of Python
When I try to execute the make command of Makefile with os / exec of golang, the second and subsequent executions result in an error.
How to execute a command using subprocess in Python
I wanted to know the number of lines in multiple files, so I tried to get it with a command
I made a twitter app that decodes the characters of Pricone with heroku (failure)
I made a command to wait for Django to start until the DB is ready
I made a dot picture of the image of Irasutoya. (part1)
I made a dot picture of the image of Irasutoya. (part2)
[Golang] Command to check the supported GOOS and GOARCH in a list (Check the supported platforms of the build)
I made a tool to get the answer links of OpenAI Gym all at once
[For beginners] I want to explain the number of learning times in an easy-to-understand manner.
I tried to create a Python script to get the value of a cell in Microsoft Excel
I made a function to crop the image of python openCV, so please use it.
I wrote a doctest in "I tried to simulate the probability of a bingo game with Python"
How to pass the execution result of a shell command in a list in Python (non-blocking version)
In the python command python points to python3.8
I made a program to solve (hint) Saizeriya's spot the difference
[Linux] I tried to summarize the command of resource confirmation system
[sh] How to store the command execution result in a variable
How to determine the existence of a selenium element in Python
How to know the internal structure of an object in Python
I made a program that solves the spot the difference in seconds
How to check the memory size of a variable in Python
[Introduction to StyleGAN] I played with "The Life of a Man" ♬
I wrote the code to write the code of Brainf * ck in python
An easy way to re-execute a previously executed command in ipython
How to check the memory size of a dictionary in Python
How to output the output result of the Linux man command to a file
How to get the vertex coordinates of a feature in ArcPy
I tried to make an analysis base of 5 patterns in 3 years
A command to easily check the speed of the network on the console
I made a slack bot that notifies me of the temperature
Create a function to get the contents of the database in Go
I used the worldcup command to check the outcome of the World Cup.
I want to know the population of each country in the world.
[Kaggle] I made a collection of questions using the Titanic tutorial