• Justfiles

    ,

    Today I Learned

    TIL Justfiles can also be Just Scripts

    Please note: passing an argument like --justfile It only works on MacOS and on Linux.

    TIL that Justfiles can turn into Just Scripts by adding #!/usr/bin/env just --justfile to the top of the file and running chmod +x on the file.

    From the docs:

    By adding a shebang line to the top of a justfile and making it executable, just can be used as an interpreter for scripts: https://github.com/casey/just?tab=readme-ov-file#just-scripts

    just.sh

    #!/usr/bin/env just --justfile
    
    @_default:
    	just --justfile just.sh --list
    
    @lint *ARGS:
        uv --quiet run --with pre-commit-uv pre-commit run {{ ARGS }} --all-files
    

    After you run chmod +x just.sh, this file may be run using ./just.sh, and sub-commands everything just <subcommand> will just work.

    Please note that --justfile just.sh is needed if you want your Just Script to be able to introspect or call itself.

    Why?

    More and more of my clients are using Justfiles, and occasionally, I want some other recipes that may belong outside the default workflows. These can also be reusable between projects for some of my other internal tooling, so it’s an excellent resource to learn about.

    Wednesday October 23, 2024
  • Django

    ,

    Python

    ,

    Justfiles

    ,

    Docker

    ,

    Today I Learned

    🐳 Using Just and Compose for interactive Django and Python debugging sessions

    When I wrote REST APIs, I spent weeks and months writing tests and debugging without looking at the front end. It’s all JSON, after all.

    For most of my projects, I will open two or three tabs. I’m running Docker Compose in tab one to see the logs as I work. I’ll use the following casey/just recipe to save some keystrokes and to standardize what running my project looks like:

    # tab 1
    $ just up 
    

    In my second tab, I’ll open a shell that is inside my main web or app container so that I can interact with the environment, run migrations, and run tests.

    We can nitpick the meaning of “console” here, but I tend to have another just recipe for “shell” which will open a Django shell using shell_plus or something more interactive:

    # tab 2
    $ just console
    

    In my third tab, I’ll run a shell session for creating git branches, switching git branches, stashing git changes, and running my linter, which I prefer to run by hand.

    # tab 3
    $ echo "I'm boring"
    

    Over the last year or two, the web has returned to doing more frontend work with Django and less with REST. Using ipdb, in my view, to figure out what’s going on has been really helpful. Trying to get ipdb to “just work” takes a few steps in my normal workflow.

    # tab 1 (probably)
    
    # start everything
    $ just start
    
    # stop our web container
    $ just stop web
    
    # start our web container with "--service-ports" 
    # just start-web-with-debug
    

    The only real magic here is using Docker’s --service-ports, which opens ports so we may connect to the open ipdb session when we open one in our view code.

    My main justfile for all of these recipes/workflows looks very similar to this:

    # justfile
    set dotenv-load := false
    
    @build *ARGS:
        docker compose build {{ ARGS }}
    
    # opens a console
    @console:
        docker compose run --rm --no-deps utility/bin/bash
    
    @down:
        docker compose down
    
    @start *ARGS:
        just up --detach {{ ARGS }}
    
    @start-web-with-debug:
        docker compose run --service-ports --rm web python -m manage runserver 0.0.0.0:8000
    
    @stop *ARGS:
        docker compose down {{ ARGS }}
    
    @up *ARGS:
        docker compose up {{ ARGS }}
    

    If you work on multiple projects, I encourage you to find patterns you can scale across them. Using Just, Make, shell scripts or even Python lightens the cognitive load when switching between them.

    Sunday June 30, 2024
  • Justfiles

    ,

    Docker

    ,

    Postgres

    🐘 A Just recipe to backup and restore a Postgres database

    I have used this casey/just recipe to help backup and restore my Postgres databases from my Docker containers.

    I work with a few machines, and it’s an excellent way to create a database dump from one machine and then restore it from another machine. I sometimes use it to test data migrations because restoring a database dump takes a few seconds.

    I have been migrating from Docker to OrbStack, and the only real pain point is moving data from one volume to another. I sometimes need to switch between the two, so I have recipes set to back up and restore my database from one context to another.

    # justfile
    
    DATABASE_URL := env_var_or_default('DATABASE_URL', 'postgres://postgres@db/postgres')
    
    # dump database to file
    @pg_dump file='db.dump':
        docker compose run \
            --no-deps \
            --rm \
            db \
            pg_dump \
                --dbname "{{ DATABASE_URL }}" \
                --file /code/{{ file }} \
                --format=c \
                --verbose
    
    # restore database dump from file
    @pg_restore file='db.dump':
        docker compose run \
            --no-deps \
            --rm \
            db \
            pg_restore \
                --clean \
                --dbname "{{ DATABASE_URL }}" \
                --if-exists \
                --no-owner \
                --verbose \
                /code/{{ file }}
    

    Shoutout to Josh Thomas for help on this recipe since we both iterated on this for several projects.

    Friday June 28, 2024
  • Justfiles

    ,

    Docker

    🐳 Managing Docker Compose Profiles with Just: Switching Between Default and Celery Configurations

    For a recent client project, we wanted to toggle between various Docker Compose profiles to run the project with or without Celery.

    Using Compose’s profiles option, we can label services that we may not want to start by default a label. This might look something like this:

    services:
    
      beat:
        profiles:
          - celery
        ...
    
      celery:
        profiles:
          - celery
        ...
    
    
      web:
        ...
    

    We use a casey/just justfile for some of our common workflows, and I realized I could set a COMPOSE_PROFILES environment variable to switch between running a “default” profile and a “celery” profile.

    Using just’s env_var_or_default feature, we can set both an ENV variable and a default value to fall back on for our project.

    # justfie 
    
    export COMPOSE_PROFILES := env_var_or_default('COMPOSE_PROFILES', 'default')
    
    @up *ARGS:
        docker compose up {{ ARGS }}
    
    # ... the rest of your justfile...
    
    

    To start our service without Celery, I would run:

    $ just up
    

    ` To start our service with Celery, I would run:

    $ export COMPOSE_PROFILES=celery
    $ just up
    

    Our COMPOSE_PROFILES environment variable will get passed into our just up recipe, and if we don’t include one, it will have a default value of default, which will skip running the Celery service.

    Tuesday June 25, 2024
  • Python

    ,

    Justfiles

    ,

    Today I Learned

    My Python Roots

    Last week, during office hours, I shared the two libraries that were my gateways to learning Python.

    Cog

    I stumbled on Ned Batchelder’s Cog while running an ISP in SWMO in the mid-00s. At the time, I was writing lots of PHP code and had a few layers of ORM code that I could generate with Cog’s help. This code was mainly boilerplate, and Cog was great at templating code. Thankfully, I didn’t need to know Python with Cog to make it work.

    In recent years, I have still used Cog to update docs and to document Justfiles, Click, Typer, and console apps by grabbing the output and embedding it into docs.

    Beautiful Soup

    Beautiful Soup is the library that pushed me to learn Python. Beautiful Soup motivated me to learn Python and even more advanced feats like installing LXML and processing unparseable HTML or XML. I have always liked writing web scrapers and processing HTML documents, which is a weird hobby of mine.

    My first Python app

    My friends and I worked in our first post-college dot com job, and Dell was running an incredible deal on their 20" widescreen monitors over the Christmas holiday.

    Dell ran a daily Dell Elf (Delf) contest where you gave them your email address, and they would give you a discount code for their various products.

    The best code was 50% off of their 20" widescreen displays, which was an incredible deal then. The display retailed for $499, so getting one for $249.50 was great. These codes were random, and the odds were 1 in 25 to get one.

    Using Python and having an email catchall, I wrote my first script to submit a series of email addresses until we found the daily 50% off code. At least four or five of my friends and I stocked up on these monitors that fall, and I have been a fan of Dell displays ever since.

    Today

    I still use Cog and Beautiful Soup 4 in several projects, including a few daily drivers. Last year, during their end-of-year sale, I picked three Dell 27-inch displays, and I still have fond memories of Dell’s displays.

    Monday March 11, 2024