First, the Phoenix deployment makes use of Releases, though I’m not doing anything fancy to track versions or anything like that.
This means the general upload procedure looks like this, and I can put it in a bash script to make updating a bit easier:
#!/usr/bin/env bash cd /opt/myapp73k # update from master /usr/bin/git pull 73k master # fetch prod deps & compile /usr/bin/mix deps.get --only prod MIX_ENV=prod /usr/bin/mix compile # perform any migrations MIX_ENV=prod /usr/bin/mix ecto.migrate # update node packages via package-lock.json /usr/bin/npm --prefix /opt/myapp73k/assets/ ci # rebuild static assets: rm -rf /opt/myapp73k/priv/static/* /usr/bin/npm --prefix /opt/myapp73k/assets/ run deploy MIX_ENV=prod /usr/bin/mix phx.digest # rebuild release MIX_ENV=prod /usr/bin/mix release --overwrite # restart service sudo /bin/systemctl restart myapp73k.service
To allow the user that’s running the script to invoke
sudo we need to give it explicit permission, e.g. by placing the following in a file like
git ALL=(runuser) NOPASSWD: /home/runuser/deploy_hooks/deploy-myapp73k.sh runuser ALL= NOPASSWD: /bin/systemctl restart myapp73k.service
The first line allows the user
git to run the script
/home/runuser/deploy_hooks/deploy-myapp73k.sh as the user with username
runuser – and without requiring a password. Permissions should only allow the
runuser accoutn to modify the script, so someone with access to the
git account can’t modify it to make it run something else.
The second line allows the user
runuser to run only the command
/bin/systemctl restart myapp73k.service without a password. Doing anything else with sudo will still ask for a password.
What this enables is that a git post-receive hook, running as user
git, can call the deploy script, which runs as user
runuser and performs the app update, which can finish by calling
gitea post-receive hook content
For git to run the deployment script after the repository receives a new commit, we set a git hook. But I want to be able to commit development branches to the repository without those commits getting deployed, so I want the hook to do nothing unless it’s a commit to the master branch (could be any other branch, say “prod”)
My hook looks something like this:
#!/usr/bin/env bash while read oldrev newrev refname do branch=$(git rev-parse --symbolic --abbrev-ref $refname) if [ "master" = "$branch" ]; then sudo -u runuser /home/runuser/deploy_hooks/deploy-myapp73k.sh fi done
Here we can see the hook checks the branch first, and if master, runs
sudo -u runuser to run the script as user
elixir phoenix release systemd unit
That should just about do it, but as an extra note, here’s the elixir phoenix release systemd unit:
[Unit] Description=MyApp73k service After=local-fs.target network.target [Service] Type=simple User=runuser Group=runuser WorkingDirectory=/opt/myapp/_build/prod/rel/myapp73k ExecStart=/opt/myapp73k/_build/prod/rel/myapp73k/bin/myapp73k start ExecStop=/opt/myapp73k/_build/prod/rel/myapp73k/bin/myapp73k stop #EnvironmentFile=/etc/default/myApp.env Environment=LANG=en_US.utf8 Environment=MIX_ENV=prod Environment=PORT=4000 LimitNOFILE=65535 UMask=0027 SyslogIdentifier=myapp73k Restart=always [Install] WantedBy=multi-user.target
I hope this is helpful to someone — most of all my future self!