diff --git a/.github/workflows/check-linux.yml b/.github/workflows/check-linux.yml index 12b3765..0b74f1a 100644 --- a/.github/workflows/check-linux.yml +++ b/.github/workflows/check-linux.yml @@ -47,6 +47,8 @@ jobs: run: | sudo apt-get update sudo apt-get install -y zsh + - name: "⚙️ Generate checksums" + run: sh ./public/sh/generate-checksums.sh - name: "⚙️ Check: unit fixtures" run: sh ./tests/installers.sh - name: "⚙️ Check: install.sh -- -i skip" diff --git a/.github/workflows/check-macos.yml b/.github/workflows/check-macos.yml index ab2189a..81e87d2 100644 --- a/.github/workflows/check-macos.yml +++ b/.github/workflows/check-macos.yml @@ -47,6 +47,8 @@ jobs: - name: ⚙️ Prepare dependencies run: | brew install zsh + - name: "⚙️ Generate checksums" + run: sh ./public/sh/generate-checksums.sh - name: "⚙️ Check: unit fixtures" run: sh ./tests/installers.sh - name: "⚙️ Check: install.sh -- -i skip" diff --git a/.github/workflows/checksum.yml b/.github/workflows/checksum.yml index 500f336..25e6d72 100644 --- a/.github/workflows/checksum.yml +++ b/.github/workflows/checksum.yml @@ -3,6 +3,7 @@ name: Checksum on: push: paths: + - "public/sh/generate-checksums.sh" - "public/sh/install_zpmod.sh" - "public/sh/install.sh" - "public/sh/sync-init.sh" @@ -25,14 +26,7 @@ jobs: steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: "🆗 Generate checksum" - uses: jmgilman/actions-generate-checksum@3ea6dc9bf8eecf28e2ecc982fab683484a1a8561 # v1.0.1 - with: - patterns: | - public/sh/install_zpmod.sh - public/sh/install.sh - public/sh/sync-init.sh - public/zsh/init.zsh - - run: mv checksum.txt public/ + run: sh ./public/sh/generate-checksums.sh - name: "🆗 Commit" uses: z-shell/.github/actions/commit@3222f53692e448ed6e2caf8b8722e74fb2ca2461 # v1.1.1 with: diff --git a/public/checksum.txt b/public/checksum.txt index d85758a..26ac375 100644 --- a/public/checksum.txt +++ b/public/checksum.txt @@ -1,4 +1,4 @@ 0140801e80b2d8767e95d215850b6feab3642ee2b6fd70fbf4aab09afcfe7fca public/sh/install_zpmod.sh -0cca0bb9f95101b4bf392d6aa3c23123602d99a7cbb0decacaa72973e016d89d public/sh/install.sh +f9d8ef697ba682bdfe66fdd67d4517caa4e62bb8baf2868ec8ed9932809b76aa public/sh/install.sh 08cc893ceb982fc99d17db1966c6c30790cc571e16e4f5392352d995f5252952 public/sh/sync-init.sh 5c7af31d7fc848d80f9fedf69c75e6844503f550a3d5e020bc7cdfdaca624763 public/zsh/init.zsh diff --git a/public/sh/generate-checksums.sh b/public/sh/generate-checksums.sh new file mode 100755 index 0000000..318dc9c --- /dev/null +++ b/public/sh/generate-checksums.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env sh +# -*- mode: sh; sh-indentation: 2; indent-tabs-mode: nil; sh-basic-offset: 2; -*- +# vim: ft=sh sw=2 ts=2 et +# +# generate-checksums.sh — regenerate public/checksum.txt +# +# Usage (from any directory): +# sh public/sh/generate-checksums.sh +# +# This script is also invoked by the CI workflows before running +# tests/installers.sh so that public/checksum.txt is always current. + +set -eu + +ROOT="$( + unset CDPATH + cd "$(dirname "$0")/../.." 2>/dev/null && pwd +)" || { printf '%s\n' "generate-checksums: cannot determine repository root" >&2; exit 1; } + +CHECKSUM_FILE="${ROOT}/public/checksum.txt" + +sha256_file() { + if command -v sha256sum >/dev/null 2>&1; then + sha256sum "$1" | awk '{print $1}' + elif command -v shasum >/dev/null 2>&1; then + shasum -a 256 "$1" | awk '{print $1}' + else + printf '%s\n' "generate-checksums: sha256sum or shasum is required" >&2 + exit 1 + fi +} + +# Clear (or create) the checksum file before writing fresh entries with `: > file`. +: > "${CHECKSUM_FILE}" +for f in \ + public/sh/install_zpmod.sh \ + public/sh/install.sh \ + public/sh/sync-init.sh \ + public/zsh/init.zsh +do + hash="$(sha256_file "${ROOT}/${f}")" + printf '%s %s\n' "${hash}" "${f}" >> "${CHECKSUM_FILE}" +done + +printf '%s\n' "Checksums written to ${CHECKSUM_FILE}" diff --git a/public/sh/install.sh b/public/sh/install.sh index 1eeccc7..2062910 100755 --- a/public/sh/install.sh +++ b/public/sh/install.sh @@ -36,6 +36,17 @@ while getopts ":i:a:b:" opt; do done shift $((OPTIND - 1)) +# Validate BOPT to prevent sed delimiter injection when building init.zsh. +# | is the sed delimiter used in the substitution; \ and & are special in +# sed replacement expressions. The *[\\]* pattern matches a single backslash. +case "${BOPT}" in + # [\\] is a bracket expression for a literal backslash. + *'|'* | *[\\]* | *'&'* ) + printf '%s\n' "-- ERROR -- Invalid -b value: branch name must not contain '|', '\\', or '&'." >&2 + exit 1 + ;; +esac + SCRIPT_DIR="" LOCAL_INIT_ZSH="" LOCAL_INSTALL_ZPMOD="" @@ -147,6 +158,20 @@ fi command chmod a+x /tmp/zi/git-process-output.zsh if test -d "${ZI_HOME}/${ZI_BIN_DIR_NAME}/.git"; then + _zi_valid=0 + if test -f "${ZI_HOME}/${ZI_BIN_DIR_NAME}/zi.zsh"; then + # Canonical zi remote URLs (HTTPS and SSH, with and without .git suffix) + case "$(command git -C "${ZI_HOME}/${ZI_BIN_DIR_NAME}" remote get-url origin 2>/dev/null || true)" in + https://github.com/z-shell/zi | https://github.com/z-shell/zi.git \ + | git@github.com:z-shell/zi | git@github.com:z-shell/zi.git) + _zi_valid=1 ;; + esac + fi + if [ "${_zi_valid}" -ne 1 ]; then + printf '%s\n' "[1;31m▓▒░[0m ${ZI_HOME}/${ZI_BIN_DIR_NAME} contains a .git directory but does not appear to be a zi repository." >&2 + printf '%s\n' "[1;31m▓▒░[0m Expected zi.zsh and a z-shell/zi remote origin. Unset ZI_HOME/ZI_BIN_DIR_NAME or remove the directory to install fresh." >&2 + exit 1 + fi cd "${ZI_HOME}/${ZI_BIN_DIR_NAME}" || exit 1 printf '%s\n' "[1;34m▓▒░[0m Updating [1;36m(z-shell/zi)[1;33m plugin manager[0m at [1;35m${ZI_HOME}/${ZI_BIN_DIR_NAME}[0m" command git clean -d -f -f diff --git a/tests/installers.sh b/tests/installers.sh index 2be2319..26a8418 100755 --- a/tests/installers.sh +++ b/tests/installers.sh @@ -139,6 +139,12 @@ EOF #!/usr/bin/env sh set -eu +# Strip -C