Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,193 changes: 89 additions & 1,104 deletions scripts/github/download-artifacts.sh

Large diffs are not rendered by default.

82 changes: 82 additions & 0 deletions scripts/utils/common.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#!/usr/bin/env bash

set -euo pipefail

# -----------------------------------------------------------------------------------
# Function: resolve_script_dir
# Purpose : Resolves the absolute path of the script's directory, handling symlinks.
# Returns :
# Exports SOURCE, SCRIPT_DIR
# Note : This function relies on BASH_SOURCE, so it must be used in a Bash shell.
# -----------------------------------------------------------------------------------
resolve_script_dir() {
local source="${BASH_SOURCE[0]}"

# Follow symbolic links until we get the real file location
while [ -L "${source}" ]; do
# Get the directory path where the symlink is located
dir="$(cd -P "$(dirname "${source}")" >/dev/null 2>&1 && pwd)"
# Use readlink to get the target the symlink points to
source="$(readlink "${source}")"
# If the source was a relative symlink, convert it to an absolute path
[[ "${source}" != /* ]] && source="${dir}/${source}"
done

# After resolving symlinks, retrieve the directory of the final source
SCRIPT_DIR="$(cd -P "$(dirname "${source}")" >/dev/null 2>&1 && pwd)"

# Exports
export SOURCE="${source}"
export SCRIPT_DIR="${SCRIPT_DIR}"
}

# -----------------------------------------------------------------------------------
# Function: initialize_environment
# Purpose :
# 1) Resolve the script's directory.
# 2) Change into that directory plus an optional subdirectory (if provided).
# 3) Export ENV_ROOT as the new working directory.
#
# Arguments:
# 1) $1 - Relative or absolute path to a subdirectory (optional).
# If omitted or empty, defaults to '.' (the same directory as resolve_script_dir).
#
# Usage Example:
# initialize_environment # Uses the script's directory
# initialize_environment "dev" # Changes to script's directory + "/dev"
# -----------------------------------------------------------------------------------
initialize_environment() {
# Resolve script's directory
resolve_script_dir

# Local variables
local subdir
# Check if a subdirectory argument was passed; default to '.' if not
subdir="${1:-.}"

# Attempt to change into the resolved directory + the subdirectory
if cd "${SCRIPT_DIR}/${subdir}"; then
ENV_ROOT="$(pwd)"
export ENV_ROOT
else
printf "Error: %s\n" "Failed to change directory to '${SCRIPT_DIR}/${subdir}'." >&2 && exit 1
fi
}

require() {
local modules="${1}"
local name="${2}"
local path="${modules}/${name}"
if [[ -f ${path} ]]; then
# shellcheck source=/dev/null
source "${path}"
else
echo "Module '${name}' not found in ${modules}" >&2
exit 1
fi
}

initialize_environment "../.."

require "${ENV_ROOT}/scripts/utils/common" fs.sh
require "${ENV_ROOT}/scripts/utils/common" log.sh
26 changes: 26 additions & 0 deletions scripts/utils/common/fs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/usr/bin/env bash

# shellcheck shell=bash disable=SC2034
set -euo pipefail

# Ensure a directory exists or create if absent
ensure_dir() {
local _dir=$1
[[ -d "$_dir" ]] || mkdir -p -- "$_dir"
}

# Create one global workspace that is wiped automatically
mk_tmp() {
WORKDIR=$(mktemp -d)
trap 'rm -rf -- "$WORKDIR"' EXIT
echo "$WORKDIR"
}

# Human readable byte size KiB/MiB/GiB with one decimal
hr_size() {
local _bytes=${1:-0}
(( _bytes < 1024 )) && { printf "%d B" "${_bytes}"; return; }
(( _bytes < 1048576 )) && { printf "%.1f KiB" "$(bc -l <<< "${_bytes}/1024")"; return; }
(( _bytes < 1073741824 )) && { printf "%.1f MiB" "$(bc -l <<< "${_bytes}/1048576")"; return; }
printf "%.1f GiB" "$(bc -l <<< "${_bytes}/1073741824")"
}
42 changes: 42 additions & 0 deletions scripts/utils/common/log.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#!/usr/bin/env bash

# shellcheck shell=bash disable=SC2034,SC2155
set -euo pipefail

# colour palette
readonly _CLR_RESET="\033[0m"
readonly _CLR_INFO="\033[1;34m" # bright blue
readonly _CLR_WARN="\033[1;33m" # bright yellow
readonly _CLR_ERROR="\033[1;31m" # bright red
readonly _CLR_SUCCESS="\033[1;32m" # bright green
readonly _CLR_DEBUG="\033[1;90m" # dim gray

# core logger
_log() {
local _level=$1; shift
local _msg="$*"
local _stamp
_stamp=$(date +"%Y-%m-%d %H:%M:%S")

# example: INFO _CLR_INFO
local _clr_var="_CLR_${_level^^}"
# shellcheck disable=SC2086
printf "%s %b%-7s%b %s\n" "${_stamp}" "${!_clr_var}" "${_level}" "${_CLR_RESET}" "${_msg}"
}

info() { _log INFO "$*"; }
warn() { _log WARN "$*"; }
error() { _log ERROR "$*"; }
success() { _log SUCCESS "$*"; }

debug() {
[[ ${VERBOSE:-0} -eq 1 ]] && _log DEBUG "$*" || true
}

# timing helpers
_timer_start() { TIMER_START=${SECONDS:-0}; }
_timer_end() {
local _op="$1"
local _elapsed=$(( SECONDS - TIMER_START ))
info "${_op} finished in ${_elapsed}s"
}
84 changes: 84 additions & 0 deletions scripts/utils/github.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#!/usr/bin/env bash

set -euo pipefail

# -----------------------------------------------------------------------------------
# Function: resolve_script_dir
# Purpose : Resolves the absolute path of the script's directory, handling symlinks.
# Returns :
# Exports SOURCE, SCRIPT_DIR
# Note : This function relies on BASH_SOURCE, so it must be used in a Bash shell.
# -----------------------------------------------------------------------------------
resolve_script_dir() {
local source="${BASH_SOURCE[0]}"

# Follow symbolic links until we get the real file location
while [ -L "${source}" ]; do
# Get the directory path where the symlink is located
dir="$(cd -P "$(dirname "${source}")" >/dev/null 2>&1 && pwd)"
# Use readlink to get the target the symlink points to
source="$(readlink "${source}")"
# If the source was a relative symlink, convert it to an absolute path
[[ "${source}" != /* ]] && source="${dir}/${source}"
done

# After resolving symlinks, retrieve the directory of the final source
SCRIPT_DIR="$(cd -P "$(dirname "${source}")" >/dev/null 2>&1 && pwd)"

# Exports
export SOURCE="${source}"
export SCRIPT_DIR="${SCRIPT_DIR}"
}

# -----------------------------------------------------------------------------------
# Function: initialize_environment
# Purpose :
# 1) Resolve the script's directory.
# 2) Change into that directory plus an optional subdirectory (if provided).
# 3) Export ENV_ROOT as the new working directory.
#
# Arguments:
# 1) $1 - Relative or absolute path to a subdirectory (optional).
# If omitted or empty, defaults to '.' (the same directory as resolve_script_dir).
#
# Usage Example:
# initialize_environment # Uses the script's directory
# initialize_environment "dev" # Changes to script's directory + "/dev"
# -----------------------------------------------------------------------------------
initialize_environment() {
# Resolve script's directory
resolve_script_dir

# Local variables
local subdir
# Check if a subdirectory argument was passed; default to '.' if not
subdir="${1:-.}"

# Attempt to change into the resolved directory + the subdirectory
if cd "${SCRIPT_DIR}/${subdir}"; then
ENV_ROOT="$(pwd)"
export ENV_ROOT
else
printf "Error: %s\n" "Failed to change directory to '${SCRIPT_DIR}/${subdir}'." >&2 && exit 1
fi
}

require() {
local modules="${1}"
local name="${2}"
local path="${modules}/${name}"
if [[ -f ${path} ]]; then
# shellcheck source=/dev/null
source "${path}"
else
echo "Module '${name}' not found in ${modules}" >&2
exit 1
fi
}

initialize_environment "../.."

require "${ENV_ROOT}/scripts/utils/github" gh.sh
require "${ENV_ROOT}/scripts/utils/github" cli.sh
require "${ENV_ROOT}/scripts/utils/github" video.sh
require "${ENV_ROOT}/scripts/utils/github" artifact.sh
52 changes: 52 additions & 0 deletions scripts/utils/github/artifact.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#!/usr/bin/env bash
# Requires: gh, jq, unzip

set -euo pipefail

# Convert workflow filename to a lowercase slug (basename without extension)
_slugify() {
local _x=$1
_x="${_x##*/}"
_x="${_x%.*}"
echo "${_x,,}"
}

# Fetch, extract, and post-process artifacts for one workflow.
# Arguments: <repo> <workflow_file> <commit_sha> <output_dir>
process_workflow() {
local _repo=$1 _workflow=$2 _sha=$3 _out=$4

local _slug _run_id
_slug=$(_slugify "${_workflow}")

# Resolve the most relevant run ID
_run_id=$(gh_get_run_id "${_repo}" "${_workflow}" "${_sha}" || true)
if [[ -z ${_run_id} || ${_run_id} == "null" ]]; then
warn "No run found for ${_workflow} @ ${_sha} | skipping"
return 0
fi

info "Workflow ${_workflow}: using run-id ${_run_id}"
ensure_dir "${_out}/${_slug}"

# Temporary location for zips
local _tmp
_tmp=$(mktemp -d)
trap 'rm -rf -- "${_tmp}"' RETURN

# Download & unzip every artifact
gh_get_artifacts "${_repo}" "${_run_id}" | while read -r _id _name _size; do
info "↳ downloading artifact ${_name} ($(hr_size ${_size}))"
local _zip
_zip=$(gh_download_artifact "${_repo}" "${_id}" "${_tmp}")
unzip -q -o "${_zip}" -d "${_out}/${_slug}"
done

# Post-process all raw videos (skip ones already renamed)
find "${_out}/${_slug}" -type f -name "*.mp4" ! -name "recording.mp4" -print0 |
while IFS= read -r -d '' _vid; do
process_video "${_vid}"
done

success "Finished processing ${_workflow}"
}
59 changes: 59 additions & 0 deletions scripts/utils/github/cli.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#!/usr/bin/env bash

# shellcheck shell=bash disable=SC2034,SC2155
set -euo pipefail

parse_cli() {
# defaults
REPO=""
COMMIT_SHA=""
OUTDIR="site/static/artifacts"
VERBOSE=0
WORKFLOWS_CSV=""

# getopt (POSIX long‑only)
local _TEMP
_TEMP=$(getopt -o '' -l repo:,commit:,workflows:,outdir:,verbose,help -- "$@") || {
echo "Try --help" >&2; return 1; }
eval set -- "${_TEMP}"

while true; do
case "$1" in
--repo) REPO="$2"; shift 2;;
--commit) COMMIT_SHA="$2"; shift 2;;
--workflows) WORKFLOWS_CSV="$2"; shift 2;;
--outdir) OUTDIR="$2"; shift 2;;
--verbose) VERBOSE=1; shift;;
--help)
cat <<EOF
Usage: $(basename "$0") [options]
--repo owner/name (defaults to current git remote)
--commit SHA (defaults to HEAD)
--workflows CSV list (defaults to pattern search)
--outdir path (default: site/static/artifacts)
--verbose enable debug logging
EOF
exit 0;;
--) shift; break;;
*) echo "Unknown option: $1" >&2; exit 1;;
esac
done

# derive defaults when unset
[[ -z $REPO ]] && REPO=$(git remote get-url origin 2>/dev/null | sed -E 's#.*/([^/]+/[^/.]+)(\.git)?#\1#')
[[ -z $COMMIT_SHA ]] && COMMIT_SHA=$(git rev-parse HEAD 2>/dev/null)

# expose as readonly
readonly REPO COMMIT_SHA OUTDIR VERBOSE

# workflow list as array
if [[ -n $WORKFLOWS_CSV ]]; then
IFS=',' read -r -a WORKFLOWS <<< "$WORKFLOWS_CSV"
else
shopt -s nullglob
WORKFLOWS=( .github/workflows/{login,share-link,share-with,invite-link}-*.yml )
for i in "${!WORKFLOWS[@]}"; do WORKFLOWS[$i]=$(basename "${WORKFLOWS[$i]}"); done
shopt -u nullglob
fi
readonly -a WORKFLOWS
}
Loading