5
\$\begingroup\$

I made a script for my Ubuntu server(s) running on Google Cloud Platform. The idea behind it is I can create a new instance and paste this script in Google Cloud so it runs it on the new instance and every time it reboots. The script works, so no problem there.

The idea is that it installs all the required software to run my website the first time it boots up and then clones my git repository to /var/www/html/. Every other time it boots it only updates the software and then pulls the changes from the git repository.

My questions:

  1. From what I understand, it's not very wise to put your git password in the startup script but I know no other way to automate this fully. Can this be done in another way while keeping it fully automated?

  2. Cloning the git repository gave an error about the folder not being empty so I just deleted it upfront. It works now but I am not sure if that's the way to go ...

Here is my startup script:

file="/var/www/check.txt"

if [ -e $file ]
then
sudo apt-get update
sudo git -C /var/www/html pull https://username:password@bitbucket.org/username/repository.git
else
sudo apt-get update
sudo apt-get install apache2 libapache2-mod-php5 php5-mcrypt php5-mysql git -y
sudo cat <<EOF > /etc/apache2/mods-enabled/dir.conf
<IfModule mod_dir.c>
        DirectoryIndex index.php index.cgi index.pl index.html index.xhtml index.htm
</IfModule>

# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
EOF
sudo rm -rf /var/www/html
sudo git clone https://username:password@bitbucket.org/username/repository.git /var/www/html/ /var/www/html/
sudo cat <<EOF > /var/www/check.txt
aanwezig!
EOF
fi
\$\endgroup\$
2
\$\begingroup\$

Readability

The script is hard to read because code blocks are not indented, and there are not blank lines to separate cohesive units. Consider this alternative writing style of the same script:

file="/var/www/check.txt"

if [ -e $file ]
then
    sudo apt-get update
    sudo git -C /var/www/html pull https://username:password@bitbucket.org/username/repository.git
else
    sudo apt-get update
    sudo apt-get install apache2 libapache2-mod-php5 php5-mcrypt php5-mysql git -y
    sudo cat <<EOF > /etc/apache2/mods-enabled/dir.conf

<IfModule mod_dir.c>
        DirectoryIndex index.php index.cgi index.pl index.html index.xhtml index.htm
</IfModule>

# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
EOF
    sudo rm -rf /var/www/html
    sudo git clone https://username:password@bitbucket.org/username/repository.git /var/www/html/ /var/www/html/
    sudo cat <<EOF > /var/www/check.txt
aanwezig!
EOF
fi

I know, this is still not so great, due to the here-documents interrupting the indented blocks. We'll improve that in another step.

sudo and here-documents

The scope of sudo is strictly the specified command. For example in sudo date > /tmp/out, the date command will be executed as root, but the redirection is a different matter, it will be executed by the shell, as the current user, not root. Try this yourself, the owner of /tmp/out will be the current user, not root.

This means that the sudo cat <<EOF ... commands don't work as you may think they do. If you want to create a file as root from a here-document, you can write like this:

cat <<EOF | sudo tee /etc/apache2/mods-enabled/dir.conf
<IfModule mod_dir.c>
        DirectoryIndex index.php index.cgi index.pl index.html index.xhtml index.htm
</IfModule>

# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
EOF

The fact that you haven't noticed the problem suggests that maybe the script is already running as root, so you don't need any of the sudo.

Here-documents and formatting

Since here-documents break the indentation, I recommend extracting them to helper functions, for example:

print_mod_dir() {
    cat <<EOF
<IfModule mod_dir.c>
        DirectoryIndex index.php index.cgi index.pl index.html index.xhtml index.htm
</IfModule>

# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
EOF    
}

print_mod_dir | sudo tee /etc/apache2/mods-enabled/dir.conf

Although the here-document disrupts the indentation of the function, that's ok, because the entire function body is about the here-document, so it's less intrusive as elsewhere. The rest of the code can be nicely readable, for example:

file="/var/www/check.txt"

sudo apt-get update

if [ -e $file ]
then
    sudo git -C $docroot pull $repo
else
    sudo apt-get update
    sudo apt-get install apache2 libapache2-mod-php5 php5-mcrypt php5-mysql git -y
    print_mod_dir | sudo tee /etc/apache2/mods-enabled/dir.conf
    sudo rm -rf $docroot
    sudo git clone $repo $docroot $docroot
    sudo touch $file
fi

I replaced some repeated values with variables as @RubberDuck suggested in his review.

I also replaced the second here-document creating a file with a single line, because as far as this script is concerned, it's enough if the file exists, and touch is the perfect tool for that.

Naming

What is file? What is /var/www/check.txt? Neither the variable name nor the file name tell anything useful to the reader. It would be better to use names that make your intention obvious to readers.

\$\endgroup\$
  • \$\begingroup\$ Thank you for this extensive answer! This will help me a lot! \$\endgroup\$ – Lennart Giaccotto Jul 3 '17 at 11:31
  • \$\begingroup\$ i use /var/www/check.txt litteraly as a check. if the file is there then do the "if" statement. if not then do the "else" statement. \$\endgroup\$ – Lennart Giaccotto Jul 3 '17 at 14:26
  • \$\begingroup\$ @LennartGiaccotto I understood. But I had to read the context to understand. It's better to use names that make your intention obvious to readers without thinking. These variable and file names are too generic to be helpful. \$\endgroup\$ – Stop ongoing harm to Monica Jul 3 '17 at 14:58
  • \$\begingroup\$ indeed. will do from now on. stupid question maybe but using # in my script for comments is no problem right? As wel as indenting etc. or doesnt linux like it if i do that? \$\endgroup\$ – Lennart Giaccotto Jul 4 '17 at 13:43
  • \$\begingroup\$ @LennartGiaccotto Feel free to use comments. Just keep in mind that it's best when the code is so clear that comments are unnecessary. When you feel like you need comments to help understand something, consider refactoring so that the code can speak for itself. For example a common technique to clarify the purpose of an unclear snippet is to introduce a helper function with a descriptive name, rather than to add a comment. \$\endgroup\$ – Stop ongoing harm to Monica Jul 4 '17 at 13:51
5
\$\begingroup\$

So, this might not be quite in line with your 100% automation approach, but the best way to provide a shell script access to a git repository is to use ssh keys to allow access to your git repository. Of course, this means you either need to log on and set up that key and grant access it to Git (goodbye 100% automation) or securely copy a private key that already has access to the repository to your server. You'll probably want to keep that key someplace that requires a password to access it, so... chicken and the egg scenario. Unless your cloud provider gives you the ability to create a template that already has that key on it's file system when it's created. If you can do that, then you definitely want to go the ssh route.

Your clone would look something like this.

git clone ssh://username@bitbucket.org/username/repository.git

Okay, let's review your code now.

  • You're using sudo very liberally. It would be better to set file permissions appropriately. You can run into weirdness when using sudo along with git. You can end up with a repository on the file system owned by root and.. it can get messy.
  • There's no error handling what so ever. If any given step of your script fails, it will continue on trying to execute the next line.
  • If you needed to change the location of the repository, you'd need to change the /var/www/html string in 3 different places. If your repository changes locations, it would change in 2. Use variables appropriately.
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.