Skip to content

Instantly share code, notes, and snippets.

@chadwcarlson
Created April 27, 2023 13:23
Show Gist options
  • Save chadwcarlson/15cb3cff7243cccf67fd69bb1345747a to your computer and use it in GitHub Desktop.
Save chadwcarlson/15cb3cff7243cccf67fd69bb1345747a to your computer and use it in GitHub Desktop.
#!/usr/bin/env bash
# pbt - Platform.sh Build Tools
# 1) build cache handling
# 2) common framework recipes
#
# What also could change?
# - any variable defined in variables in .platform.app.yaml
# - Using a specific variables.pbt for cache handling config
# - A forced rebuild environment variable (to catch other cases)
# - composability: resusing the caching logic for dirs and build deps --> cached-poetry; cached-pip
# - yq: installing and caching yq so as to make parsing CACHE_DIRS and other array vars simpler.
#######################################################################################################################
# User/app-specific configuration.
# Directories to hold in build cache.
CACHED_DIRS=( ".global" ".local" ".venv" )
# The primary build steps.
build() {
# Upgrade pip.
./cached_pip.sh
# Install poetry.
./cached_poetry.sh
}
#######################################################################################################################
# Script config, defaults, and helper functions.
# Track build time.
SECONDS=0
REBUILD=0
# Internal build cache tracking files.
CACHED_LOCKFILE_NAME="$PLATFORM_CACHE_DIR/platform_cache.deps"
CACHED_VARIABLES_FILE_NAME="$PLATFORM_CACHE_DIR/platform_cache.vars"
BUILD_TIME_FILE_NAME="$PLATFORM_CACHE_DIR/platform_cache.time"
# Helper function to handle cached directories.
apply_to_all_dirs() {
CMD=$1
for cache_dir in "${CACHED_DIRS[@]}"
do
eval $CMD
done
}
# Helper function for human readable build time output.
# Modified from https://stackoverflow.com/a/56530876.
secs_to_human() {
if [[ -z ${1} || ${1} -lt 60 ]] ;then
min=0 ; secs="${1}"
else
time_mins=$(echo "scale=2; ${1}/60" | bc)
min=$(echo ${time_mins} | cut -d'.' -f1)
secs="0.$(echo ${time_mins} | cut -d'.' -f2)"
secs=$(echo ${secs}*60|bc|awk '{print int($1+0.5)}')
fi
echo "${min}m${secs}s"
}
# Helper function for tracking cached build time.
track_build_time () {
COMPLETE_TIME=$(secs_to_human $SECONDS)
if [ -f "$BUILD_TIME_FILE_NAME" ]; then
LAST_FULL_BUILD_TIME=$(cat $BUILD_TIME_FILE_NAME)
if [ "$REBUILD" -eq "0" ]; then
printf "\nCached build time: $COMPLETE_TIME"
else
printf "\nCurrent build time: $COMPLETE_TIME"
fi
printf "\nLast full build time: $LAST_FULL_BUILD_TIME"
echo $COMPLETE_TIME > $BUILD_TIME_FILE_NAME
else
echo $COMPLETE_TIME > $BUILD_TIME_FILE_NAME
printf "\nCurrent build time: $COMPLETE_TIME"
fi
}
cached_build() {
# rm -rf $PLATFORM_CACHE_DIR/*
# Check that we're in a build phase on Platform.sh first.
if [[ -v PLATFORM_CACHE_DIR ]]; then
printf "\nPlatform.sh build cache handling.\n"
# Compare current dependencies to cached versions.
if [ -f "$CACHED_LOCKFILE_NAME" ] && [ -f "$CACHED_VARIABLES_FILE_NAME" ]; then
printf "\nBuild cache found."
# Restore current cache to $HOME.
printf "\nTemporarily restoring from build cache.\n"
apply_to_all_dirs 'rsync -ar $PLATFORM_CACHE_DIR/$cache_dir/ $HOME/$cache_dir'
# Compare build cache versions.
# a. From dependencies
export PATH="$HOME/.local/bin:$PATH"
CURRENT_VERSION=$(poetry export | base64)
CACHED_LOCKFILE=$(cat $CACHED_LOCKFILE_NAME)
# b. From .platform.app.yaml defined variables.
CURRENT_VARS=$(echo $PLATFORM_APPLICATION | base64 --decode | jq -r '.variables' | base64)
CACHED_VARS=$(cat $CACHED_VARIABLES_FILE_NAME)
echo $CURRENT_VARS
echo $CACHED_VARS
if [ "$CACHED_LOCKFILE" != "$CURRENT_VERSION" ] || [ "$CACHED_VARS" != "$CURRENT_VARS" ]; then
printf "\nThe environment has changed and must be rebuilt.\n"
printf "\nClearing build cache.\n"
# 1. Remove cached dirs.
printf "\nRemoving cached dirs"
apply_to_all_dirs 'rm -rf $HOME/$cache_dir'
# 2. Run build.
printf "\nRunning build"
build
# 3. Track rebuild.
printf "\nTracking build"
export REBUILD=1
# 4. Cache dirs.
printf "\nCaching dirs"
apply_to_all_dirs 'rsync -ar $HOME/$cache_dir/ $PLATFORM_CACHE_DIR/$cache_dir'
# 5. Generate cache lock file.
printf "\nGenerating new cache lock file"
poetry export | base64 > $CACHED_LOCKFILE_NAME
else
printf "\nNo changes to environment found. Using restored build cache.\n"
fi
else
printf "\nBuild cache lockfiles do not exist."
# 1. Run build.
printf "\nRunning build"
build
# 2. Track rebuild.
printf "\nTracking rebuild"
export REBUILD=1
# 3. Cache dirs.
printf "\nCaching dirs"
apply_to_all_dirs 'rsync -ar $HOME/$cache_dir/ $PLATFORM_CACHE_DIR/$cache_dir'
# 4. Generate cache lock file.
printf "\nGenerating cache lock files"
# a. For dependencies.
export PATH="$HOME/.local/bin:$PATH"
poetry export | base64 > $CACHED_LOCKFILE_NAME
# b. For .platform.app.yaml defined variables.
echo $PLATFORM_APPLICATION | base64 --decode | jq -r '.variables' | base64 > $CACHED_VARIABLES_FILE_NAME
fi
else
printf "\nNot in a Platform.sh build phase. Skipping.\n"
fi
track_build_time $SECONDS
}
# Build the app if cache differs or does not exist yet.
cached_build
@chadwcarlson
Copy link
Author

