Skip to content

Instantly share code, notes, and snippets.

@smoser
Last active January 14, 2026 18:02
Show Gist options
  • Select an option

  • Save smoser/0a11e2643b884960c1e5349d4dc0b8c7 to your computer and use it in GitHub Desktop.

Select an option

Save smoser/0a11e2643b884960c1e5349d4dc0b8c7 to your computer and use it in GitHub Desktop.
wolfi get file list and apk info

Random wolfi and tools.

  • get-archive-info - get a tar tvf output and the .APKINFO for every file in the archive.

  • build-stage - throw a bunch of files and see which build. they do not depend on each other (each only builds with the wolfi repo)

    I used this to help create batches of things when changing lots of files.

  • test-installable - its like the c-i test that checks that all packages that were built are installable.

  • docker-run-action - it is like the docker run action

  • get-log - get a build or test log for a package.

#!/bin/bash
# shellcheck disable=SC2015,SC2039,SC2166,SC3043
#
# the goal of this script was to ultimately be given a long
# list of packages and to sort out batches of packages that
# depended on another so that they could be submitted. wolfictl's
# dependency resolution / build-order solving was having problems
# and queing up N batches that could land in order was nice.
#
# Other thing that this does well is give nice per-package logs.
VERBOSITY=0
TEMP_D=""
stderr() { echo "$@" 1>&2; }
fail() { local r=$?; [ $r -eq 0 ] && r=1; failrc "$r" "$@"; }
failrc() { local r="$1"; shift; [ $# -eq 0 ] || stderr "$@"; exit "$r"; }
Usage() {
cat <<EOF
Usage: ${0##*/} [ options ] output-dir/ package1 package2 ....
build packages one by one.
each package builds with access to wolfi repo only.
successful builds populate output-dir/packages/
logs for each build are put in output-dir/logs/<package>.log
summary log written to output-dir/build.log
options:
-h | --help show usage
-v | --verbose increase verbosity
-j | --jobs N use N processes
--skip-test do not 'melange test'
--skip-install do not test install of output.
EOF
}
bad_Usage() { Usage 1>&2; [ $# -eq 0 ] || stderr "$@"; return 1; }
cleanup() {
[ -z "${TEMP_D}" -o ! -d "${TEMP_D}" ] || rm -Rf "${TEMP_D}"
}
debug() {
local level="$1"
shift
[ "${level}" -gt "${VERBOSITY}" ] && return
stderr "${@}"
}
do_build_Usage() {
cat <<EOF
${0##*/} do-build [options] log-dir packages-dir package ...
options:
--summary file write short log to file
EOF
}
sumlog() {
local f="$SUMLOG"
if [ -n "$f" ]; then
echo "$@" >> "$f"
else
echo "$@" 1>&2
fi
}
vrun() {
local rc=0
stderr "running:" "$@"
"$@" || rc=$?
[ $rc -eq 0 ] && stderr "success" || stderr "fail: $rc"
stderr
return $rc
}
dothing() {
local msg="$1" log="$2" rc=0 rs="SUCCESS" opstart="$SECONDS"
shift 2
sumlog "START $msg [${log##*/}]"
vrun "$@" >>"$log" 2>&1 ||
{ rc=$? ; rs=FAIL; }
sumlog "END $msg $rs [$((SECONDS-opstart))s]"
return $rc
}
build_index() {
# FIXME: this should probably lock
local pdir="$1" key="" d=""
shift
[ $# -ge 1 ] && key=$1 && shift
for arch in x86_64 aarch64; do
[ -d "$pdir/$arch" ] || continue
( cd "$pdir/$arch" && set -- *.apk;
[ -f "$1" ] || {
stderr "no .apk files in $PWD ($1 not a file)";
exit 1;
}
melange index ${key:+"--signing-key=$key"} "$@"
) || {
stderr "failed to generate index in $pdir/$arch"
return 1
}
done
return 0
}
publish() {
local src="${1%/}" dest="$2" key="$3"
rsync -a --include="*.apk" --include='*/' \
--exclude='*' "$src/" "$dest"
build_index "$dest" ${key:+"$key"} ||
fail "failed to build index in $dest"
}
# shellcheck disable=SC2115
empty_dir() {
local d="$1"
[ "$d" = "/" -o "$d" = "" ] &&
{ stderr "empty_dir '$d' - not doing that"; return 1; }
[ -d "$d" ] || return 0
(
# all gets done in a subprocess do i don't have to change dir back.
shopt -s nullglob
cd "$d" || { stderr "could not cd '$d'"; exit 1; }
set -- * .?*
[ $# -eq 0 ] && exit 0
lf=$(mktemp) || exit 1
trap 'rm -Rf "$lf"' EXIT
# don't want to spew errors
rm -Rf "$@" >"$lf" 2>&1 && exit 0
rrc0=$? crc=0 rrc1=0
chmod -R u+w "$@" >> "$lf" 2>&1
crc=$?
if rm -Rf "$@" >"$lf" 2>&1; then
stderr "empty_dir($d) required chmod u+w (rrc0=$rrc0 crc=$crc)"
exit 0
fi
rrc1=$?
stderr "empty_dir($d) failed $rrc1 (rrc0=$rrc0 crc=$crc)"
tail -n 10 "$lf" 1>&2
exit $rrc1
)
}
do_build() {
local short_opts="hv"
local long_opts="help,skip-build,skip-test,skip-install,summary:,verbose"
local getopt_out=""
getopt_out=$(getopt --name "${0##*/}" \
--options "${short_opts}" --long "${long_opts}" -- "$@") &&
eval set -- "${getopt_out}" ||
{ bad_Usage; return; }
local summary="" dbuild=true dtest=true dinstall=true
while [ $# -ne 0 ]; do
{ cur="$1"; next="$2"; }
case "$cur" in
-h|--help) Usage ; exit 0;;
--summary) summary="$next"; shift;;
--skip-build) dbuild=false;;
--skip-test) dtest=false;;
--skip-install) dinstall=false;;
-v|--verbose) VERBOSITY=$((VERBOSITY+1));;
--) shift; break;;
esac
shift;
done
local logd="$1" trepo="$2"
shift 2
SUMLOG="$summary"
mkdir -p "$logd" "$trepo" || fail "failed making dirs"
TEMP_D=$(mktemp -d "${TMPDIR:-/tmp}/build-XXXXXX") ||
fail "failed to make tempdir"
trap cleanup EXIT
local lkey=""
[ -f "local-melange.rsa" ] && lkey="$PWD/local-melange.rsa"
local merepos="" ierepos="" r="" stages=""
# a 'stage' here would be the output of another build-stage
# where we include it's packages for use.
# shellcheck disable=SC2206
stages=( ${MY_STAGES} )
for r in "${stages[@]}"; do
merepos="$merepos --repository-append=$r/packages"
ierepos="$ierepos --repo=$r/packages"
done
merepos=${merepos# }
ierepos=${ierepos# }
local pkg="" pstart="" log="" rs="" rc="" pfail=""
for pkg in "$@"; do
pstart=$SECONDS
pkg=${pkg%.yaml}
log="$logd/$pkg.log"
: > "$log" || fail "failed writing to $log"
echo " ${#stages[@]} stages - ${stages[*]}" >> "$log"
echo "start $pkg $log"
pdir="$TEMP_D/$pkg"
prepo="$pdir/repo"
tdir="$TEMP_D/$pkg.tmp"
mkdir -p "$prepo" "$tdir" || fail "failed create prepo dir"
pfail=""
if [ $dbuild = true ]; then
dothing "$pkg build" "$log" \
env TMPDIR="$tdir" \
make "package/$pkg" "REPO=$prepo" \
MELANGE_EXTRA_OPTS="--out-dir=$prepo $merepos" ||
pfail=build
empty_dir "$tdir"
fi
if [ -z "$pfail" ] && [ $dtest = true ]; then
dothing "$pkg test" "$log" \
env TMPDIR="$tdir" \
make "test/$pkg" "REPO=$prepo" \
"MELANGE_EXTRA_OPTS=$merepos" ||
pfail="test"
empty_dir "$tdir"
fi
if [ -z "$pfail" ] && [ $dinstall = true ]; then
# shellcheck disable=SC2086
dothing "$pkg test-install" "$log" \
test-installable "--repo=$prepo" $ierepos ||
pfail="install"
empty_dir "$tdir"
fi
if [ -z "$pfail" ]; then
## FIXME: need a local signing key here. just using local-signing
dothing "$pkg publish" "$log" \
publish "$prepo" "$trepo" ${lkey:+"$lkey"} ||
pfail="publish"
fi
if [ -n "$pfail" ]; then
rs="FAIL/$pfail"
fails="$fails $pkg"
else
rs="PASS"
fi
rm -Rf "$pdir"
echo "finish $pkg $rs $log [$((SECONDS-pstart))s]"
sumlog "RESULT $pkg $rs [$((SECONDS-pstart))s] ${log##*/}"
done
if [ -n "$fails" ]; then
return 1
fi
return 0
}
main() {
local short_opts="hj:v"
local long_opts="help,jobs:,skip-build,skip-test,skip-install,verbose"
local getopt_out=""
getopt_out=$(getopt --name "${0##*/}" \
--options "${short_opts}" --long "${long_opts}" -- "$@") &&
eval set -- "${getopt_out}" ||
{ bad_Usage; return; }
local cur="" next="" pt="" jobs=1
pt=( )
while [ $# -ne 0 ]; do
# shellcheck disable=SC2034
{ cur="$1"; next="$2"; }
case "$cur" in
-h|--help) Usage ; exit 0;;
-j|--jobs) jobs=$next; shift;;
-v|--verbose) VERBOSITY=$((VERBOSITY+1));;
--skip-build|--skip-test|--skip-install) pt[${#pt[@]}]="$cur";;
--) shift; break;;
esac
shift;
done
mkdir -p ~/.parallel;
: > ~/.parallel/will-cite
[ $# -ge 2 ] || {
bad_Usage "must provide arguments. got $# ($*) expect 2+";
return;
}
local outd="$1" outd_in="$1"
shift
mkdir -p "$outd_in" || fail "failed create out-dir '$outd_in'"
outd=$(cd "$outd_in" && pwd) || fail "failed to get real path to $outd_in"
mkdir -p "$outd/packages" "$outd/logs" ||
fail "failed mkdir $outd/packages $outd/logs"
TEMP_D=$(mktemp -d "$outd/.build-XXXXXX") ||
fail "failed to make tempdir"
trap cleanup EXIT
local workd="$TEMP_D"
local pkg="" sumlog="$outd/logs/short.log" fails=""
local failn=0 passn=0
: > "$sumlog"
echo "building $# packages in $jobs jobs. output in $outd."
TMPDIR="$workd"
parallel \
"--jobs=$jobs" --line-buffer -- \
"$0" do-build "${pt[@]}" "--summary=$sumlog" \
"$outd/logs" "$outd/packages" \
::: "$@"
rc=$?
awk '$1 == "RESULT" && $3 ~ "^FAIL" { print $2 }' "$sumlog" > "$outd/logs/fails"
awk '$1 == "RESULT" && $3 ~ "PASS" { print $2 }' "$sumlog" > "$outd/logs/passes"
sort "$outd/logs/fails" "$outd/logs/passes" > "$outd/logs/attempts"
failn=$(wc -l < "$outd/logs/fails")
passn=$(wc -l < "$outd/logs/passes")
echo "$((failn+passn)) attempts. $failn fails. $passn pass. took $((SECONDS))s"
[ "$failn" -eq 0 ]
}
if [ "$1" = "do-build" ]; then
shift
do_build "$@"
exit
elif [ "$1" = "publish" ]; then
shift
publish "$@"
exit
fi
main "$@"
# vi: ts=4 expandtab
#!/bin/bash
#
# needs usage and stuff, but just commit it for now.
#
# Basically this calls 'build-stage' until it doesn't
# accomplish anything. so you will end up with 'stageXX'
# directories each having built more things.
#
# When I did this with the 184 ruby 3.2 files changing to
# ruby 3.3, it took 6 stages. All packages are built in
# isolation in a stage, only given access to previous
# stages and archive.
set -o pipefail
Usage() {
cat <<EOF
Usage: ${0##*/} out-base-dir package [...]
build provided packages in stages
out-base-dir will get logs and stage builds.
EOF
}
vrun() {
local out="$1"
local start=$SECONDS
shift
{
echo "execute: $*"
time "$@"
r=$?
echo "r=$r [$((SECONDS-start))s]"
} 2>&1 | tee "$out"
}
check_output() {
local rc="$1" stageoutd="$2" missing=""
for f in logs/fails logs/passes ; do
[ -e "$stageoutd/$f" ] || {
echo "$f in $stageoutd/ did not exist" 1>&2
missing="$missing $f"
}
[ -f "$stageoutd/$f" ] || {
echo "$f in $stageoutd/ was not a file"
missing="${missing} $f"
continue
}
[ -r "$stageoutd/$f" ] || {
missing="${missing} $f"
echo "$f in $stageoutd/ was not readable"
continue
}
done
if [ -z "$missing" ]; then
return 0
fi
echo "rc=$rc stage output dir $stageoutd missing ${missing# }"
return "$rc"
}
if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
Usage
exit 0
fi
[ $# -gt 1 ] || {
Usage 1>&2
echo "got $# , need more than 1"
exit 1
}
outd="$1"
shift
[ -d "$outd" ] || mkdir -p "$outd" ||
{ echo "failed to create $outd"; exit 1; }
outd=$(cd "$outd" && pwd) ||
{ echo "failed to get full path to $outd"; exit 1; }
njobs=16
n=1
max=20
n=0
to_build="$*"
to_buildn=$#
lto_buildn=0
lstage=""
MY_STAGES=""
while n=$((n+1)) && [ $n -lt $max ]; do
if [ -n "$lstage" ]; then
to_build=$(cat "$outd/$lstage/logs/fails")
to_buildn=$(wc -l < "$outd/$lstage/logs/fails")
fi
if [ "$to_buildn" -eq 0 ]; then
echo "stage '$lstage' had no failures"
exit 0
fi
if [ "$to_buildn" -eq "$lto_buildn" ]; then
echo "Quitting, stage $lstage did not make progress ($to_buildn fails)"
exit 1
fi
stage=$(printf "stage%02d" "$n")
echo "===== stage $stage to_buildn=$to_buildn (lstage=$lstage) ====="
MY_STAGES="${MY_STAGES:+${MY_STAGES} }$outd/$lstage"
# shellcheck disable=SC2086
set -- $to_build
vrun "$outd/log-$stage.txt" \
env MY_STAGES="$MY_STAGES" \
build-stage "-j$njobs" "$outd/$stage" "$@"
check_output "$?" "$outd/$stage" || {
echo "stage $stage failed without producing fails/passes in $outd/$stage"
exit 1
}
lstage=$stage
lto_buildn=$to_buildn
fails=$(wc -l < "$outd/$stage/logs/fails")
passes=$(wc -l < "$outd/$stage/logs/passes")
echo "==== stage $stage attempts=$to_buildn fails=$fails passes=$passes ==="
done
echo "Quitting n=$n"
#!/bin/sh
# shellcheck disable=SC3043,SC2059,SC2015,SC2162
#
# this is here as demonstration of 'gh' usage and cutting up a mega-pr
# into smaller prs.
# I had made https://github.com/wolfi-dev/os/pull/45833 which changed 555
# packages one commit each (using a python program) and then
# needed those split up to get them landed.
#
# To do so:
# git log --no-decorate --no-abbrev-commit --format=oneline \
# upstream/main..test/tw/ldd-check-the-things > list.txt
# create-multiple-prs < list.txt
fail() { echo "$@" 1>&2; exit 1; }
vr() { echo "$ $*" 1>&2; "$@"; return; }
finish() {
local branch="$1" num="$2" numpad="" githubname="smoser" out=""
cat >pr-body.txt <<EOF
This cleans up use of ldd-check, replacing
test/ldd-check with test/tw/ldd-check and dropping
the defaults where applicable.
EOF
numpad=$(printf "%02d" "$num")
echo "finish $branch [$num]"
vr git push $githubname HEAD || fail "failed push $githubname HEAD"
out=$(vr gh pr create \
--label="automated pr" \
--label="smoser/tw-ldd-check" \
--base=wolfi-dev/os/main --body-file=pr-body.txt \
--title="tw/ldd-check cleanup batch $numpad" \
--repo=wolfi-dev/os --base=main \
"--head=$githubname:$branch" </dev/null) || fail "bad - $branch/$num"
echo "$branch - $out" | tee -a prs.txt
#gh pr create --label="automated pr" --draft --body-file=pr-body.txt --title="tw/ldd-check cleanup batch 1" --base=main --repo=wolfi-dev/os --head=smoser:tw/ldd-check-cleanup-01
}
cutup() {
local branchfmt="tw/ldd-check-cleanup-%02d"
local branchnum=1 batchsize=15
while read commit _desc; do
fullnum=$((fullnum+1))
if [ $((fullnum%batchsize)) -eq 1 ]; then
branchname=$(printf "$branchfmt" "$(((fullnum/batchsize)+1))")
vr git checkout -b "$branchname" upstream/main &&
vr git reset --hard upstream/main ||
fail "failed branch create for $branchname"
fi
vr git cherry-pick "$commit" || fail "cherry-pick $commit failed"
if [ $((fullnum%batchsize)) -eq 0 ]; then
finish "$branchname" "$((fullnum/batchsize))" || fail
branchnum=$((branchnum+1))
[ $branchnum -gt 40 ] && { echo "skipping out"; exit 0; }
fi
done
if [ "$fullnum" -eq 0 ]; then
fail "didn't get any input?"
fi
if [ $((fullnum%batchsize)) -ne 0 ]; then
finish "$branchname" $((fullnum/batchsize)) || fail
fi
}
# run this with input of 'git log upstream/main..HEAD
# git log --no-decorate --no-abbrev-commit --format=oneline upstream/main..HEAD | go-cut.sh
cutup
#!/bin/bash
# shellcheck disable=SC2015,SC2039,SC2166,SC3043
IMAGE="ghcr.io/wolfi-dev/sdk:latest"
VERBOSITY=0
TEMP_D=""
stderr() { echo "$@" 1>&2; }
fail() { local r=$?; [ $r -eq 0 ] && r=1; failrc "$r" "$@"; }
failrc() { local r="$1"; shift; [ $# -eq 0 ] || stderr "$@"; exit "$r"; }
Usage() {
cat <<EOF
Usage: ${0##*/} [ options ] command args
Taken from .github/actions/docker-run/action.yaml
Run stuff in a container.
options:
-h | --help help
-v | --verbose increase verbosity
-i | --image IMAGE image to run [default=$IMAGE]
-w | --workdir DIR default is current working dir
-e | --entrypoint entrypoint - default is /bin/bash
-t | --tty use a tty - default is to give tty if /dev/stdin is tty
--no-tty do not use --tty
--pt OPT pass through to docker run
Example:
${0##*/} --repo=packages packages/*.apk
EOF
}
bad_Usage() { Usage 1>&2; [ $# -eq 0 ] || stderr "$@"; return 1; }
cleanup() {
[ -z "${TEMP_D}" -o ! -d "${TEMP_D}" ] || rm -Rf "${TEMP_D}"
}
debug() {
local level="$1"
shift
[ "${level}" -gt "${VERBOSITY}" ] && return
stderr "${@}"
}
main() {
local short_opts="hi:w:e:tr:v"
local long_opts="help,entrypoint:,image:,no-tty,no-priv,tty,pt:,volume:,workdir:,verbose"
local getopt_out=""
getopt_out=$(getopt --name "${0##*/}" \
--options "${short_opts}" --long "${long_opts}" -- "$@") &&
eval set -- "${getopt_out}" ||
{ bad_Usage; return; }
local cur="" next="" pt="" priv=true
local tty="" image="$IMAGE" workdir="$PWD" entrypoint="/bin/bash"
pt=( )
while [ $# -ne 0 ]; do
{ cur="$1"; next="$2"; }
case "$cur" in
-h|--help) Usage ; exit 0;;
-i|--image) image=$next; shift;;
-w|--workdir) workdir=$next; shift;;
-e|--entrypoint) entrypoint=$next; shift;;
-t|--tty) tty=true;;
--no-tty) tty=false;;
--pt) pt[${#pt[@]}]="$next"; shift;;
-r|--repo) repo="$next"; shift;;
--volume) volumes[${#volumes[@]}]="--volume=$next"; shift;;
-v|--verbose) VERBOSITY=$((VERBOSITY+1));;
--no-priv) priv=false;;
--) shift; break;;
esac
shift;
done
local privops="" ttyflag=""
if [ -z "$tty" ] ; then
[ -t 0 ] && tty="true" || tty=false
fi
[ $tty = "true" ] && ttyflag="--tty"
local home="${HOME:-/home/user}"
case "$image" in
*.*/*) :;;
*) image="cgr.dev/chainguard-private/$image";;
esac
case "$entrypoint" in
none) entrypoint="";;
esac
privops=(
--privileged
--security-opt="seccomp=unconfined"
--security-opt="apparmor:unconfined" \
)
[ "$priv" = "true" ] || privops=( )
docker run \
"${privops[@]}" \
--volume="$workdir:$workdir" \
--volume="$home:$home" \
"${volumes[@]}" \
--workdir="$workdir" \
${entrypoint:+"--entrypoint=${entrypoint}"} \
--env "HOME=$home" \
--interactive ${ttyflag:+"${ttyflag}"} \
"${pt[@]}" \
"$image" "$@"
}
main "$@"
# vi: ts=4 expandtab
#!/bin/bash
# shellcheck disable=SC2015,SC2039,SC2166,SC3043
#
# the goal of this script was to ultimately be given a long
# list of packages and to sort out batches of packages that
# depended on another so that they could be submitted. wolfictl's
# dependency resolution / build-order solving was having problems
# and queing up N batches that could land in order was nice.
#
# Other thing that this does well is give nice per-package logs.
VERBOSITY=0
TEMP_D=""
WOLFI_ARCHIVE="https://packages.wolfi.dev/os"
WOLFI_ARCHIVE="https://apk.cgr.dev/chainguard"
ENTERPRISE_ARCHIVE="https://apk.cgr.dev/chainguard-private"
EXTRAS_ARCHIVE="https://packages.cgr.dev/extras"
DEF_ARCHIVE="$WOLFI_ARCHIVE"
stderr() { echo "$@" 1>&2; }
fail() { local r=$?; [ $r -eq 0 ] && r=1; failrc "$r" "$@"; }
failrc() { local r="$1"; shift; [ $# -eq 0 ] || stderr "$@"; exit "$r"; }
Usage() {
cat <<EOF
Usage: ${0##*/} [ options ] output-dir/ [package1 package2] ....
Grab information about packages in archive.
options:
-h | --help show usage
-v | --verbose increase verbosity
-j | --jobs N use N processes - default to 1 per cpu
-a | --archive A use archive rather than default (wolfi)
--arch ARCH use arch rather than current arch
EOF
}
bad_Usage() { Usage 1>&2; [ $# -eq 0 ] || stderr "$@"; return 1; }
cleanup() {
[ -z "${TEMP_D}" -o ! -d "${TEMP_D}" ] || rm -Rf "${TEMP_D}"
}
debug() {
local level="$1"
shift
[ "${level}" -gt "${VERBOSITY}" ] && return
stderr "${@}"
}
apk_info_Usage() {
cat <<EOF
${0##*/} apk-info [options] url output-base
EOF
}
# shellcheck disable=SC2120
get_arch() {
local archin="$1" unamem=""
[ -n "$archin" ] && { echo "$archin"; return 0; }
unamem=$(uname -m) || { stderr "failed to get 'uname -m'"; return 1; }
case "$unamem" in
x86_64|aarch64) : ;;
*) stderr "do not know what to do with uname -m output '$unamem'"
return 1;;
esac
echo "$unamem"
}
dl() {
local url="$1" dest="$2" out="" rc=""
[ $# -lt 2 ] && { stderr "dl got $# args ($*) needed 2"; return 1; }
shift 2
set -- ${HTTP_AUTH:+--user "user:${HTTP_AUTH}"} --fail --location "$@"
if [ -z "$dest" -o "$dest" = "-" ]; then
curl --quiet "$@" "$url"
return
fi
out=$(curl "$@" --output "$dest" "$url" 2>&1) &&
return 0
rc=$?
echo "$out" 1>&2
echo "failed $rc dl $url to $dest" 1>&2
return $rc
}
apk_info() {
local short_opts="hv"
local long_opts="help,verbose"
local getopt_out=""
getopt_out=$(getopt --name "${0##*/}" \
--options "${short_opts}" --long "${long_opts}" -- "$@") &&
eval set -- "${getopt_out}" ||
{ bad_Usage; return; }
local cur="" next=""
local outbase="" url="" keep_apk=false
while [ $# -ne 0 ]; do
{ cur="$1"; next="$2"; }
case "$cur" in
-h|--help) Usage ; exit 0;;
-v|--verbose) VERBOSITY=$((VERBOSITY+1));;
--) shift; break;;
esac
shift;
done
local url="$1" outbase="$2" tmpd="" out=""
shift 2
TEMP_D=$(mktemp -d "${TMPDIR:-/tmp}/apk-info-XXXXXX") ||
fail "failed to make tempdir"
trap cleanup EXIT
tmpd="${TEMP_D}"
bn=${url##*/}
case "$url" in
http*) dl "$url" "$tmpd/$bn" || return;;
*) cp "$url" "$tmpd/$bn" || fail "failed copy $url";;
esac
tar --warning=none --to-stdout -xf "$tmpd/$bn" --occurrence=1 .PKGINFO > "$tmpd/info" &&
mv "$tmpd/info" "${outbase}info" ||
fail "failed reading .PKGINFO"
tar --warning=none -tvf "$tmpd/$bn" > "$tmpd/list" &&
mv "$tmpd/list" "${outbase}flist" ||
fail "$url: could not get list"
if [ "$keep_apk" = "true" ]; then
mv "$tmpd/$bn" "${outbase}apk" ||
fail "$url: failed renaming apk"
fi
echo "${outbase##*/} ${SECONDS}s"
return 0
}
main() {
local short_opts="a:hj:v"
local long_opts="arch:,archive:,help,jobs:,verbose"
local getopt_out=""
getopt_out=$(getopt --name "${0##*/}" \
--options "${short_opts}" --long "${long_opts}" -- "$@") &&
eval set -- "${getopt_out}" ||
{ bad_Usage; return; }
local cur="" next="" pt="" jobs=0 archives="" arch=""
pt=( )
archives=( )
while [ $# -ne 0 ]; do
# shellcheck disable=SC2034
{ cur="$1"; next="$2"; }
case "$cur" in
-h|--help) Usage ; exit 0;;
-j|--jobs) jobs=$next; shift;;
-v|--verbose) VERBOSITY=$((VERBOSITY+1));;
-a|--archive)
case "$next" in
enterprise) archives[${#archives[@]}]="$ENTERPRISE_ARCHIVE" ;;
extras) archives[${#archives[@]}]="$EXTRAS_ARCHIVE";;
wolfi) archives[${#archives[@]}]="$WOLFI_ARCHIVE";;
*) archives[${#archives[@]}]="$next";;
esac
shift;;
--arch) arch=$next; shift;;
--) shift; break;;
esac
shift;
done
mkdir -p ~/.parallel;
: > ~/.parallel/will-cite
if [ "$jobs" = "0" ]; then
local out=""
[ -f /proc/cpuinfo ] &&
out=$(grep -c processor /proc/cpuinfo) && jobs=$((out*2))
fi
[ $# -ge 1 ] || {
bad_Usage "must provide arguments. got $# ($*) expect 1+";
return;
}
local outd="$1" outd_in="$1" packages="" archive=""
shift
packages=( "$@" )
mkdir -p "$outd_in" || fail "failed create out-dir '$outd_in'"
outd=$(cd "$outd_in" && pwd) || fail "failed to get real path to $outd_in"
if [ "${#archives[@]}" = "0" ]; then
archives=( "$DEF_ARCHIVE" )
elif [ "${#archives[@]}" -gt 1 ]; then
# TODO : more than one archive
fail "cannot have more than one archive, sorry"
fi
archive=${archives[0]}
archive=${archive%/}
if [ "$archive" = "$ENTERPRISE_ARCHIVE" ]; then
if [ -z "$HTTP_AUTH" ]; then
HTTP_AUTH=$(chainctl auth token --audience=apk.cgr.dev) ||
{ stderr "failed to get token for apk.cgr.dev with chainctl"; return 1; }
fi
fi
if [ -z "$arch" ]; then
arch=$(get_arch) || fail "failed to get arch"
fi
TEMP_D=$(mktemp -d "$outd/.build-XXXXXX") ||
fail "failed to make tempdir"
trap cleanup EXIT
local workd="$TEMP_D"
local jsonl="$workd/archive.json" fullflat="$workd/archive.flat" flat=""
dl "$archive/$arch/APKINDEX.tar.gz" "$workd/apkindex.tar.gz" || {
stderr "failed to get index for $archive/$arch"
return 1
}
apkrane ls --latest --json "$workd/apkindex.tar.gz" > "$jsonl" || {
stderr "failed apkrane ls --latest --json $archive/$arch/APKINDEX.tar.gz";
return 1;
}
# get space delimited origin, version, name, size, filename
local rmold=true jqr=""
jqr='"\(.Origin) \(.Version) \(.Name) \(.Size) \(.Name)-\(.Version).apk"'
jq -r "$jqr" "$jsonl" > "$fullflat.raw"
sort <"$fullflat.raw" > "$fullflat"
local numorigins="" origins="$workd/origins.list"
local fullcurflat="$workd/archive-cur.flat"
## there will be only one package that has the same name as its origin
## so we assume that that package is latest.
## that way we can filter binaries with other versions
awk '$1 == $3 { printf("%s %s\n", $1, $2); }' "$fullflat" | sort > "$origins"
numorigins=$(wc -l < "$origins")
# fullcurflat gets only those packages that have the same version as the origin
grep --fixed-strings --file="$origins" "$fullflat" > "$fullcurflat"
if [ ${#packages[@]} -eq 0 ]; then
flat="$fullcurflat"
rmold=true
else
rmold=false
flat="$workd/packages.flat"
{
for p in "${packages[@]}"; do
awk '$1 == n { print $0 }' "n=$p" "$fullcurflat"
done
} > "$flat"
fi
local numbins="" dlsize="" pinput="$workd/p-input"
numbins=$(wc -l < "$flat")
# remove any .flist/.info files that are not in fullflat
local archivebins="$workd/archive-bins.list"
local dirbins="$workd/dir-bins.list"
local oldbins="$workd/old-bins.list"
local newbins="$workd/new-bins.list"
local existbins="$workd/existing-bins.list"
local newfull="$workd/new-full.flat"
awk '{ printf("%s-%s.apk\n", $3, $2); }' "$flat" > "$archivebins"
( cd "$outd" &&
find . -maxdepth 1 -name "*.flist" -type f |
sed -e 's,^[.]/,,' -e 's,.flist$,,' -e 's,$,.apk,') > "$dirbins"
sort "$archivebins" "$archivebins" "$dirbins" | uniq -u > "$oldbins"
sort "$archivebins" "$dirbins" "$dirbins" | uniq -u > "$newbins"
sort "$archivebins" "$dirbins" | uniq -d > "$existbins"
existed=$(wc -l < "$existbins")
grep --fixed-strings --file="$newbins" "$flat" > "$newfull"
dlsize=$(awk '{sum+=$4}; END {printf("%d\n", sum/1024/1024)}' "$newfull")
awk '{base=$1; sub("[.]apk$","",base); printf("%s/%s\n%s/%s.\n", burl, $1, outd, base) }' \
burl="$archive/$arch" "outd=$outd" "$newbins" >"$pinput" ||
fail "failed to read '$newbins' into '$pinput'"
numnew=$(wc -l <"$newbins")
numold=$(wc -l <"$oldbins")
echo "$numorigins total origins. $numnew (${dlsize}MiB) new APKs." \
"$existed existing APK. $jobs jobs"
# get output-base url
env TMPDIR="$workd" ${HTTP_AUTH:+HTTP_AUTH="${HTTP_AUTH}"} \
parallel \
"--jobs=$jobs" --line-buffer --max-replace-args=2 -- \
"$0" apk-info "${pt[@]}" < "$pinput"
rc=$?
if [ "$rmold" = "true" ] && [ "$numold" -ge 1 ]; then
echo "cleaning local data for $numold local binaries not in archive"
( cd "$outd" && while read bin ; do rm -f "${bin%.apk}".*; done ) < "$oldbins"
fi
echo "finished $numorigins origins, $numbins apks" \
"($existed existing, $numnew new [${dlsize}MiB]) in ${SECONDS}s. Exiting $rc"
exit $rc
}
if [ "$1" = "apk-info" ]; then
shift
apk_info "$@"
exit
fi
main "$@"
# vi: ts=4 expandtab
#!/bin/sh
# shellcheck disable=SC2015,SC2039,SC2166,SC3043,SC2059
archives="
wolfi:https://apk.cgr.dev/chainguard
enterprise:https://apk.cgr.dev/chainguard-private
extras:https://packages.cgr.dev/extras
"
stderr(){ echo "$@" 1>&2; }
fail() { echo "$@" 1>&2; exit 1; }
dl() {
local url="$1" dest="$2" out="" rc=""
[ $# -lt 2 ] && { stderr "dl got $# args ($*) needed 2"; return 1; }
shift 2
set -- ${HTTP_AUTH:+--user "user:${HTTP_AUTH}"} --fail --location "$@"
if [ -z "$dest" -o "$dest" = "-" ]; then
curl --quiet "$@" "$url"
return
fi
out=$(curl "$@" --output "$dest" "$url" 2>&1) &&
return 0
rc=$?
echo "$out" 1>&2
echo "failed $rc dl $url to $dest" 1>&2
return $rc
}
json2txt() { jq -r '"\(.Origin) \(.Version) \(.Name) \(.Size) \(.InstalledSize)"' ; }
info() {
local name="$1" arch="$2" t="$3" file="$4"
local origins="" bins="" installed="" size=""
local fmt="" mft=""
origins=$(awk '{printf("%s\n", $1)}' "$file" | sort -u | wc -l)
bins=$(wc -l < "$file")
installed=$(awk '{sum+=$5}; END { printf("%d\n", sum/1024/1024/1024); }' "$file")
size=$(awk '{sum+=$4}; END { printf("%d\n", sum/1024/1024/1024); }' "$file")
local fmt="%-10s %-7s %-6s %8d %8d %8d %8d\n"
if [ "${HEADER:-1}" = "1" ]; then
HEADER=0
mft=$(echo "$fmt" | tr 'd' 's')
printf "$mft\n" "name" "arch" "type" "origins" bins "dl(GB)" "inst(GB)"
fi
printf "$fmt" "$name" "$arch" "$t" "$origins" "$bins" "$size" "$installed"
}
for pair in $archives; do
name=${pair%%:*}
url=${pair#*:}
HTTP_AUTH=""
if [ "$name" = "enterprise" ]; then
if [ -z "$HTTP_AUTH" ]; then
HTTP_AUTH=$(chainctl auth token --audience=apk.cgr.dev) ||
fail "failed to get token for apk.cgr.dev with chainctl"
fi
fi
for arch in x86_64 aarch64; do
index=$name-$arch.tar.gz
if [ ! -f "$index" ]; then
dl "$url/$arch/APKINDEX.tar.gz" "$index" ||
fail "failed download of $url/$arch/APKINDEX.tar.gz"
fi
apkrane ls --json "$index" > "$name-$arch-all.json" 2>/dev/null &&
apkrane ls --json --latest "$index" > "$name-$arch-latest.json" 2>/dev/null ||
fail "failed apkrane ls $index"
json2txt < "$name-$arch-all.json" > "$name-$arch-all.txt" &&
json2txt < "$name-$arch-latest.json" > "$name-$arch-latest.txt"
info "$name" "$arch" "all" "$name-$arch-all.txt"
info "$name" "$arch" "latest" "$name-$arch-latest.txt"
done
done
#!/bin/bash
# shellcheck disable=SC2015,SC2039,SC2166,SC3043
VERBOSITY=0
TEMP_D=""
GL_REPO=${GL_REPO:-wolfi}
GL_ARCH=${GL_ARCH:-amd64}
stderr() { echo "$@" 1>&2; }
fail() { local r=$?; [ $r -eq 0 ] && r=1; failrc "$r" "$@"; }
failrc() { local r="$1"; shift; [ $# -eq 0 ] || stderr "$@"; exit "$r"; }
Usage() {
local me="${0##*/}"
cat <<EOF
Usage: $me [ options ] log-type pkg[/ver] [ver]
get the build or test log for provided pkg and version
log-type is one of 'build' or 'test'
ver is required, but can be provided as part of pkg input as 'pkg/ver'
options:
-v | --verbose increase verbosity
-t | --no-timestamp do not prefix lines with timestamp
-d | --descending output in reverse chronological order (newest first)
-a | --arch ARCH get arch ARCH (default amd64)
'amd64', 'x86_64', 'arm64' or 'aarch64'
default can be set via env GL_ARCH ($GL_ARCH)
-r | --repo REPO wolfi (w), enterprise (e), extras (x)
default can be set via env GL_REPO ($GL_REPO)
shorthand in parenthesis
-l | --limit X limit query to X lines
Examples:
* $me --arch=arm64 build flux-2.7 2.7.5-r1
get arm64 build log for flux-2.7 at 2.7.5-r1
* $me --repo=enterprise build eks-distro-1.32/1.32.29-r1
get build log from enterprise for arch=$GL_ARCH
you can provide pkg and version as 'pkg/ver'
* $me test -t php-8.1-xdebug 3.5.0-r0
get test log from wolfi for php-8.1-xdebug at version 3.5.0-r0
without timestamps
* $me -rx -aamd64 t chainctl/0.2.182-r0
amd64 test log for extras package chainctl
* $me --descending --limit 100 build grep ""
get the most recent 100 lines of a grep build,
version is empty.
EOF
}
bad_Usage() { Usage 1>&2; [ $# -eq 0 ] || stderr "$@"; return 1; }
cleanup() {
[ -z "${TEMP_D}" -o ! -d "${TEMP_D}" ] || rm -Rf "${TEMP_D}"
}
debug() {
local level="$1"
shift
[ "${level}" -gt "${VERBOSITY}" ] && return
stderr "${@}"
}
main() {
local short_opts="a:Dhl:r:tv"
local long_opts="arch:,descending,help,limit:,repo,timestamp,verbose"
local getopt_out=""
getopt_out=$(getopt --name "${0##*/}" \
--options "${short_opts}" --long "${long_opts}" -- "$@") &&
eval set -- "${getopt_out}" ||
{ bad_Usage; return; }
local arch=${GL_ARCH} repo=${GL_REPO}
local cur="" next="" timestamp=true pt="" tac=true
pt=( )
while [ $# -ne 0 ]; do
# shellcheck disable=SC2034
{ cur="$1"; next="$2"; }
case "$cur" in
-h|--help) Usage ; exit 0;;
-v|--verbose) VERBOSITY=$((VERBOSITY+1));;
-a|--arch) arch="$next"; shift;;
-l|--limit) pt+=("--limit=$next"); shift;;
-t|--no-timestamp) timestamp=false;;
-r|--repo) repo="$next"; shift;;
-D|--descending) tac=false;;
--) shift; break;;
esac
shift;
done
## check arguments here
## how many args do you expect?
local ltype="" pkg="" ver="" nargs="$#"
[ $# -ne 0 ] || { bad_Usage "must provide arguments"; return; }
ltype="$1"
shift || bad_Usage "got $nargs arguments, need more"
case "$1" in
*/*) pkg=${1%/*}; ver=${1#*/}; shift;;
*) [ $# -lt 2 ] && { bad_Usage "got $nargs args"; return; }
pkg="$1"
ver="$2"
shift 2;;
esac
case "$ltype" in
b|build) ltype="build";;
t|test) ltype="test";;
*) bad_Usage "log-type must be 'build' or 'test', not $ltype";
return 1;;
esac
case "$arch" in
x86_64|amd64|x) arch=amd64;;
aarch64|arm64|a) arch=arm64;;
*) bad_Usage "bad arch $arch. should be arm64 or amd64"; return;;
esac
case "$repo" in
w|wolfi)
repo=wolfi
project="prod-wolfi-os"
;;
e|enterprise)
repo=enterprise
project="prod-images-c6e5"
;;
x|extras)
repo=extras
project="prod-images-c6e5"
;;
*) bad_Usage "repo must be wolfi (w), enterprise (e), extras (x)"
return;;
esac
TEMP_D=$(mktemp -d "${TMPDIR:-/tmp}/${0##*/}.XXXXXX") ||
fail "failed to make tempdir"
trap cleanup EXIT
local mns="labels.k8s-pod/melange_chainguard_dev/"
local filter="" filters="" cmd="" testbool=""
[ "$ltype" = "build" ] && testbool=false || testbool=true
filters=(
"${mns}arch=$arch"
"${mns}package=$pkg"
${ver:+"${mns}version=$ver"}
"${mns}test=$testbool"
"textPayload:*"
)
for f in "${filters[@]}"; do
filter="${filter}AND $f "
done
filter=${filter#AND }
local outfmt="value(timestamp,textPayload)"
if [ "$timestamp" = "false" ]; then
outfmt="value(textPayload)"
fi
cmd=(
gcloud logging read
--project="$project"
--format="$outfmt"
"${pt[@]}"
"$filter"
)
local start=$SECONDS end=""
debug 1 "execute: ${cmd[*]}"
start=$SECONDS
if [ "$tac" = "true" ]; then
"${cmd[@]}" > "${TEMP_D}/log.out" ||
fail "failed gcloud cmd: ${cmd[*]}"
tac "${TEMP_D}/log.out"
else
"${cmd[@]}" || fail "failed gcloud cmd: ${cmd[*]}"
fi
end=$SECONDS
debug 1 "gcloud logging read took $((end-start))s"
}
main "$@"
# vi: ts=4 expandtab
#!/bin/sh
# shellcheck disable=SC2015,SC2039,SC2166,SC3043
Usage() {
cat <<EOF
${0##*/} [-e/-p] [-o out] pkg[==ver] [...]
options:
-c | --chainguard use libraries.cgr.dev [default]
-p | --pypi use pypi
-o | --output DIR write output to DIR [default .]
-v | --verbose increase verbosity
EOF
}
VERBOSITY=0
TEMP_D=""
stderr() { echo "$@" 1>&2; }
fail() { local r=$?; [ $r -eq 0 ] && r=1; failrc "$r" "$@"; }
failrc() { local r="$1"; shift; [ $# -eq 0 ] || stderr "$@"; exit "$r"; }
bad_Usage() { Usage 1>&2; [ $# -eq 0 ] || stderr "$@"; return 1; }
cleanup() {
[ -z "${TEMP_D}" -o ! -d "${TEMP_D}" ] || rm -Rf "${TEMP_D}"
}
debug() {
local level="$1"
shift
[ "${level}" -gt "${VERBOSITY}" ] && return
stderr "${@}"
}
main() {
local short_opts="hcdo:pv"
local long_opts="help,chainguard,deps,pypi,output:,verbose"
local getopt_out=""
getopt_out=$(getopt --name "${0##*/}" \
--options "${short_opts}" --long "${long_opts}" -- "$@") &&
eval set -- "${getopt_out}" ||
{ bad_Usage; return; }
local cur="" next="" index="" index_name="" index_print="" output="."
local depsflag="--no-deps"
while [ $# -ne 0 ]; do
cur="$1"
next="$2"
case "$cur" in
-h|--help) Usage ; exit 0;;
-c|--chainguard) index_name="cgr";;
-o|--out) output="$next";;
-d|--deps) depsflag="";;
-p|--pypi) index_name="pypi";;
-v|--verbose) VERBOSITY=$((VERBOSITY+1));;
--) shift; break;;
esac
shift;
done
[ $# -ne 0 ] || { bad_Usage "must provide arguments"; return; }
[ -n "$index_name" ] || index_name=cgr
case "$index_name" in
pypi)
index="https://pypi.org/simple"
index_print="$index"
;;
cgr)
local tok=""
if [ -n "$AUTH_TOK" ]; then
tok="$AUTH_TOK"
else
out=$(chainctl auth token --audience="libraries.cgr.dev") ||
fail "could not get token for libraries.cgr.dev"
tok="$out"
fi
index="https://user:[email protected]/python/simple"
index_print="https://libraries.cgr.dev/python/simple"
;;
esac
if [ $((VERBOSITY)) -gt 0 ]; then
stderr "using index $index_print"
fi
TEMP_D=$(mktemp -d "${TMPDIR:-/tmp}/${0##*/}.XXXXXX") ||
fail "failed to make tempdir"
trap cleanup EXIT
local vdir="$TEMP_D/venv" out="" py3=""
out=$(uv venv "$vdir" 2>&1) || { stderr "$out"; fail "uv venv failed"; }
# shellcheck disable=SC1091
out=$(
export UV_LINK_MODE=copy
. "$vdir/bin/activate" &&
uv pip install pip 2>&1
) || fail "uv pip install pip failed: $out"
py3="$vdir/bin/python3"
out=$(cd "$TEMP_D" &&
"$py3" -m pip download ${depsflag:+"$depsflag"} --only-binary=:all: \
--index-url="$index" "$@" 2>&1) || {
stderr "$out"
fail "[$index_name] failed from $index pip download $*"
}
local wheels=""
wheels=$(cd "${TEMP_D}" && echo *.whl)
[ "$wheels" = "*.whl" ] &&
fail "no wheels were downloaded"
mv "${TEMP_D}"/*.whl "$output" ||
fail "failed mv wheel(s) to $output"
stderr "[$index_name] wrote to $output: $wheels"
return 0
}
main "$@"
#!/bin/sh
# shellcheck disable=SC3043
Usage() {
cat <<EOF
${0##*/} [-f|--force] remote [thing]
This is just a very simplistic wrapper around 'git push'
that attempts to sign things before pushing if not already signed.
Use it by disabling git commit signing by default
git config --local commit.gpgsign false
And then just using this wrapper around 'git push' instead
of 'git push'.
git spush origin
This will sign the top commit of not signed and then push.
EOF
}
fail() { [ $# -eq 0 ] || msg "$@"; exit 1; }
msg() { echo "[$0]" "$@" 1>&2; }
checkrun() {
local rc=""
"$@" && return 0
rc=$?
msg "failed [$rc] running $*"
return $rc
}
spush() {
local remote="" out="" refspec="" branch="" force=""
case " $* " in
*\ --force\ *)
# shellcheck disable=SC2046
set -- $(echo "$@" | sed 's,--force,,')
force="--force";;
esac
[ $# -gt 0 ] || fail "must have a remote"
remote="$1"
shift
if [ $# -eq 0 ]; then
set -- "HEAD"
elif [ $# -gt 1 ]; then
fail "cannot handle multiple refspecs"
fi
refspec="$1"
branch=$(checkrun git branch --show-current) || return
case "$refspec" in
HEAD|"$branch") :;;
*) fail "do not know how to handle push refspec $refspec";;
esac
out=$(checkrun git log --pretty="format:%G?" --max-count=1 "$refspec") ||
return
case "$out" in
N)
msg "$refspec: signing"
checkrun git commit --amend --no-edit --gpg-sign || return
;;
*) msg "$refspec: already signed [$out]";;
esac
git push ${force:+"$force"} "$remote" "$refspec"
}
spush "$@"
#!/bin/sh
# this works to force gitsign to use workstation identity if the system
# is a google compute node. Just put it in a place in PATH before
# the real gitsign.
#
# The problem it works around is
# 1. you want gitsign to use the gcp service account to sign
# so that you don't have to do the headless url dance.
# 2. You need to 'gcloud auth login' (and maybe use application-default)
# in order to gain access to some things.
# After you've done step 2, gitsign will
# see $HOME/.config/gcloud/application_default_credentials.json and
# use those credentials.
# The solution here is to just wrap /usr/bin/gitsign and set
# HOME to some non-useful value ($HOME/gitsign-home) so that it
# will use the default gcp service path.
# set GITSIGN_WRAP_LOG to a file path to write debug there.
GITSIGN_WRAP_ENABLE=${GITSIGN_WRAP_ENABLE:-true}
debug() {
[ -n "$GITSIGN_WRAP_LOG" ] || return 0
echo "[$0]" "$@" >> "$GITSIGN_WRAP_LOG"
}
fail() { debug "$@"; echo "[$0]" "$@" 1>&2; exit 1; }
[ -z "$_GITSIGN_WRAPPED" ] ||
fail "exec loop detected real=$real _GITSIGN_WRAPPED=$_GITSIGN_WRAPPED"
case "$GITSIGN_WRAP_ENABLE" in
true|false) :;;
*) fail "GITSIGN_WRAP_ENABLE='$GITSIGN_WRAP_ENABLE'" \
"must be 'true' or 'false'";;
esac
real=""
oifs="$IFS"
IFS=:
if [ -z "$real" ]; then
for p in $PATH; do
# dont call yourself - skip $0
[ "$p/gitsign" -ef "$0" ] && continue
# must be an executable file
[ -f "$p/gitsign" ] && [ -x "$p/gitsign" ] || continue
real="$p/gitsign"
done
fi
IFS="$oifs"
[ -x "$real" ] || fail "did not find real gitsign in $PATH"
pnf="/sys/class/dmi/id/product_name"
ohome=$HOME
# shellcheck disable=SC2162
[ -r "$pnf" ] && read pn < "$pnf" &&
[ "$pn" = "Google Compute Engine" ] &&
HOME="$HOME/gitsign-home"
[ "$GITSIGN_WRAP_ENABLE" = "false" ] && HOME="$ohome"
debug "pn='$pn' real='$real' GITSIGN_WRAP_ENABLE='$GITSIGN_WRAP_ENABLE'" \
"WRAPPED=$_GITSIGN_WRAPPED HOME=$HOME -" "$@"
HOME="$HOME" _GITSIGN_WRAPPED="pid-$$" exec "$real" "$@"
#!/bin/sh
# shellcheck disable=SC3043
fail() { echo "$@" 1>&2; exit 1; }
token() {
local audience="$1" myuid=""
set -- chainctl auth token --audience="$audience"
[ -z "${SUDO_UID}" ] && { "$@"; return; }
myuid=$(id -u) || { echo "id -u failed. seriously" 1>&2; return 1; }
[ "$myuid" = "0" ] && set -- sudo "--user=#${SUDO_UID}" "$@"
"$@"
}
withenv() {
local env="$1"
shift
if [ -n "$SUDO" ]; then
set -- "$SUDO" "$env" "$@"
[ "$verbose" = "true" ] && echo "$@" | sed -e "s/$tok/<token>/" 1>&2
exec "$@"
fi
if [ "$verbose" = "true" ]; then
echo "$env" "$@" | sed -e "s/$tok/<token>/" 1>&2
fi
# shellcheck disable=SC2163
eval "$env" exec '"$@"'
}
main() {
local SUDO="" audience="${AUDIENCE:-apk.cgr.dev}" verbose="false"
if [ "$1" = "-v" ]; then
verbose=true
shift
fi
tok=$(token "$audience") || fail "failed to get token for AUDIENCE=$audience"
if [ "${1##*/}" = "sudo" ]; then
SUDO="$1"
shift
fi
case "$1" in
curl|*/curl)
p="$1"; shift
set -- "$p" --user "user:$tok" "$@"
[ "$verbose" = "true" ] && echo "$@" | sed -e "s/$tok/<token>/" 1>&2
exec "$@"
;;
apk|*/apk)
withenv HTTP_AUTH="basic:$audience:user:$tok" "$@";;
esac
# unknown. just put AUTH_TOK in environment.
withenv AUTH_TOK="$tok" "$@"
}
main "$@"
#!/bin/sh
Usage() {
cat <<EOF
Usage: ${0##*/} pypi-package
List all the releases and files for pypi-package from pypi
Output is
version upload-time delta filename size
delta shows the time in seconds from the first file upload of that release
to the listed file upload.
Packages with large 'delta', have been built post-release.
EOF
}
[ "$1" = "-h" ] || [ "$1" = "--help" ] && { Usage; exit 0 ; }
[ $# -eq 1 ] || { Usage 1>&2; exit 1; }
pkg="$1"
# shellcheck disable=SC3040
set -o pipefail
curl -s "https://pypi.org/pypi/$pkg/json" | jq -r '
.releases
| to_entries
| map({
version: .key,
files: (
.value
| map({
raw_upload_time: .upload_time_iso_8601,
upload_time: (
.upload_time_iso_8601
| sub("\\..*Z$"; "") # strip fractional seconds + Z
),
filename,
size,
upload_time_unix: (
.upload_time_iso_8601
| sub("\\..*Z$"; "Z")
| strptime("%Y-%m-%dT%H:%M:%SZ")
| mktime
)
})
)
})
| map(
. as $v
| $v.files
| map(
. + {
version: $v.version,
oldest_time_unix: ($v.files | map(.upload_time_unix) | min)
}
)
)
| flatten
| map(. + {
delta: (.upload_time_unix - .oldest_time_unix),
oldest_time: (.oldest_time_unix | strflocaltime("%Y-%m-%dT%H:%M:%S"))
})
| sort_by(.version, .delta)
| .[]
| "\(.version) \(.upload_time) \(.delta) \(.filename) \(.size)"
'
#!/bin/sh
# shellcheck disable=SC2015,SC2039,SC2166,SC3043
VERBOSITY=0
TEMP_D=""
stderr() { echo "$@" 1>&2; }
fail() { local r=$?; [ $r -eq 0 ] && r=1; failrc "$r" "$@"; }
failrc() { local r="$1"; shift; [ $# -eq 0 ] || stderr "$@"; exit "$r"; }
Usage() {
cat <<EOF
Usage: ${0##*/} [ options ] packages
Taken partially from wolfi-dev/os/.github/workflows/ci-build.yaml
"Check that packages can be installed with apk add"
options:
-h | --help help
-v | --verbose increase verbosity
Example:
${0##*/} --repo=packages packages/*.apk
EOF
}
bad_Usage() { Usage 1>&2; [ $# -eq 0 ] || stderr "$@"; return 1; }
cleanup() {
[ -z "${TEMP_D}" -o ! -d "${TEMP_D}" ] || rm -Rf "${TEMP_D}"
}
debug() {
local level="$1"
shift
[ "${level}" -gt "${VERBOSITY}" ] && return
stderr "${@}"
}
inside_Usage() {
cat <<EOF
Usage: ${0##*/} inside repo [ repo ... ] -- packages
EOF
}
vr() {
"$@" && return 0;
fail "FAILED[$?]: $*";
}
runv() {
local v="$1"
shift
debug "$v" "execute:" "$@"
"$@"
}
inside() {
local short_opts="hr:v"
local long_opts="help,repo:,verbose"
local getopt_out=""
getopt_out=$(getopt --name "${0##*/}" \
--options "${short_opts}" --long "${long_opts}" -- "$@") &&
eval set -- "${getopt_out}" ||
{ bad_Usage; return; }
local cur="" next=""
while [ $# -ne 0 ]; do
# shellcheck disable=SC2034
{ cur="$1"; next="$2"; }
case "$cur" in
-h|--help) Usage ; exit 0;;
-v|--verbose) VERBOSITY=$((VERBOSITY+1));;
--) shift; break;;
esac
shift;
done
for r in /repos/repo*; do
[ -d "$r" ] || continue
rflags="$rflags --repository=$r"
done
rflags=${rflags# }
ls /repos/
if [ $# -eq 0 ]; then
[ -d /repos/repo0 ] ||
fail "sorry, no /repos/repo0 and no packages"
set -- $(find /repos/repo0 -name "*.apk" -type f)
fi
eroot="/tmp/emptyroot"
vr mkdir -p "$eroot/etc/apk"
vr cp -r "/etc/apk"/* "$eroot/etc/apk"
: > "$eroot/etc/apk/world"
vr mkdir -p "$eroot/lib/apk/db"
vr touch "$eroot/lib/apk/db"/{installed,lock,scripts.tar,triggers}
vr mkdir -p "$eroot" "$eroot/var/cache/apk"
vr apk update "--root=$eroot"
fails=""
n=$#
for f in "$@"; do
echo "== $f =="
vr tar -Oxf "$f" .PKGINFO
apk add --allow-untrusted --simulate \
--root="$eroot" ${rflags} \
"$f"
[ $? -eq 0 ] || fails="$fails $f"
echo
done
echo "tested $# apk installs"
[ -z "$fails" ] && exit 0
set +f
set -- $fails
echo "$# failed install: $fails"
exit 1
}
fullpath() {
local p="$1"
( cd "$p" && pwd )
}
main() {
local short_opts="hr:v"
local long_opts="help,repo:,verbose"
local getopt_out=""
getopt_out=$(getopt --name "${0##*/}" \
--options "${short_opts}" --long "${long_opts}" -- "$@") &&
eval set -- "${getopt_out}" ||
{ bad_Usage; return; }
local cur="" next="" repos="" r=""
while [ $# -ne 0 ]; do
# shellcheck disable=SC2034
{ cur="$1"; next="$2"; }
case "$cur" in
-h|--help) Usage ; exit 0;;
-r|--repo)
r=$(fullpath "$next") ||
fail "failed to get full path to $next"
repos="$repos $r"; shift;;
-v|--verbose) VERBOSITY=$((VERBOSITY+1));;
--) shift; break;;
esac
shift;
done
repos=${repos# }
if [ -z "$repos" ]; then
[ -d ./packages ] ||
fail "no --repo given and default (./packages) is not a dir"
repos="$PWD/packages"
fi
debug 1 "repos: $repos"
set +f
local volumes="" r="" n=0
volumes=""
for r in ${repos}; do
volumes="$volumes --volume=$r:/repos/repo$n"
n=$((n+1))
done
volumes=${volumes# }
runv 1 docker-run-action \
$volumes \
--image=cgr.dev/chainguard/wolfi-base:latest \
--entrypoint=/bin/sh -- -s "inside" "$@" <"$0"
}
if [ "$1" = "inside" ]; then
shift
inside "$@"
fi
main "$@"
# vi: ts=4 expandtab
#!/bin/bash
#
# This was built to grab a list of packages that needed to be updated
# for usrmerge. It outputs 3 different files, each with
# a list of repo, pkgname, origin, version, B, S, US, LIB
# where B=number of files in bin/
# S=number of files in sbin/
# US=number of files in usr/sbin/
# LIB=number of files in lib/
#
# As of 2025-04-01 there are no files in wolfi, enterprise or extra
# in bin/, sbin/, or usr/sbin/.
set -o pipefail
repo_info() {
local name="$1" infod="$2" gitd="$3"
shift 3
(
cd "$infod" || exit 1
if [ $# -eq 0 ]; then
set -- *.info
fi
for info in "$@"; do
flist=${info%.info}.flist
out=$(awk '
$1 == "origin" { o=$3; };
$1 == "pkgname" { p=$3; };
$1 == "pkgver" { v=$3 };
END { printf("%s\t%s\t%s\n", o, v, p); }' "$info") || { echo "failed"; exit 1; }
set -- $out
origin=$1
pkg=$2
ver=$3
if [ ! -f "$gitd/$origin.yaml" ]; then
echo "[$name] skipping $origin/$pkg [obsolete/not in git]" 1>&2
continue
fi
out=$(awk '
$6 ~ mus { us=us+1; }
$6 ~ mb { b=b+1; }
$6 ~ ms { s=s+1; }
$6 ~ mlib { lib=lib+1; }
END { printf("%d %d %d %d\n", b, s, us, lib); }' \
mus="^usr/sbin/" mb="^bin/" ms="^sbin/" mlib="^lib/" \
"$flist") || exit 1
set -- $out
b=$1
s=$2
us=$3
lib=$4
[ "$b" = "0" ] && [ "$s" = "0" ] && [ "$us" = "0" ] && [ "$lib" = "0" ] && continue
printf "%s\t%s\t%s\t%s\t%d\t%d\t%d\t%d\n" \
"$name" "$origin" "$ver" "$pkg" "$b" "$s" "$us" "$lib"
done
)
}
outd=${1:-/tmp}
mkdir -p "$outd" || exit 1
repos="extras enterprise wolfi"
for repo in $repos; do
case $repo in
enterprise) upstream="[email protected]:chainguard-dev/enterprise-packages";;
extras) upstream="[email protected]:chainguard-dev/extra-packages";;
wolfi) upstream="[email protected]:wolfi-dev/os";;
esac
if [ ! -d "$repo.git" ]; then
git clone -o upstream "$upstream" "$repo.git" || { rm -Rf $repo.git; exit 1; }
else
( cd "$repo.git" && git fetch upstream && git checkout upstream/main ) ||
{ echo "failed update $repo.git"; exit 1; }
fi
done
for repo in $repos; do
get-archive-info "--archive=$repo" "$PWD/$repo" ||
{ echo "failed get-archive-info --archive=$repo $PWD/$repo"; exit 1; }
repo_info "$repo" "$PWD/$repo" "$PWD/$repo.git" > "$outd/$repo-usrmerge.txt" ||
exit 1
done
sort -k2 $outd/*-usrmerge.txt > $outd/all.txt
#!/bin/bash
# shellcheck disable=SC2015,SC2039,SC2166,SC3043
VERBOSITY=0
TEMP_D=""
IMAGE=${IMAGE:-"cgr.dev/chainguard-private/chainguard-base:latest"}
WOLFI_IMAGE="cgr.dev/chainguard/wolfi-base:latest"
stderr() { echo "$@" 1>&2; }
fail() { local r=$?; [ $r -eq 0 ] && r=1; failrc "$r" "$@"; }
failrc() { local r="$1"; shift; [ $# -eq 0 ] || stderr "$@"; exit "$r"; }
cleanup() {
[ -z "${TEMP_D}" -o ! -d "${TEMP_D}" ] || rm -Rf "${TEMP_D}"
}
Usage() {
cat <<EOF
Usage: ${0##*/} [ options ] <<ARGUMENTS>>
run wolfi-base in docker
options:
-e | --enterprise add repo for enterprise
-x | --extras add extras repo
-W | --wolfi use wolfi image ${WOLFI_IMAGE}
default: ${IMAGE}
image can also be set with env IMAGE
-h | --help Usage
--mount M add --mount=M
-v | --volume V add --volume=V
--entrypoint N add --entrypoint=N
-w | --workdir W add --workdir=W
-V | --verbose increase verbosity
--user U add --user=U
--batch non-interactive (do not use -it)
--keep keep (do not use --rm)
-P --priv run privliged container
EOF
}
shellquote(){
python3 -c 'import shlex, sys; print("$ " + " ".join([shlex.quote(a) for a in sys.argv[1:]]))' "$@"
}
bad_Usage() { Usage 1>&2; [ $# -eq 0 ] || stderr "$@"; return 1; }
debug() {
local level="$1"
shift
[ "${level}" -gt "${VERBOSITY}" ] && return
stderr "${@}"
}
repos() {
local enterprise="$1" extras="$2"
echo "https://apk.cgr.dev/chainguard"
[ "$enterprise" = "true" ] &&
echo "https://apk.cgr.dev/chainguard-private"
[ "$extras" = "true" ] &&
echo "https://packages.cgr.dev/extras"
return 0
}
keys() {
# $ docker run --entrypoint=/bin/sh --rm -it
# cgr.dev/chainguard-private/chainguard-base:latest
# -c 'cd /etc/apk/keys && for k in chainguard-*.pub; do echo == $k ==; cat $k; done'
local dir="$1" enterprise="$2" extras="$3"
if [ "$enterprise" = "true" ]; then
cat > "$dir/chainguard-ed520f52bd1d4cab06899f4b3cee067d1d9dda408f181e818227372a4757b0b8.rsa.pub" <<EOF
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAphF6T7nrZv3CpCP/0QtZ
KhjnVCsGv3UvLpZakfApuTmyhEAXbACiBRN/Raq7/5Mb7WaDZSUbm0kt/NsED1MU
9owXaa7l257kX6Em50pm3d7bvlji7lXJACCMObSViW8w5+tBdXeROR6F2MsIFF9R
hEVoY11gJw7+m+z4sjZTC2E5jX0QwEokwrYrcKRMVEtyan+aOnHL/2khSpEJrjtL
0I/zXUK1Fghj3htWdQAKFK4HVGTcSEGjcgh+2MJzEGaLPQOpw0o6x5AQtuvrqGVE
ySAKqXlfpDIOcoi5gXGvOzn9p7OhSf5pLGr4J6QcghUuLlBJJgfMqO8mwv+eW6uq
Ss93UIWycdf6IzbN6yCBXXFhiSXQ+0T4u7y/o58GW28jQ5CwsUXACuX9KdJTMWSa
uyk7pEALLTlR6nrWULvcRbLjvO+P6xSEywxC2KsHMYnpTp4rP25Mm2/lfI/qV5Xj
B93hlEdHXiNsSUjYlSRVJbgGWKq2pAPqaCIPeFFFjmOrj1t1e1GJt4cuPaZZmTrX
REJW8++bVHidfg/EXJnxHiAlWmu1Nruv3xaI4F0U9hTh6ber6wl6erl+bMlBlTPo
f7UHP0Zc5XkR1oPkhxxfKnNleqmKg4RmoyBWrZeFkYdthRSbzH0yl5pdyL8q2ujt
mtYzK5KoeklsR7MjC+2DZ+UCAwEAAQ==
-----END PUBLIC KEY-----
EOF
cat > "$dir/chainguard-f7fe2e42ceb7963e8dc4117e6bba19516ef3c4c7d86cc684404461d942342e93.rsa.pub" <<EOF
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEApPUZzx7q16nGmlJUdFBs
W36H9S4HtBzA83FSGBiltzl3W9uesH0elFLjJIhNL8nYY7y+vQgIk3e506ISxlMk
ZUWEx7/CCS4r1p81yaCRywFuaMRfBcDJI4h/y5scjyHUb9qmI/EtHCv1KrGnTppe
mHkUeNCvLkSZYjo4/QoLSw0PLtW06Xj36pMHugDh83q+NUKXgnD83wRmpUaAKH8i
Btmk63oqQHvFirxdAj8JMdClHHHDQRv059k7lnjAne5RPHZY6vV89CIBil0GcII+
Yj6sOjwMPNDv8ILItpvT+ntuQU6roXeu6yiIXRSYy1rTzp1RQI4utWlQoDcTEcCs
nV492t4J+YE6UDGe+QXLtViMvUZjXH2DJbMQvfNemNR/F1ZZjzzFZeBCDyi+hRNy
Wdt1PrJVnFAQzC6z1hMAUrgyz5XS0XIkvQ6/ziuwGXp7Arart0LOT1WRFS0dEFlx
ySk3qyQ1yOLdshbwvZ5OO5IxRrOZPCyWaB5vXshd8QMT4/bNwqt8n27q9CNUfebH
VhTiJo6enTGrEON0OF1u+6aUn/s+/zDQrYMOz4AVa+t4lLykgLnW7ZX3aw3KXTva
/4O4rUx8dtSdhGULXVxknSn1AVHmtY2s0927m9CN3tipCK1Sl+iJLeDEDH+kQHe8
yPqNtAI+bL6cwg/E2yL5KwMCAwEAAQ==
-----END PUBLIC KEY-----
EOF
fi
if [ "$extras" = "true" ]; then
cat > "$dir/chainguard-extras.rsa.pub" <<EOF
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxQe5KE+z6aPUl/FUzewy
MogEwmlWYyyUdkd7IIp3EFn3ml8cuC1YgMFhCJH/jjIzp0GShTNWbs9ZHrWUf66Y
fKiOfz970UgjocSx2KOiL1C3C9HlnqRPoUWgvhs7RpYRr4CPJJBtl5zb16BSoA0L
XIxNn/saFzb+DDaTTSg2HuWab2ZJlwI9Hoi6ViEeCLmTcBH+OOm4PfPcHVlBDIXs
Hs3Iwaypbf9txCtH0yynYUKz+IqKT5iSWe2rBEHIG7KE8uf0eNgITFRnqnQ751kI
qBkbyOMcGmAvdvrszY8lEmi8i+NMss0R7KOGLlOUV/U9eBAWq1gykm4ouCSQcoFm
TUJFAhPxZdLWyX2terIegJIpBNj+G7Q6bt9VD7PW+UfoyZbN4pgRk3+5v2QFJEAy
lIrebpc1Ca4xcWAM8GhD9XvHBWVLH9Q3GmQ8CtrVjHFeMrUVAMPmsTELZ0PRqost
MNBBUkyfk46iVi2HoC/sc8thSshDUmgbJs37lEo1/wB+l9l/RDxIn44SCvMWR6GV
jfDXdS+f4xR3D12hFbtInDWgQSek8tlHFCpQDpmtcQ6J38EkN4ZYUvLIoW4u74RG
SHH6dYe1sbDVOd0oxNPttXddRse/XOkHq/4G80AMRU0sytV0e7NauHIyNB4/DI+2
XwqkGfBhFCU7oKtwuixmBH0CAwEAAQ==
-----END PUBLIC KEY-----
EOF
fi
}
main() {
local short_opts="A:ehI:Pv:Vw:xW"
local long_opts="arch:,batch,help,enterprise:,entrypoint:,extras:,image:,keep,mount:,priv,user:,verbose,volume:,workdir:,wolfi"
local getopt_out=""
getopt_out=$(getopt --name "${0##*/}" \
--options "${short_opts}" --long "${long_opts}" -- "$@") &&
eval set -- "${getopt_out}" ||
{ bad_Usage "bad usage"; return; }
local cur="" next=""
local enterprise=false extras=false vols="" mounts="" wolfi=false priv=false uarch=""
local keep=false batch=false
vols=()
mounts=()
while [ $# -ne 0 ]; do
# shellcheck disable=SC2034
{ cur="$1"; next="$2"; }
case "$cur" in
-h|--help) Usage ; exit 0;;
-A|--arch) uarch=$next; shift;;
-e|--enterprise) enterprise=true;;
-x|--extras) extras=true;;
--mount) mounts+=( "--mount=$next" );;
-v|--volume) vols+=( "--volume=$next" );;
--entrypoint) entrypoint=$next; shift;;
-w|--workdir) workdir="$next"; shift;;
-V|--verbose) VERBOSITY=$((VERBOSITY+1));;
-I|--image) IMAGE=$next; shift;;
-W|--wolfi) IMAGE=${WOLFI_IMAGE}; wolfi=true;;
--user) user=$next; shift;;
--keep) keep=true;;
--batch) batch=true;;
-P|--priv) priv=true;;
--) shift; break;;
esac
shift;
done
# program starts here
TEMP_D=$(mktemp -d "${TMPDIR:-/tmp}/${0##*/}.XXXXXX") ||
fail "failed to make tempdir"
trap cleanup EXIT
local unamem="" os=linux
if [ -n "$uarch" ]; then
case "$uarch" in
amd64|x86|x86_64) arch="amd64";;
aarch64|arm64) arch="arm64";;
*) fail "dont know what arch=$uarch is";;
esac
else
unamem=$(uname -m) || fail "what? no uname -m"
case "$unamem" in
x86_64) arch="amd64";;
aarch64) arch="$unamem";;
*) fail "unknown uname -m '$unamem'"
esac
fi
local mymounts="" tok="" envarg=""
if [ "$enterprise" = "true" ]; then
: > "${TEMP_D}/env"
tok=$(chainctl auth token --audience="apk.cgr.dev") ||
fail "failed to get token for apk.cgr.dev"
echo "HTTP_AUTH=basic:apk.cgr.dev:user:$tok" >> "${TEMP_D}/env"
envarg="--env-file=${TEMP_D}/env"
fi
mymounts=()
if [ "$enterprise" = "true" -o "$extras" = "true" ]; then
repos "$enterprise" "$extras" > "${TEMP_D}/repositories" ||
fail "failed to create repositories"
mymounts+=( "--mount=type=bind,source=${TEMP_D}/repositories,destination=/etc/apk/repositories" )
fi
if [ "$wolfi" = "true" ] &&
[ "$enterprise" = "true" -o "$extras" = "true" ]; then
mkdir "${TEMP_D}/keys" &&
keys "${TEMP_D}/keys" "$enterprise" "$extras" ||
fail "failed writing keys for wolfi"
local k="" b=""
for k in "${TEMP_D}/keys/"*; do
b=${k##*/}
mymounts+=( "--mount=type=bind,source=$k,destination=/etc/apk/keys/$b" )
done
fi
local privops=""
privops=(
--privileged
--security-opt="seccomp=unconfined"
--security-opt="apparmor:unconfined" \
)
[ "$priv" = "true" ] || privops=( )
local name="" cmd=() flags=()
[ "$keep" = "false" ] && flags+=("--rm")
[ "$batch" = "true" ] || flags+=("-it")
name=$(petname) || fail "failed to get a name"
stderr "$name eprise=$enterprise extras=$extras priv=$priv arch=$arch $IMAGE"
cmd=(
docker run --rm -it --name="$name"
--platform="$os/$arch"
"${privops[@]}"
"${mymounts[@]}" "${mounts[@]}" "${vols[@]}"
${envarg:+"${envarg}"}
${workdir:+"--workdir=$workdir"}
${entrypoint:+"--entrypoint=${entrypoint}"}
${user:+"--user=$user"}
"${IMAGE}" "$@"
)
[ $VERBOSITY -gt 0 ] && shellquote "${cmd[@]}" 1>&2
"${cmd[@]}"
}
main "$@"
# vi: ts=4 expandtab
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment