Skip to content

Instantly share code, notes, and snippets.

@timmc-edx
Created October 19, 2023 20:55
Show Gist options
  • Select an option

  • Save timmc-edx/a0ba7aac8ab1ae4e5e54ea9695c569f3 to your computer and use it in GitHub Desktop.

Select an option

Save timmc-edx/a0ba7aac8ab1ae4e5e54ea9695c569f3 to your computer and use it in GitHub Desktop.

Revisions

  1. timmc-edx created this gist Oct 19, 2023.
    73 changes: 73 additions & 0 deletions make-requirements-if-needed.sh
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,73 @@
    #!/usr/bin/env bash
    # Run `make requirements` if virtualenv or requirements dir has changed since
    # last run.

    set -eu -o pipefail

    if [[ -z "${VIRTUAL_ENV:-}" ]]; then
    echo >&2 "Not in a virtualenv"
    exit 1
    fi

    if [[ ! -d "requirements" ]]; then
    echo >&2 "./requirements/ missing from current dir"
    exit 1
    fi

    # Bare filename to write at root of virtualenv.
    hashfile_name="deps-venv.chk"
    # Actual path
    hashfile_path="$VIRTUAL_ENV/$hashfile_name"

    function fingerprint_data_venv() {
    # The virtualenv is large, so do a fingerprint that is fast. And
    # it isn't directly affected by git checkouts, so we don't have to
    # worry about spurious file modification time changes. Therefore,
    # use file metadata rather than contents.
    #
    # For fingerprinting of these we include paths (sorted) with full
    # metadata.
    find "$VIRTUAL_ENV" -printf '%P %t %s %i %m %U %G\n' \
    | grep -vP "^\\Q${hashfile_name}\\E " \
    | sort
    }

    function fingerprint_data_reqs() {
    # The requirements files are small, so we can use contents-based
    # fingerprinting. These files also have their metadata changed by
    # git branch switching, and we don't want the fingerprint to
    # change just because we've switched branches and then switched
    # back again. That's a reason to avoid using the metadata, even
    # though it might be a little faster.
    #
    # All we need for fingerprinting are the file paths (in order)
    # along with a hash of each file's contents.
    find "./requirements/" -type f | sort | xargs -r sha256sum 2>/dev/null
    }

    function compute_fingerprint() {
    # Combine the two fingerprint data sources and hash it all together
    (fingerprint_data_venv && fingerprint_data_reqs) \
    | sha256sum | head -c64
    }

    do_run=yes
    if [[ -f "${hashfile_path}" ]]; then
    prev_hash="$(cat -- "${hashfile_path}")"
    cur_hash="$(compute_fingerprint)"
    if [[ "$prev_hash" == "$cur_hash" ]]; then
    echo >&2 "Dependency hashes match, skipping 'make requirements'"
    do_run=no
    else
    echo >&2 "Dependency hashes do not match; running 'make requirements'"
    echo >&2 "(prev=$prev_hash, cur=$cur_hash)"
    fi
    else
    echo >&2 "Dependency hash file not found"
    fi

    if [[ "$do_run" == 'yes' ]]; then
    make requirements
    fi

    compute_fingerprint > "${hashfile_path}"