cached_pip

#!/usr/bin/env bash

# Get the in use python version.
PYTHON_VERSION=$(python -c "import platform; versions=platform.python_version().split('.'); print('.'.join(versions[:2]))")
printf "\nPython version: $PYTHON_VERSION\n"

# Update pip to the latest or specified version.
PIP_VERSION_CURRENT=$(pip -V)
printf "\npip version: $PIP_VERSION_CURRENT"
if [ -z ${PIP_VERSION+x} ]; then 
    printf "\nUpgrading pip ($PIP_VERSION_CURRENT) to the latest version.\n"
    python$PYTHON_VERSION -m pip install --upgrade pip --disable-pip-version-check
else 
    printf "\nUpgrading pip ($PIP_VERSION_CURRENT) to version $PIP_VERSION as configured by user.\n"
    python$PYTHON_VERSION -m pip install pip==$PIP_VERSION --disable-pip-version-check
fi
printf "\nNow using $(pip -V)\n"

cached_poetry.sh

#!/usr/bin/env bash

# Get the in use python version.
PYTHON_VERSION=$(python -c "import platform; versions=platform.python_version().split('.'); print('.'.join(versions[:2]))")
printf "\nPython version: $PYTHON_VERSION\n"

# Install poetry.
printf "\nInstalling Poetry.\n"
export PIP_USER=false
if [ -z ${POETRY_VERSION+x} ]; then 
    printf "\nUsing latest version.\n"
    curl -sSL https://install.python-poetry.org | python$PYTHON_VERSION -
else 
    LATEST=$(curl -s https://api.github.com/repos/python-poetry/poetry/releases | jq -r '.[0].tag_name')
    if [ "$LATEST" != "$POETRY_VERSION" ]; then
        echo "You're using Poetry $POETRY_VERSION, but $LATEST is the latest release version.\n"
    fi
    printf "\nUsing latest version ($LATEST).\n"
    curl -sSL https://install.python-poetry.org | python$PYTHON_VERSION - --version $POETRY_VERSION
fi
export PATH="$HOME/.local/bin:$PATH"
export PIP_USER=true

printf "\nChecking for existing venvs in build cache\n"
poetry env list

# Check lockfile and environment.
poetry env use python$PYTHON_VERSION
poetry env info
poetry env list
poetry check

# Install dependencies (to be cached).
poetry install --sync --without dev

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment