diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index ccc65901..6727f4c3 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -6,61 +6,26 @@ on: - master pull_request: +env: + PYTHON_MIN_VERSION: "3.7.13" + jobs: - shellcheck: + asdf: runs-on: ubuntu-latest steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Install asdf dependencies - uses: asdf-vm/actions/install@v2 - - - name: Run ShellCheck - run: scripts/shellcheck.bash - - shellfmt: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Install Fish (for fish_indent) - run: | - sudo add-apt-repository -y ppa:fish-shell/nightly-master - sudo apt-get update - sudo apt-get -y install fish - - - name: Install asdf dependencies - uses: asdf-vm/actions/install@v2 - - - name: List file to shfmt - run: shfmt -f . - - - name: Run shfmt - run: scripts/shfmt.bash - - checkstyle-py: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Install Python - uses: actions/setup-python@v4 + - uses: actions/checkout@v3 + - uses: asdf-vm/actions/install@v2 + - uses: actions/setup-python@v4 with: - python-version: "3.7.13" + python-version: ${{ env.PYTHON_MIN_VERSION }} + - run: scripts/install_dependencies.bash + - run: scripts/lint.bash --check - - name: Run checkstyle.py - run: scripts/checkstyle.py - - actionlint: + actions: runs-on: ubuntu-latest steps: - - name: Checkout code - uses: actions/checkout@v3 - + - uses: actions/checkout@v3 - name: Check workflow files - uses: docker://rhysd/actionlint:1.6.23 + uses: docker://rhysd/actionlint:1.6.24 with: args: -color diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d45730b6..ec5931d5 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -6,12 +6,6 @@ on: - master pull_request: -env: - ELVISH_VERSION: v0.19.2 - FISH_VERSION: 3.6.1 - NUSHELL_VERSION: 0.78.0 - POWERSHELL_VERSION: 7.3.3 - jobs: detect-changes: runs-on: ubuntu-latest @@ -22,11 +16,9 @@ jobs: documentation: ${{ steps.filter.outputs.documentation }} cli: ${{ steps.filter.outputs.cli }} steps: - - name: Checkout code - uses: actions/checkout@v3 + - uses: actions/checkout@v3 with: fetch-depth: 0 - - uses: dorny/paths-filter@v2 id: filter with: @@ -34,7 +26,6 @@ jobs: documentation: - '.github/workflows/**' - 'docs/**' - - '.tool-versions' cli: - '.github/workflows/**' - 'bin/**' @@ -42,10 +33,9 @@ jobs: - 'scripts/**' - 'test/**' - '.tool-versions' - - 'asdf.elv' - - 'asdf.fish' - - 'asdf.nu' - - 'asdf.sh' + - 'asdf.*' + - 'defaults' + - 'help.txt' ubuntu: needs: detect-changes @@ -54,46 +44,11 @@ jobs: if: ${{ needs.detect-changes.outputs.cli == 'true' }} runs-on: ubuntu-latest steps: - - name: Checkout code - uses: actions/checkout@v3 + - uses: actions/checkout@v3 with: fetch-depth: 0 - - - name: Install test dependencies - run: | - curl -fsSLo- https://packages.microsoft.com/keys/microsoft.asc | sudo tee >/dev/null /etc/apt/trusted.gpg.d/microsoft.asc - sudo sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-debian-bullseye-prod bullseye main" > /etc/apt/sources.list.d/microsoft.list' - sudo add-apt-repository -y ppa:fish-shell/release-3 - sudo apt-get update - sudo apt-get -y install curl parallel \ - fish="${{ env.FISH_VERSION }}-1~jammy" \ - powershell="${{ env.POWERSHELL_VERSION }}-1.deb" - - # Create $HOME/bin - mkdir -p "$HOME/bin" - - # Download elvish binary and add to path - curl https://dl.elv.sh/linux-amd64/elvish-${{ env.ELVISH_VERSION }}.tar.gz -o elvish-${{ env.ELVISH_VERSION }}.tar.gz - tar xzf elvish-${{ env.ELVISH_VERSION }}.tar.gz - rm elvish-${{ env.ELVISH_VERSION }}.tar.gz - mv elvish-${{ env.ELVISH_VERSION }} "$HOME/bin/elvish" - - # Download nushell binary and add to path - curl -L https://github.com/nushell/nushell/releases/download/${{ env.NUSHELL_VERSION }}/nu-${{ env.NUSHELL_VERSION }}-x86_64-unknown-linux-gnu.tar.gz -o nu-${{ env.NUSHELL_VERSION }}-x86_64-unknown-linux-gnu.tar.gz - tar xzf nu-${{ env.NUSHELL_VERSION }}-x86_64-unknown-linux-gnu.tar.gz - rm nu-${{ env.NUSHELL_VERSION }}-x86_64-unknown-linux-gnu.tar.gz - mv nu-${{ env.NUSHELL_VERSION }}-x86_64-unknown-linux-gnu/* "$HOME/bin" - - # Add $HOME/bin to path - echo "$HOME/bin" >>"$GITHUB_PATH" - - - name: Install bats - run: | - git clone --depth 1 --branch "v$(grep -Eo "^\\s*bats\\s*.*$" ".tool-versions" | cut -d ' ' -f2-)" https://github.com/bats-core/bats-core.git "$HOME/bats-core" - echo "$HOME/bats-core/bin" >>"$GITHUB_PATH" - - - name: Run tests - run: scripts/test.bash + - run: scripts/install_dependencies.bash + - run: scripts/test.bash env: GITHUB_API_TOKEN: ${{ github.token }} @@ -104,26 +59,11 @@ jobs: if: ${{ needs.detect-changes.outputs.cli == 'true' }} runs-on: macos-latest steps: - - name: Checkout code - uses: actions/checkout@v3 + - uses: actions/checkout@v3 with: fetch-depth: 0 - - - name: Install test dependencies - run: | - brew install coreutils parallel \ - elvish \ - fish \ - nushell \ - powershell - - - name: Install bats - run: | - git clone --depth 1 --branch "v$(grep -Eo "^\\s*bats\\s*.*$" ".tool-versions" | cut -d ' ' -f2-)" https://github.com/bats-core/bats-core.git "$HOME/bats-core" - echo "$HOME/bats-core/bin" >>"$GITHUB_PATH" - - - name: Run tests - run: scripts/test.bash + - run: scripts/install_dependencies.bash + - run: scripts/test.bash env: GITHUB_API_TOKEN: ${{ github.token }} @@ -141,13 +81,11 @@ jobs: fetch-depth: 0 # only run steps past here if changes to docs/** directory - - name: Setup Node.js - uses: actions/setup-node@v3 + - uses: actions/setup-node@v3 with: node-version: "18" - - name: Cache dependencies - uses: actions/cache@v3 + - uses: actions/cache@v3 id: npm-cache with: path: | diff --git a/docs/contribute/core.md b/docs/contribute/core.md index 76201e2d..046f195f 100644 --- a/docs/contribute/core.md +++ b/docs/contribute/core.md @@ -40,14 +40,15 @@ If you want to try out your changes without making change to your installed `asd It is best to format, lint and test your code locally before you commit or push to the remote. Use the following scripts/commands: ```shell:no-line-numbers -# Shellcheck -./scripts/shellcheck.bash +# Lint +./scripts/lint.bash --check -# Format -./scripts/shfmt.bash +# Fix & Format +./scripts/lint.bash --fix # Test: all tests -bats test/ +./scripts/test.bash + # Test: for specific command bats test/list_commands.bash ``` diff --git a/scripts/format.bash b/scripts/format.bash deleted file mode 100755 index f8d4385b..00000000 --- a/scripts/format.bash +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -# check .bash files -shfmt --language-dialect bash --indent 2 --write \ - completions/*.bash \ - bin/asdf \ - bin/private/asdf-exec \ - lib/utils.bash \ - lib/commands/*.bash \ - lib/functions/*.bash \ - scripts/*.bash \ - test/test_helpers.bash \ - test/fixtures/dummy_broken_plugin/bin/* \ - test/fixtures/dummy_legacy_plugin/bin/* \ - test/fixtures/dummy_plugin/bin/* - -# check .bats files -shfmt --language-dialect bats --indent 2 --write \ - test/*.bats - -# check .fish files -fish_indent --write ./**/*.fish diff --git a/scripts/install_dependencies.bash b/scripts/install_dependencies.bash new file mode 100755 index 00000000..f8c6ecf5 --- /dev/null +++ b/scripts/install_dependencies.bash @@ -0,0 +1,77 @@ +#!/usr/bin/env bash + +set -euo pipefail +IFS=$'\n\t' + +### Used env vars set by default in GitHub Actions +# docs: https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables +# GITHUB_ACTIONS +# RUNNER_OS + +if [ -z "$GITHUB_ACTIONS" ]; then + printf "%s\n" "GITHUB_ACTIONS is not set. This script is only intended to be run in GitHub Actions. Exiting." + exit 1 +fi + +if [ -z "$RUNNER_OS" ]; then + printf "%s\n" "RUNNER_OS is not set. This script is only intended to be run in GitHub Actions. Exiting." + exit 1 +fi + +### Set environment variables for tracking versions +# Elvish +elvish_semver="v0.19.2" +# Fish +fish_semver="3.6.1" +fish_apt_semver="${fish_semver}-1~jammy" +# Nushell +nushell_semver="0.78.0" +# Powershell +powershell_semver="7.3.3" +powershell_apt_semver="${powershell_semver}-1.deb" + +### Install dependencies on Linux +if [ "$RUNNER_OS" = "Linux" ]; then + printf "%s\n" "Installing dependencies on Linux" + + curl -fsSLo- https://packages.microsoft.com/keys/microsoft.asc | sudo tee /etc/apt/trusted.gpg.d/microsoft.asc >/dev/null + sudo sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-debian-bullseye-prod bullseye main" > /etc/apt/sources.list.d/microsoft.list' + sudo add-apt-repository -y ppa:fish-shell/release-3 + sudo apt-get update + sudo apt-get -y install curl parallel \ + fish="${fish_apt_semver}" \ + powershell="${powershell_apt_semver}" + + # Create $HOME/bin + mkdir -p "$HOME/bin" + + # Download elvish binary and add to path + curl https://dl.elv.sh/linux-amd64/elvish-${elvish_semver}.tar.gz -o elvish-${elvish_semver}.tar.gz + tar xzf elvish-${elvish_semver}.tar.gz + rm elvish-${elvish_semver}.tar.gz + mv elvish-${elvish_semver} "$HOME/bin/elvish" + + # Download nushell binary and add to path + curl -L https://github.com/nushell/nushell/releases/download/${nushell_semver}/nu-${nushell_semver}-x86_64-unknown-linux-gnu.tar.gz -o nu-${nushell_semver}-x86_64-unknown-linux-gnu.tar.gz + tar xzf nu-${nushell_semver}-x86_64-unknown-linux-gnu.tar.gz + rm nu-${nushell_semver}-x86_64-unknown-linux-gnu.tar.gz + mv nu-${nushell_semver}-x86_64-unknown-linux-gnu/* "$HOME/bin" + + # Add $HOME/bin to path (add Elvish & Nushell to path) + echo "$HOME/bin" >>"$GITHUB_PATH" +fi + +### Install dependencies on macOS +if [ "$RUNNER_OS" = "macOS" ]; then + printf "%s\n" "Installing dependencies on macOS" + brew install coreutils parallel \ + elvish \ + fish \ + nushell \ + powershell +fi + +### Install bats-core +printf "%s\n" "Installing bats-core" +git clone --depth 1 --branch "v$(grep -Eo "^\\s*bats\\s*.*$" ".tool-versions" | cut -d ' ' -f2-)" https://github.com/bats-core/bats-core.git "$HOME/bats-core" +echo "$HOME/bats-core/bin" >>"$GITHUB_PATH" diff --git a/scripts/lint.bash b/scripts/lint.bash new file mode 100755 index 00000000..392d36b6 --- /dev/null +++ b/scripts/lint.bash @@ -0,0 +1,180 @@ +#!/usr/bin/env bash + +set -euo pipefail +IFS=$'\n\t' + +usage() { + printf "%s\n" "Lint script for the asdf codebase. Must be executed from the" + printf "%s\n\n" "repository root directory." + printf "%s\n\n" "Usage: scripts/lint.bash [options]" + printf "%s\n" "Options:" + printf "%s\n" " -c, --check Error if any issues are found" + printf "%s\n" " -f, --fix Automatically fix issues if possible" + printf "%s\n" " -h, --help Display this help message" +} + +run_shfmt_stylecheck() { + local shfmt_flag="" + if [ "$1" = "fix" ]; then + shfmt_flag="--write" + else + shfmt_flag="--diff" + fi + + printf "%s\n" "[INFO] Checking .bash with shfmt" + shfmt --language-dialect bash --indent 2 "${shfmt_flag}" \ + completions/*.bash \ + bin/asdf \ + bin/private/asdf-exec \ + lib/utils.bash \ + lib/commands/*.bash \ + lib/functions/*.bash \ + scripts/*.bash \ + test/test_helpers.bash \ + test/fixtures/dummy_broken_plugin/bin/* \ + test/fixtures/dummy_legacy_plugin/bin/* \ + test/fixtures/dummy_plugin/bin/* + + printf "%s\n" "[INFO] Checking .bats with shfmt" + shfmt --language-dialect bats --indent 2 "${shfmt_flag}" \ + test/*.bats +} + +run_shellcheck_linter() { + printf "%s\n" "[INFO] Checking .sh files with Shellcheck" + shellcheck --shell sh --external-sources \ + asdf.sh + + printf "%s\n" "[INFO] Checking .bash files with Shellcheck" + shellcheck --shell bash --external-sources \ + completions/*.bash \ + bin/asdf \ + bin/private/asdf-exec \ + lib/utils.bash \ + lib/commands/*.bash \ + lib/functions/*.bash \ + scripts/*.bash \ + test/test_helpers.bash \ + test/fixtures/dummy_broken_plugin/bin/* \ + test/fixtures/dummy_legacy_plugin/bin/* \ + test/fixtures/dummy_plugin/bin/* + + printf "%s\n" "[INFO] Checking .bats files with Shellcheck" + shellcheck --shell bats --external-source \ + test/*.bats +} + +run_custom_python_stylecheck() { + local github_actions=${GITHUB_ACTIONS:-} + local flag= + + if [ "$1" = "fix" ]; then + flag="--fix" + fi + + if [ -n "$github_actions" ] && ! command -v python3 &>/dev/null; then + # fail if CI and no python3 + printf "%s\n" "[ERROR] Detected execution in GitHub Actions but python3 was not found. This is required during CI linting." + exit 1 + fi + + if ! command -v python3 &>/dev/null; then + # skip if local and no python3 + printf "%s\n" "[WARNING] python3 not found. Skipping Custom Python Script." + else + printf "%s\n" "[INFO] Checking files with Custom Python Script." + "${0%/*}/checkstyle.py" "${flag}" + fi + +} + +# TODO: there is no elvish linter/formatter yet +# see https://github.com/elves/elvish/issues/1651 +#run_elvish_linter() { +# printf "%s\n" "[WARNING] elvish linter/formatter not found, skipping for now." +#} + +run_fish_linter() { + local github_actions=${GITHUB_ACTIONS:-} + local flag= + + if [ "$1" = "fix" ]; then + flag="--write" + else + flag="--check" + fi + + if [ -n "$github_actions" ] && ! command -v fish_indent &>/dev/null; then + # fail if CI and no fish_ident + printf "%s\n" "[ERROR] Detected execution in GitHub Actions but fish_indent was not found. This is required during CI linting." + exit 1 + fi + + if ! command -v fish_indent &>/dev/null; then + # skip if local and no fish_ident + printf "%s\n" "[WARNING] fish_indent not found. Skipping .fish files." + else + printf "%s\n" "[INFO] Checking .fish files with fish_indent" + fish_indent "${flag}" ./**/*.fish + fi +} + +# TODO: there is no nushell linter/formatter yet +#run_nushell_linter() { +# printf "%s\n" "[WARNING] nushell linter/formatter not found, skipping for now." +#} + +# TODO: select powershell linter/formatter & setup installation in CI +#run_powershell_linter() { +# printf "%s\n" "[WARNING] powershell linter/formatter not found, skipping for now." +#} + +repo_root=$(git rev-parse --show-toplevel) +if [ "$repo_root" != "$PWD" ]; then + printf "%s\n" "[ERROR] This scripts requires execution from the repository root directory." + printf "\t%s\t%s\n" "Repo root dir:" "$repo_root" + printf "\t%s\t%s\n\n" "Current dir:" "$PWD" + printf "%s\n" "See usage details with -h or --help." + exit 1 +fi + +if [ $# -eq 0 ]; then + printf "%s\n" "[ERROR] At least one option required." + printf "=%.0s" {1..60} + printf "\n" + usage + exit 1 +fi + +mode= +case "$1" in +-h | --help) + usage + exit 0 + ;; +-c | --check) + mode="check" + ;; +-f | --fix) + mode="fix" + ;; +*) + printf "%s\n" "[ERROR] Invalid flag: $1" + printf "=%.0s" {1..60} + printf "\n" + usage + exit 1 + ;; +esac + +printf "%s\"%s\"\n" "[INFO] Executing with mode: " "$mode" + +run_shfmt_stylecheck "$mode" +run_custom_python_stylecheck "$mode" +run_shellcheck_linter "$mode" +run_fish_linter "$mode" +#run_elvish_linter "$mode" +#run_nushell_linter "$mode" +#run_powershell_linter "$mode" + +printf "%s\n" "[INFO] Success!" diff --git a/scripts/shellcheck.bash b/scripts/shellcheck.bash deleted file mode 100755 index 7b56f745..00000000 --- a/scripts/shellcheck.bash +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -# check .sh files -shellcheck --shell sh --external-sources \ - asdf.sh - -# check .bash files -shellcheck --shell bash --external-sources \ - completions/*.bash \ - bin/asdf \ - bin/private/asdf-exec \ - lib/utils.bash \ - lib/commands/*.bash \ - lib/functions/*.bash \ - scripts/*.bash \ - test/test_helpers.bash \ - test/fixtures/dummy_broken_plugin/bin/* \ - test/fixtures/dummy_legacy_plugin/bin/* \ - test/fixtures/dummy_plugin/bin/* - -shellcheck --shell bats --external-source \ - test/*.bats diff --git a/scripts/shfmt.bash b/scripts/shfmt.bash deleted file mode 100755 index d42ae205..00000000 --- a/scripts/shfmt.bash +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -# check .bash files -shfmt --language-dialect bash --indent 2 --diff \ - completions/*.bash \ - bin/asdf \ - bin/private/asdf-exec \ - lib/utils.bash \ - lib/commands/*.bash \ - lib/functions/*.bash \ - scripts/*.bash \ - test/test_helpers.bash \ - test/fixtures/dummy_broken_plugin/bin/* \ - test/fixtures/dummy_legacy_plugin/bin/* \ - test/fixtures/dummy_plugin/bin/* - -# check .bats files -shfmt --language-dialect bats --indent 2 --diff \ - test/*.bats - -# check .fish files -fish_indent --check ./**/*.fish diff --git a/scripts/test.bash b/scripts/test.bash index 5e361fdd..13148590 100755 --- a/scripts/test.bash +++ b/scripts/test.bash @@ -1,6 +1,15 @@ #!/usr/bin/env bash set -euo pipefail +IFS=$'\n\t' + +repo_root=$(git rev-parse --show-toplevel) +if [ "$repo_root" != "$PWD" ]; then + printf "%s\n" "[ERROR] This scripts requires execution from the repository root directory." + printf "\t%s\t%s\n" "Repo root dir:" "$repo_root" + printf "\t%s\t%s\n\n" "Current dir:" "$PWD" + exit 1 +fi test_directory="test" bats_options=(--timing --print-output-on-failure)