diff options
Diffstat (limited to 'src/scripts')
| -rwxr-xr-x | src/scripts/actions_to_type.sh | 77 | ||||
| -rw-r--r-- | src/scripts/build-action.sh | 135 | ||||
| -rw-r--r-- | src/scripts/channels.scm | 20 | ||||
| -rwxr-xr-x | src/scripts/repos_to_actions_map.sh | 53 |
4 files changed, 285 insertions, 0 deletions
diff --git a/src/scripts/actions_to_type.sh b/src/scripts/actions_to_type.sh new file mode 100755 index 0000000..5d93955 --- /dev/null +++ b/src/scripts/actions_to_type.sh @@ -0,0 +1,77 @@ +#!/usr/bin/env bash +# kenku --- crawl and reproduce github actions +# Copyright © 2026 bdunahu <bdunahu@operationnull.com> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. +# +# +# Takes a file with a repository identifier per line, like this: +# +# actions/deploy-pages v4 +# actions/upload-pages-artifact v4 +# astral-sh/setup-uv v7 +# actions/setup-python v2 +# actions/checkout v2 +# +# And writes an equivalent file with what it's using. 'using' in this case can +# be one of the standard action types in the github actions lingo: 'node', +# 'docker', or 'composite'. +# The `actions.yaml` or `actions.yml` file does a pretty good job explaining +# what is used or not. + +function curl_action { + curl -s \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer ${TOKEN}" \ + "https://api.github.com/repos/$1?ref=$2" +} + +function find_action { + owner=$(echo "$1" | awk -F'/' '{print $(1)}') + repo=$(echo "$1" | awk -F'/' '{print $(2)}') + fname=$(echo "$1" | cut -d'/' -f3-) + sha="$2" + + path="$owner/$repo/contents/$fname" + + if [[ "$path" =~ \.(yml|yaml)$ ]]; then + action=$(curl_action "$path" "$sha") + exists=true + else + for c in action.yml action.yaml; do + p="$path/$c" + + action=$(curl_action "$p" "$sha") + + # endpoint doesn't exist + exists=$(echo "$action" | jq -e 'has("message") | not') + [[ "$exists" == true ]] && break + done + fi + + using=$("$exists" && { + echo "$action" \ + | jq -r '.content' \ + | base64 -d \ + | grep -E '^\s*"?using"?:' \ + | sed -E 's/.*"?using"?:[[:space:]]*//' + } || echo "NO_FILE" + ) + [[ -z "$using" ]] && using="NO_USING" + echo "$1 $sha $using" +} + +while read -r action sha; do + find_action "$action" "$sha" +done diff --git a/src/scripts/build-action.sh b/src/scripts/build-action.sh new file mode 100644 index 0000000..9c1d502 --- /dev/null +++ b/src/scripts/build-action.sh @@ -0,0 +1,135 @@ +#!/usr/bin/env bash +# kenku --- crawl and reproduce github actions +# Copyright © 2026 bdunahu <bdunahu@operationnull.com> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +function get_output_file { + local output_file action_file + action_file="action.yml" + [ ! -f "$action_file" ] && action_file="action.yaml" + output_file=$(grep -E '^\s*main:\s*' "${TO_ACT}${action_file}" | awk '{print $2}') + output_file=${output_file//\"/} + echo ${output_file//\'/} +} + +function script_exists { + npm run | grep -q "^ $1" +} + +ALL="$1" +SHA="$2" +DIR="$3" +BUILD_FAILURE_DIR="$4" +MISSING_ARTIFACT_DIR="$5" +MAYBE_REPRODUCIBLE_DIR="$6" +REPRODUCIBLE_DIR="$7" + +REPO_PRINTABLE="${ALL//\//_}_$SHA" +# holds temporary print strings + +REPO=$(echo "$ALL" | awk -F'/' '{print $(1)}') +TO_ACT=$(echo "$ALL" | awk -F'/' '{print $(2)}') +REPO="$REPO/$TO_ACT" +TO_ACT=$(echo "$ALL" | cut -d'/' -f3-) +[[ -n "$TO_ACT" ]] && TO_ACT="$TO_ACT/" + +BUILD_DIR=$(mktemp -d) +cd "$BUILD_DIR" + +echo "Cloning $REPO..." +git clone "https://github.com/$REPO.git" repo > /dev/null 2>&1 +cd repo +echo "Checking out $SHA" +git checkout "$SHA" > /dev/null 2>&1 + +# actions.yml refers to actions relatively, so if we parse +# ../dist/foo/ as the action final build dir, we need to +# refer to it relative to the curr dir of the script +# this is one way +DIST="$TO_ACT$(get_output_file)" +# some actions just name an index.js in the root dir. +# In cases they name a directory, we'll diff the full thing. +[ "$(dirname "$DIST")" != "." ] && DIST="$(dirname "$DIST")" +REFERENCE_DIST="$DIST.1" +echo "Saving $DIST to $REFERENCE_DIST" + +mv "$DIST" "$REFERENCE_DIST" + +cd "$DIR" +echo "Installing packages..." + +INSTALL=$(npm ci 2>&1) + +echo "Attempting to build..." + +TMP="owo what's the build command" +# surely ONE of these will work... +if COMM="release"; script_exists "$COMM"; then + echo "Trying '$COMM'..." + TMP=$(npm run "$COMM" 2>&1) +elif COMM="package"; script_exists "$COMM"; then + echo "Trying '$COMM'..." + TMP=$(npm run "$COMM" 2>&1) +elif COMM="bundle"; script_exists "$COMM"; then + echo "Trying '$COMM'..." + TMP=$(npm run "$COMM" 2>&1) +elif COMM="build"; script_exists "$COMM"; then + echo "Trying '$COMM'..." + TMP=$(npm run "$COMM" 2>&1) +fi + +# if the build fails, the developers may have failed to setup +# the environment properly, or it uses deprecated features. +# It may be that there is no build command at all! This needs +# manual review. +if [[ $? -ne 0 ]]; then + echo "$ALL did not build." + echo "$INSTALL" > "$BUILD_FAILURE_DIR/${REPO_PRINTABLE}.log" + echo "$TMP" >> "$BUILD_FAILURE_DIR/${REPO_PRINTABLE}.log" + exit 0 #don't kill outer script +fi + +# return back to where we were +cd "$BUILD_DIR" +cd repo + +if [[ ! -d "$DIST" ]]; then + # TODO: if the dist dir wasn't produced, then the commands + # used were likely wrong + echo "$ALL did not produce artifacts." + echo "$INSTALL" >> "$MISSING_ARTIFACT_DIR/${REPO_PRINTABLE}.log" + echo "$TMP" >> "$MISSING_ARTIFACT_DIR/${REPO_PRINTABLE}.log" + exit 0 +fi + +# diffoscope will output binary diffs if line endings are not +# comparable. --force forces it to convert binary files too. +find "$REFERENCE_DIST" -type f -exec dos2unix --force {} \; + +# TODO: solves path errors in containers when using global install +mkdir -p "$HOME/.npm-global" +npm config set prefix "$HOME/.npm-global" +export PATH="$HOME/.npm-global/bin:$PATH" + +# diffoscope uses to compare pretty +npm install -g js-beautify + +diffoscope "$DIST" "$REFERENCE_DIST" \ + --exclude-directory-metadata=yes \ + --html "$MAYBE_REPRODUCIBLE_DIR/${REPO_PRINTABLE}.html" +if [[ ! -f "$MAYBE_REPRODUCIBLE_DIR/${REPO_PRINTABLE}.html" ]]; then + touch "$REPRODUCIBLE_DIR/${REPO_PRINTABLE}.flag" + exit 0 +fi diff --git a/src/scripts/channels.scm b/src/scripts/channels.scm new file mode 100644 index 0000000..b49849d --- /dev/null +++ b/src/scripts/channels.scm @@ -0,0 +1,20 @@ +(list (channel + (name 'guix) + (url "https://codeberg.org/guix/guix") + (branch "master") + (commit "c3c2f3be04364e3f616bfcc38875b112bedbe901") + (introduction + (make-channel-introduction + "9edb3f66fd807b096b48283debdcddccfea34bad" + (openpgp-fingerprint + "BBB0 2DDF 2CEA F6A8 0D1D E643 A2A0 6DF2 A33A 54FA")))) + (channel + (name 'tanelorn) + (url "https://git.operationnull.com/tanelorn.git") + (branch "master") + (commit "1c25e9f613d8654f800f9be1d4b232ec9de25aa0") + (introduction + (make-channel-introduction + "3960d45383c672f8aacab8e354824793256c9d29" + (openpgp-fingerprint + "5550 5CA6 9DE5 D342 7F31 F9AE 5F86 6C65 2A34 C996"))))) diff --git a/src/scripts/repos_to_actions_map.sh b/src/scripts/repos_to_actions_map.sh new file mode 100755 index 0000000..2216a5a --- /dev/null +++ b/src/scripts/repos_to_actions_map.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +# kenku --- crawl and reproduce github actions +# Copyright © 2026 bdunahu <bdunahu@operationnull.com> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. +# +# Takes a file with one repo per line and outputs a master list +# of REPO, ACTION pairs. This script takes a long time to run. +# There is an obvious part of this script that is pretty gross. +# I got it working sometime in early march and have forgotten +# why it does what it does. + +function get_flows { + curl -s \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer $TOKEN" \ + "https://api.github.com/repos/$1/$2/contents/.github/workflows" +} + +function has_flows { + echo "$1" \ + | jq -e 'type == "object" and has("message") | not' >/dev/null 2>&1 +} + +function get_url { + local owner repo flows + owner=$(echo "$1" | awk -F'/' '{print $(NF-1)}') + repo=$(echo "$1" | awk -F'/' '{print $(NF)}') + + flows=$(get_flows "$owner" "$repo") + has_flows "$flows" && + echo "$flows" | jq -r '.[] | select(.type=="file") | .download_url' \ + | xargs -n1 sh -c ' + for url do + curl -s "$url" | grep -E "^\s*-?\s*uses:" | sed "s|.*uses:\s*|$1 |" + done + ' _ "$1" # passes arg to sh -c +} + +while read -r url; do + get_url "$url" +done |
