diff --git a/.cspell/project-words.txt b/.cspell/project-words.txt deleted file mode 100644 index bb883fb..0000000 --- a/.cspell/project-words.txt +++ /dev/null @@ -1,45 +0,0 @@ -AOPT -atinit -atload -atpull -autoload -blockf -BOPT -CFLAGS -Cgreen -Checkig -COLORTERM -COMPINIT -CPPFLAGS -creinstall -Creset -distclean -gdbm -LDFLAGS -mgit -mhas -mktemp -mload -nearcolor -OMZL -OMZP -OMZT -pname -robbyrussell -SAVEHIST -setopt -srand -tcsetpgrp -truecolor -WORKDIR -ZDOTDIR -zicdreplay -zicompinit -zinit -zmodload -zmodules -ZOPT -ZPMOD -zstyle -zunit -zzinit diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index c25c645..0000000 --- a/.editorconfig +++ /dev/null @@ -1,60 +0,0 @@ -# Space or Tabs? -# https://stackoverflow.com/questions/35649847/objective-reasons-for-using-spaces-instead-of-tabs-for-indentation -# https://stackoverflow.com/questions/12093748/how-to-use-tabs-instead-of-spaces-in-a-shell-script -# https://github.com/editorconfig/editorconfig-defaults/blob/master/editorconfig-defaults.json -# -# 1. What happens when I press the Tab key in my text editor? -# 2. What happens when I request my editor to indent one or more lines? -# 3. What happens when I view a file containing U+0009 HORIZONTAL TAB characters? -# -# Answers: -# -# 1. Pressing the Tab key should indent the current line (or selected lines) one additional level. -# 2. As a secondary alternative, I can also tolerate an editor that, -# like Emacs, uses this key for a context-sensitive fix-my-indentation command. -# 3. Indenting one or more lines should follow the reigning convention, if consensus is sufficiently strong; otherwise, -# I greatly prefer 2-space indentation at each level. U+0009 characters should shift subsequent characters to the next tab stop. -# -# Note: VIM users should use alternate marks [[[ and ]]] as the original ones can confuse nested substitutions, e.g.: ${${${VAR}}} -# -# -*- mode: zsh; sh-indentation: 2; indent-tabs-mode: nil; sh-basic-offset: 2; -*- -# vim: ft=zsh sw=2 ts=2 et - -root = true - -[*] -charset = utf-8 -indent_style = space -indent_size = 2 -insert_final_newline = true -trim_trailing_whitespace = true - -[*.sln] -indent_style = tab - -[*.{md,mdx,rst}] -trim_trailing_whitespace = false - -[*.{cmd,bat}] -end_of_line = crlf - -[*za-*] -end_of_line = lf - -[*.{sh,bash,zsh,fish}] -end_of_line = lf - -[Makefile] -indent_style = tab -indent_size = 4 - -[*.{py,rb}] -indent_size = 4 - -[*.{go,java,scala,groovy,kotlin}] -indent_style = tab -indent_size = 4 - -[*.{cs,csx,cake,vb,vbx}] -# Default Severity for all .NET Code Style rules below -dotnet_analyzer_diagnostic.severity = warning diff --git a/.geminiignore b/.geminiignore new file mode 100644 index 0000000..e69de29 diff --git a/.github/.cspell/project-ignored.txt b/.github/.cspell/project-ignored.txt deleted file mode 100644 index 01e9305..0000000 --- a/.github/.cspell/project-ignored.txt +++ /dev/null @@ -1,2 +0,0 @@ -mhas -mload diff --git a/.github/.cspell/project-usernames.txt b/.github/.cspell/project-usernames.txt deleted file mode 100644 index 43ee3d2..0000000 --- a/.github/.cspell/project-usernames.txt +++ /dev/null @@ -1 +0,0 @@ -robbyrussell diff --git a/.github/.cspell/project-words.txt b/.github/.cspell/project-words.txt deleted file mode 100644 index e09d106..0000000 --- a/.github/.cspell/project-words.txt +++ /dev/null @@ -1,34 +0,0 @@ -AOPT -atinit'COMPLETION -atinit'setopt -atinit'typeset -atinit'ZI -atinit'zstyle -atload -atpull -autoload -blockf -BOPT -CFLAGS -COMPINIT -CPPFLAGS -creinstall -distclean -gdbm -LDFLAGS -mktemp -OMZL -OMZP -OMZT -SAVEHIST -tcsetpgrp -WORKDIR -ZDOTDIR -zicdreplay -zicompinit -zinit -zmodload -zmodules -ZOPT -zpmod -zzinit diff --git a/.github/workflows/check-linux.yml b/.github/workflows/check-linux.yml index 3af9a27..f4c3382 100644 --- a/.github/workflows/check-linux.yml +++ b/.github/workflows/check-linux.yml @@ -5,24 +5,34 @@ on: branches: [main] paths: - "lib/sh/**" + - "lib/zsh/**" + - "tests/**" + - ".github/workflows/check-linux.yml" pull_request: branches: [main] paths: - "lib/sh/**" + - "lib/zsh/**" + - "tests/**" + - ".github/workflows/check-linux.yml" workflow_dispatch: +permissions: + contents: read + concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: shellcheck: runs-on: ubuntu-latest + timeout-minutes: 10 steps: - name: ⤵️ Check out code from GitHub - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: ☑️ ShellCheck - uses: ludeeus/action-shellcheck@00b27aa7cb85167568cb48a3838b75f4265f2bca + uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # 2.0.0 with: scandir: "./lib/sh" @@ -32,24 +42,31 @@ jobs: needs: [shellcheck] steps: - name: ⤵️ Check out code from GitHub - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: ⚙️ Prepare dependencies run: | sudo apt-get update sudo apt-get install -y zsh + - name: "⚙️ Check: unit fixtures" + run: sh ./tests/installers.sh - name: "⚙️ Check: install.sh -- -i skip" - run: sh -x ./lib/sh/install.sh -- -i skip; command rm -rf ~/.zi - - name: "⚙️ Check: install.sh -- -i annex" - run: sh -x ./lib/sh/install.sh -- -a annex; command rm -rf ~/.zi - - name: "⚙️ Check: install.sh -- -i loader" - run: sh -x ./lib/sh/install.sh -- -a loader; command rm -rf ~/.zi - - name: "⚙️ Check: install.sh -- -i zunit" - run: sh -x ./lib/sh/install.sh -- -a zunit; command rm -rf ~/.zi + run: sh -x ./lib/sh/install.sh -- -i skip; command rm -rf "${XDG_DATA_HOME:-$HOME/.local/share}/zi" ~/.zi + - name: "⚙️ Check: install.sh -- -a annex" + run: sh -x ./lib/sh/install.sh -- -a annex; command rm -rf "${XDG_DATA_HOME:-$HOME/.local/share}/zi" ~/.zi + - name: "⚙️ Check: install.sh -- -a loader" + run: sh -x ./lib/sh/install.sh -- -a loader; command rm -rf "${XDG_DATA_HOME:-$HOME/.local/share}/zi" ~/.zi + - name: "⚙️ Check: install.sh -- -a zunit" + run: sh -x ./lib/sh/install.sh -- -a zunit; command rm -rf "${XDG_DATA_HOME:-$HOME/.local/share}/zi" ~/.zi - name: "⚙️ Check: install_zpmod.sh" run: sh -x ./lib/sh/install_zpmod.sh - name: ⚙️ Load zpmod module run: | - module_path+=( "$HOME/.zi/zmodules/zpmod/Src" ) + module_path+=( "${XDG_DATA_HOME:-$HOME/.local/share}/zi/zmodules/zpmod/Src" ) zmodload zi/zpmod zpmod source-study -l shell: zsh {0} + - name: "⚙️ Check: init.zsh sync drift" + run: > + sh lib/sh/sync-init.sh + --remote https://raw.githubusercontent.com/z-shell/src/main/lib/zsh/init.zsh + --checksum-url https://raw.githubusercontent.com/z-shell/src/main/lib/zsh/init.zsh.md5 diff --git a/.github/workflows/check-macos.yml b/.github/workflows/check-macos.yml index 0acf6d1..1d1d8e0 100644 --- a/.github/workflows/check-macos.yml +++ b/.github/workflows/check-macos.yml @@ -6,50 +6,62 @@ on: branches: [main] paths: - "lib/sh/**" + - "lib/zsh/**" + - "tests/**" + - ".github/workflows/check-macos.yml" pull_request: branches: [main] paths: - "lib/sh/**" + - "lib/zsh/**" + - "tests/**" + - ".github/workflows/check-macos.yml" workflow_dispatch: +permissions: + contents: read + concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: shellcheck: runs-on: ubuntu-latest + timeout-minutes: 10 steps: - name: ⤵️ Check out code from GitHub - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: ☑️ ShellCheck - uses: ludeeus/action-shellcheck@00b27aa7cb85167568cb48a3838b75f4265f2bca + uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # 2.0.0 with: scandir: "./lib/sh" build: - runs-on: ubuntu-latest + runs-on: macos-latest timeout-minutes: 30 needs: [shellcheck] steps: - name: ⤵️ Check out code from GitHub - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: ⚙️ Prepare dependencies run: | brew install zsh + - name: "⚙️ Check: unit fixtures" + run: sh ./tests/installers.sh - name: "⚙️ Check: install.sh -- -i skip" - run: sh -x ./lib/sh/install.sh -- -i skip; command rm -rf ~/.zi - - name: "⚙️ Check: install.sh -- -i annex" - run: sh -x ./lib/sh/install.sh -- -a annex; command rm -rf ~/.zi - - name: "⚙️ Check: install.sh -- -i loader" - run: sh -x ./lib/sh/install.sh -- -a loader; command rm -rf ~/.zi - - name: "⚙️ Check: install.sh -- -i zunit" - run: sh -x ./lib/sh/install.sh -- -a zunit; command rm -rf ~/.zi + run: sh -x ./lib/sh/install.sh -- -i skip; command rm -rf "${XDG_DATA_HOME:-$HOME/.local/share}/zi" ~/.zi + - name: "⚙️ Check: install.sh -- -a annex" + run: sh -x ./lib/sh/install.sh -- -a annex; command rm -rf "${XDG_DATA_HOME:-$HOME/.local/share}/zi" ~/.zi + - name: "⚙️ Check: install.sh -- -a loader" + run: sh -x ./lib/sh/install.sh -- -a loader; command rm -rf "${XDG_DATA_HOME:-$HOME/.local/share}/zi" ~/.zi + - name: "⚙️ Check: install.sh -- -a zunit" + run: sh -x ./lib/sh/install.sh -- -a zunit; command rm -rf "${XDG_DATA_HOME:-$HOME/.local/share}/zi" ~/.zi - name: "⚙️ Check: install_zpmod.sh" run: sh -x ./lib/sh/install_zpmod.sh - name: ⚙️ Load zpmod module run: | - module_path+=( "$HOME/.zi/zmodules/zpmod/Src" ) + module_path+=( "${XDG_DATA_HOME:-$HOME/.local/share}/zi/zmodules/zpmod/Src" ) zmodload zi/zpmod zpmod source-study -l shell: zsh {0} diff --git a/.github/workflows/checksum.yml b/.github/workflows/checksum.yml index 66080e7..29fdb4e 100644 --- a/.github/workflows/checksum.yml +++ b/.github/workflows/checksum.yml @@ -3,20 +3,34 @@ name: "🆗 Checksum" on: push: paths: - - "lib/**" + - "lib/sh/install_zpmod.sh" + - "lib/sh/install.sh" + - "lib/sh/sync-init.sh" + - "lib/zsh/init.zsh" + - ".github/workflows/checksum.yml" workflow_dispatch: {} +permissions: + contents: write + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + jobs: checksum: + if: github.repository == 'z-shell/src' runs-on: ubuntu-latest + timeout-minutes: 10 steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: "🆗 Generate checksum" - uses: jmgilman/actions-generate-checksum@521a903edf511407d8bd5535d257402fd9bb5db0 + uses: jmgilman/actions-generate-checksum@3ea6dc9bf8eecf28e2ecc982fab683484a1a8561 # v1.0.1 with: patterns: | lib/sh/install_zpmod.sh lib/sh/install.sh + lib/sh/sync-init.sh lib/zsh/init.zsh - run: mv checksum.txt lib/ - name: "🆗 Commit" diff --git a/.github/workflows/deploy-gh-pages.yml b/.github/workflows/deploy-gh-pages.yml index 688a65f..351dbeb 100644 --- a/.github/workflows/deploy-gh-pages.yml +++ b/.github/workflows/deploy-gh-pages.yml @@ -8,17 +8,35 @@ on: tags: ["v*.*.*"] paths: - "lib/**" + - ".github/workflows/deploy-gh-pages.yml" + +permissions: + contents: write jobs: deploy: + if: github.repository == 'z-shell/src' environment: name: github-pages runs-on: ubuntu-latest + timeout-minutes: 15 concurrency: group: ${{ github.workflow }}-${{ github.ref }} steps: - name: ⤵️ Check out code from GitHub - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: "🏷 Prepare deployment metadata" + id: prepare_deploy + run: | + commit_message="$(jq -r '.head_commit.message // empty' "$GITHUB_EVENT_PATH")" + if [ -z "$commit_message" ]; then + commit_message="Deploy ${GITHUB_REF_NAME}" + fi + { + echo "commit_message<> "$GITHUB_OUTPUT" - name: "🏷 Prepare tag" id: prepare_tag if: startsWith(github.ref, 'refs/tags/') @@ -34,6 +52,6 @@ jobs: publish_dir: ./lib user_name: ${{ secrets.ACTIONS_USER }} user_email: ${{ secrets.ACTIONS_MAIL }} - commit_message: ${{ github.event.head_commit.message }} + commit_message: ${{ steps.prepare_deploy.outputs.commit_message }} tag_name: ${{ steps.prepare_tag.outputs.deploy_tag_name }} tag_message: "Deployment ${{ steps.prepare_tag.outputs.tag_name }}" diff --git a/.github/workflows/rclone-action.yml b/.github/workflows/rclone-action.yml index e4f62b6..5b0f0e0 100644 --- a/.github/workflows/rclone-action.yml +++ b/.github/workflows/rclone-action.yml @@ -5,24 +5,29 @@ on: branches: [main] paths: - "lib/**" + - ".github/workflows/rclone-action.yml" workflow_dispatch: {} +permissions: + contents: read + concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: sync: - if: github.repository == 'z-shell/zi-src' + if: github.repository == 'z-shell/src' runs-on: ubuntu-latest + timeout-minutes: 15 env: local_path: "lib" remote_path: "r2:r2-store/src" steps: - name: "⤵️ Check out code from GitHub" - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: "⏫ Run rclone/r2-store" - uses: z-shell/.github/actions/rclone@v1 + uses: z-shell/.github/actions/rclone@91068ee88e8788deff439d6ee36b77329edeb98a # v1.0.8 with: config: ${{ secrets.R2_STORE }} args: "copy --check-first ${{ env.local_path }} ${{ env.remote_path }}" diff --git a/.github/workflows/rebase-action.yml b/.github/workflows/rebase-action.yml deleted file mode 100644 index 83e6615..0000000 --- a/.github/workflows/rebase-action.yml +++ /dev/null @@ -1,29 +0,0 @@ ---- -name: "🔁 Rebase" -on: - issue_comment: - types: [created] - -jobs: - rebase: - runs-on: ubuntu-latest - name: 🔁 Rebase - # Automate with comments: /autosquash, /rebase - if: >- - github.event.issue.pull_request != '' && - ( - contains(github.event.comment.body, '/rebase') || - contains(github.event.comment.body, '/autosquash') - ) - steps: - - name: ⤵️ Check out code from GitHub - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - with: - token: ${{ secrets.ORG_TOKEN }} - fetch-depth: 0 # otherwise, you will fail to push refs to dest repo - - name: 🔁 Rebase - uses: z-shell/.github/actions/rebase@91068ee88e8788deff439d6ee36b77329edeb98a # v1.0.8 - with: - autosquash: ${{ contains(github.event.comment.body, '/autosquash') || contains(github.event.comment.body, '/rebase-autosquash') }} - env: - GITHUB_TOKEN: ${{ secrets.ORG_TOKEN }} diff --git a/.github/workflows/stale-action.yml b/.github/workflows/stale-action.yml deleted file mode 100644 index d6fd984..0000000 --- a/.github/workflows/stale-action.yml +++ /dev/null @@ -1,35 +0,0 @@ ---- -name: "👻 Stale" - -on: - schedule: - - cron: "0 8 * * *" - workflow_dispatch: - -jobs: - stale: - name: "🧹 Clean up stale issues and PRs" - runs-on: ubuntu-latest - steps: - - name: "🚀 Run stale" - uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9 - with: - # 📋 https://github.com/actions/stale#all-options - days-before-stale: 30 - days-before-close: 7 - exempt-all-pr-assignees: true - exempt-all-pr-milestones: true - remove-stale-when-updated: true - stale-issue-label: "stale 👻" - exempt-issue-labels: "no-stale 🔒,help-wanted 👥" - stale-issue-message: > - There hasn't been any activity on this issue recently, and in order to prioritize active issues, it will be - marked as stale. Please make sure to update to the latest version and check if that solves the issue. Let us - know if that works for you by leaving a 👍 Because this issue is marked as stale, it will be closed and - locked in 7 days if no further activity occurs. Thank you for your contributions! - stale-pr-label: "stale 👻" - exempt-pr-labels: "no-stale 🔒" - stale-pr-message: > - There hasn't been any activity on this pull request recently, and in order to prioritize active work, it has - been marked as stale. This PR will be closed and locked in 7 days if no further activity occurs. Thank you - for your contributions! diff --git a/.github/workflows/win-install.yml b/.github/workflows/win-install.yml index c9c32f1..365e6bc 100644 --- a/.github/workflows/win-install.yml +++ b/.github/workflows/win-install.yml @@ -5,20 +5,28 @@ on: pull_request: paths: - "lib/sh/**" + - "lib/zsh/**" + - "tests/**" - ".github/workflows/win-install.yml" push: paths: - "lib/sh/**" + - "lib/zsh/**" + - "tests/**" - ".github/workflows/win-install.yml" workflow_dispatch: {} +permissions: + contents: read + jobs: shellcheck: runs-on: ubuntu-latest + timeout-minutes: 10 steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: ☑️ ShellCheck - uses: ludeeus/action-shellcheck@00b27aa7cb85167568cb48a3838b75f4265f2bca + uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # 2.0.0 with: scandir: "./lib/sh" run-install: @@ -32,22 +40,33 @@ jobs: - name: 🪟 Set CRLF (Windows) run: | git config --global core.autocrlf input - git config --global --add safe.directory /cygdrive/d/a/zi-src/zi-src - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + git config --global --add safe.directory /cygdrive/d/a/src/src + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: 🪟 Dependencies (Windows) - uses: egor-tensin/setup-cygwin@d2c752bab416d4b0662591bd366fc2686297c82d # v4 + uses: egor-tensin/setup-cygwin@d2c752bab416d4b0662591bd366fc2686297c82d # v4.0.1 with: platform: x64 packages: curl git zsh - name: 🪟 Run Install run: | + sh ./tests/installers.sh sh -x ./lib/sh/install.sh -- -i skip - command rm -rf /home/runneradmin/.zi + command rm -rf "${XDG_DATA_HOME:-$HOME/.local/share}/zi" /home/runneradmin/.zi sh -x ./lib/sh/install.sh -- -a annex - command rm -rf /home/runneradmin/.zi + command rm -rf "${XDG_DATA_HOME:-$HOME/.local/share}/zi" /home/runneradmin/.zi sh -x ./lib/sh/install.sh -- -a loader - command rm -rf /home/runneradmin/.zi + command rm -rf "${XDG_DATA_HOME:-$HOME/.local/share}/zi" /home/runneradmin/.zi sh -x ./lib/sh/install.sh -- -a zunit - command rm -rf /home/runneradmin/.zi + command rm -rf "${XDG_DATA_HOME:-$HOME/.local/share}/zi" /home/runneradmin/.zi sh -x ./lib/sh/install.sh -- -a zpmod shell: C:\tools\cygwin\bin\bash.exe --login -o igncr '{0}' + - name: 🪟 Smoke-test — verify zi.zsh present + run: | + ZI_BIN="${XDG_DATA_HOME:-${HOME}/.local/share}/zi/bin" + if [ ! -f "${ZI_BIN}/zi.zsh" ]; then + printf '%s\n' "FAIL: zi.zsh not found at ${ZI_BIN}/zi.zsh" + exit 1 + fi + printf '%s\n' "OK: zi.zsh found at ${ZI_BIN}/zi.zsh" + command rm -rf "${XDG_DATA_HOME:-${HOME}/.local/share}/zi" /home/runneradmin/.zi + shell: C:\tools\cygwin\bin\bash.exe --login -o igncr '{0}' diff --git a/.github/workflows/wrangler.yml b/.github/workflows/wrangler.yml deleted file mode 100644 index 57f590a..0000000 --- a/.github/workflows/wrangler.yml +++ /dev/null @@ -1,40 +0,0 @@ ---- -name: "🤠 Wrangler" - -on: - # schedule: - # - cron: "0 03 * * 1/3" - # push: - # branches: [main] - # paths: - # - "lib/**" - # - "workers/**" - workflow_dispatch: - inputs: - environment: - description: "Choose an environment to deploy to: " - required: true - default: "dev" - -jobs: - deploy: - runs-on: ubuntu-latest - concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - timeout-minutes: 15 - environment: wrangler - steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - name: "🤠 Deploy > cdn" - uses: cloudflare/wrangler-action@da0e0dfe58b7a431659754fdf3f186c529afbe65 # v3.14.1 - with: - apiToken: ${{ secrets.CF_API_TOKEN }} - workingDirectory: "workers/cdn" - command: "publish" - - name: "🤠 Deploy > r2-store" - uses: cloudflare/wrangler-action@da0e0dfe58b7a431659754fdf3f186c529afbe65 # v3.14.1 - with: - apiToken: ${{ secrets.CF_API_TOKEN }} - workingDirectory: "workers/r2-store" - command: "publish" diff --git a/.gitignore b/.gitignore index c8968ab..030f2a7 100644 --- a/.gitignore +++ b/.gitignore @@ -150,3 +150,7 @@ worker/ node_modules/ .pnpm-store/ .cargo-ok +AGENTS.md +CLAUDE.md +GEMINI.md +.github/copilot-instructions.md diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml index edc1935..7cdfae6 100644 --- a/.trunk/trunk.yaml +++ b/.trunk/trunk.yaml @@ -1,17 +1,28 @@ version: 0.1 cli: - version: 1.22.11 + version: 1.25.0 plugins: sources: - id: trunk - ref: v1.6.7 + ref: v1.10.0 uri: https://github.com/trunk-io/plugins repo: repo: host: github.com owner: z-shell - name: zi-src + name: src lint: + definitions: + - name: shfmt + commands: + - name: format + output: shfmt + run: shfmt -w -s -ln=bash -i 2 ${target} + success_codes: [0, 1] + cache_results: true + formatter: true + batch: true + in_place: true disabled: - yamllint - trufflehog @@ -20,13 +31,13 @@ lint: - trivy enabled: - git-diff-check@SYSTEM - - actionlint@1.7.7 - - gitleaks@8.24.0 - - markdownlint@0.44.0 - - prettier@3.5.3 - - shellcheck@0.10.0 + - actionlint@1.7.12 + - gitleaks@8.30.1 + - markdownlint@0.48.0 + - prettier@3.8.3 + - shellcheck@0.11.0 - shfmt@3.6.0 - - taplo@0.9.3 + - taplo@0.10.0 actions: enabled: - trunk-announce @@ -36,6 +47,6 @@ actions: - trunk-cache-prune runtimes: enabled: - - python@3.10.8 + - python@3.14.4 - go@1.21.0 - - node@18.20.5 + - node@22.16.0 diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index c17dc3e..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "[markdown]": { - "editor.formatOnSave": true, - "editor.formatOnPaste": true - }, - "editor.codeActionsOnSave": { - "source.fixAll.markdownlint": true - }, - "conventionalCommits.scopes": [ - "maintenance", - "workspace", - "community", - "general", - "misc" - ], - "shellformat.useEditorConfig": true -} diff --git a/docs/README.md b/docs/README.md index cd6b128..7b2fa8c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -21,7 +21,7 @@ VIM - + Visual Studio Code


