bolha.us is one of the many independent Mastodon servers you can use to participate in the fediverse.
We're a Brazilian IT Community. We love IT/DevOps/Cloud, but we also love to talk about life, the universe, and more. | Nós somos uma comunidade de TI Brasileira, gostamos de Dev/DevOps/Cloud e mais!

Server stats:

256
active users

#cyphonautes

0 posts0 participants0 posts today
Bruno C. Vellutini<p><strong>Experimenting with just</strong></p><p>I took some time this weekend to experiment with <a href="https://just.systems" rel="nofollow noopener noreferrer" target="_blank"><code>just</code></a>—a modern, <a href="https://www.gnu.org/software/make/" rel="nofollow noopener noreferrer" target="_blank">make</a>-inspired command runner. I’m interested in using it for managing the routine tasks of the <a href="https://brunovellutini.com/posts/cifonauta-v2-live/" rel="nofollow noopener noreferrer" target="_blank">Cifonauta database</a> (a Django-based app), which are currently handled by <a href="https://www.fabfile.org" rel="nofollow noopener noreferrer" target="_blank">Fabric</a>.</p><p>Fabric is a Python library for running shell commands locally or remotely via SSH. I use it for interacting with the Cifonauta’s servers, like fetching the latest database from production or running the deployment routine after code updates.</p><p>Although Fabric works mostly fine, I’ve encountered a few command quirks over the years. I also often get confused when creating tasks because of all its lower level libraries (<a href="https://www.pyinvoke.org" rel="nofollow noopener noreferrer" target="_blank">Invoke</a>, <a href="https://www.paramiko.org" rel="nofollow noopener noreferrer" target="_blank">Paramiko</a>, <a href="https://invocations.readthedocs.io/en/latest/" rel="nofollow noopener noreferrer" target="_blank">Invocations</a>, and <a href="https://fabric-patchwork.readthedocs.io/en/latest/" rel="nofollow noopener noreferrer" target="_blank">Patchwork</a>…). So, in the back of my mind, I’ve been roaming for a simpler alternative.</p><p><code>just</code> seems to have a nice set of features and appears to be gaining some momentum lately.</p><p><code>Justfile</code>‘s syntax is similar to that <code>Makefile</code>‘s. It was straightforward to create some recipes for the standard&nbsp;<code>manage.py</code>&nbsp;commands of Django, like&nbsp;<code>runserver</code>&nbsp;or&nbsp;<code>shell</code>. Here are a few of them:</p><pre># Cifonauta's justfile<br><br># Set the default shell to bash<br>set shell := ["bash", "-cu"]<br><br># List all available recipes<br>default:<br>@just --list<br><br># Run Django development server<br>runserver:<br>@echo "Starting Django development server..."<br>python manage.py runserver<br><br># Run Django shell<br>shell:<br>@echo "Starting Django shell..."<br>python manage.py shell<br><br># Run Django database shell<br>dbshell:<br>@echo "Starting Django database shell..."<br>python manage.py dbshell<br><br># Run Django makemigrations<br>makemigrations:<br>@echo "Making migrations..."<br>python manage.py makemigrations<br><br># Run Django migrate<br>migrate:<br>@echo "Applying migrations..."<br>python manage.py migrate<br><br># Run Django collectstatic<br>collectstatic:<br>@echo "Collecting static files..."<br>python manage.py collectstatic --noinput<br><br># Run Django test<br>test:<br>@echo "Running tests..."<br>python manage.py test</pre><p>I run the <code>manage.py</code> commands every time I work on the Cifonauta code. They are simple enough, but running <code>just runserver</code>&nbsp;or&nbsp;<code>just migrate</code>&nbsp;is even simpler and feels semantically right. And the autocomplete is also snappier than the standard <code><a href="https://github.com/django/django/blob/main/extras/django_bash_completion" rel="nofollow noopener noreferrer" target="_blank">django_bash_completion</a></code>. I liked it.</p><p>A note: <code>just</code> prints the command itself to the terminal by default. Adding the <code>@</code> in front hides the command; only the output is shown when running the recipe.</p><p>The first issue I had was trying to activate Cifonauta’s Python virtual environment automatically for each of the recipes above. Based on intuition, I created a recipe to activate the environment and added it as a dependency to other recipes. However, this didn’t work out.</p><p>It turns out that each line in <code>just</code> runs <a href="https://just.systems/man/en/setting-variables-in-a-recipe.html" rel="nofollow noopener noreferrer" target="_blank">a new shell instance</a>. After digging a bit, the solutions I found were complicating the code too much, so for now, I’m sticking with activating the environment myself beforehand—it’s quick enough (a one-key alias).</p><p>After this initial setup, I started migrating some <code>fabfile</code> tasks of the Cifonauta workflow to <code>justfile</code> recipes. One very nice feature I discovered is that you can run a <code>just</code> recipe in any language using&nbsp;<a href="https://just.systems/man/en/shebang-recipes.html" rel="nofollow noopener noreferrer" target="_blank">shebangs</a>. Different from regular recipes, the <a href="https://just.systems/man/en/setting-variables-in-a-recipe.html" rel="nofollow noopener noreferrer" target="_blank">variables will persist across lines</a> like in a standard script.</p><p>Here’s a simple Bash recipe to dump the local database to a file for backup:</p><pre># Backup local database<br>backup-local-db:<br> #!/usr/bin/env bash<br> echo "Dumping local database to file... ({{dbname}})"<br> set -euo pipefail<br> mkdir -p {{local_bkp_dir}}<br> FILEPATH="{{local_bkp_dir}}/$(hostname)_{{dbname}}_$(date +%Y-%m-%d_%H%M).sql.gz"<br> pg_dump {{dbname}} | gzip -c9 &gt; "$FILEPATH"<br> echo "Backup created: $FILEPATH"</pre><p>And this one is a bit more complex as it runs a couple of commands remotely via SSH to get the latest production database.</p><pre># Fetch latest database from production<br>get-production-db:<br> #!/usr/bin/env bash<br> echo "Fetching latest database from production..."<br> set -euo pipefail<br> mkdir -p {{local_bkp_dir}}<br> LATEST_DBNAME=$(ssh {{user}}@{{prod_server}} "readlink -f {{prod_bkp_dir}}/latest.sql.gz")<br> FILENAME=$(basename "$LATEST_DBNAME")<br> scp {{user}}@{{prod_server}}:"$LATEST_DBNAME" {{local_bkp_dir}}/"$FILENAME"<br> echo "File copied: {{local_bkp_dir}}/$FILENAME"</pre><p>There was only one idea left that I wanted to test: can I run some code from the Django app within a recipe? I made a test recipe to count the number of taxa in our database using the <code>--command</code> parameter of <code>manage.py shell</code> and it works!</p><pre><code># Test recipe for counting taxacount-taxa-test:@echo "Counting taxa..."@python manage.py shell --command "\from meta.models import Taxon;\taxa = Taxon.objects.all();\print(f'Total: {taxa.count()}')\"</code></pre><p>While this type of query is probably better as a standard Django management in the long term, <code>just</code> recipes can be a handy shortcut to quickly draft and test commands before they graduate to the codebase.</p><p>To summarize, <code>just</code> feels promising. And it’ll most likely be easier to maintain than Fabric, since I can <em>just</em> create recipes in Bash or Python. But let’s see how it goes. I still need to migrate other, more complex tasks like the staging and production deployment routines, which will be some work.</p><p>—<br>URL: <a href="https://brunovellutini.com/posts/just-experimenting/" rel="nofollow noopener noreferrer" translate="no" target="_blank"><span class="invisible">https://</span><span class="ellipsis">brunovellutini.com/posts/just-</span><span class="invisible">experimenting/</span></a></p><p><a rel="nofollow noopener noreferrer" class="hashtag u-tag u-category" href="https://brunovellutini.com/posts/tag/bash-en/" target="_blank">#bash</a> <a rel="nofollow noopener noreferrer" class="hashtag u-tag u-category" href="https://brunovellutini.com/posts/tag/cyphonautes/" target="_blank">#cyphonautes</a> <a rel="nofollow noopener noreferrer" class="hashtag u-tag u-category" href="https://brunovellutini.com/posts/tag/django-en/" target="_blank">#django</a> <a rel="nofollow noopener noreferrer" class="hashtag u-tag u-category" href="https://brunovellutini.com/posts/tag/just/" target="_blank">#just</a> <a rel="nofollow noopener noreferrer" class="hashtag u-tag u-category" href="https://brunovellutini.com/posts/tag/python-en/" target="_blank">#python</a> <a rel="nofollow noopener noreferrer" class="hashtag u-tag u-category" href="https://brunovellutini.com/posts/tag/web-development/" target="_blank">#webDevelopment</a></p>