@@ -31,13 +31,40 @@ - https://wiki.zshell.dev - Loader: - https://init.zshell.dev - - https://git.io/zi-loader - Installer: - https://get.zshell.dev - - https://git.io/get-zi - R2: - https://r2.zshell.dev - IPFS: - https://ipfs.zshell.dev - jsDeliver: - - https://cdn.jsdelivr.net/gh/z-shell/zi-src@main/ + - https://cdn.jsdelivr.net/gh/z-shell/src@main/ + +### Maintainer — Verify and Sync Loader + +Check whether the local `lib/zsh/init.zsh` matches the canonical GitHub raw `main` copy: + +```sh +sh lib/sh/sync-init.sh +``` + +Replace the local file if it drifts: + +```sh +sh lib/sh/sync-init.sh --write +``` + +Run against local fixtures (no network required, useful in tests): + +```sh +sh lib/sh/sync-init.sh \ + --local /tmp/my-init.zsh \ + --remote /tmp/remote-init.zsh \ + --checksum-url /tmp/checksum.txt +``` + +Skip checksum validation: + +```sh +sh lib/sh/sync-init.sh --no-checksum +``` diff --git a/lib/checksum.txt b/lib/checksum.txt index 7335fdd..cb840e2 100644 --- a/lib/checksum.txt +++ b/lib/checksum.txt @@ -1,3 +1,4 @@ -13668e0d6edce92994b0434932873d8a8a2c2579a2cb53964de19212f02a2954 lib/sh/install_zpmod.sh -6214cad3026f4150dfe1070592f6b24256cae5c60c596bdfebda07ffeaaf39fc lib/sh/install.sh -23a563e80249a866c7cba3ac44eaedb1ca38f20c35690fb764a7cb75e95d38be lib/zsh/init.zsh +5d8fdd881b84548f38c1d5ffc6d5f46a18919d8dd6c1398d69abe055dd76ef98 lib/sh/install_zpmod.sh +b2363e18f5dff0d94b475150922a8646d55d1c0fa2ef47dbf5f513361d111f44 lib/sh/install.sh +83d9f0dc013376b5aa03b7a11f3908058da176df3407ac6e2872857d7c9658fd lib/sh/sync-init.sh +66dc5bb0575d44e4e1192e229204abe1ac47979a05ee3c3bb23afc2b9b493637 lib/zsh/init.zsh diff --git a/lib/sh/i.sh b/lib/sh/i.sh deleted file mode 100755 index 285a610..0000000 --- a/lib/sh/i.sh +++ /dev/null @@ -1,547 +0,0 @@ -#!/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 -trap 'rm -rf "$WORKDIR"' EXIT INT -WORKDIR="$(mktemp -d)" - -col_pname="" -col_error="" -col_info="" -col_rst="" -zsh_current=$(zsh --version &2 <&2 - exit 1 -} - -_os_type() { - OS="$(command -v uname)" - case $("${OS}" | tr '[:upper:]' '[:lower:]') in - android*) - OS='android' - ;; - darwin*) - OS='darwin' - ;; - linux*) - OS='linux' - ;; - freebsd*) - OS='freebsd' - ;; - netbsd*) - OS='netbsd' - ;; - openbsd*) - OS='openbsd' - ;; - sunos*) - OS='solaris' - ;; - msys* | cygwin* | mingw*) - OS='windows' - ;; - nt | win*) - OS='windows' - ;; - *) - echo 'OS not supported' - ;; - esac -} - -_cpu_type() { - case "$(uname -m)" in - x86_64 | x86-64 | x64 | amd64) - ARCH='amd64' - ;; - i?86 | x86) - ARCH='386' - ;; - armv8* | aarch64 | arm64) - ARCH='arm64' - ;; - armv7*) - ARCH='armv7' - ;; - armv6*) - ARCH='armv6' - ;; - arm*) - ARCH='arm' - ;; - mips64le*) - ARCH='mips64le' - ;; - mips64*) - ARCH='mips64' - ;; - mipsle*) - ARCH='mipsle' - ;; - mips*) - ARCH='mips' - ;; - ppc64le*) - ARCH='ppc64le' - ;; - ppc64*) - ARCH='ppc64' - ;; - ppcle*) - ARCH='ppcle' - ;; - ppc*) - ARCH='ppc' - ;; - s390*) - ARCH='s390x' - ;; - *) - echo 'OS architecture not supported' - ;; - esac -} - -# Synopsis: -# -# annex: (available: recommended) -# -a recommended -# branch: (available: main or any other existing branch) -# -b main -# configuration directory: (default: ~/.config/zi, can be overridden to prefered location) -# -c ${HOME}/.config/zi -# ZI home directory (default: ~/.zi, can be overriden prefered location) -# -d ${HOME}/.zi -# zshrc header: (available loader, installer) -# -e loader -# clone options: (default: --progress, can be overridden to prefered git clone options) -# -o --progress -# make/build options: (build, make additonal. Available: zpmod) -# -m zpmod -# host to use: (default: host to use. Available: github.com, gitlab.com) -# -h github.com -# install profile: (profile to run: install, uninstall) -# -p install -# snippets: (group of snippets to install: not-available yet) -# -s not-available yet -# plugins: group of plugins to install: not-available yet) -# -s not-available yet -# -# Example: ./install.sh -p install -c ~/.config/zi -d ~/.zi -e loader -o --progress -m zpmod -h github.com - -while getopts ":a:b:c:d:e:o:m:h:p:z:s:" opt; do - case ${opt} in - a) - ANNEX="${ANNEX}${OPTARG}" - ;; - b) - BRANCH="${BRANCH}${OPTARG}" - ;; - c) - CONFIG_DIR="${CONFIG_DIR}${OPTARG}" - ;; - d) - ZI_HOME="${ZI_HOME}${OPTARG}" - ;; - e) - ZHEADER="${ZHEADER}${OPTARG}" - ;; - o) - CLONE_OPTS="${CLONE_OPTS}${OPTARG}" - ;; - m) - MAKE="${MAKE}${OPTARG}" - ;; - h) - HOST="${HOST}${OPTARG}" - ;; - p) - PROFILE="${PROFILE}${OPTARG}" - ;; - z) - OMZ="${OMZ}${OPTARG}" - ;; - s) - STD="${STD}${OPTARG}" - ;; - \?) - err "Invalid option: ${OPTARG}" 1>&2 - ;; - :) - err "Invalid option: ${OPTARG} requires an argument" 1>&2 - ;; - *) - err "Invalid option: ${OPTARG}" 1>&2 - - ;; - esac -done -shift $((OPTIND - 1)) - -if [ -z "${PROFILE}" ]; then - # Available profiles: install, uninstall, main. - PROFILE="main" -fi -if [ -z "${HOST}" ]; then - # Default host - HOST="github.com" -fi -if [ -z "${BRANCH}" ]; then - # Default branch - BRANCH="main" -fi -if [ -z "${CLONE_OPTS}" ]; then - # Default git clone options - CLONE_OPTS="--progress" -fi -if [ -z "${ZI_HOME}" ]; then - # Installiation time ZI home directory - ZI_HOME="${ZDOTDIR:-${HOME}}/.zi" -fi -if [ -z "${ZI_BIN_DIR_NAME}" ]; then - # ZI bin directory - ZI_BIN_DIR_NAME="bin" -fi -if [ -z "${CONFIG_DIR}" ]; then - # Default configuration directory - CONFIG_DIR="${XDG_CONFIG_HOME:-${HOME}/.config}/zi" -fi -if [ -z "${ZHEADER}" ]; then - # Default header script - ZHEADER="loader" -fi - -_set_externals() { - if ! command -v git >/dev/null 2>&1; then - err "▓▒░ Something went wrong: git not available, cannot proceed." - fi - if [ "${HOST}" = github.com ]; then - if curl -fsL https://raw.githubusercontent.com/z-shell/zi-src/main/lib/zsh/init.zsh >/dev/null; then - RAW_LOADER_URL="https://raw.githubusercontent.com/z-shell/zi-src/main/lib/zsh/init.zsh" - if curl -fsL https://raw.githubusercontent.com/z-shell/zi/main/lib/zsh/git-process-output.zsh >/dev/null; then - RAW_GIT_OUPUT="https://raw.githubusercontent.com/z-shell/zi/main/lib/zsh/git-process-output.zsh" - else - err "Git process script at GitHub is not reachable" - fi - else - err "GitLab and GitHub repositories are unreachable" - fi - elif [ "${HOST}" = gitlab.com ]; then - if curl -fsL https://gitlab.com/ss-o/zi-src/-/raw/main/lib/zsh/init.zsh >/dev/null; then - RAW_LOADER_URL="https://gitlab.com/ss-o/zi-src/-/raw/main/lib/zsh/init.zsh" - if curl -fsL https://gitlab.com/ss-o/zi/-/raw/main/lib/zsh/git-process-output.zsh >/dev/null; then - RAW_GIT_OUPUT="https://gitlab.com/ss-o/zi/-/raw/main/lib/zsh/git-process-output.zsh" - else - err "Git process script at GitLab is not reachable" - fi - fi - fi - # Get the download-progress bar tool - if command -v curl >/dev/null 2>&1; then - command mkdir -p "${WORKDIR}" - cd "${WORKDIR}" || return - command curl -fsSLO "${RAW_GIT_OUPUT}" && command chmod a+x "${WORKDIR}"/git-process-output.zsh - elif command -v wget >/dev/null 2>&1; then - command mkdir -p "${WORKDIR}" - cd "${WORKDIR}" || return - command wget -q "${RAW_GIT_OUPUT}" && command chmod a+x "${WORKDIR}"/git-process-output.zsh - else - say "▓▒░ Something went wrong:" - err "curl or wget not available or failed to create temp directory, cannot proceed." - fi -} - -_check_zshrc() { - THE_ZDOTDIR="${ZDOTDIR:-${HOME}}" - say "▓▒░ Updating ${THE_ZDOTDIR}/.zshrc" - if grep -E '(zi|init|zinit)\.zsh' "${THE_ZDOTDIR}/.zshrc" >/dev/null 2>&1; then - say "▓▒░ File .zshrc have conflicting commands, backuping..." - say "▓▒░ creating backup at ${CONFIG_DIR}/zshrc..." - date=$(date +%H%M%S) && command mv -f "${THE_ZDOTDIR}/.zshrc" "${CONFIG_DIR}/zshrc.${date}" - fi -} - -_set_zshrc_header() { - # current zshrc headers: (loader, installer) - _check_zshrc - if [ "${ZHEADER}" = loader ]; then - if command -v curl >/dev/null 2>&1; then - command curl -fsSL "${RAW_LOADER_URL}" -o "${CONFIG_DIR}/init.zsh" - elif command -v wget >/dev/null 2>&1; then - command wget -qO "${CONFIG_DIR}/init.zsh" "${RAW_LOADER_URL}" - else - err "▓▒░ Something went wrong: curl and wget not available, cannot proceed." - fi - command chmod a+x "${CONFIG_DIR}/init.zsh" - command sed -i "s/branch=\"main\"/branch=\"${BRANCH}\"/g" "${CONFIG_DIR}/init.zsh" - command cat <<-EOF >>"${THE_ZDOTDIR}/.zshrc" -if [[ -r "${XDG_CONFIG_HOME:-${HOME}/.config}/zi/init.zsh" ]]; then - source "${XDG_CONFIG_HOME:-${HOME}/.config}/zi/init.zsh" && zzinit -fi -EOF - say "▓▒░ Loader added successfully." - elif [ "${ZHEADER}" = installer ]; then - command cat <<-EOF >>"${THE_ZDOTDIR}/.zshrc" -if [[ ! -f ${ZI_HOME}/${ZI_BIN_DIR_NAME}/zi.zsh ]]; then - print -P "%F{33}▓▒░ %F{160}Installing (%F{33}z-shell/zi%F{160})…%f" - command mkdir -p "${ZI_HOME}" && command chmod g-rwX "${ZI_HOME}" - command git clone ${CLONE_OPTS} --branch "${BRANCH}" https://${HOST}/z-shell/zi "${ZI_HOME}/${ZI_BIN_DIR_NAME}" && \\ - print -P "%F{33}▓▒░ %F{34}Installation successful.%f%b" || \\ - print -P "%F{160}▓▒░ The clone has failed.%f%b" -fi -source "${ZI_HOME}/${ZI_BIN_DIR_NAME}/zi.zsh" -autoload -Uz _zi -(( \${+_comps} )) && _comps[zi]=_zi -EOF - else - true - fi -} - -_setup_directories() { - _set_externals "$@" - if ! test -d "${ZI_HOME}"; then - command mkdir "${ZI_HOME}" - command chmod g-w "${ZI_HOME}" - command chmod o-w "${ZI_HOME}" - fi - if ! test -d "${ZI_HOME}/${ZI_BIN_DIR_NAME}"; then - command mkdir "${ZI_HOME}/${ZI_BIN_DIR_NAME}" - command chmod g-w "${ZI_HOME}/${ZI_BIN_DIR_NAME}" - command chmod o-w "${ZI_HOME}/${ZI_BIN_DIR_NAME}" - fi - if ! test -d "${CONFIG_DIR}"; then - command mkdir "${CONFIG_DIR}" - command chmod g-w "${CONFIG_DIR}" - command chmod o-w "${CONFIG_DIR}" - fi - _set_zshrc_header "$@" -} - -_setup_profile() { - _setup_directories "$@" - if [ "${PROFILE}" = install ]; then - if test -d "${ZI_HOME}/${ZI_BIN_DIR_NAME}/.git"; then - cd "${ZI_HOME}/${ZI_BIN_DIR_NAME}" || return - say "▓▒░ Updating (z-shell/zi) plugin manager at ${ZI_HOME}/${ZI_BIN_DIR_NAME}" - command git clean -d -f -f - command git reset --hard HEAD - command git pull -q origin HEAD - command git submodule update --init --recursive - command git submodule update --recursive --remote - say "▓▒░ Successfully installed (z-shell/zi) at ${ZI_HOME}/${ZI_BIN_DIR_NAME}" - else - cd "${ZI_HOME}" || return - say "▓▒░ Installing (z-shell/zi) plugin manager at ${ZI_HOME}/${ZI_BIN_DIR_NAME}" - { git clone "${CLONE_OPTS}" --branch "${BRANCH}" https://"${HOST}"/z-shell/zi.git "${ZI_BIN_DIR_NAME}" \ - 2>&1 | { "${WORKDIR}/out/git-process-output.zsh" || cat; }; } 2>/dev/null - if [ -d "${ZI_BIN_DIR_NAME}" ]; then - say "▓▒░ Successfully installed at ${ZI_HOME}/${ZI_BIN_DIR_NAME}". - else - err "▓▒░ Something went wrong, couldn't install ZI at ${ZI_HOME}/${ZI_BIN_DIR_NAME}" - fi - fi - elif [ "${PROFILE}" = uninstall ]; then - clear - say "▓▒░ Remove ❮ ZI ❯? [y/N]" - read -r confirmation - if [ "${confirmation}" != y ] && [ "${confirmation}" != Y ]; then - echo "Uninstall process cancelled" - exit 0 - fi - clear - say "Removing ❮ ZI ❯ home directory" - sleep 2 - if [ -d "${HOME}/.zi" ]; then - rm -rvf "${HOME}/.zi" - elif [ -d "${ZDOTDIR}/.zi" ]; then - rm -rvf "${ZDOTDIR}/.zi" - elif [ -d "${XDG_DATA_HOME}/.zi" ]; then - rm -rvf "${XDG_DATA_HOME}/.zi" - fi - clear - echo "▓▒░ Clean ❮ ZI ❯ cache? [y/N]" - read -r confirmation - if [ "${confirmation}" != y ] && [ "${confirmation}" != Y ]; then - echo "Cleaning ❮ ZI ❯ cache" - sleep 2 - if [ -d "${HOME}/.cache/zi" ]; then - rm -rvf "${HOME}/.cache/zi" - elif [ -d "${ZDOTDIR}/.cache/zi" ]; then - rm -rvf "${ZDOTDIR}/.cache/zi" - elif [ -d "${XDG_DATA_HOME}/.cache/zi" ]; then - rm -rvf "${XDG_DATA_HOME}/.cache/zi" - fi - fi - clear - echo "▓▒░ Remove ❮ ZI ❯ config directory? [y/N]" - read -r confirmation - if [ "${confirmation}" != y ] && [ "${confirmation}" != Y ]; then - echo "Removing ❮ ZI ❯ config directory" - sleep 2 - if [ -d "${XDG_CONFIG_HOME}/zi" ]; then - rm -rvf "${XDG_CONFIG_HOME}/zi" - else - if [ -d "${HOME}/.config/zi" ]; then - rm -rvf "${HOME}/.config/zi" - elif [ -d "${XDG_DATA_HOME}/zi" ]; then - rm -rvf "${XDG_DATA_HOME}/zi" - fi - fi - fi - clear - echo "▓▒░ Reload shell? [y/N]" - read -r confirmation - if [ "${confirmation}" != y ] && [ "${confirmation}" != Y ]; then - say "▓▒░ Uninstall successful" - command cat <<-EOF -▓▒░ ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ ❮ ZI ❯ -▓▒░ Wiki: https://wiki.zshell.dev -▓▒░ Issues: https://github.com/z-shell/zi/issues -▓▒░ Discussions: https://discussions.zshell.dev" -EOF - rm -rf "${WORKDIR}" - exit 0 - else - rm -rf "${WORKDIR}" - exec "${SHELL}" -l - fi - elif [ "${PROFILE}" = main ]; then - true - else - err "Invalid profile: ${PROFILE}" 1>&2 - fi -} - -_make_build() { - if [ "${MAKE}" = zpmod ]; then - ZI_HOME="${ZI_HOME:-${ZDOTDIR:-${HOME}}/.zi}" - MOD_HOME="${MOD_HOME:-zmodules}/zpmod" - if ! test -d "${ZI_HOME}/${MOD_HOME}"; then - mkdir -p "${ZI_HOME}/${MOD_HOME}" - chmod g-rwX "${ZI_HOME}/${MOD_HOME}" - fi - say "${col_pname}== Downloading ZPMOD module to ${ZI_HOME}/${MOD_HOME}" - if test -d "${ZI_HOME}/${MOD_HOME}/.git"; then - cd "${ZI_HOME}/${MOD_HOME}" || return - git pull -q origin main - else - cd "${ZI_HOME}" || return - git clone "${CLONE_OPTS}" https://"${HOST}"/z-shell/zpmod.git "${MOD_HOME}" - fi - say "${col_pname}== Done" - if command -v zsh >/dev/null; then - say "${col_info}-- Checkig version --${col_rst}" - if expr "${zsh_current}" \< "${zsh_required}" >/dev/null; then - say "${col_error}-- Zsh version 5.8.1 and above required --${col_rst}" - - else - say "${col_info}-- Zsh version ${zsh_current} --${col_rst}" - cd "${ZI_HOME}/${MOD_HOME}" || return - say "${col_pname}== Building module ZPMOD, running: a make clean, then ./configure and then make ==${col_rst}" - say "${col_pname}== The module sources are located at: ${ZI_HOME}/${MOD_HOME} ==${col_rst}" - if test -f Makefile; then - if [ "$1" = "--clean" ]; then - say "${col_info}-- make distclean --${col_rst}" - make -s distclean - true - else - say "${col_info}-- make clean (pass --clean to invoke \`make distclean') --${col_rst}" - make -s clean - fi - fi - say "${col_info}-- Configuring --${col_rst}" - if CPPFLAGS=-I/usr/local/include CFLAGS="-g -Wall -O3" LDFLAGS=-L/usr/local/lib ./configure --disable-gdbm --without-tcsetpgrp; then - say "${col_info}-- Running make --${col_rst}" - if make -s; then - command cat <<-EOF -▓▒░ Module has been built correctly. -▓▒░ See 'zpmod -h' for more information. -▓▒░ Run 'zpmod source-study' to see profile data, -▓▒░ Guaranteed, automatic compilation of any sourced script. -EOF - zpmod_file="${WORKDIR}/zpmod" - command cat <<-EOF >>"${zpmod_file}" -module_path+=( "${ZI_HOME}/${MOD_HOME}/Src" ) -zmodload zi/zpmod -EOF - say "▓▒░ Enabling zpmod" - command cat "${zpmod_file}" >>"${THE_ZDOTDIR}/.zshrc" - zsh -ic "@zi-scheduler burst" - else - say "${col_error}Module did not build.${col_rst}. You can copy the error messages and submit" - err "error-report at: https://${HOST}/z-shell/zpmod/issues" - fi - fi - fi - else - err "${col_error} Zsh is not installed. Please install zsh and try again.${col_rst}" - fi - else - true - fi -} - -_set_annexes() { - if [ "${ANNEX}" = recommended ]; then - file="${WORKDIR}/annex_recommended" - command cat <<-EOF >>"${file}" -zi light-mode for \\ - z-shell/z-a-meta-plugins \\ - @annexes # <- https://wiki.zshell.dev/ecosystem/category/-annexes -# examples here -> https://wiki.zshell.dev/community/gallery/collection -zicompinit # <- https://wiki.zshell.dev/docs/guides/commands -EOF - say "▓▒░ Installing annexes" - command cat "${file}" >>"${THE_ZDOTDIR}/.zshrc" - zsh -ic "@zi-scheduler burst" - else - true - fi -} - -_finish_install() { - git_refs="$( - command cd "${ZI_HOME}/${ZI_BIN_DIR_NAME}" || true - command git log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit | head -3 - )" - say "▓▒░ Latest changes:" - say "${git_refs}" -} - -_system() { - _os_type - _cpu_type - _setup_profile "$@" - _make_build "$@" - _set_annexes "$@" - _finish_install "$@" -} - -MAIN() { - _system "$@" - say "▓▒░ System: ${OS} - ${ARCH}" - command cat <<-EOF -▓▒░ ■■■■■■■■■■■■■■■■■ Successfully installed ❮ ZI ❯ ■■■■■■■■■ -▓▒░ Wiki: https://wiki.zshell.dev -▓▒░ Issues: https://github.com/z-shell/zi/issues -▓▒░ Discussions: https://discussions.zshell.dev -▓▒░ ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ -EOF - exit 0 -} - -while true; do - MAIN "${@}" -done diff --git a/lib/sh/install.sh b/lib/sh/install.sh index 0f7bbbc..74b94a1 100755 --- a/lib/sh/install.sh +++ b/lib/sh/install.sh @@ -2,8 +2,10 @@ # -*- mode: sh; sh-indentation: 2; indent-tabs-mode: nil; sh-basic-offset: 2; -*- # vim: ft=sh sw=2 ts=2 et -trap 'rm -rf "$WORKDIR"' EXIT INT -WORKDIR="$(mktemp -d)" +set -eu + +WORKDIR="$(mktemp -d)" || exit 1 +trap 'rm -rf "${WORKDIR:?}"' EXIT INT TERM ZOPT="" AOPT="" BOPT="main" @@ -38,26 +40,26 @@ if [ "${AOPT}" = loader ]; then ZI_CONFIG_DIR="${XDG_CONFIG_HOME:-${HOME}/.config}/zi" command mkdir -p "${ZI_CONFIG_DIR}" if command -v curl >/dev/null 2>&1; then - command curl -fsSL https://raw.githubusercontent.com/z-shell/zi-src/main/lib/zsh/init.zsh -o "${ZI_CONFIG_DIR}/init.zsh" + command curl -fsSL https://raw.githubusercontent.com/z-shell/src/main/lib/zsh/init.zsh -o "${ZI_CONFIG_DIR}/init.zsh" elif command -v wget >/dev/null 2>&1; then - command wget -qO "${ZI_CONFIG_DIR}/init.zsh" https://raw.githubusercontent.com/z-shell/zi-src/main/lib/zsh/init.zsh + command wget -qO "${ZI_CONFIG_DIR}/init.zsh" https://raw.githubusercontent.com/z-shell/src/main/lib/zsh/init.zsh fi command chmod go-w "${ZI_CONFIG_DIR}" && command chmod a+x "${ZI_CONFIG_DIR}/init.zsh" - command sed -i "s/branch=\"main\"/branch=\"${BOPT}\"/g" "${ZI_CONFIG_DIR}/init.zsh" + # shellcheck disable=SC2016 + command sed -i 's|: ${ZI\[STREAM\]:="main"}|: ${ZI[STREAM]:="'"${BOPT}"'"}|' "${ZI_CONFIG_DIR}/init.zsh" fi -if [ -z "${ZI_HOME}" ]; then - ZI_HOME="${ZDOTDIR:-${HOME}}/.zi" +if [ -z "${ZI_HOME-}" ]; then + ZI_HOME="${XDG_DATA_HOME:-${HOME}/.local/share}/zi" fi -if [ -z "${ZI_BIN_DIR_NAME}" ]; then +if [ -z "${ZI_BIN_DIR_NAME-}" ]; then ZI_BIN_DIR_NAME="bin" fi if ! test -d "${ZI_HOME}"; then command mkdir "${ZI_HOME}" command chmod go-w "${ZI_HOME}" - command chmod go-w "${ZI_HOME}/${ZI_BIN_DIR_NAME}" fi if ! command -v git >/dev/null 2>&1; then @@ -68,31 +70,32 @@ fi # Get the download-progress bar tool if command -v curl >/dev/null 2>&1; then command mkdir -p /tmp/zi - cd /tmp/zi || return + cd /tmp/zi || exit 1 command curl -fsSLO https://raw.githubusercontent.com/z-shell/zi/main/lib/zsh/git-process-output.zsh && command chmod a+x /tmp/zi/git-process-output.zsh elif command -v wget >/dev/null 2>&1; then command mkdir -p /tmp/zi - cd /tmp/zi || return + cd /tmp/zi || exit 1 command wget -q https://raw.githubusercontent.com/z-shell/zi/main/lib/zsh/git-process-output.zsh && command chmod a+x /tmp/zi/git-process-output.zsh fi if test -d "${ZI_HOME}/${ZI_BIN_DIR_NAME}/.git"; then - cd "${ZI_HOME}/${ZI_BIN_DIR_NAME}" || return + cd "${ZI_HOME}/${ZI_BIN_DIR_NAME}" || exit 1 printf '%s\n' "▓▒░ Updating (z-shell/zi) plugin manager at ${ZI_HOME}/${ZI_BIN_DIR_NAME}" command git clean -d -f -f command git reset --hard HEAD - command git pull -q origin HEAD + command git pull -q origin "${BOPT}" else - cd "${ZI_HOME}" || return + cd "${ZI_HOME}" || exit 1 printf '%s\n' "▓▒░ Installing (z-shell/zi) plugin manager at ${ZI_HOME}/${ZI_BIN_DIR_NAME}" { git clone --progress --depth=1 --branch "${BOPT}" https://github.com/z-shell/zi.git "${ZI_BIN_DIR_NAME}" \ 2>&1 | { /tmp/zi/git-process-output.zsh || cat; }; } 2>/dev/null - if [ -d "${ZI_BIN_DIR_NAME}" ]; then + if [ -d "${ZI_HOME}/${ZI_BIN_DIR_NAME}" ] && [ -f "${ZI_HOME}/${ZI_BIN_DIR_NAME}/zi.zsh" ]; then printf '%s\n' "▓▒░ Successfully installed at ${ZI_HOME}/${ZI_BIN_DIR_NAME}". else printf '%s\n' "▓▒░ Something went wrong, couldn't install ZI at ${ZI_HOME}/${ZI_BIN_DIR_NAME}" + exit 1 fi fi @@ -106,7 +109,7 @@ MAIN_PROFILE() { printf '%s\n' "▓▒░ Seems that .zshrc already has content or setup skipped - no changes will be made." ZOPT='skip' fi - if [ "${ZOPT}" != skip ]; then + if [ "${ZOPT}" != skip ] && [ "${AOPT}" != loader ]; then printf '%s\n' "▓▒░ Updating ${THE_ZDOTDIR}/.zshrc" ZI_HOME="$(echo "${ZI_HOME}" | sed "s|${HOME}|\$HOME|")" command cat <<-EOF >>"${THE_ZDOTDIR}/.zshrc" @@ -126,10 +129,9 @@ EOF printf '%s\n' "▓▒░ Minimal configuration" fi if [ "${AOPT}" = loader ] && [ "${ZOPT}" != skip ]; then - command rm -rf "${THE_ZDOTDIR}/.zshrc" command cat <<-EOF >>"${THE_ZDOTDIR}/.zshrc" -if [[ -r "${XDG_CONFIG_HOME:-${HOME}/.config}/zi/init.zsh" ]]; then - source "${XDG_CONFIG_HOME:-${HOME}/.config}/zi/init.zsh" && zzinit +if [[ -r "\${XDG_CONFIG_HOME:-\${HOME}/.config}/zi/init.zsh" ]]; then + source "\${XDG_CONFIG_HOME:-\${HOME}/.config}/zi/init.zsh" && zzinit fi EOF printf '%s\n' "▓▒░ Loader added" @@ -164,86 +166,38 @@ EOF fi } -SETUP_ZPMOD() { - if ! test -d "${ZI_HOME}/${MOD_HOME}"; then - command mkdir -p "${ZI_HOME}/${MOD_HOME}" - command chmod go-w "${ZI_HOME}/${MOD_HOME}" - fi +ZPMOD_PROFILE() { + _zpmod_sh="" + case "$0" in + */*) + _script_dir="$(cd "$(dirname "$0")" 2>/dev/null && pwd)" || _script_dir="" + ;; + *) + _script_dir="" + ;; + esac - printf '%s\n' "${col_pname}== Downloading ZPMOD module to ${ZI_HOME}/${MOD_HOME}" - if test -d "${ZI_HOME}/${MOD_HOME}/.git"; then - cd "${ZI_HOME}/${MOD_HOME}" || return - git pull -q origin main + if [ -n "${_script_dir}" ] && [ -f "${_script_dir}/install_zpmod.sh" ]; then + _zpmod_sh="${_script_dir}/install_zpmod.sh" else - cd "${ZI_HOME}" || return - git clone -q https://github.com/z-shell/zpmod.git "${MOD_HOME}" - fi - printf '%s\n' "${col_pname}== Done" -} - -BUILD_ZPMOD() { - if command -v zsh >/dev/null; then - printf '%s\n' "${col_info2}-- Checkig version --${col_rst}" - ZSH_CURRENT=$(zsh --version /dev/null; then - printf '%s\n' "${col_error}-- Zsh version 5.8.1 and above required --${col_rst}" - exit 1 + _zpmod_sh="${WORKDIR}/install_zpmod.sh" + _zpmod_url="https://raw.githubusercontent.com/z-shell/src/main/lib/sh/install_zpmod.sh" + if command -v curl >/dev/null 2>&1; then + command curl -fsSL "${_zpmod_url}" -o "${_zpmod_sh}" + elif command -v wget >/dev/null 2>&1; then + command wget -qO "${_zpmod_sh}" "${_zpmod_url}" else - printf '%s\n' "${col_info2}-- Zsh version ${ZSH_CURRENT} --${col_rst}" - cd "${ZI_HOME}/${MOD_HOME}" || return - printf '%s\n' "${col_pname}== Building module ZPMOD, running: a make clean, then ./configure and then make ==${col_rst}" - printf '%s\n' "${col_pname}== The module sources are located at: ${ZI_HOME}/${MOD_HOME} ==${col_rst}" - if test -f Makefile; then - if [ "$1" = "--clean" ]; then - printf '%s\n' "${col_info2}-- make distclean --${col_rst}" - make -s distclean - true - else - printf '%s\n' "${col_info2}-- make clean (pass --clean to invoke \`make distclean') --${col_rst}" - make -s clean - fi - fi - printf '%s\n' "${col_info2}-- Configuring --${col_rst}" - if CPPFLAGS=-I/usr/local/include CFLAGS="-g -Wall -O3" LDFLAGS=-L/usr/local/lib ./configure --disable-gdbm --without-tcsetpgrp; then - printf '%s\n' "${col_info2}-- Running make --${col_rst}" - if make -s; then - command cat <<-EOF -▓▒░ Module has been built correctly. -▓▒░ To load the module, add following 2 lines to .zshrc, at top: -  module_path+=( "${ZI_HOME}/${MOD_HOME}/Src" ) -  zmodload zi/zpmod -▓▒░ See 'zpmod -h' for more information. -▓▒░ Run 'zpmod source-study' to see profile data, -▓▒░ Guaranteed, automatic compilation of any sourced script. -EOF - else - printf '%s\n' "${col_error}Module didn't build.${col_rst}. You can copy the error messages and submit" - printf '%s\n' "error-report at: https://github.com/z-shell/zpmod/issues" - fi - fi + printf '%s\n' "-- ERROR -- curl or wget is required to download install_zpmod.sh" >&2 + exit 1 fi - else - printf '%s\n' "${col_error} Zsh is not installed. Please install zsh and try again.${col_rst}" + if [ ! -s "${_zpmod_sh}" ]; then + printf '%s\n' "-- ERROR -- failed to download install_zpmod.sh" >&2 + exit 1 + fi + command chmod a+x "${_zpmod_sh}" fi -} -ZPMOD_PROFILE() { - col_pname="" - col_error="" - col_info="" - col_info2="" - col_rst="" - - ZI_HOME="${ZI_HOME:-${ZDOTDIR:-${HOME}}/.zi}" - MOD_HOME="${MOD_HOME:-zmodules}/zpmod" - - printf '%s\n' "${col_info}Re-run this script to update (from Github) and rebuild the module.${col_rst}" - printf '%s\n' "${col_info2}Press any key to continue, or Ctrl-C to exit.${col_rst}" - read -r - - SETUP_ZPMOD - BUILD_ZPMOD "$@" + exec sh "${_zpmod_sh}" "$@" } CLOSE_PROFILE() { @@ -273,6 +227,4 @@ EOF exit 0 } -while true; do - MAIN "${@}" -done +MAIN "${@}" diff --git a/lib/sh/install_alpha.sh b/lib/sh/install_alpha.sh deleted file mode 100755 index a62d423..0000000 --- a/lib/sh/install_alpha.sh +++ /dev/null @@ -1,512 +0,0 @@ -#!/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 - -# shellcheck disable=all - -# Temporary work directory -trap 'rm -rf "$WORKDIR"' EXIT INT - -# Variables -WORKDIR="$(mktemp -d)" -ZI_REPO="https://github.com/z-shell/zi" -MOD_REPO="https://github.com/z-shell/zpmod" -GIT_BAR="${WORKDIR}/git-progress-bar.zsh" -GIT_BAR_URL="https://raw.githubusercontent.com/z-shell/zi/main/lib/zsh/git-process-output.zsh" -LOADER_URL="https://raw.githubusercontent.com/z-shell/zi-src/main/lib/zsh/init.zsh" - -# Messages -say() { - while [ -n "$1" ]; do - case "$1" in - -normal) col="\033[00m" ;; - -black) col="\033[30;01m" ;; - -red) col="\033[31;01m" ;; - -green) col="\033[32;01m" ;; - -yellow) col="\033[33;01m" ;; - -blue) col="\033[34;01m" ;; - -magenta) col="\033[35;01m" ;; - -cyan) col="\033[36;01m" ;; - -white) col="\033[37;01m" ;; - -n) - one_line=1 - shift - continue - ;; - *) - printf '%s' "$1" - shift - continue - ;; - esac - shift - printf "%s${col}" - printf '%s' "$1" - printf "\033[00m" - shift - done - [ -z "${one_line}" ] && printf "\n" -} - -ask() { - question="$1" - printf "\033[34;1m▓▒░ \033[00m» " - say -yellow "$question" -n - printf " \033[00m[y/N]: " - read -r answer - case $answer in - [yY]*) - true - ;; - *) - false - ;; - esac -} - -err() { - say -red "$1" >&2 - exit 1 -} - -say_ok() { - printf "\033[34;1m▓▒░\033[32;01m ✔ \033[00m» " - say -green "$1" - printf "\033[00m" -} - -say_err() { - printf "\033[34;01m▓▒░\033[31;01m ✘ \033[00m» " - say -red "$*" >&2 - printf "\033[00m" - exit 1 -} - -say_info() { - printf "\033[34;1m▓▒░\033[36;01m ⚡\033[00m» " - say -cyan "$1" - printf "\033[00m" -} - -while getopts ":i:a:b:" opt; do - case ${opt} in - i) - ZOPT="${ZOPT}${OPTARG}" - ;; - a) - AOPT="${AOPT}${OPTARG}" - ;; - b) - BOPT="${OPTARG}" - ;; - \?) - say_err "Invalid option: ${OPTARG}" - ;; - :) - say_err "Invalid option: ${OPTARG} requires an argument" - ;; - *) - say_err "Invalid option: ${OPTARG}" - ;; - esac -done -shift $((OPTIND - 1)) - -# Default options -[ -z "$BOPT" ] && BOPT="main" - -# Functions -is_cmd() { command -v "$1" >/dev/null 2>&1; } - -check_cmd() { - if ! is_cmd "$1"; then - say_err "$1 not found. Please install it and try again." - fi -} - -download() { - # Set download command - if is_cmd curl; then - command curl -fsSL "$1" -o "$2" && command chmod a+x "$2" - elif is_cmd wget; then - command wget -qO "$2" "$1" && command chmod a+x "$2" - else - say_err "curl or wget is required. Please install it and try again." - fi -} - -git_clone() { - command git clone --progress --depth 1 --branch "$BOPT" "$1" "$2" 2>&1 | { "$GIT_BAR" || cat; } 2>/dev/null -} - -prepare_installer() { - # Check for required commands - check_cmd git - check_cmd zsh - - # Establish Zi home directory - if [ -z "$ZI_HOME" ]; then - if [ -d "${HOME}" ]; then - ZSH_HOME_DIR="$HOME" - ZI_HOME="${HOME}/.zi" - elif [ -d "${ZDOTDIR}" ]; then - ZSH_HOME_DIR="$ZDOTDIR" - ZI_HOME="${ZDOTDIR}/.zi" - elif [ -d "${XDG_DATA_HOME}" ]; then - ZSH_HOME_DIR="$XDG_DATA_HOME" - ZI_HOME="${XDG_DATA_HOME}/.zi" - fi - fi - - if [ ! -d "$ZI_HOME" ]; then - command mkdir -p "$ZI_HOME" - fi - - if [ ! -w "$ZI_HOME" ]; then - command chown -R "$(whoami)" - command chmod -R go-w "$ZI_HOME" - fi - - # Establish Zi bin directory - if [ -z "$ZI_BIN_DIR" ]; then - ZI_BIN_DIR="${ZI_HOME}/bin" - fi - - if [ ! -d "$ZI_BIN_DIR" ]; then - command mkdir -p "$ZI_BIN_DIR" - fi - - if [ ! -w "$ZI_BIN_DIR" ]; then - command chown -R "$(whoami)" - command chmod -R go-w "$ZI_BIN_DIR" - fi - - if [ -z "$ZSH_CACHE_DIR" ]; then - ZSH_CACHE_DIR="${ZSH_HOME_DIR}/.cache/zi" - fi - - if [ ! -d "$ZSH_CACHE_DIR" ]; then - command mkdir -p "$ZSH_CACHE_DIR" - fi - - if [ ! -w "$ZSH_CACHE_DIR" ]; then - command chown -R "$(whoami)" - command chmod -R go-w "$ZSH_CACHE_DIR" - fi - - if [ -z "$ZSH_LOG_DIR" ]; then - ZSH_LOG_DIR="${ZSH_HOME_DIR}/.cache/zi/logs" - fi - - if [ -z "$ZSH_LOG_FILE" ]; then - ZSH_LOG_FILE="${ZSH_LOG_DIR}/$(date +%Y-%m-%d).log" - fi - - if [ ! -f "$GIT_BAR" ]; then - download "$GIT_BAR_URL" "$GIT_BAR" - fi -} - -check_zshrc() { - # Check if Zi is already installed - if grep -E '(zi|init|zinit)\.zsh' "${ZSH_HOME_DIR}/.zshrc" >/dev/null 2>&1; then - say_info "Zi already set in .zshrc, backing up to .zshrc.bak" - command mv "${ZSH_HOME_DIR}/.zshrc" "${ZSH_HOME_DIR}/.zshrc.bak" - elif [ -f "${ZSH_HOME_DIR}/.zshrc" ]; then - say_info "Backing up to current .zshrc to .zshrc.bak" - command mv "${ZSH_HOME_DIR}/.zshrc" "${ZSH_HOME_DIR}/.zshrc.bak" - fi -} - -set_repository() { - prepare_installer "$@" - - if [ -d "${ZI_BIN_DIR}/.git" ]; then - builtin cd "${ZI_BIN_DIR}" && say_info "Found Zi at $ZI_BIN_DIR, updating..." - command git clean --quiet -d -f -f - command git reset --quiet --hard HEAD - command git pull --quiet origin HEAD - say_ok "Update Successful!" - return 0 - elif [ -d "$ZI_BIN_DIR" ]; then - git_clone "$ZI_REPO" "$ZI_BIN_DIR" - if [ -f "${ZI_BIN_DIR}/zi.zsh" ]; then - command cat <<-EOF -▓▒░ ■■■■■■■■■■■■■■■■■ Successfully installed Zi ■■■■■■■■■■■■■ -EOF - return 0 - else - say_err "Something went wrong, couldn't proceed with installation." - fi - fi -} - -set_loader() { - check_zshrc - # Establish Zi config directory - if [ -z "$ZI_CONFIG_DIR" ]; then - ZI_CONFIG_DIR="${XDG_CONFIG_HOME:-${HOME}/.config}/zi" - fi - - if [ ! -d "$ZI_CONFIG_DIR" ]; then - command mkdir -p "$ZI_CONFIG_DIR" - fi - - if [ ! -w "$ZI_CONFIG_DIR" ]; then - command chmod go-w "$ZI_CONFIG_DIR" - fi - - download "$LOADER_URL" "${ZI_CONFIG_DIR}/init.zsh" - command sed -i "s/branch=\"main\"/branch=\"${BOPT}\"/g" "${ZI_CONFIG_DIR}/init.zsh" - command cat <<-EOF >>"$ZSH_HOME_DIR/.zshrc" -# Zi Loader ========================================================================================================= # -# https://wiki.zshell.dev/docs/getting_started/installation -if [[ -r "${XDG_CONFIG_HOME:-${HOME}/.config}/zi/init.zsh" ]]; then - source "${XDG_CONFIG_HOME:-${HOME}/.config}/zi/init.zsh" && zzinit -fi - -EOF - return 0 -} - -set_installer() { - check_zshrc - command cat <<-EOF >>"$ZSH_HOME_DIR/.zshrc" -# Zi source directory =============================================================================================== # -# https://wiki.zshell.dev/docs/guides/customization#customizing-paths -typeset -A ZI -ZI[BIN_DIR]="$ZI_BIN_DIR" - -# Auto install Zi =================================================================================================== # -if [[ ! -f \${ZI[BIN_DIR]}/zi.zsh ]]; then - print -P "%F{33}▓▒░ %F{160}Installing (%F{33}z-shell/zi%F{160})…%f" - command mkdir -p "\$ZI[BIN_DIR]" && \\ - command git clone -q --branch "${BOPT}" $ZI_REPO "\${ZI[BIN_DIR]}" && \\ - print -P "%F{33}▓▒░ %F{34}Installation successful…%f%b" || print -P "%F{160}▓▒░ The clone has failed.%f%b" -fi - -# Enable Zi ========================================================================================================= # -# https://wiki.zshell.dev/docs/getting_started/installation#manual-setup -source "\${ZI[BIN_DIR]}/zi.zsh" -autoload -Uz _zi -(( \${+_comps} )) && _comps[zi]=_zi - -EOF - return 0 -} - -set_omz_lib() { - command cat <<-EOF >>"$ZSH_HOME_DIR/.zshrc" -# Oh-My-Zsh lib ===================================================================================================== # -# https://wiki.zshell.dev/docs/getting_started/migration#omz-library -zi is-snippet wait lucid for \\ - OMZL::{git,theme-and-appearance,prompt_info_functions,vcs_info}.zsh \\ - atinit'COMPLETION_WAITING_DOTS=true' \\ - OMZL::completion.zsh \\ - atinit'typeset -gx HISTSIZE=290000 SAVEHIST=290000 HISTFILE=${ZSH_CACHE_DIR}/.history' \\ - OMZL::history.zsh - -EOF -} - -set_omz_plugins() { - command cat <<-EOF >>"$ZSH_HOME_DIR/.zshrc" -# Oh-My-Zsh plugins ================================================================================================= # -# https://wiki.zshell.dev/docs/getting_started/migration#omz-plugins -zi is-snippet wait lucid for \\ - atload"unalias grv" \\ - OMZP::git \\ - if'[[ -d ~/.ssh ]]' \\ - OMZP::ssh-agent \\ - if'[[ -d ~/.gnupg ]]' \\ - OMZP::gpg-agent - -EOF -} - -set_omz_themes() { - command cat <<-EOF >>"$ZSH_HOME_DIR/.zshrc" -# Oh-My-Zsh theme =================================================================================================== # -# https://wiki.zshell.dev/community/gallery/collection/themes -# https://wiki.zshell.dev/docs/getting_started/migration#omz-themes -zi wait'!' lucid for \\ - atinit'setopt prompt_subst' \\ - OMZT::robbyrussell - -EOF -} - -set_plugins() { - command cat <<-EOF >>"$ZSH_HOME_DIR/.zshrc" -# Popular plugins =================================================================================================== # -# https://wiki.zshell.dev/ecosystem -# https://wiki.zshell.dev/community/gallery/collection/plugins -zi wait lucid for \\ - atinit'ZI[COMPINIT_OPTS]=-C; zicompinit; zicdreplay' \\ - z-shell/F-Sy-H \\ - atload'!_zsh_autosuggest_start' \\ - zsh-users/zsh-autosuggestions \\ - blockf atpull' zi creinstall -q .' \\ - zsh-users/zsh-completions \\ - atinit'zstyle ":history-search-multi-word" page-size "7"' \\ - z-shell/H-S-MW -EOF -} - -set_themes() { - command cat <<-EOF >>"$ZSH_HOME_DIR/.zshrc" -# Popular themes ==================================================================================================== # -# https://wiki.zshell.dev/community/gallery/collection/themes - -EOF -} - -set_annexes() { - command cat <<-EOF >>"$ZSH_HOME_DIR/.zshrc" -# Meta-plugins & annexes =========================================================================================== # -# https://wiki.zshell.dev/ecosystem/category/-annexes -zi for \\ - z-shell/z-a-meta-plugins \\ - @annexes - -EOF -} - -set_zpmod() { - check_cmd make - - # Establish zpmod directory - if [ -z "$MOD_HOME" ]; then - MOD_HOME="${ZI_HOME}/zmodules/zpmod" - fi - - if [ ! -d "$MOD_HOME" ]; then - command mkdir -p "$MOD_HOME" - fi - - if [ ! -w "$MOD_HOME" ]; then - command chmod go-w "$MOD_HOME" - fi - - if [ -d "${MOD_HOME}/.git" ]; then - say_info "Updating ZPMOD at $MOD_HOME" - builtin cd "$MOD_HOME" && command git pull -q --ff-only origin main - else - say_info "Downloading ZPMOD to $MOD_HOME" - command git clone -q "$MOD_REPO" "$MOD_HOME" - fi - - say_info "Checkig version for zsh..." - ZSH_CURRENT=$(zsh --version /dev/null; then - say_err "Zsh version 5.8.1 and above required." - else - say_info "Zsh version ${ZSH_CURRENT} is compatible." - builtin cd "$MOD_HOME" || err "Failed to change directory to $MOD_HOME." - say_info "Building module ZPMOD, running: a make clean, then ./configure and then make." - say_info "The module source are located at: $MOD_HOME" - if test -f Makefile; then - if [ "$1" = "--clean" ]; then - say_info "Running: make distclean..." - make distclean - true - else - say_info "Running: make clean (pass --clean to invoke \`make distclean')..." - make clean - fi - fi - say_info "Configuring..." - if CPPFLAGS=-I/usr/local/include CFLAGS="-g -Wall -O3" LDFLAGS=-L/usr/local/lib ./configure --disable-gdbm --without-tcsetpgrp; then - say_info "Building..." - if make -s; then - command cat <<-EOF -▓▒░ Module has been built correctly. -▓▒░ To load the module, add following 2 lines to .zshrc, at top: -  module_path+=( "${MOD_HOME}/Src" ) -  zmodload zi/zpmod -▓▒░ See 'zpmod -h' for more information. -▓▒░ Run 'zpmod source-study' to see profile data, -▓▒░ Guaranteed, automatic compilation of any sourced script. -EOF - else - say_err "Module failed build. Please report error at: ${MOD_REPO}/issues" - fi - fi - fi -} - -interactive_zshrc() { - if [ "$ZOPT" != skip ] && [ "$AOPT" = interactive ]; then - say_info "Creating .zshrc interactively..." - if ask "Install Zi Loader?"; then - set_loader - else - set_installer - fi - - if ask "Install annexes?"; then - set_annexes - fi - - if ask "Add recommended Oh-My-Zsh library?"; then - set_omz_lib - fi - - if ask "Add recommended Oh-My-Zsh plugins?"; then - set_omz_plugins - fi - - if ask "Add recommended Oh-My-Zsh theme?"; then - set_omz_themes - fi - - if ask "Add recommended plugins?"; then - set_plugins - fi - - zsh -ilc "@zi-scheduler burst" - command cat <<-EOF -▓▒░ ■■■■■■■■■■■■■■■■ Successfully created .zshrc ■■■■■■■■■■■■ -EOF - return 0 - fi -} - -default_zshrc() { - if [ "$ZOPT" != skip ] && [ "$AOPT" = default ]; then - say_info "Creating .zshrc file..." - set_loader - set_annexes - set_omz_lib - set_omz_plugins - set_omz_themes - set_plugins - - zsh -ilc "@zi-scheduler burst" - command cat <<-EOF -▓▒░ ■■■■■■■■■■■■■■■■ Successfully created .zshrc ■■■■■■■■■■■■ -EOF - return 0 - fi -} - -MAIN() { - set_repository "$@" - default_zshrc - - command cat <<-EOF - -▓▒░ Wiki: https://wiki.zshell.dev -▓▒░ Issues: https://github.com/z-shell/zi/issues -▓▒░ Discussions: https://discussions.zshell.dev - -▓▒░ ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ - -EOF - exit $? -} - -while true; do - MAIN "${@}" -done diff --git a/lib/sh/install_zpmod.sh b/lib/sh/install_zpmod.sh index d694f88..c49b99c 100755 --- a/lib/sh/install_zpmod.sh +++ b/lib/sh/install_zpmod.sh @@ -1,34 +1,56 @@ #!/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 + +set -eu + +WORKDIR="$(mktemp -d)" || exit 1 +trap 'rm -rf "${WORKDIR:?}"' EXIT INT TERM + +# Returns 0 if version $1 >= version $2 (dot-separated integers) +_zi_ver_ge() { + _vga="$1" _vgb="$2" + while [ -n "${_vga}${_vgb}" ]; do + _af="${_vga%%.*}" _bf="${_vgb%%.*}" + case "${_vga}" in *.*) _vga="${_vga#*.}" ;; *) _vga="" ;; esac + case "${_vgb}" in *.*) _vgb="${_vgb#*.}" ;; *) _vgb="" ;; esac + _af="${_af:-0}" _bf="${_bf:-0}" + if [ "${_af}" -gt "${_bf}" ]; then return 0; fi + if [ "${_af}" -lt "${_bf}" ]; then return 1; fi + done + return 0 +} setup_environment() { - if [ -z "${ZI_HOME}" ]; then - if [ -d "${HOME}"/.zi ]; then + if [ -z "${ZI_HOME-}" ]; then + _xdg_zi="${XDG_DATA_HOME:-${HOME}/.local/share}/zi" + if [ -d "${_xdg_zi}" ]; then + ZI_HOME="${_xdg_zi}" + elif [ -d "${HOME}/.zi" ]; then ZI_HOME="${HOME}/.zi" - elif [ -d "${ZDOTDIR}"/.zi ]; then + elif [ -n "${ZDOTDIR-}" ] && [ -d "${ZDOTDIR}/.zi" ]; then ZI_HOME="${ZDOTDIR}/.zi" - elif [ -d "${XDG_DATA_HOME}"/.zi ]; then - ZI_HOME="${XDG_DATA_HOME}/.zi" else - ZI_HOME="${HOME}/.zi" + ZI_HOME="${_xdg_zi}" fi fi - if [ -z "${MOD_HOME}" ]; then + if [ -z "${MOD_HOME-}" ]; then MOD_HOME="${ZI_HOME}/zmodules/zpmod" fi if ! test -d "${MOD_HOME}"; then mkdir -p "${MOD_HOME}" - chmod g-rwX "${MOD_HOME}" + chmod go-w "${MOD_HOME}" fi if [ ! -d "${MOD_HOME}" ]; then printf '%s\n' "${col_error}== Error: Failed to setup module directory ==${col_rst}" - exit 255 + exit 1 fi } setup_zpmod_repository() { printf '%s\n' "${col_pname}== Downloading ZPMOD module to ${MOD_HOME}" if test -d "${MOD_HOME}/.git"; then - cd "${MOD_HOME}" || exit 255 + cd "${MOD_HOME}" || exit 1 git pull -q origin main else git clone --depth 10 -q https://github.com/z-shell/zpmod.git "${MOD_HOME}" @@ -39,18 +61,19 @@ build_zpmod_module() { if command -v zsh >/dev/null; then printf '%s\n' "${col_info2}-- Checking version --${col_rst}" ZSH_CURRENT=$(zsh --version /dev/null; then - printf '%s\n' "${col_error}-- Zsh version 5.8.1 and above required --${col_rst}" + ZSH_REQUIRED="5.8.1" + # shellcheck disable=SC2310 + if ! _zi_ver_ge "${ZSH_CURRENT}" "${ZSH_REQUIRED}"; then + printf '%s\n' "${col_error}-- Zsh version ${ZSH_REQUIRED} and above required --${col_rst}" exit 1 else ( printf '%s\n' "${col_info2}-- Zsh version ${ZSH_CURRENT} --${col_rst}" - cd "${MOD_HOME}" || exit 255 + cd "${MOD_HOME}" || exit 1 printf '%s\n' "${col_pname}== Building module ZPMOD, running: a make clean, then ./configure and then make ==${col_rst}" printf '%s\n' "${col_pname}== The module sources are located at: ${MOD_HOME} ==${col_rst}" if test -f Makefile; then - if [ "$1" = "--clean" ]; then + if [ "${1-}" = "--clean" ]; then printf '%s\n' "${col_info2}-- make distclean --${col_rst}" make distclean true @@ -62,9 +85,10 @@ build_zpmod_module() { INSTALL_PATH="/usr/local" export PATH="${INSTALL_PATH}"/bin:"${PATH}" - export LD_LIBRARY_PATH="${INSTALL_PATH}"/lib:"${LD_LIBRARY_PATH}" - export CFLAGS=-I"${INSTALL_PATH}"/include - export CPPFLAGS="-I${INSTALL_PATH}/include" LDFLAGS="-L${INSTALL_PATH}/lib" + export LD_LIBRARY_PATH="${INSTALL_PATH}/lib:${LD_LIBRARY_PATH-}" + export CFLAGS="-I${INSTALL_PATH}/include" + export CPPFLAGS="-I${INSTALL_PATH}/include" + export LDFLAGS="-L${INSTALL_PATH}/lib" CFLAGS="-g -Wall -O3" ./configure --disable-gdbm --without-tcsetpgrp --quiet printf '%s\n' "${col_info2}-- Running make --${col_rst}" @@ -84,13 +108,13 @@ EOF else printf '%s\n' "${col_error}Module didn't build.${col_rst}. You can copy the error messages and submit" printf '%s\n' "error-report at: https://github.com/z-shell/zpmod/issues" - exit 255 + exit 1 fi ) fi else printf '%s\n' "${col_error} Zsh is not installed. Please install zsh and try again.${col_rst}" - exit 255 + exit 1 fi } @@ -106,6 +130,4 @@ MAIN() { exit 0 } -while true; do - MAIN "${@}" -done +MAIN "${@}" diff --git a/lib/sh/sync-init.sh b/lib/sh/sync-init.sh new file mode 100755 index 0000000..5cc30ac --- /dev/null +++ b/lib/sh/sync-init.sh @@ -0,0 +1,252 @@ +#!/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 +# +# sync-init.sh — verify and optionally sync local lib/zsh/init.zsh with remote. +# +# Usage: +# sh lib/sh/sync-init.sh [OPTIONS] +# +# Options: +# --write Replace local file with remote copy (requires valid checksum) +# --local PATH Local file to compare (default: lib/zsh/init.zsh) +# --remote URL|PATH Remote URL or local path (default: GitHub raw main) +# --checksum-url URL|PATH Checksum.txt URL or path (default: GitHub raw main) +# --no-checksum Skip checksum validation of remote content +# --help Print this help and exit +# +# Exit codes: +# 0 Files match (or --write sync succeeded) +# 1 Files differ, fetch error, or checksum mismatch +# 2 Usage error + +set -eu + +WORKDIR="$(mktemp -d)" || exit 1 +trap 'rm -rf "${WORKDIR:?}"' EXIT INT TERM + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" + +DEFAULT_LOCAL="${REPO_ROOT}/lib/zsh/init.zsh" +DEFAULT_REMOTE="https://raw.githubusercontent.com/z-shell/zi-src/main/lib/zsh/init.zsh" +DEFAULT_CHECKSUM_URL="https://raw.githubusercontent.com/z-shell/zi-src/main/lib/checksum.txt" +CHECKSUM_KEY="lib/zsh/init.zsh" + +OPT_LOCAL="${DEFAULT_LOCAL}" +OPT_REMOTE="${DEFAULT_REMOTE}" +OPT_CHECKSUM_URL="${DEFAULT_CHECKSUM_URL}" +OPT_WRITE=0 +OPT_NO_CHECKSUM=0 + +# ── Helpers ─────────────────────────────────────────────────────────────────── + +print_help() { + cat </dev/null 2>&1; then + command curl -fsSL "${_src}" + elif command -v wget >/dev/null 2>&1; then + command wget -qO- "${_src}" + else + printf '%s\n' "[1;31m▓▒░[0m No curl or wget available." >&2 + return 1 + fi + ;; + *) + if [ -r "${_src}" ]; then + command cat "${_src}" + else + printf '%s\n' "[1;31m▓▒░[0m Cannot read local path: ${_src}" >&2 + return 1 + fi + ;; + esac +} + +# Compute SHA-256 hex digest of a file. +_sha256() { + _file="$1" + if command -v sha256sum >/dev/null 2>&1; then + sha256sum "${_file}" | command awk '{print $1}' + elif command -v shasum >/dev/null 2>&1; then + shasum -a 256 "${_file}" | command awk '{print $1}' + else + printf '%s\n' "[1;31m▓▒░[0m No sha256sum or shasum available." >&2 + return 1 + fi +} + +# ── Argument Parsing ────────────────────────────────────────────────────────── + +while [ "$#" -gt 0 ]; do + case "$1" in + --write) + OPT_WRITE=1 + shift + ;; + --local) + [ "$#" -ge 2 ] || { + printf '%s\n' "Error: --local requires an argument." >&2 + exit 2 + } + OPT_LOCAL="$2" + shift 2 + ;; + --remote) + [ "$#" -ge 2 ] || { + printf '%s\n' "Error: --remote requires an argument." >&2 + exit 2 + } + OPT_REMOTE="$2" + shift 2 + ;; + --checksum-url) + [ "$#" -ge 2 ] || { + printf '%s\n' "Error: --checksum-url requires an argument." >&2 + exit 2 + } + OPT_CHECKSUM_URL="$2" + shift 2 + ;; + --no-checksum) + OPT_NO_CHECKSUM=1 + shift + ;; + --help | -h) + print_help + exit 0 + ;; + *) + printf '%s\n' "Error: unknown option: $1" >&2 + exit 2 + ;; + esac +done + +# ── Validate Inputs ─────────────────────────────────────────────────────────── + +if [ ! -f "${OPT_LOCAL}" ] && [ "${OPT_WRITE}" -eq 0 ]; then + printf '%s\n' "[1;31m▓▒░[0m Local file not found: ${OPT_LOCAL}" >&2 + printf '%s\n' "[1;33m▓▒░[0m Use --write to create it from remote." >&2 + exit 1 +fi + +# ── Fetch Remote ────────────────────────────────────────────────────────────── + +REMOTE_FILE="${WORKDIR}/remote-init.zsh" +printf '%s\n' "▓▒░ Fetching remote: ${OPT_REMOTE}" +# shellcheck disable=SC2310 +if ! _fetch "${OPT_REMOTE}" >"${REMOTE_FILE}"; then + printf '%s\n' "[1;31m▓▒░[0m Failed to fetch remote file." >&2 + exit 1 +fi + +# ── Verify Remote Against Checksum ─────────────────────────────────────────── + +if [ "${OPT_NO_CHECKSUM}" -eq 0 ]; then + CHECKSUM_FILE="${WORKDIR}/checksum.txt" + printf '%s\n' "▓▒░ Fetching checksum: ${OPT_CHECKSUM_URL}" + # shellcheck disable=SC2310 + if ! _fetch "${OPT_CHECKSUM_URL}" >"${CHECKSUM_FILE}"; then + printf '%s\n' "[1;31m▓▒░[0m Failed to fetch checksum file." >&2 + exit 1 + fi + + EXPECTED_HASH="$(grep "${CHECKSUM_KEY}" "${CHECKSUM_FILE}" | command awk '{print $1}')" + if [ -z "${EXPECTED_HASH}" ]; then + printf '%s\n' "[1;31m▓▒░[0m No checksum entry for '${CHECKSUM_KEY}' in checksum.txt." >&2 + exit 1 + fi + + REMOTE_HASH="$(_sha256 "${REMOTE_FILE}")" + if [ "${REMOTE_HASH}" != "${EXPECTED_HASH}" ]; then + printf '%s\n' "[1;31m▓▒░[0m Remote checksum mismatch!" >&2 + printf '%s\n' " expected : ${EXPECTED_HASH}" >&2 + printf '%s\n' " got : ${REMOTE_HASH}" >&2 + exit 1 + fi + printf '%s\n' "▓▒░ Remote checksum verified: ${REMOTE_HASH}" +else + REMOTE_HASH="$(_sha256 "${REMOTE_FILE}")" + printf '%s\n' "▓▒░ Remote hash (checksum validation skipped): ${REMOTE_HASH}" +fi + +# ── Compare ─────────────────────────────────────────────────────────────────── + +if [ -f "${OPT_LOCAL}" ]; then + LOCAL_HASH="$(_sha256 "${OPT_LOCAL}")" +else + LOCAL_HASH="(file not present)" +fi + +if [ "${LOCAL_HASH}" = "${REMOTE_HASH}" ]; then + printf '%s\n' "[1;32m▓▒░[0m Local file matches remote. No sync needed." + printf '%s\n' " hash : ${LOCAL_HASH}" + printf '%s\n' " path : ${OPT_LOCAL}" + exit 0 +fi + +printf '%s\n' "[1;33m▓▒░[0m Local and remote differ." +printf '%s\n' " local : ${LOCAL_HASH}" +printf '%s\n' " remote : ${REMOTE_HASH}" +printf '%s\n' " source : ${OPT_REMOTE}" + +if command -v diff >/dev/null 2>&1 && [ -f "${OPT_LOCAL}" ]; then + printf '\n%s\n' "--- diff (local vs remote) ---" + diff -u "${OPT_LOCAL}" "${REMOTE_FILE}" || true + printf '%s\n' "--- end diff ---" +fi + +# ── Sync ────────────────────────────────────────────────────────────────────── + +if [ "${OPT_WRITE}" -eq 0 ]; then + printf '\n%s\n' "▓▒░ Run with --write to replace the local file." + exit 1 +fi + +LOCAL_DIR="$(dirname "${OPT_LOCAL}")" +TMP_TARGET="${LOCAL_DIR}/.sync-init.zsh.tmp.$$" + +command cp "${REMOTE_FILE}" "${TMP_TARGET}" + +if [ -f "${OPT_LOCAL}" ]; then + ORIG_MODE="$(command stat -c '%a' "${OPT_LOCAL}" 2>/dev/null || + command stat -f '%A' "${OPT_LOCAL}" 2>/dev/null || + printf '755')" + command chmod "${ORIG_MODE}" "${TMP_TARGET}" +else + command chmod 755 "${TMP_TARGET}" +fi + +command mv "${TMP_TARGET}" "${OPT_LOCAL}" + +NEW_HASH="$(_sha256 "${OPT_LOCAL}")" +printf '%s\n' "[1;32m▓▒░[0m Sync complete." +printf '%s\n' " before : ${LOCAL_HASH}" +printf '%s\n' " after : ${NEW_HASH}" +printf '%s\n' " path : ${OPT_LOCAL}" diff --git a/lib/zsh/init.zsh b/lib/zsh/init.zsh index 49b86a6..01add00 100755 --- a/lib/zsh/init.zsh +++ b/lib/zsh/init.zsh @@ -1,88 +1,136 @@ #!/usr/bin/env zsh -# ZI Loader (Values set: default) +# -*- mode: zsh; sh-indentation: 2; indent-tabs-mode: nil; sh-basic-offset: 2; -*- +# vim: ft=zsh sw=2 ts=2 et # -# https://z.digitalclouds.dev/community/zsh_plugin_standard -0="${ZERO:-${${0:#$ZSH_ARGZERO}:-${(%):-%N}}}" -0="${${(M)0:#/*}:-$PWD/$0}" - -# https://z.digitalclouds.dev/docs/guides/customization -local repo="https://github.com/z-shell/zi.git" -local branch="main" -local verbose_mode="${verbose_mode:-false}" -typeset -A ZI -# Where ZI should create all working directories, e.g.: "~/.zi" -ZI[HOME_DIR]="${ZI[HOME_DIR]:-${HOME}/.zi}" -# Where ZI code resides, e.g.: "~/.zi/bin" -ZI[BIN_DIR]="$ZI[HOME_DIR]/bin" -# Zsh modules directory -ZI[ZMODULES_DIR]="$ZI[HOME_DIR]/zmodules" -# Where ZI cache is, e.g.: "~/.cache/zi" -ZI[CACHE_DIR]="${ZI[CACHE_DIR]:-$HOME/.cache/zi}" -# Path to .zcompdump file, with the file included (i.e. its name can be different) -ZI[ZCOMPDUMP_PATH]="$ZI[CACHE_DIR]/.zcompdump" -# If set to 1, then mutes some of the ZI warnings, specifically the plugin already registered warning -ZI[MUTE_WARNINGS]="${ZI[MUTE_WARNINGS]:-0}" - -# Clone ZI repository if it doesn't exist -zzsetup() { - [[ $verbose_mode == true ]] && builtin print "(ZI): Checking if ZI (zi.zsh) is available." +# Zi Loader — bootstrap and source the Zi plugin manager. +# +# Sourced early in .zshrc. Defines zzinit() which clones Zi on first run, +# sources zi.zsh, registers completions, and loads zpmod if built. +# All helper functions are cleaned up after zzinit() returns. + +# ── Zi Configuration ────────────────────────────────────────────────────────── + +typeset -ghA ZI + +# https://wiki.zshell.dev/docs/guides/customization +: ${ZI[REPOSITORY]:="https://github.com/z-shell/zi.git"} +: ${ZI[STREAM]:="main"} +: ${ZI[HOME_DIR]:="${XDG_DATA_HOME:-$HOME/.local/share}/zi"} +: ${ZI[BIN_DIR]:="${ZI[HOME_DIR]}/bin"} +: ${ZI[CACHE_DIR]:="${XDG_CACHE_HOME:-$HOME/.cache}/zi"} +: ${ZI[CONFIG_DIR]:="${XDG_CONFIG_HOME:-$HOME/.config}/zi"} + +# https://wiki.zshell.dev/community/zsh_plugin_standard#global-parameter-with-prefix +: ${ZPFX:=${ZI[HOME_DIR]}/polaris} +: ${ZI[ZMODULES_DIR]:=${ZI[HOME_DIR]}/zmodules} +: ${ZI[ZCOMPDUMP_PATH]:=${ZI[CACHE_DIR]}/.zcompdump} +: ${ZI[MUTE_WARNINGS]:=0} + +# History defaults +: ${HISTFILE:=${XDG_STATE_HOME:-$HOME/.local/state}/zsh/history} +[[ -e "$HISTFILE" ]] || { command mkdir -p "${HISTFILE:h}"; command touch "$HISTFILE"; } +[[ -w "$HISTFILE" ]] && typeset -gx SAVEHIST=440000 HISTSIZE=441000 + +# ── Bootstrap Helpers ───────────────────────────────────────────────────────── + +# Fetch content from a URL to stdout. +_zi_fetch() { + builtin emulate -L zsh + if (( $+commands[curl] )); then + command curl -fsSL "$1" + elif (( $+commands[wget] )); then + command wget -qO- "$1" + else + return 255 + fi +} + +# Clone Zi repository if it doesn't exist. +_zi_setup() { + builtin emulate -L zsh + builtin autoload colors; colors + local -a git_refs + local tmp_dir show_process process_url + if [[ ! -f "${ZI[BIN_DIR]}/zi.zsh" ]]; then - [[ $verbose_mode == true ]] && builtin print "(ZI): ZI (zi.zsh) is not found. Installing..." - builtin print -P "%F{33}▓▒░ %F{160}Installing interactive feature-rich plugin manager (%F{33}z-shell/zi%F{160})%f%b" - command mkdir -p "${ZI[BIN_DIR]}" && command chmod -R go-w "${ZI[HOME_DIR]}" - command git clone -q --progress --branch "$branch" "$repo" "${ZI[BIN_DIR]}" + tmp_dir="${TMPDIR:-/tmp}/zi" + [[ -d "$tmp_dir" ]] || command mkdir -p "$tmp_dir" + + show_process="${tmp_dir}/git-process.zsh" + process_url="https://raw.githubusercontent.com/z-shell/zi/main/lib/zsh/git-process-output.zsh" + + if [[ ! -f "$show_process" ]]; then + if _zi_fetch "$process_url" > "${tmp_dir}/git-process.zsh"; then + command chmod a+x "${tmp_dir}/git-process.zsh" + else + return 1 + fi + fi + + (( $+commands[clear] )) && command clear + builtin print -P "%F{33}▓▒░ %F{160}Installing interactive & feature-rich plugin manager (%F{33}z-shell/zi%F{160})%f%b…\n" + command mkdir -p "${ZI[BIN_DIR]}" && \ + command git clone --verbose --progress --branch \ + "${ZI[STREAM]}" "${ZI[REPOSITORY]}" "${ZI[BIN_DIR]}" \ + |& { command "$show_process" || command cat; } + if [[ -f "${ZI[BIN_DIR]}/zi.zsh" ]]; then - [[ $verbose_mode == true ]] && builtin print "(ZI): Installed and ZI (zi.zsh) is found" - local git_refs=("$(cd "${ZI[BIN_DIR]}"; command git log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit | head -10)") - print -P "%F{33}▓▒░ %F{34}Successfully installed %F{160}(%F{33}z-shell/zi%F{160})%f%b" - print -P "%F{33}▓▒░ %F{226}Last changes:%f%b" - print -P "%F{33}▓▒░ %F{160}%F{33}\n${git_refs}%F{160}%f%b" + command chmod -R go-w "${ZI[HOME_DIR]}" + git_refs=("${(f@)$(builtin cd -q "${ZI[BIN_DIR]}" && command git log --color --graph --abbrev-commit \ + --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' | command head -5)}") + builtin print + builtin print -P "%F{33}▓▒░ %F{34}Successfully installed %F{160}(%F{33}z-shell/zi%F{160})%f%b\n" + builtin print -rl -- "${git_refs[@]}" else - print -P "%F{160}▓▒░ The clone has failed…%f%b" - print -P "%F{160}▓▒░ %F{33} Please report the issue:%f%b" - print -P "%F{160}▓▒░ %F{33} https://github.com/z-shell/zi/issues/new%f%b" + builtin print -P "%F{160}▓▒░ The clone has failed…%f%b" + builtin print -P "%F{160}▓▒░ %F{33} Please report the issue: %F{226}https://github.com/z-shell/zi/issues/new%f%b" return 1 fi - return 0 fi + return 0 } -# If setup is successful or ZI is already installed, then load ZI. Otherwise, not continue and exit. -zzsource() { - [[ $verbose_mode == true ]] && builtin print "(ZI): If (zzsetup) function status code 0, then load ZI." - if zzsetup; then - [[ $verbose_mode == true ]] && builtin print "(ZI): Loading (zi.zsh)" - source "${ZI[BIN_DIR]}/zi.zsh" +# Source zi.zsh, bootstrapping first if needed. +_zi_source() { + builtin emulate -L zsh + if [[ -f "${ZI[BIN_DIR]}/zi.zsh" ]]; then + builtin source "${ZI[BIN_DIR]}/zi.zsh" else - [[ $verbose_mode == true ]] && builtin print "(ZI): (zzsetup) function status code 1, not continue and exit." - exit 1 + _zi_setup || return 1 + # Guard: if setup succeeded but zi.zsh is still missing, don't recurse + if [[ -f "${ZI[BIN_DIR]}/zi.zsh" ]]; then + builtin source "${ZI[BIN_DIR]}/zi.zsh" + else + return 1 + fi fi } -# Load zi module if built -zzpmod() { - [[ $verbose_mode == true ]] && builtin print "(ZI): Checking for ZI module." +# Load zpmod module if built. +_zi_pmod() { + builtin emulate -L zsh if [[ -f "${ZI[ZMODULES_DIR]}/zpmod/Src/zi/zpmod.so" ]]; then - [[ $verbose_mode == true ]] && builtin print "(ZI): Loading ZI module." module_path+=( "${ZI[ZMODULES_DIR]}/zpmod/Src" ) - zmodload zi/zpmod &>/dev/null - ZI[ZPMOD_ENABLED]=1 + zmodload zi/zpmod 2>/dev/null fi + return 0 } -# Enable completion (completions should be loaded after zzsource) -zzcomps() { - [[ $verbose_mode == true ]] && builtin print "(ZI): Loading completion… (_zi)" - autoload -Uz _zi - (( ${+_comps} )) && _comps[zi]=_zi - ZI[COMPS_ENABLED]=1 +# Register Zi completion if the completion system is active. +_zi_comps() { + builtin emulate -L zsh + if (( ${+_comps} )) && [[ -f "${ZI[BIN_DIR]}/lib/_zi" ]]; then + (( ${+_comps[zi]} )) || _comps[zi]="${ZI[BIN_DIR]}/lib/_zi" + fi + return 0 } -# If ZI is installed, load ZI, enable completion and load zpmod. +# ── Entry Point ─────────────────────────────────────────────────────────────── + zzinit() { - (( ZI[SOURCED] )) && return - [[ $verbose_mode == true ]] && builtin print "(ZI): Loading ZI (zi.zsh)" - zzsource - zzcomps - zzpmod + { + _zi_source && _zi_comps && _zi_pmod + } always { + unset -f _zi_fetch _zi_setup _zi_source _zi_comps _zi_pmod zzinit 2>/dev/null + } } diff --git a/lib/zsh/snippets/color.zsh b/lib/zsh/snippets/color.zsh index ef67fef..e67dc04 100644 --- a/lib/zsh/snippets/color.zsh +++ b/lib/zsh/snippets/color.zsh @@ -1,8 +1,8 @@ +#!/usr/bin/env zsh + # Source: https://github.com/molovo/color # License: MIT — Copyright (c) 2015 Joe Letchford # Maintained by z-shell/src — https://github.com/z-shell/src -# -#!/usr/bin/env zsh function color() { local color=$1 style=$2 b=0 diff --git a/lib/zsh/snippets/revolver b/lib/zsh/snippets/revolver index 37b0a3d..b5789d6 100644 --- a/lib/zsh/snippets/revolver +++ b/lib/zsh/snippets/revolver @@ -1,8 +1,8 @@ #!/usr/bin/env zsh + # Source: https://github.com/molovo/revolver # License: MIT — Copyright (c) 2016 Joe Letchford # Maintained by z-shell/src — https://github.com/z-shell/src -# local -A _revolver_spinners _revolver_spinners=( @@ -79,7 +79,7 @@ function _revolver_usage() { echo " start Start the spinner" echo " update Update the message" echo " stop Stop the spinner" - echo " demo Display an demo of each style" + echo " demo Display a demo of each style" } ### @@ -88,6 +88,10 @@ function _revolver_usage() { function _revolver_process() { local dir statefile state msg pid="$1" spinner_index=0 + if [[ -z $pid || $pid != <-> ]]; then + exit 1 + fi + # Find the directory and load the statefile dir=${REVOLVER_DIR:-"${ZDOTDIR:-$HOME}/.revolver"} statefile="$dir/$pid" @@ -106,12 +110,9 @@ function _revolver_process() { exit 1 fi - # Check for the existence of the parent process - $(kill -s 0 $pid 2&>/dev/null) - - # If process doesn't exist, exit the script - # to prevent it from being orphaned - if [[ $? -ne 0 ]]; then + # Check for the existence of the parent process # and exit if process doesn't exist to prevent + # the spinner from being orphaned + if ! kill -s 0 "$pid" 2>/dev/null; then exit 1 fi diff --git a/scripts/add-project-workflow.sh b/scripts/add-project-workflow.sh new file mode 100755 index 0000000..3436536 --- /dev/null +++ b/scripts/add-project-workflow.sh @@ -0,0 +1,126 @@ +#!/usr/bin/env sh +# add-project-workflow.sh — Add project-tracker workflow to all org repos. +# +# Usage: +# ./add-project-workflow.sh [owner] [repos-file] +# +# Defaults: +# owner = z-shell +# repos-file = scripts/repos.txt +# +# Requirements: +# - gh CLI authenticated with repo scope +# - SSH key configured for git push +# - z-shell/.github already has add-to-project.yml deployed + +set -e + +OWNER="${1:-z-shell}" +REPOS="${2:-$(dirname "$0")/repos.txt}" +WORKFLOW_FILE=".github/workflows/project-tracker.yml" +BRANCH="ci/add-project-tracker" + +if [ ! -f "$REPOS" ]; then + printf 'Error: repos file not found: %s\n' "$REPOS" >&2 + exit 1 +fi + +# Skip repos that already have the workflow or are the .github meta-repo +SKIP_REPOS=".github" + +WORKFLOW_CONTENT='# Add issues and PRs to the Z-Shell Tracker project automatically. +name: Track in Z-Shell Tracker + +on: + issues: + types: [opened, reopened, transferred] + pull_request: + types: [opened, reopened] + +jobs: + track: + uses: z-shell/.github/.github/workflows/add-to-project.yml@main + secrets: inherit +' + +total=0 +ok=0 +skipped=0 +failed=0 + +add_workflow() { + repo="$1" + printf '=== %s/%s ===\n' "$OWNER" "$repo" + + # Skip the .github meta-repo itself + for skip in $SKIP_REPOS; do + if [ "$repo" = "$skip" ]; then + printf 'Skipping meta-repo\n' + skipped=$((skipped + 1)) + return 0 + fi + done + + tmpdir=$(mktemp -d) + trap 'rm -rf "$tmpdir"' EXIT INT TERM + + # Clone (shallow) — skip archived/empty repos gracefully + if ! git clone --depth=1 "git@github.com:$OWNER/$repo.git" "$tmpdir/$repo" 2>/dev/null; then + printf 'SKIP: cannot clone (archived or empty?)\n' + skipped=$((skipped + 1)) + trap - EXIT INT TERM + rm -rf "$tmpdir" + return 0 + fi + + cd "$tmpdir/$repo" + + # Skip if workflow already exists + if [ -f "$WORKFLOW_FILE" ]; then + printf 'SKIP: workflow already present\n' + skipped=$((skipped + 1)) + cd - >/dev/null + trap - EXIT INT TERM + rm -rf "$tmpdir" + return 0 + fi + + # Get default branch + default_branch=$(git symbolic-ref --short HEAD 2>/dev/null || echo "main") + + git checkout -b "$BRANCH" 2>/dev/null || git checkout "$BRANCH" + mkdir -p "$(dirname "$WORKFLOW_FILE")" + printf '%s' "$WORKFLOW_CONTENT" >"$WORKFLOW_FILE" + + git add "$WORKFLOW_FILE" + git commit -m "ci: add project tracker workflow" + git push origin "$BRANCH" + + # Open a PR + gh pr create \ + --repo "$OWNER/$repo" \ + --base "$default_branch" \ + --head "$BRANCH" \ + --title "ci: add project tracker workflow" \ + --body 'Adds the `.github/workflows/project-tracker.yml` workflow so new issues and PRs are automatically added to the [Z-Shell Tracker](https://github.com/orgs/z-shell/projects/28) project. + +Uses the shared reusable workflow from `z-shell/.github` — no per-repo secrets needed.' \ + 2>/dev/null && printf 'PR opened\n' || printf 'PR already open or failed\n' + + ok=$((ok + 1)) + cd - >/dev/null + trap - EXIT INT TERM + rm -rf "$tmpdir" +} + +while IFS= read -r repo || [ -n "$repo" ]; do + [ -z "$repo" ] && continue + total=$((total + 1)) + add_workflow "$repo" || { + failed=$((failed + 1)) + printf 'FAILED: %s/%s\n' "$OWNER" "$repo" >&2 + } +done <"$REPOS" + +printf '\nDone — %d total, %d ok, %d skipped, %d failed\n' \ + "$total" "$ok" "$skipped" "$failed" diff --git a/scripts/repos.txt b/scripts/repos.txt new file mode 100644 index 0000000..2aacb30 --- /dev/null +++ b/scripts/repos.txt @@ -0,0 +1,94 @@ +F-Sy-H +z-a-bin-gem-node +z-a-patch-dl +z-a-readurl +z-a-rust +z-a-submods +z-a-meta-plugins +zsh-navigation-tools +declare-zsh +zsh-unique-id +zsh-diff-so-fancy +zflai +null +zconvey +zui +z-a-man +zsh +z-a-unscope +z-a-default-ice +zi-crasis +zi-vim-syntax +zsh-lint +zsh-github-issues +zzcomplete +zbrowse +firefox-dev +any-node +any-gem +apr +doctoc +asciidoctor +ecs-cli +fzf +fzy +github-issues-srv +github-issues +ls_colors +pyenv +z-a-test +z-a-linkbin +z-a-eval +dircolors-material +zi-rbenv +ztrace +zsnapshot +zsh-select +zsh-morpho +zsh-log-lines +zservice-py3http +zsh-editing-workbench +pm-perf-test +system-completions +subversion +remark +community +zsh-tig-plugin +zsh-string-lib +zsh-util-lib +zsh-cmd-architect +zredis-cmd +zredis +zprompts +zi-console +playground +zgdbm +redis +VATS +zi +H-S-MW +src +zsh-bin +docs +zsdoc +vramsteg-zsh +zd +status +.github +z-shell.github.io +zpmod +zsh-eza +brew-completions +0 +wiki +zsh-zoxide +zsh-fancy-completions +web +zsh-web-artwork +merch +nb +.github-private +z-a-linkman +zsh-lsd +.trunk +zunit diff --git a/scripts/sync-labels.sh b/scripts/sync-labels.sh new file mode 100755 index 0000000..328ded5 --- /dev/null +++ b/scripts/sync-labels.sh @@ -0,0 +1,94 @@ +#!/usr/bin/env sh +# sync-labels.sh — Apply canonical Z-Shell label set to org repos. +# +# Usage: +# ./sync-labels.sh [owner] [repos-file] +# +# Defaults: +# owner = z-shell +# repos-file = scripts/repos.txt (one repo name per line) +# +# Requirements: +# - gh CLI authenticated with repo scope +# - gh label create supports --force (gh >= 2.14) + +set -e + +OWNER="${1:-z-shell}" +REPOS="${2:-$(dirname "$0")/repos.txt}" + +if [ ! -f "$REPOS" ]; then + printf 'Error: repos file not found: %s\n' "$REPOS" >&2 + exit 1 +fi + +apply_labels() { + repo="$1" + printf '=== %s/%s ===\n' "$OWNER" "$repo" + + # ── Type labels ────────────────────────────────────────────────────────────── + gh label create "bug 🐛" --repo "$OWNER/$repo" --color d73a4a \ + --description "Something isn't working" --force + gh label create "feature-request 💡" --repo "$OWNER/$repo" --color 0075ca \ + --description "New feature or request" --force + gh label create "enhancement ✨" --repo "$OWNER/$repo" --color a2eeef \ + --description "Improvement to existing functionality" --force + gh label create "documentation 📝" --repo "$OWNER/$repo" --color 0052cc \ + --description "Documentation-only changes" --force + gh label create "performance 🚀" --repo "$OWNER/$repo" --color 006b75 \ + --description "Performance improvements" --force + gh label create "security 🛡️" --repo "$OWNER/$repo" --color ee0701 \ + --description "Security vulnerability or hardening" --force + gh label create "breaking-change 💥" --repo "$OWNER/$repo" --color d93f0b \ + --description "Breaks backward compatibility" --force + gh label create "dependencies 📦" --repo "$OWNER/$repo" --color 0366d6 \ + --description "Dependency updates" --force + gh label create "chore 🔧" --repo "$OWNER/$repo" --color c5def5 \ + --description "Routine maintenance, no behavior change" --force + gh label create "refactor ♻️" --repo "$OWNER/$repo" --color 5319e7 \ + --description "Code refactor, no behavior change" --force + gh label create "ci ⚙️" --repo "$OWNER/$repo" --color 1d76db \ + --description "CI/CD pipeline changes" --force + + # ── Status / workflow labels ───────────────────────────────────────────────── + gh label create "triage 🔍" --repo "$OWNER/$repo" --color fbca04 \ + --description "Needs investigation before action" --force + gh label create "in-progress 🚧" --repo "$OWNER/$repo" --color f9d0c4 \ + --description "Currently being worked on" --force + gh label create "blocked ⛔" --repo "$OWNER/$repo" --color e4e669 \ + --description "Blocked on external dependency/decision" --force + gh label create "stale 💤" --repo "$OWNER/$repo" --color f2f2f2 \ + --description "No activity for an extended period" --force + gh label create "invalid ⚠️" --repo "$OWNER/$repo" --color e4e669 \ + --description "Off-topic, cannot reproduce, or incorrect" --force + gh label create "wontfix 🚫" --repo "$OWNER/$repo" --color ffffff \ + --description "Will not be fixed" --force + gh label create "no-stale 🔒" --repo "$OWNER/$repo" --color 0e8a16 \ + --description "Exempt from stale-bot closing" --force + + # ── Community labels ───────────────────────────────────────────────────────── + gh label create "help-wanted 🙋" --repo "$OWNER/$repo" --color 008672 \ + --description "Extra attention or expertise needed" --force + gh label create "good-first-issue 🌱" --repo "$OWNER/$repo" --color 7057ff \ + --description "Good for newcomers" --force + gh label create "Q&A ✍️" --repo "$OWNER/$repo" --color d4c5f9 \ + --description "Questions and answers" --force +} + +# ── Main loop ──────────────────────────────────────────────────────────────── +total=0 +ok=0 +failed=0 + +while IFS= read -r repo || [ -n "$repo" ]; do + [ -z "$repo" ] && continue + total=$((total + 1)) + if apply_labels "$repo"; then + ok=$((ok + 1)) + else + failed=$((failed + 1)) + printf 'FAILED: %s/%s\n' "$OWNER" "$repo" >&2 + fi +done <"$REPOS" + +printf '\nDone — %d total, %d ok, %d failed\n' "$total" "$ok" "$failed" diff --git a/tests/installers.sh b/tests/installers.sh new file mode 100755 index 0000000..5e58b13 --- /dev/null +++ b/tests/installers.sh @@ -0,0 +1,251 @@ +#!/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 + +set -eu + +ROOT="$( + unset CDPATH + cd -- "$(dirname "$0")/.." && pwd +)" +TMP_ROOT="$(mktemp -d)" || exit 1 +trap 'rm -rf "${TMP_ROOT:?}"' EXIT INT TERM + +fail() { + printf '%s\n' "not ok - $*" >&2 + exit 1 +} + +pass() { + printf '%s\n' "ok - $*" +} + +contains() { + file="$1" + pattern="$2" + if ! grep -F "${pattern}" "${file}" >/dev/null 2>&1; then + printf '%s\n' "--- ${file} ---" >&2 + if [ -f "${file}" ]; then + sed -n '1,120p' "${file}" >&2 + else + printf '%s\n' "(missing)" >&2 + fi + printf '%s\n' "--- end ${file} ---" >&2 + fail "${file} does not contain: ${pattern}" + fi +} + +sha256_file() { + file="$1" + if command -v sha256sum >/dev/null 2>&1; then + sha256sum "${file}" | awk '{print $1}' + elif command -v shasum >/dev/null 2>&1; then + shasum -a 256 "${file}" | awk '{print $1}' + else + fail "sha256sum or shasum is required" + fi +} + +check_syntax() { + sh -n "${ROOT}/lib/sh/install.sh" + sh -n "${ROOT}/lib/sh/install_zpmod.sh" + sh -n "${ROOT}/lib/sh/sync-init.sh" + command -v zsh >/dev/null 2>&1 || fail "zsh is required for init.zsh syntax checks" + zsh -n "${ROOT}/lib/zsh/init.zsh" + pass "script syntax" +} + +check_checksums() { + while read -r expected path; do + [ -n "${expected}" ] || continue + actual="$(sha256_file "${ROOT}/${path}")" + [ "${actual}" = "${expected}" ] || fail "checksum mismatch for ${path}" + done <"${ROOT}/lib/checksum.txt" + pass "checksums" +} + +write_fake_tools() { + FAKE_BIN="${TMP_ROOT}/bin" + command mkdir -p "${FAKE_BIN}" + + cat >"${FAKE_BIN}/curl" <<'EOF' +#!/usr/bin/env sh +set -eu + +out="" +remote_name=0 +url="" + +while [ "$#" -gt 0 ]; do + case "$1" in + -o) + shift + out="${1:-}" + [ -n "${out}" ] || exit 64 + shift + ;; + -*O*) + remote_name=1 + shift + ;; + -*) + shift + ;; + *) + url="$1" + shift + ;; + esac +done + +[ -n "${url}" ] || { printf '%s\n' "curl test double: missing URL" >&2; exit 64; } +if [ -z "${out}" ] && [ "${remote_name}" -eq 1 ]; then + out="${url##*/}" +fi + +case "${url}" in + */lib/zsh/init.zsh) + if [ -n "${out}" ]; then + cp "${ZI_SRC_TEST_ROOT}/lib/zsh/init.zsh" "${out}" + else + cat "${ZI_SRC_TEST_ROOT}/lib/zsh/init.zsh" + fi + ;; + */git-process-output.zsh) + [ -n "${out}" ] || out="git-process-output.zsh" + cat > "${out}" <<'SCRIPT' +#!/usr/bin/env sh +cat +SCRIPT + chmod a+x "${out}" + ;; + */lib/sh/install_zpmod.sh) + [ -n "${out}" ] || { printf '%s\n' "curl test double: missing output path" >&2; exit 64; } + cat > "${out}" <<'SCRIPT' +#!/usr/bin/env sh +set -eu +printf '%s\n' "zpmod fallback executed" > "${ZI_SRC_TEST_MARKER:?}" +SCRIPT + chmod a+x "${out}" + ;; + *) + printf '%s\n' "curl test double: unexpected URL ${url}" >&2 + exit 65 + ;; +esac +EOF + + cat >"${FAKE_BIN}/git" <<'EOF' +#!/usr/bin/env sh +set -eu + +cmd="${1:-}" +[ "$#" -gt 0 ] && shift + +case "${cmd}" in + clone) + dest="" + for arg do + dest="${arg}" + done + [ -n "${dest}" ] || { printf '%s\n' "git test double: missing clone destination" >&2; exit 64; } + mkdir -p "${dest}/.git" "${dest}/lib" + printf '%s\n' '# fake zi.zsh' > "${dest}/zi.zsh" + printf '%s\n' '# fake _zi completion' > "${dest}/lib/_zi" + ;; + clean | reset | pull) + ;; + log) + printf '%s\n' 'abcdef0 - fake zi commit (now) ' + ;; + *) + printf '%s\n' "git test double: unexpected command ${cmd}" >&2 + exit 65 + ;; +esac +EOF + + command chmod a+x "${FAKE_BIN}/curl" "${FAKE_BIN}/git" +} + +test_loader_install() { + home="${TMP_ROOT}/loader-home" + config="${TMP_ROOT}/loader-config" + data="${TMP_ROOT}/loader-data" + command mkdir -p "${home}" "${config}" "${data}" + + HOME="${home}" \ + ZDOTDIR="${home}" \ + XDG_CONFIG_HOME="${config}" \ + XDG_DATA_HOME="${data}" \ + ZI_SRC_TEST_ROOT="${ROOT}" \ + PATH="${FAKE_BIN}:${PATH}" \ + sh "${ROOT}/lib/sh/install.sh" -a loader -b feature/test >/dev/null + + # shellcheck disable=SC2016 + contains "${config}/zi/init.zsh" ': ${ZI[STREAM]:="feature/test"}' + # shellcheck disable=SC2016 + contains "${home}/.zshrc" 'source "${XDG_CONFIG_HOME:-${HOME}/.config}/zi/init.zsh" && zzinit' + [ -f "${data}/zi/bin/zi.zsh" ] || fail "loader install did not clone Zi into XDG data home" + pass "loader install uses XDG paths and branch override" +} + +test_standalone_zpmod_delegation() { + standalone_dir="${TMP_ROOT}/standalone" + home="${TMP_ROOT}/zpmod-home" + data="${TMP_ROOT}/zpmod-data" + marker="${TMP_ROOT}/zpmod-marker" + command mkdir -p "${standalone_dir}" "${home}" "${data}" + command cp "${ROOT}/lib/sh/install.sh" "${standalone_dir}/install.sh" + + HOME="${home}" \ + ZDOTDIR="${home}" \ + XDG_DATA_HOME="${data}" \ + ZI_SRC_TEST_ROOT="${ROOT}" \ + ZI_SRC_TEST_MARKER="${marker}" \ + PATH="${FAKE_BIN}:${PATH}" \ + sh "${standalone_dir}/install.sh" -a zpmod -i skip >/dev/null + + contains "${marker}" 'zpmod fallback executed' + pass "standalone install.sh fetches zpmod helper" +} + +test_sync_init() { + local_file="${TMP_ROOT}/local-init.zsh" + remote_file="${TMP_ROOT}/remote-init.zsh" + checksum_file="${TMP_ROOT}/checksum.txt" + + printf '%s\n' '# remote init fixture' >"${remote_file}" + command cp "${remote_file}" "${local_file}" + remote_hash="$(sha256_file "${remote_file}")" + printf '%s %s\n' "${remote_hash}" 'lib/zsh/init.zsh' >"${checksum_file}" + + sh "${ROOT}/lib/sh/sync-init.sh" \ + --local "${local_file}" \ + --remote "${remote_file}" \ + --checksum-url "${checksum_file}" >/dev/null + + printf '%s\n' '# stale init fixture' >"${local_file}" + if sh "${ROOT}/lib/sh/sync-init.sh" \ + --local "${local_file}" \ + --remote "${remote_file}" \ + --checksum-url "${checksum_file}" >/dev/null 2>&1; then + fail "sync-init mismatch check unexpectedly succeeded" + fi + + sh "${ROOT}/lib/sh/sync-init.sh" \ + --write \ + --local "${local_file}" \ + --remote "${remote_file}" \ + --checksum-url "${checksum_file}" >/dev/null + + cmp -s "${local_file}" "${remote_file}" || fail "sync-init --write did not replace local file" + pass "sync-init fixtures" +} + +check_syntax +check_checksums +write_fake_tools +test_loader_install +test_standalone_zpmod_delegation +test_sync_init