[
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\nindent_style = space\nindent_size = 2\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = true\ninsert_final_newline = true\n\n[*.go]\nindent_style = tab\n\n[*.py]\nindent_size = 4\n\n[*.fish]\nindent_size = 4\n\n[*.md]\ntrim_trailing_whitespace = false\n"
  },
  {
    "path": ".git-blame-ignore-revs",
    "content": "# Run shfmt on bash files\nb8dc5f160447baabab9c69683824512ded577254\n\n# fix: Remove == inside [ (#1421)\nd81b81f9de2dc5961624464df04cef7cafae588c\n\n# chore: Fix ShellCheck errors in tests\n407a6696c0739f5e368543c4fc1b14e41458f177\n998180e3625643644603504bea75e3bcc668465f\n6f64aa8d847d53f272d30bcf2532937c02826080\nb5e981cf1d9a37af17e48f6a871ff692720df810\n28b348a041b3dfd1898eb0073aa1e83bdae70e4a\n720fd172004c9f29045eb2ad19f35eba47ae31c7\nffa018763c10de63e6548372e7eaad2ae53643d6\n90ead5ea0ae06418c58a47f4c7732797af5fe336\n"
  },
  {
    "path": ".gitattributes",
    "content": "## GITATTRIBUTES\n#\n# Details per file setting:\n#   text    These files should be normalized (i.e. convert CRLF to LF).\n#   binary  These files are binary and should be left untouched.\n#\n# Note that binary is a macro for -text -diff.\n######################################################################\n\n## AUTO-DETECT\n##   Handle line endings automatically for files detected as\n##   text and leave all files detected as binary untouched.\n##   This will handle all files NOT defined below.\n* text=auto\n\n## SOURCE CODE\n*.bats     text\n*.css      text\n*.htm      text\n*.html     text\n*.js       text\n*.sh       text\n*.bash     text\n*.fish     text\n*.elv      text\n#### asdf/bin/* explicitly define as text\n*asdf      text\n*asdf-exec text\n*_asdf     text\n#### asdf/test/fixtures/* should be covered by * text=auto on L14\n\n## DOCKER\n*.dockerignore    text\nDockerfile        text\n\n## DOCUMENTATION\n*.markdown   text\n*.md         text\n*.mdwn       text\n*.mdown      text\n*.mkd        text\n*.mkdn       text\n*.mdtxt      text\n*.mdtext     text\n*.txt        text\nAUTHORS      text\nCHANGELOG    text\nCHANGES      text\nCONTRIBUTING text\nCOPYING      text\ncopyright    text\n*COPYRIGHT*  text\nINSTALL      text\nlicense      text\nLICENSE      text\nNEWS         text\nreadme       text\n*README*     text\nTODO         text\n\n## CONFIGS\n.editorconfig  text\n.gitattributes text\n.gitconfig     text\n*.yaml         text\n*.yml          text\n"
  },
  {
    "path": ".github/CODEOWNERS",
    "content": "# Default owners for all the code in this repo\n* @asdf-vm/core\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yaml",
    "content": "name: Bug Report\ndescription: Create a report to help us improve\ntitle: \"bug: \"\nlabels: [bug]\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        Thanks for taking the time to fill out this bug report!\n  - type: textarea\n    id: description\n    attributes:\n      label: Describe the Bug\n      description: A clear and concise description of what the bug is.\n    validations:\n      required: true\n  - type: textarea\n    id: reproduction\n    attributes:\n      label: Steps to Reproduce\n      description: Tell us what actions you performed before the issue occurred\n      placeholder: |\n        1. Go to '...'\n        2. Click on '....'\n        3. Scroll down to '....'\n        4. See error\n    validations:\n      required: true\n  - type: textarea\n    id: expected\n    attributes:\n      label: Expected Behaviour\n      description: Tell us what should have happened?\n    validations:\n      required: true\n  - type: textarea\n    id: actual\n    attributes:\n      label: Actual Behaviour\n      description: Tell us what happened instead\n    validations:\n      required: true\n  - type: textarea\n    id: environment\n    attributes:\n      label: Environment\n      description: Copy the output of `asdf info` here\n      render: shell\n    validations:\n      required: true\n  - type: textarea\n    id: plugins\n    attributes:\n      label: asdf plugins affected (if relevant)\n      description: The plugins you list here should appear in the list from `asdf info`\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "# Force users to use one of our predefined issue templates\nblank_issues_enabled: false\ncontact_links:\n  - name: Question (StackOverflow)\n    url: https://stackoverflow.com/questions/tagged/asdf-vm\n    about: Ask new or browse existing questions on StackOverflow.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/documentation.yaml",
    "content": "name: Documentation\ndescription: Suggest a documentation improvements for this project\nlabels: \"documentation\"\nbody: \n  - type: markdown\n    attributes:\n      value: |\n        Thanks for taking the time to help improve our docs!\n  - type: textarea\n    id: problem\n    attributes:\n      label: How can we improve the documentation?\n      description: Please provide a clear and concise description of the problem. The more information you can provide here, the better.\n      placeholder: The documentation can be improved by...\n    validations:\n      required: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.yaml",
    "content": "name: Feature Request\ndescription: Suggest an idea for this project\nlabels: \"enhancement\"\nbody: \n  - type: markdown\n    attributes:\n      value: |\n        Thanks for taking the time to request this feature!\n  - type: textarea\n    id: problem\n    attributes:\n      label: Is your feature request related to a problem? Please describe\n      description: Please provide a clear and concise description the problem this feature would solve. The more information you can provide here, the better.\n      placeholder: I'm always frustrated when...\n    validations:\n      required: true\n  - type: textarea\n    id: solution\n    attributes:\n      label: Describe the proposed solution\n      description: Please provide a clear and concise description of what you would like to happen.\n      placeholder: I would like to see...\n    validations:\n      required: true\n  - type: textarea\n    id: alternatives\n    attributes:\n      label: \"Describe similar `asdf` features and why they are not sufficient\"\n      description: \"Describe asdf features you tried to use to accomplish what you wanted. Explain why they are insufficient for the task you were trying to accomplish.\"\n    validations:\n      required: true\n  - type: textarea\n    id: workarounds\n    attributes:\n      label: \"Describe other workarounds you've considered\"\n      description: \"A clear and concise description of any alternative solutions or features you've considered.\"\n    validations:\n      required: true\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "# Summary\n\n<!-- Provide a general description of the code changes in your pull request. -->\n\nFixes: List issue numbers here\n\n## Other Information\n\n<!-- If there is anything else that is relevant to your pull request include that information here. Thank you for contributing to asdf! -->\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  # Maintain dependencies for GitHub Actions\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n    reviewers:\n      - \"jthegedus\"\n      - \"stratus3d\"\n  # Maintain dependencies for npm used in Documentation site\n  - package-ecosystem: \"npm\"\n    directory: \"/docs\"\n    schedule:\n      interval: \"monthly\"\n    reviewers:\n      - \"jthegedus\"\n    groups:\n      docs:\n        patterns:\n          - \"*\"\n"
  },
  {
    "path": ".github/workflows/documentation.yml",
    "content": "name: Documentation\n\non:\n  # trigger deployment on push to master branch when changes to docs/**\n  push:\n    paths:\n      - \"docs/**\"\n    branches:\n      - master\n  # trigger deployment manually\n  workflow_dispatch:\n\njobs:\n  deploy:\n    runs-on: ubuntu-latest\n    defaults:\n      run:\n        working-directory: docs/\n\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          # fetch all commits to get last updated time or other git log info\n          fetch-depth: 0\n\n      - name: Setup Node.js\n        uses: actions/setup-node@v6\n        with:\n          node-version: \"20\"\n\n      - name: Cache dependencies\n        uses: actions/cache@v5\n        id: npm-cache\n        with:\n          path: |\n            **/node_modules\n          key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}\n          restore-keys: |\n            ${{ runner.os }}-npm-\n\n      - name: Install dependencies\n        if: steps.npm-cache.outputs.cache-hit != 'true'\n        run: npm install\n\n      - name: Build VitePress site\n        run: npm run build\n\n      - name: Bundle CNAME with site dist\n        run: cp CNAME .vitepress/dist\n\n      - name: Deploy to GitHub Pages\n        uses: crazy-max/ghaction-github-pages@v5\n        with:\n          # deploy to gh-pages branch\n          target_branch: gh-pages\n          # deploy the default output dir of VitePress\n          build_dir: docs/.vitepress/dist\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/lint.yml",
    "content": "name: Lint\n\non:\n  push:\n    branches:\n      - master\n  pull_request:\n\nenv:\n  PYTHON_MIN_VERSION: \"3.13.1\"\n\njobs:\n  asdf-bash:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n      - uses: asdf-vm/actions/install@v3\n      - uses: actions/setup-python@v6\n        with:\n          python-version: ${{ env.PYTHON_MIN_VERSION }}\n      - run: scripts/install_dependencies.bash\n      - run: scripts/lint.bash --check\n\n  asdf-golang:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n      - name: Setup Go\n        uses: actions/setup-go@v6\n        with:\n          go-version: '1.24.9'\n      - name: Install dependencies\n        run: go get ./...\n      - name: Run 'gofumpt'\n        run: go run mvdan.cc/gofumpt -l -w .\n      - name: Check format\n        run: '[ -z \"$(gofmt -l ./...)\" ]'\n      - name: Run 'revive'\n        run: go run github.com/mgechev/revive -set_exit_status ./...\n      - name: Vet\n        run: go vet ./...\n      - name: Lint\n        run: go run honnef.co/go/tools/cmd/staticcheck -tests -show-ignored ./...\n      - name: Build\n        run: go build -v ./...\n\n  actions:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n      - name: Check workflow files\n        uses: docker://rhysd/actionlint:1.6.24\n        with:\n          args: -color\n"
  },
  {
    "path": ".github/workflows/release-build.yml",
    "content": "name: Build Binaries for Release\n\non:\n  release:\n    types: [published]\n  workflow_dispatch:\n    inputs:\n      tag:\n        description: 'Tag to build binaries for'\n        required: true\n        type: string\n\npermissions:\n  contents: write\n  packages: write\n\njobs:\n  build:\n    name: Build release binaries\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        # windows isn't working on windows right now, add it to this list once\n        # I fix the code.\n        goos: [linux, darwin]\n        goarch: [\"386\", amd64, arm64]\n        exclude:\n          - goarch: \"386\"\n            goos: darwin\n          #- goarch: arm64\n          #  goos: windows\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n      - name: Build Go binaries\n        uses: wangyoucao577/go-release-action@v1\n        with:\n          pre_command: \"export CGO_ENABLED=0\"\n          github_token: ${{ secrets.GITHUB_TOKEN }}\n          goos: ${{ matrix.goos }}\n          goarch: ${{ matrix.goarch }}\n          goversion: \"1.24.9\"\n          binary_name: \"asdf\"\n          project_path: ./cmd/asdf\n          release_tag: ${{ github.event.release.tag_name || inputs.tag }}\n          release_name: ${{ github.event.release.tag_name || inputs.tag }}\n          ldflags: -s -X main.version=${{ github.event.release.tag_name || inputs.tag }}\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Release\n\non:\n  push:\n    branches:\n      - master\n\npermissions:\n  contents: write\n  pull-requests: write\n\njobs:\n  release:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: googleapis/release-please-action@v4\n        name: create release\n        with:\n          token: ${{ secrets.ASDF_WORKFLOW_TOKEN }}\n          config-file: release-please-config.json\n          manifest-file: .release-please-manifest.json\n"
  },
  {
    "path": ".github/workflows/semantic-pr.yml",
    "content": "name: Lint\n\non:\n  pull_request_target:\n    types:\n      - opened\n      - edited\n      - synchronize\n\njobs:\n  semantic-pr:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: amannn/action-semantic-pull-request@v6.1.1\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          scopes: |\n            # The scope for all the Golang rewrite commits\n            golang-rewrite\n            # A list of all used scopes can be computed by running this command:\n            #\n            # git log --pretty=format:%s | rg '^[^: ]*\\(([^):]*)\\).*' -r '$1' | sort | uniq\n            #\n            # We only want to allow a limited set of scopes going forward, so\n            # the list of valid scopes has been pared down here.\n            docs\n            website\n            plugin\n            completions\n            deps\n"
  },
  {
    "path": ".github/workflows/tests.yml",
    "content": "name: Test\n\non:\n  push:\n    branches:\n      - master\n  pull_request:\n\njobs:\n  detect-changes:\n    runs-on: ubuntu-latest\n    # Set job outputs to values from filter step\n    outputs:\n      documentation: ${{ steps.filter.outputs.documentation }}\n      cli: ${{ steps.filter.outputs.cli }}\n      go: ${{ steps.filter.outputs.go }}\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n      - uses: dorny/paths-filter@v3\n        id: filter\n        with:\n          filters: |\n            documentation:\n              - '.github/workflows/**'\n              - 'docs/**'\n            cli:\n              - '.github/workflows/**'\n              - 'bin/**'\n              - 'lib/**'\n              - 'scripts/**'\n              - 'test/**'\n              - '.tool-versions'\n              - 'asdf.*'\n              - 'defaults'\n              - 'help.txt'\n            go:\n              - '**.go'\n              - 'go.mod'\n              - 'go.sum'\n\n  test-golang:\n    needs: detect-changes\n    if: ${{ needs.detect-changes.outputs.go == 'true' || needs.detect-changes.outputs.cli == 'true' }}\n    strategy:\n      matrix:\n        os: [ubuntu-latest, macos-latest]\n    runs-on: ${{ matrix.os }}\n    steps:\n      - uses: actions/checkout@v6\n      - name: Setup Go\n        uses: actions/setup-go@v6\n        with:\n          go-version: '1.24.9'\n      - run: scripts/install_dependencies.bash\n      - name: Install dependencies\n        run: go get ./...\n      - name: Run Go tests\n        run: go test -coverprofile=/tmp/coverage.out  -bench= -race ./...\n\n  # Because I changed the test helper code Bash tests now fail. I removed them\n  # from here to get passing checks. They can be added back at a later time if\n  # I fix the test helper.\n\n  documentation-site:\n    needs: detect-changes\n    # only run if\n    # - changes to documentation\n    # - pull_request (workflows/docs.yml deploys on main branch)\n    if: ${{ github.event_name == 'pull_request' && needs.detect-changes.outputs.documentation == 'true' }}\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          # fetch all commits to get git log info for Vuepress\n          fetch-depth: 0\n\n      # only run steps past here if changes to docs/** directory\n      - uses: actions/setup-node@v6\n        with:\n          node-version: \"18\"\n\n      - uses: actions/cache@v5\n        id: npm-cache\n        with:\n          path: |\n            docs/node_modules\n          key: ${{ runner.os }}-npm-${{ hashFiles('docs/package-lock.json') }}\n          restore-keys: |\n            ${{ runner.os }}-npm-\n\n      - name: Install dependencies\n        if: steps.npm-cache.outputs.cache-hit != 'true'\n        working-directory: docs/\n        run: npm install\n\n      - name: Check errors by building Documentation site\n        working-directory: docs/\n        run: npm run build\n"
  },
  {
    "path": ".gitignore",
    "content": "/installs\n/downloads\n/shims\nrepository\n.vagrant\nkeyrings\n/tmp\n\ndist/\n\n# ignore build binary\nasdf\n"
  },
  {
    "path": ".release-please-manifest.json",
    "content": "{\n  \".\": \"0.18.1\"\n}\n"
  },
  {
    "path": ".tool-versions",
    "content": "golang 1.24.9\nbats 1.8.2\nshellcheck 0.10.0\nshfmt 3.6.0\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\n## [0.18.1](https://github.com/asdf-vm/asdf/compare/v0.18.0...v0.18.1) (2026-03-04)\n\n\n### Bug Fixes\n\n* Only show already installed versions in shell completion for the asdf set command ([#2172](https://github.com/asdf-vm/asdf/pull/2172)) ([c3910d7](https://github.com/asdf-vm/asdf/commit/c3910d73776980e649605205352334c09c8c85b1))\n* set LANG=C for locale-dependent tests ([#2197](https://github.com/asdf-vm/asdf/issues/2197)) ([2423f6d](https://github.com/asdf-vm/asdf/commit/2423f6da496b5b1955ce9cb19d524dc0a7b88e26))\n\n## [0.18.0](https://github.com/asdf-vm/asdf/compare/v0.17.0...v0.18.0) (2025-06-07)\n\n\n### Features\n\n* `asdf list` exit with status code of 0 when no versions installed ([#2116](https://github.com/asdf-vm/asdf/issues/2116)) ([e7d5289](https://github.com/asdf-vm/asdf/commit/e7d5289c57894ebbc0f966cb91794efd970377af))\n\n\n### Bug Fixes\n\n* correct flag handling in commands run by `asdf exec` ([#2115](https://github.com/asdf-vm/asdf/issues/2115)) ([d6cd693](https://github.com/asdf-vm/asdf/commit/d6cd6930cff8e7159cb2f1a57b23bd0ec1faa6ac))\n* only return version starting with number when no filter is supplied ([#2120](https://github.com/asdf-vm/asdf/issues/2120)) ([cf29b51](https://github.com/asdf-vm/asdf/commit/cf29b5136bbe481ae3803dbdb78086c808eeef7a))\n* print all error output to stderr when shim can't be resolved ([#2109](https://github.com/asdf-vm/asdf/issues/2109)) ([c9049ea](https://github.com/asdf-vm/asdf/commit/c9049ea2fd09fc7958fb1a5a5b44e0670740465b))\n* rename tool version filename environment variable for clarity ([#2101](https://github.com/asdf-vm/asdf/issues/2101)) ([e3d6014](https://github.com/asdf-vm/asdf/commit/e3d6014419296281c4156fc65a3e02bb542495a2))\n* upgrade urfave/cli to version 3 ([#2105](https://github.com/asdf-vm/asdf/issues/2105)) ([392d09a](https://github.com/asdf-vm/asdf/commit/392d09a8b263a5ef18fd05f27312717bf9baa292))\n\n## [0.17.0](https://github.com/asdf-vm/asdf/compare/v0.16.7...v0.17.0) (2025-05-19)\n\n\n### Features\n\n* **golang-rewrite:** add support for shim templates resolution ([#2076](https://github.com/asdf-vm/asdf/issues/2076)) ([a3bccea](https://github.com/asdf-vm/asdf/commit/a3bccea5c9b64bf81675efaa5c76d6eb367fd37f))\n* log failure to add plugin in \"plugin test\" ([#2059](https://github.com/asdf-vm/asdf/issues/2059)) ([92de803](https://github.com/asdf-vm/asdf/commit/92de803ff15f1a887f031d570ae6404f008d829d))\n* switch back to native git client ([#1998](https://github.com/asdf-vm/asdf/issues/1998)) ([1efa2bb](https://github.com/asdf-vm/asdf/commit/1efa2bbd04b833d0435a15bddd882beb973cfc2d))\n\n\n### Bug Fixes\n\n* address linter warning ([67581cf](https://github.com/asdf-vm/asdf/commit/67581cf030d4eb39f261acac5e861444fedad7f6))\n* correct intersection logic in `shims.FindExecutable` function so ordering of multiple versions is preserved ([#2063](https://github.com/asdf-vm/asdf/issues/2063)) ([083f20a](https://github.com/asdf-vm/asdf/commit/083f20aa3e21cad594b35972ca570eb47e389899))\n* correct output of install command when system or path version set ([#2097](https://github.com/asdf-vm/asdf/issues/2097)) ([82d67e3](https://github.com/asdf-vm/asdf/commit/82d67e3242b0ac4d01cccd9712daaae574ce6eca))\n* ensures output always ends with a newline when printed ([#2098](https://github.com/asdf-vm/asdf/issues/2098)) ([6f4837e](https://github.com/asdf-vm/asdf/commit/6f4837ea9b82b442fdfa78c3bb315b088e60dc9c))\n* handle tilde in env vars ([#2092](https://github.com/asdf-vm/asdf/issues/2092)) ([6da599a](https://github.com/asdf-vm/asdf/commit/6da599a93ad2655c2bf061c038da330ee4413985))\n* remove default error action from plugin command ([#2027](https://github.com/asdf-vm/asdf/issues/2027)) ([c376481](https://github.com/asdf-vm/asdf/commit/c376481cb4d1fa1e67dd9ef326381c07c935151d))\n* remove unused ForcePrepend option from Go code ([#2089](https://github.com/asdf-vm/asdf/issues/2089)) ([49e9f33](https://github.com/asdf-vm/asdf/commit/49e9f330a719de6bd599b84c90b93e7d7358043c))\n* set correct version for go install and make builds ([#2077](https://github.com/asdf-vm/asdf/issues/2077)) ([4c73527](https://github.com/asdf-vm/asdf/commit/4c73527d6323ca41d8ea9a9e78a8db49f3794d16))\n\n## [0.16.7](https://github.com/asdf-vm/asdf/compare/v0.16.6...v0.16.7) (2025-03-25)\n\n\n### Bug Fixes\n\n* remove comment from first line zsh completion output ([#2035](https://github.com/asdf-vm/asdf/issues/2035)) ([#2037](https://github.com/asdf-vm/asdf/issues/2037)) ([74d7b17](https://github.com/asdf-vm/asdf/commit/74d7b17a1cc9f640cf0f5134416d1cf7a56fe19f))\n\n## [0.16.6](https://github.com/asdf-vm/asdf/compare/v0.16.5...v0.16.6) (2025-03-21)\n\n\n### Bug Fixes\n\n* correct ASDF_INSTALL_* envvar unset test ([#2006](https://github.com/asdf-vm/asdf/issues/2006)) ([6fbf94a](https://github.com/asdf-vm/asdf/commit/6fbf94a75b8e045eea53038182e376b21a6947e4))\n* correct concurrency to align with documentation ([#2014](https://github.com/asdf-vm/asdf/issues/2014)) ([807ea38](https://github.com/asdf-vm/asdf/commit/807ea3883139da48300e72931680431aa35e593d))\n* correct handling of `ASDF_FORCE_PREPEND` environment variable ([#2011](https://github.com/asdf-vm/asdf/issues/2011)) ([43a84a0](https://github.com/asdf-vm/asdf/commit/43a84a024faeacb04044c9e2cf20ccbe87ea4263))\n* improve zsh completion suggestions ([#2022](https://github.com/asdf-vm/asdf/issues/2022)) ([b1cf58d](https://github.com/asdf-vm/asdf/commit/b1cf58d2bd01c1c2c3662cca6bd8927d68a37258))\n* remove filtering from latest-stable call ([#2032](https://github.com/asdf-vm/asdf/issues/2032)) ([6fcdcdf](https://github.com/asdf-vm/asdf/commit/6fcdcdf6df693fec6d643fab54e2d520bd5b539b))\n* remove install directory for version when install fails ([#2024](https://github.com/asdf-vm/asdf/issues/2024)) ([932ac46](https://github.com/asdf-vm/asdf/commit/932ac468b7c24c2adef90a293a1f7280a0074cc4))\n\n## [0.16.5](https://github.com/asdf-vm/asdf/compare/v0.16.4...v0.16.5) (2025-03-04)\n\n\n### Bug Fixes\n\n* always propagate env variables when executing commands ([#1982](https://github.com/asdf-vm/asdf/issues/1982)) ([80265a8](https://github.com/asdf-vm/asdf/commit/80265a8eecedc623cb8cf5cca18ae563e9d4f94c))\n* build static binary to improve portability ([#1993](https://github.com/asdf-vm/asdf/issues/1993)) ([45047a6](https://github.com/asdf-vm/asdf/commit/45047a6c451599e718f996fdadbdcea3ecf683fd))\n* correct exit status when sub-command does not exist ([#1991](https://github.com/asdf-vm/asdf/issues/1991)) ([3dd0dd3](https://github.com/asdf-vm/asdf/commit/3dd0dd3b475d1c4ddcb6d76248a988be5cceef51)), closes [#1928](https://github.com/asdf-vm/asdf/issues/1928)\n* latest version returns latest version ([#1996](https://github.com/asdf-vm/asdf/issues/1996)) ([0ceac7a](https://github.com/asdf-vm/asdf/commit/0ceac7af8c126980901caba4d8daa80900819451))\n* preserve files untracked by Git on plugin update ([#1995](https://github.com/asdf-vm/asdf/issues/1995)) ([d4d8db0](https://github.com/asdf-vm/asdf/commit/d4d8db035d9f349bfed513af6976734db18e2c14))\n* set correct env vars on recursive calls ([#1989](https://github.com/asdf-vm/asdf/issues/1989)) ([97a91cc](https://github.com/asdf-vm/asdf/commit/97a91cc8d01bda0896a50dff50a162e87fd61e57))\n* simplify env vars parsing ([#1988](https://github.com/asdf-vm/asdf/issues/1988)) ([8990b6b](https://github.com/asdf-vm/asdf/commit/8990b6b4ae3c9754f3764289f0d7cf410815d29d)), closes [#1986](https://github.com/asdf-vm/asdf/issues/1986)\n\n## [0.16.4](https://github.com/asdf-vm/asdf/compare/v0.16.3...v0.16.4) (2025-02-19)\n\n\n### Bug Fixes\n\n* Add a newline delimiter when suggesting versions to install ([#1972](https://github.com/asdf-vm/asdf/issues/1972)) ([38bea71](https://github.com/asdf-vm/asdf/commit/38bea7145495a53c1a6fbad0542a32a4e7937e91))\n* correct version resolution order to restore legacy file fallback behavior ([#1956](https://github.com/asdf-vm/asdf/issues/1956)) ([6696d47](https://github.com/asdf-vm/asdf/commit/6696d4702937442842a3643fab31d21a7fd0208f))\n* support environment variables with equals sign and newlines in value ([#1977](https://github.com/asdf-vm/asdf/issues/1977)) ([1acf082](https://github.com/asdf-vm/asdf/commit/1acf0824ccfd33f118cb7440970df9e43899a1c1))\n\n## [0.16.3](https://github.com/asdf-vm/asdf/compare/v0.16.2...v0.16.3) (2025-02-17)\n\n\n### Bug Fixes\n\n* add missing version command ([#1931](https://github.com/asdf-vm/asdf/issues/1931)) ([5339c41](https://github.com/asdf-vm/asdf/commit/5339c413d2fd77e971ed9b7621f0454b96fe3a0d))\n* correct formatting of version in \"already installed\" error message ([df5e283](https://github.com/asdf-vm/asdf/commit/df5e283fb74a63faecd9ab234af1f0c24f1afdcd))\n* correct typo in `Upgrading to 0.16.0` documentation ([#1938](https://github.com/asdf-vm/asdf/issues/1938)) ([7e8e5f6](https://github.com/asdf-vm/asdf/commit/7e8e5f60d13b0672e65982e21d7dc864246be8eb))\n* don't error if version already installed ([06f8990](https://github.com/asdf-vm/asdf/commit/06f89907b2002db0e53b9bb2acd8ad11935f051c))\n* pass environment variables through to `exec-env` callback ([9e6b559](https://github.com/asdf-vm/asdf/commit/9e6b5594080acd4208427505d9018123f1fb1f36))\n* repair invalid fish shell completion code ([#1936](https://github.com/asdf-vm/asdf/issues/1936)) ([8388f99](https://github.com/asdf-vm/asdf/commit/8388f992e9be7f21313d8c8e363b43e82e44f207))\n* return no error from shims.RemoveAll when shims dir missing ([#1967](https://github.com/asdf-vm/asdf/issues/1967)) ([45c31c9](https://github.com/asdf-vm/asdf/commit/45c31c9761f62a45896f6e45707a60fd0c1f4111))\n\n## [0.16.2](https://github.com/asdf-vm/asdf/compare/v0.16.1...v0.16.2) (2025-02-08)\n\n\n### Bug Fixes\n\n* correct Bash completion ([#1886](https://github.com/asdf-vm/asdf/issues/1886)) ([fdb1bc7](https://github.com/asdf-vm/asdf/commit/fdb1bc793a06263a0eac8818c14498e23906108a))\n* improve completions for Zsh and Fish ([#1912](https://github.com/asdf-vm/asdf/issues/1912)) ([2f806de](https://github.com/asdf-vm/asdf/commit/2f806de830655d9146d25663d74e3fceedcc300f))\n* correct help for asdf set command ([#1920](https://github.com/asdf-vm/asdf/issues/1920)) ([554b4ea](https://github.com/asdf-vm/asdf/commit/554b4eaf351c08fed2261715308320838f0c5afb))\n* correct help for asdf set command in Bash completion ([#1921](https://github.com/asdf-vm/asdf/issues/1921)) ([63e7dca](https://github.com/asdf-vm/asdf/commit/63e7dcaeae46d6d43ae63db9bf635e227a6ba944))\n* improve Zsh completion suggestions ([#1925](https://github.com/asdf-vm/asdf/issues/1925)) ([e190624](https://github.com/asdf-vm/asdf/commit/e190624fa82fb2caf4d56521232de4e873b63118))\n* run go tests when go.mod or go.sum change ([#1917](https://github.com/asdf-vm/asdf/issues/1917)) ([6e4a7b5](https://github.com/asdf-vm/asdf/commit/6e4a7b5ad3d6abdbb98faa383bc41643b35ef6bf))\n* fix typo ([#1897](https://github.com/asdf-vm/asdf/issues/1897)) ([98ffa86](https://github.com/asdf-vm/asdf/commit/98ffa861e9b64a2e029a0ed26205bfcc81838180))\n* update make utils script to set correct version ([892736b](https://github.com/asdf-vm/asdf/commit/892736bf76ac37487cfca75d2cfcc57d5cdec913))\n* update urfave/cli to fix cmd output ([#1914](https://github.com/asdf-vm/asdf/issues/1914)) ([3525e9e](https://github.com/asdf-vm/asdf/commit/3525e9ed4edb05f15a15f00378f5336ef29aa2f4))\n\n## [0.16.1](https://github.com/asdf-vm/asdf/compare/v0.16.0...v0.16.1) (2025-02-05)\n\n\n### Bug Fixes\n\n* correct SliceToMap environment variable parsing function ([#1879](https://github.com/asdf-vm/asdf/issues/1879)) ([e63aec6](https://github.com/asdf-vm/asdf/commit/e63aec61020a907fe5960d74b6d3dbf229214ae0))\n* remove old hyphenated command from help ([1f0296a](https://github.com/asdf-vm/asdf/commit/1f0296a3d11a9df0d300ca61f59d097750ab6f1c))\n* replace reference to removed subcommands ([#1868](https://github.com/asdf-vm/asdf/issues/1868)) ([0785d35](https://github.com/asdf-vm/asdf/commit/0785d35263b2bd5ad570ee6706e1b38d8dd05422))\n* revert change to old Bash help text ([de019fd](https://github.com/asdf-vm/asdf/commit/de019fde849d2f3a258b3f40e5b2004d1973c804))\n* use version in home dir when no version found in root dir ([#1883](https://github.com/asdf-vm/asdf/issues/1883)) ([5ae5f76](https://github.com/asdf-vm/asdf/commit/5ae5f769f1042add6219e98633063e76a03e12b9))\n\n## [0.16.0](https://github.com/asdf-vm/asdf/compare/v0.15.0...v0.16.0) (2025-01-30)\n\n\n### Features\n\n* **Rewrite asdf in Golang** The rewrite in Go was spread across 88 pull requests that are all included in this release. The primary goal of the rewrite was to create a codebase that was faster, simpler and easier to maintain. The rewrite tries to maintain feature parity with the previous version. However, a number of breaking changes were introduced. Some of these were due to the change of language, a few out of a desire to simplify the code, and some to improve the user experience. For the full list of breaking changes and the upgrade guide visit the [Upgrading to 0.16.0 page](https://asdf-vm.com/guide/upgrading-to-v0-16.html#breaking-changes) on the asdf website. **It is highly recommended that you read this guide before upgrading**.\n\nA warning has also been added to the Bash code for asdf in 0.16.0. anyone trying to use asdf as they did in version 0.15.0 and earlier will get a warning message instructing them to follow the upgrade guide.\n\nThe full list of pull requests and merge commits for this rewrite are listed below.\n\n   ([3a9f539](https://github.com/asdf-vm/asdf/commit/3a9f539aa092fa7c043661232f8caa154e861c6f)) ([f41ce90](https://github.com/asdf-vm/asdf/commit/f41ce90dc4410da7bb76c5ccf73147759716be07)) ([#1833](https://github.com/asdf-vm/asdf/issues/1833)) ([4f9a5d3](https://github.com/asdf-vm/asdf/commit/4f9a5d3d314bc6a8abd5e14d9c38472055033fa7)) ([d06d71f](https://github.com/asdf-vm/asdf/commit/d06d71f9f6b2fd6069c6fbf27bc7b13b8b0ec5c2)) ([7d5281a](https://github.com/asdf-vm/asdf/commit/7d5281a8a9151d57113ea0dd5d2fea2b07f55228)) ([8ad3472](https://github.com/asdf-vm/asdf/commit/8ad3472abc64d8a07589a16096142b52aeae18af)) ([b40beb6](https://github.com/asdf-vm/asdf/commit/b40beb6039b031d350bd1c621f34d90f23f72765)) ([b23e5a3](https://github.com/asdf-vm/asdf/commit/b23e5a320fd231c4fa55baa3b32d90b55a6ff4f1)) ([bc05110](https://github.com/asdf-vm/asdf/commit/bc0511015920acd8421bbccaead86e2badf0c2ae)) ([477e9d5](https://github.com/asdf-vm/asdf/commit/477e9d57293b8c99bcb184cfc72e064f4b68eda0)) ([6d708b2](https://github.com/asdf-vm/asdf/commit/6d708b280720d2144ae7976229fd5630eeb31eaf)) ([572ed07](https://github.com/asdf-vm/asdf/commit/572ed07f2bdb31f04a7244d1d127594ef9922db0)) ([19a0597](https://github.com/asdf-vm/asdf/commit/19a0597502ebecb0dfe2946fe19d9f9da3d57a18)) ([b33ab64](https://github.com/asdf-vm/asdf/commit/b33ab6463c7825a0d03f82f430cfad18a1e941c8)) ([b966ca6](https://github.com/asdf-vm/asdf/commit/b966ca66271f1be81abee751dec929f97cadfab4)) ([8db188a](https://github.com/asdf-vm/asdf/commit/8db188a702b1a64812fbb6e5832ec74ed47dfb34)) ([3fd4a83](https://github.com/asdf-vm/asdf/commit/3fd4a839757a2646054f2d86f487731e0715eeaa)) ([09d06ff](https://github.com/asdf-vm/asdf/commit/09d06ff125107c19a24043e52cf60b378b8b4c3b)) ([d2afb85](https://github.com/asdf-vm/asdf/commit/d2afb85eb80bce85e79c9c0d91ad3103a7f985f0)) ([778ab34](https://github.com/asdf-vm/asdf/commit/778ab34a6f47ac913a0ebc5035d74bfbb3744ebe)) ([9f09f78](https://github.com/asdf-vm/asdf/commit/9f09f78ec06b28bd90d7cdd42762acc1e18011c7)) ([6568891](https://github.com/asdf-vm/asdf/commit/65688915a5d6816b15acf7332d22920d10e8d99a)) ([771f184](https://github.com/asdf-vm/asdf/commit/771f18493fcc8684f02373b2f03750937016f51a)) ([8313ebc](https://github.com/asdf-vm/asdf/commit/8313ebca2d2305b8e176286ae0a43b0359d78059)) ([be52d8f](https://github.com/asdf-vm/asdf/commit/be52d8f39c3aa253496c5469963cd0ecc995d19c)) ([c2e5ee6](https://github.com/asdf-vm/asdf/commit/c2e5ee652532f54aaed7e93796f7239a2a333446)) ([9097696](https://github.com/asdf-vm/asdf/commit/9097696a4fa5ad7efeb383509ecce16125df3cbf)) ([ad0907a](https://github.com/asdf-vm/asdf/commit/ad0907a74df1edf42e7c72ecba96ea52e3de2bcf)) ([2b02f51](https://github.com/asdf-vm/asdf/commit/2b02f51fa1acb2090713f6dfa70903281d28735a)) ([c480044](https://github.com/asdf-vm/asdf/commit/c4800443bd805afd1878891c56108366f3faba0c)) ([26b91aa](https://github.com/asdf-vm/asdf/commit/26b91aa8288787e552dbc50a1c2234ad2264205c)) ([202cdae](https://github.com/asdf-vm/asdf/commit/202cdae831b4909280e0f19ff77bbe4acaec36b4)) ([325cd33](https://github.com/asdf-vm/asdf/commit/325cd3334b3898cbd084ac3b3686458cc92b613e)) ([07b5813](https://github.com/asdf-vm/asdf/commit/07b5813566431ce9f6245e884fe86dd13ad1c195)) ([822e14c](https://github.com/asdf-vm/asdf/commit/822e14c561ab1df74f091bb46bcb340ae9c2bda6)) ([53cd454](https://github.com/asdf-vm/asdf/commit/53cd4544741cab2e58ca7ea8aa36c9c28890b0a1)) ([9f6a65f](https://github.com/asdf-vm/asdf/commit/9f6a65f5dda41d25a9e894ff6a8482810fd6dec8)) ([bd7ab9a](https://github.com/asdf-vm/asdf/commit/bd7ab9ae0844c4c9e41760661a555bab13509752)) ([b6ec89f](https://github.com/asdf-vm/asdf/commit/b6ec89f95f6afedbbdb2daaf4c3d2f67f50f0cf1)) ([162cb8e](https://github.com/asdf-vm/asdf/commit/162cb8eceed53f58c20c547d29ca8068e411ce64)) ([d94bace](https://github.com/asdf-vm/asdf/commit/d94baceb184b8a501a8eb1fd2cd33ca75bf962d9)) ([e7df5ff](https://github.com/asdf-vm/asdf/commit/e7df5ff3253b6caba8ceef6b0754d17fcb765a9b)) ([369beeb](https://github.com/asdf-vm/asdf/commit/369beebab9c80197eb877660add870c0a6be5bda)) ([#1829](https://github.com/asdf-vm/asdf/issues/1829)) ([f68b29b](https://github.com/asdf-vm/asdf/commit/f68b29bd508cfe993d4926377891d0a3e902f2ef)) ([26a3815](https://github.com/asdf-vm/asdf/commit/26a38159483588cd0143b58272f42c96e127d265)) ([ccc98ad](https://github.com/asdf-vm/asdf/commit/ccc98ad4e997b73dcc1e9d6839a17a539c5ec649)) ([447acd1](https://github.com/asdf-vm/asdf/commit/447acd13d1225e2e9ef80ae853c5da02fca034c3)) ([72c20b1](https://github.com/asdf-vm/asdf/commit/72c20b1b51d223dd6633f279258cb8bc54eb4661)) ([924eecf](https://github.com/asdf-vm/asdf/commit/924eecfa6af25f59162e3f35b913a1d0247bd34f)) ([f5a5967](https://github.com/asdf-vm/asdf/commit/f5a59677df42f990529bd8a1668fcfea353ea4a0)) ([3af0291](https://github.com/asdf-vm/asdf/commit/3af02913169abf9049933abc4d99406687d743c3)) ([9ed4216](https://github.com/asdf-vm/asdf/commit/9ed4216525a2076b84e9e5fc6d454746a3b4d825)) ([b9e79e6](https://github.com/asdf-vm/asdf/commit/b9e79e64564ad5a94c9d435458e1a57053744b8d)) ([626bde0](https://github.com/asdf-vm/asdf/commit/626bde0a9785e400eaeb2fa72fb11fff6102b458)) ([f639f8a](https://github.com/asdf-vm/asdf/commit/f639f8a4d0a3fcc3cc175def3b3b2d1ebdac1ade)) ([c0963a3](https://github.com/asdf-vm/asdf/commit/c0963a38a62e61586ef0dd34ea568d23345e3739)) ([cb49b64](https://github.com/asdf-vm/asdf/commit/cb49b64a5adb8944acff0c03e617df0a0f39453c)) ([f74efbf](https://github.com/asdf-vm/asdf/commit/f74efbf1bff5d7489f86f2f568eba2667d95645e)) ([15e1f06](https://github.com/asdf-vm/asdf/commit/15e1f06f3751c9b72deea5cbad587140ba03c645)) ([620c0d8](https://github.com/asdf-vm/asdf/commit/620c0d87e8bf1e1c99a5772c9103fa017e75f487)) ([518a0fa](https://github.com/asdf-vm/asdf/commit/518a0fa4422cff1625cb7b676f40c4bc0a42eed9)) ([5d5d04f](https://github.com/asdf-vm/asdf/commit/5d5d04fbb7a41c83ae5031182d7d671fae434cb5)) ([2fc8006](https://github.com/asdf-vm/asdf/commit/2fc80064902a3b467a82466add1db186a99f075a)) ([c859384](https://github.com/asdf-vm/asdf/commit/c8593842eeb2a3eae74af7747afc62fa406a5b54)) ([2951011](https://github.com/asdf-vm/asdf/commit/2951011090a2a8ac3f42d54198a2447213171c38)) ([c5092c6](https://github.com/asdf-vm/asdf/commit/c5092c6dbfce3614f3de2db08afaa6fcb8b7792e)) ([3f9744d](https://github.com/asdf-vm/asdf/commit/3f9744df0fd8b23684ef2c705642232c5d2151af)) ([985c181](https://github.com/asdf-vm/asdf/commit/985c181118b39bb555a2bd2a1cdbf111a25bd512)) ([18e21c9](https://github.com/asdf-vm/asdf/commit/18e21c90284be35fa01e957b49336ffac515e19b)) ([0058988](https://github.com/asdf-vm/asdf/commit/005898800bded840fa7dd9b62a0c60a93c124c8c)) ([#1820](https://github.com/asdf-vm/asdf/issues/1820)) ([c3bd8fe](https://github.com/asdf-vm/asdf/commit/c3bd8feccd2e4589bc905ccf6b1bcd8dc74f4f37)) ([8394e85](https://github.com/asdf-vm/asdf/commit/8394e858fee419ed38833fa953b122fe04754830)) ([5266ba5](https://github.com/asdf-vm/asdf/commit/5266ba581ddf5f96de2d20391e340bb4f7c123c4)) ([3155dc3](https://github.com/asdf-vm/asdf/commit/3155dc374e9cbea6d2d792fef08914124320a292)) ([7dfa8b4](https://github.com/asdf-vm/asdf/commit/7dfa8b40ae5e557b90fb7917bc86a569cf2bd0a6)) ([5a24864](https://github.com/asdf-vm/asdf/commit/5a2486463238e2473aab739520793011267be19f)) ([80ac9bb](https://github.com/asdf-vm/asdf/commit/80ac9bb51c684c04829328c28a06aa1f1f67a8f2)) ([#1849](https://github.com/asdf-vm/asdf/issues/1849)) ([8b1b024](https://github.com/asdf-vm/asdf/commit/8b1b024f51b166bf29481162fa57e5752d0d9300)) ([1b3c426](https://github.com/asdf-vm/asdf/commit/1b3c42699a30b6bcc7295d3f433dad9cd520f130)) ([163d6b4](https://github.com/asdf-vm/asdf/commit/163d6b4b462954a2f52b6a1b9e09faa806292260)) ([87d3c06](https://github.com/asdf-vm/asdf/commit/87d3c06cf5dc859dab4d1af3dec290ed49651b67)) ([3f17a80](https://github.com/asdf-vm/asdf/commit/3f17a80fbe34ee68a3d5580798374caf1a7bc4ae)) ([#1841](https://github.com/asdf-vm/asdf/issues/1841)) ([251812b](https://github.com/asdf-vm/asdf/commit/251812bfd58a768c4a51b82c0f5f88601abfd84c)) ([#1852](https://github.com/asdf-vm/asdf/issues/1852)) ([78a00fc](https://github.com/asdf-vm/asdf/commit/78a00fc90345bd77d83bb9b2b61f85756fceb1a6)) ([6b45a5e](https://github.com/asdf-vm/asdf/commit/6b45a5e5f74f60a22fd20b331163f5e5d6a3881c)) ([2a31caf](https://github.com/asdf-vm/asdf/commit/2a31cafd38128be86696c0a46e0223a95b8129fe)) ([7439ea9](https://github.com/asdf-vm/asdf/commit/7439ea916829f7c3f1f2932fa17b3d2848c2dbe7)) ([88af4ee](https://github.com/asdf-vm/asdf/commit/88af4eea00c395407a4b904e80e307ad864a6535))\n\n### Bug Fixes\n\n* bash command set end of line LF ([#1847](https://github.com/asdf-vm/asdf/issues/1847)) ([8211421](https://github.com/asdf-vm/asdf/commit/8211421f4e0d1f62d2d2b7436f9070040231fcbd))\n\n## [0.15.0](https://github.com/asdf-vm/asdf/compare/v0.14.1...v0.15.0) (2024-12-18)\n\n\n### Features\n\n* golang-rewrite: remove `asdf update` command to prepare for Go version ([#1806](https://github.com/asdf-vm/asdf/issues/1806)) ([15571a2](https://github.com/asdf-vm/asdf/commit/15571a2d28818644673bbaf0fcf7d1d9e342cda4))\n\n\n### Patches\n\n* completions: Address two Bash completion bugs ([#1770](https://github.com/asdf-vm/asdf/issues/1770)) ([ebdb229](https://github.com/asdf-vm/asdf/commit/ebdb229ce68979a18dae5c0922620b860c56b22f))\n* make plugin-test work on alpine linux ([#1778](https://github.com/asdf-vm/asdf/issues/1778)) ([f5a1f3a](https://github.com/asdf-vm/asdf/commit/f5a1f3a0a8bb50796f6ccf618d2bf4cf3bdea097))\n* nushell: nushell spread operator ([#1777](https://github.com/asdf-vm/asdf/issues/1777)) ([a0ce37b](https://github.com/asdf-vm/asdf/commit/a0ce37b89bd5eb4ddaa806f96305ee99a8c5d365))\n* nushell: Use correct env var for shims dir ([#1742](https://github.com/asdf-vm/asdf/issues/1742)) ([2f07629](https://github.com/asdf-vm/asdf/commit/2f0762991c35da933b81ba6ab75457a504deedbb))\n* when download path got removed, it should use -f to force delete the download files ([#1746](https://github.com/asdf-vm/asdf/issues/1746)) ([221507f](https://github.com/asdf-vm/asdf/commit/221507f1c0288f0df13315a7f0f2c0a7bc39e7c2))\n\n\n### Documentation\n\n* add Korean translation ([#1757](https://github.com/asdf-vm/asdf/issues/1757)) ([9e16306](https://github.com/asdf-vm/asdf/commit/9e16306f42b4bbffd62779aaebb9cbbc9ba59007))\n* propose edits for tiny typographical/grammatical errors ([#1747](https://github.com/asdf-vm/asdf/issues/1747)) ([d462b55](https://github.com/asdf-vm/asdf/commit/d462b55ec9868eeaddba4b70850aba908236dd93))\n* split Lint and Test badges for title asdf in `README.MD` ([#1725](https://github.com/asdf-vm/asdf/issues/1725)) ([c778ea1](https://github.com/asdf-vm/asdf/commit/c778ea1deca19d8ccd91253c2f206a6b51a0a9b1))\n* Update Japanese(ja-jp) Translations ([#1715](https://github.com/asdf-vm/asdf/issues/1715)) ([bd19e4c](https://github.com/asdf-vm/asdf/commit/bd19e4cbdc2f0a9380dbdfcec46584d619e8ed56))\n\n## [0.14.1](https://github.com/asdf-vm/asdf/compare/v0.14.0...v0.14.1) (2024-08-15)\n\n\n### Patches\n\n* Only display the \"can't keep downloads\" warning when asked to keep downloads ([#1756](https://github.com/asdf-vm/asdf/issues/1756)) ([44f3efb](https://github.com/asdf-vm/asdf/commit/44f3efb63b7517520f4610d504d30783a85c9d79))\n\n## [0.14.0](https://github.com/asdf-vm/asdf/compare/v0.13.1...v0.14.0) (2024-01-19)\n\n\n### ⚠ BREAKING CHANGES\n\n* Enable `pipefail` ([#1608](https://github.com/asdf-vm/asdf/issues/1608))\n\n### Patches\n\n* `plugin test` git-ref to use plugin repo default branch ([#1694](https://github.com/asdf-vm/asdf/issues/1694)) ([6d8cf9d](https://github.com/asdf-vm/asdf/commit/6d8cf9d44b3d985ac59f1eac827c5275392f90fd))\n* avoid mention of `ASDF_NU_DIR` ([#1660](https://github.com/asdf-vm/asdf/issues/1660)) ([dfea89c](https://github.com/asdf-vm/asdf/commit/dfea89ccc703f3ef5a87c4b85726456d70167d89))\n* Enable `pipefail` ([#1608](https://github.com/asdf-vm/asdf/issues/1608)) ([4085e55](https://github.com/asdf-vm/asdf/commit/4085e5542bac824ea124610ad247c2f90d1b8d93))\n* **fish:** use PATH instead of fish_user_paths ([#1709](https://github.com/asdf-vm/asdf/issues/1709)) ([5327697](https://github.com/asdf-vm/asdf/commit/53276973f7c99695cd9a28b04c010b006d7f60ca))\n* list `asdf version` command under help.txt UTILS section ([#1673](https://github.com/asdf-vm/asdf/issues/1673)) ([240a5fb](https://github.com/asdf-vm/asdf/commit/240a5fbdea1de055672d02f83db1de990ea2bf83))\n* **nushell:** Use `def --env` instead of `def-env` ([#1681](https://github.com/asdf-vm/asdf/issues/1681)) ([3b8f400](https://github.com/asdf-vm/asdf/commit/3b8f400c3e628851286bfebd8da5bc7ab45cd676))\n* plugin extension commands to not require `bin/` directory ([#1643](https://github.com/asdf-vm/asdf/issues/1643)) ([61420ad](https://github.com/asdf-vm/asdf/commit/61420ad90829b2c9bf1ca16681a2eb652adcc755))\n* use universal scope for fish_user_paths ([#1699](https://github.com/asdf-vm/asdf/issues/1699)) ([0ffee72](https://github.com/asdf-vm/asdf/commit/0ffee7224bc00a917ceaea689c6268fd1f03bd62))\n* warn if plugin does not support keeping downloads if configured ([#1644](https://github.com/asdf-vm/asdf/issues/1644)) ([19515ed](https://github.com/asdf-vm/asdf/commit/19515eda3b91167b0d76c35ffc4402de688007e0))\n\n\n### Documentation\n\n* add Japanese translation ([#1667](https://github.com/asdf-vm/asdf/issues/1667)) ([2b9bec7](https://github.com/asdf-vm/asdf/commit/2b9bec7710cd18e51a01652e1f58cc309baf2fd7))\n* fix some pt-br spelling ([#1640](https://github.com/asdf-vm/asdf/issues/1640)) ([0c7c41a](https://github.com/asdf-vm/asdf/commit/0c7c41ab44d3a42a9e57e3d20a646569c2eacfdc))\n* fix typo \"node version\" filename ([#1679](https://github.com/asdf-vm/asdf/issues/1679)) ([fad23bc](https://github.com/asdf-vm/asdf/commit/fad23bc9f4d38747f28d6708ab01689749030063))\n* fix typo ([#1670](https://github.com/asdf-vm/asdf/issues/1670)) ([5737fa3](https://github.com/asdf-vm/asdf/commit/5737fa316eab01c4033565eacf678222cd861f8d))\n* Improve `.asdfrc` plugin hook documentation ([#1661](https://github.com/asdf-vm/asdf/issues/1661)) ([8fbf9a3](https://github.com/asdf-vm/asdf/commit/8fbf9a396bd4a5b71ec7cf215d12040fb5365d6a))\n\n## [0.13.1](https://github.com/asdf-vm/asdf/compare/v0.13.0...v0.13.1) (2023-09-12)\n\n\n### Patches\n\n* **fish:** use builtin realpath over system one ([#1637](https://github.com/asdf-vm/asdf/issues/1637)) ([5ac3032](https://github.com/asdf-vm/asdf/commit/5ac30328a7bbd1a8d974bb5fb1f14d8bd2d1e03f))\n\n## [0.13.0](https://github.com/asdf-vm/asdf/compare/v0.12.0...v0.13.0) (2023-09-11)\n\n\n### ⚠ BREAKING CHANGES\n\n* `plugin list` exit code 0 when no plugins are installed ([#1597](https://github.com/asdf-vm/asdf/issues/1597))\n* 0 exit code for success when adding an existing plugin ([#1598](https://github.com/asdf-vm/asdf/issues/1598))\n* **fish:** don't resolve symlinks for ASDF_DIR ([#1583](https://github.com/asdf-vm/asdf/issues/1583))\n\n### Features\n\n* add plugin location when update the plugin ([#1602](https://github.com/asdf-vm/asdf/issues/1602)) ([36c7024](https://github.com/asdf-vm/asdf/commit/36c7024baa4b829b3629b4e0430157266d354158))\n\n\n### Patches\n\n* `plugin list` exit code 0 when no plugins are installed ([#1597](https://github.com/asdf-vm/asdf/issues/1597)) ([a029c00](https://github.com/asdf-vm/asdf/commit/a029c007503f2eec911a0c836e8622bb38c5e065))\n* 0 exit code for success when adding an existing plugin ([#1598](https://github.com/asdf-vm/asdf/issues/1598)) ([4dd1904](https://github.com/asdf-vm/asdf/commit/4dd190466a9855dac300ce691e66a7629ef37b82))\n* **fish:** don't resolve symlinks for ASDF_DIR ([#1583](https://github.com/asdf-vm/asdf/issues/1583)) ([d1a563d](https://github.com/asdf-vm/asdf/commit/d1a563dcc0107d5c631f73b114044898b5cadcf9))\n* improve lint and test scripts ([#1607](https://github.com/asdf-vm/asdf/issues/1607)) ([b320803](https://github.com/asdf-vm/asdf/commit/b3208031204aabad6e85346155baacab16862da8))\n* Make asdf.fish compatible with Fish 3.1.2 ([#1590](https://github.com/asdf-vm/asdf/issues/1590)) ([e83d71e](https://github.com/asdf-vm/asdf/commit/e83d71e43f525453994eb4cfda8ad66f8b914529))\n* no longer write temporary files to home directory ([#1592](https://github.com/asdf-vm/asdf/issues/1592)) ([624604a](https://github.com/asdf-vm/asdf/commit/624604a8626dc6006d78121d4cf0f6c920449c56))\n* nushell language syntax update ([#1624](https://github.com/asdf-vm/asdf/issues/1624)) ([0ddab5d](https://github.com/asdf-vm/asdf/commit/0ddab5dfaf28ad97c84a6aa56b08ccc212e07b4d))\n* set default shell version values on POSIX entrypoint ([#1594](https://github.com/asdf-vm/asdf/issues/1594)) ([4d5f22d](https://github.com/asdf-vm/asdf/commit/4d5f22ddb89ce53e24b1ab1cbefce3be95238a19))\n* warn when any ./lib/commands are marked as executable ([#1593](https://github.com/asdf-vm/asdf/issues/1593)) ([2043a09](https://github.com/asdf-vm/asdf/commit/2043a09574bdfdfcf2daf2fdb3bff2d9d2dad64e))\n\n\n### Documentation\n\n* `bin/latest-stable` empty query is set to default ([#1591](https://github.com/asdf-vm/asdf/issues/1591)) ([299dc97](https://github.com/asdf-vm/asdf/commit/299dc97a5b63d8afe1a0bba03e32dddfb7fb8e51))\n* migrate to VitePress from VuePress ([#1578](https://github.com/asdf-vm/asdf/issues/1578)) ([5133819](https://github.com/asdf-vm/asdf/commit/5133819a77aaa393def347bfecb1c442ece4c7f8))\n* upgrade deps & fix breaking changes ([446f8c5](https://github.com/asdf-vm/asdf/commit/446f8c5f947cc5f30f03403c2cfe4dec71b0a494))\n\n## [0.12.0](https://github.com/asdf-vm/asdf/compare/v0.11.3...v0.12.0) (2023-06-09)\n\n\n### ⚠ BREAKING CHANGES\n\n* Remove files containing only `asdf` wrapper functions ([#1525](https://github.com/asdf-vm/asdf/issues/1525))\n* align Fish entrypoint behaviour with other shells ([#1524](https://github.com/asdf-vm/asdf/issues/1524))\n* do not remove items from PATH in POSIX entrypoint ([#1521](https://github.com/asdf-vm/asdf/issues/1521))\n* rework POSIX entrypoint for greater shell support ([#1480](https://github.com/asdf-vm/asdf/issues/1480))\n\n### Features\n\n* Support configurable `ASDF_CONCURRENCY` ([#1532](https://github.com/asdf-vm/asdf/issues/1532)) ([684f4f0](https://github.com/asdf-vm/asdf/commit/684f4f058f24cc418f77825a59a22bacd16a9bee))\n* Support PowerShell Core ([#1522](https://github.com/asdf-vm/asdf/issues/1522)) ([213aa22](https://github.com/asdf-vm/asdf/commit/213aa22378cf0ecf5b1924f1bfc4fee43338255a))\n\n\n### Documentation\n\n* Add Nushell installation instructions for all languages ([#1519](https://github.com/asdf-vm/asdf/issues/1519)) ([6a6c539](https://github.com/asdf-vm/asdf/commit/6a6c539f4a21fdb863fd938edd94ac3bdced349b))\n* fix `ASDF_${LANG}_VERSION` usage ([#1528](https://github.com/asdf-vm/asdf/issues/1528)) ([63f422b](https://github.com/asdf-vm/asdf/commit/63f422b4c7afcf53ef72002e39967eb9ca2da2a9))\n* fix Nushell-Homebrew setup instructions ([#1495](https://github.com/asdf-vm/asdf/issues/1495)) ([49e541a](https://github.com/asdf-vm/asdf/commit/49e541a29ff7a2f35917a4544a8b9adbc02bb1b4))\n* fix uninstall instructions for Fish Shell ([#1547](https://github.com/asdf-vm/asdf/issues/1547)) ([a1e858d](https://github.com/asdf-vm/asdf/commit/a1e858d2542691adabf9b066add86f16e759a90c))\n* Improve wording of env vars section ([#1514](https://github.com/asdf-vm/asdf/issues/1514)) ([ec3eb2d](https://github.com/asdf-vm/asdf/commit/ec3eb2d64f0531be86d10e1202a92f6b7820e294))\n* verbose plugin create command details ([#1445](https://github.com/asdf-vm/asdf/issues/1445)) ([8108ca6](https://github.com/asdf-vm/asdf/commit/8108ca6d7e5f34b9b9723f945a9c4b137f2e10ef))\n\n\n### Patches\n\n* `asdf info` show BASH_VERSION & all asdf envs ([#1513](https://github.com/asdf-vm/asdf/issues/1513)) ([a1b5eee](https://github.com/asdf-vm/asdf/commit/a1b5eeec1caf605c0e4c80748703b9e227b57aeb))\n* align Fish entrypoint behaviour with other shells ([#1524](https://github.com/asdf-vm/asdf/issues/1524)) ([8919f40](https://github.com/asdf-vm/asdf/commit/8919f4009ea233c32298911b28ceb879e2dbc675))\n* assign default values to all internal variables ([#1518](https://github.com/asdf-vm/asdf/issues/1518)) ([86477ee](https://github.com/asdf-vm/asdf/commit/86477ee8dea14ab63faf7132133304855a647fde))\n* Better handling with paths that include spaces ([#1485](https://github.com/asdf-vm/asdf/issues/1485)) ([bbcbddc](https://github.com/asdf-vm/asdf/commit/bbcbddcdd4ffa0f49c3772b66d87331420fa5727))\n* create install directory with `mkdir -p` ([#1563](https://github.com/asdf-vm/asdf/issues/1563)) ([d6185a2](https://github.com/asdf-vm/asdf/commit/d6185a21207e0ac45e69499883dad5e2b585c1b6))\n* do not remove items from PATH in POSIX entrypoint ([#1521](https://github.com/asdf-vm/asdf/issues/1521)) ([b6d0ca2](https://github.com/asdf-vm/asdf/commit/b6d0ca28d5fd2b63c7da67b127e6c2a0e01b2670))\n* enforce consistent shell redirection format ([#1533](https://github.com/asdf-vm/asdf/issues/1533)) ([1bc205e](https://github.com/asdf-vm/asdf/commit/1bc205e8aa61287c766c673acb8f0d4f9c6ee249))\n* improve readability of the non-set `nullglob` guard ([#1545](https://github.com/asdf-vm/asdf/issues/1545)) ([f273612](https://github.com/asdf-vm/asdf/commit/f273612155188f62cf8daf584d5581cd4214daf4))\n* Introduce `ASDF_FORCE_PREPEND` variable on POSIX entrypoint ([#1560](https://github.com/asdf-vm/asdf/issues/1560)) ([5b7d0fe](https://github.com/asdf-vm/asdf/commit/5b7d0fea0a10681d89dd7bf4010e0a39e6696841))\n* lint & style errors in `bin/asdf` ([#1516](https://github.com/asdf-vm/asdf/issues/1516)) ([13c0e2f](https://github.com/asdf-vm/asdf/commit/13c0e2fab0e9ad4dccf72b6f5586fb32458b8709))\n* Nushell plugin list --urls ([#1507](https://github.com/asdf-vm/asdf/issues/1507)) ([9363fb2](https://github.com/asdf-vm/asdf/commit/9363fb2f72e7fa08d3580b22d465af48a7d37031))\n* nushell plugin list all ([#1501](https://github.com/asdf-vm/asdf/issues/1501)) ([#1502](https://github.com/asdf-vm/asdf/issues/1502)) ([c5b8b3c](https://github.com/asdf-vm/asdf/commit/c5b8b3c128b48e1531f6d03d2083435f413a4738))\n* Remove files containing only `asdf` wrapper functions ([#1525](https://github.com/asdf-vm/asdf/issues/1525)) ([00fee78](https://github.com/asdf-vm/asdf/commit/00fee78423de0e399f5705bb483e599e39b707c9))\n* remove leading asterick in Fish completion ([#1543](https://github.com/asdf-vm/asdf/issues/1543)) ([198ced5](https://github.com/asdf-vm/asdf/commit/198ced50327b20b136cb6ec165610d37334a2962))\n* rename internal function `asdf_tool_versions_filename` ([#1544](https://github.com/asdf-vm/asdf/issues/1544)) ([b36ec73](https://github.com/asdf-vm/asdf/commit/b36ec7338654abc3773314147540dfa8297b48b8))\n* rename internal plugin repository functions ([#1537](https://github.com/asdf-vm/asdf/issues/1537)) ([5367f1f](https://github.com/asdf-vm/asdf/commit/5367f1f09079070c7b47551dc453c686991564a0))\n* rework POSIX entrypoint for greater shell support ([#1480](https://github.com/asdf-vm/asdf/issues/1480)) ([3379af8](https://github.com/asdf-vm/asdf/commit/3379af845ed2e281703bc0e9e4f388a7845edc2a))\n* support `asdf shim-versions` completions in fish & bash ([#1554](https://github.com/asdf-vm/asdf/issues/1554)) ([99623d7](https://github.com/asdf-vm/asdf/commit/99623d7eac0fe17e330a950c71b7ba378f656b2c))\n* Typo in POSIX entrypoint ([#1562](https://github.com/asdf-vm/asdf/issues/1562)) ([6b2ebf5](https://github.com/asdf-vm/asdf/commit/6b2ebf575ff98d3970b518de04238d30804a40d1))\n* warn if `.tool-versions` or asdfrc contains carriage returns ([#1561](https://github.com/asdf-vm/asdf/issues/1561)) ([097f773](https://github.com/asdf-vm/asdf/commit/097f7733d67aaf8d0dca1c793407babbdf6f8394))\n\n## [0.11.3](https://github.com/asdf-vm/asdf/compare/v0.11.2...v0.11.3) (2023-03-16)\n\n\n### Bug Fixes\n\n* Prepend asdf directories to the PATH in Nushell ([#1496](https://github.com/asdf-vm/asdf/issues/1496)) ([745950c](https://github.com/asdf-vm/asdf/commit/745950c3589c4047a5b94b34bc9cf06dff5dc3c7))\n\n## [0.11.2](https://github.com/asdf-vm/asdf/compare/v0.11.1...v0.11.2) (2023-02-21)\n\n\n### Bug Fixes\n\n* bash completion for latest command ([#1472](https://github.com/asdf-vm/asdf/issues/1472)) ([2606a87](https://github.com/asdf-vm/asdf/commit/2606a875eba8d74be56c78c97a76f3eb92c8253d))\n* enforce & use consistent function definitions ([#1464](https://github.com/asdf-vm/asdf/issues/1464)) ([e0fd7a7](https://github.com/asdf-vm/asdf/commit/e0fd7a7be8bbbbf0f3cb6dc38cea3b62963eb0c9))\n* nushell PATH conversion to list before filter ([#1471](https://github.com/asdf-vm/asdf/issues/1471)) ([cd0e12b](https://github.com/asdf-vm/asdf/commit/cd0e12b3ee4090242b884ac4aea0f65784e52946))\n* Remove `==` inside `[` ([#1421](https://github.com/asdf-vm/asdf/issues/1421)) ([d81b81f](https://github.com/asdf-vm/asdf/commit/d81b81f9de2dc5961624464df04cef7cafae588c))\n* support nushell v0.75.0 ([#1481](https://github.com/asdf-vm/asdf/issues/1481)) ([dd8d399](https://github.com/asdf-vm/asdf/commit/dd8d3999d41cfdd8518a9ea478929b5291b8838c))\n\n## [0.11.1](https://github.com/asdf-vm/asdf/compare/v0.11.0...v0.11.1) (2023-01-13)\n\n\n### Bug Fixes\n\n* `reshim` did not rewrite executable path ([#1311](https://github.com/asdf-vm/asdf/issues/1311)) ([5af7625](https://github.com/asdf-vm/asdf/commit/5af76257693d1f560b9c27c9cdcc6f5a5a33c4d5))\n* Add test for nushell integration and fix some bugs ([#1415](https://github.com/asdf-vm/asdf/issues/1415)) ([60d4494](https://github.com/asdf-vm/asdf/commit/60d4494d5d21f9d7bdd0778ca962ddb44280aff7))\n* Allow `path:` versions to use `~` ([#1403](https://github.com/asdf-vm/asdf/issues/1403)) ([670c96d](https://github.com/asdf-vm/asdf/commit/670c96d1a6d6d2c19ff63ce2ed14f784c340e9b9))\n* Ban use of 'test' ([#1383](https://github.com/asdf-vm/asdf/issues/1383)) ([ec972cb](https://github.com/asdf-vm/asdf/commit/ec972cbdf0acbecf70e3678c055e27866c49341d))\n* correct order of checks in conditional for adding a missing newline ([#1418](https://github.com/asdf-vm/asdf/issues/1418)) ([4125d2b](https://github.com/asdf-vm/asdf/commit/4125d2b5560efc646e6048202ceb00fffd0b9eaf)), closes [#1417](https://github.com/asdf-vm/asdf/issues/1417)\n* Do not use `pwd` ([dd37b6f](https://github.com/asdf-vm/asdf/commit/dd37b6f0c0ed20d15e3d96b730db82f21c9e2e6f))\n* Do not use type not exported on older Python versions ([#1409](https://github.com/asdf-vm/asdf/issues/1409)) ([7460809](https://github.com/asdf-vm/asdf/commit/74608098cdfc70c2d2e85d1f3861500ef668a041))\n* force lwrcase plugin name fix capitalization mismatch errs ([#1400](https://github.com/asdf-vm/asdf/issues/1400)) ([196a05b](https://github.com/asdf-vm/asdf/commit/196a05b2dcef48f3a281350734c76ba7bc73fa81))\n* lint errors from `scripts/checkstyle.py` ([#1385](https://github.com/asdf-vm/asdf/issues/1385)) ([3492043](https://github.com/asdf-vm/asdf/commit/3492043241e466337c5965a6fe2e089147bc4152))\n* mv dev dep from repo root to subdir to avoid clash ([#1408](https://github.com/asdf-vm/asdf/issues/1408)) ([5df70da](https://github.com/asdf-vm/asdf/commit/5df70dadacd66b4150ed47e58c861418c0d1281f))\n* Remove unnecessary backslashes ([#1384](https://github.com/asdf-vm/asdf/issues/1384)) ([15faf93](https://github.com/asdf-vm/asdf/commit/15faf93a0d3615834e550ea1562fb6b8cee5a205))\n* Remove usage of `$(pwd)` in favor of `$PWD` ([f522ab9](https://github.com/asdf-vm/asdf/commit/f522ab98797345d767b239041246dfb4b740423e))\n\n## [0.11.0](https://github.com/asdf-vm/asdf/compare/v0.10.2...v0.11.0) (2022-12-12)\n\n\n### Features\n\n* **completions:** bash improvements ([#1329](https://github.com/asdf-vm/asdf/issues/1329)) ([7c802c3](https://github.com/asdf-vm/asdf/commit/7c802c3fc9b5dc556993a98e5aaf96650cbb0d5b))\n* Disable short-name repository with config value ([#1227](https://github.com/asdf-vm/asdf/issues/1227)) ([18caea3](https://github.com/asdf-vm/asdf/commit/18caea3eb7d989d195cf13b3c9ffc2058d906fc5))\n* mark current resolved versions in `asdf list` output ([#762](https://github.com/asdf-vm/asdf/issues/762)) ([5ea6795](https://github.com/asdf-vm/asdf/commit/5ea67953be74cb5fde11240dc40a541c69afc65c))\n* support nushell ([#1355](https://github.com/asdf-vm/asdf/issues/1355)) ([274a638](https://github.com/asdf-vm/asdf/commit/274a638e155c08cd0d6dbda1a0d4da02c3466c97))\n\n\n### Bug Fixes\n\n* add missing \"does not add paths to PATH more than once\" test for elvish ([#1275](https://github.com/asdf-vm/asdf/issues/1275)) ([3c55167](https://github.com/asdf-vm/asdf/commit/3c55167a6807b48cacaaed18df7bf0db2526ed59))\n* append trailing newline to .tool-versions files when missing ([#1310](https://github.com/asdf-vm/asdf/issues/1310)) ([eb7dac3](https://github.com/asdf-vm/asdf/commit/eb7dac3a2b15ad458f55a897d49a377508ea92fe)), closes [#1299](https://github.com/asdf-vm/asdf/issues/1299)\n* excludes \"milestone\" releases in \"latest\" command ([#1307](https://github.com/asdf-vm/asdf/issues/1307)) ([5334d1d](https://github.com/asdf-vm/asdf/commit/5334d1db3d390c46ed49101528f74483eb6b2987)), closes [#1306](https://github.com/asdf-vm/asdf/issues/1306)\n* improve formatting of ballad ([#1367](https://github.com/asdf-vm/asdf/issues/1367)) ([e0c2c31](https://github.com/asdf-vm/asdf/commit/e0c2c31fc3274387efdddebe1450f0662f91a726))\n* use ELVISH_VERSION to specify elvish test version ([#1276](https://github.com/asdf-vm/asdf/issues/1276)) ([72c3a23](https://github.com/asdf-vm/asdf/commit/72c3a2377a1afa3027c6f29cb9f3f1a7fbddaa8c))\n\n### [0.10.2](https://www.github.com/asdf-vm/asdf/compare/v0.10.1...v0.10.2) (2022-06-08)\n\n\n### Bug Fixes\n\n* always use ASDF_DEFAULT_TOOL_VERSIONS_FILENAME for filename when present ([#1238](https://www.github.com/asdf-vm/asdf/issues/1238)) ([711ad99](https://www.github.com/asdf-vm/asdf/commit/711ad991043a1980fa264098f29e78f2ecafd610)), closes [#1082](https://www.github.com/asdf-vm/asdf/issues/1082)\n* get invalid ASDF_DATA_DIR when exec asdf shims by non-shell ([#1154](https://www.github.com/asdf-vm/asdf/issues/1154)) ([b9962f7](https://www.github.com/asdf-vm/asdf/commit/b9962f71564ce77cf97772cc100b80f9d77019b1))\n* update event trigger for doc-version workflow ([#1232](https://www.github.com/asdf-vm/asdf/issues/1232)) ([0bc8c3a](https://www.github.com/asdf-vm/asdf/commit/0bc8c3ab6895b88c96bff86f5f79575ee80cc718))\n* update plugin-add regex to support other languages ([#1241](https://www.github.com/asdf-vm/asdf/issues/1241)) ([92d005d](https://www.github.com/asdf-vm/asdf/commit/92d005dacd2ec434a9d912ab9938b59ab1b7c51f)), closes [#1237](https://www.github.com/asdf-vm/asdf/issues/1237)\n* updating references to legacy github.io site ([#1240](https://www.github.com/asdf-vm/asdf/issues/1240)) ([738306b](https://www.github.com/asdf-vm/asdf/commit/738306bc5d1c53a22c06e4d6d3ddb6d511dc5d50))\n\n### [0.10.1](https://www.github.com/asdf-vm/asdf/compare/v0.10.0...v0.10.1) (2022-05-17)\n\n\n### Bug Fixes\n\n* add asdf to list of banned commands ([#1224](https://www.github.com/asdf-vm/asdf/issues/1224)) ([39909e0](https://www.github.com/asdf-vm/asdf/commit/39909e01af2bbf23fc821de5cec6c5c9661c59bb))\n* don't invoke asdf inside asdf commands ([#1208](https://www.github.com/asdf-vm/asdf/issues/1208)) ([27f7ef7](https://www.github.com/asdf-vm/asdf/commit/27f7ef78529649534b8383daa58e4b370b3cbd7f))\n* fixing bats ([#1215](https://www.github.com/asdf-vm/asdf/issues/1215)) ([a9caa5b](https://www.github.com/asdf-vm/asdf/commit/a9caa5bdffca5401798fb37e6f34af933b6ce0d2))\n* instead /tmp, use TMPDIR if defined ([9113623](https://www.github.com/asdf-vm/asdf/commit/91136234e90b5fe8718338f513fa770c99151d3e))\n* make fish shell setup match other shells ([#1209](https://www.github.com/asdf-vm/asdf/issues/1209)) ([6fc4bb8](https://www.github.com/asdf-vm/asdf/commit/6fc4bb8fc650e73152ce326267f89df6865cdd24))\n* only iterate over directories in the plugins/ directory ([#1228](https://www.github.com/asdf-vm/asdf/issues/1228)) ([788ccab](https://www.github.com/asdf-vm/asdf/commit/788ccab5971cb828cf25364b0df5ed6f5e9e713d))\n* update elvish to 0.18.0 ([5a89563](https://www.github.com/asdf-vm/asdf/commit/5a89563c0a37f244fa3daa46c5100b7711edde1d))\n\n## [0.10.0](https://www.github.com/asdf-vm/asdf/compare/v0.9.0...v0.10.0) (2022-04-14)\n\n\n### Features\n\n* case-insensitive filtering of unstable versions in `latest` ([#1139](https://www.github.com/asdf-vm/asdf/issues/1139)) ([e61e3d9](https://www.github.com/asdf-vm/asdf/commit/e61e3d9ade0d7bdfb4413184284038c50ba1e09c))\n* **latest:** adds the flag --all to the latest command ([#1096](https://www.github.com/asdf-vm/asdf/issues/1096)) ([f85fef5](https://www.github.com/asdf-vm/asdf/commit/f85fef533f249df5a9f58307d288f2f069351e88))\n* upgrade elvish to 0.17.0 ([#1159](https://www.github.com/asdf-vm/asdf/issues/1159)) ([824550e](https://www.github.com/asdf-vm/asdf/commit/824550ed2009c7e8c4c84afd7a01197d451c47bf))\n\n\n### Bug Fixes\n\n* Ban `ls` command ([#1141](https://www.github.com/asdf-vm/asdf/issues/1141)) ([87137e4](https://www.github.com/asdf-vm/asdf/commit/87137e41031f17b30acf12ee35925e689c84e2d8))\n* ban grep long flags ([#1117](https://www.github.com/asdf-vm/asdf/issues/1117)) ([6e4c39c](https://www.github.com/asdf-vm/asdf/commit/6e4c39c244a289a54f235cf15a29874fb8885927))\n* do not print `find` errors ([#1102](https://www.github.com/asdf-vm/asdf/issues/1102)) ([5992abb](https://www.github.com/asdf-vm/asdf/commit/5992abb09e6f5e0af690bf0e99619386187949db))\n* don't generate on error if backup file doesn't exists ([#1057](https://www.github.com/asdf-vm/asdf/issues/1057)) ([288468f](https://www.github.com/asdf-vm/asdf/commit/288468f93f6c5cb4e7cca1173d4ad73154d0d564))\n* **elvish:** prepend asdf paths to `$PATH` ([#1174](https://www.github.com/asdf-vm/asdf/issues/1174)) ([682b7a1](https://www.github.com/asdf-vm/asdf/commit/682b7a1d6dc1a35f7f8b0ddbab977e0a3dae2c9c))\n* latest --all correctly report plugins as missing ([#1118](https://www.github.com/asdf-vm/asdf/issues/1118)) ([aafe1e5](https://www.github.com/asdf-vm/asdf/commit/aafe1e5304c2d2a026831976c18faa6fb48d25bc))\n* local plugin in then clause too ([#1203](https://www.github.com/asdf-vm/asdf/issues/1203)) ([448f750](https://www.github.com/asdf-vm/asdf/commit/448f750891a4366f45d905b112ad20d4be66c496))\n* newline after error msg for ASDF_DIR ([#1113](https://www.github.com/asdf-vm/asdf/issues/1113)) ([ac2791e](https://www.github.com/asdf-vm/asdf/commit/ac2791e49f7fcdbeb420415d8ddcb5f17bcf296e))\n* Prevent unbound variable error with nounset in asdf.sh ([#1158](https://www.github.com/asdf-vm/asdf/issues/1158)) ([b7dd291](https://www.github.com/asdf-vm/asdf/commit/b7dd291c983af321af20550fa89bf1cfbc888aec))\n* remove comments from whole file instead of line by line for performance ([#1198](https://www.github.com/asdf-vm/asdf/issues/1198)) ([de6e22f](https://www.github.com/asdf-vm/asdf/commit/de6e22f909946f7d17047f4aeab41e581546b674))\n* shorthand grep options for alpine support ([#1106](https://www.github.com/asdf-vm/asdf/issues/1106)) ([234778a](https://www.github.com/asdf-vm/asdf/commit/234778a397f19c398d2f76a0321fef3273c9a086))\n\n## [0.9.0](https://www.github.com/asdf-vm/asdf/compare/v0.8.1...v0.9.0) (2021-11-18)\n\n\n### Features\n\n* add post update plugin support ([#1049](https://www.github.com/asdf-vm/asdf/issues/1049)) ([304f72d](https://www.github.com/asdf-vm/asdf/commit/304f72dbb207606fd82c04ee2c73cf11e9e6e0cc))\n* asdf latest defer to plugin to determine the latest version ([#938](https://www.github.com/asdf-vm/asdf/issues/938)) ([664d82e](https://www.github.com/asdf-vm/asdf/commit/664d82ed8a734eb30988840829a972f8ddd8e523))\n* configurable plugin repo last check time ([#957](https://www.github.com/asdf-vm/asdf/issues/957)) ([1716afa](https://www.github.com/asdf-vm/asdf/commit/1716afa02125aa322d8a688ff4b3e95f2e08b33c))\n* display plugin repo refs alongside urls in info cmd ([#1014](https://www.github.com/asdf-vm/asdf/issues/1014)) ([cd0a6a7](https://www.github.com/asdf-vm/asdf/commit/cd0a6a779eb18236fe7bf1f84403e33e636ef1f3))\n* Displays a warning when a plugin from the tools-version list does not exist ([#1033](https://www.github.com/asdf-vm/asdf/issues/1033)) ([9430a39](https://www.github.com/asdf-vm/asdf/commit/9430a39aef1dbf806a8954d71711747be1001076))\n* Elvish Shell support ([#1066](https://www.github.com/asdf-vm/asdf/issues/1066)) ([cc7778a](https://www.github.com/asdf-vm/asdf/commit/cc7778a040751f6801524135f5f5ece3a748fa8c))\n* toggle off repo sync completely ([#1011](https://www.github.com/asdf-vm/asdf/issues/1011)) ([a3ba5a7](https://www.github.com/asdf-vm/asdf/commit/a3ba5a794c07efb4aa9cce9c15d41b4b61d5df01))\n\n\n### Bug Fixes\n\n* Adds \"grep -P\" to the list of banned commands ([#1064](https://www.github.com/asdf-vm/asdf/issues/1064)) ([8a515f4](https://www.github.com/asdf-vm/asdf/commit/8a515f49d7443ee318badbd4d8f092ad0d8f04ca))\n* allow plugin callbacks to be in any language ([#995](https://www.github.com/asdf-vm/asdf/issues/995)) ([2ad0f5e](https://www.github.com/asdf-vm/asdf/commit/2ad0f5ea452bd8f843951c4a9cc56a020e172b07))\n* clarify the wording when no version is set ([#1088](https://www.github.com/asdf-vm/asdf/issues/1088)) ([4116284](https://www.github.com/asdf-vm/asdf/commit/41162849cf5c966c749ec435ebe32bd649a86ee8))\n* completions for asdf plugin list ([#1061](https://www.github.com/asdf-vm/asdf/issues/1061)) ([43412aa](https://www.github.com/asdf-vm/asdf/commit/43412aad5f668686daa058505a61c070561b46fc))\n* Correct typo on getting started page ([#1086](https://www.github.com/asdf-vm/asdf/issues/1086)) ([4321980](https://www.github.com/asdf-vm/asdf/commit/4321980c3385ac1bafd77503c8ec77b26042d05b))\n* don't override existing ASDF_DIR ([#1008](https://www.github.com/asdf-vm/asdf/issues/1008)) ([73efc9f](https://www.github.com/asdf-vm/asdf/commit/73efc9fa97744c49c5004ee8bb9b6064b6ce22f2))\n* ensure shims get created when data dir has spaces ([#996](https://www.github.com/asdf-vm/asdf/issues/996)) ([39c9999](https://www.github.com/asdf-vm/asdf/commit/39c9999519a1d3c51ffb3b8dddd141dbc29b3bd1))\n* Fix plugin-test arg parsing ([#1084](https://www.github.com/asdf-vm/asdf/issues/1084)) ([c911f2d](https://www.github.com/asdf-vm/asdf/commit/c911f2d43198945f21bb25100c9dab5a375c780b))\n* full_version_name is not available here ([#1031](https://www.github.com/asdf-vm/asdf/issues/1031)) ([8490526](https://www.github.com/asdf-vm/asdf/commit/84905265467c9fdf618c11f69a5ae71408e18bea))\n* help for extension commands for plugins with hyphens in the name. ([#1048](https://www.github.com/asdf-vm/asdf/issues/1048)) ([3e0cb9a](https://www.github.com/asdf-vm/asdf/commit/3e0cb9aaea7f2bf282a18c4912454737fef0741b))\n* help text as per new feats in [#633](https://www.github.com/asdf-vm/asdf/issues/633) ([#991](https://www.github.com/asdf-vm/asdf/issues/991)) ([0d95663](https://www.github.com/asdf-vm/asdf/commit/0d956635b5cabe35f0895121929e8e668a3ee03d))\n* incorrect usage of grep ([#1035](https://www.github.com/asdf-vm/asdf/issues/1035)) ([30d27cb](https://www.github.com/asdf-vm/asdf/commit/30d27cbe6b358cd790fb66dbc8a14806eca23f05))\n* insert error handling in list-all & download plugin scripts ([#881](https://www.github.com/asdf-vm/asdf/issues/881)) ([a7d3661](https://www.github.com/asdf-vm/asdf/commit/a7d3661f6c53b24ae1c21e93f94209f3af243349))\n* lint scripts for local and CI ([#961](https://www.github.com/asdf-vm/asdf/issues/961)) ([5dafbc8](https://www.github.com/asdf-vm/asdf/commit/5dafbc8e390eacbcfcf97d6d2890e0aa6ef9cd60))\n* pipe find into while ([26d2c64](https://www.github.com/asdf-vm/asdf/commit/26d2c64477a1faabedd9a5f97aa7da706988cd72))\n* Quote commands correctly in plugin-test ([#1078](https://www.github.com/asdf-vm/asdf/issues/1078)) ([69ff2d0](https://www.github.com/asdf-vm/asdf/commit/69ff2d0c9a4fd273c9dac151345f60f7b146e569))\n* regex validate plugin names on plugin add cmd ([#1010](https://www.github.com/asdf-vm/asdf/issues/1010)) ([7697e6e](https://www.github.com/asdf-vm/asdf/commit/7697e6e344809ab4603d0764fb8a969c2bbaf3b6))\n* remove find -print0 ([b9228a2](https://www.github.com/asdf-vm/asdf/commit/b9228a26de6a0337a7b59fb5252323d368a72a92))\n* Sed improvements ([#1087](https://www.github.com/asdf-vm/asdf/issues/1087)) ([4b93bc8](https://www.github.com/asdf-vm/asdf/commit/4b93bc81aa982b72621cd09e71eeea71ee009185))\n* sed re error trailing backslash on FreeBSD ([#1046](https://www.github.com/asdf-vm/asdf/issues/1046)). ([#1047](https://www.github.com/asdf-vm/asdf/issues/1047)) ([47e8fb0](https://www.github.com/asdf-vm/asdf/commit/47e8fb051b3577d251376976d5767c520f3524fc))\n* support latest with filter on local and global ([#633](https://www.github.com/asdf-vm/asdf/issues/633)) ([5cf8f89](https://www.github.com/asdf-vm/asdf/commit/5cf8f8962fbd5fe2bc86856bc4676f88e1aa8885))\n* Use more idiomatic fish ([#1042](https://www.github.com/asdf-vm/asdf/issues/1042)) ([847ec73](https://www.github.com/asdf-vm/asdf/commit/847ec73751ced9d149ce0826309fee0f894ca664))\n* wait until the plugin update are finished ([#1037](https://www.github.com/asdf-vm/asdf/issues/1037)) ([7e1f2a0](https://www.github.com/asdf-vm/asdf/commit/7e1f2a0d938052d4fa5ce6546f07b3decbd740cf))\n\n## 0.8.1\n\nFeatures\n\n* Support for latest version in shell, local, and global commands (#802, #801)\n* Parallel updating of all plugins (#626, #530)\n* Print documentation website and GitHub URLs in help command (#820)\n\nFixed Bugs\n\n* Fix plugin-update --all when there are no plugins (#805, #803)\n* Ban `echo` command from asdf codebase (#806, #781)\n* Add basic tests for plugin-update command (#807)\n* Cleanup unused code in plugin update tests (#810)\n* Fix resolution of relative symlinks (#815, #625)\n* Fixes to GitHub workflow (#833)\n* Update no plugin installed error message (#818)\n* Remove process substitution that was problematic when POSIXLY_CORRECT is set (#851, #581)\n* Fix warnings from find command (#853)\n* Ban the `sort -V` command from the asdf codebase (#755, #867)\n* Fix `plugin update --all` so that the default branch is used for each plugin (#800)\n* Fix issues with awk command on some platforms used by plugin update command (#924, #899, #919)\n* Add completion for the `system` version (#911)\n\nDocumentation\n\n* Link to Homebrew common issues from documentation site (#795)\n* Remove -vm suffix name in documentation (#798, #796)\n* Fix file renames in release script (#809)\n* Update supported versions in documentation (#825)\n* Fix references to icongram files (#827)\n* Fix broken links in CONTRIBUTING.md (#832, #852)\n* Fix broken link in README.md (#835)\n* Improve zsh completion directions for macOS,ZSH,Homebrew (#843)\n* Add GitHub discussions link (#839)\n* Add note about unsolicited formatting pull requests (#848)\n* Fix formatting of GitHub name (#847)\n* Explain the difference between ASDF_DIR and ASDF_DATA_DIR (#855)\n* Update BATS link to bats-core GitHub repo (#858)\n* Instruct users to symlink completions for Fish shell (#860)\n* Support alternate locations for `.zshrc` (#871)\n* Add \"Add translation\" link to navbar (#876)\n* Clarify usage of the ASDF_DEFAULT_TOOL_VERSIONS_FILENAME variable (#912, #900)\n* Show how to use the `system` version (#925, #868)\n* Remove instructions for installing dependencies for Homebrew installs (#937, #936)\n\n## 0.8.0\n\nFeatures\n\n* Add support for plugin documentation callback scripts (#512, #757)\n* Add support for installing one tool specified in `.tool-versions` (#759, #760)\n* Improve introduction and install sections of documentation (#699, #740)\n* Add dependencies for openSUSE and ArchLinux to documentation (#714)\n* Add support for keeping downloaded tool source code (#74, #669)\n* Add `asdf info` command to print debug information (#786, #787)\n\nFixed Bugs\n\n* Fix typo that caused plugin-test to erroneously fail (#780)\n* Make sure shims are only appended to `PATH` once in Fish shell (#767, #777, #778)\n* Print `.tool-versions` file path on shim error (#749, #750)\n* Add `column` and `sort -V` to list of banned commands for the asdf codebase (#661, #754)\n* Use editorconfig for shell formatting (#751)\n* Remove use of `column` command in favor of awk (#721)\n* Add `asdf shell` command to help output (#715, #737)\n* Ensure consistency in indentation for message shown when no versions installed (#728)\n* Fix dead link in documentation (#733)\n* Fix typo in docs/core-manage-versions.md (#722)\n* Fix a typo in the `asdf env` command documentation (#717)\n* Fix Fish shell documentation (#709)\n* Only list asdf dependencies on asdf website (#511, #710)\n* Add CODEOWNERS file for GitHub reviews (#705)\n* Add unit test for `asdf plugin-add` exit code (#689)\n\n## 0.7.8\n\nFeatures\n\n* Add support for `post-plugin-add` and `pre-plugin-remove` in plugins. Add configurable command hooks for plugin installation and removal (#670, #683)\n\n    ```shell\n    pre_asdf_plugin_remove = echo will remove plugin ${1}\n    pre_asdf_plugin_remove_foo = echo will remove plugin foo\n    post_asdf_plugin_remove = echo removed plugin ${1}\n    post_asdf_plugin_remove_foo = echo removed plugin foo\n    ```\n\n* Use different exit code if updates are disabled (#676)\n\nFixed Bugs\n\n* Make sure extension commands are properly displayed by `asdf help`\n\n  Extension commands are now expected to be inside plugins's `lib/commands/command-*.bash` instead of `bin/command*`.\n\n  This change was made for two reasons: Keep the convention that all files to be sourced by bash should end with\n  the `.bash` extension. And the `lib/commands/` directory mirrors the location of asdf own core commands.\n\n  Added tests to make sure `asdf help` properly displays available extension commands.\n\n* Remove automatic `compinit` from asdf.sh (#674, #678)\n\n## 0.7.7\n\nFeatures\n\n* Add .bash file extension to files executed by Bash (#664)\n* Add security policy (#660)\n\nFixed Bugs\n\n* consistent use of plugin_name (#657)\n* Default ZSH_VERSION to empty string (#656)\n* Fix support for path version (#654)\n* Fix hanging 'asdf update is a noop for non-git repos' test (#644)\n* Fix Bash completions for `plugin-add` (#643)\n* Fix `--unset` for Fish shell (#640)\n* Misc. documentation fixes (#631, #652)\n* Defaults to empty ASDF_DATA_DIR (#630)\n* Remove shebang lines of sourced scripts (#629)\n* Ignore shim directory for executable lookups (#623)\n* Fix issue with preset version warning assuming that the shim name and plugin name are the same (#622)\n\n## 0.7.6\n\nFeatures\n\n* Improve output format of `asdf plugin list all`\n\n  Long plugin names were causing problems with how we used printf.\n  Now we use the `column` command to properly render output.\n\n* Now `asdf plugin list` can take both `--urls` and `--refs` options.\n\n  When `--url` is used, we print the plugin's remote origin URL.\n  While `--refs` prints the git branch/commit the plugin is at.\n\n* It's now possible to update a plugin to an specific branch/commit.\n\n  `asdf plugin update <name> [git-ref]`\n\n  Checkouts a plugin to the specified `git-ref`. Defaults to `master`\n\n* Now the `asdf plugin test` command can be specified with a plugin commit/branch to test.\n\n  This will help CI checks to actually test the commit they are running for.\n  Previously we always used the plugin's `master` branch.\n\n* Subcommand CLI support.\n\n   Users familiar with sub-command aware tools like `git` can now\n   use `asdf` commands in the same way. For example:\n\n   `asdf plugin list all` is equivalent to `asdf plugin-list-all`\n\n   This is also the case for plugin extension commands, where the\n   plugin name is an asdf main subcommand. ie. Having a `foo` plugin\n   you can invoke: `asdf foo bar`\n\n* Make `asdf plugin test` use the new `asdf latest` command. (#541)\n\n   If a plugin version is not given explicitly, we use `asdf latest` to\n   obtain the version of plugin to install for testing.\n\n* `asdf --version` displays git revision when asdf_dir is a git clone.\n\n   This will allow better bug reports since people can now include the\n   git commit they are using.\n\n* Add support for asdf extension commands.\n\n   Plugins can provide `bin/command*` scripts or executables that will\n   be callable using the asdf command line interface.\n\n   See `docs/plugins-create.md` for more info.\n\n* Add support for installing the latest stable version of a tool (#216)\n\n    ```shell\n    asdf install python latest\n    asdf install python latest:3.7 # installs latest Python 3.7 version\n    ```\n\n* Add `asdf latest` command to display the latest stable version of a tool (#575)\n\n    ```shell\n    asdf latest python\n    asdf latest python 3.7 # displays latest Python 3.7 version\n    ```\n\n* Add support for filtering versions returned by `asdf list-all`\n\n    ```shell\n    asdf list-all python 3.7 # lists available Python 3.7 versions\n    ````\n\n## 0.7.5\n\nFeatures\n\n* Add AppVeyor config for builds on Windows, for eventual Windows support (#450, #451)\n* Add `--unset` flag to shell command (#563)\n\nFixed Bugs\n\n* Fix multiple version install (#540, #585)\n* Handle dashes in executable/shim names properly (#565, #589)\n* Fix bug in sed command so `path:...` versions are handled correctly (#559, #591)\n\n## 0.7.4\n\nFeatures\n\n* Add quite flag to git clone (#546)\n* Improve docs for Homebrew (#553, #554)\n\nFixed Bugs\n\n* Don't include the current directory in `PATH` variable in `asdf env` environment (#543, #560)\n* Fix `asdf plugin-test` dependency on Git when installed via Homebrew (#509, #556)\n\n## 0.7.3\n\nFeatures\n\n* Make `asdf install` check for versions in legacy files (#533, #539)\n\nFixed Bugs\n\n* Address shellcheck warning and use shell globbing instead of `ls` (#525)\n\n## 0.7.2\n\nFeatures\n\n* Add unit tests for untested code in asdf.sh and asdf.fish (#286, #507, #508)\n* Switched to a maintained version of BATS (#521)\n\nFixed Bugs\n\n* Don't iterate on output of `ls` (#513)\n* Check shims for full tool version so adding new versions to a shim works properly (#517, #524)\n\n## 0.7.1\n\nFeatures\n\n* Add mksh support\n* Add documentation about using multiple versions of the same plugin\n* Remove post_COMMAND hooks\n* Add `asdf shell` command to set a version for the current shell (#480)\n* Ignore comments in .tool-versions (#498, #504)\n\nFixed Bugs\n\n* Avoid modifying `fish_user_paths`\n* Restore support for legacy file version (#484)\n* Restore support for multiple versions\n* Fix bug when trying to locate shim (#488)\n* Run executable using `exec` (#502)\n\n## 0.7.0\n\nFeatures\n\n* Shims can be invoked directly via `asdf exec <command> [args...]` without requiring to have all shims on path (#374).\n* New `asdf env <command>` can be used to print or execute with the env that would be used to execute a shim. (#435)\n* Configurable command hooks from `.asdfrc` (#432, #434)\n  Suppose a `foo` plugin is installed and provides a `bar` executable,\n  The following hooks will be executed when set:\n\n    ```shell\n    pre_asdf_install_foo = echo will install foo version ${1}\n    post_asdf_install_foo = echo installed foo version ${1}\n\n    pre_asdf_reshim_foo = echo will reshim foo version ${1}\n    post_asdf_reshim_foo = echo reshimmed foo version ${1}\n\n    pre_foo_bar = echo about to execute command bar from foo with args: ${@}\n    post_foo_bar = echo just executed command bar from foo with args: ${@}\n\n    pre_asdf_uninstall_foo = echo will remove foo version ${1}\n    post_asdf_uninstall_foo = echo removed foo version ${1}\n    ```\n* New shim version meta-data allows shims to not depend on a particular plugin\n  nor on its relative executable path (#431)\n  Upgrading requires shim re-generation and should happen automatically by `asdf-exec`:\n  `rm -rf ~/.asdf/shims/` followed by `asdf reshim`\n* Added lots of tests for shim execution.\n  We now make sure that shim execution obeys plugins hooks like `list-bin-paths` and\n  `exec-path`.\n* Shims now are thin wrappers around `asdf exec` that might be faster\n  for most common use case: (versions on local .tool-versions file) but fallbacks to\n  slower `get_preset_version_for` which takes legacy formats into account.\n* Shim exec recommends which plugins or versions to set when command is not found.\n* `asdf reshim` without arguments now reshims all installed plugins (#407)\n* Add `asdf shim-versions <executable>` to list on which plugins and versions is a command\n  available. (#380, #433)\n* Add documentation on installing dependencies via Spack (#471)\n\nFixed Bugs\n\n* Fix `update` command so it doesn't crash when used on Brew installations (#429, #474, #439, #436)\n\n## 0.6.3\n\nFeatures\n\n* Make `which` command work with any binary included in a plugin installation (#205, #382)\n* Add documentation for documentation website (#274, #396, #422, #423, #427, #430)\n\nFixed Bugs\n\n* Silence errors during tab completion (#404)\n* Remove unused asdf shims directory from `PATH` (#408)\n* Fix issues with update command that prevented updates for installations in custom locations (#411)\n* Fix shellcheck warnings on OSX (#416)\n* Add tests for versions set by environment variables (#417, #327)\n* Continue `list` output even when version is not found (#419)\n* Fixed user paths for fish (#420, #421)\n* Custom exec path tests (#324, #424)\n\n## 0.6.2\n\nFixed Bugs\n\n* Fix `system` logic so shims directory is removed from `PATH` properly (#402, #406)\n* Support `.tool-versions` files that don't end in a newline (#403)\n\n## 0.6.1\n\nFeatures\n\n* Make `where` command default to current version (#389)\n* Optimize code for listing all plugins (#388)\n* Document `$TRAVIS_BUILD_DIR` in the plugin guide (#386)\n* Add `--asdf-tool-version` flag to plugin-test command (#381)\n* Add `-p` flag to `local` command (#377)\n\nFixed Bugs\n\n* Fix behavior of `current` command when multiple versions are set (#401)\n* Fix fish shell init code (#392)\n* Fix `plugin-test` command (#379)\n* Add space before parenthesis in `current` command output (#371)\n\n## 0.6.0\n\nFeatures\n\n* Add support for `ASDF_DATA_DIR` environment variable (#275, #335, #361, #364, #365)\n\nFixed Bugs\n\n* Fix `asdf current` so it works when no versions are installed (#368, #353)\n* Don't try to install system version (#369, #351)\n* Make `resolve_symlink` function work with relative symlinks (#370, #366)\n* Fix version changing code so it preserves symlinks (#329, #337)\n* Fix ShellCheck warnings (#336)\n\n## 0.5.1\n\nFeatures\n\n* Better formatting for `asdf list` output (#330, #331)\n\nFixed Bugs\n\n* Correct environment variable name used for version lookup (#319, #326 #328)\n* Remove unnecessary `cd` in `asdf.sh` (#333, #334)\n* Correct Fish shell load script (#340)\n\n## 0.5.0\n\nFeatures\n\n* Changed exit codes for shims so we use codes with special meanings when possible (#305, #310)\n* Include plugin name in error message if plugin doesn't exist (#315)\n* Add support for custom executable paths (#314)\n* `asdf list` with no arguments should list all installed versions of all plugins (#311)\n\nFixed Bugs\n\n* Print \"No version set\" message to stderr (#309)\n* Fix check for asdf directories in path for Fish shell (#306)\n\n## 0.4.3\n\nFeatures\n\n* Suggest action when no version is set (#291, #293)\n\nFixed Bugs\n\n* Fix issue with asdf not always being added to beginning of `$PATH` (#288, #303, #304)\n* Fix incorrect `ASDF_CONFIG_FILE` environment variable name (#300)\n* Fix `asdf current` so it shows environment variables that are setting versions (#292, 294)\n\n## 0.4.2\n\nFeatures\n\n* Add support for `ASDF_DEFAULT_TOOL_VERSIONS_FILENAME` environment variable (#201, #228)\n* Only add asdf to `PATH` once (#261, #271)\n* Add `--urls` flag to `plugin-list` commands (#273)\n\nFixed Bugs\n\n* Incorrect `grep` command caused version command to look at the wrong tool when reporting the version (#262)\n\n## 0.4.1\n\nFeatures\n\n* `asdf install` will also search for `.tool-versions` in parent directories (#237)\n\nFixed Bugs\n\n* bad use of `sed` caused shims and `.tool-versions` to be duplicated with `-e` (#242, #250)\n* `asdf list` now outputs ref-versions as used on `.tool-versions` file (#243)\n* `asdf update` will explicitly use the `origin` remote when updating tags (#231)\n* All code is now linted by shellcheck (#223)\n* Add test to fail builds if banned commands are found (#251)\n\n## 0.4.0\n\nFeatures\n\n* Add CONTRIBUTING guidelines and GitHub issue and pull request templates (#217)\n* Add `plugin-list-all` command to list plugins from asdf-plugins repo. (#221)\n* `asdf current` shows all current tool versions when given no args (#219)\n* Add asdf-plugin-version metadata to shims (#212)\n* Add release.sh script to automate release of new versions (#220)\n\nFixed Bugs\n\n* Allow spaces on path containing the `.tool-versions` file (#224)\n* Fixed bug in `--version` functionality so it works regardless of how asdf was installed (#198)\n\n## 0.3.0\n\nFeatures\n\n* Add `update` command to make it easier to update asdf to the latest release (#172, #180)\n* Add support for `system` version to allow passthrough to system installed tools (#55, #182)\n\nFixed Bugs\n\n* Set `GREP_OPTIONS` and `GREP_COLORS` variables in util.sh so grep is always invoked with the correct settings (#170)\n* Export `ASDF_DIR` variable so the Zsh plugin can locate asdf if it's in a custom location (#156)\n* Don't add execute permission to files in a plugin's bin directory when adding the plugin (#124, #138, #154)\n\n## 0.2.1\n\nFeatures\n\n* Determine global tool version even when used outside of home directory (#106)\n\nFixed Bugs\n\n* Correct reading of `ref:` and `path:` versions (#112)\n* Remove shims when uninstalling a version or removing a plugin (#122, #123, #125, #128, #131)\n* Add a helpful error message to the install command (#135)\n\n## 0.2.0\n\nFeatures\n\n* Improve plugin API for legacy file support (#87)\n* Unify `asdf local` and `asdf global` version getters as `asdf current` (#83)\n* Rename `asdf which` to `asdf current` (#78)\n\nFixed Bugs\n\n* Fix bug that caused the `local` command to crash when the directory contains whitespace (#90)\n* Misc typo corrections (#93, #99)\n\n## 0.1.0\n\n* First tagged release\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# How to Contribute to asdf\n\nThere are many ways to contribute to `asdf`, thanks for taking the time to read and help.\n\n## `asdf` Core\n\n### Did you find a bug?\n\nEnsure the bug is actually an issue with asdf and not a plugin. If the bug only occurs when using one tool installed by `asdf` and not others it is likely an issue with the **plugin**. Find the plugin repo URL with `asdf plugin list --urls`, browse their repo Issues and if no solution is found open a new Issue there.\n\nEnsure the bug was not already reported in existing [Issues](https://github.com/asdf-vm/asdf/issues). If not, then please [open a new Issue](https://github.com/asdf-vm/asdf/issues/new/choose). Please be as specific as possible when reporting the issue.\n\n### New Features/Proposals\n\nPlease [open a new Feature Request](https://github.com/asdf-vm/asdf/issues/new/choose) to discuss the feature before implementing a solution.\n\n### Developing with the Core\n\nSee [docs/contribute/core.md](docs/contribute/core.md) or on our [Docs Site](https://asdf-vm.com/contribute/core.html).\n\n## Documentation\n\nDocumentation can always be improved! See [docs/contribute/documentation.md](docs/contribute/documentation.md) or on our [Docs Site](https://asdf-vm.com/contribute/documentation.html).\n\n## First-Party Plugins\n\nWe always need help to maintain our plugins. See [docs/contribute/first-party-plugins.md](docs/contribute/first-party-plugins.md) or on our [Docs Site](https://asdf-vm.com/contribute/first-party-plugins.html).\n\n## Create a Plugin?\n\nPlease read the [docs/plugins/create.md](docs/plugins/create.md) or on our [Docs Site](https://asdf-vm.com/plugins/create.html).\n\n## GitHub Action\n\nSee the [asdf actions repo](https://github.com/asdf-vm/actions) for existing Issues, conversations and Contributing Guidelines.\n\n---\n\nThanks for contributing!\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2014 Akash Manohar J\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "Makefile",
    "content": "MAIN_PACKAGE_PATH := ./cmd/asdf\nTARGET_DIR := .\nTARGET := asdf\nLINKER_FLAGS = '-s'\n\n# Not sure what the default location should be for builds\nbuild: # test lint\n\tgo build -ldflags=${LINKER_FLAGS} -o=${TARGET_DIR}/${TARGET} ${MAIN_PACKAGE_PATH}\n\nfmt:\n\tgo fmt ./...\n\tgo run mvdan.cc/gofumpt -l -w .\n\nverify:\n\tgo mod verify\n\ntidy:\n\tgo mod tidy -v\n\naudit: verify vet test\n\ntest:\n\tgo test -coverprofile=/tmp/coverage.out  -bench= -race ./...\n\ncover: test\n\tgo tool cover -html=/tmp/coverage.out\n\nlint: fmt\n\tgo run honnef.co/go/tools/cmd/staticcheck -tests -show-ignored ./...\n\tgo run github.com/mgechev/revive -set_exit_status ./...\n\nvet: fmt\n\tgo vet ./...\n\nrun: build\n\t${TARGET_DIR}/${TARGET}\n\n.PHONY: fmt lint vet build test run\n"
  },
  {
    "path": "README.md",
    "content": "# asdf\n\n[![Lint](https://github.com/asdf-vm/asdf/actions/workflows/lint.yml/badge.svg)](https://github.com/asdf-vm/asdf/actions/workflows/lint.yml) [![Tests](https://github.com/asdf-vm/asdf/actions/workflows/tests.yml/badge.svg)](https://github.com/asdf-vm/asdf/actions/workflows/tests.yml)\n\n**Manage multiple runtime versions with a single CLI tool, extendable via plugins** - [docs at asdf-vm.com](https://asdf-vm.com/)\n\nasdf is a CLI tool that can manage multiple language runtime versions on a per-project basis. It is like `gvm`, `nvm`, `rbenv` & `pyenv` (and more) all in one! Simply install your language's plugin!\n\n## Why use asdf?\n\n- single CLI for multiple languages\n- consistent commands to manage all your languages\n- single global config keeping defaults in one place\n- single `.tool-versions` config file per project\n- support for existing config files `.node-version`, `.nvmrc`, `.ruby-version` for easy migration\n- automatically switches runtime versions as you traverse your directories\n- simple plugin system to add support for your language of choice\n- shell completion available for common shells (Bash, Zsh, Fish, Elvish)\n\n## Documentation\n\n[Please head over to the documentation site for more information](https://asdf-vm.com/)!\n\n- [Getting Started](https://asdf-vm.com/guide/getting-started.html)\n- [All Commands](https://asdf-vm.com/manage/commands.html)\n- [All Plugins](https://github.com/asdf-vm/asdf-plugins)\n- [Create a Plugin](https://asdf-vm.com/plugins/create.html) with our [asdf-plugin-template](https://github.com/asdf-vm/asdf-plugin-template)\n- [asdf GitHub Actions](https://github.com/asdf-vm/actions)\n\n## Contributing\n\nSee [CONTRIBUTING.md in the repo](https://github.com/asdf-vm/asdf/blob/master/CONTRIBUTING.md) or the [Contributing section on the docs site](http://asdf-vm.com/contribute/core.html#initial-setup).\n\n## Community & Questions\n\n- [FAQ](https://asdf-vm.com/more/faq.html)\n- [![GitHub Issues](https://icongr.am/simple/github.svg?color=808080&size=16) GitHub Issues](https://github.com/asdf-vm/asdf/issues): report a bug or raise a feature request to the `asdf` core team\n- [![StackOverflow Tag](https://icongr.am/fontawesome/stack-overflow.svg?size=16&color=808080) StackOverflow Tag](https://stackoverflow.com/questions/tagged/asdf-vm): see existing Q&A for `asdf`. Some of the core team watch this tag in addition to our helpful community\n\n## Ballad of asdf\n\n> Once upon a time there was a programming language<br/>\n> There were many versions of it<br/>\n> So people wrote a version manager for it<br/>\n> To switch between versions for projects<br/>\n> Different, old, new.\n> \n> Then there came more programming languages<br/>\n> So there came more version managers<br/>\n> And many commands for them\n> \n> I installed a lot of them<br/>\n> I learnt a lot of commands\n> \n> Then I said, just one more version manager<br/>\n> Which I will write instead\n> \n> So, there came another version manager<br/>\n> **asdf version manager** - <https://github.com/asdf-vm/asdf>\n> \n> A version manager so extendable<br/>\n> for which anyone can create a plugin<br/>\n> To support their favourite language<br/>\n> No more installing more version managers<br/>\n> Or learning more commands\n\n---\n\n<figure>\n  <blockquote>\n  This was the mail I wrote to a few friends to tell them about the project. Thanks to <a href=\"https://twitter.com/roshanvid\" target=\"_blank\" rel=\"noreferrer\">@roshanvid</a> for suggesting that this go into the readme\n  </blockquote>\n  <figcaption>\n    <a href=\"https://github.com/HashNuke\" target=\"_blank\" rel=\"noreferrer\">@HashNuke</a>\n  </figcaption>\n</figure>\n"
  },
  {
    "path": "SECURITY.md",
    "content": "# Security Policy\n\n**This security policy only applies to asdf core, which is the code contained in\nthis repository. All plugins are the responsibility of their creators and are\nnot covered under this security policy.**\n\n## Supported Versions\n\n<!-- x-release-please-start-version -->\n\n```\n0.18.1\n```\n\n<!-- x-release-please-end -->\n\nTypically only the latest version is maintained. Versions are released\nsequentially, with major, minor, and patch versions incremented as necessary.\n\nIf a vulnerability is found in an old version of asdf, updating to the latest\nversion of asdf will be required.\n\n## Reporting a Vulnerability\n\nTo report a vulnerability please contact one of:\n\n- @Stratus3D via email: see https://stratus3d.com/contact/ for email. PGP is not\n  setup for this email address.\n- @danhper via email: see https://daniel.perez.sh/ for email and PGP key\n- @jthegedus via email: see https://github.com/jthegedus for email. PGP is not\n  setup for this email address.\n"
  },
  {
    "path": "asdf.elv",
    "content": "use path\nuse re\nuse str\n\nvar asdf_dir = ~'/.asdf'\nif (and (has-env ASDF_DIR) (!=s $E:ASDF_DIR '')) {\n  set asdf_dir = $E:ASDF_DIR\n} else {\n  set-env ASDF_DIR $asdf_dir\n}\n\nvar asdf_data_dir = $asdf_dir\nif (and (has-env ASDF_DATA_DIR) (!=s $E:ASDF_DATA_DIR '')) {\n  set asdf_data_dir = $E:ASDF_DATA_DIR\n}\n\n# Add function wrapper so we can export variables\nfn asdf {|command @args|\n  if (==s $command 'shell') {\n    # set environment variables\n    var parts = [($asdf_dir'/bin/asdf' export-shell-version elvish $@args)]\n    if (==s $parts[0] 'set-env') {\n      set-env $parts[1] $parts[2]\n    } elif (==s $parts[0] 'unset-env') {\n      unset-env $parts[1]\n    }\n  } else {\n    # forward other commands to asdf script\n    $asdf_dir'/bin/asdf' $command $@args\n  }\n}\n\nfn match {|argz @pats|\n  var matched = $true\n  if (!= (count $argz) (count $pats)) {\n    set matched = $false\n  } else {\n    for i [(range (count $pats))] {\n      var pat = '^'$pats[$i]'$'\n      var arg = $argz[$i]\n      if (not (re:match $pat $arg)) {\n        set matched = $false\n        break\n      }\n    }\n  }\n  put $matched\n}\n\nfn ls-shims {\n  ls $asdf_data_dir'/shims'\n}\n\nfn ls-executables {\n  # Print all executable files and links in path\n  try {\n    find $@paths '(' -type f -o -type l ')' -print 2>/dev/null | each {|p|\n      try {\n        if (test -x $p) {\n          path:base $p\n        }\n      } catch {\n        # don't fail if permission denied\n      }\n    }\n  } catch {\n    # silence default non-zero exit status\n  }\n}\n\nfn ls-installed-versions {|plugin_name|\n  asdf list $plugin_name | each {|version|\n    put (re:replace '\\s*(.*)\\s*' '${1}' $version)\n  }\n}\n\nfn ls-all-versions {|plugin_name|\n  asdf list-all $plugin_name | each {|version|\n    put (re:replace '\\s*(.*)\\s*' '${1}' $version)\n  }\n}\n\n# Append ~/.asdf/bin and ~/.asdf/shims to PATH\nfor path [\n  $asdf_dir'/bin'\n  $asdf_data_dir'/shims'\n] {\n  if (not (has-value $paths $path)) {\n    set paths = [\n      $path\n      $@paths\n    ]\n  }\n}\n\n# Setup argument completions\nfn arg-completer {|@argz|\n  set argz = $argz[1..-1]  # strip 'asdf' and trailing empty string\n  var num = (count $argz)\n  if (== $num 0) {\n    # list all subcommands\n    find $asdf_dir'/lib/commands' -name 'command-*' | each {|cmd|\n      put (re:replace '.*/command-(.*)\\.bash' '${1}' $cmd)\n    }\n    put 'plugin'\n  } else {\n    if (match $argz 'current') {\n      # asdf current <name>\n      asdf plugin-list\n    } elif (match $argz 'env') {\n      # asdf env <command>\n      ls-shims\n    } elif (match $argz 'env' '.*') {\n      # asdf env <command> [util]\n      ls-executables\n    } elif (match $argz 'exec') {\n      # asdf exec <command>\n      ls-shims\n    } elif (match $argz 'global') {\n      # asdf global <name>\n      asdf plugin-list\n    } elif (match $argz 'global' '.*') {\n      # asdf global <name> <version>\n      ls-installed-versions $argz[-1]\n    } elif (match $argz 'install') {\n      # asdf install <name>\n      asdf plugin-list\n    } elif (match $argz 'install' '.*') {\n      # asdf install <name> <version>\n      ls-all-versions $argz[-1]\n    } elif (match $argz 'install' '.*' '.*') {\n      # asdf install <name> <version> [--keep-download]\n      put '--keep-download'\n    } elif (match $argz 'latest') {\n      # asdf latest <name>\n      asdf plugin-list\n    } elif (match $argz 'latest' '.*') {\n      # asdf latest <name> [<version>]\n      ls-all-versions $argz[-1]\n    } elif (match $argz 'list-all') {\n      # asdf list all <name>\n      asdf plugin-list\n    } elif (match $argz 'list-all' '.*') {\n      # asdf list all <name> [<version>]\n      ls-all-versions $argz[-1]\n    } elif (match $argz 'list') {\n      # asdf list <name>\n      asdf plugin-list\n    } elif (match $argz 'list' '.*') {\n      # asdf list <name> [<version>]\n      ls-installed-versions $argz[-1]\n    } elif (match $argz 'local') {\n      # asdf local <name> [-p|--parent]\n      asdf plugin-list\n      put '-p'\n      put '--parent'\n    } elif (match $argz 'local' '(-p|(--parent))') {\n      # asdf local <name> [-p|--parent] <version>\n      asdf plugin-list\n    } elif (match $argz 'local' '.*') {\n      # asdf local <name> [-p|--parent]\n      # asdf local <name> <version>\n      ls-installed-versions $argz[-1]\n      put '-p'\n      put '--parent'\n    } elif (match $argz 'local' '(-p|(--parent))' '.*') {\n      # asdf local [-p|--parent] <name> <version>\n      ls-installed-versions $argz[-1]\n    } elif (match $argz 'local' '.*' '(-p|(--parent))') {\n      # asdf local <name> [-p|--parent] <version>\n      ls-installed-versions $argz[-2]\n    } elif (match $argz 'local' '.*' '.*') {\n      # asdf local <name> <version> [-p|--parent]\n      put '-p'\n      put '--parent'\n    } elif (or (match $argz 'plugin-add') (match $argz 'plugin' 'add')) {\n      # asdf plugin add <name>\n      asdf plugin-list-all | each {|line|\n        put (re:replace '([^\\s]+)\\s+.*' '${1}' $line)\n      }\n    } elif (or (match $argz 'plugin-list') (match $argz 'plugin' 'list')) {\n      # asdf plugin list\n      put '--urls'\n      put '--refs'\n      put 'all'\n    } elif (or (match $argz 'plugin-push') (match $argz 'plugin' 'push')) {\n      # asdf plugin push <name>\n      asdf plugin-list\n    } elif (or (match $argz 'plugin-remove') (match $argz 'plugin' 'remove')) {\n      # asdf plugin remove <name>\n      asdf plugin-list\n    } elif (and (>= (count $argz) 3) (match $argz[..3] 'plugin-test' '.*' '.*')) {\n      # asdf plugin-test <plugin-name> <plugin-url> [--asdf-tool-version <version>] [--asdf-plugin-gitref <git-ref>] [test-command*]\n      put '--asdf-plugin-gitref'\n      put '--asdf-tool-version'\n      ls-executables\n      ls-shims\n    } elif (and (>= (count $argz) 4) (match $argz[..4] 'plugin' 'test' '.*' '.*')) {\n      # asdf plugin test <plugin-name> <plugin-url> [--asdf-tool-version <version>] [--asdf-plugin-gitref <git-ref>] [test-command*]\n      put '--asdf-plugin-gitref'\n      put '--asdf-tool-version'\n      ls-executables\n      ls-shims\n    } elif (or (match $argz 'plugin-update') (match $argz 'plugin' 'update')) {\n      # asdf plugin update <name>\n      asdf plugin-list\n      put '--all'\n    } elif (match $argz 'plugin') {\n      # list plugin-* subcommands\n      find $asdf_dir'/lib/commands' -name 'command-plugin-*' | each {|cmd|\n        put (re:replace '.*/command-plugin-(.*)\\.bash' '${1}' $cmd)\n      }\n    } elif (match $argz 'reshim') {\n      # asdf reshim <name>\n      asdf plugin-list\n    } elif (match $argz 'reshim' '.*') {\n      # asdf reshim <name> <version>\n      ls-installed-versions $argz[-1]\n    } elif (match $argz 'shim-versions') {\n      # asdf shim-versions <command>\n      ls-shims\n    } elif (match $argz 'uninstall') {\n      # asdf uninstall <name>\n      asdf plugin-list\n    } elif (match $argz 'uninstall' '.*') {\n      # asdf uninstall <name> <version>\n      ls-installed-versions $argz[-1]\n    } elif (match $argz 'update') {\n      if (== $num 1) {\n        # asdf update\n        put '--head'\n      }\n    } elif (match $argz 'where') {\n      # asdf where <name>\n      asdf plugin-list\n    } elif (match $argz 'where' '.*') {\n      # asdf where <name> [<version>]\n      ls-installed-versions $argz[-1]\n    } elif (match $argz 'which') {\n      ls-shims\n    }\n  }\n}\n"
  },
  {
    "path": "asdf.fish",
    "content": "if test -z $ASDF_DIR\n    set ASDF_DIR (builtin realpath --no-symlinks (dirname (status filename)))\nend\nset --export ASDF_DIR $ASDF_DIR\n\nset -l _asdf_bin \"$ASDF_DIR/bin\"\nif test -z $ASDF_DATA_DIR\n    set _asdf_shims \"$HOME/.asdf/shims\"\nelse\n    set _asdf_shims \"$ASDF_DATA_DIR/shims\"\nend\n\n# Do not use fish_add_path (added in Fish 3.2) because it\n# potentially changes the order of items in PATH\nif not contains $_asdf_bin $PATH\n    set -gx --prepend PATH $_asdf_bin\nend\nif not contains $_asdf_shims $PATH\n    set -gx --prepend PATH $_asdf_shims\nend\nset --erase _asdf_bin\nset --erase _asdf_shims\n\n# The asdf function is a wrapper so we can export variables\nfunction asdf\n    set command $argv[1]\n    set -e argv[1]\n\n    switch \"$command\"\n        case shell\n            # Source commands that need to export variables.\n            command asdf export-shell-version fish $argv | source # asdf_allow: source\n        case '*'\n            # Forward other commands to asdf script.\n            command asdf \"$command\" $argv\n    end\nend\n"
  },
  {
    "path": "asdf.nu",
    "content": "def --env configure-asdf [] {\n    $env.ASDF_DIR = (\n      if ($env | get --ignore-errors ASDF_NU_DIR | is-empty) == false {\n        $env.ASDF_NU_DIR\n      }\n      else if ($env | get --ignore-errors ASDF_DIR | is-empty) == false {\n        $env.ASDF_DIR\n      } else {\n        print --stderr \"asdf: Either ASDF_NU_DIR or ASDF_DIR must not be empty\"\n        return\n      }\n    )\n\n    let shims_dir = (\n      if ( $env | get --ignore-errors ASDF_DATA_DIR | is-empty ) {\n        $env.HOME | path join '.asdf'\n      } else {\n        $env.ASDF_DATA_DIR\n      } | path join 'shims'\n    )\n    let asdf_bin_dir = ( $env.ASDF_DIR | path join 'bin' )\n\n    $env.PATH = ( $env.PATH | split row (char esep) | where { |p| $p != $shims_dir } | prepend $shims_dir )\n    $env.PATH = ( $env.PATH | split row (char esep) | where { |p| $p != $asdf_bin_dir } | prepend $asdf_bin_dir )\n}\n\nconfigure-asdf\n\n## Completions\n\nmodule asdf {\n\n    def \"complete asdf sub-commands\" [] {\n        [\n            \"plugin\",\n            \"list\",\n            \"install\",\n            \"uninstall\",\n            \"current\",\n            \"where\",\n            \"which\",\n            \"local\",\n            \"global\",\n            \"shell\",\n            \"latest\",\n            \"help\",\n            \"exec\",\n            \"env\",\n            \"info\",\n            \"reshim\",\n            \"shim-version\",\n            \"update\"\n        ]\n    }\n\n    def \"complete asdf installed\" [] {\n        ^asdf plugin list | lines | each { |line| $line | str trim }\n    }\n\n\n    def \"complete asdf plugin sub-commands\" [] {\n        [\n            \"list\",\n            \"list all\",\n            \"add\",\n            \"remove\",\n            \"update\"\n        ]\n    }\n\n    def \"complete asdf installed plugins\" [] {\n        ^asdf plugin list | lines | each { |line|\n            $line | str trim\n        }\n    }\n\n    def \"complete asdf plugin versions all\" [context: string] {\n        let plugin = $context | str trim | split words | last\n        ^asdf list all $plugin \n        | lines \n        | each { |line| $line | str trim } \n        | prepend \"latest\"\n    }\n\n    def \"complete asdf plugin versions installed\" [context: string] {\n        let plugin = $context | str trim | split words | last\n        let versions = ^asdf list $plugin \n        | lines \n        | each { |line| $line | str trim }\n        | each { |version| if ($version | str starts-with \"*\") {{value: ($version | str substring 1..), description: \"current version\"}} else {{value: $version, description: \"\"}} }\n        \n        let latest = ^asdf latest $plugin | str trim\n\n        if ($versions | get value | any {|el| $el == $latest}) {\n            $versions | prepend {value: \"latest\", description: $\"alias to ($latest)\"}\n        } else {\n            $versions\n        }\n    }\n\n    # ASDF version manager\n    export extern main [\n        subcommand?: string@\"complete asdf sub-commands\"\n    ]\n\n    # Manage plugins\n    export extern \"asdf plugin\" [\n        subcommand?: string@\"complete asdf plugin sub-commands\"\n    ]\n\n    # List installed plugins\n    export def \"asdf plugin list\" [\n        --urls # Show urls\n        --refs # Show refs\n    ] {\n\n        let params = [\n            {name: 'urls', enabled: $urls, flag: '--urls',\n             template: '\\s+?(?P<repository>(?:http[s]?|git).+\\.git|/.+)'}\n            {name: 'refs', enabled: $refs, flag: '--refs',\n             template: '\\s+?(?P<branch>\\w+)\\s+(?P<ref>\\w+)'}\n        ]\n\n        let template = '(?P<name>.+)' + (\n                            $params |\n                            where enabled |\n                            get --ignore-errors template |\n                            str join '' |\n                            str trim\n                        )\n\n        let flags = ($params | where enabled | get --ignore-errors flag | default '' )\n\n        ^asdf plugin list ...$flags | lines | parse -r $template | str trim\n    }\n\n    # list all available plugins\n    export def \"asdf plugin list all\" [] {\n        let template = '(?P<name>.+)\\s+?(?P<installed>[*]?)(?P<repository>(?:git|http|https).+)'\n        let is_installed = { |it| $it.installed == '*' }\n\n        ^asdf plugin list all |\n            lines |\n            parse -r $template |\n            str trim |\n            update installed $is_installed |\n            sort-by name\n    }\n\n    # Add a plugin\n    export extern  \"asdf plugin add\" [\n        name: string # Name of the plugin\n        git_url?: string # Git url of the plugin\n    ]\n\n    # Remove an installed plugin and their package versions\n    export extern \"asdf plugin remove\" [\n        name: string@\"complete asdf installed plugins\" # Name of the plugin\n    ]\n\n    # Update a plugin\n    export extern \"asdf plugin update\" [\n        name: string@\"complete asdf installed plugins\" # Name of the plugin\n        git_ref?: string # Git ref to update the plugin\n    ]\n\n    # Update all plugins to the latest commit\n    export extern \"asdf plugin update --all\" []\n\n    # install a package version\n    export extern \"asdf install\" [\n        name?: string@\"complete asdf installed plugins\" # Name of the package\n        version?: string@\"complete asdf plugin versions all\" # Version of the package or latest\n    ]\n\n\n    # Remove an installed package version\n    export extern \"asdf uninstall\" [\n        name: string@\"complete asdf installed\" # Name of the package\n        version: string@\"complete asdf plugin versions installed\" # Version of the package\n    ]\n\n    # Display current version\n    export extern \"asdf current\" [\n        name?: string@\"complete asdf installed\" # Name of installed version of a package\n    ]\n\n    # Display path of an executable\n    export extern \"asdf which\" [\n        command: string # Name of command\n    ]\n\n    # Display install path for an installed package version\n    export extern \"asdf where\" [\n        name: string@\"complete asdf installed\" # Name of installed package\n        version?: string@\"complete asdf plugin versions installed\" # Version of installed package\n    ]\n\n    # Set the package local version\n    export extern \"asdf local\" [\n        name: string@\"complete asdf installed\" # Name of the package\n        version?: string@\"complete asdf plugin versions installed\" # Version of the package or latest\n    ]\n\n    # Set the package global version\n    export extern \"asdf global\" [\n        name: string@\"complete asdf installed\" # Name of the package\n        version?: string@\"complete asdf plugin versions installed\" # Version of the package or latest\n    ]\n\n    # Set the package to version in the current shell\n    export extern \"asdf shell\" [\n        name: string@\"complete asdf installed\" # Name of the package\n        version?: string@\"complete asdf plugin versions installed\" # Version of the package or latest\n    ]\n\n    # Show latest stable version of a package\n    export extern \"asdf latest\" [\n        name: string@\"complete asdf installed\" # Name of the package\n        version?: string@\"complete asdf plugin versions installed\" # Filter latest stable version from this version\n    ]\n\n    # Show latest stable version for all installed packages\n    export extern \"asdf latest --all\" []\n\n    # List installed package versions\n    export extern \"asdf list\" [\n        name?: string@\"complete asdf installed\" # Name of the package\n        version?: string@\"complete asdf plugin versions installed\" # Filter the version\n    ]\n\n    # List all available package versions\n    export def \"asdf list all\" [\n        name: string@\"complete asdf installed\" # Name of the package\n        version?: string@\"complete asdf plugin versions installed\"=\"\" # Filter the version\n    ]    {\n        ^asdf list all $name $version | lines | parse \"{version}\" | str trim\n    }\n\n    # Show documentation for plugin\n    export extern \"asdf help\" [\n        name: string@\"complete asdf installed\" # Name of the plugin\n        version?: string@\"complete asdf plugin versions installed\" # Version of the plugin\n    ]\n\n    # Execute a command shim for the current version\n    export extern \"asdf exec\" [\n        command: string # Name of the command\n        ...args: any # Arguments to pass to the command\n    ]\n\n    # Run util (default: env) inside the environment used for command shim execution\n    export extern \"asdf env\" [\n        command?: string # Name of the command\n        util?: string = 'env' # Name of util to run\n    ]\n\n    # Show information about OS, Shell and asdf Debug\n    export extern \"asdf info\" []\n\n    # Recreate shims for version package\n    export extern \"asdf reshim\" [\n        name?: string@\"complete asdf installed\" # Name of the package\n        version?: string@\"complete asdf plugin versions installed\" # Version of the package\n    ]\n\n    # List the plugins and versions that provide a command\n    export extern \"asdf shim-version\" [\n        command: string # Name of the command\n    ]\n\n    # Update asdf to the latest version on the stable branch\n    export extern \"asdf update\" []\n\n    # Update asdf to the latest version on the main branch\n    export extern \"asdf update --head\" []\n\n}\n\nuse asdf *\n"
  },
  {
    "path": "asdf.ps1",
    "content": "$Env:ASDF_DIR = $PSScriptRoot\n\n$_asdf_bin = \"$Env:ASDF_DIR/bin\"\nif ($null -eq $ASDF_DATA_DIR -or $ASDF_DATA_DIR -eq '') {\n  $_asdf_shims = \"${env:HOME}/.asdf/shims\"\n}\nelse {\n  $_asdf_shims = \"$ASDF_DATA_DIR/shims\"\n}\n\n$env:PATH = \"${_asdf_bin}:${_asdf_shims}:${env:PATH}\"\n\nif ($env:PATH -cnotlike \"*${_asdf_bin}*\") {\n  $env:PATH = \"_asdf_bin:${env:PATH}\"\n}\nif ($env:PATH -cnotlike \"*${_asdf_shims}*\") {\n  $env:PATH = \"_asdf_shims:${env:PATH}\"\n}\n\nRemove-Variable -Force _asdf_bin, _asdf_shims\n\nfunction asdf {\n  $asdf = $(Get-Command -CommandType Application asdf).Source\n\n  if ($args.Count -gt 0 -and $args[0] -eq 'shell') {\n    Invoke-Expression $(& $asdf 'export-shell-version' pwsh $args[1..($args.Count + -1)])\n  }\n  else {\n    & $asdf $args\n  }\n}\n"
  },
  {
    "path": "asdf.sh",
    "content": "# shellcheck shell=sh\n# shellcheck disable=SC1007\n\n# This file is the entrypoint for all POSIX-compatible shells. If `ASDF_DIR` is\n# not already set, this script is able to calculate it, but only if the shell is\n# either Bash, Zsh, and Ksh. For other shells, `ASDF_DIR` must be manually set.\n\nexport ASDF_DIR=\"${ASDF_DIR:-}\"\n\nif [ -z \"$ASDF_DIR\" ]; then\n  if [ -n \"${BASH_VERSION:-}\" ]; then\n    # Use BASH_SOURCE[0] to obtain the relative path to this source'd file. Since it's\n    # a relative path, 'cd' to its dirname and use '$PWD' to obtain the fullpath.\n    # Use 'builtin cd' to ensure user-defined 'cd()' functions aren't called.\n    # Use variable '_asdf_old_dir' to avoid using subshells.\n\n    _asdf_old_dir=$PWD\n    # shellcheck disable=SC3028,SC3054\n    if ! CDPATH= builtin cd -- \"${BASH_SOURCE[0]%/*}\"; then\n      printf '%s\\n' 'asdf: Error: Failed to cd' >&2\n      unset -v _asdf_old_dir\n      return 1\n    fi\n    ASDF_DIR=$PWD\n    if ! CDPATH= builtin cd -- \"$_asdf_old_dir\"; then\n      printf '%s\\n' 'asdf: Error: Failed to cd' >&2\n      unset -v _asdf_old_dir\n      return 1\n    fi\n    unset -v _asdf_old_dir\n  elif [ -n \"${ZSH_VERSION:-}\" ]; then\n    # Use '%x' to expand to path of current file. It must be prefixed\n    # with '(%):-', so it expands in non-prompt-string contexts.\n\n    # shellcheck disable=SC2296\n    ASDF_DIR=${(%):-%x}\n    ASDF_DIR=${ASDF_DIR%/*}\n  elif [ -n \"${KSH_VERSION:-}\" ] && [ -z \"$PATHSEP\" ]; then\n    # Only the original KornShell (kornshell.com) has a '.sh.file' variable with the path\n    # of the current file. To prevent errors with other variations, such as the MirBSD\n    # Korn shell (mksh), test for 'PATHSEP' which is _not_ set on the original Korn Shell.\n\n    # shellcheck disable=SC2296\n    ASDF_DIR=${.sh.file}\n    ASDF_DIR=${ASDF_DIR%/*}\n  fi\nfi\n\nif [ -z \"$ASDF_DIR\" ]; then\n  printf \"%s\\n\" \"asdf: Error: Source directory could not be calculated. Please set \\$ASDF_DIR manually before sourcing this file.\" >&2\n  return 1\nfi\n\nif [ ! -d \"$ASDF_DIR\" ]; then\n  printf \"%s\\n\" \"asdf: Error: Variable '\\$ASDF_DIR' is not a directory: $ASDF_DIR\" >&2\n  return 1\nfi\n\n_asdf_bin=\"$ASDF_DIR/bin\"\n_asdf_shims=\"${ASDF_DATA_DIR:-$HOME/.asdf}/shims\"\n\n_asdf_should_prepend=no\nif [ -n \"${ASDF_FORCE_PREPEND+x}\" ]; then\n  _asdf_should_prepend=$ASDF_FORCE_PREPEND\nelse\n  # If ASDF_FORCE_PREPEND is not set, then prepend by default on macOS\n  # to workaround `path_helper`.\n  if [ -n \"${BASH_VERSION:-}\" ] || [ -n \"${ZSH_VERSION:-}\" ]; then\n    # shellcheck disable=SC3028\n    case $OSTYPE in\n      darwin*) _asdf_should_prepend=yes ;;\n    esac\n  else\n    if ! _asdf_output=$(uname); then\n      printf \"%s\\n\" \"asdf: Error: Failed to execute 'uname'\" >&2\n      return 1\n    fi\n    if [ \"$_asdf_output\" = 'Darwin' ]; then\n      _asdf_should_prepend=yes\n    fi\n    unset -v _asdf_output\n  fi\nfi\n\n# If prepending is enabled, remove any existing instances of asdf from PATH so\n# the prepending done after is always at the frontmost part of the PATH.\nif [ \"$_asdf_should_prepend\" = 'yes' ]; then\n  if [ -n \"${BASH_VERSION:-}\" ] || [ -n \"${ZSH_VERSION:-}\" ]; then\n    # shellcheck disable=SC3060\n    case \":$PATH:\" in\n      *\":${_asdf_bin}:\"*) PATH=${PATH//$_asdf_bin:/} ;;\n    esac\n    # shellcheck disable=SC3060\n    case \":$PATH:\" in\n      *\":${_asdf_shims}:\"*) PATH=${PATH//$_asdf_shims:/} ;;\n    esac\n  else\n    _path=${PATH}:\n    _new_path=\n    while [ -n \"$_path\" ]; do\n      _part=${_path%%:*}\n      _path=${_path#*:}\n\n      if [ \"$_part\" = \"$_asdf_bin\" ] || [ \"$_part\" = \"$_asdf_shims\" ]; then\n        continue\n      fi\n\n      _new_path=\"$_new_path${_new_path:+:}$_part\"\n    done\n    PATH=$_new_path\n    unset -v _path _new_path _part\n  fi\nfi\nunset -v _asdf_should_prepend\n\ncase \":$PATH:\" in\n  *\":$_asdf_bin:\"*) : ;;\n  *) PATH=\"$_asdf_bin:$PATH\" ;;\nesac\ncase \":$PATH:\" in\n  *\":$_asdf_shims:\"*) : ;;\n  *) PATH=\"$_asdf_shims:$PATH\" ;;\nesac\n\nunset -v _asdf_bin _asdf_shims\n\n# The asdf function is a wrapper so we can export variables\nasdf() {\n  case $1 in\n  \"shell\")\n    if ! shift; then\n      printf '%s\\n' 'asdf: Error: Failed to shift' >&2\n      return 1\n    fi\n\n    # Invoke command that needs to export variables.\n    eval \"$(asdf export-shell-version sh \"$@\")\" # asdf_allow: eval\n    ;;\n  *)\n    # Forward other commands to asdf script.\n    command asdf \"$@\" # asdf_allow: ' asdf '\n    ;;\n  esac\n}\n"
  },
  {
    "path": "ballad-of-asdf.md",
    "content": "# Ballad of asdf\n\n> Once upon a time there was a programming language<br/>\n> There were many versions of it<br/>\n> So people wrote a version manager for it<br/>\n> To switch between versions for projects<br/>\n> Different, old, new.\n\n> Then there came more programming languages<br/>\n> So there came more version managers<br/>\n> And many commands for them\n\n> I installed a lot of them<br/>\n> I learnt a lot of commands\n\n> Then I said, just one more version manager<br/>\n> Which I will write instead\n\n> So, there came another version manager<br/>\n> **asdf version manager** - <https://github.com/HashNuke/asdf>\n\n> A version manager so extendable<br/>\n> for which anyone can create a plugin<br/>\n> To support their favourite language<br/>\n> No more installing more version managers<br/>\n> Or learning more commands\n\n---\n\n*This was the mail I wrote to a few friends to tell them about the project. Thanks to [@roshanvid](https://twitter.com/roshanvid) for suggesting that this go into the readme*\n"
  },
  {
    "path": "bin/private/asdf-exec",
    "content": "#!/usr/bin/env bash\n\n# remove this asdf-exec file when releasing >=0.6.5\nprintf \"asdf is self upgrading shims to new asdf exec ...\\\\n\"\n\nasdf_dir=\"$(dirname \"$(dirname \"$(dirname \"$0\")\")\")\"\n# shellcheck source=lib/utils.bash\n. \"$asdf_dir/lib/utils.bash\"\nrm \"$(asdf_data_dir)\"/shims/*\n\"$asdf_dir\"/bin/asdf reshim\nshim_name=$(basename \"$2\")\n\nprintf \"asdf: now running %s\\\\n\" \"$shim_name\"\n\nexec \"$shim_name\" \"${@:3}\"\n"
  },
  {
    "path": "defaults",
    "content": "# See the docs for explanations: https://asdf-vm.com/manage/configuration.html\n\nlegacy_version_file = no\nuse_release_candidates = no\nalways_keep_download = no\nplugin_repository_last_check_duration = 60\ndisable_plugin_short_name_repository = no\nconcurrency = auto\n"
  },
  {
    "path": "docs/.gitattributes",
    "content": "## GITATTRIBUTES FOR WEB PROJECTS\n#\n# These settings are for any web project.\n#\n# Details per file setting:\n#   text    These files should be normalized (i.e. convert CRLF to LF).\n#   binary  These files are binary and should be left untouched.\n#\n# Note that binary is a macro for -text -diff.\n######################################################################\n\n# Auto detect\n##   Handle line endings automatically for files detected as\n##   text and leave all files detected as binary untouched.\n##   This will handle all files NOT defined below.\n*                 text=auto\n\n# Source code\n*.bash            text eol=lf\n*.bat             text eol=crlf\n*.cmd             text eol=crlf\n*.coffee          text\n*.css             text\n*.htm             text diff=html\n*.html            text diff=html\n*.inc             text\n*.ini             text\n*.js              text\n*.json            text\n*.jsx             text\n*.less            text\n*.ls              text\n*.map             text -diff\n*.od              text\n*.onlydata        text\n*.php             text diff=php\n*.pl              text\n*.ps1             text eol=crlf\n*.py              text diff=python\n*.rb              text diff=ruby\n*.sass            text\n*.scm             text\n*.scss            text diff=css\n*.sh              text eol=lf\n*.sql             text\n*.styl            text\n*.tag             text\n*.ts              text\n*.tsx             text\n*.xml             text\n*.xhtml           text diff=html\n\n# Docker\nDockerfile        text\n\n# Documentation\n*.ipynb           text\n*.markdown        text\n*.md              text\n*.mdwn            text\n*.mdown           text\n*.mkd             text\n*.mkdn            text\n*.mdtxt           text\n*.mdtext          text\n*.txt             text\nAUTHORS           text\nCHANGELOG         text\nCHANGES           text\nCONTRIBUTING      text\nCOPYING           text\ncopyright         text\n*COPYRIGHT*       text\nINSTALL           text\nlicense           text\nLICENSE           text\nNEWS              text\nreadme            text\n*README*          text\nTODO              text\n\n# Templates\n*.dot             text\n*.ejs             text\n*.haml            text\n*.handlebars      text\n*.hbs             text\n*.hbt             text\n*.jade            text\n*.latte           text\n*.mustache        text\n*.njk             text\n*.phtml           text\n*.tmpl            text\n*.tpl             text\n*.twig            text\n*.vue             text\n\n# Configs\n*.cnf             text\n*.conf            text\n*.config          text\n.editorconfig     text\n.env              text\n.gitattributes    text\n.gitconfig        text\n.htaccess         text\n*.lock            text -diff\npackage-lock.json text -diff\n*.toml            text\n*.yaml            text\n*.yml             text\nbrowserslist      text\nMakefile          text\nmakefile          text\n\n# Heroku\nProcfile          text\n\n# Graphics\n*.ai              binary\n*.bmp             binary\n*.eps             binary\n*.gif             binary\n*.gifv            binary\n*.ico             binary\n*.jng             binary\n*.jp2             binary\n*.jpg             binary\n*.jpeg            binary\n*.jpx             binary\n*.jxr             binary\n*.pdf             binary\n*.png             binary\n*.psb             binary\n*.psd             binary\n# SVG treated as an asset (binary) by default.\n*.svg             text\n# If you want to treat it as binary,\n# use the following line instead.\n# *.svg           binary\n*.svgz            binary\n*.tif             binary\n*.tiff            binary\n*.wbmp            binary\n*.webp            binary\n\n# Audio\n*.kar             binary\n*.m4a             binary\n*.mid             binary\n*.midi            binary\n*.mp3             binary\n*.ogg             binary\n*.ra              binary\n\n# Video\n*.3gpp            binary\n*.3gp             binary\n*.as              binary\n*.asf             binary\n*.asx             binary\n*.fla             binary\n*.flv             binary\n*.m4v             binary\n*.mng             binary\n*.mov             binary\n*.mp4             binary\n*.mpeg            binary\n*.mpg             binary\n*.ogv             binary\n*.swc             binary\n*.swf             binary\n*.webm            binary\n\n# Archives\n*.7z              binary\n*.gz              binary\n*.jar             binary\n*.rar             binary\n*.tar             binary\n*.zip             binary\n\n# Fonts\n*.ttf             binary\n*.eot             binary\n*.otf             binary\n*.woff            binary\n*.woff2           binary\n\n# Executables\n*.exe             binary\n*.pyc             binary\n\n# RC files (like .babelrc or .eslintrc)\n*.*rc             text\n\n# Ignore files (like .npmignore or .gitignore)\n*.*ignore         text\n"
  },
  {
    "path": "docs/.gitignore",
    "content": "### Custom ###\n# VitePress build output & cache directory\n.vitepress/cache\n.vitepress/dist\n\n# Created by https://www.toptal.com/developers/gitignore/api/node\n# Edit at https://www.toptal.com/developers/gitignore?templates=node\n\n### Node ###\n# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nlerna-debug.log*\n.pnpm-debug.log*\n\n# Diagnostic reports (https://nodejs.org/api/report.html)\nreport.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n*.lcov\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (https://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\njspm_packages/\n\n# Snowpack dependency directory (https://snowpack.dev/)\nweb_modules/\n\n# TypeScript cache\n*.tsbuildinfo\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Microbundle cache\n.rpt2_cache/\n.rts2_cache_cjs/\n.rts2_cache_es/\n.rts2_cache_umd/\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n.env.test\n.env.production\n\n# parcel-bundler cache (https://parceljs.org/)\n.cache\n.parcel-cache\n\n# Next.js build output\n.next\nout\n\n# Nuxt.js build / generate output\n.nuxt\ndist\n\n# Gatsby files\n.cache/\n# Comment in the public line in if your project uses Gatsby and not Next.js\n# https://nextjs.org/blog/next-9-1#public-directory-support\n# public\n\n# vuepress build output\n.vuepress/.cache\n.vuepress/.temp\n\n# Serverless directories\n.serverless/\n\n# FuseBox cache\n.fusebox/\n\n# DynamoDB Local files\n.dynamodb/\n\n# TernJS port file\n.tern-port\n\n# Stores VSCode versions used for testing VSCode extensions\n.vscode-test\n\n# yarn v2\n.yarn/cache\n.yarn/unplugged\n.yarn/build-state.yml\n.yarn/install-state.gz\n.pnp.*\n\n# End of https://www.toptal.com/developers/gitignore/api/node\n"
  },
  {
    "path": "docs/.prettierignore",
    "content": "# VitePress build output & cache directory\n.vitepress/cache\n.vitepress/dist\nnode_modules\npackage.json\npackage-lock.json\n"
  },
  {
    "path": "docs/.tool-versions",
    "content": "nodejs 22.10.0\n"
  },
  {
    "path": "docs/.vitepress/config.ts",
    "content": "import { defineConfig } from \"vitepress\";\nimport * as navbars from \"./navbars\";\nimport * as sidebars from \"./sidebars\";\n\n// https://vitepress.dev/reference/site-config\nexport default defineConfig({\n  title: \"asdf\",\n  description: \"Manage multiple runtime versions with a single CLI tool\",\n  lastUpdated: true,\n  locales: {\n    root: {\n      label: \"English\",\n      lang: \"en-US\",\n      themeConfig: {\n        nav: navbars.en,\n        sidebar: sidebars.en,\n      },\n    },\n    \"ko-kr\": {\n      label: \"한국어\",\n      lang: \"ko-kr\",\n      themeConfig: {\n        nav: navbars.ko_kr,\n        sidebar: sidebars.ko_kr,\n      },\n    },\n    \"ja-jp\": {\n      label: \"日本語\",\n      lang: \"ja-jp\",\n      themeConfig: {\n        nav: navbars.ja_jp,\n        sidebar: sidebars.ja_jp,\n      },\n    },\n    \"pt-br\": {\n      label: \"Brazilian Portuguese\",\n      lang: \"pr-br\",\n      themeConfig: {\n        nav: navbars.pt_br,\n        sidebar: sidebars.pt_br,\n      },\n    },\n    \"zh-hans\": {\n      label: \"简体中文\",\n      lang: \"zh-hans\",\n      themeConfig: {\n        nav: navbars.zh_hans,\n        sidebar: sidebars.zh_hans,\n      },\n    },\n  },\n  themeConfig: {\n    // https://vitepress.dev/reference/default-theme-config\n    search: {\n      provider: \"local\",\n    },\n    socialLinks: [\n      { icon: \"github\", link: \"https://github.com/asdf-vm/asdf\" },\n      // { icon: \"twitter\", link: \"https://twitter.com/asdf_vm\" },\n    ],\n  },\n});\n"
  },
  {
    "path": "docs/.vitepress/navbars.ts",
    "content": "import path from \"path\";\nimport fs from \"fs\";\nimport process from \"process\";\n\nexport const getVersion = () => {\n  const versionFilepath = path.join(__dirname, \"../../.release-please-manifest.json\");\n  try {\n    const version = JSON.parse(fs.readFileSync(versionFilepath, \"utf8\"))['.'];\n    console.log(`Found version ${version} from ${versionFilepath}`);\n    return version;\n  } catch (error) {\n    console.error(`Failed to find version from file ${versionFilepath}`, error);\n    process.exit(1);\n  }\n};\n\nconst en = [\n  { text: \"Guide\", link: \"/guide/getting-started\" },\n  {\n    text: \"Reference\",\n    link: \"/manage/configuration\",\n  },\n  {\n    text: getVersion(),\n    items: [\n      {\n        text: \"Changelog\",\n        link: \"https://github.com/asdf-vm/asdf/blob/master/CHANGELOG.md\",\n      },\n      { text: \"Contribute\", link: \"/contribute/core\" },\n    ],\n  },\n];\n\nconst ja_jp = [\n  { text: \"ガイド\", link: \"/ja-jp/guide/getting-started\" },\n  {\n    text: \"リファレンス\",\n    link: \"/ja-jp/manage/configuration\",\n  },\n  {\n    text: getVersion(),\n    items: [\n      {\n        text: \"変更履歴\",\n        link: \"https://github.com/asdf-vm/asdf/blob/master/CHANGELOG.md\",\n      },\n      { text: \"コントリビューション\", link: \"/ja-jp/contribute/core\" },\n    ],\n  },\n];\n\nconst ko_kr = [\n  { text: \"가이드\", link: \"/ko-kr/guide/getting-started\" },\n  {\n    text: \"참고자료\",\n    link: \"/ko-kr/manage/configuration\",\n  },\n  {\n    text: getVersion(),\n    items: [\n      {\n        text: \"변동사항\",\n        link: \"https://github.com/asdf-vm/asdf/blob/master/CHANGELOG.md\",\n      },\n      { text: \"기여하기\", link: \"/ko-kr/contribute/core\" },\n    ],\n  },\n];\n\nconst pt_br = [\n  { text: \"Guia\", link: \"/pt-br/guide/getting-started\" },\n  {\n    text: \"Referência\",\n    link: \"/pt-br/manage/configuration\",\n  },\n  {\n    text: getVersion(),\n    items: [\n      {\n        text: \"Changelog\",\n        link: \"https://github.com/asdf-vm/asdf/blob/master/CHANGELOG.md\",\n      },\n      { text: \"Contribute\", link: \"/pt-br/contribute/core\" },\n    ],\n  },\n];\n\nconst zh_hans = [\n  { text: \"指导\", link: \"/zh-hans/guide/getting-started\" },\n  {\n    text: \"参考\",\n    link: \"/zh-hans/manage/configuration\",\n  },\n  {\n    text: getVersion(),\n    items: [\n      {\n        text: \"Changelog\",\n        link: \"https://github.com/asdf-vm/asdf/blob/master/CHANGELOG.md\",\n      },\n      { text: \"如何贡献\", link: \"/zh-hans/contribute/core\" },\n    ],\n  },\n];\n\nexport { en, ko_kr, ja_jp, pt_br, zh_hans };\n"
  },
  {
    "path": "docs/.vitepress/sidebars.ts",
    "content": "const en = [\n  {\n    text: \"Guide\",\n    collapsed: false,\n    items: [\n      { text: \"What is asdf?\", link: \"/guide/introduction\" },\n      { text: \"Getting Started\", link: \"/guide/getting-started\" },\n      { text: \"Getting Started (pre-0.16.0)\", link: \"/guide/getting-started-legacy\" },\n      { text: \"Upgrading to 0.16.0\", link: \"/guide/upgrading-to-v0-16\" },\n\n    ],\n  },\n  {\n    text: \"Usage\",\n    collapsed: false,\n    items: [\n      { text: \"Core\", link: \"/manage/core\" },\n      { text: \"Plugins\", link: \"/manage/plugins\" },\n      { text: \"Versions\", link: \"/manage/versions\" },\n    ],\n  },\n  {\n    text: \"Reference\",\n    collapsed: false,\n    items: [\n      { text: \"Configuration\", link: \"/manage/configuration\" },\n      { text: \"All Commands\", link: \"/manage/commands\" },\n      { text: \"Dependencies\", link: \"/manage/dependencies\" },\n      {\n        text: \"Plugin Shortname Index\",\n        link: \"https://github.com/asdf-vm/asdf-plugins\",\n      },\n    ],\n  },\n  {\n    text: \"Plugins\",\n    collapsed: true,\n    items: [\n      {\n        text: \"Authors\",\n        items: [\n          { text: \"Create a Plugin\", link: \"/plugins/create\" },\n          {\n            text: \"GitHub Plugin Template\",\n            link: \"https://github.com/asdf-vm/asdf-plugin-template\",\n          },\n        ],\n      },\n      {\n        text: \"First Party Plugins\",\n        items: [\n          {\n            text: \"Elixir\",\n            link: \"https://github.com/asdf-vm/asdf-elixir\",\n          },\n          {\n            text: \"Erlang\",\n            link: \"https://github.com/asdf-vm/asdf-erlang\",\n          },\n          {\n            text: \"Node.js\",\n            link: \"https://github.com/asdf-vm/asdf-nodejs\",\n          },\n          {\n            text: \"Ruby\",\n            link: \"https://github.com/asdf-vm/asdf-ruby\",\n          },\n        ],\n      },\n      {\n        text: \"Community Plugins\",\n        items: [\n          {\n            text: \"asdf-community\",\n            link: \"https://github.com/asdf-community\",\n          },\n          {\n            text: \"GitHub Topics Search\",\n            link: \"https://github.com/topics/asdf-plugin\",\n          },\n        ],\n      },\n    ],\n  },\n  {\n    text: \"Questions\",\n    collapsed: true,\n    items: [\n      { text: \"FAQ\", link: \"/more/faq\" },\n      {\n        text: \"GitHub Issues\",\n        link: \"https://github.com/asdf-vm/asdf/issues\",\n      },\n      {\n        text: \"Stack Overflow Tag\",\n        link: \"https://stackoverflow.com/questions/tagged/asdf-vm\",\n      },\n    ],\n  },\n  {\n    text: \"Contribute\",\n    collapsed: true,\n    items: [\n      { text: \"Core asdf\", link: \"/contribute/core\" },\n      { text: \"Documentation\", link: \"/contribute/documentation\" },\n      {\n        text: \"First-Party Plugins\",\n        link: \"/contribute/first-party-plugins\",\n      },\n      { text: \"GitHub Actions\", link: \"/contribute/github-actions\" },\n    ],\n  },\n  { text: \"Community Projects\", link: \"/more/community-projects\" },\n  { text: \"Thanks\", link: \"/more/thanks\" },\n];\n\nconst ko_kr = [\n  {\n    text: \"가이드\",\n    collapsed: false,\n    items: [\n      { text: \"asdf이란?\", link: \"/ko-kr/guide/introduction\" },\n      { text: \"시작하기\", link: \"/ko-kr/guide/getting-started\" },\n    ],\n  },\n  {\n    text: \"사용방법\",\n    collapsed: false,\n    items: [\n      { text: \"코어\", link: \"/ko-kr/manage/core\" },\n      { text: \"플러그인\", link: \"/ko-kr/manage/plugins\" },\n      { text: \"버전\", link: \"/ko-kr/manage/versions\" },\n    ],\n  },\n  {\n    text: \"참고자료\",\n    collapsed: false,\n    items: [\n      { text: \"설정\", link: \"/ko-kr/manage/configuration\" },\n      { text: \"모든 명령어\", link: \"/ko-kr/manage/commands\" },\n      {\n        text: \"플러그인 Shortname 인덱스\",\n        link: \"https://github.com/asdf-vm/asdf-plugins\",\n      },\n    ],\n  },\n  {\n    text: \"플러그인\",\n    collapsed: true,\n    items: [\n      {\n        text: \"저자\",\n        items: [\n          { text: \"플러그인 만들기\", link: \"/ko-kr/plugins/create\" },\n          {\n            text: \"GitHub 플러그인 템플릿\",\n            link: \"https://github.com/asdf-vm/asdf-plugin-template\",\n          },\n        ],\n      },\n      {\n        text: \"공식 플러그인\",\n        items: [\n          {\n            text: \"Elixir\",\n            link: \"https://github.com/asdf-vm/asdf-elixir\",\n          },\n          {\n            text: \"Erlang\",\n            link: \"https://github.com/asdf-vm/asdf-erlang\",\n          },\n          {\n            text: \"Node.js\",\n            link: \"https://github.com/asdf-vm/asdf-nodejs\",\n          },\n          {\n            text: \"Ruby\",\n            link: \"https://github.com/asdf-vm/asdf-ruby\",\n          },\n        ],\n      },\n      {\n        text: \"커뮤니티 플러그인\",\n        items: [\n          {\n            text: \"asdf-community\",\n            link: \"https://github.com/asdf-community\",\n          },\n          {\n            text: \"GitHub 토픽 검색\",\n            link: \"https://github.com/topics/asdf-plugin\",\n          },\n        ],\n      },\n    ],\n  },\n  {\n    text: \"질문\",\n    collapsed: true,\n    items: [\n      { text: \"자주 묻는 질문\", link: \"/ko-kr/more/faq\" },\n      {\n        text: \"GitHub 이슈\",\n        link: \"https://github.com/asdf-vm/asdf/issues\",\n      },\n      {\n        text: \"Stack Overflow 태그\",\n        link: \"https://stackoverflow.com/questions/tagged/asdf-vm\",\n      },\n    ],\n  },\n  {\n    text: \"기여하기\",\n    collapsed: true,\n    items: [\n      { text: \"코어 asdf\", link: \"/ko-kr/contribute/core\" },\n      { text: \"문서\", link: \"/ko-kr/contribute/documentation\" },\n      {\n        text: \"공식 플러그인\",\n        link: \"/ko-kr/contribute/first-party-plugins\",\n      },\n      { text: \"GitHub Actions\", link: \"/ko-kr/contribute/github-actions\" },\n    ],\n  },\n  { text: \"커뮤니티 프로젝트\", link: \"/ko-kr/more/community-projects\" },\n  { text: \"감사인사\", link: \"/ko-kr/more/thanks\" },\n];\n\nconst ja_jp = [\n  {\n    text: \"ガイド\",\n    collapsed: false,\n    items: [\n      { text: \"asdfってなに?\", link: \"/ja-jp/guide/introduction\" },\n      { text: \"はじめよう\", link: \"/ja-jp/guide/getting-started\" },\n      { text: \"はじめよう (0.16.0以前)\", link: \"/ja-jp/guide/getting-started-legacy\" },\n      { text: \"0.16.0にアップグレードする\", link: \"/ja-jp/guide/upgrading-to-v0-16\" },\n    ],\n  },\n  {\n    text: \"使い方\",\n    collapsed: false,\n    items: [\n      { text: \"コア\", link: \"/ja-jp/manage/core\" },\n      { text: \"プラグイン\", link: \"/ja-jp/manage/plugins\" },\n      { text: \"バージョン\", link: \"/ja-jp/manage/versions\" },\n    ],\n  },\n  {\n    text: \"リファレンス\",\n    collapsed: false,\n    items: [\n      { text: \"構成設定\", link: \"/ja-jp/manage/configuration\" },\n      { text: \"すべてのコマンド\", link: \"/ja-jp/manage/commands\" },\n      {\n        text: \"プラグインショートネームの一覧\",\n        link: \"https://github.com/asdf-vm/asdf-plugins\",\n      },\n    ],\n  },\n  {\n    text: \"プラグイン\",\n    collapsed: true,\n    items: [\n      {\n        text: \"開発者向け\",\n        items: [\n          { text: \"プラグインの作成\", link: \"/ja-jp/plugins/create\" },\n          {\n            text: \"GitHubプラグインテンプレート\",\n            link: \"https://github.com/asdf-vm/asdf-plugin-template\",\n          },\n        ],\n      },\n      {\n        text: \"公式プラグイン\",\n        items: [\n          {\n            text: \"Elixir\",\n            link: \"https://github.com/asdf-vm/asdf-elixir\",\n          },\n          {\n            text: \"Erlang\",\n            link: \"https://github.com/asdf-vm/asdf-erlang\",\n          },\n          {\n            text: \"Node.js\",\n            link: \"https://github.com/asdf-vm/asdf-nodejs\",\n          },\n          {\n            text: \"Ruby\",\n            link: \"https://github.com/asdf-vm/asdf-ruby\",\n          },\n        ],\n      },\n      {\n        text: \"コミュニティプラグイン\",\n        items: [\n          {\n            text: \"asdf-community\",\n            link: \"https://github.com/asdf-community\",\n          },\n          {\n            text: \"GitHubトピック検索\",\n            link: \"https://github.com/topics/asdf-plugin\",\n          },\n        ],\n      },\n    ],\n  },\n  {\n    text: \"困ったときは\",\n    collapsed: true,\n    items: [\n      { text: \"FAQ\", link: \"/ja-jp/more/faq\" },\n      {\n        text: \"GitHub イシュー\",\n        link: \"https://github.com/asdf-vm/asdf/issues\",\n      },\n      {\n        text: \"Stack Overflow タグ\",\n        link: \"https://stackoverflow.com/questions/tagged/asdf-vm\",\n      },\n    ],\n  },\n  {\n    text: \"コントリビューション\",\n    collapsed: true,\n    items: [\n      { text: \"asdf コア\", link: \"/ja-jp/contribute/core\" },\n      { text: \"ドキュメント\", link: \"/ja-jp/contribute/documentation\" },\n      {\n        text: \"公式プラグイン\",\n        link: \"/ja-jp/contribute/first-party-plugins\",\n      },\n      { text: \"GitHub Actions\", link: \"/ja-jp/contribute/github-actions\" },\n    ],\n  },\n  { text: \"コミュニティプロジェクト\", link: \"/ja-jp/more/community-projects\" },\n  { text: \"謝辞\", link: \"/ja-jp/more/thanks\" },\n];\n\nconst pt_br = [\n  {\n    text: \"Guia\",\n    collapsed: false,\n    items: [\n      { text: \"O que é asdf?\", link: \"/pt-br/guide/introduction\" },\n      { text: \"Começar\", link: \"/pt-br/guide/getting-started\" },\n    ],\n  },\n  {\n    text: \"Uso\",\n    collapsed: false,\n    items: [\n      { text: \"Essencial\", link: \"/pt-br/manage/core\" },\n      { text: \"Plugins\", link: \"/pt-br/manage/plugins\" },\n      { text: \"Versões\", link: \"/pt-br/manage/versions\" },\n    ],\n  },\n  {\n    text: \"Referência\",\n    collapsed: false,\n    items: [\n      { text: \"Configuração\", link: \"/pt-br/manage/configuration\" },\n      { text: \"Todos os comandos\", link: \"/pt-br/manage/commands\" },\n      {\n        text: \"Plugin Shortname Index\",\n        link: \"https://github.com/asdf-vm/asdf-plugins\",\n      },\n    ],\n  },\n  {\n    text: \"Plugins\",\n    collapsed: true,\n    items: [\n      {\n        text: \"Autoria\",\n        items: [\n          { text: \"Criar um plug-in\", link: \"/pt-br/plugins/create\" },\n          {\n            text: \"GitHub Plugin Template\",\n            link: \"https://github.com/asdf-vm/asdf-plugin-template\",\n          },\n        ],\n      },\n      {\n        text: \"Plug-ins Próprios\",\n        items: [\n          {\n            text: \"Elixir\",\n            link: \"https://github.com/asdf-vm/asdf-elixir\",\n          },\n          {\n            text: \"Erlang\",\n            link: \"https://github.com/asdf-vm/asdf-erlang\",\n          },\n          {\n            text: \"Node.js\",\n            link: \"https://github.com/asdf-vm/asdf-nodejs\",\n          },\n          {\n            text: \"Ruby\",\n            link: \"https://github.com/asdf-vm/asdf-ruby\",\n          },\n        ],\n      },\n      {\n        text: \"Plug-ins da Comunidade\",\n        items: [\n          {\n            text: \"asdf-community\",\n            link: \"https://github.com/asdf-community\",\n          },\n          {\n            text: \"GitHub Topics Search\",\n            link: \"https://github.com/topics/asdf-plugin\",\n          },\n        ],\n      },\n    ],\n  },\n  {\n    text: \"Questões\",\n    collapsed: true,\n    items: [\n      { text: \"Perguntas Frequentes\", link: \"/pt-br/more/faq\" },\n      {\n        text: \"GitHub Issues\",\n        link: \"https://github.com/asdf-vm/asdf/issues\",\n      },\n      {\n        text: \"Stack Overflow Tag\",\n        link: \"https://stackoverflow.com/questions/tagged/asdf-vm\",\n      },\n    ],\n  },\n  {\n    text: \"Contribute\",\n    collapsed: true,\n    items: [\n      { text: \"Essencial asdf\", link: \"/pt-br/contribute/core\" },\n      { text: \"Documentação\", link: \"/pt-br/contribute/documentation\" },\n      {\n        text: \"Plug-ins Próprios\",\n        link: \"/pt-br/contribute/first-party-plugins\",\n      },\n      { text: \"GitHub Actions\", link: \"/pt-br/contribute/github-actions\" },\n    ],\n  },\n  { text: \"Projetos Comunitários\", link: \"/pt-br/more/community-projects\" },\n  { text: \"Créditos\", link: \"/pt-br/more/thanks\" },\n];\n\nconst zh_hans = [\n  {\n    text: \"指导\",\n    collapsed: false,\n    items: [\n      { text: \"什么是 asdf？\", link: \"/zh-hans/guide/introduction\" },\n      { text: \"快速入门\", link: \"/zh-hans/guide/getting-started\" },\n      {\n        text: \"快速入门 (0.16.0 之前)\",\n        link: \"/zh-hans/guide/getting-started-legacy\",\n      },\n      { text: \"升级到 0.16.0\", link: \"/zh-hans/guide/upgrading-to-v0-16\" },\n    ],\n  },\n  {\n    text: \"用法\",\n    collapsed: false,\n    items: [\n      { text: \"核心\", link: \"/zh-hans/manage/core\" },\n      { text: \"插件\", link: \"/zh-hans/manage/plugins\" },\n      { text: \"版本\", link: \"/zh-hans/manage/versions\" },\n    ],\n  },\n  {\n    text: \"参考\",\n    collapsed: false,\n    items: [\n      { text: \"配置\", link: \"/zh-hans/manage/configuration\" },\n      { text: \"所有命令\", link: \"/zh-hans/manage/commands\" },\n      { text: \"依赖\", link: \"/zh-hans/manage/dependencies\" },\n      {\n        text: \"插件缩写索引\",\n        link: \"https://github.com/asdf-vm/asdf-plugins\",\n      },\n    ],\n  },\n  {\n    text: \"插件\",\n    collapsed: true,\n    items: [\n      {\n        text: \"成为作者\",\n        items: [\n          { text: \"创建插件\", link: \"/zh-hans/plugins/create\" },\n          {\n            text: \"GitHub 插件模板\",\n            link: \"https://github.com/asdf-vm/asdf-plugin-template\",\n          },\n        ],\n      },\n      {\n        text: \"官方插件\",\n        items: [\n          {\n            text: \"Elixir\",\n            link: \"https://github.com/asdf-vm/asdf-elixir\",\n          },\n          {\n            text: \"Erlang\",\n            link: \"https://github.com/asdf-vm/asdf-erlang\",\n          },\n          {\n            text: \"Node.js\",\n            link: \"https://github.com/asdf-vm/asdf-nodejs\",\n          },\n          {\n            text: \"Ruby\",\n            link: \"https://github.com/asdf-vm/asdf-ruby\",\n          },\n        ],\n      },\n      {\n        text: \"社区插件\",\n        items: [\n          {\n            text: \"asdf-community\",\n            link: \"https://github.com/asdf-community\",\n          },\n          {\n            text: \"GitHub 主题搜索\",\n            link: \"https://github.com/topics/asdf-plugin\",\n          },\n        ],\n      },\n    ],\n  },\n  {\n    text: \"问题\",\n    collapsed: true,\n    items: [\n      { text: \"经常问的问题\", link: \"/zh-hans/more/faq\" },\n      {\n        text: \"GitHub Issues\",\n        link: \"https://github.com/asdf-vm/asdf/issues\",\n      },\n      {\n        text: \"Stack Overflow Tag\",\n        link: \"https://stackoverflow.com/questions/tagged/asdf-vm\",\n      },\n    ],\n  },\n  {\n    text: \"如何贡献\",\n    collapsed: true,\n    items: [\n      { text: \"核心\", link: \"/zh-hans/contribute/core\" },\n      { text: \"文档\", link: \"/zh-hans/contribute/documentation\" },\n      {\n        text: \"官方插件\",\n        link: \"/zh-hans/contribute/first-party-plugins\",\n      },\n      { text: \"GitHub Actions\", link: \"/zh-hans/contribute/github-actions\" },\n    ],\n  },\n  { text: \"社区项目\", link: \"/zh-hans/more/community-projects\" },\n  { text: \"致谢\", link: \"/zh-hans/more/thanks\" },\n];\n\nexport { en, ko_kr, ja_jp, pt_br, zh_hans };\n"
  },
  {
    "path": "docs/.vitepress/theme/custom.css",
    "content": ":root {\n  --vp-c-brand-1: #b744b8;\n  --vp-c-brand-2: #a379c9;\n  /* TODO: make brand-2 and brand-3 different & set brand-soft */\n  --vp-c-brand-3: #a379c9;\n  /* --vp-c-brand-soft: #fae3ff; */\n}\n"
  },
  {
    "path": "docs/.vitepress/theme/index.ts",
    "content": "import DefaultTheme from \"vitepress/theme\";\nimport \"./custom.css\";\n\nexport default DefaultTheme;\n"
  },
  {
    "path": "docs/CNAME",
    "content": "asdf-vm.com"
  },
  {
    "path": "docs/contribute/core.md",
    "content": "# asdf\n\n`asdf` core contribution guide.\n\n## Initial Setup\n\nFork `asdf` on GitHub and/or Git clone the default branch:\n\n```shell\n# clone your fork\ngit clone https://github.com/<GITHUB_USER>/asdf.git\n# or clone asdf\ngit clone https://github.com/asdf-vm/asdf.git\n```\n\nThe tools for core development are in this repo's `.tool-versions`. If you wish to manage with `asdf` itself, add the plugins:\n\n```shell\nasdf plugin add bats https://github.com/timgluz/asdf-bats.git\nasdf plugin add shellcheck https://github.com/luizm/asdf-shellcheck.git\nasdf plugin add shfmt https://github.com/luizm/asdf-shfmt.git\n```\n\nInstall the versions to develop `asdf` with:\n\n```shell\nasdf install\n```\n\nIt _may_ be useful to not use `asdf` to manage the tools during development on your local machine as you may need to break functionality which would then break your dev tooling. Here's the raw list of tools:\n\n- [bats-core](https://github.com/bats-core/bats-core): Bash Automated Testing System, for unit testing Bash or POSIX compliant scripts.\n- [shellcheck](https://github.com/koalaman/shellcheck): Static analysis tool for shell scripts.\n- [shfmt](https://github.com/mvdan/sh): A shell parser, formatter, and interpreter with bash support; includes shfmt\n\n## Development\n\nIf you want to try out your changes without making change to your installed `asdf`, you can set the `$ASDF_DIR` variable to the path where you cloned the repository, and temporarily prepend the `bin` and `shims` directory of the directory to your path.\n\nIt is best to format, lint and test your code locally before you commit or push to the remote. Use the following scripts/commands:\n\n```shell\n# Lint\n./scripts/lint.bash --check\n\n# Fix & Format\n./scripts/lint.bash --fix\n\n# Test: all tests\n./scripts/test.bash\n\n# Test: for specific command\nbats test/list_commands.bash\n```\n\n::: tip\n\n**Add tests!** - Tests are **required** for new features and speed up review of bug fixes. Please cover new code paths before you create a Pull Request. See [bats-core documentation](https://bats-core.readthedocs.io/en/stable/index.html)\n\n:::\n\n### Gitignore\n\nThe following is the `.gitignore` file in the `asdf-vm/asdf` repository. We ignore project-specific files. Files specific to your OS, tools or workflows should be ignored in your global `.gitignore` configuration, [see here](http://stratus3d.com/blog/2018/06/03/stop-excluding-editor-temp-files-in-gitignore/) for more details.\n\n<<< @/../.gitignore\n\n### `.git-blame-ignore-revs`\n\n`asdf` uses a `.git-blame-ignore-revs` to reduce noise when running a blame. See the [git blame documentation](https://git-scm.com/docs/git-blame) for more information.\n\nUse the file with `git blame` like so:\n\n```sh\ngit blame --ignore-revs-file .git-blame-ignore-revs ./test/install_command.bats\n```\n\nOptionally, configure to use the file on every invocation of `blame` without manually supplying it:\n\n```sh\ngit config blame.ignoreRevsFile .git-blame-ignore-revs\n```\n\nIt is possible to configure IDEs to use this file. For example, when using VSCode (with [GitLens](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens)), write the following to `.vscode/settings.json`:\n\n```json\n{\n  \"gitlens.advanced.blame.customArguments\": [\n    \"--ignore-revs-file\",\n    \".git-blame-ignore-revs\"\n  ]\n}\n```\n\n## Bats Testing\n\nExecute tests locally with:\n\n```shell\n./scripts/test.bash\n```\n\nBefore writing tests **please read**:\n\n- existing tests in `test/`\n- [bats-core documentation](https://bats-core.readthedocs.io/en/stable/index.html)\n- existing Bats settings used in `scripts/test.bash`\n\n### Bats Tips\n\nBats debugging can be difficult at times. Using the TAP output with `-t` flag will enable you to print outputs with the special file descriptor `>&3` during test execution, simplifying debugging. As an example:\n\n```shell\n# test/some_tests.bats\n\nprintf \"%s\\n\" \"Will not be printed during bats test/some_tests.bats\"\nprintf \"%s\\n\" \"Will be printed during bats -t test/some_tests.bats\" >&3\n```\n\nThis is further documented in bats-core [Printing to the Terminal](https://bats-core.readthedocs.io/en/stable/writing-tests.html#printing-to-the-terminal).\n\n## Pull Requests, Releases & Conventional Commits\n\n`asdf` is using an automated release tool called [Release Please](https://github.com/googleapis/release-please) to automatically bump the [SemVer](https://semver.org/) version and generate the [Changelog](https://github.com/asdf-vm/asdf/blob/master/CHANGELOG.md). This information is determined by reading the commit history since the last release.\n\n[Conventional Commit messages](https://www.conventionalcommits.org/) define the format of the Pull Request Title which becomes the commit message format on the default branch. This is enforced with GitHub Action [`amannn/action-semantic-pull-request`](https://github.com/amannn/action-semantic-pull-request).\n\nConventional Commit follows this format:\n\n```\n<type>[optional scope][optional !]: <description>\n\n<!-- examples -->\nfix: some fix\nfeat: a new feature\ndocs: some documentation update\ndocs(website): some change for the website\nfeat!: feature with breaking change\n```\n\nThe full list of `<types>` are: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`.\n\n- `!`: indicates a breaking change\n- `fix`: will create a new SemVer `patch`\n- `feat`: will create a new SemVer `minor`\n- `<type>!`: will create a new SemVer `major`\n\nThe Pull Request Title must follow this format.\n\n::: tip\n\nUse Conventional Commit message format for your Pull Request Title.\n\n:::\n\n## Docker Images\n\nThe [asdf-alpine](https://github.com/vic/asdf-alpine) and [asdf-ubuntu](https://github.com/vic/asdf-ubuntu) projects are an ongoing effort to provide Dockerized images of some asdf tools. You can use these docker images as base for your development servers, or for running your production apps.\n"
  },
  {
    "path": "docs/contribute/documentation.md",
    "content": "# Docs & Site\n\nDocumentation & site contribution guide.\n\n## Initial Setup\n\nFork `asdf` on GitHub and/or Git clone the default branch:\n\n```shell\n# clone your fork\ngit clone https://github.com/<GITHUB_USER>/asdf.git\n# or clone asdf\ngit clone https://github.com/asdf-vm/asdf.git\n```\n\nThe tools for Docs site development are managed with `asdf` in the `docs/.tool-versions`. Add the plugins with:\n\n```shell\nasdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs\n```\n\nInstall the tool version(s) with:\n\n```shell\nasdf install\n```\n\n- [Node.js](https://nodejs.org): JavaScript runtime built on Chrome's V8 JavaScript engine.\n\nInstall Node.js dependencies from `docs/package.json`:\n\n```shell\nnpm install\n```\n\n## Development\n\n[VitePress (v2)](https://vitepress.dev/) is the Static Site Generator (SSG) we use to build the asdf documentation site. It was chosen to replace [Docsify.js](https://docsify.js.org/) and subsequently VuePress as we would like to support an HTML only fallback when users do not have JavaScript available or enabled. This was not possible with Docsify & VitePress quickly supplanted VuePress. Other than this, the feature-set is largely the same, with the focus on writing Markdown files with minimal configuration.\n\n`package.json` contains the scripts required for development:\n\n@[code json{3-5}](../package.json)\n\nTo start the local development server:\n\n```shell\nnpm run dev\n```\n\nFormat the code before committing:\n\n```shell\nnpm run format\n```\n\n## Pull Requests, Releases & Conventional Commits\n\n`asdf` is using an automated release pipeline which relies on Conventional Commits in PR titles. Detailed documentation found in the [core contribution guide](./core.md).\n\nWhen creating a PR for documentation changes please make the PR title with the Conventional Commit type `docs` in the format `docs: <description>`.\n\n## Vitepress\n\nConfiguration of the site is contained within a few TypeScript files with JS Objects used to represent the config. They are:\n\n- `docs/.vitepress/config.js`: the root config file for the site. Read the [VitePress documentation](https://vitepress.dev/reference/site-config) for it's spec.\n\nTo simplify the root config, the larger JS Objects representing the _navbar_ and _sidebar_ configuration have been extracted and separated by their locale. See both in:\n\n- `docs/.vitepress/navbars.js`\n- `docs/.vitepress/sidebars.js`\n\nWith the official documentation for these configs living in the [Default Theme Reference](https://vitepress.dev/reference/default-theme-config).\n\n## I18n\n\nVitePress has first-class support for internationalization. The\nroot config `docs/.vitepress/config.js` defines the supported locales with their URL, title in the selection dropdown menu and navbar/sidebar configs references.\n\nThe navbar/sidebar configs are captured in the aforementioned config files, separated by locale and exported individually.\n\nThe markdown content for each locale must fall under a folder with the same name as the keys for `locales` in the root config. That is:\n\n```js\n// docs/.vitepress/config.js\nexport default defineConfig({\n  ...\n  locales: {\n    root: {\n      label: \"English\",\n        lang: \"en-US\",\n        themeConfig: {\n        nav: navbars.en,\n          sidebar: sidebars.en,\n      },\n    },\n    \"pt-br\": {\n      label: \"Brazilian Portuguese\",\n        lang: \"pr-br\",\n        themeConfig: {\n        nav: navbars.pt_br,\n          sidebar: sidebars.pt_br,\n      },\n    },\n    \"zh-hans\": {\n      label: \"简体中文\",\n        lang: \"zh-hans\",\n        themeConfig: {\n        nav: navbars.zh_hans,\n          sidebar: sidebars.zh_hans,\n      },\n    },\n  },\n})\n```\n\n`/pt-BR/` will require the same set of markdown files located under `docs/pt-BR/`, like so:\n\n```shell\ndocs\n├─ README.md\n├─ foo.md\n├─ nested\n│  └─ README.md\n└─ pt-BR\n   ├─ README.md\n   ├─ foo.md\n   └─ nested\n      └─ README.md\n```\n\nThe [official VitePress i18n documentation](https://vitepress.dev/guide/i18n) goes into more detail.\n"
  },
  {
    "path": "docs/contribute/first-party-plugins.md",
    "content": "# First-Party Plugins\n\nThe asdf core team has authored some plugins relevant to their daily work life. Help is always welcome in maintaining and improving these plugins. See the associated repo for each linked below:\n\n- [Elixir](https://github.com/asdf-vm/asdf-elixir)\n- [Erlang](https://github.com/asdf-vm/asdf-erlang)\n- [Node.js](https://github.com/asdf-vm/asdf-nodejs)\n- [Ruby](https://github.com/asdf-vm/asdf-ruby)\n\nFor community plugins, see:\n\n- [`asdf-community` organisation](https://github.com/asdf-community): A collaborative, community-driven project for long-term maintenance of `asdf` plugins.\n- [`asdf-plugins` shortname repo](https://github.com/asdf-vm/asdf-plugins): Short-name list used by `asdf` core to lookup popular `asdf` plugins.\n- [GitHub `asdf-plugin` topic search](https://github.com/topics/asdf-plugin)\n"
  },
  {
    "path": "docs/contribute/github-actions.md",
    "content": "# GitHub Actions\n\nThanks for your interest, please see the [asdf actions repo](https://github.com/asdf-vm/actions) for their existing Issues, conversations and Contributing Guidelines.\n"
  },
  {
    "path": "docs/guide/getting-started-legacy.md",
    "content": "# Getting Started\n\n`asdf` installation involves:\n\n1. Installing dependencies\n2. Downloading `asdf` core\n3. Installing `asdf`\n4. Installing a plugin for each tool/runtime you wish to manage\n5. Installing a version of the tool/runtime\n6. Setting global and project versions via `.tool-versions` config files\n\n## 1. Install Dependencies\n\nasdf primarily requires `git` & `curl`. Here is a _non-exhaustive_ list of commands to run for _your_ package manager (some might automatically install these tools in later steps).\n\n| OS    | Package Manager | Command                            |\n| ----- | --------------- | ---------------------------------- |\n| linux | Aptitude        | `apt install curl git`             |\n| linux | DNF             | `dnf install curl git`             |\n| linux | Pacman          | `pacman -S curl git`               |\n| linux | Zypper          | `zypper install curl git`          |\n| macOS | Homebrew        | `brew install coreutils curl git`  |\n| macOS | Spack           | `spack install coreutils curl git` |\n\n::: tip Note\n\n`sudo` may be required depending on your system configuration.\n\n:::\n\n## 2. Download asdf\n\n### Official Download\n\n<!-- x-release-please-start-version -->\n\n\n```shell\ngit clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.15.0\n\n```\n\n<!-- x-release-please-end -->\n\n### Community Supported Download Methods\n\nWe highly recommend using the official `git` method.\n\n| Method   | Command                                                                                                                                                             |\n| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Homebrew | `brew install asdf`                                                                                                                                                 |\n| Pacman   | `git clone https://aur.archlinux.org/asdf-vm.git && cd asdf-vm && makepkg -si` or use your preferred [AUR helper](https://wiki.archlinux.org/index.php/AUR_helpers) |\n\n## 3. Install asdf\n\nThere are many different combinations of Shells, OSs & Installation methods all of which affect the configuration here. Expand the selection below that best matches your system.\n\n**macOS users, be sure to read the warning about `path_helper` at the end of this section.**\n\n::: details Bash & Git\n\nAdd the following to `~/.bashrc`:\n\n```shell\n. \"$HOME/.asdf/asdf.sh\"\n```\n\nCompletions must be configured by adding the following to your `.bashrc`:\n\n```shell\n. \"$HOME/.asdf/completions/asdf.bash\"\n```\n\n:::\n\n::: details Bash & Git (macOS)\n\nIf using **macOS Catalina or newer**, the default shell has changed to **ZSH**. Unless changing back to Bash, follow the ZSH instructions.\n\nAdd the following to `~/.bash_profile`:\n\n```shell\n. \"$HOME/.asdf/asdf.sh\"\n```\n\nCompletions must be configured manually with the following entry in your `.bash_profile`:\n\n```shell\n. \"$HOME/.asdf/completions/asdf.bash\"\n```\n\n:::\n\n::: details Bash & Homebrew\n\nAdd `asdf.sh` to your `~/.bashrc` with:\n\n```shell\necho -e \"\\n. \\\"$(brew --prefix asdf)/libexec/asdf.sh\\\"\" >> ~/.bashrc\n```\n\nCompletions will need to be [configured as per Homebrew's instructions](https://docs.brew.sh/Shell-Completion#configuring-completions-in-bash) or with the following:\n\n```shell\necho -e \"\\n. \\\"$(brew --prefix asdf)/etc/bash_completion.d/asdf.bash\\\"\" >> ~/.bashrc\n```\n\n:::\n\n::: details Bash & Homebrew (macOS)\n\nIf using **macOS Catalina or newer**, the default shell has changed to **ZSH**. Unless changing back to Bash, follow the ZSH instructions.\n\nAdd `asdf.sh` to your `~/.bash_profile` with:\n\n```shell\necho -e \"\\n. \\\"$(brew --prefix asdf)/libexec/asdf.sh\\\"\" >> ~/.bash_profile\n```\n\nCompletions will need to be [configured as per Homebrew's instructions](https://docs.brew.sh/Shell-Completion#configuring-completions-in-bash) or with the following:\n\n```shell\necho -e \"\\n. \\\"$(brew --prefix asdf)/etc/bash_completion.d/asdf.bash\\\"\" >> ~/.bash_profile\n```\n\n:::\n\n::: details Bash & Pacman\n\nAdd the following to `~/.bashrc`:\n\n```shell\n. /opt/asdf-vm/asdf.sh\n```\n\n[`bash-completion`](https://wiki.archlinux.org/title/bash#Common_programs_and_options) needs to be installed for the completions to work.\n:::\n\n::: details Fish & Git\n\nAdd the following to `~/.config/fish/config.fish`:\n\n```shell\nsource ~/.asdf/asdf.fish\n```\n\nCompletions must be configured manually with the following command:\n\n```shell\nmkdir -p ~/.config/fish/completions; and ln -s ~/.asdf/completions/asdf.fish ~/.config/fish/completions\n```\n\n:::\n\n::: details Fish & Homebrew\n\nAdd `asdf.fish` to your `~/.config/fish/config.fish` with:\n\n```shell\necho -e \"\\nsource \"(brew --prefix asdf)\"/libexec/asdf.fish\" >> ~/.config/fish/config.fish\n```\n\nCompletions are [handled by Homebrew for the Fish shell](https://docs.brew.sh/Shell-Completion#configuring-completions-in-fish). Friendly!\n:::\n\n::: details Fish & Pacman\n\nAdd the following to `~/.config/fish/config.fish`:\n\n```shell\nsource /opt/asdf-vm/asdf.fish\n```\n\nCompletions are automatically configured on installation by the AUR package.\n:::\n\n::: details Elvish & Git\n\nAdd `asdf.elv` to your `~/.config/elvish/rc.elv` with:\n\n```shell\nmkdir -p ~/.config/elvish/lib; ln -s ~/.asdf/asdf.elv ~/.config/elvish/lib/asdf.elv\necho \"\\n\"'use asdf _asdf; var asdf~ = $_asdf:asdf~' >> ~/.config/elvish/rc.elv\necho \"\\n\"'set edit:completion:arg-completer[asdf] = $_asdf:arg-completer~' >> ~/.config/elvish/rc.elv\n```\n\nCompletions are automatically configured.\n\n:::\n\n::: details Elvish & Homebrew\n\nAdd `asdf.elv` to your `~/.config/elvish/rc.elv` with:\n\n```shell\nmkdir -p ~/.config/elvish/lib; ln -s (brew --prefix asdf)/libexec/asdf.elv ~/.config/elvish/lib/asdf.elv\necho \"\\n\"'use asdf _asdf; var asdf~ = $_asdf:asdf~' >> ~/.config/elvish/rc.elv\necho \"\\n\"'set edit:completion:arg-completer[asdf] = $_asdf:arg-completer~' >> ~/.config/elvish/rc.elv\n```\n\nCompletions are automatically configured.\n:::\n\n::: details Elvish & Pacman\n\nAdd `asdf.elv` to your `~/.config/elvish/rc.elv` with:\n\n```shell\nmkdir -p ~/.config/elvish/lib; ln -s /opt/asdf-vm/asdf.elv ~/.config/elvish/lib/asdf.elv\necho \"\\n\"'use asdf _asdf; var asdf~ = $_asdf:asdf~' >> ~/.config/elvish/rc.elv\necho \"\\n\"'set edit:completion:arg-completer[asdf] = $_asdf:arg-completer~' >> ~/.config/elvish/rc.elv\n```\n\nCompletions are automatically configured.\n:::\n\n::: details ZSH & Git\n\nAdd the following to `~/.zshrc`:\n\n```shell\n. \"$HOME/.asdf/asdf.sh\"\n```\n\n**OR** use a ZSH Framework plugin like [asdf for oh-my-zsh](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/asdf) which will source this script and setup completions.\n\nCompletions are configured by either a ZSH Framework `asdf` plugin or by adding the following to your `.zshrc`:\n\n```shell\n# append completions to fpath\nfpath=(${ASDF_DIR}/completions $fpath)\n# initialise completions with ZSH's compinit\nautoload -Uz compinit && compinit\n```\n\n- if you are using a custom `compinit` setup, ensure `compinit` is below your sourcing of `asdf.sh`\n- if you are using a custom `compinit` setup with a ZSH Framework, ensure `compinit` is below your sourcing of the framework\n\n:::\n\n::: details ZSH & Homebrew\n\nAdd `asdf.sh` to your `~/.zshrc` with:\n\n```shell\necho -e \"\\n. $(brew --prefix asdf)/libexec/asdf.sh\" >> ${ZDOTDIR:-~}/.zshrc\n```\n\n**OR** use a ZSH Framework plugin like [asdf for oh-my-zsh](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/asdf) which will source this script and setup completions.\n\nCompletions are configured by either a ZSH Framework `asdf` or will need to be [configured as per Homebrew's instructions](https://docs.brew.sh/Shell-Completion#configuring-completions-in-zsh). If you are using a ZSH Framework the associated plugin for asdf may need to be updated to use the new ZSH completions properly via `fpath`. The Oh-My-ZSH asdf plugin is yet to be updated, see [ohmyzsh/ohmyzsh#8837](https://github.com/ohmyzsh/ohmyzsh/pull/8837).\n:::\n\n::: details ZSH & Pacman\n\nAdd the following to `~/.zshrc`:\n\n```shell\n. /opt/asdf-vm/asdf.sh\n```\n\nCompletions are placed in a ZSH friendly location, but [ZSH must be configured to use the autocompletions](https://wiki.archlinux.org/index.php/zsh#Command_completion).\n:::\n\n::: details PowerShell Core & Git\n\nAdd the following to `~/.config/powershell/profile.ps1`:\n\n```shell\n. \"$HOME/.asdf/asdf.ps1\"\n```\n\n:::\n\n::: details PowerShell Core & Homebrew\n\nAdd `asdf.sh` to your `~/.config/powershell/profile.ps1` with:\n\n```shell\necho -e \"\\n. \\\"$(brew --prefix asdf)/libexec/asdf.ps1\\\"\" >> ~/.config/powershell/profile.ps1\n```\n\n:::\n\n::: details PowerShell Core & Pacman\n\nAdd the following to `~/.config/powershell/profile.ps1`:\n\n```shell\n. /opt/asdf-vm/asdf.ps1\n```\n\n:::\n\n::: details Nushell & Git\n\nAdd `asdf.nu` to your `~/.config/nushell/config.nu` with:\n\n```shell\n\"\\n$env.ASDF_DIR = ($env.HOME | path join '.asdf')\\n source \" + ($env.HOME | path join '.asdf/asdf.nu') | save --append $nu.config-path\n```\n\nCompletions are automatically configured\n:::\n\n::: details Nushell & Homebrew\n\nAdd `asdf.nu` to your `~/.config/nushell/config.nu` with:\n\n```shell\n\"\\n$env.ASDF_DIR = (brew --prefix asdf | str trim | into string | path join 'libexec')\\n source \" +  (brew --prefix asdf | str trim | into string | path join 'libexec/asdf.nu') | save --append $nu.config-path\n```\n\nCompletions are automatically configured\n:::\n\n::: details Nushell & Pacman\n\nAdd `asdf.nu` to your `~/.config/nushell/config.nu` with:\n\n```shell\n\"\\n$env.ASDF_DIR = '/opt/asdf-vm/'\\n source /opt/asdf-vm/asdf.nu\" | save --append $nu.config-path\n```\n\nCompletions are automatically configured.\n:::\n\n::: details POSIX Shell & Git\n\nAdd the following to `~/.profile`:\n\n```shell\nexport ASDF_DIR=\"$HOME/.asdf\"\n. \"$HOME/.asdf/asdf.sh\"\n```\n\n:::\n\n::: details POSIX Shell & Homebrew\n\nAdd `asdf.sh` to your `~/.profile` with:\n\n```shell\necho -e \"\\nexport ASDF_DIR=\\\"$(brew --prefix asdf)/libexec/asdf.sh\\\"\" >> ~/.profile\necho -e \"\\n. \\\"$(brew --prefix asdf)/libexec/asdf.sh\\\"\" >> ~/.profile\n```\n\n:::\n\n::: details POSIX Shell & Pacman\n\nAdd the following to `~/.profile`:\n\n```shell\nexport ASDF_DIR=\"/opt/asdf-vm\"\n. /opt/asdf-vm/asdf.sh\n```\n\n:::\n\n`asdf` scripts need to be sourced **after** you have set your `$PATH` and **after** you have sourced your framework (oh-my-zsh etc).\n\n::: warning\nOn macOS, starting a Bash or Zsh shell automatically calls a utility called `path_helper`. `path_helper` can rearrange items in `PATH` (and `MANPATH`), causing inconsistent behavior for tools that require specific ordering. To workaround this, `asdf` on macOS defaults to forcibly adding its `PATH`-entries to the front (taking highest priority). This is controllable with the `ASDF_FORCE_PREPEND` variable.\n:::\n\nRestart your shell so that `PATH` changes take effect. Opening a new terminal tab will usually do it.\n\n## Core Installation Complete!\n\nThis completes the installation of the `asdf` core :tada:\n\n`asdf` is only useful once you install a **plugin**, install a **tool** and manage its **versions**. Continue the guide below to learn how to do this.\n\n## 4. Install a Plugin\n\nFor demonstration purposes we will install & set [Node.js](https://nodejs.org/) via the [`asdf-nodejs`](https://github.com/asdf-vm/asdf-nodejs/) plugin.\n\n### Plugin Dependencies\n\nEach plugin has dependencies so we need to check the plugin repo where they should be listed. For `asdf-nodejs` they are:\n\n| OS                             | Dependency Installation                 |\n| ------------------------------ | --------------------------------------- |\n| Debian                         | `apt-get install dirmngr gpg curl gawk` |\n| CentOS/ Rocky Linux/ AlmaLinux | `yum install gnupg2 curl gawk`          |\n| macOS                          | `brew install gpg gawk`                 |\n\nWe should install dependencies first as some Plugins have post-install hooks.\n\n### Install the Plugin\n\n```shell\nasdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs.git\n```\n\n## 5. Install a Version\n\nNow we have a plugin for Node.js we can install a version of the tool.\n\nWe can see which versions are available with `asdf list all nodejs` or a subset of versions with `asdf list all nodejs 14`.\n\nWe will just install the `latest` available version:\n\n```shell\nasdf install nodejs latest\n```\n\n::: tip Note\n`asdf` enforces exact versions. `latest` is a helper throughout `asdf` that will resolve to the actual version number at the time of execution.\n:::\n\n## 6. Set a Version\n\n`asdf` performs a version lookup of a tool in all `.tool-versions` files from the current working directory up to the `$HOME` directory. The lookup occurs just-in-time when you execute a tool that `asdf` manages.\n\n::: warning\nWithout a version listed for a tool execution of the tool will **error**. `asdf current` will show you the tool & version resolution, or absence of, from your current directory so you can observe which tools will fail to execute.\n:::\n\n### Global\n\nGlobal defaults are managed in `$HOME/.tool-versions`. Set a global version with:\n\n```shell\nasdf global nodejs latest\n```\n\n`$HOME/.tool-versions` will then look like:\n\n```\nnodejs 16.5.0\n```\n\nSome OSs already have tools installed that are managed by the system and not `asdf`, `python` is a common example. You need to tell `asdf` to pass the management back to the system. The [Versions reference section](/manage/versions.md) will guide you.\n\n### Local\n\nLocal versions are defined in the `$PWD/.tool-versions` file (your current working directory). Usually, this will be the Git repository for a project. When in your desired directory execute:\n\n```shell\nasdf local nodejs latest\n```\n\n`$PWD/.tool-versions` will then look like:\n\n```\nnodejs 16.5.0\n```\n\n### Using Existing Tool Version Files\n\n`asdf` supports the migration from existing version files from other version managers. Eg: `.ruby-version` for the case of `rbenv`. This is supported on a per-plugin basis.\n\n[`asdf-nodejs`](https://github.com/asdf-vm/asdf-nodejs/) supports this via both `.nvmrc` and `.node-version` files. To enable this, add the following to your `asdf` configuration file `$HOME/.asdfrc`:\n\n```\nlegacy_version_file = yes\n```\n\nSee the [configuration](/manage/configuration.md) reference page for more config options.\n\n## Guide Complete!\n\nThat completes the Getting Started guide for `asdf` :tada: You can now manage `nodejs` versions for your project. Follow similar steps for each type of tool in your project!\n\n`asdf` has many more commands to become familiar with, you can see them all by running `asdf --help` or `asdf`. The core of the commands are broken into three categories:\n\n- [core `asdf`](/manage/core.md)\n- [plugins](/manage/plugins.md)\n- [versions (of tools)](/manage/versions.md)\n"
  },
  {
    "path": "docs/guide/getting-started.md",
    "content": "# Getting Started\n\n## 1. Install asdf\n\nasdf can be installed in several different ways:\n\n::: details With Package Manager - **Recommended**\n\n| Package Manager   | Command                                                                                                                                                             |\n| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Homebrew | `brew install asdf`                                                                                                                                                 |\n| Zypper   | `zypper install asdf`                                                                                                                                               |\n| Pacman   | `git clone https://aur.archlinux.org/asdf-vm.git && cd asdf-vm && makepkg -si` or use your preferred [AUR helper](https://wiki.archlinux.org/index.php/AUR_helpers) |\n\n:::\n\n:::: details Download Pre-Compiled Binary - **Easy**\n\n<!--@include: @/parts/install-dependencies.md-->\n\n##### Install asdf\n\n1. Visit https://github.com/asdf-vm/asdf/releases and download the appropriate archive for your operating system/architecture combination.\n2. Extract the `asdf` binary in the archive into a directory on your `$PATH`.\n3. Verify `asdf` is on your shell's `$PATH` by running `type -a asdf`. The directory you placed the `asdf` binary in should be listed on the first line of the output from `type`. If it is not that means step #2 was not completed correctly.\n\n::::\n\n:::: details With `go install`\n\n<!--@include: @/parts/install-dependencies.md-->\n\n##### Install asdf\n\n<!-- x-release-please-start-version -->\n1. [Install Go](https://go.dev/doc/install)\n2. Run `go install github.com/asdf-vm/asdf/cmd/asdf@v0.18.1`\n<!-- x-release-please-end -->\n\n::::\n\n:::: details Build from Source\n\n<!--@include: @/parts/install-dependencies.md-->\n\n##### Install asdf\n\n<!-- x-release-please-start-version -->\n1. Clone the asdf repository:\n  ```shell\n  git clone https://github.com/asdf-vm/asdf.git --branch v0.18.1\n  ```\n<!-- x-release-please-end -->\n2. Run `make`\n3. Copy the `asdf` binary into a directory on your `$PATH`.\n4. Verify `asdf` is on your shell's `$PATH` by running `type -a asdf`. The directory you placed the `asdf` binary in should be listed on the first line of the output from `type`. If it is not that means step #3 was not completed correctly.\n\n::::\n\n## 2. Configure asdf\n\n::: tip Note\nMost users **DO NOT** need to customize the location that asdf writes plugin,\ninstall, and shim data to. However, if `$HOME/.asdf` isn't the directory you\nwant asdf writing to, you can change it. Specify the directory by exporting\na variable named `ASDF_DATA_DIR` in your shell's RC file.\n:::\n\nThere are many different combinations of Shells, OSs & Installation methods all of which affect the configuration here. Expand the selection below that best matches your system.\n\n::: details Bash\n\n**macOS Catalina or newer**: The default shell has changed to **ZSH**. Unless changing back to Bash, follow the ZSH instructions.\n\n**Pacman**: [`bash-completion`](https://wiki.archlinux.org/title/bash#Common_programs_and_options) needs to be installed for the completions to work.\n\n##### Add shims directory to path (required)\n\nAdd the following to `~/.bash_profile`:\n```shell\nexport PATH=\"${ASDF_DATA_DIR:-$HOME/.asdf}/shims:$PATH\"\n```\n\n###### Custom data directory (optional)\n\nAdd the following to `~/.bash_profile` above the line you added above:\n\n```shell\nexport ASDF_DATA_DIR=\"/your/custom/data/dir\"\n```\n\n##### Set up shell completions (optional)\n\nCompletions must be configured by adding the following to your `.bashrc`:\n\n```shell\n. <(asdf completion bash)\n```\n\n:::\n\n::: details Fish\n\n##### Add shims directory to path (required)\n\nAdd the following to `~/.config/fish/config.fish`:\n\n```shell\n# ASDF configuration code\nif test -z $ASDF_DATA_DIR\n    set _asdf_shims \"$HOME/.asdf/shims\"\nelse\n    set _asdf_shims \"$ASDF_DATA_DIR/shims\"\nend\n\n# Do not use fish_add_path (added in Fish 3.2) because it\n# potentially changes the order of items in PATH\nif not contains $_asdf_shims $PATH\n    set -gx --prepend PATH $_asdf_shims\nend\nset --erase _asdf_shims\n```\n\n###### Custom data directory (optional)\n\n**Pacman**: Completions are automatically configured on installation by the AUR package.\n\nAdd the following to `~/.config/fish/config.fish` above the lines you added above:\n\n```shell\nset -gx --prepend ASDF_DATA_DIR \"/your/custom/data/dir\"\n```\n\n##### Set up shell completions (optional)\n\nCompletions must be configured manually with the following command:\n\n```shell\n$ asdf completion fish > ~/.config/fish/completions/asdf.fish\n```\n\n:::\n\n::: details Elvish\n\n##### Add shims directory to path (required)\n\nAdd the following to `~/.config/elvish/rc.elv`:\n\n```shell\nvar asdf_data_dir = ~'/.asdf'\nif (and (has-env ASDF_DATA_DIR) (!=s $E:ASDF_DATA_DIR '')) {\n  set asdf_data_dir = $E:ASDF_DATA_DIR\n}\n\nif (not (has-value $paths $asdf_data_dir'/shims')) {\n  set paths = [$path $@paths]\n}\n```\n\n###### Custom data directory (optional)\n\nChange the following line in the above snippet to set a custom data directory:\n\n```diff\n-var asdf_data_dir = ~'/.asdf'\n+var asdf_data_dir = '/your/custom/data/dir'\n```\n\n##### Set up shell completions (optional)\n\n```shell\n$ asdf completion elvish >> ~/.config/elvish/rc.elv\n$ echo \"\\n\"'set edit:completion:arg-completer[asdf] = $_asdf:arg-completer~' >> ~/.config/elvish/rc.elv\n```\n\n:::\n\n::: details ZSH\n\n**Pacman**: Completions are placed in a ZSH friendly location, but [ZSH must be configured to use the autocompletions](https://wiki.archlinux.org/index.php/zsh#Command_completion).\n\n##### Add shims directory to path (required)\n\nAdd the following to `~/.zshrc`:\n```shell\nexport PATH=\"${ASDF_DATA_DIR:-$HOME/.asdf}/shims:$PATH\"\n```\n\n###### Custom data directory (optional)\n\nAdd the following to `~/.zshrc` above the line you added above:\n\n```shell\nexport ASDF_DATA_DIR=\"/your/custom/data/dir\"\n```\n\n##### Set up shell completions (optional)\n\nCompletions are configured by either a ZSH Framework `asdf` plugin (like [asdf for oh-my-zsh](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/asdf)) or by doing the following:\n\n```shell\n$ mkdir -p \"${ASDF_DATA_DIR:-$HOME/.asdf}/completions\"\n$ asdf completion zsh > \"${ASDF_DATA_DIR:-$HOME/.asdf}/completions/_asdf\"\n```\n\nThen add the following to your `.zshrc`:\n\n```shell\n# append completions to fpath\nfpath=(${ASDF_DATA_DIR:-$HOME/.asdf}/completions $fpath)\n# initialise completions with ZSH's compinit\nautoload -Uz compinit && compinit\n```\n\n**Note**\n\nIf you are using a custom `compinit` setup with a ZSH Framework, ensure `compinit` is below your sourcing of the framework\n\nCompletions are configured by either a ZSH Framework `asdf` or will need to be [configured as per Homebrew's instructions](https://docs.brew.sh/Shell-Completion#configuring-completions-in-zsh). If you are using a ZSH Framework the associated plugin for asdf may need to be updated to use the new ZSH completions properly via `fpath`. The Oh-My-ZSH asdf plugin is yet to be updated, see [ohmyzsh/ohmyzsh#8837](https://github.com/ohmyzsh/ohmyzsh/pull/8837).\n:::\n\n::: details PowerShell Core\n\n##### Add shims directory to path (required)\n\nAdd the following to `~/.config/powershell/profile.ps1`:\n```shell\n# Determine the location of the shims directory\nif ($null -eq $ASDF_DATA_DIR -or $ASDF_DATA_DIR -eq '') {\n  $_asdf_shims = \"${env:HOME}/.asdf/shims\"\n}\nelse {\n  $_asdf_shims = \"$ASDF_DATA_DIR/shims\"\n}\n\n# Then add it to path\n$env:PATH = \"${_asdf_shims}:${env:PATH}\"\n```\n\n###### Custom data directory (optional)\n\nAdd the following to `~/.config/powershell/profile.ps1` above the snippet you added above:\n\n```shell\n$env:ASDF_DATA_DIR = \"/your/custom/data/dir\"\n```\n\nShell completions not available for PowerShell\n\n:::\n\n::: details Nushell\n\n##### Add shims directory to path (required)\n\nAdd the following to `~/.config/nushell/config.nu`:\n\n```shell\nlet shims_dir = (\n  if ( $env | get --ignore-errors ASDF_DATA_DIR | is-empty ) {\n    $env.HOME | path join '.asdf'\n  } else {\n    $env.ASDF_DATA_DIR\n  } | path join 'shims'\n)\n$env.PATH = ( $env.PATH | split row (char esep) | where { |p| $p != $shims_dir } | prepend $shims_dir )\n```\n\n###### Custom data directory (optional)\n\nAdd the following to `~/.config/nushell/config.nu` above the line you added above:\n\n```shell\n$env.ASDF_DATA_DIR = \"/your/custom/data/dir\"\n```\n\n##### Set up shell completions (optional)\n\n```shell\n# If you've not customized the asdf data directory:\n$ mkdir $\"($env.HOME)/.asdf/completions\"\n$ asdf completion nushell | save $\"($env.HOME)/.asdf/completions/nushell.nu\"\n\n# If you have customized the data directory by setting ASDF_DATA_DIR:\n$ mkdir $\"($env.ASDF_DATA_DIR)/completions\"\n$ asdf completion nushell | save $\"($env.ASDF_DATA_DIR)/completions/nushell.nu\"\n```\n\nThen add the following to `~/.config/nushell/config.nu`:\n\n```shell\nlet asdf_data_dir = (\n  if ( $env | get --ignore-errors ASDF_DATA_DIR | is-empty ) {\n    $env.HOME | path join '.asdf'\n  } else {\n    $env.ASDF_DATA_DIR\n  }\n)\n. \"$asdf_data_dir/completions/nushell.nu\"\n```\n\n:::\n\n::: details POSIX Shell\n\n##### Add shims directory to path (required)\n\nAdd the following to `~/.profile`:\n```shell\nexport PATH=\"${ASDF_DATA_DIR:-$HOME/.asdf}/shims:$PATH\"\n```\n\n###### Custom data directory (optional)\n\nAdd the following to `~/.profile` above the line you added above:\n\n```shell\nexport ASDF_DATA_DIR=\"/your/custom/data/dir\"\n```\n\n:::\n\n`asdf` scripts need to be sourced **after** you have set your `$PATH` and **after** you have sourced your framework (oh-my-zsh etc).\n\nRestart your shell so that `PATH` changes take effect. Opening a new terminal tab will usually do it.\n\n\n## Core Installation Complete!\n\nThis completes the installation of the `asdf` core :tada:\n\n`asdf` is only useful once you install a **plugin**, install a **tool** and manage its **versions**. Continue the guide below to learn how to do this.\n\n## 4. Install a Plugin\n\nFor demonstration purposes we will install & set [Node.js](https://nodejs.org/) via the [`asdf-nodejs`](https://github.com/asdf-vm/asdf-nodejs/) plugin.\n\n### Plugin Dependencies\n\nEach plugin has dependencies so we need to check the plugin repo where they should be listed. For `asdf-nodejs` they are:\n\n| OS                             | Dependency Installation                 |\n| ------------------------------ | --------------------------------------- |\n| Debian                         | `apt-get install dirmngr gpg curl gawk` |\n| CentOS/ Rocky Linux/ AlmaLinux | `yum install gnupg2 curl gawk`          |\n| macOS                          | `brew install gpg gawk`                 |\n\nWe should install dependencies first as some Plugins have post-install hooks.\n\n### Install the Plugin\n\n```shell\nasdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs.git\n```\n\n## 5. Install a Version\n\nNow we have a plugin for Node.js we can install a version of the tool.\n\nWe can see which versions are available with `asdf list all nodejs` or a subset of versions with `asdf list all nodejs 14`.\n\nWe will just install the `latest` available version:\n\n```shell\nasdf install nodejs latest\n```\n\n::: tip Note\n`asdf` enforces exact versions. `latest` is a helper throughout `asdf` that will resolve to the actual version number at the time of execution.\n:::\n\n## 6. Set a Version\n\n`asdf` performs a version lookup of a tool in all `.tool-versions` files from the current working directory up to the `$HOME` directory. The lookup occurs just-in-time when you execute a tool that `asdf` manages.\n\n::: warning\nWithout a version listed for a tool execution of the tool will **error**. `asdf current` will show you the tool & version resolution, or absence of, from your current directory so you can observe which tools will fail to execute.\n:::\n\nBecause asdf looks for a `.tool-versions` file in the current directory first, and if the file is not found it then climbs up the file tree looking for a `.tool-versions` in a parent directory until it finds one. If no `.tool-versions` file is found the version resolution process will fail and an error will be printed.\n\nIf you want to set a default version that will apply to all directories you work in you can set a version in `$HOME/.tool-versions`. Any directory under your home directory will have that same version set, unless a particular directory sets another version.\n\n```shell\nasdf set -u nodejs 16.5.0\n```\n\n`$HOME/.tool-versions` will then look like:\n\n```\nnodejs 16.5.0\n```\n\nSome OSs already have tools installed that are managed by the system and not `asdf`, `python` is a common example. You need to tell `asdf` to pass the management back to the system. The [Versions reference section](/manage/versions.md) will guide you.\n\nThe first place asdf looks for a version is your current working directory (`$PWD/.tool-versions`). This may be a directory containing a source code or Git repository for a project. When in your desired directory execute you can use `asdf set` to set the version:\n\n```shell\nasdf set nodejs 16.5.0\n```\n\n`$PWD/.tool-versions` will then look like:\n\n```\nnodejs 16.5.0\n```\n\n### Using Existing Tool Version Files\n\n`asdf` supports the migration from existing version files from other version managers. Eg: `.ruby-version` for the case of `rbenv`. This is supported on a per-plugin basis.\n\n[`asdf-nodejs`](https://github.com/asdf-vm/asdf-nodejs/) supports this via both `.nvmrc` and `.node-version` files. To enable this, add the following to your `asdf` configuration file `$HOME/.asdfrc`:\n\n```\nlegacy_version_file = yes\n```\n\nSee the [configuration](/manage/configuration.md) reference page for more config options.\n\n## Guide Complete!\n\nThat completes the Getting Started guide for `asdf` :tada: You can now manage `nodejs` versions for your project. Follow similar steps for each type of tool in your project!\n\n`asdf` has many more commands to become familiar with, you can see them all by running `asdf --help` or `asdf`. The core of the commands are broken into three categories:\n\n- [core `asdf`](/manage/core.md)\n- [plugins](/manage/plugins.md)\n- [versions (of tools)](/manage/versions.md)\n"
  },
  {
    "path": "docs/guide/introduction.md",
    "content": "# Introduction\n\n`asdf` is a tool version manager. All tool version definitions are contained within one file (`.tool-versions`) which you can check in to your project's Git repository to share with your team, ensuring everyone is using the **exact** same versions of tools.\n\nThe old way of working required multiple CLI version managers, each with their distinct API, configurations files and implementation (e.g. `$PATH` manipulation, shims, environment variables, etc...). `asdf` provides a single interface and configuration file to simplify development workflows, and can be extended to all tools and runtimes via a simple plugin interface.\n\n## How It Works\n\nOnce `asdf` core is set up with your Shell configuration, plugins are installed to manage particular tools. When a tool is installed by a plugin, the executables that are installed have [shims](<https://en.wikipedia.org/wiki/Shim_(computing)>) created for each of them. When you try and run one of these executables, the shim is run instead, allowing `asdf` to identify which version of the tool is set in `.tool-versions` and execute that version.\n\n## Related Projects\n\n### nvm / n / rbenv etc\n\nTools like [nvm](https://github.com/nvm-sh/nvm), [n](https://github.com/tj/n) and [rbenv](https://github.com/rbenv/rbenv) are all written as Shell scripts which create shims for the executables installed by these tools.\n\n`asdf` is very similar and was built to compete in this space of tool/runtime version management. The differentiating factor for `asdf` is its plugin system which removes the need for a manager per tool/runtime, different commands per manager and different `*-version` files in your repo.\n\n<!-- ### pyenv\n\nTODO: someone with Python background expand on this\n\n`asdf` has some similarities to `pyenv` but is missing some key features. The `asdf` team is looking at introducing some of these `pyenv` specific features, though no roadmap or timeline is available. -->\n\n### direnv\n\n> augments existing shells with a new feature that can load and unload environment variables depending on the current directory.\n\n`asdf` does not manage Environment Variables, however there is a plugin [`asdf-direnv`](https://github.com/asdf-community/asdf-direnv) to integrate direnv behaviour with `asdf`.\n\nSee [direnv docs](https://direnv.net/) for more.\n\n### Homebrew\n\n> The Missing Package Manager for macOS (or Linux)\n\nHomebrew manages your packages and their upstream dependencies. `asdf` does not manage upstream dependencies, it is not a package manager, that burden is upon the user, though we try and keep the dependency list small.\n\nSee [Homebrew docs](https://brew.sh/) for more.\n\n### NixOS\n\n> Nix is a tool that takes a unique approach to package management and system configuration\n\nNixOS aims to build truly reproducible environments by managing exact versions of packages up the entire dependency tree of each tool, something `asdf` does not do. NixOS does this with its own programming language, many CLI tools and a package collection of over 60,000 packages.\n\nAgain, `asdf` does not manage upstream dependencies and is not a package manager.\n\nSee [NixOS docs](https://nixos.org/guides/how-nix-works.html) for more.\n\n## Why use asdf?\n\n`asdf` ensures teams are using the **exact** same versions of tools, with support for **many** tools via a plugin system, and the _simplicity and familiarity_ of being a single **Shell** script you include in your Shell config.\n\n::: tip Note\n`asdf` is not intended to be a system package manager. It is a tool version manager. Just because you can create a plugin for any tool and manage its versions with `asdf`, does not mean that is the best course of action for that specific tool.\n:::\n"
  },
  {
    "path": "docs/guide/upgrading-to-v0-16.md",
    "content": "# Upgrading to 0.16.0\n\nasdf versions 0.15.0 and older were written in Bash and distributed as a set of\nBash scripts with the `asdf` function loaded into your shell. asdf version\n0.16.0 is a complete rewrite of asdf in Go. Since it is a complete rewrite\nthere are a [number of breaking](#breaking-changes) changes and it is now\na binary rather than a set of scripts.\n\n## Installation\n\nInstallation of version 0.16.0 and newer is much simpler than previous versions\nof asdf. It's just three steps:\n\n* Download the appropriate `asdf` binary for your operating system/architecture\ncombo via [any of the install methods available](/guide/getting-started.html#_1-install-asdf).\nIf using a package manager verify it's installing version 0.16.0 or later.\n* Add `$ASDF_DATA_DIR/shims` to the front of your `$PATH`.\n* Optionally, if you previously had a customized location for asdf data, set\n`ASDF_DATA_DIR` to the directory you already had the old version installing\nplugins, versions, and shims.\n\nIf your operating system's package manager already offers asdf 0.16.0 that is\nprobably the best method for installing it. Upgrading asdf is now only possible\nvia OS package managers and manual installation. There is no self-upgrade\nfeature.\n\n### Upgrading Without Losing Data\n\nYou can upgrade to the latest version of asdf without losing your existing\ninstall data. It's the same sequence of steps as above.\n\n#### 1. Download the appropriate `asdf` binary for your operating system & architecture\n\nDownload the binary from the [GitHub releases page](https://github.com/asdf-vm/asdf/releases) and place it in a directory on your path. I chose to place\nthe asdf binary in `$HOME/bin` and then added `$HOME/bin` to the front of my\n`$PATH`:\n\n```\n# In .zshrc, .bashrc, etc...\nexport PATH=\"$HOME/bin:$PATH\"\n```\n\n#### 2. Set `ASDF_DATA_DIR`\n\nRun `asdf info` and copy the line containing the `ASDF_DATA_DIR` variable:\n\n```\n...\nASDF_DATA_DIR=\"/home/myuser/.asdf\"\n...\n```\n\nIn your shell RC file (`.zshrc` if Zsh, `.bashrc` if Bash, etc...) add a line\nto the end setting `ASDF_DATA_DIR` to that same value:\n\n```bash\nexport ASDF_DATA_DIR=\"/home/myuser/.asdf\"\n```\n\n#### 3. Add `$ASDF_DATA_DIR/shims` to the front of your `$PATH`\n\nIn your shell RC file (same file as step #2) add `$ASDF_DATA_DIR/shims` to the\nfront of your path:\n\n```bash\nexport ASDF_DATA_DIR=\"/home/myuser/.asdf\"\nexport PATH=\"$ASDF_DATA_DIR/shims:$PATH\"\n```\n\n#### 4. Remove Old Config\n\nIn your shells RC file you'll have the old code running the asdf shell script at\nstartup. It'll probably look something like this:\n\n```\n. \"$HOME/.asdf/asdf.sh\"\n```\n\nOr this:\n\n```\n. /opt/homebrew/opt/asdf/libexec/asdf.sh\n```\n\nComment out these lines or remove them entirely.\n\nIf you are not using Zsh or Bash please see the legacy\n[Getting Started guide](https://asdf-vm.com/guide/getting-started-legacy.html#_3-install-asdf)\nfor the code snippet you need to remove.\n\n#### 5. Regenerate Shims\n\nVerify that `asdf` command in your shell session is version 0.16.0+ by running\n`asdf --help`. If you still see an older version you will need to start a new\nshell session.\n\nOnce you've verified the `asdf` command is the new version run `asdf reshim` to\nregenerate all shims. This is necessary as the old shims may still reference\nthe old Bash version.\n\n### Testing\n\nIf you aren't sure if the upgrade to 0.16.0 will break things for you can you\ncan test by installing 0.16.0 in addition to your existing version as described\nabove in \"Upgrading Without Losing Data\". If it turns out that the upgrade to\n0.16.0 or greater breaks things for you you can revert back to the older\nversion. Remove the lines you added to your shell RC file, and add back in the\nlines you removed or commented out.\n\n### Removing old files\n\n**Only do this after you've completed all the steps above and have verified\nyour new asdf installation is working correctly!** After upgrade there are\nvarious files you can remove from the old Bash-script based versions of asdf.\nMost of the files in your data directory (typically `~/.asdf/`) can be removed.\nNote that you don't have to do this. There is no harm in keeping the files from\nold versions of asdf around. The only directories that must be **kept** are:\n\n* `downloads/`\n* `installs/`\n* `plugins/`\n* `shims/`\n\nThe rest can be deleted. This can be done in one command with `find`:\n\n```\nfind ${ASDF_DATA_DIR:-$HOME/.asdf}/ -maxdepth 1 -mindepth 1 -not -name downloads -not -name plugins -not -name installs -not -name shims -exec rm -r {} \\;\n```\n\n## Breaking Changes\n\n### Hyphenated commands have been removed\n\nasdf version 0.15.0 and earlier supported by hyphenated and non-hyphenated\nversions of certain commands. With version 0.16.0 only the non-hyphenated\nversions are supported. The affected commands:\n\n* `asdf list-all` -> `asdf list all`\n* `asdf plugin-add` -> `asdf plugin add`\n* `asdf plugin-list` -> `asdf plugin list`\n* `asdf plugin-list-all` -> `asdf plugin list all`\n* `asdf plugin-update` -> `asdf plugin update`\n* `asdf plugin-remove` -> `asdf plugin remove`\n* `asdf plugin-test` -> `asdf plugin test`\n* `asdf shim-versions` -> `asdf shimversions`\n\n### `asdf global` and `asdf local` commands have been replaced with `asdf set`\n\n`asdf global` and `asdf local` have been removed. The \"global\" and \"local\"\nterminology was wrong and also misleading. asdf doesn't actually support\n\"global\" versions that apply everywhere. Any version that was specified with\n`asdf global` could easily be overridden by a `.tool-versions` file in your\ncurrent directory specifying a different version. This was confusing to users.\nThe new `asdf set` behaves the same as `asdf local` by default, but also has\nflags for setting versions in the user's home directory (`--home`) and in an\nexisting `.tool-versions` file in one of the parent directories (`--parent`).\nThis new interface will hopefully convey a better understanding of how asdf\nresolves versions and provide equivalent functionality.\n\n### `asdf update` command has been removed\n\nUpdates can no longer be performed this way. Use your OS package manager or\ndownload the latest binary manually. Additionally, the `asdf update` command\npresent in versions 0.15.0 and older cannot upgrade to version 0.16.0 because\nthe install process has changed. **You cannot upgrade to the latest Go\nimplementation using `asdf update`.**\n\n### `asdf shell` command has been removed\n\nThis command actually set an environment variable in the user's current shell\nsession. It was able to do this because `asdf` was actually a shell function,\nnot an executable. The new rewrite removes all shell code from asdf, and it is\nnow a binary rather than a shell function, so setting environment variables\ndirectly in the shell is no longer possible.\n\n### `asdf current` has changed\n\nInstead of three columns in the output, with the last being either the location\nthe version is set or a suggested command that could be run to set or install a\nversion. The third column has been split into two columns. The third column now\nonly indicates the source of the version if it is set (typically either version\nfile or environment variable) and the fourth is a boolean indicating whether\nthe specified version is actually installed. If it is not installed, a\nsuggested install command is shown.\n\n### Plugin extension commands must now be prefixed with `cmd`\n\nPreviously plugin extension commands could be run like this:\n\n```\nasdf nodejs nodebuild --version\n```\n\nNow they must be prefixed with `cmd` to avoid causing confusion with built-in\ncommands:\n\n```\nasdf cmd nodejs nodebuild --version\n```\n\n### Extension commands have been redesigned\n\nThere are a number of breaking changes for plugin extension commands:\n\n* They must be runnable by `exec` syscall. If your extension commands are shell\nscripts in order to be run with `exec` they must start with a proper shebang\nline.\n* They can now be binaries or scripts in any language. It no\nlonger makes sense to require a `.bash` extension as it is misleading.\n* They must have executable permission set.\n* They are no longer sourced by asdf as Bash scripts when they lack executable\npermission.\n\nAdditionally, only the first argument after plugin name is used to determine\nthe extension command to run. This means effectively there is the default\n`command` extension command that asdf defaults to when no command matching the\nfirst argument after plugin name is found. For example:\n\n```\nfoo/\n  lib/commands/\n    command\n    command-bar\n    command-bat-man\n```\n\nPreviously these scripts would work like this:\n\n```\n$ asdf cmd foo         # same as running `$ASDF_DATA_DIR/plugins/foo/lib/commands/command`\n$ asdf cmd foo bar     # same as running `$ASDF_DATA_DIR/plugins/foo/lib/commands/command-bar`\n$ asdf cmd foo bat man # same as running `$ASDF_DATA_DIR/plugins/foo/lib/commands/command-bat-man`\n```\n\nNow:\n\n```\n$ asdf cmd foo         # same as running `$ASDF_DATA_DIR/plugins/foo/lib/commands/command`\n$ asdf cmd foo bar     # same as running `$ASDF_DATA_DIR/plugins/foo/lib/commands/command-bar`\n$ asdf cmd foo bat man # same as running `$ASDF_DATA_DIR/plugins/foo/lib/commands/command-bat man`\n```\n\n### Executables Shims Resolve to Must Runnable by `syscall.Exec`\n\nThe most obvious example of this breaking change are scripts that lack a proper\nshebang line. asdf 0.15.0 and older were implemented in Bash, so as long it was\nan executable that could be executed with Bash it would run. This mean that\nscripts lacking a shebang could still be run by `asdf exec`. With asdf 0.16.x\nimplemented in Go we now invoke executables via Go's `syscall.Exec` function,\nwhich cannot handle scripts lacking a shebang.\n\nIn practice this isn't much of a problem. Most shell scripts DO contain a\nshebang line. If a tool managed by asdf provides scripts that don't have a\nshebang line one will need to be added to them.\n\n### Custom shim templates are no longer supported\n\nThis was a rarely used feature. The only plugin maintained by the core team\nthat used it was the Elixir plugin, and it no longer needs it. This feature\nwas originally added so that shim that get evaluated by a program rather than\nexecuted contain code that is suitable for evaluation by a particular program\n(in the case of Elixir this was the `iex` shell). Upon further investigation\nit seems this feature only exists because the `PATH` for executables was\nsometimes improperly set to include the **shims** rather than the other\n**executables** for the selected version(s).\n"
  },
  {
    "path": "docs/index.md",
    "content": "---\n# https://vitepress.dev/reference/default-theme-home-page\nlayout: home\n\nhero:\n  name: asdf\n  text: The Multiple Runtime Version Manager\n  tagline: Manage all your runtime versions with one tool!\n  actions:\n    - theme: brand\n      text: Get Started\n      link: /guide/getting-started\n    - theme: alt\n      text: What is asdf?\n      link: /guide/introduction\n    - theme: alt\n      text: View on GitHub\n      link: https://github.com/asdf-vm/asdf\n\nfeatures:\n  - title: One Tool\n    details: \"Manage each of your project runtimes with a single CLI tool and command interface.\"\n    icon: 🎉\n  - title: Plugins\n    details: \"Large ecosystem of existing runtimes & tools. Simple API to add support for new tools as you need!\"\n    icon: 🔌\n  - title: Backwards Compatible\n    details: \"Support for existing config files .nvmrc, .node-version, .ruby-version for smooth migration!\"\n    icon: ⏮\n  - title: \"One Config File\"\n    details: \".tool-versions to manage all your tools, runtimes and their versions in a single, sharable place.\"\n    icon: 📄\n  - title: \"Shells\"\n    details: \"Supports Bash, ZSH, Fish & Elvish with completions available.\"\n    icon: 🐚\n  - title: \"GitHub Actions\"\n    details: \"Provides a GitHub Action to install and utilize your .tool-versions in your CI/CD workflows.\"\n    icon: 🤖\n\n---\n"
  },
  {
    "path": "docs/ja-jp/contribute/core.md",
    "content": "# asdf\n\nこれは、`asdf`コアのコントリビューションガイドです。\n\n## 初期セットアップ\n\nGitHubで`asdf`をフォークするか、デフォルトのブランチをGitクローンしてください:\n\n```shell\n# clone your fork\ngit clone https://github.com/<GITHUB_USER>/asdf.git\n# or clone asdf\ngit clone https://github.com/asdf-vm/asdf.git\n```\n\nコア開発用のツールは、このリポジトリの`.tool-versions`で定義されています。`asdf`自身でこれらのツールを管理したい場合は、下記のようにプラグインを追加してください:\n\n```shell\nasdf plugin add bats https://github.com/timgluz/asdf-bats.git\nasdf plugin add shellcheck https://github.com/luizm/asdf-shellcheck.git\nasdf plugin add shfmt https://github.com/luizm/asdf-shfmt.git\n```\n\n`asdf`の開発に必要なバージョンを、下記のようにインストールします:\n\n```shell\nasdf install\n```\n\n開発ツールに影響を与える特定の機能を壊す可能性もあるため、ローカルマシンで開発する際は、`asdf`を使用しないほうが _良いかもしれません_ 。下記に、使用しているツールを列挙します:\n\n- [bats-core](https://github.com/bats-core/bats-core): BashまたはPOSIX準拠のスクリプトを単体テストするための、Bash自動テストシステムです。\n- [shellcheck](https://github.com/koalaman/shellcheck): シェルスクリプトの静的解析ツールです。\n- [shfmt](https://github.com/mvdan/sh): Bashをサポートするシェルパーサ、フォーマッタ、インタプリタです。\n\n## 開発\n\nインストール済みの`asdf`に変更を加えずに、あなたが開発した変更内容を試したいときは、`$ASDF_DIR`変数に、クローンしたリポジトリのパスを設定し、そのディレクトリの`bin`と`shims`ディレクトリを一時的にパスの先頭へ追加します。\n\nリモートにコミットまたはプッシュする前に、コードをローカルでフォーマット、Lint、およびテストすることを推奨します。その際は、次のスクリプト/コマンドを使用してください:\n\n```shell\n# Lint\n./scripts/lint.bash --check\n\n# Fix & Format\n./scripts/lint.bash --fix\n\n# Test: all tests\n./scripts/test.bash\n\n# Test: for specific command\nbats test/list_commands.bash\n```\n\n::: tip ヒント\n\n**テストを作ってください!** - 新機能にとってテストは**必要不可欠**であり、バグ修正のレビューをスピードアップさせることができます。プルリクエストを作成する前に、新しいコードをカバーするようなテストを作成してください。[bats-coreのドキュメント](https://bats-core.readthedocs.io/en/stable/index.html)もご覧ください。\n\n:::\n\n### Gitignore\n\n下記は、`asdf-vm/asdf`リポジトリの`.gitignore`ファイルです。プロジェクト固有のファイルは無視をしています。使用しているOS、ツール、およびワークフロー固有のファイルは、グローバルな`.gitignore`構成で無視する必要があります。詳しくは[こちら](http://stratus3d.com/blog/2018/06/03/stop-excluding-editor-temp-files-in-gitignore/)をご覧ください。\n\n@[Gitignoreコード](https://github.com/asdf-vm/asdf/blob/master/.gitignore)\n\n### `.git-blame-ignore-revs`\n\n`asdf`では、`.git-blame-ignore-revs`を使用して、Blameを実行する際のノイズを減らしています。詳しくは、[git blameのドキュメント](https://git-scm.com/docs/git-blame)をご覧ください。\n\n`git blame`を実行するときは、下記のように、このファイルと共に使います:\n\n```sh\ngit blame --ignore-revs-file .git-blame-ignore-revs ./test/install_command.bats\n```\n\n毎回手動でファイルを指定しなくても、gitのオプションで、`blame`を呼び出すたびにこのファイルを使うように設定することもできます:\n\n```sh\ngit config blame.ignoreRevsFile .git-blame-ignore-revs\n```\n\nこのファイルを使用するように、IDEを設定することもできます。例えば、VSCode(および[GitLens](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens))を使う場合は、`.vscode/settings.json`に下記のように記述します:\n\n```json\n{\n  \"gitlens.advanced.blame.customArguments\": [\n    \"--ignore-revs-file\",\n    \".git-blame-ignore-revs\"\n  ]\n}\n```\n\n## Batsテスト\n\nローカルでテストを実行するには、下記のようにテストを呼び出します:\n\n```shell\n./scripts/test.bash\n```\n\nテストを作成する前に、**下記項目を一通り参照してください**:\n\n- `test/`内にすでに作成されているテスト\n- [bats-coreのドキュメント](https://bats-core.readthedocs.io/en/stable/index.html)\n- `scripts/test.bash`で使用されている既存のBatsの設定\n\n### Batsのヒント\n\nBatsでのデバッグは、難しいことがあります。`-t`フラグを指定してTAP出力を有効にすると、テスト実行中に特殊なファイルディスクリプタ`>&3`を使用して出力を表示できるため、デバッグが簡単になります。例えば次のとおりです:\n\n```shell\n# test/some_tests.bats\n\nprintf \"%s\\n\" \"Will not be printed during bats test/some_tests.bats\"\nprintf \"%s\\n\" \"Will be printed during bats -t test/some_tests.bats\" >&3\n```\n\n詳しくは、bats-coreドキュメント内の[Printing to the Terminal](https://bats-core.readthedocs.io/en/stable/writing-tests.html#printing-to-the-terminal)で説明されています。\n\n## プルリクエスト、リリース、Conventional Commits\n\n`asdf`は、[Release Please](https://github.com/googleapis/release-please)という自動リリースツールを使用して、[セマンティックバージョン](https://semver.org/)を自動的に引き上げ、[Changelog](https://github.com/asdf-vm/asdf/blob/master/CHANGELOG.md)を生成しています。この情報は、前回のリリースからのコミット履歴を読み込むことで生成されます。\n\n[Conventional Commit messages](https://www.conventionalcommits.org/ja/)では、デフォルトブランチでのコミットメッセージのフォーマットとなる、プルリクエストタイトルのフォーマットを定義しています。これは、GitHub Action[`amannn/action-semantic-pull-request`](https://github.com/amannn/action-semantic-pull-request)で強制されます。\n\nConventional Commitは、下記のフォーマットに従います:\n\n```\n<type>[optional scope][optional !]: <description>\n\n<!-- examples -->\nfix: some fix\nfeat: a new feature\ndocs: some documentation update\ndocs(website): some change for the website\nfeat!: feature with breaking change\n```\n\n`<types>`の種類は次のとおりです: `feat`、`fix`、`docs`、`style`、 `refactor`、 `perf`、`test`、`build`、`ci`、`chore`、 `revert`。\n\n- `!`: 破壊的変更を示します\n- `fix`: セマンティックバージョンの`patch`を新しく作成します\n- `feat`: セマンティックバージョンの`minor`を新しく作成します\n- `<type>!`: セマンティックバージョンの`major`を新しく作成します\n\nプルリクエストのタイトルは、このフォーマットに従う必要があります。\n\n::: tip ヒント\n\nプルリクエストのタイトルには、Conventional Commit messageのフォーマットを使用してください。\n\n:::\n\n## Dockerイメージ\n\n[asdf-alpine](https://github.com/vic/asdf-alpine)および[asdf-ubuntu](https://github.com/vic/asdf-ubuntu)プロジェクトは、一部のasdfツールのDocker化されたイメージを提供する取り組みを継続的に行っています。これらのDockerイメージは、開発用サーバのベースとしたり、本番用アプリケーションの実行用途として使用することができます。\n"
  },
  {
    "path": "docs/ja-jp/contribute/documentation.md",
    "content": "# ドキュメント & サイト\n\nこれは、ドキュメントおよびサイトのコントリビューションガイドです。\n\n## 初期セットアップ\n\nGitHubで`asdf`をフォークするか、デフォルトのブランチをGitクローンしてください:\n\n```shell\n# clone your fork\ngit clone https://github.com/<GITHUB_USER>/asdf.git\n# or clone asdf\ngit clone https://github.com/asdf-vm/asdf.git\n```\n\nドキュメントサイト開発用のツールは、`asdf`によって`docs/.tool-versions`で管理されています。下記のようにプラグインを追加してください:\n\n```shell\nasdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs\n```\n\n開発に必要なバージョンを、下記のようにインストールします:\n\n```shell\nasdf install\n```\n\n- [Node.js](https://nodejs.org): ChromeのV8 JavaScriptエンジンをベースに構築されたJavaScriptランタイムです。\n\n`docs/package.json`をもとに、Node.jsの依存関係をインストールしてください:\n\n```shell\nnpm install\n```\n\n## 開発\n\n[VitePress (v2)](https://vitepress.dev/)は、asdfドキュメントサイトを構築するために使用している静的サイトジェネレータ(SSG)です。類似ツールである[Docsify.js](https://docsify.js.org/)やVuePressに代わってVitePressが採用されたのは、ユーザがJavaScriptを使用できない、または有効にしていない場合に、HTMLのみのフォールバックをサポートしたいからでした。これは、DocsifyとVitePressがVuePressに急速に取って代わっていた場合には不可能でした。これ以外の機能セットはほとんど同じで、最小限の構成でMarkdownファイルを書くことに重点を置いています。\n\n`package.json`には、開発に必要なスクリプトが含まれています:\n\n@[`package.json`のコード](https://github.com/asdf-vm/asdf/blob/master/docs/package.json#L3-L5)\n\nローカルの開発サーバを起動するには、次のように実行します:\n\n```shell\nnpm run dev\n```\n\nコミットする前にコードをフォーマットするには、次のように実行します:\n\n```shell\nnpm run format\n```\n\n## プルリクエスト、リリース、Conventional Commits\n\n`asdf`は、プルリクエストタイトルのConventional Commitsに依存する自動リリースパイプラインを使用しています。詳しくは、[コアのコントリビューションガイド](./core.md)のドキュメントに記述されています。\n\nドキュメントの変更に関するプルリクエストを作成する場合、プルリクエストのタイトルは、Conventional Commit typeを`docs`として、`docs: <description>`というフォーマットで作成するようにしてください。\n\n## Vitepress\n\nサイトの構成設定は、構成を示すために使用されるJSオブジェクト含んだ、いくつかのTypeScriptファイルに記述されています。以下のとおりです:\n\n- `docs/.vitepress/config.js`: サイトのルート構成ファイルです。仕様については、[VitePressのドキュメント](https://vitepress.dev/reference/site-config)をご覧ください。\n\nルート構成ファイルを簡素化するために、 _Navバー_ と _サイドバー_ の構成を示す大きなJSオブジェクトについては、別ファイルに切り出されており、かつ、ロケールごとに分類されています。次の両方のファイルを参照してください:\n\n- `docs/.vitepress/navbars.js`\n- `docs/.vitepress/sidebars.js`\n\nこれらの構成設定に関する公式ドキュメントは、[Default Theme Reference](https://vitepress.dev/reference/default-theme-config)をご覧ください。\n\n## I18n\n\nVitePressは、国際化対応に関して最高のサポートを備えています。\nルート構成ファイルである`docs/.vitepress/config.js`では、サポートされているロケールとそのURL、ドロップメニューのタイトル、Navバー/サイドバーの構成への参照を定義しています。\n\nNavバー/サイドバーの構成設定は前述の構成ファイルにキャプチャされ、ロケールごとに分類され、個別にエクスポートされます。\n\n各ロケールのMarkdownコンテンツは、ルート構成ファイル内の`locales`内のキーと同じ名前のディレクトリ配下に配置する必要があります。ルート構成が下記の場合:\n\n```js\n// docs/.vitepress/config.js\nexport default defineConfig({\n  ...\n  locales: {\n    root: {\n      label: \"English\",\n        lang: \"en-US\",\n        themeConfig: {\n        nav: navbars.en,\n          sidebar: sidebars.en,\n      },\n    },\n    \"pt-br\": {\n      label: \"Brazilian Portuguese\",\n        lang: \"pr-br\",\n        themeConfig: {\n        nav: navbars.pt_br,\n          sidebar: sidebars.pt_br,\n      },\n    },\n    \"zh-hans\": {\n      label: \"简体中文\",\n        lang: \"zh-hans\",\n        themeConfig: {\n        nav: navbars.zh_hans,\n          sidebar: sidebars.zh_hans,\n      },\n    },\n  },\n})\n```\n\n`/pt-BR/`を有効にするには、下記のように、`docs/pt-BR/`配下に同じMarkdownファイルのセットを配置する必要があります:\n\n```shell\ndocs\n├─ README.md\n├─ foo.md\n├─ nested\n│  └─ README.md\n└─ pt-BR\n   ├─ README.md\n   ├─ foo.md\n   └─ nested\n      └─ README.md\n```\n\n[公式のVitePress i18nドキュメント](https://vitepress.dev/guide/i18n)には、より詳細な説明が記述されています。\n"
  },
  {
    "path": "docs/ja-jp/contribute/first-party-plugins.md",
    "content": "# 公式プラグイン\n\nasdfコアチームでは、日々のワークライフに関連するプラグインをいくつか作成しています。これらのプラグインのメンテナンスおよび改善にご協力いただける方を、いつでも歓迎しています。詳しくは、下記リンクから、それぞれのリポジトリを参照してください:\n\n- [Elixir](https://github.com/asdf-vm/asdf-elixir)\n- [Erlang](https://github.com/asdf-vm/asdf-erlang)\n- [Node.js](https://github.com/asdf-vm/asdf-nodejs)\n- [Ruby](https://github.com/asdf-vm/asdf-ruby)\n\nコミュニティプラグインについては、下記をご覧ください:\n\n- [`asdf-community`オーガナイゼーション](https://github.com/asdf-community): `asdf`プラグインの長期的なメンテナンスを目的としたコミュニティ主導の共同プロジェクトです。\n- [`asdf-plugins`ショートネームリポジトリ](https://github.com/asdf-vm/asdf-plugins): ポピュラーな`asdf`プラグインを検索するために`asdf`コアが使用する、ショートネームのリストです。\n- [GitHub `asdf-plugin`トピック検索](https://github.com/topics/asdf-plugin)\n"
  },
  {
    "path": "docs/ja-jp/contribute/github-actions.md",
    "content": "# GitHub Actions\n\nご興味を持っていただきありがとうございます。既存のイシュー、議論、コントリビューションガイドについては、[asdf actions リポジトリ](https://github.com/asdf-vm/actions)をご覧ください。\n"
  },
  {
    "path": "docs/ja-jp/guide/getting-started-legacy.md",
    "content": "# はじめよう\n\n`asdf`のインストールには次の手順が必要です:\n\n1. 依存関係のインストール\n2. `asdf`コアのダウンロード\n3. `asdf`のインストール\n4. 管理したいツール/ランタイムごとにプラグインをインストール\n5. ツール/ランタイムの特定バージョンをインストール\n6. `.tool-versions`ファイルで、グローバルまたはプロジェクトのバージョンをセット\n\n## 1. 依存関係のインストール\n\nasdfの動作には`git`および`curl`が必要です。以下の表は、 _あなたが使用している_ パッケージマネージャで実行するコマンドの _一部例_ です(いくつかのツールは、後の手順で自動的にインストールされます)。\n\n| OS    | パッケージマネージャ | コマンド                           |\n| ----- | -------------------- | ---------------------------------- |\n| linux | Aptitude             | `apt install curl git`             |\n| linux | DNF                  | `dnf install curl git`             |\n| linux | Pacman               | `pacman -S curl git`               |\n| linux | Zypper               | `zypper install curl git`          |\n| macOS | Homebrew             | `brew install coreutils curl git`  |\n| macOS | Spack                | `spack install coreutils curl git` |\n\n::: tip 備考\n\nお使いのシステムの構成によっては、接頭に`sudo`が必要となる場合もあります。\n\n:::\n\n## 2. asdfのダウンロード\n\n### 公式ダウンロード\n\n<!-- x-release-please-start-version -->\n\n\n```shell\ngit clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.14.0\n\n```\n\n<!-- x-release-please-end -->\n\n### コミュニティがサポートするダウンロード方法\n\n理由がない限り、`git`コマンドを使用した公式ダウンロードの手順を使用することを強く推奨します。\n\n| 方法     | コマンド                                                                                                                                                         |\n| -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Homebrew | `brew install asdf`                                                                                                                                              |\n| Pacman   | `git clone https://aur.archlinux.org/asdf-vm.git && cd asdf-vm && makepkg -si` または好みの[AURヘルパー](https://wiki.archlinux.jp/index.php/AUR_ヘルパー)を使用 |\n\n## 3. asdfのインストール\n\nあなたが使用しているシェル、OS、およびインストール方法によって、ここでの設定方法が変わります。最も適したものを選択してください。\n\n**masOSユーザの方は、この節の最後にある`path_helper`に関する警告を必ず参照してください。**\n\n::: details Bash & Git\n\n`~/.bashrc`に下記の行を追記します:\n\n```shell\n. \"$HOME/.asdf/asdf.sh\"\n```\n\nコマンド補完が必要な場合は、`.bashrc`に下記の行を追記します:\n\n```shell\n. \"$HOME/.asdf/completions/asdf.bash\"\n```\n\n:::\n\n::: details Bash & Git (macOS)\n\n**macOS Catalina以降**を使用している場合、デフォルトのシェルは**ZSH**です。Bashに変更していない限り、ZSHの手順を参照してください。\n\n`~/.bash_profile`に下記の行を追記します:\n\n```shell\n. \"$HOME/.asdf/asdf.sh\"\n```\n\nコマンド補完が必要な場合は、`.bash_profile`に下記の行を追記します:\n\n```shell\n. \"$HOME/.asdf/completions/asdf.bash\"\n```\n\n:::\n\n::: details Bash & Homebrew\n\n下記コマンドで、`~/.bashrc`に`asdf.sh`を追加します:\n\n```shell\necho -e \"\\n. \\\"$(brew --prefix asdf)/libexec/asdf.sh\\\"\" >> ~/.bashrc\n```\n\nコマンド補完が必要な場合は、[Homebrewのガイドに従って設定を完了させる](https://docs.brew.sh/Shell-Completion#configuring-completions-in-bash)か、下記コマンドを実行します:\n\n```shell\necho -e \"\\n. \\\"$(brew --prefix asdf)/etc/bash_completion.d/asdf.bash\\\"\" >> ~/.bashrc\n```\n\n:::\n\n::: details Bash & Homebrew (macOS)\n\n**macOS Catalina以降**を使用している場合、デフォルトのシェルは**ZSH**です。Bashに変更していない限り、ZSHの手順を参照してください。\n\n下記コマンドで、`~/.bash_profile`に`asdf.sh`を追加します:\n\n```shell\necho -e \"\\n. \\\"$(brew --prefix asdf)/libexec/asdf.sh\\\"\" >> ~/.bash_profile\n```\n\nコマンド補完が必要な場合は、[Homebrewのガイドに従って設定を完了させる](https://docs.brew.sh/Shell-Completion#configuring-completions-in-bash)か、下記コマンドを実行します:\n\n```shell\necho -e \"\\n. \\\"$(brew --prefix asdf)/etc/bash_completion.d/asdf.bash\\\"\" >> ~/.bash_profile\n```\n\n:::\n\n::: details Bash & Pacman\n\n`~/.bashrc`に下記の行を追記します:\n\n```shell\n. /opt/asdf-vm/asdf.sh\n```\n\nコマンド補完が必要な場合は、[`bash-completion`](https://wiki.archlinux.jp/index.php/Bash#プログラムとオプションを追加)をインストールします。\n:::\n\n::: details Fish & Git\n\n`~/.config/fish/config.fish`に下記の行を追記します:\n\n```shell\nsource ~/.asdf/asdf.fish\n```\n\nコマンド補完が必要な場合は、下記コマンドを実行します:\n\n```shell\nmkdir -p ~/.config/fish/completions; and ln -s ~/.asdf/completions/asdf.fish ~/.config/fish/completions\n```\n\n:::\n\n::: details Fish & Homebrew\n\n下記コマンドで、`~/.config/fish/config.fish`に`asdf.sh`を追加します:\n\n```shell\necho -e \"\\nsource \"(brew --prefix asdf)\"/libexec/asdf.fish\" >> ~/.config/fish/config.fish\n```\n\nコマンド補完は、[Fish shellのHomebrewが担います](https://docs.brew.sh/Shell-Completion#configuring-completions-in-fish)。親切ですね!\n:::\n\n::: details Fish & Pacman\n\n`~/.config/fish/config.fish`に下記の行を追記します:\n\n```shell\nsource /opt/asdf-vm/asdf.fish\n```\n\nコマンド補完は、AURパッケージのインストール時に自動的に設定されます。\n:::\n\n::: details Elvish & Git\n\n下記コマンドで、`~/.config/elvish/rc.elv`に`asdf.elv`を追加します:\n\n```shell\nmkdir -p ~/.config/elvish/lib; ln -s ~/.asdf/asdf.elv ~/.config/elvish/lib/asdf.elv\necho \"\\n\"'use asdf _asdf; var asdf~ = $_asdf:asdf~' >> ~/.config/elvish/rc.elv\necho \"\\n\"'set edit:completion:arg-completer[asdf] = $_asdf:arg-completer~' >> ~/.config/elvish/rc.elv\n```\n\nコマンド補完は自動的に設定されます。\n\n:::\n\n::: details Elvish & Homebrew\n\n下記コマンドで、`~/.config/elvish/rc.elv`に`asdf.elv`を追加します:\n\n```shell\nmkdir -p ~/.config/elvish/lib; ln -s (brew --prefix asdf)/libexec/asdf.elv ~/.config/elvish/lib/asdf.elv\necho \"\\n\"'use asdf _asdf; var asdf~ = $_asdf:asdf~' >> ~/.config/elvish/rc.elv\necho \"\\n\"'set edit:completion:arg-completer[asdf] = $_asdf:arg-completer~' >> ~/.config/elvish/rc.elv\n```\n\nコマンド補完は自動的に設定されます。\n:::\n\n::: details Elvish & Pacman\n\n下記コマンドで、`~/.config/elvish/rc.elv`に`asdf.elv`を追加します:\n\n```shell\nmkdir -p ~/.config/elvish/lib; ln -s /opt/asdf-vm/asdf.elv ~/.config/elvish/lib/asdf.elv\necho \"\\n\"'use asdf _asdf; var asdf~ = $_asdf:asdf~' >> ~/.config/elvish/rc.elv\necho \"\\n\"'set edit:completion:arg-completer[asdf] = $_asdf:arg-completer~' >> ~/.config/elvish/rc.elv\n```\n\nコマンド補完は自動的に設定されます。\n:::\n\n::: details ZSH & Git\n\n`~/.zshrc`に下記の行を追記します:\n\n```shell\n. \"$HOME/.asdf/asdf.sh\"\n```\n\n**または**、[asdf for oh-my-zsh](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/asdf)のようなZSHフレームワークプラグインを使用して、このスクリプトをsourceし、コマンド補完をセットアップします。\n\nコマンド補完は、ZSHフレームワークの`asdf`プラグインで設定するか、`~/.zshrc`に下記の行を追記することで設定できます:\n\n```shell\n# append completions to fpath\nfpath=(${ASDF_DIR}/completions $fpath)\n# initialise completions with ZSH's compinit\nautoload -Uz compinit && compinit\n```\n\n- `compinit`のセットアップをカスタマイズしている場合は、`asdf.sh`ソース以下に`compinit`がくるようにしてください。\n- ZSHフレームワークで`compinit`のセットアップをカスタマイズしている場合は、フレームワークソース以下に`compinit`がくるようにしてください。\n\n:::\n\n::: details ZSH & Homebrew\n\n下記コマンドで、`~/.zshrc`に`asdf.sh`を追加します:\n\n```shell\necho -e \"\\n. $(brew --prefix asdf)/libexec/asdf.sh\" >> ${ZDOTDIR:-~}/.zshrc\n```\n\n**OR** use a ZSH Framework plugin like [asdf for oh-my-zsh](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/asdf) which will source this script and setup completions.\n\nコマンド補完は、ZSHフレームワーク`asdf`によって設定されるか、[Homebrewの説明に従って設定](https://docs.brew.sh/Shell-Completion#configuring-completions-in-zsh)必要があります。ZSHフレームワークを使用している場合、新しいZSHコマンド補完を使用するには、`fpath`経由で、関連する`asdf`プラグインの更新が必要となることがあります。Oh-My-ZSH asdfプラグインは、[ohmyzsh/ohmyzsh#8837](https://github.com/ohmyzsh/ohmyzsh/pull/8837)でご覧いただくと分かるとおり、まだ更新されていません。\n:::\n\n::: details ZSH & Pacman\n\n`~/.zshrc`に下記の行を追記します:\n\n```shell\n. /opt/asdf-vm/asdf.sh\n```\n\nコマンド補完は、ZSHに適した場所に配置されますが、[オートコンプリートを使用するようにZSHを設定する必要があります](https://wiki.archlinux.jp/index.php/Zsh#.E3.82.B3.E3.83.9E.E3.83.B3.E3.83.89.E8.A3.9C.E5.AE.8C)。\n:::\n\n::: details PowerShell Core & Git\n\n`~/.config/powershell/profile.ps1`に下記の行を追記します:\n\n```shell\n. \"$HOME/.asdf/asdf.ps1\"\n```\n\n:::\n\n::: details PowerShell Core & Homebrew\n\n下記コマンドで、`~/.config/powershell/profile.ps1`に`asdf.sh`を追加します:\n\n```shell\necho -e \"\\n. \\\"$(brew --prefix asdf)/libexec/asdf.ps1\\\"\" >> ~/.config/powershell/profile.ps1\n```\n\n:::\n\n::: details PowerShell Core & Pacman\n\n`~/.config/powershell/profile.ps1`に下記の行を追記します:\n\n```shell\n. /opt/asdf-vm/asdf.ps1\n```\n\n:::\n\n::: details Nushell & Git\n\n下記コマンドで、`~/.config/nushell/config.nu`に`asdf.nu`を追加します:\n\n```shell\n\"\\n$env.ASDF_DIR = ($env.HOME | path join '.asdf')\\n source \" + ($env.HOME | path join '.asdf/asdf.nu') | save --append $nu.config-path\n```\n\nコマンド補完は自動的に設定されます。\n:::\n\n::: details Nushell & Homebrew\n\n下記コマンドで、`~/.config/nushell/config.nu`に`asdf.nu`を追加します:\n\n```shell\n\"\\n$env.ASDF_DIR = (brew --prefix asdf | str trim | into string | path join 'libexec')\\n source \" +  (brew --prefix asdf | str trim | into string | path join 'libexec/asdf.nu') | save --append $nu.config-path\n```\n\nコマンド補完は自動的に設定されます。\n:::\n\n::: details Nushell & Pacman\n\n下記コマンドで、`~/.config/nushell/config.nu`に`asdf.nu`を追加します:\n\n```shell\n\"\\n$env.ASDF_DIR = '/opt/asdf-vm/'\\n source /opt/asdf-vm/asdf.nu\" | save --append $nu.config-path\n```\n\nコマンド補完は自動的に設定されます。\n:::\n\n::: details POSIX Shell & Git\n\n`~/.profile`に下記の行を追記します:\n\n```shell\nexport ASDF_DIR=\"$HOME/.asdf\"\n. \"$HOME/.asdf/asdf.sh\"\n```\n\n:::\n\n::: details POSIX Shell & Homebrew\n\n下記コマンドで、`~/.profile`に`asdf.sh`を追加します:\n\n```shell\necho -e \"\\nexport ASDF_DIR=\\\"$(brew --prefix asdf)/libexec/asdf.sh\\\"\" >> ~/.profile\necho -e \"\\n. \\\"$(brew --prefix asdf)/libexec/asdf.sh\\\"\" >> ~/.profile\n```\n\n:::\n\n::: details POSIX Shell & Pacman\n\n`~/.profile`に下記の行を追記します:\n\n```shell\nexport ASDF_DIR=\"/opt/asdf-vm\"\n. /opt/asdf-vm/asdf.sh\n```\n\n:::\n\n`asdf`のスクリプトは、`$PATH`を設定した**あと**、かつ、使用中のフレームワーク(oh-my-zsh など)を呼び出した**あと**に記述する必要があります。\n\n::: warning 警告\nmacOSでは、BashまたはZSHシェルを起動すると、自動的に`path_helper`というユーティリティが呼び出されます。`path_helper`は`PATH`(および`MANPATH`)内の項目の順番を並び替えることができるため、特定の順序を必要とするツールの動作に、一貫性が無くなってしまいます。これを回避するため、macOSで`asdf`を利用するときは、強制的に`PATH`エントリの先頭に追加する(優先度を一番高くする)ようにしてください。これは、`ASDF_FORCE_PREPEND`環境変数で制御できます。\n:::\n\n`PATH`の変更を反映するために、シェルを再起動してください。たいていの場合、ターミナルのタブを新たに開けばOKです。\n\n## コアのインストールが完了!\n\nこれで、`asdf`のコアのインストールは完了です:tada:\n\nしかし、`asdf`が役に立つようになるのは、**プラグイン**をインストールしてから**ツール**をインストールし、**バージョン**を管理するようになってからです。引き続き、ガイドを進めていきましょう。\n\n## 4. プラグインのインストール\n\nここではデモとして、[`asdf-nodejs`](https://github.com/asdf-vm/asdf-nodejs/)プラグインを使用して[Node.js](https://nodejs.org/)をインストール・設定してみましょう。\n\n### プラグインの依存関係\n\n各プラグインには依存関係があるため、プラグインのリポジトリを確認しておきましょう。`asdf-nodejs`の場合、必要なものは次のとおりです:\n\n| OS                             | 依存関係インストールコマンド            |\n| ------------------------------ | --------------------------------------- |\n| Debian                         | `apt-get install dirmngr gpg curl gawk` |\n| CentOS/ Rocky Linux/ AlmaLinux | `yum install gnupg2 curl gawk`          |\n| macOS                          | `brew install gpg gawk`                 |\n\n一部のプラグインではインストール後の事後処理でこれらの依存関係が必要となるため、あらかじめインストールしておきましょう。\n\n### プラグインのインストール\n\n```shell\nasdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs.git\n```\n\n## 5. 特定のバージョンのインストール\n\nNode.js用のプラグインをインストールしたので、このツールの特定のバージョンをインストールしましょう。\n\nインストール可能なバージョンは`asdf list all nodejs`コマンドで確認できますし、特定のメジャーバージョンのサブセットは`asdf list all nodejs 14`コマンドで確認できます。\n\n最新版をインストールするには、次のコマンドを実行します:\n\n```shell\nasdf install nodejs latest\n```\n\n::: tip 備考\n`asdf`では正確なバージョン番号を指定してください。`latest`は、現時点での最新バージョンを指定できる`asdf`のヘルパーです。\n:::\n\n## 6. バージョンをセット\n\n`asdf`は、カレントディレクトリから上位の`$HOME`ディレクトリまでに存在するすべての`.tool-versions`ファイルをもとに、ツールのバージョンを照会します。照会は、`asdf`で管理するツールを実行した際に、ジャストインタイムで行われます。\n\n::: warning 警告\nツールで指定されたバージョンが見つからない場合、**エラー**が発生します。`asdf current`コマンドを実行すると、カレントディレクトリにおいてツールのバージョンを解決可能か確認できるため、どのツールが実行に失敗するか検証することができます。\n:::\n\n### グローバル\n\nグローバルのデフォルト設定は、`$HOME/.tool-versions`で管理されます。グローバルのバージョンをセットするには、次のコマンドを実行します:\n\n```shell\nasdf global nodejs latest\n```\n\nすると、`$HOME/.tool-versions`内には次のように書き込まれます:\n\n```\nnodejs 16.5.0\n```\n\n一部のOSでは、`python`のように、`asdf`ではなくシステムが管理するツールが既にインストールされていることがあります。それを使用する場合、`asdf`に対して、バージョン管理をシステムに委任するように指示する必要があります。詳しくは、[バージョンのリファレンス](/ja-jp/manage/versions.md)をご覧ください。\n\n### ローカル\n\nローカルのバージョン設定は、`$PWD/.tool-versions`ファイル(カレントディレクトリ内)で定義されます。たいていの場合は、プロジェクトのGitリポジトリ内となるでしょう。対象となるディレクトリで、下記コマンドを実行します:\n\n```shell\nasdf local nodejs latest\n```\n\nすると、`$PWD/.tool-versions`内には次のように書き込まれます:\n\n```\nnodejs 16.5.0\n```\n\n### ツールごとに用意された既存バージョンファイルの利用\n\n`asdf`は、他のバージョンマネージャ向けに作られた既存のバージョンファイル(例: `rbenv`の場合は`.ruby-version`ファイル)からの移行をサポートしています。これはプラグイン単位でのサポートです。\n\n[`asdf-nodejs`](https://github.com/asdf-vm/asdf-nodejs/)であれば、`.nvmrc`ファイルと`.node-version`ファイルの両方に対応しています。このサポートを有効にするには、`asdf`の構成設定ファイルである`$HOME/.asdfrc`内に、下記の行を追記してください:\n\n```\nlegacy_version_file = yes\n```\n\n構成設定でのその他のオプションについて詳しくは、[構成設定](/ja-jp/manage/configuration.md)のリファレンスをご覧ください。\n\n## 入門完了!\n\n以上で、`asdf`の入門は完了です:tada: ここまでで、プロジェクトでの`nodejs`のバージョン管理ができるようになりました。プロジェクトで使用するツールごとに、同様の手順を実施してください!\n\n`asdf`には使いこなすと便利なコマンドが他にもいっぱいあり、`asdf --help`コマンドまたは単に`asdf`コマンドを実行すれば、すべてのコマンドの説明を見ることができます。コマンドは大きく分けて3つのカテゴリに分けられます:\n\n- [`asdf`のコア](/ja-jp/manage/core.md)\n- [プラグイン](/ja-jp/manage/plugins.md)\n- [ツールのバージョン](/ja-jp/manage/versions.md)\n"
  },
  {
    "path": "docs/ja-jp/guide/getting-started.md",
    "content": "# はじめよう\n\n## 1. asdfのインストール\n\nasdfはいくつかの方法でインストールできます:\n\n::: details パッケージマネージャーを使用 - **推奨**\n\n| パッケージマネージャー   | コマンド                                                                                                                                                             |\n| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Homebrew | `brew install asdf`                                                                                                                                                 |\n| Pacman   | `git clone https://aur.archlinux.org/asdf-vm.git && cd asdf-vm && makepkg -si` または お好みの [AUR ヘルパー](https://wiki.archlinux.jp/index.php/AUR_%E3%83%98%E3%83%AB%E3%83%91%E3%83%BC) |\n\n:::\n\n:::: details コンパイル済みバイナリをダウンロード - **かんたん**\n\n<!--@include: ./parts/install-dependencies.md-->\n\n##### asdfのインストール\n\n1. https://github.com/asdf-vm/asdf/releases から、お使いのオペレーティングシステム/アーキテクチャの組み合わせに適したアーカイブをダウンロード。\n2. アーカイブ内の`asdf`バイナリを`$PATH`のディレクトリに解凍。\n3. `type -a asdf`を実行して、シェルの`$PATH`に`asdf`があることを確認します。`asdf`のバイナリを置いたディレクトリが`type`の出力の1行目に表示されるはずです。うまくいかない場合は、2の手順が正しく行えていない可能性があります。\n\n::::\n\n:::: details `go install` を使用\n\n<!--@include: ./parts/install-dependencies.md-->\n\n##### asdfのインストール\n\n<!-- x-release-please-start-version -->\n1. [Goをインストールする](https://go.dev/doc/install)\n2. コマンドを実行: `go install github.com/asdf-vm/asdf/cmd/asdf@v0.18.1`\n<!-- x-release-please-end -->\n\n::::\n\n:::: details ソースコードからビルドする\n\n<!--@include: ./parts/install-dependencies.md-->\n\n##### asdfのインストール\n\n<!-- x-release-please-start-version -->\n1. asdfリポジトリをクローン:\n  ```shell\n  git clone https://github.com/asdf-vm/asdf.git --branch v0.18.1\n  ```\n<!-- x-release-please-end -->\n2. `make`を実行。\n3. `asdf`バイナリを`$PATH`上のディレクトリに解凍。\n4. `type -a asdf`を実行して、シェルの`$PATH`に`asdf`があることを確認します。`asdf`のバイナリを置いたディレクトリが`type`の出力の1行目に表示されるはずです。うまくいかない場合は、3の手順が正しく行えていない可能性があります。\n\n::::\n\n## 2. asdfの設定\n\n::: tip 備考\nほとんどのユーザーは、asdfが管理するデータ(plugin, install, shim data)の保存先を変更する必要は**ありません**。ただし、デフォルトの`$HOME/.asdf`以外を指定したい場合は変更することができます。別のディレクトリを指定するには、シェルのRCファイルで`ASDF_DATA_DIR`変数をエクスポートしてください。\n:::\n\nシェル、OS、インストール方法には様々な組み合わせがあり、その全てがここでの設定に影響します。あなたのシステムに最も適したものを選んでください。\n\n**masOSユーザーはこの節の最後にある`path_helper`に関する警告を必ず参照してください。**\n\n::: details Bash\n\n**macOS Catalina以降**: デフォルトのシェルが**ZSH**に変更されました。Bashに変更していない限り、ZSHの手順を参照してください。\n\n**Pacman**: コマンド補完が必要な場合は、[`bash-completion`](https://wiki.archlinux.jp/index.php/Bash#.E3.82.88.E3.81.8F.E4.BD.BF.E3.82.8F.E3.82.8C.E3.82.8B.E3.83.97.E3.83.AD.E3.82.B0.E3.83.A9.E3.83.A0.E3.81.A8.E3.82.AA.E3.83.97.E3.82.B7.E3.83.A7.E3.83.B3)をインストールしてください。\n\n##### shimsディレクトリをパスに追加する(必須)\n\n`~/.bash_profile`に以下を追記します:\n```shell\nexport PATH=\"${ASDF_DATA_DIR:-$HOME/.asdf}/shims:$PATH\"\n```\n\n###### カスタムデータディレクトリの設定(オプション)\n\n`~/.bash_profile`に以下を追記します(先述したパス追加よりも上の行に書くこと):\n\n```shell\nexport ASDF_DATA_DIR=\"/your/custom/data/dir\"\n```\n\n##### シェルのコマンド補完設定(オプション)\n\n`.bashrc`に以下を追記します:\n\n```shell\n. <(asdf completion bash)\n```\n\n:::\n\n::: details Fish\n\n##### shimsディレクトリをパスに追加する(必須)\n\n`~/.config/fish/config.fish`に以下を追記します:\n\n```shell\n# ASDF configuration code\nif test -z $ASDF_DATA_DIR\n    set _asdf_shims \"$HOME/.asdf/shims\"\nelse\n    set _asdf_shims \"$ASDF_DATA_DIR/shims\"\nend\n\n# Do not use fish_add_path (added in Fish 3.2) because it\n# potentially changes the order of items in PATH\nif not contains $_asdf_shims $PATH\n    set -gx --prepend PATH $_asdf_shims\nend\nset --erase _asdf_shims\n```\n\n###### カスタムデータディレクトリの設定(オプション)\n\n**Pacman**: コマンド補完はAURパッケージのインストール時に自動的に設定されます。\n\n`~/.config/fish/config.fish`に以下を追記します(先述したパス追加よりも上の行に書くこと):\n\n```shell\nset -gx --prepend ASDF_DATA_DIR \"/your/custom/data/dir\"\n```\n\n##### シェルのコマンド補完設定(オプション)\n\nコマンド補完は以下を実行して手動で設定する必要があります:\n\n```shell\n$ asdf completion fish > ~/.config/fish/completions/asdf.fish\n```\n\n:::\n\n::: details Elvish\n\n##### shimsディレクトリをパスに追加する(必須)\n\n`~/.config/elvish/rc.elv`に以下を追記します:\n\n```shell\nvar asdf_data_dir = ~'/.asdf'\nif (and (has-env ASDF_DATA_DIR) (!=s $E:ASDF_DATA_DIR '')) {\n  set asdf_data_dir = $E:ASDF_DATA_DIR\n}\n\nif (not (has-value $paths $asdf_data_dir'/shims')) {\n  set paths = [$path $@paths]\n}\n```\n\n###### カスタムデータディレクトリの設定(オプション)\n\nカスタムデータディレクトリを設定するには、上記のスニペットの以下の行を変更してください:\n\n```diff\n-var asdf_data_dir = ~'/.asdf'\n+var asdf_data_dir = '/your/custom/data/dir'\n```\n\n##### シェルのコマンド補完設定(オプション)\n\n```shell\n$ asdf completion elvish >> ~/.config/elvish/rc.elv\n$ echo \"\\n\"'set edit:completion:arg-completer[asdf] = $_asdf:arg-completer~' >> ~/.config/elvish/rc.elv\n```\n\n:::\n\n::: details ZSH\n\n**Pacman**: コマンド補完はZSHから使いやすい場所に配置されますが、[自動補完を使うにはZSHの設定で有効化する必要があります](https://wiki.archlinux.jp/index.php/Zsh#.E3.82.B3.E3.83.9E.E3.83.B3.E3.83.89.E8.A3.9C.E5.AE.8C)。\n\n##### shimsディレクトリをパスに追加する(必須)\n\n`~/.zshrc`に以下を追記します:\n\n```shell\nexport PATH=\"${ASDF_DATA_DIR:-$HOME/.asdf}/shims:$PATH\"\n```\n\n###### カスタムデータディレクトリの設定(オプション)\n\n`~/.zshrc`に以下を追記します(先述したパス追加よりも上の行に書くこと):\n\n```shell\nexport ASDF_DATA_DIR=\"/your/custom/data/dir\"\n```\n\n##### シェルのコマンド補完設定(オプション)\n\nコマンド補完はZSHフレームワークの`asdf`プラグイン（[asdf for oh-my-zsh](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/asdf)のようなもの）を使用するか、以下の手順で設定します:\n\n```shell\n$ mkdir -p \"${ASDF_DATA_DIR:-$HOME/.asdf}/completions\"\n$ asdf completion zsh > \"${ASDF_DATA_DIR:-$HOME/.asdf}/completions/_asdf\"\n```\n\nその場合は`.zshrc`に以下を追記します:\n\n```shell\n# append completions to fpath\nfpath=(${ASDF_DATA_DIR:-$HOME/.asdf}/completions $fpath)\n# initialise completions with ZSH's compinit\nautoload -Uz compinit && compinit\n```\n\n**備考**\n\nZSHフレームワークでカスタムされた`compinit`セットアップを使っている場合は、`compinit`がフレームワークのソース配下にあることを確認してください。\n\nコマンド補完はZSHフレームワークの`asdf`で設定するか、[Homebrewの指示に従って設定](https://docs.brew.sh/Shell-Completion#configuring-completions-in-zsh)する必要があります。ZSHフレームワークを使っている場合、asdf用のプラグインを更新して`fpath`経由で新しいZSH補完機能を正しく使えるようにする必要があるかもしれません。なお、Oh-My-ZSH asdfプラグインはまだ対応していません。[ohmyzsh/ohmyzsh#8837](https://github.com/ohmyzsh/ohmyzsh/pull/8837) を参照してください。\n:::\n\n::: details PowerShell Core\n\n##### shimsディレクトリをパスに追加する(必須)\n\n`~/.config/powershell/profile.ps1`に以下を追記します:\n```shell\n# Determine the location of the shims directory\nif ($null -eq $ASDF_DATA_DIR -or $ASDF_DATA_DIR -eq '') {\n  $_asdf_shims = \"${env:HOME}/.asdf/shims\"\n}\nelse {\n  $_asdf_shims = \"$ASDF_DATA_DIR/shims\"\n}\n\n# Then add it to path\n$env:PATH = \"${_asdf_shims}:${env:PATH}\"\n```\n\n###### カスタムデータディレクトリの設定(オプション)\n\n`~/.config/powershell/profile.ps1`に以下を追記します(先述したスニペットよりも上の行に書くこと):\n\n```shell\n$env:ASDF_DATA_DIR = \"/your/custom/data/dir\"\n```\n\nPowerShellはコマンド補完に対応していません。\n\n:::\n\n::: details Nushell\n\n##### shimsディレクトリをパスに追加する(必須)\n\n`~/.config/nushell/config.nu`に以下を追記します:\n\n```shell\nlet shims_dir = (\n  if ( $env | get --ignore-errors ASDF_DATA_DIR | is-empty ) {\n    $env.HOME | path join '.asdf'\n  } else {\n    $env.ASDF_DATA_DIR\n  } | path join 'shims'\n)\n$env.PATH = ( $env.PATH | split row (char esep) | where { |p| $p != $shims_dir } | prepend $shims_dir )\n```\n\n###### カスタムデータディレクトリの設定(オプション)\n\n`~/.config/nushell/config.nu`に以下を追記します(先述したパス追加よりも上の行に書くこと)：\n\n```shell\n$env.ASDF_DATA_DIR = \"/your/custom/data/dir\"\n```\n\n##### シェルのコマンド補完設定(オプション)\n\n```shell\n# If you've not customized the asdf data directory:\n$ mkdir $\"($env.HOME)/.asdf/completions\"\n$ asdf completion nushell | save $\"($env.HOME)/.asdf/completions/nushell.nu\"\n\n# If you have customized the data directory by setting ASDF_DATA_DIR:\n$ mkdir $\"($env.ASDF_DATA_DIR)/completions\"\n$ asdf completion nushell | save $\"($env.ASDF_DATA_DIR)/completions/nushell.nu\"\n```\n\n次に、`~/.config/nushell/config.nu`に以下を追記します:\n\n```shell\nlet asdf_data_dir = (\n  if ( $env | get --ignore-errors ASDF_DATA_DIR | is-empty ) {\n    $env.HOME | path join '.asdf'\n  } else {\n    $env.ASDF_DATA_DIR\n  }\n)\n. \"$asdf_data_dir/completions/nushell.nu\"\n```\n\n:::\n\n::: details POSIX Shell\n\n##### shimsディレクトリをパスに追加する(必須)\n\n`~/.profile`に以下を追記します:\n```shell\nexport PATH=\"${ASDF_DATA_DIR:-$HOME/.asdf}/shims:$PATH\"\n```\n\n###### カスタムデータディレクトリの設定(オプション)\n\n`~/.profile`に以下を追記します(先述したパス追加よりも上の行に書くこと):\n\n```shell\nexport ASDF_DATA_DIR=\"/your/custom/data/dir\"\n```\n\n:::\n\n`asdf`のスクリプトは、`$PATH`を設定した**あと**、かつ、使用中のフレームワーク(oh-my-zsh など)を呼び出した**あと**に記述する必要があります。\n\n`PATH`の変更を反映するために、シェルを再起動してください。たいていの場合、ターミナルのタブを新たに開けばOKです。\n\n\n## コアのインストールが完了！\n\nこれで、`asdf`のコアのインストールは完了です :tada:\n\nしかし、`asdf`が役に立つようになるのは、**プラグイン**をインストールしてから**ツール**をインストールし、**バージョン**を管理するようになってからです。引き続き、ガイドを進めていきましょう。\n\n## 4. プラグインのインストール\n\nここではデモとして、[`asdf-nodejs`](https://github.com/asdf-vm/asdf-nodejs/)プラグインを使用して[Node.js](https://nodejs.org/)をインストール・設定してみましょう。\n\n### プラグインの依存関係\n\n各プラグインには依存関係があるため、プラグインのリポジトリを確認しておきましょう。`asdf-nodejs`の場合、必要なものは次のとおりです:\n\n| OS                             | 依存関係インストールコマンド            |\n| ------------------------------ | --------------------------------------- |\n| Debian                         | `apt-get install dirmngr gpg curl gawk` |\n| CentOS/ Rocky Linux/ AlmaLinux | `yum install gnupg2 curl gawk`          |\n| macOS                          | `brew install gpg gawk`                 |\n\n一部のプラグインではインストール後の事後処理でこれらの依存関係が必要となるため、あらかじめインストールしておきましょう。\n\n### プラグインのインストール\n\n```shell\nasdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs.git\n```\n\n## 5. 特定のバージョンのインストール\n\nNode.js用のプラグインをインストールしたので、このツールの特定のバージョンをインストールしましょう。\n\nインストール可能なバージョンは`asdf list all nodejs`コマンドで確認できますし、特定のメジャーバージョンのサブセットは`asdf list all nodejs 14`コマンドで確認できます。\n\n最新版をインストールするには、次のコマンドを実行します:\n\n```shell\nasdf install nodejs latest\n```\n\n::: tip 備考\n`asdf`では正確なバージョン番号を指定してください。`latest`は、現時点での最新バージョンを指定できる`asdf`のヘルパーです。\n:::\n\n## 6. バージョンをセット\n\n`asdf`は、カレントディレクトリから上位の`$HOME`ディレクトリまでに存在するすべての`.tool-versions`ファイルをもとに、ツールのバージョンを照会します。照会は、`asdf`で管理するツールを実行した際に、ジャストインタイムで行われます。\n\n::: warning 警告\nツールで指定されたバージョンが見つからない場合、**エラー**が発生します。`asdf current`コマンドを実行すると、カレントディレクトリにおいてツールのバージョンを解決可能か確認できるため、どのツールが実行に失敗するか検証することができます。\n:::\n\nasdfはまずカレントディレクトリにある `.tool-versions` ファイルを探し、見つからなければ親ディレクトリを参照し `.tool-versions` ファイルが見つかるまでファイルツリーの上位階層を探索します。`.tool-versions`ファイルが見つからない場合、バージョン解決処理は失敗し、エラーが表示されます。\n\nすべてのディレクトリに適用されるデフォルトのバージョンを設定したい場合、`$HOME/.tool-versions`にバージョンを設定できます。特定のディレクトリで別のバージョンを設定しない限り、ホームディレクトリ以下のすべてのディレクトリに同じバージョンが設定されるようになります。\n\n```shell\nasdf set -u nodejs 16.5.0\n```\n\n`$HOME/.tool-versions`は次のようになります:\n\n```\nnodejs 16.5.0\n```\n\n一部のOSでは、`python`のように、`asdf`ではなくシステムが管理するツールが既にインストールされていることがあります。それを使用する場合、`asdf`に対して、バージョン管理をシステムに委任するように指示する必要があります。詳しくは、[バージョンのリファレンス](/ja-jp/manage/versions.md)をご覧ください。\n\nasdfが最初にバージョンを探す場所は、現在の作業ディレクトリ（`$PWD/.tool-versions`）です。これはプロジェクトのソースコードやGitリポジトリを含むディレクトリです。目的のディレクトリで`asdf set`を実行すると、バージョンを設定することができます:\n\n```shell\nasdf set nodejs 16.5.0\n```\n\n`$PWD/.tool-versions`は次のようになります:\n\n```\nnodejs 16.5.0\n```\n\n### ツールごとに用意された既存バージョンファイルの利用\n\n`asdf`は、他のバージョンマネージャ向けに作られた既存のバージョンファイル(例: `rbenv`の場合は`.ruby-version`ファイル)からの移行をサポートしています。これはプラグイン単位でのサポートです。\n\n[`asdf-nodejs`](https://github.com/asdf-vm/asdf-nodejs/)であれば、`.nvmrc`ファイルと`.node-version`ファイルの両方に対応しています。このサポートを有効にするには、`asdf`の構成設定ファイルである`$HOME/.asdfrc`内に、下記の行を追記してください:\n\n```\nlegacy_version_file = yes\n```\n\n構成設定でのその他のオプションについて詳しくは、[構成設定](/ja-jp/manage/configuration.md)のリファレンスをご覧ください。\n\n## 入門完了！\n\n以上で、`asdf`の入門は完了です:tada: ここまでで、プロジェクトでの`nodejs`のバージョン管理ができるようになりました。プロジェクトで使用するツールごとに、同様の手順を実施してください!\n\n`asdf`には使いこなすと便利なコマンドが他にもいっぱいあり、`asdf --help`コマンドまたは単に`asdf`コマンドを実行すれば、すべてのコマンドの説明を見ることができます。コマンドは大きく分けて3つのカテゴリに分けられます:\n\n- [`asdf`のコア](/ja-jp/manage/core.md)\n- [プラグイン](/ja-jp/manage/plugins.md)\n- [ツールのバージョン](/ja-jp/manage/versions.md)\n"
  },
  {
    "path": "docs/ja-jp/guide/introduction.md",
    "content": "# イントロダクション\n\n`asdf`は、ツールのためのバージョンマネージャです。すべてのツールのバージョンは単一のファイル(`.tool-versions`)内で定義されるため、プロジェクトのGitリポジトリにチェックインして共有することで、チーム全員に同じーバージョンのツールを使ってもらえるようになります。\n\n従来は、複数のCLIのバージョンマネージャを用意する必要があり、それぞれが異なるAPI、構成ファイル、および実装(`$PATH`の操作、Shim、環境変数など)となっていました。`asdf`は、開発ワークフローを簡素化するための単一インターフェースと構成ファイルを提供しており、シンプルなプラグインインターフェースを使って、すべてのツール・ランタイムに拡張することができます。\n\n## どうやって動いているの\n\nシェル上で`asdf`コアがセットアップすると、特定のツールを管理するためのプラグインをインストールすることができるようになります。プラグインによってツールがインストールされると、インストールされる実行ファイルごとに[Shim](<https://en.wikipedia.org/wiki/Shim_(computing)>)が作成されます。これらの実行ファイルを実行しようとすると、代わりにShimが実行され、`.tool-versions`で定義されているバージョンを`asdf`が認識して、当該バージョンが実行されます。\n\n## 関連プロジェクト\n\n### nvm / n / rbenv etc\n\n[nvm](https://github.com/nvm-sh/nvm)、[n](https://github.com/tj/n)、および[rbenv](https://github.com/rbenv/rbenv)のようなツールはすべて、ツールによってインストールされる実行ファイルのShimを作成するシェルスクリプトです。\n\n`asdf`はこれらのツールと非常に似ていて、ツール/ランタイムのバージョン管理という分野では競合しています。`asdf`はプラグインシステムを採用し、他のツールと差別化することで、ツール/ランタイムごとのマネージャ、マネージャごとの異なるコマンド、そしてリポジトリ内の様々な`*-version`ファイルといったものを排除しています。\n\n<!-- ### pyenv\n\nTODO: someone with Python background expand on this\n\n`asdf` has some similarities to `pyenv` but is missing some key features. The `asdf` team is looking at introducing some of these `pyenv` specific features, though no roadmap or timeline is available. -->\n\n### direnv\n\n> シェルに、ディレクトリごとに環境変数をロード/アンロードできる機能を付け加えます。\n\n`asdf`は環境変数を管理することはしませんが、direnvの動作を`asdf`に統合する[`asdf-direnv`](https://github.com/asdf-community/asdf-direnv)プラグインが存在します。\n\n詳しくは[direnvのドキュメント](https://direnv.net/)をご覧ください。\n\n### Homebrew\n\n> macOS(またはLinux)のためのパッケージマネージャー\n\nHomebrewは、パッケージとその上位の依存関係を管理します。`asdf`は上位の依存関係を管理することはしませんし、パッケージマネージャでもありません。それらの管理はユーザが負担することとなりますが、`asdf`はなるべく依存関係のリストを小さく保つように努めています。\n\n詳しくは[Homebrewのドキュメント](https://brew.sh/)をご覧ください。\n\n### NixOS\n\n> Nixは、パッケージ管理とシステム構成に独自のアプローチを取り入れたツールです。\n\nNixOSは、各ツールの依存関係ツリー全体でパッケージのバージョンを正確に管理することで、真に再現可能な環境を構築することを目指しています。`asdf`でそのようなことはできません。NixOSは、独自のプログラミング言語、たくさんのCLIツール、そして60,000を超えるパッケージコレクションによって、それらの機能を支えています。\n\n繰り返しになりますが、`asdf`は上位の依存関係を管理することはしませんし、パッケージマネージャでもありません。\n\n詳しくは[NixOSのドキュメント](https://nixos.org/guides/how-nix-works.html)をご覧ください。\n\n## なぜasdfを使うの?\n\n`asdf`は、プラグインシステムによって**多くの**ツールをサポートしており、シェル構成にたった1行の**シェル**スクリプトを記述するだけで使えるというシンプルさ・親しみやすさによって、チームが**確実に**同じバージョンのツールを使用することを保証できます。\n\n::: tip Note\n`asdf`はシステムのパッケージマネージャになることを目指してはいません。あくまで、ツールのバージョンマネージャです。プラグインを作成することで、どのようなツールでも`asdf`で管理できるようになりますが、それがそのツールにとって最善の方法であるとは限りませんのでご注意ください。\n:::\n"
  },
  {
    "path": "docs/ja-jp/guide/parts/install-dependencies.md",
    "content": "##### 依存関係のインストール\n\nasdfの動作には`git`が必要です。以下の表は、 _あなたが使用している_ パッケージマネージャで実行するコマンドの _一部例_ です(いくつかのツールは、後の手順で自動的にインストールされます)。\n\n| OS    | パッケージマネージャー | コマンド                       |\n| ----- | ------------------ | ----------------------------- |\n| linux | Aptitude           | `apt install git`             |\n| linux | DNF                | `dnf install git`             |\n| linux | Pacman             | `pacman -S git`               |\n| linux | Zypper             | `zypper install git`          |\n| macOS | Homebrew           | `brew install coreutils git`  |\n| macOS | Spack              | `spack install coreutils git` |\n\n::: tip 備考\n\nお使いのシステムの構成によっては、`sudo`が必要となる場合があります。\n\n:::\n"
  },
  {
    "path": "docs/ja-jp/guide/upgrading-to-v0-16.md",
    "content": "# 0.16.0にアップグレードする\n\nasdfはバージョン0.15.0以前まではBashで書かれており、`asdf`関数がシェルにロードされたBashスクリプトのセットとして配布されていました。バージョン0.16.0はasdfをGoで完全に書き直したものです。完全に書き直したことによって多くの[破壊的変更](#breaking-changes)があり、現在ではスクリプトのセットではなくバイナリになっています。\n\n## インストール\n\nバージョン0.16.0のインストールは、以前のバージョンのasdfよりもはるかに簡単です。\nたったの3ステップで完了します:\n\n* お使いのオペレーティングシステム/アーキテクチャの組み合わせに適した`asdf`バイナリをダウンロードし、`$PATH`のディレクトリに配置する。\n* `$PATH`の前に`$ASDF_DATA_DIR/shims`を追加する。\n* 以前にasdfのデータの保存場所をカスタマイズしていた場合は、`ASDF_DATA_DIR`に旧バージョンのプラグイン、バージョン、Shimをインストールしていたディレクトリを設定する(オプション)。\n\nオペレーティングシステムのパッケージマネージャがすでにasdf 0.16.0を提供している場合は、それをインストールするのがおそらく最良の方法です。asdfのアップグレードはOSのパッケージマネージャーと手動インストールでのみ可能です。セルフアップグレード機能はありません。\n\n### データを失わずにアップグレードする\n\n既存のインストールデータを失うことなく、asdfの最新バージョンにアップグレードできます。上記の手順と同じです。\n\n#### 1. お使いのオペレーティングシステムとアーキテクチャに適した`asdf`バイナリをダウンロードする\n\nバイナリをダウンロードして、パスの通ったディレクトリに配置します。以下ではasdfのバイナリを`$HOME/bin`に配置し、`$HOME/bin`を`$PATH`の先頭に追加します:\n\n```\n# In .zshrc, .bashrc, etc...\nexport PATH=\"$HOME/bin:$PATH\"\n```\n\n#### 2. `ASDF_DATA_DIR` を設定する\n\n`asdf info`を実行し、変数`ASDF_DATA_DIR`を含む行をコピーします:\n\n```\n...\nASDF_DATA_DIR=\"/home/myuser/.asdf\"\n...\n```\n\nシェルのRCファイル（Zshの場合は`.zshrc`、Bashの場合は`.bashrc`など）で、`ASDF_DATA_DIR`を設定してエクスポートします:\n\n```bash\nexport ASDF_DATA_DIR=\"/home/myuser/.asdf\"\n```\n\n#### 3. `$ASDF_DATA_DIR/shims`を`$PATH`の前に追加する。\n\nシェルのRCファイル（手順2と同じファイル）で、パスの前に`$ASDF_DATA_DIR/shims`を追加します：\n\n```bash\nexport ASDF_DATA_DIR=\"/home/myuser/.asdf\"\nexport PATH=\"$ASDF_DATA_DIR/shims:$PATH\"\n```\n\n#### 4. Shimを再生成する\n\n`asdf --help`を実行して、シェルセッションの`asdf`コマンドのバージョンが0.16.0以上であることを確認してください。まだ古いバージョンが表示されている場合は、新しいシェルセッションを開始する必要があります。\n\n`asdf`コマンドが新しいバージョンであることを確認したら、`asdf reshim`を実行してすべてのShimを再生成します。この手順は、古いShimが古いバージョンのBashを参照している可能性があるため必要となります。\n\n### テスト\n\n0.16.0へのアップグレードで問題が発生するかどうか確信が持てない場合は、「データを失わずにアップグレードする」で説明したように、既存のバージョンに加えて0.16.0をインストールしてテストすることができます。0.16.0へのアップグレードで不具合が生じることがわかったら、シェルのRCファイルに追加した行を削除してください。\n\n## 破壊的変更\n\n### ハイフン付きバージョンのコマンドは削除されました\n\nasdfバージョン0.15.0およびそれ以前では、特定のコマンドのハイフン付きバージョンとハイフンなしバージョンがサポートされていました。バージョン 0.16.0 では、ハイフンなしバージョンのみがサポートされます。影響を受けるコマンドは以下です:\n\n* `asdf list-all` -> `asdf list all`\n* `asdf plugin-add` -> `asdf plugin add`\n* `asdf plugin-list` -> `asdf plugin list`\n* `asdf plugin-list-all` -> `asdf plugin list all`\n* `asdf plugin-update` -> `asdf plugin update`\n* `asdf plugin-remove` -> `asdf plugin remove`\n* `asdf plugin-test` -> `asdf plugin test`\n* `asdf shim-versions` -> `asdf shimversions`\n\n### `asdf global` と `asdf local` コマンドが `asdf set` に変更されました\n\n`asdf global` と `asdf local` は削除されました。「グローバル」と「ローカル」という用語は誤りで、誤解を招く恐れがありました。asdf は、実際にはあらゆる場所に適用される「グローバル」バージョンをサポートしていません。`asdf global`で指定したバージョンは、カレントディレクトリにある `.tool-versions` ファイルで簡単に上書きすることができました。これはユーザーを混乱させていました。 新しい`asdf set`はデフォルトでは`asdf local`と同じように動作しますが、ユーザーのホームディレクトリ (`--home`) や親ディレクトリ (`--parent`) にある既存の `.tool-versions` ファイルにバージョンを設定するためのフラグも用意されています。 この新しいインターフェイスは、asdfがどのようにバージョンを解決するかをよりよく理解し、同等の機能を提供することを期待しています。\n\n### `asdf update`コマンドが削除されました\n\nこの方法ではアップデートを実行できなくなりました。OS のパッケージマネージャを使用するか、手動で最新のバイナリをダウンロードしてください。さらに、バージョン 0.15.0 以前の`asdf update`コマンドは、インストールプロセスが変更されたため、バージョン 0.16.0 にアップグレードできません。\n**`asdf update`を使用して最新のGo実装にアップグレードすることはできません。**\n\n### `asdf shell`コマンドが削除されました\n\nこのコマンドは実際にユーザーの現在のシェルセッションに環境変数を設定していました。これは`asdf`が実行可能ファイルではなく、実際にはシェル関数であったために可能でした。新しい実装では、asdfからすべてのシェルコードが削除され、シェル関数ではなくバイナリになったので、シェルで直接環境変数を設定することはできなくなりました。\n\n### `asdf current`が変更されました\n\nこれまで出力されていた3つのカラムの代わりに、最後のカラムはバージョンが設定されている場所か、バージョンを設定またはインストールするために実行可能な推奨コマンドのいずれかが表示されるようになりました。3番目のカラムが2つのカラムに分割されています。3番目のカラムは、バージョンが設定されている場合（通常、バージョンファイルか環境変数のいずれか）のみ、バージョンのソースを示すようになり、4番目は、指定されたバージョンが実際にインストールされているかどうかを示すブール値です。インストールされていない場合は、インストールコマンドの候補が表示されます。\n\n### プラグイン拡張コマンドの前に`cmd`を付ける必要があります\n\n\n以前のプラグイン拡張コマンドは次のように実行できました:\n\n```\nasdf nodejs nodebuild --version\n```\n\n現在では、組み込みコマンドと混同するのを避けるために、`cmd`をプレフィックスとして付ける必要があります:\n\n```\nasdf cmd nodejs nodebuild --version\n```\n\n### 拡張コマンドが再設計されました\n\nプラグイン拡張コマンドにはいくつかの変更点があります：\n\n* 拡張コマンドは`exec`システムコールで実行可能でなければなりません。拡張コマンドがシェルスクリプトの場合、`exec`で実行するためには適切なshebang行で始まる必要があります。\n* 拡張コマンドはバイナリでもスクリプトでも、どの言語でも実行できるようになりました。もはや`.bash`という拡張子を要求するのは誤解を招くので意味がありません。\n* 実行権限が設定されていなければなりません。\n* 実行権限がない場合、asdfによってBashスクリプトとしてソースされなくなりました。\n\nさらに、プラグイン名の後の最初の引数のみが、実行する拡張コマンドを決定するために使用されます。これは、プラグイン名の後の最初の引数にマッチするコマンドが見つからない場合にasdfがデフォルトで実行する`command`拡張コマンドが存在することを意味します。たとえばこのようになります:\n\n```\nfoo/\n  lib/commands/\n    command\n    command-bar\n    command-bat-man\n```\n\n以前は、これらのスクリプトはこのように動作していました:\n\n```\n$ asdf cmd foo         # same as running `$ASDF_DATA_DIR/plugins/foo/lib/commands/command`\n$ asdf cmd foo bar     # same as running `$ASDF_DATA_DIR/plugins/foo/lib/commands/command-bar`\n$ asdf cmd foo bat man # same as running `$ASDF_DATA_DIR/plugins/foo/lib/commands/command-bat-man`\n```\n\n現在はこのようになります:\n\n```\n$ asdf cmd foo         # same as running `$ASDF_DATA_DIR/plugins/foo/lib/commands/command`\n$ asdf cmd foo bar     # same as running `$ASDF_DATA_DIR/plugins/foo/lib/commands/command-bar`\n$ asdf cmd foo bat man # same as running `$ASDF_DATA_DIR/plugins/foo/lib/commands/command-bat man`\n```\n\n### 実行可能なShimは`syscall.Exec`によって実行できる必要があります\n\nasdf 0.15.0以前はBashで実装されていたので、Bashで実行できる実行ファイルであれば実行することができました。つまり、shebang行がないスクリプトでも`asdf exec`で実行することができました。しかし、asdf 0.16.xがGoで実装されたことで、Goの`syscall.Exec`関数を使って実行ファイルを呼び出すようになり、shebangがないスクリプトは扱えなくなりました。\n\n実際には、これはあまり問題ではありません。ほとんどのシェルスクリプトはshebang行を含んでいます。asdfによって管理されるツールがshebang行を持たないスクリプトを提供する場合、shebang行を追加する必要があります。\n\n### カスタムShimテンプレートはサポートされなくなりました\n\nこれはめったに使われない機能でした。コアチームがメンテナンスしているプラグインでこの機能を使用していたのはElixirプラグインだけで、もう必要ありません。この機能はもともと、実行されるのではなくプログラムによって評価されるShimが、特定のプログラム(Elixir の場合は`iex`シェル)による評価に適したコードを含むように追加されました。さらに調べてみると、この機能は実行可能ファイルの`PATH`が不適切に設定され、選択されたバージョンの**実行可能ファイル**ではなく、**Shim**を含むように設定されていたために存在していたようです。\n"
  },
  {
    "path": "docs/ja-jp/index.md",
    "content": "---\n# https://vitepress.dev/reference/default-theme-home-page\nlayout: home\n\nhero:\n  name: asdf\n  text: マルチランタイム<br/>バージョンマネージャ\n  tagline: 1つのツールですべてのランタイムのバージョンを管理しましょう!\n  actions:\n    - theme: brand\n      text: はじめよう\n      link: /ja-jp/guide/getting-started\n    - theme: alt\n      text: asdfってなに?\n      link: /ja-jp/guide/introduction\n    - theme: alt\n      text: GitHubをみる\n      link: https://github.com/asdf-vm/asdf\n\nfeatures:\n  - title: 単一ツール\n    details: \"単体のCLIツールとコマンドインターフェースで、各プロジェクトのランタイムを管理できます。\"\n    icon: 🎉\n  - title: プラグイン\n    details: \"既存ランタイム・ツールを使用した大規模なエコシステムです。必要に応じて新しいツールをサポートできるシンプルなAPIを用意しています!\"\n    icon: 🔌\n  - title: 後方互換性\n    details: \".nvmrc、.node-version、.ruby-versionといった既存構成ファイルから、スムーズに移行できます!\"\n    icon: ⏮\n  - title: \"単一の構成ファイル\"\n    details: \".tool-versionsを使用すると、すべてのツール、ランタイム、およびそれらのバージョンを、共有された単一の場所で管理できます。\"\n    icon: 📄\n  - title: \"シェル\"\n    details: \"Bash、ZSH、Fish、およびElvishをサポートし、コマンド補完にも対応しています。\"\n    icon: 🐚\n  - title: \"GitHub Actions\"\n    details: \"CI/CDワークフローで、.tool-versionsをインストールし利用するためのGitHub Actionを提供しています。\"\n    icon: 🤖\n---\n"
  },
  {
    "path": "docs/ja-jp/manage/commands.md",
    "content": "# すべてのコマンド\n\n`asdf`で利用可能なすべてのコマンドの一覧です。この一覧は、`asdf help`コマンドで表示されるテキストです。\n\n<<< @../../internal/help/help.txt\n"
  },
  {
    "path": "docs/ja-jp/manage/configuration.md",
    "content": "# 構成設定\n\n`asdf`の構成設定には、他人と共有可能な`.tool-versions`ファイルと、`.asdfrc`や環境変数によってカスタマイズ可能なユーザ固有の設定とがあります。\n\n## `.tool-versions`\n\n`.tool-versions`ファイルがディレクトリに存在する場合、当該ディレクトリおよびサブディレクトリで、ファイル内で宣言しているツールのバージョンが使用されます。\n\n`.tool-versions`ファイル内は下記のような記述となっています:\n\n```\nruby 2.5.3\nnodejs 10.15.0\n```\n\nコメントを含めることもできます:\n\n```\nruby 2.5.3 # This is a comment\n# This is another comment\nnodejs 10.15.0\n```\n\nバージョンの表記は下記の形式があります:\n\n- `10.15.0` - 実バージョンの表記です。バイナリのダウンロードに対応しているプラグインの場合、バイナリがダウンロードされます。\n- `ref:v1.0.2-a` or `ref:39cb398vb39` - 指定されたタグ/コミット/ブランチをgithubからダウンロードし、コンパイルされます。\n- `path:~/src/elixir` - 使用するツールをカスタムコンパイルしたバージョンへのパスです。言語開発者などが使用します。\n- `system` - このキーワードを指定した場合、asdfが管理していない、システム上のツールバージョンへパススルーします。\n\n::: tip ヒント\n\nスペースで区切れば、複数のバージョンを指定できます。例えば、Python `3.7.2`を使用し、Python `2.7.15`にフォールバックし、最終的に`system`のPythonにフォールバックさせるには、`.tool-versions`に下記の行を追記します。\n\n```\npython 3.7.2 2.7.15 system\n```\n\n:::\n\n`.tool-versions`ファイルで定義されているすべてのツールをインストールするには、`.tool-versions`ファイルを含むディレクトリで、`asdf install`コマンドを引数を指定せずに実行します。\n\n`.tool-versions`ファイルで定義されている単一のツールをインストールするには、`.tool-versions`ファイルを含むディレクトリで、`asdf install <name>`コマンドを実行します。ツールは、`.tool-versions`ファイルで指定されたバージョンでインストールされます。\n\nファイルは、直接編集するか、`asdf set`コマンドを使用して更新してください。\n\n## `.asdfrc`\n\n`.asdfrc`では、ユーザのマシン固有の構成を設定します。\n\nasdfはデフォルトで`${HOME}/.asdfrc`に構成ファイルを配置します。ファイルの場所は、[`ASDF_CONFIG_FILE`環境変数](#asdf-config-file)で設定できます。\n\n下記は、構成に必要な項目とそのデフォルト値を示しています:\n\n```txt\nlegacy_version_file = no\nuse_release_candidates = no\nalways_keep_download = no\nplugin_repository_last_check_duration = 60\ndisable_plugin_short_name_repository = no\nconcurrency = auto\n```\n\n### `legacy_version_file`\n\n**対応している**プラグインの場合、他のバージョンマネージャで使用されているバージョンファイルを読み込むことができます。例えば、Rubyの`rbenv`であれば`.ruby-version`ファイルを読み込みます。\n\n| オプション                                                    | 説明                                                                                                        |\n| :------------------------------------------------------------ | :---------------------------------------------------------------------------------------------------------- |\n| `no` <Badge type=\"tip\" text=\"デフォルト\" vertical=\"middle\" /> | バージョンの読み込みには`.tool-versions`を使用します                                                        |\n| `yes`                                                         | 利用可能なレガシーバージョンファイル(`.ruby-version`など)がある場合、プラグインのフォールバックで使用します |\n\n### `always_keep_download`\n\n`asdf install`コマンドでダウンロードしたソースコードやバイナリを、保持しておくか削除するかを制御します。\n\n| オプション                                                    | 説明                                                         |\n| :------------------------------------------------------------ | :----------------------------------------------------------- |\n| `no` <Badge type=\"tip\" text=\"デフォルト\" vertical=\"middle\" /> | インストールが成功したら、ソースコードやバイナリを削除します |\n| `yes`                                                         | インストール後も、ソースコードやバイナリを保持します         |\n\n### `plugin_repository_last_check_duration`\n\nasdfプラグインリポジトリの同期間隔(分)を制御します。何らかのトリガーイベントが発生した際に、最後に同期した時刻からの経過時間をチェックします。設定された間隔以上の時間が経過していた倍は、新たに同期が開始されます。\n\n| オプション                                                                                          | 説明                                                                                 |\n| :-------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------- |\n| `1`から`999999999`までの整数値 <br/> <Badge type=\"tip\" text=\"デフォルト\" vertical=\"middle\" />は`60` | 最後に同期した時刻から指定時間(分)以上経過していた場合、トリガーイベントで同期します |\n| `0`                                                                                                 | トリガーイベントのたびに同期します                                                   |\n| `never`                                                                                             | 同期しません                                                                         |\n\n同期は、以下のコマンドが実行されたときに発生します:\n\n- `asdf plugin add <name>`\n- `asdf plugin list all`\n\n`asdf plugin add <name> <git-url>`コマンドでは、プラグインの同期はトリガーされません。\n\n::: warning 備考\n\n値を`never`にしても、プラグインリポジトリの初期同期は停止されません。この動作については、`disable_plugin_short_name_repository`の節をご覧ください。\n\n:::\n\n### `disable_plugin_short_name_repository`\n\nasdfプラグインのショートネームリポジトリの同期を無効化します。ショートネームリポジトリが無効となっている場合、同期イベントはすぐに終了します。\n\n| オプション                                                    | 説明                                                                           |\n| :------------------------------------------------------------ | :----------------------------------------------------------------------------- |\n| `no` <Badge type=\"tip\" text=\"デフォルト\" vertical=\"middle\" /> | 同期イベントが発生した際に、asdfプラグインリポジトリをクローンまたは更新します |\n| `yes`                                                         | プラグインショートネームリポジトリを無効化します                               |\n\n同期は、以下のコマンドが実行されたときに発生します:\n\n- `asdf plugin add <name>`\n- `asdf plugin list all`\n\n`asdf plugin add <name> <git-url>`コマンドでは、プラグインの同期はトリガーされません。\n\n::: warning 備考\n\nプラグインショートネームリポジトリを無効化しても、すでに同期されたリポジトリは削除されません。プラグインリポジトリを削除するには、`rm --recursive --trash $ASDF_DATA_DIR/repository`コマンドを実行してください。\n\nまた、プラグインショートネームリポジトリを無効化しても、以前にこのソースからインストールされたプラグインは削除されません。プラグインを削除するには、`asdf plugin remove <name>`コマンドを実行してください。プラグインを削除すると、そのプラグインでインストールされたすべてのツールバージョンが削除されます。\n\n:::\n\n### `concurrency`\n\nコンパイル時に使用するデフォルトのコア数です。\n\n| Options | Description                                                                         |\n| :------ | :---------------------------------------------------------------------------------- |\n| 整数値  | ソースコードのコンパイル時に使用するコア数です                                      |\n| `auto`  | `nproc`、`sysctl hw.ncpu`、`/proc/cpuinfo`、または`1`、の優先順でコア数を計算します |\n\n備考: `ASDF_CONCURRENCY`環境変数が設定されている場合はそちらが優先されます。\n\n### プラグインフック\n\n下記のタイミングで、カスタムコードを実行することができます:\n\n- プラグインのインストール、Shim再生成、更新および削除をする際の前後\n- プラグインコマンドの実行前後\n\n例えば、`foo`というプラグインがインストールされていて、`bar`という実行可能ファイルが提供されている場合、以下のようなフックを使うことで、一番最初にカスタムコードを実行することができます:\n\n```text\npre_foo_bar = echo Executing with args: $@\n```\n\n以下のパターンがサポートされています:\n\n- `pre_<plugin_name>_<command>`\n- `pre_asdf_download_<plugin_name>`\n- `{pre,post}_asdf_{install,reshim,uninstall}_<plugin_name>`\n  - `$1`: フルバージョン\n- `{pre,post}_asdf_plugin_{add,update,remove,reshim}`\n  - `$1`: プラグイン名\n- `{pre,post}_asdf_plugin_{add,update,remove}_<plugin_name>`\n\nどのようなコマンドの前後にどのようなコマンドフックを実行すべきかについての詳細は、[プラグインの作成](../plugins/create.md)をご覧ください。\n\n## 環境変数\n\n環境変数の設定値は、お使いのシステムやシェルによって異なります。デフォルトロケーションは、インストールした場所や方法(Gitクローン、Homebrew、AUR)によって異なります。\n\n環境変数は通常、`asdf.sh`/`asdf.fish`などをsourceする前に設定する必要があります。Elvishの場合は、`use asdf`の上側に設定します。\n\n以下では、Bashシェルでの使用方法について説明します。\n\n### `ASDF_CONFIG_FILE`\n\n`.asdfrc`構成ファイルへのパスです。任意の場所に設定できます。必ず絶対パスで設定してください。\n\n- 未設定の場合: `$HOME/.asdfrc`の値が使用されます。\n- 使用方法: `export ASDF_CONFIG_FILE=/home/john_doe/.config/asdf/.asdfrc`\n\n### `ASDF_TOOL_VERSIONS_FILENAME`\n\nツール名とバージョンの情報を格納するファイルのファイル名です。有効なファイル名であれば何でも設定できます。通常、`.tool-versions`ファイルを無視したい場合を除き、この値を変更するべきではありません。\n\n- 未設定の場合: `.tool-versions`の値が使用されます。\n- 使用方法: `export ASDF_TOOL_VERSIONS_FILENAME=tool_versions`\n\n### `ASDF_DIR`\n\n`asdf`のコアスクリプト場所です。任意の場所に設定できます。必ず絶対パスで設定してください。\n\n- 未設定の場合: `bin/asdf`実行ファイルの親ディレクトリが使用されます。\n- 使用方法: `export ASDF_DIR=/home/john_doe/.config/asdf`\n\n### `ASDF_DATA_DIR`\n\n`asdf`がプラグイン、Shim、ツールのバージョンをインストールする場所です。任意の場所に設定できます。必ず絶対パスで設定してください。\n\n- 未設定の場合: `$HOME/.asdf`ディレクトリが存在すればその場所、存在しない場合は`ASDF_DIR`の値を使用します。\n- 使用方法: `export ASDF_DATA_DIR=/home/john_doe/.asdf`\n\n### `ASDF_CONCURRENCY`\n\nソースコードのコンパイル時に使用するコア数です。この環境変数の値は、asdf構成ファイルの`concurrency`の値よりも優先されます。\n\n- 未設定の場合: asdf構成ファイルの`concurrency`の値が使用されます。\n- 使用方法: `export ASDF_CONCURRENCY=32`\n\n## 完全な構成の例\n\n下記のように、asdfをシンプルにセットアップしたとします:\n\n- Bashシェル\n- インストール先は`$HOME/.asdf`\n- Git経由でインストール\n- 環境変数は何も設定していない\n- `.asdfrc`ファイルは何もカスタマイズしていない\n\nすると、結果として以下のような構成となります:\n\n| 構成                                  | 値               | 値がセットされる過程                                                                                                                  |\n| :------------------------------------ | :--------------- | :------------------------------------------------------------------------------------------------------------------------------------ |\n| config file location                  | `$HOME/.asdfrc`  | `ASDF_CONFIG_FILE`は空なので、`$HOME/.asdfrc`が使用されます。                                                                         |\n| default tool versions filename        | `.tool-versions` | `ASDF_TOOL_VERSIONS_FILENAME`は空なので、`.tool-versions`が使用されます。                                                     |\n| asdf dir                              | `$HOME/.asdf`    | `ASDF_DIR`は空なので、`bin/asdf`の親ディレクトリが使用されます。                                                                      |\n| asdf data dir                         | `$HOME/.asdf`    | `ASDF_DATA_DIR`は空であり、`$HOME`が存在するので、`$HOME/.asdf`が使用されます。                                                       |\n| concurrency                           | `auto`           | `ASDF_CONCURRENCY`は空なので、[デフォルト構成](https://github.com/asdf-vm/asdf/blob/master/defaults)の`concurrency`の値に依存します。 |\n| legacy_version_file                   | `no`             | `.asdfrc`をカスタマイズしていないので、[デフォルト構成](https://github.com/asdf-vm/asdf/blob/master/defaults)を使用します。           |\n| use_release_candidates                | `no`             | `.asdfrc`をカスタマイズしていないので、[デフォルト構成](https://github.com/asdf-vm/asdf/blob/master/defaults)を使用します。           |\n| always_keep_download                  | `no`             | `.asdfrc`をカスタマイズしていないので、[デフォルト構成](https://github.com/asdf-vm/asdf/blob/master/defaults)を使用します。           |\n| plugin_repository_last_check_duration | `60`             | `.asdfrc`をカスタマイズしていないので、[デフォルト構成](https://github.com/asdf-vm/asdf/blob/master/defaults)を使用します。           |\n| disable_plugin_short_name_repository  | `no`             | `.asdfrc`をカスタマイズしていないので、[デフォルト構成](https://github.com/asdf-vm/asdf/blob/master/defaults)を使用します。           |\n"
  },
  {
    "path": "docs/ja-jp/manage/core.md",
    "content": "# コア\n\nコアとなる`asdf`のコマンドはかなり少量ですが、多くのワークフローを円滑に進めることができます。\n\n## インストール & セットアップ\n\n[はじめよう](/ja-jp/guide/getting-started.md)のガイドで説明されています。\n\n## 実行\n\n```shell\nasdf exec <command> [args...]\n```\n\n現在のバージョンのShimでコマンドを実行します。\n\n<!-- TODO: expand on this with example -->\n\n## 環境変数\n\n```shell\nasdf env <command> [util]\n```\n\n<!-- TODO: expand on this with example -->\n\n## 情報\n\n```shell\nasdf info\n```\n\nOS、シェル、および`asdf`のデバッグ情報を表示するヘルパーコマンドです。バグレポート作成時に共有してください。\n\n## Shimの再作成\n\n```shell\nasdf reshim <name> <version>\n```\n\n特定のパッケージ・バージョンのShimを再作成します。デフォルトでは、Shimはプラグインによってツールのインストール中に作成されます。[npm CLI](https://docs.npmjs.com/cli/)などのツールは、実行ファイルをグローバルインストールができます(例:`npm install -g yarn`コマンドで[Yarn](https://yarnpkg.com/)をインストール)が、これらの実行ファイルはプラグインのライフサイクルを通してインストールされないため、Shimはまだ存在しません。そのような時に、例えば`asdf reshim nodejs <version>`を実行すると、`nodejs`の`<version>`に対して、`yarn`のような新しい実行ファイルのShimを強制的に再作成させることができます。\n\n## Shimのバージョン\n\n```shell\nasdf shimversions <command>\n```\n\n`<command>`のShimを提供するプラグインおよびバージョンを一覧で表示します。\n\n例えば、[Node.js](https://nodejs.org/)には`node`と`npm`という2つの実行ファイルが提供されています。[`asdf-nodejs`](https://github.com/asdf-vm/asdf-nodejs/)プラグインで、複数のバージョンのツールがインストールされている場合、`shimversions`は下記のような一覧を返します:\n\n```shell\n➜ asdf shimversions node\nnodejs 14.8.0\nnodejs 14.17.3\nnodejs 16.5.0\n```\n\n```shell\n➜ asdf shimversions npm\nnodejs 14.8.0\nnodejs 14.17.3\nnodejs 16.5.0\n```\n\n## 更新\n\n`asdf` をインストールするときと同じ方法で更新してください。このページの右上に asdf の最新バージョンが表示されます。\n\n## アンインストール\n\n`asdf`をアンインストールするには以下の手順に従ってください:\n\n::: details Bash & Git\n\n1. `~/.bashrc`で、`asdf.sh`およびコマンド補完をsourceしている行を削除します:\n\n```shell\n. \"$HOME/.asdf/asdf.sh\"\n. \"$HOME/.asdf/completions/asdf.bash\"\n```\n\n2. `$HOME/.asdf`ディレクトリを削除します:\n\n```shell\nrm -rf \"${ASDF_DATA_DIR:-$HOME/.asdf}\"\n```\n\n3. `asdf`のすべての構成ファイルを削除するために次のコマンドを実行します:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Bash & Git (macOS)\n\n1. `~/.bash_profile`で、`asdf.sh`およびコマンド補完をsourceしている行を削除します:\n\n```shell\n. \"$HOME/.asdf/asdf.sh\"\n. \"$HOME/.asdf/completions/asdf.bash\"\n```\n\n2. `$HOME/.asdf`ディレクトリを削除します:\n\n```shell\nrm -rf \"${ASDF_DATA_DIR:-$HOME/.asdf}\"\n```\n\n3. `asdf`のすべての構成ファイルを削除するために次のコマンドを実行します:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Bash & Homebrew\n\n1. `~/.bashrc`で、`asdf.sh`およびコマンド補完をsourceしている行を削除します:\n\n```shell\n. $(brew --prefix asdf)/libexec/asdf.sh\n. $(brew --prefix asdf)/etc/bash_completion.d/asdf.bash\n```\n\nコマンド補完については、[Homebrewで説明されている方法で設定](https://docs.brew.sh/Shell-Completion#configuring-completions-in-bash)されている可能性があるため、そちらのガイドに従って削除する行を見つけてください。\n\n2. パッケージマネージャでアンインストールします:\n\n```shell\nbrew uninstall asdf --force\n```\n\n3. `asdf`のすべての構成ファイルを削除するために次のコマンドを実行します:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Bash & Homebrew (macOS)\n\n**macOS Catalina以降**では、デフォルトのシェルが**ZSH**に変更されました。もし、`~/.bash_profile`に設定が見つからない場合は、`~/.zshrc`にある可能性があります。その場合は、ZSHの手順をご覧ください。\n\n1. `~/.bash_profile`で、`asdf.sh`およびコマンド補完をsourceしている行を削除します:\n\n```shell\n. $(brew --prefix asdf)/libexec/asdf.sh\n. $(brew --prefix asdf)/etc/bash_completion.d/asdf.bash\n```\n\nコマンド補完については、[Homebrewで説明されている方法で設定](https://docs.brew.sh/Shell-Completion#configuring-completions-in-bash)されている可能性があるため、そちらのガイドに従って削除する行を見つけてください。\n\n2. パッケージマネージャでアンインストールします:\n\n```shell\nbrew uninstall asdf --force\n```\n\n3. `asdf`のすべての構成ファイルを削除するために次のコマンドを実行します:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Bash & Pacman\n\n1. `~/.bashrc`で、`asdf.sh`およびコマンド補完をsourceしている行を削除します:\n\n```shell\n. /opt/asdf-vm/asdf.sh\n```\n\n2. パッケージマネージャでアンインストールします:\n\n```shell\npacman -Rs asdf-vm\n```\n\n3. `$HOME/.asdf`ディレクトリを削除します:\n\n```shell\nrm -rf \"${ASDF_DATA_DIR:-$HOME/.asdf}\"\n```\n\n4. `asdf`のすべての構成ファイルを削除するために次のコマンドを実行します:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Fish & Git\n\n1. `~/.config/fish/config.fish`で、`asdf.fish`をsourceしている行を削除します:\n\n```shell\nsource ~/.asdf/asdf.fish\n```\n\nそして、次のコマンドで、コマンド補完を削除します:\n\n```shell\nrm -rf ~/.config/fish/completions/asdf.fish\n```\n\n2. `$HOME/.asdf`ディレクトリを削除します:\n\n```shell\nrm -rf (string join : -- $ASDF_DATA_DIR $HOME/.asdf)\n```\n\n3. `asdf`のすべての構成ファイルを削除するために次のコマンドを実行します:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Fish & Homebrew\n\n1. `~/.config/fish/config.fish`で、`asdf.fish`をsourceしている行を削除します:\n\n```shell\nsource \"(brew --prefix asdf)\"/libexec/asdf.fish\n```\n\n2. パッケージマネージャでアンインストールします:\n\n```shell\nbrew uninstall asdf --force\n```\n\n3. `asdf`のすべての構成ファイルを削除するために次のコマンドを実行します:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Fish & Pacman\n\n1. `~/.config/fish/config.fish`で、`asdf.fish`をsourceしている行を削除します:\n\n```shell\nsource /opt/asdf-vm/asdf.fish\n```\n\n2. パッケージマネージャでアンインストールします:\n\n```shell\npacman -Rs asdf-vm\n```\n\n3. `$HOME/.asdf`ディレクトリを削除します:\n\n```shell\nrm -rf (string join : -- $ASDF_DATA_DIR $HOME/.asdf)\n```\n\n4. `asdf`のすべての構成ファイルを削除するために次のコマンドを実行します:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Elvish & Git\n\n1. `~/.config/elvish/rc.elv`で、`asdf`モジュールを使用している行を削除します:\n\n```shell\nuse asdf _asdf; var asdf~ = $_asdf:asdf~\nset edit:completion:arg-completer[asdf] = $_asdf:arg-completer~\n```\n\nそして、次のコマンドで、`asdf`モジュールを削除します:\n\n```shell\nrm -f ~/.config/elvish/lib/asdf.elv\n```\n\n2. `$HOME/.asdf`ディレクトリを削除します:\n\n```shell\nif (!=s $E:ASDF_DATA_DIR \"\") { rm -rf $E:ASDF_DATA_DIR } else { rm -rf ~/.asdf }\n```\n\n3. `asdf`のすべての構成ファイルを削除するために次のコマンドを実行します:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Elvish & Homebrew\n\n1. `~/.config/elvish/rc.elv`で、`asdf`モジュールを使用している行を削除します:\n\n```shell\nuse asdf _asdf; var asdf~ = $_asdf:asdf~\nset edit:completion:arg-completer[asdf] = $_asdf:arg-completer~\n```\n\nそして、次のコマンドで、`asdf`モジュールを削除します:\n\n```shell\nrm -f ~/.config/elvish/lib/asdf.elv\n```\n\n2. パッケージマネージャでアンインストールします:\n\n```shell\nbrew uninstall asdf --force\n```\n\n3. `asdf`のすべての構成ファイルを削除するために次のコマンドを実行します:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Elvish & Pacman\n\n1. `~/.config/elvish/rc.elv`で、`asdf`モジュールを使用している行を削除します:\n\n```shell\nuse asdf _asdf; var asdf~ = $_asdf:asdf~\nset edit:completion:arg-completer[asdf] = $_asdf:arg-completer~\n```\n\nそして、次のコマンドで、`asdf`モジュールを削除します:\n\n```shell\nrm -f ~/.config/elvish/lib/asdf.elv\n```\n\n2. パッケージマネージャでアンインストールします:\n\n```shell\npacman -Rs asdf-vm\n```\n\n3. `$HOME/.asdf`ディレクトリを削除します:\n\n```shell\nif (!=s $E:ASDF_DATA_DIR \"\") { rm -rf $E:ASDF_DATA_DIR } else { rm -rf ~/.asdf }\n```\n\n4. `asdf`のすべての構成ファイルを削除するために次のコマンドを実行します:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details ZSH & Git\n\n1. `~/.zshrc`で、`asdf.sh`およびコマンド補完をsourceしている行を削除します:\n\n```shell\n. \"$HOME/.asdf/asdf.sh\"\n# ...\nfpath=(${ASDF_DIR}/completions $fpath)\nautoload -Uz compinit\ncompinit\n```\n\n**または**、ZSHフレームワークプラグインを使用します。\n\n2. `$HOME/.asdf`ディレクトリを削除します:\n\n```shell\nrm -rf \"${ASDF_DATA_DIR:-$HOME/.asdf}\"\n```\n\n3. `asdf`のすべての構成ファイルを削除するために次のコマンドを実行します:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details ZSH & Homebrew\n\n1. `~/.zshrc`で、`asdf.sh`をsourceしている行を削除します:\n\n```shell\n. $(brew --prefix asdf)/libexec/asdf.sh\n```\n\n2. パッケージマネージャでアンインストールします:\n\n```shell\nbrew uninstall asdf --force && brew autoremove\n```\n\n3. `asdf`のすべての構成ファイルを削除するために次のコマンドを実行します:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details ZSH & Pacman\n\n1. `~/.zshrc`で、`asdf.sh`をsourceしている行を削除します:\n\n```shell\n. /opt/asdf-vm/asdf.sh\n```\n\n2. パッケージマネージャでアンインストールします:\n\n```shell\npacman -Rs asdf-vm\n```\n\n3. `$HOME/.asdf`ディレクトリを削除します:\n\n```shell\nrm -rf \"${ASDF_DATA_DIR:-$HOME/.asdf}\"\n```\n\n4. `asdf`のすべての構成ファイルを削除するために次のコマンドを実行します:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\nたったこれだけです! 🎉\n"
  },
  {
    "path": "docs/ja-jp/manage/plugins.md",
    "content": "# プラグイン\n\nプラグインは、`asdf`がNode.jsやRuby、Elixirなどの様々なツールを取り扱えるようにするためのものです。\n\n様々なツールをサポートするために使用されるプラグインAPIについては、[プラグインの作成](/ja-jp/plugins/create.md)をご覧ください。\n\n## 追加\n\n下記コマンドでは、GitのURLからプラグインを追加します:\n\n```shell\nasdf plugin add <name> <git-url>\n# asdf plugin add elm https://github.com/vic/asdf-elm\n```\n\nまたは下記のコマンドで、プラグインリポジトリのショートネームを指定して追加します:\n\n```shell\nasdf plugin add <name>\n# asdf plugin add erlang\n```\n\n::: tip 推奨\n\nリポジトリのショートネームに依存しないために、`git-url`を使用することを推奨します。\n\n:::\n\n## インストール済みプラグイン一覧\n\n```shell\nasdf plugin list\n# asdf plugin list\n# java\n# nodejs\n```\n\n```shell\nasdf plugin list --urls\n# asdf plugin list\n# java            https://github.com/halcyon/asdf-java.git\n# nodejs          https://github.com/asdf-vm/asdf-nodejs.git\n```\n\n## 全プラグインのショートネーム一覧\n\n```shell\nasdf plugin list all\n```\n\n全プラグインのショートネーム一覧については、[プラグインショートネームの一覧](https://github.com/asdf-vm/asdf-plugins)もご覧ください。\n\n## 更新\n\n```shell\nasdf plugin update --all\n```\n\n特定のプラグインパッケージを更新したい場合は、下記のように指定してください。\n\n```shell\nasdf plugin update <name>\n# asdf plugin update erlang\n```\n\nこの更新コマンドは、プラグインリポジトリの _origin_ の _デフォルトブランチ_ における _最新コミット_ を取得します。バージョニングされたプラグインの更新機能については、現在開発中です([#916](https://github.com/asdf-vm/asdf/pull/916))。\n\n## 削除\n\n```bash\nasdf plugin remove <name>\n# asdf plugin remove erlang\n```\n\nプラグインを削除すると、当該プラグインでインストールされたすべてのツールが削除されます。これは、各ツールの未使用バージョンを手っ取り早くクリーンアップ/プルーニングするのに有用です。\n\n## ショートネームリポジトリの同期\n\nショートネームリポジトリはローカルマシンに同期され、定期的に更新されます。同期のタイミングの条件は、次のとおりです:\n\n- 同期イベントは、下記コマンドによってトリガーされます:\n  - `asdf plugin add <name>`\n  - `asdf plugin list all`\n- 構成設定の`disable_plugin_short_name_repository`オプションが`yes`の場合、同期は中止されます。詳しくは[asdfの構成設定](/ja-jp/manage/configuration.md)のリファレンスをご覧ください。\n- もし、過去`X`分の間に同期が行われていない場合、同期が開始されます。\n  - `X`のデフォルト値は`60`ですが、`.asdfrc`ファイルの`plugin_repository_last_check_duration`オプションで変更することができます。詳しくは[asdfの構成設定](/ja-jp/manage/configuration.md)のリファレンスをご覧ください。\n"
  },
  {
    "path": "docs/ja-jp/manage/versions.md",
    "content": "# バージョン\n\n## バージョンのインストール\n\n```shell\nasdf install <name> <version>\n# asdf install erlang 17.3\n```\n\nプラグインが、ソースコードからのダウンロード・コンパイルをサポートしている場合、`ref:foo`の形式(`foo`は特定のブランチ、タグ、またはコミット)でバージョンを指定できます。アンインストールするときも、同じ名前とバージョンを指定する必要があります。\n\n## 最新安定バージョンのインストール\n\n```shell\nasdf install <name> latest\n# asdf install erlang latest\n```\n\n下記のように、特定のプレフィックスでの最新安定バージョンをインストールすることもできます。\n\n```shell\nasdf install <name> latest:<version>\n# asdf install erlang latest:17\n```\n\n## インストール済みバージョン一覧\n\n```shell\nasdf list <name>\n# asdf list erlang\n```\n\n下記のように、特定のプレフィックスでのバージョンでフィルタすることもできます。\n\n```shell\nasdf list <name> <version>\n# asdf list erlang 17\n```\n\n## インストール可能な全バージョン一覧\n\n```shell\nasdf list all <name>\n# asdf list all erlang\n```\n\n下記のように、特定のプレフィックスでのバージョンでフィルタすることもできます。\n\n```shell\nasdf list all <name> <version>\n# asdf list all erlang 17\n```\n\n## 最新安定バージョンの表示\n\n```shell\nasdf latest <name>\n# asdf latest erlang\n```\n\n下記のように、特定のプレフィックスでの最新安定バージョンで表示することもできます。\n\n```shell\nasdf latest <name> <version>\n# asdf latest erlang 17\n```\n\n## バージョンのセット\n\n#### `.tool-versions`ファイルで管理する\n\n```shell\nasdf set [flags] <name> <version> [<version>...]\n# asdf set elixir 1.2.4 # set in current dir\n# asdf set -u elixir 1.2.4 # set in .tool-versions file in home directory\n# asdf set -p elixir 1.2.4 # set in existing .tool-versions file in a parent dir\n\nasdf set <name> latest[:<version>]\n# asdf set elixir latest\n```\n\n`asdf set`はカレントディレクトリの`.tool-versions`ファイルにバージョンを書き込みます。これは単純に利便性のために存在します。`echo \"<tool> <version>\" > .tool-versions`を実行するようなものと考えてください。\n\n`u` または `--home`フラグをつけて`asdf set`を実行すると、`$HOME`ディレクトリの`.tool-versions`ファイルにバージョンを書き込みます。ファイルが存在しない場合は作成されます。\n\n`p`または `--parent`フラグをつけて`asdf set`を実行すると、カレントディレクトリから親ディレクトリを探索し、最初に見つかった`.tool-versions` ファイルにバージョンを書き込みます。\n\n#### 環境変数で管理する\n\nバージョンを決定するときに、`ASDF_${TOOL}_VERSION`というパターンの環境変数を探します。バージョンの形式は`.tool-versions`ファイルでサポートされているものと同じです。設定されている場合、この環境変数の値は`.tool-versions`ファイルでのバージョン指定よりも優先されます。たとえば:\n\n```shell\nexport ASDF_ELIXIR_VERSION=1.18.1\n```\n\nこれは現在のシェルセッションではElixir `1.18.1`を使うようasdfに指示します。\n\n:::warning\nこれは環境変数なので、設定されたセッションでのみ有効になります。\n実行中の他のセッションでは、`.tool-versions`ファイルに設定されているバージョンを引き続き使用します。\n\n`.tool-versions`ファイルについて詳しくは、[構成設定のリファレンス](/ja-jp/manage/configuration.md)をご覧ください。\n\n:::\n\n下記の例では、バージョン`1.4.0`のElixirプロジェクトに対して、テストを実行させています。\n\n```shell\nASDF_ELIXIR_VERSION=1.4.0 mix test\n```\n\n## システムバージョンへの委任\n\nasdfで管理されているバージョンではなく、`<name>`で指定されたツールのシステムバージョンを使用するには、バージョンとして`system`を指定します。\n\n[バージョンのセット](#バージョンのセット)と同様に、`asdf set`または環境変数で`system`をセットしてください。\n\n```shell\nasdf set <name> system\n# asdf set python system\n```\n\n## カレントバージョンの表示\n\n```shell\nasdf current\n# asdf current\n# erlang          17.3          /Users/kim/.tool-versions\n# nodejs          6.11.5        /Users/kim/cool-node-project/.tool-versions\n\nasdf current <name>\n# asdf current erlang\n# erlang          17.3          /Users/kim/.tool-versions\n```\n\n## バージョンのアンインストール\n\n```shell\nasdf uninstall <name> <version>\n# asdf uninstall erlang 17.3\n```\n\n## Shims\n\nasdfがパッケージをインストールすると、そのパッケージに含まれるすべての実行プログラムのShimが`$ASDF_DATA_DIR/shims`ディレクトリ(デフォルトは`~/.asdf/shims`)に作成されます。このディレクトリが(`asdf.sh`や`asdf.fish`などによって)`$PATH`に設定されることで、インストールされているプログラムが当該環境で利用できるようになります。\n\nShim自体は非常に単純なラッパーであり、`asdf exec`というヘルパープログラムに、プラグイン名と、Shimがラップしているインストール済みパッケージの実行ファイルのパスを渡して、`exec`します。\n\n`asdf exec`ヘルパーは、使用するパッケージのバージョン(`.tool-versions`ファイルで指定されたもの、または環境変数で指定されたもの)、パッケージのインストールディレクトリにある実行ファイルの完全パス(プラグインの`exec-path`コールバックで操作可能)、および実行環境(プラグインの`exec-env`スクリプトで提供)を決定し、実行します。\n\n::: warning 備考\n本システムは`exec`呼び出しを使用するため、シェルによってsourceされるパッケージ内のスクリプトは、Shimラッパーを経由させずに直接アクセスする必要があります。`asdf`で用意されている`which`および`where`コマンドは、下記のように、インストールされたパッケージへのパスを返すため、この状況を解決するのに役立ちます:\n:::\n\n```shell\n# returns path to main executable in current version\nsource $(asdf which ${PLUGIN})/../script.sh\n\n# returns path to the package installation directory\nsource $(asdf where ${PLUGIN})/bin/script.sh\n```\n\n### asdfのShimのバイパス\n\n何らかの理由でasdfのShimをバイパスしたい場合や、プロジェクトのディレクトリに移動した際に自動的に環境変数を設定したい場合は、[asdf-direnv](https://github.com/asdf-community/asdf-direnv)プラグインが役に立ちます。詳細はREADMEをご確認ください。\n"
  },
  {
    "path": "docs/ja-jp/more/community-projects.md",
    "content": "# コミュニティプロジェクト\n\n`asdf`に関連するコミュニティプロジェクトをいくつか紹介します:\n\n- [asdf-community](https://github.com/asdf-community): asdfプラグインの長期的なメンテナンスを目的とした\n  コミュニティ主導の共同プロジェクトです。\n- [asdf dev container](https://github.com/iloveitaly/asdf-devcontainer): GitHub Codespacesでasdfによるツール管理をサポートする、\n  [GitHub Dev Container](https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/introduction-to-dev-containers)\n  です。\n\n::: warning 備考\n\nasdfコアチームは、これらのプロジェクトやコードを所有していません。\nasdfコアでは、ここに掲載されているものに関連する品質やセキュリティについては責任を負いません。\n\n:::\n"
  },
  {
    "path": "docs/ja-jp/more/faq.md",
    "content": "# FAQ\n\nここでは、`asdf`に関するよくある質問を紹介します。\n\n## WSL1をサポートしていますか?\n\nWSL1 ([Windows Subsystem for Linux](https://ja.wikipedia.org/wiki/Windows_Subsystem_for_Linux) 1)は公式にはサポートしていません。`asdf`は正常に動作しない可能性があります。WSL1を公式にサポートする予定はありません。\n\n## WSL2をサポートしていますか?\n\nWSL2 ([Windows Subsystem for Linux](https://ja.wikipedia.org/wiki/Windows_Subsystem_for_Linux#WSL2) 2)では、あなたが選択したWSLディストリビューションに基づいて、セットアップと依存関係の解決を済ませれば、動作するはずです。\n\n重要なのは、WSL2が正常に動作するのは、カレントワークディレクトリがWindowsドライブではなくUnixドライブである場合に _限られる_ ということです。\n\nGitHub Actionsでホストランナーのサポートが可能になれば、WSL2でテストスイートを実行する予定ですが、現時点ではそうではないようです。\n\n## 新しくインストールした実行ファイルが実行できないのですが?\n\n> `npm install -g yarn`を実行したにも関わらず、`yarn`が実行できません。どうなっているの?\n\n`asdf`は[Shim](<https://en.wikipedia.org/wiki/Shim_(computing)>)を使って実行ファイルを管理しています。プラグインによってインストールされるものは、自動的にShimが作成されますが、`asdf`が管理しているツールによって実行ファイルがインストールされた場合は、Shimを作成しなければならないということを`asdf`に通知する必要があります。上記の例では、[Yarn](https://yarnpkg.com/)のShimを作成しなければいけません。詳しくは、[`asdf reshim`コマンドのドキュメント](/ja-jp/manage/core.md#shimの再作成)をご覧ください。\n\n## シェルが、新しくインストールされたShimを検知してくれないのですが?\n\n`asdf reshim`コマンドを実行しても問題が解決しない場合、`asdf.sh`や`asdf.fish`のsourceが、シェルの構成ファイル(`.bash_profile`、`.zshrc`、`config.fish`など)の**一番下**にないことが原因である可能性があります。`$PATH`を設定した**後**、そしてフレームワーク(oh-my-zshなど)を使用しているのれあればそれをsourceした**後**に、sourceする必要があります。\n"
  },
  {
    "path": "docs/ja-jp/more/thanks.md",
    "content": "# 謝辞\n\nこのページを借りて、asdfのオーサーおよびコントリビューターの皆様に感謝を申し上げます!\n\n## クレジット\n\n私 ([@HashNuke](https://github.com/HashNuke))、高熱、風邪、咳。\n\nコピーライト 2014年から今まで ([MIT License](https://github.com/asdf-vm/asdf/blob/master/LICENSE))\n\n## メンテナー\n\n- [@HashNuke](https://github.com/HashNuke)\n- [@danhper](https://github.com/danhper)\n- [@Stratus3D](https://github.com/Stratus3D)\n- [@vic](https://github.com/vic)\n- [@jthegedus](https://github.com/jthegedus)\n\n## コントリビューター\n\nGitHubの[コントリビューターリスト](https://github.com/asdf-vm/asdf/graphs/contributors):pray:をご覧ください。\n"
  },
  {
    "path": "docs/ja-jp/plugins/create.md",
    "content": "# プラグインの作成\n\nプラグインとは、\n言語/ツールのバージョン管理をサポートするための実行スクリプトを含めたGitリポジトリのことです。\nこれらのスクリプトは、`asdf list-all <name>`や`asdf install <name> <version>`などの機能をサポートするコマンドを使って、\nasdfによって実行されます。\n\n## クイックスタート\n\nオリジナルのプラグインを作成するには、次の2つの方法があります:\n\n1. [asdf-vm/asdf-plugin-template](https://github.com/asdf-vm/asdf-plugin-template)リポジトリを使用し、\n   デフォルトのスクリプトが実装されたプラグインリポジトリ(名前は`asdf-<tool_name>`)を\n   [生成](https://github.com/asdf-vm/asdf-plugin-template/generate)\n   します。\n   生成できたら、\n   そのリポジトリをクローンして`setup.bash`のスクリプトを実行し、\n   テンプレートを対話的に更新していきます。\n2. `asdf-<tool_name>`という名前のリポジトリを自分で立ち上げ、\n   以降に記載されている必要なスクリプトを実装します。\n\n### プラグインスクリプトの鉄則\n\n- スクリプト内で他の`asdf`コマンドを呼び出しては**いけません**。\n- シェルのツール/コマンドへの依存関係を小さく保つようにしてください。\n- 移植性のないツールやコマンドフラグの使用は避けてください。\n  例えば、`sort -V`などです。\n  asdfコアの[禁止コマンド一覧](https://github.com/asdf-vm/asdf/blob/master/test/banned_commands.bats)もご覧ください。\n\n## スクリプトの概要\n\n以下は、asdfから呼び出せるスクリプトの全リストです。\n\n| スクリプト                                                                                     | 説明                                                                         |\n| :--------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------- |\n| [bin/list-all](#bin-list-all) <Badge type=\"tip\" text=\"必須\" vertical=\"middle\" />               | インストール可能なすべてのバージョンをリストします。                         |\n| [bin/download](#bin-download) <Badge type=\"tip\" text=\"必須\" vertical=\"middle\" />               | ツールの特定バージョンのソースコードまたはバイナリをダウンロードします。     |\n| [bin/install](#bin-install) <Badge type=\"tip\" text=\"必須\" vertical=\"middle\" />                 | ツールの特定バージョンをインストールします。                                 |\n| [bin/latest-stable](#bin-latest-stable) <Badge type=\"warning\" text=\"推奨\" vertical=\"middle\" /> | 指定されたツールの最新安定バージョンをリストします。                         |\n| [bin/help.overview](#bin-help.overview)                                                        | プラグインおよびツールに関する概要説明を出力します。                         |\n| [bin/help.deps](#bin-help.deps)                                                                | オペレーティングシステムに合わせた依存関係のリストを出力します。             |\n| [bin/help.config](#bin-help.config)                                                            | プラグインおよびツールの構成設定一覧を出力します。                           |\n| [bin/help.links](#bin-help.links)                                                              | プラグインとツールに関連するリンクリストを出力します。                       |\n| [bin/list-bin-paths](#bin-list-bin-paths)                                                      | Shimを作成するバイナリが存在するディレクトリへの相対パスの一覧を出力します。 |\n| [bin/exec-env](#bin-exec-env)                                                                  | ツールのバイナリのShimを実行する前に環境を準備します。                       |\n| [bin/exec-path](#bin-exec-path)                                                                | ツールの特定バージョンの実行ファイルパスを出力します。                       |\n| [bin/uninstall](#bin-uninstall)                                                                | ツールの特定バージョンをアンインストールします。                             |\n| [bin/list-legacy-filenames](#bin-list-legacy-filenames)                                        | `.ruby-version`のような、レガシー構成ファイルのリストを出力します。          |\n| [bin/parse-legacy-file](#bin-parse-legacy-file)                                                | レガシーバージョンファイルのカスタムパーサーです。                           |\n| [bin/post-plugin-add](#bin-post-plugin-add)                                                    | プラグインが追加された後に実行されるフックです。                             |\n| [bin/post-plugin-update](#bin-post-plugin-update)                                              | プラグインが更新された後に実行されるフックです。                             |\n| [bin/pre-plugin-remove](#bin-pre-plugin-remove)                                                | プラグインが削除される前に実行されるフックです。                             |\n\nどのコマンドがどのスクリプトを呼び出すかについては、\n各スクリプトの詳細なドキュメントを参照してください。\n\n## 環境変数の概要\n\n以下は、すべてのスクリプトで使用される環境変数の全リストです。\n\n| 環境変数                 | 説明                                                                                            |\n| :----------------------- | :---------------------------------------------------------------------------------------------- |\n| `ASDF_INSTALL_TYPE`      | `version`または`ref`です。                                                                      |\n| `ASDF_INSTALL_VERSION`   | `ASDF_INSTALL_TYPE`に応じてフルバージョンナンバーまたはGit Refの値が入ります。                  |\n| `ASDF_INSTALL_PATH`      | ツールがインストール _されている_ 場所、またはインストール _されるべき_ 場所へのパスです。      |\n| `ASDF_CONCURRENCY`       | ソースコードのコンパイル時に使用するコア数です。`make -j`のようなフラグを設定する際に便利です。 |\n| `ASDF_DOWNLOAD_PATH`     | `bin/download`によってソースコードまたはバイナリがダウンロードされる場所へのパスです。          |\n| `ASDF_PLUGIN_PATH`       | プラグインがインストールされている場所へのパスです。                                            |\n| `ASDF_PLUGIN_SOURCE_URL` | プラグインソースのURLです。                                                                     |\n| `ASDF_PLUGIN_PREV_REF`   | プラグインの以前の`git-ref`です。                                                               |\n| `ASDF_PLUGIN_POST_REF`   | 更新後のプラグインの`git-ref`です。                                                             |\n| `ASDF_CMD_FILE`          | ソースとなるファイルのフルパスに解決されます。                                                  |\n\n::: tip 備考\n\n**すべてのスクリプトですべての環境変数が使用できるわけではありません。**\n以下の各スクリプトのドキュメントで、そのスクリプトで利用可能な環境変数を確認してください。\n\n:::\n\n## 必須スクリプト\n\n### `bin/list-all` <Badge type=\"tip\" text=\"必須\" vertical=\"middle\" />\n\n**説明**\n\nインストール可能なすべてのバージョンをリストします。\n\n**出力フォーマット**\n\n**スペース区切り**のバージョンリストの文字列を出力する必要があります。例えば次のとおりです:\n\n```txt\n1.0.1 1.0.2 1.3.0 1.4\n```\n\n最新バージョンが末尾にくる必要があります。\n\nasdfコアは各バージョンを1行ずつ表示するため、\nいくつかのバージョンは画面外にはみ出る場合があります。\n\n**並べ替え**\n\nウェブサイト上のリリースページからバージョンを取得する場合、\n提供されている順序は正しいリリース順となっていることが多いため、\nそのままの順序を使用することを推奨します。\n逆順にしたければ、`tsc`をパイプで通すだけで十分です。\n\nどうしても並べ替えが避けられない場合、`sort -V`は移植性が無いため、次のいずれかの方法を使用することを推奨します:\n\n- [Git sort capabilityを使用する](https://github.com/asdf-vm/asdf-plugin-template/blob/main/template/lib/utils.bash)\n  (Git `v2.18.0`以上が必要です)\n- [カスタムソートメソッドを自分で書く](https://github.com/vic/asdf-idris/blob/master/bin/list-all#L6)\n  (`sed`、`sort`、および`awk`が必要です)\n\n**スクリプトで使用できる環境変数**\n\nこのスクリプトに環境変数は提供されません。\n\n**このスクリプトを呼び出すコマンド**\n\n- `asdf list all <name> [version]`\n- `asdf list all nodejs`: このスクリプトで返されるすべてのバージョンを、\n  1行ずつリストします。\n- `asdf list all nodejs 18`: このスクリプトで返されるすべてのバージョンから、\n  `18`で始まるバージョンのみフィルタし、1行ずつリストします。\n\n**asdfからの呼び出しシグネチャ**\n\n引数はありません。\n\n```bash\n\"${plugin_path}/bin/list-all\"\n```\n\n---\n\n### `bin/download` <Badge type=\"tip\" text=\"必須\" vertical=\"middle\" />\n\n**説明**\n\nツールの特定バージョンのソースコードまたはバイナリを、指定された場所にダウンロードします。\n\n**実装内容**\n\n- スクリプトは、`ASDF_DOWNLOAD_PATH`で指定されたディレクトリに、ソースコードまたはバイナリをダウンロードする必要があります。\n- 解凍されたソースコードまたはバイナリのみを、`ASDF_DOWNLOAD_PATH`ディレクトリに配置する必要があります。\n- 失敗した場合、`ASDF_DOWNLOAD_PATH`ディレクトリ内に何もファイルを配置しないようにしてください。\n- 成功した場合、終了コードは`0`としてください。\n- 失敗した場合、終了コードは非ゼロとしてください。\n\n**レガシープラグイン**\n\nこのスクリプトはすべてのプラグインで _必須_ とされていますが、このスクリプトが導入される以前の\"レガシー\"プラグインでは、 _オプション_ となっていました。\n\nこのスクリプトが存在しない場合、asdfは`bin/install`スクリプトがあると想定して、バージョンのダウンロード、**かつ**、インストールが実行されます。\n\nレガシープラグインのサポートは最終的に削除される予定のため、今後作成するすべてのプラグインでこのスクリプトを含めるようにしてください。\n\n**スクリプトで使用できる環境変数**\n\n- `ASDF_INSTALL_TYPE`: `version`または`ref`です。。\n- `ASDF_INSTALL_VERSION`:\n  - `ASDF_INSTALL_TYPE=version`の場合、バージョンのフルナンバーです。\n  - `ASDF_INSTALL_TYPE=ref`の場合、Gitのref (tag/commit/branch)です。\n- `ASDF_INSTALL_PATH`: ツールがインストール _されている_ 場所、またはインストール _されるべき_ 場所へのパスです。\n- `ASDF_DOWNLOAD_PATH`: ソースコードまたはバイナリのダウンロード先のパスです。\n\n**このスクリプトを呼び出すコマンド**\n\n- `asdf install <tool> [version]`\n- `asdf install <tool> latest[:version]`\n- `asdf install nodejs 18.0.0`: Node.jsのバージョン`18.0.0`のソースコードまたはバイナリをダウンロードし、`ASDF_DOWNLOAD_PATH`ディレクトリに配置します。\n  そして`bin/install`スクリプトを実行します。\n\n**asdfからの呼び出しシグネチャ**\n\n引数はありません。\n\n```bash\n\"${plugin_path}\"/bin/download\n```\n\n---\n\n### `bin/install` <Badge type=\"tip\" text=\"必須\" vertical=\"middle\" />\n\n**説明**\n\nツールの特定バージョンを指定された場所にインストールします。\n\n**実装内容**\n\n- スクリプトは、指定されたバージョンを`ASDF_INSTALL_PATH`のパスのディレクトリにインストールする必要があります。\n- Shimはデフォルトで、`$ASDF_INSTALL_PATH/bin`内にあるファイルに対して作成されます。\n  この動作は、オプションの[bin/list-bin-paths](#binlist-bin-paths)スクリプトでカスタマイズできます。\n- 成功した場合、終了コードは`0`としてください。\n- 失敗した場合、終了コードは非ゼロとしてください。\n- TOCTOU (Time-of-Check-to-Time-of-Use)の問題を避けるために、ツールのビルドとインストールが成功したとみなされた場合にのみ、`ASDF_INSTALL_PATH`にファイルを配置するようなスクリプトとしてください。\n\n**レガシープラグイン**\n\n`bin/download`スクリプトが存在しない場合、このスクリプトでは、指定されたバージョンをダウンロード、**かつ**、インストールをする必要があります。\n\n`0.7._`以前と`0.8._`以降のasdfコアの互換性を保つために、`ASDF_DOWNLOAD_PATH`環境変数が設定されているかを確認してください。\n設定されている場合は、`bin/download`スクリプトがすでにバージョンをダウンロードしていると想定し、設定されていない場合は、`bin/install`でソースコードをダウンロードするようにしてください。\n\n**スクリプトで使用できる環境変数**\n\n- `ASDF_INSTALL_TYPE`: `version`または`ref`です。\n- `ASDF_INSTALL_VERSION`:\n  - `ASDF_INSTALL_TYPE=version`の場合、バージョンのフルナンバーです。\n  - `ASDF_INSTALL_TYPE=ref`の場合、Gitのref (tag/commit/branch)です。\n- `ASDF_INSTALL_PATH`: ツールがインストール _されている_ 場所、またはインストール _されるべき_ 場所へのパスです。\n- `ASDF_CONCURRENCY`: ソースコードのコンパイル時に使用するコア数です。`make -j`のようなフラグを設定する際に便利です。\n- `ASDF_DOWNLOAD_PATH`: ソースコードまたはバイナリのダウンロード先のパスです。\n\n**このスクリプトを呼び出すコマンド**\n\n- `asdf install`\n- `asdf install <tool>`\n- `asdf install <tool> [version]`\n- `asdf install <tool> latest[:version]`\n- `asdf install nodejs 18.0.0`: `ASDF_INSTALL_PATH`ディレクトリに、\n  Node.jsのバージョン`18.0.0`をインストールします。\n\n**asdfからの呼び出しシグネチャ**\n\n引数はありません。\n\n```bash\n\"${plugin_path}\"/bin/install\n```\n\n## オプションスクリプト\n\n### `bin/latest-stable` <Badge type=\"warning\" text=\"推奨\" vertical=\"middle\" />\n\n**説明**\n\nツールの最新安定バージョンを判定します。このスクリプトが存在しない場合、asdfコアは`bin/list-all`の出力を`tail`した結果をもとに判定しますが、ツールによってはこれが望ましくないことがあります。\n\n**実装内容**\n\n- スクリプトは、ツールの最新安定バージョンを標準出力する必要があります。\n- 非安定版やリリース候補版は除外されるべきです。\n- フィルタクエリは、スクリプトの第1引数で提供されます。このクエリは、バージョン番号やツールプロバイダによる出力をフィルタするために使用されるべきです。\n  - 例えば、[rubyプラグイン](https://github.com/asdf-vm/asdf-ruby)での`asdf list all ruby`の出力は、`jruby`や`rbx`、`truffleruby`などの多くのプロバイダのRubyバージョンをリストアップします。ユーザが提供したフィルタは、セマンティックバージョンやプロバイダをフィルタするために、プラグインで使用できます。\n    ```\n    > asdf latest ruby\n    3.2.2\n    > asdf latest ruby 2\n    2.7.8\n    > asdf latest ruby truffleruby\n    truffleruby+graalvm-22.3.1\n    ```\n- 成功した場合、終了コードは`0`としてください。\n- 失敗した場合、終了コードは非ゼロとしてください。\n\n**スクリプトで使用できる環境変数**\n\n- `ASDF_INSTALL_TYPE`: `version`または`ref`です。\n- `ASDF_INSTALL_VERSION`:\n  - `ASDF_INSTALL_TYPE=version`の場合、バージョンのフルナンバーです。\n  - `ASDF_INSTALL_TYPE=ref`の場合、Gitのref (tag/commit/branch)です。\n- `ASDF_INSTALL_PATH`: ツールがインストール _されている_ 場所、またはインストール _されるべき_ 場所へのパスです。\n\n**このスクリプトを呼び出すコマンド**\n\n- `asdf set <tool> latest`: ツールのバージョンをそのツールの最新安定バージョンに設定します。\n- `asdf install <tool> latest`: ツールの最新安定バージョンをインストールします。\n- `asdf latest <tool> [<version>]`: オプションのフィルタに基づいて、ツールの最新バージョンを出力します。\n- `asdf latest --all`: asdfによって管理されているすべてのツールの最新バージョンと、それらがインストールされているかどうかを出力します。\n\n**asdfからの呼び出しシグネチャ**\n\nこのスクリプトは、フィルタクエリという1つの引数を受け取ります。\n\n```bash\n\"${plugin_path}\"/bin/latest-stable \"$query\"\n```\n\n---\n\n### `bin/help.overview`\n\n**説明**\n\nプラグインおよび管理されているツールに関する概要説明を出力します。\n\n**実装内容**\n\n- このスクリプトは、プラグインのヘルプを表示するために必要です。\n- ヘッダはasdfコア側で表示するため、スクリプト内では表示しないでください。\n- 自由な形式のテキストで出力して構いませんが、短い1段落程度の説明が理想です。\n- コアとなるasdf-vmドキュメントですでに説明されている情報は出力しないでください。\n- オペレーティングシステムと、インストールされているツールのバージョンに合わせて出力を調整する必要があります(必要に応じて、`ASDF_INSTALL_VERSION`および`ASDF_INSTALL_TYPE`環境変数の値を使用してください)。\n- 成功した場合、終了コードは`0`としてください。\n- 失敗した場合、終了コードは非ゼロとしてください。\n\n**スクリプトで使用できる環境変数**\n\n- `ASDF_INSTALL_TYPE`: `version`または`ref`です。\n- `ASDF_INSTALL_VERSION`:\n  - `ASDF_INSTALL_TYPE=version`の場合、バージョンのフルナンバーです。\n  - `ASDF_INSTALL_TYPE=ref`の場合、Gitのref (tag/commit/branch)です。\n- `ASDF_INSTALL_PATH`: ツールがインストール _されている_ 場所、またはインストール _されるべき_ 場所へのパスです。\n\n**このスクリプトを呼び出すコマンド**\n\n- `asdf help <name> [<version>]`: プラグインおよびツールのドキュメントを出力します。\n\n**asdfからの呼び出しシグネチャ**\n\n```bash\n\"${plugin_path}\"/bin/help.overview\n```\n\n---\n\n### `bin/help.deps`\n\n**説明**\n\nオペレーティングシステムに合わせた依存関係のリストを出力します。依存関係を1行ごとに出力します。\n\n```bash\ngit\ncurl\nsed\n```\n\n**実装内容**\n\n- このスクリプトの出力を考慮するために、`bin/help.overview`を用意する必要があります。\n- オペレーティングシステムと、インストールされているツールのバージョンに合わせて出力を調整する必要があります(必要に応じて、`ASDF_INSTALL_VERSION`および`ASDF_INSTALL_TYPE`環境変数の値を使用してください)。\n- 成功した場合、終了コードは`0`としてください。\n- 失敗した場合、終了コードは非ゼロとしてください。\n\n**スクリプトで使用できる環境変数**\n\n- `ASDF_INSTALL_TYPE`: `version`または`ref`です。\n- `ASDF_INSTALL_VERSION`:\n  - `ASDF_INSTALL_TYPE=version`の場合、バージョンのフルナンバーです。\n  - `ASDF_INSTALL_TYPE=ref`の場合、Gitのref (tag/commit/branch)です。\n- `ASDF_INSTALL_PATH`: ツールがインストール _されている_ 場所、またはインストール _されるべき_ 場所へのパスです。\n\n**このスクリプトを呼び出すコマンド**\n\n- `asdf help <name> [<version>]`: プラグインおよびツールのドキュメントを出力します。\n\n**asdfからの呼び出しシグネチャ**\n\n```bash\n\"${plugin_path}\"/bin/help.deps\n```\n\n---\n\n### `bin/help.config`\n\n**説明**\n\nプラグインおよびツールで設定必須または任意設定可能な構成設定一覧を出力します。例えば、ツールのインストール・コンパイルに必要な環境変数やその他フラグについて説明します。\n\n**実装内容**\n\n- このスクリプトの出力を考慮するために、`bin/help.overview`を用意する必要があります。\n- 自由な形式のテキストで出力できます。\n- オペレーティングシステムと、インストールされているツールのバージョンに合わせて出力を調整する必要があります(必要に応じて、`ASDF_INSTALL_VERSION`および`ASDF_INSTALL_TYPE`環境変数の値を使用してください)。\n- 成功した場合、終了コードは`0`としてください。\n- 失敗した場合、終了コードは非ゼロとしてください。\n\n**スクリプトで使用できる環境変数**\n\n- `ASDF_INSTALL_TYPE`: `version`または`ref`です。\n- `ASDF_INSTALL_VERSION`:\n  - `ASDF_INSTALL_TYPE=version`の場合、バージョンのフルナンバーです。\n  - `ASDF_INSTALL_TYPE=ref`の場合、Gitのref (tag/commit/branch)です。\n- `ASDF_INSTALL_PATH`: ツールがインストール _されている_ 場所、またはインストール _されるべき_ 場所へのパスです。\n\n**このスクリプトを呼び出すコマンド**\n\n- `asdf help <name> [<version>]`: プラグインおよびツールのドキュメントを出力します。\n\n**asdfからの呼び出しシグネチャ**\n\n```bash\n\"${plugin_path}\"/bin/help.config\n```\n\n---\n\n### `bin/help.links`\n\n**説明**\n\nプラグインとツールに関連するリンクリストを出力します。リンクを1行ごとに出力します。\n\n```bash\nGit Repository:\thttps://github.com/vlang/v\nDocumentation:\thttps://vlang.io\n```\n\n**実装内容**\n\n- このスクリプトの出力を考慮するために、`bin/help.overview`を用意する必要があります。\n- リンクを1行ごとに出力してください。\n- 形式は以下のいずれかである必要があります:\n  - `<title>: <link>`\n  - または`<link>`のみ\n- オペレーティングシステムと、インストールされているツールのバージョンに合わせて出力を調整する必要があります(必要に応じて、`ASDF_INSTALL_VERSION`および`ASDF_INSTALL_TYPE`環境変数の値を使用してください)。\n- 成功した場合、終了コードは`0`としてください。\n- 失敗した場合、終了コードは非ゼロとしてください。\n\n**スクリプトで使用できる環境変数**\n\n- `ASDF_INSTALL_TYPE`: `version`または`ref`です。\n- `ASDF_INSTALL_VERSION`:\n  - `ASDF_INSTALL_TYPE=version`の場合、バージョンのフルナンバーです。\n  - `ASDF_INSTALL_TYPE=ref`の場合、Gitのref (tag/commit/branch)です。\n- `ASDF_INSTALL_PATH`: ツールがインストール _されている_ 場所、またはインストール _されるべき_ 場所へのパスです。\n\n**このスクリプトを呼び出すコマンド**\n\n- `asdf help <name> [<version>]`: プラグインおよびツールのドキュメントを出力します。\n\n**asdfからの呼び出しシグネチャ**\n\n```bash\n\"${plugin_path}\"/bin/help.links\n```\n\n---\n\n### `bin/list-bin-paths`\n\n**説明**\n\nツールの特定バージョンにおける、実行ファイルが含まれるディレクトリの一覧を出力します。\n\n**実装内容**\n\n- このスクリプトが存在しない場合、asdfは`\"${ASDF_INSTALL_PATH}\"/bin`ディレクトリ内にあるバイナリを探し、そのバイナリ向けのShimを作成します。\n- 実行ファイルが含まれるディレクトリのパスをスペース区切りで出力してください。\n- パスは`ASDF_INSTALL_PATH`からの相対パスである必要があります。例えば、次のような出力となります:\n\n```bash\nbin tools veggies\n```\n\n以上の場合、下記ディレクトリ内のファイルへのShimを作成するよう、asdfへ指示されます:\n- `\"${ASDF_INSTALL_PATH}\"/bin`\n- `\"${ASDF_INSTALL_PATH}\"/tools`\n- `\"${ASDF_INSTALL_PATH}\"/veggies`\n\n**スクリプトで使用できる環境変数**\n\n- `ASDF_INSTALL_TYPE`: `version`または`ref`です。\n- `ASDF_INSTALL_VERSION`:\n  - `ASDF_INSTALL_TYPE=version`の場合、バージョンのフルナンバーです。\n  - `ASDF_INSTALL_TYPE=ref`の場合、Gitのref (tag/commit/branch)です。\n- `ASDF_INSTALL_PATH`: ツールがインストール _されている_ 場所、またはインストール _されるべき_ 場所へのパスです。\n\n**このスクリプトを呼び出すコマンド**\n\n- `asdf install <tool> [version]`: バイナリへのShimを初期作成します。\n- `asdf reshim <tool> <version>`: バイナリへのShimを再作成します。\n\n**asdfからの呼び出しシグネチャ**\n\n```bash\n\"${plugin_path}/bin/list-bin-paths\"\n```\n\n---\n\n### `bin/exec-env`\n\n**説明**\n\nツールのバイナリのShimを実行する前に環境を準備します。\n\n**スクリプトで使用できる環境変数**\n\n- `ASDF_INSTALL_TYPE`: `version`または`ref`です。\n- `ASDF_INSTALL_VERSION`:\n  - `ASDF_INSTALL_TYPE=version`の場合、バージョンのフルナンバーです。\n  - `ASDF_INSTALL_TYPE=ref`の場合、Gitのref (tag/commit/branch)です。\n- `ASDF_INSTALL_PATH`: ツールがインストール _されている_ 場所、またはインストール _されるべき_ 場所へのパスです。\n\n**このスクリプトを呼び出すコマンド**\n\n- `asdf which <command>`: 実行ファイルのパスを表示します。\n- `asdf exec <command> [args...]`: 現在のバージョンでShimコマンドを実行します。\n- `asdf env <command> [util]`: Shimコマンドの実行時に使用される環境において、util(デフォルト: `env`)を実行します。\n\n**asdfからの呼び出しシグネチャ**\n\n```bash\n\"${plugin_path}/bin/exec-env\"\n```\n\n---\n\n### `bin/exec-path`\n\nツールの特定バージョンの実行ファイルパスを取得します。\n実行ファイルへの相対パスを文字列で出力する必要があります。\nこれにより、プラグインはShimで指定された実行ファイルパスを条件付きで上書きして返すか、\nそうでなければ、Shimで指定されたデフォルトのパスを返すことができます。\n\n**説明**\n\nツールの特定バージョンの実行ファイルパスを取得します。\n\n**実装内容**\n\n- 実行ファイルへの相対パスを文字列で出力する必要があります。\n- Shimで指定された実行ファイルパスを条件付きで上書きして返すか、そうでなければ、Shimで指定されたデフォルトのパスを返してください。\n\n```shell\nUsage:\n  plugin/bin/exec-path <install-path> <command> <executable-path>\n\nExample Call:\n  ~/.asdf/plugins/foo/bin/exec-path \"~/.asdf/installs/foo/1.0\" \"foo\" \"bin/foo\"\n\nOutput:\n  bin/foox\n```\n\n**スクリプトで使用できる環境変数**\n\n- `ASDF_INSTALL_TYPE`: `version`または`ref`です。\n- `ASDF_INSTALL_VERSION`:\n  - `ASDF_INSTALL_TYPE=version`の場合、バージョンのフルナンバーです。\n  - `ASDF_INSTALL_TYPE=ref`の場合、Gitのref (tag/commit/branch)です。\n- `ASDF_INSTALL_PATH`: ツールがインストール _されている_ 場所、またはインストール _されるべき_ 場所へのパスです。\n\n**このスクリプトを呼び出すコマンド**\n\n- `asdf which <command>`: 実行ファイルのパスを表示します。\n- `asdf exec <command> [args...]`: 現在のバージョンでShimコマンドを実行します。\n- `asdf env <command> [util]`: Shimコマンドの実行時に使用される環境において、util(デフォルト: `env`)を実行します。\n\n**asdfからの呼び出しシグネチャ**\n\n```bash\n\"${plugin_path}/bin/exec-path\" \"$install_path\" \"$cmd\" \"$relative_path\"\n```\n\n---\n\n### `bin/uninstall`\n\n**説明**\n\nツールの特定バージョンをアンインストールします。\n\n**出力フォーマット**\n\nユーザへの出力は、`stdout`または`stderr`へ適切に送信してください。後続のコア実行によってこれらの出力が読み取られることはありません。\n\n**スクリプトで使用できる環境変数**\n\nこのスクリプトに環境変数は提供されません。\n\n**このスクリプトを呼び出すコマンド**\n\n- `asdf list all <name> <version>`\n- `asdf uninstall nodejs 18.15.0`: nodejsのバージョン`18.15.0`をアンインストールし、`npm i -g`でグローバルにインストールしたものを含むすべてのShimを削除します。\n\n**asdfからの呼び出しシグネチャ**\n\n引数はありません。\n\n```bash\n\"${plugin_path}/bin/uninstall\"\n```\n\n---\n\n### `bin/list-legacy-filenames`\n\n**説明**\n\nツールのバージョンを決定するために使用されるレガシー構成ファイルのリストを出力します。\n\n**実装内容**\n\n- スペース区切りのファイル名リストを出力してください。\n  ```bash\n  .ruby-version .rvmrc\n  ```\n- この内容は、`\"${HOME}\"/.asdfrc`内の`legacy_version_file`オプションを有効にしたユーザにのみ適用されます。\n\n**スクリプトで使用できる環境変数**\n\n- `ASDF_INSTALL_TYPE`: `version`または`ref`です。\n- `ASDF_INSTALL_VERSION`:\n  - `ASDF_INSTALL_TYPE=version`の場合、バージョンのフルナンバーです。\n  - `ASDF_INSTALL_TYPE=ref`の場合、Gitのref (tag/commit/branch)です。\n- `ASDF_INSTALL_PATH`: ツールがインストール _されている_ 場所、またはインストール _されるべき_ 場所へのパスです。\n\n**このスクリプトを呼び出すコマンド**\n\nツールのバージョンを読み込むすべてのコマンドから呼び出されます。\n\n**asdfからの呼び出しシグネチャ**\n\n引数はありません。\n\n```bash\n\"${plugin_path}/bin/list-legacy-filenames\"\n```\n\n---\n\n### `bin/parse-legacy-file`\n\n**説明**\n\nasdfによって発見されたレガシーファイルをパースして、ツールのバージョンを決定します。JavaScriptの`package.json`や、Go言語の`go.mod`のようなファイルから、バージョン番号を抽出するのに役立ちます。\n\n**実装内容**\n\n- このスクリプトが存在しない場合、asdfは単純にレガシーファイルを`cat`してバージョンを決定します。\n- **決定論的**で、常に正確で同じバージョンを返す必要があります:\n  - 同じレガシーファイルを解析したら、同じバージョンを返すようにしてください。\n  - マシンに何がインストールされているか、また、レガシーバージョンが有効で完全かどうかは関係ありません。一部のレガシーファイルのフォーマットは適切でないときもあります。\n- 下記のように、バージョン番号を1行で出力してください:\n  ```bash\n  1.2.3\n  ```\n\n**スクリプトで使用できる環境変数**\n\nこのスクリプトが呼び出される前に、環境変数が設定されることはありません。\n\n**このスクリプトを呼び出すコマンド**\n\nツールのバージョンを読み込むすべてのコマンドから呼び出されます。\n\n**asdfからの呼び出しシグネチャ**\n\nこのスクリプトは、レガシーファイルの内容を読み込むために、レガシーファイルのパスという1つの引数を受け取ります。\n\n```bash\n\"${plugin_path}/bin/parse-legacy-file\" \"$file_path\"\n```\n\n---\n\n### `bin/post-plugin-add`\n\n**説明**\n\nこのスクリプトは、asdfの`asdf plugin add <tool>`コマンドで、プラグインが _追加_ された **後に** 呼び出されます。\n\n関連するコマンドフックについても参照してください:\n\n- `pre_asdf_plugin_add`\n- `pre_asdf_plugin_add_${plugin_name}`\n- `post_asdf_plugin_add`\n- `post_asdf_plugin_add_${plugin_name}`\n\n**スクリプトで使用できる環境変数**\n\n- `ASDF_PLUGIN_PATH`: プラグインがインストールされている場所へのパスです。\n- `ASDF_PLUGIN_SOURCE_URL`: プラグインソースのURLです。ローカルディレクトリパスを指定することもできます。\n\n**asdfからの呼び出しシグネチャ**\n\n引数はありません。\n\n```bash\n\"${plugin_path}/bin/post-plugin-add\"\n```\n\n---\n\n### `bin/post-plugin-update`\n\n**説明**\n\nこのスクリプトは、asdfの`asdf plugin update <tool> [<git-ref>]`コマンドで、 _更新_ されたプラグインがダウンロードされた **後に** 呼び出されます。\n\n関連するコマンドフックについても参照してください:\n\n- `pre_asdf_plugin_updated`\n- `pre_asdf_plugin_updated_${plugin_name}`\n- `post_asdf_plugin_updated`\n- `post_asdf_plugin_updated_${plugin_name}`\n\n**スクリプトで使用できる環境変数**\n\n- `ASDF_PLUGIN_PATH`: プラグインがインストールされている場所へのパスです。\n- `ASDF_PLUGIN_PREV_REF`: プラグインの以前のgit-refです。\n- `ASDF_PLUGIN_POST_REF`: 更新後のプラグインのgit-refです。\n\n**asdfからの呼び出しシグネチャ**\n\n引数はありません。\n\n```bash\n\"${plugin_path}/bin/post-plugin-update\"\n```\n\n---\n\n### `bin/pre-plugin-remove`\n\n**説明**\n\nこのスクリプトは、asdfの`asdf plugin remove <tool>`コマンドで、プラグインが _削除_ される **前に** 呼び出されます。\n\n関連するコマンドフックについても参照してください:\n\n- `pre_asdf_plugin_remove`\n- `pre_asdf_plugin_remove_${plugin_name}`\n- `post_asdf_plugin_remove`\n- `post_asdf_plugin_remove_${plugin_name}`\n\n**スクリプトで使用できる環境変数**\n\n- `ASDF_PLUGIN_PATH`: プラグインがインストールされている場所へのパスです。\n\n**asdfからの呼び出しシグネチャ**\n\n引数はありません。\n\n```bash\n\"${plugin_path}/bin/pre-plugin-remove\"\n```\n\n<!-- TODO: document command hooks -->\n<!-- ## Command Hooks -->\n\n## asdf CLIの拡張コマンド <Badge type=\"danger\" text=\"高度\" vertical=\"middle\" />\n\nプラグイン名をサブコマンドとして使用し、\nasdfコマンドラインインターフェースを通して呼び出すことのできる`lib/commands/command*.bash`スクリプトまたは実行ファイルを用意することで、\n新しいasdfコマンドを定義することができます。\n\n例えば、`foo`というプラグインがあるとすると:\n\n```shell\nfoo/\n  lib/commands/\n    command.bash\n    command-bat.bash\n    command-bat-man.bash\n    command-help.bash\n```\n\nユーザは下記コマンドが実行できるようになります:\n\n```shell\n$ asdf foo         # same as running `$ASDF_DATA_DIR/plugins/foo/lib/commands/command.bash`\n$ asdf foo bar     # same as running `$ASDF_DATA_DIR/plugins/foo/lib/commands/command.bash bar`\n$ asdf foo help    # same as running `$ASDF_DATA_DIR/plugins/foo/lib/commands/command-help.bash`\n$ asdf foo bat man # same as running `$ASDF_DATA_DIR/plugins/foo/lib/commands/command-bat-man.bash`\n$ asdf foo bat baz # same as running `$ASDF_DATA_DIR/plugins/foo/lib/commands/command-bat.bash baz`\n```\n\nプラグイン開発者はこの機能を使って、ツールに関連するユーティリティを提供したり、\nasdf自体のコマンド拡張プラグインを作成したりすることができます。\n\n実行可能ビット(executable bit)が付与されている場合、\nasdfの実行に代わって、当該スクリプトが実行されます。\n\n実行可能ビット(executable bit)が付与されていない場合、asdfは当該スクリプトをBashスクリプトとしてsourceします。\n\n`$ASDF_CMD_FILE`環境変数は、ソースとなるファイルのフルパスに解決されます。\n\n[`haxe`](https://github.com/asdf-community/asdf-haxe)は、\nこの機能使ったプラグインの素晴らしい例です。\nこのプラグインは、`asdf haxe neko-dylibs-link`を提供しており、\nHaxeの実行ファイルが実行ディレクトリから相対的に動的ライブラリを見つけようとしてしまう問題を修正します。\n\nプラグインのREADMEには、asdf拡張コマンドに関することを必ず記載するようにしてください。\n\n## カスタムShimテンプレート <Badge type=\"danger\" text=\"高度\" vertical=\"middle\" />\n\n::: warning 警告\n\n**どうしても**必要な場合にのみ使用してください。\n\n:::\n\nasdfでは、カスタムShimテンプレートを使用することができます。\n`foo`という実行ファイルに対して、プラグイン内に`shims/foo`ファイルが存在すれば、\nasdfは標準Shimテンプレートを使用する代わりに、そのファイルをコピーします。\n\n**この機能は賢く使う必要があります。**\n\nasdfコアチームが把握している限り、\nこの機能は公式プラグインである[Elixirプラグイン](https://github.com/asdf-vm/asdf-elixir)でのみ使用されています。\n実行ファイルは、実行ファイルであると同時に、Elixirファイルとしても読み込まれます。\nそのため、標準的なBashのShimを使用できないのです。\n\n## テスト\n\nasdfでは、プラグインをテストするための`plugin-test`コマンドを用意しており、下記のように使用できます:\n\n```shell\nasdf plugin test <plugin_name> <plugin_url> [--asdf-tool-version <version>] [--asdf-plugin-gitref <git_ref>] [test_command...]\n```\n\n- `<plugin_name>`と`<plugin_url>`は必須です。\n- オプションで`[--asdf-tool-version <version>]`を指定すると、そのバージョンのツールがインストールされます。\n  デフォルトは、`asdf latest <plugin-name>`です。\n- オプションで`[--asdf-plugin-gitref <git_ref>]`を指定すると、\n  そのコミット/ブランチ/タグでプラグイン自体をチェックアウトします。\n  これは、プラグインのCIにおいて、プルリクエストをテストする際に便利です。デフォルトは、プラグインリポジトリのデフォルトブランチとなります。\n- オプションの`[test_command...]`パラメータは、インストールしたツールが正しく動作するかを確認するために実行するコマンドです。\n  通常は、`<tool> --version`または`<tool> --help`となります。\n  例えば、NodeJSプラグインをテストするときは、次のように実行します:\n  ```shell\n  # asdf plugin test <plugin_name>  <plugin_url>                               [test_command]\n    asdf plugin test nodejs         https://github.com/asdf-vm/asdf-nodejs.git node --version\n  ```\n\n::: tip 備考\n\nLinuxとmacOSの両方のCI環境でテストすることを推奨します。\n\n:::\n\n### GitHub Action\n\n[asdf-vm/actions](https://github.com/asdf-vm/actions)リポジトリでは、\nGitHub上でホストされているプラグインをテストするためのGitHub Actionを提供しています。\n`.github/workflows/test.yamlのActionsワークフローの例は以下のとおりです:\n\n```yaml\nname: Test\non:\n  push:\n    branches:\n      - main\n  pull_request:\n\njobs:\n  plugin_test:\n    name: asdf plugin test\n    strategy:\n      matrix:\n        os:\n          - ubuntu-latest\n          - macos-latest\n    runs-on: ${{ matrix.os }}\n    steps:\n      - name: asdf_plugin_test\n        uses: asdf-vm/actions/plugin-test@v2\n        with:\n          command: \"<MY_TOOL> --version\"\n```\n\n### TravisCI 構成設定\n\n以下は、`.travis.yml`ファイルの例です。必要に応じてカスタマイズしてください:\n\n```yaml\nlanguage: c\nscript: asdf plugin test <MY_TOOL> $TRAVIS_BUILD_DIR '<MY_TOOL> --version'\nbefore_script:\n  - git clone https://github.com/asdf-vm/asdf.git asdf\n  - . asdf/asdf.sh\nos:\n  - linux\n  - osx\n```\n\n::: tip 備考\n\n他のCIを使用する場合、\nプラグインの場所への相対パスを渡す必要がある場合があります:\n\n```shell\nasdf plugin test <tool_name> <path> '<tool_command> --version'\n```\n\n:::\n\n## APIレート制限\n\n`bin/list-all`や`bin/latest-stable`のように、コマンドが外部APIへのアクセスに依存している場合、\n自動テスト中にレート制限が発生することがあります。\nこれを軽減するため、環境変数経由で認証トークンを提供するコードパスがあることを確認してください。\n以下に例を示します:\n\n```shell\ncmd=\"curl --silent\"\nif [ -n \"$GITHUB_API_TOKEN\" ]; then\n cmd=\"$cmd -H 'Authorization: token $GITHUB_API_TOKEN'\"\nfi\n\ncmd=\"$cmd $releases_path\"\n```\n\n### `GITHUB_API_TOKEN`\n\n`GITHUB_API_TOKEN`を利用する際は、\nまず、\n`public_repo`アクセスのみをもつ[新しいパーソナルトークン](https://github.com/settings/tokens/new)を作成してください。\n\n次に、このトークンをCIパイプライン環境変数に追加してください。\n\n::: warning 警告\n\n認証トークンをコードリポジトリで公開してはいけません。\n\n:::\n\n## プラグインショートネームインデックス\n\n::: tip ヒント\n\n推奨されるプラグインのインストール方法は、URLをもとに直接インストールする方法です:\n\n```shell\n# asdf plugin add <name> <git_url>\n  asdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs\n```\n\n:::\n\nasdfの各種コマンドで`git_url`が指定されなかった場合、\nasdfは正確な`git_url`を決定するために、\n[ショートネームインデックスリポジトリ](https://github.com/asdf-vm/asdf-plugins)を使用します。\n\nこのリポジトリの指示に従うことで、\nあなたが作成したプラグインを、\n[ショートネームインデックス](https://github.com/asdf-vm/asdf-plugins)に追加することができます。\n"
  },
  {
    "path": "docs/ko-kr/contribute/core.md",
    "content": "# asdf\n\n`asdf` 코어 기여 가이드.\n\n## 초기 설정\n\nGithub의 `asdf`를 fork하거나 clone하세요:\n\n```shell\n# clone your fork\ngit clone https://github.com/<GITHUB_USER>/asdf.git\n# or clone asdf\ngit clone https://github.com/asdf-vm/asdf.git\n```\n\n코어 개발을 위한 툴들은 이 리포지토리의 `.tool-versions`에 있습니다. 만약 `asdf`가 직접 관리하기를 바라신다면, 다음 플러그인들을 설치하세요:\n\n```shell\nasdf plugin add bats https://github.com/timgluz/asdf-bats.git\nasdf plugin add shellcheck https://github.com/luizm/asdf-shellcheck.git\nasdf plugin add shfmt https://github.com/luizm/asdf-shfmt.git\n```\n\n`asdf`를 개발하기 위한 버전들을 설치하세요:\n\n```shell\nasdf install\n```\n\n로컬 머신에서 개발 도구를 손상시킬 수 있는 기능을 개발 중일 경우 `asdf`를 사용하지 않는 것이 _좋을 수_ 있습니다. 개발 도구들의 원본 목록입니다:\n\n- [bats-core](https://github.com/bats-core/bats-core): Bash 또는 POSIX 준수 스크립트를 단위 테스트하기 위한 Bash 자동화 테스트 시스템.\n- [shellcheck](https://github.com/koalaman/shellcheck): 셸 스크립트 정적 분석 도구.\n- [shfmt](https://github.com/mvdan/sh): Bash를 지원하는 셸 parser, formatter, interpreter; shfmt 포함\n\n## 개발\n\n만약 설치된 `asdf`에 영향 없이 변화들을 테스트해보시고 싶으시다면, `$ASDF_DIR` 변수를 리포지토리를 clone한 경로에 지정하시고, 그 다음 임시로 `bin`와 `shims` 디렉토리들을 경로 앞에 추가하세요.\n\n원격 리포지토리에 커밋 혹은 push하기 전에, 당신의 코드를 format, lint, 그리고 locally test하세요. 다음 스크립트/명령어들을 사용하세요:\n\n```shell\n# Lint\n./scripts/lint.bash --check\n\n# Fix & Format\n./scripts/lint.bash --fix\n\n# Test: all tests\n./scripts/test.bash\n\n# Test: for specific command\nbats test/list_commands.bash\n```\n\n::: tip\n\n**테스트 추가!** - 새로운 기능들과 버그 수정들의 리뷰 속도 향상을 위해 테스트들은 **필수적**입니다. 풀 리퀘스트 요청을 만드시기 전에 새로운 코드 경로들을 추가해주세요. [bats-core 문서](https://bats-core.readthedocs.io/en/stable/index.html) 참조\n\n:::\n\n### Gitignore\n\n다음은 `asdf-vm/asdf` 리포지토리에 `.gitignore` 파일입니다. 우리는 프로젝트에 관련된 특정한 파일들을 무시합니다. 운영체제, 툴, workflows에 관련된 파일들은 글로벌 `.gitignore` 설정에서 무시되어야합니다, [더 보기](http://stratus3d.com/blog/2018/06/03/stop-excluding-editor-temp-files-in-gitignore/).\n\n@/../.gitignore\n\n### `.git-blame-ignore-revs`\n\n`asdf`는 `.git-blame-ignore-revs` 사용해 blame 실행에서 잡음을 줄입니다. 더 많은 정보 [git blame 문서](https://git-scm.com/docs/git-blame).\n\n다음과 같이 `git blame`과 `.git-blame-ignore-revs`을 사용:\n\n```sh\ngit blame --ignore-revs-file .git-blame-ignore-revs ./test/install_command.bats\n```\n\n선택적으로, 수동적으로 파일을 제공하는 대신 모든 `blame`에서 해당 파일을 사용하도록 설정:\n\n```sh\ngit config blame.ignoreRevsFile .git-blame-ignore-revs\n```\n\nIDE들에서 이 파일을 사용하도록 설정할 수 있습니다. 예를 들어, VSCode를 사용하실 경우 ([GitLens](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens)와 함께), 다음을 `.vscode/settings.json`에 추가하세요:\n\n```json\n{\n  \"gitlens.advanced.blame.customArguments\": [\n    \"--ignore-revs-file\",\n    \".git-blame-ignore-revs\"\n  ]\n}\n```\n\n## Bats 테스팅\n\n로컬 테스트 실행:\n\n```shell\n./scripts/test.bash\n```\n\n테스트 작성 전 **반드시 읽기**:\n\n- `test/`에 존재하는 테스트들\n- [bats-core 문서](https://bats-core.readthedocs.io/en/stable/index.html)\n- `scripts/test.bash`에 존재하는 Bats 설정들\n\n### Bats 팁\n\nBats 디버깅은 때에 따라 어려울 수 있습니다. 디버깅 단순화를 위해, `-t` flag로 TAP output을 사용하여 테스트 실행 중 결과물 출력을 위한 특별한 파일 디스크립터 `>&3`를 사용할 수 있습니다. 예시:\n\n```shell\n# test/some_tests.bats\n\nprintf \"%s\\n\" \"Will not be printed during bats test/some_tests.bats\"\nprintf \"%s\\n\" \"Will be printed during bats -t test/some_tests.bats\" >&3\n```\n\nbats-core의 더 자세한 문서 [Printing to the Terminal](https://bats-core.readthedocs.io/en/stable/writing-tests.html#printing-to-the-terminal).\n\n## 풀 리퀘스트, 릴리스 & 관습적 커밋\n\n`asdf`는 자동화 배포 툴 [Release Please](https://github.com/googleapis/release-please)를 사용하여 자동으로 [SemVer](https://semver.org/) 버전을 올리고 [변동사항](https://github.com/asdf-vm/asdf/blob/master/CHANGELOG.md)을 작성합니다. 이 정보들은 지난 배포들로부터 커밋 history를 읽음으로써 결정됩니다.\n\n[유의적 커밋 메세지](https://www.conventionalcommits.org/)는 기본 브랜치의 커밋 메세지 형식이 되는 풀 리퀘스트 제목의 형식을 정의합니다. 이것은 GitHub Action에서 강제됩니다 [`amannn/action-semantic-pull-request`](https://github.com/amannn/action-semantic-pull-request).\n\n관습적인 커밋 다음 형식을 따릅니다:\n\n```\n<type>[optional scope][optional !]: <description>\n\n<!-- examples -->\nfix: some fix\nfeat: a new feature\ndocs: some documentation update\ndocs(website): some change for the website\nfeat!: feature with breaking change\n```\n\n`<types>`의 모든 목록: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`.\n\n- `!`:  주요한(breaking) 변화들을 나타냅니다\n- `fix`: 새로운 SemVer `patch`을 만듭니다\n- `feat`: 새로운 SemVer `minor`을 만듭니다\n- `<type>!`: 새로운 SemVer `major`을 만듭니다\n\n풀 리퀘스트 제목은 반드시 이 형식을 따라야 합니다.\n\n::: tip\n\n풀 리퀘스트 제목을 관습적 커밋 메세지 형식을 사용하세요.\n\n:::\n\n## Docker 이미지\n\n[asdf-alpine](https://github.com/vic/asdf-alpine)와 [asdf-ubuntu](https://github.com/vic/asdf-ubuntu) 프로젝트들은 asdf 툴들의 Dockerized 이미지들을 제공하기 위해 진행되고있습니다. 개발 서버의 베이스 혹은 프로덕션 앱들을 위해 docker 이미지들을 사용할 수 있습니다.\n"
  },
  {
    "path": "docs/ko-kr/contribute/documentation.md",
    "content": "# 문서 & 사이트\n\n문서 & 사이트 기여 가이드.\n\n## 초기 세팅\n\nGithub의 `asdf` fork 그리고/혹은 기본 브랜치 Git clone:\n\n```shell\n# clone your fork\ngit clone https://github.com/<GITHUB_USER>/asdf.git\n# or clone asdf\ngit clone https://github.com/asdf-vm/asdf.git\n```\n\n문서 사이트 개발을 위한 도구들은 `asdf`의 `docs/.tool-versions`에서 관리되고 있습니다. 플러그인들을 추가하기:\n\n```shell\nasdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs\n```\n\n툴 버전들을 설치하기:\n\n```shell\nasdf install\n```\n\n- [Node.js](https://nodejs.org): Chrome의 V8 JavaScript 엔진을 기반으로 구축된 JavaScript 런타임.\n\n`docs/package.json`로부터 Node.js dependencies 설치하기:\n\n```shell\nnpm install\n```\n\n## 개발\n\n[VitePress (v2)](https://vitepress.dev/)는 우리가 asdf 문서 사이트를 빌드하기 위해 사용하는 정적 사이트 생성기(SSG)입니다. 이는 사용자가 JavaScript를 사용중이지 않을때도 HTML 폴백을 지원하고, [Docsify.js](https://docsify.js.org/)와 결과적으로 VuePress를 대체하기 위해 선택되었습니다. 이는 VuePress로부터 대체된 Docsify & VitePress가 아니면 불가능했을 것입니다. 이것을 제외하면, 최소한의 설정과 함께 마크다운 작성에 집중하는 feature-set은 대부분 비슷합니다.\n\n`package.json`은 개발에 필요한 스크립트들을 포함합니다:\n\n@[code json{3-5}](../../package.json)\n\n로컬 개발 서버 시작하기:\n\n```shell\nnpm run dev\n```\n\n커밋 전 코드 형식 맞추기:\n\n```shell\nnpm run format\n```\n\n## 풀 리퀘스트, 릴리스 & 관습적 커밋\n\n`asdf`는 PR 제목들의 관습적인 커밋들에 의존하는 자동화된 배포 pipeline을 사용하고 있습니다. 더 자세한 문서는 [코어 기여 가이드](./core.md)에서 찾을 수 있습니다.\n\n문서 업데이트를 위한 PR을 만드실때는, PR `docs: <description>` 형식인 관습적인 커밋 타입 `docs` 제목을 만들어주세요.\n\n## Vitepress\n\n사이트의 설정은 설정을 대표하는 JS 오브젝트의 TypeScript 파일들로 구성되어 있습니다. 그 파일들은 다음과 같습니다:\n\n- `docs/.vitepress/config.js`: 사이트를 위한 root 설정 파일. [VitePress 문서](https://vitepress.dev/reference/site-config) 참조.\n\nroot 설정 단순화를 위해, _navbar_ 와 _sidebar_ 를 대표하는 더 큰 JS 객체가 추출되었고 로케일로 구분되었습니다. 다음을 참조하세요: \n\n- `docs/.vitepress/navbars.js`\n- `docs/.vitepress/sidebars.js`\n\n[기본 테마 참고자료](https://vitepress.dev/reference/default-theme-config)에서 위 설정들의 공식 문서를 보실 수 있습니다.\n\n## I18n\n\nVitePress는 국제화를 공식적으로 지원합니다.\nroot 설정 `docs/.vitepress/config.js`는 선택 dropdown에서의 지원되는 로케일들의 URL, 제목과 navbar/sidebar의 설정 레퍼런스들을 정의합니다.\n\nnavbar/sidebar 설정들은 앞서 언급한 로케일 별로 나누어지고 내보내기된 설정파일들에 의해 결정됩니다.\n\n각 로케일을 위한 Markdown 내용은 반드시 root 설정안에 `locales`의 키들과 같은 이름의 폴더에 위치해야합니다. 다시 말해서: \n\n```js\n// docs/.vitepress/config.js\nexport default defineConfig({\n  ...\n  locales: {\n    root: {\n      label: \"English\",\n        lang: \"en-US\",\n        themeConfig: {\n        nav: navbars.en,\n          sidebar: sidebars.en,\n      },\n    },\n    \"pt-br\": {\n      label: \"Brazilian Portuguese\",\n        lang: \"pr-br\",\n        themeConfig: {\n        nav: navbars.pt_br,\n          sidebar: sidebars.pt_br,\n      },\n    },\n    \"zh-hans\": {\n      label: \"简体中文\",\n        lang: \"zh-hans\",\n        themeConfig: {\n        nav: navbars.zh_hans,\n          sidebar: sidebars.zh_hans,\n      },\n    },\n  },\n})\n```\n\n`/pt-BR/`는 `docs/pt-BR/`에 위치한 Markdown 파일들의 세트가 똑같이 필요합니다, 예를 들어:\n\n```shell\ndocs\n├─ README.md\n├─ foo.md\n├─ nested\n│  └─ README.md\n└─ pt-BR\n   ├─ README.md\n   ├─ foo.md\n   └─ nested\n      └─ README.md\n```\n\n더 자세한 정보는 [공식 VitePress i18n 문서](https://vitepress.dev/guide/i18n)에서 확인 가능합니다.\n"
  },
  {
    "path": "docs/ko-kr/contribute/first-party-plugins.md",
    "content": "# 공식 플러그인\n\nasdf 코어 팀은 일상 업무 환경에서 사용되는 플러그인들을 작성해왔습니다. 이 플러그인들을 관리하고 발전시키는 도움은 언제든 환영입니다. 아래 각 링크들에서 관련된 리포지토리들을 확인하세요:\n\n- [Elixir](https://github.com/asdf-vm/asdf-elixir)\n- [Erlang](https://github.com/asdf-vm/asdf-erlang)\n- [Node.js](https://github.com/asdf-vm/asdf-nodejs)\n- [Ruby](https://github.com/asdf-vm/asdf-ruby)\n\n커뮤니티 플러그인 보기:\n\n- [`asdf-community` 공동체](https://github.com/asdf-community): `asdf` 플러그인의 장기 유지보수를 위한 공동체 주도의 프로젝트입니다.\n- [`asdf-plugins` shortname 리포지토리](https://github.com/asdf-vm/asdf-plugins): 인기 있는 `asdf` 플러그인 검색을 위해 `asdf` 코어가 사용하는 Short-name 목록.\n- [Github `asdf-plugin` 토픽 검색](https://github.com/topics/asdf-plugin)\n"
  },
  {
    "path": "docs/ko-kr/contribute/github-actions.md",
    "content": "# GitHub Actions\n\n당신의 관심에 감사드리며, 존재하는 이슈들, 대화들, 그리고 기여 가이드라인을 [asdf actions 리포지토리](https://github.com/asdf-vm/actions)에서 확인 해주세요.\n"
  },
  {
    "path": "docs/ko-kr/guide/getting-started.md",
    "content": "# 시작하기\n\n`asdf` 설치는 다음과 같습니다:\n\n1. dependencies 설치\n2. `asdf` 코어 다운로드\n3. `asdf` 설치\n4. 관리하고 싶은 각각의 툴/런타임 플러그인 설치\n5. 툴/런타임 버전 설치\n6. `.tool-versions` 설정 파일들을 통해 글로벌 혹은 프로젝트 버전들 설정\n\n## 1. Dependencies 설치\n\nasdf는 `git` & `curl`이 필요합니다. _당신이_ 필요한 패키지 매니저를 구동하기 위한 _일부_ 명령어들의 목록입니다. (몇몇은 나중 단계에서 자동으로 설치될 수 있습니다).\n\n| 운영체제 | 패키지 매니저 | 명령어                             |\n| -------- | ------------- | ---------------------------------- |\n| linux    | Aptitude      | `apt install curl git`             |\n| linux    | DNF           | `dnf install curl git`             |\n| linux    | Pacman        | `pacman -S curl git`               |\n| linux    | Zypper        | `zypper install curl git`          |\n| macOS    | Homebrew      | `brew install coreutils curl git`  |\n| macOS    | Spack         | `spack install coreutils curl git` |\n\n::: tip 노트\n\n시스템 설정에 의해 `sudo`가 필요할 수 있습니다.\n\n:::\n\n## 2. asdf 다운로드\n\n### 공식 다운로드\n\n<!-- x-release-please-start-version -->\n\n```shell\ngit clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.18.1\n\n```\n\n<!-- x-release-please-end -->\n\n### 커뮤니티 지원 다운로드 방법\n\n공식 `git` 방식을 사용할 것을 적극적으로 권장드립니다.\n\n| 방법     | 명령어                                                                                                                                                             |\n| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| Homebrew | `brew install asdf`                                                                                                                                                |\n| Pacman   | `git clone https://aur.archlinux.org/asdf-vm.git && cd asdf-vm && makepkg -si` 혹은 선호하시는 [AUR helper](https://wiki.archlinux.org/index.php/AUR_helpers) 사용 |\n\n## 3. asdf 설치\n\n설정에 영향을 미치는 다양한 셸, 운영체제들 & 설치방법의 조합들이 있습니다. 아래 선택사항들 중 가장 적합한 것을 사용하세요.\n\n**macOS 사용자들은 이 섹션 마지막 부분에 `path_helper`에 경고를 반드시 읽어보세요.**\n\n::: details Bash & Git\n\n다음을 `~/.bashrc`에 추가하세요:\n\n```shell\n. \"$HOME/.asdf/asdf.sh\"\n```\n\n자동완성 설정은 다음을 `.bashrc`에 추가하세요:\n\n```shell\n. \"$HOME/.asdf/completions/asdf.bash\"\n```\n\n:::\n\n::: details Bash & Git (macOS)\n\n**macOS Catalina 혹은 그 이상**을 사용하신다면, 기본 셸이 **ZSH**로 변경되었습니다. Bash로 다시 변경하지 않으셨다면, ZSH의 설치방법을 따라주세요.\n\n다음을 `~/.bash_profile`에 추가하세요:\n\n```shell\n. \"$HOME/.asdf/asdf.sh\"\n```\n\n자동완성 설정은 다음을 `.bash_profile`에 직접 추가하세요:\n\n```shell\n. \"$HOME/.asdf/completions/asdf.bash\"\n```\n\n:::\n\n::: details Bash & Homebrew\n\n`~/.bashrc`에 `asdf.sh`를 추가하세요:\n\n```shell\necho -e \"\\n. \\\"$(brew --prefix asdf)/libexec/asdf.sh\\\"\" >> ~/.bashrc\n```\n\n자동완성은 [Homebrew'의 방법에 따라 설정되어야 합니다](https://docs.brew.sh/Shell-Completion#configuring-completions-in-bash) 혹은 다음을 이용하세요:\n\n```shell\necho -e \"\\n. \\\"$(brew --prefix asdf)/etc/bash_completion.d/asdf.bash\\\"\" >> ~/.bashrc\n```\n\n:::\n\n::: details Bash & Homebrew (macOS)\n\n**macOS Catalina 혹은 그 이상**을 사용하신다면, 기본 셸이 **ZSH**로 변경되었습니다. Bash로 다시 변경하지 않으셨다면, ZSH의 설치방법을 따라주세요.\n\n`~/.bash_profile`에 `asdf.sh` 추가하기:\n\n```shell\necho -e \"\\n. \\\"$(brew --prefix asdf)/libexec/asdf.sh\\\"\" >> ~/.bash_profile\n```\n\n자동완성은 [Homebrew'의 방법에 따라 설정되어야 합니다](https://docs.brew.sh/Shell-Completion#configuring-completions-in-bash) 혹은 다음을 이용하세요:\n\n```shell\necho -e \"\\n. \\\"$(brew --prefix asdf)/etc/bash_completion.d/asdf.bash\\\"\" >> ~/.bash_profile\n```\n\n:::\n\n::: details Bash & Pacman\n\n다음을 `~/.bashrc`에 추가하세요:\n\n```shell\n. /opt/asdf-vm/asdf.sh\n```\n\n자동완성을 위해 [`bash-completion`](https://wiki.archlinux.org/title/bash#Common_programs_and_options)이 설치 되어야합니다.\n:::\n\n::: details Fish & Git\n\n다음을 `~/.config/fish/config.fish`에 추가하세요:\n\n```shell\nsource ~/.asdf/asdf.fish\n```\n\n다음 명령어로 자동완성을 설정하세요:\n\n```shell\nmkdir -p ~/.config/fish/completions; and ln -s ~/.asdf/completions/asdf.fish ~/.config/fish/completions\n```\n\n:::\n\n::: details Fish & Homebrew\n\n`~/.config/fish/config.fish`에 `asdf.fish`를 추가하세요:\n\n```shell\necho -e \"\\nsource \"(brew --prefix asdf)\"/libexec/asdf.fish\" >> ~/.config/fish/config.fish\n```\n\n자동완성은 [Fish 셸 Homebrew에 의해 관리됩니다](https://docs.brew.sh/Shell-Completion#configuring-completions-in-fish). 편하죠!\n:::\n\n::: details Fish & Pacman\n\n다음을 `~/.config/fish/config.fish`에 추가하세요:\n\n```shell\nsource /opt/asdf-vm/asdf.fish\n```\n\n자동완성은 AUR 패키지를 통한 설치로 자동적으로 설정됩니다.\n:::\n\n::: details Elvish & Git\n\n`~/.config/elvish/rc.elv`에 `asdf.elv`를 추가하세요:\n\n```shell\nmkdir -p ~/.config/elvish/lib; ln -s ~/.asdf/asdf.elv ~/.config/elvish/lib/asdf.elv\necho \"\\n\"'use asdf _asdf; var asdf~ = $_asdf:asdf~' >> ~/.config/elvish/rc.elv\necho \"\\n\"'set edit:completion:arg-completer[asdf] = $_asdf:arg-completer~' >> ~/.config/elvish/rc.elv\n```\n\n자동완성은 자동적으로 설정됩니다.\n\n:::\n\n::: details Elvish & Homebrew\n\nAdd `asdf.elv` to your `~/.config/elvish/rc.elv` with:\n\n```shell\nmkdir -p ~/.config/elvish/lib; ln -s (brew --prefix asdf)/libexec/asdf.elv ~/.config/elvish/lib/asdf.elv\necho \"\\n\"'use asdf _asdf; var asdf~ = $_asdf:asdf~' >> ~/.config/elvish/rc.elv\necho \"\\n\"'set edit:completion:arg-completer[asdf] = $_asdf:arg-completer~' >> ~/.config/elvish/rc.elv\n```\n\n자동완성은 자동적으로 설정됩니다.\n:::\n\n::: details Elvish & Pacman\n\n`~/.config/elvish/rc.elv`에 `asdf.elv`를 추가하세요:\n\n```shell\nmkdir -p ~/.config/elvish/lib; ln -s /opt/asdf-vm/asdf.elv ~/.config/elvish/lib/asdf.elv\necho \"\\n\"'use asdf _asdf; var asdf~ = $_asdf:asdf~' >> ~/.config/elvish/rc.elv\necho \"\\n\"'set edit:completion:arg-completer[asdf] = $_asdf:arg-completer~' >> ~/.config/elvish/rc.elv\n```\n\n자동완성은 자동적으로 설정됩니다.\n:::\n\n::: details ZSH & Git\n\n다음을 `~/.zshrc`에 추가하세요:\n\n```shell\n. \"$HOME/.asdf/asdf.sh\"\n```\n\n**혹은** 위 스크립트와 자동완성을 설정하는 [asdf를 위한 oh-my-zsh](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/asdf)와 같은 ZSH 프레임워크 플러그인을 사용하세요.\n\n자동완성은 ZSH 프레임워크 `asdf` 플러그인 혹은 다음을 `.zshrc`에 추가함으로써 설정됩니다:\n\n```shell\n# append completions to fpath\nfpath=(${ASDF_DIR}/completions $fpath)\n# initialise completions with ZSH's compinit\nautoload -Uz compinit && compinit\n```\n\n- 만약 custom `compinit` 설정을 사용중이라면, `asdf.sh`를 source하고 난 다음 `compinit`가 오도록 해주세요\n- 만약 ZSH 프레임워크를 통해 custom `compinit` 설정을 사용중이라면, 해당 프레임워크를 source하고 난 다음 `compinit`가 오도록 해주세요\n\n**경고**\n\n만약 ZSH 프레임워크를 사용중이라면, `fpath`를 통해 새로운 ZSH 자동완성을 사용하려면 관련된 `asdf` 플러그인이 업데이트 되어야합니다. Oh-My-ZSH asdf 플로그인이 아직 업데이트 되지 않았습니다, [ohmyzsh/ohmyzsh#8837](https://github.com/ohmyzsh/ohmyzsh/pull/8837) 참고.\n:::\n\n::: details ZSH & Homebrew\n\n`~/.zshrc`에 `asdf.sh`를 추가하세요:\n\n```shell\necho -e \"\\n. $(brew --prefix asdf)/libexec/asdf.sh\" >> ${ZDOTDIR:-~}/.zshrc\n```\n\n**혹은** 위 스크립트와 자동완성을 설정하는 [asdf를 위한 oh-my-zsh](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/asdf)와 같은 ZSH 프레임워크 플러그인을 사용하세요.\n\n자동완성은 `asdf` ZSH 프레임워크 혹은 [Homebrew'의 방법](https://docs.brew.sh/Shell-Completion#configuring-completions-in-zsh)에 따라 설정되어야 합니다. 만약 ZSH 프레임워크를 사용중이라면, `fpath`를 통해 새로운 ZSH 자동완성을 사용하려면 관련된 asdf 플러그인이 업데이트 되어야합니다. Oh-My-ZSH asdf 플로그인이 아직 업데이트 되지 않았습니다, [ohmyzsh/ohmyzsh#8837](https://github.com/ohmyzsh/ohmyzsh/pull/8837) 참고.\n:::\n\n::: details ZSH & Pacman\n\n다음을 `~/.zshrc`에 추가하세요:\n\n```shell\n. /opt/asdf-vm/asdf.sh\n```\n\n자동완성은 ZSH 친화적인 위치에 있지만, [ZSH는 자동완성 사용을 위해 반드시 설정 되어야합니다](https://wiki.archlinux.org/index.php/zsh#Command_completion).\n:::\n\n::: details PowerShell Core & Git\n\n다음을 `~/.config/powershell/profile.ps1`에 추가하세요:\n\n```shell\n. \"$HOME/.asdf/asdf.ps1\"\n```\n\n:::\n\n::: details PowerShell Core & Homebrew\n\n`~/.config/powershell/profile.ps1`에 `asdf.sh`를 추가하세요:\n\n```shell\necho -e \"\\n. \\\"$(brew --prefix asdf)/libexec/asdf.ps1\\\"\" >> ~/.config/powershell/profile.ps1\n```\n\n:::\n\n::: details PowerShell Core & Pacman\n\n다음을 `~/.config/powershell/profile.ps1`에 추가하세요:\n\n```shell\n. /opt/asdf-vm/asdf.ps1\n```\n\n:::\n\n::: details Nushell & Git\n\n`~/.config/nushell/config.nu`에 `asdf.nu`를 추가하세요:\n\n```shell\n\"\\n$env.ASDF_DIR = ($env.HOME | path join '.asdf')\\n source \" + ($env.HOME | path join '.asdf/asdf.nu') | save --append $nu.config-path\n```\n\n자동완성은 자동적으로 설정됩니다.\n:::\n\n::: details Nushell & Homebrew\n\n`~/.config/nushell/config.nu`에 `asdf.nu`를 추가하세요:\n\n```shell\n\"\\n$env.ASDF_DIR = (brew --prefix asdf | str trim | into string | path join 'libexec')\\n source \" +  (brew --prefix asdf | str trim | into string | path join 'libexec/asdf.nu') | save --append $nu.config-path\n```\n\n자동완성은 자동적으로 설정됩니다.\n:::\n\n::: details Nushell & Pacman\n\n`~/.config/nushell/config.nu`에 `asdf.nu`를 추가하세요:\n\n```shell\n\"\\n$env.ASDF_DIR = '/opt/asdf-vm/'\\n source /opt/asdf-vm/asdf.nu\" | save --append $nu.config-path\n```\n\n자동완성은 자동적으로 설정됩니다.\n:::\n\n::: details POSIX Shell & Git\n\n다음을 `~/.profile`에 추가하세요:\n\n```shell\nexport ASDF_DIR=\"$HOME/.asdf\"\n. \"$HOME/.asdf/asdf.sh\"\n```\n\n:::\n\n::: details POSIX Shell & Homebrew\n\n`~/.profile`에 `asdf.sh`를 추가하세요:\n\n```shell\necho -e \"\\nexport ASDF_DIR=\\\"$(brew --prefix asdf)/libexec/asdf.sh\\\"\" >> ~/.profile\necho -e \"\\n. \\\"$(brew --prefix asdf)/libexec/asdf.sh\\\"\" >> ~/.profile\n```\n\n:::\n\n::: details POSIX Shell & Pacman\n\n다음을 `~/.profile`에 추가하세요:\n\n```shell\nexport ASDF_DIR=\"/opt/asdf-vm\"\n. /opt/asdf-vm/asdf.sh\n```\n\n:::\n\n`asdf` 스크립트들은 `$PATH` 설정한 **이후에** 프레임워크 (oh-my-zsh 등) source **이후에** source 되어야 합니다.\n\n`PATH` 업데이트를 위해 셸을 재시작하세요. 새로운 터미널을 여는 경우 대부분 해결됩니다.\n\n## 코어 설치 완료!\n\n`asdf` 코어 설치를 완료했습니다 :tada:\n\n`asdf`는 **플러그인**과 **툴**을 설치하고, **버전**들을 관리해야 유용합니다. 설치 및 관리방법을 이 가이드 아래에서 계속해서 배우세요.\n\n## 4. 플러그인 설치\n\n데모 목적으로 우리는 [`asdf-nodejs`](https://github.com/asdf-vm/asdf-nodejs/) 플러그인을 통해 [Node.js](https://nodejs.org/) 설치 & 설정을 해보겠습니다.\n\n### 플러그인 Dependencies\n\n각 플러그인은 dependencies 갖고 있어 우리는 플러그인 리포지토리의 목록을 확인해야합니다. `asdf-nodejs`는 다음을 가지고 있습니다:\n\n| OS                             | Dependency 설치                         |\n| ------------------------------ | --------------------------------------- |\n| Debian                         | `apt-get install dirmngr gpg curl gawk` |\n| CentOS/ Rocky Linux/ AlmaLinux | `yum install gnupg2 curl gawk`          |\n| macOS                          | `brew install gpg gawk`                 |\n\n우리는 어떤 플러그인들은 설치-후 훅들을 갖고있어 dependencies 먼저 설치해야합니다.\n\n### 플러그인 설치\n\n```shell\nasdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs.git\n```\n\n## 5. 버전 설치\n\n이제 우리는 Node.js를 위한 플러그인을 갖고있어 툴 버전을 설치할 수 있습니다.\n\n우리는 `asdf list all nodejs`를 통해 어떤 버전들이 이용가능한지 혹은 `asdf list all nodejs 14`를 통해 하위 버전들을 확인가능합니다.\n\n우리는 이용가능한 `latest` 버전을 설치할 것입니다.\n\n```shell\nasdf install nodejs latest\n```\n\n::: tip 노트\n`asdf`는 정확한 버전들을 강제합니다. `latest`는 `asdf`가 실행했을때 실제 버전을 찾는 헬퍼입니다.\n:::\n\n## 6. 버전 설정\n\n`asdf`는 현재 작업 디렉토리부터 `$HOME` 디렉토리까지 모든 `.tool-versions` 파일들에서 버전 검색을 수행합니다. 검색은 `asdf`가 관리하는 툴을 실행시킬때 맞춰서 실행됩니다.\n\n::: 경고\n툴 실행을 위한 툴 버전을 설정하지 않으면 **에러**가 발생합니다. `asdf current`는 현재 디렉토리로부터 툴 & 버전을 표시함으로써 어떤 툴들이 실행을 실패하는지 관찰할 수 있게합니다.\n:::\n\n### 글로벌\n\n글로벌 기본값들은 `$HOME/.tool-versions`에서 관리됩니다. 글로벌 버전을 다음을 이용해 설정하세요:\n\n```shell\nasdf set --home nodejs 16.5.0\n```\n\n`$HOME/.tool-versions`은 다음과 같습니다:\n\n```\nnodejs 16.5.0\n```\n\n어떤 운영체제들은 `asdf`가 아닌 시스템에 의해 관리되는 툴들이 이미 설치되어 있습니다, `python`이 대표적인 예시입니다. 당신은 시스템에 의한 툴 관리를 위해 `asdf`를 설정해야합니다. [버전 참조 섹션](/ko-kr/manage/versions.md)를 참고하세요.\n\n### 로컬\n\n로컬 버전들은 (현재 작업 디렉토리) `$PWD/.tool-versions` 파일에 정의 되어 있습니다. 보통, 이 디렉토리는 하나의 프로젝트의 Git 리포지토리입니다. 툴 버전을 설정하고 싶은 디렉토리에서 다음을 실행시키세요:\n\n```shell\nasdf set nodejs latest\n```\n\n`$PWD/.tool-versions`은 다음과 같습니다:\n\n```\nnodejs 16.5.0\n```\n\n### 기존의 툴 버전 파일들 사용하기\n\n`asdf`는 기존의 다른 버전 매니저들의 버전 파일들 마이그레이션을 지원합니다. 예시: `rbenv`의 `.ruby-version`. 이는 각 플러그인 기준으로 지원됩니다.\n\n[`asdf-nodejs`](https://github.com/asdf-vm/asdf-nodejs/)는 `.nvmrc`와 `.node-version` 파일들을 지원합니다. 이를 활성화하기 위해, 다음을 `asdf` 설정 파일 `$HOME/.asdfrc`에 추가하세요:\n\n```\nlegacy_version_file = yes\n```\n\n더 많은 설정 옵션들은 [configuration](/ko-kr/manage/configuration.md) 페이지를 참고하세요.\n\n## 가이드 끝!\n\n`asdf` 시작하기 가이드가 끝났습니다. :tada: 당신은 이제 당신의 프로젝트의 `nodejs` 버전들을 관리할 수 있습니다. 같은 방법으로 다른 각각의 툴들의 버전을 관리하세요!\n\n`asdf`는 우리가 익숙해져야하는 더 많은 명령어들을 가지고 있고, `asdf --help` 혹은 `asdf`를 통해 확인할 수 있습니다. 명령어들의 코어의 3가지 카테고리로 나눠질 수 있습니다:\n\n- [코어 `asdf`](/ko-kr/manage/core.md)\n- [플러그인](/ko-kr/manage/plugins.md)\n- [툴 버전](/ko-kr/manage/versions.md)\n"
  },
  {
    "path": "docs/ko-kr/guide/introduction.md",
    "content": "# 소개\n\n`asdf`는 툴 버전 매니저입니다. 모든 툴 버전 정의들은 당신의 팀들과 공유되는 프로젝트의 Git 리포지토리에서 확인할 수 있는 하나의 (`.tool-versions`) 파일에 있으며, 모든 사람들이 **정확히** 같은 버전의 툴들을 사용하게 합니다.\n\n기존의 작업 방식은 여러 CLI 버전 매니저들, 각각의 고유한 API, 설정 파일들 그리고 구현이 필요했었습니다 (e.g. `$PATH` 조정, shims, 환경 변수 등...). `asdf`는 개발 워크플로우 단순화를 위해 단 하나의 인터페이스와 설정파일을 제공하고 단순한 플러그인 인터페이스를 통해 모든 툴과 런타임들 확장가능합니다.\n\n## 작동방식\n\n`asdf` 코어가 셸 설정과 함께 설치되면, 플러그인들이 특정 툴들을 관리하기 위해 설치됩니다. 플러그인에 의해 한가지 툴이 설치되면, [shims](<https://en.wikipedia.org/wiki/Shim_(computing)>)들을 가진 실행파일들이 각각의 툴들을 위해 생성됩니다. 실행파일들을 실행하려 할 때, `.tool-versions`에 설정된 툴의 버전을 통해 `asdf`가 어떤 버전을 실행시킬 지 결정하고 해당 shim이 대신 실행됩니다.\n\n## 관련된 프로젝트\n\n### nvm / n / rbenv 등\n\n[nvm](https://github.com/nvm-sh/nvm), [n](https://github.com/tj/n) 그리고 [rbenv](https://github.com/rbenv/rbenv)과 같은 툴들은 설치된 실행파일을 위한 shim들을 만드는 셸 스크립트들로 작성되어 있습니다.\n\n`asdf`는 매우 비슷하고 툴/런타임 버전 관리의 영역에서 경쟁하기 위해 만들어졌습니다. `asdf`의 차별화 요소는 플러그인 시스템이 툴/런타임 별 매니저의 필요성, 각기 다른 명령어들, 그리고 리포지토리에 각각 `*-version` 파일들을 제거하였다는 것입니다.\n\n<!-- ### pyenv\n\nTODO: someone with Python background expand on this\n\n`asdf` has some similarities to `pyenv` but is missing some key features. The `asdf` team is looking at introducing some of these `pyenv` specific features, though no roadmap or timeline is available. -->\n\n### direnv\n\n> 현재 디렉토리에 따라 환경 변수들을 load와 unload 할 수 있는 새로운 기능을 기존의 셸에 추가합니다.\n\n`asdf`는 환경 변수들을 관리하지 않습니다만, [`asdf-direnv`](https://github.com/asdf-community/asdf-direnv) 플러그인을 통해 direnv 동작를 `asdf`에 통합할 수 있습니다.\n\n[direnv 문서](https://direnv.net/)에서 더보기.\n\n### Homebrew\n\n> macOS (혹은 Linux)에서의 패키지 매니저 부재\n\nHomebrew는 패키지들과 upstream dependencies들을 관리합니다. `asdf`는 upstream dependencies들을 관리하지 않고, 패키지 매니저가 아니고, 우리가 dependency 목록들을 작게 유지하므로, 사용자가 직접 관리해야합니다.\n\n[Homebrew 문서](https://brew.sh/)에서 더보기.\n\n### NixOS\n\n> Nix는 패키지 관리와 시스템 설정에 창의적으로 접근하는 툴입니다.\n\nNixOS는, `asdf`가 제공하지 않는, 각 툴의 전체 dependency tree를 통해 패키지들의 정확한 버전들을 관리함으로써 재현가능한 환경 구축을 목표로 합니다. NixOS는 자신만의 프로그래밍 언어, 많은 CLI 툴들, 그리고 6000개가 넘는 패키지 컬렉션을 통해 해당 기능을 제공합니다.\n\n다시 한번 말씀드리지만, `asdf`는 upstream dependencies들을 관리하지 않으며 패키지 매니저가 아닙니다.\n\n[NixOS 문서](https://nixos.org/guides/how-nix-works.html)에서 더보기.\n\n## 왜 asdf를 사용할까요?\n\n`asdf`는 팀들이 플러그인 시스템을 통해 **다양한** 툴들의 지원 그리고 셸 설정에 포함시킬 하나의 **셸** 스크립트의 _단순함_ 과 _친숙성_ 을 통해 **정확히** 같은 버전의 툴들을 사용하는 것을 보장합니다.\n\n::: tip 노트\n`asdf`는 시스템 패키지 매니저가 아닙니다. 이것은 툴 버전 매니저입니다. 단지 어떠한 툴을 위한 플러그인을 생성하고 그것의 버전을 `asdf`로 관리할 수 있다고 해서, 그 특정한 툴을 버전 관리를 위한 최선의 방법을 의미하는 것은 아닙니다.\n:::\n"
  },
  {
    "path": "docs/ko-kr/index.md",
    "content": "---\n# https://vitepress.dev/reference/default-theme-home-page\nlayout: home\n\nhero:\n  name: asdf\n  text: 다중 런타임 버전 매니저\n  tagline: 한가지 툴로 모든 런타임 버전들을 관리하세요!\n  actions:\n    - theme: brand\n      text: 시작하기\n      link: /ko-kr/guide/getting-started\n    - theme: alt\n      text: asdf이란?\n      link: /ko-kr/guide/introduction\n    - theme: alt\n      text: GitHub에서 보기\n      link: https://github.com/asdf-vm/asdf\n\nfeatures:\n  - title: \"단 한가지 도구\"\n    details: \"각 프로젝트 런타임들을 단 한가지 CLI 툴과 커맨드 인터페이스로 관리.\"\n    icon: 🎉\n  - title: \"플러그인\"\n    details: \"런타임과 툴들의 거대한 생태계. 당신이 필요한 새로운 툴들을 더해주는 간단한 API!\"\n    icon: 🔌\n  - title: \"구버전 호환\"\n    details: \"원활한 마이그레이션을 위해 이미 존재하던 .nvmrc, .node-version, .ruby-version 등의 설정 파일들 지원!\"\n    icon: ⏮\n  - title: \"단 하나의 설정 파일\"\n    details: \"단 하나의 공유된 .tool-versions 파일로 모든 툴, 런타임, 그리고 버전들을 관리.\"\n    icon: 📄\n  - title: \"셸\"\n    details: \"Bash, ZSH, Fish & Elvish 자동완성 기능 지원.\"\n    icon: 🐚\n  - title: \"GitHub Actions\"\n    details: \"GitHub Action 설치 제공과 .tool-versions 파일을 CI/CD 워크플로우에서 활용.\"\n    icon: 🤖\n\n---\n"
  },
  {
    "path": "docs/ko-kr/manage/commands.md",
    "content": "# 모든 명령어\n\n다음 목록은 `asdf`에서 이용가능한 모든 명령어들입니다. 해당 목록은 `asdf help` 명령어를 통해 확인가능합니다.\n\n<<< @../../internal/help/help.txt\n"
  },
  {
    "path": "docs/ko-kr/manage/configuration.md",
    "content": "# 설정\n\n`asdf`의 설정은 공유가능한 `.tool-versions` 파일들 뿐만 아니라 `.asdfrc`를 통한 특정한 사용자 맞춤화 및 환경 변수들을 모두 포함합니다.\n\n## `.tool-versions`\n\n한 디렉토리에 `.tool-versions` 파일이 존재하면, 해당 파일에 정의된 툴 버전들은 해당 디렉토리와 모든 하위 디렉토리에서 사용됩니다.\n\n::: warning 노트\n\n글로벌 기본값들은 `$HOME/.tool-versions` 파일에 설정 가능합니다\n\n:::\n\n`.tool-versions` 파일의 형태는 다음과 같습니다:\n\n```\nruby 2.5.3\nnodejs 10.15.0\n```\n\n다음과 같이 주석을 넣을 수 있습니다:\n\n```\nruby 2.5.3 # This is a comment\n# This is another comment\nnodejs 10.15.0\n```\n\n버전들은 다음과 같은 형식일 수 있습니다:\n\n- `10.15.0` - 실제 버전. 바이너리 다운로드를 지원하는 플러그인은 바이너리를 다운로드합니다.\n- `ref:v1.0.2-a` 혹은 `ref:39cb398vb39` - 지정된 태그/커밋/브랜치 Github로부터 다운로드하고 컴파일됩니다.\n- `path:~/src/elixir` - 사용하려는 툴의 맞춤 컴파일 버전을 위한 경로. 언어 개발자들 등이 사용합니다.\n- `system` - 이 키워드는 asdf가 asdf에 의해 관리되지 않는 시스템 버전 툴의 버전을 사용하게합니다.\n\n::: tip\n\n다양한 버전들은 공백으로 구분하여 설정될 수 있습니다. 예를 들어, 파이썬 `3.7.2`를 사용하고, 파이썬 `2.7.15`로 그리고 마지막으로 `system` 파이썬으로 폴백하려면, 다음을 `.tool-versions`에 추가해주세요.\n\n```\npython 3.7.2 2.7.15 system\n```\n\n:::\n\n`.tool-version` 파일에 정의된 모든 툴들을 설치하려면 `.tool-version` 파일이 포함된 디렉토리에서 다른 인수 없이 `asdf install`을 실행합니다.\n\n`.tool-versions` 파일에 정의된 하나의 툴을 설치하려면 `.tool-version` 파일이 포함된 디렉토리에서 `asdf install <name>`를 실행합니다. 이 툴은 `.tool-versions` 파일에 정의된 버전으로 설치됩니다.\n\n해당 파일은 직접 편집하거나 `asdf set` 명령어(또는 `asdf set --home` 명령어)를 사용하여 업데이트해 주세요.\n\n## `.asdfrc`\n\n`.asdfrc` 파일은 사용자의 머신별 설정을 정의합니다.\n\n`${HOME}/.asdfrc`는 asdf가 사용하는 기본 위치입니다. 이는 [환경 변수 `ASDF_CONFIG_FILE`](#asdf-config-file)로 설정 가능합니다.\n\n아래 파일은 필수적인 형식과 기본값들을 보여줍니다:\n\n```txt\nlegacy_version_file = no\nuse_release_candidates = no\nalways_keep_download = no\nplugin_repository_last_check_duration = 60\ndisable_plugin_short_name_repository = no\nconcurrency = auto\n```\n\n### `legacy_version_file`\n\n**지원되는** 플러그인들은 다른 버전 매니저에서 사용되는 버전 파일들을 읽을 수 있습니다, 예를 들어, 루비의 `rbenv`에서 `.ruby-version`.\n\n| 옵션                                                    | 설명                                                                                        |\n| :------------------------------------------------------ | :------------------------------------------------------------------------------------------ |\n| `no` <Badge type=\"tip\" text=\"기본\" vertical=\"middle\" /> | 버전을 불러오는 데는 `.tool-versions`를 사용합니다                                          |\n| `yes`                                                   | 이용 가능한 레거시 버전 파일(`.ruby-version` 등)이 있는 경우 플러그인의 폴백으로 사용합니다 |\n\n### `always_keep_download`\n\n`asdf install` 명령어로 다운로드하는 소스 코드 또는 바이너리를 유지 또는 제거하도록 설정합니다\n\n| 옵션                                                    | 설명                                          |\n| :------------------------------------------------------ | :-------------------------------------------- |\n| `no` <Badge type=\"tip\" text=\"기본\" vertical=\"middle\" /> | 성공적인 설치 후 소스 코드 또는 바이너리 제거 |\n| `yes`                                                   | 설치 후 소스 코드 또는 바이너리 유지          |\n\n### `plugin_repository_last_check_duration`\n\nasdf 플러그인 리포지토리 동기화 간격(분)을 설정합니다. 트리거 이벤트는 지난 동기화 시간을 확인하게 합니다. 마지막 동기화 이후 지정된 동기화 간격보다 더 많은 시간이 경과하면, 새로운 동기화가 발생합니다.\n\n| 옵션                                                                                          | 설명                                                                |\n| :-------------------------------------------------------------------------------------------- | :------------------------------------------------------------------ |\n| `1`에서 `999999999` 사이의 정수 <br/> `60` <Badge type=\"tip\" text=\"기본\" vertical=\"middle\" /> | 마지막 동기화 이후 지속 시간(분)이 초과된 경우 트리거 이벤트 동기화 |\n| `0`                                                                                           | 각 트리거 이벤트에서 동기화                                         |\n| `never`                                                                                       | 동기화 하지 않음                                                    |\n\n동기화 이벤트는 다음 명령어들을 실행할 때 발생합니다:\n\n- `asdf plugin add <name>`\n- `asdf plugin list all`\n\n`asdf plugin add <name> <git-url>` 플러그인 동기화를 트리거하지 않습니다.\n\n::: warning 노트\n\n해당 값을 `never`로 설정하는 것은 플러그인 리포지토리의 초기 동기화를 막지 않고, 해당 기능을 위해 `disable_plugin_short_name_repository`를 참조하세요.\n\n:::\n\n### `disable_plugin_short_name_repository`\n\nasdf 플러그인 short-name 리포지토리의 동기화를 비활성화합니다. short-name 리포지토리가 비활성화 되어있으면 동기화 이벤트가 조기 종료됩니다.\n\n| 옵션                                                    | 설명                                                           |\n| :------------------------------------------------------ | :------------------------------------------------------------- |\n| `no` <Badge type=\"tip\" text=\"기본\" vertical=\"middle\" /> | 동기화 이벤트에서 asdf 플러그인 리포지토리 clone 또는 업데이트 |\n| `yes`                                                   | short-name 플러그인 리포지토리 비활성화                        |\n\n동기화 이벤트는 다음 명령어들을 실행할 때 발생합니다:\n\n- `asdf plugin add <name>`\n- `asdf plugin list all`\n\n`asdf plugin add <name> <git-url>`는 플러그인 동기화를 트리거하지 않습니다.\n\n::: warning 노트\n\n플러그인 short-name repository를 비활성화해도 리포지토리가 이미 동기화된 경우 제거되지 않습니다. `rm --recursive --trash $ASDF_DATA_DIR/repository`로 플러그인 리포지토리를 제거합니다.\n\n플러그인 short-name 리포지토리를 비활성화해도 그 리포지토리로부터 설치된 이전의 플러그인은 제거되지 않습니다. `asdf plugin remove <name>`을 사용하여 플러그인을 제거할 수 있습니다. 플러그인을 제거하면 해당 툴의 모든 설치된 버전이 제거됩니다.\n\n:::\n\n### `concurrency`\n\n컴파일 중에 사용할 기본 코어 수입니다.\n\n| 옵션   | 설명                                                                                           |\n| :----- | :--------------------------------------------------------------------------------------------- |\n| 정수   | 소스 코드를 컴파일할 때 사용할 코어 수                                                         |\n| `auto` | `nproc`, `sysctl hw.ncpu`, `/proc/cpuinfo` 또는 `1`을 순차적으로 사용하여 코어 수를 계산합니다 |\n\n노트: `ASDF_CONCURRENCY` 환경 변수가 존재하는 경우 우선 순위를 갖습니다.\n\n### 플러그인 훅\n\n다음에서 사용자 맞춤 코드를 실행이 가능합니다:\n\n- 플러그인 설치, shim 재생성, 업데이트, 또는 제거 전 또는 후\n- 플러그인 명령어 실행 전 또는 후\n\n예를 들어 `foo`라는 플러그인이 설치되어 있고 `bar`라는 실행파일이 제공된 경우, 다음 훅들을 사용하여 사용자 맞춤 코드를 먼저 실행할 수 있습니다:\n\n```text\npre_foo_bar = echo Executing with args: $@\n```\n\n지원되는 패턴은 다음과 같습니다:\n\n- `pre_<plugin_name>_<command>`\n- `pre_asdf_download_<plugin_name>`\n- `{pre,post}_asdf_{install,reshim,uninstall}_<plugin_name>`\n  - `$1`: 풀 버전\n- `{pre,post}_asdf_plugin_{add,update,remove,reshim}`\n  - `$1`: 플러그인 이름\n- `{pre,post}_asdf_plugin_{add,update,remove}_<plugin_name>`\n\n어떤 명령어 훅들이 어떤 명령어 이전 또는 이후에 실행되는 지에 대한 자세한 내용은 [플러그인 생성하기](../plugins/create.md)를 참조하세요.\n\n## 환경 변수\n\n환경 변수 설정은 시스템과 셸에 따라 다릅니다. 기본 위치는 설치 위치와 방식(Git clone, Homebrew, AUR)에 달려있습니다.\n\n환경 변수들은 일반적으로 `asdf.sh`/`asdf.fish` 등을 source하기 전에 설정됩니다. Elvish의 경우는, 상단에서 `use asdf`로 설정합니다.\n\n다음은 Bash 셸에서 사용법에 관한 설명입니다.\n\n### `ASDF_CONFIG_FILE`\n\n`.asdfrc` 설정 파일의 경로. 임의의 위치로 설정 가능합니다. 절대 경로여야 합니다.\n\n- 미설정 시: `$HOME/.asdfrc`가 사용됩니다.\n- 사용법: `export ASDF_CONFIG_FILE=/home/john_doe/.config/asdf/.asdfrc`\n\n### `ASDF_TOOL_VERSIONS_FILENAME`\n\n툴 이름과 버전을 저장하는 파일의 파일이름입니다. 임의의 유효한 파일 이름이면 됩니다. 일반적으로, `.tool-version` 파일들을 무시하고 싶을 때 해당 값을 설정하세요.\n\n- 미설정 시: `.tool-versions`가 사용됩니다.\n- 사용법: `export ASDF_TOOL_VERSIONS_FILENAME=tool_versions`\n\n### `ASDF_DIR`\n\n`asdf` 코어 스크립트의 위치. 임의의 위치로 설정할 수 있습니다. 절대 경로여야 합니다.\n\n- 미설정 시: `bin/asdf` 실행파일의 한 단계 상위 디렉토리가 사용됩니다.\n- 사용법: `export ASDF_DIR=/home/john_doe/.config/asdf`\n\n### `ASDF_DATA_DIR`\n\n`asdf`가 플러그인, shim들, 툴 버전들을 설치하는 위치. 임의의 위치로 설정할 수 있습니다. 절대 경로여야 합니다.\n\n- 미설정 시: `$HOME/.asdf` 존재 시 사용, 존재하지 않는 경우 `ASDF_DIR` 사용\n- 사용법: `export ASDF_DATA_DIR=/home/john_doe/.asdf`\n\n### `ASDF_CONCURRENCY`\n\n소스 코드를 컴파일할 때 사용할 코어 수입니다. 설정하면 이 값이 asdf 설정 `concurrency` 값보다 우선 시 됩니다.\n\n- 미설정 시: asdf 설정 `concurrency` 값이 사용됩니다.\n- 사용법: `export ASDF_CONCURRENCY=32`\n\n## 전체 설정의 예시\n\n다음을 이용한 간단한 asdf 설치는:\n\n- Bash 셸\n- `$HOME/.asdf` 설치 위치\n- Git을 통한 설치\n- 환경 변수 설정 없음\n- 맞춤 `.asdfrc` 파일 없음\n\n다음의 결과가 나오게 됩니다:\n\n| 항목                                  | 값               | 값이 세팅되는 과정                                                                                                           |\n| :------------------------------------ | :--------------- | :--------------------------------------------------------------------------------------------------------------------------- |\n| config file location                  | `$HOME/.asdfrc`  | `ASDF_CONFIG_FILE`가 비었으므로, `$HOME/.asdfrc`을 사용                                                                      |\n| default tool versions filename        | `.tool-versions` | `ASDF_TOOL_VERSIONS_FILENAME`가 비었으므로, `.tool-versions`을 사용                                                          |\n| asdf dir                              | `$HOME/.asdf`    | `ASDF_DIR`가 비었으므로, `bin/asdf`의 한 단계 상위 디렉토리 사용                                                             |\n| asdf data dir                         | `$HOME/.asdf`    | `ASDF_DATA_DIR`가 비었으므로, `$HOME/.asdf`를 `$HOME`으로 사용.                                                              |\n| concurrency                           | `auto`           | `ASDF_CONCURRENCY`가 비었으므로, [기본 설정](https://github.com/asdf-vm/asdf/blob/master/defaults)의 `concurrency` 값에 의존 |\n| legacy_version_file                   | `no`             | 맞춤 `.asdfrc` 없음, [기본 설정](https://github.com/asdf-vm/asdf/blob/master/defaults) 사용                                  |\n| use_release_candidates                | `no`             | 맞춤 `.asdfrc` 없음, [기본 설정](https://github.com/asdf-vm/asdf/blob/master/defaults) 사용                                  |\n| always_keep_download                  | `no`             | 맞춤 `.asdfrc` 없음, [기본 설정](https://github.com/asdf-vm/asdf/blob/master/defaults) 사용                                  |\n| plugin_repository_last_check_duration | `60`             | 맞춤 `.asdfrc` 없음, [기본 설정](https://github.com/asdf-vm/asdf/blob/master/defaults) 사용                                  |\n| disable_plugin_short_name_repository  | `no`             | 맞춤 `.asdfrc` 없음, [기본 설정](https://github.com/asdf-vm/asdf/blob/master/defaults) 사용                                  |\n"
  },
  {
    "path": "docs/ko-kr/manage/core.md",
    "content": "# 코어\n\n코어 `asdf` 명령어는 소수지만, 많은 워크플로우를 원활하게 만들어줍니다.\n\n## 설치 & 설정\n\n[시작하기](/ko-kr/guide/getting-started.md)의 가이드에 설명되어 있습니다.\n\n## 실행\n\n```shell\nasdf exec <command> [args...]\n```\n\n현재 버전의 shim 명령어를 실행합니다.\n\n<!-- TODO: expand on this with example -->\n\n## 환경 변수\n\n```shell\nasdf env <command> [util]\n```\n\n<!-- TODO: expand on this with example -->\n\n## 정보\n\n```shell\nasdf info\n```\n\n운영체제, 셸 및 `asdf` 디버깅 정보를 출력하는 헬퍼 명령어입니다. 버그 리포트 작성시 공유해주세요.\n\n## Shim 재생성 <a id='Shim-재생성'></a>\n\n```shell\nasdf reshim <name> <version>\n```\n\n패키지의 현재 버전 shim을 재생성합니다. 기본적으로, shim들은 플러그인을 통해 툴 설치 중에 생성됩니다. [npm CLI](https://docs.npmjs.com/cli/) 등과 같은 툴들은 실행파일을 글로벌 설치할 수 있습니다, 예를 들어, `npm install -g yarn`을 통한 [Yarn](https://yarnpkg.com/) 설치. 이러한 실행파일은 플러그인의 라이프사이클을 통해 설치되지 않았기 때문에, 해당 플러그인을 위한 shim이 아직 존재하지 않습니다. 이때, `nodejs`의 `<version>`에 대해서, 예를 들면 `yarn`과 같은, 새로운 실행파일의 shim을 `asdf reshim nodejs <version>`을 통해 강제적으로 재작성 할 수 있습니다.\n\n## Shim 버전\n\n```shell\nasdf shimversions <command>\n```\n\nshim을 제공하는 플러그인 및 버전들을 나열합니다.\n\n예를 들면, [Node.js](https://nodejs.org/)에는 `node`와 `npm`이라고 하는 2개의 실행파일이 제공되고 있습니다. [`asdf-nodejs`](https://github.com/asdf-vm/asdf-nodejs/)을 통해 여러 버전의 툴이 설치되어 있는 경우, `shimversions`는 아래와 같은 내용을 출력할 수 있습니다:\n\n```shell\n➜ asdf shimversions node\nnodejs 14.8.0\nnodejs 14.17.3\nnodejs 16.5.0\n```\n\n```shell\n➜ asdf shimversions npm\nnodejs 14.8.0\nnodejs 14.17.3\nnodejs 16.5.0\n```\n\n## 업데이트\n\n`asdf`를 설치하는 데 사용한 것과 같은 방법을 사용하여 업데이트하세요. `asdf`의 최신 버전은 이 페이지의 오른쪽 상단 모서리에 표시됩니다.\n\n## 제거\n\n`asdf` 제거를 위해 다음 절차를 따르세요:\n\n::: details Bash & Git\n\n1. `~/.bashrc`에서, `asdf.sh` 및 자동완성을 source하고 있는 행들을 삭제:\n\n```shell\n. \"$HOME/.asdf/asdf.sh\"\n. \"$HOME/.asdf/completions/asdf.bash\"\n```\n\n2. `$HOME/.asdf` 디렉토리 제거:\n\n```shell\nrm -rf \"${ASDF_DATA_DIR:-$HOME/.asdf}\"\n```\n\n3. 모든 `asdf` 설정 파일들 제거를 위해 아래 명령어 실행:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Bash & Git (macOS)\n\n1. `~/.bash_profile`에서, `asdf.sh` 및 자동완성을 source하고 있는 행들을 삭제:\n\n```shell\n. \"$HOME/.asdf/asdf.sh\"\n. \"$HOME/.asdf/completions/asdf.bash\"\n```\n\n2. `$HOME/.asdf` 디렉토리 제거:\n\n```shell\nrm -rf \"${ASDF_DATA_DIR:-$HOME/.asdf}\"\n```\n\n3. 모든 `asdf` 설정 파일들 제거를 위해 아래 명령어 실행:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Bash & Homebrew\n\n1. `~/.bashrc`에서, `asdf.sh` 및 자동완성을 source하고 있는 행들을 삭제:\n\n```shell\n. $(brew --prefix asdf)/libexec/asdf.sh\n. $(brew --prefix asdf)/etc/bash_completion.d/asdf.bash\n```\n\n명령어 자동완성에 대해서는 [Homebrew에 설명되어 있는 방법으로 설정](https://docs.brew.sh/Shell-Completion#configuring-completions-in-bash) 되어 있을 가능성이 있으므로, 그 가이드에 따라 삭제할 행을 찾아주세요.\n\n2. 패키지 관리자를 사용하여 제거:\n\n```shell\nbrew uninstall asdf --force\n```\n\n3. 모든 `asdf` 설정 파일들 제거를 위해 아래 명령어 실행:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Bash & Homebrew (macOS)\n\n**macOS Catalina 혹은 그 이상**을 사용하신다면, 기본 셸이 **ZSH**로 변경되었습니다. 만약, `~/.bash_profile`에서 설정을 찾을 수 없는 경우는, `~/.zshrc`에 있을 가능성이 있는데 이 경우 ZSH의 설명을 봐 주세요.\n\n1. `~/.bash_profile`에서, `asdf.sh` 및 자동완성을 source하고 있는 행들을 삭제:\n\n```shell\n. $(brew --prefix asdf)/libexec/asdf.sh\n. $(brew --prefix asdf)/etc/bash_completion.d/asdf.bash\n```\n\n명령어 자동완성에 대해서는 [Homebrew에 설명되어 있는 방법으로 설정](https://docs.brew.sh/Shell-Completion#configuring-completions-in-bash) 되어 있을 가능성이 있으므로, 그 가이드에 따라 삭제할 행을 찾아주세요.\n\n2. 패키지 관리자를 사용하여 제거:\n\n```shell\nbrew uninstall asdf --force\n```\n\n3. 모든 `asdf` 설정 파일들 제거를 위해 아래 명령어 실행:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Bash & Pacman\n\n1. `~/.bashrc`에서, `asdf.sh` 및 자동완성을 source하고 있는 행들을 삭제:\n\n```shell\n. /opt/asdf-vm/asdf.sh\n```\n\n2. 패키지 관리자를 사용하여 제거:\n\n```shell\npacman -Rs asdf-vm\n```\n\n3. `$HOME/.asdf` 디렉토리 제거:\n\n```shell\nrm -rf \"${ASDF_DATA_DIR:-$HOME/.asdf}\"\n```\n\n4. 모든 `asdf` 설정 파일들 제거를 위해 아래 명령어 실행:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Fish & Git\n\n1. `~/.config/fish/config.fish`에서, `asdf.fish`를 source하고 있는 행들을 삭제:\n\n```shell\nsource ~/.asdf/asdf.fish\n```\n\n그리고 자동완성을 다음 명령어로 제거:\n\n```shell\nrm -rf ~/.config/fish/completions/asdf.fish\n```\n\n2. `$HOME/.asdf` 디렉토리 제거:\n\n```shell\nrm -rf (string join : -- $ASDF_DATA_DIR $HOME/.asdf)\n```\n\n3. 모든 `asdf` 설정 파일들 제거를 위해 아래 명령어 실행:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Fish & Homebrew\n\n1. `~/.config/fish/config.fish`에서, `asdf.fish`를 source하고 있는 행들을 삭제:\n\n```shell\nsource \"(brew --prefix asdf)\"/libexec/asdf.fish\n```\n\n2. 패키지 관리자를 사용하여 제거:\n\n```shell\nbrew uninstall asdf --force\n```\n\n3. 모든 `asdf` 설정 파일들 제거를 위해 아래 명령어 실행:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Fish & Pacman\n\n1. `~/.config/fish/config.fish`에서, `asdf.fish`를 source하고 있는 행들을 삭제:\n\n```shell\nsource /opt/asdf-vm/asdf.fish\n```\n\n2. 패키지 관리자를 사용하여 제거:\n\n```shell\npacman -Rs asdf-vm\n```\n\n3. `$HOME/.asdf` 디렉토리 제거:\n\n```shell\nrm -rf (string join : -- $ASDF_DATA_DIR $HOME/.asdf)\n```\n\n4. 모든 `asdf` 설정 파일들 제거를 위해 아래 명령어 실행:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Elvish & Git\n\n1. `~/.config/elvish/rc.elv`에서, `asdf` 모듈을 사용하는 행들을 삭제:\n\n```shell\nuse asdf _asdf; var asdf~ = $_asdf:asdf~\nset edit:completion:arg-completer[asdf] = $_asdf:arg-completer~\n```\n\n그리고 `asdf` 모듈을 다음 명령어로 제거:\n\n```shell\nrm -f ~/.config/elvish/lib/asdf.elv\n```\n\n2. `$HOME/.asdf` 디렉토리 제거:\n\n```shell\nif (!=s $E:ASDF_DATA_DIR \"\") { rm -rf $E:ASDF_DATA_DIR } else { rm -rf ~/.asdf }\n```\n\n3. 모든 `asdf` 설정 파일들 제거를 위해 아래 명령어 실행:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Elvish & Homebrew\n\n1. `~/.config/elvish/rc.elv`에서, `asdf` 모듈을 사용하는 행들을 삭제:\n\n```shell\nuse asdf _asdf; var asdf~ = $_asdf:asdf~\nset edit:completion:arg-completer[asdf] = $_asdf:arg-completer~\n```\n\n그리고 `asdf` 모듈을 다음 명령어로 제거:\n\n```shell\nrm -f ~/.config/elvish/lib/asdf.elv\n```\n\n2. 패키지 관리자를 사용하여 제거:\n\n```shell\nbrew uninstall asdf --force\n```\n\n3. 모든 `asdf` 설정 파일들 제거를 위해 아래 명령어 실행:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Elvish & Pacman\n\n1. `~/.config/elvish/rc.elv`에서, `asdf` 모듈을 사용하는 행들을 삭제:\n\n```shell\nuse asdf _asdf; var asdf~ = $_asdf:asdf~\nset edit:completion:arg-completer[asdf] = $_asdf:arg-completer~\n```\n\n그리고 `asdf` 모듈을 다음 명령어로 제거:\n\n```shell\nrm -f ~/.config/elvish/lib/asdf.elv\n```\n\n2. 패키지 관리자를 사용하여 제거:\n\n```shell\npacman -Rs asdf-vm\n```\n\n3. `$HOME/.asdf` 디렉토리 제거:\n\n```shell\nif (!=s $E:ASDF_DATA_DIR \"\") { rm -rf $E:ASDF_DATA_DIR } else { rm -rf ~/.asdf }\n```\n\n4. 모든 `asdf` 설정 파일들 제거를 위해 아래 명령어 실행:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details ZSH & Git\n\n1. `~/.zshrc`에서, `asdf.sh` 및 자동완성을 source하고 있는 행들을 삭제:\n\n```shell\n. \"$HOME/.asdf/asdf.sh\"\n# ...\nfpath=(${ASDF_DIR}/completions $fpath)\nautoload -Uz compinit\ncompinit\n```\n\n**혹은** 사용된 ZSH 프레임워크 플러그인 제거.\n\n2. `$HOME/.asdf` 디렉토리 제거:\n\n```shell\nrm -rf \"${ASDF_DATA_DIR:-$HOME/.asdf}\"\n```\n\n3. 모든 `asdf` 설정 파일들 제거를 위해 아래 명령어 실행:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details ZSH & Homebrew\n\n1. `~/.zshrc`에서, `asdf.sh`을 source하고 있는 행들을 삭제:\n\n```shell\n. $(brew --prefix asdf)/libexec/asdf.sh\n```\n\n2. 패키지 관리자를 사용하여 제거:\n\n```shell\nbrew uninstall asdf --force && brew autoremove\n```\n\n3. 모든 `asdf` 설정 파일들 제거를 위해 아래 명령어 실행:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details ZSH & Pacman\n\n1. `~/.zshrc`에서, `asdf.sh`을 source하고 있는 행들을 삭제:\n\n```shell\n. /opt/asdf-vm/asdf.sh\n```\n\n2. 패키지 관리자를 사용하여 제거:\n\n```shell\npacman -Rs asdf-vm\n```\n\n3. `$HOME/.asdf` 디렉토리 제거:\n\n```shell\nrm -rf \"${ASDF_DATA_DIR:-$HOME/.asdf}\"\n```\n\n4. 모든 `asdf` 설정 파일들 제거를 위해 아래 명령어 실행:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n끝! 🎉\n"
  },
  {
    "path": "docs/ko-kr/manage/plugins.md",
    "content": "# 플러그인\n\n플로그인들은 `asdf`가 Node.js, Ruby, Elixir 등 여러가지 툴들을 취급하는 방법입니다.\n\n더 많은 툴들 지원을 위한 플러그인 API는 [플러그인 생성하기](/ko-kr/plugins/create.md) 참고하세요.\n\n## 추가하기\n\nGit URL로 플러그인 추가하기:\n\n```shell\nasdf plugin add <name> <git-url>\n# asdf plugin add elm https://github.com/vic/asdf-elm\n```\n\n또는 플러그인 리포지토리에 short-name을 통해 추가하기:\n\n```shell\nasdf plugin add <name>\n# asdf plugin add erlang\n```\n\n::: tip 추천\n\nshort-name 리포지토리에 독립적인 긴 `git-url` 방식이 선호됩니다.\n\n:::\n\n## 설치된 목록\n\n```shell\nasdf plugin list\n# asdf plugin list\n# java\n# nodejs\n```\n\n```shell\nasdf plugin list --urls\n# asdf plugin list\n# java            https://github.com/halcyon/asdf-java.git\n# nodejs          https://github.com/asdf-vm/asdf-nodejs.git\n```\n\n## 모든 Short-name 리포지토리 목록\n\n```shell\nasdf plugin list all\n```\n\n플러그인들의 전체 short-name 목록을 [플러그인 Shortname 인덱스](https://github.com/asdf-vm/asdf-plugins)에서 확인하세요.\n\n## 업데이트\n\n```shell\nasdf plugin update --all\n```\n\n특정 패키지를 업데이트하고 싶다면, 다음 명령어를 사용하세요.\n\n```shell\nasdf plugin update <name>\n# asdf plugin update erlang\n```\n\n이 명령어는 해당 플러그인 리포지토리의 _origin_ _기본 브랜치_ 의 _가장 최근 커밋_ 을 fetch합니다. 버전화된 플러그인들과 업데이트들은 현재 개발 진행중 입니다 ([#916](https://github.com/asdf-vm/asdf/pull/916)).\n\n## 제거\n\n```bash\nasdf plugin remove <name>\n# asdf plugin remove erlang\n```\n\n플러그인 제거는 해당 툴과 관련된 모든 것을 제거합니다. 이것은 한 툴의 미사용중인 많은 버전들의 cleaning/pruning에 유용합니다.\n\n## asdf Short-name 리포지토리 동기화\n\nShort-name 리포지토리는 로컬 머신에 주기적으로 동기화됩니다. 동기화 방식들로 다음 방식들이 있습니다:\n\n- 명령어들에 의한 동기화 이벤트:\n  - `asdf plugin add <name>`\n  - `asdf plugin list all`\n- 만약 `disable_plugin_short_name_repository` 설정 옵션이 `yes`로 설정되어 있다면, 동기화는 조기 종료됩니다. [asdf 설정 문서](/ko-kr/manage/configuration.md)에서 더보기.\n- 만약 동기화가 지난 `X`분 동안 진행되지 않았다면, 동기화가 진행됩니다.\n  - `X`의 기본값은 `60`입니다만, `.asdfrc`의 `plugin_repository_last_check_duration` 옵션을 통해 설정될 수 있습니다. [asdf 설정 문서](/ko-kr/manage/configuration.md)에서 더보기.\n"
  },
  {
    "path": "docs/ko-kr/manage/versions.md",
    "content": "# 버전\n\n## 버전 설치\n\n```shell\nasdf install <name> <version>\n# asdf install erlang 17.3\n```\n\n플러그인이 소스에서 다운로드 & 컴파일을 지원하는 경우, `ref:foo`를 지정할 수 있으며 여기서 `foo`는 특정 브랜치, 태그 또는 커밋입니다. 제거할 때도 동일한 이름과 참조를 사용해야 합니다.\n\n## 최신 안정 버전 설치\n\n```shell\nasdf install <name> latest\n# asdf install erlang latest\n```\n\n주어진 문자열로 시작하는 최신 안정 버전을 설치합니다.\n\n```shell\nasdf install <name> latest:<version>\n# asdf install erlang latest:17\n```\n\n## 설치된 버전 목록\n\n```shell\nasdf list <name>\n# asdf list erlang\n```\n\n주어진 문자열로 시작하는 버전으로 필터링합니다.\n\n```shell\nasdf list <name> <version>\n# asdf list erlang 17\n```\n\n## 사용 가능한 모든 버전 목록\n\n```shell\nasdf list all <name>\n# asdf list all erlang\n```\n\n주어진 문자열로 시작하는 버전으로 필터링합니다.\n\n```shell\nasdf list all <name> <version>\n# asdf list all erlang 17\n```\n\n## 최신 안정 버전 보기\n\n```shell\nasdf latest <name>\n# asdf latest erlang\n```\n\n주어진 문자열로 시작하는 최신 안정 버전을 보여줍니다.\n\n```shell\nasdf latest <name> <version>\n# asdf latest erlang 17\n```\n\n## 현재 버전 설정 <a id='현재-버전-설정'></a>\n\n```shell\nasdf set [flags] <name> <version> [<version>...]\n# asdf set elixir 1.2.4 # set in current dir\n# asdf set -u elixir 1.2.4 # set in .tool-versions file in home directory\n# asdf set -p elixir 1.2.4 # set in existing .tool-versions file in a parent dir\n\nasdf set <name> latest[:<version>]\n# asdf set elixir latest\n```\n\n`asdf set`은 현재 디렉터리에 `.tool-versions` 파일에 버전을 기록하며, 파일이 없으면 새로 생성합니다. 이는 순전히 편의 기능으로,\n`echo \"<tool> <version>\" > .tool-versions` 를 실행하는 것과 같다고 생각하면 됩니다.\n\n`-u` / `--home` 플래그를 사용하면 `asdf set`은 `$HOME` 디렉터리에 있는 `.tool-versions` 파일에 기록하며, 해당 파일이 없을 경우 새로 생성합니다.\n\n`-p` / `--parent` 플래그를 사용하면 `asdf set`은 현재 디렉터리에서 가장 가까운 상위 디렉터리에 있는 `.tool-versions` 파일을 찾아 그 파일에 기록합니다.\n\n### 환경 변수 사용 (Via Environment Variable)\n\n버전을 결정할 때 `asdf`는 `ASDF_${TOOL}_VERSION` 형식의 환경 변수를 먼저 확인합니다.\n버전 형식은 `.tool-versions` 파일에서 지원하는 형식과 동일합니다.\n\n이 환경 변수가 설정되어 있으면, 어떤 `.tool-versions` 파일에 해당 도구의 버전이 설정되어 있더라도 **해당 값이 우선 적용**됩니다.\n\n예를 들어:\n\n```bash\nexport ASDF_ELIXIR_VERSION=1.18.1\n```\n\n위 설정은 현재 셸 세션에서 `asdf`가 **Elixir 1.18.1**을 사용하도록 지정합니다.\n\n---\n\n:::warning 대체 수단\n\n이 설정은 **환경 변수**이기 때문에, **해당 변수가 설정된 위치(셸 세션)**에서만 적용됩니다.\n이미 실행 중인 다른 셸 세션들은 `.tool-versions` 파일에 설정된 버전을 계속 사용합니다.\n\n세부 내용은 Configuration 섹션의 `.tool-versions` [설정 섹션에 파일](/ko-kr/manage/configuration.md)을 참고하세요.\n:::\n\n---\n\n다음 예시는 Elixir 프로젝트의 테스트를 **버전 1.4.0**으로 실행합니다:\n\n```bash\nASDF_ELIXIR_VERSION=1.4.0 mix test\n```\n\n## 시스템 버전으로의 폴백\n\nasdf 관리 버전이 아닌 `<name>` 도구의 시스템 버전을 사용하려면 도구의 버전을 `system`으로 설정할 수 있습니다.\n\n위에 [현재 버전 설정](#현재-버전-설정) 섹션에 나와있는대로, `asdf set`이나 환경 변수를 사용하여 설정하세요.\n\n```shell\nasdf set <name> system\n# asdf set python system\n```\n\n## 현재 버전 보기\n\n```shell\nasdf current\n# asdf current\n# erlang          17.3          /Users/kim/.tool-versions\n# nodejs          6.11.5        /Users/kim/cool-node-project/.tool-versions\n\nasdf current <name>\n# asdf current erlang\n# erlang          17.3          /Users/kim/.tool-versions\n```\n\n## 버전 제거\n\n```shell\nasdf uninstall <name> <version>\n# asdf uninstall erlang 17.3\n```\n\n## Shims\n\nasdf는 패키지를 설치할 때 해당 패키지의 모든 실행 프로그램에 대한 shim들을 `$ASDF_DATA_DIR/shims` 디렉토리 (기본값은 `~/.asdf/shims`)에 생성합니다. 이 디렉토리는 설치된 프로그램들이 이용가능하도록 `$PATH` (`asdf.sh`, `asdf.fish` 등)에 존재합니다.\n\nShim 자체는 플러그인 이름과 shim이 감싸고 있는 설치된 패키지의 실행파일의 경로를 넘겨주는 `asdf exec`라는 헬퍼 프로그램을 `exec`시키는 매우 단순한 wrapper입니다.\n\n`asdf exec` 헬퍼는 사용할 패키지의 버전( `.tool-versions` 파일이나 환경 변수에 지정된 버전)을 결정하고,\n패키지 설치 디렉터리 안에서 실행 파일의 최종 경로를 산출합니다\n(이 경로는 플러그인의 `exec-path` 콜백을 통해 조정될 수 있습니다).\n\n또한 실행에 사용할 환경을 결정하는데, 이 역시 플러그인이 제공하는 `exec-env` 스크립트를 통해 설정됩니다.\n\n이 모든 과정이 끝나면, 해당 실행 파일을 실제로 실행합니다.\n\n::: warning 노트\n이 시스템은 `exec` 호출을 사용하기 때문에, 실행 대신 셸에 의해 source 되야하는 패키지의 스크립트는 shim wrapper를 통하지 않고 직접 액세스되야 합니다. 두 가지 `asdf` 명령어: `which`와 `where`는 설치된 패키지로의 경로를 반환할 수 있습니다:\n:::\n\n```shell\n# returns path to main executable in current version\nsource $(asdf which ${PLUGIN})/../script.sh\n\n# returns path to the package installation directory\nsource $(asdf where ${PLUGIN})/bin/script.sh\n```\n\n### asdf shims 우회\n\n어떠한 이유로 asdf의 shim들을 우회하고 싶거나 프로젝트의 디렉토리로 이동했을 때 자동으로 환경 변수를 설정되게 하고 싶으시면 [asdf-direnv](https://github.com/asdf-community/asdf-direnv) 플러그인이 도움이 될 것입니다. 상세한 내용은 README를 확인해 주세요.\n"
  },
  {
    "path": "docs/ko-kr/more/community-projects.md",
    "content": "# 커뮤니티 프로젝트\n\n`asdf`와 관련된 커뮤니티 프로젝트들입니다:\n\n- [asdf-community](https://github.com/asdf-community): \n  asdf 플로그인들의 장기 관리를 위한 커뮤니티 주도의 프로젝트입니다.\n- [asdf dev container](https://github.com/iloveitaly/asdf-devcontainer):\n  GitHub 코드스페이스에서 asdf가 관리하는 툴들을 지원하는 \n  [GitHub 개발 컨테이너](https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/introduction-to-dev-containers)\n\n::: warning 노트\n\nasdf 코어 팀은 이 프로젝트들 혹은 코드를 소유하지 않습니다. \nasdf 코어는 위의 목록들에 품질과 보안을 책임지지 않습니다.\n\n:::\n"
  },
  {
    "path": "docs/ko-kr/more/faq.md",
    "content": "# 자주 묻는 질문\n\n`asdf`에 관련된 공통된 질문들입니다.\n\n## WSL1을 지원하나요?\n\nWSL1 ([Windows Subsystem for Linux](https://en.wikipedia.org/wiki/Windows_Subsystem_for_Linux) 1)는 공식적으로 지원되지 않습니다. 어떤 부분의 `asdf`의 제대로 동작하지 않을 수 있습니다. 우리는 WSL1의 공식 지원을 추가할 계획이 없습니다.\n\n## WSL2을 지원하나요?\n\nWSL2 ([Windows Subsystem for Linux](https://en.wikipedia.org/wiki/Windows_Subsystem_for_Linux#WSL_2) 2)는 당신이 선택한 WSL distro를 위한 설치 & dependency 설명서를 따르면 작동합니다.\n\n중요한 것은, WSL2는 _오직_ 현재 작업 디렉토리가 Unix 드라이브 그리고 Windows 드라이브에 종속되어 있지 않을때 정상적으로 동작합니다.\n\n우리는 호스트 runner support가 GitHub Actions에서 사용가능할 때 WSL2에서 테스트 suite를 진행할 계획입니다만, 현재는 아직 이용가능하지 않은 것 같습니다.\n\n## 새롭게 설치된 실행파일이 동작하지 않나요?\n\n> 방금 `npm install -g yarn`, 그러나 `yarn`을 실행시킬 수 없습니다. 어떻게 해야하나요?\n\n`asdf`는 [shims](<https://en.wikipedia.org/wiki/Shim_(computing)>)를 사용하여 실행파일들을 관리합니다. 플러그인에 의해서 설치되는 실행파일들은 자동적으로 shim이 생성되지만, `asdf`가 관리하고 있는 툴에 의해서 실행파일이 설치 된 경우는 shim을 생성해야 한다고 하는 것을 `asdf`에 알려줄 필요가 있습니다. 이러한 경우, [Yarn](https://yarnpkg.com/)의 shim을 생성하기 위해 [`asdf reshim` 명령어 문서](/ko-kr/manage/core.md#Shim-재생성)를 참고하세요.\n\n## 셸이 새롭게 설치된 shims들을 감지하지 못하나요?\n\n만약 `asdf reshim`가 문제를 해결하지 못한다면, 대부분의 경우 `asdf.sh` 혹은 `asdf.fish` sourcing이 당신의 셸 설정 파일 (`.bash_profile`, `.zshrc`, `config.fish` etc) **아래쪽에** 있지 _않을_ 가능성이 높습니다. 당신의 `$PATH`가 설정 된 **후에** 그리고 사용중인 프레임워크 (oh-my-zsh etc)가 source 된 **후에** source 되어야 합니다.\n"
  },
  {
    "path": "docs/ko-kr/more/thanks.md",
    "content": "# 감사인사\n\nasdf 저자들 & 기여자들을 위한 감사 페이지!\n\n## Credits\n\n나 ([@HashNuke](https://github.com/HashNuke)), 고열, 감기, 기침.\n\n2014년부터 종료까지의 저작권 ([MIT License](https://github.com/asdf-vm/asdf/blob/master/LICENSE))\n\n## 관리자\n\n- [@HashNuke](https://github.com/HashNuke)\n- [@danhper](https://github.com/danhper)\n- [@Stratus3D](https://github.com/Stratus3D)\n- [@vic](https://github.com/vic)\n- [@jthegedus](https://github.com/jthegedus)\n\n## 기여자\n\nGitHub에서 [기여자들의 목록](https://github.com/asdf-vm/asdf/graphs/contributors)를 확인하세요 :pray:\n"
  },
  {
    "path": "docs/ko-kr/plugins/create.md",
    "content": "# 플러그인 생성하기\n\n플러그인은 언어 / 툴의 버전 관리를 지원하는 실행 가능한 스크립트들이 있는\nGit 리포지토리입니다. 이 스크립트들은 asdf에 의해 특정 명령어들을 받아\n`asdf list-all <name>`, `asdf install <name> <version>`\n등의 지원을 위해 실행됩니다.\n\n## 빠른 시작\n\n자체 플러그인을 만드는 것을 시작하는 두 가지 옵션이 있습니다:\n\n1. [asdf-vm/asdf-plugin-template](https://github.com/asdf-vm/asdf-plugin-template)\n   리포지토리 사용해서\n   기본 스크립트가 구현된 (`asdf-<tool_name>` 이름으로) 플러그인 리포지토리\n   [생성하기](https://github.com/asdf-vm/asdf-plugin-template/generate). 리포지토리가 생성되면,\n   그 리포지토리를 clone하고 템플릿을\n   유기적으로 업데이트하여\n   `setup.bash` 스크립트를 실행합니다.\n2. `asdf-<tool_name>`로 이룸 붙인 리포지토리를 시작하고\n   아래 문서에 필수 스크립트들을 구현하세요.\n\n### 플리그인 스크립트들을 위한 황금률\n\n- 스크립트는 다른 `asdf` 명령어를 호출하면 **안됩니다**.\n- 셸 툴/명령어의 dependency를 최소로 유지하세요.\n- non-portable 툴이나 명령어 플래그의 사용을 피하세요. 예를 들어, `sort -V`.\n  asdf core를 참고하세요\n  [금지된 명령어 목록](https://github.com/asdf-vm/asdf/blob/master/test/banned_commands.bats)\n\n## 스크립트 개요\n\nasdf에서 호출 가능한 스크립트의 전체 목록입니다.\n\n| 스크립트                                                                                       | 설명                                                                       |\n| :--------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------- |\n| [bin/list-all](#bin-list-all) <Badge type=\"tip\" text=\"필수\" vertical=\"middle\" />               | 모든 설치 가능한 버전들을 나열                                             |\n| [bin/download](#bin-download) <Badge type=\"tip\" text=\"필수\" vertical=\"middle\" />               | 지정한 버전에 대한 소스 코드 또는 바이너리 다운로드                        |\n| [bin/install](#bin-install) <Badge type=\"tip\" text=\"필수\" vertical=\"middle\" />                 | 지정된 버전을 설치                                                         |\n| [bin/latest-stable](#bin-latest-stable) <Badge type=\"warning\" text=\"추천\" vertical=\"middle\" /> | 지정된 도구의 최신 안정 버전 나열                                          |\n| [bin/help.overview](#bin-help.overview)                                                        | 플러그인 및 도구에 대한 일반적인 설명을 출력                               |\n| [bin/help.deps](#bin-help.deps)                                                                | 운영 체제별 dependencies 목록 출력                                         |\n| [bin/help.config](#bin-help.config)                                                            | 플러그인 및 툴 설정 정보 출력                                              |\n| [bin/help.links](#bin-help.links)                                                              | 플러그인 및 툴에 대한 링크 목록 출력                                       |\n| [bin/list-bin-paths](#bin-list-bin-paths)                                                      | shim들을 생성하기 위해 바이너리 파일이 있는 디렉토리에 대한 상대 경로 나열 |\n| [bin/exec-env](#bin-exec-env)                                                                  | 바이너리 실행을 위한 환경 준비                                             |\n| [bin/exec-path](#bin-exec-path)                                                                | 툴 버전의 실행파일 경로 출력                                               |\n| [bin/uninstall](#bin-uninstall)                                                                | 툴의 특정 버전 제거                                                        |\n| [bin/list-legacy-filenames](#bin-list-legacy-filenames)                                        | 레거시 버전 파일의 이름 출력: `.ruby-version`                              |\n| [bin/parse-legacy-file](#bin-parse-legacy-file)                                                | 레거시 버전 파일들을 위한 맞춤 parser                                      |\n| [bin/post-plugin-add](#bin-post-plugin-add)                                                    | 플러그인이 추가된 후 실행될 훅                                             |\n| [bin/post-plugin-update](#bin-post-plugin-update)                                              | 플러그인이 업데이트 된 후 실행될 훅                                        |\n| [bin/pre-plugin-remove](#bin-pre-plugin-remove)                                                | 플러그인이 제거되기 전 실행될 훅                                           |\n\n어떤 명령어가 어떤 스크립트를 호출하는지 확인하려면, 각 스크립트에 대한 자세한 문서를\n참조하세요.\n\n## 환경 변수 개요\n\n모든 스크립트에서 사용되는 환경 변수의 전체 목록입니다.\n\n| 환경 변수                | 설명                                                              |\n| :----------------------- | :---------------------------------------------------------------- |\n| `ASDF_INSTALL_TYPE`      | `version` 또는 `ref`                                              |\n| `ASDF_INSTALL_VERSION`   | `ASDF_INSTALL_TYPE`에 따른 풀 버전 번호 또는 Git Ref              |\n| `ASDF_INSTALL_PATH`      | 툴이 설치 _되어야하는_ 혹은 _되어있는_ 경로                       |\n| `ASDF_CONCURRENCY`       | 소스 코드를 컴파일할 때 사용할 코어 수. `make-j`를 설정할 때 유용 |\n| `ASDF_DOWNLOAD_PATH`     | `bin/download`에 의해 소스 코드 또는 바이너리가 다운로드 된 경로  |\n| `ASDF_PLUGIN_PATH`       | 플러그인이 설치된 경로                                            |\n| `ASDF_PLUGIN_SOURCE_URL` | 플러그인의 소스 URL                                               |\n| `ASDF_PLUGIN_PREV_REF`   | 플러그인 리포지토리의 이전 `git-ref`                              |\n| `ASDF_PLUGIN_POST_REF`   | 플러그인 리포지토리의 업데이트 된 `git-ref`                       |\n| `ASDF_CMD_FILE`          | source 되는 파일의 전체 경로를 해결                               |\n\n::: tip 노트\n\n**모든 스크립트에서 모든 환경 변수를 사용할 수 있는 것은 아닙니다.** 아래 각 스크립트에 대한\n문서를 확인하여 사용할 수 있는 환경 변수들을 확인하세요.\n\n:::\n\n## 필수적 스크립트\n\n### `bin/list-all` <Badge type=\"tip\" text=\"필수\" vertical=\"middle\" />\n\n**설명**\n\n설치 가능한 모든 버전 나열.\n\n**출력 형식**\n\n**공백으로 구분된** 문자열을 반드시 출력. 예를 들어:\n\n```txt\n1.0.1 1.0.2 1.3.0 1.4\n```\n\n최신 버전이 마지막에 와야 합니다.\n\nasdf core는 각 버전을 각각의 행에 출력하여, 일부 버전을 화면 밖으로\n밀어낼 가능성이 있습니다.\n\n**정렬**\n\n웹사이트의 릴리스 페이지에서 버전을 가져오는 경우에는\n이미 올바른 순서로 되어 있는 경우가 많기 때문에 제공된 순서대로 두는 것이\n좋습니다. 역순으로 되어 있는 경우 `tac`을 통해 해당 버전들을 바로 잡는것으로\n충분합니다.\n\n정렬이 불가피한 경우, `sort -V`는 사용이 불가능하므로, 다음 중 하나를 제안합니다:\n\n- [Git 정렬 기능 사용](https://github.com/asdf-vm/asdf-plugin-template/blob/main/template/lib/utils.bash)\n  (Git `v2.18.0` 이상 필요)\n- [맞춤 정렬 함수 작성](https://github.com/vic/asdf-idris/blob/master/bin/list-all#L6)\n  (`sed`, `sort` & `awk` 필요)\n\n**스크립트에서 사용 가능한 환경 변수**\n\n이 스크립트에는 환경 변수가 제공되지 않습니다.\n\n**이 스크립트를 호출하는 명령어**\n\n- `asdf list all <name> [version]`\n- `asdf list all nodejs`: 이 스크립트에 의해 반환된 모든 버전을 나열합니다,\n  한 행에 한개씩.\n- `asdf list all nodejs 18`: 이 스크립트에 의해 반환된 모든 버전을 나열하며,\n  각 행에 하나씩, `18`로 시작하는 모든 버전에 필터가 적용됩니다.\n\n**asdf core에서 호출 시그니처**\n\n제공되는 매개변수는 없습니다.\n\n```bash\n\"${plugin_path}/bin/list-all\"\n```\n\n---\n\n### `bin/download` <Badge type=\"tip\" text=\"필수\" vertical=\"middle\" />\n\n**설명**\n\n지정된 장소에 지정된 버전에 대한 소스 코드 또는 바이너리 다운로드\n\n**구현 세부사항**\n\n- 스크립트는 소스 또는 바이너리를 `ASDF_DOWNLOAD_PATH`에서 지정된 디렉토리에 다운로드해야합니다.\n- 압축 해제된 소스 코드 또는 바이너리만 `ASDF_DOWNLOAD_PATH` 디렉토리에 위치해야합니다.\n- 실패 시에는 `ASDF_DOLOAD_PATH`에 어떠한 파일도 남아서는 안 됩니다.\n- 성공 시에는 `0`이 종료 코드입니다.\n- 실패 시에는 0이 아닌 상태의 종료 코드입니다.\n\n**레거시 플러그인**\n\n비록 이 스크립트는 모든 플러그인에서 *필수*로 되어 있지만, 이 스크립트가 도입되기 이전의 \"레거시\" 플러그인에서는 _선택_ 입니다.\n\n이 스크립트가 없는 경우, asdf는 `bin/install` 스크립트가 있다고 가정하고 해당 버전을 다운로드 **그리고** 설치합니다.\n\n레거시 플러그인 지원은 최종적으로 제거될 예정이기 때문에 앞으로 작성할 모든 플러그인에서 이 스크립트를 포함해야합니다.\n\n**스크립트에서 사용 가능한 환경 변수**\n\n- `ASDF_INSTALL_TYPE`: `version` 또는 `ref`\n- `ASDF_INSTALL_VERSION`:\n  - `ASDF_INSTALL_TYPE=version`의 경우, 풀 버전 번호.\n  - `ASDF_INSTALL_TYPE=ref`의 경우, Git ref (태그/커밋/브랜치).\n- `ASDF_INSTALL_PATH`: 툴이 설치 _되어있는_, 또는 _되어야하는_ 경로.\n- `ASDF_DOWNLOAD_PATH`: 소스 코드 또는 바이너리 파일이 다운로드 된 경로.\n\n**이 스크립트를 호출하는 명령어**\n\n- `asdf install <tool> [version]`\n- `asdf install <tool> latest[:version]`\n- `asdf install nodejs 18.0.0`: Node.js 버전 `18.0.0`의 소스 코드 또는 바이너리를 다운로드하고\n  `ASDF_DOWNLOAD_PATH` 디렉토리에 저장. 그 다음 `bin/install` 스크립트를 실행.\n\n**asdf core에서 호출 시그니처**\n\n제공되는 매개변수는 없습니다.\n\n```bash\n\"${plugin_path}\"/bin/download\n```\n\n---\n\n### `bin/install` <Badge type=\"tip\" text=\"필수\" vertical=\"middle\" />\n\n**설명**\n\n특정 버전의 도구를 지정된 위치에 설치.\n\n**구현 세부사항**\n\n- 스크립트는 `ASDF_INSTALL_PATH` 경로에 지정된 버전을 설치해야합니다.\n- Shim은 `$ASDF_INSTALL_PATH/bin`에 있는 어떠한 파일에 대해서든 기본적으로 생성됩니다. 이 동작은\n  선택적 [bin/list-bin-paths](#binlist-bin-paths) 스크립트로 맞춤 설정 가능합니다.\n- 성공 시에는 `0`이 종료 코드입니다.\n- 실패 시에는 0이 아닌 상태의 종료 코드입니다.\n- TOCTOU(Time-of-Check-to-Off-Use) 문제를 방지하려면, 툴의 빌드 및 설치가 성공적이라고 판단될때만 스크립트에서 파일을 `ASDF_INSTALL_PATH`에 배치합니다.\n\n**레거시 플러그인**\n\n`bin/download` 스크립트가 없는 경우, 이 스크립트는 지정된 버전을 다운로드 **그리고** 설치해야합니다.\n\n`0.7._`보다 이전 그리고 `0.8._`보다 이후 asdf 코어 버전들의 호환성을 확인하려면, `ASDF_DOWNLOAD_PATH` 환경 변수가 있는지 확인합니다.\n그 환경 변수가 존재하는 경우, 이미 `bin/download` 스크립트가 그 버전을 다운로드했다고 가정하고, 존재하지 않으면 `bin/install` 스크립트에서 소스 코드를 다운로드합니다.\n\n**스크립트에서 사용 가능한 환경 변수**\n\n- `ASDF_INSTALL_TYPE`: `version` 또는 `ref`\n- `ASDF_INSTALL_VERSION`:\n  - `ASDF_INSTALL_TYPE=version`의 경우, 풀 버전 번호.\n  - `ASDF_INSTALL_TYPE=ref`의 경우, Git ref (태그/커밋/브랜치).\n- `ASDF_INSTALL_PATH`: 툴이 설치 _되어있는_, 또는 _되어야하는_ 경로.\n- `ASDF_CONCURRENCY`: 소스 코드를 컴파일할 때 사용할 코어 수. `make-j`를 설정할 때 유용.\n- `ASDF_DOWNLOAD_PATH`: 소스 코드 또는 바이너리 파일이 다운로드 된 경로.\n\n**이 스크립트를 호출하는 명령어**\n\n- `asdf install`\n- `asdf install <tool>`\n- `asdf install <tool> [version]`\n- `asdf install <tool> latest[:version]`\n- `asdf install nodejs 18.0.0`: Node.js 버전 `18.0.0`을\n  `ASDF_INSTALL_PATH` 디렉토리에 설치.\n\n**asdf core에서 호출 시그니처**\n\n제공되는 매개변수는 없습니다.\n\n```bash\n\"${plugin_path}\"/bin/install\n```\n\n## 선택적 스크립트\n\n### `bin/latest-stable` <Badge type=\"warning\" text=\"추천\" vertical=\"middle\" />\n\n**설명**\n\n도구의 최신 안정 버전을 결정합니다. 이 스크립트가 존재하지 않는 경우, asdf 코어는 `bin/list-all`의 출력을 비의도적으로 `tail`합니다.\n\n**구현 세부사항**\n\n- 스크립트는 도구의 최신 안정 버전을 표준 출력에 출력해야합니다.\n- 비안정판이나 릴리스 후보판은 제외되어야 합니다.\n- 필터 쿼리는 스크립트의 첫 번째 인수로 제공됩니다 이 쿼리는 버전 번호나 툴 제공자에 의한 출력값을 필터하기 위해 사용되어야 합니다.\n  - 예를 들어 [ruby 플러그인](https://github.com/asdf-vm/asdf-ruby)에서의 `asdf list all ruby`는 `jruby`, `rbx`, `truffleruby` 등의 많은 제공자들의 Ruby 버전 목록을 출력합니다. 사용자가 제공한 필터는 플러그인이 유의적 버전 및/또는 공급자를 필터링하는 데 사용될 수 있습니다.\n    ```\n    > asdf latest ruby\n    3.2.2\n    > asdf latest ruby 2\n    2.7.8\n    > asdf latest ruby truffleruby\n    truffleruby+graalvm-22.3.1\n    ```\n- 성공 시에는 `0`이 종료 코드입니다.\n- 실패 시에는 0이 아닌 상태의 종료 코드입니다.\n\n**스크립트에서 사용 가능한 환경 변수**\n\n- `ASDF_INSTALL_TYPE`: `version` 또는 `ref`\n- `ASDF_INSTALL_VERSION`:\n  - `ASDF_INSTALL_TYPE=version`의 경우, 풀 버전 번호.\n  - `ASDF_INSTALL_TYPE=ref`의 경우, Git ref (태그/커밋/브랜치).\n- `ASDF_INSTALL_PATH`: 툴이 설치 _되어있는_, 또는 _되어야하는_ 경로.\n\n**이 스크립트를 호출하는 명령어**\n\n- `asdf set <tool> latest`: 툴의 버전을 해당 툴의 최신 안정 버전으로 설정합니다.\n- `asdf install <tool> latest`: 최신 버전의 툴을 설치합니다.\n- `asdf latest <tool> [<version>]`: 선택적인 필터를 기반으로 도구의 최신 버전을 출력합니다.\n- `asdf latest --all`: asdf에서 관리하는 모든 툴의 최신 버전과 설치 여부를 출력합니다.\n\n**asdf core에서 호출 시그니처**\n\n이 스크립트는 필터 쿼리라는 하나의 인수를 받습니다.\n\n```bash\n\"${plugin_path}\"/bin/latest-stable \"$query\"\n```\n\n---\n\n### `bin/help.overview`\n\n**설명**\n\n플러그인 및 관리 중인 툴에 대한 일반적인 설명을 출력.\n\n**구현 세부사항**\n\n- 플러그인에 대한 도움말 출력을 표시하려면 이 스크립트가 필요합니다.\n- asdf 코어가 머리말를 인쇄하므로 머리말을 출력해서는 안 됩니다.\n- 자유로운 형식의 텍스트로 출력해도 상관없지만 짧은 한 단락 정도의 설명이 이상적입니다.\n- 핵심이 되는 asdf-vm 문서에서 이미 설명되어 있는 정보는 출력하지 않아야 합니다.\n- 운영 체제와 설치된 툴의 버전에 맞게 출력해야합니다 (필요에 따라 `ASDF_INSTALL_VERSION` 및 `ASDF_INSTALL_TYPE` 환경 변수의 값을 사용하십시오).\n- 성공 시에는 `0`이 종료 코드입니다.\n- 실패 시에는 0이 아닌 상태의 종료 코드입니다.\n\n**스크립트에서 사용 가능한 환경 변수**\n\n- `ASDF_INSTALL_TYPE`: `version` 또는 `ref`\n- `ASDF_INSTALL_VERSION`:\n  - `ASDF_INSTALL_TYPE=version`의 경우, 풀 버전 번호.\n  - `ASDF_INSTALL_TYPE=ref`의 경우, Git ref (태그/커밋/브랜치).\n- `ASDF_INSTALL_PATH`: 툴이 설치 _되어있는_, 또는 _되어야하는_ 경로.\n\n**이 스크립트를 호출하는 명령어**\n\n- `asdf help <name> [<version>]`: 플러그인 및 도구 문서를 출력\n\n**asdf core에서 호출 시그니처**\n\n```bash\n\"${plugin_path}\"/bin/help.overview\n```\n\n---\n\n### `bin/help.deps`\n\n**설명**\n\n운영 체제에 맞는 dependencies 목록을 출합니다. 한 행마다 한 개의 dependency.\n\n```bash\ngit\ncurl\nsed\n```\n\n**구현 세부사항**\n\n- 이 스크립트의 출력되기 위해서는 `bin/help.overview`가 필요합니다.\n- 운영 체제와 설치된 툴의 버전에 맞게 출력해야합니다 (필요에 따라 `ASDF_INSTALL_VERSION` 및 `ASDF_INSTALL_TYPE` 환경 변수의 값을 사용하십시오).\n- 성공 시에는 `0`이 종료 코드입니다.\n- 실패 시에는 0이 아닌 상태의 종료 코드입니다.\n\n**스크립트에서 사용 가능한 환경 변수**\n\n- `ASDF_INSTALL_TYPE`: `version` 또는 `ref`\n- `ASDF_INSTALL_VERSION`:\n  - `ASDF_INSTALL_TYPE=version`의 경우, 풀 버전 번호.\n  - `ASDF_INSTALL_TYPE=ref`의 경우, Git ref (태그/커밋/브랜치).\n- `ASDF_INSTALL_PATH`: 툴이 설치 _되어있는_, 또는 _되어야하는_ 경로.\n\n**이 스크립트를 호출하는 명령어**\n\n- `asdf help <name> [<version>]`: 플러그인 및 도구 문서를 출력\n\n**asdf core에서 호출 시그니처**\n\n```bash\n\"${plugin_path}\"/bin/help.deps\n```\n\n---\n\n### `bin/help.config`\n\n**설명**\n\n플러그인 및 도구에 필수적 또는 선택적 설정 출력. 예를 들어, 도구를 설치하거나 컴파일하는 데 필요한 환경 변수나 기타 플래그를 설명.\n\n**구현 세부사항**\n\n- 이 스크립트의 출력되기 위해서는 `bin/help.overview`가 필요합니다.\n- 자유로운 형식의 텍스트로 출력할 수 있습니다.\n- 운영 체제와 설치된 툴의 버전에 맞게 출력해야합니다 (필요에 따라 `ASDF_INSTALL_VERSION` 및 `ASDF_INSTALL_TYPE` 환경 변수의 값을 사용하십시오).\n- 성공 시에는 `0`이 종료 코드입니다.\n- 실패 시에는 0이 아닌 상태의 종료 코드입니다.\n\n**스크립트에서 사용 가능한 환경 변수**\n\n- `ASDF_INSTALL_TYPE`: `version` 또는 `ref`\n- `ASDF_INSTALL_VERSION`:\n  - `ASDF_INSTALL_TYPE=version`의 경우, 풀 버전 번호.\n  - `ASDF_INSTALL_TYPE=ref`의 경우, Git ref (태그/커밋/브랜치).\n- `ASDF_INSTALL_PATH`: 툴이 설치 _되어있는_, 또는 _되어야하는_ 경로.\n\n**이 스크립트를 호출하는 명령어**\n\n- `asdf help <name> [<version>]`: 플러그인 및 도구 문서를 출력\n\n**asdf core에서 호출 시그니처**\n\n```bash\n\"${plugin_path}\"/bin/help.config\n```\n\n---\n\n### `bin/help.links`\n\n**설명**\n\n플러그인 및 툴과 관련된 링크 목록을 출력. 한 행마다 한 개의 링크.\n\n```bash\nGit Repository:\thttps://github.com/vlang/v\nDocumentation:\thttps://vlang.io\n```\n\n**구현 세부사항**\n\n- 이 스크립트의 출력되기 위해서는 `bin/help.overview`가 필요합니다.\n- 한행마다 한 개의 링크.\n- 형식은 다음 중에 하나여야합니다:\n  - `<title>: <link>`\n  - 또는 그냥 `<link>`\n- 운영 체제와 설치된 툴의 버전에 맞게 출력해야합니다 (필요에 따라 `ASDF_INSTALL_VERSION` 및 `ASDF_INSTALL_TYPE` 환경 변수의 값을 사용하십시오).\n- 성공 시에는 `0`이 종료 코드입니다.\n- 실패 시에는 0이 아닌 상태의 종료 코드입니다.\n\n**스크립트에서 사용 가능한 환경 변수**\n\n- `ASDF_INSTALL_TYPE`: `version` 또는 `ref`\n- `ASDF_INSTALL_VERSION`:\n  - `ASDF_INSTALL_TYPE=version`의 경우, 풀 버전 번호.\n  - `ASDF_INSTALL_TYPE=ref`의 경우, Git ref (태그/커밋/브랜치).\n- `ASDF_INSTALL_PATH`: 툴이 설치 _되어있는_, 또는 _되어야하는_ 경로.\n\n**이 스크립트를 호출하는 명령어**\n\n- `asdf help <name> [<version>]`: 플러그인 및 도구 문서를 출력\n\n**asdf core에서 호출 시그니처**\n\n```bash\n\"${plugin_path}\"/bin/help.links\n```\n\n---\n\n### `bin/list-bin-paths`\n\n**설명**\n\n툴의 특정 버전에서 실행파일이 포함된 디렉토리 목록을 출력.\n\n**구현 세부사항**\n\n- 이 스크립트가 존재하지 않는 경우, asdf는 `\"${ASDF_INSTALL_PATH}\"/bin` 디렉토리 내에 있는 바이너리들을 찾아 그 바이너리를 위한 shim들을 생성합니다.\n- 실행파일이 포함된 디렉토리의 경로를 공백으로 구분하여 출력합니다.\n- 경로는 `ASDF_INSTALL_PATH`로의 상대 경로이어야 합니다. 출력 예시는 다음과 같습니다:\n\n```bash\nbin tools veggies\n```\n\n이는 asdf가 그 파일들을 위한 shim들을 다음 위치에 생성하게 지시합니다:\n\n- `\"${ASDF_INSTALL_PATH}\"/bin`\n- `\"${ASDF_INSTALL_PATH}\"/tools`\n- `\"${ASDF_INSTALL_PATH}\"/veggies`\n\n**스크립트에서 사용 가능한 환경 변수**\n\n- `ASDF_INSTALL_TYPE`: `version` 또는 `ref`\n- `ASDF_INSTALL_VERSION`:\n  - `ASDF_INSTALL_TYPE=version`의 경우, 풀 버전 번호.\n  - `ASDF_INSTALL_TYPE=ref`의 경우, Git ref (태그/커밋/브랜치).\n- `ASDF_INSTALL_PATH`: 툴이 설치 _되어있는_, 또는 _되어야하는_ 경로.\n\n**이 스크립트를 호출하는 명령어**\n\n- `asdf install <tool> [version]`: 초기에 바이너리들을 위한 shim들 생성.\n- `asdf reshim <tool> <version>`: 바이너리들을 위한 shim들 재생성.\n\n**asdf core에서 호출 시그니처**\n\n```bash\n\"${plugin_path}/bin/list-bin-paths\"\n```\n\n---\n\n### `bin/exec-env`\n\n**설명**\n\n툴 바이너리의 shim을 실행하기 전에 환경을 준비.\n\n**스크립트에서 사용 가능한 환경 변수**\n\n- `ASDF_INSTALL_TYPE`: `version` 또는 `ref`\n- `ASDF_INSTALL_VERSION`:\n  - `ASDF_INSTALL_TYPE=version`의 경우, 풀 버전 번호.\n  - `ASDF_INSTALL_TYPE=ref`의 경우, Git ref (태그/커밋/브랜치).\n- `ASDF_INSTALL_PATH`: 툴이 설치 _되어있는_, 또는 _되어야하는_ 경로.\n\n**이 스크립트를 호출하는 명령어**\n\n- `asdf which <command>`: 실행파일의 경로 표시\n- `asdf exec <command> [args...]`: 현재 버전에서 shim 명령을 실행\n- `asdf env <command> [util]`: shim 명령어 실행 시 사용되는 환경에서 유틸리티(기본값: `env`)를 실행.\n\n**asdf core에서 호출 시그니처**\n\n```bash\n\"${plugin_path}/bin/exec-env\"\n```\n\n---\n\n### `bin/exec-path`\n\n툴의 특정 버전의 실행파일 경로를 가져옵니다.\n실행파일에 대한 상대 경로를 문자열로 출력해야합니다.\n이는 플러그인이 shim에서 지정한 실행파일 경로를 조건부로 덮어쓰게 하거나,\n그렇지 않으면 shim에서 지정한 기본 경로를 반환합니다.\n\n**설명**\n\n툴의 특정 버전의 실행파일 경로를 가져옵니다.\n\n**구현 세부사항**\n\n- 실행파일에 대한 상대 경로를 문자열로 출력.\n- Shim에서 지정한 실행파일 경로를 조건부로 덮어쓰거나, 그렇지 않으면 shim에서 지정한 기본 경로를 반환.\n\n```shell\nUsage:\n  plugin/bin/exec-path <install-path> <command> <executable-path>\n\nExample Call:\n  ~/.asdf/plugins/foo/bin/exec-path \"~/.asdf/installs/foo/1.0\" \"foo\" \"bin/foo\"\n\nOutput:\n  bin/foox\n```\n\n**스크립트에서 사용 가능한 환경 변수**\n\n- `ASDF_INSTALL_TYPE`: `version` 또는 `ref`\n- `ASDF_INSTALL_VERSION`:\n  - `ASDF_INSTALL_TYPE=version`의 경우, 풀 버전 번호.\n  - `ASDF_INSTALL_TYPE=ref`의 경우, Git ref (태그/커밋/브랜치).\n- `ASDF_INSTALL_PATH`: 툴이 설치 _되어있는_, 또는 _되어야하는_ 경로.\n\n**이 스크립트를 호출하는 명령어**\n\n- `asdf which <command>`: 실행파일의 경로 표시\n- `asdf exec <command> [args...]`: 현재 버전에서 shim 명령을 실행\n- `asdf env <command> [util]`: shim 명령어 실행 시 사용되는 환경에서 유틸리티(기본값: `env`)를 실행.\n\n**asdf core에서 호출 시그니처**\n\n```bash\n\"${plugin_path}/bin/exec-path\" \"$install_path\" \"$cmd\" \"$relative_path\"\n```\n\n---\n\n### `bin/uninstall`\n\n**설명**\n\n툴의 지정된 버전을 제거합니다.\n\n**출력 형식**\n\n출력값은 `stdout` 또는 `stderr`에 적절히 송신 되어야 합니다. 어떠한 출력값도 후속 코어 실행에 의해 사용되지 않습니다.\n\n**스크립트에서 사용 가능한 환경 변수**\n\n이 스크립트에는 환경 변수가 제공되지 않습니다.\n\n**이 스크립트를 호출하는 명령어**\n\n- `asdf list all <name> <version>`\n- `asdf uninstall nodejs 18.15.0`: nodejs의 `18.15.0` 버전을 제거, `npm i -g`로 설치된 모든 글로벌 shim들 또한 제거.\n\n**asdf core에서 호출 시그니처**\n\n제공되는 매개변수는 없습니다.\n\n```bash\n\"${plugin_path}/bin/uninstall\"\n```\n\n---\n\n### `bin/list-legacy-filenames`\n\n**설명**\n\n툴 버전을 결정하는 데 사용된 레거시 설정 파일 목록을 출력.\n\n**구현 세부사항**\n\n- 파일이름들의 목록을 공백으로 구분하여 출력.\n  ```bash\n  .ruby-version .rvmrc\n  ```\n- `\"${HOME}\"/.asdfrc`에서 `legacy_version_file` 옵션을 활성화한 사용자에게만 적용됩니다.\n\n**스크립트에서 사용 가능한 환경 변수**\n\n- `ASDF_INSTALL_TYPE`: `version` 또는 `ref`\n- `ASDF_INSTALL_VERSION`:\n  - `ASDF_INSTALL_TYPE=version`의 경우, 풀 버전 번호.\n  - `ASDF_INSTALL_TYPE=ref`의 경우, Git ref (태그/커밋/브랜치).\n- `ASDF_INSTALL_PATH`: 툴이 설치 _되어있는_, 또는 _되어야하는_ 경로.\n\n**이 스크립트를 호출하는 명령어**\n\n툴 버전을 가져오는 모든 명령에서 호출됩니다.\n\n**asdf core에서 호출 시그니처**\n\n제공되는 매개변수는 없습니다.\n\n```bash\n\"${plugin_path}/bin/list-legacy-filenames\"\n```\n\n---\n\n### `bin/parse-legacy-file`\n\n**설명**\n\nasdf에 의해 발견된 레거시 파일을 parse하여 툴의 버전을 결정. 자바스크립트의 `package.json`이나 Go 언어의 `go.mod`와 같은 파일에서 버전 번호를 추출하는 데 유용.\n\n**구현 세부사항**\n\n- 이 스크립트가 존재하지 않는 경우, asdf는 단순히 레거시 파일을 `cat`하여 버전을 결정합니다.\n- 다음과 같은 상황에서도 **결정론적**이고 항상 동일하고 정확한 버전을 반환해야합니다:\n  - 동일한 레거시 파일을 구문 parsing할 때.\n  - 무엇이 설치되어 있는지 또는 레거시 버전이 유효하거나 완전한지는 관계 없이. 일부 레거시 파일 형식은 맞지 않을 수도 있습니다.\n- 아래와 같이 버전 번호를 한 줄로 출력해 주세요:\n  ```bash\n  1.2.3\n  ```\n\n**스크립트에서 사용 가능한 환경 변수**\n\n이 스크립트가 호출되기 전에 환경 변수가 설정되지 않습니다.\n\n**이 스크립트를 호출하는 명령어**\n\n툴 버전을 가져오는 모든 명령에서 호출됩니다.\n\n**asdf core에서 호출 시그니처**\n\n이 스크립트는 레거시 파일의 내용을 읽기 위해 레거시 파일의 경로라는 하나의 인수를 받습니다.\n\n```bash\n\"${plugin_path}/bin/parse-legacy-file\" \"$file_path\"\n```\n\n---\n\n### `bin/post-plugin-add`\n\n**설명**\n\n이 콜백 스크립트는 asdf의 `asdf plugin add <tool>` 명령어로 플러그인이 _추가된_ **후에** 실행됩니다.\n\n관련된 명령어 훅들을 참조하세요:\n\n- `pre_asdf_plugin_add`\n- `pre_asdf_plugin_add_${plugin_name}`\n- `post_asdf_plugin_add`\n- `post_asdf_plugin_add_${plugin_name}`\n\n**스크립트에서 사용 가능한 환경 변수**\n\n- `ASDF_PLUGIN_PATH`: 플러그인이 설치된 경로.\n- `ASDF_PLUGIN_SOURCE_URL`: 플러그인 소스의 URL. 로컬 디렉토리 경로일 수 있음.\n\n**asdf core에서 호출 시그니처**\n\n제공되는 매개변수는 없습니다.\n\n```bash\n\"${plugin_path}/bin/post-plugin-add\"\n```\n\n---\n\n### `bin/post-plugin-update`\n\n**설명**\n\n이 콜백 스크립트는 asdf가 `asdf plugin update <tool> [<git-ref>]` 커맨드로 플러그인 _업데이트_ 를 다운로드한 **후에** 실행됩니다.\n\n관련된 명령어 훅들을 참조하세요:\n\n- `pre_asdf_plugin_update`\n- `pre_asdf_plugin_update_${plugin_name}`\n- `post_asdf_plugin_update`\n- `post_asdf_plugin_update_${plugin_name}`\n\n**스크립트에서 사용 가능한 환경 변수**\n\n- `ASDF_PLUGIN_PATH`: 플러그인이 설치된 경로.\n- `ASDF_PLUGIN_PREV_REF`: 플러그인의 이전 git-ref\n- `ASDF_PLUGIN_POST_REF`: 플러그인의 업데이트 된 git-ref\n\n**asdf core에서 호출 시그니처**\n\n제공되는 매개변수는 없습니다.\n\n```bash\n\"${plugin_path}/bin/post-plugin-update\"\n```\n\n---\n\n### `bin/pre-plugin-remove`\n\n**설명**\n\nasdf가 `asdf plugin remove <tool>` 커맨드로 플러그인을 제거하기 **전에** 이 콜백 스크립트를 실행시키세요.\n\n관련된 명령어 훅들을 참조하세요:\n\n- `pre_asdf_plugin_remove`\n- `pre_asdf_plugin_remove_${plugin_name}`\n- `post_asdf_plugin_remove`\n- `post_asdf_plugin_remove_${plugin_name}`\n\n**스크립트에서 사용 가능한 환경 변수**\n\n- `ASDF_PLUGIN_PATH`: 플러그인이 설치된 경로.\n\n**asdf core에서 호출 시그니처**\n\n제공되는 매개변수는 없습니다.\n\n```bash\n\"${plugin_path}/bin/pre-plugin-remove\"\n```\n\n<!-- TODO: document command hooks -->\n<!-- ## Command Hooks -->\n\n## asdf CLI 확장 명령어 <Badge type=\"danger\" text=\"고급\" vertical=\"middle\" />\n\n`lib/commands/command*.bash` 스크립트 또는 플러그인 이름을 하위명령어로 사용하여\nasdf 명령줄 인터페이스를 통해 호출할 수 있는 실행파일을 제공함으로써\n새로운 asdf 명령어를 정의할 수 있습니다.\n\n예를 들면, `foo`라고 하는 플러그인이 있다고 하면:\n\n```shell\nfoo/\n  lib/commands/\n    command.bash\n    command-bat.bash\n    command-bat-man.bash\n    command-help.bash\n```\n\n사용자는 아래 명령을 실행할 수 있게 됩니다:\n\n```shell\n$ asdf foo         # same as running `$ASDF_DATA_DIR/plugins/foo/lib/commands/command.bash`\n$ asdf foo bar     # same as running `$ASDF_DATA_DIR/plugins/foo/lib/commands/command.bash bar`\n$ asdf foo help    # same as running `$ASDF_DATA_DIR/plugins/foo/lib/commands/command-help.bash`\n$ asdf foo bat man # same as running `$ASDF_DATA_DIR/plugins/foo/lib/commands/command-bat-man.bash`\n$ asdf foo bat baz # same as running `$ASDF_DATA_DIR/plugins/foo/lib/commands/command-bat.bash baz`\n```\n\n플러그인 개발자는 이 기능을 사용하여 툴과 관련된 유틸리티를 제공하거나,\nasdf 자체의 명령어 확장 플러그인을 생성할 수 있습니다.\n\n실행 가능 비트(executable bit)가 부여되어 있는 경우, asdf 실행을 대신하여\n해당 스크립트가 실행됩니다.\n\n실행 가능 비트(executable bit)가 부여되지 않은 경우, asdf는 해당 스크립트를 Bash 스크립트로 source합니다.\n\n`$ASDF_CMD_FILE`는 source 되는 파일의 전체 경로를 해결합니다.\n\n[`haxe`](https://github.com/asdf-community/asdf-haxe)는\n이 기능을 사용하는 플러그인의 좋은 예시입니다.\n이 플러그인은 Haxe 실행파일이 해당 디렉토리에서 상대적으로 동적 라이브러리를 찾으려하는\n문제해결을 위해 `asdf haxe neko-dylibs-link`를 제공합니다.\n\n플러그인 README에는 asdf 확장 명령어에 관한 것을 반드시 기재하도록 하십시오.\n\n## 맞춤 Shim 템플릿 <Badge type=\"danger\" text=\"고급\" vertical=\"middle\" />\n\n::: warning 경고\n\n**반드시** 필요한 경우에만 사용하세요.\n\n:::\n\nasdf에서는 맞춤 shim 템플릿을 사용할 수 있습니다. `foo`라고 하는 실행파일에 대해,\n플러그인 내에 `shims/foo` 파일이 존재하면, asdf는 표준 shim 템플릿을\n사용하는 대신 그 파일을 복사합니다.\n\n**이 기능은 현명하게 사용해야합니다.**\n\nasdf 코어팀이 파악하고 있는 것은, 이 기능은 오직 공식 플러그인\n[Elixir 플러그인](https://github.com/asdf-vm/asdf-elixir)에서만 사용되고 있습니다.\n이는 실행파일은 실행파일일 뿐만 아니라 Elixir 파일로도 읽히기 때문입니다.\n이 때문에 표준 Bash shim을 사용할 수 없습니다.\n\n## 테스팅\n\n`asdf`에는 플러그인을 테스트하기 위한 `plugin-test` 명령어가 포함되어 있습니다:\n\n```shell\nasdf plugin test <plugin_name> <plugin_url> [--asdf-tool-version <version>] [--asdf-plugin-gitref <git_ref>] [test_command...]\n```\n\n- `<plugin_name>` 및 `<plugin_url>`는 필수적입니다\n- 옵션에서 `[--asdf-tool-version <version>]`를 지정하면, 해당 지정된 버전의 툴이 설치됩니다.\n  기본값은 `asdf latest <plugin-name>`입니다.\n- 옵션에서 `[--asdf-plugin-gitref <git_ref>]`를 지정하면,\n  그 커밋/브랜치/태그로 플러그인 자체를 체크아웃합니다.\n  이것은 플러그인 CI에서 풀 요청을 테스트할 때 유용합니다.\n- 선택적 매개변수 `[test_command...]`는 설치된 툴이 올바르게 동작하는지 확인하기위해 실행시키는 명령어입니다.\n  일반적으로 `<tool> --version` 또는\n  `<tool> --help`입니다. 예를 들어, NodeJS 플러그인을 테스트하기 위해, 다음을 실행시킬 수 있습니다\n  ```shell\n  # asdf plugin test <plugin_name>  <plugin_url>                               [test_command]\n    asdf plugin test nodejs         https://github.com/asdf-vm/asdf-nodejs.git node --version\n  ```\n\n::: tip 노트\n\n리눅스와 맥 운영체제 양쪽 CI 환경에서 모두 테스트하는 것을 권장합니다.\n\n:::\n\n### GitHub Action\n\n[asdf-vm/actions](https://github.com/asdf-vm/actions) 리포지토리는\nGitHub에서 호스팅되는 플러그인을 테스트하기 위한 GitHub Action을 제공합니다.\n`.github/workflows/test.yaml` 액션 워크플로우 예시:\n\n```yaml\nname: Test\non:\n  push:\n    branches:\n      - main\n  pull_request:\n\njobs:\n  plugin_test:\n    name: asdf plugin test\n    strategy:\n      matrix:\n        os:\n          - ubuntu-latest\n          - macos-latest\n    runs-on: ${{ matrix.os }}\n    steps:\n      - name: asdf_plugin_test\n        uses: asdf-vm/actions/plugin-test@v2\n        with:\n          command: \"<MY_TOOL> --version\"\n```\n\n### TravisCI 설정\n\n`.travis.yml` 예시 파일, 필요에 따라 바꿔 사용하세요:\n\n```yaml\nlanguage: c\nscript: asdf plugin test <MY_TOOL> $TRAVIS_BUILD_DIR '<MY_TOOL> --version'\nbefore_script:\n  - git clone https://github.com/asdf-vm/asdf.git asdf\n  - . asdf/asdf.sh\nos:\n  - linux\n  - osx\n```\n\n::: tip 노트\n\n다른 CI를 사용하는 경우,\n플러그인 위치에 대한 상대 경로를 전달할 필요가 있는 경우가 있습니다:\n\n```shell\nasdf plugin test <tool_name> <path> '<tool_command> --version'\n```\n\n:::\n\n## API 속도 제한\n\n`bin/list-all`이나 `bin/latest-stable`과 같이 명령어가 외부 API에 대한 접근에 의존하고 있는 경우,\n자동화 테스트 중에 속도 제한이 발생할 수 있습니다.\n이를 줄이기 위해, 환경 변수를 통해 인증 토큰을 제공하는 코드 경로가 있는지 확인하십시오.\n예를 들어:\n\n```shell\ncmd=\"curl --silent\"\nif [ -n \"$GITHUB_API_TOKEN\" ]; then\n cmd=\"$cmd -H 'Authorization: token $GITHUB_API_TOKEN'\"\nfi\n\ncmd=\"$cmd $releases_path\"\n```\n\n### `GITHUB_API_TOKEN`\n\n`GITHUB_API_TOKEN`를 사용할 때는,\n오직 `public_repo` 액세스 권환으로\n[새로운 개인 토큰](https://github.com/settings/tokens/new)을 생성합니다.\n\n다음으로 이 토큰을 CI pipeline 환경 변수에 추가하십시오.\n\n::: warning 경고\n\n절대 인증 토큰을 코드 리포지토리에 공개해서는 안됩니다.\n\n:::\n\n## 플러그인 Shortname 인덱스\n\n::: tip\n\n권장되는 플러그인 설치 방법은 URL을 바탕으로 직접 설치입니다:\n\n```shell\n# asdf plugin add <name> <git_url>\n  asdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs\n```\n\n:::\n\n`git_url`이 지정되지 않은 경우,\nasdf는 사용될 `git_url`을 정확히 결정하기 위해\n[Shortname 인덱스 리포지토리](https://github.com/asdf-vm/asdf-plugins)를 사용합니다.\n\n[Shortname 인덱스](https://github.com/asdf-vm/asdf-plugins)에\n설명서에 따라 플러그인을\n해당 리포지토리에 추가할 수 있습니다.\n"
  },
  {
    "path": "docs/manage/commands.md",
    "content": "# All Commands\n\nThe list of all commands available in `asdf`. This list is the `asdf help` command text.\n\n<<< @../../internal/help/help.txt\n"
  },
  {
    "path": "docs/manage/configuration.md",
    "content": "# Configuration\n\nConfiguration of `asdf` encompasses both the sharable `.tool-versions` files as well as user specific customisations with `.asdfrc` and Environment Variables.\n\n## `.tool-versions`\n\nWhenever `.tool-versions` file is present in a directory, the tool versions it declares will be used in that directory and any subdirectories.\n\nThis is what a `.tool-versions` file looks like:\n\n```\nruby 2.5.3\nnodejs 10.15.0\n```\n\nYou can also include comments:\n\n```\nruby 2.5.3 # This is a comment\n# This is another comment\nnodejs 10.15.0\n```\n\nThe versions can be in the following format:\n\n- `10.15.0` - an actual version. Plugins that support downloading binaries, will download binaries.\n- `ref:v1.0.2-a` or `ref:39cb398vb39` - tag/commit/branch to download from github and compile\n- `path:~/src/elixir` - a path to custom compiled version of a tool to use. For use by language developers and such.\n- `system` - this keyword causes asdf to passthrough to the version of the tool on the system that is not managed by asdf.\n\n::: tip\n\nMultiple versions can be set by separating them with a space. For example, to use Python `3.7.2`, fallback to Python `2.7.15` and finally to the `system` Python, the following line can be added to `.tool-versions`.\n\n```\npython 3.7.2 2.7.15 system\n```\n\n:::\n\nTo install all the tools defined in a `.tool-versions` file run `asdf install` with no other arguments in the directory containing the `.tool-versions` file.\n\nTo install a single tool defined in a `.tool-versions` file run `asdf install <name>` in the directory containing the `.tool-versions` file. The tool will be installed at the version specified in the `.tool-versions` file.\n\nEdit the file directly or use `asdf set` which updates it.\n\n## `.asdfrc`\n\nThe `.asdfrc` file defines the user's machine specific configuration.\n\n`${HOME}/.asdfrc` is the default location used by asdf. This can be set with the [Environment Variable `ASDF_CONFIG_FILE`](#asdf-config-file).\n\nThe below file shows the required format with the default values:\n\n```txt\nlegacy_version_file = no\nuse_release_candidates = no\nalways_keep_download = no\nplugin_repository_last_check_duration = 60\ndisable_plugin_short_name_repository = no\nconcurrency = auto\n```\n\n### `legacy_version_file`\n\nPlugins **with support** can read the versions files used by other version managers, for example, `.ruby-version` in the case of Ruby's `rbenv`.\n\n| Options                                                    | Description                                                                |\n| :--------------------------------------------------------- | :------------------------------------------------------------------------- |\n| `no` <Badge type=\"tip\" text=\"default\" vertical=\"middle\" /> | Use `.tool-versions` to read versions                                      |\n| `yes`                                                      | Use plugin fallback to legacy version files (`.ruby-version`) if available |\n\n### `always_keep_download`\n\nConfigure the `asdf install` command to keep or delete the source code or binary it downloads.\n\n| Options                                                    | Description                                           |\n| :--------------------------------------------------------- | :---------------------------------------------------- |\n| `no` <Badge type=\"tip\" text=\"default\" vertical=\"middle\" /> | Delete source code or binary after successful install |\n| `yes`                                                      | Keep source code or binary after install              |\n\n### `plugin_repository_last_check_duration`\n\nConfigure the duration (in minutes) between asdf plugin repository syncs. Trigger events result in a check of the duration. If more time has elapsed since the last sync than specified in the duration, a new sync occurs.\n\n| Options                                                                                                 | Description                                                  |\n| :------------------------------------------------------------------------------------------------------ | :----------------------------------------------------------- |\n| integer in range `1` to `999999999` <br/> `60` is <Badge type=\"tip\" text=\"default\" vertical=\"middle\" /> | Sync on trigger event if duration (in minutes) since last sync has been exceeded |\n| `0`                                                                                                     | Sync on each trigger event                                   |\n| `never`                                                                                                 | Never sync                                                   |\n\nSync events occur when the following commands are executed:\n\n- `asdf plugin add <name>`\n- `asdf plugin list all`\n\n`asdf plugin add <name> <git-url>` does NOT trigger a plugin sync.\n\n::: warning Note\n\nSetting the value to `never` does not stop the plugin repository from being initially synced, for that behaviour see `disable_plugin_short_name_repository`.\n\n:::\n\n### `disable_plugin_short_name_repository`\n\nDisable synchronization of the asdf plugin short-name repository. Sync events will exit early if the short-name repository is disabled.\n\n| Options                                                    | Description                                               |\n| :--------------------------------------------------------- | :-------------------------------------------------------- |\n| `no` <Badge type=\"tip\" text=\"default\" vertical=\"middle\" /> | Clone or update the asdf plugin repository on sync events |\n| `yes`                                                      | Disable the plugin short-name repository                  |\n\nSync events occur when the following commands are executed:\n\n- `asdf plugin add <name>`\n- `asdf plugin list all`\n\n`asdf plugin add <name> <git-url>` does NOT trigger a plugin sync.\n\n::: warning Note\n\nDisabling the plugin short-name repository does not remove the repository if it has already synced. Remove the plugin repo with `rm --recursive --trash $ASDF_DATA_DIR/repository`.\n\nDisabling the plugin short-name repository does not remove plugins previously installed from this source. Plugins can be removed with `asdf plugin remove <name>`. Removing a plugin will remove all installed versions of the managed tool.\n\n:::\n\n### `concurrency`\n\nThe default number of cores to use during compilation.\n\n| Options | Description                                                                                          |\n| :------ | :--------------------------------------------------------------------------------------------------- |\n| integer | Number of cores to use when compiling the source code                                                |\n| `auto`  | Calculate the number of cores using `nproc`, then `sysctl hw.ncpu`, then `/proc/cpuinfo` or else `1` |\n\nNote: the environment variable `ASDF_CONCURRENCY` take precedence if set.\n\n### Plugin Hooks\n\nIt is possible to execute custom code:\n\n- Before or after a plugin is installed, reshimed, updated, or uninstalled\n- Before or after a plugin command is executed\n\nFor example, if a plugin called `foo` is installed and provides a `bar` executable, then the following hooks can be used to execute custom code first:\n\n```text\npre_foo_bar = echo Executing with args: $@\n```\n\nThe following patterns are supported:\n\n- `pre_<plugin_name>_<command>`\n- `pre_asdf_download_<plugin_name>`\n- `{pre,post}_asdf_{install,reshim,uninstall}_<plugin_name>`\n  - `$1`: full version\n- `{pre,post}_asdf_plugin_{add,update,remove,reshim}`\n  - `$1`: plugin name\n- `{pre,post}_asdf_plugin_{add,update,remove}_<plugin_name>`\n\nSee [Create a Plugin](../plugins/create.md) for specifics on what command hooks are ran before or after what commands.\n\n## Environment Variables\n\nSetting environment variables varies depending on your system and Shell. Default locations depend upon your installation location and method (Git clone, Homebrew, AUR).\n\nEnvironment variables should generally be set before sourcing `asdf.sh`/`asdf.fish` etc. For Elvish set above `use asdf`.\n\nThe following describe usage with a Bash Shell.\n\n### `ASDF_CONFIG_FILE`\n\nPath to the `.asdfrc` configuration file. Can be set to any location. Must be an absolute path.\n\n- If Unset: `$HOME/.asdfrc` will be used.\n- Usage: `export ASDF_CONFIG_FILE=/home/john_doe/.config/asdf/.asdfrc`\n\n### `ASDF_TOOL_VERSIONS_FILENAME`\n\nThe filename of the file storing the tool names and versions. Can be any valid filename. Typically, you should not set this value unless you want to ignore `.tool-versions` files.\n\n- If Unset: `.tool-versions` will be used.\n- Usage: `export ASDF_TOOL_VERSIONS_FILENAME=tool_versions`\n\n### `ASDF_DIR`\n\nThe location of `asdf` core scripts. Can be set to any location. Must be an absolute path.\n\n- If Unset: the parent directory of the `bin/asdf` executable is used.\n- Usage: `export ASDF_DIR=/home/john_doe/.config/asdf`\n\n### `ASDF_DATA_DIR`\n\nThe location where `asdf` will install plugins, shims and tool versions. Can be set to any location. Must be an absolute path.\n\n- If Unset: `$HOME/.asdf` if it exists, or else the value of `ASDF_DIR`\n- Usage: `export ASDF_DATA_DIR=/home/john_doe/.asdf`\n\n### `ASDF_CONCURRENCY`\n\nNumber of cores to use when compiling the source code. If set, this value takes precedence over the asdf config `concurrency` value.\n\n- If Unset: the asdf config `concurrency` value is used.\n- Usage: `export ASDF_CONCURRENCY=32`\n\n## Full Configuration Example\n\nFollowing a simple asdf setup with:\n\n- a Bash Shell\n- an installation location of `$HOME/.asdf`\n- installed via Git\n- NO environment variables set\n- NO custom `.asdfrc` file\n\nwould result in the following outcomes:\n\n| Configuration                         | Value            | Calculated by                                                                                                                                      |\n| :------------------------------------ | :--------------- | :------------------------------------------------------------------------------------------------------------------------------------------------- |\n| config file location                  | `$HOME/.asdfrc`  | `ASDF_CONFIG_FILE` is empty, so use `$HOME/.asdfrc`                                                                                                |\n| default tool versions filename        | `.tool-versions` | `ASDF_TOOL_VERSIONS_FILENAME` is empty, so use `.tool-versions`                                                                            |\n| asdf dir                              | `$HOME/.asdf`    | `ASDF_DIR` is empty, so use parent dir of `bin/asdf`                                                                                               |\n| asdf data dir                         | `$HOME/.asdf`    | `ASDF_DATA_DIR` is empty so use `$HOME/.asdf` as `$HOME` exists.                                                                                   |\n| concurrency                           | `auto`           | `ASDF_CONCURRENCY` is empty, so rely on `concurrency` value from the [default configuration](https://github.com/asdf-vm/asdf/blob/master/defaults) |\n| legacy_version_file                   | `no`             | No custom `.asdfrc`, so use the [default configuration](https://github.com/asdf-vm/asdf/blob/master/defaults)                                      |\n| use_release_candidates                | `no`             | No custom `.asdfrc`, so use the [default configuration](https://github.com/asdf-vm/asdf/blob/master/defaults)                                      |\n| always_keep_download                  | `no`             | No custom `.asdfrc`, so use the [default configuration](https://github.com/asdf-vm/asdf/blob/master/defaults)                                      |\n| plugin_repository_last_check_duration | `60`             | No custom `.asdfrc`, so use the [default configuration](https://github.com/asdf-vm/asdf/blob/master/defaults)                                      |\n| disable_plugin_short_name_repository  | `no`             | No custom `.asdfrc`, so use the [default configuration](https://github.com/asdf-vm/asdf/blob/master/defaults)                                      |\n"
  },
  {
    "path": "docs/manage/core.md",
    "content": "# Core\n\nThe core `asdf` command list is rather small, but can facilitate many workflows.\n\n## Installation & Setup\n\nCovered in the [Getting Started](/guide/getting-started.md) guide.\n\n## Exec\n\n```shell\nasdf exec <command> [args...]\n```\n\nExecutes the command shim for the current version.\n\n<!-- TODO: expand on this with example -->\n\n## Env\n\n```shell\nasdf env <command> [util]\n```\n\n<!-- TODO: expand on this with example -->\n\n## Info\n\n```shell\nasdf info\n```\n\nA helper command to print the OS, Shell and `asdf` debug information. Share this when making a bug report.\n\n## Reshim\n\n```shell\nasdf reshim <name> <version>\n```\n\nThis recreates the shims for the current version of a package. By default, shims are created by plugins during installation of a tool. Some tools like the [npm CLI](https://docs.npmjs.com/cli/) allow global installation of executables, for example, installing [Yarn](https://yarnpkg.com/) via `npm install -g yarn`. Since this executable was not installed via the plugin lifecycle, no shim exists for it yet. `asdf reshim nodejs <version>` will force recalculation of shims for any new executables, like `yarn`, for `<version>` of `nodejs` .\n\n## Shim-versions\n\n```shell\nasdf shimversions <command>\n```\n\nLists the plugins and versions that provide shims for a command.\n\nAs an example, [Node.js](https://nodejs.org/) ships with two executables, `node` and `npm`. When many versions of the tools are installed with [`asdf-nodejs`](https://github.com/asdf-vm/asdf-nodejs/) `shimversions` can return:\n\n```shell\n➜ asdf shimversions node\nnodejs 14.8.0\nnodejs 14.17.3\nnodejs 16.5.0\n```\n\n```shell\n➜ asdf shimversions npm\nnodejs 14.8.0\nnodejs 14.17.3\nnodejs 16.5.0\n```\n\n## Update\n\nPlease use the same method you used to install asdf to update it. The latest\nversion of asdf is shown in the top right corner of this page.\n\n## Uninstall\n\nTo uninstall `asdf` follow these steps:\n\n::: details Bash & Git\n\n1. In your `~/.bashrc` remove the lines that source `asdf.sh` and the completions:\n\n```shell\n. \"$HOME/.asdf/asdf.sh\"\n. \"$HOME/.asdf/completions/asdf.bash\"\n```\n\n2. Remove the `$HOME/.asdf` dir:\n\n```shell\nrm -rf \"${ASDF_DATA_DIR:-$HOME/.asdf}\"\n```\n\n3. Run this command to remove all `asdf` config files:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Bash & Git (macOS)\n\n1. In your `~/.bash_profile` remove the lines that source `asdf.sh` and the completions:\n\n```shell\n. \"$HOME/.asdf/asdf.sh\"\n. \"$HOME/.asdf/completions/asdf.bash\"\n```\n\n2. Remove the `$HOME/.asdf` dir:\n\n```shell\nrm -rf \"${ASDF_DATA_DIR:-$HOME/.asdf}\"\n```\n\n3. Run this command to remove all `asdf` config files:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Bash & Homebrew\n\n1. In your `~/.bashrc` remove the lines that source `asdf.sh` and the completions:\n\n```shell\n. $(brew --prefix asdf)/libexec/asdf.sh\n. $(brew --prefix asdf)/etc/bash_completion.d/asdf.bash\n```\n\nCompletions may have been [configured as per Homebrew's instructions](https://docs.brew.sh/Shell-Completion#configuring-completions-in-bash) so follow their guide to find out what to remove.\n\n2. Uninstall with your package manager:\n\n```shell\nbrew uninstall asdf --force\n```\n\n3. Run this command to remove all `asdf` config files:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Bash & Homebrew (macOS)\n\nIf using **macOS Catalina or newer**, the default shell has changed to **ZSH**. If you can't find any config in your `~/.bash_profile` it may be in a `~/.zshrc` in which case please follow the ZSH instructions.\n\n1. In your `~/.bash_profile` remove the lines that source `asdf.sh` and the completions:\n\n```shell\n. $(brew --prefix asdf)/libexec/asdf.sh\n. $(brew --prefix asdf)/etc/bash_completion.d/asdf.bash\n```\n\nCompletions may have been [configured as per Homebrew's instructions](https://docs.brew.sh/Shell-Completion#configuring-completions-in-bash) so follow their guide to find out what to remove.\n\n2. Uninstall with your package manager:\n\n```shell\nbrew uninstall asdf --force\n```\n\n3. Run this command to remove all `asdf` config files:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Bash & Pacman\n\n1. In your `~/.bashrc` remove the lines that source `asdf.sh` and the completions:\n\n```shell\n. /opt/asdf-vm/asdf.sh\n```\n\n2. Uninstall with your package manager:\n\n```shell\npacman -Rs asdf-vm\n```\n\n3. Remove the `$HOME/.asdf` dir:\n\n```shell\nrm -rf \"${ASDF_DATA_DIR:-$HOME/.asdf}\"\n```\n\n4. Run this command to remove all `asdf` config files:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Fish & Git\n\n1. In your `~/.config/fish/config.fish` remove the lines that source `asdf.fish`:\n\n```shell\nsource ~/.asdf/asdf.fish\n```\n\nand remove completions with this command:\n\n```shell\nrm -rf ~/.config/fish/completions/asdf.fish\n```\n\n2. Remove the `$HOME/.asdf` dir:\n\n```shell\nrm -rf (string join : -- $ASDF_DATA_DIR $HOME/.asdf)\n```\n\n3. Run this command to remove all `asdf` config files:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Fish & Homebrew\n\n1. In your `~/.config/fish/config.fish` remove the lines that source `asdf.fish`:\n\n```shell\nsource \"(brew --prefix asdf)\"/libexec/asdf.fish\n```\n\n2. Uninstall with your package manager:\n\n```shell\nbrew uninstall asdf --force\n```\n\n3. Run this command to remove all `asdf` config files:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Fish & Pacman\n\n1. In your `~/.config/fish/config.fish` remove the lines that source `asdf.fish`:\n\n```shell\nsource /opt/asdf-vm/asdf.fish\n```\n\n2. Uninstall with your package manager:\n\n```shell\npacman -Rs asdf-vm\n```\n\n3. Remove the `$HOME/.asdf` dir:\n\n```shell\nrm -rf (string join : -- $ASDF_DATA_DIR $HOME/.asdf)\n```\n\n4. Run this command to remove all `asdf` config files:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Elvish & Git\n\n1. In your `~/.config/elvish/rc.elv` remove the lines that use the `asdf` module:\n\n```shell\nuse asdf _asdf; var asdf~ = $_asdf:asdf~\nset edit:completion:arg-completer[asdf] = $_asdf:arg-completer~\n```\n\nand uninstall the `asdf` module with this command:\n\n```shell\nrm -f ~/.config/elvish/lib/asdf.elv\n```\n\n2. Remove the `$HOME/.asdf` dir:\n\n```shell\nif (!=s $E:ASDF_DATA_DIR \"\") { rm -rf $E:ASDF_DATA_DIR } else { rm -rf ~/.asdf }\n```\n\n3. Run this command to remove all `asdf` config files:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Elvish & Homebrew\n\n1. In your `~/.config/elvish/rc.elv` remove the lines that use the `asdf` module:\n\n```shell\nuse asdf _asdf; var asdf~ = $_asdf:asdf~\nset edit:completion:arg-completer[asdf] = $_asdf:arg-completer~\n```\n\nand uninstall the `asdf` module with this command:\n\n```shell\nrm -f ~/.config/elvish/lib/asdf.elv\n```\n\n2. Uninstall with your package manager:\n\n```shell\nbrew uninstall asdf --force\n```\n\n3. Run this command to remove all `asdf` config files:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Elvish & Pacman\n\n1. In your `~/.config/elvish/rc.elv` remove the lines that use the `asdf` module:\n\n```shell\nuse asdf _asdf; var asdf~ = $_asdf:asdf~\nset edit:completion:arg-completer[asdf] = $_asdf:arg-completer~\n```\n\nand uninstall the `asdf` module with this command:\n\n```shell\nrm -f ~/.config/elvish/lib/asdf.elv\n```\n\n2. Uninstall with your package manager:\n\n```shell\npacman -Rs asdf-vm\n```\n\n3. Remove the `$HOME/.asdf` dir:\n\n```shell\nif (!=s $E:ASDF_DATA_DIR \"\") { rm -rf $E:ASDF_DATA_DIR } else { rm -rf ~/.asdf }\n```\n\n4. Run this command to remove all `asdf` config files:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details ZSH & Git\n\n1. In your `~/.zshrc` remove the lines that source `asdf.sh` and completions:\n\n```shell\n. \"$HOME/.asdf/asdf.sh\"\n# ...\nfpath=(${ASDF_DIR}/completions $fpath)\nautoload -Uz compinit\ncompinit\n```\n\n**OR** the ZSH Framework plugin if used.\n\n2. Remove the `$HOME/.asdf` dir:\n\n```shell\nrm -rf \"${ASDF_DATA_DIR:-$HOME/.asdf}\"\n```\n\n3. Run this command to remove all `asdf` config files:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details ZSH & Homebrew\n\n1. In your `~/.zshrc` remove the lines that source `asdf.sh`:\n\n```shell\n. $(brew --prefix asdf)/libexec/asdf.sh\n```\n\n2. Uninstall with your package manager:\n\n```shell\nbrew uninstall asdf --force && brew autoremove\n```\n\n3. Run this command to remove all `asdf` config files:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details ZSH & Pacman\n\n1. In your `~/.zshrc` remove the lines that source `asdf.sh`:\n\n```shell\n. /opt/asdf-vm/asdf.sh\n```\n\n2. Uninstall with your package manager:\n\n```shell\npacman -Rs asdf-vm\n```\n\n3. Remove the `$HOME/.asdf` dir:\n\n```shell\nrm -rf \"${ASDF_DATA_DIR:-$HOME/.asdf}\"\n```\n\n4. Run this command to remove all `asdf` config files:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\nThat's it! 🎉\n"
  },
  {
    "path": "docs/manage/dependencies.md",
    "content": "# Dependencies\n\nThis list is for asdf version 0.16.0 and greater. Older versions of asdf have\nadditional dependencies.\n\nasdf itself requires the following to be installed:\n\n* Bash version `3.2.48`\n* Git version `1.7.7.2`\n\n::: tip Note\n\nNote that asdf plugins may require additional programs to be installed before\nthey can be used. Read the plugin's documentation and verify you have all of\nthe plugin's dependencies installed before installing it.\n\n:::\n\n## Install\n\nIf you need to manually install dependencies for asdf run the command for your\nOS below.\n\n<!--@include: @/parts/install-dependencies-cmds.md-->\n\n::: tip Note\n\n`sudo` may be required depending on your system configuration.\n\n:::\n"
  },
  {
    "path": "docs/manage/plugins.md",
    "content": "# Plugins\n\nPlugins are how `asdf` knows to handle different tools like Node.js, Ruby, Elixir etc.\n\nSee [Creating Plugins](/plugins/create.md) for the plugin API used to support more tools.\n\n## Add\n\nAdd plugins via their Git URL:\n\n```shell\nasdf plugin add <name> <git-url>\n# asdf plugin add elm https://github.com/vic/asdf-elm\n```\n\nor via the short-name association in the plugins repository:\n\n```shell\nasdf plugin add <name>\n# asdf plugin add erlang\n```\n\n::: tip Recommendation\n\nPrefer the longer `git-url` method as it is independent of the short-name repo.\n\n:::\n\n## List Installed\n\n```shell\nasdf plugin list\n# asdf plugin list\n# java\n# nodejs\n```\n\n```shell\nasdf plugin list --urls\n# asdf plugin list\n# java            https://github.com/halcyon/asdf-java.git\n# nodejs          https://github.com/asdf-vm/asdf-nodejs.git\n```\n\n## List All in Short-name Repository\n\n```shell\nasdf plugin list all\n```\n\nSee [Plugins Shortname Index](https://github.com/asdf-vm/asdf-plugins) for the entire short-name list of plugins.\n\n## Update\n\n```shell\nasdf plugin update --all\n```\n\nIf you want to update a specific package, just say so.\n\n```shell\nasdf plugin update <name>\n# asdf plugin update erlang\n```\n\nThis update will fetch the _latest commit_ on the _default branch_ of the _origin_ of the plugin repository. Versioned plugins and updates are currently being developed ([#916](https://github.com/asdf-vm/asdf/pull/916))\n\n## Remove\n\n```bash\nasdf plugin remove <name>\n# asdf plugin remove erlang\n```\n\nRemoving a plugin will remove all installations of the tool made with the plugin. This can be used as a shorthand for cleaning/pruning many unused versions of a tool.\n\n## Syncing the asdf Short-name Repository\n\nThe short-name repo is synced to your local machine and periodically refreshed. This method to determine a sync is as follows:\n\n- sync events are triggered by commands:\n  - `asdf plugin add <name>`\n  - `asdf plugin list all`\n- if configuration option `disable_plugin_short_name_repository` is set to `yes`, then sync is aborted early. See the [asdf config docs](/manage/configuration.md) for more.\n- if there has not been a synchronization in the last `X` minutes then the sync will occur.\n  - `X` defaults to `60`, but can be configured in your `.asdfrc` via the `plugin_repository_last_check_duration` option. See the [asdf config docs](/manage/configuration.md) for more.\n"
  },
  {
    "path": "docs/manage/versions.md",
    "content": "# Versions\n\n## Install Version\n\n```shell\nasdf install <name> <version>\n# asdf install erlang 17.3\n```\n\nIf a plugin supports downloading & compiling from source, you can specify `ref:foo` where `foo` is a specific branch, tag, or commit. You'll need to use the same name and reference when uninstalling too.\n\n## Install Latest Stable Version\n\n```shell\nasdf install <name> latest\n# asdf install erlang latest\n```\n\nInstall latest stable version that begins with a given string.\n\n```shell\nasdf install <name> latest:<version>\n# asdf install erlang latest:17\n```\n\n## List Installed Versions\n\n```shell\nasdf list <name>\n# asdf list erlang\n```\n\nFilter versions to those that begin with a given string.\n\n```shell\nasdf list <name> <version>\n# asdf list erlang 17\n```\n\n## List All Available Versions\n\n```shell\nasdf list all <name>\n# asdf list all erlang\n```\n\nFilter versions to those that begin with a given string.\n\n```shell\nasdf list all <name> <version>\n# asdf list all erlang 17\n```\n\n## Show Latest Stable Version\n\n```shell\nasdf latest <name>\n# asdf latest erlang\n```\n\nShow latest stable version that begins with a given string.\n\n```shell\nasdf latest <name> <version>\n# asdf latest erlang 17\n```\n\n## Set Version\n\n#### Via `.tool-versions` file\n\n```shell\nasdf set [flags] <name> <version> [<version>...]\n# asdf set elixir 1.2.4 # set in current dir\n# asdf set -u elixir 1.2.4 # set in .tool-versions file in home directory\n# asdf set -p elixir 1.2.4 # set in existing .tool-versions file in a parent dir\n\nasdf set <name> latest[:<version>]\n# asdf set elixir latest\n```\n\n`asdf set` writes the version to a `.tool-versions` file in the current directory,\ncreating it if needed. It exists purely for convenience. You can think of it as\njust doing `echo \"<tool> <version>\" > .tool-versions`.\n\nWith the `-u`/`--home` flag `asdf set` writes to the `.tool-versions` file in\nyour `$HOME` directory, creating the file if it does not exist.\n\nWith the `-p`/`--parent` flag `asdf set` finds a `.tool-versions` file in the\nclosest parent directory of the current directory.\n\n#### Via Environment Variable\n\nWhen determining the version looks for an environment variable with the pattern\n`ASDF_${TOOL}_VERSION`. The version format is the same supported by the\n`.tool-versions` file. If set, the value of this environment variable overrides\nany versions set in for the tool in any `.tool-versions` file. For example:\n\n```shell\nexport ASDF_ELIXIR_VERSION=1.18.1\n```\n\nWill tell asdf to use Elixir `1.18.1` in the current shell session.\n\n:::warning\nBecause this is an environment variable, it only takes effect where it is set.\nAny other shell sessions that are running will still use to whatever version is\nset in a `.tool-versions` file.\n\nSee the `.tool-versions` [file in the Configuration section](/manage/configuration.md) for details.\n\n:::\n\nThe following example runs tests on an Elixir project with version `1.4.0`.\n\n```shell\nASDF_ELIXIR_VERSION=1.4.0 mix test\n```\n\n## Fallback to System Version\n\nTo use the system version of tool `<name>` instead of an asdf managed version you can set the version for the tool to `system`.\n\nSet system with either `asdf set` or via environment variable as outlined in [Set Version](#set-version) section above.\n\n```shell\nasdf set <name> system\n# asdf set python system\n```\n\n## View Current Version\n\n```shell\nasdf current\n# asdf current\n# erlang          17.3          /Users/kim/.tool-versions\n# nodejs          6.11.5        /Users/kim/cool-node-project/.tool-versions\n\nasdf current <name>\n# asdf current erlang\n# erlang          17.3          /Users/kim/.tool-versions\n```\n\n## Uninstall Version\n\n```shell\nasdf uninstall <name> <version>\n# asdf uninstall erlang 17.3\n```\n\n## Shims\n\nWhen asdf installs a package it creates shims for every executable program in that package in a `$ASDF_DATA_DIR/shims` directory (default `~/.asdf/shims`). This directory being on the `$PATH` (by means of `asdf.sh`, `asdf.fish`, etc) is how the installed programs are made available in the environment.\n\nThe shims themselves are really simple wrappers that `exec` a helper program `asdf exec` passing it the name of the plugin and path to the executable in the installed package that the shim is wrapping.\n\nThe `asdf exec` helper determines the version of the package to use (as specified in `.tool-versions` file or environment variable), the final path to the executable in the package installation directory (this can be manipulated by the `exec-path` callback in the plugin) and the environment to execute in (also provided by the plugin - `exec-env` script), and finally it executes it.\n\n::: warning Note\nBecause this system uses `exec` calls, any scripts in the package that are meant to be sourced by the shell instead of executed need to be accessed directly instead of via the shim wrapper. The two `asdf` commands: `which` and `where` can help with this by returning the path to the installed package:\n:::\n\n```shell\n# returns path to main executable in current version\nsource $(asdf which ${PLUGIN})/../script.sh\n\n# returns path to the package installation directory\nsource $(asdf where ${PLUGIN})/bin/script.sh\n```\n\n### By-passing asdf shims\n\nIf for some reason you want to by-pass asdf shims or want your environment variables automatically set upon entering your project's directory, the [asdf-direnv](https://github.com/asdf-community/asdf-direnv) plugin can be helpful. Be sure to check its README for more details.\n"
  },
  {
    "path": "docs/more/community-projects.md",
    "content": "# Community Projects\n\nHere are some community projects related to `asdf`:\n\n- [asdf-community](https://github.com/asdf-community): A collaborative,\n  community-driven project for long-term maintenance of asdf plugins.\n- [asdf dev container](https://github.com/iloveitaly/asdf-devcontainer): A\n  [GitHub Dev Container](https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/introduction-to-dev-containers)\n  supporting asdf managed tools in GitHub Codespaces.\n\n::: warning Note\n\nasdf core team do not own these projects or their code. asdf core are not responsible\nfor the quality or security as they relate to those listed here.\n\n:::\n"
  },
  {
    "path": "docs/more/faq.md",
    "content": "# FAQ\n\nHere are some common questions regarding `asdf`.\n\n## WSL1 support?\n\nWSL1 ([Windows Subsystem for Linux](https://en.wikipedia.org/wiki/Windows_Subsystem_for_Linux) 1) is not officially supported. Some aspects of `asdf` may not work properly. We do not intend to add official support for WSL1.\n\n## WSL2 support?\n\nWSL2 ([Windows Subsystem for Linux](https://en.wikipedia.org/wiki/Windows_Subsystem_for_Linux#WSL_2) 2) should work using the setup & dependency instructions for you chosen WSL distro.\n\nImportantly, WSL2 is _only_ expected to work properly when the current working directory is a Unix drive and not a bound Windows drive.\n\nWe intend to run out test suite on WSL2 when host runner support is available on GitHub Actions, currently this does not appear to be the case.\n\n## Newly installed executable not running?\n\n> I just `npm install -g yarn`, but cannot execute `yarn`. What gives?\n\n`asdf` uses [shims](<https://en.wikipedia.org/wiki/Shim_(computing)>) to manage executables. Those installed by plugins have shims automatically created, whereas installing executables via an `asdf` managed tool will require you to notify `asdf` of the need to create shims. In this instance, to create a shim for [Yarn](https://yarnpkg.com/). See the [`asdf reshim` command docs](/manage/core.md#reshim).\n\n## Shell not detecting newly installed shims?\n\nIf `asdf reshim` is not resolving your issue, then it is most-likely due to the sourcing of `asdf.sh` or `asdf.fish` _not_ being at the **BOTTOM** of your Shell config file (`.bash_profile`, `.zshrc`, `config.fish` etc). It needs to be sourced **AFTER** you have set your `$PATH` and **AFTER** you have sourced your framework (oh-my-zsh etc) if any.\n\n## Why can't I use a version of `latest` in the `.tool-versions` file?\n\nasdf must always have an exact version of every tool in the current directory, not version ranges or special values like `latest` are not permitted. This ensure that asdf behaves in a deterministic and consistent way across time and across different machines. A special version like `latest` would change over time, and could vary between machines if `asdf install` was run at different times. As such it's allowed in asdf commands like `asdf set <tool> latest`, but forbidden in the `.tool-versions` file.\n\nThink of `.tool-versions` file as `Gemfile.lock` or `package-lock.json`. It is a file that contains the exact version of every tool your project depends on.\n\nNote that the `system` version is allowed in `.tool-versions` files, and it could resolve to different versions when used. It is a special value that  effectively disables asdf for a particular tool in the given directory.\n\nSee issue https://github.com/asdf-vm/asdf/issues/1012\n\n## Why can't version ranges be used in the `.tool-versions` files?\n\nSimilar to the question above on the use of `latest`. With a version range specified, asdf would be free to choose any installed version in the specified range. This could result in different behavior across machines if they have different versions installed. The intent is for asdf to be fully deterministic so the same `.tool-versions` file produces the exact same environment across time and across different computers.\n\nSee issue https://github.com/asdf-vm/asdf-nodejs/issues/235#issuecomment-885809776\n\n## Why is a command completely unrelated to the plugins I'm using is getting shimmed by asdf?\n\n**asdf is only going to generate shims for executables it manages**. If for example you use the Ruby plugin then you'll expect to see commands like `ruby` and `irb` replaced with shims, along with other executables present in Ruby packages you installed.\n\nIf you see a shim that you don't expect, it's likely because you installed a package under a tool that was managed by asdf, and the package provided the executable.\n\nThis is surprising when the executable has the same name as an executable already on your system. [Some users reported](https://github.com/asdf-vm/asdf/issues/584) a Node.JS package that provided it's own version of the `which` command. This resulted in asdf creating a shim for it, and it replacing the version of the `which` command present on the operating system. In such instances it's best to locate the package introducing the executable and remove it. `asdf which <command>` is helping in showing you where the offending executable is located, making it possible to determine what package added it.\n\nSee issues https://github.com/asdf-vm/asdf/issues/584 https://github.com/asdf-vm/asdf/issues/1653\n"
  },
  {
    "path": "docs/more/thanks.md",
    "content": "# Thanks\n\nThank you page to the asdf authors & contributors!\n\n## Credits\n\nMe ([@HashNuke](https://github.com/HashNuke)), High-fever, cold, cough.\n\nCopyright 2014 to the end of time ([MIT License](https://github.com/asdf-vm/asdf/blob/master/LICENSE))\n\n## Maintainers\n\n- [@HashNuke](https://github.com/HashNuke)\n- [@danhper](https://github.com/danhper)\n- [@Stratus3D](https://github.com/Stratus3D)\n- [@vic](https://github.com/vic)\n- [@jthegedus](https://github.com/jthegedus)\n\n## Contributors\n\nSee the [list of contributors](https://github.com/asdf-vm/asdf/graphs/contributors) :pray: on GitHub\n"
  },
  {
    "path": "docs/package.json",
    "content": "{\n  \"type\": \"module\",\n  \"scripts\": {\n    \"fmt\": \"prettier --write '.vitepress/{config,navbars,sidebars}.ts' '.vitepress/theme/**/*'\",\n    \"dev\": \"vitepress dev\",\n    \"build\": \"vitepress build\",\n    \"preview\": \"vitepress preview\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^25.3.3\",\n    \"prettier\": \"^3.8.1\",\n    \"vitepress\": \"^1.6.4\"\n  }\n}"
  },
  {
    "path": "docs/parts/install-dependencies-cmds.md",
    "content": "| OS    | Package Manager | Command                            |\n| ----- | --------------- | ---------------------------------- |\n| linux | Aptitude        | `apt install git bash`             |\n| linux | DNF             | `dnf install git bash`             |\n| linux | Pacman          | `pacman -S git bash`               |\n| linux | Zypper          | `zypper install git bash`          |\n| macOS | Homebrew        | `brew install coreutils git bash`  |\n| macOS | Spack           | `spack install coreutils git bash` |\n"
  },
  {
    "path": "docs/parts/install-dependencies.md",
    "content": "##### Install Dependencies\n\nasdf primarily requires `git`. Here is a _non-exhaustive_ list of commands to run for _your_ package manager (some might automatically install these tools in later steps).\n\n<!--@include: @/parts/install-dependencies-cmds.md-->\n\n::: tip Note\n\n`sudo` may be required depending on your system configuration.\n\n:::\n\nPlease see the [Dependencies page](/manage/dependencies) for more information.\n"
  },
  {
    "path": "docs/plugins/create.md",
    "content": "# Create a Plugin\n\nA plugin is a Git repo with some executable scripts to support versioning a\nlanguage / tool. These scripts are run by asdf using specific commands to\nsupport features such as `asdf list-all <name>`, `asdf install <name> <version>`\netc.\n\n## Quickstart\n\nThere are two options to get started with creating your own plugin:\n\n1. use the\n   [asdf-vm/asdf-plugin-template](https://github.com/asdf-vm/asdf-plugin-template)\n   repository to\n   [generate](https://github.com/asdf-vm/asdf-plugin-template/generate) a plugin\n   repo (named `asdf-<tool_name>`) with default scripts implemented. Once\n   generated, clone the repo and run the `setup.bash` script to interactively\n   update the template.\n2. start your own repo called `asdf-<tool_name>` and implement the required\n   scripts as listed in the documentation below.\n\n### Golden Rules for Plugin Scripts\n\n- scripts should **NOT** call other `asdf` commands\n- keep your dependency list of Shell tools/commands small\n- avoid non-portable tools or command flags. For example, `sort -V`. See our\n  asdf core\n  [list of banned commands](https://github.com/asdf-vm/asdf/blob/master/test/banned_commands.bats)\n\n## Scripts Overview\n\nThe full list of scripts callable from asdf.\n\n| Script                                                                                                | Description                                                      |\n| :---------------------------------------------------------------------------------------------------- |:-----------------------------------------------------------------|\n| [bin/list-all](#bin-list-all) <Badge type=\"tip\" text=\"required\" vertical=\"middle\" />                  | List all installable versions                                    |\n| [bin/download](#bin-download) <Badge type=\"warning\" text=\"recommended\" vertical=\"middle\" />           | Download source code or binary for the specified version         |\n| [bin/install](#bin-install) <Badge type=\"tip\" text=\"required\" vertical=\"middle\" />                    | Installs the specified version                                   |\n| [bin/latest-stable](#bin-latest-stable) <Badge type=\"warning\" text=\"recommended\" vertical=\"middle\" /> | List the latest stable version of the specified tool             |\n| [bin/help.overview](#bin-help.overview)                                                               | Output a general description about the plugin & tool             |\n| [bin/help.deps](#bin-help.deps)                                                                       | Output a list of dependencies per Operating System               |\n| [bin/help.config](#bin-help.config)                                                                   | Output plugin or tool configuration information                  |\n| [bin/help.links](#bin-help.links)                                                                     | Output a list of links for the plugin or tool                    |\n| [bin/list-bin-paths](#bin-list-bin-paths)                                                             | List relative paths to directories with binaries to create shims |\n| [bin/exec-env](#bin-exec-env)                                                                         | Prepare the environment for running the binaries                 |\n| [bin/exec-path](#bin-exec-path)                                                                       | Output the executable path for a version of a tool               |\n| [bin/uninstall](#bin-uninstall)                                                                       | Uninstall a specific version of a tool                           |\n| [bin/list-legacy-filenames](#bin-list-legacy-filenames)                                               | Output filenames of legacy version files: `.ruby-version`        |\n| [bin/parse-legacy-file](#bin-parse-legacy-file)                                                       | Custom parser for legacy version files                           |\n| [bin/post-plugin-add](#bin-post-plugin-add)                                                           | Hook to execute after a plugin has been added                    |\n| [bin/post-plugin-update](#bin-post-plugin-update)                                                     | Hook to execute after a plugin has been updated                  |\n| [bin/pre-plugin-remove](#bin-pre-plugin-remove)                                                       | Hook to execute before a plugin is removed                       |\n\nTo see which commands invoke which scripts, see the detailed documentation for\neach script.\n\n## Environment Variables Overview\n\nThe full list of Environment Variables used throughout all scripts.\n\n| Environment Variables    | Description                                                                             |\n| :----------------------- |:----------------------------------------------------------------------------------------|\n| `ASDF_INSTALL_TYPE`      | `version` or `ref`                                                                      |\n| `ASDF_INSTALL_VERSION`   | full version number or Git Ref depending on `ASDF_INSTALL_TYPE`                         |\n| `ASDF_INSTALL_PATH`      | the path to where the tool _should_, or _has been_ installed                            |\n| `ASDF_CONCURRENCY`       | the number of cores to use when compiling the source code. Useful for setting `make -j` |\n| `ASDF_DOWNLOAD_PATH`     | the path to where the source code or binary was downloaded to by `bin/download`         |\n| `ASDF_PLUGIN_PATH`       | the path the plugin was installed                                                       |\n| `ASDF_PLUGIN_SOURCE_URL` | the source URL of the plugin                                                            |\n| `ASDF_PLUGIN_PREV_REF`   | previous `git-ref` of the plugin repo                                                    |\n| `ASDF_PLUGIN_POST_REF`   | updated `git-ref` of the plugin repo                                                    |\n| `ASDF_CMD_FILE`          | resolves to the full path of the file being sourced                                     |\n\n::: tip NOTE\n\n**Not all environment variables are available in all scripts.** Check the\ndocumentation for each script below to see which env vars are available to it.\n\n:::\n\n## Required Scripts\n\n### `bin/list-all` <Badge type=\"tip\" text=\"required\" vertical=\"middle\" />\n\n**Description**\n\nList all installable versions.\n\n**Output Format**\n\nMust print a string with a **space-separated** list of versions. For example:\n\n```txt\n1.0.1 1.0.2 1.3.0 1.4\n```\n\nNewest version should be last.\n\nasdf core will print each version on its own line, potentially pushing some\nversions offscreen.\n\n**Sorting**\n\nIf versions are being pulled from releases page on a website it's recommended to\nleave the versions in the provided order as they are often already in the\ncorrect order. If they are in reverse order piping them through `tac` should\nsuffice.\n\nIf sorting is unavoidable, `sort -V` is not portable, so we suggest either:\n\n- [using the Git sort capability](https://github.com/asdf-vm/asdf-plugin-template/blob/main/template/lib/utils.bash)\n  (requires Git >= `v2.18.0`)\n- [writing a custom sort method](https://github.com/vic/asdf-idris/blob/master/bin/list-all#L6)\n  (requires `sed`, `sort` & `awk`)\n\n**Environment Variables available to script**\n\nNo environment variables are provided to this script.\n\n**Commands that invoke this script**\n\n- `asdf list all <name> [version]`\n- `asdf list all nodejs`: lists all versions as returned by this script, one on\n  each line.\n- `asdf list all nodejs 18`: lists all versions as returned by this script, one\n  on each line, with a filter matching any version beginning with `18` applied.\n\n**Call signature from asdf core**\n\nNo parameters provided.\n\n```bash\n\"${plugin_path}/bin/list-all\"\n```\n\n---\n\n### `bin/download` <Badge type=\"tip\" text=\"required\" vertical=\"middle\" />\n\n**Description**\n\nDownload the source code or binary for a specific version of a tool to a specified location.\n\n**Implementation Details**\n\n- The script must download the source or binary to the directory specified by `ASDF_DOWNLOAD_PATH`.\n- Only the decompressed source code or binary should be placed in the `ASDF_DOWNLOAD_PATH` directory.\n- On failure, no files should be placed in `ASDF_DOWNLOAD_PATH`.\n- Success should exit with `0`.\n- Failure should exit with a non-zero status.\n\n**Legacy Plugins**\n\nThough this script is marked as _required_ for all plugins, it is _optional_ for \"legacy\" plugins which predate its introduction.\n\nIf this script is absent, asdf will assume that the `bin/install` script is present and will download **and** install the version.\n\nAll plugins must include this script as support for legacy plugins will eventually be removed.\n\n**Environment Variables available to script**\n\n- `ASDF_INSTALL_TYPE`: `version` or `ref`\n- `ASDF_INSTALL_VERSION`:\n  - Full version number if `ASDF_INSTALL_TYPE=version`.\n  - Git ref (tag/commit/branch) if `ASDF_INSTALL_TYPE=ref`.\n- `ASDF_INSTALL_PATH`: The path to where the tool _has been_, or _should be_ installed.\n- `ASDF_DOWNLOAD_PATH`: The path to where the source code or binary was downloaded to.\n\n**Commands that invoke this script**\n\n- `asdf install <tool> [version]`\n- `asdf install <tool> latest[:version]`\n- `asdf install nodejs 18.0.0`: downloads the source code or binary for Node.js\n  version `18.0.0` and places it in the `ASDF_DOWNLOAD_PATH` directory. Then runs the `bin/install` script.\n\n**Call signature from asdf core**\n\nNo parameters provided.\n\n```bash\n\"${plugin_path}\"/bin/download\n```\n\n---\n\n### `bin/install` <Badge type=\"tip\" text=\"required\" vertical=\"middle\" />\n\n**Description**\n\nInstall a specific version of a tool to a specified location.\n\n**Implementation Details**\n\n- The script should install the specified version in the path `ASDF_INSTALL_PATH`.\n- Shims will be created by default for any files in `$ASDF_INSTALL_PATH/bin`. This behaviour can be customised with the optional\n[bin/list-bin-paths](#bin-list-bin-paths) script.\n- Success should exit with `0`.\n- Failure should exit with a non-zero status.\n- To avoid TOCTOU (Time-of-Check-to-Time-of-Use) issues, ensure the script only places files in `ASDF_INSTALL_PATH` once the build and installation of the tool is deemed a success.\n\n**Legacy Plugins**\n\nIf the `bin/download` script is absent, this script should download **and** install the specified version.\n\nFor compatibility with versions of the asdf core earlier than `0.7._` and newer than `0.8._`, check for the presence of the `ASDF_DOWNLOAD_PATH` environment\nvariable. If set, assume the `bin/download` script already downloaded the version, else download the source code in the `bin/install` script.\n\n**Environment Variables available to script**\n\n- `ASDF_INSTALL_TYPE`: `version` or `ref`\n- `ASDF_INSTALL_VERSION`:\n  - Full version number if `ASDF_INSTALL_TYPE=version`.\n  - Git ref (tag/commit/branch) if `ASDF_INSTALL_TYPE=ref`.\n- `ASDF_INSTALL_PATH`: The path to where the tool _has been_, or _should be_ installed.\n- `ASDF_CONCURRENCY`: The number of cores to use when compiling source code. Useful for setting flags like `make -j`.\n- `ASDF_DOWNLOAD_PATH`: The path where the source code or binary was downloaded to.\n\n**Commands that invoke this script**\n\n- `asdf install`\n- `asdf install <tool>`\n- `asdf install <tool> [version]`\n- `asdf install <tool> latest[:version]`\n- `asdf install nodejs 18.0.0`: installs Node.js version `18.0.0` in the\n  `ASDF_INSTALL_PATH` directory.\n\n**Call signature from asdf core**\n\nNo parameters provided.\n\n```bash\n\"${plugin_path}\"/bin/install\n```\n\n## Optional Scripts\n\n### `bin/latest-stable` <Badge type=\"warning\" text=\"recommended\" vertical=\"middle\" />\n\n**Description**\n\nDetermine the latest stable version of a tool. If absent, the asdf core will `tail` the `bin/list-all` output which may be undesirable.\n\n**Implementation Details**\n\n- The script should print the latest stable version of the tool to stdout.\n- Non-stable or release candidate versions should be omitted.\n- A filter query is provided as the first argument to the script. This should be used to filter the output by version number or tool provider.\n  - For instance, the output of `asdf list all ruby` from the [ruby plugin](https://github.com/asdf-vm/asdf-ruby) lists versions of Ruby from many providers: `jruby`, `rbx` & `truffleruby` amongst others. The user provided filter could be used by the plugin to filter the semver versions and/or provider.\n    ```\n    > asdf latest ruby\n    3.2.2\n    > asdf latest ruby 2\n    2.7.8\n    > asdf latest ruby truffleruby\n    truffleruby+graalvm-22.3.1\n    ```\n- Success should exit with `0`.\n- Failure should exit with a non-zero status.\n\n**Environment Variables available to script**\n\n- `ASDF_INSTALL_TYPE`: `version` or `ref`\n- `ASDF_INSTALL_VERSION`:\n  - Full version number if `ASDF_INSTALL_TYPE=version`.\n  - Git ref (tag/commit/branch) if `ASDF_INSTALL_TYPE=ref`.\n- `ASDF_INSTALL_PATH`: The path to where the tool _has been_, or _should be_ installed.\n\n**Commands that invoke this script**\n\n- `asdf set <tool> latest`: set the version of a tool to the latest stable version for that tool.\n- `asdf install <tool> latest`: installs the latest version of a tool.\n- `asdf latest <tool> [<version>]`: outputs the latest version of a tool based on the optional filter.\n- `asdf latest --all`: outputs the latest version of all tools managed by asdf and whether they are installed.\n\n**Call signature from asdf core**\n\nThe script should accept a single argument, the filter query.\n\n```bash\n\"${plugin_path}\"/bin/latest-stable \"$query\"\n```\n\n---\n\n### `bin/help.overview`\n\n**Description**\n\nOutput a general description about the plugin and the tool being managed.\n\n**Implementation Details**\n\n- This script is required for any help output to be displayed for the plugin.\n- No heading should be printed as asdf core will print headings.\n- Output may be free-form text but ideally only one short paragraph.\n- Must not output any information that is already covered in the core asdf-vm documentation.\n- Should be tailored to the Operating System and version of the tool being installed (using optionally set Environment Variables `ASDF_INSTALL_VERSION` and `ASDF_INSTALL_TYPE`).\n- Success should exit with `0`.\n- Failure should exit with a non-zero status.\n\n**Environment Variables available to script**\n\n- `ASDF_INSTALL_TYPE`: `version` or `ref`\n- `ASDF_INSTALL_VERSION`:\n  - Full version number if `ASDF_INSTALL_TYPE=version`.\n  - Git ref (tag/commit/branch) if `ASDF_INSTALL_TYPE=ref`.\n- `ASDF_INSTALL_PATH`: The path to where the tool _has been_, or _should be_ installed.\n\n**Commands that invoke this script**\n\n- `asdf help <name> [<version>]`: Output documentation for plugin and tool\n\n**Call signature from asdf core**\n\n```bash\n\"${plugin_path}\"/bin/help.overview\n```\n\n---\n\n### `bin/help.deps`\n\n**Description**\n\nOutput the list of dependencies tailored to the operating system. One dependency per line.\n\n```bash\ngit\ncurl\nsed\n```\n\n**Implementation Details**\n\n- This script requires `bin/help.overview` for its output to be considered.\n- Should be tailored to the Operating System and version of the tool being installed (using optionally set Environment Variables `ASDF_INSTALL_VERSION` and `ASDF_INSTALL_TYPE`).\n- Success should exit with `0`.\n- Failure should exit with a non-zero status.\n\n**Environment Variables available to script**\n\n- `ASDF_INSTALL_TYPE`: `version` or `ref`\n- `ASDF_INSTALL_VERSION`:\n  - Full version number if `ASDF_INSTALL_TYPE=version`.\n  - Git ref (tag/commit/branch) if `ASDF_INSTALL_TYPE=ref`.\n- `ASDF_INSTALL_PATH`: The path to where the tool _has been_, or _should be_ installed.\n\n**Commands that invoke this script**\n\n- `asdf help <name> [<version>]`: Output documentation for plugin and tool\n\n**Call signature from asdf core**\n\n```bash\n\"${plugin_path}\"/bin/help.deps\n```\n\n---\n\n### `bin/help.config`\n\n**Description**\n\nOutput any required or optional configuration for the plugin and tool. For example, describe any environment variables or other flags needed to install or compile the tool.\n\n**Implementation Details**\n\n- This script requires `bin/help.overview` for its output to be considered.\n- Output can be free-form text.\n- Should be tailored to the Operating System and version of the tool being installed (using optionally set Environment Variables `ASDF_INSTALL_VERSION` and `ASDF_INSTALL_TYPE`).\n- Success should exit with `0`.\n- Failure should exit with a non-zero status.\n\n**Environment Variables available to script**\n\n- `ASDF_INSTALL_TYPE`: `version` or `ref`\n- `ASDF_INSTALL_VERSION`:\n  - Full version number if `ASDF_INSTALL_TYPE=version`.\n  - Git ref (tag/commit/branch) if `ASDF_INSTALL_TYPE=ref`.\n- `ASDF_INSTALL_PATH`: The path to where the tool _has been_, or _should be_ installed.\n\n**Commands that invoke this script**\n\n- `asdf help <name> [<version>]`: Output documentation for plugin and tool\n\n**Call signature from asdf core**\n\n```bash\n\"${plugin_path}\"/bin/help.config\n```\n\n---\n\n### `bin/help.links`\n\n**Description**\n\nOutput a list of links relevant to the plugin and tool. One link per line.\n\n```bash\nGit Repository:\thttps://github.com/vlang/v\nDocumentation:\thttps://vlang.io\n```\n\n**Implementation Details**\n\n- This script requires `bin/help.overview` for its output to be considered.\n- One link per line.\n- Format must be either:\n  - `<title>: <link>`\n  - or just `<link>`\n- Should be tailored to the Operating System and version of the tool being installed (using optionally set Environment Variables `ASDF_INSTALL_VERSION` and `ASDF_INSTALL_TYPE`).\n- Success should exit with `0`.\n- Failure should exit with a non-zero status.\n\n**Environment Variables available to script**\n\n- `ASDF_INSTALL_TYPE`: `version` or `ref`\n- `ASDF_INSTALL_VERSION`:\n  - Full version number if `ASDF_INSTALL_TYPE=version`.\n  - Git ref (tag/commit/branch) if `ASDF_INSTALL_TYPE=ref`.\n- `ASDF_INSTALL_PATH`: The path to where the tool _has been_, or _should be_ installed.\n\n**Commands that invoke this script**\n\n- `asdf help <name> [<version>]`: Output documentation for plugin and tool\n\n**Call signature from asdf core**\n\n```bash\n\"${plugin_path}\"/bin/help.links\n```\n\n---\n\n### `bin/list-bin-paths`\n\n**Description**\n\nList directories containing executables for the specified version of the tool.\n\n**Implementation Details**\n\n- If this script is not present, asdf will look for binaries in the `\"${ASDF_INSTALL_PATH}\"/bin` directory & create shims for those.\n- Output a space-separated list of paths containing executables.\n- Paths must be relative to `ASDF_INSTALL_PATH`. Example output would be:\n\n```bash\nbin tools veggies\n```\n\nThis will instruct asdf to create shims for the files in:\n- `\"${ASDF_INSTALL_PATH}\"/bin`\n- `\"${ASDF_INSTALL_PATH}\"/tools`\n- `\"${ASDF_INSTALL_PATH}\"/veggies`\n\n**Environment Variables available to script**\n\n- `ASDF_INSTALL_TYPE`: `version` or `ref`\n- `ASDF_INSTALL_VERSION`:\n  - Full version number if `ASDF_INSTALL_TYPE=version`.\n  - Git ref (tag/commit/branch) if `ASDF_INSTALL_TYPE=ref`.\n- `ASDF_INSTALL_PATH`: The path to where the tool _has been_, or _should be_ installed.\n\n**Commands that invoke this script**\n\n- `asdf install <tool> [version]`: initially create shims for binaries.\n- `asdf reshim <tool> <version>`: recreate shims for binaries.\n\n**Call signature from asdf core**\n\n```bash\n\"${plugin_path}/bin/list-bin-paths\"\n```\n\n---\n\n### `bin/exec-env`\n\n**Description**\n\nPrepare the environment before executing the shims for the binaries for the tool.\n\n**Environment Variables available to script**\n\n- `ASDF_INSTALL_TYPE`: `version` or `ref`\n- `ASDF_INSTALL_VERSION`:\n  - Full version number if `ASDF_INSTALL_TYPE=version`.\n  - Git ref (tag/commit/branch) if `ASDF_INSTALL_TYPE=ref`.\n- `ASDF_INSTALL_PATH`: The path to where the tool _has been_, or _should be_ installed.\n\n**Commands that invoke this script**\n\n- `asdf which <command>`: Display the path to an executable\n- `asdf exec <command> [args...]`: Executes the command shim for current version\n- `asdf env <command> [util]`: Runs util (default: `env`) inside the environment used for command shim execution.\n\n**Call signature from asdf core**\n\n```bash\n\"${plugin_path}/bin/exec-env\"\n```\n\n---\n\n### `bin/exec-path`\n\nGet the executable path for the specified version of the tool. Must print a\nstring with the relative executable path. This allows the plugin to\nconditionally override the shim's specified executable path, otherwise return\nthe default path specified by the shim.\n\n**Description**\n\nGet the executable path for the specified version of the tool.\n\n**Implementation Details**\n\n- Must print a string with the relative executable path.\n- Conditionally override the shim's specified executable path, otherwise return the default path specified by the shim.\n\n```shell\nUsage:\n  plugin/bin/exec-path <install-path> <command> <executable-path>\n\nExample Call:\n  ~/.asdf/plugins/foo/bin/exec-path \"~/.asdf/installs/foo/1.0\" \"foo\" \"bin/foo\"\n\nOutput:\n  bin/foox\n```\n\n**Environment Variables available to script**\n\n- `ASDF_INSTALL_TYPE`: `version` or `ref`\n- `ASDF_INSTALL_VERSION`:\n  - Full version number if `ASDF_INSTALL_TYPE=version`.\n  - Git ref (tag/commit/branch) if `ASDF_INSTALL_TYPE=ref`.\n- `ASDF_INSTALL_PATH`: The path to where the tool _has been_, or _should be_ installed.\n\n**Commands that invoke this script**\n\n- `asdf which <command>`: Display the path to an executable\n- `asdf exec <command> [args...]`: Executes the command shim for current version\n- `asdf env <command> [util]`: Runs util (default: `env`) inside the environment used for command shim execution.\n\n**Call signature from asdf core**\n\n```bash\n\"${plugin_path}/bin/exec-path\" \"$install_path\" \"$cmd\" \"$relative_path\"\n```\n\n---\n\n### `bin/uninstall`\n\n**Description**\n\nUninstall the provided version of a tool.\n\n**Output Format**\n\nOutput should be sent to `stdout` or `stderr` as appropriate for the user. No output is read by subsequent execution in the core.\n\n**Environment Variables available to script**\n\nNo environment variables are provided to this script.\n\n**Commands that invoke this script**\n\n- `asdf list all <name> <version>`\n- `asdf uninstall nodejs 18.15.0`: Uninstalls the version `18.15.0` of nodejs, removing all shims including those installed global with `npm i -g`\n\n**Call signature from asdf core**\n\nNo parameters provided.\n\n```bash\n\"${plugin_path}/bin/uninstall\"\n```\n\n---\n\n### `bin/list-legacy-filenames`\n\n**Description**\n\nList legacy configuration filenames for determining the specified version of the tool.\n\n**Implementation Details**\n\n- Output a space-separated list of filenames.\n  ```bash\n  .ruby-version .rvmrc\n  ```\n- Only applies for users who have enabled the `legacy_version_file` option in their `\"${HOME}\"/.asdfrc`.\n\n**Environment Variables available to script**\n\n- `ASDF_INSTALL_TYPE`: `version` or `ref`\n- `ASDF_INSTALL_VERSION`:\n  - Full version number if `ASDF_INSTALL_TYPE=version`.\n  - Git ref (tag/commit/branch) if `ASDF_INSTALL_TYPE=ref`.\n- `ASDF_INSTALL_PATH`: The path to where the tool _has been_, or _should be_ installed.\n\n**Commands that invoke this script**\n\nAny command which reads a tool version.\n\n**Call signature from asdf core**\n\nNo parameters provided.\n\n```bash\n\"${plugin_path}/bin/list-legacy-filenames\"\n```\n\n---\n\n### `bin/parse-legacy-file`\n\n**Description**\n\nParse the legacy file found by asdf to determine the version of the tool. Useful to extract version numbers from files like JavaScript's `package.json` or Golangs `go.mod`.\n\n**Implementation Details**\n\n- If not present, asdf will simply `cat` the legacy file to determine the version.\n- Should be **deterministic** and always return the same exact version:\n  - when parsing the same legacy file.\n  - regardless of what is installed on the machine or whether the legacy version is valid or complete. Some legacy file formats may not be suitable.\n- Output a single line with the version:\n  ```bash\n  1.2.3\n  ```\n\n**Environment Variables available to script**\n\nNo environment variables specifically set before this script is called.\n\n**Commands that invoke this script**\n\nAny command which reads a tool version.\n\n**Call signature from asdf core**\n\nThe script should accept a single argument, the path to the legacy file for reading its contents.\n\n```bash\n\"${plugin_path}/bin/parse-legacy-file\" \"$file_path\"\n```\n\n---\n\n### `bin/post-plugin-add`\n\n**Description**\n\nExecute this callback script **after** the plugin has been _added_ to asdf with `asdf plugin add <tool>`.\n\nSee also the related command hooks:\n\n- `pre_asdf_plugin_add`\n- `pre_asdf_plugin_add_${plugin_name}`\n- `post_asdf_plugin_add`\n- `post_asdf_plugin_add_${plugin_name}`\n\n**Environment Variables available to script**\n\n- `ASDF_PLUGIN_PATH`: path where the plugin was installed.\n- `ASDF_PLUGIN_SOURCE_URL`: URL of the plugin source. Can be a local directory path.\n\n**Call signature from asdf core**\n\nNo parameters provided.\n\n```bash\n\"${plugin_path}/bin/post-plugin-add\"\n```\n\n---\n\n### `bin/post-plugin-update`\n\n**Description**\n\nExecute this callback script **after** asdf has downloaded the _update_ plugin with `asdf plugin update <tool> [<git-ref>]`.\n\nSee also the related command hooks:\n\n- `pre_asdf_plugin_update`\n- `pre_asdf_plugin_update_${plugin_name}`\n- `post_asdf_plugin_update`\n- `post_asdf_plugin_update_${plugin_name}`\n\n**Environment Variables available to script**\n\n- `ASDF_PLUGIN_PATH`: path where the plugin was installed.\n- `ASDF_PLUGIN_PREV_REF`: the plugin's previous git-ref\n- `ASDF_PLUGIN_POST_REF`: the plugin's updated git-ref\n\n**Call signature from asdf core**\n\nNo parameters provided.\n\n```bash\n\"${plugin_path}/bin/post-plugin-update\"\n```\n\n---\n\n### `bin/pre-plugin-remove`\n\n**Description**\n\nExecute this callback script **before** asdf has removed the plugin with `asdf plugin remove <tool>`.\n\nSee also the related command hooks:\n\n- `pre_asdf_plugin_remove`\n- `pre_asdf_plugin_remove_${plugin_name}`\n- `post_asdf_plugin_remove`\n- `post_asdf_plugin_remove_${plugin_name}`\n\n**Environment Variables available to script**\n\n- `ASDF_PLUGIN_PATH`: path where the plugin was installed.\n\n**Call signature from asdf core**\n\nNo parameters provided.\n\n```bash\n\"${plugin_path}/bin/pre-plugin-remove\"\n```\n\n<!-- TODO: document command hooks -->\n<!-- ## Command Hooks -->\n\n## Extension Commands for asdf CLI <Badge type=\"danger\" text=\"advanced\" vertical=\"middle\" />\n\nIt's possible for plugins to define new asdf commands by providing\n`lib/commands/command*.bash` scripts or executables that will be callable using\nthe asdf command line interface by using the plugin name as a subcommand.\n\nFor example, suppose a `foo` plugin has:\n\n```shell\nfoo/\n  lib/commands/\n    command.bash\n    command-bat.bash\n    command-bat-man.bash\n    command-help.bash\n```\n\nUsers can now execute:\n\n```shell\n$ asdf foo         # same as running `$ASDF_DATA_DIR/plugins/foo/lib/commands/command.bash`\n$ asdf foo bar     # same as running `$ASDF_DATA_DIR/plugins/foo/lib/commands/command.bash bar`\n$ asdf foo help    # same as running `$ASDF_DATA_DIR/plugins/foo/lib/commands/command-help.bash`\n$ asdf foo bat man # same as running `$ASDF_DATA_DIR/plugins/foo/lib/commands/command-bat-man.bash`\n$ asdf foo bat baz # same as running `$ASDF_DATA_DIR/plugins/foo/lib/commands/command-bat.bash baz`\n```\n\nPlugin authors can use this feature to provide utilities related to their tools,\nor even create plugins that are just new command extensions of asdf itself.\n\nIf the executable bit is set, the script is executed, replacing the asdf\nexecution.\n\nIf the executable bit is not set, asdf will source the scripts as Bash scripts.\n\n`$ASDF_CMD_FILE` resolves to the full path of the file being sourced.\n\n[`haxe`](https://github.com/asdf-community/asdf-haxe) is a great example of a\nplugin which uses this feature. It provides the `asdf haxe neko-dylibs-link` to\nfix an issue where Haxe executables expect to find dynamic libraries relative to\nthe executable directory.\n\nBe sure to list your asdf Extension Commands in your plugins README.\n\n## Custom Shim Templates <Badge type=\"danger\" text=\"advanced\" vertical=\"middle\" />\n\n::: warning\n\nPlease only use if **absolutely** required\n\n:::\n\nasdf allows custom shim templates. For an executable called `foo`, if there's a\n`shims/foo` file in the plugin, then asdf will copy that file instead of using\nits standard shim template.\n\n**This must be used wisely.**\n\nAs far as the asdf core team is aware, this feature is only in use in the\nfirst-party [Elixir plugin](https://github.com/asdf-vm/asdf-elixir). This is\nbecause an executable is also read as an Elixir file in addition to being an\nexecutable. This makes it not possible to use the standard Bash shim.\n\n## Testing\n\n`asdf` contains the `plugin-test` command to test your plugin:\n\n```shell\nasdf plugin test <plugin_name> <plugin_url> [--asdf-tool-version <version>] [--asdf-plugin-gitref <git_ref>] [test_command...]\n```\n\n- `<plugin_name>` & `<plugin_url>` are required\n- If optional `[--asdf-tool-version <version>]` is specified, the tool will be\n  installed with that specific version. Defaults to `asdf latest <plugin-name>`\n- If optional `[--asdf-plugin-gitref <git_ref>]` is specified, the plugin itself\n  is checked out at that commit/branch/tag. This is useful for testing a\n  pull-request on your plugin's CI. Defaults to the default branch of the plugin's repository.\n- Optional parameter `[test_command...]` is the command to execute to validate\n  the installed tool works correctly. Typically `<tool> --version` or\n  `<tool> --help`. For example, to test the NodeJS plugin, we could run\n  ```shell\n  # asdf plugin test <plugin_name>  <plugin_url>                               [test_command]\n    asdf plugin test nodejs         https://github.com/asdf-vm/asdf-nodejs.git node --version\n  ```\n\n::: tip Note\n\nWe recommend testing in both Linux & macOS CI environments\n\n:::\n\n### GitHub Action\n\nThe [asdf-vm/actions](https://github.com/asdf-vm/actions) repo provides a GitHub\nAction for testing your plugins hosted on GitHub. A sample\n`.github/workflows/test.yaml` Actions Workflow:\n\n```yaml\nname: Test\non:\n  push:\n    branches:\n      - main\n  pull_request:\n\njobs:\n  plugin_test:\n    name: asdf plugin test\n    strategy:\n      matrix:\n        os:\n          - ubuntu-latest\n          - macos-latest\n    runs-on: ${{ matrix.os }}\n    steps:\n      - name: asdf_plugin_test\n        uses: asdf-vm/actions/plugin-test@v2\n        with:\n          command: \"<MY_TOOL> --version\"\n```\n\n### TravisCI Config\n\nA sample `.travis.yml` file, customize it to your needs\n\n```yaml\nlanguage: c\nscript: asdf plugin test <MY_TOOL> $TRAVIS_BUILD_DIR '<MY_TOOL> --version'\nbefore_script:\n  - git clone https://github.com/asdf-vm/asdf.git asdf\n  - . asdf/asdf.sh\nos:\n  - linux\n  - osx\n```\n\n::: tip NOTE\n\nWhen using another CI you may need to pass a relative path to the plugin\nlocation:\n\n```shell\nasdf plugin test <tool_name> <path> '<tool_command> --version'\n```\n\n:::\n\n## API Rate Limiting\n\nIf a command depends on accessing an external API, like `bin/list-all` or\n`bin/latest-stable`, it may experience rate limiting during automated testing.\nTo mitigate this, ensure there is a code-path which provides an authentication\ntoken via an environment variable. For example:\n\n```shell\ncmd=\"curl --silent\"\nif [ -n \"$GITHUB_API_TOKEN\" ]; then\n cmd=\"$cmd -H 'Authorization: token $GITHUB_API_TOKEN'\"\nfi\n\ncmd=\"$cmd $releases_path\"\n```\n\n### `GITHUB_API_TOKEN`\n\nTo utilise the `GITHUB_API_TOKEN`, create a\n[new personal token](https://github.com/settings/tokens/new) with only\n`public_repo` access.\n\nThen add this to your CI pipeline environment variables.\n\n::: warning\n\nNEVER publish your authentication tokens in your code repository\n\n:::\n\n## Plugin Shortname Index\n\n::: tip\n\nThe recommended installation method for a plugin is via direct URL installation:\n\n```shell\n# asdf plugin add <name> <git_url>\n  asdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs\n```\n\n:::\n\nIf the `git_url` is not provided, asdf will use the\n[Shortname Index repository](https://github.com/asdf-vm/asdf-plugins) to\ndetermine the exact `git_url` to use.\n\nYou can add your plugin to the\n[Shortname Index](https://github.com/asdf-vm/asdf-plugins) by following the\ninstructions in that repo.\n"
  },
  {
    "path": "docs/pt-br/contribute/core.md",
    "content": "# asdf\n\n> Hi, we've recently migrated our docs and added some new pages. If you would like to help translate this page, see the \"Edit this page\" link at the bottom of the page.\n\nguia de contribuição principal `asdf`.\n\n## Configuração inicial\n\nFork `asdf` no GitHub e/ou Git clone o branch padrão:\n\n```shell\n# clone your fork\ngit clone https://github.com/<GITHUB_USER>/asdf.git\n# or clone asdf\ngit clone https://github.com/asdf-vm/asdf.git\n```\n\nAs ferramentas para o desenvolvimento do núcleo estão em `.tool-versions` deste repositório.  Se você deseja gerenciar com o próprio `asdf`, adicione os plugins:\n\n```shell\nasdf plugin add bats https://github.com/timgluz/asdf-bats.git\nasdf plugin add shellcheck https://github.com/luizm/asdf-shellcheck.git\nasdf plugin add shfmt https://github.com/luizm/asdf-shfmt.git\n```\n\nInstale as versões para desenvolver `asdf` com:\n\n```shell\nasdf install\n```\n\n_pode_ ser útil não usar `asdf` para gerenciar as ferramentas durante o desenvolvimento em sua máquina local, pois você pode precisar quebrar funcionalidades que, então, quebrariam suas ferramentas de desenvolvimento.  Aqui está a lista bruta de ferramentas:\n\n- [bats-core](https://github.com/bats-core/bats-core): Bash Automated Testing System, para testes unitários de scripts compatíveis com Bash ou POSIX.\n- [shellcheck](https://github.com/koalaman/shellcheck): Ferramenta de análise estática para scripts de shell.\n- [shfmt](https://github.com/mvdan/sh): Um analisador, formatador e interpretador de shell com suporte a bash; inclui shfmt\n\n## Desenvolvimento\n\nSe você quiser testar suas alterações sem fazer alterações em seu `asdf` instalado, você pode definir a variável `$ASDF_DIR` para o caminho onde você clonou o repositório e anexar temporariamente o diretório `bin` e `shims` do diretório para o seu caminho.\n\nÉ melhor formatar, lint e testar seu código localmente antes de confirmar ou enviar para o controle remoto. Use os seguintes scripts/comandos:\n\n```shell\n# Shellcheck\n./scripts/shellcheck.bash\n\n# Format\n./scripts/shfmt.bash\n\n# Test: all tests\nbats test/\n# Test: for specific command\nbats test/list_commands.bash\n```\n\n::: tip\n\n **Adicione testes!** - Os testes são **necessários** para novos recursos e aceleram a revisão de correções de bugs.  Por favor, cubra novos caminhos de código antes de criar um Pull Request.  Consulte [documentação do bats-core](https://bats-core.readthedocs.io/en/stable/index.html)\n\n:::\n\n## Teste de BATS\n\nÉ **fortemente recomendado** examinar o conjunto de testes existente e a [documentação do bats-core](https://bats-core.readthedocs.io/en/stable/index.html) antes de escrever os testes.\n\nA depuração de BATs pode ser difícil às vezes. Usar a saída TAP com o sinalizador `-t` permitirá que você imprima saídas com o descritor de arquivo especial `>&3` durante a execução do teste, simplificando a depuração. Como um exemplo:\n\n```shell\n# test/some_tests.bats\n\nprintf \"%s\\n\" \"Will not be printed during bats test/some_tests.bats\"\nprintf \"%s\\n\" \"Will be printed during bats -t test/some_tests.bats\" >&3\n```\n\nIsso está documentado em bats-core [Imprimindo no Terminal](https://bats-core.readthedocs.io/en/stable/writing-tests.html#printing-to-the-terminal).\n\n## Pull Requests, Releases e Commits Convencionais\n\nO `asdf` está usando uma ferramenta de lançamento automatizada chamada [Release Please](https://github.com/googleapis/release-please) para aumentar automaticamente a versão [SemVer](https://semver.org/) e gerar a [Changelog](https://github.com/asdf-vm/asdf/blob/master/CHANGELOG.md).  Essas informações são determinadas lendo o histórico de confirmação desde a última versão.\n\n[Mensagens de confirmação convencionais](https://www.conventionalcommits.org/) definem o formato do título da solicitação pull que se torna o formato da mensagem de confirmação na ramificação padrão. Isso é aplicado com GitHub Action [`amannn/action-semantic-pull-request`](https://github.com/amannn/action-semantic-pull-request).\n\nO Commit Convencional segue este formato:\n\n```\n<type>[optional scope][optional !]: <description>\n\n<!-- examples -->\nfix: some fix\nfeat: a new feature\ndocs: some documentation update\ndocs(website): some change for the website\nfeat!: feature with breaking change\n```\n\nA lista completa de `<types>` é: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`.\n\nO `!` indica uma mudança de ruptura.\n\n`fix`: will create a new SemVer `patch`\n`feat`: will create a new SemVer `minor`\n`<type>!`: will create a new SemVer `major`\n\nO título da solicitação pull deve seguir este formato.\n\n::: tip\n\nUse o formato de mensagem de confirmação convencional para seu título de solicitação de pull.\n\n:::\n\n## Imagens Docker\n\nOs projetos [asdf-alpine](https://github.com/vic/asdf-alpine) e [asdf-ubuntu](https://github.com/vic/asdf-ubuntu) são um esforço contínuo para fornecer imagens de algumas ferramentas asdf.  Você pode usar essas imagens docker como base para seus servidores de desenvolvimento ou para executar seus aplicativos de produção.\n"
  },
  {
    "path": "docs/pt-br/contribute/documentation.md",
    "content": "# Docs & Site\n\n> Hi, we've recently migrated our docs and added some new pages. If you would like to help translate this page, see the \"Edit this page\" link at the bottom of the page.\n\nDocumentação e guia de contribuição do site.\n\n## Configuração inicial\n\nFork `asdf` no GitHub e/ou Git clone o branch padrão:\n\n```shell\n# clone your fork\ngit clone https://github.com/<GITHUB_USER>/asdf.git\n# or clone asdf\ngit clone https://github.com/asdf-vm/asdf.git\n```\n\nAs ferramentas para desenvolvimento de sites Docs são gerenciadas com `asdf` em `docs/.tool-versions`. Adicione os plugins com:\n\n```shell\nasdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs\n```\n\nInstale a(s) versão(ões) da ferramenta com:\n\n```shell\nasdf install\n```\n\n- [Node.js](https://nodejs.org): tempo de execução JavaScript criado no mecanismo JavaScript V8 do Chrome.\n-\nInstale as dependências do Node.js do `docs/package.json`:\n\n```shell\nnpm install\n```\n\n## Desenvolvimento\n\n[Vuepress (v2)](https://v2.vuepress.vuejs.org/) é o Static Site Generator (SSG) que usamos para construir o site de documentação do asdf. Foi escolhido para substituir [Docsify.js](https://docsify.js.org/), pois gostaríamos de oferecer suporte a um substituto somente HTML quando os usuários não tiverem JavaScript disponível ou ativado. Isso não era possível com o Docsify. Fora isso, o conjunto de recursos é basicamente o mesmo, com foco em escrever arquivos Markdown com configuração mínima.\n\n`package.json` contém os scripts necessários para o desenvolvimento:\n\n@[code json{3-5}](../package.json)\n\n Para iniciar o servidor de desenvolvimento local:\n\n```shell\nnpm run dev\n```\n\nFormate o código antes de confirmar:\n\n```shell\nnpm run format\n```\n\n## Pull Requests, Releases e Commits Convencionais\n\n`asdf` está usando um pipeline de lançamento automatizado que depende de Commits Convencionais em títulos de PR. Documentação detalhada encontrada no [guia de contribuição principal](./core.md).\n\nAo criar um PR para alterações na documentação, por favor, faça o título do PR com o tipo de Commit Convencional `docs` no formato `docs: <description>`.\n\n## Vuepress\n\nA configuração do site está contida em alguns arquivos JavaScript com objetos JS usados para representar a configuração. Eles estão:\n\n- `docs/.vuepress/config.js`: o arquivo de configuração raiz do site. Leia a [documentação do Vuepress](https://v2.vuepress.vuejs.org/guide/configuration.html#config-file) para obter as especificações.\n\nPara simplificar a configuração raiz, os objetos JS maiores que representam a configuração _navbar_ e _sidebar_ foram extraídos e separados por sua localidade. Veja os dois em:\n\n- `docs/.vuepress/navbar.js`\n- `docs/.vuepress/sidebar.js`\n\nCom a documentação oficial para essas configurações vivendo na [Referência de tema padrão](https://v2.vuepress.vuejs.org/reference/default-theme/config.html#locale-config).\n\n## I18n\n\nVuepress tem suporte de primeira classe para internacionalização. O root config `docs/.vuepress/config.js` define os locais suportados com sua URL, título no menu suspenso de seleção e referências de configurações navbar/sidebar.\n\nAs configurações da barra de navegação/barra lateral são capturadas nos arquivos de configuração mencionados acima, separadas por localidade e exportadas individualmente.\n\nO conteúdo de markdown para cada localidade deve estar em uma pasta com o mesmo nome das chaves para `locales` na configuração raiz.  Isso é:\n\n```js\n{\n  ...\n  themeConfig: {\n    locales: {\n      \"/\": {\n        selectLanguageName: \"English (US)\",\n        sidebar: sidebar.en,\n        navbar: navbar.en\n      },\n      \"/pt-BR/\": {\n        selectLanguageName: \"Brazilian Portuguese\",\n        sidebar: sidebar.pt_br,\n        navbar: navbar.pt_br\n      }\n    }\n  }\n}\n```\n\n`/pt-BR/` exigirá o mesmo conjunto de arquivos markdown localizados em `docs/pt-BR/`, assim:\n\n```shell\ndocs\n├─ README.md\n├─ foo.md\n├─ nested\n│  └─ README.md\n└─ pt-BR\n   ├─ README.md\n   ├─ foo.md\n   └─ nested\n      └─ README.md\n```\n\nA [documentação oficial do Vuepress i18n](https://v2.vuepress.vuejs.org/guide/i18n.html#site-i18n-config) entra em mais detalhes.\n"
  },
  {
    "path": "docs/pt-br/contribute/first-party-plugins.md",
    "content": "# First-Party Plugins\n\n> Hi, we've recently migrated our docs and added some new pages. If you would like to help translate this page, see the \"Edit this page\" link at the bottom of the page.\n\nA equipe principal do asdf criou alguns plugins relevantes para sua vida profissional diária. A ajuda é sempre bem-vinda na manutenção e melhoria desses plugins. Veja o repositório associado para cada link abaixo:\n\n- [Elixir](https://github.com/asdf-vm/asdf-elixir)\n- [Erlang](https://github.com/asdf-vm/asdf-erlang)\n- [Node.js](https://github.com/asdf-vm/asdf-nodejs)\n- [Ruby](https://github.com/asdf-vm/asdf-ruby)\n\nPara plugins da comunidade, consulte:\n\n- [`asdf-community` organisation](https://github.com/asdf-community): A collaborative, community-driven project for long-term maintenance of `asdf` plugins.\n- [`asdf-plugins` shortname repo](https://github.com/asdf-vm/asdf-plugins): Short-name list used by `asdf` core to lookup popular `asdf` plugins.\n- [GitHub `asdf-plugin` topic search](https://github.com/topics/asdf-plugin)\n"
  },
  {
    "path": "docs/pt-br/contribute/github-actions.md",
    "content": "# GitHub Actions\n\n> Hi, we've recently migrated our docs and added some new pages. If you would like to help translate this page, see the \"Edit this page\" link at the bottom of the page.\n\nObrigado pelo seu interesse, consulte o [repositório de ações asdf](https://github.com/asdf-vm/actions) para ver os problemas, conversas e diretrizes de contribuição existentes.\n"
  },
  {
    "path": "docs/pt-br/guide/getting-started.md",
    "content": "# Começando\n\n> Hi, we've recently migrated our docs and added some new pages. If you would like to help translate this page, see the \"Edit this page\" link at the bottom of the page.\n\nA instalação do `asdf` envolve:\n\n1. Instalar as dependências\n2. Instalar o núcleo do `asdf`\n3. Adicionar o `asdf` ao seu shell\n4. Instalar um plugin para cada ferramenta que você gostaria de gerenciar\n5. Instalar uma versão desta ferramenta\n6. Definir uma versão global e uma versão local através do arquivo de configuração `.tool-versions`\n\nVocê pode também acompanhar o passo a passo da instalação através [deste vídeo](https://youtu.be/8W3xaSPjeog).\n\n## 1. Instalando as dependências\n\nasdf primarily requires `git` & `curl`. Here is a _non-exhaustive_ list of commands to run for _your_ package manager (some might automatically install these tools in later steps).\n\n| OS    | Package Manager | Command                            |\n| ----- | --------------- | ---------------------------------- |\n| linux | Aptitude        | `apt install curl git`             |\n| linux | DNF             | `dnf install curl git`             |\n| linux | Pacman          | `pacman -S curl git`               |\n| linux | Zypper          | `zypper install curl git`          |\n| macOS | Homebrew        | `brew install coreutils curl git`  |\n| macOS | Spack           | `spack install coreutils curl git` |\n\n::: tip Note\n\n`sudo` may be required depending on your system configuration.\n\n:::\n\n## 2. Download asdf\n\n### Official Download\n\n<!-- x-release-please-start-version -->\n\n```shell\ngit clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.18.1\n```\n\n<!-- x-release-please-end -->\n\n### Community Supported Download Methods\n\nWe highly recommend using the official `git` method.\n\n| Method   | Command                                                                                                                                                             |\n| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Homebrew | `brew install asdf`                                                                                                                                                 |\n| Pacman   | `git clone https://aur.archlinux.org/asdf-vm.git && cd asdf-vm && makepkg -si` or use your preferred [AUR helper](https://wiki.archlinux.org/index.php/AUR_helpers) |\n\n## 3. Adicionando ao seu shell\n\nExistem diversas combinações de shells, sistemas operacionais e métodos de instalação que podem impactar a configuração. Abaixo, expanda a seção que se adeque mais com o seu sistema:\n\n::: details Bash & Git\n\nAdicione esta linha ao seu `~/.bashrc`:\n\n```shell\n. \"$HOME/.asdf/asdf.sh\"\n```\n\nO auto completar deve ser configurado manualmente a partir da adição da seguinte linha ao `.bashrc`:\n\n```shell\n. \"$HOME/.asdf/completions/asdf.bash\"\n```\n\n:::\n\n::: details Bash & Git (macOS)\n\nSe você estiver usando o **macOS Catalina ou mais recente**, o shell padrão mudou para o **ZSH**. A não ser que você tenha voltado para o bash, siga as instruções de instalação para o ZSH.\n\nAdicione esta linha ao seu `~/.bash_profile`:\n\n```shell\n. \"$HOME/.asdf/asdf.sh\"\n```\n\nO auto completar deve ser configurado manualmente a partir da adição da seguinte linha ao `.bash_profile`:\n\n```shell\n. \"$HOME/.asdf/completions/asdf.bash\"\n```\n\n:::\n\n::: details Bash & Homebrew\n\nAdicione `asdf.sh` ao `~/.bashrc` através do comando:\n\n```shell\necho -e \"\\n. $(brew --prefix asdf)/asdf.sh\" >> ~/.bashrc\n```\n\nO auto completar deve ser configurado seguindo as [instruções da Homebrew](https://docs.brew.sh/Shell-Completion#configuring-completions-in-bash), ou as seguintes:\n\n```shell\necho -e \"\\n. \\\"$(brew --prefix asdf)/etc/bash_completion.d/asdf.bash\\\"\" >> ~/.bashrc\n```\n\n:::\n\n::: details Bash & Homebrew (macOS)\n\nSe você estiver usando o **macOS Catalina ou mais recente**, o shell padrão mudou para o **ZSH**. A não ser que você tenha voltado para o bash, siga as instruções de instalação para o ZSH.\n\nAdicione `asdf.sh` ao `~/.bash_profile` através do comando:\n\n```shell\necho -e \"\\n. $(brew --prefix asdf)/asdf.sh\" >> ~/.bash_profile\n```\n\nO auto completar deve ser configurado seguindo as [instruções da Homebrew](https://docs.brew.sh/Shell-Completion#configuring-completions-in-bash), ou as seguintes:\n\n```shell\necho -e \"\\n. \\\"$(brew --prefix asdf)/etc/bash_completion.d/asdf.bash\\\"\" >> ~/.bash_profile\n```\n\n:::\n\n::: details Bash & Pacman\n\nAdicione a seguinte linha ao seu `~/.bashrc`:\n\n```shell\n. /opt/asdf-vm/asdf.sh\n```\n\nO [pacote `bash-completion`](https://wiki.archlinux.org/title/bash#Common_programs_and_options) precisa ser instalado para o auto completar funcionar.\n:::\n\n::: details Fish & Git\n\nAdicione a seguinte linha ao seu `~/.config/fish/config.fish`:\n\n```shell\nsource ~/.asdf/asdf.fish\n```\n\nO auto completar deve ser configurado manualmente através do seguinte comando:\n\n```shell\nmkdir -p ~/.config/fish/completions; and ln -s ~/.asdf/completions/asdf.fish ~/.config/fish/completions\n```\n\n:::\n\n::: details Fish & Homebrew\n\nAdicione `asdf.fish` ao seu `~/.config/fish/config.fish` através do comando:\n\n```shell\necho -e \"\\nsource \"(brew --prefix asdf)\"/asdf.fish\" >> ~/.config/fish/config.fish\n```\n\nO auto completar é [configurado pela Homebrew para o fish shell](https://docs.brew.sh/Shell-Completion#configuring-completions-in-fish).\n:::\n\n::: details Fish & Pacman\n\nAdicione a seguinte linha ao seu `~/.config/fish/config.fish`:\n\n```shell\nsource /opt/asdf-vm/asdf.fish\n```\n\nO auto completar é configurado automaticamente durante a instalação do pacote AUR.\n:::\n\n::: details Elvish & Git\n\nAdicione `asdf.elv` ao `~/.config/elvish/rc.elv` através do comando:\n\n```shell\nmkdir -p ~/.config/elvish/lib; ln -s ~/.asdf/asdf.elv ~/.config/elvish/lib/asdf.elv\necho \"\\n\"'use asdf _asdf; var asdf~ = $_asdf:asdf~' >> ~/.config/elvish/rc.elv\necho \"\\n\"'set edit:completion:arg-completer[asdf] = $_asdf:arg-completer~' >> ~/.config/elvish/rc.elv\n```\n\nAo concluir atualizará automaticamente\n\n:::\n\n::: details Elvish & Homebrew\n\nAdicione `asdf.elv` ao `~/.config/elvish/rc.elv` através do comando:\n\n```shell\nmkdir -p ~/.config/elvish/lib; ln -s (brew --prefix asdf)/libexec/asdf.elv ~/.config/elvish/lib/asdf.elv\necho \"\\n\"'use asdf _asdf; var asdf~ = $_asdf:asdf~' >> ~/.config/elvish/rc.elv\necho \"\\n\"'set edit:completion:arg-completer[asdf] = $_asdf:arg-completer~' >> ~/.config/elvish/rc.elv\n```\n\nAo concluir atualizará automaticamente\n:::\n\n::: details Elvish & Pacman\n\nAdicione `asdf.elv` ao `~/.config/elvish/rc.elv` através do comando:\n\n```shell\nmkdir -p ~/.config/elvish/lib; ln -s /opt/asdf-vm/asdf.elv ~/.config/elvish/lib/asdf.elv\necho \"\\n\"'use asdf _asdf; var asdf~ = $_asdf:asdf~' >> ~/.config/elvish/rc.elv\necho \"\\n\"'set edit:completion:arg-completer[asdf] = $_asdf:arg-completer~' >> ~/.config/elvish/rc.elv\n```\n\nAo concluir atualizará automaticamente\n:::\n\n::: details ZSH & Git\n\nAdicione a seguinte linha ao seu `~/.zshrc`:\n\n```shell\n. \"$HOME/.asdf/asdf.sh\"\n```\n\n**OU** utilize um framework para ZSH, como [asdf para oh-my-zsh](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/asdf) que irá adicionar o script e o auto completar.\n\nO auto completar pode ser configurado ou pelo plugin do asdf para framework para ZSH, ou através da adição das seguintes linhas ao seu `.zshrc`:\n\n```shell\n# append completions to fpath\nfpath=(${ASDF_DIR}/completions $fpath)\n# initialise completions with ZSH's compinit\nautoload -Uz compinit && compinit\n```\n\n- Se você está utilizando uma configuração `compinit` customizada, garanta que `compinit` esteja abaixo chamada `asdf.sh`\n- Se você está utilizando uma configuração `compinit` customizada com um framework para ZSH, garanta que `compinit` esteja abaixo da chamada do framework.\n\n**Aviso**\n\nSe você está utilizando um framework para ZSH, o plugin do asdf pode precisar ser atualizado para utilização adequada do novo auto completar do ZSH através do `fpath`. O plugin do asdf para o oh-my-zsh ainda não foi atualizado, veja: [ohmyzsh/ohmyzsh#8837](https://github.com/ohmyzsh/ohmyzsh/pull/8837).\n:::\n\n::: details ZSH & Homebrew\n\nAdicione `asdf.sh` ao seu `~/.zshrc` através do comando:\n\n```shell\necho -e \"\\n. $(brew --prefix asdf)/asdf.sh\" >> ${ZDOTDIR:-~}/.zshrc\n```\n\n**OU** utilize um framework para ZSH, como [asdf para oh-my-zsh](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/asdf) que irá adicionar o script e o auto completar.\n\nO auto completar pode ser configurado ou pelo framework para ZSH, ou de acordo com as [instruções da Homebrew](https://docs.brew.sh/Shell-Completion#configuring-completions-in-zsh). Se você está usando um framework para ZSH, pode ser que seja necessário atualizar o plugin do asdf para que o novo auto completar funcione adequadamente através do `fpath`. O plugin do asdf para o Oh-My-ZSH ainda será atualizado, veja: [ohmyzsh/ohmyzsh#8837](https://github.com/ohmyzsh/ohmyzsh/pull/8837).\n:::\n\n::: details ZSH & Pacman\n\nAdicione a seguinte linha ao seu `~/.zshrc`:\n\n```shell\n. /opt/asdf-vm/asdf.sh\n```\n\n::: details PowerShell Core & Git\n\nAdicione a seguinte linha ao seu `~/.config/powershell/profile.ps1`:\n\n```shell\n. \"$HOME/.asdf/asdf.ps1\"\n```\n\n:::\n\n::: details PowerShell Core & Homebrew\n\nAdicione `asdf.ps1` ao seu `~/.config/powershell/profile.ps1` através do comando:\n\n```shell\necho -e \"\\n. \\\"$(brew --prefix asdf)/libexec/asdf.ps1\\\"\" >> ~/.config/powershell/profile.ps1\n```\n\n:::\n\n::: details PowerShell Core & Pacman\n\nAdicione a seguinte linha ao seu `~/.config/powershell/profile.ps1`:\n\n```shell\n. /opt/asdf-vm/asdf.ps1\n```\n\n:::\n\n::: details Nushell & Git\n\nAdicione `asdf.nu` ao seu `~/.config/nushell/config.nu` através do comando:\n\n```shell\n\"\\n$env.ASDF_DIR = ($env.HOME | path join '.asdf')\\n source \" + ($env.HOME | path join '.asdf/asdf.nu') | save --append $nu.config-path\n```\n\nAo concluir atualizará automaticamente\n:::\n\n::: details Nushell & Homebrew\n\nAdicione `asdf.nu` ao seu `~/.config/nushell/config.nu` através do comando:\n\n```shell\n\"\\n$env.ASDF_DIR = (brew --prefix asdf | str trim | into string | path join 'libexec')\\n source \" +  (brew --prefix asdf | str trim | into string | path join 'libexec/asdf.nu') | save --append $nu.config-path\n```\n\nAo concluir atualizará automaticamente\n:::\n\n::: details Nushell & Pacman\n\nAdicione `asdf.nu` ao seu `~/.config/nushell/config.nu` através do comando:\n\n```shell\n\"\\n$env.ASDF_DIR = '/opt/asdf-vm/'\\n source /opt/asdf-vm/asdf.nu\" | save --append $nu.config-path\n```\n\nAo concluir atualizará automaticamente\n:::\n\n::: details POSIX Shell & Git\n\nAdicione a seguinte linha ao seu `~/.profile`:\n\n```shell\nexport ASDF_DIR=\"$HOME/.asdf\"\n. \"$HOME/.asdf/asdf.sh\"\n```\n\n:::\n\n::: details POSIX Shell & Homebrew\n\nAdicione `asdf.sh` ao `~/.profile` através do comando:\n\n```shell\necho -e \"\\nexport ASDF_DIR=\\\"$(brew --prefix asdf)/libexec/asdf.sh\\\"\" >> ~/.profile\necho -e \"\\n. \\\"$(brew --prefix asdf)/libexec/asdf.sh\\\"\" >> ~/.profile\n```\n\n:::\n\n::: details POSIX Shell & Pacman\n\nAdicione a seguinte linha ao seu `~/.profile`:\n\n```shell\nexport ASDF_DIR=\"/opt/asdf-vm\"\n. /opt/asdf-vm/asdf.sh\n```\n\n:::\n\nO auto completar é colocado em um local familiar para o ZSH, [mas o ZSH deve ser configurado para conseguir utilizá-lo](https://wiki.archlinux.org/index.php/zsh#Command_completion).\n:::\n\nOs scripts do `asdf` precisam ser chamados **depois** de ter configurado a sua variável `$PATH` e **depois** de ter chamado o seu framework para ZSH (oh-my-zsh etc).\n\nReinicie seu shell para que as mudanças na variável `PATH` tenham efeito. Abrir uma nova janela/sessão de terminal o fará.\n\n## 4. Instalando um plugin\n\nPara demonstração, vamos instalar e configurar o [Node.js](https://nodejs.org/) através do plugin [`asdf-nodejs`](https://github.com/asdf-vm/asdf-nodejs/).\n\n### Dependências dos plugins\n\nCada plugin possui algumas dependências, por isso precisamos checar no repositório onde elas estão listadas. Por exemplo, para o `asdf-nodejs` são:\n\n| SO             | Instalação de dependencia               |\n| -------------- | --------------------------------------- |\n| Linux (Debian) | `apt-get install dirmngr gpg curl gawk` |\n| macOS          | `brew install gpg gawk`                 |\n\nDevemos instalar instalar as dependências primeiro, pois alguns plugins exigem algumas ações após a instalação.\n\n### Instalando o plugin\n\n```shell\nasdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs.git\n```\n\n## 5. Instalando uma versão\n\nAgora temos o plugin para o Node.js, nós podemos instalar uma versão desta ferramenta.\n\nPodemos ver quais versões tão disponíveis através do comando `asdf list all nodejs`, ou uma lista específica de versões com `asdf list all nodejs 14`\n\nVamos instalar somente a última versão disponível, utilizando a tag `latest`:\n\n```shell\nasdf install nodejs latest\n```\n\n::: tip Nota\n`asdf` exige versões exatas. A palavra `latest` resulta na instalação da versão atual na data da execução.\n:::\n\n## 6. Definindo uma versão\n\n`asdf` executa uma verificação das versões das ferramentas a serem utilizadas através do arquivo `.tool-versions` presente desde diretório atual, até o diretório `$HOME`. A varredura ocorre no momento em que você executa uma ferramenta que o asdf gerencia.\n\n::: warning\nSe uma versão não for especificada para uma ferramenta, ao executá-la resultará em erro. `asdf current` mostrará a ferramenta e sua versão, ou então a falta dela no seu diretório atual para que você possa observar quais ferramentas falharão ao serem executadas.\n:::\n\n### Versões globais\n\nOs padrões globais são gerenciados em `$HOME/.tool-versions`. Defina uma versão global através do comando:\n\n```shell\nasdf global nodejs latest\n```\n\n`$HOME/.tool-versions` ficará assim:\n\n```\nnodejs 16.5.0\n```\n\nAlguns sistemas operacionais vêm por padrão com ferramentas que são gerenciadas pelo próprio sistema e não pelo `asdf`, `python` é um exemplo. Você precisa indicar para o `asdf` para devolver o gerenciamento para o sistema. A [seção de referência de versões](/pt-br/manage/versions.md) irá guiá-lo.\n\n### Versões locais\n\nVersões locais são definidas no arquivo `$PWD/.tool-versions` (seu diretório atual). Geralmente, será um repositório Git para um projeto. Quando estiver no diretório desejado, execute:\n\n```shell\nasdf local nodejs latest\n```\n\n`$PWD/.tool-versions` ficará assim:\n\n```\nnodejs 16.5.0\n```\n\n### Usando arquivos de versão existentes\n\n`asdf` suporta a migração de arquivos de versão provenientes de outros gerenciadores de versão. Por exemplo: `.ruby-version` para o `rbenv`. Essa funcionalidade é baseada no plugin de cada ferramenta.\n\nO [`asdf-nodejs`](https://github.com/asdf-vm/asdf-nodejs/) suporta tanto arquivos `.nvmrc` quanto `.node-version`. Para ativar essa funcionalidade, adicione a seguinte linha ao seu arquivo de configuração do `asdf` - `$HOME/.asdfrc`:\n\n```\nlegacy_version_file = yes\n```\n\nVeja a página de refencia da [configuração](/pt-br/manage/configuration.md) para mais opções de configuração.\n\n## Setup finalizado!\n\nA configuração inicial do `asdf` foi finalizada :tada:. Agora, você pode gerenciar versões do `nodejs` para o seus projetos. Siga passos semelhantes para cada ferramenta do seu projeto.\n\nO `asdf` possui diversos outros comandos para se acustomar ainda, você pode ver todos eles através do comando `asdf --help` ou simplesmente `asdf`. Eles estão divididos em três categorias:\n\n- [núcleo `asdf`](/pt-br/manage/core.md)\n- [plugins](/pt-br/manage/plugins.md)\n- [versões (de ferramentas)](/pt-br/manage/versions.md)\n"
  },
  {
    "path": "docs/pt-br/guide/introduction.md",
    "content": "# Introdução\n\n> Hi, we've recently migrated our docs and added some new pages. If you would like to help translate this page, see the \"Edit this page\" link at the bottom of the page.\n\nO `asdf` é um gerenciador de versões. Todas as definições de versão das ferramentas estão contidas no arquivo (`.tool-versions`), o qual você pode compartilhar com o seu time no repositório Git de um projeto e garantir que todos estejam usando **exatamente** a mesma versão das ferramentas.\n\nA maneira antiga de trabalhar necessitava diversos gerenciadores de versão, cada um deles com uma documentação, arquivos de configuração diferentes (manipulação do `$PATH`, shims e variáveis de ambiente, por exemplo). O `asdf` provê um único arquivo de configuração e uma única interface para simplificar o fluxo de desenvolvimento, podendo ser ampliado para todas as ferramentas através de um simples plugin.\n\n## Funcionamento\n\nApós instalar e configurar o `asdf` no seu shell, plugins podem ser instalados para gerenciar determinadas ferramentas. Quando uma ferramenta é instalada por um plugin, os executáveis que foram instalados possuem [shims](<https://en.wikipedia.org/wiki/Shim_(computing)>) criados para cada um deles. Quando você roda algum destes executáveis, o shim é executado, permitindo que o `asdf` identifique qual versão da ferramenta está configurada no arquivo `.tool-versions` e execute esta versão.\n\n## Projetos relacionados\n\n### nvm / n / rbenv etc\n\nFerramentas como o [nvm](https://github.com/nvm-sh/nvm), [n](https://github.com/tj/n) e [rbenv](https://github.com/rbenv/rbenv) são escritas em shell scripts que criam shims para os executáveis instalados para essas ferramentas.\n\nO `asdf` é bem similar e foi criado para competir neste meio de ferramentas de gerenciamento de versão. O grande diferencial do `asdf` é que seu sistema de plugins elimina a necessidade de um gerenciador de versões para cada ferramenta, esta com diferentes comandos e arquivos de configuração.\n\n<!-- ### pyenv\n\nTODO: someone with Python background expand on this\n\n`asdf` has some similarities to `pyenv` but is missing some key features. The `asdf` team is looking at introducing some of these `pyenv` specific features, though no roadmap or timeline is available. -->\n\n### direnv\n\n> Aumenta os shells existentes com a possibilidade de utilizar diferentes variáveis de ambiente com base no diretório atual.\n\nO `asdf` não gerencia variáveis de ambiente, entretanto existe o plugin [`asdf-direnv`](https://github.com/asdf-community/asdf-direnv) para integrar o comportamento do direnv ao asdf.\n\nVeja a [documentação do direnv](https://direnv.net/) para mais detalhes.\n\n### Homebrew\n\n> O gerenciador de pacotes faltante para o macOS (ou Linux)\n\nO Homebrew gerencia seus pacotes e dependências destes pacotes. O `asdf` não gerencia dependencias, não é um gerenciador de pacotes, a escolha do gerenciador de pacotes é reservada ao usuário.\n\nVeja a [documentação do Homebrew](https://brew.sh/) para mais detalhes.\n\n### NixOS\n\n> O Nix é uma ferramenta que relaciona o gerenciamento de pacotes e as configurações de sistema.\n\nO NixOS visa construir ambientes verdadeiramente replicáveis através da gerência das versões exatas dos pacotes e dependências de cada ferramenta, algo que o `asdf` não faz. O NixOS faz isso através da sua própria linguagem de programação, muitas ferramentas da linha de comando e uma coleção de pacotes contendo mais de 60,000 destes.\n\nNovamente, o `asdf` não gerencia dependências/pacotes e não é um gerenciador de pacotes.\n\nVeja a [documentação do NixOS](https://nixos.org/guides/how-nix-works.html) para mais detalhes.\n\n## Por que usar o asdf?\n\nO `asdf` garante que equipes utilizem exatamente a mesma versão de alguma ferramenta, com suporte para **diversas** delas através do sistema de plugins e a _simplicidade e familiaridade_ de ser um único **shell** script que você inclui na configuração do seu shell\n\n::: tip Nota\nO `asdf` não foi feito para ser o gerenciador de pacotes do seu sistema, mas sim uma ferramenta para gerenciar versões de outras ferramentas. Não é por que é possível criar um plugin para qualquer ferramenta/linguagem com o `asdf` que esta sempre será a solução mais adequada.\n:::\n"
  },
  {
    "path": "docs/pt-br/index.md",
    "content": "---\nlayout: home\n\nhero:\n  name: asdf\n  text: O Gerenciador de Múltiplas Versões de Tempo de Execução\n  tagline: Gerencie todas as suas versões de tempo de execução com uma ferramenta!\n  actions:\n    - theme: brand\n      text: Começar\n      link: /pt-br/guide/getting-started\n    - theme: alt\n      text: O que é asdf?\n      link: /pt-br/guide/introduction\n    - theme: alt\n      text: Ver no GitHub\n      link: https://github.com/asdf-vm/asdf\n\nfeatures:\n  - title: Uma super ferramenta\n    details: \"Gerencie cada um dos runtimes e ferramentas dos seus projetos com uma única ferramenta de CLI\"\n    icon: 🎉\n  - title: Plugins\n    details: \"Grande ecossistema de runtimes e ferramentas existentes. API simples para adicionar suporte para novas ferramentas conforme necessário!\"\n    icon: 🔌\n  - title: \"Compatível com vários arquivos de configuração\"\n    details: \"Suporte para arquivos de configuração existentes .nvmrc, .node-version, .ruby-version para uma migração tranquila!\"\n    icon: ⏮\n  - title: \"Só um arquivo de configuração\"\n    details: \".tool-versions para gerenciar todas as suas ferramentas, runtimes e suas versões em um único arquivo\"\n    icon: 📄\n  - title: \"Shells\"\n    details: \"Suporta Bash, ZSH, Fish & Elvish com autocomplete.\"\n    icon: 🐚\n  - title: \"GitHub Actions\"\n    details: \"Fornece um GitHub Action para instalar e utilizar seu .tool-versions em seu fluxo de trabalho CICD.\"\n    icon: 🤖\n---\n"
  },
  {
    "path": "docs/pt-br/manage/commands.md",
    "content": "# All Commands\n\n> Hi, we've recently migrated our docs and added some new pages. If you would like to help translate this page, see the \"Edit this page\" link at the bottom of the page.\n\nA lista de todos os comandos disponíveis em `asdf`. Esta lista é o texto do comando `asdf help`.\n\n<<< @../../internal/help/help.txt\n"
  },
  {
    "path": "docs/pt-br/manage/configuration.md",
    "content": "# Configuration\n\n> Hi, we've recently migrated our docs and added some new pages. If you would like to help translate this page, see the \"Edit this page\" link at the bottom of the page.\n\nA configuração do `asdf` abrange tanto os arquivos `.tool-versions` compartilháveis quanto as personalizações específicas do usuário com `.asdfrc` e variáveis de ambiente.\n\n## .tool-versions\n\nSempre que o arquivo `.tool-versions` estiver presente em um diretório, as versões da ferramenta que ele declara serão usadas nesse diretório e em seus subdiretórios.\n\nConfigurações globais podem ser modificadas no arquivo `$HOME/.tool-versions`\n\nO arquivo `.tool-versions` se parece assim:\n\n```\nruby 2.5.3\nnodejs 10.15.0\n```\n\nAs versões podem estar no seguinte formato:\n\n- `10.15.0` - uma versão real. Os plugins que suportam o download de binários farão o download de binários.\n- `ref:v1.0.2-a` ou `ref:39cb398vb39` - _tag/commit/branch_ para download pelo github e compilação\n  um path costumizado e compi\n- `path:~/src/elixir` - um path para uma versão compilada e personalizada de uma ferramenta pronta para usar. Para uso por linguagens de desenvolvimento e outros.\n- `system` - faz com que asdf passe para a versão da ferramenta no sistema que não é gerenciada por asdf .\n\nVárias versões podem ser definidas, separando-as com um espaço. Por exemplo, para usar Python 3.7.2, e também Python 2.7.15, use a linha abaixo em seu arquivo `.tool-versions`.\n\n```\npython 3.7.2 2.7.15 system\n```\n\nPara instalar todas as ferramentas definidas em `.tool-versions`, execute o camando `asdf install` sem argumentos no mesmo diretório de `.tool-versions`.\n\nPara isntalar somente uma ferramenta definida em `.tool-versions`, execute o camando `asdf install` sem argumentos no mesmo diretório de `.tool-versions`. A ferramenta será instalada na versão especificada no arquivo `.tool-versions`.\n\nEdite o arquivo diretamente no diretório ou use `asdf local` (ou `asdf global`) para atualiza-lo.\n\n## `$HOME/.asdfrc`\n\nAdicione um arquivo `.asdfrc` ao seu diretório home e asdf usará as configurações especificadas no arquivo. O arquivo deve ser formatado assim:\n\n```\nlegacy_version_file = yes\n```\n\n**Configurações**\n\n- `legacy_version_file` - por padrão é `no`. Se definido como `yes`, fará com que os plug-ins que suportam esse recurso leiam os arquivos de versão usados por outros gerenciadores de versão (por exemplo, `.ruby-version` no caso do `rbenv` do Ruby).\n\n- `always_keep_download` - por padrão é `no`. Se definido como `yes`, fará com que o `asdf install` sempre mantenha o código-fonte ou binário baixado. Se definido como `no`, o código fonte ou binário baixado por `asdf install` será excluído após a instalação bem sucedida.\n\n- `plugin_repository_last_check_duration` - por padrão é `60` min (1 hrs). Ele define a duração da última verificação do repositório de plugins asdf. Quando o comando `asdf plugin add <nome>`, `asdf plugin list all` for executado, ele verificará a duração da última atualização para atualizar o repositório. Se definido como `0`, ele atualizará o repositório de plugins asdf todas as vezes.\n\n## Variáveis de ambiente\n\n- `ASDF_CONFIG_FILE` - O padrão é `~ /.asdfrc` conforme descrito acima. Pode ser definido para qualquer local.\n- `ASDF_TOOL_VERSIONS_FILENAME` - O nome do arquivo que armazena os nomes e versões das ferramentas. O padrão é `.tool-versions`. Pode ser qualquer nome de arquivo válido. Normalmente você não deve substituir o valor padrão, a menos que deseja que o asdf ignore os arquivos `.tool-versions`.\n- `ASDF_DIR` - O padrão é `~/.asdf` - Localização dos arquivos `asdf`. Se você instalar `asdf` em algum outro diretório, defina-o para esse diretório. Por exemplo, se você estiver instalando através do AUR, você deve definir isso para `/opt/asdf-vm`.\n- `ASDF_DATA_DIR` - O padrão é `~/.asdf` - Local onde `asdf` instala plugins, correções e instalações. Pode ser definido para qualquer local antes de fornecer `asdf.sh` ou `asdf.fish` mencionado na seção acima. Para Elvish, isso pode ser definido acima de `use asdf`.\n"
  },
  {
    "path": "docs/pt-br/manage/core.md",
    "content": "# Core\n\n> Hi, we've recently migrated our docs and added some new pages. If you would like to help translate this page, see the \"Edit this page\" link at the bottom of the page.\n\nA lista de comandos do núcleo `asdf` é bastante pequena, mas pode facilitar muitos fluxos de trabalho.\n\n## Instalação e configuração\n\nBaseado no [Guia de Introdução](/pt-br/guide/getting-started.md).\n\n## Execute\n\n```shell\nasdf exec <command> [args...]\n```\n\nExecuta o comando shim para a versão atual\n\n<!-- TODO: expand on this with example -->\n\n## Variável de Ambiente\n\n```shell\nasdf env <command> [util]\n```\n\n<!-- TODO: expand on this with example -->\n\n## Informações\n\n```shell\nasdf info\n```\n\n Um comando auxiliar para imprimir as informações de depuração do SO, Shell e `asdf`. Compartilhe isso ao fazer um relatório de bug.\n\n## Reshim\n\n```shell\nasdf reshim <name> <version>\n```\n\nIsso recria os shims para a versão atual de um pacote. Por padrão, os calços são criados por plugins durante a instalação de uma ferramenta. Algumas ferramentas como a [npm CLI](https://docs.npmjs.com/cli/) permitem a instalação global de executáveis, por exemplo, instalando [Yarn](https://yarnpkg.com/) via `npm install -g fio`.  Como este executável não foi instalado por meio do ciclo de vida do plug-in, ainda não existe shim para ele. `asdf reshim nodejs <version>` forçará o recálculo de shims para quaisquer novos executáveis, como `yarn`, para `<version>` de `nodejs`.\n\n## Versionamento do Shim\n\n```shell\nasdf shimversions <command>\n```\n\nLista os plugins e versões que fornecem shims para um comando.\n\nComo exemplo, o [Node.js](https://nodejs.org/) vem com dois executáveis, `node` e `npm`. Quando muitas versões das ferramentas são instaladas com [`asdf-nodejs`](https://github.com/asdf-vm/asdf-nodejs/) `shimversions` pode retornar:\n\n```shell\n➜ asdf shimversions node\nnodejs 14.8.0\nnodejs 14.17.3\nnodejs 16.5.0\n```\n\n```shell\n➜ asdf shimversions npm\nnodejs 14.8.0\nnodejs 14.17.3\nnodejs 16.5.0\n```\n\n## Atualizar\n\nPor favor, use o mesmo método que você usou para instalar o asdf para atualizá-lo. A versão mais recente do asdf é mostrada no canto superior direito desta página.\n\n## Desinstalar\n\nPara desinstalar `asdf` siga os passos:\n\n::: details Bash & Git\n\n1. Em seu `~/.bashrc` remova as linhas do `asdf.sh` e seus complementos:\n\n```shell\n. \"$HOME/.asdf/asdf.sh\"\n. \"$HOME/.asdf/completions/asdf.bash\"\n```\n\n2. Remova o diretório `$HOME/.asdf`:\n\n```shell\nrm -rf \"${ASDF_DATA_DIR:-$HOME/.asdf}\"\n```\n\n3. Execute o comando para remover todos os arquivos de configurações do `asdf`:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Bash & Git (macOS)\n\n1. Em seu `~/.bash_profile` remova as linhas do `asdf.sh` e remova seus complementos:\n\n```shell\n. \"$HOME/.asdf/asdf.sh\"\n. \"$HOME/.asdf/completions/asdf.bash\"\n```\n\n2. Remova o diretório `$HOME/.asdf`:\n\n```shell\nrm -rf \"${ASDF_DATA_DIR:-$HOME/.asdf}\"\n```\n\n3. Execute o comando para remover todos os arquivos de configurações do `asdf`:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Bash & Homebrew\n\n1. Em seu `~/.bashrc` remova as linhas do `asdf.sh` e remova seus complementos:\n\n```shell\n. $(brew --prefix asdf)/libexec/asdf.sh\n. $(brew --prefix asdf)/etc/bash_completion.d/asdf.bash\n```\n\n?> Os complementos precisam [instruções de configuração do Homebrew](https://docs.brew.sh/Shell-Completion#configuring-completions-in-bash) e siga o guia de remoção.\n\n2. Desinstale usando seu gerenciador de pacotes:\n\n```shell\nbrew uninstall asdf --force\n```\n\n3. Execute o comando para remover todos os arquivos de configurações do `asdf`:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Bash & Homebrew (macOS)\n\nCaso esteja usando **macOs Catalina ou mais recente**, por padrão o _shell_ é **ZSH**. Se não achar alguma configuração em seu `~/.bash_profile` talvez esteja em `~/.zshrc` em cada caso siga as intruções do ZSH.\n\n1. Em seu `~/.bash_profile` remova as linhas do `asdf.sh` e remova seus complementos:\n\n```shell\n. $(brew --prefix asdf)/libexec/asdf.sh\n. $(brew --prefix asdf)/etc/bash_completion.d/asdf.bash\n```\n\n?> Os complementos precisam [instruções de configuração do Homebrew](https://docs.brew.sh/Shell-Completion#configuring-completions-in-bash) e siga o guia de remoção.\n\n2. Desinstale usando seu gerenciador de pacotes:\n\n```shell\nbrew uninstall asdf --force\n```\n\n3. Execute o comando para remover todos os arquivos de configurações do `asdf`:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Bash & Pacman\n\n1. Em seu `~/.bashrc` remova as linhas do `asdf.sh` e seus complementos:\n\n```shell\n. /opt/asdf-vm/asdf.sh\n```\n\n2. Desinstale usando seu gerenciador de pacotes:\n\n```shell\npacman -Rs asdf-vm\n```\n\n3. Remova o diretório `$HOME/.asdf`:\n\n```shell\nrm -rf \"${ASDF_DATA_DIR:-$HOME/.asdf}\"\n```\n\n4. Execute o comando para remover todos os arquivos de configurações do `asdf`:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Fish & Git\n\n1. Em seu `~/.config/fish/config.fish` remova as linhas do `asdf.sh`:\n\n```shell\nsource ~/.asdf/asdf.fish\n```\n\ne remova os complementos de com esse comando:\n\n```shell\nrm -rf ~/.config/fish/completions/asdf.fish\n```\n\n2. Remova o diretório `$HOME/.asdf`:\n\n```shell\nrm -rf (string join : -- $ASDF_DATA_DIR $HOME/.asdf)\n```\n\n3. Execute o comando para remover todos os arquivos de configurações do `asdf`:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Fish & Homebrew\n\n1. Em seu `~/.config/fish/config.fish` remova as linhas do `asdf.fish`:\n\n```shell\nsource \"(brew --prefix asdf)\"/libexec/asdf.fish\n```\n\n2. Desinstale usando seu gerenciador de pacotes:\n\n```shell\nbrew uninstall asdf --force\n```\n\n3. Execute o comando para remover todos os arquivos de configurações do `asdf`:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Fish & Pacman\n\n1. Em seu `~/.config/fish/config.fish` remova as linhas do `asdf.fish`:\n\n```shell\nsource /opt/asdf-vm/asdf.fish\n```\n\n2. Desinstale usando seu gerenciador de pacotes:\n\n```shell\npacman -Rs asdf-vm\n```\n\n3. Remova o diretório `$HOME/.asdf`:\n\n```shell\nrm -rf (string join : -- $ASDF_DATA_DIR $HOME/.asdf)\n```\n\n4. Execute o comando para remover todos os arquivos de configurações do `asdf`:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Elvish & Git\n\n1. Em seu `~/.config/elvish/rc.elv` remova as linhas que importa o módulo `asdf`:\n\n```shell\nuse asdf _asdf; var asdf~ = $_asdf:asdf~\nset edit:completion:arg-completer[asdf] = $_asdf:arg-completer~\n```\n\ne desinstale o módulo `asdf` com este comando:\n\n```shell\nrm -f ~/.config/elvish/lib/asdf.elv\n```\n\n2. Remova o diretório `$HOME/.asdf`:\n\n```shell\nif (!=s $E:ASDF_DATA_DIR \"\") { rm -rf $E:ASDF_DATA_DIR } else { rm -rf ~/.asdf }\n```\n\n3. Execute este comando para remover todos os arquivos de configuração `asdf`:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Elvish & Homebrew\n\n1. Em seu `~/.config/elvish/rc.elv` remova as linhas que importa o módulo `asdf`:\n\n```shell\nuse asdf _asdf; var asdf~ = $_asdf:asdf~\nset edit:completion:arg-completer[asdf] = $_asdf:arg-completer~\n```\n\ne desinstale o módulo `asdf` com este comando:\n\n```shell\nrm -f ~/.config/elvish/lib/asdf.elv\n```\n\n2. Desinstale com seu gerenciador de pacotes:\n\n```shell\nbrew uninstall asdf --force\n```\n\n3. Execute este comando para remover todos os arquivos de configuração `asdf`:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Elvish & Pacman\n\n1. Em seu `~/.config/elvish/rc.elv` remova as linhas que importa o módulo `asdf`:\n\n```shell\nuse asdf _asdf; var asdf~ = $_asdf:asdf~\nset edit:completion:arg-completer[asdf] = $_asdf:arg-completer~\n```\n\ne desinstale o módulo `asdf` com este comando:\n\n```shell\nrm -f ~/.config/elvish/lib/asdf.elv\n```\n\n2. Desinstale com seu gerenciador de pacotes:\n\n```shell\npacman -Rs asdf-vm\n```\n\n3. Remova o diretório `$ HOME/.asdf`:\n\n```shell\nif (!=s $E:ASDF_DATA_DIR \"\") { rm -rf $E:ASDF_DATA_DIR } else { rm -rf ~/.asdf }\n```\n\n4. Execute este comando para remover todos os arquivos de configuração `asdf`:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details ZSH & Git\n\n1. Em seu `~/.zshrc` remova as linhas do `asdf.sh` e seus complementos:\n\n```shell\n. \"$HOME/.asdf/asdf.sh\"\n# ...\nfpath=(${ASDF_DIR}/completions $fpath)\nautoload -Uz compinit\ncompinit\n```\n\n**Ou** use ZSH Framework plugin.\n\n2. Remova o diretório `$HOME/.asdf`:\n\n```shell\nrm -rf \"${ASDF_DATA_DIR:-$HOME/.asdf}\"\n```\n\n3. Execute o comando para remover todos os arquivos de configurações do `asdf`:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details ZSH & Homebrew\n\n1. Em seu `~/.zshrc` remova as linhas do `asdf.sh`:\n\n```shell\n. $(brew --prefix asdf)/libexec/asdf.sh\n```\n\n2. Desinstale usando seu gerenciador de pacotes:\n\n```shell\nbrew uninstall asdf --force\n```\n\n3. Execute o comando para remover todos os arquivos de configurações do `asdf`:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details ZSH & Pacman\n\n1. Em seu `~/.zshrc` remova as linhas do `asdf.sh`:\n\n```shell\n. /opt/asdf-vm/asdf.sh\n```\n\n2. Desinstale usando seu gerenciador de pacotes:\n\n```shell\npacman -Rs asdf-vm\n```\n\n3. Remova o diretório `$HOME/.asdf`:\n\n```shell\nrm -rf \"${ASDF_DATA_DIR:-$HOME/.asdf}\"\n```\n\n4. Execute o comando para remover todos os arquivos de configurações do `asdf`:\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\nTudo pronto! 🎉\n"
  },
  {
    "path": "docs/pt-br/manage/plugins.md",
    "content": "# Plugins\n\n> Hi, we've recently migrated our docs and added some new pages. If you would like to help translate this page, see the \"Edit this page\" link at the bottom of the page.\n\nPlugins são como `asdf` sabe lidar com diferentes ferramentas, tais quais Node.js, Ruby, Elixir etc.\n\nVeja [Criando Plugins](/pt-br/plugins/create.md) para a API do plugin usada para suportar mais ferramentas.\n\n## Adicionar\n\nAdicione os plugins via sua Url Git:\n\n```shell\nasdf plugin add <name> <git-url>\n# asdf plugin add elm https://github.com/vic/asdf-elm\n```\n\nou pelo nome abreviado dentro do repositório de plugins:\n\n```shell\nasdf plugin add <name>\n# asdf plugin add erlang\n```\n\n::: tip Recommendation\nPrefira o método mais longo `git-url`, pois ele é independente do repositório de nome abreviado.\n:::\n\n## Listar Instalados\n\n```shell\nasdf plugin list\n# asdf plugin list\n# java\n# nodejs\n```\n\n```shell\nasdf plugin list --urls\n# asdf plugin list\n# java            https://github.com/halcyon/asdf-java.git\n# nodejs          https://github.com/asdf-vm/asdf-nodejs.git\n```\n\n## Listar todos nomes abreviados no repositório\n\n```shell\nasdf plugin list all\n```\n\nVeja [Plugins Shortname Index](https://github.com/asdf-vm/asdf-plugin-template) para toda a lista de nomes curtos de plugins.\n\n## Atualizar\n\n```shell\nasdf plugin update --all\n```\n\nSe você quiser atualizar um pacote específico, apenas use.\n\n```shell\nasdf plugin update <name>\n# asdf plugin update erlang\n```\n\nEsta atualização irá buscar o último _commit_ na _branch_ padrão no _origin_ de seu respositório. Plugins e atualizações das versões estão sendo desenvolvidas ([#916](https://github.com/asdf-vm/asdf/pull/916))\n\n## Remover\n\n```bash\nasdf plugin remove <name>\n# asdf plugin remove erlang\n```\n\nRemovendo o plugin irá remover todas as instalações feitas com o plugin. Isso pode ser usado como um atalho para apagar/remover sujeiras de versões não utilizadas de uma ferramenta.\n\n## Sincronizar nome abreviado no repositório\n\nO nome abreviado do repositório é sincronizado em seu máquina local e periodicamente atualizado. Esse período pode ser determinado com o seguinte método:\n\n- comandos `asdf plugin add <name>` ou `asdf plugin list all` disparam a sincronização\n- ocorre uma sincronização se não houver nenhuma nos últimos `X` minutos\n- `X` por padrão é `60`, mas pode ser mudado em `.asdfrc` via as opções do `plugin_repository_last_check_duration`. Seja mais em [asdf documentação de configuração](/pt-br/manage/configuration.md).\n"
  },
  {
    "path": "docs/pt-br/manage/versions.md",
    "content": "# Versões\n\n> Hi, we've recently migrated our docs and added some new pages. If you would like to help translate this page, see the \"Edit this page\" link at the bottom of the page.\n\n## Instalar Versão\n\n```shell\nasdf install <name> <version>\n# asdf install erlang 17.3\n```\n\nSe um plugin suporta o download e compilação do código-fonte, você pode especificar `ref:foo` no qual `foo` é uma 'branch' especifica, 'tag', ou 'commit'. Você também precisará usar o mesmo nome e referência ao desinstalar.\n\n## Instalar última versão estável\n\n```shell\nasdf install <name> latest\n# asdf install erlang latest\n```\n\nInstale a última versão estável que inicia com um texto.\n\n```shell\nasdf install <name> latest:<version>\n# asdf install erlang latest:17\n```\n\n## Listar versões instaladas\n\n```shell\nasdf list <name>\n# asdf list erlang\n```\n\nLimite as versões que inicie com um determinado texto.\n\n```shell\nasdf list <name> <version>\n# asdf list erlang 17\n```\n\n## Listar todas as versões disponíveis\n\n```shell\nasdf list all <name>\n# asdf list all erlang\n```\n\nLimite as versões que inicie com um determinado texto.\n\n```shell\nasdf list all <name> <version>\n# asdf list all erlang 17\n```\n\n## Mostrar última versão estável\n\n```shell\nasdf latest <name>\n# asdf latest erlang\n```\n\nMostrar última versão estável que inicie com um determinado texto.\n\n```shell\nasdf latest <name> <version>\n# asdf latest erlang 17\n```\n\n## Selecionar versão atual\n\n```shell\nasdf global <name> <version> [<version>...]\nasdf shell <name> <version> [<version>...]\nasdf local <name> <version> [<version>...]\n# asdf global elixir 1.2.4\n\nasdf global <name> latest[:<version>]\nasdf local <name> latest[:<version>]\n# asdf global elixir latest\n```\n\n`global` escreve a versão para `$HOME/.tool-versions`.\n\n`shell` selecione a versão na variável de ambiente `ASDF_${LANG}_VERSION`, para a atual seção do _shell_.\n\n`local` escreve a versão para `$PWD/.tool-versions`, crie se necessário .\n\nVeja em `.tool-versions` [arquivo de seleção de configuração](/pt-br/manage/configuration) para mais detalhes.\n\n::: warning Alternativa\nSe você quiser selecionar a versão atual do seu _shell_ ou para executar um comando em uma versão específica de sua ferramenta, você pode selecionar a versão na variável de ambiente `ASDF_${TOOL}_VERSION`.\n:::\n\nO seguinte exemplo executa os testes em um projeto Elixir na versão `1.4.0`.\nO formato da versão é o mesmo suportado pelo arquivo `.tool-versions`.\n\n```shell\nASDF_ELIXIR_VERSION=1.4.0 mix test\n```\n\n## Resposta do sistema de versão\n\nPara usar o sistema de versão da ferramenta `<name>` inicie um gerenciador de versões do asdf para selecionar a versão na ferramenta do `system`.\n\nSelecione o sistema com `global`, `local` ou `shell`\nSet system with either `global`, `local` or `shell` conforme descrito em [Selecionar versão atual](#selecionar-versão-atual).\n\n```shell\nasdf local <name> system\n# asdf local python system\n```\n\n## Verificar a versão atual\n\n```shell\nasdf current\n# asdf current\n# erlang 17.3 (set by /Users/kim/.tool-versions)\n# nodejs 6.11.5 (set by /Users/kim/cool-node-project/.tool-versions)\n\nasdf current <name>\n# asdf current erlang\n# 17.3 (set by /Users/kim/.tool-versions)\n```\n\n## Desinstalar versão\n\n```shell\nasdf uninstall <name> <version>\n# asdf uninstall erlang 17.3\n```\n\n## Shims\n\nQuando asdf instala um pacote é criado _shims_ para cada programa executado no pacote do diretório `$ASDF_DATA_DIR/shims` (padrão `~/.asdf/shims`). Esse diretório começa no `$PATH` (pelos `asdf.sh`, `asdf.fish`, etc) é como o programa instalado é disponibilizado no ambiente do sistema.\n\nOs _shims_ em si são atalhos simples que executam um programa auxiliar `asdf exec` passando o nome do plugin e o caminho para o executável no pacote instalado que o _shim_ está contido.\n\nO `asdf exec` ajuda a determinar a versão do pacote usado (como especificado no arquivo `.tool-versions`, pelo `asdf local ...` ou `asdf global ...`), o final do _path_ do executavél no pacote instalado no diretório (pode ser manipulado pelo `exec-path` no _callback_ do plugin) e o ambiente executado em (também fornecido pelo plugin - `exec-env`) e finalmente executado.\n\n::: warning Observação\nObserve que, como este sistema usa chamadas `exec`, qualquer _scripts_ no pacote devem ser fornecidos pelo _shell_, a instancia em execução precisa ser aessado diretamente ao invés do _shim_. Os dois comandos do asdf: `which` e `where` pode ajudar com o retorno do caminho para o pacote instalado:\n:::\n\n```shell\n# retorna o 'path' da versão atual em execução\nsource $(asdf which ${PLUGIN})/../script.sh\n\n# retorna o 'path' do pacote instalado no diretório\nsource $(asdf where ${PLUGIN} $(asdf current ${PLUGIN}))/bin/script.sh\n```\n\n### Ignorando _shims_ do asdf\n\nSe por algum motivo você deseja ignorar _shims_ do asdf ou deseja que suas variáveis de ambiente sejam definidas automaticamente ao entrar no diretório do seu projeto, pode ser útil o [asdf-direnv](https://github.com/asdf-community/asdf-direnv). Verifique o README para mais detalhes.\n"
  },
  {
    "path": "docs/pt-br/more/community-projects.md",
    "content": "# Community Projects\n\nHere are some community projects related to `asdf`:\n\n- [asdf-community](https://github.com/asdf-community): A collaborative,\n  community-driven project for long-term maintenance of asdf plugins.\n- [asdf dev container](https://github.com/iloveitaly/asdf-devcontainer): A\n  [GitHub Dev Container](https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/introduction-to-dev-containers)\n  supporting asdf managed tools in GitHub Codespaces.\n\n::: warning Note\n\nasdf core do not own these projects or their code. asdf core are not responsible\nfor the quality or security as they relate to those listed here.\n\n:::\n"
  },
  {
    "path": "docs/pt-br/more/faq.md",
    "content": "# Perguntas frequentes\n\nAqui estão algumas perguntas comuns sobre `asdf`.\n\n## Suporte WSL1?\n\nWSL1 ([Windows Subsystem for Linux](https://en.wikipedia.org/wiki/Windows_Subsystem_for_Linux) 1) não é oficialmente suportado. Alguns aspectos do `asdf` podem não funcionar corretamente. Não temos a intenção de adicionar suporte oficial para WSL1.\n\n## Suporte WSL2?\n\nWSL2 ([Subsistema Windows para Linux](https://en.wikipedia.org/wiki/Windows_Subsystem_for_Linux#WSL_2) 2) deve funcionar usando as instruções de configuração e dependência para a distribuição WSL escolhida.\n\nÉ importante ressaltar que o WSL2 _apenas_ deve funcionar corretamente quando o diretório de trabalho atual é uma unidade Unix e não uma unidade Windows vinculada.\n\nPretendemos executar o conjunto de testes no WSL2 quando o suporte ao host runner estiver disponível nas Ações do GitHub; atualmente, esse não parece ser o caso.\n\n## Exectable recém-instalado não está funcionando?\n\n> Acabei de instalar o `npm -g yarn`, mas não consigo executar o `yarn`. O que da?\n\n`asdf` usa [shims](<https://en.wikipedia.org/wiki/Shim_(computing)>) para gerenciar executáveis. Aqueles instalados por plug-ins têm shims criados automaticamente, enquanto a instalação de executáveis ​​por meio de uma ferramenta gerenciada `asdf` exigirá que você notifique o`asdf` sobre a necessidade de criar shims. Neste caso, para criar um shim para [Yarn](https://yarnpkg.com/). Veja a documentação do comando [`asdf reshim`](/ manage / core.md # reshim).\n\n## Shell não detecta shims recém-instalados?\n\nSe `asdf reshim` não está resolvendo seu problema, então é mais provável devido ao sourcing de`asdf.sh` ou `asdf.fish` _não_ estar no ** BOTTOM ** de seu arquivo de configuração Shell (`.bash_profile`, `.zshrc`, `config.fish`, etc). Ele precisa ser fornecido **DEPOIS** de você definir seu `$PATH` e **DEPOIS** de ter fornecido seu framework (oh-meu-zsh etc), se houver.\n"
  },
  {
    "path": "docs/pt-br/more/thanks.md",
    "content": "# Créditos\n\nEu ([@HashNuke](https://github.com/HashNuke)), febre alta, resfriado e tosse.\n\nCopyright 2014 até o final dos tempos ([MIT License](https://github.com/asdf-vm/asdf/blob/master/LICENSE))\n\n## Mantenedores\n\n- [@HashNuke](https://github.com/HashNuke)\n- [@danhper](https://github.com/danhper)\n- [@Stratus3D](https://github.com/Stratus3D)\n- [@vic](https://github.com/vic)\n- [@jthegedus](https://github.com/jthegedus)\n\n## Contribuidores\n\nVeja em [lista de contribuidores](https://github.com/asdf-vm/asdf/graphs/contributors) :pray: no GitHub\n"
  },
  {
    "path": "docs/pt-br/plugins/create.md",
    "content": "# Criar um plug-in\n\n> Hi, we've recently migrated our docs and added some new pages. If you would like to help translate this page, see the \"Edit this page\" link at the bottom of the page.\n\n## O que há em um plug-in\n\nUm plugin é um repositório git, com alguns scripts executáveis, para dar suporte ao versionamento de outra linguagem ou ferramenta.  Esses scripts são executados quando os comandos `list-all`, `install` ou `uninstall` são executados.  Você pode definir ou desmarcar env vars e fazer qualquer coisa necessária para configurar o ambiente para a ferramenta.\n\n## Scripts obrigatórios\n\n\n- `bin/list-all` - lista todas as versões instaláveis\n- `bin/download` - baixe o código fonte ou binário para a versão especificada\n- `bin/install` - instala a versão especificada\n\n## Variavéis de Ambiente\n\nTodos os scripts, exceto `bin/list-all`, terão acesso aos seguintes env vars para agir:\n\n- `ASDF_INSTALL_TYPE` - `version` ou `ref`\n- `ASDF_INSTALL_VERSION` - se `ASDF_INSTALL_TYPE` é `version` então este será o número da versão. Caso contrário, será o git ref que será passado. Pode apontar para uma tag/commit/branch no repositório.\n- `ASDF_INSTALL_PATH` - o diretório onde _foi_ instalado (ou _deve_ ser instalado no caso do script `bin/install`)\n\nEssas variáveis de ambiente adicionais estarão disponíveis para o script `bin/install`:\n\n- `ASDF_CONCURRENCY` - o número de núcleos a serem usados ao compilar o código-fonte. Útil para definir `make -j`.\n- `ASDF_DOWNLOAD_PATH` - o caminho para onde o código fonte ou binário foi baixado pelo script `bin/download`.\n\nEssas variáveis de ambiente adicionais estarão disponíveis para o script `bin/download`:\n\n- `ASDF_DOWNLOAD_PATH` - o caminho para onde o código-fonte ou binário deve ser baixado.\n\n#### bin/list-all\n\nDeve imprimir uma string com uma lista de versões separadas por espaço. A saída de exemplo seria a seguinte:\n\n```shell\n1.0.1 1.0.2 1.3.0 1.4\n```\n\nObserve que a versão mais recente deve ser listada por último para que apareça mais próxima do prompt do usuário. Isso é útil já que o comando `list-all` imprime cada versão em sua própria linha. Se houver muitas versões, é possível que as primeiras versões fiquem fora da tela.\n\nSe as versões estiverem sendo extraídas da página de lançamentos em um site, é recomendável não classificar as versões, se possível. Muitas vezes as versões já estão na ordem correta ou, na ordem inversa, nesse caso algo como `tac` deve ser suficiente. Se você precisar classificar as versões manualmente, não poderá confiar em `sort -V`, pois não é suportado no OSX. Uma função de classificação alternativa [como esta é uma escolha melhor](https://github.com/vic/asdf-idris/blob/master/bin/list-all#L6).\n\n#### bin/download\n\nEste script deve baixar o código fonte ou binário, no caminho contido na variável de ambiente `ASDF_DOWNLOAD_PATH`. Se o código-fonte ou binário baixado estiver compactado, apenas o código-fonte ou binário descompactado poderá ser colocado no diretório `ASDF_DOWNLOAD_PATH`.\n\nO script deve sair com um status de `0` quando o download for bem-sucedido. Se o download falhar, o script deve sair com qualquer status de saída diferente de zero.\n\nSe possível, o script deve apenas colocar arquivos no `ASDF_DOWNLOAD_PATH`.  Se o download falhar, nenhum arquivo deve ser colocado no diretório.\n\nSe este script não estiver presente, o asdf assumirá que o script `bin/install` está presente e fará o download e instalará a versão. asdf só funciona sem este script para suportar plugins legados. Todos os plugins devem incluir este script e, eventualmente, o suporte para plugins legados será removido.\n\n#### bin/install\n\nEste script deve instalar a versão, no caminho mencionado em `ASDF_INSTALL_PATH`. Por padrão, o asdf criará shims para qualquer arquivo em `$ASDF_INSTALL_PATH/bin` (isso pode ser personalizado com o script opcional [bin/list-bin-paths](#binlist-bin-paths)).\n\nO script de instalação deve sair com um status de `0` quando a instalação for bem-sucedida.  Se a instalação falhar, o script deve sair com qualquer status de saída diferente de zero.\n\nSe possível, o script deve apenas colocar os arquivos no diretório `ASDF_INSTALL_PATH` uma vez que a compilação e instalação da ferramenta são consideradas bem sucedidas pelo script de instalação. asdf [verifica a existência](https://github.com/asdf-vm/asdf/blob/242d132afbf710fe3c7ec23c68cec7bdd2c78ab5/lib/utils.sh#L44) do diretório `ASDF_INSTALL_PATH` para determinar se essa versão da ferramenta está instalado. Se o diretório `ASDF_INSTALL_PATH` for preenchido no início do processo de instalação, outros comandos asdf executados em outros terminais durante a instalação podem considerar essa versão da ferramenta instalada, mesmo quando não estiver totalmente instalada.\n\nSe você quiser que seu plugin funcione com asdf versão 0.7._ e anterior e versão 0.8._ e mais recente, verifique a presença da variável de ambiente `ASDF_DOWNLOAD_PATH`.  Se não estiver definido, baixe o código-fonte no retorno de chamada bin/install.  Se estiver definido, suponha que o script `bin/download` já tenha baixado.\n\n## Scripts Opcional\n\n#### scripts bin/help\n\nEste não é um script de retorno de chamada, mas sim um conjunto de scripts de retorno de chamada que imprimem documentação diferente para STDOUT. Os scripts de retorno de chamada possíveis estão listados abaixo. Observe que `bin/help.overview` é um caso especial, pois deve estar presente para que qualquer saída de ajuda seja exibida para o script.\n\n- `bin/help.overview` - Este script deve gerar uma descrição geral sobre o plugin e a ferramenta que está sendo gerenciada. Nenhum título deve ser impresso, pois o asdf imprimirá títulos. A saída pode ser um texto de formato livre, mas idealmente apenas um parágrafo curto. Este script deve estar presente se você quiser que o asdf forneça informações de ajuda para seu plugin. Todos os outros scripts de retorno de chamada de ajuda são opcionais.\n- `bin/help.deps` - Esse script deve gerar a lista de dependências adaptadas ao sistema operacional. Uma dependência por linha.\n- `bin/help.config` - Este script deve imprimir qualquer configuração obrigatória ou opcional que possa estar disponível para o plug-in e a ferramenta. Quaisquer variáveis de ambiente ou outros sinalizadores necessários para instalar ou compilar a ferramenta (para o sistema operacional dos usuários quando possível). A saída pode ser texto de formato livre.\n- `bin/help.links` - Esta deve ser uma lista de links relevantes para o plug-in e a ferramenta (mais uma vez, adaptados ao sistema operacional atual, quando possível). Um link por linha. As linhas podem estar no formato `<title>: <link>` ou apenas `<link>`.\n\nCada um desses scripts deve adaptar sua saída ao sistema operacional atual. Por exemplo, quando no Ubuntu, o script deps pode gerar as dependências como pacotes apt-get que devem ser instalados. O script também deve adaptar sua saída ao valor de `ASDF_INSTALL_VERSION` e `ASDF_INSTALL_TYPE` quando as variáveis forem definidas.  Eles são opcionais e nem sempre serão definidos.\n\nO script de retorno de chamada de ajuda NÃO DEVE gerar nenhuma informação que já esteja coberta na documentação principal do asdf-vm. As informações gerais de uso do asdf não devem estar presentes.\n\n#### bin/list-bin-paths\n\nListe os executáveis para a versão especificada da ferramenta. Deve imprimir uma string com uma lista separada por espaços de caminhos de diretórios que contêm executáveis. Os caminhos devem ser relativos ao caminho de instalação passado. A saída de exemplo seria:\n\n```shell\nbin tools veggies\n```\n\nIsso instruirá o asdf a criar shims para os arquivos em `<install-path>/bin`, `<install-path>/tools` e `<install-path>/veggies`\n\nSe este script não for especificado, o asdf procurará o diretório `bin` em uma instalação e criará shims para eles.\n\n#### bin/exec-env\n\nConfigure o env para executar os binários no pacote.\n\n#### bin/exec-path\n\nObtenha o caminho executável para a versão especificada da ferramenta. Deve imprimir uma string com o caminho executável relativo. Isso permite que o plug-in substitua condicionalmente o caminho executável especificado do shim, caso contrário, retorne o caminho padrão especificado pelo shim.\n\n```shell\nUsage:\n  plugin/bin/exec-path <install-path> <command> <executable-path>\n\nExample Call:\n  ~/.asdf/plugins/foo/bin/exec-path \"~/.asdf/installs/foo/1.0\" \"foo\" \"bin/foo\"\n\nOutput:\n  bin/foox\n```\n\n#### bin/uninstall\n\nDesinstala uma versão específica de uma ferramenta.\n\n#### bin/list-legacy-filenames\n\nRegistre arquivos setter adicionais para este plugin. Deve imprimir uma string com uma lista de nomes de arquivos separados por espaços.\n\n```shell\n.ruby-version .rvmrc\n```\n\nNota: Isso só se aplica a usuários que habilitaram a opção `legacy_version_file` em seu `~/.asdfrc`.\n\n#### bin/parse-legacy-file\n\nIsso pode ser usado para analisar ainda mais o arquivo legado encontrado pelo asdf. Se o `parse-legacy-file` não for implementado, o asdf simplesmente irá cat o arquivo para determinar a versão. O script receberá o caminho do arquivo como seu primeiro argumento.\n\n#### bin/post-plugin-add\n\nIsso pode ser usado para executar qualquer ação pós-instalação depois que o plug-in for adicionado ao asdf.\n\nO script tem acesso ao caminho em que o plugin foi instalado (`${ASDF_PLUGIN_PATH}`) e o URL de origem (`${ASDF_PLUGIN_SOURCE_URL}`), se algum foi usado.\n\nVeja também os ganchos relacionados:\n\n- `pre_asdf_plugin_add`\n- `pre_asdf_plugin_add_${plugin_name}`\n- `post_asdf_plugin_add`\n- `post_asdf_plugin_add_${plugin_name}`\n\n#### bin/pre-plugin-remove\n\nIsso pode ser usado para executar qualquer ação de pré-remoção antes que o plug-in seja removido do asdf.\n\nO script tem acesso ao caminho em que o plugin foi instalado (`${ASDF_PLUGIN_PATH}`).\n\nVeja também os ganchos relacionados:\n\n- `pre_asdf_plugin_remove`\n- `pre_asdf_plugin_remove_${plugin_name}`\n- `post_asdf_plugin_remove`\n- `post_asdf_plugin_remove_${plugin_name}`\n\n## Comandos de extensão para asdf CLI.\n\n\nÉ possível que plugins definam novos comandos asdf fornecendo scripts ou executáveis `lib/commands/command*.bash` que será chamado usando a interface de linha de comando asdf usando o nome do plug-in como um subcomando.\n\nPor exemplo, suponha que um plugin `foo` tenha:\n\n```shell\nfoo/\n  lib/commands/\n    command.bash\n    command-bat.bash\n    command-bat-man.bash\n    command-help.bash\n```\n\nOs usuários agora podem executar\n\n```shell\n$ asdf foo         # same as running `$ASDF_DATA_DIR/plugins/foo/lib/commands/command.bash`\n$ asdf foo bar     # same as running `$ASDF_DATA_DIR/plugins/foo/lib/commands/command.bash bar`\n$ asdf foo help    # same as running `$ASDF_DATA_DIR/plugins/foo/lib/commands/command-help.bash`\n$ asdf foo bat man # same as running `$ASDF_DATA_DIR/plugins/foo/lib/commands/command-bat-man.bash`\n$ asdf foo bat baz # same as running `$ASDF_DATA_DIR/plugins/foo/lib/commands/command-bat.bash baz`\n```\n\nOs autores de plugins podem usar esse recurso para fornecer utilitários relacionados às suas ferramentas,\nou até mesmo criar plugins que são apenas novas extensões de comando para o próprio asdf.\n\nQuando invocados, se os comandos de extensão não tiverem seus bits executáveis definidos, eles serão\noriginado como scripts bash, tendo todas as funções de `$ASDF_DIR/lib/utils.bash` disponíveis.\nAlém disso, o `$ASDF_CMD_FILE` resolve para o caminho completo do arquivo que está sendo originado.\nSe o bit executável estiver definido, eles são apenas executados e substituem a execução do asdf.\n\nUm bom exemplo desse recurso é para plugins como [`haxe`](https://github.com/asdf-community/asdf-haxe)\nque fornece o `asdf haxe neko-dylibs-link` para corrigir um problema onde os executáveis haxe esperam encontrar\nbibliotecas dinâmicas relativas ao diretório executável.\n\nSe o seu plug-in fornecer um comando de extensão asdf, certifique-se de mencioná-lo no README do seu plug-in.\n\n## Modelos de calços personalizados\n\n**POR FAVOR, use este recurso apenas se for absolutamente necessário**\n\nasdf permite modelos de calços personalizados. Para um executável chamado `foo`, se houver um arquivo `shims/foo` no plug-in, o asdf copiará esse arquivo em vez de usar seu modelo padrão de shim.\n\nIsso deve ser usado com sabedoria. Por enquanto AFAIK, está sendo usado apenas no plugin Elixir, porque um executável também é lido como um arquivo Elixir, além de ser apenas um executável. O que torna impossível usar o calço bash padrão.\n\n## Testando plug-ins\n\n `asdf` contém o comando `plugin-test` para testar seu plugin. Você pode usá-lo da seguinte forma\n\n```shell\nasdf plugin test <plugin-name> <plugin-url> [--asdf-tool-version <version>] [--asdf-plugin-gitref <git-ref>] [test-command*]\n```\n\nApenas os dois primeiros argumentos são necessários.\nSe \\__version_ for especificado, a ferramenta será instalada com essa versão específica. O padrão é o que retorna `asdf latest <plugin-name>`.\nSe _git-ref_ for especificado, o plug-in em si é verificado nesse commit/branch/tag, útil para testar um pull-request no CI do seu plug-in. O padrão é o branch _default_ do repositório do plugin.\n\nOs argumentos Rest são considerados o comando a ser executado para garantir que a ferramenta instalada funcione corretamente.\nNormalmente seria algo que leva `--version` ou `--help`.\nPor exemplo, para testar o plugin NodeJS, podemos executar\n\n```shell\nasdf plugin test nodejs https://github.com/asdf-vm/asdf-nodejs.git node --version\n```\n\nÉ altamente recomendável que você teste seu plug-in em um ambiente CI e verifique se ele funciona no Linux e no OSX.\n\n#### Exemplo GitHub Action\n\nO repositório [asdf-vm/actions](https://github.com/asdf-vm/actions) fornece uma ação do GitHub para testar seus plugins hospedados no github.\n\n```yaml\nsteps:\n  - name: asdf_plugin_test\n    uses: asdf-vm/actions/plugin-test@v1\n    with:\n      command: \"my_tool --version\"\n    env:\n      GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} # automatically provided\n```\n\n#### Exemplo de configuração do TravisCI\n\nAqui está um arquivo `.travis.yml` de amostra, personalize-o de acordo com suas necessidades\n\n```yaml\nlanguage: c\nscript: asdf plugin test nodejs $TRAVIS_BUILD_DIR 'node --version'\nbefore_script:\n  - git clone https://github.com/asdf-vm/asdf.git asdf\n  - . asdf/asdf.sh\nos:\n  - linux\n  - osx\n```\n\nNotas:\nAo usar outro IC, você precisará verificar qual variável mapeia para o caminho do repositório.\n\nVocê também tem a opção de passar um caminho relativo para `plugin-test`.\n\nPor exemplo, se o script de teste for executado no diretório: `asdf plugin test nodejs . 'node --version'`.\n\n## Limitação de taxa da API do GitHub\n\nSe o `list-all` do seu plug-in depender do acesso à API do GitHub, certifique-se de fornecer um token de autorização ao acessá-lo, caso contrário, seus testes podem falhar devido à limitação de taxa.\n\nPara fazer isso, crie um [novo token pessoal](https://github.com/settings/tokens/new) com apenas acesso `public_repo`.\n\nEm seguida, nas configurações de compilação do travis.ci, adicione uma variável de ambiente _secure_ para ela chamada algo como `GITHUB_API_TOKEN`.  E _NUNCA_ publique seu token em seu código.\n\nFinalmente, adicione algo como o seguinte para `bin/list-all`\n\n```shell\ncmd=\"curl -s\"\nif [ -n \"$GITHUB_API_TOKEN\" ]; then\n cmd=\"$cmd -H 'Authorization: token $GITHUB_API_TOKEN'\"\nfi\n\ncmd=\"$cmd $releases_path\"\n```\n\n## Enviando plugins para o repositório oficial de plugins\n\n`asdf` pode facilmente instalar plugins especificando o url do repositório de plugins, por exemplo. `plugin add my-plugin https://github.com/user/asdf-my-plugin.git`.\n\nPara facilitar para seus usuários, você pode adicionar seu plugin ao repositório oficial de plugins para ter seu plugin listado e facilmente instalável usando um comando mais curto, por exemplo `asdf plugin add my-plugin`.\n\nSiga as instruções no repositório de plugins: [asdf-vm/asdf-plugins](https://github.com/asdf-vm/asdf-plugins).\n"
  },
  {
    "path": "docs/zh-hans/contribute/core.md",
    "content": "# asdf\n\n`asdf` 核心贡献指南.\n\n## 初始化安装\n\n在 Github 上 fork `asdf` 并且/或者使用 Git 克隆默认分支：\n\n```shell\n# 克隆你 fork 的 asdf\ngit clone https://github.com/<GITHUB_USER>/asdf.git\n# 或者直接克隆 asdf\ngit clone https://github.com/asdf-vm/asdf.git\n```\n\n核心开发所需的工具都列举在这个存储库的 `.tool-versions` 文件中。如果你想要使用 `asdf` 自身来管理它，请使用以下命令添加这些插件：\n\n```shell\nasdf plugin add bats https://github.com/timgluz/asdf-bats.git\nasdf plugin add shellcheck https://github.com/luizm/asdf-shellcheck.git\nasdf plugin add shfmt https://github.com/luizm/asdf-shfmt.git\n```\n\n使用以下命令安装这些版本来开发 `asdf`：\n\n```shell\nasdf install\n```\n\n在本地机器的开发过程中不使用 `asdf` 来管理工具 _或许_ 对你有帮助，因为你可能需要打破某些可能会影响到你的开发工具链的功能。以下是所需工具的原始列表：\n\n- [bats-core](https://github.com/bats-core/bats-core)：Bash 自动化测试系统，用于单元测试 Bash 或 POSIX 兼容脚本。\n- [shellcheck](https://github.com/koalaman/shellcheck)：Shell 脚本的静态分析工具。\n- [shfmt](https://github.com/mvdan/sh)：支持 Bash 的 Shell 解析器、格式化器和解释器；包含 shfmt。\n\n## 开发\n\n如果你想要在不更改已安装的 `asdf` 的情况下尝试应用你的更改，可以将 `$ASDF_DIR` 变量设置为克隆存储库的路径，并临时将目录的 `bin` 和 `shims` 目录添加到你的路径中。\n\n最好在提交或推送到远程之前，在本地做好格式化、lint 检查和测试你的代码。可以使用以下脚本/命令：\n\n```shell\n# 脚本检查\n./scripts/lint.bash --check\n\n# 格式化\n./scripts/lint.bash --fix\n\n# 测试：所有案例\n./scripts/test.bash\n\n# 测试：特定命令\nbats test/list_commands.bash\n```\n\n::: tip 提示\n\n**增加测试！** - 新特性需要进行测试，并加快错误修复的审查速度。请在创建拉取请求之前覆盖新的代码路径。查看 [bats-core 文档](https://bats-core.readthedocs.io/en/stable/index.html) 了解更多。\n\n:::\n\n### Gitignore\n\n以下是 `asdf-vm/asdf` 仓库中的 `.gitignore` 文件。我们忽略项目特定的文件。与操作系统、工具或工作流程相关的文件应在全 `.gitignore` 配置中忽略， [请查看此处](http://stratus3d.com/blog/2018/06/03/stop-excluding-editor-temp-files-in-gitignore/) 了解更多。\n\n<<< @/../.gitignore\n\n### `.git-blame-ignore-revs`\n\n`asdf` 使用 `.git-blame-ignore-revs` 文件来减少在运行 blame 命令时的噪音。请查看 [git blame documentation](https://git-scm.com/docs/git-blame) 了解更多。\n\n使用 `git blame` 时，可通过以下方式使用该文件：\n\n```sh\ngit blame --ignore-revs-file .git-blame-ignore-revs ./test/install_command.bats\n```\n\n可选地，配置为在每次调用 `blame` 时自动使用该文件，无需手动提供：\n\n```sh\ngit config blame.ignoreRevsFile .git-blame-ignore-revs\n```\n\n可以配置集成开发环境（IDEs）使用该文件。例如，在使用 VSCode (搭配 [GitLens](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens)) 时，将以下内容写入 `.vscode/settings.json`：\n\n```json\n{\n  \"gitlens.advanced.blame.customArguments\": [\n    \"--ignore-revs-file\",\n    \".git-blame-ignore-revs\"\n  ]\n}\n```\n\n## Bats 测试\n\n在本地执行测试：\n\n```shell\n./scripts/test.bash\n```\n\n在编写测试之前，**请务必阅读**：\n\n- `test/` 目录中的现有测试\n- [bats-core 文档](https://bats-core.readthedocs.io/en/stable/index.html)\n- `scripts/test.bash` 中使用的现有 Bats 设置\n\n### Bats 提示\n\nBats 调试有时可能很困难。使用带有 `-t` 标识的 TAP 输出将使你能够在测试执行期间打印带有特殊文件描述符 `>&3` 的输出，从而简化调试。例如：\n\n```shell\n# test/some_tests.bats\n\nprintf \"%s\\n\" \"Will not be printed during bats test/some_tests.bats\"\nprintf \"%s\\n\" \"Will be printed during bats -t test/some_tests.bats\" >&3\n```\n\n进一步相关文档请查看 bats-core 的 [终端打印](https://bats-core.readthedocs.io/en/stable/writing-tests.html#printing-to-the-terminal) 部分.\n\n## 拉取请求、发布以及约定式提交\n\n`asdf` 正在使用一个名为 [Release Please](https://github.com/googleapis/release-please) 的自动发布工具来自动碰撞 [SemVer](https://semver.org/) 版本并生成 [变更日志](https://github.com/asdf-vm/asdf/blob/master/CHANGELOG.md)。这个信息是通过读取自上次发布以来的提交历史记录来确定的。\n\n[约定式提交](https://www.conventionalcommits.org/zh-hans/) 定义了拉取请求标题的格式，该标题成为默认分支上的提交消息格式。这是通过 Github Action [`amannn/action-semantic-pull-request`](https://github.com/amannn/action-semantic-pull-request) 强制执行的。\n\n约定式提交遵循以下格式：\n\n```\n<type>[optional scope][optional !]: <description>\n\n<!-- 例子 -->\nfix: some fix\nfeat: a new feature\ndocs: some documentation update\ndocs(website): some change for the website\nfeat!: feature with breaking change\n```\n\n`<types>` 的所有类型包含： `feat`、`fix`、`docs`、`style`、`refactor`、`perf`、`test`、`build`、`ci`、`chore`、`revert`。\n\n- `!`：表示重大更改\n- `fix`：将会创建一个新的 SemVer `patch` 补丁\n- `feat`：将会创建一个新的 SemVer `minor` 小版本\n- `<type>!`：将会创建一个新的 SemVer `major` 大版本\n\n拉取请求标题必须遵循这种格式。\n\n::: tip 提示\n\n请使用约定式提交信息格式作为拉取请求标题。\n\n:::\n\n## Docker 镜像\n\n[asdf-alpine](https://github.com/vic/asdf-alpine) 和 [asdf-ubuntu](https://github.com/vic/asdf-ubuntu) 项目正在努力提供一些 asdf 工具的容器化镜像。你可以使用这些容器镜像作为开发服务器的基础镜像，或者运行生产应用。\n"
  },
  {
    "path": "docs/zh-hans/contribute/documentation.md",
    "content": "# 文档 & 网站\n\n文档 & 网站贡献指南。\n\n## 初始化设置\n\n在 Github 上 fork `asdf` 并且/或者使用 Git 克隆默认分支：\n\n```shell\n# 克隆你 fork 的 asdf\ngit clone https://github.com/<GITHUB_USER>/asdf.git\n# 或者直接克隆 asdf\ngit clone https://github.com/asdf-vm/asdf.git\n```\n\n文档网站开发所需的工具都在文件 `docs/.tool-versions` 中使用 `asdf` 进行管理。使用以下命令添加插件：\n\n```shell\nasdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs\n```\n\n使用以下命令安装工具版本：\n\n```shell\nasdf install\n```\n\n- [Node.js](https://nodejs.org/zh-cn/)：基于 Chrome 的 V8 引擎的 JavaScript 运行环境。\n\n根据 `docs/package.json` 文件安装 Node.js 依赖：\n\n```shell\nnpm install\n```\n\n## 开发\n\n[Vitepress (v2)](https://vitepress.dev/zh/) 是我们用来构建 asdf 文档网站的静态站点生成器（SSG）。它被选中来取代 [Docsify.js](https://docsify.js.org/#/zh-cn/)，因为我们希望在用户没有可用或未启用 JavaScript 时支持仅依靠 HTML。Docsify 无法做到这一点。除此之外，两者特性集合大致相同，重点是 Vuepress 可以用最少的配置编写 Markdown 文件。\n\n`package.json` 包含了开发所需的脚本：\n\n@[code json{3-5}](../../package.json)\n\n启动本地开发服务器：\n\n```shell\nnpm run dev\n```\n\n在提交之前格式化代码：\n\n```shell\nnpm run format\n```\n\n## 拉取请求、发布以及约定式提交\n\n`asdf` 正在使用依赖 PR 标题中的约定式提交的自动化发布流水线。具体的文档可以查看 [核心贡献指南](./core.md).\n\n当为文档更改创建 PR 请求时，请确保 PR 标题使用了约定式提交类型 `docs` 以及 `docs: <description>` 的格式。\n\n## Vitepress\n\n网站的配置包含在几个 JavaScript 文件中，其中 JS 对象用于表示配置。它们是：\n\n- `docs/.vitepress/config.js`：网站的根配置文件。请查看 [Vitepress 文档](https://vitepress.dev/zh/reference/site-config) 了解更多详情。\n\n为了简化根配置文件，更大的 JS 对象表示 _导航栏和侧边栏_ 配置已经被提取并按照语言类型分隔开来。请参考以下文件：\n\n- `docs/.vitepress/navbar.js`\n- `docs/.vitepress/sidebar.js`\n\n这些配置的官方文档位于 [默认主题参考](https://vitepress.dev/zh/reference/default-theme-config)。\n\n## I18n 国际化\n\nVitepress 有一流的国际化支持。根配置文件 `docs/.vitepress/config.js` 定义了支持的语言类型及其 URL、在选择下拉菜单中的标题以及导航栏/侧边栏配置引用。\n\n导航栏/侧边栏配置在上述配置文件中捕获，按语言类型分隔开并单独导出。\n\n每种语言的 markdown 内容必须位于与根配置文件中 `locale` 键同名的目录位置。也就是：\n\n```js\n// docs/.vitepress/config.js\nexport default defineConfig({\n  ...\n  locales: {\n    root: {\n      label: \"English\",\n        lang: \"en-US\",\n        themeConfig: {\n        nav: navbars.en,\n          sidebar: sidebars.en,\n      },\n    },\n    \"pt-br\": {\n      label: \"Brazilian Portuguese\",\n        lang: \"pr-br\",\n        themeConfig: {\n        nav: navbars.pt_br,\n          sidebar: sidebars.pt_br,\n      },\n    },\n    \"zh-hans\": {\n      label: \"简体中文\",\n        lang: \"zh-hans\",\n        themeConfig: {\n        nav: navbars.zh_hans,\n          sidebar: sidebars.zh_hans,\n      },\n    },\n  },\n})\n```\n\n`/pt-BR/` 将要求 markdown 文件的同一集合位于 `docs/pt-BR/` 目录下，如下所示：\n\n```shell\ndocs\n├─ README.md\n├─ foo.md\n├─ nested\n│  └─ README.md\n└─ pt-BR\n   ├─ README.md\n   ├─ foo.md\n   └─ nested\n      └─ README.md\n```\n\n请查看 [Vitepress i18n 国际化官方文档](https://vitepress.dev/zh/guide/i18n) 了解更多详情。\n"
  },
  {
    "path": "docs/zh-hans/contribute/first-party-plugins.md",
    "content": "# 官方插件\n\nasdf 核心团队已经开发了一些与他们日常工作相关的插件。随时欢迎大家维护和改进这些插件。这些插件所对应的存储库链接如下所示：\n\n- [Elixir](https://github.com/asdf-vm/asdf-elixir)\n- [Erlang](https://github.com/asdf-vm/asdf-erlang)\n- [Node.js](https://github.com/asdf-vm/asdf-nodejs)\n- [Ruby](https://github.com/asdf-vm/asdf-ruby)\n\n对于社区插件，请参考：\n\n- [`asdf-community` 组织](https://github.com/asdf-community)：一个用于长期维护 `asdf` 插件的协作、社区驱动的项目。\n- [`asdf-plugins` 缩写存储库](https://github.com/asdf-vm/asdf-plugins)：`asdf` 核心用于查找流行的 `asdf` 插件的缩写列表。\n- [GitHub `asdf-plugin` 主题搜索](https://github.com/topics/asdf-plugin)\n"
  },
  {
    "path": "docs/zh-hans/contribute/github-actions.md",
    "content": "# GitHub Actions\n\n感谢你的关注，请参考 [asdf actions repo](https://github.com/asdf-vm/actions) 了解现有的问题、对话和贡献指南。\n"
  },
  {
    "path": "docs/zh-hans/guide/getting-started-legacy.md",
    "content": "# 快速入门\n\n`asdf` 安装过程包括：\n\n1. 安装依赖\n2. 下载 `asdf` 核心\n3. 安装 `asdf`\n4. 为每一个你想要管理的工具/运行环境安装插件\n5. 安装工具/运行环境的一个版本\n6. 通过 `.tool-versions` 配置文件设置全局和项目版本\n\n## 1. 安装依赖\n\nasdf 主要依赖 `git` 和 `curl`。这是一份 _非穷举_ 的命令清单用于运行 _你的_ 包管理器（有些可能在后面的步骤自动安装这些工具）。\n\n| 操作系统| 包管理器         | 命令                            |\n| ----- | --------------- | ---------------------------------- |\n| linux | Aptitude        | `apt install curl git`             |\n| linux | DNF             | `dnf install curl git`             |\n| linux | Pacman          | `pacman -S curl git`               |\n| linux | Zypper          | `zypper install curl git`          |\n| macOS | Homebrew        | `brew install coreutils curl git`  |\n| macOS | Spack           | `spack install coreutils curl git` |\n\n::: tip 注意\n\n取决于你的系统配置，可能会需要 `sudo`。 \n\n:::\n\n## 2. 下载 asdf\n\n### 官方支持的下载方式\n\n<!-- x-release-please-start-version -->\n\n```shell\ngit clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.15.0\n```\n\n<!-- x-release-please-end -->\n\n### 社区支持的下载方式\n\n我们强烈推荐使用官方 `git` 方式。\n\n| 方式   | 命令                                                                                                                                                             |\n| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Homebrew | `brew install asdf`                                                                                                                                                 |\n| Pacman   | `git clone https://aur.archlinux.org/asdf-vm.git && cd asdf-vm && makepkg -si` 或者你希望使用 [AUR helper](https://wiki.archlinux.org/index.php/AUR_helpers) |\n\n## 3. 安装 asdf\n\n根据 Shell 脚本、操作系统和安装方法的组合不同，相应的配置也会不同。展开以下与你的系统最匹配的选项。\n\n**macOS 用户，请务必阅读本节最后关于 `path_helper` 的警告。**\n\n::: details Bash & Git\n\n在 `~/.bashrc` 文件中加入以下内容：\n\n```shell\n. \"$HOME/.asdf/asdf.sh\"\n```\n\n补全功能必须在 `.bashrc` 文件中加入以下内容来配置完成：\n\n```shell\n. \"$HOME/.asdf/completions/asdf.bash\"\n```\n\n:::\n\n::: details Bash & Git (macOS)\n\n如果你正在使用 **macOS Catalina 或者更新的版本**, 默认的 shell 已经被修改为 **ZSH**。除非修改回 Bash, 否则请遵循 ZSH 的说明。\n\n在 `~/.bash_profile` 文件中加入以下内容：\n\n```shell\n. \"$HOME/.asdf/asdf.sh\"\n```\n\n补全功能必须在 `.bash_profile` 文件中使用以下内容手动配置完成：\n\n```shell\n. \"$HOME/.asdf/completions/asdf.bash\"\n```\n\n:::\n\n::: details Bash & Homebrew\n\n使用以下命令将 `asdf.sh` 加入到 `~/.bashrc` 文件中：\n\n```shell\necho -e \"\\n. \\\"$(brew --prefix asdf)/libexec/asdf.sh\\\"\" >> ~/.bashrc\n```\n\n补全功能将需要 [按照 Homebrew 的说明完成配置](https://docs.brew.sh/Shell-Completion#configuring-completions-in-bash) 或者执行以下命令：\n\n```shell\necho -e \"\\n. \\\"$(brew --prefix asdf)/etc/bash_completion.d/asdf.bash\\\"\" >> ~/.bashrc\n```\n\n:::\n\n::: details Bash & Homebrew (macOS)\n\n如果你正在使用 **macOS Catalina 或者更新的版本**, 默认的 shell 已经被修改为 **ZSH**。除非修改回 Bash, 否则请遵循 ZSH 的说明。\n\n使用以下命令将 `asdf.sh` 加入到 `~/.bash_profile` 文件中：\n\n```shell\necho -e \"\\n. \\\"$(brew --prefix asdf)/libexec/asdf.sh\\\"\" >> ~/.bash_profile\n```\n\n补全功能将需要 [按照 Homebrew 的说明完成配置](https://docs.brew.sh/Shell-Completion#configuring-completions-in-bash) 或者执行以下命令：\n\n```shell\necho -e \"\\n. \\\"$(brew --prefix asdf)/etc/bash_completion.d/asdf.bash\\\"\" >> ~/.bash_profile\n```\n\n:::\n\n::: details Bash & Pacman\n\n在 `~/.bashrc` 文件中加入以下内容：\n\n```shell\n. /opt/asdf-vm/asdf.sh\n```\n\n为了让补全功能正常工作需要安装 [`bash-completion`](https://wiki.archlinux.org/title/bash#Common_programs_and_options) 。\n:::\n\n::: details Fish & Git\n\n在 `~/.config/fish/config.fish` 文件中加入以下内容：\n\n```shell\nsource ~/.asdf/asdf.fish\n```\n\n补全功能必须按照以下命令手动配置完成：\n\n```shell\nmkdir -p ~/.config/fish/completions; and ln -s ~/.asdf/completions/asdf.fish ~/.config/fish/completions\n```\n\n:::\n\n::: details Fish & Homebrew\n\n使用以下命令将 `asdf.fish` 加入到 `~/.config/fish/config.fish` 文件中：\n\n```shell\necho -e \"\\nsource \"(brew --prefix asdf)\"/libexec/asdf.fish\" >> ~/.config/fish/config.fish\n```\n\nFish shell 的补全功能可以交给 [Homebrew 处理](https://docs.brew.sh/Shell-Completion#configuring-completions-in-fish). 很友好！\n:::\n\n::: details Fish & Pacman\n\n在 `~/.config/fish/config.fish` 文件中加入以下内容：\n\n```shell\nsource /opt/asdf-vm/asdf.fish\n```\n\n补全功能将会在安装过程中由 AUR 包管理器自动配置完成。\n:::\n\n::: details Elvish & Git\n\n使用以下命令将 `asdf.elv` 加入到 `~/.config/elvish/rc.elv` 文件中：\n\n```shell\nmkdir -p ~/.config/elvish/lib; ln -s ~/.asdf/asdf.elv ~/.config/elvish/lib/asdf.elv\necho \"\\n\"'use asdf _asdf; var asdf~ = $_asdf:asdf~' >> ~/.config/elvish/rc.elv\necho \"\\n\"'set edit:completion:arg-completer[asdf] = $_asdf:arg-completer~' >> ~/.config/elvish/rc.elv\n```\n\n补全功能将会自动配置。\n\n:::\n\n::: details Elvish & Homebrew\n\n使用以下命令将 `asdf.elv` 加入到 `~/.config/elvish/rc.elv` 文件中：\n\n```shell\nmkdir -p ~/.config/elvish/lib; ln -s (brew --prefix asdf)/libexec/asdf.elv ~/.config/elvish/lib/asdf.elv\necho \"\\n\"'use asdf _asdf; var asdf~ = $_asdf:asdf~' >> ~/.config/elvish/rc.elv\necho \"\\n\"'set edit:completion:arg-completer[asdf] = $_asdf:arg-completer~' >> ~/.config/elvish/rc.elv\n```\n\n补全功能将会自动配置。\n\n:::\n\n::: details Elvish & Pacman\n\n使用以下命令将 `asdf.elv` 加入到 `~/.config/elvish/rc.elv` 文件中：\n\n```shell\nmkdir -p ~/.config/elvish/lib; ln -s /opt/asdf-vm/asdf.elv ~/.config/elvish/lib/asdf.elv\necho \"\\n\"'use asdf _asdf; var asdf~ = $_asdf:asdf~' >> ~/.config/elvish/rc.elv\necho \"\\n\"'set edit:completion:arg-completer[asdf] = $_asdf:arg-completer~' >> ~/.config/elvish/rc.elv\n```\n\n补全功能将会自动配置。\n\n:::\n\n::: details ZSH & Git\n\n在 `~/.zshrc` 文件中加入以下内容：\n\n```shell\n. \"$HOME/.asdf/asdf.sh\"\n```\n\n**或者** 使用 ZSH 框架插件，比如 [asdf for oh-my-zsh](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/asdf) 将会使脚本生效并安装补全功能。\n\n补全功能会被 ZSH 框架 `asdf` 插件或者通过在 `.zshrc` 文件中加入以下内容自动配置：\n\n```shell\n# append completions to fpath\nfpath=(${ASDF_DIR}/completions $fpath)\n# initialise completions with ZSH's compinit\nautoload -Uz compinit && compinit\n```\n\n- 如果你正在使用自定义的 `compinit` 配置，请确保 `compinit` 在 `asdf.sh` 生效位置的下方\n- 如果你正在使用自定义的 `compinit` 配置和 ZSH 框架，请确保 `compinit` 在框架生效位置的下方\n\n**警告**\n\n如果你正在使用 ZSH 框架，有关的 `asdf` 插件或许需要更新才能通过 `fpath` 正确地使用最新的 ZSH 补全功能。Oh-My-ZSH asdf 插件还在更新中，请查看 [ohmyzsh/ohmyzsh#8837](https://github.com/ohmyzsh/ohmyzsh/pull/8837) 了解更多。\n:::\n\n::: details ZSH & Homebrew\n\n使用以下命令将 `asdf.sh` 加入到 `~/.zshrc` 文件中：\n\n```shell\necho -e \"\\n. $(brew --prefix asdf)/libexec/asdf.sh\" >> ${ZDOTDIR:-~}/.zshrc\n```\n\n**或者** 使用 ZSH 框架插件，比如 [asdf for oh-my-zsh](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/asdf) 将会使脚本生效并安装补全功能。\n\n补全功能可以被 ZSH 框架 `asdf` 或者 [按照 Homebrew 的指引](https://docs.brew.sh/Shell-Completion#configuring-completions-in-zsh) 完成配置。如果你正在使用 ZSH 框架，有关的 `asdf` 插件或许需要更新才能通过 `fpath` 正确地使用最新的 ZSH 补全功能。Oh-My-ZSH asdf 插件还在更新中，请查看 [ohmyzsh/ohmyzsh#8837](https://github.com/ohmyzsh/ohmyzsh/pull/8837) 了解更多。\n:::\n\n::: details ZSH & Pacman\n\n在 `~/.zshrc` 文件中加入以下内容：\n\n```shell\n. /opt/asdf-vm/asdf.sh\n```\n\n补全功能会被放在一个对 ZSH 很友好的位置，但是 [ZSH 必须使用自动补全完成配置](https://wiki.archlinux.org/index.php/zsh#Command_completion)。\n:::\n\n::: details PowerShell Core & Git\n\n在 `~/.config/powershell/profile.ps1` 文件中加入以下内容：\n\n```shell\n. \"$HOME/.asdf/asdf.ps1\"\n```\n\n:::\n\n::: details PowerShell Core & Homebrew\n\n使用以下命令将 `asdf.ps1` 加入到 `~/.config/powershell/profile.ps1` 文件中：\n\n```shell\necho -e \"\\n. \\\"$(brew --prefix asdf)/libexec/asdf.ps1\\\"\" >> ~/.config/powershell/profile.ps1\n```\n\n:::\n\n::: details PowerShell Core & Pacman\n\n在 `~/.config/powershell/profile.ps1` 文件中加入以下内容：\n\n```shell\n. /opt/asdf-vm/asdf.ps1\n```\n\n:::\n\n::: details Nushell & Git\n\n使用以下命令将 `asdf.nu` 加入到 `~/.config/nushell/config.nu` 文件中：\n\n```shell\n\"\\n$env.ASDF_DIR = ($env.HOME | path join '.asdf')\\n source \" + ($env.HOME | path join '.asdf/asdf.nu') | save --append $nu.config-path\n```\n\n补全功能将会自动配置。\n:::\n\n::: details Nushell & Homebrew\n\n使用以下命令将 `asdf.nu` 加入到 `~/.config/nushell/config.nu` 文件中:\n\n```shell\n\"\\n$env.ASDF_DIR = (brew --prefix asdf | str trim | into string | path join 'libexec')\\n source \" +  (brew --prefix asdf | str trim | into string | path join 'libexec/asdf.nu') | save --append $nu.config-path\n```\n\n补全功能将会自动配置。\n:::\n\n::: details Nushell & Pacman\n\n使用以下命令将 `asdf.nu` 加入到 `~/.config/nushell/config.nu` 文件中:\n\n```shell\n\"\\n$env.ASDF_DIR = '/opt/asdf-vm/'\\n source /opt/asdf-vm/asdf.nu\" | save --append $nu.config-path\n```\n\n补全功能将会自动配置。\n:::\n\n::: details POSIX Shell & Git\n\n在 `~/.profile` 文件中加入以下内容：\n\n```shell\nexport ASDF_DIR=\"$HOME/.asdf\"\n. \"$HOME/.asdf/asdf.sh\"\n```\n\n:::\n\n::: details POSIX Shell & Homebrew\n\n使用以下命令将 `asdf.sh` 加入到 `~/.profile` 文件中：\n\n```shell\necho -e \"\\nexport ASDF_DIR=\\\"$(brew --prefix asdf)/libexec/asdf.sh\\\"\" >> ~/.profile\necho -e \"\\n. \\\"$(brew --prefix asdf)/libexec/asdf.sh\\\"\" >> ~/.profile\n```\n\n:::\n\n::: details POSIX Shell & Pacman\n\n在 `~/.profile` 文件中加入以下内容：\n\n```shell\nexport ASDF_DIR=\"/opt/asdf-vm\"\n. /opt/asdf-vm/asdf.sh\n```\n\n:::\n\n`asdf` 脚本需要在设置好的 `$PATH` **之后**和已经生效的框架（比如 oh-my-zsh 等等）**之后**的位置生效。\n\n::: warning 警告\n在 macOS，启动 Bash 或 Zsh shell 会自动调用 `path_helper` 实用工具。`path_helper` 会重新排列 `PATH` (和 `MANPATH`) 中的条目，这可能会导致需要特定顺序的工具出现不一致的行为。为了解决这个问题，macOS 上的 `asdf` 默认会将其 `PATH`-条目强制添加到最前面（拥有最高优先级）。这可以通过 `ASDF_FORCE_PREPEND` 变量进行控制。\n:::\n\n通常打开一个新的终端标签页来重启你的 shell 让 `PATH` 更改即时生效。\n\n## 核心安装完成！\n\n这样就完成了 `asdf` 核心的安装 🎉\n\n`asdf` 仅在你安装**插件**、**工具**和管理它们的**版本**时才开始真正发挥作用。请继续阅读下面的指南来了解这些是如何做到的。\n\n## 4. 安装插件\n\n出于演示目的，我们将通过 [`asdf-nodejs`](https://github.com/asdf-vm/asdf-nodejs/) 插件来安装和设置 [Node.js](https://nodejs.org/)。\n\n### 插件依赖\n\n每个插件都有依赖，所以我们需要确认应该列举了这些依赖的插件源码。对于 `asdf-nodejs` 来说，它们是：\n\n| 操作系统                         | 安装依赖                                 |\n| ------------------------------ | --------------------------------------- |\n| Debian                         | `apt-get install dirmngr gpg curl gawk` |\n| CentOS/ Rocky Linux/ AlmaLinux | `yum install gnupg2 curl gawk`          |\n| macOS                          | `brew install gpg gawk`                 |\n\n我们应该提前安装这些依赖，因为有些插件有 post-install 钩子。\n\n### 安装插件\n\n```shell\nasdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs.git\n```\n\n## 5. 安装指定版本\n\n现在我们已经有了 Node.js 插件，所以我们可以开始安装某个版本了。\n\n我们通过 `asdf list all nodejs` 可以看到所有可用的版本或者通过 `asdf list all nodejs 14` 查看版本子集。\n\n我们将只安装最新可用的 `latest` 版本：\n\n```shell\nasdf install nodejs latest\n```\n\n::: tip 注意\n`asdf` 强制使用准确的版本。`latest` 是一个通过 `asdf` 来解析到执行时刻的实际版本号的辅助工具。\n:::\n\n## 6. 设置默认版本\n\n`asdf` 在从当前工作目录一直到 `$HOME` 目录的所有 `.tool-versions` 文件中进行工具的版本查找。查找在执行 `asdf` 管理的工具时实时发生。\n\n::: warning 警告\n如果没有为工具找到指定的版本，则会出现**错误**。`asdf current` 将显示当前目录中的工具和版本解析结果，或者不存在，以便你可以观察哪些工具将无法执行。\n:::\n\n### 全局\n\n全局默认配置在 `$HOME/.tool-versions` 文件中进行管理。使用以下命令可以设置一个全局版本：\n\n```shell\nasdf global nodejs latest\n```\n\n`$HOME/.tool-versions` 文件内容将会如下所示：\n\n```\nnodejs 16.5.0\n```\n\n某些操作系统已经有一些由系统而非 `asdf` 安装和管理的工具了，`python` 就是一个常见的例子。你需要告诉 `asdf` 将管理权还给系统。[版本参考部分](/zh-hans/manage/versions.md) 将会引导你。\n\n### 本地\n\n本地版本被定义在 `$PWD/.tool-versions` 文件中（当前工作目录）。通常，这将会是一个项目的 Git 存储库。当在你想要的目录执行：\n\n```shell\nasdf local nodejs latest\n```\n\n`$PWD/.tool-versions` 文件内容将会如下所示：\n\n```\nnodejs 16.5.0\n```\n\n### 使用现有工具版本文件\n\n`asdf` 支持从其他版本管理器的现有版本文件中迁移过来，比如 `rbenv` 的 `.ruby-version` 文件。这在每个插件中都原生支持。\n\n[`asdf-nodejs`](https://github.com/asdf-vm/asdf-nodejs/) 支持从 `.nvmrc` 和 `.node-version` 文件进行迁移。为了启用此功能，请在 `asdf` 配置文件 `$HOME/.asdfrc` 中加入以下内容：\n\n```\nlegacy_version_file = yes\n```\n\n请查看 [配置](/zh-hans/manage/configuration.md) 参考页面可以了解更多配置选项。\n\n## 完成指南！\n\n恭喜你完成了 `asdf` 的快速上手 🎉 你现在可以管理你的项目的 `nodejs` 版本了。对于项目中的其他工具类型可以执行类似步骤即可！\n\n`asdf` 还有更多命令需要熟悉，你可以通过运行 `asdf --help` 或者 `asdf` 来查看它们。命令主要分为三类：\n\n- [`asdf` 核心](/zh-hans/manage/core.md)\n- [插件](/zh-hans/manage/plugins.md)\n- [（工具的）版本](/zh-hans/manage/versions.md)\n"
  },
  {
    "path": "docs/zh-hans/guide/getting-started.md",
    "content": "# 快速入门\n\n## 1. 安装 asdf\n\nasdf 的安装方式有以下几种：\n\n::: details 使用包管理器 - **推荐**\n\n| 包管理器 | 命令 |\n| -------- | ----- |\n| Homebrew | `brew install asdf` |\n| Zypper | `zypper install asdf` |\n| Pacman | `git clone https://aur.archlinux.org/asdf-vm.git && cd asdf-vm && makepkg -si` 或者你希望使用 [AUR helper](https://wiki.archlinux.org/index.php/AUR_helpers) |\n\n:::\n\n:::: details 下载预编译二进制 - **简单**\n\n<!--@include: @/zh-hans/parts/install-dependencies.md-->\n\n##### 安装 asdf\n\n1. 访问 https://github.com/asdf-vm/asdf/releases 并下载与操作系统和架构匹配的压缩包。\n2. 从压缩包中解压 `asdf` 二进制文件到 `$PATH` 路径的某个文件夹.\n3. 运行 `type -a asdf` 来验证 `asdf` 是否已经在 `$PATH` 路径中。放置 `asdf` 二进制文件的目录应该包含在 `type` 命令的输出中。如果不在，那么意味着第 2 步不是完全正确。\n\n::::\n\n:::: details 使用 `go install`\n\n<!--@include: @/zh-hans/parts/install-dependencies.md-->\n\n##### 安装 asdf\n\n<!-- x-release-please-start-version -->\n1. [安装 Go](https://go.dev/doc/install)\n2. 运行 `go install github.com/asdf-vm/asdf/cmd/asdf@v0.18.1`\n<!-- x-release-please-end -->\n\n::::\n\n:::: details 从源码构建\n\n<!--@include: @/zh-hans/parts/install-dependencies.md-->\n\n##### 安装 asdf\n\n<!-- x-release-please-start-version -->\n1. 克隆 asdf 仓库:\n  ```shell\n  git clone https://github.com/asdf-vm/asdf.git --branch v0.18.1\n  ```\n<!-- x-release-please-end -->\n2. 运行 `make`\n3. 复制 `asdf` 二进制文件到 `$PATH` 路径的某个文件夹.\n4. 运行 `type -a asdf` 来验证 `asdf` 是否已经在 `$PATH` 路径中。放置 `asdf` 二进制文件的目录应该包含在 `type` 命令的输出中。如果不在，那么意味着第 3 步不是完全正确。\n\n::::\n\n## 2. 配置 asdf\n\n::: tip 注意\n大部分用户 **不** 需要自定义 asdf 插件、安装包、垫片数据的位置。但是，如果 `$HOME/.asdf` 不是你想要 asdf 写入的目录，你可以修改它。请通过在 Shell 的 RC 文件中定义 `ASDF_DATA_DIR` 变量来指定你想要的目录。\n:::\n\n根据 Shell 脚本、操作系统和安装方法的组合不同，相应的配置方式也会有所不同。展开以下与你的系统最匹配的选项。\n\n**macOS 用户，请务必阅读本节最后关于 `path_helper` 的警告。**\n\n::: details Bash\n\n**macOS Catalina 或者更新的版本**： 默认的 shell 已经被修改为 **ZSH**。除非修改回 Bash, 否则请遵循 ZSH 的说明。\n\n**Pacman**： 补全功能需要安装 [`bash-completion`](https://wiki.archlinux.org/title/bash#Common_programs_and_options)。\n\n##### 将垫片目录添加到路径（必须）\n\n在 `~/.bash_profile` 文件中添加以下内容：\n\n```shell\nexport PATH=\"${ASDF_DATA_DIR:-$HOME/.asdf}/shims:$PATH\"\n```\n\n###### 自定义数据目录（可选）\n\n在 `~/.bash_profile` 文件中上面一行声明之前添加以下变量声明：\n\n```shell\nexport ASDF_DATA_DIR=\"/your/custom/data/dir\"\n```\n\n##### 设置 shell 补全（可选）\n\n在 `.bashrc` 文件中添加下面内容来配置补全功能：\n\n```shell\n. <(asdf completion bash)\n```\n\n:::\n\n::: details Fish\n\n##### 将垫片目录添加到路径（必须）\n\n在 `~/.config/fish/config.fish` 文件中添加以下内容：\n\n```shell\n# ASDF configuration code\nif test -z $ASDF_DATA_DIR\n    set _asdf_shims \"$HOME/.asdf/shims\"\nelse\n    set _asdf_shims \"$ASDF_DATA_DIR/shims\"\nend\n\n# Do not use fish_add_path (added in Fish 3.2) because it\n# potentially changes the order of items in PATH\nif not contains $_asdf_shims $PATH\n    set -gx --prepend PATH $_asdf_shims\nend\nset --erase _asdf_shims\n```\n\n###### 自定义数据目录（可选）\n\n**Pacman**: 补全功能会在 AUR 包安装时自动配置。\n\n在 `~/.config/fish/config.fish` 文件中上面一行声明之前添加下面内容：\n\n```shell\nset -gx --prepend ASDF_DATA_DIR \"/your/custom/data/dir\"\n```\n\n##### 设置 shell 补全（可选）\n\n必须通过以下命令手动配置补全功能：\n\n```shell\n$ asdf completion fish > ~/.config/fish/completions/asdf.fish\n```\n\n:::\n\n::: details Elvish\n\n##### 将垫片目录添加到路径（必须）\n\n在 `~/.config/elvish/rc.elv` 文件中添加以下内容：\n\n```shell\nvar asdf_data_dir = ~'/.asdf'\nif (and (has-env ASDF_DATA_DIR) (!=s $E:ASDF_DATA_DIR '')) {\n  set asdf_data_dir = $E:ASDF_DATA_DIR\n}\n\nif (not (has-value $paths $asdf_data_dir'/shims')) {\n  set paths = [$path $@paths]\n}\n```\n\n###### 自定义数据目录（可选）\n\n修改在上面片段之前的如下一行内容为自定义数据目录：\n\n```diff\n-var asdf_data_dir = ~'/.asdf'\n+var asdf_data_dir = '/your/custom/data/dir'\n```\n\n##### 设置 shell 补全（可选）\n\n```shell\n$ asdf completion elvish >> ~/.config/elvish/rc.elv\n$ echo \"\\n\"'set edit:completion:arg-completer[asdf] = $_asdf:arg-completer~' >> ~/.config/elvish/rc.elv\n```\n\n:::\n\n::: details ZSH\n\n**Pacman**： 补全功能被放置在对 ZSH 友好的位置，但是 [ZSH 必须配置使用自动补全](https://wiki.archlinux.org/index.php/zsh#Command_completion)。\n\n##### 将垫片目录添加到路径（必须）\n\n在 `~/.zshrc` 文件中添加以下内容：\n```shell\nexport PATH=\"${ASDF_DATA_DIR:-$HOME/.asdf}/shims:$PATH\"\n```\n\n###### 自定义数据目录（可选）\n\n在 `~/.zshrc` 文件中上面一行声明之前添加以下内容：\n\n```shell\nexport ASDF_DATA_DIR=\"/your/custom/data/dir\"\n```\n\n##### 设置 shell 补全（可选）\n\n补全功能可以通过 ZSH 框架的 `asdf` 插件 (类似 [asdf for oh-my-zsh](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/asdf)) 或如下操作启用：\n\n```shell\n$ mkdir -p \"${ASDF_DATA_DIR:-$HOME/.asdf}/completions\"\n$ asdf completion zsh > \"${ASDF_DATA_DIR:-$HOME/.asdf}/completions/_asdf\"\n```\n\n然后在 `.zshrc` 文件中添加以下内容：\n\n```shell\n# 添加补全功能到 fpath\nfpath=(${ASDF_DATA_DIR:-$HOME/.asdf}/completions $fpath)\n# 使用 ZSH 的 compinit 初始化补全功能\nautoload -Uz compinit && compinit\n```\n\n**注意**\n\n如果你正在 ZSH 框架中使用自定义的 `compinit` 设置 ，请确保 `compinit` 在框架加载之后加载。\n\n补全功能可以通过 ZSH 框架 `asdf` 或者将需要 [按照 Homebrew 的说明进行配置](https://docs.brew.sh/Shell-Completion#configuring-completions-in-zsh). 如果你正在使用 ZSH 框架，与 asdf 关联的插件或许需要更新以便通过 `fpath` 正确使用新 ZSH。 Oh-My-ZSH asdf 插件尚未更新，请查看 [ohmyzsh/ohmyzsh#8837](https://github.com/ohmyzsh/ohmyzsh/pull/8837) 了解更多。\n:::\n\n::: details PowerShell Core\n\n##### 将垫片目录添加到路径（必须）\n\n在 `~/.config/powershell/profile.ps1` 文件中添加以下内容：\n```shell\n# 确定垫片目录的位置\nif ($null -eq $ASDF_DATA_DIR -or $ASDF_DATA_DIR -eq '') {\n  $_asdf_shims = \"${env:HOME}/.asdf/shims\"\n}\nelse {\n  $_asdf_shims = \"$ASDF_DATA_DIR/shims\"\n}\n\n# 然后添加到 path 路径\n$env:PATH = \"${_asdf_shims}:${env:PATH}\"\n```\n\n###### 自定义数据目录（可选）\n\n在 `~/.config/powershell/profile.ps1` 文件中上面片段之前添加以下内容：\n\n```shell\n$env:ASDF_DATA_DIR = \"/your/custom/data/dir\"\n```\n\nShell 补全功能不支持 PowerShell。\n\n:::\n\n::: details Nushell\n\n##### 将垫片目录添加到路径（必须）\n\n在 `~/.config/nushell/config.nu` 文件中添加以下内容：\n\n```shell\nlet shims_dir = (\n  if ( $env | get --ignore-errors ASDF_DATA_DIR | is-empty ) {\n    $env.HOME | path join '.asdf'\n  } else {\n    $env.ASDF_DATA_DIR\n  } | path join 'shims'\n)\n$env.PATH = ( $env.PATH | split row (char esep) | where { |p| $p != $shims_dir } | prepend $shims_dir )\n```\n\n###### 自定义数据目录（可选）\n\n在 `~/.config/nushell/config.nu` 文件中上面内容之前添加下面变量声明：\n\n```shell\n$env.ASDF_DATA_DIR = \"/your/custom/data/dir\"\n```\n\n##### 设置 shell 补全（可选）\n\n```shell\n# If you've not customized the asdf data directory:\n$ mkdir $\"($env.HOME)/.asdf/completions\"\n$ asdf completion nushell | save $\"($env.HOME)/.asdf/completions/nushell.nu\"\n\n# If you have customized the data directory by setting ASDF_DATA_DIR:\n$ mkdir $\"($env.ASDF_DATA_DIR)/completions\"\n$ asdf completion nushell | save $\"($env.ASDF_DATA_DIR)/completions/nushell.nu\"\n```\n\n然后在 `~/.config/nushell/config.nu` 文件中添加以下内容：\n\n```shell\nlet asdf_data_dir = (\n  if ( $env | get --ignore-errors ASDF_DATA_DIR | is-empty ) {\n    $env.HOME | path join '.asdf'\n  } else {\n    $env.ASDF_DATA_DIR\n  }\n)\n. \"$asdf_data_dir/completions/nushell.nu\"\n```\n\n:::\n\n::: details POSIX Shell\n\n##### 将垫片目录添加到路径（必须）\n\n在 `~/.profile` 文件中添加以下内容：\n```shell\nexport PATH=\"${ASDF_DATA_DIR:-$HOME/.asdf}/shims:$PATH\"\n```\n\n###### 自定义数据目录（可选）\n\n在 `~/.profile` 文件中上面一行内容之前添加以下内容：\n\n```shell\nexport ASDF_DATA_DIR=\"/your/custom/data/dir\"\n```\n\n:::\n\n`asdf` 脚本需要在设置好的 `$PATH` **之后**和已经生效的框架（比如 oh-my-zsh 等等）**之后**的位置生效。\n\n通常打开一个新的终端标签页来重启你的 shell 让 `PATH` 更改即时生效。\n\n## 核心安装完成！\n\n这样就完成了 `asdf` 核心的安装 🎉\n\n`asdf` 仅在你安装**插件**、**工具**和管理它们的**版本**时才开始真正发挥作用。请继续阅读下面的指南来了解这些是如何做到的。\n\n## 4. 安装插件\n\n出于演示目的，我们将通过 [`asdf-nodejs`](https://github.com/asdf-vm/asdf-nodejs/) 插件来安装和设置 [Node.js](https://nodejs.org/)。\n\n### 插件依赖\n\n每个插件都有依赖，所以我们需要确认应该列举了这些依赖的插件源码。对于 `asdf-nodejs` 来说，它们是：\n\n| 操作系统                         | 安装依赖                                |\n| ------------------------------ | --------------------------------------- |\n| Debian                         | `apt-get install dirmngr gpg curl gawk` |\n| CentOS/ Rocky Linux/ AlmaLinux | `yum install gnupg2 curl gawk`          |\n| macOS                          | `brew install gpg gawk`                 |\n\n我们应该提前安装这些依赖，因为有些插件有 post-install 钩子。\n\n### 安装插件\n\n```shell\nasdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs.git\n```\n\n## 5. 安装指定版本\n\n现在我们已经有了 Node.js 插件，所以我们可以开始安装某个版本了。\n\n我们通过 `asdf list all nodejs` 可以看到所有可用的版本或者通过 `asdf list all nodejs 14` 查看版本子集。\n\n我们将只安装最新可用的 `latest` 版本：\n\n```shell\nasdf install nodejs latest\n```\n\n::: tip 注意\n`asdf` 强制使用准确的版本。`latest` 是一个通过 `asdf` 来解析到执行时刻的实际版本号的辅助工具。\n:::\n\n## 6. 设置默认版本\n\n`asdf` 在从当前工作目录一直到 `$HOME` 目录的所有 `.tool-versions` 文件中进行工具的版本查找。查找在执行 `asdf` 管理的工具时实时发生。\n\n::: warning 警告\n如果没有为工具找到指定的版本，则会出现**错误**。`asdf current` 将显示当前目录中的工具和版本解析结果，或者不存在，以便你可以观察哪些工具将无法执行。\n:::\n\n因为 asdf 会在当前目录寻找 `.tool-versions` 文件，如果没有找到将会继续逐层向上在父目录寻找 `.tool-versions` 文件直到找到。如果在父目录也没有找到 `.tool-versions` 文件，版本解析进程将会失败并且打印错误。\n\n如果你想要设置一个默认版本用来应用在你工作的所有目录，你可以在 `$HOME/.tool-versions` 文件中定义版本。任何在家目录下的子目录都会被解析为同样的版本，除非子目录中设置了另外一个版本。\n\n```shell\nasdf set -u nodejs 16.5.0\n```\n\n`$HOME/.tool-versions` 文件内容将会变成：\n\n```\nnodejs 16.5.0\n```\n\n某些操作系统已经有一些由系统而非 `asdf` 安装和管理的工具了，`python` 就是一个常见的例子。你需要告诉 `asdf` 将管理权还给系统。[版本](/zh-hans/manage/versions.md) 参考页面将会引导你。\n\nasdf 首先从当前工作目录的 `$PWD/.tool-versions` 文件中寻找版本。这可能是一个包含源代码或某个项目 Git 存储库的目录。当在你想要的目录执行时，你可以用 `asdf set` 来设置版本：\n\n```shell\nasdf set nodejs 16.5.0\n```\n\n`$PWD/.tool-versions` 文件内容将会变成：\n\n```\nnodejs 16.5.0\n```\n\n### 使用现有工具版本文件\n\n`asdf` 支持从其他版本管理器的现有版本文件中迁移过来，比如 `rbenv` 的 `.ruby-version` 文件。这在每个插件中都原生支持。\n\n[`asdf-nodejs`](https://github.com/asdf-vm/asdf-nodejs/) 支持从 `.nvmrc` 和 `.node-version` 文件进行迁移。为了启用此功能，请在 `asdf` 配置文件 `$HOME/.asdfrc` 中添加以下内容：\n\n```\nlegacy_version_file = yes\n```\n\n查看 [配置](/zh-hans/manage/configuration.md) 参考页面可以了解更多配置选项。\n\n## 完成指南！\n\n恭喜你完成了 `asdf` 的快速上手 🎉 你现在可以管理你的项目的 `nodejs` 版本了。对于项目中的其他工具类型可以执行类似步骤即可！\n\n`asdf` 还有更多命令需要熟悉，你可以通过运行 `asdf --help` 或者 `asdf` 来查看它们。命令主要分为三类：\n\n- [`asdf` 核心](/zh-hans/manage/core.md)\n- [插件](/zh-hans/manage/plugins.md)\n- [（工具的）版本](/zh-hans/manage/versions.md)\n"
  },
  {
    "path": "docs/zh-hans/guide/introduction.md",
    "content": "# 项目简介\n\n`asdf` 是一个工具版本管理器。所有的工具版本定义都包含在一个文件（`.tool-versions`）中，你可以将配置文件放在项目的 Git 存储库中以便于和团队其他成员共享，从而确保每个人都使用**完全**相同的工具版本。\n\n传统工作方式需要多个命令行版本管理器，而且每个管理器都有其不同的 API、配置文件和实现方式（比如，`$PATH` 操作、垫片、环境变量等等）。`asdf` 提供单个交互方式和配置文件来简化开发工作流程，并可通过简单的插件接口扩展到所有工具和运行环境。\n\n## 工作原理\n\n一旦 `asdf` 核心在 Shell 配置中设置好之后，你可以安装插件来管理特定的工具。当通过插件安装工具时，安装的可执行程序会为每个可执行程序创建 [垫片](<https://zh.wikipedia.org/wiki/%E5%9E%AB%E7%89%87_(%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1)>)。当你尝试运行其中一个可执行程序时，将运行垫片，从而让 `asdf` 识别 `.tool-versions` 文件中的工具版本并执行该版本。\n\n## 相关项目\n\n### nvm / n / rbenv 等\n\n[nvm](https://github.com/nvm-sh/nvm), [n](https://github.com/tj/n) 和 [rbenv](https://github.com/rbenv/rbenv) 等工具都是用 Shell 脚本写的，这些脚本能为工具安装的可执行程序创建垫片。\n\n`asdf` 非常相似，目的是在工具/运行环境版本管理领域竞争。`asdf` 的独特之处在于插件系统，它消除了每个工具/运行环境对管理工具的需求、每个管理工具的不同命令以及存储库中不同的`*-版本`文件。\n\n<!-- ### pyenv\n\nTODO: someone with Python background expand on this\n\n`asdf` has some similarities to `pyenv` but is missing some key features. The `asdf` team is looking at introducing some of these `pyenv` specific features, though no roadmap or timeline is available. -->\n\n### direnv\n\n> 以“根据当前所在目录动态加载和卸载环境变量”的新特性来增强现有 shell。\n\n`asdf` 不直接管理环境变量，但是插件 [`asdf-direnv`](https://github.com/asdf-community/asdf-direnv) 可以集成 direnv 的特性到 `asdf` 中。\n\n请查看 [direnv 文档](https://direnv.net/) 了解更多。\n\n### Homebrew\n\n> macOS（或者 Linux）上缺失包的管理器\n\nHomebrew 管理你的软件包及其上游依赖。`asdf` 不管理上游依赖，它不是包管理器（这应该由用户来负责），尽管我们试图保持依赖列表尽可能小。\n\n请查看 [Homebrew 文档](https://brew.sh/) 了解更多。\n\n### NixOS\n\n> Nix 是一种采用独特方法进行软件包管理和系统配置的工具\n\nNixOS 旨在通过管理每个工具的整个依赖关系树中软件包的确切版本来构建真正可重复的环境，这不是 `asdf` 想做的。NixOS 使用自己的编程语言、许多命令行工具和超过 60,000 个包的包集合来实现这一点。\n\n再次声明，`asdf` 不管理上游依赖，并且它不是一个包管理器。\n\n请查看 [NixOS 文档](https://nixos.org/guides/how-nix-works.html) 了解更多。\n\n## 为什么使用 asdf？\n\n`asdf` 确保团队可以使用**完全**相同的工具版本，通过插件系统支持**很多**工具，以及作为 Shell 配置中包含的单个 **Shell** 脚本的 _简单性和熟悉性_ 。\n\n::: tip 注意\n`asdf` 并不打算成为一个系统包管理器。它是一个工具版本管理器。仅仅因为你可以为任何工具创建插件并使用 `asdf` 管理其版本，但这并不意味着一定是某个特定工具的最佳实践方案。\n:::\n"
  },
  {
    "path": "docs/zh-hans/guide/upgrading-to-v0-16.md",
    "content": "# 升级到 0.16.0\n\nasdf 0.15.0 版本以及更早版本是用 Bash 编写的，并以一系列 Bash 脚本的形式分发，其中 `asdf` 函数加载到 shell 中。从 0.16.0 版本开始 asdf 用 Go 语言完全重写了。由于是完全重写，存在一些 [重大变更](#重大变更)，现在它是一个二进制文件，而不是一系列脚本。\n\n## 安装\n\n0.16.0 版本及更高版本的安装比之前的 asdf 版本要简单多了。只需要三个步骤：\n\n* 通过 [任意可能的安装方式](/zh-hans/guide/getting-started.html#_1-安装-asdf) 下载与操作系统和架构匹配的 `asdf` 二进制文件。如果使用包管理器，请验证安装的是 0.16.0 版本及更高版本。\n* 添加 `$ASDF_DATA_DIR/shims` 变量到 `$PATH` 路径的最前面。\n* 可选的是，如果你之前自定义了 asdf 数据目录，请将 `ASDF_DATA_DIR` 变量设置为包含插件、版本和垫片的旧版本安装目录。\n\n如果操作系统包管理器已经提供 asdf 0.16.0，那么使用它来安装 asdf 可能是最佳方法。现在，升级 asdf 只能通过操作系统包管理器或手动安装来完成，不再支持自动升级功能。\n\n### 不丢失数据的升级\n\n你可以升级到 asdf  的最新版本，而不会丢失现有的安装数据。操作步骤同上。\n\n#### 1. 下载匹配操作系统和架构的 `asdf` 二进制文件\n\n从 [GitHub 发布页面](https://github.com/asdf-vm/asdf/releases) 下载二进制文件，并将其放置在系统路径中的某个目录下。我选择将 asdf 二进制文件放置在 `$HOME/bin` 目录中，然后将 `$HOME/bin` 添加在 `$PATH` 路径最前面：\n\n```\n# 在 .zshrc, .bashrc, 等...\nexport PATH=\"$HOME/bin:$PATH\"\n```\n\n#### 2. 设置 `ASDF_DATA_DIR`\n\n运行 `asdf info` 并复制包含 `ASDF_DATA_DIR` 变量的行： \n\n```\n...\nASDF_DATA_DIR=\"/home/myuser/.asdf\"\n...\n```\n\n在 shell RC 配置文件中（比如 Zsh 的 `.zshrc`，Bash 的 `.bashrc` 等）末尾添加一行设置 `ASDF_DATA_DIR` 为相同的值：\n\n```bash\nexport ASDF_DATA_DIR=\"/home/myuser/.asdf\"\n```\n\n#### 3. 将 `$ASDF_DATA_DIR/shims` 加在 `$PATH` 最前面\n\n在 shell RC 配置文件（与第 2 步相同的文件）中，将 `$ASDF_DATA_DIR/shims` 添加到路劲的开头：\n\n```bash\nexport ASDF_DATA_DIR=\"/home/myuser/.asdf\"\nexport PATH=\"$ASDF_DATA_DIR/shims:$PATH\"\n```\n\n#### 4. 移除旧的配置文件\n\n在 shell RC 配置中，你会有旧代码在启动时运行 asdf shell 脚本。它可能看起来像这样：\n\n```\n. \"$HOME/.asdf/asdf.sh\"\n```\n\n或者这样：\n\n```\n. /opt/homebrew/opt/asdf/libexec/asdf.sh\n```\n\n注释掉这些行或者完全删除它们。\n\n如果你未使用 Zsh 或者 Bash，请查阅旧版的\n[快速入门](https://asdf-vm.com/zh-hans/guide/getting-started-legacy.html#_3-安装-asdf)\n获悉需要删除的代码片段。\n\n#### 5. 重新生成垫片\n\n请通过运行 `asdf --help` 命令，确认当前 shell 会话中 `asdf` 命令的版本为 0.16.0 或更高版本。如果仍显示旧版本，你需要启动一个新的 shell 会话。\n\n一旦确认 `asdf` 命令为新版本后，运行 `asdf reshim` 来重新生成所有的垫片。这是必要的，因为旧的垫片可能仍使用旧的 Bash 版本。\n\n### 测试\n\n如果你不确定升级到 0.16.0 是否会导致问题，那么可以按照上述“不丢失数据的升级”的描述，在现有版本的基础上安装 0.16.0 进行测试。如果升级到 0.16.0 或更高版本导致问题，你可以回退到旧版本。删除添加在 shell RC 配置文件中的行，并重新添加删除或注释掉的行即可。\n\n### 移除旧文件\n\n**仅在完成上述所有步骤并确认新的 asdf 安装正常运行后再执行此操作！** 升级后，你可以从旧版基于 Bash 脚本的 asdf 版本中移除旧文件。数据目录（通常为 `~/.asdf/`）中的大多数文件均可删除。需要注意的是，此操作并非强制要求。保留旧版本 asdf 的文件不会造成任何问题。必须**保留**的目录仅有：\n\n* `downloads/`\n* `installs/`\n* `plugins/`\n* `shims/`\n\n其余文件可以删除。这可以通过 `find` 命令一次性完成：\n\n```\nfind ${ASDF_DATA_DIR:-$HOME/.asdf}/ -maxdepth 1 -mindepth 1 -not -name downloads -not -name plugins -not -name installs -not -name shims -exec rm -r {} \\;\n```\n\n## 重大变更\n\n### 连字符连接的命令已被移除\n\nasdf 版本 0.15.0 及更早版本对某些命令支持带连字符和不带连字符。从版本 0.16.0 开始，仅支持不带连字符的版本。受影响的命令有： \n\n* `asdf list-all` -> `asdf list all`\n* `asdf plugin-add` -> `asdf plugin add`\n* `asdf plugin-list` -> `asdf plugin list`\n* `asdf plugin-list-all` -> `asdf plugin list all`\n* `asdf plugin-update` -> `asdf plugin update`\n* `asdf plugin-remove` -> `asdf plugin remove`\n* `asdf plugin-test` -> `asdf plugin test`\n* `asdf shim-versions` -> `asdf shimversions`\n\n### `asdf global` 和 `asdf local` 命令已被 `asdf set` 取代\n\n`asdf global` 和 `asdf local` 已被移除。\"global\" 和 \"local\" 这一术语存在错误且容易引起误解。asdf 实际上并不支持适用于所有位置的 \"global\" 版本。任何通过 `asdf global` 指定的版本都可能被当前目录中的 `.tool-versions` 文件中指定的不同版本覆盖。这会让用户感到困惑。新的 `asdf set` 默认行为与 `asdf local` 相同，但还提供了用于在用户主目录（`--home`）和父目录中的现有 `.tool-versions` 文件（`--parent`）中设置版本的标志。这个新接口有望更好地传达 asdf 如何解析版本，并提供等效的功能。\n\n### `asdf update` 命令已被移除\n\n更新不再支持此方式。请使用操作系统包管理器或手动下载最新二进制文件。此外，版本 0.15.0 及更早版本中存在的 `asdf update` 命令无法升级到 0.16.0 版本，因为安装流程已经发生了改变。**无法通过 `asdf update` 命令升级到最新 Go 实现版本。**\n\n### `asdf shell` 命令已被移除\n\n该命令实际上在用户的当前 shell 会话中设置了一个环境变量。它能够做到这一点是因为 `asdf` 实际上是一个 shell 函数，而不是可执行文件。新的重写版本移除了 asdf 中的所有 shell 代码，现在它是一个二进制文件而非 shell 函数，因此直接在 shell 中设置环境变量已不再可能。\n\n### `asdf current` 已发生改变\n\n输出中不再显示三列，最后一列不再显示版本设置的位置或建议用于设置或安装的命令。第三列已拆分成两列。现在的第三列仅指示版本的来源（如果已设置，通常为版本文件或环境变量），而第四列是布尔值，表示指定的版本是否实际已安装。如果未安装，将显示建议的安装命令。\n\n### 插件扩展命令现在必须以 `cmd` 为前缀\n\n之前的插件扩展命令可以像这样运行：\n\n```\nasdf nodejs nodebuild --version\n```\n\n现在它们必须以 `cmd` 为前缀，以避免与内置命令混淆：\n\n```\nasdf cmd nodejs nodebuild --version\n```\n\n### 扩展命令已重新设计\n\n插件扩展命令有一系列的重大变更：\n\n* 它们必须可以通过 `exec` 系统调用运行。若扩展命令是 shell 脚本，为了能通过 `exec` 运行，它们必须以正确的 shebang（ `#!`）行开头。\n* 它们现在可以是任何语言的二进制文件或脚本。不再要求使用 `.bash` 扩展名，因为这会引起误解。\n* 它们必须具有可执行权限。\n* 当缺少可执行权限时，它们不再被 asdf 作为 Bash 脚本加载。\n\n此外，仅使用插件名称后的第一个参数来确定要运行的扩展命令。这意味着实际上存在一个默认的 `command` 扩展命令，当未找到与插件名称后第一个参数匹配的命令时，asdf 会默认使用该命令。例如：\n\n```\nfoo/\n  lib/commands/\n    command\n    command-bar\n    command-bat-man\n```\n\n先前这些脚本的工作方式是这样的：\n\n```\n$ asdf cmd foo         # 等同于运行 `$ASDF_DATA_DIR/plugins/foo/lib/commands/command`\n$ asdf cmd foo bar     # 等同于运行 `$ASDF_DATA_DIR/plugins/foo/lib/commands/command-bar`\n$ asdf cmd foo bat man # 等同于运行 `$ASDF_DATA_DIR/plugins/foo/lib/commands/command-bat-man`\n```\n\n现在：\n\n```\n$ asdf cmd foo         # 等同于运行 `$ASDF_DATA_DIR/plugins/foo/lib/commands/command`\n$ asdf cmd foo bar     # 等同于运行 `$ASDF_DATA_DIR/plugins/foo/lib/commands/command-bar`\n$ asdf cmd foo bat man # 等同于运行 `$ASDF_DATA_DIR/plugins/foo/lib/commands/command-bat man`\n```\n\n### 可执行文件的兼容性问题由 `syscall.Exec` 解决\n\n最明显的例子是缺少正确 shebang 行的脚本。asdf 0.15.0 及更早版本使用 Bash 实现，因此只要该可执行文件可通过 Bash 执行，即可运行。这意味着缺少 shebang 行的脚本仍可通过 `asdf exec` 运行。随着 asdf 0.16.x 改用 Go 语言实现，我们现在通过 Go 的 `syscall.Exec` 函数调用可执行文件，而该函数无法处理缺少 shebang 行的脚本。\n\n实际上这并不是什么大问题。大多数 shell 脚本确实包含 shebang 行。如果由 asdf 管理且缺少 shebang 行，则需要手动添加。\n\n### 不再支持自定义垫片模版\n\n这是一个鲜少使用的功能。核心团队维护的唯一使用该功能的插件是 Elixir 插件，而该插件现已不再需要此功能。该功能最初添加的目的是，使由程序评估而非执行的垫片包含适合特定程序评估的代码（在 Elixir 的情况下，这是 `iex` shell。）经过进一步调查，似乎该功能仅存在于 `PATH` 环境变量中可执行文件路径有时被错误地设置为包含**垫片**文件而非其他**可执行文件**的情况，且该设置针对选定的版本。\n"
  },
  {
    "path": "docs/zh-hans/index.md",
    "content": "---\n# https://vitepress.dev/reference/default-theme-home-page\nlayout: home\n\nhero:\n  name: asdf\n  text: 多运行时版本管理器\n  tagline: 使用一个工具管理所有运行时版本！\n  actions:\n    - theme: brand\n      text: 快速上手\n      link: /zh-hans/guide/getting-started\n    - theme: alt\n      text: 什么是asdf？\n      link: /zh-hans/guide/introduction\n    - theme: alt\n      text: 在 Github 上查看\n      link: https://github.com/asdf-vm/asdf\n\nfeatures:\n  - title: 一个工具\n    details: \"使用单个命令行工具和命令界面管理你的每个项目运行环境。\"\n    icon: 🎉\n  - title: 插件\n    details: \"现有运行环境和工具的大型生态系统。简单 API 用于根据需要添加对新工具的支持！\"\n    icon: 🔌\n  - title: 向后兼容\n    details: \"支持从现有配置文件 .nvmrc、.node-version、.ruby-version 平滑迁移！\"\n    icon: ⏮\n  - title: \"一个配置文件\"\n    details: \"一个可共享的 .tool-versions 配置文件管理所有工具、运行环境及其版本。\"\n    icon: 📄\n  - title: \"Shells\"\n    details: \"支持 Bash、ZSH、Fish 和 Elvish，并提供补全功能。\"\n    icon: 🐚\n  - title: \"GitHub Actions\"\n    details: \"提供 Github Action 在 CI/CD 工作流中安装和使用 .tool-versions。\"\n    icon: 🤖\n---\n"
  },
  {
    "path": "docs/zh-hans/manage/commands.md",
    "content": "# 所有命令\n\n`asdf` 中所有可用命令的列表。这个列表就是 `asdf help` 命令的打印内容。\n\n<<< @../../internal/help/help.txt\n"
  },
  {
    "path": "docs/zh-hans/manage/configuration.md",
    "content": "# 配置\n\n`asdf` 配置既包括可共享的 `.tool-versions` 文件，也包括用户特定的自定义 `.asdfrc` 和环境变量。\n\n## `.tool-versions`\n\n无论何时 `.tool-versions` 出现在目录中，它所声明的工具版本将会被用于该目录和任意子目录。\n\n`.tool-versions` 文件示例如下所示：\n\n```\nruby 2.5.3\nnodejs 10.15.0\n```\n\n你也可以包含注释在里面：\n\n```\nruby 2.5.3 # 这是一个注释\n# 这是另一个注释\nnodejs 10.15.0\n```\n\n版本号可以有如下格式：\n\n- `10.15.0` - 实际的版本号。支持下载二进制文件的插件将会下载二进制文件。\n- `ref:v1.0.2-a` 或者 `ref:39cb398vb39` - 指定标签/提交/分支从 github 下载并编译。\n- `path:~/src/elixir` - 要使用的工具的自定义编译版本的路径。这种方式供语言开发者等使用。\n- `system` - 此关键字会导致 asdf 传递系统上未由 asdf 管理的工具版本。\n\n::: tip 提示\n\n多版本可以通过空格将它们分隔开来。比如，使用 Python `3.7.2` 回退到 Python `2.7.15` 最后回退到 `system` Python，可以将以下行的内容添加到 `.tool-versions` 文件中。\n\n```\npython 3.7.2 2.7.15 system\n```\n\n:::\n\n为了安装 `.tool-versions` 文件中定义的所有工具，在包含 `.tool-versions` 文件的目录中不带其他参数执行 `asdf install` 命令。\n\n为了安装 `.tool-versions` 文件中定义的某个工具，在包含 `.tool-versions` 文件的目录中运行 `asdf install <name>` 命令。这个工具将会安装 `.tool-versions` 文件所指定的版本。\n\n可以直接编辑这个文件或者使用 `asdf local` （或者 `asdf global`）来更新工具版本。\n\n## `.asdfrc`\n\n`.asdfrc` 文件定义了用户机器的特定配置。\n\n`$HOME/.asdfrc` 是 asdf 使用的默认位置。这可以通过 [环境变量 `ASDF_CONFIG_FILE`](#asdf-config-file) 进行配置。\n\n以下文件展示了所需的格式及其默认值：\n\n```txt\nlegacy_version_file = no\nuse_release_candidates = no\nalways_keep_download = no\nplugin_repository_last_check_duration = 60\ndisable_plugin_short_name_repository = no\nconcurrency = auto\n```\n\n### `legacy_version_file`\n\n插件 **支持** 读取其他版本管理器使用的版本文件，比如，Ruby 的 `rbenv` 的 `.ruby-version` 文件。\n\n| 选项                                                    | 描述                                                     |\n| :------------------------------------------------------ | :------------------------------------------------------- |\n| `no` <Badge type=\"tip\" text=\"默认\" vertical=\"middle\" /> | 从 `.tool-versions` 文件读取版本                         |\n| `yes`                                                   | 如果可行的话，从传统版本文件读取版本（`.ruby-versions`） |\n\n### `always_keep_download`\n\n配置 `asdf install` 命令以保留或删除下载的源代码或二进制文件。\n\n| 选项                                                    | 描述                               |\n| :------------------------------------------------------ | :--------------------------------- |\n| `no` <Badge type=\"tip\" text=\"默认\" vertical=\"middle\" /> | 在成功安装后删除源代码或二进制文件 |\n| `yes`                                                   | 在安装后保留源代码或二进制文件 |\n\n### `plugin_repository_last_check_duration`\n\n配置自上次 asdf 插件存储库同步到下一次存储库同步的持续时间。命令 `asdf plugin add <name>` 或者 `asdf plugin list all` 将会触发持续时间的检查，如果持续时间已过，则进行同步。\n\n| 选项                                                                                          | 描述                                               |\n| :-------------------------------------------------------------------------------------------- | :------------------------------------------------- |\n| 从 `1` 到 `999999999` 的数字 <br/> <Badge type=\"tip\" text=\"默认\" vertical=\"middle\" /> 为 `60` | 如果已过自上次同步的持续时间，触发器事件发生时同步 |\n| `0`                                                                                           | 每个触发器事件发生时同步                           |\n| `never`                                                                                       | 从不同步                                           |\n\n同步事件在执行以下命令时发生：\n\n- `asdf plugin add <name>`\n- `asdf plugin list all`\n\n`asdf plugin add <name> <git-url>` 不会触发插件同步。\n\n::: warning 注意\n\n将值设置为 `never` 并不会阻止插件仓库的初始同步，如需实现此行为，请查看 `disable_plugin_short_name_repository` 了解更多。\n\n:::\n\n### `disable_plugin_short_name_repository`\n\n禁用 asdf 插件的缩写仓库同步功能。如果缩写仓库被禁用，同步事件将提前退出。\n\n| 选项                                                        | 描述                                |\n| :--------------------------------------------------------- | :--------------------------------- |\n| `no` <Badge type=\"tip\" text=\"default\" vertical=\"middle\" /> | 在同步事件发生时克隆或更新 asdf 插件仓库 |\n| `yes`                                                      | 禁用插件缩写仓库                      |\n\n同步事件在执行以下命令时发生：\n\n- `asdf plugin add <name>`\n- `asdf plugin list all`\n\n`asdf plugin add <name> <git-url>` 不会触发插件同步。\n\n::: warning 注意\n\n禁用插件缩写仓库不会删除该仓库，如果它已经同步过。使用 `rm --recursive --trash $ASDF_DATA_DIR/repository` 才可以删除插件仓库。\n\n禁用插件缩写仓库不会删除从该源之前安装的插件。可使用 `asdf plugin remove <name>` 命令删除插件。删除插件将移除该管理工具所有已安装版本。\n\n:::\n\n### `concurrency`\n\n编译时使用的默认核心数。\n\n| 选项 | 描述                                                                                        |\n| :------ | :--------------------------------------------------------------------------------------------------- |\n| integer | 编译源代码时使用的核心数 code                                                |\n| `auto`  | 使用 `nproc` 命令计算核心数量，然后使用 `sysctl hw.ncpu` 命令，接着查看 `/proc/cpuinfo` 文件，如果无法获取则默认使用 `1`。 |\n\n注意：如果设置了环境变量 `ASDF_CONCURRENCY`，则该变量具有优先级。\n\n### 插件钩子\n\n可以执行自定义代码：\n\n- 在插件安装、重新加载、更新或卸载之前或之后\n- 在执行插件命令之前或之后\n\n比如，如果安装了一个名为 `foo` 的插件并提供了 `bar` 可执行文件，则可以使用以下钩子在执行插件命令之前先执行自定义代码：\n\n```text\npre_foo_bar = echo Executing with args: $@\n```\n\n支持以下模式：\n\n- `pre_<plugin_name>_<command>`\n- `pre_asdf_download_<plugin_name>`\n- `{pre,post}_asdf_{install,reshim,uninstall}_<plugin_name>`\n  - `$1`: 完整版本\n- `{pre,post}_asdf_plugin_{add,update,remove,reshim}`\n  - `$1`: 插件名称\n- `{pre,post}_asdf_plugin_{add,update,remove}_<plugin_name>`\n\n请查看 [创建插件](../plugins/create.md) 了解在哪些命令执行之前或之后会运行哪些命令钩子。\n\n## 环境变量\n\n设置环境变量会因系统和 Shell 的不同而有所差异。默认位置取决于安装位置和方法（Git 克隆、Homebrew、AUR）。\n\n环境变量通常应在加载 `asdf.sh`/`asdf.fish` 等文件之前设置。对于 Elvish，应在 `use asdf` 之前设置。\n\n以下内容描述了在 Bash Shell 中的使用方法。\n\n### `ASDF_CONFIG_FILE`\n\n`.asdfrc` 配置文件的路径。可以设置为任何位置。必须是绝对路径。\n\n- 如果未设置：将使用 `$HOME/.asdfrc`。\n- 使用方法：`export ASDF_CONFIG_FILE=/home/john_doe/.config/asdf/.asdfrc`\n\n### `ASDF_TOOL_VERSIONS_FILENAME`\n\n用于存储工具名称和版本的文件名。可以是任何合法的文件名。通常不建议设置这个值，除非你希望忽略 `.tool-versions` 文件。\n\n- 如果未设置：将使用 `.tool-versions`。\n- 使用方法：`export ASDF_TOOL_VERSIONS_FILENAME=tool_versions`\n\n### `ASDF_DIR`\n\n`asdf` 核心脚本的位置。可以设置为任何位置，必须是绝对路径。\n\n- 如果未设置，将使用 `bin/asdf` 可执行文件的父目录。\n- 使用方法：`export ASDF_DIR=/home/john_doe/.config/asdf`\n\n### `ASDF_DATA_DIR`\n\n`asdf` 安装插件、垫片和工具版本的位置，可以设置为任何位置，必须是绝对路径。\n\n- 如果未设置：将使用 `$HOME/.asdf` 如果存在，或者 `ASDF_DIR` 的值。\n- 使用方法：`export ASDF_DATA_DIR=/home/john_doe/.asdf`\n\n### `ASDF_CONCURRENCY`\n\n编译源代码时使用的 CPU 核心数。如果设置了这个值，它将优先于 asdf 配置中的 `concurrency` 值。\n\n- 如果未设置：将使用 asdf 配置中的 `concurrency` 值。\n- 使用方法：`export ASDF_CONCURRENCY=32`\n\n## 全配置样例\n\n按照以下简单的 asdf 配置：\n\n- 使用 Bash Shell\n- 安装位置为 `$HOME/.asdf`\n- 通过 Git 安装\n- **未**设置任何环境变量\n- **没有**自定义的 `.asdfrc` 文件\n\n将会产生以下结果：\n\n| 配置                                   | 值               | 如何计算                                                                                                               |\n| :------------------------------------ | :--------------- | :-------------------------------------------------------------------------------------------------------------------- |\n| 配置文件位置                            | `$HOME/.asdfrc`  | `ASDF_CONFIG_FILE` 是空的，所以请使用 `$HOME/.asdfrc`                                                                    |\n| 默认工具版本声明文件名                    | `.tool-versions` | `ASDF_TOOL_VERSIONS_FILENAME` 是空的，所以请使用 `.tool-versions`                                                       |\n| asdf 目录                              | `$HOME/.asdf`    | `ASDF_DIR` 是空的，所以请使用 `bin/asdf` 的父目录                                                                         |\n| asdf 数据目录                          | `$HOME/.asdf`     | `ASDF_DATA_DIR` 是空的，所以请使用 `$HOME/.asdf` 因为 `$HOME` 存在                                                        |\n| concurrency                           | `auto`           | `ASDF_CONCURRENCY` 是空的，所以依赖于 [默认配置](https://github.com/asdf-vm/asdf/blob/master/defaults) 的 `concurrency` 值 |\n| legacy_version_file                   | `no`             | 没有自定义 `.asdfrc`，所以请使用 [默认配置](https://github.com/asdf-vm/asdf/blob/master/defaults)                          |\n| use_release_candidates                | `no`             | 没有自定义 `.asdfrc`，所以请使用 [默认配置](https://github.com/asdf-vm/asdf/blob/master/defaults)                          |\n| always_keep_download                  | `no`             | 没有自定义 `.asdfrc`，所以请使用 [默认配置](https://github.com/asdf-vm/asdf/blob/master/defaults)                          |\n| plugin_repository_last_check_duration | `60`             | 没有自定义 `.asdfrc`，所以请使用 [默认配置](https://github.com/asdf-vm/asdf/blob/master/defaults)                          |\n| disable_plugin_short_name_repository  | `no`             | 没有自定义 `.asdfrc`，所以请使用 [默认配置](https://github.com/asdf-vm/asdf/blob/master/defaults)                          |\n"
  },
  {
    "path": "docs/zh-hans/manage/core.md",
    "content": "# 核心\n\n核心 `asdf` 命令列表很小，但可以促进很多工作流。\n\n## 安装和配置\n\n请查看 [快速上手](/zh-hans/guide/getting-started.md) 了解更多详情。\n\n## Exec\n\n```shell\nasdf exec <command> [args...]\n```\n\n执行当前版本的命令垫片。\n\n<!-- TODO: expand on this with example -->\n\n## Env\n\n```shell\nasdf env <command> [util]\n```\n\n<!-- TODO: expand on this with example -->\n\n## Info\n\n```shell\nasdf info\n```\n\n用于打印操作系统、Shell 和 `asdf` 调试信息的辅助命令。在报告 bug 时需要共享这些信息。\n\n## Reshim\n\n```shell\nasdf reshim <name> <version>\n```\n\n这将为某个包的当前版本重新创建垫片。默认情况下，垫片是在某个工具安装的过程中由插件创建。一些工具像 [npm 命令行](https://docs.npmjs.com/cli/) 允许全局安装可执行程序，比如使用 `npm install -g yarn` 命令安装 [Yarn](https://yarnpkg.com/)。因为这个可执行程序不是通过插件生命周期安装的，所以还没有对应的垫片存在。`asdf reshim nodejs <version>` 命令将会强制重新计算任何新可执行程序的垫片，类似 `nodejs` 的 `versions` 版本下的 `yarn`。\n\n## Shim-versions\n\n```shell\nasdf shimversions <command>\n```\n\n列举为命令提供垫片的插件和版本。\n\n例如，[Node.js](https://nodejs.org/) 附带了两个可执行程序，`node` 和 `npm`。当使用 [`asdf-nodejs`](https://github.com/asdf-vm/asdf-nodejs/) 插件安装了这些工具的很多版本时，执行`shimversions` 命令会返回：\n\n```shell\n➜ asdf shimversions node\nnodejs 14.8.0\nnodejs 14.17.3\nnodejs 16.5.0\n```\n\n```shell\n➜ asdf shimversions npm\nnodejs 14.8.0\nnodejs 14.17.3\nnodejs 16.5.0\n```\n\n## 更新\n\n请使用与安装 `asdf` 相同的方法进行更新。`asdf` 的最新版本显示在本页面的右上角。\n\n## 卸载\n\n根据以下步骤卸载 `asdf`：\n\n::: details Bash & Git\n\n1. 在 `~/.bashrc` 配置文件中移除生效 `asdf.sh` 和补全功能的行：\n\n```shell\n. \"$HOME/.asdf/asdf.sh\"\n. \"$HOME/.asdf/completions/asdf.bash\"\n```\n\n2. 移除 `$HOME/.asdf` 目录：\n\n```shell\nrm -rf \"${ASDF_DATA_DIR:-$HOME/.asdf}\"\n```\n\n3. 执行以下命令移除 `asdf` 所有配置文件：\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Bash & Git (macOS)\n\n1. 在 `~/.bash_profile` 配置文件中移除生效 `asdf.sh` 和补全功能的行：\n\n```shell\n. \"$HOME/.asdf/asdf.sh\"\n. \"$HOME/.asdf/completions/asdf.bash\"\n```\n\n2. 移除 `$HOME/.asdf` 目录：\n\n```shell\nrm -rf \"${ASDF_DATA_DIR:-$HOME/.asdf}\"\n```\n\n3. 执行以下命令移除 `asdf` 所有配置文件：\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Bash & Homebrew\n\n1. 在 `~/.bashrc` 配置文件中移除生效 `asdf.sh` 和补全功能的行：\n\n```shell\n. $(brew --prefix asdf)/libexec/asdf.sh\n. $(brew --prefix asdf)/etc/bash_completion.d/asdf.bash\n```\n\n补全功能可能已经如 [Homebrew 的指南](https://docs.brew.sh/Shell-Completion#configuring-completions-in-bash) 那样配置了，因此请按照他们的指南找出要删除的内容。\n\n2. 用包管理器卸载：\n\n```shell\nbrew uninstall asdf --force\n```\n\n3. 执行以下命令移除 `asdf` 所有配置文件：\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Bash & Homebrew (macOS)\n\n如果你正在使用 **macOS Catalina 以及更新版本**，默认的 shell 已经变成了 **ZSH**。如果你在 `~/.bash_profile` 文件中找不到任何配置，则可能位于 `~/.zshrc` 中。在这种情况下，请按照 ZSH 指南进行操作。\n\n1. 在 `~/.bash_profile` 配置文件中移除生效 `asdf.sh` 和补全功能的行：\n\n```shell\n. $(brew --prefix asdf)/libexec/asdf.sh\n. $(brew --prefix asdf)/etc/bash_completion.d/asdf.bash\n```\n\n补全功能可能已经如 [Homebrew 的指南](https://docs.brew.sh/Shell-Completion#configuring-completions-in-bash) 那样配置了，因此请按照他们的指南找出要删除的内容。\n\n2. 用包管理器卸载：\n\n```shell\nbrew uninstall asdf --force\n```\n\n3. 执行以下命令移除 `asdf` 所有配置文件：\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Bash & Pacman\n\n1. 在 `~/.bashrc` 配置文件中移除生效 `asdf.sh` 和补全功能的行：\n\n```shell\n. /opt/asdf-vm/asdf.sh\n```\n\n2. 用包管理器卸载：\n\n```shell\npacman -Rs asdf-vm\n```\n\n3. 移除 `$HOME/.asdf` 目录：\n\n```shell\nrm -rf \"${ASDF_DATA_DIR:-$HOME/.asdf}\"\n```\n\n4. 执行以下命令移除 `asdf` 所有配置文件：\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Fish & Git\n\n1. 在 `~/.config/fish/config.fish` 配置文件中移除生效 `asdf.fish` 的行：\n\n```shell\nsource ~/.asdf/asdf.fish\n```\n\n以及使用以下命令移除补全功能：\n\n```shell\nrm -rf ~/.config/fish/completions/asdf.fish\n```\n\n2. 移除 `$HOME/.asdf` 目录：\n\n```shell\nrm -rf (string join : -- $ASDF_DATA_DIR $HOME/.asdf)\n```\n\n3. 执行以下命令移除 `asdf` 所有配置文件：\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Fish & Homebrew\n\n1. 在 `~/.config/fish/config.fish` 配置文件中移除生效 `asdf.fish` 的行：\n\n```shell\nsource \"(brew --prefix asdf)\"/libexec/asdf.fish\n```\n\n2. 用包管理器卸载：\n\n```shell\nbrew uninstall asdf --force\n```\n\n3. 执行以下命令移除 `asdf` 所有配置文件：\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Fish & Pacman\n\n1. 在 `~/.config/fish/config.fish` 配置文件中移除生效 `asdf.fish` 的行：\n\n```shell\nsource /opt/asdf-vm/asdf.fish\n```\n\n2. 用包管理器卸载：\n\n```shell\npacman -Rs asdf-vm\n```\n\n3. 移除 `$HOME/.asdf` 目录：\n\n```shell\nrm -rf (string join : -- $ASDF_DATA_DIR $HOME/.asdf)\n```\n\n4. 执行以下命令移除 `asdf` 所有配置文件：\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Elvish & Git\n\n1. 在 `~/.config/elvish/rc.elv` 配置文件中移除使用 `asdf` 模块的行：\n\n```shell\nuse asdf _asdf; var asdf~ = $_asdf:asdf~\nset edit:completion:arg-completer[asdf] = $_asdf:arg-completer~\n```\n\n以及使用以下命令卸载 `asdf` 模块：\n\n```shell\nrm -f ~/.config/elvish/lib/asdf.elv\n```\n\n2. 移除 `$HOME/.asdf` 目录：\n\n```shell\nif (!=s $E:ASDF_DATA_DIR \"\") { rm -rf $E:ASDF_DATA_DIR } else { rm -rf ~/.asdf }\n```\n\n3. 执行以下命令移除 `asdf` 所有配置文件：\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Elvish & Homebrew\n\n1. 在 `~/.config/elvish/rc.elv` 配置文件中移除使用 `asdf` 模块的行：\n\n```shell\nuse asdf _asdf; var asdf~ = $_asdf:asdf~\nset edit:completion:arg-completer[asdf] = $_asdf:arg-completer~\n```\n\n以及使用以下命令卸载 `asdf` 模块：\n\n```shell\nrm -f ~/.config/elvish/lib/asdf.elv\n```\n\n2. 用包管理器卸载：\n\n```shell\nbrew uninstall asdf --force\n```\n\n3. 执行以下命令移除 `asdf` 所有配置文件：\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details Elvish & Pacman\n\n1. 在 `~/.config/elvish/rc.elv` 配置文件中移除使用 `asdf` 模块的行：\n\n```shell\nuse asdf _asdf; var asdf~ = $_asdf:asdf~\nset edit:completion:arg-completer[asdf] = $_asdf:arg-completer~\n```\n\n以及使用以下命令卸载 `asdf` 模块：\n\n```shell\nrm -f ~/.config/elvish/lib/asdf.elv\n```\n\n2. 用包管理器卸载：\n\n```shell\npacman -Rs asdf-vm\n```\n\n3. 移除 `$HOME/.asdf` 目录：\n\n```shell\nif (!=s $E:ASDF_DATA_DIR \"\") { rm -rf $E:ASDF_DATA_DIR } else { rm -rf ~/.asdf }\n```\n\n4. 执行以下命令移除 `asdf` 所有配置文件：\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details ZSH & Git\n\n1. 在 `~/.zshrc` 配置文件中移除生效 `asdf.sh` 和补全功能的行：\n\n```shell\n. \"$HOME/.asdf/asdf.sh\"\n# ...\nfpath=(${ASDF_DIR}/completions $fpath)\nautoload -Uz compinit\ncompinit\n```\n\n**或者** ZSH 框架插件（如果用了的话）\n\n2. 移除 `$HOME/.asdf` 目录：\n\n```shell\nrm -rf \"${ASDF_DATA_DIR:-$HOME/.asdf}\"\n```\n\n3. 执行以下命令移除 `asdf` 所有配置文件：\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details ZSH & Homebrew\n\n1. 在 `~/.zshrc` 配置文件中移除生效 `asdf.sh` 的行：\n\n```shell\n. $(brew --prefix asdf)/libexec/asdf.sh\n```\n\n2. 用包管理器卸载：\n\n```shell\nbrew uninstall asdf --force && brew autoremove\n```\n\n3. 执行以下命令移除 `asdf` 所有配置文件：\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n::: details ZSH & Pacman\n\n1. 在 `~/.zshrc` 配置文件中移除生效 `asdf.sh` 的行：\n\n```shell\n. /opt/asdf-vm/asdf.sh\n```\n\n2. 用包管理器卸载：\n\n```shell\npacman -Rs asdf-vm\n```\n\n3. 移除 `$HOME/.asdf` 目录\n\n```shell\nrm -rf \"${ASDF_DATA_DIR:-$HOME/.asdf}\"\n```\n\n4. 执行以下命令移除 `asdf` 所有配置文件：\n\n```shell\nrm -rf \"$HOME/.tool-versions\" \"$HOME/.asdfrc\"\n```\n\n:::\n\n恭喜你完成了 🎉\n"
  },
  {
    "path": "docs/zh-hans/manage/dependencies.md",
    "content": "# 依赖\n\n此列表适用于 asdf 版本 0.16.0 及更高版本。旧版本的 asdf 具有额外的依赖。\n\nasdf 本身需要安装以下内容：\n\n* Bash 版本 `3.2.48`\n* Git 版本 `1.7.7.2`\n\n::: tip 注意\n\n请注意，asdf 插件可能需要在使用前安装额外的程序。请阅读插件文档，并确保已安装所有插件所需的依赖，然后再安装插件。\n\n:::\n\n## 安装\n\n如果需要手动安装 asdf 的依赖，请根据操作系统运行以下命令。\n\n<!--@include: @/zh-hans/parts/install-dependencies-cmds.md-->\n\n::: tip 注意\n\n取决于你的系统配置，可能会需要 `sudo`。\n\n:::\n"
  },
  {
    "path": "docs/zh-hans/manage/plugins.md",
    "content": "# 插件\n\n插件告诉 `asdf` 如何处理不同的工具，如 Node.js、 Ruby、 Elixir 等。\n\n请参考 [创建插件](/zh-hans/plugins/create.md) 了解用于支持更多工具的插件 API。\n\n## 添加\n\n通过 Git URL 地址添加插件：\n\n```shell\nasdf plugin add <name> <git-url>\n# asdf plugin add elm https://github.com/vic/asdf-elm\n```\n\n或者通过插件存储库中的缩写添加插件：\n\n```shell\nasdf plugin add <name>\n# asdf plugin add erlang\n```\n\n::: tip 建议\n推荐独立于缩写存储库的、更长的 `git-url` 方法。\n:::\n\n## 列举已安装\n\n```shell\nasdf plugin list\n# asdf plugin list\n# java\n# nodejs\n```\n\n```shell\nasdf plugin list --urls\n# asdf plugin list\n# java            https://github.com/halcyon/asdf-java.git\n# nodejs          https://github.com/asdf-vm/asdf-nodejs.git\n```\n\n## 列举缩写存储库中的所有插件\n\n```shell\nasdf plugin list all\n```\n\n请参考 [插件缩写索引](https://github.com/asdf-vm/asdf-plugins) 了解插件的完整缩写列表。\n\n## 更新\n\n```shell\nasdf plugin update --all\n```\n\n如果你想要更新特定的包，如下所示。\n\n```shell\nasdf plugin update <name>\n# asdf plugin update erlang\n```\n\n这种更新方式将会获取插件存储库的 _源代码_ 的 _默认分支_ 的 _最新提交_。版本化的插件和更新正在开发中 ([#916](https://github.com/asdf-vm/asdf/pull/916))。\n\n## 移除\n\n```bash\nasdf plugin remove <name>\n# asdf plugin remove erlang\n```\n\n移除一个插件将会移除该插件安装的所有工具。这可以当作是清理/修剪工具的许多未使用版本的简单方法。\n\n## 同步缩写存储库\n\n缩写存储库将同步到你的本地计算机并定期刷新。这个周期由以下方法确定：\n\n- 命令触发的同步事件：\n    - `asdf plugin add <name>` \n    - `asdf plugin list all`\n- 如果配置选项 `disable_plugin_short_name_repository` 设置为 `yes`，那么同步操作将提前终止。请查看 [asdf 配置文档](/zh-hans/manage/configuration.md) 了解更多。\n- 如果在过去的 `X` 分钟内没有同步，则进行同步。\n    - `X` 默认是 `60`，但可以通过在 `.asdfrc` 文件中配置 `plugin_repository_last_check_duration` 选项来进行配置。请查看 [asdf 配置文档](/zh-hans/manage/configuration.md) 了解更多。\n"
  },
  {
    "path": "docs/zh-hans/manage/versions.md",
    "content": "# 版本\n\n## 安装版本\n\n```shell\nasdf install <name> <version>\n# asdf install erlang 17.3\n```\n\n如果一个插件支持从源代码下载和编译，你可以指定 `ref:foo`，其中 `foo` 是特定的分支、标签或者提交。卸载该版本时，你也需要使用相同的名称和引用。\n\n## 安装最新稳定版本\n\n```shell\nasdf install <name> latest\n# asdf install erlang latest\n```\n\n安装给定字符串开头的最新稳定版本。\n\n```shell\nasdf install <name> latest:<version>\n# asdf install erlang latest:17\n```\n\n## 列举已安装版本\n\n```shell\nasdf list <name>\n# asdf list erlang\n```\n\n筛选出以给定字符串开头的版本。\n\n```shell\nasdf list <name> <version>\n# asdf list erlang 17\n```\n\n## 列举所有可用版本\n\n```shell\nasdf list all <name>\n# asdf list all erlang\n```\n\n筛选出以给定字符串开头的版本。\n\n```shell\nasdf list all <name> <version>\n# asdf list all erlang 17\n```\n\n## 显示最新稳定版本\n\n```shell\nasdf latest <name>\n# asdf latest erlang\n```\n\n显示以给定字符串开头的最新稳定版本。\n\n```shell\nasdf latest <name> <version>\n# asdf latest erlang 17\n```\n\n## 设置版本\n\n#### 通过 `.tool-versions` 文件\n\n```shell\nasdf set [flags] <name> <version> [<version>...]\n# asdf set elixir 1.2.4 # set in current dir\n# asdf set -u elixir 1.2.4 # set in .tool-versions file in home directory\n# asdf set -p elixir 1.2.4 # set in existing .tool-versions file in a parent dir\n\nasdf set <name> latest[:<version>]\n# asdf set elixir latest\n```\n\n`asdf set` 将版本信息写入当前目录下的 `.tool-versions` 文件中，若该文件不存在则自动创建。此功能仅为方便使用而设。你可以将其视为执行 `echo \"<tool> <version>\" > .tool-versions` 的操作。\n\n使用 `-u`/`--home` 选项时，`asdf set` 将写入位于 `$HOME` 目录下的 `.tool-versions` 文件，如果该文件不存在则创建它。\n\n使用 `-p`/`--parent` 选项时，`asdf set` 会寻找在当前目录的最近父目录的 `.tool-versions` 文件进行操作。\n\n#### 通过环境变量\n\n在确定版本时，系统会查询符合模式的环境变量 `ASDF_${TOOL}_VERSION`。该版本格式与 `.tool-versions` 文件中支持的格式一致。如果设置了该环境变量，其值将覆盖任何 `.tool-versions` 文件中为该工具设置的版本。例如：\n\n```shell\nexport ASDF_ELIXIR_VERSION=1.18.1\n```\n\n这将会告诉 asdf 在当前会话中使用 Elixir `1.18.1`。\n\n:::warning 警告\n由于这是一个环境变量，它仅在设置的位置生效。其他正在运行的 shell 会话仍将使用在 `.tool-versions` 文件中设置的版本。\n\n请查看 [配置部分](/zh-hans/manage/configuration.md) 的 `.tool-versions` 文件了解更多详情。\n:::\n\n下面的示例在版本为 `1.4.0` 的 Elixir 项目上运行测试。\n\n```shell\nASDF_ELIXIR_VERSION=1.4.0 mix test\n```\n\n## 回退到系统版本\n\n要使用工具 `<name>` 的系统版本而非 asdf 管理版本，你可以将工具的版本设置为 `system`。\n\n使用 `asdf set` 命令或如上文 [设置版本](#设置版本) 部分所述的环境变量来设置系统。\n\n```shell\nasdf set <name> system\n# asdf set python system\n```\n\n## 显示当前版本\n\n```shell\nasdf current\n# asdf current\n# erlang          17.3          /Users/kim/.tool-versions\n# nodejs          6.11.5        /Users/kim/cool-node-project/.tool-versions\n\nasdf current <name>\n# asdf current erlang\n# erlang          17.3          /Users/kim/.tool-versions\n```\n\n## 卸载版本\n\n```shell\nasdf uninstall <name> <version>\n# asdf uninstall erlang 17.3\n```\n\n## 垫片（Shims）\n\n当 asdf 安装一个包时，它会在 `$ASDF_DATA_DIR/shims` 目录（默认为 `~/.asdf/shims`）中为该包中的每个可执行程序创建垫片。这个位于 `$PATH` 中（通过 `asdf.sh`、 `asdf.fish` 等等实现）的目录是已安装程序在环境中可用的方式。\n\n垫片本身是非常简单的包装器，它 `exec` （执行）一个辅助程序 `asdf exec`，向其传递插件的名称和垫片正在包装的已安装包中的可执行程序的路径。\n\n`asdf exec` 辅助程序确定要使用的软件包版本（比如在 `.tool-versions` 文件中指定，通过 `asdf local ...` 或者 `asdf global ...` 命令选择）、软件包安装目录中的可执行程序的最终路径（这可以在插件中通过 `exec-path` 回调来操作）以及要在其中执行的环境（也由插件 - `exec-env` 脚本提供），最后完成执行。\n\n::: warning 注意\n因为此系统使用 `exec` 调用，所以软件包中的任何脚本如果要由 shell 生效而不是执行的脚本都需要直接访问，而不是通过垫片包装器进行访问。两个 `asdf` 命令：`which` 和 `where` 可以通过返回已安装软件包的路径来帮助解决这个问题。\n:::\n\n```shell\n# 返回当前版本中主要可执行程序的路径\nsource $(asdf which ${PLUGIN})/../script.sh\n\n# 返回软件包安装目录的路径\nsource $(asdf where ${PLUGIN})/bin/script.sh\n```\n\n### 绕过 asdf 垫片\n\n如果由于某种原因，你希望绕过 asdf 垫片，或者希望在进入项目目录时自动设置环境变量，则 [asdf-direnv](https://github.com/asdf-community/asdf-direnv) 插件可能会有所帮助。请务必查看其 README 文件了解更多详情。\n"
  },
  {
    "path": "docs/zh-hans/more/community-projects.md",
    "content": "# Community Projects\n\nHere are some community projects related to `asdf`:\n\n- [asdf-community](https://github.com/asdf-community): A collaborative,\n  community-driven project for long-term maintenance of asdf plugins.\n- [asdf dev container](https://github.com/iloveitaly/asdf-devcontainer): A\n  [GitHub Dev Container](https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/introduction-to-dev-containers)\n  supporting asdf managed tools in GitHub Codespaces.\n\n::: warning Note\n\nasdf core do not own these projects or their code. asdf core are not responsible\nfor the quality or security as they relate to those listed here.\n\n:::\n"
  },
  {
    "path": "docs/zh-hans/more/faq.md",
    "content": "# FAQ\n\n以下是 `asdf` 相关的一些常见问题。\n\n## 支持 WSL1 吗？\n\nWSL1 ([Windows Subsystem for Linux 1](https://en.wikipedia.org/wiki/Windows_Subsystem_for_Linux#WSL_1)) 不受官方支持。`asdf` 的某些方面可能无法正常工作。我们不打算添加对 WSL1 的官方支持。\n\n## 支持 WSL2 吗？\n\nWSL2 ([Windows Subsystem for Linux 2](https://en.wikipedia.org/wiki/Windows_Subsystem_for_Linux#WSL_2)) 应该作为你选择的 WSL 发行版来使用本设置和依赖说明。\n\n重要的是，只有当前工作目录是 Unix 驱动器而不是绑定的 Windows 驱动器时，WSL2 _才能_ 正常工作。\n\n当 Github Actions 上提供主机运行器支持时，我们打算在 WSL2 上运行测试套件。（Github Actions 目前还未提供 WSL2 支持）\n\n## 新安装的可执行程序无法运行？\n\n> 我执行了 `npm install -g yarn` 命令，但是之后不能运行 `yarn` 命令。这是为什么？\n\n`asdf` 使用 [垫片](<https://zh.wikipedia.org/wiki/垫片_(程序设计)>) 来管理可执行程序。插件所安装的那些命令会自动创建垫片，而通过 `asdf` 管理工具安装过的可执行程序则需要通知 `asdf` 创建垫片的需要。在这个例子中，为 [Yarn](https://yarnpkg.com/) 创建一个垫片即可。请查看 [`asdf reshim` 命令文档](/zh-hans/manage/core.md#reshim) 了解更多。\n\n## Shell 没有检测到新安装的垫片？\n\n如果 `asdf reshim` 没有解决你的问题，那么很有可能是在 `asdf.sh` 或者 `asdf.fish` 的生效不在你的 Shell 配置文件（`.bash_profile`、`.zshrc`、`config.fish` 等等）的**下方**。这需要你在设置你的 `$PATH` **之后**和生效你的框架（oh-my-zsh 等等）（如果有的话）**之后**再生效。\n\n## 为什么不能在 `.tool-versions` 文件中使用 `latest` 版本？\n\nasdf 必须始终使用当前目录的每个工具的精确版本，不允许使用版本范围或特殊值（如 `latest`）。这确保 asdf 在不同时间和不同机器上以确定性和一致性方式运行。像 `latest` 这样的特殊版本会随时间变化，并且如果在不同时间运行 `asdf install`，不同机器上的版本可能会有所不同。因此，它可以在 asdf 命令如 `asdf set <tool> latest` 中使用，但在 `.tool-versions` 文件中是被禁止的。\n\n可将 `.tool-versions` 文件看成 `Gemfile.lock` 或者 `package-lock.json`。该文件包含项目依赖的每个工具的精确版本。\n\n需要注意的是，`system` 版本在 `.tool-versions` 文件中是允许的，且在使用时可能解析为不同版本。这是一个特殊值，可有效禁用指定目录下特定工具的 asdf 功能。\n\n请查看 https://github.com/asdf-vm/asdf/issues/1012 了解更多。\n\n## 为什么不能在 `.tool-versions` 文件中使用版本范围？\n\n与上述关于使用 `latest` 的问题类似。如果指定了版本范围，asdf 将可以自由选择该范围内的任何已安装版本。这可能导致不同机器上出现不同行为，因为它们可能安装了不同版本。asdf 的设计意图是完全确定性的，即相同的 `.tool-versions` 文件在不同时间和不同计算机上应生成完全相同的环境。\n\n请查看 https://github.com/asdf-vm/asdf-nodejs/issues/235#issuecomment-885809776 了解更多。\n\n## 为什么与我使用的插件完全无关的命令会被 asdf 生成垫片？\n\n**asdf 只会为其管理的可执行文件生成垫片**。例如，如果你使用 Ruby 插件，那么你可能会看到 `ruby` 和 `irb` 等命令被垫片替换，以及你安装的 Ruby 包中包含的其他可执行文件。\n\n如果你看到一个意料之外的垫片，很可能是因为你通过 asdf 管理工具安装了一个包，而该包提供了该可执行文件。\n\n当可执行文件与系统中已存在的可执行文件名称相同，这种情况会令人意外。[部分用户报告称](https://github.com/asdf-vm/asdf/issues/584) 某个 Node.JS 包提供了自己的 `which` 命令版本。这导致 asdf 为其创建了垫片，并替换了操作系统中已存在的 `which` 命令版本。在这种情况下，最好定位引入可执行文件的包并将其移除。`asdf which <command>` 命令可帮助你确定问题可执行文件的位置，从而判断是哪一个包添加了可执行文件。\n\n请查看 https://github.com/asdf-vm/asdf/issues/584 https://github.com/asdf-vm/asdf/issues/1653 了解更多。\n"
  },
  {
    "path": "docs/zh-hans/more/thanks.md",
    "content": "# 致谢\n\nasdf 作者和贡献者的感谢页！\n\n## 作者\n\n我 ([@HashNuke](https://github.com/HashNuke))，高烧、感冒、咳嗽。\n\n版权 2014 直到时间尽头 ([MIT License](https://github.com/asdf-vm/asdf/blob/master/LICENSE))\n\n## 维护者\n\n- [@HashNuke](https://github.com/HashNuke)\n- [@danhper](https://github.com/danhper)\n- [@Stratus3D](https://github.com/Stratus3D)\n- [@vic](https://github.com/vic)\n- [@jthegedus](https://github.com/jthegedus)\n\n## 贡献者\n\n请查看 Github 上的 [贡献者名单](https://github.com/asdf-vm/asdf/graphs/contributors) 🙏 了解更多。\n"
  },
  {
    "path": "docs/zh-hans/parts/install-dependencies-cmds.md",
    "content": "| 操作系统 |    包管理器     | 命令                               |\n| ----- | --------------- | ---------------------------------- |\n| linux | Aptitude        | `apt install git bash`             |\n| linux | DNF             | `dnf install git bash`             |\n| linux | Pacman          | `pacman -S git bash`               |\n| linux | Zypper          | `zypper install git bash`          |\n| macOS | Homebrew        | `brew install coreutils git bash`  |\n| macOS | Spack           | `spack install coreutils git bash` |\n"
  },
  {
    "path": "docs/zh-hans/parts/install-dependencies.md",
    "content": "##### 安装依赖\n\nasdf 主要依赖 `git`。 这是一份 _非穷举_ 的命令清单用于运行 _你的_ 包管理器（有些可能在后面的步骤自动安装这些工具）。\n\n<!--@include: @/zh-hans/parts/install-dependencies-cmds.md-->\n\n::: tip 注意\n\n取决于你的系统配置，可能会需要 `sudo`。\n\n:::\n\n请查看 [依赖](/zh-hans/manage/dependencies) 页面了解更多。\n"
  },
  {
    "path": "docs/zh-hans/plugins/create.md",
    "content": "# 创建插件\n\n插件是一个包含一些可执行脚本的 Git 存储库，用于支持对某种语言/工具进行版本控制。这些脚本由 asdf 通过命令运行，以支持诸如 `asdf list-all <name>`、`asdf install <name> <version>` 等功能。\n\n## 快速入门\n\n创建自己的插件有两种方式：\n\n1. 使用 [asdf-vm/asdf-plugin-template](https://github.com/asdf-vm/asdf-plugin-template) 仓库来 [生成](https://github.com/asdf-vm/asdf-plugin-template/generate) 一个插件仓库（命名为 `asdf-<tool_name>`），其中已实现默认脚本。生成后，克隆该仓库并运行 `setup.bash` 脚本以交互式更新模版。\n2. 创建名为 `asdf-<tool_name>` 的仓库，并实现文档中列出的所需脚本。\n\n### 插件脚本的黄金规则\n\n- 脚本**不应**调用其他 `asdf` 命令\n- 保持 Shell 工具/命令的依赖列表简短\n- 避免使用非便携式工具或命令标志。例如，`sort -V`。请参考 asdf 核心 [禁止命令列表](https://github.com/asdf-vm/asdf/blob/master/test/banned_commands.bats)。\n\n## 脚本概述\n\n可从 asdf 调用的所有脚本的完整列表。\n\n| 脚本                                                                                                   | 描述                                   |\n| :---------------------------------------------------------------------------------------------------- |:---------------------------------------|\n| [bin/list-all](#bin-list-all) <Badge type=\"tip\" text=\"必要\" vertical=\"middle\" />                       | 列出所有可安装的版本                      |\n| [bin/download](#bin-download) <Badge type=\"warning\" text=\"推荐\" vertical=\"middle\" />                   | 下载指定版本的源代码或二进制文件            |\n| [bin/install](#bin-install) <Badge type=\"tip\" text=\"必要\" vertical=\"middle\" />                         | 安装指定版本                             |\n| [bin/latest-stable](#bin-latest-stable) <Badge type=\"warning\" text=\"推荐\" vertical=\"middle\" />         | 列出指定工具的最新稳定版本                 |\n| [bin/help.overview](#bin-help.overview)                                                               | 输出插件及工具的通用描述                   |\n| [bin/help.deps](#bin-help.deps)                                                                       | 输出按操作系统分类的依赖项列表              |\n| [bin/help.config](#bin-help.config)                                                                   | 输出插件或工具的配置信息                   |\n| [bin/help.links](#bin-help.links)                                                                     | 输出插件或工具的链接列表                   |\n| [bin/list-bin-paths](#bin-list-bin-paths)                                                             | 列出包含二进制文件的目录的相对路径以创建垫片  |\n| [bin/exec-env](#bin-exec-env)                                                                         | 为运行二进制文件准备环境                   |\n| [bin/exec-path](#bin-exec-path)                                                                       | 输出工具某个版本的可执行路径                |\n| [bin/uninstall](#bin-uninstall)                                                                       | 卸载工具的特定版本Uninstall               |\n| [bin/list-legacy-filenames](#bin-list-legacy-filenames)                                               | 输出旧版本文件的文件名：`.ruby-version`    |\n| [bin/parse-legacy-file](#bin-parse-legacy-file)                                                       | 用于解析旧版本文件的自定义解析器            |\n| [bin/post-plugin-add](#bin-post-plugin-add)                                                           | 在插件添加后执行的钩子                    |\n| [bin/post-plugin-update](#bin-post-plugin-update)                                                     | 插件更新后执行的钩子                      |\n| [bin/pre-plugin-remove](#bin-pre-plugin-remove)                                                       | 插件删除前执行的钩子                      |\n\n要查看哪些命令调用了哪些脚本，请参考每个脚本的详细文档。\n\n## 环境变量概述\n\n所有脚本中使用的环境变量完整列表。\n\n| 环境变量                  | 描述                                            |\n| :----------------------- |:-----------------------------------------------|\n| `ASDF_INSTALL_TYPE`      | `version` 或 `ref`                             |\n| `ASDF_INSTALL_VERSION`   | 完整版本号或 Git 引用，取决于 `ASDF_INSTALL_TYPE`  |\n| `ASDF_INSTALL_PATH`      | 工具 _应_ 安装或 _已_ 安装的路径                   |\n| `ASDF_CONCURRENCY`       | 编译源代码时使用的核心数。用于设置 `make -j`        |\n| `ASDF_DOWNLOAD_PATH`     | `bin/download` 下载源代码或二进制文件的路径        |\n| `ASDF_PLUGIN_PATH`       | 插件的安装路径                                   |\n| `ASDF_PLUGIN_SOURCE_URL` | 插件的来源 URL                                  |\n| `ASDF_PLUGIN_PREV_REF`   | 插件仓库的上一版本 `git-ref`                     |\n| `ASDF_PLUGIN_POST_REF`   | 插件仓库的更新版本 `git-ref`                     |\n| `ASDF_CMD_FILE`          | 解析为被引用的文件的完整路径                       |\n\n::: tip 注意\n\n**并非所有环境变量在所有脚本中都可以用。** 请查看下方每个脚本的文档，以了解其可用的环境变量。\n\n:::\n\n## 必要的脚本\n\n### `bin/list-all` <Badge type=\"tip\" text=\"必要\" vertical=\"middle\" />\n\n**描述**\n\n列出所有可安装的版本。\n\n**输出格式**\n\n必须打印一个以**空格分隔**的版本列表字符串。例如：\n\n```txt\n1.0.1 1.0.2 1.3.0 1.4\n```\n\n最新版本应放在最后。\n\nasdf core 会将每个版本单独打印在单独的行上，可能导致部分版本超出屏幕范围。\n\n**排序**\n\n如果版本是从网站的发布页面获取的，建议保持提供的顺序，因为它们通常已经按正确顺序排列。如果版本顺序相反，通过 `tac` 管道处理应\n能解决问题。\n\n如果排序不可避免，`sort -V` 不可移植，因此我们建议：\n\n- [使用 Git 的排序功能](https://github.com/asdf-vm/asdf-plugin-template/blob/main/template/lib/utils.bash)\n  （需要 Git >= `v2.18.0`）\n- [编写自定义排序方法](https://github.com/vic/asdf-idris/blob/master/bin/list-all#L6)\n  （需要 `sed`、`sort` 和 `awk`）\n\n**脚本可用的环境变量**\n\n此脚本未提供任何环境变量。\n\n**调用此脚本的命令**\n\n- `asdf list all <name> [version]`\n- `asdf list all nodejs`：列出此脚本返回的所有版本，每个版本占一行。\n- `asdf list all nodejs 18`：列出此脚本返回的所有版本，每个版本占一行，并应用过滤器匹配以 `18` 开头的任何版本。\n\n**asdf 核心调用签名**\n\n未提供参数。\n\n```bash\n“${plugin_path}/bin/list-all”\n```\n\n---\n\n### `bin/download` <Badge type=\"tip\" text=\"必要\" vertical=\"middle\" />\n\n**描述**\n\n将特定版本的工具的源代码或二进制文件下载到指定位置。\n\n**实现细节**\n\n- 脚本必须将源代码或二进制文件下载到由 `ASDF_DOWNLOAD_PATH` 指定的目录中。\n- 仅应将解压后的源代码或二进制文件放置在 `ASDF_DOWNLOAD_PATH` 目录中。\n- 失败时，不应将任何文件放置在 `ASDF_DOWNLOAD_PATH` 中。\n- 成功时应以 `0` 退出。\n- 失败时应以非零状态退出。\n\n**旧插件**\n\n尽管此脚本被标记为所有插件的 _必需_，但对于在引入此脚本之前存在的“旧”插件，它是 _可选_ 的。\n\n如果此脚本缺失，asdf 将假设 `bin/install` 脚本存在，并会下载**并**安装该版本。\n\n所有插件必须包含此脚本，因为对旧插件的支持最终将被移除。\n\n**脚本可用的环境变量**\n\n- `ASDF_INSTALL_TYPE`：`version` 或 `ref`\n- `ASDF_INSTALL_VERSION`：\n  - 如果 `ASDF_INSTALL_TYPE=version`，则为完整版本号。\n  - 如果 `ASDF_INSTALL_TYPE=ref`，则为 Git 引用（标签/提交/分支）。\n- `ASDF_INSTALL_PATH`：工具 _已_ 安装或 _应_ 安装的路径。\n- `ASDF_DOWNLOAD_PATH`：源代码或二进制文件的下载路径。\n\n**调用此脚本的命令**\n\n- `asdf install <tool> [version]]`\n- `asdf install <tool> latest[:version]`\n- `asdf install nodejs 18.0.0`：下载 Node.js 版本 `18.0.0` 的源代码或二进制文件，\n  并将它们放置在 `ASDF_DOWNLOAD_PATH` 目录中。然后运行 `bin/install` 脚本。\n\n**asdf 核心的调用签名**\n\n未提供参数。\n\n```bash\n\"${plugin_path}\"/bin/download\n```\n\n---\n\n### `bin/install` <Badge type=\"tip\" text=\"必要\" vertical=\"middle\" />\n\n**描述**\n\n将特定版本的工具安装到指定位置。\n\n**实现细节**\n\n- 脚本应将指定版本安装到路径 `ASDF_INSTALL_PATH` 中。\n- 默认情况下，`$ASDF_INSTALL_PATH/bin` 目录下的文件将自动垫片（shims）。此行为可通过可选的\n[bin/list-bin-paths](#bin-list-bin-paths) 脚本进行自定义。\n- 成功应以 `0` 退出。\n- 失败应以非零状态退出。\n- 为避免 TOCTOU（Time-of-Check-to-Time-of-Use）问题，确保脚本仅在工具的构建和安装被视为成功后，才将文件放置在 `ASDF_INSTALL_PATH` 中。\n\n**旧插件**\n\n如果 `bin/download` 脚本不存在，该脚本应下载并安装指定版本。\n\n为了与 `0.7._` 之前和 `0.8._` 之后的 asdf 核心版本兼容，检查 `ASDF_DOWNLOAD_PATH` 环境变量的存在。如果设置了该变量，则假设 `bin/download` 脚本已下载该版本，否则在 `bin/install` 脚本中下载源代码。\n\n**脚本可用的环境变量**\n\n- `ASDF_INSTALL_TYPE`：`version` 或 `ref`\n- `ASDF_INSTALL_VERSION`：\n- 如果 `ASDF_INSTALL_TYPE=version`，则为完整版本号。\n  - 如果 `ASDF_INSTALL_TYPE=ref`，则为 Git 引用（标签/提交/分支）。\n- `ASDF_INSTALL_PATH`：工具 _已_ 安装或 _应_ 安装的路径。\n- `ASDF_CONCURRENCY`：编译源代码时使用的核心数。可用于设置如 `make -j` 之类的标志。\n- `ASDF_DOWNLOAD_PATH`：源代码或二进制文件的下载路径。\n\n**调用此脚本的命令**\n\n- `asdf install`\n- `asdf install <tool>`\n- `asdf install <tool> [version]]`\n- `asdf install <tool> latest[:version]`\n- `asdf install nodejs 18.0.0`：在\n`ASDF_INSTALL_PATH` 目录中安装 Node.js 版本 `18.0.0`。\n\n**asdf 核心的调用签名**\n\n未提供参数。\n\n```bash\n\"${plugin_path}\"/bin/install\n```\n\n## 可选脚本\n\n### `bin/latest-stable` <Badge type=\"warning\" text=\"推荐\" vertical=\"middle\" />\n\n**描述**\n\n确定工具的最新稳定版本。如果不存在，asdf 核心将 `tail` `bin/list-all` 的输出，这可能是不希望看到的。\n\n**实现细节**\n\n- 脚本应将工具的最新稳定版本打印到标准输出。\n- 不稳定版本或发布候选版本应被省略。\n- 脚本的第一个参数提供了一个过滤查询。这应用于按版本号或工具提供商过滤输出。\n  - 例如，来自 [ruby 插件](https://github.com/asdf-vm/asdf-ruby) 的 `asdf list all ruby` 输出列出了来自多个提供商的 Ruby 版本：`jruby`、`rbx` 和 `truffleruby` 等。用户提供的过滤器可由插件用于过滤语义化版本和/或提供商。\n    ```\n    > asdf latest ruby\n    3.2.2\n    > asdf latest ruby 2\n    2.7.8\n    > asdf latest ruby truffleruby\n    truffleruby+graalvm-22.3.1\n    ```\n- 成功应以 `0` 退出。\n- 失败应以非零状态退出。\n\n**脚本可用的环境变量**\n\n- `ASDF_INSTALL_TYPE`：`version` 或 `ref`\n- `ASDF_INSTALL_VERSION`：\n  - 若 `ASDF_INSTALL_TYPE=version`，则为完整版本号。\n  - 若 `ASDF_INSTALL_TYPE=ref`，则为 Git 引用（标签/提交/分支）。\n- `ASDF_INSTALL_PATH`：工具 _已_ 安装或 _应_ 安装的路径。\n\n**调用此脚本的命令**\n\n- `asdf set <tool> latest`：将工具的版本设置为该工具的最新稳定版本。\n- `asdf install <tool> latest`：安装工具的最新版本。\n- `asdf latest <tool> [<version>]`：根据可选过滤器输出工具的最新版本。\n- `asdf latest --all`：输出由 asdf 管理的所有工具的最新版本及其安装状态。\n\n**asdf 核心的调用签名**\n\n该脚本应接受一个参数，即过滤查询。\n\n```bash\n\"${plugin_path}\"/bin/latest-stable \"$query\"\n```\n\n---\n\n### `bin/help.overview`\n\n**描述**\n\n输出关于插件和所管理工具的通用描述。\n\n**实现细节**\n\n- 该脚本是显示插件帮助输出所必需的。\n- 不得打印任何标题，因为 asdf 核心会打印标题。\n- 输出可以是自由格式文本，但理想情况下应仅包含一个简短段落。\n- 不得输出已在 asdf-vm 核心文档中涵盖的任何信息。\n- 应根据所安装工具的操作系统和版本进行定制（可选设置环境变量 `ASDF_INSTALL_VERSION` 和 `ASDF_INSTALL_TYPE`）。\n- 成功应以 `0` 退出。\n- 失败应以非零状态退出。\n\n**脚本可用的环境变量**\n\n- `ASDF_INSTALL_TYPE`：`version` 或 `ref`\n- `ASDF_INSTALL_VERSION`：\n  - 若 `ASDF_INSTALL_TYPE=version`，则为完整版本号。\n  - 若 `ASDF_INSTALL_TYPE=ref`，则为 Git 引用（标签/提交/分支）。\n- `ASDF_INSTALL_PATH`：工具 _已_ 安装或 _应_ 安装的路径。\n\n**调用此脚本的命令**\n\n- `asdf help <name> [<version>]`：输出插件和工具的文档\n\n**asdf 核心的调用签名**\n\n```bash\n\"${plugin_path}\"/bin/help.overview\n```\n\n---\n\n### `bin/help.deps`\n\n**描述**\n\n输出针对操作系统定制的依赖项列表。每个依赖项占一行。\n\n```bash\ngit\ncurl\nsed\n```\n\n**实现细节**\n\n- 该脚本需要 `bin/help.overview` 才能被视为有效输出。\n- 应根据操作系统和要安装的工具版本进行定制（可选设置环境变量 `ASDF_INSTALL_VERSION` 和 `ASDF_INSTALL_TYPE`）。\n- 成功应以 `0` 退出。\n- 失败应以非零状态退出。\n\n**脚本可用的环境变量**\n\n- `ASDF_INSTALL_TYPE`：`version` 或 `ref`\n- `ASDF_INSTALL_VERSION`：\n  - 若 `ASDF_INSTALL_TYPE=version`，则为完整版本号。\n  - 若 `ASDF_INSTALL_TYPE=ref`，则为 Git 引用（标签/提交/分支）。\n- `ASDF_INSTALL_PATH`：工具 _已_ 安装或 _应_ 安装的路径。\n\n**调用此脚本的命令**\n\n- `asdf help <name> [<version>]`：输出插件和工具的文档\n\n**asdf 核心的调用签名**\n\n```bash\n\"${plugin_path}\"/bin/help.deps\n```\n\n---\n\n### `bin/help.config`\n\n**描述**\n\n输出插件和工具所需的任何必要或可选配置。例如，描述安装或编译工具所需的任何环境变量或其他标志。\n\n**实现细节**\n\n- 该脚本需要 `bin/help.overview` 才能被视为有效输出。\n- 输出可以是自由格式文本。\n- 应根据所安装工具的操作系统和版本进行定制（可选设置环境变量 `ASDF_INSTALL_VERSION` 和 `ASDF_INSTALL_TYPE`）。\n- 成功应以 `0` 退出。\n- 失败应以非零状态退出。\n\n**脚本可用的环境变量**\n\n- `ASDF_INSTALL_TYPE`：`version` 或 `ref`\n- `ASDF_INSTALL_VERSION`：\n  - 若 `ASDF_INSTALL_TYPE=version`，则为完整版本号。\n  - 若 `ASDF_INSTALL_TYPE=ref`，则为 Git 引用（标签/提交/分支）。\n- `ASDF_INSTALL_PATH`：工具 _已_ 安装或 _应_ 安装的路径。\n\n**调用此脚本的命令**\n\n- `asdf help <name> [<version>]`：输出插件和工具的文档\n\n**asdf 核心的调用签名**\n\n```bash\n\"${plugin_path}\"/bin/help.config\n```\n\n---\n\n### `bin/help.links`\n\n**描述**\n\n输出与插件和工具相关的链接列表。每个链接占一行。\n\n```bash\nGit Repository:\thttps://github.com/vlang/v\nDocumentation:\thttps://vlang.io\n```\n\n**实现细节**\n\n- 该脚本需要 `bin/help.overview` 才能被视为有效输出。\n- 每行一个链接。\n- 格式必须为以下之一：\n  - `<标题>: <链接>`\n  - 或仅 `<链接>`\n- 应根据所安装工具的操作系统和版本进行调整（可选设置环境变量 `ASDF_INSTALL_VERSION` 和 `ASDF_INSTALL_TYPE`）。\n- 成功应以 `0` 退出。\n- 失败应以非零状态退出。\n\n**脚本可用的环境变量**\n\n- `ASDF_INSTALL_TYPE`：`version` 或 `ref`\n- `ASDF_INSTALL_VERSION`：\n  - 若 `ASDF_INSTALL_TYPE=version`，则为完整版本号。\n  - 若 `ASDF_INSTALL_TYPE=ref`，则为 Git 引用（标签/提交/分支）。\n- `ASDF_INSTALL_PATH`：工具 _已_ 安装或 _应_ 安装的路径。\n\n**调用此脚本的命令**\n\n- `asdf help <name> [<version>]`：输出插件和工具的文档\n\n**asdf 核心的调用签名**\n\n```bash\n\"${plugin_path}\"/bin/help.links\n```\n\n---\n\n### `bin/list-bin-paths`\n\n**描述**\n\n列出包含指定版本工具可执行文件的目录。\n\n**实现细节**\n\n- 如果该脚本不存在，asdf 将搜索 `“${ASDF_INSTALL_PATH}”/bin` 目录中的二进制文件并为其创建垫片。\n- 输出包含可执行文件的路径列表，路径以空格分隔。\n- 路径必须相对于 `ASDF_INSTALL_PATH`。示例输出如下：\n\n```bash\nbin tools veggies\n```\n\n这将指示 asdf 为以下目录中的文件创建垫片：\n- `“${ASDF_INSTALL_PATH}”/bin`\n- `“${ASDF_INSTALL_PATH}”/tools`\n- `“${ASDF_INSTALL_PATH}”/veggies`\n\n**脚本可用的环境变量**\n\n- `ASDF_INSTALL_TYPE`：`version` 或 `ref`\n- `ASDF_INSTALL_VERSION`：\n- 若 `ASDF_INSTALL_TYPE=version`，则为完整版本号。\n  - 如果 `ASDF_INSTALL_TYPE=ref`，则为 Git 引用（标签/提交/分支）。\n- `ASDF_INSTALL_PATH`：工具 _已_ 安装或 _应_ 安装的路径。\n\n**调用此脚本的命令**\n\n- `asdf install <tool> [version]`：初始创建二进制文件的垫片。\n- `asdf reshim <tool> <version>`：重新创建二进制文件的垫片。\n\n**asdf 核心的调用签名**\n\n```bash\n\"${plugin_path}/bin/list-bin-paths\"\n```\n\n---\n\n### `bin/exec-env`\n\n**描述**\n\n在执行工具二进制文件的垫片之前，请先准备好环境。\n\n**脚本可用的环境变量**\n\n- `ASDF_INSTALL_TYPE`: `version` 或 `ref`\n- `ASDF_INSTALL_VERSION`:\n- 如果 `ASDF_INSTALL_TYPE=version`，则为完整版本号。\n  - 如果 `ASDF_INSTALL_TYPE=ref`，则为 Git 引用（标签/提交/分支）。\n- `ASDF_INSTALL_PATH`：工具 _已_ 安装或 _应_ 安装的路径。\n\n**调用此脚本的命令**\n\n- `asdf which <command>`：显示可执行文件的路径\n- `asdf exec <command> [args...]`：执行当前版本的命令垫片\n- `asdf env <command> [util]`：在命令垫片执行所用的环境中运行工具（默认：`env`）。\n\n**asdf 核心的调用签名**\n\n```bash\n\"${plugin_path}/bin/exec-env\"\n```\n\n---\n\n### `bin/exec-path`\n\n获取指定版本工具的可执行文件路径。必须输出一个\n包含相对可执行文件路径的字符串。这允许插件\n在满足条件时覆盖 Shim 指定的可执行文件路径，否则返回\nShim 指定的默认路径。\n\n**描述**\n\n获取指定版本工具的可执行文件路径。\n\n**实现细节**\n\n- 必须输出包含相对可执行文件路径的字符串。\n- 条件性地覆盖shim指定的可执行文件路径，否则返回shim指定的默认路径。\n\n```shell\nUsage:\n  plugin/bin/exec-path <install-path> <command> <executable-path>\n\nExample Call:\n  ~/.asdf/plugins/foo/bin/exec-path \"~/.asdf/installs/foo/1.0\" \"foo\" \"bin/foo\"\n\nOutput:\n  bin/foox\n```\n\n**脚本可用的环境变量**\n\n- `ASDF_INSTALL_TYPE`: `version` 或 `ref`\n- `ASDF_INSTALL_VERSION`:\n- 若 `ASDF_INSTALL_TYPE=version`，则为完整版本号。\n  - 如果 `ASDF_INSTALL_TYPE=ref`，则为 Git 引用（标签/提交/分支）。\n- `ASDF_INSTALL_PATH`：工具 _已_ 安装或 _应_ 安装的路径。\n\n**调用此脚本的命令**\n\n- `asdf which <command>`：显示可执行文件的路径\n- `asdf exec <command> [args...]`：执行当前版本的命令垫片\n- `asdf env <command> [util]`：在命令垫片执行所用的环境中运行工具（默认：`env`）。\n\n**asdf 核心的调用签名**\n\n```bash\n\"${plugin_path}/bin/exec-path\" \"$install_path\" \"$cmd\" \"$relative_path\"\n```\n\n---\n\n### `bin/uninstall`\n\n**描述**\n\n卸载提供的工具版本。\n\n**输出格式**\n\n输出应根据用户情况发送到 `stdout` 或 `stderr`。后续核心执行不会读取任何输出。\n\n**脚本可用的环境变量**\n\n此脚本未提供任何环境变量。\n\n**调用此脚本的命令**\n\n- `asdf list all <name> <version>`\n- `asdf uninstall nodejs 18.15.0`：卸载 nodejs 的版本`18.15.0`，并移除所有垫片，包括通过`npm i -g`全局安装的垫片\n\n**asdf 核心的调用签名**\n\n不提供参数。\n\n```bash\n\"${plugin_path}/bin/uninstall\"\n```\n\n---\n\n### `bin/list-legacy-filenames`\n\n**描述**\n\n列出用于确定指定工具版本的旧配置文件名。\n\n**实现细节**\n\n- 输出以空格分隔的文件名列表。\n```bash\n  .ruby-version .rvmrc\n  ```\n- 仅适用于在 `“${HOME}”/.asdfrc` 中启用了 `legacy_version_file` 选项的用户。\n\n**脚本可用的环境变量**\n\n- `ASDF_INSTALL_TYPE`：`version` 或 `ref`\n- `ASDF_INSTALL_VERSION`：\n  - 若 `ASDF_INSTALL_TYPE=version`，则为完整版本号。\n  - 若 `ASDF_INSTALL_TYPE=ref`，则为 Git 引用（标签/提交/分支）。\n- `ASDF_INSTALL_PATH`：工具 _已_ 安装或 _应_ 安装的路径。\n\n**调用此脚本的命令**\n\n任何读取工具版本的命令。\n\n**asdf 核心的调用签名**\n\n未提供参数。\n\n```bash\n\"${plugin_path}/bin/list-legacy-filenames\"\n```\n\n---\n\n### `bin/parse-legacy-file`\n\n**描述**\n\n解析由 asdf 找到的旧文件以确定工具的版本。适用于从 JavaScript 的 `package.json` 或 Go 的 `go.mod` 等文件中提取版本号。\n\n**实现细节**\n\n- 如果不存在，asdf 将简单地使用 `cat` 命令读取旧文件以确定版本。\n- 该过程应具有 **确定性**，并始终返回完全相同的版本：\n  - 当解析同一旧文件时。\n  - 无论机器上安装了什么，或旧版本是否有效或完整。某些旧文件格式可能不适用。\n- 输出包含版本号的单行内容：\n  ```bash\n  1.2.3\n  ```\n\n**脚本可用的环境变量**\n\n在调用此脚本之前，没有专门设置的环境变量。\n\n**调用此脚本的命令**\n\n任何读取工具版本的命令。\n\n**asdf 核心的调用签名**\n\n该脚本应接受一个参数，即读取其内容的旧版文件的路径。\n\n```bash\n\"${plugin_path}/bin/parse-legacy-file\" \"$file_path\"\n```\n\n---\n\n### `bin/post-plugin-add`\n\n**描述**\n\n在使用 `asdf plugin add <tool>` 命令将插件添加到 asdf 之后，执行此回调脚本。\n\n参见相关命令钩子：\n\n- `pre_asdf_plugin_add`\n- `pre_asdf_plugin_add_${plugin_name}`\n- `post_asdf_plugin_add`\n- `post_asdf_plugin_add_${plugin_name}`\n\n**脚本可用的环境变量**\n\n- `ASDF_PLUGIN_PATH`：插件的安装路径。\n- `ASDF_PLUGIN_SOURCE_URL`：插件来源 URL。可以是本地目录路径。\n\n**asdf 核心的调用签名**\n\n未提供参数。\n\n```bash\n\"${plugin_path}/bin/post-plugin-add\"\n```\n\n---\n\n### `bin/post-plugin-update`\n\n**描述**\n\n在 asdf 使用 `asdf plugin update <tool> [<git-ref>]` 命令下载 _update_ 插件**之后**，执行此回调脚本。\n\n参见相关命令钩子：\n\n- `pre_asdf_plugin_update`\n- `pre_asdf_plugin_update_${plugin_name}`\n- `post_asdf_plugin_update`\n- `post_asdf_plugin_update_${plugin_name}`\n\n**脚本可用的环境变量**\n\n- `ASDF_PLUGIN_PATH`：插件的安装路径。\n- `ASDF_PLUGIN_PREV_REF`：插件的上一版本 Git 引用。\n- `ASDF_PLUGIN_POST_REF`：插件的更新后 Git 引用。\n\n**asdf 核心的调用签名**\n\n未提供参数。\n\n```bash\n\"${plugin_path}/bin/post-plugin-update\"\n```\n\n---\n\n### `bin/pre-plugin-remove`\n\n**描述**\n\n在使用 `asdf plugin remove <工具>` 命令移除插件**之前**，执行此回调脚本。\n\n参见相关命令钩子：\n\n- `pre_asdf_plugin_remove`\n- `pre_asdf_plugin_remove_${plugin_name}`\n- `post_asdf_plugin_remove`\n- `post_asdf_plugin_remove_${plugin_name}`\n\n**脚本可用的环境变量**\n\n- `ASDF_PLUGIN_PATH`：插件的安装路径。\n\n**asdf 核心的调用签名**\n\n未提供参数。\n\n```bash\n\"${plugin_path}/bin/pre-plugin-remove\"\n```\n\n<!-- TODO: document command hooks -->\n<!-- ## Command Hooks -->\n\n## asdf 命令行的扩展命令 <Badge type=\"danger\" text=\"进阶\" vertical=\"middle\" />\n\n插件可以通过提供 `lib/commands/command*.bash` 脚本或者可执行程序来定义新的 asdf 命令，这些脚本或可执行程序将使用插件名称作为 asdf 命令的子命令进行调用。\n\n例如，假设一个 `foo` 插件有以下文件：\n\n```shell\nfoo/\n  lib/commands/\n    command.bash\n    command-bat.bash\n    command-bat-man.bash\n    command-help.bash\n```\n\n用户现在可以执行：\n\n```shell\n$ asdf foo         # 等同于运行 `$ASDF_DATA_DIR/plugins/foo/lib/commands/command.bash`\n$ asdf foo bar     # 等同于运行 `$ASDF_DATA_DIR/plugins/foo/lib/commands/command.bash bar`\n$ asdf foo help    # 等同于运行 `$ASDF_DATA_DIR/plugins/foo/lib/commands/command-help.bash`\n$ asdf foo bat man # 等同于运行 `$ASDF_DATA_DIR/plugins/foo/lib/commands/command-bat-man.bash`\n$ asdf foo bat baz # 等同于运行 `$ASDF_DATA_DIR/plugins/foo/lib/commands/command-bat.bash baz`\n```\n\n插件作者可以使用此功能来提供与其工具相关的实用命令，或者可以创建 asdf 本身的新命令扩展的插件。\n\n如果可执行位被设置，脚本将被执行，替换 asdf 的执行。\n\n如果可执行位未被设置，asdf 将把脚本作为 Bash 脚本加载。\n\n`$ASDF_CMD_FILE` 解析为正在加载的文件的完整路径。\n\n这个功能的一个很好的例子是像 [`haxe`](https://github.com/asdf-community/asdf-haxe) 这样的插件。\n它提供了 `asdf haxe neko-dylibs-link` 来修复 haxe 可执行文件期望找到相对于可执行目录的动态链接库的问题。\n\n如果你的插件提供了 asdf 扩展命令，请务必在插件的 README 文件中提及。\n\n## 自定义垫片模板 <Badge type=\"danger\" text=\"进阶\" vertical=\"middle\" />\n\n::: warning 警告\n\n请仅在**真的需要**时才使用此功能\n\n:::\n\nasdf 允许自定义垫片模板。对于名为 `foo` 的可执行程序，如果有一个 `shims/foo` 的文件在插件中，那么 asdf 将复制这个文件替代使用标准垫片模板。\n\n**这必须谨慎使用。**\n\n据 asdf 核心团队所知，此功能仅在官方 [Elixir 插件](https://github.com/asdf-vm/asdf-elixir) 中使用。这是\n因为可执行文件不仅被视为可执行文件，还被视为 Elixir 文件。这使得无法使用标准的 Bash 垫片。\n\n## 测试\n\n`asdf` 包含 `plugin-test` 命令用于测试插件：\n\n```shell\nasdf plugin test <plugin-name> <plugin-url> [--asdf-tool-version <version>] [--asdf-plugin-gitref <git-ref>] [test-command*]\n```\n\n- `<plugin_name>` 和 `<plugin_url>` 是必需的\n- 如果指定了可选参数 `[--asdf-tool-version <version>]`，工具将以该特定版本进行安装。默认值为 `asdf latest <plugin_name>`\n- 如果指定了可选参数 `[--asdf-plugin-gitref <git_ref>]`，插件本身将从指定的提交/分支/标签检出。这在测试插件的 CI 中的拉取请求时非常有用。默认使用插件仓库的默认分支。\n- 可选参数 `[test_command...]` 是用于验证已安装工具是否正常工作的命令。通常为 `<tool> --version` 或\n  `<tool> --help`。例如，要测试 NodeJS 插件，我们可以运行\n  ```shell\n  # asdf plugin test <plugin_name>  <plugin_url>                               [test_command]\n    asdf plugin test nodejs         https://github.com/asdf-vm/asdf-nodejs.git node --version\n  ```\n\n::: tip 注意\n\n我们强烈建议你在 CI 环境中测试你的插件，并确保它可以在 Linux 和 OSX 上运行。\n\n:::\n\n### GitHub Action\n\n[asdf-vm/actions](https://github.com/asdf-vm/actions) 仓库提供了一个 GitHub Action，用于测试托管在 GitHub 上的插件。一个示例 `.github/workflows/test.yaml` Actions 工作流如下：\n\n```yaml\nname: Test\non:\n  push:\n    branches:\n      - main\n  pull_request:\n\njobs:\n  plugin_test:\n    name: asdf plugin test\n    strategy:\n      matrix:\n        os:\n          - ubuntu-latest\n          - macos-latest\n    runs-on: ${{ matrix.os }}\n    steps:\n      - name: asdf_plugin_test\n        uses: asdf-vm/actions/plugin-test@v2\n        with:\n          command: \"<MY_TOOL> --version\"\n```\n\n#### TravisCI 配置示例\n\n这是一个 `.travis.yml` 示例文件，请根据你的需要进行自定义：\n\n```yaml\nlanguage: c\nscript: asdf plugin test <MY_TOOL> $TRAVIS_BUILD_DIR '<MY_TOOL> --version'\nbefore_script:\n  - git clone https://github.com/asdf-vm/asdf.git asdf\n  - . asdf/asdf.sh\nos:\n  - linux\n  - osx\n```\n\n::: tip 注意\n\n当使用其他 CI 时，可能需要传递插件位置的相对路径：\n\n```shell\nasdf plugin test <tool_name> <path> '<tool_command> --version'\n```\n\n:::\n\n## API 频率限制\n\n如果某个命令依赖于访问外部 API，例如 `bin/list-all` 或 `bin/latest-stable`，那么在自动化测试过程中可能会遇到频率限制问题。为了解决这个问题，请确保存在一条代码路径，通过环境变量提供认证令牌。例如：\n\n```shell\ncmd=\"curl -s\"\nif [ -n \"$GITHUB_API_TOKEN\" ]; then\n cmd=\"$cmd -H 'Authorization: token $GITHUB_API_TOKEN'\"\nfi\n\ncmd=\"$cmd $releases_path\"\n```\n\n### `GITHUB_API_TOKEN`\n\n要使用 `GITHUB_API_TOKEN`，请创建一个 [新个人令牌](https://github.com/settings/tokens/new)，仅授予 `public_repo` 访问权限。\n\n然后将此令牌添加到 CI 管道的环境变量中。\n\n::: tip 注意\n\n**切勿**将认证令牌发布到代码仓库中\n\n:::\n\n## 插件缩写索引\n\n::: tip 注意\n\n插件的推荐安装方法是通过直接 URL 安装：\n\n```shell\n# asdf plugin add <name> <git_url>\n  asdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs\n```\n\n:::\n\n如果未提供 `git_url`，asdf 将使用 [缩写索引仓库](https://github.com/asdf-vm/asdf-plugins) 来确定要使用的确切 `git_url`。\n\n您可以通过遵循该仓库中的 [缩写索引](https://github.com/asdf-vm/asdf-plugins) 中的说明，将你的插件添加到缩写索引中。\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/asdf-vm/asdf\n\ngo 1.24.9\n\nrequire (\n\tgithub.com/go-git/go-git/v5 v5.16.5\n\tgithub.com/mgechev/revive v1.7.0\n\tgithub.com/otiai10/copy v1.14.0\n\tgithub.com/rogpeppe/go-internal v1.14.1\n\tgithub.com/stretchr/testify v1.10.0\n\tgithub.com/urfave/cli/v3 v3.3.3\n\tgolang.org/x/sys v0.38.0\n\tgopkg.in/ini.v1 v1.67.0\n\thonnef.co/go/tools v0.5.1\n\tmvdan.cc/gofumpt v0.7.0\n)\n\nrequire (\n\tdario.cat/mergo v1.0.0 // indirect\n\tgithub.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect\n\tgithub.com/Microsoft/go-winio v0.6.2 // indirect\n\tgithub.com/ProtonMail/go-crypto v1.1.6 // indirect\n\tgithub.com/chavacava/garif v0.1.0 // indirect\n\tgithub.com/cloudflare/circl v1.6.1 // indirect\n\tgithub.com/cyphar/filepath-securejoin v0.4.1 // indirect\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/emirpasic/gods v1.18.1 // indirect\n\tgithub.com/fatih/color v1.18.0 // indirect\n\tgithub.com/fatih/structtag v1.2.0 // indirect\n\tgithub.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect\n\tgithub.com/go-git/go-billy/v5 v5.6.2 // indirect\n\tgithub.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect\n\tgithub.com/google/go-cmp v0.7.0 // indirect\n\tgithub.com/hashicorp/go-version v1.7.0 // indirect\n\tgithub.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect\n\tgithub.com/kevinburke/ssh_config v1.2.0 // indirect\n\tgithub.com/mattn/go-colorable v0.1.14 // indirect\n\tgithub.com/mattn/go-isatty v0.0.20 // indirect\n\tgithub.com/mattn/go-runewidth v0.0.16 // indirect\n\tgithub.com/mgechev/dots v0.0.0-20210922191527-e955255bf517 // indirect\n\tgithub.com/olekukonko/tablewriter v0.0.5 // indirect\n\tgithub.com/pjbgf/sha1cd v0.3.2 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.0 // indirect\n\tgithub.com/rivo/uniseg v0.4.7 // indirect\n\tgithub.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect\n\tgithub.com/skeema/knownhosts v1.3.1 // indirect\n\tgithub.com/spf13/afero v1.12.0 // indirect\n\tgithub.com/xanzy/ssh-agent v0.3.3 // indirect\n\tgolang.org/x/crypto v0.45.0 // indirect\n\tgolang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678 // indirect\n\tgolang.org/x/mod v0.29.0 // indirect\n\tgolang.org/x/net v0.47.0 // indirect\n\tgolang.org/x/sync v0.18.0 // indirect\n\tgolang.org/x/text v0.31.0 // indirect\n\tgolang.org/x/tools v0.38.0 // indirect\n\tgolang.org/x/tools/go/expect v0.1.1-deprecated // indirect\n\tgopkg.in/warnings.v0 v0.1.2 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=\ndario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=\ngithub.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs=\ngithub.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=\ngithub.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=\ngithub.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=\ngithub.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=\ngithub.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw=\ngithub.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=\ngithub.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=\ngithub.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=\ngithub.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=\ngithub.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=\ngithub.com/chavacava/garif v0.1.0 h1:2JHa3hbYf5D9dsgseMKAmc/MZ109otzgNFk5s87H9Pc=\ngithub.com/chavacava/garif v0.1.0/go.mod h1:XMyYCkEL58DF0oyW4qDjjnPWONs2HBqYKI+UIPD+Gww=\ngithub.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=\ngithub.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=\ngithub.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=\ngithub.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=\ngithub.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=\ngithub.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=\ngithub.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=\ngithub.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=\ngithub.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=\ngithub.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=\ngithub.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=\ngithub.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=\ngithub.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=\ngithub.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=\ngithub.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=\ngithub.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM=\ngithub.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=\ngithub.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=\ngithub.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=\ngithub.com/go-git/go-git/v5 v5.16.5 h1:mdkuqblwr57kVfXri5TTH+nMFLNUxIj9Z7F5ykFbw5s=\ngithub.com/go-git/go-git/v5 v5.16.5/go.mod h1:QOMLpNf1qxuSY4StA/ArOdfFR2TrKEjJiye2kel2m+M=\ngithub.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=\ngithub.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=\ngithub.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=\ngithub.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=\ngithub.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=\ngithub.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=\ngithub.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=\ngithub.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=\ngithub.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=\ngithub.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=\ngithub.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=\ngithub.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=\ngithub.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=\ngithub.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=\ngithub.com/mgechev/dots v0.0.0-20210922191527-e955255bf517 h1:zpIH83+oKzcpryru8ceC6BxnoG8TBrhgAvRg8obzup0=\ngithub.com/mgechev/dots v0.0.0-20210922191527-e955255bf517/go.mod h1:KQ7+USdGKfpPjXk4Ga+5XxQM4Lm4e3gAogrreFAYpOg=\ngithub.com/mgechev/revive v1.7.0 h1:JyeQ4yO5K8aZhIKf5rec56u0376h8AlKNQEmjfkjKlY=\ngithub.com/mgechev/revive v1.7.0/go.mod h1:qZnwcNhoguE58dfi96IJeSTPeZQejNeoMQLUZGi4SW4=\ngithub.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=\ngithub.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=\ngithub.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=\ngithub.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=\ngithub.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU=\ngithub.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w=\ngithub.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks=\ngithub.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=\ngithub.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=\ngithub.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=\ngithub.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=\ngithub.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=\ngithub.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=\ngithub.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=\ngithub.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=\ngithub.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=\ngithub.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=\ngithub.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=\ngithub.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=\ngithub.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=\ngithub.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=\ngithub.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=\ngithub.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=\ngithub.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=\ngithub.com/urfave/cli/v3 v3.3.3 h1:byCBaVdIXuLPIDm5CYZRVG6NvT7tv1ECqdU4YzlEa3I=\ngithub.com/urfave/cli/v3 v3.3.3/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo=\ngithub.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=\ngithub.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=\ngolang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=\ngolang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=\ngolang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=\ngolang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=\ngolang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=\ngolang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678 h1:1P7xPZEwZMoBoz0Yze5Nx2/4pxj6nw9ZqHWXqP0iRgQ=\ngolang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=\ngolang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=\ngolang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=\ngolang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=\ngolang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=\ngolang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=\ngolang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=\ngolang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=\ngolang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=\ngolang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=\ngolang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=\ngolang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM=\ngolang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=\ngopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=\ngopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nhonnef.co/go/tools v0.5.1 h1:4bH5o3b5ZULQ4UrBmP+63W9r7qIkqJClEA9ko5YKx+I=\nhonnef.co/go/tools v0.5.1/go.mod h1:e9irvo83WDG9/irijV44wr3tbhcFeRnfpVlRqVwpzMs=\nmvdan.cc/gofumpt v0.7.0 h1:bg91ttqXmi9y2xawvkuMXyvAA/1ZGJqYAEGjXuP0JXU=\nmvdan.cc/gofumpt v0.7.0/go.mod h1:txVFJy/Sc/mvaycET54pV8SW8gWxTlUuGHVEcncmNUo=\n"
  },
  {
    "path": "help.txt",
    "content": "MANAGE PLUGINS\nasdf plugin add <name> [<git-url>]      Add a plugin from the plugin repo OR,\n                                        add a Git repo as a plugin by\n                                        specifying the name and repo url\nasdf plugin list [--urls] [--refs]      List installed plugins. Optionally show\n                                        git urls and git-ref\nasdf plugin list all                    List plugins registered on asdf-plugins\n                                        repository with URLs\nasdf plugin remove <name>               Remove plugin and package versions\nasdf plugin update <name> [<git-ref>]   Update a plugin to latest commit on\n                                        default branch or a particular git-ref\nasdf plugin update --all                Update all plugins to latest commit on\n                                        default branch\n\n\nMANAGE PACKAGES\nasdf current                            Display current version set or being\n                                        used for all packages\nasdf current <name>                     Display current version set or being\n                                        used for package\nasdf global <name> <version>            Set the package global version\nasdf global <name> latest[:<version>]   Set the package global version to the\n                                        latest provided version\nasdf help <name> [<version>]            Output documentation for plugin and tool\nasdf install                            Install all the package versions listed\n                                        in the .tool-versions file\nasdf install <name>                     Install one tool at the version\n                                        specified in the .tool-versions file\nasdf install <name> <version>           Install a specific version of a package\nasdf install <name> latest[:<version>]  Install the latest stable version of a\n                                        package, or with optional version,\n                                        install the latest stable version that\n                                        begins with the given string\nasdf latest <name> [<version>]          Show latest stable version of a package\nasdf latest --all                       Show latest stable version of all the\n                                        packages and if they are installed\nasdf list <name> [version]              List installed versions of a package and\n                                        optionally filter the versions\nasdf list all <name> [<version>]        List all versions of a package and\n                                        optionally filter the returned versions\nasdf local <name> <version>             Set the package local version\nasdf local <name> latest[:<version>]    Set the package local version to the\n                                        latest provided version\nasdf shell <name> <version>             Set the package version to\n                                        `ASDF_${LANG}_VERSION` in the current shell\nasdf uninstall <name> <version>         Remove a specific version of a package\nasdf where <name> [<version>]           Display install path for an installed\n                                        or current version\nasdf which <command>                    Display the path to an executable\n\n\nUTILS\nasdf exec <command> [args...]           Executes the command shim for current version\nasdf env <command> [util]               Runs util (default: `env`) inside the\n                                        environment used for command shim execution.\nasdf info                               Print OS, Shell and ASDF debug information.\nasdf version                            Print the currently installed version of ASDF\nasdf reshim <name> <version>            Recreate shims for version of a package\nasdf shim-versions <command>            List the plugins and versions that\n                                        provide a command\nasdf update                             Update asdf to the latest stable release\nasdf update --head                      Update asdf to the latest on the master branch\n\nRESOURCES\nGitHub: https://github.com/asdf-vm/asdf\nDocs:   https://asdf-vm.com\n"
  },
  {
    "path": "internal/cli/cli.go",
    "content": "// Package cli contains the asdf CLI command code\npackage cli\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"log\"\n\t\"net/mail\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"slices\"\n\t\"strings\"\n\t\"text/tabwriter\"\n\n\t\"github.com/asdf-vm/asdf/internal/cli/set\"\n\t\"github.com/asdf-vm/asdf/internal/completions\"\n\t\"github.com/asdf-vm/asdf/internal/config\"\n\t\"github.com/asdf-vm/asdf/internal/exec\"\n\t\"github.com/asdf-vm/asdf/internal/execenv\"\n\t\"github.com/asdf-vm/asdf/internal/execute\"\n\t\"github.com/asdf-vm/asdf/internal/help\"\n\t\"github.com/asdf-vm/asdf/internal/hook\"\n\t\"github.com/asdf-vm/asdf/internal/info\"\n\t\"github.com/asdf-vm/asdf/internal/installs\"\n\t\"github.com/asdf-vm/asdf/internal/pluginindex\"\n\t\"github.com/asdf-vm/asdf/internal/plugins\"\n\t\"github.com/asdf-vm/asdf/internal/resolve\"\n\t\"github.com/asdf-vm/asdf/internal/shims\"\n\t\"github.com/asdf-vm/asdf/internal/toolversions\"\n\t\"github.com/asdf-vm/asdf/internal/versions\"\n\t\"github.com/urfave/cli/v3\"\n)\n\nconst usageText = `The Multiple Runtime Version Manager.\n\nManage all your runtime versions with one tool!\n\nComplete documentation is available at https://asdf-vm.com/`\n\nconst updateCommandRemovedText = `\nUpgrading asdf via asdf update is no longer supported. Please use your OS\npackage manager (Homebrew, APT, etc...) to upgrade asdf or download the\nlatest asdf binary manually from the asdf website.\n\nPlease visit https://asdf-vm.com/ or https://github.com/asdf-vm/asdf for more\ndetails.`\n\n// Execute defines the full CLI API and then runs it\nfunc Execute(version string) {\n\tlogger := log.New(os.Stderr, \"\", 0)\n\tlog.SetFlags(0)\n\n\tapp := &cli.Command{\n\t\tName:      \"asdf\",\n\t\tVersion:   version,\n\t\tCopyright: \"(c) 2024 Trevor Brown\",\n\t\tAuthors: []any{\n\t\t\tmail.Address{Name: \"Trevor Brown\", Address: \"someguy@example.com\"},\n\t\t},\n\t\tUsage:     \"The multiple runtime version manager\",\n\t\tUsageText: usageText,\n\t\tCommands: []*cli.Command{\n\t\t\t{\n\t\t\t\tName: \"cmd\",\n\t\t\t\tAction: func(_ context.Context, cmd *cli.Command) error {\n\t\t\t\t\targs := cmd.Args().Slice()\n\n\t\t\t\t\treturn extensionCommand(logger, args)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tName: \"completion\",\n\t\t\t\tAction: func(_ context.Context, cmd *cli.Command) error {\n\t\t\t\t\tshell := cmd.Args().Get(0)\n\t\t\t\t\treturn completionCommand(logger, shell)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tName: \"current\",\n\t\t\t\tFlags: []cli.Flag{\n\t\t\t\t\t&cli.BoolFlag{\n\t\t\t\t\t\tName:  \"no-header\",\n\t\t\t\t\t\tUsage: \"Whether or not to print a header line\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tAction: func(_ context.Context, cmd *cli.Command) error {\n\t\t\t\t\ttool := cmd.Args().Get(0)\n\n\t\t\t\t\tnoHeader := cmd.Bool(\"no-header\")\n\t\t\t\t\treturn currentCommand(logger, tool, noHeader)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tName: \"env\",\n\t\t\t\tAction: func(_ context.Context, cmd *cli.Command) error {\n\t\t\t\t\tshimmedCommand := cmd.Args().Get(0)\n\t\t\t\t\targs := cmd.Args().Slice()\n\n\t\t\t\t\treturn envCommand(logger, shimmedCommand, args)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tName: \"exec\",\n\t\t\t\t// We want all arguments to exec to remain unparsed so we can pass them\n\t\t\t\t// directly to the command asdf will exec on behalf of the shim/user.\n\t\t\t\t// SkipFlagParsing tells urfave/cli to do this.\n\t\t\t\tSkipFlagParsing: true,\n\t\t\t\tAction: func(_ context.Context, cmd *cli.Command) error {\n\t\t\t\t\tcommand := cmd.Args().Get(0)\n\t\t\t\t\targs := cmd.Args().Slice()\n\n\t\t\t\t\treturn execCommand(logger, command, args)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tName: \"help\",\n\t\t\t\tAction: func(_ context.Context, cmd *cli.Command) error {\n\t\t\t\t\ttoolName := cmd.Args().Get(0)\n\t\t\t\t\ttoolVersion := cmd.Args().Get(1)\n\t\t\t\t\treturn helpCommand(logger, version, toolName, toolVersion)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tName: \"info\",\n\t\t\t\tAction: func(_ context.Context, _ *cli.Command) error {\n\t\t\t\t\tconf, err := config.LoadConfig()\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tlogger.Printf(\"error loading config: %s\", err)\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\n\t\t\t\t\treturn infoCommand(conf, version)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tName: \"version\",\n\t\t\t\tAction: func(_ context.Context, _ *cli.Command) error {\n\t\t\t\t\tfmt.Fprintf(os.Stdout, \"%s\\n\", version)\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tName: \"install\",\n\t\t\t\tFlags: []cli.Flag{\n\t\t\t\t\t&cli.BoolFlag{\n\t\t\t\t\t\tName:  \"keep-download\",\n\t\t\t\t\t\tUsage: \"Whether or not to keep download directory after successful install\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tAction: func(_ context.Context, cmd *cli.Command) error {\n\t\t\t\t\targs := cmd.Args()\n\t\t\t\t\tkeepDownload := cmd.Bool(\"keep-download\")\n\t\t\t\t\treturn installCommand(logger, args.Get(0), args.Get(1), keepDownload)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tName: \"latest\",\n\t\t\t\tFlags: []cli.Flag{\n\t\t\t\t\t&cli.BoolFlag{\n\t\t\t\t\t\tName:  \"all\",\n\t\t\t\t\t\tUsage: \"Show latest version of all tools\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tAction: func(_ context.Context, cmd *cli.Command) error {\n\t\t\t\t\ttool := cmd.Args().Get(0)\n\t\t\t\t\tpattern := cmd.Args().Get(1)\n\t\t\t\t\tall := cmd.Bool(\"all\")\n\n\t\t\t\t\treturn latestCommand(logger, all, tool, pattern)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tName: \"list\",\n\t\t\t\tAction: func(_ context.Context, cmd *cli.Command) error {\n\t\t\t\t\targs := cmd.Args()\n\t\t\t\t\treturn listCommand(logger, args.Get(0), args.Get(1), args.Get(2))\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tName: \"plugin\",\n\t\t\t\tCommands: []*cli.Command{\n\t\t\t\t\t{\n\t\t\t\t\t\tName: \"add\",\n\t\t\t\t\t\tAction: func(_ context.Context, cmd *cli.Command) error {\n\t\t\t\t\t\t\targs := cmd.Args()\n\t\t\t\t\t\t\tconf, err := config.LoadConfig()\n\t\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\t\tlogger.Printf(\"error loading config: %s\", err)\n\t\t\t\t\t\t\t\treturn err\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn pluginAddCommand(cmd, conf, logger, args.Get(0), args.Get(1))\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tName: \"list\",\n\t\t\t\t\t\tFlags: []cli.Flag{\n\t\t\t\t\t\t\t&cli.BoolFlag{\n\t\t\t\t\t\t\t\tName:  \"urls\",\n\t\t\t\t\t\t\t\tUsage: \"Show URLs\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t&cli.BoolFlag{\n\t\t\t\t\t\t\t\tName:  \"refs\",\n\t\t\t\t\t\t\t\tUsage: \"Show Refs\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tAction: func(_ context.Context, cmd *cli.Command) error {\n\t\t\t\t\t\t\treturn pluginListCommand(cmd, logger)\n\t\t\t\t\t\t},\n\t\t\t\t\t\tCommands: []*cli.Command{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tName: \"all\",\n\t\t\t\t\t\t\t\tAction: func(_ context.Context, _ *cli.Command) error {\n\t\t\t\t\t\t\t\t\treturn pluginListAllCommand(logger)\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tName: \"remove\",\n\t\t\t\t\t\tAction: func(_ context.Context, cmd *cli.Command) error {\n\t\t\t\t\t\t\targs := cmd.Args()\n\t\t\t\t\t\t\treturn pluginRemoveCommand(cmd, logger, args.Get(0))\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tName: \"update\",\n\t\t\t\t\t\tFlags: []cli.Flag{\n\t\t\t\t\t\t\t&cli.BoolFlag{\n\t\t\t\t\t\t\t\tName:  \"all\",\n\t\t\t\t\t\t\t\tUsage: \"Update all installed plugins\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tAction: func(_ context.Context, cmd *cli.Command) error {\n\t\t\t\t\t\t\targs := cmd.Args()\n\t\t\t\t\t\t\treturn pluginUpdateCommand(cmd, logger, args.Get(0), args.Get(1))\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tName: \"test\",\n\t\t\t\t\t\tFlags: []cli.Flag{\n\t\t\t\t\t\t\t&cli.StringFlag{\n\t\t\t\t\t\t\t\tName:  \"asdf-tool-version\",\n\t\t\t\t\t\t\t\tUsage: \"The tool version to use during testing\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t&cli.StringFlag{\n\t\t\t\t\t\t\t\tName:  \"asdf-plugin-gitref\",\n\t\t\t\t\t\t\t\tUsage: \"The plugin Git ref to test\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tAction: func(_ context.Context, cmd *cli.Command) error {\n\t\t\t\t\t\t\ttoolVersion := cmd.String(\"asdf-tool-version\")\n\t\t\t\t\t\t\tgitRef := cmd.String(\"asdf-plugin-gitref\")\n\t\t\t\t\t\t\targs := cmd.Args().Slice()\n\t\t\t\t\t\t\tpluginTestCommand(logger, args, toolVersion, gitRef)\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tName: \"reshim\",\n\t\t\t\tAction: func(_ context.Context, cmd *cli.Command) error {\n\t\t\t\t\targs := cmd.Args()\n\t\t\t\t\treturn reshimCommand(logger, args.Get(0), args.Get(1))\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tName: \"set\",\n\t\t\t\tFlags: []cli.Flag{\n\t\t\t\t\t&cli.BoolFlag{\n\t\t\t\t\t\tName:    \"home\",\n\t\t\t\t\t\tAliases: []string{\"u\"},\n\t\t\t\t\t\tUsage:   \"The version should be set in the current users home directory\",\n\t\t\t\t\t},\n\t\t\t\t\t&cli.BoolFlag{\n\t\t\t\t\t\tName:    \"parent\",\n\t\t\t\t\t\tAliases: []string{\"p\"},\n\t\t\t\t\t\tUsage:   \"The version should be set in the closest existing .tool-versions file in a parent directory\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tAction: func(_ context.Context, cmd *cli.Command) error {\n\t\t\t\t\targs := cmd.Args().Slice()\n\t\t\t\t\thome := cmd.Bool(\"home\")\n\t\t\t\t\tparent := cmd.Bool(\"parent\")\n\t\t\t\t\treturn set.Main(os.Stdout, os.Stderr, args, home, parent, func() (string, error) {\n\t\t\t\t\t\treturn os.UserHomeDir()\n\t\t\t\t\t})\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tName: \"shimversions\",\n\t\t\t\tAction: func(_ context.Context, cmd *cli.Command) error {\n\t\t\t\t\targs := cmd.Args()\n\t\t\t\t\treturn shimVersionsCommand(logger, args.Get(0))\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tName: \"uninstall\",\n\t\t\t\tAction: func(_ context.Context, cmd *cli.Command) error {\n\t\t\t\t\ttool := cmd.Args().Get(0)\n\t\t\t\t\tversion := cmd.Args().Get(1)\n\n\t\t\t\t\treturn uninstallCommand(logger, tool, version)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tName: \"update\",\n\t\t\t\tAction: func(_ context.Context, _ *cli.Command) error {\n\t\t\t\t\tfmt.Println(updateCommandRemovedText)\n\t\t\t\t\treturn errors.New(\"command removed\")\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tName: \"where\",\n\t\t\t\tAction: func(_ context.Context, cmd *cli.Command) error {\n\t\t\t\t\ttool := cmd.Args().Get(0)\n\t\t\t\t\tversion := cmd.Args().Get(1)\n\n\t\t\t\t\treturn whereCommand(logger, tool, version)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tName: \"which\",\n\t\t\t\tAction: func(_ context.Context, cmd *cli.Command) error {\n\t\t\t\t\ttool := cmd.Args().Get(0)\n\n\t\t\t\t\treturn whichCommand(logger, tool)\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tCommandNotFound: func(_ context.Context, _ *cli.Command, s string) {\n\t\t\tlogger.Printf(\"invalid command provided: %s\\n\\n\", s)\n\t\t\thelpCommand(logger, version, \"\", \"\")\n\t\t\tcli.OsExiter(1)\n\t\t},\n\t}\n\n\terr := unsetAsdfReservedEnvVars()\n\tif err != nil {\n\t\tcli.OsExiter(1)\n\t}\n\n\tif err = app.Run(context.Background(), os.Args); err != nil {\n\t\tcli.OsExiter(1)\n\t}\n}\n\nfunc completionCommand(l *log.Logger, shell string) error {\n\tfile, ok := completions.Get(shell)\n\tif !ok {\n\t\tl.Printf(`No completions available for shell with name %q\nCompletions are available for: %v`, shell, strings.Join(completions.Names(), \", \"))\n\t\treturn errors.New(\"bad shell name\")\n\t}\n\tdefer file.Close()\n\n\tio.Copy(os.Stdout, file)\n\n\treturn nil\n}\n\n// This function is a whole mess and needs to be refactored\nfunc currentCommand(logger *log.Logger, tool string, noHeader bool) error {\n\tconf, err := config.LoadConfig()\n\tif err != nil {\n\t\tlogger.Printf(\"error loading config: %s\", err)\n\t\treturn err\n\t}\n\n\tcurrentDir, err := os.Getwd()\n\tif err != nil {\n\t\tlogger.Printf(\"unable to get current directory: %s\", err)\n\t\treturn err\n\t}\n\n\t// settings here to match legacy implementation\n\tw := tabwriter.NewWriter(os.Stdout, 16, 0, 1, ' ', 0)\n\tif !noHeader {\n\t\twriteHeader(w)\n\t}\n\n\tif tool == \"\" {\n\t\t// show all\n\t\tallPlugins, err := plugins.List(conf, false, false)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif len(allPlugins) < 1 {\n\t\t\tfmt.Println(\"No plugins installed\")\n\t\t\treturn nil\n\t\t}\n\n\t\tfor _, plugin := range allPlugins {\n\t\t\ttoolversion, versionFound, versionInstalled := getVersionInfo(conf, plugin, currentDir)\n\t\t\tformatCurrentVersionLine(w, plugin, toolversion, versionFound, versionInstalled, err)\n\t\t}\n\t\tw.Flush()\n\t\treturn nil\n\t}\n\n\t// show single tool\n\tplugin := plugins.New(conf, tool)\n\terr = plugin.Exists()\n\t_, ok := err.(plugins.PluginMissing)\n\tpluginExists := !ok\n\n\tif pluginExists {\n\t\ttoolversion, versionFound, versionInstalled := getVersionInfo(conf, plugin, currentDir)\n\t\tformatCurrentVersionLine(w, plugin, toolversion, versionFound, versionInstalled, err)\n\t\tw.Flush()\n\t\tif !versionFound {\n\t\t\tos.Exit(126)\n\t\t}\n\n\t\tif !versionInstalled {\n\t\t\tcli.OsExiter(1)\n\t\t}\n\t} else {\n\t\tfmt.Printf(\"No such plugin: %s\\n\", tool)\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc getVersionInfo(conf config.Config, plugin plugins.Plugin, currentDir string) (resolve.ToolVersions, bool, bool) {\n\ttoolversion, found, _ := resolve.Version(conf, plugin, currentDir)\n\tinstalled := false\n\tif found {\n\t\tfirstVersion := toolversion.Versions[0]\n\t\tversion := toolversions.Parse(firstVersion)\n\t\tinstalled = installs.IsInstalled(conf, plugin, version)\n\t}\n\treturn toolversion, found, installed\n}\n\nfunc writeHeader(w *tabwriter.Writer) {\n\tfmt.Fprintf(w, \"%s\\t%s\\t%s\\t%s\\n\", \"Name\", \"Version\", \"Source\", \"Installed\")\n}\n\nfunc formatCurrentVersionLine(w *tabwriter.Writer, plugin plugins.Plugin, toolversion resolve.ToolVersions, found bool, installed bool, err error) error {\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// columns are: name, version, source, installed\n\tversion := formatVersions(toolversion.Versions)\n\tsource := formatSource(toolversion, found)\n\tinstalledStatus := formatInstalled(toolversion, plugin.Name, found, installed)\n\tfmt.Fprintf(w, \"%s\\t%s\\t%s\\t%s\\n\", plugin.Name, version, source, installedStatus)\n\treturn nil\n}\n\nfunc formatInstalled(toolversion resolve.ToolVersions, name string, found, installed bool) string {\n\tif !found {\n\t\treturn \"\"\n\t}\n\tif !installed {\n\t\treturn fmt.Sprintf(\"false - Run `asdf install %s %s`\", name, toolversion.Versions[0])\n\t}\n\treturn \"true\"\n}\n\nfunc formatSource(toolversion resolve.ToolVersions, found bool) string {\n\tif found {\n\t\treturn filepath.Join(toolversion.Directory, toolversion.Source)\n\t}\n\treturn \"______\"\n}\n\nfunc formatVersions(versions []string) string {\n\tswitch len(versions) {\n\tcase 0:\n\t\treturn \"______\"\n\tcase 1:\n\t\treturn versions[0]\n\tdefault:\n\t\treturn strings.Join(versions, \" \")\n\t}\n}\n\nfunc envCommand(logger *log.Logger, shimmedCommand string, args []string) error {\n\tcommand := \"env\"\n\n\tif shimmedCommand == \"\" {\n\t\tlogger.Printf(\"usage: asdf env <command>\")\n\t\treturn fmt.Errorf(\"usage: asdf env <command>\")\n\t}\n\n\tif len(args) >= 2 {\n\t\tcommand = args[1]\n\t}\n\n\trealArgs := []string{}\n\tif len(args) > 2 {\n\t\trealArgs = args[2:]\n\t}\n\n\tconf, err := config.LoadConfig()\n\tif err != nil {\n\t\tlogger.Printf(\"error loading config: %s\", err)\n\t\treturn err\n\t}\n\n\t_, plugin, version, err := getExecutable(logger, conf, shimmedCommand)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tparsedVersion := toolversions.Parse(version)\n\texecPaths, err := shims.ExecutablePaths(conf, plugin, parsedVersion)\n\tif err != nil {\n\t\treturn err\n\t}\n\tenv := map[string]string{\n\t\t\"ASDF_INSTALL_TYPE\":    parsedVersion.Type,\n\t\t\"ASDF_INSTALL_VERSION\": parsedVersion.Value,\n\t\t\"ASDF_INSTALL_PATH\":    installs.InstallPath(conf, plugin, parsedVersion),\n\t\t\"PATH\":                 setPath(execPaths),\n\t}\n\n\tif parsedVersion.Type != \"system\" {\n\t\tenv, err = execenv.Generate(plugin, env)\n\t\tif _, ok := err.(plugins.NoCallbackError); !ok && err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tfname, err := shims.ExecutableOnPath(env[\"PATH\"], command)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfinalEnv := execute.MergeWithCurrentEnv(env)\n\terr = exec.Exec(fname, realArgs, finalEnv)\n\tif err != nil {\n\t\tfmt.Printf(\"err %#+v\\n\", err.Error())\n\t}\n\treturn err\n}\n\nfunc setPath(paths []string) string {\n\treturn strings.Join(paths, \":\") + \":\" + os.Getenv(\"PATH\")\n}\n\nfunc execCommand(logger *log.Logger, command string, args []string) error {\n\tif command == \"\" {\n\t\tlogger.Printf(\"usage: asdf exec <command>\")\n\t\treturn fmt.Errorf(\"usage: asdf exec <command>\")\n\t}\n\n\tconf, err := config.LoadConfig()\n\tif err != nil {\n\t\tlogger.Printf(\"error loading config: %s\", err)\n\t\treturn err\n\t}\n\n\texecutable, plugin, version, err := getExecutable(logger, conf, command)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif len(args) > 1 {\n\t\targs = args[1:]\n\t} else {\n\t\targs = []string{}\n\t}\n\n\tparsedVersion := toolversions.Parse(version)\n\texecPaths, err := shims.ExecutablePaths(conf, plugin, parsedVersion)\n\tif err != nil {\n\t\treturn err\n\t}\n\tenv := map[string]string{\n\t\t\"ASDF_INSTALL_TYPE\":    parsedVersion.Type,\n\t\t\"ASDF_INSTALL_VERSION\": parsedVersion.Value,\n\t\t\"ASDF_INSTALL_PATH\":    installs.InstallPath(conf, plugin, parsedVersion),\n\t\t\"PATH\":                 setPath(execPaths),\n\t}\n\n\tif parsedVersion.Type != \"system\" {\n\t\tenv, err = execenv.Generate(plugin, env)\n\t\tif _, ok := err.(plugins.NoCallbackError); !ok && err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\terr = hook.RunWithOutput(conf, fmt.Sprintf(\"pre_%s_%s\", plugin.Name, filepath.Base(executable)), args, os.Stdout, os.Stderr)\n\tif err != nil {\n\t\tcli.OsExiter(1)\n\t\treturn err\n\t}\n\n\tfinalEnv := execute.MergeWithCurrentEnv(env)\n\treturn exec.Exec(executable, args, finalEnv)\n}\n\nfunc extensionCommand(logger *log.Logger, args []string) error {\n\tif len(args) < 1 {\n\t\terr := errors.New(\"no plugin name specified\")\n\t\tlogger.Printf(\"%s\", err.Error())\n\t\treturn err\n\t}\n\n\tconf, err := config.LoadConfig()\n\tif err != nil {\n\t\tlogger.Printf(\"error loading config: %s\", err)\n\t\treturn err\n\t}\n\n\tpluginName := args[0]\n\tplugin := plugins.New(conf, pluginName)\n\n\terr = runExtensionCommand(plugin, args[1:])\n\tlogger.Printf(\"error running extension command: %s\", err.Error())\n\treturn err\n}\n\nfunc runExtensionCommand(plugin plugins.Plugin, args []string) (err error) {\n\tpath := \"\"\n\tif len(args) > 0 {\n\t\tpath, err = plugin.ExtensionCommandPath(args[0])\n\n\t\tif err != nil {\n\t\t\tpath, err = plugin.ExtensionCommandPath(\"\")\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else {\n\t\t\targs = args[1:]\n\t\t}\n\t} else {\n\t\tpath, err = plugin.ExtensionCommandPath(\"\")\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn exec.Exec(path, args, os.Environ())\n}\n\nfunc getExecutable(logger *log.Logger, conf config.Config, command string) (executable string, plugin plugins.Plugin, version string, err error) {\n\tcurrentDir, err := os.Getwd()\n\tif err != nil {\n\t\tlogger.Printf(\"unable to get current directory: %s\", err)\n\t\treturn \"\", plugins.Plugin{}, \"\", err\n\t}\n\n\texecutable, plugin, version, found, err := shims.FindExecutable(conf, command, currentDir)\n\tif err != nil {\n\n\t\tif _, ok := err.(shims.NoExecutableForPluginError); ok {\n\t\t\tlogger.Printf(\"No executable %s found for current version. Please select a different version or install %s manually for the current version\", command, command)\n\t\t\tcli.OsExiter(1)\n\t\t\treturn \"\", plugin, version, err\n\t\t}\n\t\tshimPath := shims.Path(conf, command)\n\t\ttoolVersions, _ := shims.GetToolsAndVersionsFromShimFile(shimPath)\n\n\t\tif len(toolVersions) > 0 {\n\t\t\tif anyInstalled(conf, toolVersions) {\n\t\t\t\tlogger.Printf(\"No version is set for command %s\", command)\n\t\t\t\tlogger.Printf(\"Consider adding one of the following versions in your config file at %s/.tool-versions\\n\", currentDir)\n\t\t\t} else {\n\t\t\t\tlogger.Printf(\"No preset version installed for command %s\", command)\n\t\t\t\tfor _, toolVersion := range toolVersions {\n\t\t\t\t\tfor _, version := range toolVersion.Versions {\n\t\t\t\t\t\tlogger.Printf(\"asdf install %s %s\\n\", toolVersion.Name, version)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tlogger.Printf(\"or add one of the following versions in your config file at %s/.tool-versions\\n\", currentDir)\n\t\t\t}\n\n\t\t\tfor _, toolVersion := range toolVersions {\n\t\t\t\tfor _, version := range toolVersion.Versions {\n\t\t\t\t\tlogger.Printf(\"%s %s\\n\", toolVersion.Name, version)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tos.Exit(126)\n\t\treturn executable, plugins.Plugin{}, \"\", err\n\t}\n\n\tif !found {\n\t\tlogger.Print(\"executable not found\")\n\t\tos.Exit(126)\n\t\treturn executable, plugins.Plugin{}, \"\", fmt.Errorf(\"executable not found\")\n\t}\n\n\treturn executable, plugin, version, nil\n}\n\nfunc anyInstalled(conf config.Config, toolVersions []toolversions.ToolVersions) bool {\n\tfor _, toolVersion := range toolVersions {\n\t\tfor _, version := range toolVersion.Versions {\n\t\t\tversion := toolversions.Parse(version)\n\t\t\tplugin := plugins.New(conf, toolVersion.Name)\n\t\t\tif installs.IsInstalled(conf, plugin, version) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}\n\treturn false\n}\n\nfunc pluginAddCommand(_ *cli.Command, conf config.Config, logger *log.Logger, pluginName, pluginRepo string) error {\n\tif pluginName == \"\" {\n\t\t// Invalid arguments\n\t\t// Maybe one day switch this to show the generated help\n\t\t// cli.ShowSubcommandHelp(cCtx)\n\t\treturn cli.Exit(\"usage: asdf plugin add <name> [<git-url>]\", 1)\n\t}\n\n\terr := plugins.Add(conf, pluginName, pluginRepo, \"\")\n\tif err != nil {\n\t\tlogger.Printf(\"%s\", err)\n\n\t\tvar existsErr plugins.PluginAlreadyExists\n\t\tif errors.As(err, &existsErr) {\n\t\t\tos.Exit(0)\n\t\t\treturn nil\n\t\t}\n\n\t\tcli.OsExiter(1)\n\t\treturn nil\n\t}\n\n\tos.Exit(0)\n\treturn nil\n}\n\nfunc pluginRemoveCommand(_ *cli.Command, logger *log.Logger, pluginName string) error {\n\tif pluginName == \"\" {\n\t\tlogger.Print(\"No plugin given\")\n\t\tcli.OsExiter(1)\n\t\treturn nil\n\t}\n\n\tconf, err := config.LoadConfig()\n\tif err != nil {\n\t\tlogger.Printf(\"error loading config: %s\", err)\n\t\treturn err\n\t}\n\n\terr = plugins.Remove(conf, pluginName, os.Stdout, os.Stderr)\n\tif err != nil {\n\t\t// Needed to match output of old version\n\t\tlogger.Printf(\"%s\", err)\n\t}\n\n\t// This feels a little hacky but it works, to re-generate shims we delete them\n\t// all and generate them again.\n\terr2 := shims.RemoveAll(conf)\n\tif err2 != nil {\n\t\tlogger.Printf(\"%s\", err2)\n\t\tcli.OsExiter(1)\n\t\treturn err2\n\t}\n\n\tshims.GenerateAll(conf, os.Stdout, os.Stderr)\n\treturn err\n}\n\nfunc pluginListCommand(cCtx *cli.Command, logger *log.Logger) error {\n\turls := cCtx.Bool(\"urls\")\n\trefs := cCtx.Bool(\"refs\")\n\n\tconf, err := config.LoadConfig()\n\tif err != nil {\n\t\tlogger.Printf(\"error loading config: %s\", err)\n\t\treturn err\n\t}\n\n\tplugins, err := plugins.List(conf, urls, refs)\n\tif err != nil {\n\t\tlogger.Printf(\"error loading plugin list: %s\", err)\n\t\treturn err\n\t}\n\n\tif len(plugins) == 0 {\n\t\tlogger.Println(\"No plugins installed\")\n\t\treturn nil\n\t}\n\n\t// TODO: Add some sort of presenter logic in another file so we\n\t// don't clutter up this cmd code with conditional presentation\n\t// logic\n\tfor _, plugin := range plugins {\n\t\tif urls && refs {\n\t\t\tfmt.Printf(\"%s\\t\\t%s\\t%s\\n\", plugin.Name, plugin.URL, plugin.Ref)\n\t\t} else if refs {\n\t\t\tfmt.Printf(\"%s\\t\\t%s\\n\", plugin.Name, plugin.Ref)\n\t\t} else if urls {\n\t\t\tfmt.Printf(\"%s\\t\\t%s\\n\", plugin.Name, plugin.URL)\n\t\t} else {\n\t\t\tfmt.Printf(\"%s\\n\", plugin.Name)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc pluginListAllCommand(logger *log.Logger) error {\n\tconf, err := config.LoadConfig()\n\tif err != nil {\n\t\tlogger.Printf(\"error loading config: %s\", err)\n\t\treturn err\n\t}\n\n\tdisableRepo, err := conf.DisablePluginShortNameRepository()\n\tif err != nil {\n\t\tlogger.Printf(\"unable to check config\")\n\t\treturn err\n\t}\n\tif disableRepo {\n\t\tlogger.Printf(\"Short-name plugin repository is disabled\")\n\t\tcli.OsExiter(1)\n\t\treturn nil\n\t}\n\n\tlastCheckDuration := 0\n\t// We don't care about errors here as we can use the default value\n\tcheckDuration, _ := conf.PluginRepositoryLastCheckDuration()\n\n\tif !checkDuration.Never {\n\t\tlastCheckDuration = checkDuration.Every\n\t}\n\n\tindex := pluginindex.Build(conf.DataDir, conf.PluginIndexURL, false, lastCheckDuration)\n\tavailablePlugins, err := index.Get()\n\tif err != nil {\n\t\tlogger.Printf(\"error loading plugin index: %s\", err)\n\t\treturn err\n\t}\n\n\tinstalledPlugins, err := plugins.List(conf, true, false)\n\tif err != nil {\n\t\tlogger.Printf(\"error loading plugin list: %s\", err)\n\t\treturn err\n\t}\n\n\tw := tabwriter.NewWriter(os.Stdout, 15, 0, 1, ' ', 0)\n\tfor _, availablePlugin := range availablePlugins {\n\t\tif pluginInstalled(availablePlugin, installedPlugins) {\n\t\t\tfmt.Fprintf(w, \"%s\\t\\t*%s\\n\", availablePlugin.Name, availablePlugin.URL)\n\t\t} else {\n\t\t\tfmt.Fprintf(w, \"%s\\t\\t%s\\n\", availablePlugin.Name, availablePlugin.URL)\n\t\t}\n\t}\n\tw.Flush()\n\n\treturn nil\n}\n\nfunc pluginInstalled(plugin pluginindex.Plugin, installedPlugins []plugins.Plugin) bool {\n\tfor _, installedPlugin := range installedPlugins {\n\t\tif installedPlugin.Name == plugin.Name && installedPlugin.URL == plugin.URL {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc infoCommand(conf config.Config, version string) error {\n\treturn info.Print(conf, version)\n}\n\nfunc helpCommand(logger *log.Logger, asdfVersion, tool, version string) error {\n\tconf, err := config.LoadConfig()\n\tif err != nil {\n\t\tlogger.Printf(\"error loading config: %s\", err)\n\t\treturn err\n\t}\n\n\tif tool != \"\" {\n\t\tif version != \"\" {\n\t\t\terr := help.PrintToolVersion(conf, tool, version)\n\t\t\tif err != nil {\n\t\t\t\tcli.OsExiter(1)\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\n\t\terr := help.PrintTool(conf, tool)\n\t\tif err != nil {\n\t\t\tcli.OsExiter(1)\n\t\t}\n\t\treturn err\n\t}\n\n\tallPlugins, err := plugins.List(conf, false, false)\n\tif err != nil {\n\t\tcli.OsExiter(1)\n\t}\n\n\terr = help.Print(asdfVersion, allPlugins)\n\tif err != nil {\n\t\tcli.OsExiter(1)\n\t}\n\n\treturn err\n}\n\nfunc pluginUpdateCommand(cCtx *cli.Command, logger *log.Logger, pluginName, ref string) error {\n\tupdateAll := cCtx.Bool(\"all\")\n\tif !updateAll && pluginName == \"\" {\n\t\treturn cli.Exit(\"usage: asdf plugin update {<name> [git-ref] | --all}\", 1)\n\t}\n\n\tconf, err := config.LoadConfig()\n\tif err != nil {\n\t\tlogger.Printf(\"error loading config: %s\", err)\n\t\treturn err\n\t}\n\n\tif updateAll {\n\t\tinstalledPlugins, err := plugins.List(conf, false, false)\n\t\tif err != nil {\n\t\t\tlogger.Printf(\"failed to get plugin list: %s\", err)\n\t\t\treturn err\n\t\t}\n\n\t\tfor _, plugin := range installedPlugins {\n\t\t\tupdatedToRef, err := plugin.Update(conf, \"\", os.Stdout, os.Stderr)\n\t\t\tformatUpdateResult(logger, plugin.Name, updatedToRef, err)\n\t\t}\n\n\t\treturn nil\n\t}\n\n\tplugin := plugins.New(conf, pluginName)\n\tupdatedToRef, err := plugin.Update(conf, ref, os.Stdout, os.Stderr)\n\tformatUpdateResult(logger, pluginName, updatedToRef, err)\n\treturn err\n}\n\nfunc pluginTestCommand(l *log.Logger, args []string, toolVersion, ref string) {\n\tconf, err := config.LoadConfig()\n\tif err != nil {\n\t\tl.Printf(\"error loading config: %s\", err)\n\t\tcli.OsExiter(1)\n\t\treturn\n\t}\n\n\tif len(args) < 2 {\n\t\tfailTest(l, \"please provide a plugin name and url\")\n\t}\n\n\tname := args[0]\n\turl := args[1]\n\ttestName := fmt.Sprintf(\"asdf-test-%s\", name)\n\n\t// Install plugin\n\terr = plugins.Add(conf, testName, url, ref)\n\tif err != nil {\n\t\tfailTest(l, fmt.Sprintf(\"%s was not properly installed reason: %s\", name, err))\n\t}\n\n\t// Remove plugin\n\tvar blackhole strings.Builder\n\tdefer plugins.Remove(conf, testName, &blackhole, &blackhole)\n\n\t// Assert callbacks are present\n\tplugin := plugins.New(conf, testName)\n\tfiles, err := os.ReadDir(filepath.Join(plugin.Dir, \"bin\"))\n\tif _, ok := err.(*fs.PathError); ok {\n\t\tfailTest(l, \"bin/ directory does not exist\")\n\t}\n\n\tcallbacks := []string{}\n\tfor _, file := range files {\n\t\tcallbacks = append(callbacks, file.Name())\n\t}\n\n\tfor _, expectedCallback := range []string{\"download\", \"install\", \"list-all\"} {\n\t\tif !slices.Contains(callbacks, expectedCallback) {\n\t\t\tfailTest(l, fmt.Sprintf(\"missing callback %s\", expectedCallback))\n\t\t}\n\t}\n\n\tallCallbacks := []string{\"download\", \"install\", \"list-all\", \"latest-stable\", \"help.overview\", \"help.deps\", \"help.config\", \"help.links\", \"list-bin-paths\", \"exec-env\", \"exec-path\", \"uninstall\", \"list-legacy-filenames\", \"parse-legacy-file\", \"post-plugin-add\", \"post-plugin-update\", \"pre-plugin-remove\"}\n\n\t// Assert all callbacks present are executable\n\tfor _, file := range files {\n\t\t// file is a callback...\n\t\tif slices.Contains(allCallbacks, file.Name()) {\n\t\t\t// check if it is executable\n\t\t\tinfo, _ := file.Info()\n\t\t\tif !(info.Mode()&0o111 != 0) {\n\t\t\t\tfailTest(l, fmt.Sprintf(\"callback lacks executable permission: %s\", file.Name()))\n\t\t\t}\n\t\t}\n\t}\n\n\t// Assert has license\n\tlicensePath := filepath.Join(plugin.Dir, \"LICENSE\")\n\tif _, err := os.Stat(licensePath); errors.Is(err, os.ErrNotExist) {\n\t\tfailTest(l, \"LICENSE file must be present in the plugin repository\")\n\t}\n\n\tbytes, err := os.ReadFile(licensePath)\n\tif err != nil {\n\t\tfailTest(l, \"LICENSE file must be present in the plugin repository\")\n\t}\n\n\t// Validate license file not empty\n\tif len(bytes) == 0 {\n\t\tfailTest(l, \"LICENSE file in the plugin repository must not be empty\")\n\t}\n\n\t// Validate it returns at least one available version\n\tvar output strings.Builder\n\terr = plugin.RunCallback(\"list-all\", []string{}, map[string]string{}, &output, &blackhole)\n\tif err != nil {\n\t\tfailTest(l, \"Unable to list available versions\")\n\t}\n\n\tallVersions := strings.Fields(output.String())\n\tif len(allVersions) < 1 {\n\t\tfailTest(l, \"list-all did not return any version\")\n\t}\n\n\t// grab first version returned by list-all callback if no version provided as\n\t// a CLI argument\n\tif toolVersion == \"\" {\n\t\ttoolVersion = allVersions[0]\n\t}\n\n\terr = versions.InstallOneVersion(conf, plugin, toolVersion, false, os.Stdout, os.Stderr)\n\tif err != nil {\n\t\tfailTest(l, \"install exited with an error\")\n\t}\n}\n\nfunc failTest(logger *log.Logger, msg string) {\n\tlogger.Printf(\"FAILED: %s\", msg)\n\tcli.OsExiter(1)\n}\n\nfunc formatUpdateResult(logger *log.Logger, pluginName, updatedToRef string, err error) {\n\tif err != nil {\n\t\tlogger.Printf(\"failed to update %s due to error: %s\\n\", pluginName, err)\n\n\t\treturn\n\t}\n\n\tlogger.Printf(\"updated %s to ref %s\\n\", pluginName, updatedToRef)\n}\n\nfunc installCommand(logger *log.Logger, toolName, version string, keepDownload bool) error {\n\tconf, err := config.LoadConfig()\n\tif err != nil {\n\t\tlogger.Printf(\"error loading config: %s\", err)\n\t\treturn err\n\t}\n\n\tdir, err := os.Getwd()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to fetch current directory: %w\", err)\n\t}\n\n\tif toolName == \"\" {\n\t\t// Install all versions\n\t\terrs := versions.InstallAll(conf, dir, os.Stdout, os.Stderr)\n\t\tif len(errs) > 0 {\n\t\t\tfor _, err := range errs {\n\t\t\t\t// Don't print error if no version set, this just means the current\n\t\t\t\t// dir doesn't use a particular plugin that is installed.\n\t\t\t\tif _, ok := err.(versions.NoVersionSetError); ok {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tif _, ok := err.(versions.UninstallableVersionError); ok {\n\t\t\t\t\tmsg := fmt.Sprintf(\"skipping %s\\n\", err.Error())\n\t\t\t\t\tos.Stderr.Write([]byte(msg))\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tos.Stderr.Write([]byte(err.Error()))\n\t\t\t\tos.Stderr.Write([]byte(\"\\n\"))\n\t\t\t}\n\n\t\t\tfiltered := filterInstallErrors(errs)\n\t\t\tif len(filtered) > 0 {\n\t\t\t\treturn filtered[0]\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\t} else {\n\t\t// Install specific version\n\t\tplugin := plugins.New(conf, toolName)\n\n\t\tif version == \"\" {\n\t\t\terr = versions.Install(conf, plugin, dir, os.Stdout, os.Stderr)\n\t\t\tif err != nil {\n\t\t\t\tvar vaiErr versions.VersionAlreadyInstalledError\n\t\t\t\tif errors.As(err, &vaiErr) {\n\t\t\t\t\tlogger.Println(err)\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\n\t\t\t\tif _, ok := err.(versions.NoVersionSetError); ok {\n\t\t\t\t\tlogger.Printf(\"No versions specified for %s in config files or environment\", toolName)\n\t\t\t\t\tcli.OsExiter(1)\n\t\t\t\t}\n\n\t\t\t\tlogger.Printf(\"error installing version: %v\", err)\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else {\n\t\t\tparsedVersion := toolversions.ParseFromCliArg(version)\n\n\t\t\tif parsedVersion.Type == \"latest\" {\n\t\t\t\terr = versions.InstallVersion(conf, plugin, parsedVersion, os.Stdout, os.Stderr)\n\t\t\t} else {\n\t\t\t\t// Adding this here to get tests passing. The other versions.Install*\n\t\t\t\t// calls here could have a keepDownload argument added as well. PR\n\t\t\t\t// welcome!\n\t\t\t\terr = versions.InstallOneVersion(conf, plugin, version, keepDownload, os.Stdout, os.Stderr)\n\t\t\t}\n\n\t\t\tif err != nil {\n\t\t\t\tvar vaiErr versions.VersionAlreadyInstalledError\n\t\t\t\tif errors.As(err, &vaiErr) {\n\t\t\t\t\tlogger.Println(err)\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\n\t\t\t\tlogger.Printf(\"error installing version: %v\", err)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn err\n}\n\nfunc filterInstallErrors(errs []error) []error {\n\tvar filtered []error\n\tfor _, err := range errs {\n\t\tvar vaiErr versions.VersionAlreadyInstalledError\n\t\tif errors.As(err, &vaiErr) {\n\t\t\tcontinue\n\t\t}\n\n\t\tif _, ok := err.(versions.NoVersionSetError); !ok {\n\t\t\tfiltered = append(filtered, err)\n\t\t}\n\t}\n\treturn filtered\n}\n\nfunc latestCommand(logger *log.Logger, all bool, toolName, pattern string) (err error) {\n\tconf, err := config.LoadConfig()\n\tif err != nil {\n\t\tlogger.Printf(\"error loading config: %s\", err)\n\t\treturn err\n\t}\n\n\tif !all {\n\t\terr = latestForPlugin(conf, toolName, pattern, false)\n\t\tif err != nil {\n\t\t\tcli.OsExiter(1)\n\t\t}\n\n\t\treturn err\n\t}\n\n\tplugins, err := plugins.List(conf, false, false)\n\tif err != nil {\n\t\tlogger.Printf(\"error loading plugin list: %s\", err)\n\t\treturn err\n\t}\n\n\tvar maybeErr error\n\t// loop over all plugins and show latest for each one.\n\tfor _, plugin := range plugins {\n\t\tmaybeErr = latestForPlugin(conf, plugin.Name, \"\", true)\n\t\tif maybeErr != nil {\n\t\t\terr = maybeErr\n\t\t}\n\t}\n\n\tif err != nil {\n\t\tcli.OsExiter(1)\n\t\treturn maybeErr\n\t}\n\treturn nil\n}\n\nfunc listCommand(logger *log.Logger, first, second, third string) (err error) {\n\tconf, err := config.LoadConfig()\n\tif err != nil {\n\t\tlogger.Printf(\"error loading config: %s\", err)\n\t\treturn err\n\t}\n\n\t// Both listAllCommand and listLocalCommand need to be refactored and extracted\n\t// out into another package.\n\tif first == \"all\" {\n\t\treturn listAllCommand(logger, conf, second, third)\n\t}\n\n\treturn listLocalCommand(logger, conf, first, second)\n}\n\nfunc listAllCommand(logger *log.Logger, conf config.Config, toolName, filter string) error {\n\tif toolName == \"\" {\n\t\tlogger.Print(\"No plugin given\")\n\t\tcli.OsExiter(1)\n\t\treturn nil\n\t}\n\n\tplugin, err := loadPlugin(logger, conf, toolName)\n\tif err != nil {\n\t\tcli.OsExiter(1)\n\t\treturn err\n\t}\n\n\tvar stdout strings.Builder\n\tvar stderr strings.Builder\n\n\terr = plugin.RunCallback(\"list-all\", []string{}, map[string]string{}, &stdout, &stderr)\n\tif err != nil {\n\t\tfmt.Printf(\"Plugin %s's list-all callback script failed with output:\\n\", plugin.Name)\n\t\t// Print to stderr\n\t\tos.Stderr.WriteString(stderr.String())\n\t\tos.Stderr.WriteString(stdout.String())\n\n\t\tcli.OsExiter(1)\n\t\treturn err\n\t}\n\n\tversions := strings.Split(stdout.String(), \" \")\n\n\tif filter != \"\" {\n\t\tversions = filterByExactMatch(versions, filter)\n\t}\n\n\tif len(versions) == 0 {\n\t\tlogger.Printf(\"No compatible versions available (%s %s)\", plugin.Name, filter)\n\t\tcli.OsExiter(1)\n\t\treturn nil\n\t}\n\n\tfor _, version := range versions {\n\t\tfmt.Printf(\"%s\\n\", version)\n\t}\n\n\treturn nil\n}\n\nfunc filterByExactMatch(allVersions []string, pattern string) (versions []string) {\n\tfor _, version := range allVersions {\n\t\tif strings.HasPrefix(version, pattern) {\n\t\t\tversions = append(versions, version)\n\t\t}\n\t}\n\n\treturn versions\n}\n\nfunc listLocalCommand(logger *log.Logger, conf config.Config, pluginName, filter string) error {\n\tcurrentDir, err := os.Getwd()\n\tif err != nil {\n\t\tlogger.Printf(\"unable to get current directory: %s\", err)\n\t\treturn err\n\t}\n\n\tif pluginName != \"\" {\n\t\tplugin, err := loadPlugin(logger, conf, pluginName)\n\t\tif err != nil {\n\t\t\tcli.OsExiter(1)\n\t\t\treturn err\n\t\t}\n\t\tversions, _ := installs.Installed(conf, plugin)\n\n\t\tif filter != \"\" {\n\t\t\tversions = filterByExactMatch(versions, filter)\n\t\t}\n\n\t\tif len(versions) == 0 {\n\t\t\tif filter == \"\" {\n\t\t\t\tlogger.Printf(\"No compatible versions installed (%s)\", plugin.Name)\n\t\t\t} else {\n\t\t\t\tlogger.Printf(\"No compatible versions installed (%s %s)\", plugin.Name, filter)\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\n\t\tcurrentVersions, _, err := resolve.Version(conf, plugin, currentDir)\n\t\tif err != nil {\n\t\t\tcli.OsExiter(1)\n\t\t\treturn err\n\t\t}\n\n\t\tfor _, version := range versions {\n\t\t\tif slices.Contains(currentVersions.Versions, version) {\n\t\t\t\tfmt.Printf(\" *%s\\n\", version)\n\t\t\t} else {\n\t\t\t\tfmt.Printf(\"  %s\\n\", version)\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\n\tallPlugins, err := plugins.List(conf, false, false)\n\tif err != nil {\n\t\tlogger.Printf(\"unable to list plugins due to error: %s\", err)\n\t\treturn err\n\t}\n\n\tfor _, plugin := range allPlugins {\n\t\tfmt.Printf(\"%s\\n\", plugin.Name)\n\t\tversions, _ := installs.Installed(conf, plugin)\n\n\t\tif len(versions) > 0 {\n\t\t\tcurrentVersions, _, err := resolve.Version(conf, plugin, currentDir)\n\t\t\tif err != nil {\n\t\t\t\tcli.OsExiter(1)\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tfor _, version := range versions {\n\t\t\t\tif slices.Contains(currentVersions.Versions, version) {\n\t\t\t\t\tfmt.Printf(\" *%s\\n\", version)\n\t\t\t\t} else {\n\t\t\t\t\tfmt.Printf(\"  %s\\n\", version)\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tfmt.Print(\"  No versions installed\\n\")\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc reshimCommand(logger *log.Logger, tool, version string) (err error) {\n\tconf, err := config.LoadConfig()\n\tif err != nil {\n\t\tlogger.Printf(\"error loading config: %s\", err)\n\t\treturn err\n\t}\n\n\tvar plugin plugins.Plugin\n\n\tif tool != \"\" {\n\t\tplugin = plugins.New(conf, tool)\n\t\tif err := plugin.Exists(); err != nil {\n\t\t\tlogger.Printf(\"No such plugin: %s\", plugin.Name)\n\t\t\tcli.OsExiter(1)\n\t\t\treturn err\n\t\t}\n\t}\n\t// if either tool or version are missing just regenerate all shims. This is\n\t// fast enough now.\n\tif tool == \"\" || version == \"\" {\n\t\terr = shims.RemoveAll(conf)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\treturn shims.GenerateAll(conf, os.Stdout, os.Stderr)\n\t}\n\n\t// If provided a specific version it could be something special like a path\n\t// version so we need to generate it manually\n\treturn reshimToolVersion(conf, plugin, version, os.Stdout, os.Stderr)\n}\n\nfunc shimVersionsCommand(logger *log.Logger, shimName string) error {\n\tif shimName == \"\" {\n\t\tlogger.Printf(\"usage: asdf shimversions <command>\")\n\t\treturn fmt.Errorf(\"usage: asdf shimversions <command>\")\n\t}\n\n\tconf, err := config.LoadConfig()\n\tif err != nil {\n\t\tlogger.Printf(\"error loading config: %s\", err)\n\t\treturn err\n\t}\n\n\tshimPath := shims.Path(conf, shimName)\n\ttoolVersions, err := shims.GetToolsAndVersionsFromShimFile(shimPath)\n\tfor _, toolVersion := range toolVersions {\n\t\tfor _, version := range toolVersion.Versions {\n\t\t\tfmt.Printf(\"%s %s\\n\", toolVersion.Name, version)\n\t\t}\n\t}\n\treturn err\n}\n\n// This function is a whole mess and needs to be refactored\nfunc whichCommand(logger *log.Logger, command string) error {\n\tconf, err := config.LoadConfig()\n\tif err != nil {\n\t\tlogger.Printf(\"error loading config: %s\", err)\n\t\treturn err\n\t}\n\n\tcurrentDir, err := os.Getwd()\n\tif err != nil {\n\t\tlogger.Printf(\"unable to get current directory: %s\", err)\n\t\treturn err\n\t}\n\n\tif command == \"\" {\n\t\tfmt.Println(\"usage: asdf which <command>\")\n\t\treturn errors.New(\"must provide command\")\n\t}\n\n\tpath, _, _, _, err := shims.FindExecutable(conf, command, currentDir)\n\tif _, ok := err.(shims.UnknownCommandError); ok {\n\t\tlogger.Printf(\"unknown command: %s. Perhaps you have to reshim?\", command)\n\t\treturn errors.New(\"command not found\")\n\t}\n\n\tif _, ok := err.(shims.NoExecutableForPluginError); ok {\n\t\tlogger.Printf(\"%s\", err.Error())\n\t\treturn errors.New(\"no executable for tool version\")\n\t}\n\n\tif err != nil {\n\t\tfmt.Printf(\"unexpected error: %s\\n\", err.Error())\n\t\treturn err\n\t}\n\n\tfmt.Printf(\"%s\\n\", path)\n\treturn nil\n}\n\nfunc uninstallCommand(logger *log.Logger, tool, version string) error {\n\tif tool == \"\" || version == \"\" {\n\t\tlogger.Print(\"No plugin given\")\n\t\tcli.OsExiter(1)\n\t\treturn nil\n\t}\n\n\tconf, err := config.LoadConfig()\n\tif err != nil {\n\t\tlogger.Printf(\"error loading config: %s\", err)\n\t\tcli.OsExiter(1)\n\t\treturn err\n\t}\n\n\tplugin := plugins.New(conf, tool)\n\terr = versions.Uninstall(conf, plugin, version, os.Stdout, os.Stderr)\n\tif err != nil {\n\t\tlogger.Printf(\"%s\", err)\n\t\tcli.OsExiter(1)\n\t\treturn err\n\t}\n\n\t// This feels a little hacky but it works, to re-generate shims we delete them\n\t// all and generate them again.\n\terr = shims.RemoveAll(conf)\n\tif err != nil {\n\t\tlogger.Printf(\"%s\", err)\n\t\tcli.OsExiter(1)\n\t\treturn err\n\t}\n\n\treturn shims.GenerateAll(conf, os.Stdout, os.Stderr)\n}\n\nfunc whereCommand(logger *log.Logger, tool, versionStr string) error {\n\tconf, err := config.LoadConfig()\n\tif err != nil {\n\t\tlogger.Printf(\"error loading config: %s\", err)\n\t\treturn err\n\t}\n\n\tcurrentDir, err := os.Getwd()\n\tif err != nil {\n\t\tlogger.Printf(\"unable to get current directory: %s\", err)\n\t\treturn err\n\t}\n\n\tplugin := plugins.New(conf, tool)\n\terr = plugin.Exists()\n\tif err != nil {\n\t\tif _, ok := err.(plugins.PluginMissing); ok {\n\t\t\tlogger.Printf(\"No such plugin: %s\", tool)\n\t\t}\n\t\treturn err\n\t}\n\n\tversion := toolversions.Parse(versionStr)\n\n\tif version.Type == \"system\" {\n\t\tlogger.Printf(\"System version is selected\")\n\t\treturn errors.New(\"System version is selected\")\n\t}\n\n\tif version.Value == \"\" {\n\t\t// resolve version\n\t\tversions, found, err := resolve.Version(conf, plugin, currentDir)\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"err %#+v\\n\", err)\n\t\t\treturn err\n\t\t}\n\n\t\tif found && len(versions.Versions) > 0 {\n\t\t\tversionStruct := toolversions.Version{Type: \"version\", Value: versions.Versions[0]}\n\t\t\tif installs.IsInstalled(conf, plugin, versionStruct) {\n\t\t\t\tinstallPath := installs.InstallPath(conf, plugin, versionStruct)\n\t\t\t\tfmt.Printf(\"%s\", installPath)\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\n\t\t// not found\n\t\tmsg := fmt.Sprintf(\"No version is set for %s; please run `asdf set [options] %s <version>`\", tool, tool)\n\t\tlogger.Print(msg)\n\t\treturn errors.New(msg)\n\t}\n\n\tif !installs.IsInstalled(conf, plugin, version) {\n\t\tlogger.Printf(\"Version not installed\")\n\t\treturn errors.New(\"Version not installed\")\n\t}\n\n\tinstallPath := installs.InstallPath(conf, plugin, version)\n\tfmt.Printf(\"%s\", installPath)\n\n\treturn nil\n}\n\nfunc loadPlugin(logger *log.Logger, conf config.Config, pluginName string) (plugins.Plugin, error) {\n\tplugin := plugins.New(conf, pluginName)\n\terr := plugin.Exists()\n\tif err != nil {\n\t\tlogger.Printf(\"No such plugin: %s\", pluginName)\n\t\treturn plugin, err\n\t}\n\n\treturn plugin, err\n}\n\nfunc reshimToolVersion(conf config.Config, plugin plugins.Plugin, versionStr string, out io.Writer, errOut io.Writer) error {\n\tversion := toolversions.Parse(versionStr)\n\n\treturn shims.GenerateForVersion(conf, plugin, version, out, errOut)\n}\n\nfunc latestForPlugin(conf config.Config, toolName, pattern string, showStatus bool) error {\n\t// show single plugin\n\tplugin := plugins.New(conf, toolName)\n\tlatest, err := versions.Latest(plugin, pattern)\n\tif err != nil && err.Error() != \"no latest version found\" {\n\t\tfmt.Printf(\"unable to load latest version: %s\\n\", err)\n\t\treturn err\n\t}\n\n\tif latest == \"\" {\n\t\terr := fmt.Errorf(\"No compatible versions available (%s %s)\", toolName, pattern)\n\t\tfmt.Println(err.Error())\n\t\treturn err\n\t}\n\n\tif showStatus {\n\t\tinstalled := installs.IsInstalled(conf, plugin, toolversions.Version{Type: \"version\", Value: latest})\n\t\tfmt.Printf(\"%s\\t%s\\t%s\\n\", plugin.Name, latest, installedStatus(installed))\n\t} else {\n\t\tfmt.Printf(\"%s\\n\", latest)\n\t}\n\treturn nil\n}\n\nfunc installedStatus(installed bool) string {\n\tif installed {\n\t\treturn \"installed\"\n\t}\n\treturn \"missing\"\n}\n\nfunc unsetAsdfReservedEnvVars() error {\n\t// These are environment variables which are passed via env or exec.\n\t// We strip these out to avoid any potential issues with recursive calls to asdf.\n\tasdfManagedVars := []string{\"ASDF_INSTALL_TYPE\", \"ASDF_INSTALL_VERSION\", \"ASDF_INSTALL_PATH\"}\n\tfor _, v := range asdfManagedVars {\n\t\terr := os.Unsetenv(v)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "internal/cli/set/set.go",
    "content": "// Package set provides the 'asdf set' command\npackage set\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/asdf-vm/asdf/internal/config\"\n\t\"github.com/asdf-vm/asdf/internal/plugins\"\n\t\"github.com/asdf-vm/asdf/internal/toolversions\"\n\t\"github.com/asdf-vm/asdf/internal/versions\"\n)\n\n// Main function is the entrypoint for the 'asdf set' command\nfunc Main(_ io.Writer, stderr io.Writer, args []string, home bool, parent bool, homeFunc func() (string, error)) error {\n\tif len(args) < 1 {\n\t\treturn printError(stderr, \"tool and version must be provided as arguments\")\n\t}\n\n\tif len(args) < 2 {\n\t\treturn printError(stderr, \"version must be provided as an argument\")\n\t}\n\n\tif home && parent {\n\t\treturn printError(stderr, \"home and parent flags cannot both be specified; must be one location or the other\")\n\t}\n\n\tconf, err := config.LoadConfig()\n\tif err != nil {\n\t\treturn printError(stderr, fmt.Sprintf(\"error loading config: %s\", err))\n\t}\n\n\tresolvedVersions := []string{}\n\n\tfor _, version := range args[1:] {\n\t\tparsedVersion := toolversions.ParseFromCliArg(version)\n\t\tif parsedVersion.Type == \"latest\" {\n\t\t\tplugin := plugins.New(conf, args[0])\n\t\t\tresolvedVersion, err := versions.Latest(plugin, parsedVersion.Value)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"unable to resolve latest version for %s\", plugin.Name)\n\t\t\t}\n\t\t\tresolvedVersions = append(resolvedVersions, resolvedVersion)\n\t\t\tcontinue\n\t\t}\n\t\tresolvedVersions = append(resolvedVersions, version)\n\t}\n\n\ttv := toolversions.ToolVersions{Name: args[0], Versions: resolvedVersions}\n\n\tif home {\n\t\thomeDir, err := homeFunc()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tfilepath := filepath.Join(homeDir, conf.DefaultToolVersionsFilename)\n\t\terr = toolversions.WriteToolVersionsToFile(filepath, []toolversions.ToolVersions{tv})\n\t\tif err != nil {\n\t\t\terr = printError(stderr, fmt.Sprintf(\"error writing version file: %s\", err))\n\t\t}\n\t\treturn err\n\t}\n\n\tcurrentDir, err := os.Getwd()\n\tif err != nil {\n\t\treturn printError(stderr, fmt.Sprintf(\"unable to get current directory: %s\", err))\n\t}\n\n\tif parent {\n\t\t// locate file in parent dir and update it\n\t\tpath, found := findVersionFileInParentDir(conf, currentDir)\n\t\tif !found {\n\t\t\treturn printError(stderr, fmt.Sprintf(\"No %s version file found in parent directory\", conf.DefaultToolVersionsFilename))\n\t\t}\n\n\t\terr = toolversions.WriteToolVersionsToFile(path, []toolversions.ToolVersions{tv})\n\t\tif err != nil {\n\t\t\terr = printError(stderr, fmt.Sprintf(\"error writing version file: %s\", err))\n\t\t}\n\t\treturn err\n\t}\n\n\t// Write new file in current dir\n\tfilepath := filepath.Join(currentDir, conf.DefaultToolVersionsFilename)\n\treturn toolversions.WriteToolVersionsToFile(filepath, []toolversions.ToolVersions{tv})\n}\n\nfunc printError(stderr io.Writer, msg string) error {\n\tif !strings.HasSuffix(msg, \"\\n\") {\n\t\tmsg += \"\\n\"\n\t}\n\tfmt.Fprint(stderr, msg)\n\treturn errors.New(strings.TrimSuffix(msg, \"\\n\"))\n}\n\nfunc findVersionFileInParentDir(conf config.Config, directory string) (string, bool) {\n\tdirectory = filepath.Dir(directory)\n\n\tfor {\n\t\tpath := filepath.Join(directory, conf.DefaultToolVersionsFilename)\n\t\tif _, err := os.Stat(path); err == nil {\n\t\t\treturn path, true\n\t\t}\n\n\t\tif directory == \"/\" {\n\t\t\treturn \"\", false\n\t\t}\n\n\t\tdirectory = filepath.Dir(directory)\n\t}\n}\n"
  },
  {
    "path": "internal/cli/set/set_test.go",
    "content": "package set\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestAll(t *testing.T) {\n\thomeFunc := func() (string, error) {\n\t\treturn \"\", nil\n\t}\n\n\tt.Run(\"prints error when no arguments specified\", func(t *testing.T) {\n\t\tstdout, stderr := buildOutputs()\n\t\terr := Main(&stdout, &stderr, []string{}, false, false, homeFunc)\n\n\t\tassert.Error(t, err, \"tool and version must be provided as arguments\")\n\t\tassert.Equal(t, stdout.String(), \"\")\n\t\tassert.Equal(t, stderr.String(), \"tool and version must be provided as arguments\\n\")\n\t})\n\n\tt.Run(\"prints error when no version specified\", func(t *testing.T) {\n\t\tstdout, stderr := buildOutputs()\n\t\terr := Main(&stdout, &stderr, []string{\"lua\"}, false, false, homeFunc)\n\n\t\tassert.Error(t, err, \"version must be provided as an argument\")\n\t\tassert.Equal(t, stdout.String(), \"\")\n\t\tassert.Equal(t, stderr.String(), \"version must be provided as an argument\\n\")\n\t})\n\n\tt.Run(\"prints error when both --parent and --home flags are set\", func(t *testing.T) {\n\t\tstdout, stderr := buildOutputs()\n\t\terr := Main(&stdout, &stderr, []string{\"lua\", \"5.2.3\"}, true, true, homeFunc)\n\n\t\tassert.Error(t, err, \"home and parent flags cannot both be specified; must be one location or the other\")\n\t\tassert.Equal(t, stdout.String(), \"\")\n\t\tassert.Equal(t, stderr.String(), \"home and parent flags cannot both be specified; must be one location or the other\\n\")\n\t})\n\n\tt.Run(\"sets version in current directory when no flags provided\", func(t *testing.T) {\n\t\tstdout, stderr := buildOutputs()\n\t\tdir := t.TempDir()\n\t\tassert.Nil(t, os.Chdir(dir))\n\n\t\terr := Main(&stdout, &stderr, []string{\"lua\", \"5.2.3\"}, false, false, homeFunc)\n\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, stdout.String(), \"\")\n\t\tassert.Equal(t, stderr.String(), \"\")\n\n\t\tpath := filepath.Join(dir, \".tool-versions\")\n\t\tbytes, err := os.ReadFile(path)\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, \"lua 5.2.3\\n\", string(bytes))\n\t})\n\n\tt.Run(\"sets version in parent directory when --parent flag provided\", func(t *testing.T) {\n\t\tstdout, stderr := buildOutputs()\n\t\tdir := t.TempDir()\n\t\tsubdir := filepath.Join(dir, \"subdir\")\n\t\tassert.Nil(t, os.Mkdir(subdir, 0o777))\n\t\tassert.Nil(t, os.Chdir(subdir))\n\t\tassert.Nil(t, os.WriteFile(filepath.Join(dir, \".tool-versions\"), []byte(\"lua 4.0.0\"), 0o666))\n\n\t\terr := Main(&stdout, &stderr, []string{\"lua\", \"5.2.3\"}, false, true, homeFunc)\n\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, stdout.String(), \"\")\n\t\tassert.Equal(t, stderr.String(), \"\")\n\n\t\tpath := filepath.Join(dir, \".tool-versions\")\n\t\tbytes, err := os.ReadFile(path)\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, \"lua 5.2.3\\n\", string(bytes))\n\t})\n\n\tt.Run(\"sets version in home directory when --home flag provided\", func(t *testing.T) {\n\t\tstdout, stderr := buildOutputs()\n\t\thomedir := filepath.Join(t.TempDir(), \"home\")\n\t\tassert.Nil(t, os.Mkdir(homedir, 0o777))\n\t\terr := Main(&stdout, &stderr, []string{\"lua\", \"5.2.3\"}, true, false, func() (string, error) {\n\t\t\treturn homedir, nil\n\t\t})\n\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, stdout.String(), \"\")\n\t\tassert.Equal(t, stderr.String(), \"\")\n\n\t\tpath := filepath.Join(homedir, \".tool-versions\")\n\t\tbytes, err := os.ReadFile(path)\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, \"lua 5.2.3\\n\", string(bytes))\n\t})\n\n\tt.Run(\"sets version in current directory only once\", func(t *testing.T) {\n\t\tstdout, stderr := buildOutputs()\n\t\tdir := t.TempDir()\n\t\tassert.Nil(t, os.Chdir(dir))\n\n\t\t_ = Main(&stdout, &stderr, []string{\"lua\", \"5.2.3\"}, false, false, homeFunc)\n\t\terr := Main(&stdout, &stderr, []string{\"lua\", \"5.2.3\"}, false, false, homeFunc)\n\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, stdout.String(), \"\")\n\t\tassert.Equal(t, stderr.String(), \"\")\n\n\t\tpath := filepath.Join(dir, \".tool-versions\")\n\t\tbytes, err := os.ReadFile(path)\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, \"lua 5.2.3\\n\", string(bytes))\n\t})\n}\n\nfunc buildOutputs() (strings.Builder, strings.Builder) {\n\tvar stdout strings.Builder\n\tvar stderr strings.Builder\n\n\treturn stdout, stderr\n}\n"
  },
  {
    "path": "internal/completions/asdf.bash",
    "content": "_asdf_list_shims() (\n  # this function runs in a subshell so shopt is scoped\n  shopt -s nullglob # globs that don't match should disappear\n  shopt -u failglob # globs that don't match shouldn't fail\n  for shim in \"${ASDF_DATA_DIR:-$HOME/.asdf}\"/shims/*; do\n    basename \"$shim\"\n  done\n)\n\n_asdf() {\n  local cur\n  cur=${COMP_WORDS[COMP_CWORD]}\n  local cmd\n  cmd=${COMP_WORDS[1]}\n  cmd2=${COMP_WORDS[2]}\n  cmd3=${COMP_WORDS[3]}\n  local prev\n  prev=${COMP_WORDS[COMP_CWORD - 1]}\n  local plugins\n  plugins=$(asdf plugin list 2>/dev/null | tr '\\n' ' ')\n\n  # We can safely ignore warning SC2207 since it warns that it will uses the\n  # shell's sloppy word splitting and globbing. The possible commands here are\n  # all single words, and most likely won't contain special chars the shell will\n  # expand.\n  COMPREPLY=()\n\n  case \"$cmd\" in\n  plugin)\n    case \"$cmd2\" in\n    update)\n      # shellcheck disable=SC2207\n      COMPREPLY=($(compgen -W \"$plugins --all\" -- \"$cur\"))\n      ;;\n    remove)\n      # shellcheck disable=SC2207\n      COMPREPLY=($(compgen -W \"$plugins\" -- \"$cur\"))\n      ;;\n    add)\n      local available_plugins\n      available_plugins=$(asdf plugin list all 2>/dev/null | awk '{ if ($2 !~ /^\\*/) print $1}')\n      # shellcheck disable=SC2207\n      COMPREPLY=($(compgen -W \"$available_plugins\" -- \"$cur\"))\n      ;;\n    list)\n      case \"$cmd3\" in\n      all) ;;\n      *)\n        local cmds='all --urls --refs'\n        # shellcheck disable=SC2207\n        COMPREPLY=($(compgen -W \"$cmds\" -- \"$cur\"))\n        ;;\n      esac\n      ;;\n    *)\n      local cmds='add list remove update'\n      # shellcheck disable=SC2207\n      COMPREPLY=($(compgen -W \"$cmds\" -- \"$cur\"))\n      ;;\n    esac\n    ;;\n  list)\n    if [[ \" $plugins \" == *\" $prev \"* ]]; then\n      local versions\n      versions=$(asdf list all \"$prev\" 2>/dev/null)\n      # shellcheck disable=SC2207\n      COMPREPLY=($(compgen -W \"$versions\" -- \"$cur\"))\n    else\n      case \"$cmd2\" in\n      all)\n        # shellcheck disable=SC2207\n        COMPREPLY=($(compgen -W \"$plugins\" -- \"$cur\"))\n        ;;\n      *)\n        # shellcheck disable=SC2207\n        COMPREPLY=($(compgen -W \"$plugins all\" -- \"$cur\"))\n        ;;\n      esac\n    fi\n    ;;\n  install | help)\n    if [[ \" $plugins \" == *\" $prev \"* ]]; then\n      local versions\n      versions=$(asdf list all \"$prev\" 2>/dev/null)\n      # shellcheck disable=SC2207\n      COMPREPLY=($(compgen -W \"$versions\" -- \"$cur\"))\n    else\n      # shellcheck disable=SC2207\n      COMPREPLY=($(compgen -W \"$plugins\" -- \"$cur\"))\n    fi\n    ;;\n  uninstall | where | reshim)\n    if [[ \" $plugins \" == *\" $prev \"* ]]; then\n      local versions\n      # The first two columns are either blank or contain the \"current\" marker.\n      versions=$(asdf list \"$prev\" 2>/dev/null | colrm 1 2)\n      # shellcheck disable=SC2207\n      COMPREPLY=($(compgen -W \"$versions\" -- \"$cur\"))\n    else\n      # shellcheck disable=SC2207\n      COMPREPLY=($(compgen -W \"$plugins\" -- \"$cur\"))\n    fi\n    ;;\n  set)\n    if [[ \" $plugins \" == *\" $prev \"* ]]; then\n      local versions\n      # The first two columns are either blank or contain the \"current\" marker.\n      versions=$(asdf list \"$prev\" 2>/dev/null | colrm 1 2)\n      versions+=\" system\"\n      # shellcheck disable=SC2207\n      COMPREPLY=($(compgen -W \"$versions\" -- \"$cur\"))\n    else\n      # shellcheck disable=SC2207\n      COMPREPLY=($(compgen -W \"$plugins -u -p\" -- \"$cur\"))\n    fi\n    ;;\n  latest)\n    if [[ \" $plugins \" == *\" $prev \"* ]]; then\n      local versions\n      versions=$(asdf list all \"$prev\" 2>/dev/null)\n      # shellcheck disable=SC2207\n      COMPREPLY=($(compgen -W \"$versions\" -- \"$cur\"))\n    else\n      # shellcheck disable=SC2207\n      COMPREPLY=($(compgen -W \"$plugins --all\" -- \"$cur\"))\n    fi\n    ;;\n  exec | env | which | shimversions)\n    # shellcheck disable=SC2207\n    COMPREPLY=($(compgen -W \"$(_asdf_list_shims)\" -- \"$cur\"))\n    ;;\n  current)\n    # shellcheck disable=SC2207\n    COMPREPLY=($(compgen -W \"$plugins\" -- \"$cur\"))\n    ;;\n  info | version) ;;\n  *)\n    local cmds='current set help install latest list plugin reshim shimversions uninstall where which exec env info version'\n    # shellcheck disable=SC2207\n    COMPREPLY=($(compgen -W \"$cmds\" -- \"$cur\"))\n    ;;\n  esac\n\n  return 0\n}\n\ncomplete -F _asdf asdf\n"
  },
  {
    "path": "internal/completions/asdf.elvish",
    "content": "# Setup argument completions\nfn arg-completer {|@argz|\n  set argz = $argz[1..-1]  # strip 'asdf' and trailing empty string\n  var num = (count $argz)\n  if (== $num 0) {\n    # list all subcommands\n    find $asdf_dir'/lib/commands' -name 'command-*' | each {|cmd|\n      put (re:replace '.*/command-(.*)\\.bash' '${1}' $cmd)\n    }\n    put 'plugin'\n  } else {\n    if (match $argz 'current') {\n      # asdf current <name>\n      asdf plugin-list\n    } elif (match $argz 'env') {\n      # asdf env <command>\n      ls-shims\n    } elif (match $argz 'env' '.*') {\n      # asdf env <command> [util]\n      ls-executables\n    } elif (match $argz 'exec') {\n      # asdf exec <command>\n      ls-shims\n    } elif (match $argz 'global') {\n      # asdf global <name>\n      asdf plugin-list\n    } elif (match $argz 'global' '.*') {\n      # asdf global <name> <version>\n      ls-installed-versions $argz[-1]\n    } elif (match $argz 'install') {\n      # asdf install <name>\n      asdf plugin-list\n    } elif (match $argz 'install' '.*') {\n      # asdf install <name> <version>\n      ls-all-versions $argz[-1]\n    } elif (match $argz 'install' '.*' '.*') {\n      # asdf install <name> <version> [--keep-download]\n      put '--keep-download'\n    } elif (match $argz 'latest') {\n      # asdf latest <name>\n      asdf plugin-list\n    } elif (match $argz 'latest' '.*') {\n      # asdf latest <name> [<version>]\n      ls-all-versions $argz[-1]\n    } elif (match $argz 'list-all') {\n      # asdf list all <name>\n      asdf plugin-list\n    } elif (match $argz 'list-all' '.*') {\n      # asdf list all <name> [<version>]\n      ls-all-versions $argz[-1]\n    } elif (match $argz 'list') {\n      # asdf list <name>\n      asdf plugin-list\n    } elif (match $argz 'list' '.*') {\n      # asdf list <name> [<version>]\n      ls-installed-versions $argz[-1]\n    } elif (match $argz 'local') {\n      # asdf local <name> [-p|--parent]\n      asdf plugin-list\n      put '-p'\n      put '--parent'\n    } elif (match $argz 'local' '(-p|(--parent))') {\n      # asdf local <name> [-p|--parent] <version>\n      asdf plugin-list\n    } elif (match $argz 'local' '.*') {\n      # asdf local <name> [-p|--parent]\n      # asdf local <name> <version>\n      ls-installed-versions $argz[-1]\n      put '-p'\n      put '--parent'\n    } elif (match $argz 'local' '(-p|(--parent))' '.*') {\n      # asdf local [-p|--parent] <name> <version>\n      ls-installed-versions $argz[-1]\n    } elif (match $argz 'local' '.*' '(-p|(--parent))') {\n      # asdf local <name> [-p|--parent] <version>\n      ls-installed-versions $argz[-2]\n    } elif (match $argz 'local' '.*' '.*') {\n      # asdf local <name> <version> [-p|--parent]\n      put '-p'\n      put '--parent'\n    } elif (or (match $argz 'plugin-add') (match $argz 'plugin' 'add')) {\n      # asdf plugin add <name>\n      asdf plugin-list-all | each {|line|\n        put (re:replace '([^\\s]+)\\s+.*' '${1}' $line)\n      }\n    } elif (or (match $argz 'plugin-list') (match $argz 'plugin' 'list')) {\n      # asdf plugin list\n      put '--urls'\n      put '--refs'\n      put 'all'\n    } elif (or (match $argz 'plugin-push') (match $argz 'plugin' 'push')) {\n      # asdf plugin push <name>\n      asdf plugin-list\n    } elif (or (match $argz 'plugin-remove') (match $argz 'plugin' 'remove')) {\n      # asdf plugin remove <name>\n      asdf plugin-list\n    } elif (and (>= (count $argz) 3) (match $argz[..3] 'plugin-test' '.*' '.*')) {\n      # asdf plugin-test <plugin-name> <plugin-url> [--asdf-tool-version <version>] [--asdf-plugin-gitref <git-ref>] [test-command*]\n      put '--asdf-plugin-gitref'\n      put '--asdf-tool-version'\n      ls-executables\n      ls-shims\n    } elif (and (>= (count $argz) 4) (match $argz[..4] 'plugin' 'test' '.*' '.*')) {\n      # asdf plugin test <plugin-name> <plugin-url> [--asdf-tool-version <version>] [--asdf-plugin-gitref <git-ref>] [test-command*]\n      put '--asdf-plugin-gitref'\n      put '--asdf-tool-version'\n      ls-executables\n      ls-shims\n    } elif (or (match $argz 'plugin-update') (match $argz 'plugin' 'update')) {\n      # asdf plugin update <name>\n      asdf plugin-list\n      put '--all'\n    } elif (match $argz 'plugin') {\n      # list plugin-* subcommands\n      find $asdf_dir'/lib/commands' -name 'command-plugin-*' | each {|cmd|\n        put (re:replace '.*/command-plugin-(.*)\\.bash' '${1}' $cmd)\n      }\n    } elif (match $argz 'reshim') {\n      # asdf reshim <name>\n      asdf plugin-list\n    } elif (match $argz 'reshim' '.*') {\n      # asdf reshim <name> <version>\n      ls-installed-versions $argz[-1]\n    } elif (match $argz 'shim-versions') {\n      # asdf shim-versions <command>\n      ls-shims\n    } elif (match $argz 'uninstall') {\n      # asdf uninstall <name>\n      asdf plugin-list\n    } elif (match $argz 'uninstall' '.*') {\n      # asdf uninstall <name> <version>\n      ls-installed-versions $argz[-1]\n    } elif (match $argz 'update') {\n      if (== $num 1) {\n        # asdf update\n        put '--head'\n      }\n    } elif (match $argz 'where') {\n      # asdf where <name>\n      asdf plugin-list\n    } elif (match $argz 'where' '.*') {\n      # asdf where <name> [<version>]\n      ls-installed-versions $argz[-1]\n    } elif (match $argz 'which') {\n      ls-shims\n    }\n  }\n}\n"
  },
  {
    "path": "internal/completions/asdf.fish",
    "content": "set -x asdf_data_dir (\n  if test -n \"$ASDF_DATA_DIR\"; echo $ASDF_DATA_DIR;\n  else; echo $HOME/.asdf; end)\n\nfunction __fish_asdf_needs_command\n    set -l cmd (commandline -opc)\n    if test (count $cmd) -eq 1\n        return 0\n    end\n    return 1\nend\n\nfunction __fish_asdf_using_command -a current_command\n    set -l cmd (commandline -opc)\n    if test (count $cmd) -gt 1\n        if test $current_command = $cmd[2]\n            return 0\n        end\n    end\n    return 1\nend\n\nfunction __fish_asdf_arg_number -a number\n    set -l cmd (commandline -opc)\n    test (count $cmd) -eq $number\nend\n\nfunction __fish_asdf_arg_at -a number\n    set -l cmd (commandline -opc)\n    echo $cmd[$number]\nend\n\nfunction __fish_asdf_list_versions -a plugin\n    asdf list $plugin 2>/dev/null | string trim | string trim --left --chars '*'\nend\n\nfunction __fish_asdf_list_all -a plugin\n    asdf list all $plugin 2>/dev/null\nend\n\nfunction __fish_asdf_plugin_list\n    asdf plugin list 2>/dev/null\nend\n\nfunction __fish_asdf_plugin_list_all\n    asdf plugin list all 2>/dev/null\nend\n\nfunction __fish_asdf_list_shims\n    path basename $asdf_data_dir/shims/*\nend\n\n# plugin completion\ncomplete -f -c asdf -n __fish_asdf_needs_command -a plugin -d \"Plugin management sub-commands\"\n# suggest `add` after `plugin`\ncomplete -f -c asdf -n '__fish_asdf_using_command plugin; and __fish_asdf_arg_number 2' -a add -d \"Add git repo as plugin\"\n# show available plugins after `plugin add`\ncomplete -f -c asdf -n '__fish_asdf_using_command plugin; and __fish_asdf_arg_at 2 = \"add\"; and __fish_asdf_arg_number 3' -a '(__fish_asdf_plugin_list_all | grep -v \\'*\\' | awk \\'{ print $1 }\\')'\n# show repository urls for selected plugin\ncomplete -f -c asdf -n '__fish_asdf_using_command plugin; and __fish_asdf_arg_at 2 = \"add\"; and __fish_asdf_arg_number 4' -a '(__fish_asdf_plugin_list_all | grep (__fish_asdf_arg_at 3) | awk \\'{ print $2 }\\')'\n\n# plugin list completion\ncomplete -f -c asdf -n '__fish_asdf_using_command plugin; and __fish_asdf_arg_number 2' -a list -d \"List installed plugins\"\ncomplete -f -c asdf -n '__fish_asdf_using_command plugin; and __fish_asdf_arg_at 2 = \"list\"; and __fish_asdf_arg_number 3' -a all -d \"List all available plugins\"\n\n# plugin remove completion\n# show `remove` as an option for `plugin`\ncomplete -f -c asdf -n '__fish_asdf_using_command plugin; and __fish_asdf_arg_number 2' -a remove -d \"Remove plugin and package versions\"\n# Show list of plugins after `remove`\ncomplete -f -c asdf -n '__fish_asdf_using_command plugin; and __fish_asdf_arg_at 2 = \"remove\"; and __fish_asdf_arg_number 3' -a '(__fish_asdf_plugin_list)'\n\n# plugin update completion\ncomplete -f -c asdf -n '__fish_asdf_using_command plugin; and __fish_asdf_arg_number 2' -a update -d \"Update plugin\"\n# suggest the plugin list\ncomplete -f -c asdf -n '__fish_asdf_using_command plugin; and __fish_asdf_arg_at 2 = \"update\"; and __fish_asdf_arg_number 3' -a '(__fish_asdf_plugin_list)'\n# suggest '--all'\ncomplete -f -c asdf -n '__fish_asdf_using_command plugin; and __fish_asdf_arg_at 2 = \"update\"; and __fish_asdf_arg_number 3' -a --all\n\n# install completion\ncomplete -f -c asdf -n __fish_asdf_needs_command -a install -d \"Install a specific version of a package\"\ncomplete -f -c asdf -n '__fish_asdf_using_command install; and __fish_asdf_arg_number 2' -a '(__fish_asdf_plugin_list)'\ncomplete -f -c asdf -n '__fish_asdf_using_command install; and __fish_asdf_arg_number 3' -a '(__fish_asdf_list_all (__fish_asdf_arg_at 3))'\n\n# uninstall completion\ncomplete -f -c asdf -n __fish_asdf_needs_command -a uninstall -d \"Remove a specific version of a package\"\ncomplete -f -c asdf -n '__fish_asdf_using_command uninstall; and __fish_asdf_arg_number 2' -a '(__fish_asdf_plugin_list)'\ncomplete -f -c asdf -n '__fish_asdf_using_command uninstall; and __fish_asdf_arg_number 3' -a '(__fish_asdf_list_versions (__fish_asdf_arg_at 3))'\n\n# current completion\ncomplete -f -c asdf -n __fish_asdf_needs_command -a current -d \"Display version set or being used for package\"\ncomplete -f -c asdf -n '__fish_asdf_using_command current; and __fish_asdf_arg_number 2' -a '(__fish_asdf_plugin_list)'\n\n# where completion\ncomplete -f -c asdf -n __fish_asdf_needs_command -a where -d \"Display install path for an installed version\"\ncomplete -f -c asdf -n '__fish_asdf_using_command where; and __fish_asdf_arg_number 2' -a '(__fish_asdf_plugin_list)'\ncomplete -f -c asdf -n '__fish_asdf_using_command where; and __fish_asdf_arg_number 3' -a '(__fish_asdf_list_versions (__fish_asdf_arg_at 3))'\n\n# which completion\ncomplete -f -c asdf -n __fish_asdf_needs_command -a which -d \"Display executable path for a command\"\ncomplete -f -c asdf -n '__fish_asdf_using_command which; and __fish_asdf_arg_number 2' -a '(__fish_asdf_list_shims)'\n\n# latest completion\ncomplete -f -c asdf -n __fish_asdf_needs_command -a latest -d \"Show latest stable version of a package\"\ncomplete -f -c asdf -n '__fish_asdf_using_command latest; and __fish_asdf_arg_number 2' -a '(__fish_asdf_plugin_list)'\ncomplete -f -c asdf -n '__fish_asdf_using_command latest; and __fish_asdf_arg_number 2' -a --all\n\n# list completion\ncomplete -f -c asdf -n __fish_asdf_needs_command -a list -d \"List installed versions of a package\"\ncomplete -f -c asdf -n '__fish_asdf_using_command list; and __fish_asdf_arg_number 2' -a '(__fish_asdf_plugin_list)'\n\n# list-all completion\ncomplete -f -c asdf -n '__fish_asdf_using_command list; and __fish_asdf_arg_number 2' -a all -d \"List all versions of a package\"\ncomplete -f -c asdf -n '__fish_asdf_using_command list; and __fish_asdf_arg_at 2 = \"all\"; and __fish_asdf_arg_number 3' -a '(__fish_asdf_plugin_list)'\n\n# reshim completion\ncomplete -f -c asdf -n __fish_asdf_needs_command -a reshim -d \"Recreate shims for version of a package\"\ncomplete -f -c asdf -n '__fish_asdf_using_command reshim; and __fish_asdf_arg_number 2' -a '(__fish_asdf_plugin_list)'\ncomplete -f -c asdf -n '__fish_asdf_using_command reshim; and __fish_asdf_arg_number 3' -a '(__fish_asdf_list_versions (__fish_asdf_arg_at 3))'\n\n# shim versions completion\ncomplete -f -c asdf -n __fish_asdf_needs_command -a shimversions -d \"List the plugins and versions that provide a command\"\ncomplete -f -c asdf -n '__fish_asdf_using_command shimversions; and __fish_asdf_arg_number 2' -a '(__fish_asdf_list_shims)'\n\n# set completion\ncomplete -f -c asdf -n __fish_asdf_needs_command -a set -d \"Set version for a plugin\"\ncomplete -f -c asdf -n '__fish_asdf_using_command set; and __fish_asdf_arg_number 2' -a '(__fish_asdf_plugin_list)'\ncomplete -f -c asdf -n '__fish_asdf_using_command set; and test (count (commandline -opc)) -gt 2' -a '(__fish_asdf_list_versions (__fish_asdf_arg_at 3)) system'\n\n# set commands\ncomplete -f -c asdf -n '__fish_asdf_using_command set' -l home -d \"Set version in home directory\"\ncomplete -f -c asdf -n '__fish_asdf_using_command set' -l parent -d \"Set version in parent directory\"\n\n# misc\ncomplete -f -c asdf -n __fish_asdf_needs_command -l help -d \"Displays help\"\ncomplete -f -c asdf -n __fish_asdf_needs_command -a info -d \"Print OS, Shell and ASDF debug information\"\ncomplete -f -c asdf -n __fish_asdf_needs_command -l version -d \"Print the currently installed version of ASDF\"\n"
  },
  {
    "path": "internal/completions/asdf.nushell",
    "content": "module asdf {\n\n    def \"complete asdf sub-commands\" [] {\n        [\n            \"plugin\",\n            \"list\",\n            \"install\",\n            \"uninstall\",\n            \"current\",\n            \"where\",\n            \"which\",\n            \"local\",\n            \"global\",\n            \"shell\",\n            \"latest\",\n            \"help\",\n            \"exec\",\n            \"env\",\n            \"info\",\n            \"version\",\n            \"reshim\",\n            \"shim-version\",\n            \"update\"\n        ]\n    }\n\n    def \"complete asdf installed\" [] {\n        ^asdf plugin list | lines | each { |line| $line | str trim }\n    }\n\n\n    def \"complete asdf plugin sub-commands\" [] {\n        [\n            \"list\",\n            \"list all\",\n            \"add\",\n            \"remove\",\n            \"update\"\n        ]\n    }\n\n    def \"complete asdf installed plugins\" [] {\n        ^asdf plugin list | lines | each { |line|\n            $line | str trim\n        }\n    }\n\n    def \"complete asdf plugin versions all\" [context: string] {\n        let plugin = $context | str trim | split words | last\n        ^asdf list all $plugin\n        | lines\n        | each { |line| $line | str trim }\n        | prepend \"latest\"\n    }\n\n    def \"complete asdf plugin versions installed\" [context: string] {\n        let plugin = $context | str trim | split words | last\n        let versions = ^asdf list $plugin\n        | lines\n        | each { |line| $line | str trim }\n        | each { |version| if ($version | str starts-with \"*\") {{value: ($version | str substring 1..), description: \"current version\"}} else {{value: $version, description: \"\"}} }\n\n        let latest = ^asdf latest $plugin | str trim\n\n        if ($versions | get value | any {|el| $el == $latest}) {\n            $versions | prepend {value: \"latest\", description: $\"alias to ($latest)\"}\n        } else {\n            $versions\n        }\n    }\n\n    # ASDF version manager\n    export extern main [\n        subcommand?: string@\"complete asdf sub-commands\"\n    ]\n\n    # Manage plugins\n    export extern \"asdf plugin\" [\n        subcommand?: string@\"complete asdf plugin sub-commands\"\n    ]\n\n    # List installed plugins\n    export def \"asdf plugin list\" [\n        --urls # Show urls\n        --refs # Show refs\n    ] {\n\n        let params = [\n            {name: 'urls', enabled: $urls, flag: '--urls',\n             template: '\\s+?(?P<repository>(?:http[s]?|git).+\\.git|/.+)'}\n            {name: 'refs', enabled: $refs, flag: '--refs',\n             template: '\\s+?(?P<branch>\\w+)\\s+(?P<ref>\\w+)'}\n        ]\n\n        let template = '(?P<name>.+)' + (\n                            $params |\n                            where enabled |\n                            get --ignore-errors template |\n                            str join '' |\n                            str trim\n                        )\n\n        let flags = ($params | where enabled | get --ignore-errors flag | default '' )\n\n        ^asdf plugin list ...$flags | lines | parse -r $template | str trim\n    }\n\n    # list all available plugins\n    export def \"asdf plugin list all\" [] {\n        let template = '(?P<name>.+)\\s+?(?P<installed>[*]?)(?P<repository>(?:git|http|https).+)'\n        let is_installed = { |it| $it.installed == '*' }\n\n        ^asdf plugin list all |\n            lines |\n            parse -r $template |\n            str trim |\n            update installed $is_installed |\n            sort-by name\n    }\n\n    # Add a plugin\n    export extern  \"asdf plugin add\" [\n        name: string # Name of the plugin\n        git_url?: string # Git url of the plugin\n    ]\n\n    # Remove an installed plugin and their package versions\n    export extern \"asdf plugin remove\" [\n        name: string@\"complete asdf installed plugins\" # Name of the plugin\n    ]\n\n    # Update a plugin\n    export extern \"asdf plugin update\" [\n        name: string@\"complete asdf installed plugins\" # Name of the plugin\n        git_ref?: string # Git ref to update the plugin\n    ]\n\n    # Update all plugins to the latest commit\n    export extern \"asdf plugin update --all\" []\n\n    # install a package version\n    export extern \"asdf install\" [\n        name?: string@\"complete asdf installed plugins\" # Name of the package\n        version?: string@\"complete asdf plugin versions all\" # Version of the package or latest\n    ]\n\n\n    # Remove an installed package version\n    export extern \"asdf uninstall\" [\n        name: string@\"complete asdf installed\" # Name of the package\n        version: string@\"complete asdf plugin versions installed\" # Version of the package\n    ]\n\n    # Display current version\n    export extern \"asdf current\" [\n        name?: string@\"complete asdf installed\" # Name of installed version of a package\n    ]\n\n    # Display path of an executable\n    export extern \"asdf which\" [\n        command: string # Name of command\n    ]\n\n    # Display install path for an installed package version\n    export extern \"asdf where\" [\n        name: string@\"complete asdf installed\" # Name of installed package\n        version?: string@\"complete asdf plugin versions installed\" # Version of installed package\n    ]\n\n    # Set the package local version\n    export extern \"asdf local\" [\n        name: string@\"complete asdf installed\" # Name of the package\n        version?: string@\"complete asdf plugin versions installed\" # Version of the package or latest\n    ]\n\n    # Set the package global version\n    export extern \"asdf global\" [\n        name: string@\"complete asdf installed\" # Name of the package\n        version?: string@\"complete asdf plugin versions installed\" # Version of the package or latest\n    ]\n\n    # Set the package to version in the current shell\n    export extern \"asdf shell\" [\n        name: string@\"complete asdf installed\" # Name of the package\n        version?: string@\"complete asdf plugin versions installed\" # Version of the package or latest\n    ]\n\n    # Show latest stable version of a package\n    export extern \"asdf latest\" [\n        name: string@\"complete asdf installed\" # Name of the package\n        version?: string@\"complete asdf plugin versions installed\" # Filter latest stable version from this version\n    ]\n\n    # Show latest stable version for all installed packages\n    export extern \"asdf latest --all\" []\n\n    # List installed package versions\n    export extern \"asdf list\" [\n        name?: string@\"complete asdf installed\" # Name of the package\n        version?: string@\"complete asdf plugin versions installed\" # Filter the version\n    ]\n\n    # List all available package versions\n    export def \"asdf list all\" [\n        name: string@\"complete asdf installed\" # Name of the package\n        version?: string@\"complete asdf plugin versions installed\"=\"\" # Filter the version\n    ]    {\n        ^asdf list all $name $version | lines | parse \"{version}\" | str trim\n    }\n\n    # Show documentation for plugin\n    export extern \"asdf help\" [\n        name: string@\"complete asdf installed\" # Name of the plugin\n        version?: string@\"complete asdf plugin versions installed\" # Version of the plugin\n    ]\n\n    # Execute a command shim for the current version\n    export extern \"asdf exec\" [\n        command: string # Name of the command\n        ...args: any # Arguments to pass to the command\n    ]\n\n    # Run util (default: env) inside the environment used for command shim execution\n    export extern \"asdf env\" [\n        command?: string # Name of the command\n        util?: string = 'env' # Name of util to run\n    ]\n\n    # Show information about OS, Shell and asdf Debug\n    export extern \"asdf info\" []\n\n    # Print the currently installed version of ASDF\n    export extern \"asdf version\" []\n\n    # Recreate shims for version package\n    export extern \"asdf reshim\" [\n        name?: string@\"complete asdf installed\" # Name of the package\n        version?: string@\"complete asdf plugin versions installed\" # Version of the package\n    ]\n\n    # List the plugins and versions that provide a command\n    export extern \"asdf shim-version\" [\n        command: string # Name of the command\n    ]\n\n    # Update asdf to the latest version on the stable branch\n    export extern \"asdf update\" []\n\n    # Update asdf to the latest version on the main branch\n    export extern \"asdf update --head\" []\n\n}\n\nuse asdf *\n"
  },
  {
    "path": "internal/completions/asdf.zsh",
    "content": "#compdef asdf\n#description tool to manage versions of multiple runtimes\n\n# Initialize local variables for ZSH completion context\nlocal state subcmd\n\n# Set asdf directory path, using ASDF_DATA_DIR if set, otherwise default to ~/.asdf\nlocal asdf_dir=\"${ASDF_DATA_DIR:-$HOME/.asdf}\"\n\n# Define plugin management commands\nlocal -a asdf_plugin_commands\nasdf_plugin_commands=(\n  'add:add plugin from asdf-plugins repo or from git URL'\n  'list:list installed plugins (--urls with URLs)'\n  'remove:remove named plugin and all packages for it'\n  'update:update named plugin (or --all)'\n)\n\n# Define main asdf commands array with descriptions\nlocal -a asdf_commands\nasdf_commands=( # 'asdf help' lists commands with help text\n  # Plugin related commands\n  'plugin:plugin management sub-commands'\n\n  # tools\n  'help:Output documentation for plugin and tool'\n  'install:install tool at stated version, or all from .tools-versions'\n  'uninstall:remove a specific version of a tool'\n  'current:display current versions for named tool (else all)'\n  'latest:display latest version available to install for a named tool'\n  'where:display install path for given tool at optional specified version'\n  'which:display path to an executable'\n  'set:Set a tool version in a .tool-version file'\n  'list:list installed versions of a tool'\n\n  # Utility commands\n  'exec:executes the command shim for the current version'\n  'env:prints or runs an executable under a command environment'\n  'info:print os, shell and asdf debug information'\n  'version:print the currently installed version of ASDF'\n  'reshim:recreate shims for version of a tool'\n  'shim:shim management sub-commands'\n  'shimversions:list for given command which plugins and versions provide it'\n)\n\n# Function to list all available plugins from the repository\n_asdf__available_plugins() {\n  local plugin_dir=\"${asdf_dir:?}/repository/plugins\"\n  if [[ ! -d \"$plugin_dir\" ]]; then\n    _wanted asdf-available-plugins expl 'ASDF Installable Plugins' \\\n      compadd -x \"no plugins repository found\"\n    return\n  fi\n  local -a plugins\n  plugins=( \"$plugin_dir\"/*(:t) )\n  _wanted asdf-available-plugins expl 'ASDF Installable Plugins' \\\n    compadd -a plugins\n}\n\n# Function to list currently installed plugins\n_asdf__installed_plugins() {\n  local plugin_dir=\"${asdf_dir:?}/plugins\"\n  if [[ ! -d \"$plugin_dir\" ]]; then\n    _wanted asdf-plugins expl 'ASDF Plugins' \\\n      compadd -x \"no plugins dir, none installed yet\"\n    return\n  fi\n  local -a plugins\n  plugins=( \"$plugin_dir\"/*(:t) )\n  _wanted asdf-plugins expl 'ASDF Plugins' \\\n    compadd -a plugins\n}\n\n# Function to list installed versions for a specific plugin\n_asdf__installed_versions_of() {\n  local plugin_dir=\"${asdf_dir:?}/installs/${1:?need a plugin version}\"\n  if [[ ! -d \"$plugin_dir\" ]]; then\n    _wanted \"asdf-versions-$1\" expl \"ASDF Plugin ${(q-)1} versions\" \\\n      compadd -x \"no versions installed\"\n    return\n  fi\n  local -a versions\n  versions=( \"$plugin_dir\"/*(:t) )\n  _wanted \"asdf-versions-$1\" expl \"ASDF Plugin ${(q-)1} versions\" \\\n    compadd -a versions\n}\n\n# Similar to _asdf__installed_versions_of but includes 'system' as an option\n_asdf__installed_versions_of_plus_system() {\n  local plugin_dir=\"${asdf_dir:?}/installs/${1:?need a plugin version}\"\n  if [[ ! -d \"$plugin_dir\" ]]; then\n    _wanted \"asdf-versions-$1\" expl \"ASDF Plugin ${(q-)1} versions\" \\\n      compadd -x \"no versions installed\"\n    return\n  fi\n  local -a versions\n  versions=( \"$plugin_dir\"/*(:t) )\n  versions+=\"system\"\n  _wanted \"asdf-versions-$1\" expl \"ASDF Plugin ${(q-)1} versions\" \\\n    compadd -a versions\n}\n\n# Function to get available git references for a plugin\n_asdf__plugin_git_refs() {\n  local plugin=$1\n  local data_dir=${ASDF_DATA_DIR:-$HOME/.asdf}\n  local plugin_path=\"$data_dir/plugins/$plugin\"\n\n  if [[ -d \"$plugin_path/.git\" ]]; then\n    # Get remote branches and format them\n    git -C \"$plugin_path\" branch -r 2> /dev/null | \\\n      sed \\\n        -e 's/^[[:space:]]*[^\\/]*\\///' \\\n        -e 's/[[:space:]]*->.*$//' \\\n        -e 's/\\(.*\\)/\\1:Remote branch \\1/' | \\\n      sort -fd\n    # Get tags and format them\n    git -C \"$plugin_path\" tag 2> /dev/null | \\\n      sed -e 's/\\(.*\\)/\\1:Tag \\1/' | \\\n      sort -V\n    # Get recent commit hashes and messages (last 10 commits)\n    git -C \"$plugin_path\" log --pretty=format:'%h:%s' -n 10 2> /dev/null\n  fi\n}\n\n# Handle top-level command completion first\nif (( CURRENT == 2 )); then\n  _arguments -C : '--version[version]' ':command:->command'\nfi\n\n# Process command state for top-level commands\ncase \"$state\" in\n(command)\n  _describe -t asdf-commands 'ASDF Commands' asdf_commands\n  return\n  ;;\nesac\n\n# Get the subcommand for further processing\nsubcmd=\"${words[2]}\"\n\n# Complex completion logic for each subcommand\n# Each case handles specific completion scenarios for the respective command\ncase \"$subcmd\" in\n  (plugin)\n    # Handle plugin subcommand completions with nested subcommands\n    if (( CURRENT == 3 )); then\n      _describe -t asdf-plugin-commands 'ASDF Plugin Commands' asdf_plugin_commands\n    else\n      local plugin_subcmd=\"${words[3]}\"\n      case \"$plugin_subcmd\" in\n      (add)\n        # Complete available plugins or URLs for add command\n        if (( CURRENT == 4 )); then\n          _asdf__available_plugins\n        elif (( CURRENT == 5 )); then\n            _arguments \"*:${words[4]} plugin url:_urls\"\n        fi\n        return\n        ;;\n      (update)\n        # Handle update command with support for --all flag and git refs\n        if (( CURRENT == 4 )); then\n          _alternative \\\n            'flags:flags:((--all\\:\"Update all installed plugins\"))' \\\n            'asdf-available-plugins:Installed ASDF Plugins:_asdf__installed_plugins'\n        elif (( CURRENT == 5 )); then\n          if [[ ${words[4]} != \"--all\" ]]; then\n            local -a refs\n            while IFS=: read -r value descr; do\n              refs+=( \"${value}:${descr}\" )\n            done < <(_asdf__plugin_git_refs ${words[4]})\n            _describe -V -t git-refs 'Git References' refs\n          fi\n        fi\n        ;;\n      (remove)\n        # Complete installed plugin names for remove command\n        _asdf__installed_plugins\n        return\n        ;;\n      (list)\n        # Handle list command options with support for --urls and --refs flags\n        case $CURRENT in\n          4)\n            _alternative \\\n              'flags:flags:((--urls\\:\"Show repository URLs\" --refs\\:\"Show Git references\"))' \\\n              'commands:commands:((all\\:\"List all available plugins\"))'\n            return\n            ;;\n          5)\n            # Handle remaining available flags\n            if [[ ${words[4]} == --* ]]; then\n              local used_flags=(\"${words[@]}\")\n              local -a available_flags\n              available_flags=()\n              if [[ ! \"${used_flags[@]}\" =~ \"--urls\" ]]; then\n                available_flags+=(\"--urls\")\n              fi\n              if [[ ! \"${used_flags[@]}\" =~ \"--refs\" ]]; then\n                available_flags+=(\"--refs\")\n              fi\n              (( ${#available_flags[@]} )) && compadd -- \"${available_flags[@]}\"\n            fi\n            return\n            ;;\n        esac\n        ;;\n      esac\n    fi\n    ;;\n  (current)\n    # Complete with installed plugins for current command\n    _asdf__installed_plugins\n    ;;\n  (list)\n    # Handle list command completions with support for 'all' and specific plugins\n    case $CURRENT in\n      3)\n        _alternative \\\n          'commands:commands:((all\\:\"List all available (remote) versions\"))' \\\n          'plugin:plugin:_asdf__installed_plugins'\n        ;;\n      4)\n        if [[ ${words[3]} == \"all\" ]]; then\n          _asdf__installed_plugins\n        else\n          # For normal list: show installed versions with optional filter\n          _asdf__installed_versions_of ${words[3]}\n        fi\n        ;;\n      5)\n        # When listing all versions of a specific plugin\n        if [[ ${words[3]} == \"all\" ]]; then\n          local versions\n          if versions=$(asdf list all \"${words[4]}\" 2>/dev/null); then\n            _wanted \"remote-versions-${words[4]}\" \\\n              expl \"Available versions of ${words[4]}\" \\\n              compadd -- ${(f)versions}\n          else\n            _message \"Unable to fetch versions for ${words[4]}\"\n          fi\n        fi\n        ;;\n    esac\n    ;;\n  (help)\n    # Complete installed plugins and their versions for help command\n    if (( CURRENT == 3 )); then\n      _asdf__installed_plugins\n    elif (( CURRENT == 4 )); then\n      _asdf__installed_versions_of ${words[3]}\n    fi\n    ;;\n  (install)\n    # Handle complex install command completion with latest tag support\n    if (( CURRENT == 3 )); then\n      _asdf__installed_plugins\n      return\n    elif (( CURRENT == 4 )); then\n      local tool=\"${words[3]}\"\n      local ver_prefix=\"${words[4]}\"\n      if [[ $ver_prefix == latest:* ]]; then\n        # Handle latest:<version> syntax\n        _wanted \"latest-versions-$tool\" \\\n          expl \"Latest version\" \\\n          compadd -- latest:${^$(asdf list all \"$tool\")}\n      else\n        # Offer both latest options and specific versions\n        _wanted \"latest-tag-$tool\" \\\n          expl \"Latest version\" \\\n          compadd -- 'latest' 'latest:'\n        _wanted \"remote-versions-$tool\" \\\n          expl \"Available versions of $tool\" \\\n          compadd -- $(asdf list all \"$tool\")\n      fi\n      return\n    fi\n    ;;\n  (latest)\n    # Complete plugin names or --all flag for latest command\n    if (( CURRENT == 3 )); then\n      _alternative  \\\n        'flags:flags:((--all\\:\"Show latest version of all tools\"))' \\\n        'asdf-available-plugins:Installed ASDF Plugins:_asdf__installed_plugins'\n    fi\n    ;;\n  (uninstall|reshim|where)\n    # Handle complex install command completion with latest tag support\n    if (( CURRENT == 3 )); then\n      _asdf__installed_plugins\n      return\n    elif (( CURRENT == 4 )); then\n      # For normal list: show installed versions with optional filter\n      _asdf__installed_versions_of ${words[3]}\n      return\n    fi\n    ;;\n  (set)\n    # Handle set command completion\n    case $CURRENT in\n      3)\n        _alternative \\\n          'flags:flags:((-u\\:\"set version in user home directory\" -p\\:\"set version in closest parent .tool-versions\"))' \\\n          'plugin:plugin:_asdf__installed_plugins'\n        ;;\n      4)\n        if [[ ${words[3]} == -* ]]; then\n          # After flag, complete with plugin name\n          _asdf__installed_plugins\n        else\n          # Complete with installed versions for the plugin\n          _asdf__installed_versions_of_plus_system ${words[3]}\n        fi\n        ;;\n      *)\n        # Support for multiple version specifications\n        if [[ ${words[3]} == -* ]]; then\n          local plugin=\"${words[4]}\"\n        else\n          local plugin=\"${words[3]}\"\n        fi\n        _asdf__installed_versions_of_plus_system $plugin\n        ;;\n    esac\n    ;;\n  (which|shimversions)\n    # Complete with available shims for which and shimversions commands\n    _wanted asdf-shims expl \"ASDF Shims\" compadd -- \"${asdf_dir:?}/shims\"/*(:t)\n    ;;\n  (exec)\n    # Handle exec command completion with shim command and args\n    if (( CURRENT == 3 )); then\n      _wanted asdf-shims expl \"ASDF Shims\" compadd -- \"${asdf_dir:?}/shims\"/*(:t)\n    else\n      compset -n 3\n      _normal -p \"asdf-shims-${words[3]}\"\n    fi\n    ;;\n  (env)\n    # Handle env command completion with shim name and arbitrary command\n    if (( CURRENT == 3 )); then\n      _wanted asdf-shims expl \"ASDF Shims\" compadd -- \"${asdf_dir:?}/shims\"/*(:t)\n    else\n      compset -n 4\n      _normal -p \"asdf-shims-${words[3]}\"\n    fi\n    ;;\nesac\n"
  },
  {
    "path": "internal/completions/completions.go",
    "content": "// Package completions handles shell completion files.\n//\n// To add completion support for a shell, simply add a file named\n// \"asdf.<shell>\" to this directory, replacing \"<shell>\" with the name\n// of the shell.\npackage completions\n\nimport (\n\t\"embed\"\n\t\"errors\"\n\t\"io/fs\"\n\t\"slices\"\n\t\"strings\"\n)\n\n//go:embed asdf.*\nvar completions embed.FS\n\n// Get returns a file containing completion code for the given shell if it is\n// found.\nfunc Get(name string) (fs.File, bool) {\n\tfile, err := completions.Open(\"asdf.\" + name)\n\tif err != nil {\n\t\tif errors.Is(err, fs.ErrNotExist) {\n\t\t\treturn nil, false\n\t\t}\n\t\tpanic(err) // This should never happen.\n\t}\n\treturn file, true\n}\n\n// Names returns a slice of shell names that completion is available for.\nfunc Names() []string {\n\tfiles, _ := fs.Glob(completions, \"asdf.*\")\n\tfor i, file := range files {\n\t\tfiles[i] = strings.TrimPrefix(file, \"asdf.\")\n\t}\n\tslices.Sort(files)\n\treturn files\n}\n"
  },
  {
    "path": "internal/completions/completions_test.go",
    "content": "package completions\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestGet(t *testing.T) {\n\tt.Run(\"returns file when completion file found with matching name\", func(t *testing.T) {\n\t\tfile, found := Get(\"bash\")\n\n\t\tinfo, err := file.Stat()\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, \"asdf.bash\", info.Name())\n\n\t\tassert.True(t, found)\n\t})\n\n\tt.Run(\"returns false when completion file not found\", func(t *testing.T) {\n\t\t_, found := Get(\"non-existent\")\n\t\tassert.False(t, found)\n\t})\n}\n\nfunc TestNames(t *testing.T) {\n\tt.Run(\"returns slice of shell names for which completion is available\", func(t *testing.T) {\n\t\tassert.Equal(t, []string{\"bash\", \"elvish\", \"fish\", \"nushell\", \"zsh\"}, Names())\n\t})\n}\n"
  },
  {
    "path": "internal/config/config.go",
    "content": "// Package config provides a unified API for fetching asdf config. Either from\n// the asdfrc file or environment variables.\npackage config\n\nimport (\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"gopkg.in/ini.v1\"\n)\n\nconst (\n\tdataDirDefault                     = \"~/.asdf\"\n\tconfigFileDefault                  = \"~/.asdfrc\"\n\tdefaultToolVersionsFilenameDefault = \".tool-versions\"\n\tdefaultPluginIndexURL              = \"https://github.com/asdf-vm/asdf-plugins.git\"\n)\n\n/* PluginRepoCheckDuration represents the remote plugin repo check duration\n* (never or every N seconds). It's not clear to me how this should be\n* represented in Golang so using a struct for maximum flexibility. */\ntype PluginRepoCheckDuration struct {\n\tNever bool\n\tEvery int\n}\n\nvar pluginRepoCheckDurationDefault = PluginRepoCheckDuration{Every: 60}\n\n// Config is the primary value this package builds and returns\ntype Config struct {\n\tHome                        string\n\tConfigFile                  string\n\tDefaultToolVersionsFilename string\n\tDataDir                     string\n\tSettings                    Settings\n\tPluginIndexURL              string\n}\n\n// Settings is a struct that stores config values from the asdfrc file\ntype Settings struct {\n\tLoaded            bool\n\tRaw               *ini.Section\n\tLegacyVersionFile bool\n\t// I don't think this setting should be supported in the Golang implementation\n\t// UseReleaseCandidates bool\n\tAlwaysKeepDownload                bool\n\tPluginRepositoryLastCheckDuration PluginRepoCheckDuration\n\tDisablePluginShortNameRepository  bool\n\tConcurrency                       string\n}\n\nfunc defaultConfig(dataDir, configFile string) *Config {\n\treturn &Config{\n\t\tDataDir:                     dataDir,\n\t\tConfigFile:                  configFile,\n\t\tDefaultToolVersionsFilename: defaultToolVersionsFilenameDefault,\n\t\tPluginIndexURL:              defaultPluginIndexURL,\n\t}\n}\n\nfunc defaultSettings() *Settings {\n\treturn &Settings{\n\t\tLoaded:                            false,\n\t\tRaw:                               nil,\n\t\tLegacyVersionFile:                 false,\n\t\tAlwaysKeepDownload:                false,\n\t\tPluginRepositoryLastCheckDuration: pluginRepoCheckDurationDefault,\n\t\tDisablePluginShortNameRepository:  false,\n\t\tConcurrency:                       getConcurrency(\"auto\"),\n\t}\n}\n\nfunc newPluginRepoCheckDuration(checkDuration string) PluginRepoCheckDuration {\n\tif strings.ToLower(checkDuration) == \"never\" {\n\t\treturn PluginRepoCheckDuration{Never: true}\n\t}\n\n\tevery, err := strconv.Atoi(checkDuration)\n\tif err != nil {\n\t\t// if error parsing config use default value\n\t\treturn pluginRepoCheckDurationDefault\n\t}\n\n\treturn PluginRepoCheckDuration{Every: every}\n}\n\n// LoadConfig builds the Config struct from environment variables\nfunc LoadConfig() (Config, error) {\n\tconfig := defaultConfig(dataDirDefault, configFileDefault)\n\n\thomeDir, err := os.UserHomeDir()\n\tif err != nil {\n\t\treturn Config{}, err\n\t}\n\n\tconfigFile := os.Getenv(\"ASDF_CONFIG_FILE\")\n\tif configFile != \"\" {\n\t\tconfig.ConfigFile = configFile\n\t}\n\n\tdataDir := os.Getenv(\"ASDF_DATA_DIR\")\n\tif dataDir != \"\" {\n\t\tconfig.DataDir = dataDir\n\t}\n\n\tversionFilename := os.Getenv(\"ASDF_TOOL_VERSIONS_FILENAME\")\n\tif versionFilename != \"\" {\n\t\tconfig.DefaultToolVersionsFilename = versionFilename\n\t} else {\n\t\t// ASDF_TOOL_VERSIONS_FILENAME is the new environment variable name. It used\n\t\t// to be named ASDF_DEFAULT_TOOL_VERSIONS_FILENAME\n\t\tversionFilename = os.Getenv(\"ASDF_DEFAULT_TOOL_VERSIONS_FILENAME\")\n\t\tif versionFilename != \"\" {\n\t\t\tconfig.DefaultToolVersionsFilename = versionFilename\n\t\t}\n\t}\n\n\tconfig.Home = homeDir\n\tconfig.DataDir = normalizePath(homeDir, config.DataDir)\n\tconfig.ConfigFile = normalizePath(homeDir, config.ConfigFile)\n\n\treturn *config, nil\n}\n\n// Methods on the Config struct that allow it to load and cache values from the\n// Settings struct, which is loaded from file on disk and therefore somewhat\n// \"expensive\".\n\n// LegacyVersionFile loads the asdfrc if it isn't already loaded and fetches\n// the legacy version file support flag\nfunc (c *Config) LegacyVersionFile() (bool, error) {\n\terr := c.loadSettings()\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\treturn c.Settings.LegacyVersionFile, nil\n}\n\n// AlwaysKeepDownload loads the asdfrc if it isn't already loaded and fetches\n// the keep downloads boolean flag\nfunc (c *Config) AlwaysKeepDownload() (bool, error) {\n\terr := c.loadSettings()\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\treturn c.Settings.AlwaysKeepDownload, nil\n}\n\n// PluginRepositoryLastCheckDuration loads the asdfrc if it isn't already loaded\n// and fetches the keep  boolean flag\nfunc (c *Config) PluginRepositoryLastCheckDuration() (PluginRepoCheckDuration, error) {\n\terr := c.loadSettings()\n\tif err != nil {\n\t\treturn newPluginRepoCheckDuration(\"\"), err\n\t}\n\n\treturn c.Settings.PluginRepositoryLastCheckDuration, nil\n}\n\n// DisablePluginShortNameRepository loads the asdfrc if it isn't already loaded\n// and fetches the disable plugin short name repo flag\nfunc (c *Config) DisablePluginShortNameRepository() (bool, error) {\n\terr := c.loadSettings()\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\treturn c.Settings.DisablePluginShortNameRepository, nil\n}\n\n// Concurrency returns concurrency setting from asdfrc file\nfunc (c *Config) Concurrency() (string, error) {\n\terr := c.loadSettings()\n\tif err != nil {\n\t\treturn getConcurrency(\"auto\"), err\n\t}\n\n\treturn c.Settings.Concurrency, nil\n}\n\n// GetHook returns a hook command from config if it is there\nfunc (c *Config) GetHook(hook string) (string, error) {\n\terr := c.loadSettings()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tif c.Settings.Raw != nil {\n\t\treturn c.Settings.Raw.Key(hook).String(), nil\n\t}\n\n\treturn \"\", nil\n}\n\nfunc (c *Config) loadSettings() error {\n\tif c.Settings.Loaded {\n\t\treturn nil\n\t}\n\n\tsettings, err := loadSettings(c.ConfigFile)\n\n\tc.Settings = settings\n\n\tif err != nil {\n\t\t_, ok := err.(*fs.PathError)\n\t\tif ok {\n\t\t\treturn nil\n\t\t}\n\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc normalizePath(homeDir string, path string) string {\n\tif path == \"~\" || strings.HasPrefix(path, \"~/\") {\n\t\tpath = filepath.Join(homeDir, path[1:])\n\t}\n\treturn filepath.Clean(path)\n}\n\nfunc loadSettings(asdfrcPath string) (Settings, error) {\n\tsettings := defaultSettings()\n\n\t// asdfrc is effectively formatted as ini\n\tconfig, err := ini.Load(asdfrcPath)\n\tif err != nil {\n\t\treturn *settings, err\n\t}\n\n\tmainConf := config.Section(\"\")\n\n\tsettings.Raw = mainConf\n\n\tsettings.Loaded = true\n\tsettings.PluginRepositoryLastCheckDuration = newPluginRepoCheckDuration(mainConf.Key(\"plugin_repository_last_check_duration\").String())\n\n\tboolOverride(&settings.LegacyVersionFile, mainConf, \"legacy_version_file\")\n\tboolOverride(&settings.AlwaysKeepDownload, mainConf, \"always_keep_download\")\n\tboolOverride(&settings.DisablePluginShortNameRepository, mainConf, \"disable_plugin_short_name_repository\")\n\n\tconcurrency := strings.ToLower(mainConf.Key(\"concurrency\").String())\n\tif concurrency != \"\" {\n\t\tsettings.Concurrency = getConcurrency(concurrency)\n\t}\n\n\treturn *settings, nil\n}\n\nfunc boolOverride(field *bool, section *ini.Section, key string) {\n\tlcYesOrNo := strings.ToLower(section.Key(key).String())\n\n\tif lcYesOrNo == \"yes\" {\n\t\t*field = true\n\t}\n\tif lcYesOrNo == \"no\" {\n\t\t*field = false\n\t}\n}\n\nfunc getConcurrency(concurrency string) string {\n\tconcurrencyFromEnv := strings.ToLower(os.Getenv(\"ASDF_CONCURRENCY\"))\n\tif concurrencyFromEnv != \"\" {\n\t\tconcurrency = concurrencyFromEnv\n\t}\n\n\tif concurrency == \"auto\" || concurrency == \"\" {\n\t\treturn strconv.Itoa(runtime.NumCPU())\n\t}\n\treturn concurrency\n}\n"
  },
  {
    "path": "internal/config/config_test.go",
    "content": "package config\n\nimport (\n\t\"os\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestLoadConfig(t *testing.T) {\n\tt.Run(\"With defaults\", func(t *testing.T) {\n\t\tconfig, err := LoadConfig()\n\t\tassert.Nil(t, err, \"Returned error when loading env for config\")\n\n\t\thomeDir, err := os.UserHomeDir()\n\t\tassert.Nil(t, err)\n\n\t\tassert.Equal(t, homeDir, config.Home, \"Home directory has the wrong value\")\n\t\tassert.True(t, strings.HasPrefix(config.DataDir, homeDir), \"DataDir has the wrong value\")\n\t\tassert.True(t, strings.HasPrefix(config.ConfigFile, homeDir))\n\t})\n\n\tt.Run(\"With ASDF_DATA_DIR containing a tilde\", func(t *testing.T) {\n\t\tt.Setenv(\"ASDF_DATA_DIR\", \"~/some/other/dir\")\n\t\tconfig, err := LoadConfig()\n\t\tassert.Nil(t, err, \"Returned error when loading env for config\")\n\n\t\thomeDir, err := os.UserHomeDir()\n\t\tassert.Nil(t, err)\n\n\t\tassert.Equal(t, homeDir, config.Home, \"Home directory has the wrong value\")\n\t\tassert.Equal(t, homeDir+\"/some/other/dir\", config.DataDir, \"DataDir has the wrong value\")\n\t\tassert.True(t, strings.HasPrefix(config.ConfigFile, homeDir))\n\t})\n}\n\nfunc TestLoadSettings(t *testing.T) {\n\tt.Run(\"When given invalid path returns error\", func(t *testing.T) {\n\t\tsettings, err := loadSettings(\"./foobar\")\n\n\t\tif err == nil {\n\t\t\tt.Fatal(\"Didn't get an error\")\n\t\t}\n\n\t\tif settings.Loaded {\n\t\t\tt.Fatal(\"Didn't expect settings to be loaded\")\n\t\t}\n\t})\n\n\tt.Run(\"When given path to populated asdfrc returns populated settings struct\", func(t *testing.T) {\n\t\tsettings, err := loadSettings(\"testdata/asdfrc\")\n\n\t\tassert.Nil(t, err)\n\n\t\tassert.True(t, settings.Loaded, \"Expected Loaded field to be set to true\")\n\t\tassert.True(t, settings.LegacyVersionFile, \"LegacyVersionFile field has wrong value\")\n\t\tassert.True(t, settings.AlwaysKeepDownload, \"AlwaysKeepDownload field has wrong value\")\n\t\tassert.True(t, settings.PluginRepositoryLastCheckDuration.Never, \"PluginRepositoryLastCheckDuration field has wrong value\")\n\t\tassert.Zero(t, settings.PluginRepositoryLastCheckDuration.Every, \"PluginRepositoryLastCheckDuration field has wrong value\")\n\t\tassert.True(t, settings.DisablePluginShortNameRepository, \"DisablePluginShortNameRepository field has wrong value\")\n\t\tassert.Equal(t, \"5\", settings.Concurrency, \"Concurrency field has wrong value\")\n\t})\n\n\tt.Run(\"ASDF_CONCURRENCY=99 takes precedence over asdfrc value\", func(t *testing.T) {\n\t\tt.Setenv(\"ASDF_CONCURRENCY\", \"99\")\n\t\tsettings, err := loadSettings(\"testdata/asdfrc\")\n\t\tassert.Nil(t, err)\n\n\t\tassert.True(t, settings.Loaded, \"Expected Loaded field to be set to true\")\n\t\tassert.Equal(t, \"99\", settings.Concurrency, \"Concurrency field has wrong value\")\n\t})\n\n\tt.Run(\"ASDF_CONCURRENCY=auto takes precedence over asdfrc value\", func(t *testing.T) {\n\t\texpectedConcurrency := strconv.Itoa(runtime.NumCPU())\n\t\tt.Setenv(\"ASDF_CONCURRENCY\", \"auto\")\n\t\tsettings, err := loadSettings(\"testdata/asdfrc\")\n\t\tassert.Nil(t, err)\n\n\t\tassert.True(t, settings.Loaded, \"Expected Loaded field to be set to true\")\n\t\tassert.Equal(t, expectedConcurrency, settings.Concurrency, \"Concurrency field has wrong value\")\n\t})\n\n\tt.Run(\"When given path to empty file returns settings struct with defaults\", func(t *testing.T) {\n\t\texpectedConcurrency := strconv.Itoa(runtime.NumCPU())\n\t\tsettings, err := loadSettings(\"testdata/empty-asdfrc\")\n\t\tassert.Nil(t, err)\n\n\t\tassert.False(t, settings.LegacyVersionFile, \"LegacyVersionFile field has wrong value\")\n\t\tassert.False(t, settings.AlwaysKeepDownload, \"AlwaysKeepDownload field has wrong value\")\n\t\tassert.False(t, settings.PluginRepositoryLastCheckDuration.Never, \"PluginRepositoryLastCheckDuration field has wrong value\")\n\t\tassert.Equal(t, settings.PluginRepositoryLastCheckDuration.Every, 60, \"PluginRepositoryLastCheckDuration field has wrong value\")\n\t\tassert.False(t, settings.DisablePluginShortNameRepository, \"DisablePluginShortNameRepository field has wrong value\")\n\t\tassert.Equal(t, expectedConcurrency, settings.Concurrency, \"Concurrency field has wrong value\")\n\t})\n}\n\nfunc TestConfigMethods(t *testing.T) {\n\t// Set the asdf config file location to the test file\n\tt.Setenv(\"ASDF_CONFIG_FILE\", \"testdata/asdfrc\")\n\n\tconfig, err := LoadConfig()\n\tassert.Nil(t, err, \"Returned error when building config\")\n\n\tt.Run(\"Returns LegacyVersionFile from asdfrc file\", func(t *testing.T) {\n\t\tlegacyFile, err := config.LegacyVersionFile()\n\t\tassert.Nil(t, err, \"Returned error when loading settings\")\n\t\tassert.True(t, legacyFile, \"Expected LegacyVersionFile to be set\")\n\t})\n\n\tt.Run(\"Returns AlwaysKeepDownload from asdfrc file\", func(t *testing.T) {\n\t\talwaysKeepDownload, err := config.AlwaysKeepDownload()\n\t\tassert.Nil(t, err, \"Returned error when loading settings\")\n\t\tassert.True(t, alwaysKeepDownload, \"Expected AlwaysKeepDownload to be set\")\n\t})\n\n\tt.Run(\"Returns PluginRepositoryLastCheckDuration from asdfrc file\", func(t *testing.T) {\n\t\tcheckDuration, err := config.PluginRepositoryLastCheckDuration()\n\t\tassert.Nil(t, err, \"Returned error when loading settings\")\n\t\tassert.True(t, checkDuration.Never, \"Expected PluginRepositoryLastCheckDuration to be set\")\n\t\tassert.Zero(t, checkDuration.Every, \"Expected PluginRepositoryLastCheckDuration to be set\")\n\t})\n\n\tt.Run(\"Returns DisablePluginShortNameRepository from asdfrc file\", func(t *testing.T) {\n\t\tDisablePluginShortNameRepository, err := config.DisablePluginShortNameRepository()\n\t\tassert.Nil(t, err, \"Returned error when loading settings\")\n\t\tassert.True(t, DisablePluginShortNameRepository, \"Expected DisablePluginShortNameRepository to be set\")\n\t})\n\n\tt.Run(\"When file does not exist returns settings struct with defaults\", func(t *testing.T) {\n\t\tconfig := Config{ConfigFile: \"non-existent\"}\n\n\t\tlegacy, err := config.LegacyVersionFile()\n\t\tassert.Nil(t, err)\n\t\tassert.False(t, legacy)\n\n\t\tkeepDownload, err := config.AlwaysKeepDownload()\n\t\tassert.Nil(t, err)\n\t\tassert.False(t, keepDownload)\n\n\t\tlastCheck, err := config.PluginRepositoryLastCheckDuration()\n\t\tassert.Nil(t, err)\n\t\tassert.False(t, lastCheck.Never)\n\n\t\tcheckDuration, err := config.PluginRepositoryLastCheckDuration()\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, checkDuration.Every, 60)\n\n\t\tshortName, err := config.DisablePluginShortNameRepository()\n\t\tassert.Nil(t, err)\n\t\tassert.False(t, shortName)\n\t})\n}\n\nfunc TestConfigGetHook(t *testing.T) {\n\t// Set the asdf config file location to the test file\n\tt.Setenv(\"ASDF_CONFIG_FILE\", \"testdata/asdfrc\")\n\n\tconfig, err := LoadConfig()\n\tassert.Nil(t, err, \"Returned error when building config\")\n\n\tt.Run(\"Returns empty string when hook not present in asdfrc file\", func(t *testing.T) {\n\t\thookCmd, err := config.GetHook(\"post_asdf_plugin_add\")\n\t\tassert.Nil(t, err)\n\t\tassert.Zero(t, hookCmd)\n\t})\n\n\tt.Run(\"Returns string containing Bash expression when present in asdfrc file\", func(t *testing.T) {\n\t\thookCmd, err := config.GetHook(\"pre_asdf_plugin_add\")\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, hookCmd, \"echo Executing with args: $@\")\n\t})\n\n\tt.Run(\"Ignores trailing and leading spaces\", func(t *testing.T) {\n\t\thookCmd, err := config.GetHook(\"pre_asdf_plugin_add_test\")\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, hookCmd, \"echo Executing with args: $@\")\n\t})\n\n\tt.Run(\"Preserves quoting\", func(t *testing.T) {\n\t\thookCmd, err := config.GetHook(\"pre_asdf_plugin_add_test2\")\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, hookCmd, \"echo 'Executing' \\\"with args: $@\\\"\")\n\t})\n\n\tt.Run(\"works if no config file\", func(t *testing.T) {\n\t\tconfig := Config{}\n\n\t\thookCmd, err := config.GetHook(\"some_hook\")\n\t\tassert.Nil(t, err)\n\t\tassert.Empty(t, hookCmd)\n\t})\n}\n"
  },
  {
    "path": "internal/config/testdata/asdfrc",
    "content": "# This is a test asdfrc file containing all possible values. Each field to set\n# to a value that is different than the default.\nlegacy_version_file = yes\nuse_release_candidates = yes\nalways_keep_download = yes\nplugin_repository_last_check_duration = never\ndisable_plugin_short_name_repository = yes\nconcurrency = 5\n\n# Hooks\npre_asdf_plugin_add = echo Executing with args: $@\npre_asdf_plugin_add_test =      echo Executing with args: $@\npre_asdf_plugin_add_test2 = echo 'Executing' \"with args: $@\"\n"
  },
  {
    "path": "internal/config/testdata/empty-asdfrc",
    "content": ""
  },
  {
    "path": "internal/data/data.go",
    "content": "// Package data provides constants and functions pertaining to directories and\n// files in the asdf data directory on disk, specified by the $ASDF_DATA_DIR\npackage data\n\nimport (\n\t\"path/filepath\"\n)\n\nconst (\n\tdataDirDownloads = \"downloads\"\n\tdataDirInstalls  = \"installs\"\n\tdataDirPlugins   = \"plugins\"\n)\n\n// DownloadDirectory returns the directory a plugin will be placing\n// downloads of version source code\nfunc DownloadDirectory(dataDir, pluginName string) string {\n\treturn filepath.Join(dataDir, dataDirDownloads, pluginName)\n}\n\n// InstallDirectory returns the path to a plugin directory\nfunc InstallDirectory(dataDir, pluginName string) string {\n\treturn filepath.Join(dataDir, dataDirInstalls, pluginName)\n}\n\n// PluginsDirectory returns the path to the plugins directory in the data dir\nfunc PluginsDirectory(dataDir string) string {\n\treturn filepath.Join(dataDir, dataDirPlugins)\n}\n\n// PluginDirectory returns the directory a plugin with a given name would be in\n// if it were installed\nfunc PluginDirectory(dataDir, pluginName string) string {\n\treturn filepath.Join(dataDir, dataDirPlugins, pluginName)\n}\n"
  },
  {
    "path": "internal/data/data_test.go",
    "content": "package data\n\nimport \"testing\"\n\nconst testPluginName = \"lua\"\n\nfunc TestPluginDirectory(t *testing.T) {\n\tt.Run(\"returns new path with plugin name as last segment\", func(t *testing.T) {\n\t\tpluginDir := PluginDirectory(\"~/.asdf/\", testPluginName)\n\t\texpected := \"~/.asdf/plugins/lua\"\n\t\tif pluginDir != expected {\n\t\t\tt.Errorf(\"got %v, expected %v\", pluginDir, expected)\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "internal/exec/exec.go",
    "content": "// Package exec handles replacing the asdf go process with\npackage exec\n\nimport (\n\t\"syscall\"\n)\n\n// Exec invokes syscall.Exec to exec an executable. Requires an absolute path to\n// executable.\nfunc Exec(executablePath string, args []string, env []string) error {\n\treturn syscall.Exec(executablePath, append([]string{executablePath}, args...), env)\n}\n"
  },
  {
    "path": "internal/exec/exec_test.go",
    "content": "package exec\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\t\"testing\"\n\n\t\"github.com/rogpeppe/go-internal/testscript\"\n)\n\nfunc execit() {\n\t// Exec only works with absolute path\n\tcmdPath, _ := exec.LookPath(os.Args[1])\n\terr := Exec(cmdPath, os.Args[2:], os.Environ())\n\tif err != nil {\n\t\tfmt.Printf(\"Err: %#+v\\n\", err.Error())\n\t}\n}\n\nfunc TestMain(m *testing.M) {\n\ttestscript.Main(m, map[string]func() {\n\t\t\"execit\": execit,\n\t})\n}\n\nfunc TestExec(t *testing.T) {\n\ttestscript.Run(t, testscript.Params{\n\t\tDir: \"testdata/script\",\n\t})\n}\n"
  },
  {
    "path": "internal/exec/testdata/script/exec-env.txtar",
    "content": "env ENV=foo\nexecit echo this is a $ENV\nstdout 'this is a foo\\n'\n"
  },
  {
    "path": "internal/exec/testdata/script/exec-simple.txtar",
    "content": "execit echo this is a test\nstdout 'this is a test\\n'\n"
  },
  {
    "path": "internal/execenv/execenv.go",
    "content": "// Package execenv contains logic for generating execution environing using a plugin's\n// exec-env callback script if available.\npackage execenv\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/asdf-vm/asdf/internal/execute\"\n\t\"github.com/asdf-vm/asdf/internal/plugins\"\n)\n\nconst execEnvCallbackName = \"exec-env\"\n\n// Generate runs exec-env callback if available and captures the environment\n// variables it sets. It then parses them and returns them as a map.\nfunc Generate(plugin plugins.Plugin, callbackEnv map[string]string) (env map[string]string, err error) {\n\texecEnvPath, err := plugin.CallbackPath(execEnvCallbackName)\n\tif err != nil {\n\t\treturn callbackEnv, err\n\t}\n\n\tvar stdout strings.Builder\n\n\t// This is done to support the legacy behavior. exec-env is the only asdf\n\t// callback that works by exporting environment variables. Because of this,\n\t// executing the callback isn't enough. We actually need to source it (.) so\n\t// the environment variables get set, and then run `env` so they get printed\n\t// to STDOUT.\n\texpression := execute.NewExpression(fmt.Sprintf(\". \\\"%s\\\"; env -0\", execEnvPath), []string{})\n\texpression.Env = callbackEnv\n\texpression.Stdout = &stdout\n\terr = expression.Run()\n\n\tstr := stdout.String()\n\treturn execute.SliceToMap(strings.Split(str, \"\\x00\")), err\n}\n"
  },
  {
    "path": "internal/execenv/execenv_test.go",
    "content": "package execenv\n\nimport (\n\t\"testing\"\n\n\t\"github.com/asdf-vm/asdf/internal/config\"\n\t\"github.com/asdf-vm/asdf/internal/plugins\"\n\t\"github.com/asdf-vm/asdf/internal/repotest\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nconst (\n\ttestPluginName  = \"lua\"\n\ttestPluginName2 = \"ruby\"\n)\n\nfunc TestGenerate(t *testing.T) {\n\tt.Run(\"returns map of environment variables\", func(t *testing.T) {\n\t\ttestDataDir := t.TempDir()\n\t\tconf := config.Config{DataDir: testDataDir}\n\t\t_, err := repotest.InstallPlugin(\"dummy_plugin\", testDataDir, testPluginName)\n\t\tassert.Nil(t, err)\n\t\tplugin := plugins.New(conf, testPluginName)\n\t\tassert.Nil(t, repotest.WritePluginCallback(plugin.Dir, \"exec-env\", \"#!/usr/bin/env bash\\nexport BAZ=bar\"))\n\t\tenv, err := Generate(plugin, map[string]string{\"ASDF_INSTALL_VERSION\": \"test\"})\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, \"bar\", env[\"BAZ\"])\n\t\tassert.Equal(t, \"test\", env[\"ASDF_INSTALL_VERSION\"])\n\t})\n\n\tt.Run(\"returns error when plugin lacks exec-env callback\", func(t *testing.T) {\n\t\ttestDataDir := t.TempDir()\n\t\tconf := config.Config{DataDir: testDataDir}\n\t\t_, err := repotest.InstallPlugin(\"dummy_plugin\", testDataDir, testPluginName2)\n\t\tassert.Nil(t, err)\n\t\tplugin := plugins.New(conf, testPluginName2)\n\t\tenv, err := Generate(plugin, map[string]string{})\n\t\tassert.Equal(t, err.(plugins.NoCallbackError).Error(), \"Plugin named ruby does not have a callback named exec-env\")\n\t\t_, found := env[\"FOO\"]\n\t\tassert.False(t, found)\n\t})\n\n\tt.Run(\"preserves environment variables that contain equals sign in value\", func(t *testing.T) {\n\t\ttestDataDir := t.TempDir()\n\t\tconf := config.Config{DataDir: testDataDir}\n\t\t_, err := repotest.InstallPlugin(\"dummy_plugin\", testDataDir, testPluginName)\n\t\tassert.Nil(t, err)\n\t\tplugin := plugins.New(conf, testPluginName)\n\t\tassert.Nil(t, repotest.WritePluginCallback(plugin.Dir, \"exec-env\", \"#!/usr/bin/env bash\\nexport BAZ=bar\"))\n\t\tenv, err := Generate(plugin, map[string]string{\"EQUALSTEST\": \"abc=123\"})\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, \"bar\", env[\"BAZ\"])\n\t\tassert.Equal(t, \"abc=123\", env[\"EQUALSTEST\"])\n\t})\n\n\tt.Run(\"preserves environment variables that contain equals sign in value\", func(t *testing.T) {\n\t\ttestDataDir := t.TempDir()\n\t\tconf := config.Config{DataDir: testDataDir}\n\t\t_, err := repotest.InstallPlugin(\"dummy_plugin\", testDataDir, testPluginName)\n\t\tassert.Nil(t, err)\n\t\tplugin := plugins.New(conf, testPluginName)\n\t\tassert.Nil(t, repotest.WritePluginCallback(plugin.Dir, \"exec-env\", \"#!/usr/bin/env bash\\nexport BAZ=bar\"))\n\t\tenv, err := Generate(plugin, map[string]string{\"EQUALSTEST\": \"abc\\n123\"})\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, \"bar\", env[\"BAZ\"])\n\t\tassert.Equal(t, \"abc\\n123\", env[\"EQUALSTEST\"])\n\t})\n\n\tt.Run(\"preserves environment variables that contain equals sign and line breaks in value\", func(t *testing.T) {\n\t\tvalue := \"-----BEGIN CERTIFICATE-----\\nMANY\\\\LINES\\\\THE\\nLAST\\\\ONE\\\\ENDS\\\\IN\\nAN=\\n-----END CERTIFICATE-----\"\n\t\ttestDataDir := t.TempDir()\n\t\tconf := config.Config{DataDir: testDataDir}\n\t\t_, err := repotest.InstallPlugin(\"dummy_plugin\", testDataDir, testPluginName)\n\t\tassert.Nil(t, err)\n\t\tplugin := plugins.New(conf, testPluginName)\n\t\tassert.Nil(t, repotest.WritePluginCallback(plugin.Dir, \"exec-env\", \"#!/usr/bin/env bash\\nexport BAZ=\\\"\"+value+\"\\\"\"))\n\t\tenv, err := Generate(plugin, map[string]string{})\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, value, env[\"BAZ\"])\n\t})\n}\n"
  },
  {
    "path": "internal/execute/execute.go",
    "content": "// Package execute is a simple package that wraps the os/exec Command features\n// for convenient use in asdf. It was inspired by\n// https://github.com/chen-keinan/go-command-eval\npackage execute\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"os/exec\"\n\t\"strings\"\n)\n\n// Command represents a Bash command that can be executed by asdf\ntype Command struct {\n\tCommand    string\n\tExpression string\n\tArgs       []string\n\tStdin      io.Reader\n\tStdout     io.Writer\n\tStderr     io.Writer\n\tEnv        map[string]string\n}\n\n// New takes a string containing the path to a Bash script, and a slice of\n// string arguments and returns a Command struct\nfunc New(command string, args []string) Command {\n\treturn Command{Command: command, Args: args}\n}\n\n// NewExpression takes a string containing a Bash expression and a slice of\n// string arguments and returns a Command struct\nfunc NewExpression(expression string, args []string) Command {\n\treturn Command{Expression: expression, Args: args}\n}\n\n// Run executes a Command with Bash and returns the error if there is one\nfunc (c Command) Run() error {\n\tvar command string\n\tif c.Expression != \"\" {\n\t\t// Expressions need to be invoked inside a Bash function, so variables like\n\t\t// $0 and $@ are available\n\t\tcommand = fmt.Sprintf(\"fn() { %s; }; fn %s\", c.Expression, formatArgString(c.Args))\n\t} else {\n\t\t// Scripts can be invoked directly, with args provided\n\t\tcommand = fmt.Sprintf(\"%s %s\", c.Command, formatArgString(c.Args))\n\t}\n\n\tcmd := exec.Command(\"bash\", \"-c\", command)\n\n\tif len(c.Env) > 0 {\n\t\tcmd.Env = MergeWithCurrentEnv(c.Env)\n\t} else {\n\t\tcmd.Env = os.Environ()\n\t}\n\n\tcmd.Stdin = c.Stdin\n\n\t// Capture stdout and stderr\n\tcmd.Stdout = c.Stdout\n\tcmd.Stderr = c.Stderr\n\n\treturn cmd.Run()\n}\n\n// MergeWithCurrentEnv merges the provided map into the current environment variables\nfunc MergeWithCurrentEnv(env map[string]string) (slice []string) {\n\treturn MapToSlice(MergeEnv(CurrentEnv(), env))\n}\n\n// CurrentEnv returns the current environment as a map\nfunc CurrentEnv() map[string]string {\n\treturn SliceToMap(os.Environ())\n}\n\n// MergeEnv takes two maps with string keys and values and merges them.\nfunc MergeEnv(map1, map2 map[string]string) map[string]string {\n\tfor key, value := range map2 {\n\t\tmap1[key] = value\n\t}\n\n\treturn map1\n}\n\n// MapToSlice converts an env map to env slice suitable for syscall.Exec\nfunc MapToSlice(env map[string]string) (slice []string) {\n\tfor key, value := range env {\n\t\tslice = append(slice, fmt.Sprintf(\"%s=%s\", key, value))\n\t}\n\n\treturn slice\n}\n\n// SliceToMap converts an env map to env slice suitable for syscall.Exec\nfunc SliceToMap(env []string) map[string]string {\n\tenvMap := map[string]string{}\n\n\tfor _, envVar := range env {\n\t\tvarValue := strings.SplitN(envVar, \"=\", 2)\n\n\t\tif len(varValue) == 2 {\n\t\t\tenvMap[varValue[0]] = varValue[1]\n\t\t}\n\t}\n\n\treturn envMap\n}\n\nfunc formatArgString(args []string) string {\n\tvar newArgs []string\n\tfor _, str := range args {\n\t\tnewArgs = append(newArgs, fmt.Sprintf(\"\\\"%s\\\"\", str))\n\t}\n\treturn strings.Join(newArgs, \" \")\n}\n"
  },
  {
    "path": "internal/execute/execute_test.go",
    "content": "package execute\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestNew(t *testing.T) {\n\tt.Run(\"Returns new command\", func(t *testing.T) {\n\t\tcmd := New(\"echo\", []string{\"test string\"})\n\t\tassert.Equal(t, \"echo\", cmd.Command)\n\t\tassert.Equal(t, \"\", cmd.Expression)\n\t})\n}\n\nfunc TestNewExpression(t *testing.T) {\n\tt.Run(\"Returns new command expression\", func(t *testing.T) {\n\t\tcmd := NewExpression(\"echo\", []string{\"test string\"})\n\t\tassert.Equal(t, \"echo\", cmd.Expression)\n\t\tassert.Equal(t, \"\", cmd.Command)\n\t})\n}\n\nfunc TestRun_Command(t *testing.T) {\n\tt.Run(\"command is executed with bash\", func(t *testing.T) {\n\t\t// \"type\" (bash) output is locale sensitive\n\t\tcmd := New(\"echo $(LANG=C type -a sh);\", []string{})\n\n\t\tvar stdout strings.Builder\n\t\tcmd.Stdout = &stdout\n\t\terr := cmd.Run()\n\n\t\tassert.Nil(t, err)\n\t\tassert.Contains(t, stdout.String(), \"sh is /\")\n\t})\n\n\tt.Run(\"positional arg is passed to command\", func(t *testing.T) {\n\t\tcmd := New(\"testdata/script\", []string{\"test string\"})\n\n\t\tvar stdout strings.Builder\n\t\tcmd.Stdout = &stdout\n\t\terr := cmd.Run()\n\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, \"test string\\n\", stdout.String())\n\t})\n\n\tt.Run(\"positional args are passed to command\", func(t *testing.T) {\n\t\tcmd := New(\"testdata/script\", []string{\"test string\", \"another string\"})\n\n\t\tvar stdout strings.Builder\n\t\tcmd.Stdout = &stdout\n\t\terr := cmd.Run()\n\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, \"test string another string\\n\", stdout.String())\n\t})\n\n\tt.Run(\"environment variables are passed to command\", func(t *testing.T) {\n\t\tcmd := New(\"echo $MYVAR;\", []string{})\n\t\tcmd.Env = map[string]string{\"MYVAR\": \"my var value\"}\n\n\t\tvar stdout strings.Builder\n\t\tcmd.Stdout = &stdout\n\t\terr := cmd.Run()\n\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, \"my var value\\n\", stdout.String())\n\t})\n\n\tt.Run(\"system environment variables are passed to command\", func(t *testing.T) {\n\t\tcmd := New(\"echo $MYVAR1;\", []string{})\n\t\terr := os.Setenv(\"MYVAR1\", \"my var value\")\n\t\tassert.Nil(t, err)\n\n\t\tvar stdout strings.Builder\n\t\tcmd.Stdout = &stdout\n\t\terr = cmd.Run()\n\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, \"my var value\\n\", stdout.String())\n\t})\n\n\tt.Run(\"provided env overwrites system environment variables when passed to command\", func(t *testing.T) {\n\t\tcmd := New(\"echo $MYVAR2;\", []string{})\n\t\terr := os.Setenv(\"MYVAR2\", \"should be dropped\")\n\t\tassert.Nil(t, err)\n\n\t\tvar stdout strings.Builder\n\t\tcmd.Stdout = &stdout\n\t\tcmd.Env = map[string]string{\"MYVAR2\": \"final value\"}\n\t\terr = cmd.Run()\n\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, \"final value\\n\", stdout.String())\n\t})\n\n\tt.Run(\"captures stdout and stdin\", func(t *testing.T) {\n\t\tcmd := New(\"echo 'a test' | tee /dev/stderr\", []string{})\n\t\tcmd.Env = map[string]string{\"MYVAR\": \"my var value\"}\n\n\t\tvar stdout strings.Builder\n\t\tcmd.Stdout = &stdout\n\t\tvar stderr strings.Builder\n\t\tcmd.Stderr = &stderr\n\n\t\terr := cmd.Run()\n\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, \"a test\\n\", stdout.String())\n\t\tassert.Equal(t, \"a test\\n\", stderr.String())\n\t})\n\n\tt.Run(\"returns error when non-zero exit code\", func(t *testing.T) {\n\t\tcmd := New(\"exit 12\", []string{})\n\n\t\tvar stdout strings.Builder\n\t\tcmd.Stdout = &stdout\n\t\terr := cmd.Run()\n\n\t\tassert.NotNil(t, err)\n\t\tassert.Equal(t, \"\", stdout.String())\n\t\tassert.Equal(t, 12, err.(*exec.ExitError).ExitCode())\n\t})\n}\n\nfunc TestRun_Expression(t *testing.T) {\n\tt.Run(\"expression is executed with bash\", func(t *testing.T) {\n\t\t// \"type\" (bash) output is locale sensitive\n\t\tcmd := NewExpression(\"echo $(LANG=C type -a sh)\", []string{})\n\n\t\tvar stdout strings.Builder\n\t\tcmd.Stdout = &stdout\n\t\terr := cmd.Run()\n\n\t\tassert.Nil(t, err)\n\t\tassert.Contains(t, stdout.String(), \"sh is /\")\n\t})\n\n\tt.Run(\"positional arg is passed to expression\", func(t *testing.T) {\n\t\tcmd := NewExpression(\"echo $1; true\", []string{\"test string\"})\n\n\t\tvar stdout strings.Builder\n\t\tcmd.Stdout = &stdout\n\t\terr := cmd.Run()\n\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, \"test string\\n\", stdout.String())\n\t})\n\n\tt.Run(\"positional args are passed to expression\", func(t *testing.T) {\n\t\tcmd := NewExpression(\"echo $@; true\", []string{\"test string\", \"another string\"})\n\n\t\tvar stdout strings.Builder\n\t\tcmd.Stdout = &stdout\n\t\terr := cmd.Run()\n\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, \"test string another string\\n\", stdout.String())\n\t})\n\n\tt.Run(\"environment variables are passed to expression\", func(t *testing.T) {\n\t\tcmd := NewExpression(\"echo $MYVAR\", []string{})\n\t\tcmd.Env = map[string]string{\"MYVAR\": \"my var value\"}\n\n\t\tvar stdout strings.Builder\n\t\tcmd.Stdout = &stdout\n\t\terr := cmd.Run()\n\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, \"my var value\\n\", stdout.String())\n\t})\n\n\tt.Run(\"captures stdout and stdin\", func(t *testing.T) {\n\t\tcmd := NewExpression(\"echo 'a test' | tee /dev/stderr\", []string{})\n\t\tcmd.Env = map[string]string{\"MYVAR\": \"my var value\"}\n\n\t\tvar stdout strings.Builder\n\t\tcmd.Stdout = &stdout\n\t\tvar stderr strings.Builder\n\t\tcmd.Stderr = &stderr\n\n\t\terr := cmd.Run()\n\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, \"a test\\n\", stdout.String())\n\t\tassert.Equal(t, \"a test\\n\", stderr.String())\n\t})\n\n\tt.Run(\"returns error when non-zero exit code\", func(t *testing.T) {\n\t\tcmd := NewExpression(\"exit 12\", []string{})\n\n\t\tvar stdout strings.Builder\n\t\tcmd.Stdout = &stdout\n\t\terr := cmd.Run()\n\n\t\tassert.NotNil(t, err)\n\t\tassert.Equal(t, \"\", stdout.String())\n\t\tassert.Equal(t, 12, err.(*exec.ExitError).ExitCode())\n\t})\n}\n\nfunc TestMergeWithCurrentEnv(t *testing.T) {\n\tt.Run(\"merge with current env\", func(t *testing.T) {\n\t\tpath := os.Getenv(\"PATH\")\n\t\tassert.NotEmpty(t, path)\n\n\t\tnewEnv := map[string]string{\"PATH\": \"new_path\"}\n\t\tmergedEnv := MergeWithCurrentEnv(newEnv)\n\t\tassert.Contains(t, mergedEnv, \"PATH=new_path\")\n\t\tassert.NotContains(t, mergedEnv, \"PATH=\"+path)\n\t})\n}\n\nfunc TestCurrentEnv(t *testing.T) {\n\tt.Run(\"returns map of current environment\", func(t *testing.T) {\n\t\tenvMap := CurrentEnv()\n\t\tpath, found := envMap[\"PATH\"]\n\t\tassert.True(t, found)\n\t\tassert.NotEmpty(t, path)\n\t})\n}\n\nfunc TestMergeEnv(t *testing.T) {\n\tt.Run(\"merges two maps\", func(t *testing.T) {\n\t\tmap1 := map[string]string{\"Key\": \"value\"}\n\t\tmap2 := map[string]string{\"Key2\": \"value2\"}\n\t\tmap3 := MergeEnv(map1, map2)\n\t\tassert.Equal(t, map3[\"Key\"], \"value\")\n\t\tassert.Equal(t, map3[\"Key2\"], \"value2\")\n\t})\n\n\tt.Run(\"doesn't change original map\", func(t *testing.T) {\n\t\tmap1 := map[string]string{\"Key\": \"value\"}\n\t\tmap2 := map[string]string{\"Key2\": \"value2\"}\n\t\t_ = MergeEnv(map1, map2)\n\t\tassert.Equal(t, map1[\"Key2\"], \"value2\")\n\t})\n\n\tt.Run(\"second map overwrites values in first\", func(t *testing.T) {\n\t\tmap1 := map[string]string{\"Key\": \"value\"}\n\t\tmap2 := map[string]string{\"Key\": \"value2\"}\n\t\tmap3 := MergeEnv(map1, map2)\n\t\tassert.Equal(t, map3[\"Key\"], \"value2\")\n\t})\n}\n\nfunc TestSliceToMap(t *testing.T) {\n\ttests := []struct {\n\t\tinput  []string\n\t\toutput map[string]string\n\t}{\n\t\t{\n\t\t\tinput:  []string{\"VAR=value\"},\n\t\t\toutput: map[string]string{\"VAR\": \"value\"},\n\t\t},\n\t\t{\n\t\t\tinput:  []string{\"BASH_FUNC_bats_readlinkf%%=() {  readlink -f \\\"$1\\\"\\n}\"},\n\t\t\toutput: map[string]string{\"BASH_FUNC_bats_readlinkf%%\": \"() {  readlink -f \\\"$1\\\"\\n}\"},\n\t\t},\n\t\t{\n\t\t\tinput:  []string{\"MYVAR=some things = with = in it\"},\n\t\t\toutput: map[string]string{\"MYVAR\": \"some things = with = in it\"},\n\t\t},\n\t\t{\n\t\t\tinput:  []string{\"MYVAR=value\\nwith\\nnewlines\"},\n\t\t\toutput: map[string]string{\"MYVAR\": \"value\\nwith\\nnewlines\"},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(fmt.Sprintf(\"input: %s, output: %s\", tt.input, tt.output), func(t *testing.T) {\n\t\t\tassert.Equal(t, tt.output, SliceToMap(tt.input))\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "internal/execute/testdata/script",
    "content": "#!/usr/bin/env bash\n\necho $@\n"
  },
  {
    "path": "internal/git/git.go",
    "content": "// Package git contains all the Git operations that can be applied to asdf\n// Git repositories like the plugin index repo and individual asdf plugins.\npackage git\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io/fs\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/asdf-vm/asdf/internal/execute\"\n)\n\n// DefaultRemoteName for Git repositories in asdf\nconst DefaultRemoteName = \"origin\"\n\n// Repoer is an interface for operations that can be applied to asdf plugins.\n// Right now we only support Git, but in the future we might have other\n// mechanisms to install and upgrade plugins. asdf doesn't require a plugin\n// to be a Git repository when asdf uses it, but Git is the only way to install\n// and upgrade plugins. If other approaches are supported this will be\n// extracted into the `plugins` module.\ntype Repoer interface {\n\tClone(pluginURL, ref string) error\n\tHead() (string, error)\n\tRemoteURL() (string, error)\n\tUpdate(ref string) (string, string, string, error)\n}\n\n// Repo is a struct to contain the Git repository details\ntype Repo struct {\n\tDirectory string\n\tURL       string\n}\n\n// NewRepo builds a new Repo instance\nfunc NewRepo(directory string) Repo {\n\treturn Repo{Directory: directory}\n}\n\n// Clone installs a plugin via Git\nfunc (r Repo) Clone(pluginURL, ref string) error {\n\tcmdStr := []string{\"git\", \"clone\", pluginURL, r.Directory}\n\n\tif ref != \"\" {\n\t\tcmdStr = []string{\"git\", \"clone\", pluginURL, r.Directory, \"--branch\", ref}\n\t}\n\n\t_, stderr, err := exec(cmdStr)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to clone plugin: %s\", stdErrToErrMsg(stderr))\n\t}\n\n\treturn nil\n}\n\n// Head returns the current HEAD ref of the plugin's Git repository\nfunc (r Repo) Head() (string, error) {\n\terr := repositoryExists(r.Directory)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tstdout, stderr, err := exec([]string{\"git\", \"-C\", r.Directory, \"rev-parse\", \"HEAD\"})\n\tif err != nil {\n\t\treturn \"\", errors.New(stdErrToErrMsg(stderr))\n\t}\n\n\treturn strings.TrimSpace(stdout), nil\n}\n\n// RemoteURL returns the URL of the default remote for the plugin's Git repository\nfunc (r Repo) RemoteURL() (string, error) {\n\terr := repositoryExists(r.Directory)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tremote, err := r.defaultRemote()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tstdout, _, err := exec([]string{\"git\", \"-C\", r.Directory, \"remote\", \"get-url\", remote})\n\treturn stdout, err\n}\n\n// Update updates the plugin's Git repository to the ref if provided, or the\n// latest commit on the current branch\nfunc (r Repo) Update(ref string) (string, string, string, error) {\n\tshortRef := ref\n\n\toldHash, err := r.Head()\n\tif err != nil {\n\t\treturn \"\", \"\", \"\", err\n\t}\n\n\tremoteName, err := r.defaultRemote()\n\tif err != nil {\n\t\treturn \"\", \"\", \"\", err\n\t}\n\n\t// If no ref is provided we take the default branch of the remote\n\tif strings.TrimSpace(ref) == \"\" {\n\t\tref, err = r.remoteDefaultBranch()\n\t\tif err != nil {\n\t\t\treturn \"\", \"\", \"\", err\n\t\t}\n\n\t\tshortRef = strings.SplitN(ref, \"/\", 3)[2]\n\t}\n\n\tcommonOpts := []string{\"git\", \"-C\", r.Directory}\n\n\trefSpec := fmt.Sprintf(\"%s:%s\", shortRef, shortRef)\n\tcmdStr := append(commonOpts, []string{\"fetch\", \"--prune\", \"--update-head-ok\", remoteName, refSpec}...)\n\n\t_, stderr, err := exec(cmdStr)\n\tif err != nil {\n\t\treturn \"\", \"\", \"\", errors.New(stdErrToErrMsg(stderr))\n\t}\n\n\tcmdStr = append(commonOpts, []string{\"-c\", \"advice.detachedHead=false\", \"checkout\", \"--force\", shortRef}...)\n\t_, stderr, err = exec(cmdStr)\n\tif err != nil {\n\t\treturn \"\", \"\", \"\", errors.New(stdErrToErrMsg(stderr))\n\t}\n\n\tnewHash, err := r.Head()\n\tif err != nil {\n\t\treturn ref, oldHash, newHash, fmt.Errorf(\"unable to resolve plugin new Git HEAD: %w\", err)\n\t}\n\treturn ref, oldHash, newHash, nil\n}\n\nfunc (r Repo) defaultRemote() (string, error) {\n\tstdout, _, err := exec([]string{\"git\", \"-C\", r.Directory, \"remote\"})\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn strings.SplitN(stdout, \"\\n\", 2)[0], nil\n}\n\nfunc (r Repo) remoteDefaultBranch() (string, error) {\n\tremote, err := r.defaultRemote()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tstdout, stderr, err := exec([]string{\"git\", \"-C\", r.Directory, \"ls-remote\", \"--symref\", remote, \"HEAD\"})\n\tif err != nil {\n\t\treturn \"\", errors.New(stdErrToErrMsg(stderr))\n\t}\n\treturn strings.Fields(strings.Split(stdout, \"\\n\")[0])[1], nil\n}\n\nfunc stdErrToErrMsg(stdErr string) string {\n\tlines := strings.Split(strings.TrimSuffix(stdErr, \"\\n\"), \"\\n\")\n\treturn lines[len(lines)-1]\n}\n\nfunc exec(command []string) (string, string, error) {\n\tcmd := execute.New(command[0], command[1:])\n\n\tvar stdOut strings.Builder\n\tvar stdErr strings.Builder\n\tcmd.Stdout = &stdOut\n\tcmd.Stderr = &stdErr\n\terr := cmd.Run()\n\n\treturn stdOut.String(), stdErr.String(), err\n}\n\nfunc repositoryExists(directory string) error {\n\tstat, err := os.Stat(directory)\n\tif err != nil {\n\t\terr, _ := err.(*fs.PathError)\n\t\treturn err.Err\n\t}\n\n\tif stat.IsDir() {\n\t\t// directory exists\n\t\tstdout, _, _ := exec([]string{\"git\", \"-C\", directory, \"rev-parse\", \"--is-inside-work-tree\"})\n\t\tif strings.TrimSpace(stdout) == \"true\" {\n\t\t\treturn nil\n\t\t}\n\n\t\treturn errors.New(\"not a git repository\")\n\t}\n\n\treturn errors.New(\"not a directory\")\n}\n"
  },
  {
    "path": "internal/git/git_test.go",
    "content": "package git\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/asdf-vm/asdf/internal/repotest\"\n\t\"github.com/go-git/go-git/v5\"\n\t\"github.com/go-git/go-git/v5/plumbing\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestRepoClone(t *testing.T) {\n\tt.Setenv(\"LANG\", \"C\") // error messages from git are locale dependent\n\n\tt.Run(\"when repo name is valid but URL is invalid prints an error\", func(t *testing.T) {\n\t\trepo := NewRepo(t.TempDir())\n\t\terr := repo.Clone(\"foobar\", \"\")\n\n\t\tassert.ErrorContains(t, err, \"unable to clone plugin: fatal: repository 'foobar' does not exist\")\n\t})\n\n\tt.Run(\"clones provided Git URL to repo directory when URL is valid\", func(t *testing.T) {\n\t\trepoDir := generateRepo(t)\n\t\tdirectory := t.TempDir()\n\t\trepo := NewRepo(directory)\n\n\t\terr := repo.Clone(repoDir, \"\")\n\t\tassert.Nil(t, err)\n\n\t\t// Assert repo directory contains Git repo with bin directory\n\t\t_, err = os.ReadDir(directory + \"/.git\")\n\t\tassert.Nil(t, err)\n\n\t\tentries, err := os.ReadDir(directory + \"/bin\")\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, 12, len(entries))\n\t})\n\n\tt.Run(\"when repo name and URL are valid but ref is invalid prints an error\", func(t *testing.T) {\n\t\trepoDir := generateRepo(t)\n\t\tdirectory := t.TempDir()\n\t\trepo := NewRepo(directory)\n\n\t\terr := repo.Clone(repoDir, \"non-existent\")\n\n\t\tassert.ErrorContains(t, err, \"unable to clone plugin: fatal: Remote branch non-existent not found in upstream origin\")\n\t})\n\n\tt.Run(\"clones a provided Git URL and checks out a specific ref when URL is valid and ref is provided\", func(t *testing.T) {\n\t\trepoDir := generateRepo(t)\n\t\tdirectory := t.TempDir()\n\t\trepo := NewRepo(directory)\n\n\t\terr := repo.Clone(repoDir, \"master\")\n\t\tassert.Nil(t, err)\n\n\t\t// Assert repo directory contains Git repo with bin directory\n\t\t_, err = os.ReadDir(directory + \"/.git\")\n\t\tassert.Nil(t, err)\n\n\t\tentries, err := os.ReadDir(directory + \"/bin\")\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, 12, len(entries))\n\t})\n}\n\nfunc TestRepoHead(t *testing.T) {\n\trepoDir := generateRepo(t)\n\tdirectory := t.TempDir()\n\n\trepo := NewRepo(directory)\n\n\terr := repo.Clone(repoDir, \"\")\n\tassert.Nil(t, err)\n\n\thead, err := repo.Head()\n\n\tassert.Nil(t, err)\n\tassert.NotZero(t, head)\n}\n\nfunc TestRepoRemoteURL(t *testing.T) {\n\trepoDir := generateRepo(t)\n\tdirectory := t.TempDir()\n\n\trepo := NewRepo(directory)\n\n\terr := repo.Clone(repoDir, \"\")\n\tassert.Nil(t, err)\n\n\turl, err := repo.RemoteURL()\n\tassert.Nil(t, err)\n\tassert.NotZero(t, url)\n}\n\nfunc TestRepoUpdate(t *testing.T) {\n\tt.Setenv(\"LANG\", \"C\") // error messages from git are locale dependent\n\n\trepoDir := generateRepo(t)\n\tdirectory := t.TempDir()\n\n\trepo := NewRepo(directory)\n\n\terr := repo.Clone(repoDir, \"\")\n\tassert.Nil(t, err)\n\n\tt.Run(\"returns error when repo with name does not exist\", func(t *testing.T) {\n\t\tnonexistentPath := filepath.Join(directory, \"nonexistent\")\n\t\tnonexistentRepo := NewRepo(nonexistentPath)\n\t\tupdatedToRef, _, _, err := nonexistentRepo.Update(\"\")\n\n\t\tassert.NotNil(t, err)\n\t\tassert.Equal(t, updatedToRef, \"\")\n\t\tassert.ErrorContains(t, err, \"no such file or directory\")\n\t})\n\n\tt.Run(\"returns error when repo repo does not exist\", func(t *testing.T) {\n\t\tbadRepoDir := t.TempDir()\n\t\tbadRepo := NewRepo(badRepoDir)\n\n\t\tupdatedToRef, _, _, err := badRepo.Update(\"\")\n\n\t\tassert.NotNil(t, err)\n\t\tassert.Equal(t, updatedToRef, \"\")\n\t\texpectedErrMsg := \"not a git repository\"\n\t\tassert.ErrorContains(t, err, expectedErrMsg)\n\t})\n\n\tt.Run(\"does not return error when repo is already updated\", func(t *testing.T) {\n\t\t// update repo twice to test already updated case\n\t\tupdatedToRef, _, _, err := repo.Update(\"\")\n\t\tassert.Nil(t, err)\n\t\tupdatedToRef2, oldHash, newHash, err := repo.Update(\"\")\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, updatedToRef, updatedToRef2)\n\t\tassert.Equal(t, oldHash, newHash)\n\t})\n\n\tt.Run(\"updates repo when repo when repo exists\", func(t *testing.T) {\n\t\tlatestHash, err := getCurrentCommit(directory)\n\t\tassert.Nil(t, err)\n\n\t\t_, err = checkoutPreviousCommit(directory)\n\t\tassert.Nil(t, err)\n\n\t\tupdatedToRef, _, _, err := repo.Update(\"\")\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, \"refs/heads/master\", updatedToRef)\n\n\t\tcurrentHash, err := getCurrentCommit(directory)\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, latestHash, currentHash)\n\t})\n\n\tt.Run(\"updates repo while leaving untracked files in place\", func(t *testing.T) {\n\t\tlatestHash, err := getCurrentCommit(directory)\n\t\tassert.Nil(t, err)\n\n\t\t_, err = checkoutPreviousCommit(directory)\n\t\tassert.Nil(t, err)\n\n\t\tuntrackedDir := filepath.Join(directory, \"untracked\")\n\t\terr = os.Mkdir(untrackedDir, 0o777)\n\t\tassert.Nil(t, err)\n\n\t\texpectedContent := []byte(\"dummy_content\")\n\t\terr = os.WriteFile(filepath.Join(untrackedDir, \"file_one\"), expectedContent, 0o777)\n\t\tassert.Nil(t, err)\n\t\terr = os.WriteFile(filepath.Join(untrackedDir, \"file_two\"), expectedContent, 0o777)\n\t\tassert.Nil(t, err)\n\n\t\tupdatedToRef, _, _, err := repo.Update(\"\")\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, \"refs/heads/master\", updatedToRef)\n\n\t\tcurrentHash, err := getCurrentCommit(directory)\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, latestHash, currentHash)\n\n\t\tcontent, err := os.ReadFile(filepath.Join(untrackedDir, \"file_one\"))\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, expectedContent, content)\n\n\t\tcontent, err = os.ReadFile(filepath.Join(untrackedDir, \"file_two\"))\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, expectedContent, content)\n\t})\n\n\tt.Run(\"Returns error when specified ref does not exist\", func(t *testing.T) {\n\t\tref := \"non-existent\"\n\t\tupdatedToRef, _, _, err := repo.Update(ref)\n\t\tassert.Equal(t, updatedToRef, \"\")\n\t\texpectedErrMsg := \"fatal: couldn't find remote ref non-existent\"\n\t\tassert.ErrorContains(t, err, expectedErrMsg)\n\t})\n\n\tt.Run(\"updates repo to ref when repo with name and ref exist\", func(t *testing.T) {\n\t\tref := \"master\"\n\n\t\thash, err := getCommit(directory, ref)\n\t\tassert.Nil(t, err)\n\n\t\tupdatedToRef, _, newHash, err := repo.Update(ref)\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, \"master\", updatedToRef)\n\n\t\t// Check that repo was updated to ref\n\t\tlatestHash, err := getCurrentCommit(directory)\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, hash, latestHash)\n\t\tassert.Equal(t, newHash, latestHash)\n\t})\n}\n\nfunc getCurrentCommit(path string) (string, error) {\n\treturn getCommit(path, \"HEAD\")\n}\n\nfunc getCommit(path, revision string) (string, error) {\n\trepo, err := git.PlainOpen(path)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\thash, err := repo.ResolveRevision(plumbing.Revision(revision))\n\n\treturn hash.String(), err\n}\n\nfunc checkoutPreviousCommit(path string) (string, error) {\n\trepo, err := git.PlainOpen(path)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tpreviousHash, err := repo.ResolveRevision(plumbing.Revision(\"HEAD~\"))\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tworktree, err := repo.Worktree()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\terr = worktree.Reset(&git.ResetOptions{Commit: *previousHash})\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn previousHash.String(), nil\n}\n\nfunc generateRepo(t *testing.T) string {\n\tt.Helper()\n\ttempDir := t.TempDir()\n\tpath, err := repotest.GeneratePlugin(\"dummy_plugin\", tempDir, \"lua\")\n\n\tassert.Nil(t, err)\n\treturn path\n}\n"
  },
  {
    "path": "internal/help/help.go",
    "content": "// Package help contains functions responsible for generating help output for\n// asdf and asdf plugins.\npackage help\n\nimport (\n\t_ \"embed\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/asdf-vm/asdf/internal/config\"\n\t\"github.com/asdf-vm/asdf/internal/plugins\"\n\t\"github.com/asdf-vm/asdf/internal/toolversions\"\n)\n\n//go:embed help.txt\nvar helpText string\n\nconst quote = \"\\\"Late but latest\\\"\\n-- Rajinikanth\"\n\n// Print help output to STDOUT\nfunc Print(asdfVersion string, plugins []plugins.Plugin) error {\n\treturn Write(asdfVersion, plugins, os.Stdout)\n}\n\n// PrintTool write tool help output to STDOUT\nfunc PrintTool(conf config.Config, toolName string) error {\n\treturn WriteToolHelp(conf, toolName, os.Stdout, os.Stderr)\n}\n\n// PrintToolVersion write help for specific tool version to STDOUT\nfunc PrintToolVersion(conf config.Config, toolName, toolVersion string) error {\n\treturn WriteToolVersionHelp(conf, toolName, toolVersion, os.Stdout, os.Stderr)\n}\n\n// Write help output to an io.Writer\nfunc Write(asdfVersion string, allPlugins []plugins.Plugin, writer io.Writer) error {\n\t_, err := writer.Write([]byte(fmt.Sprintf(\"version: %s\\n\\n\", asdfVersion)))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t_, err = writer.Write([]byte(helpText))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t_, err = writer.Write([]byte(\"\\n\"))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\textensionCommandHelp, err := pluginExtensionCommands(allPlugins)\n\tif err != nil {\n\t\tfmt.Printf(\"err %#+v\\n\", err)\n\t\treturn err\n\t}\n\n\t_, err = writer.Write([]byte(extensionCommandHelp))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t_, err = writer.Write([]byte(\"\\n\"))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t_, err = writer.Write([]byte(quote))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t_, err = writer.Write([]byte(\"\\n\"))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\n// WriteToolHelp output to an io.Writer\nfunc WriteToolHelp(conf config.Config, toolName string, writer io.Writer, errWriter io.Writer) error {\n\treturn writePluginHelp(conf, toolName, \"\", writer, errWriter)\n}\n\n// WriteToolVersionHelp output to an io.Writer\nfunc WriteToolVersionHelp(conf config.Config, toolName, toolVersion string, writer io.Writer, errWriter io.Writer) error {\n\treturn writePluginHelp(conf, toolName, toolVersion, writer, errWriter)\n}\n\nfunc writePluginHelp(conf config.Config, toolName, toolVersion string, writer io.Writer, errWriter io.Writer) error {\n\tplugin := plugins.New(conf, toolName)\n\tenv := map[string]string{\n\t\t\"ASDF_INSTALL_PATH\": plugin.Dir,\n\t}\n\n\tif toolVersion != \"\" {\n\t\tversion := toolversions.Parse(toolVersion)\n\t\tenv[\"ASDF_INSTALL_VERSION\"] = version.Value\n\t\tenv[\"ASDF_INSTALL_TYPE\"] = version.Type\n\t}\n\n\tif err := plugin.Exists(); err != nil {\n\t\terrWriter.Write([]byte(fmt.Sprintf(\"No plugin named %s\\n\", plugin.Name)))\n\t\treturn err\n\t}\n\n\terr := plugin.RunCallback(\"help.overview\", []string{}, env, writer, errWriter)\n\tif _, ok := err.(plugins.NoCallbackError); ok {\n\t\t// No such callback, print err msg\n\t\terrWriter.Write([]byte(fmt.Sprintf(\"No documentation for plugin %s\\n\", plugin.Name)))\n\t\treturn err\n\t}\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = plugin.RunCallback(\"help.deps\", []string{}, env, writer, errWriter)\n\tif _, ok := err.(plugins.NoCallbackError); !ok {\n\t\treturn err\n\t}\n\n\terr = plugin.RunCallback(\"help.config\", []string{}, env, writer, errWriter)\n\tif _, ok := err.(plugins.NoCallbackError); !ok {\n\t\treturn err\n\t}\n\n\terr = plugin.RunCallback(\"help.links\", []string{}, env, writer, errWriter)\n\tif _, ok := err.(plugins.NoCallbackError); !ok {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc pluginExtensionCommands(plugins []plugins.Plugin) (string, error) {\n\tvar output strings.Builder\n\n\tfor _, plugin := range plugins {\n\t\tcommands, err := plugin.GetExtensionCommands()\n\t\tif err != nil {\n\t\t\treturn output.String(), err\n\t\t}\n\t\tif len(commands) > 0 {\n\t\t\toutput.WriteString(fmt.Sprintf(\"PLUGIN %s\\n\", plugin.Name))\n\t\t\tfor _, command := range commands {\n\t\t\t\tif command == \"\" {\n\t\t\t\t\t// must be default command\n\t\t\t\t\toutput.WriteString(fmt.Sprintf(\"  asdf %s\\n\", plugin.Name))\n\t\t\t\t} else {\n\t\t\t\t\toutput.WriteString(fmt.Sprintf(\"  asdf %s %s\\n\", plugin.Name, command))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn output.String(), nil\n}\n"
  },
  {
    "path": "internal/help/help.txt",
    "content": "MANAGE PLUGINS\nasdf plugin add <name> [<git-url>]      Add a plugin from the plugin repo OR,\n                                        add a Git repo as a plugin by\n                                        specifying the name and repo url\nasdf plugin list [--urls] [--refs]      List installed plugins. Optionally show\n                                        git urls and git-ref\nasdf plugin list all                    List plugins registered on asdf-plugins\n                                        repository with URLs\nasdf plugin remove <name>               Remove plugin and package versions\nasdf plugin update <name> [<git-ref>]   Update a plugin to latest commit on\n                                        default branch or a particular git-ref\nasdf plugin update --all                Update all plugins to latest commit on\n                                        default branch\n\n\nMANAGE TOOLS\nasdf current                            Display current version set or being\n                                        used for all packages\nasdf current <name>                     Display current version set or being\n                                        used for package\nasdf help <name> [<version>]            Output documentation for plugin and tool\nasdf install                            Install all the package versions listed\n                                        in the .tool-versions file\nasdf install <name>                     Install one tool at the version\n                                        specified in the .tool-versions file\nasdf install <name> <version>           Install a specific version of a package\nasdf install <name> latest[:<version>]  Install the latest stable version of a\n                                        package, or with optional version,\n                                        install the latest stable version that\n                                        begins with the given string\nasdf latest <name> [<version>]          Show latest stable version of a package\nasdf latest --all                       Show latest stable version of all the\n                                        packages and if they are installed\nasdf list <name> [version]              List installed versions of a package and\n                                        optionally filter the versions\nasdf list all <name> [<version>]        List all versions of a package and\n                                        optionally filter the returned versions\nasdf set [-u] [-p] <name> <versions...> Set a tool version in a .tool-version in\n                                        the current directory, or a parent\n                                        directory.\nasdf uninstall <name> <version>         Remove a specific version of a package\nasdf where <name> [<version>]           Display install path for an installed\n                                        or current version\nasdf which <command>                    Display the path to an executable\n\n\nUTILS\nasdf exec <command> [args...]           Executes the command shim for current version\nasdf env <command> [util]               Runs util (default: `env`) inside the\n                                        environment used for command shim execution.\nasdf info                               Print OS, Shell and ASDF debug information.\nasdf version                            Print the currently installed version of ASDF\nasdf reshim <name> <version>            Recreate shims for version of a package\nasdf shimversions <command>             List the plugins and versions that\n                                        provide a command\n\nRESOURCES\nGitHub: https://github.com/asdf-vm/asdf\nDocs:   https://asdf-vm.com\n"
  },
  {
    "path": "internal/help/help_test.go",
    "content": "package help\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/asdf-vm/asdf/internal/config\"\n\t\"github.com/asdf-vm/asdf/internal/plugins\"\n\t\"github.com/asdf-vm/asdf/internal/repotest\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nconst (\n\tversion        = \"0.15.0\"\n\ttestPluginName = \"lua\"\n)\n\nfunc TestWrite(t *testing.T) {\n\ttestDataDir := t.TempDir()\n\tconf := config.Config{DataDir: testDataDir}\n\terr := os.MkdirAll(filepath.Join(testDataDir, \"plugins\"), 0o777)\n\tassert.Nil(t, err)\n\n\t// install dummy plugin\n\t_, err = repotest.InstallPlugin(\"dummy_plugin\", testDataDir, testPluginName)\n\tassert.Nil(t, err)\n\tplugin := plugins.New(conf, testPluginName)\n\twriteExtensionCommand(t, plugin, \"\", \"\")\n\n\tvar stdout strings.Builder\n\n\terr = Write(version, []plugins.Plugin{plugin}, &stdout)\n\tassert.Nil(t, err)\n\toutput := stdout.String()\n\n\t// Simple format assertions\n\tassert.Contains(t, output, \"version: \")\n\tassert.Contains(t, output, \"MANAGE PLUGINS\\n\")\n\tassert.Contains(t, output, \"MANAGE TOOLS\\n\")\n\tassert.Contains(t, output, \"UTILS\\n\")\n\tassert.Contains(t, output, \"RESOURCES\\n\")\n\tassert.Contains(t, output, \"PLUGIN lua\\n\")\n}\n\nfunc TestWriteToolHelp(t *testing.T) {\n\tconf, plugin := generateConfig(t)\n\n\tt.Run(\"when plugin implements all help callbacks\", func(t *testing.T) {\n\t\tvar stdout strings.Builder\n\t\tvar stderr strings.Builder\n\n\t\terr := WriteToolHelp(conf, plugin.Name, &stdout, &stderr)\n\n\t\tassert.Nil(t, err)\n\t\tassert.Empty(t, stderr.String())\n\t\texpected := \"Dummy plugin documentation\\n\\nDummy plugin is a plugin only used for unit tests\\n\"\n\t\tassert.Equal(t, stdout.String(), expected)\n\t})\n\n\tt.Run(\"when plugin does not have help.overview callback\", func(t *testing.T) {\n\t\tvar stdout strings.Builder\n\t\tvar stderr strings.Builder\n\t\tplugin := installPlugin(t, conf, \"dummy_legacy_plugin\", \"legacy-plugin\")\n\n\t\terr := WriteToolHelp(conf, plugin.Name, &stdout, &stderr)\n\n\t\tassert.EqualError(t, err, \"Plugin named legacy-plugin does not have a callback named help.overview\")\n\t\tassert.Empty(t, stdout.String())\n\t\tassert.Equal(t, stderr.String(), \"No documentation for plugin legacy-plugin\\n\")\n\t})\n\n\tt.Run(\"when plugin does not exist\", func(t *testing.T) {\n\t\tvar stdout strings.Builder\n\t\tvar stderr strings.Builder\n\n\t\terr := WriteToolHelp(conf, \"non-existent\", &stdout, &stderr)\n\n\t\tassert.EqualError(t, err, \"Plugin named non-existent not installed\")\n\t\tassert.Empty(t, stdout.String())\n\t\tassert.Equal(t, stderr.String(), \"No plugin named non-existent\\n\")\n\t})\n}\n\nfunc TestWriteToolVersionHelp(t *testing.T) {\n\tconf, plugin := generateConfig(t)\n\n\tt.Run(\"when plugin implements all help callbacks\", func(t *testing.T) {\n\t\tvar stdout strings.Builder\n\t\tvar stderr strings.Builder\n\n\t\terr := WriteToolVersionHelp(conf, plugin.Name, \"1.2.3\", &stdout, &stderr)\n\n\t\tassert.Nil(t, err)\n\t\tassert.Empty(t, stderr.String())\n\t\texpected := \"Dummy plugin documentation\\n\\nDummy plugin is a plugin only used for unit tests\\n\\nDetails specific for version 1.2.3\\n\"\n\t\tassert.Equal(t, stdout.String(), expected)\n\t})\n\n\tt.Run(\"when plugin does not have help.overview callback\", func(t *testing.T) {\n\t\tvar stdout strings.Builder\n\t\tvar stderr strings.Builder\n\t\tplugin := installPlugin(t, conf, \"dummy_legacy_plugin\", \"legacy-plugin\")\n\n\t\terr := WriteToolVersionHelp(conf, plugin.Name, \"1.2.3\", &stdout, &stderr)\n\n\t\tassert.EqualError(t, err, \"Plugin named legacy-plugin does not have a callback named help.overview\")\n\t\tassert.Empty(t, stdout.String())\n\t\tassert.Equal(t, stderr.String(), \"No documentation for plugin legacy-plugin\\n\")\n\t})\n\n\tt.Run(\"when plugin does not exist\", func(t *testing.T) {\n\t\tvar stdout strings.Builder\n\t\tvar stderr strings.Builder\n\n\t\terr := WriteToolVersionHelp(conf, \"non-existent\", \"1.2.3\", &stdout, &stderr)\n\n\t\tassert.EqualError(t, err, \"Plugin named non-existent not installed\")\n\t\tassert.Empty(t, stdout.String())\n\t\tassert.Equal(t, stderr.String(), \"No plugin named non-existent\\n\")\n\t})\n}\n\nfunc generateConfig(t *testing.T) (config.Config, plugins.Plugin) {\n\tt.Helper()\n\ttestDataDir := t.TempDir()\n\tconf, err := config.LoadConfig()\n\tassert.Nil(t, err)\n\tconf.DataDir = testDataDir\n\n\treturn conf, installPlugin(t, conf, \"dummy_plugin\", testPluginName)\n}\n\nfunc installPlugin(t *testing.T, conf config.Config, fixture, pluginName string) plugins.Plugin {\n\t_, err := repotest.InstallPlugin(fixture, conf.DataDir, pluginName)\n\tassert.Nil(t, err)\n\n\treturn plugins.New(conf, pluginName)\n}\n\nfunc writeExtensionCommand(t *testing.T, plugin plugins.Plugin, name, contents string) error {\n\tt.Helper()\n\tassert.Nil(t, os.MkdirAll(filepath.Join(plugin.Dir, \"lib\", \"commands\"), 0o777))\n\tfilename := \"command\"\n\tif name != \"\" {\n\t\tfilename = fmt.Sprintf(\"command-%s\", name)\n\t}\n\n\tpath := filepath.Join(plugin.Dir, \"lib\", \"commands\", filename)\n\terr := os.WriteFile(path, []byte(contents), 0o777)\n\treturn err\n}\n"
  },
  {
    "path": "internal/hook/hook.go",
    "content": "// Package hook provides a simple interface for running hook commands that may\n// be defined in the asdfrc file\npackage hook\n\nimport (\n\t\"io\"\n\t\"os\"\n\n\t\"github.com/asdf-vm/asdf/internal/config\"\n\t\"github.com/asdf-vm/asdf/internal/execute\"\n)\n\n// Run gets a hook command from config and runs it with the provided arguments.\n// Output is sent to STDOUT and STDERR\nfunc Run(conf config.Config, hookName string, arguments []string) error {\n\treturn RunWithOutput(conf, hookName, arguments, os.Stdout, os.Stderr)\n}\n\n// RunWithOutput gets a hook command from config and runs it with the provided\n// arguments. Output is sent to the provided io.Writers.\nfunc RunWithOutput(config config.Config, hookName string, arguments []string, stdOut io.Writer, stdErr io.Writer) error {\n\thookCmd, err := config.GetHook(hookName)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif hookCmd == \"\" {\n\t\treturn nil\n\t}\n\n\tcmd := execute.NewExpression(hookCmd, arguments)\n\n\tcmd.Stdout = stdOut\n\tcmd.Stderr = stdErr\n\n\treturn cmd.Run()\n}\n"
  },
  {
    "path": "internal/hook/hook_test.go",
    "content": "package hook\n\nimport (\n\t\"os/exec\"\n\t\"testing\"\n\n\t\"github.com/asdf-vm/asdf/internal/config\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestRun(t *testing.T) {\n\t// Set the asdf config file location to the test file\n\tt.Setenv(\"ASDF_CONFIG_FILE\", \"testdata/asdfrc\")\n\n\tt.Run(\"accepts config, hook name, and a slice of string arguments\", func(t *testing.T) {\n\t\tconfig, err := config.LoadConfig()\n\t\tassert.Nil(t, err)\n\n\t\terr = Run(config, \"pre_asdf_plugin_add_test\", []string{})\n\t\tassert.Nil(t, err)\n\t})\n\n\tt.Run(\"passes argument to command\", func(t *testing.T) {\n\t\tconfig, err := config.LoadConfig()\n\t\tassert.Nil(t, err)\n\n\t\terr = Run(config, \"pre_asdf_plugin_add_test2\", []string{\"123\"})\n\t\tassert.Equal(t, 123, err.(*exec.ExitError).ExitCode())\n\t})\n\n\tt.Run(\"passes arguments to command\", func(t *testing.T) {\n\t\tconfig, err := config.LoadConfig()\n\t\tassert.Nil(t, err)\n\n\t\terr = Run(config, \"pre_asdf_plugin_add_test3\", []string{\"exit 123\"})\n\t\tassert.Equal(t, 123, err.(*exec.ExitError).ExitCode())\n\t})\n\n\tt.Run(\"does not return error when no such hook is defined in asdfrc\", func(t *testing.T) {\n\t\tconfig, err := config.LoadConfig()\n\t\tassert.Nil(t, err)\n\n\t\terr = Run(config, \"nonexistent-hook\", []string{})\n\t\tassert.Nil(t, err)\n\t})\n}\n"
  },
  {
    "path": "internal/hook/testdata/asdfrc",
    "content": "# This is a test asdfrc file containing all possible values. Each field to set\n# to a value that is different than the default.\n\n# Hooks\npre_asdf_plugin_add = echo Executing with args: $@\npre_asdf_plugin_add_test =      echo Executing with args: $@\npre_asdf_plugin_add_test2 = exit $1\npre_asdf_plugin_add_test3 = eval $@\n"
  },
  {
    "path": "internal/info/info.go",
    "content": "// Package info exists to print important info about this asdf installation to STDOUT for use in debugging and bug reports.\npackage info\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"text/tabwriter\"\n\n\t\"github.com/asdf-vm/asdf/internal/config\"\n\t\"github.com/asdf-vm/asdf/internal/execute\"\n\t\"github.com/asdf-vm/asdf/internal/plugins\"\n)\n\n// Print info output to STDOUT\nfunc Print(conf config.Config, version string) error {\n\treturn Write(conf, version, os.Stdout)\n}\n\n// Write info output to an io.Writer\nfunc Write(conf config.Config, version string, writer io.Writer) error {\n\tfmt.Fprintln(writer, \"OS:\")\n\tuname := execute.NewExpression(\"uname -a\", []string{})\n\tuname.Stdout = writer\n\terr := uname.Run()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfmt.Fprintln(writer, \"\\nSHELL:\")\n\tshellVersion := execute.NewExpression(\"$SHELL --version\", []string{})\n\tshellVersion.Stdout = writer\n\terr = shellVersion.Run()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfmt.Fprintln(writer, \"\\nBASH VERSION:\")\n\tbashVersion := execute.NewExpression(\"echo $BASH_VERSION\", []string{})\n\tbashVersion.Stdout = writer\n\terr = bashVersion.Run()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfmt.Fprintln(writer, \"\\nASDF VERSION:\")\n\tfmt.Fprintf(writer, \"%s\\n\", version)\n\n\tfmt.Fprintln(writer, \"\\nASDF INTERNAL VARIABLES:\")\n\tfmt.Fprintf(writer, \"ASDF_TOOL_VERSIONS_FILENAME=%s\\n\", conf.DefaultToolVersionsFilename)\n\tfmt.Fprintf(writer, \"ASDF_DATA_DIR=%s\\n\", conf.DataDir)\n\tfmt.Fprintf(writer, \"ASDF_CONFIG_FILE=%s\\n\", conf.ConfigFile)\n\n\tfmt.Fprintln(writer, \"\\nASDF INSTALLED PLUGINS:\")\n\tplugins, err := plugins.List(conf, true, true)\n\tif err != nil {\n\t\tfmt.Fprintf(writer, \"error loading plugin list: %s\", err)\n\t\treturn err\n\t}\n\n\tpluginsTable(plugins, writer)\n\n\treturn nil\n}\n\nfunc pluginsTable(plugins []plugins.Plugin, output io.Writer) error {\n\twriter := tabwriter.NewWriter(output, 10, 4, 1, ' ', 0)\n\n\tfor _, plugin := range plugins {\n\t\tfmt.Fprintf(writer, \"%s\\t%s\\t%s\\n\", plugin.Name, plugin.URL, plugin.Ref)\n\t}\n\n\treturn writer.Flush()\n}\n"
  },
  {
    "path": "internal/info/info_test.go",
    "content": "package info\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/asdf-vm/asdf/internal/config\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestWrite(t *testing.T) {\n\ttestDataDir := t.TempDir()\n\terr := os.MkdirAll(filepath.Join(testDataDir, \"plugins\"), 0o777)\n\tassert.Nil(t, err)\n\n\tconf := config.Config{DataDir: testDataDir}\n\tvar stdout strings.Builder\n\n\terr = Write(conf, \"0.15.0\", &stdout)\n\tassert.Nil(t, err)\n\toutput := stdout.String()\n\n\t// Simple format assertions\n\tassert.True(t, strings.Contains(output, \"OS:\\n\"))\n\tassert.True(t, strings.Contains(output, \"BASH VERSION:\\n\"))\n\tassert.True(t, strings.Contains(output, \"SHELL:\\n\"))\n\tassert.True(t, strings.Contains(output, \"ASDF VERSION:\\n\"))\n\tassert.True(t, strings.Contains(output, \"INTERNAL VARIABLES:\\n\"))\n\tassert.True(t, strings.Contains(output, \"ASDF INSTALLED PLUGINS:\\n\"))\n}\n"
  },
  {
    "path": "internal/installs/installs.go",
    "content": "// Package installs contains tool installation logic. It is \"dumb\" when it comes\n// to versions and treats versions as opaque strings. It cannot depend on the\n// versions package because the versions package relies on this page.\npackage installs\n\nimport (\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\n\t\"github.com/asdf-vm/asdf/internal/config\"\n\t\"github.com/asdf-vm/asdf/internal/data\"\n\t\"github.com/asdf-vm/asdf/internal/plugins\"\n\t\"github.com/asdf-vm/asdf/internal/toolversions\"\n)\n\n// Installed returns a slice of all installed versions for a given plugin\nfunc Installed(conf config.Config, plugin plugins.Plugin) (versions []string, err error) {\n\tinstallDirectory := data.InstallDirectory(conf.DataDir, plugin.Name)\n\tfiles, err := os.ReadDir(installDirectory)\n\tif err != nil {\n\t\tif _, ok := err.(*fs.PathError); ok {\n\t\t\treturn versions, nil\n\t\t}\n\n\t\treturn versions, err\n\t}\n\n\tfor _, file := range files {\n\t\tif !file.IsDir() {\n\t\t\tcontinue\n\t\t}\n\n\t\tversions = append(versions, toolversions.VersionStringFromFSFormat(file.Name()))\n\t}\n\n\treturn versions, err\n}\n\n// InstallPath returns the path to a tool installation\nfunc InstallPath(conf config.Config, plugin plugins.Plugin, version toolversions.Version) string {\n\tif version.Type == \"path\" {\n\t\treturn version.Value\n\t}\n\n\treturn filepath.Join(data.InstallDirectory(conf.DataDir, plugin.Name), toolversions.FormatForFS(version))\n}\n\n// DownloadPath returns the download path for a particular plugin and version\nfunc DownloadPath(conf config.Config, plugin plugins.Plugin, version toolversions.Version) string {\n\tif version.Type == \"path\" {\n\t\treturn \"\"\n\t}\n\n\treturn filepath.Join(data.DownloadDirectory(conf.DataDir, plugin.Name), toolversions.FormatForFS(version))\n}\n\n// IsInstalled checks if a specific version of a tool is installed\nfunc IsInstalled(conf config.Config, plugin plugins.Plugin, version toolversions.Version) bool {\n\tinstallDir := InstallPath(conf, plugin, version)\n\n\t// Check if version already installed\n\t_, err := os.Stat(installDir)\n\treturn !os.IsNotExist(err)\n}\n"
  },
  {
    "path": "internal/installs/installs_test.go",
    "content": "package installs\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/asdf-vm/asdf/internal/config\"\n\t\"github.com/asdf-vm/asdf/internal/installtest\"\n\t\"github.com/asdf-vm/asdf/internal/plugins\"\n\t\"github.com/asdf-vm/asdf/internal/repotest\"\n\t\"github.com/asdf-vm/asdf/internal/toolversions\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nconst testPluginName = \"lua\"\n\nfunc TestDownloadPath(t *testing.T) {\n\tconf, plugin := generateConfig(t)\n\n\tt.Run(\"returns empty string when given path version\", func(t *testing.T) {\n\t\tversion := toolversions.Version{Type: \"path\", Value: \"foo/bar\"}\n\t\tpath := DownloadPath(conf, plugin, version)\n\t\tassert.Empty(t, path)\n\t})\n\n\tt.Run(\"returns empty string when given path version\", func(t *testing.T) {\n\t\tversion := toolversions.Version{Type: \"version\", Value: \"1.2.3\"}\n\t\tpath := DownloadPath(conf, plugin, version)\n\t\tassert.Equal(t, path, filepath.Join(conf.DataDir, \"downloads\", \"lua\", \"1.2.3\"))\n\t})\n}\n\nfunc TestInstallPath(t *testing.T) {\n\tconf, plugin := generateConfig(t)\n\n\tt.Run(\"returns empty string when given path version\", func(t *testing.T) {\n\t\tversion := toolversions.Version{Type: \"path\", Value: \"foo/bar\"}\n\t\tpath := InstallPath(conf, plugin, version)\n\t\tassert.Equal(t, path, \"foo/bar\")\n\t})\n\n\tt.Run(\"returns install path when given regular version as version\", func(t *testing.T) {\n\t\tversion := toolversions.Version{Type: \"version\", Value: \"1.2.3\"}\n\t\tpath := InstallPath(conf, plugin, version)\n\t\tassert.Equal(t, path, filepath.Join(conf.DataDir, \"installs\", \"lua\", \"1.2.3\"))\n\t})\n}\n\nfunc TestInstalled(t *testing.T) {\n\tconf, plugin := generateConfig(t)\n\n\tt.Run(\"returns empty slice for newly installed plugin\", func(t *testing.T) {\n\t\tinstalledVersions, err := Installed(conf, plugin)\n\t\tassert.Nil(t, err)\n\t\tassert.Empty(t, installedVersions)\n\t})\n\n\tt.Run(\"returns slice of all installed versions for a tool\", func(t *testing.T) {\n\t\tmockInstall(t, conf, plugin, \"1.0.0\")\n\n\t\tinstalledVersions, err := Installed(conf, plugin)\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, installedVersions, []string{\"1.0.0\"})\n\t})\n}\n\nfunc TestIsInstalled(t *testing.T) {\n\tconf, plugin := generateConfig(t)\n\tinstallVersion(t, conf, plugin, \"1.0.0\")\n\n\tt.Run(\"returns false when not installed\", func(t *testing.T) {\n\t\tversion := toolversions.Version{Type: \"version\", Value: \"4.0.0\"}\n\t\tassert.False(t, IsInstalled(conf, plugin, version))\n\t})\n\tt.Run(\"returns true when installed\", func(t *testing.T) {\n\t\tversion := toolversions.Version{Type: \"version\", Value: \"1.0.0\"}\n\t\tassert.True(t, IsInstalled(conf, plugin, version))\n\t})\n}\n\n// helper functions\nfunc generateConfig(t *testing.T) (config.Config, plugins.Plugin) {\n\tt.Helper()\n\ttestDataDir := t.TempDir()\n\tconf, err := config.LoadConfig()\n\tassert.Nil(t, err)\n\tconf.DataDir = testDataDir\n\n\t_, err = repotest.InstallPlugin(\"dummy_plugin\", testDataDir, testPluginName)\n\tassert.Nil(t, err)\n\n\treturn conf, plugins.New(conf, testPluginName)\n}\n\nfunc mockInstall(t *testing.T, conf config.Config, plugin plugins.Plugin, versionStr string) {\n\tt.Helper()\n\tversion := toolversions.Version{Type: \"version\", Value: versionStr}\n\tpath := InstallPath(conf, plugin, version)\n\terr := os.MkdirAll(path, os.ModePerm)\n\tassert.Nil(t, err)\n}\n\nfunc installVersion(t *testing.T, conf config.Config, plugin plugins.Plugin, version string) {\n\tt.Helper()\n\terr := installtest.InstallOneVersion(conf, plugin, \"version\", version)\n\tassert.Nil(t, err)\n}\n"
  },
  {
    "path": "internal/installtest/installtest.go",
    "content": "// Package installtest provides functions used by various asdf tests for\n// installing versions of tools. It provides a simplified version of the\n// versions.InstallOneVersion function.\npackage installtest\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/asdf-vm/asdf/internal/config\"\n\t\"github.com/asdf-vm/asdf/internal/plugins\"\n\t\"github.com/asdf-vm/asdf/internal/toolversions\"\n)\n\nconst (\n\tdataDirInstalls  = \"installs\"\n\tdataDirDownloads = \"downloads\"\n)\n\n// InstallOneVersion is a simplified version of versions.InstallOneVersion\n// function for use in Go tests.\nfunc InstallOneVersion(conf config.Config, plugin plugins.Plugin, versionType, version string) error {\n\tvar stdOut strings.Builder\n\tvar stdErr strings.Builder\n\n\terr := plugin.Exists()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tdownloadDir := DownloadPath(conf, plugin, version)\n\tinstallDir := InstallPath(conf, plugin, version)\n\n\tenv := map[string]string{\n\t\t\"ASDF_INSTALL_TYPE\":    versionType,\n\t\t\"ASDF_INSTALL_VERSION\": version,\n\t\t\"ASDF_INSTALL_PATH\":    installDir,\n\t\t\"ASDF_DOWNLOAD_PATH\":   downloadDir,\n\t\t\"ASDF_CONCURRENCY\":     \"1\",\n\t}\n\n\terr = os.MkdirAll(downloadDir, 0o777)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to create download dir: %w\", err)\n\t}\n\n\terr = plugin.RunCallback(\"download\", []string{}, env, &stdOut, &stdErr)\n\tif _, ok := err.(plugins.NoCallbackError); err != nil && !ok {\n\t\treturn fmt.Errorf(\"failed to run download callback: %w\", err)\n\t}\n\n\terr = os.MkdirAll(installDir, 0o777)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to create install dir: %w\", err)\n\t}\n\n\terr = plugin.RunCallback(\"install\", []string{}, env, &stdOut, &stdErr)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to run install callback: %w\", err)\n\t}\n\n\treturn nil\n}\n\n// InstallPath returns the path to a tool installation\nfunc InstallPath(conf config.Config, plugin plugins.Plugin, version string) string {\n\treturn filepath.Join(pluginInstallPath(conf, plugin), formatVersionStringForFS(version))\n}\n\n// DownloadPath returns the download path for a particular plugin and version\nfunc DownloadPath(conf config.Config, plugin plugins.Plugin, version string) string {\n\treturn filepath.Join(conf.DataDir, dataDirDownloads, plugin.Name, formatVersionStringForFS(version))\n}\n\nfunc pluginInstallPath(conf config.Config, plugin plugins.Plugin) string {\n\treturn filepath.Join(conf.DataDir, dataDirInstalls, plugin.Name)\n}\n\nfunc formatVersionStringForFS(version string) string {\n\treturn toolversions.FormatForFS(toolversions.Parse(version))\n}\n"
  },
  {
    "path": "internal/paths/paths.go",
    "content": "// Package paths contains a variety of helper functions responsible for\n// computing paths to various things. This package should not depend on any\n// other asdf packages.\npackage paths\n\nimport (\n\t\"strings\"\n)\n\n// RemoveFromPath returns the PATH without asdf shims path\nfunc RemoveFromPath(currentPath, pathToRemove string) string {\n\tvar newPaths []string\n\n\tfor _, fspath := range strings.Split(currentPath, \":\") {\n\t\tif fspath != pathToRemove {\n\t\t\tnewPaths = append(newPaths, fspath)\n\t\t}\n\t}\n\n\treturn strings.Join(newPaths, \":\")\n}\n"
  },
  {
    "path": "internal/paths/paths_test.go",
    "content": "package paths\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestRemoveFromPath(t *testing.T) {\n\tt.Run(\"returns PATH string with matching path removed\", func(t *testing.T) {\n\t\tgot := RemoveFromPath(\"/foo/bar:/baz/bim:/home/user/bin\", \"/baz/bim\")\n\t\tassert.Equal(t, got, \"/foo/bar:/home/user/bin\")\n\t})\n\n\tt.Run(\"returns PATH string with multiple matching paths removed\", func(t *testing.T) {\n\t\tgot := RemoveFromPath(\"/foo/bar:/baz/bim:/baz/bim:/home/user/bin\", \"/baz/bim\")\n\t\tassert.Equal(t, got, \"/foo/bar:/home/user/bin\")\n\t})\n\n\tt.Run(\"returns PATH string unchanged when no matching path found\", func(t *testing.T) {\n\t\tgot := RemoveFromPath(\"/foo/bar:/baz/bim:/home/user/bin\", \"/path-not-present/\")\n\t\tassert.Equal(t, got, \"/foo/bar:/baz/bim:/home/user/bin\")\n\t})\n}\n"
  },
  {
    "path": "internal/pluginindex/pluginindex.go",
    "content": "// Package pluginindex is a package that handles fetching plugin repo URLs by\n// name for user convenience.\npackage pluginindex\n\nimport (\n\t\"fmt\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"time\"\n\n\t\"github.com/asdf-vm/asdf/internal/git\"\n\t\"gopkg.in/ini.v1\"\n)\n\nconst (\n\tpluginIndexDir      = \"plugin-index\"\n\trepoUpdatedFilename = \"repo-updated\"\n)\n\n// PluginIndex is a struct representing the user's preferences for plugin index\n// and the plugin index on disk.\ntype PluginIndex struct {\n\trepo                  git.Repoer\n\tdirectory             string\n\turl                   string\n\tdisableUpdate         bool\n\tupdateDurationMinutes int\n}\n\n// Plugin represents a plugin listed on a plugin index.\ntype Plugin struct {\n\tName string\n\tURL  string\n}\n\n// Build returns a complete PluginIndex struct with default values set\nfunc Build(dataDir string, URL string, disableUpdate bool, updateDurationMinutes int) PluginIndex {\n\tdirectory := filepath.Join(dataDir, pluginIndexDir)\n\treturn New(directory, URL, disableUpdate, updateDurationMinutes, &git.Repo{Directory: directory})\n}\n\n// New initializes a new PluginIndex instance with the options passed in.\nfunc New(directory, url string, disableUpdate bool, updateDurationMinutes int, repo git.Repoer) PluginIndex {\n\treturn PluginIndex{\n\t\trepo:                  repo,\n\t\tdirectory:             directory,\n\t\turl:                   url,\n\t\tdisableUpdate:         disableUpdate,\n\t\tupdateDurationMinutes: updateDurationMinutes,\n\t}\n}\n\n// Get returns a slice of all available plugins\nfunc (p PluginIndex) Get() (plugins []Plugin, err error) {\n\t_, err = p.Refresh()\n\tif err != nil {\n\t\treturn plugins, err\n\t}\n\n\treturn getPlugins(p.directory)\n}\n\n// Refresh may update the plugin repo if it hasn't been updated in longer\n// than updateDurationMinutes. If the plugin repo needs to be updated the\n// repo will be invoked to perform the actual Git pull.\nfunc (p PluginIndex) Refresh() (bool, error) {\n\terr := os.MkdirAll(p.directory, os.ModePerm)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\tfiles, err := os.ReadDir(p.directory)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\tif len(files) == 0 {\n\t\t// directory empty, clone down repo\n\t\terr := p.repo.Clone(p.url, \"\")\n\t\tif err != nil {\n\t\t\treturn false, fmt.Errorf(\"unable to initialize index: %w\", err)\n\t\t}\n\n\t\treturn touchFS(p.directory)\n\t}\n\n\t// directory must not be empty, repo must be present, maybe update\n\tupdated, err := lastUpdated(p.directory)\n\tif err != nil {\n\t\treturn p.doUpdate()\n\t}\n\n\t// Convert minutes to nanoseconds\n\tupdateDurationNs := int64(p.updateDurationMinutes) * (6e10)\n\n\tif updated > updateDurationNs && !p.disableUpdate {\n\t\treturn p.doUpdate()\n\t}\n\n\treturn false, nil\n}\n\nfunc (p PluginIndex) doUpdate() (bool, error) {\n\t// pass in empty string as we want the repo to figure out what the latest\n\t// commit is\n\t_, _, _, err := p.repo.Update(\"\")\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"unable to update plugin index: %w\", err)\n\t}\n\n\t// Touch update file\n\treturn touchFS(p.directory)\n}\n\n// GetPluginSourceURL looks up a plugin by name and returns the repository URL\n// for easy install by the user.\nfunc (p PluginIndex) GetPluginSourceURL(name string) (string, error) {\n\t_, err := p.Refresh()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\turl, err := readPlugin(p.directory, name)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn url, nil\n}\n\nfunc touchFS(directory string) (bool, error) {\n\tfilename := filepath.Join(directory, repoUpdatedFilename)\n\tfile, err := os.OpenFile(filename, os.O_RDONLY|os.O_CREATE, 0o666)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"unable to create file plugin index touch file: %w\", err)\n\t}\n\n\tfile.Close()\n\treturn true, nil\n}\n\nfunc lastUpdated(dir string) (int64, error) {\n\tinfo, err := os.Stat(filepath.Join(dir, repoUpdatedFilename))\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"unable to read last updated file: %w\", err)\n\t}\n\n\t// info.Atime_ns now contains the last access time\n\tupdated := time.Now().UnixNano() - info.ModTime().UnixNano()\n\treturn updated, nil\n}\n\nfunc readPlugin(dir, name string) (string, error) {\n\tfilename := filepath.Join(dir, \"plugins\", name)\n\n\tpluginInfo, err := ini.Load(filename)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"plugin %s not found in repository\", name)\n\t}\n\n\treturn pluginInfo.Section(\"\").Key(\"repository\").String(), nil\n}\n\nfunc getPlugins(dir string) (plugins []Plugin, err error) {\n\tfiles, err := os.ReadDir(filepath.Join(dir, \"plugins\"))\n\tif _, ok := err.(*fs.PathError); ok {\n\t\treturn plugins, nil\n\t}\n\n\tfor _, file := range files {\n\t\tif !file.IsDir() {\n\t\t\turl, err := readPlugin(dir, file.Name())\n\t\t\tif err != nil {\n\t\t\t\treturn plugins, err\n\t\t\t}\n\n\t\t\tplugins = append(plugins, Plugin{Name: file.Name(), URL: url})\n\t\t}\n\t}\n\n\treturn plugins, err\n}\n"
  },
  {
    "path": "internal/pluginindex/pluginindex_test.go",
    "content": "package pluginindex\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/asdf-vm/asdf/internal/git\"\n\t\"github.com/asdf-vm/asdf/internal/repotest\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nconst (\n\tmockIndexURL    = \"https://github.com/asdf-vm/asdf-plugins.git\"\n\tbadIndexURL     = \"http://asdf-vm.com/non-existent\"\n\tfooPluginURL    = \"http://example.com/foo\"\n\telixirPluginURL = \"https://github.com/asdf-vm/asdf-elixir.git\"\n\terlangPluginURL = \"https://github.com/asdf-vm/asdf-erlang.git\"\n)\n\ntype MockIndex struct {\n\tDirectory string\n\tURL       string\n}\n\n// Only defined so MockIndex complies with git.Repoer interface. These are not\n// used by pluginindex package code\nfunc (m *MockIndex) Head() (string, error)      { return \"\", nil }\nfunc (m *MockIndex) RemoteURL() (string, error) { return \"\", nil }\n\nfunc (m *MockIndex) Clone(URL, _ string) error {\n\tm.URL = URL\n\n\tif m.URL == badIndexURL {\n\t\treturn errors.New(\"unable to clone: repository not found\")\n\t}\n\n\terr := writeMockPluginFile(m.Directory, \"elixir\", elixirPluginURL)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (m *MockIndex) Update(_ string) (string, string, string, error) {\n\tif m.URL == badIndexURL {\n\t\treturn \"\", \"\", \"\", errors.New(\"unable to clone: repository not found\")\n\t}\n\n\t// Write another plugin file to mimic update\n\terr := writeMockPluginFile(m.Directory, \"erlang\", erlangPluginURL)\n\tif err != nil {\n\t\treturn \"\", \"\", \"\", err\n\t}\n\n\treturn \"\", \"\", \"\", nil\n}\n\nfunc writeMockPluginFile(dir, pluginName, pluginURL string) error {\n\tdirname := filepath.Join(dir, \"plugins\")\n\terr := os.MkdirAll(dirname, os.ModePerm)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfilename := filepath.Join(dirname, pluginName)\n\tfile, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, os.ModePerm)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer file.Close()\n\n\t_, err = file.WriteString(fmt.Sprintf(\"repository = %s\", pluginURL))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc TestGet(t *testing.T) {\n\tt.Run(\"returns populated slice of plugins when plugins exist in directory\", func(t *testing.T) {\n\t\tdir := t.TempDir()\n\n\t\tpluginIndex := New(dir, mockIndexURL, true, 0, &MockIndex{Directory: dir})\n\t\tplugins, err := pluginIndex.Get()\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, plugins, []Plugin{{Name: \"elixir\", URL: \"https://github.com/asdf-vm/asdf-elixir.git\"}})\n\t})\n}\n\nfunc TestGetPluginSourceURL(t *testing.T) {\n\tt.Run(\"with Git returns a plugin url when provided name of existing plugin\", func(t *testing.T) {\n\t\tdir := t.TempDir()\n\t\tindexDir := filepath.Join(dir, \"index\")\n\t\terr := os.Mkdir(indexDir, 0o777)\n\t\tassert.Nil(t, err)\n\n\t\trepoPath, err := repotest.GeneratePluginIndex(dir)\n\t\tassert.Nil(t, err)\n\n\t\tpluginIndex := New(indexDir, repoPath, true, 0, &git.Repo{Directory: indexDir})\n\t\turl, err := pluginIndex.GetPluginSourceURL(\"foo\")\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, url, fooPluginURL)\n\t})\n\n\tt.Run(\"returns a plugin url when provided name of existing plugin\", func(t *testing.T) {\n\t\tdir := t.TempDir()\n\t\tpluginIndex := New(dir, mockIndexURL, true, 0, &MockIndex{Directory: dir})\n\t\turl, err := pluginIndex.GetPluginSourceURL(\"elixir\")\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, url, elixirPluginURL)\n\t})\n\n\tt.Run(\"returns a plugin url when provided name of existing plugin when loading from cache\", func(t *testing.T) {\n\t\tdir := t.TempDir()\n\t\tpluginIndex := New(dir, mockIndexURL, false, 10, &MockIndex{Directory: dir})\n\t\turl, err := pluginIndex.GetPluginSourceURL(\"elixir\")\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, url, elixirPluginURL)\n\n\t\turl, err = pluginIndex.GetPluginSourceURL(\"elixir\")\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, url, elixirPluginURL)\n\t})\n\n\tt.Run(\"returns an error when given a name that isn't in the index\", func(t *testing.T) {\n\t\tdir := t.TempDir()\n\t\tpluginIndex := New(dir, mockIndexURL, false, 10, &MockIndex{Directory: dir})\n\t\turl, err := pluginIndex.GetPluginSourceURL(\"foobar\")\n\t\tassert.EqualError(t, err, \"plugin foobar not found in repository\")\n\t\tassert.Equal(t, url, \"\")\n\t})\n\n\tt.Run(\"returns an error when plugin index cannot be updated\", func(t *testing.T) {\n\t\tdir := t.TempDir()\n\n\t\t// create plain text file so it appears plugin index already exists on disk\n\t\tfile, err := os.OpenFile(filepath.Join(dir, \"test\"), os.O_RDONLY|os.O_CREATE, 0o666)\n\t\tassert.Nil(t, err)\n\t\tfile.Close()\n\t\trepo := MockIndex{Directory: dir, URL: badIndexURL}\n\n\t\tpluginIndex := New(dir, badIndexURL, false, 10, &repo)\n\n\t\turl, err := pluginIndex.GetPluginSourceURL(\"lua\")\n\t\tassert.EqualError(t, err, \"unable to update plugin index: unable to clone: repository not found\")\n\t\tassert.Equal(t, url, \"\")\n\t})\n\n\tt.Run(\"returns error when given non-existent plugin index\", func(t *testing.T) {\n\t\tdir := t.TempDir()\n\t\tpluginIndex := New(dir, badIndexURL, false, 10, &MockIndex{Directory: dir})\n\t\turl, err := pluginIndex.GetPluginSourceURL(\"lua\")\n\t\tassert.EqualError(t, err, \"unable to initialize index: unable to clone: repository not found\")\n\t\tassert.Equal(t, url, \"\")\n\t})\n}\n\nfunc TestRefresh(t *testing.T) {\n\tt.Run(\"with Git updates repo when called once\", func(t *testing.T) {\n\t\tdir := t.TempDir()\n\t\tindexDir := filepath.Join(dir, \"index\")\n\t\terr := os.Mkdir(indexDir, 0o777)\n\t\tassert.Nil(t, err)\n\n\t\trepoPath, err := repotest.GeneratePluginIndex(dir)\n\t\tassert.Nil(t, err)\n\n\t\tpluginIndex := New(indexDir, repoPath, false, 0, &git.Repo{Directory: indexDir})\n\t\turl, err := pluginIndex.GetPluginSourceURL(\"foo\")\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, url, fooPluginURL)\n\n\t\tupdated, err := pluginIndex.Refresh()\n\t\tassert.Nil(t, err)\n\t\tassert.True(t, updated)\n\t})\n\n\tt.Run(\"updates repo when called once\", func(t *testing.T) {\n\t\tdir := t.TempDir()\n\t\tpluginIndex := New(dir, mockIndexURL, false, 0, &MockIndex{Directory: dir})\n\n\t\tupdated, err := pluginIndex.Refresh()\n\t\tassert.Nil(t, err)\n\t\tassert.True(t, updated)\n\n\t\turl, err := pluginIndex.GetPluginSourceURL(\"erlang\")\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, url, erlangPluginURL)\n\t})\n\n\tt.Run(\"does not update index when time has not elaspsed\", func(t *testing.T) {\n\t\tdir := t.TempDir()\n\t\tpluginIndex := New(dir, mockIndexURL, false, 10, &MockIndex{Directory: dir})\n\n\t\t// Call Refresh twice, the second call should not perform an update\n\t\tupdated, err := pluginIndex.Refresh()\n\t\tassert.Nil(t, err)\n\t\tassert.True(t, updated)\n\n\t\tupdated, err = pluginIndex.Refresh()\n\t\tassert.Nil(t, err)\n\t\tassert.False(t, updated)\n\t})\n\n\tt.Run(\"updates plugin index when time has elaspsed\", func(t *testing.T) {\n\t\tdir := t.TempDir()\n\t\tpluginIndex := New(dir, mockIndexURL, false, 0, &MockIndex{Directory: dir})\n\n\t\t// Call Refresh twice, the second call should perform an update\n\t\tupdated, err := pluginIndex.Refresh()\n\t\tassert.Nil(t, err)\n\t\tassert.True(t, updated)\n\n\t\ttime.Sleep(10 * time.Nanosecond)\n\t\tupdated, err = pluginIndex.Refresh()\n\t\tassert.Nil(t, err)\n\t\tassert.True(t, updated)\n\t})\n\n\tt.Run(\"returns error when plugin index repo doesn't exist\", func(t *testing.T) {\n\t\tdir := t.TempDir()\n\n\t\tpluginIndex := New(dir, badIndexURL, false, 0, &MockIndex{Directory: dir})\n\t\tupdated, err := pluginIndex.Refresh()\n\t\tassert.EqualError(t, err, \"unable to initialize index: unable to clone: repository not found\")\n\t\tassert.False(t, updated)\n\t})\n}\n"
  },
  {
    "path": "internal/plugins/plugins.go",
    "content": "// Package plugins provides functions for interacting with asdf plugins\npackage plugins\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"strings\"\n\n\t\"github.com/asdf-vm/asdf/internal/config\"\n\t\"github.com/asdf-vm/asdf/internal/data\"\n\t\"github.com/asdf-vm/asdf/internal/execute\"\n\t\"github.com/asdf-vm/asdf/internal/git\"\n\t\"github.com/asdf-vm/asdf/internal/hook\"\n\t\"github.com/asdf-vm/asdf/internal/pluginindex\"\n)\n\n// NewPluginAlreadyExists generates a new PluginAlreadyExists error instance for\n// a particular plugin\nfunc NewPluginAlreadyExists(plugin string) PluginAlreadyExists {\n\treturn PluginAlreadyExists{plugin: plugin}\n}\n\n// PluginAlreadyExists is an error returned when the specified plugin already\n// exists\ntype PluginAlreadyExists struct {\n\tplugin string\n}\n\nfunc (e PluginAlreadyExists) Error() string {\n\treturn fmt.Sprintf(pluginAlreadyExistsMsg, e.plugin)\n}\n\n// PluginMissing is the error returned when Plugin.Exists is call and the plugin\n// doesn't exist on disk.\ntype PluginMissing struct {\n\tplugin string\n}\n\nfunc (e PluginMissing) Error() string {\n\treturn fmt.Sprintf(pluginMissingMsg, e.plugin)\n}\n\n// NoCallbackError is an error returned by RunCallback when a callback with\n// particular name does not exist\ntype NoCallbackError struct {\n\tcallback string\n\tplugin   string\n}\n\nfunc (e NoCallbackError) Error() string {\n\treturn fmt.Sprintf(hasNoCallbackMsg, e.plugin, e.callback)\n}\n\n// NoShimTemplateError is an error returned by ShimTemplatePath when an shim\n// template was not found in the plugin shims directory, or the file is not executable\ntype NoShimTemplateError struct {\n\tshimName string\n\tplugin   string\n}\n\nfunc (e NoShimTemplateError) Error() string {\n\treturn fmt.Sprintf(hasNoCallbackMsg, e.plugin, e.shimName)\n}\n\n// NoCommandError is an error returned by ExtensionCommandPath when an extension\n// command with the given name does not exist\ntype NoCommandError struct {\n\tcommand string\n\tplugin  string\n}\n\nfunc (e NoCommandError) Error() string {\n\treturn fmt.Sprintf(hasNoCommandMsg, e.plugin, e.command)\n}\n\nconst (\n\tdataDirPlugins         = \"plugins\"\n\tinvalidPluginNameMsg   = \"%s is invalid. Name may only contain lowercase letters, numbers, '_', and '-'\"\n\tpluginAlreadyExistsMsg = \"Plugin named %s already added\"\n\tpluginMissingMsg       = \"Plugin named %s not installed\"\n\thasNoCallbackMsg       = \"Plugin named %s does not have a callback named %s\"\n\thasNoShimTemplateMsg   = \"Plugin named %s does not have a shim template named %s\"\n\thasNoCommandMsg        = \"Plugin named %s does not have a extension command named %s\"\n)\n\n// Plugin struct represents an asdf plugin to all asdf code. The name and dir\n// fields are the most used fields. Ref and Dir only still git info, which is\n// only information and shown to the user at times.\ntype Plugin struct {\n\tName string\n\tDir  string\n\tRef  string\n\tURL  string\n}\n\n// New takes config and a plugin name and returns a Plugin struct. It is\n// intended for functions that need to quickly initialize a plugin.\nfunc New(config config.Config, name string) Plugin {\n\tpluginsDir := data.PluginDirectory(config.DataDir, name)\n\treturn Plugin{Dir: pluginsDir, Name: name}\n}\n\n// LegacyFilenames returns a slice of filenames if the plugin contains the\n// list-legacy-filenames callback.\nfunc (p Plugin) LegacyFilenames() (filenames []string, err error) {\n\tvar stdOut strings.Builder\n\tvar stdErr strings.Builder\n\terr = p.RunCallback(\"list-legacy-filenames\", []string{}, map[string]string{}, &stdOut, &stdErr)\n\tif err != nil {\n\t\t_, ok := err.(NoCallbackError)\n\t\tif ok {\n\t\t\treturn []string{}, nil\n\t\t}\n\n\t\treturn []string{}, err\n\t}\n\n\tfor _, filename := range strings.Split(stdOut.String(), \" \") {\n\t\tfilenames = append(filenames, strings.TrimSpace(filename))\n\t}\n\treturn filenames, nil\n}\n\n// ParseLegacyVersionFile takes a file and uses the parse-legacy-file callback\n// script to parse it if the script is present. Otherwise just reads the file\n// directly. In either case the returned string is split on spaces and a slice\n// of versions is returned.\nfunc (p Plugin) ParseLegacyVersionFile(path string) (versions []string, err error) {\n\tparseLegacyFileName := \"parse-legacy-file\"\n\tparseCallbackPath := filepath.Join(p.Dir, \"bin\", parseLegacyFileName)\n\n\tvar rawVersions string\n\n\tif _, err := os.Stat(parseCallbackPath); err == nil {\n\t\tvar stdOut strings.Builder\n\t\tvar stdErr strings.Builder\n\n\t\terr = p.RunCallback(parseLegacyFileName, []string{path}, map[string]string{}, &stdOut, &stdErr)\n\t\tif err != nil {\n\t\t\treturn versions, err\n\t\t}\n\n\t\trawVersions = stdOut.String()\n\t} else {\n\t\tbytes, err := os.ReadFile(path)\n\t\tif err != nil {\n\t\t\treturn versions, err\n\t\t}\n\n\t\trawVersions = string(bytes)\n\t}\n\n\tfor _, version := range strings.Split(rawVersions, \" \") {\n\t\tversions = append(versions, strings.TrimSpace(version))\n\t}\n\n\treturn versions, err\n}\n\n// Exists returns a boolean indicating whether or not the plugin exists on disk.\nfunc (p Plugin) Exists() error {\n\texists, err := directoryExists(p.Dir)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif !exists {\n\t\treturn PluginMissing{plugin: p.Name}\n\t}\n\n\treturn nil\n}\n\n// RunCallback invokes a callback with the given name if it exists for the plugin\nfunc (p Plugin) RunCallback(name string, arguments []string, environment map[string]string, stdOut io.Writer, errOut io.Writer) error {\n\tcallback, err := p.CallbackPath(name)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tcmd := execute.New(fmt.Sprintf(\"'%s'\", callback), arguments)\n\n\tcmd.Env = environment\n\tcmd.Stdout = stdOut\n\tcmd.Stderr = errOut\n\n\treturn cmd.Run()\n}\n\n// CallbackPath returns the full file path to a callback script\nfunc (p Plugin) CallbackPath(name string) (string, error) {\n\tpath := filepath.Join(p.Dir, \"bin\", name)\n\t_, err := os.Stat(path)\n\tif errors.Is(err, os.ErrNotExist) {\n\t\treturn \"\", NoCallbackError{callback: name, plugin: p.Name}\n\t}\n\n\treturn path, nil\n}\n\n// ShimTemplatePath returns the full file path to a shim, if it exists\nfunc (p Plugin) ShimTemplatePath(shimName string) (string, error) {\n\tconst executableMode = 0o111\n\n\tpath := filepath.Join(p.Dir, \"shims\", shimName)\n\tstat, err := os.Stat(path)\n\n\tif errors.Is(err, os.ErrNotExist) || stat.Mode()&executableMode == 0 {\n\t\treturn \"\", NoShimTemplateError{shimName: shimName, plugin: p.Name}\n\t}\n\n\treturn path, nil\n}\n\n// GetExtensionCommands returns a slice of strings representing all available\n// extension commands for the plugin.\nfunc (p Plugin) GetExtensionCommands() ([]string, error) {\n\tcommands := []string{}\n\tfiles, err := os.ReadDir(filepath.Join(p.Dir, \"lib/commands\"))\n\tif _, ok := err.(*fs.PathError); ok {\n\t\treturn commands, nil\n\t}\n\n\tif err != nil {\n\t\treturn commands, err\n\t}\n\n\tfor _, file := range files {\n\t\tif !file.IsDir() {\n\t\t\tname := file.Name()\n\t\t\tif name == \"command\" {\n\t\t\t\tcommands = append(commands, \"\")\n\t\t\t} else {\n\t\t\t\tif strings.HasPrefix(name, \"command-\") {\n\t\t\t\t\tcommands = append(commands, strings.TrimPrefix(name, \"command-\"))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn commands, nil\n}\n\n// ExtensionCommandPath returns the path to the plugin's extension command\n// script matching the name if it exists.\nfunc (p Plugin) ExtensionCommandPath(name string) (string, error) {\n\tcommandName := \"command\"\n\n\tif name != \"\" {\n\t\tcommandName = fmt.Sprintf(\"command-%s\", name)\n\t}\n\n\tpath := filepath.Join(p.Dir, \"lib\", \"commands\", commandName)\n\t_, err := os.Stat(path)\n\tif errors.Is(err, os.ErrNotExist) {\n\t\treturn \"\", NoCommandError{command: name, plugin: p.Name}\n\t}\n\n\treturn path, nil\n}\n\n// Update a plugin to a specific ref, or if no ref provided update to latest\nfunc (p Plugin) Update(conf config.Config, ref string, out, errout io.Writer) (string, error) {\n\terr := p.Exists()\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"no such plugin: %s\", p.Name)\n\t}\n\n\trepo := git.NewRepo(p.Dir)\n\n\thook.Run(conf, \"pre_asdf_plugin_update\", []string{p.Name})\n\thook.Run(conf, fmt.Sprintf(\"pre_asdf_plugin_update_%s\", p.Name), []string{p.Name})\n\n\tnewRef, oldSHA, newSHA, err := repo.Update(ref)\n\tif err != nil {\n\t\treturn newRef, err\n\t}\n\n\tenv := map[string]string{\n\t\t\"ASDF_DATA_DIR\":        conf.DataDir,\n\t\t\"ASDF_PLUGIN_PATH\":     p.Dir,\n\t\t\"ASDF_PLUGIN_PREV_REF\": oldSHA,\n\t\t\"ASDF_PLUGIN_POST_REF\": newSHA,\n\t}\n\n\terr = p.RunCallback(\"post-plugin-update\", []string{}, env, out, errout)\n\tif _, ok := err.(NoCallbackError); err != nil && !ok {\n\t\treturn newRef, err\n\t}\n\n\thook.Run(conf, \"post_asdf_plugin_update\", []string{p.Name})\n\thook.Run(conf, fmt.Sprintf(\"post_asdf_plugin_update_%s\", p.Name), []string{})\n\n\treturn newRef, nil\n}\n\n// List takes config and flags for what to return and builds a list of plugins\n// representing the currently installed plugins on the system.\nfunc List(config config.Config, urls, refs bool) (plugins []Plugin, err error) {\n\tpluginsDir := data.PluginsDirectory(config.DataDir)\n\tfiles, err := os.ReadDir(pluginsDir)\n\tif err != nil {\n\t\tif _, ok := err.(*fs.PathError); ok {\n\t\t\treturn []Plugin{}, nil\n\t\t}\n\n\t\treturn plugins, err\n\t}\n\n\tfor _, file := range files {\n\t\tif file.IsDir() {\n\t\t\tif refs || urls {\n\t\t\t\tvar url string\n\t\t\t\tvar refString string\n\t\t\t\tlocation := filepath.Join(pluginsDir, file.Name())\n\t\t\t\trepo := git.NewRepo(location)\n\n\t\t\t\t// TODO: Improve these error messages\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn plugins, err\n\t\t\t\t}\n\n\t\t\t\tif refs {\n\t\t\t\t\trefString, err = repo.Head()\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn plugins, err\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif urls {\n\t\t\t\t\turl, err = repo.RemoteURL()\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn plugins, err\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tplugins = append(plugins, Plugin{\n\t\t\t\t\tName: file.Name(),\n\t\t\t\t\tDir:  location,\n\t\t\t\t\tURL:  url,\n\t\t\t\t\tRef:  refString,\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tplugins = append(plugins, Plugin{\n\t\t\t\t\tName: file.Name(),\n\t\t\t\t\tDir:  filepath.Join(pluginsDir, file.Name()),\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n\n\treturn plugins, nil\n}\n\n// Add takes plugin name and Git URL and installs the plugin if it isn't\n// already installed\nfunc Add(config config.Config, pluginName, pluginURL, ref string) error {\n\terr := validatePluginName(pluginName)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\texists, err := PluginExists(config.DataDir, pluginName)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to check if plugin already exists: %w\", err)\n\t}\n\n\tif exists {\n\t\treturn NewPluginAlreadyExists(pluginName)\n\t}\n\n\tplugin := New(config, pluginName)\n\n\tif pluginURL == \"\" {\n\t\t// Ignore error here as the default value is fine\n\t\tdisablePluginIndex, _ := config.DisablePluginShortNameRepository()\n\n\t\tif disablePluginIndex {\n\t\t\treturn fmt.Errorf(\"Short-name plugin repository is disabled\")\n\t\t}\n\n\t\tlastCheckDuration := 0\n\t\t// We don't care about errors here as we can use the default value\n\t\tcheckDuration, _ := config.PluginRepositoryLastCheckDuration()\n\n\t\tif !checkDuration.Never {\n\t\t\tlastCheckDuration = checkDuration.Every\n\t\t}\n\n\t\tindex := pluginindex.Build(config.DataDir, config.PluginIndexURL, false, lastCheckDuration)\n\t\tvar err error\n\t\tpluginURL, err = index.GetPluginSourceURL(pluginName)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error fetching plugin URL: %s\", err)\n\t\t}\n\t}\n\n\tplugin.URL = pluginURL\n\n\t// Run pre hooks\n\thook.Run(config, \"pre_asdf_plugin_add\", []string{plugin.Name})\n\thook.Run(config, fmt.Sprintf(\"pre_asdf_plugin_add_%s\", plugin.Name), []string{})\n\n\terr = git.NewRepo(plugin.Dir).Clone(plugin.URL, ref)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = os.MkdirAll(data.DownloadDirectory(config.DataDir, plugin.Name), 0o777)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tenv := map[string]string{\"ASDF_PLUGIN_SOURCE_URL\": plugin.URL, \"ASDF_PLUGIN_PATH\": plugin.Dir}\n\tplugin.RunCallback(\"post-plugin-add\", []string{}, env, os.Stdout, os.Stderr)\n\n\t// Run post hooks\n\thook.Run(config, \"post_asdf_plugin_add\", []string{plugin.Name})\n\thook.Run(config, fmt.Sprintf(\"post_asdf_plugin_add_%s\", plugin.Name), []string{})\n\n\treturn nil\n}\n\n// Remove uninstalls a plugin by removing it from the file system if installed\nfunc Remove(config config.Config, pluginName string, stdout, stderr io.Writer) error {\n\terr := validatePluginName(pluginName)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tplugin := New(config, pluginName)\n\n\texists, err := PluginExists(config.DataDir, pluginName)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to check if plugin exists: %w\", err)\n\t}\n\n\tif !exists {\n\t\treturn fmt.Errorf(\"No such plugin: %s\", pluginName)\n\t}\n\n\thook.Run(config, \"pre_asdf_plugin_remove\", []string{plugin.Name})\n\thook.Run(config, fmt.Sprintf(\"pre_asdf_plugin_remove_%s\", plugin.Name), []string{})\n\n\tenv := map[string]string{\n\t\t\"ASDF_PLUGIN_PATH\":       plugin.Dir,\n\t\t\"ASDF_PLUGIN_SOURCE_URL\": plugin.URL,\n\t}\n\tplugin.RunCallback(\"pre-plugin-remove\", []string{}, env, stdout, stderr)\n\n\tpluginDir := data.PluginDirectory(config.DataDir, pluginName)\n\tdownloadDir := data.DownloadDirectory(config.DataDir, pluginName)\n\tinstallDir := data.InstallDirectory(config.DataDir, pluginName)\n\n\terr = os.RemoveAll(downloadDir)\n\terr2 := os.RemoveAll(pluginDir)\n\terr3 := os.RemoveAll(installDir)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif err2 != nil {\n\t\treturn err2\n\t}\n\n\thook.Run(config, \"post_asdf_plugin_remove\", []string{plugin.Name})\n\thook.Run(config, fmt.Sprintf(\"post_asdf_plugin_remove_%s\", plugin.Name), []string{})\n\n\treturn err3\n}\n\n// PluginExists returns a boolean indicating whether or not a plugin with the\n// provided name is currently installed\nfunc PluginExists(dataDir, pluginName string) (bool, error) {\n\tpluginDir := data.PluginDirectory(dataDir, pluginName)\n\treturn directoryExists(pluginDir)\n}\n\nfunc directoryExists(dir string) (bool, error) {\n\tfileInfo, err := os.Stat(dir)\n\tif errors.Is(err, os.ErrNotExist) {\n\t\treturn false, nil\n\t}\n\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\treturn fileInfo.IsDir(), nil\n}\n\nfunc validatePluginName(name string) error {\n\tmatch, err := regexp.MatchString(\"^[[:lower:][:digit:]_-]+$\", name)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif !match {\n\t\treturn fmt.Errorf(invalidPluginNameMsg, name)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "internal/plugins/plugins_test.go",
    "content": "package plugins\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/asdf-vm/asdf/internal/config\"\n\t\"github.com/asdf-vm/asdf/internal/data\"\n\t\"github.com/asdf-vm/asdf/internal/repotest\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nconst testPluginName = \"lua\"\n\nfunc TestList(t *testing.T) {\n\ttestDataDir := t.TempDir()\n\tconf := config.Config{DataDir: testDataDir}\n\ttestRepo, err := repotest.GeneratePlugin(\"dummy_plugin\", testDataDir, testPluginName)\n\tassert.Nil(t, err)\n\n\terr = Add(conf, testPluginName, testRepo, \"\")\n\tassert.Nil(t, err)\n\n\tt.Run(\"when urls and refs are set to false returns plugin names\", func(t *testing.T) {\n\t\tplugins, err := List(conf, false, false)\n\t\tassert.Nil(t, err)\n\n\t\tplugin := plugins[0]\n\t\tassert.Equal(t, \"lua\", plugin.Name)\n\t\tassert.NotZero(t, plugin.Dir)\n\t\tassert.Zero(t, plugin.URL)\n\t\tassert.Zero(t, plugin.Ref)\n\t})\n\n\tt.Run(\"when urls is set to true returns plugins with repo urls set\", func(t *testing.T) {\n\t\tplugins, err := List(conf, true, false)\n\t\tassert.Nil(t, err)\n\n\t\tplugin := plugins[0]\n\t\tassert.Equal(t, \"lua\", plugin.Name)\n\t\tassert.NotZero(t, plugin.Dir)\n\t\tassert.Zero(t, plugin.Ref)\n\t\tassert.NotZero(t, plugin.URL)\n\t})\n\n\tt.Run(\"when refs is set to true returns plugins with current repo refs set\", func(t *testing.T) {\n\t\tplugins, err := List(conf, false, true)\n\t\tassert.Nil(t, err)\n\n\t\tplugin := plugins[0]\n\t\tassert.Equal(t, \"lua\", plugin.Name)\n\t\tassert.NotZero(t, plugin.Dir)\n\t\tassert.NotZero(t, plugin.Ref)\n\t\tassert.Zero(t, plugin.URL)\n\t})\n\n\tt.Run(\"when refs and urls are both set to true returns plugins with both set\", func(t *testing.T) {\n\t\tplugins, err := List(conf, true, true)\n\t\tassert.Nil(t, err)\n\n\t\tplugin := plugins[0]\n\t\tassert.Equal(t, \"lua\", plugin.Name)\n\t\tassert.NotZero(t, plugin.Dir)\n\t\tassert.NotZero(t, plugin.Ref)\n\t\tassert.NotZero(t, plugin.URL)\n\t})\n}\n\nfunc TestNew(t *testing.T) {\n\ttestDataDir := t.TempDir()\n\tconf := config.Config{DataDir: testDataDir}\n\n\tt.Run(\"returns Plugin struct with Dir and Name fields set correctly\", func(t *testing.T) {\n\t\tplugin := New(conf, \"test-plugin\")\n\n\t\tassert.Equal(t, \"test-plugin\", plugin.Name)\n\t\tassert.Equal(t, filepath.Join(testDataDir, \"plugins\", \"test-plugin\"), plugin.Dir)\n\t})\n}\n\nfunc TestAdd(t *testing.T) {\n\ttestDataDir := t.TempDir()\n\n\tt.Run(\"when given an invalid plugin name prints an error\", func(t *testing.T) {\n\t\tinvalids := []string{\"plugin^name\", \"plugin%name\", \"plugin name\", \"PLUGIN_NAME\"}\n\n\t\tfor _, invalid := range invalids {\n\t\t\tt.Run(invalid, func(t *testing.T) {\n\t\t\t\terr := Add(config.Config{}, invalid, \"never-cloned\", \"\")\n\n\t\t\t\texpectedErrMsg := \"is invalid. Name may only contain lowercase letters, numbers, '_', and '-'\"\n\t\t\t\tif !strings.Contains(err.Error(), expectedErrMsg) {\n\t\t\t\t\tt.Errorf(\"Expected an error with message %v\", expectedErrMsg)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n\n\tt.Run(\"when plugin with same name already exists prints an error\", func(t *testing.T) {\n\t\tconf := config.Config{DataDir: testDataDir}\n\n\t\t// Add plugin\n\t\trepoPath, err := repotest.GeneratePlugin(\"dummy_plugin\", testDataDir, testPluginName)\n\t\tassert.Nil(t, err)\n\n\t\terr = Add(conf, testPluginName, repoPath, \"\")\n\t\tif err != nil {\n\t\t\tt.Fatal(\"Expected to be able to add plugin\")\n\t\t}\n\n\t\t// Add it again to trigger error\n\t\terr = Add(conf, testPluginName, repoPath, \"\")\n\n\t\tif err == nil {\n\t\t\tt.Fatal(\"expected error got nil\")\n\t\t}\n\n\t\texpectedErrMsg := \"Plugin named lua already added\"\n\t\tif !strings.Contains(err.Error(), expectedErrMsg) {\n\t\t\tt.Errorf(\"Expected an error with message %v\", expectedErrMsg)\n\t\t}\n\t})\n\n\tt.Run(\"when plugin name is valid but URL is invalid prints an error\", func(t *testing.T) {\n\t\tt.Setenv(\"LANG\", \"C\") // git error messages are locale dependent\n\n\t\tconf := config.Config{DataDir: testDataDir}\n\n\t\terr := Add(conf, \"foo\", \"foobar\", \"\")\n\n\t\tassert.ErrorContains(t, err, \"unable to clone plugin: fatal: repository 'foobar' does not exist\")\n\t})\n\n\tt.Run(\"when plugin name and URL are valid installs plugin\", func(t *testing.T) {\n\t\ttestDataDir := t.TempDir()\n\t\tconf := config.Config{DataDir: testDataDir}\n\t\tpluginPath, err := repotest.GeneratePlugin(\"dummy_plugin\", testDataDir, testPluginName)\n\t\tassert.Nil(t, err)\n\n\t\terr = Add(conf, testPluginName, pluginPath, \"\")\n\n\t\tassert.Nil(t, err, \"Expected to be able to add plugin\")\n\n\t\t// Assert plugin directory contains Git repo with bin directory\n\t\tpluginDir := data.PluginDirectory(testDataDir, testPluginName)\n\n\t\t_, err = os.ReadDir(pluginDir + \"/.git\")\n\t\tassert.Nil(t, err)\n\n\t\tentries, err := os.ReadDir(pluginDir + \"/bin\")\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, 12, len(entries))\n\t})\n\n\tt.Run(\"when parameters are valid creates plugin download dir\", func(t *testing.T) {\n\t\ttestDataDir := t.TempDir()\n\t\tconf := config.Config{DataDir: testDataDir}\n\n\t\trepoPath, err := repotest.GeneratePlugin(\"dummy_plugin\", testDataDir, testPluginName)\n\t\tassert.Nil(t, err)\n\n\t\terr = Add(conf, testPluginName, repoPath, \"\")\n\t\tassert.Nil(t, err)\n\n\t\t// Assert download dir exists\n\t\tdownloadDir := data.DownloadDirectory(testDataDir, testPluginName)\n\t\t_, err = os.Stat(downloadDir)\n\t\tassert.Nil(t, err)\n\t})\n}\n\nfunc TestRemove(t *testing.T) {\n\ttestDataDir := t.TempDir()\n\tconf := config.Config{DataDir: testDataDir}\n\n\trepoPath, err := repotest.GeneratePlugin(\"dummy_plugin\", testDataDir, testPluginName)\n\tassert.Nil(t, err)\n\n\terr = Add(conf, testPluginName, repoPath, \"\")\n\tassert.Nil(t, err)\n\n\tt.Run(\"returns error when plugin with name does not exist\", func(t *testing.T) {\n\t\tvar stdout strings.Builder\n\t\tvar stderr strings.Builder\n\t\terr := Remove(conf, \"nonexistent\", &stdout, &stderr)\n\t\tassert.NotNil(t, err)\n\t\tassert.ErrorContains(t, err, \"No such plugin\")\n\t})\n\n\tt.Run(\"returns error when invalid plugin name is given\", func(t *testing.T) {\n\t\tvar stdout strings.Builder\n\t\tvar stderr strings.Builder\n\t\terr := Remove(conf, \"foo/bar/baz\", &stdout, &stderr)\n\t\tassert.NotNil(t, err)\n\t\texpectedErrMsg := \"is invalid. Name may only contain lowercase letters, numbers, '_', and '-'\"\n\t\tassert.ErrorContains(t, err, expectedErrMsg)\n\t})\n\n\tt.Run(\"removes plugin when passed name of installed plugin\", func(t *testing.T) {\n\t\tvar stdout strings.Builder\n\t\tvar stderr strings.Builder\n\t\terr := Remove(conf, testPluginName, &stdout, &stderr)\n\t\tassert.Nil(t, err)\n\n\t\tpluginDir := data.PluginDirectory(testDataDir, testPluginName)\n\t\t_, err = os.Stat(pluginDir)\n\t\tassert.NotNil(t, err)\n\t\tassert.True(t, os.IsNotExist(err))\n\t})\n\n\tt.Run(\"removes plugin download dir when passed name of installed plugin\", func(t *testing.T) {\n\t\tvar stdout strings.Builder\n\t\tvar stderr strings.Builder\n\t\terr := Add(conf, testPluginName, repoPath, \"\")\n\t\tassert.Nil(t, err)\n\n\t\terr = Remove(conf, testPluginName, &stdout, &stderr)\n\t\tassert.Nil(t, err)\n\n\t\tdownloadDir := data.DownloadDirectory(testDataDir, testPluginName)\n\t\t_, err = os.Stat(downloadDir)\n\t\tassert.NotNil(t, err)\n\t\tassert.True(t, os.IsNotExist(err))\n\t})\n}\n\nfunc TestUpdate(t *testing.T) {\n\ttestDataDir := t.TempDir()\n\tconf := config.Config{DataDir: testDataDir}\n\n\trepoPath, err := repotest.GeneratePlugin(\"dummy_plugin\", testDataDir, testPluginName)\n\tassert.Nil(t, err)\n\tassert.Nil(t, Add(conf, testPluginName, repoPath, \"\"))\n\n\tnoPostUpdateCallbackPlugin := \"no-post-update\"\n\trepoPath, err = repotest.GeneratePlugin(\"dummy_plugin\", testDataDir, noPostUpdateCallbackPlugin)\n\tassert.Nil(t, err)\n\tassert.Nil(t, os.Remove(filepath.Join(repoPath, \"bin\", \"post-plugin-update\")))\n\tassert.Nil(t, Add(conf, noPostUpdateCallbackPlugin, repoPath, \"\"))\n\n\tbadPluginName := \"badplugin\"\n\tbadRepo := data.PluginDirectory(testDataDir, badPluginName)\n\terr = os.MkdirAll(badRepo, 0o777)\n\tassert.Nil(t, err)\n\n\ttests := []struct {\n\t\tdesc        string\n\t\tgivenConf   config.Config\n\t\tgivenName   string\n\t\tgivenRef    string\n\t\twantSomeRef bool\n\t\twantErrMsg  string\n\t}{\n\t\t{\n\t\t\tdesc:        \"returns error when plugin with name does not exist\",\n\t\t\tgivenConf:   conf,\n\t\t\tgivenName:   \"nonexistent\",\n\t\t\tgivenRef:    \"\",\n\t\t\twantSomeRef: false,\n\t\t\twantErrMsg:  \"no such plugin: nonexistent\",\n\t\t},\n\t\t{\n\t\t\tdesc:        \"returns error when plugin repo does not exist\",\n\t\t\tgivenConf:   conf,\n\t\t\tgivenName:   \"badplugin\",\n\t\t\tgivenRef:    \"\",\n\t\t\twantSomeRef: false,\n\t\t\twantErrMsg:  \"not a git repository\",\n\t\t},\n\t\t{\n\t\t\tdesc:        \"updates plugin when plugin with name exists\",\n\t\t\tgivenConf:   conf,\n\t\t\tgivenName:   testPluginName,\n\t\t\tgivenRef:    \"\",\n\t\t\twantSomeRef: true,\n\t\t\twantErrMsg:  \"\",\n\t\t},\n\t\t{\n\t\t\tdesc:        \"updates plugin when plugin when post-plugin-update callback does not exist\",\n\t\t\tgivenConf:   conf,\n\t\t\tgivenName:   noPostUpdateCallbackPlugin,\n\t\t\tgivenRef:    \"\",\n\t\t\twantSomeRef: true,\n\t\t\twantErrMsg:  \"\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tvar blackhole strings.Builder\n\t\t\tplugin := New(conf, tt.givenName)\n\t\t\tupdatedToRef, err := plugin.Update(tt.givenConf, tt.givenRef, &blackhole, &blackhole)\n\n\t\t\tif tt.wantErrMsg == \"\" {\n\t\t\t\tassert.Nil(t, err)\n\t\t\t} else {\n\t\t\t\tassert.NotNil(t, err)\n\t\t\t\tassert.ErrorContains(t, err, tt.wantErrMsg)\n\t\t\t}\n\n\t\t\tif tt.wantSomeRef == true {\n\t\t\t\tassert.NotZero(t, updatedToRef)\n\t\t\t} else {\n\t\t\t\tassert.Zero(t, updatedToRef)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestExists(t *testing.T) {\n\ttestDataDir := t.TempDir()\n\tconf := config.Config{DataDir: testDataDir}\n\t_, err := repotest.InstallPlugin(\"dummy_plugin\", testDataDir, testPluginName)\n\tassert.Nil(t, err)\n\n\texistingPlugin := New(conf, testPluginName)\n\n\tt.Run(\"returns nil if plugin exists\", func(t *testing.T) {\n\t\terr := existingPlugin.Exists()\n\t\tassert.Nil(t, err)\n\t})\n\n\tt.Run(\"returns PluginMissing error when plugin missing\", func(t *testing.T) {\n\t\tmissingPlugin := New(conf, \"non-existent\")\n\t\terr := missingPlugin.Exists()\n\t\tassert.Equal(t, err, PluginMissing{plugin: \"non-existent\"})\n\t})\n}\n\nfunc TestPluginExists(t *testing.T) {\n\ttestDataDir := t.TempDir()\n\tpluginDir := data.PluginDirectory(testDataDir, testPluginName)\n\terr := os.MkdirAll(pluginDir, 0o777)\n\tif err != nil {\n\t\tt.Errorf(\"got %v, expected nil\", err)\n\t}\n\n\tt.Run(\"returns true when plugin exists\", func(t *testing.T) {\n\t\texists, err := PluginExists(testDataDir, testPluginName)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"got %v, expected nil\", err)\n\t\t}\n\n\t\tif exists != true {\n\t\t\tt.Error(\"got false, expected true\")\n\t\t}\n\t})\n\n\tt.Run(\"returns false when plugin path is file and not dir\", func(t *testing.T) {\n\t\tpluginName := \"file\"\n\t\tpluginDir := data.PluginDirectory(testDataDir, pluginName)\n\t\terr := touchFile(pluginDir)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"got %v, expected nil\", err)\n\t\t}\n\n\t\texists, err := PluginExists(testDataDir, pluginName)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"got %v, expected nil\", err)\n\t\t}\n\n\t\tif exists != false {\n\t\t\tt.Error(\"got false, expected true\")\n\t\t}\n\t})\n\n\tt.Run(\"returns false when plugin dir does not exist\", func(t *testing.T) {\n\t\texists, err := PluginExists(testDataDir, \"non-existent\")\n\t\tif err != nil {\n\t\t\tt.Errorf(\"got %v, expected nil\", err)\n\t\t}\n\n\t\tif exists != false {\n\t\t\tt.Error(\"got false, expected true\")\n\t\t}\n\t})\n}\n\nfunc TestValidatePluginName(t *testing.T) {\n\tt.Run(\"returns no error when plugin name is valid\", func(t *testing.T) {\n\t\terr := validatePluginName(testPluginName)\n\t\tassert.Nil(t, err)\n\t})\n\n\tinvalids := []string{\"plugin^name\", \"plugin%name\", \"plugin name\", \"PLUGIN_NAME\"}\n\n\tfor _, invalid := range invalids {\n\t\tt.Run(invalid, func(t *testing.T) {\n\t\t\terr := validatePluginName(invalid)\n\n\t\t\tif err == nil {\n\t\t\t\tt.Error(\"Expected an error\")\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestRunCallback(t *testing.T) {\n\temptyEnv := map[string]string{}\n\n\ttestDataDir := t.TempDir()\n\tconf := config.Config{DataDir: testDataDir}\n\t_, err := repotest.InstallPlugin(\"dummy_plugin\", testDataDir, testPluginName)\n\tassert.Nil(t, err)\n\n\tplugin := New(conf, testPluginName)\n\n\tt.Run(\"returns NoCallback error when callback with name not found\", func(t *testing.T) {\n\t\tvar stdout strings.Builder\n\t\tvar stderr strings.Builder\n\n\t\terr = plugin.RunCallback(\"non-existent\", []string{}, emptyEnv, &stdout, &stderr)\n\n\t\tassert.Equal(t, err.(NoCallbackError).Error(), \"Plugin named lua does not have a callback named non-existent\")\n\t})\n\n\tt.Run(\"passes argument to command\", func(t *testing.T) {\n\t\tvar stdout strings.Builder\n\t\tvar stderr strings.Builder\n\n\t\terr = plugin.RunCallback(\"debug\", []string{\"123\"}, emptyEnv, &stdout, &stderr)\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, \"123\\n\", stdout.String())\n\t\tassert.Equal(t, \"\", stderr.String())\n\t})\n\n\tt.Run(\"passes arguments to command\", func(t *testing.T) {\n\t\tvar stdout strings.Builder\n\t\tvar stderr strings.Builder\n\n\t\terr = plugin.RunCallback(\"debug\", []string{\"123\", \"test string\"}, emptyEnv, &stdout, &stderr)\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, \"123 test string\\n\", stdout.String())\n\t\tassert.Equal(t, \"\", stderr.String())\n\t})\n\n\tt.Run(\"passes env to command\", func(t *testing.T) {\n\t\tvar stdout strings.Builder\n\t\tvar stderr strings.Builder\n\n\t\terr = plugin.RunCallback(\"post-plugin-update\", []string{}, map[string]string{\"ASDF_PLUGIN_PREV_REF\": \"TEST\"}, &stdout, &stderr)\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, \"plugin updated path= old git-ref=TEST new git-ref=\\n\", stdout.String())\n\t\tassert.Equal(t, \"\", stderr.String())\n\t})\n}\n\nfunc TestCallbackPath(t *testing.T) {\n\ttestDataDir := t.TempDir()\n\tconf := config.Config{DataDir: testDataDir}\n\t_, err := repotest.InstallPlugin(\"dummy_plugin\", testDataDir, testPluginName)\n\tassert.Nil(t, err)\n\tplugin := New(conf, testPluginName)\n\n\tt.Run(\"returns callback path when callback exists\", func(t *testing.T) {\n\t\tpath, err := plugin.CallbackPath(\"install\")\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, filepath.Base(path), \"install\")\n\t\tassert.Equal(t, filepath.Base(filepath.Dir(filepath.Dir(path))), plugin.Name)\n\t\tassert.Equal(t, filepath.Base(filepath.Dir(filepath.Dir(filepath.Dir(path)))), \"plugins\")\n\t})\n\n\tt.Run(\"returns error when callback does not exist\", func(t *testing.T) {\n\t\tpath, err := plugin.CallbackPath(\"non-existent\")\n\t\tassert.Equal(t, err.(NoCallbackError).Error(), \"Plugin named lua does not have a callback named non-existent\")\n\t\tassert.Equal(t, path, \"\")\n\t})\n}\n\nfunc TestGetExtensionCommands(t *testing.T) {\n\ttestDataDir := t.TempDir()\n\tconf := config.Config{DataDir: testDataDir}\n\t_, err := repotest.InstallPlugin(\"dummy_plugin\", testDataDir, testPluginName)\n\tassert.Nil(t, err)\n\tplugin := New(conf, testPluginName)\n\n\tt.Run(\"returns empty slice when no extension commands defined\", func(t *testing.T) {\n\t\tcommands, err := plugin.GetExtensionCommands()\n\t\tassert.Nil(t, err)\n\t\tassert.Empty(t, commands)\n\t})\n\n\tt.Run(\"returns slice of with default extension command if it is present\", func(t *testing.T) {\n\t\tassert.Nil(t, writeExtensionCommand(t, plugin, \"\", \"#!/usr/bin/env bash\\necho $1\"))\n\t\tcommands, err := plugin.GetExtensionCommands()\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, commands, []string{\"\"})\n\t})\n\n\tt.Run(\"returns slice of all extension commands when they are present\", func(t *testing.T) {\n\t\tassert.Nil(t, writeExtensionCommand(t, plugin, \"\", \"#!/usr/bin/env bash\\necho $1\"))\n\t\tassert.Nil(t, writeExtensionCommand(t, plugin, \"foobar\", \"#!/usr/bin/env bash\\necho $1\"))\n\n\t\tcommands, err := plugin.GetExtensionCommands()\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, commands, []string{\"\", \"foobar\"})\n\t})\n}\n\nfunc TestExtensionCommandPath(t *testing.T) {\n\ttestDataDir := t.TempDir()\n\tconf := config.Config{DataDir: testDataDir}\n\t_, err := repotest.InstallPlugin(\"dummy_plugin\", testDataDir, testPluginName)\n\tassert.Nil(t, err)\n\tplugin := New(conf, testPluginName)\n\n\tt.Run(\"returns NoCallback error when callback with name not found\", func(t *testing.T) {\n\t\tpath, err := plugin.ExtensionCommandPath(\"non-existent\")\n\n\t\tassert.Equal(t, err.(NoCommandError).Error(), \"Plugin named lua does not have a extension command named non-existent\")\n\t\tassert.Equal(t, path, \"\")\n\t})\n\n\tt.Run(\"returns default extension command script when no name\", func(t *testing.T) {\n\t\tassert.Nil(t, writeExtensionCommand(t, plugin, \"\", \"#!/usr/bin/env bash\\necho $1\"))\n\t\tpath, err := plugin.ExtensionCommandPath(\"\")\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, filepath.Base(path), \"command\")\n\t})\n\n\tt.Run(\"passes arguments to command\", func(t *testing.T) {\n\t\tassert.Nil(t, writeExtensionCommand(t, plugin, \"debug\", \"#!/usr/bin/env bash\\necho $@\"))\n\t\tpath, err := plugin.ExtensionCommandPath(\"debug\")\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, filepath.Base(path), \"command-debug\")\n\t})\n}\n\nfunc writeExtensionCommand(t *testing.T, plugin Plugin, name, contents string) error {\n\tt.Helper()\n\tassert.Nil(t, os.MkdirAll(filepath.Join(plugin.Dir, \"lib\", \"commands\"), 0o777))\n\tfilename := \"command\"\n\tif name != \"\" {\n\t\tfilename = fmt.Sprintf(\"command-%s\", name)\n\t}\n\n\tpath := filepath.Join(plugin.Dir, \"lib\", \"commands\", filename)\n\terr := os.WriteFile(path, []byte(contents), 0o777)\n\treturn err\n}\n\nfunc TestLegacyFilenames(t *testing.T) {\n\ttestDataDir := t.TempDir()\n\tconf := config.Config{DataDir: testDataDir}\n\t_, err := repotest.InstallPlugin(\"dummy_plugin\", testDataDir, testPluginName)\n\tassert.Nil(t, err)\n\tplugin := New(conf, testPluginName)\n\n\tt.Run(\"returns list of filenames when list-legacy-filenames callback is present\", func(t *testing.T) {\n\t\tfilenames, err := plugin.LegacyFilenames()\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, filenames, []string{\".dummy-version\", \".dummyrc\"})\n\t})\n\n\tt.Run(\"returns empty list when list-legacy-filenames callback not present\", func(t *testing.T) {\n\t\ttestPluginName := \"foobar\"\n\t\t_, err := repotest.InstallPlugin(\"dummy_plugin_no_download\", testDataDir, testPluginName)\n\t\tassert.Nil(t, err)\n\t\tplugin := New(conf, testPluginName)\n\n\t\tfilenames, err := plugin.LegacyFilenames()\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, filenames, []string{})\n\t})\n}\n\nfunc TestParseLegacyVersionFile(t *testing.T) {\n\ttestDataDir := t.TempDir()\n\tconf := config.Config{DataDir: testDataDir}\n\t_, err := repotest.InstallPlugin(\"dummy_plugin\", testDataDir, testPluginName)\n\tassert.Nil(t, err)\n\tplugin := New(conf, testPluginName)\n\n\tdata := []byte(\"dummy-1.2.3\")\n\tcurrentDir := t.TempDir()\n\tpath := filepath.Join(currentDir, \".dummy-version\")\n\terr = os.WriteFile(path, data, 0o666)\n\tassert.Nil(t, err)\n\n\tt.Run(\"returns file contents unchanged when parse-legacy-file callback not present\", func(t *testing.T) {\n\t\ttestPluginName := \"foobar\"\n\t\t_, err := repotest.InstallPlugin(\"dummy_plugin_no_download\", testDataDir, testPluginName)\n\t\tassert.Nil(t, err)\n\t\tplugin := New(conf, testPluginName)\n\n\t\tversions, err := plugin.ParseLegacyVersionFile(path)\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, versions, []string{\"dummy-1.2.3\"})\n\t})\n\n\tt.Run(\"returns file contents parsed by parse-legacy-file callback when it is present\", func(t *testing.T) {\n\t\tversions, err := plugin.ParseLegacyVersionFile(path)\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, versions, []string{\"1.2.3\"})\n\t})\n\n\tt.Run(\"returns error when passed file that doesn't exist\", func(t *testing.T) {\n\t\tversions, err := plugin.ParseLegacyVersionFile(\"non-existent-file\")\n\t\tassert.Error(t, err)\n\t\tassert.Empty(t, versions)\n\t})\n}\n\nfunc touchFile(name string) error {\n\tfile, err := os.OpenFile(name, os.O_RDONLY|os.O_CREATE, 0o644)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn file.Close()\n}\n"
  },
  {
    "path": "internal/repotest/repotest.go",
    "content": "// Package repotest contains various test helpers for tests that work with code\n// relying on plugin Git repos and the asdf plugin index\n//\n// Three main actions:\n//\n// * Install plugin index repo into asdf (index contains records that point to\n// local plugins defined by this package)\n// * Install plugin into asdf data dir\n// * Create local plugin repo that can be cloned into asdf\npackage repotest\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\tcp \"github.com/otiai10/copy\"\n)\n\nconst fixturesDir = \"fixtures\"\n\n// Setup copies all files into place and initializes all repos for any Go test\n// that needs either plugin repos or the plugin index repo.\nfunc Setup(asdfDataDir string) error {\n\tif err := InstallPluginIndex(asdfDataDir); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\n// WritePluginCallback is for creating new plugin callbacks on the fly.\nfunc WritePluginCallback(pluginDir, callbackName, script string) error {\n\treturn os.WriteFile(filepath.Join(pluginDir, \"bin\", callbackName), []byte(script), 0o777)\n}\n\n// InstallPlugin copies in the specified plugin fixture into the asdfDataDir's\n// plugin directory and initializes a Git repo for it so asdf treats it as\n// installed.\nfunc InstallPlugin(fixtureName, asdfDataDir, pluginName string) (string, error) {\n\troot, err := getModuleRoot()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tdestDir := filepath.Join(asdfDataDir, \"plugins\")\n\treturn generatePluginInDir(root, fixtureName, destDir, pluginName)\n}\n\n// GeneratePlugin copies in the specified plugin fixture into a test directory\n// and initializes a Git repo for it so it can be installed by asdf.\nfunc GeneratePlugin(fixtureName, dir, pluginName string) (string, error) {\n\troot, err := getModuleRoot()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tfixturesDir := filepath.Join(dir, fixturesDir)\n\treturn generatePluginInDir(root, fixtureName, fixturesDir, pluginName)\n}\n\n// InstallPluginIndex generates and installs a plugin index Git repo inside of\n// the provided asdf data directory.\nfunc InstallPluginIndex(asdfDataDir string) error {\n\troot, err := getModuleRoot()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Copy in plugin index\n\tsource := filepath.Join(root, \"test/fixtures/dummy_plugins_repo\")\n\treturn cp.Copy(source, filepath.Join(asdfDataDir, \"plugin-index\"))\n}\n\n// GeneratePluginIndex generates a mock plugin index Git repo inside the given\n// directory.\nfunc GeneratePluginIndex(asdfDataDir string) (string, error) {\n\troot, err := getModuleRoot()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\t// Copy in plugin index\n\tsource := filepath.Join(root, \"test/fixtures/dummy_plugins_repo\")\n\tdestination := filepath.Join(asdfDataDir, fixturesDir, \"plugin-index\")\n\terr = cp.Copy(source, destination)\n\tif err != nil {\n\t\treturn destination, fmt.Errorf(\"unable to copy in plugin index: %w\", err)\n\t}\n\n\t// Generate git repo for plugin\n\treturn createGitRepo(destination)\n}\n\nfunc generatePluginInDir(root, fixtureName, outputDir, pluginName string) (string, error) {\n\t// Copy in plugin files into output dir\n\tpluginPath, err := copyInPlugin(root, fixtureName, outputDir, pluginName)\n\tif err != nil {\n\t\treturn pluginPath, fmt.Errorf(\"unable to copy in plugin files: %w\", err)\n\t}\n\n\t// Generate git repo for plugin\n\treturn createGitRepo(pluginPath)\n}\n\nfunc getModuleRoot() (string, error) {\n\tcwd, err := os.Getwd()\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"unable to get current working directory: %w\", err)\n\t}\n\n\troot := findModuleRoot(cwd)\n\treturn root, nil\n}\n\nfunc createGitRepo(location string) (string, error) {\n\t// Definitely some opportunities to refactor here. This code might be\n\t// simplified by switching to the Go git library\n\terr := runCmd(\"git\", \"-C\", location, \"init\", \"-q\", \"-b\", \"master\")\n\tif err != nil {\n\t\treturn location, err\n\t}\n\n\terr = runCmd(\"git\", \"-C\", location, \"config\", \"user.name\", \"\\\"Test\\\"\")\n\tif err != nil {\n\t\treturn location, err\n\t}\n\n\terr = runCmd(\"git\", \"-C\", location, \"config\", \"user.email\", \"\\\"test@example.com\\\"\")\n\tif err != nil {\n\t\treturn location, err\n\t}\n\n\terr = runCmd(\"git\", \"-C\", location, \"add\", \"-A\")\n\tif err != nil {\n\t\treturn location, err\n\t}\n\n\terr = runCmd(\"git\", \"-C\", location, \"commit\", \"-q\", \"-m\", \"init repo\")\n\tif err != nil {\n\t\treturn location, err\n\t}\n\n\terr = runCmd(\"touch\", filepath.Join(location, \"README.md\"))\n\tif err != nil {\n\t\treturn location, err\n\t}\n\n\terr = runCmd(\"git\", \"-C\", location, \"add\", \"-A\")\n\tif err != nil {\n\t\treturn location, err\n\t}\n\n\terr = runCmd(\"git\", \"-C\", location, \"commit\", \"-q\", \"-m\", \"add readme\")\n\tif err != nil {\n\t\treturn location, err\n\t}\n\n\t// kind of ugly but I want a remote with a valid path so I use the same\n\t// location as the remote. Probably should refactor\n\terr = runCmd(\"git\", \"-C\", location, \"remote\", \"add\", \"origin\", location)\n\tif err != nil {\n\t\treturn location, err\n\t}\n\n\treturn location, err\n}\n\nfunc copyInPlugin(root, name, destination, newName string) (string, error) {\n\tsource := filepath.Join(root, \"test/fixtures/\", name)\n\tdest := filepath.Join(destination, newName)\n\treturn dest, cp.Copy(source, dest)\n}\n\n// Taken from https://github.com/golang/go/blob/9e3b1d53a012e98cfd02de2de8b1bd53522464d4/src/cmd/go/internal/modload/init.go#L1504C1-L1522C2 because that function is in an internal module\n// and I can't rely on it.\nfunc findModuleRoot(dir string) (roots string) {\n\tif dir == \"\" {\n\t\tpanic(\"dir not set\")\n\t}\n\tdir = filepath.Clean(dir)\n\n\t// Look for enclosing go.mod.\n\tfor {\n\t\tif fi, err := os.Stat(filepath.Join(dir, \"go.mod\")); err == nil && !fi.IsDir() {\n\t\t\treturn dir\n\t\t}\n\t\td := filepath.Dir(dir)\n\t\tif d == dir {\n\t\t\tbreak\n\t\t}\n\t\tdir = d\n\t}\n\treturn \"\"\n}\n\n// helper function to make running commands easier\nfunc runCmd(cmdName string, args ...string) error {\n\tcmd := exec.Command(cmdName, args...)\n\n\t// Capture stdout and stderr\n\tvar stdout strings.Builder\n\tvar stderr strings.Builder\n\tcmd.Stdout = &stdout\n\tcmd.Stderr = &stderr\n\n\t// Global env vars\n\t// GIT_CONFIG_GLOBAL=/dev/null prevents git from looking for user settings like commit.gpgSign and user.name\n\tcmd.Env = []string{\"GIT_CONFIG_GLOBAL=/dev/null\"}\n\n\terr := cmd.Run()\n\tif err != nil {\n\t\t// If command fails print both stderr and stdout\n\t\tfmt.Println(\"stdout:\", stdout.String())\n\t\tfmt.Println(\"stderr:\", stderr.String())\n\t\treturn err\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "internal/resolve/resolve.go",
    "content": "// Package resolve contains functions for resolving a tool version in a given\n// directory. This is a core feature of asdf as asdf must be able to resolve a\n// tool version in any directory if set.\npackage resolve\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\n\t\"github.com/asdf-vm/asdf/internal/config\"\n\t\"github.com/asdf-vm/asdf/internal/plugins\"\n\t\"github.com/asdf-vm/asdf/internal/toolversions\"\n)\n\n// ToolVersions represents a tool along with versions specified for it\ntype ToolVersions struct {\n\tVersions  []string\n\tDirectory string\n\tSource    string\n}\n\n// Version takes a plugin and a directory and resolves the tool to one or more\n// versions.\nfunc Version(conf config.Config, plugin plugins.Plugin, directory string) (versions ToolVersions, found bool, err error) {\n\tversion, envVariableName, found := findVersionsInEnv(plugin.Name)\n\tif found {\n\t\treturn ToolVersions{Versions: version, Source: envVariableName}, true, nil\n\t}\n\n\tfor !found {\n\t\tversions, found, err = findVersionsInDir(conf, plugin, directory)\n\t\tif err != nil {\n\t\t\treturn versions, false, err\n\t\t}\n\n\t\tnextDir := path.Dir(directory)\n\t\t// If current dir and next dir are the same it means we've reached `/` and\n\t\t// have no more parent directories to search.\n\t\tif nextDir == directory {\n\t\t\t// If no version found, try current users home directory. I'd like to\n\t\t\t// eventually remove this feature.\n\t\t\thomeDir, osErr := os.UserHomeDir()\n\t\t\tif osErr != nil {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tversions, found, err = findVersionsInDir(conf, plugin, homeDir)\n\t\t\tbreak\n\t\t}\n\t\tdirectory = nextDir\n\t}\n\n\treturn versions, found, err\n}\n\nfunc findVersionsInDir(conf config.Config, plugin plugins.Plugin, directory string) (versions ToolVersions, found bool, err error) {\n\tfilepath := path.Join(directory, conf.DefaultToolVersionsFilename)\n\n\tif _, err = os.Stat(filepath); err == nil {\n\t\tversions, found, err := toolversions.FindToolVersions(filepath, plugin.Name)\n\t\tif found || err != nil {\n\t\t\treturn ToolVersions{Versions: versions, Source: conf.DefaultToolVersionsFilename, Directory: directory}, found, err\n\t\t}\n\t}\n\n\tlegacyFiles, err := conf.LegacyVersionFile()\n\tif err != nil {\n\t\treturn versions, found, err\n\t}\n\n\tif legacyFiles {\n\t\tversions, found, err := findVersionsInLegacyFile(plugin, directory)\n\n\t\tif found || err != nil {\n\t\t\treturn versions, found, err\n\t\t}\n\t}\n\n\treturn versions, found, nil\n}\n\n// findVersionsInEnv returns the version from the environment if present\nfunc findVersionsInEnv(pluginName string) ([]string, string, bool) {\n\tenvVariableName := variableVersionName(pluginName)\n\tversionString := os.Getenv(envVariableName)\n\tif versionString == \"\" {\n\t\treturn []string{}, envVariableName, false\n\t}\n\treturn parseVersion(versionString), envVariableName, true\n}\n\n// findVersionsInLegacyFile looks up a legacy version in the given directory if\n// the specified plugin has a list-legacy-filenames callback script. If the\n// callback script exists asdf will look for files with the given name in the\n// current and extract the version from them.\nfunc findVersionsInLegacyFile(plugin plugins.Plugin, directory string) (versions ToolVersions, found bool, err error) {\n\tvar legacyFileNames []string\n\n\tlegacyFileNames, err = plugin.LegacyFilenames()\n\tif err != nil {\n\t\treturn versions, false, err\n\t}\n\n\tfor _, filename := range legacyFileNames {\n\t\tfilepath := path.Join(directory, filename)\n\t\tif _, err := os.Stat(filepath); err == nil {\n\t\t\tversionsSlice, err := plugin.ParseLegacyVersionFile(filepath)\n\n\t\t\tif len(versionsSlice) == 0 || (len(versionsSlice) == 1 && versionsSlice[0] == \"\") {\n\t\t\t\treturn versions, false, nil\n\t\t\t}\n\t\t\treturn ToolVersions{Versions: versionsSlice, Source: filename, Directory: directory}, err == nil, err\n\t\t}\n\t}\n\n\treturn versions, found, err\n}\n\n// parseVersion parses the raw version\nfunc parseVersion(rawVersions string) []string {\n\tvar versions []string\n\tfor _, version := range strings.Split(rawVersions, \" \") {\n\t\tversion = strings.TrimSpace(version)\n\t\tif len(version) > 0 {\n\t\t\tversions = append(versions, version)\n\t\t}\n\t}\n\treturn versions\n}\n\nfunc variableVersionName(toolName string) string {\n\treturn fmt.Sprintf(\"ASDF_%s_VERSION\", strings.ToUpper(toolName))\n}\n"
  },
  {
    "path": "internal/resolve/resolve_test.go",
    "content": "package resolve\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/asdf-vm/asdf/internal/config\"\n\t\"github.com/asdf-vm/asdf/internal/plugins\"\n\t\"github.com/asdf-vm/asdf/internal/repotest\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nconst testPluginName = \"test-plugin\"\n\nfunc TestVersion(t *testing.T) {\n\ttestDataDir := t.TempDir()\n\tcurrentDir := t.TempDir()\n\tconf := config.Config{DataDir: testDataDir, DefaultToolVersionsFilename: \".tool-versions\", ConfigFile: \"testdata/asdfrc\"}\n\t_, err := repotest.InstallPlugin(\"dummy_plugin\", conf.DataDir, testPluginName)\n\tassert.Nil(t, err)\n\tplugin := plugins.New(conf, testPluginName)\n\n\tt.Run(\"returns empty slice when non-existent version passed\", func(t *testing.T) {\n\t\ttoolVersion, found, err := Version(conf, plugin, t.TempDir())\n\t\tassert.Nil(t, err)\n\t\tassert.False(t, found)\n\t\tassert.Empty(t, toolVersion.Versions)\n\t})\n\n\tt.Run(\"returns single version from .tool-versions file\", func(t *testing.T) {\n\t\t// write a version file\n\t\tdata := []byte(fmt.Sprintf(\"%s 1.2.3\", testPluginName))\n\t\terr = os.WriteFile(filepath.Join(currentDir, \".tool-versions\"), data, 0o666)\n\n\t\ttoolVersion, found, err := Version(conf, plugin, currentDir)\n\t\tassert.Nil(t, err)\n\t\tassert.True(t, found)\n\t\tassert.Equal(t, toolVersion.Versions, []string{\"1.2.3\"})\n\t})\n\n\tt.Run(\"returns version from env when env variable set\", func(t *testing.T) {\n\t\t// Set env\n\t\tt.Setenv(fmt.Sprintf(\"ASDF_%s_VERSION\", strings.ToUpper(testPluginName)), \"2.3.4\")\n\n\t\t// write a version file\n\t\tdata := []byte(fmt.Sprintf(\"%s 1.2.3\", testPluginName))\n\t\terr = os.WriteFile(filepath.Join(currentDir, \".tool-versions\"), data, 0o666)\n\n\t\t// assert env variable takes precedence\n\t\ttoolVersion, found, err := Version(conf, plugin, currentDir)\n\t\tassert.Nil(t, err)\n\t\tassert.True(t, found)\n\t\tassert.Equal(t, toolVersion.Versions, []string{\"2.3.4\"})\n\t})\n\n\tt.Run(\"returns single version from .tool-versions file in parent directory\", func(t *testing.T) {\n\t\t// write a version file\n\t\tdata := []byte(fmt.Sprintf(\"%s 1.2.3\", testPluginName))\n\t\terr = os.WriteFile(filepath.Join(currentDir, \".tool-versions\"), data, 0o666)\n\n\t\tsubDir := filepath.Join(currentDir, \"subdir\")\n\t\terr = os.MkdirAll(subDir, 0o777)\n\t\tassert.Nil(t, err)\n\n\t\ttoolVersion, found, err := Version(conf, plugin, subDir)\n\t\tassert.Nil(t, err)\n\t\tassert.True(t, found)\n\t\tassert.Equal(t, toolVersion.Versions, []string{\"1.2.3\"})\n\t})\n}\n\nfunc TestFindVersionsInDir(t *testing.T) {\n\ttestDataDir := t.TempDir()\n\tconf := config.Config{DataDir: testDataDir, DefaultToolVersionsFilename: \".tool-versions\", ConfigFile: \"testdata/asdfrc\"}\n\t_, err := repotest.InstallPlugin(\"dummy_plugin\", conf.DataDir, testPluginName)\n\tassert.Nil(t, err)\n\tplugin := plugins.New(conf, testPluginName)\n\n\tt.Run(\"when no versions set returns found false\", func(t *testing.T) {\n\t\tcurrentDir := t.TempDir()\n\n\t\tversions, found, err := findVersionsInDir(conf, plugin, currentDir)\n\n\t\tassert.Empty(t, versions)\n\t\tassert.False(t, found)\n\t\tassert.Nil(t, err)\n\t})\n\n\tt.Run(\"when version is set returns found true and version\", func(t *testing.T) {\n\t\tcurrentDir := t.TempDir()\n\n\t\tdata := []byte(fmt.Sprintf(\"%s 1.2.3\", testPluginName))\n\t\terr = os.WriteFile(filepath.Join(currentDir, \".tool-versions\"), data, 0o666)\n\n\t\ttoolVersion, found, err := findVersionsInDir(conf, plugin, currentDir)\n\n\t\tassert.Equal(t, toolVersion.Versions, []string{\"1.2.3\"})\n\t\tassert.True(t, found)\n\t\tassert.Nil(t, err)\n\t})\n\n\tt.Run(\"when multiple versions present in .tool-versions returns found true and versions\", func(t *testing.T) {\n\t\tcurrentDir := t.TempDir()\n\n\t\tdata := []byte(fmt.Sprintf(\"%s 1.2.3 2.3.4\", testPluginName))\n\t\terr = os.WriteFile(filepath.Join(currentDir, \".tool-versions\"), data, 0o666)\n\n\t\ttoolVersion, found, err := findVersionsInDir(conf, plugin, currentDir)\n\n\t\tassert.Equal(t, toolVersion.Versions, []string{\"1.2.3\", \"2.3.4\"})\n\t\tassert.True(t, found)\n\t\tassert.Nil(t, err)\n\t})\n\n\tt.Run(\"when DefaultToolVersionsFilename is set reads from file with that name if exists\", func(t *testing.T) {\n\t\tconf := config.Config{DataDir: testDataDir, DefaultToolVersionsFilename: \"custom-file\"}\n\t\tcurrentDir := t.TempDir()\n\n\t\tdata := []byte(fmt.Sprintf(\"%s 1.2.3 2.3.4\", testPluginName))\n\t\terr = os.WriteFile(filepath.Join(currentDir, \"custom-file\"), data, 0o666)\n\n\t\ttoolVersion, found, err := findVersionsInDir(conf, plugin, currentDir)\n\n\t\tassert.Equal(t, toolVersion.Versions, []string{\"1.2.3\", \"2.3.4\"})\n\t\tassert.True(t, found)\n\t\tassert.Nil(t, err)\n\t})\n\n\tt.Run(\"when .tool-version exists and legacy file support is on looks up version in .tool-versions\", func(t *testing.T) {\n\t\tcurrentDir := t.TempDir()\n\n\t\tdata := []byte(fmt.Sprintf(\"%s 1.2.3\", testPluginName))\n\t\terr = os.WriteFile(filepath.Join(currentDir, \".tool-versions\"), data, 0o666)\n\t\tassert.NoError(t, err)\n\n\t\tdata = []byte(\"2.3.4 3.4.5\")\n\t\terr = os.WriteFile(filepath.Join(currentDir, \".dummy-version\"), data, 0o666)\n\t\tassert.NoError(t, err)\n\n\t\ttoolVersion, found, err := findVersionsInDir(conf, plugin, currentDir)\n\t\tassert.Equal(t, toolVersion.Versions, []string{\"1.2.3\"})\n\t\tassert.True(t, found)\n\t\tassert.NoError(t, err)\n\t})\n\n\tt.Run(\"when .tool-version does not exist and legacy file support is on looks up version in legacy file\", func(t *testing.T) {\n\t\tcurrentDir := t.TempDir()\n\n\t\tdata := []byte(\"1.2.3 2.3.4\")\n\t\terr = os.WriteFile(filepath.Join(currentDir, \".dummy-version\"), data, 0o666)\n\t\tassert.NoError(t, err)\n\n\t\ttoolVersion, found, err := findVersionsInDir(conf, plugin, currentDir)\n\t\tassert.Equal(t, toolVersion.Versions, []string{\"1.2.3\", \"2.3.4\"})\n\t\tassert.True(t, found)\n\t\tassert.NoError(t, err)\n\t})\n}\n\nfunc TestFindVersionsLegacyFiles(t *testing.T) {\n\ttestDataDir := t.TempDir()\n\tconf := config.Config{DataDir: testDataDir}\n\t_, err := repotest.InstallPlugin(\"dummy_plugin\", conf.DataDir, testPluginName)\n\tassert.Nil(t, err)\n\tplugin := plugins.New(conf, testPluginName)\n\n\tt.Run(\"when given tool that lacks list-legacy-filenames callback returns empty versions list\", func(t *testing.T) {\n\t\tpluginName := \"foobar\"\n\t\t_, err := repotest.InstallPlugin(\"dummy_plugin_no_download\", conf.DataDir, pluginName)\n\t\tassert.Nil(t, err)\n\t\tplugin := plugins.New(conf, pluginName)\n\t\ttoolVersion, found, err := findVersionsInLegacyFile(plugin, t.TempDir())\n\t\tassert.Empty(t, toolVersion.Versions)\n\t\tassert.False(t, found)\n\t\tassert.Nil(t, err)\n\t})\n\n\tt.Run(\"when given tool that has a list-legacy-filenames callback but file not found returns empty versions list\", func(t *testing.T) {\n\t\ttoolVersion, found, err := findVersionsInLegacyFile(plugin, t.TempDir())\n\t\tassert.Empty(t, toolVersion.Versions)\n\t\tassert.False(t, found)\n\t\tassert.Nil(t, err)\n\t})\n\n\tt.Run(\"when given tool that has a list-legacy-filenames callback and file found returns populated versions list\", func(t *testing.T) {\n\t\t// write legacy version file\n\t\tcurrentDir := t.TempDir()\n\t\tdata := []byte(\"1.2.3\")\n\t\terr = os.WriteFile(filepath.Join(currentDir, \".dummy-version\"), data, 0o666)\n\t\tassert.Nil(t, err)\n\n\t\ttoolVersion, found, err := findVersionsInLegacyFile(plugin, currentDir)\n\t\tassert.Equal(t, toolVersion.Versions, []string{\"1.2.3\"})\n\t\tassert.True(t, found)\n\t\tassert.Nil(t, err)\n\t})\n}\n\nfunc TestFindVersionsInEnv(t *testing.T) {\n\tt.Run(\"when env variable isn't set returns empty list of versions\", func(t *testing.T) {\n\t\tversions, envVariableName, found := findVersionsInEnv(\"non-existent\")\n\t\tassert.False(t, found)\n\t\tassert.Empty(t, versions)\n\t\tassert.Equal(t, envVariableName, \"ASDF_NON-EXISTENT_VERSION\")\n\t})\n\n\tt.Run(\"when env variable is set returns version\", func(t *testing.T) {\n\t\tos.Setenv(\"ASDF_LUA_VERSION\", \"5.4.5\")\n\t\tversions, envVariableName, found := findVersionsInEnv(\"lua\")\n\t\tassert.True(t, found)\n\t\tassert.Equal(t, versions, []string{\"5.4.5\"})\n\t\tassert.Equal(t, envVariableName, \"ASDF_LUA_VERSION\")\n\n\t\tos.Unsetenv(\"ASDF_LUA_VERSION\")\n\t})\n\n\tt.Run(\"when env variable is set to multiple versions\", func(t *testing.T) {\n\t\tos.Setenv(\"ASDF_LUA_VERSION\", \"5.4.5 5.4.6\")\n\t\tversions, envVariableName, found := findVersionsInEnv(\"lua\")\n\t\tassert.True(t, found)\n\t\tassert.Equal(t, versions, []string{\"5.4.5\", \"5.4.6\"})\n\t\tassert.Equal(t, envVariableName, \"ASDF_LUA_VERSION\")\n\t\tos.Unsetenv(\"ASDF_LUA_VERSION\")\n\t})\n}\n\nfunc TestVariableVersionName(t *testing.T) {\n\ttests := []struct {\n\t\tinput  string\n\t\toutput string\n\t}{\n\t\t{\n\t\t\tinput:  \"ruby\",\n\t\t\toutput: \"ASDF_RUBY_VERSION\",\n\t\t},\n\t\t{\n\t\t\tinput:  \"lua\",\n\t\t\toutput: \"ASDF_LUA_VERSION\",\n\t\t},\n\t\t{\n\t\t\tinput:  \"foo-bar\",\n\t\t\toutput: \"ASDF_FOO-BAR_VERSION\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(fmt.Sprintf(\"input: %s, output: %s\", tt.input, tt.output), func(t *testing.T) {\n\t\t\tassert.Equal(t, tt.output, variableVersionName(tt.input))\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "internal/resolve/testdata/asdfrc",
    "content": "legacy_version_file = yes\n"
  },
  {
    "path": "internal/shims/shims.go",
    "content": "// Package shims manages writing and parsing of asdf shim scripts.\npackage shims\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"io/fs\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"slices\"\n\t\"strings\"\n\n\t\"github.com/asdf-vm/asdf/internal/config\"\n\t\"github.com/asdf-vm/asdf/internal/hook\"\n\t\"github.com/asdf-vm/asdf/internal/installs\"\n\t\"github.com/asdf-vm/asdf/internal/paths\"\n\t\"github.com/asdf-vm/asdf/internal/plugins\"\n\t\"github.com/asdf-vm/asdf/internal/resolve\"\n\t\"github.com/asdf-vm/asdf/internal/toolversions\"\n\t\"golang.org/x/sys/unix\"\n)\n\nconst shimDirName = \"shims\"\n\n// UnknownCommandError is an error returned when a shim is not found\ntype UnknownCommandError struct {\n\tshim string\n}\n\nfunc (e UnknownCommandError) Error() string {\n\treturn fmt.Sprintf(\"unknown command: %s\", e.shim)\n}\n\n// NoVersionSetError is returned when shim is found but no version matches\ntype NoVersionSetError struct {\n\tshim string\n}\n\nfunc (e NoVersionSetError) Error() string {\n\treturn fmt.Sprintf(\"no versions set for %s\", e.shim)\n}\n\n// NoExecutableForPluginError is returned when a compatible version is found\n// but no executable matching the name is located.\ntype NoExecutableForPluginError struct {\n\tshim     string\n\ttools    []string\n\tversions []string\n}\n\nfunc (e NoExecutableForPluginError) Error() string {\n\treturn fmt.Sprintf(\"No %s executable found for %s %s\", e.shim, strings.Join(e.tools, \", \"), strings.Join(e.versions, \", \"))\n}\n\n// FindExecutable takes a shim name and a current directory and returns the path\n// to the executable that the shim resolves to.\nfunc FindExecutable(conf config.Config, shimName, currentDirectory string) (path string, plugin plugins.Plugin, version string, found bool, err error) {\n\tshimPath := Path(conf, shimName)\n\n\tif _, err := os.Stat(shimPath); err != nil {\n\t\treturn \"\", plugins.Plugin{}, \"\", false, UnknownCommandError{shim: shimName}\n\t}\n\n\ttoolVersions, err := GetToolsAndVersionsFromShimFile(shimPath)\n\tif err != nil {\n\t\treturn \"\", plugins.Plugin{}, \"\", false, err\n\t}\n\n\texistingPluginToolVersions := make(map[plugins.Plugin]resolve.ToolVersions)\n\n\t// loop over tools and check if the plugin for them still exists\n\tfor _, shimToolVersion := range toolVersions {\n\t\tplugin := plugins.New(conf, shimToolVersion.Name)\n\t\tif plugin.Exists() == nil {\n\t\t\t// If a shim template is found, we can return it before looping through versions\n\t\t\tshimTemplate, err := plugin.ShimTemplatePath(shimName)\n\t\t\tif err == nil {\n\t\t\t\treturn shimTemplate, plugin, \"\", true, nil\n\t\t\t}\n\n\t\t\tversions, found, err := resolve.Version(conf, plugin, currentDirectory)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", plugins.Plugin{}, \"\", false, nil\n\t\t\t}\n\n\t\t\tif found {\n\t\t\t\ttempVersions := toolversions.Intersect(versions.Versions, shimToolVersion.Versions)\n\n\t\t\t\tif slices.Contains(versions.Versions, \"system\") {\n\t\t\t\t\ttempVersions = append(tempVersions, \"system\")\n\t\t\t\t}\n\n\t\t\t\tparsedVersions := toolversions.ParseSlice(versions.Versions)\n\t\t\t\tfor _, parsedVersion := range parsedVersions {\n\t\t\t\t\tif parsedVersion.Type == \"path\" {\n\t\t\t\t\t\ttempVersions = append(tempVersions, toolversions.Format(parsedVersion))\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tversions.Versions = tempVersions\n\t\t\t\tif len(versions.Versions) > 0 {\n\t\t\t\t\texistingPluginToolVersions[plugin] = versions\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif len(existingPluginToolVersions) == 0 {\n\t\treturn \"\", plugins.Plugin{}, \"\", false, NoVersionSetError{shim: shimName}\n\t}\n\n\tfor plugin, toolVersions := range existingPluginToolVersions {\n\t\tfor _, version := range toolVersions.Versions {\n\t\t\tparsedVersion := toolversions.Parse(version)\n\t\t\tif parsedVersion.Type == \"system\" {\n\t\t\t\tif executablePath, found := SystemExecutableOnPath(conf, shimName); found {\n\t\t\t\t\treturn executablePath, plugin, version, true, nil\n\t\t\t\t}\n\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tif parsedVersion.Type == \"path\" {\n\t\t\t\texecutablePath, err := GetExecutablePath(conf, plugin, shimName, parsedVersion)\n\t\t\t\tif err == nil {\n\t\t\t\t\treturn executablePath, plugin, version, true, nil\n\t\t\t\t}\n\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\texecutablePath, err := GetExecutablePath(conf, plugin, shimName, parsedVersion)\n\t\t\tif err == nil {\n\t\t\t\treturn executablePath, plugin, version, true, nil\n\t\t\t}\n\t\t}\n\t}\n\n\ttools := []string{}\n\tversions := []string{}\n\tfor plugin, toolVersions := range existingPluginToolVersions {\n\t\ttools = append(tools, plugin.Name)\n\t\tversions = append(versions, toolVersions.Versions...)\n\t}\n\n\treturn \"\", plugins.Plugin{}, \"\", false, NoExecutableForPluginError{shim: shimName, tools: tools, versions: versions}\n}\n\n// SystemExecutableOnPath returns the path to the system executable if found,\n// removes asdf shim directory from search\nfunc SystemExecutableOnPath(conf config.Config, executableName string) (string, bool) {\n\tcurrentPath := os.Getenv(\"PATH\")\n\texecutablePath, err := ExecutableOnPath(paths.RemoveFromPath(currentPath, Directory(conf)), executableName)\n\treturn executablePath, err == nil\n}\n\n// ExecutableOnPath returns the path to an executable if one is found on the\n// provided paths. `path` must be in the same format as the `PATH` environment\n// variable.\nfunc ExecutableOnPath(path, command string) (string, error) {\n\tcurrentPath := os.Getenv(\"PATH\")\n\tdefer os.Setenv(\"PATH\", currentPath)\n\tos.Setenv(\"PATH\", path)\n\treturn exec.LookPath(command)\n}\n\n// GetExecutablePath returns the path of the executable\nfunc GetExecutablePath(conf config.Config, plugin plugins.Plugin, shimName string, version toolversions.Version) (string, error) {\n\texecutables, err := ToolExecutables(conf, plugin, version)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\texecutable := \"\"\n\n\tfor _, executablePath := range executables {\n\t\tif filepath.Base(executablePath) == shimName {\n\t\t\texecutable = executablePath\n\t\t}\n\t}\n\n\tpath, err := getCustomExecutablePath(conf, plugin, shimName, version, executable)\n\tif err == nil {\n\t\treturn path, err\n\t}\n\n\tif executable != \"\" {\n\t\treturn executable, nil\n\t}\n\n\treturn \"\", fmt.Errorf(\"executable not found\")\n}\n\n// GetToolsAndVersionsFromShimFile takes a file path and parses out the tools\n// and versions present in it and returns them as a slice containing info in\n// ToolVersions structs.\nfunc GetToolsAndVersionsFromShimFile(shimPath string) (versions []toolversions.ToolVersions, err error) {\n\tcontents, err := os.ReadFile(shimPath)\n\tif err != nil {\n\t\treturn versions, err\n\t}\n\n\tversions = parse(string(contents))\n\tversions = toolversions.Unique(versions)\n\n\treturn versions, err\n}\n\nfunc getCustomExecutablePath(conf config.Config, plugin plugins.Plugin, shimName string, version toolversions.Version, executablePath string) (string, error) {\n\tvar stdOut strings.Builder\n\tvar stdErr strings.Builder\n\n\tinstallPath := installs.InstallPath(conf, plugin, version)\n\tenv := map[string]string{\"ASDF_INSTALL_TYPE\": \"version\"}\n\n\trelativePath, err := filepath.Rel(installPath, executablePath)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\terr = plugin.RunCallback(\"exec-path\", []string{installPath, shimName, relativePath}, env, &stdOut, &stdErr)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn filepath.Join(installPath, strings.TrimSpace(stdOut.String())), err\n}\n\n// RemoveAll removes all shim scripts\nfunc RemoveAll(conf config.Config) error {\n\tshimDir := filepath.Join(conf.DataDir, shimDirName)\n\tentries, err := os.ReadDir(shimDir)\n\tif err != nil {\n\t\tif _, ok := err.(*fs.PathError); ok {\n\t\t\t// if directory doesn't exist we can just return because no shims can\n\t\t\t// possibly exist.\n\t\t\treturn nil\n\t\t}\n\t\treturn err\n\t}\n\n\tfor _, entry := range entries {\n\t\tos.RemoveAll(path.Join(shimDir, entry.Name()))\n\t}\n\n\treturn nil\n}\n\n// GenerateAll generates shims for all executables of every version of every\n// plugin.\nfunc GenerateAll(conf config.Config, stdOut io.Writer, stdErr io.Writer) error {\n\tplugins, err := plugins.List(conf, false, false)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, plugin := range plugins {\n\t\terr := GenerateForPluginVersions(conf, plugin, stdOut, stdErr)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// GenerateForPluginVersions generates all shims for all installed versions of\n// a tool.\nfunc GenerateForPluginVersions(conf config.Config, plugin plugins.Plugin, stdOut io.Writer, stdErr io.Writer) error {\n\tinstalledVersions, err := installs.Installed(conf, plugin)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, version := range installedVersions {\n\t\tparsedVersion := toolversions.Parse(version)\n\t\tGenerateForVersion(conf, plugin, parsedVersion, stdOut, stdErr)\n\t}\n\treturn nil\n}\n\n// GenerateForVersion loops over all the executable files found for a tool and\n// generates a shim for each one\nfunc GenerateForVersion(conf config.Config, plugin plugins.Plugin, version toolversions.Version, stdOut io.Writer, stdErr io.Writer) error {\n\terr := hook.RunWithOutput(conf, fmt.Sprintf(\"pre_asdf_reshim_%s\", plugin.Name), []string{toolversions.Format(version)}, stdOut, stdErr)\n\tif err != nil {\n\t\treturn err\n\t}\n\texecutables, err := ToolExecutables(conf, plugin, version)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, executablePath := range executables {\n\t\terr := Write(conf, plugin, version, executablePath)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\terr = hook.RunWithOutput(conf, fmt.Sprintf(\"post_asdf_reshim_%s\", plugin.Name), []string{toolversions.Format(version)}, stdOut, stdErr)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// Write generates a shim script and writes it to disk\nfunc Write(conf config.Config, plugin plugins.Plugin, version toolversions.Version, executablePath string) error {\n\terr := ensureShimDirExists(conf)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tshimName := filepath.Base(executablePath)\n\tshimPath := Path(conf, shimName)\n\tversions := []toolversions.ToolVersions{{Name: plugin.Name, Versions: []string{toolversions.Format(version)}}}\n\n\tif _, err := os.Stat(shimPath); err == nil {\n\t\toldVersions, err := GetToolsAndVersionsFromShimFile(shimPath)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tversions = toolversions.Unique(append(versions, oldVersions...))\n\t}\n\n\treturn os.WriteFile(shimPath, []byte(encode(shimName, versions)), 0o777)\n}\n\n// Path returns the path for a shim script\nfunc Path(conf config.Config, shimName string) string {\n\treturn filepath.Join(conf.DataDir, shimDirName, shimName)\n}\n\n// Directory returns the path to the shims directory for the current\n// configuration.\nfunc Directory(conf config.Config) string {\n\treturn filepath.Join(conf.DataDir, shimDirName)\n}\n\nfunc ensureShimDirExists(conf config.Config) error {\n\treturn os.MkdirAll(filepath.Join(conf.DataDir, shimDirName), 0o777)\n}\n\n// ToolExecutables returns a slice of executables for a given tool version\nfunc ToolExecutables(conf config.Config, plugin plugins.Plugin, version toolversions.Version) (executables []string, err error) {\n\tpaths, err := ExecutablePaths(conf, plugin, version)\n\tif err != nil {\n\t\treturn []string{}, err\n\t}\n\n\tfor _, path := range paths {\n\t\tentries, err := os.ReadDir(path)\n\t\tif _, ok := err.(*os.PathError); err != nil && !ok {\n\t\t\treturn executables, err\n\t\t}\n\n\t\tfor _, entry := range entries {\n\t\t\t// If entry is dir or cannot be executed by the current user ignore it\n\t\t\tfilePath := filepath.Join(path, entry.Name())\n\t\t\tif entry.IsDir() || unix.Access(filePath, unix.X_OK) != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\texecutables = append(executables, filePath)\n\t\t}\n\t}\n\treturn executables, err\n}\n\n// ExecutablePaths returns a slice of absolute directory paths that tool\n// executables are contained in.\nfunc ExecutablePaths(conf config.Config, plugin plugins.Plugin, version toolversions.Version) (paths []string, err error) {\n\tdirs, err := ExecutableDirs(plugin)\n\tif err != nil {\n\t\treturn []string{}, err\n\t}\n\n\tshimsDir, err := os.Stat(path.Join(plugin.Dir, \"shims\"))\n\tif err == nil && shimsDir.IsDir() {\n\t\tpaths = append(paths, path.Join(plugin.Dir, \"shims\"))\n\t}\n\n\tinstallPath := installs.InstallPath(conf, plugin, version)\n\treturn append(paths, dirsToPaths(dirs, installPath)...), nil\n}\n\n// ExecutableDirs returns a slice of relative directory names that tool executables are\n// contained in, *inside installs*\nfunc ExecutableDirs(plugin plugins.Plugin) ([]string, error) {\n\tvar stdOut strings.Builder\n\tvar stdErr strings.Builder\n\n\terr := plugin.RunCallback(\"list-bin-paths\", []string{}, map[string]string{}, &stdOut, &stdErr)\n\tif err != nil {\n\t\tif _, ok := err.(plugins.NoCallbackError); ok {\n\t\t\t// assume all executables are located in /bin directory\n\t\t\treturn []string{\"bin\"}, nil\n\t\t}\n\n\t\treturn []string{}, err\n\t}\n\n\t// use output from list-bin-paths to determine locations for executables\n\trawDirs := strings.Split(stdOut.String(), \" \")\n\tvar dirs []string\n\n\tfor _, dir := range rawDirs {\n\t\tdirs = append(dirs, strings.TrimSpace(dir))\n\t}\n\n\treturn dirs, nil\n}\n\nfunc parse(contents string) (versions []toolversions.ToolVersions) {\n\tlines := strings.Split(contents, \"\\n\")\n\n\tfor _, line := range lines {\n\t\tif strings.HasPrefix(line, \"# asdf-plugin:\") {\n\t\t\tsegments := strings.Split(line, \" \")\n\t\t\t// if doesn't have expected number of elements on line skip\n\t\t\tif len(segments) >= 4 {\n\t\t\t\tversions = append(versions, toolversions.ToolVersions{Name: segments[2], Versions: []string{segments[3]}})\n\t\t\t}\n\t\t}\n\t}\n\treturn versions\n}\n\nfunc encode(shimName string, toolVersions []toolversions.ToolVersions) string {\n\tvar content string\n\n\tcontent = \"#!/usr/bin/env bash\\n\"\n\n\t// Add all asdf-plugin comments\n\tfor _, tool := range toolVersions {\n\t\tfor _, version := range tool.Versions {\n\t\t\tcontent += fmt.Sprintf(\"# asdf-plugin: %s %s\\n\", tool.Name, version)\n\t\t}\n\t}\n\n\t// Add call asdf exec to actually run real command\n\tcontent += fmt.Sprintf(\"exec asdf exec \\\"%s\\\" \\\"$@\\\"\", shimName)\n\n\treturn content\n}\n\nfunc dirsToPaths(dirs []string, root string) (paths []string) {\n\tfor _, dir := range dirs {\n\t\tpaths = append(paths, filepath.Join(root, dir))\n\t}\n\n\treturn paths\n}\n"
  },
  {
    "path": "internal/shims/shims_test.go",
    "content": "package shims\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/asdf-vm/asdf/internal/config\"\n\t\"github.com/asdf-vm/asdf/internal/installs\"\n\t\"github.com/asdf-vm/asdf/internal/installtest\"\n\t\"github.com/asdf-vm/asdf/internal/plugins\"\n\t\"github.com/asdf-vm/asdf/internal/repotest\"\n\t\"github.com/asdf-vm/asdf/internal/toolversions\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"golang.org/x/sys/unix\"\n)\n\nconst testPluginName = \"lua\"\n\nfunc TestFindExecutable(t *testing.T) {\n\tversion := \"1.1.0\"\n\tconf, plugin := generateConfig(t)\n\tinstallVersion(t, conf, plugin, version)\n\tinstallVersion(t, conf, plugin, \"2.0.0\")\n\tstdout, stderr := buildOutputs()\n\tassert.Nil(t, GenerateAll(conf, &stdout, &stderr))\n\tcurrentDir := t.TempDir()\n\n\tt.Run(\"returns error when shim with name does not exist\", func(t *testing.T) {\n\t\texecutable, _, version, found, err := FindExecutable(conf, \"foo\", currentDir)\n\t\tassert.Empty(t, executable)\n\t\tassert.False(t, found)\n\t\tassert.Empty(t, version)\n\t\tassert.Equal(t, err.(UnknownCommandError).Error(), \"unknown command: foo\")\n\t})\n\n\tt.Run(\"returns error when shim is present but no version is set\", func(t *testing.T) {\n\t\texecutable, _, version, found, err := FindExecutable(conf, \"dummy\", currentDir)\n\t\tassert.Empty(t, executable)\n\t\tassert.False(t, found)\n\t\tassert.Empty(t, version)\n\t\tassert.Equal(t, err.(NoVersionSetError).Error(), \"no versions set for dummy\")\n\t})\n\n\tt.Run(\"returns string containing path to executable when found\", func(t *testing.T) {\n\t\t// write a version file\n\t\tdata := []byte(\"lua 1.1.0\")\n\t\tassert.Nil(t, os.WriteFile(filepath.Join(currentDir, \".tool-versions\"), data, 0o666))\n\n\t\texecutable, gotPlugin, version, found, err := FindExecutable(conf, \"dummy\", currentDir)\n\t\tassert.Equal(t, filepath.Base(filepath.Dir(filepath.Dir(executable))), \"1.1.0\")\n\t\tassert.Equal(t, filepath.Base(executable), \"dummy\")\n\t\tassert.Equal(t, plugin, gotPlugin)\n\t\tassert.Equal(t, version, \"1.1.0\")\n\t\tassert.True(t, found)\n\t\tassert.Nil(t, err)\n\t})\n\n\tt.Run(\"returns path to executable with first version when multiple versions are set\", func(t *testing.T) {\n\t\t// write a version file\n\t\tdata := []byte(\"lua 1.1.0 3.0.0 2.0.0\")\n\t\tassert.Nil(t, os.WriteFile(filepath.Join(currentDir, \".tool-versions\"), data, 0o666))\n\n\t\texecutable, gotPlugin, version, found, err := FindExecutable(conf, \"dummy\", currentDir)\n\t\tassert.Equal(t, filepath.Base(filepath.Dir(filepath.Dir(executable))), \"1.1.0\")\n\t\tassert.Equal(t, filepath.Base(executable), \"dummy\")\n\t\tassert.Equal(t, plugin, gotPlugin)\n\t\tassert.Equal(t, version, \"1.1.0\")\n\t\tassert.True(t, found)\n\t\tassert.Nil(t, err)\n\t})\n\n\tt.Run(\"returns string containing path to system executable when system version set\", func(t *testing.T) {\n\t\t// Create dummy `ls` executable\n\t\tversionStruct := toolversions.Version{Type: \"version\", Value: version}\n\t\tpath := filepath.Join(installs.InstallPath(conf, plugin, versionStruct), \"bin\", \"ls\")\n\t\tassert.Nil(t, os.WriteFile(path, []byte(\"echo 'I'm ls'\"), 0o777))\n\n\t\t// write system version to version file\n\t\ttoolpath := filepath.Join(currentDir, \".tool-versions\")\n\t\tassert.Nil(t, os.WriteFile(toolpath, []byte(\"lua system\\n\"), 0o666))\n\t\tassert.Nil(t, GenerateAll(conf, &stdout, &stderr))\n\n\t\texecutable, gotPlugin, version, found, err := FindExecutable(conf, \"ls\", currentDir)\n\t\tassert.Equal(t, plugin, gotPlugin)\n\t\tassert.Equal(t, version, \"system\")\n\t\tassert.True(t, found)\n\t\tassert.Nil(t, err)\n\n\t\t// see that it actually returns path to system ls\n\t\tassert.Equal(t, filepath.Base(executable), \"ls\")\n\t\tassert.NotEqual(t, executable, path)\n\t})\n\n\tt.Run(\"returns path to executable on path when path version set\", func(t *testing.T) {\n\t\t// write system version to version file\n\t\ttoolpath := filepath.Join(currentDir, \".tool-versions\")\n\t\tdir := installs.InstallPath(conf, plugin, toolversions.Version{Type: \"version\", Value: \"1.1.0\"})\n\t\tpathVersion := fmt.Sprintf(\"path:%s/./\", dir)\n\t\tassert.Nil(t, os.WriteFile(toolpath, []byte(fmt.Sprintf(\"lua %s\\n\", pathVersion)), 0o666))\n\t\tassert.Nil(t, GenerateAll(conf, &stdout, &stderr))\n\n\t\texecutable, gotPlugin, version, found, err := FindExecutable(conf, \"dummy\", currentDir)\n\t\tassert.Equal(t, plugin, gotPlugin)\n\t\tassert.Equal(t, version, pathVersion)\n\t\tassert.True(t, found)\n\t\tassert.Nil(t, err)\n\n\t\t// see that it actually returns path to system ls\n\t\tassert.Equal(t, filepath.Base(executable), \"dummy\")\n\t\tassert.True(t, strings.HasPrefix(executable, dir))\n\t})\n\n\tt.Run(\"returns string containing path to executable when shim template in plugin is set\", func(t *testing.T) {\n\t\t// write a version file\n\t\tdata := []byte(\"lua 1.1.0\")\n\t\tassert.Nil(t, os.WriteFile(filepath.Join(currentDir, \".tool-versions\"), data, 0o666))\n\n\t\t// write a shim template to the plugin shims dir\n\t\tsetupShimTemplate(t, plugin, \"dummy\", \"echo 'shim template'\")\n\n\t\texecutable, gotPlugin, version, found, err := FindExecutable(conf, \"dummy\", currentDir)\n\t\tassert.Nil(t, err)\n\n\t\trelativePath, err := filepath.Rel(conf.DataDir, executable)\n\t\tassert.Nil(t, err)\n\n\t\tassert.Equal(t, \"plugins/lua/shims/dummy\", relativePath)\n\t\tassert.Equal(t, \"dummy\", filepath.Base(executable))\n\t\tassert.Equal(t, plugin, gotPlugin)\n\t\tassert.Equal(t, \"\", version)\n\t\tassert.True(t, found)\n\t})\n}\n\nfunc TestFindExecutable_Ref(t *testing.T) {\n\tversion := \"ref:v1.1.0\"\n\tconf, plugin := generateConfig(t)\n\tinstallVersion(t, conf, plugin, version)\n\tstdout, stderr := buildOutputs()\n\tassert.Nil(t, GenerateAll(conf, &stdout, &stderr))\n\tcurrentDir := t.TempDir()\n\n\tt.Run(\"returns string containing path to ref installed executable when found\", func(t *testing.T) {\n\t\t// write a version file\n\t\tdata := []byte(\"lua ref:v1.1.0\")\n\t\tassert.Nil(t, os.WriteFile(filepath.Join(currentDir, \".tool-versions\"), data, 0o666))\n\n\t\texecutable, gotPlugin, version, found, err := FindExecutable(conf, \"dummy\", currentDir)\n\t\tassert.Nil(t, err)\n\t\tassert.True(t, found)\n\t\tassert.Equal(t, \"ref:v1.1.0\", version)\n\t\tassert.Equal(t, plugin, gotPlugin)\n\t\tassert.Equal(t, \"dummy\", filepath.Base(executable))\n\t\tassert.Equal(t, \"ref-v1.1.0\", filepath.Base(filepath.Dir(filepath.Dir(executable))))\n\t})\n}\n\nfunc TestGetExecutablePath(t *testing.T) {\n\tversion := toolversions.Version{Type: \"version\", Value: \"1.1.0\"}\n\tconf, plugin := generateConfig(t)\n\tinstallVersion(t, conf, plugin, version.Value)\n\n\tt.Run(\"returns path to executable\", func(t *testing.T) {\n\t\tpath, err := GetExecutablePath(conf, plugin, \"dummy\", version)\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, filepath.Base(path), \"dummy\")\n\t\tassert.Equal(t, filepath.Base(filepath.Dir(filepath.Dir(path))), version.Value)\n\t})\n\n\tt.Run(\"returns error when executable with name not found\", func(t *testing.T) {\n\t\tpath, err := GetExecutablePath(conf, plugin, \"foo\", version)\n\t\tassert.ErrorContains(t, err, \"executable not found\")\n\t\tassert.Equal(t, path, \"\")\n\t})\n\n\tt.Run(\"returns custom path when plugin has exec-path callback\", func(t *testing.T) {\n\t\t// Create exec-path callback\n\t\tinstallDummyExecPathScript(t, conf, plugin, version, \"dummy\", \"echo 'bin/custom/dummy'\")\n\n\t\tpath, err := GetExecutablePath(conf, plugin, \"dummy\", version)\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, filepath.Base(filepath.Dir(path)), \"custom\")\n\t\t// Doesn't contain any trailing whitespace (newlines as the last char are common)\n\t\tassert.Equal(t, path, strings.TrimSpace(path))\n\t})\n\n\tt.Run(\"returns default path when plugin has exec-path callback that prints third argument\", func(t *testing.T) {\n\t\t// Create exec-path callback\n\t\tinstallDummyExecPathScript(t, conf, plugin, version, \"dummy\", \"echo \\\"$3\\\"\")\n\n\t\tpath, err := GetExecutablePath(conf, plugin, \"dummy\", version)\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, filepath.Base(path), \"dummy\")\n\t\tassert.Equal(t, filepath.Base(filepath.Dir(path)), \"bin\")\n\n\t\t// Doesn't contain any trailing whitespace (newlines as the last char are common)\n\t\tassert.Equal(t, path, strings.TrimSpace(path))\n\t})\n}\n\nfunc TestRemoveAll(t *testing.T) {\n\tversion := \"1.1.0\"\n\tconf, plugin := generateConfig(t)\n\tinstallVersion(t, conf, plugin, version)\n\texecutables, err := ToolExecutables(conf, plugin, toolversions.Version{Type: \"version\", Value: version})\n\tassert.Nil(t, err)\n\tstdout, stderr := buildOutputs()\n\n\tt.Run(\"removes all files in shim directory\", func(t *testing.T) {\n\t\tassert.Nil(t, GenerateAll(conf, &stdout, &stderr))\n\t\tassert.Nil(t, RemoveAll(conf))\n\n\t\t// check for generated shims\n\t\tfor _, executable := range executables {\n\t\t\t_, err := os.Stat(Path(conf, filepath.Base(executable)))\n\t\t\tassert.True(t, errors.Is(err, os.ErrNotExist))\n\t\t}\n\t})\n\n\tt.Run(\"does not return error when shims directory does not exist\", func(t *testing.T) {\n\t\tshimDir := Directory(conf)\n\t\tassert.Nil(t, os.RemoveAll(shimDir))\n\t\tassert.Nil(t, RemoveAll(conf))\n\t})\n}\n\nfunc TestGenerateAll(t *testing.T) {\n\tversion := \"1.1.0\"\n\tversion2 := \"2.0.0\"\n\tconf, plugin := generateConfig(t)\n\tinstallVersion(t, conf, plugin, version)\n\tinstallPlugin(t, conf, \"dummy_plugin\", \"ruby\")\n\tinstallVersion(t, conf, plugin, version2)\n\texecutables, err := ToolExecutables(conf, plugin, toolversions.Version{Type: \"version\", Value: version})\n\tassert.Nil(t, err)\n\tstdout, stderr := buildOutputs()\n\n\tt.Run(\"generates shim script for every executable in every version of every tool\", func(t *testing.T) {\n\t\tassert.Nil(t, GenerateAll(conf, &stdout, &stderr))\n\n\t\t// check for generated shims\n\t\tfor _, executable := range executables {\n\t\t\tshimName := filepath.Base(executable)\n\t\t\tshimPath := Path(conf, shimName)\n\t\t\tassert.Nil(t, unix.Access(shimPath, unix.X_OK))\n\n\t\t\t// shim exists and has expected contents\n\t\t\tcontent, err := os.ReadFile(shimPath)\n\t\t\tassert.Nil(t, err)\n\t\t\twant := fmt.Sprintf(\"#!/usr/bin/env bash\\n# asdf-plugin: lua 2.0.0\\n# asdf-plugin: lua 1.1.0\\nexec asdf exec \\\"%s\\\" \\\"$@\\\"\", shimName)\n\t\t\tassert.Equal(t, want, string(content))\n\t\t}\n\t})\n}\n\nfunc TestGenerateForPluginVersions(t *testing.T) {\n\tt.Setenv(\"ASDF_CONFIG_FILE\", \"testdata/asdfrc\")\n\tversion := \"1.1.0\"\n\tversion2 := \"2.0.0\"\n\tconf, plugin := generateConfig(t)\n\tinstallVersion(t, conf, plugin, version)\n\tinstallVersion(t, conf, plugin, version2)\n\texecutables, err := ToolExecutables(conf, plugin, toolversions.Version{Type: \"version\", Value: version})\n\tassert.Nil(t, err)\n\tstdout, stderr := buildOutputs()\n\n\tt.Run(\"generates shim script for every executable in every version the tool\", func(t *testing.T) {\n\t\tassert.Nil(t, GenerateForPluginVersions(conf, plugin, &stdout, &stderr))\n\n\t\t// check for generated shims\n\t\tfor _, executable := range executables {\n\t\t\tshimName := filepath.Base(executable)\n\t\t\tshimPath := Path(conf, shimName)\n\t\t\tassert.Nil(t, unix.Access(shimPath, unix.X_OK))\n\n\t\t\t// shim exists and has expected contents\n\t\t\tcontent, err := os.ReadFile(shimPath)\n\t\t\tassert.Nil(t, err)\n\n\t\t\twant := fmt.Sprintf(\"#!/usr/bin/env bash\\n# asdf-plugin: lua 2.0.0\\n# asdf-plugin: lua 1.1.0\\nexec asdf exec \\\"%s\\\" \\\"$@\\\"\", shimName)\n\t\t\tassert.Equal(t, want, string(content))\n\t\t}\n\t})\n\n\tt.Run(\"runs pre and post reshim hooks\", func(t *testing.T) {\n\t\tstdout, stderr := buildOutputs()\n\t\tassert.Nil(t, GenerateForPluginVersions(conf, plugin, &stdout, &stderr))\n\n\t\twant := \"pre_reshim 1.1.0\\npost_reshim 1.1.0\\npre_reshim 2.0.0\\npost_reshim 2.0.0\\n\"\n\t\tassert.Equal(t, want, stdout.String())\n\t})\n}\n\nfunc TestGenerateForVersion(t *testing.T) {\n\tversion := toolversions.Version{Type: \"version\", Value: \"1.1.0\"}\n\tversion2 := toolversions.Version{Type: \"version\", Value: \"2.0.0\"}\n\tconf, plugin := generateConfig(t)\n\tinstallVersion(t, conf, plugin, version.Value)\n\tinstallVersion(t, conf, plugin, version2.Value)\n\texecutables, err := ToolExecutables(conf, plugin, version)\n\tassert.Nil(t, err)\n\n\tt.Run(\"generates shim script for every executable in version\", func(t *testing.T) {\n\t\tstdout, stderr := buildOutputs()\n\t\tassert.Nil(t, GenerateForVersion(conf, plugin, version, &stdout, &stderr))\n\n\t\t// check for generated shims\n\t\tfor _, executable := range executables {\n\t\t\tshimName := filepath.Base(executable)\n\t\t\tshimPath := Path(conf, shimName)\n\t\t\tassert.Nil(t, unix.Access(shimPath, unix.X_OK))\n\t\t}\n\t})\n\n\tt.Run(\"updates existing shims for every executable in version\", func(t *testing.T) {\n\t\tstdout, stderr := buildOutputs()\n\t\tassert.Nil(t, GenerateForVersion(conf, plugin, version, &stdout, &stderr))\n\t\tassert.Nil(t, GenerateForVersion(conf, plugin, version2, &stdout, &stderr))\n\n\t\t// check for generated shims\n\t\tfor _, executable := range executables {\n\t\t\tshimName := filepath.Base(executable)\n\t\t\tshimPath := Path(conf, shimName)\n\t\t\tassert.Nil(t, unix.Access(shimPath, unix.X_OK))\n\t\t}\n\t})\n}\n\nfunc TestWrite(t *testing.T) {\n\tversion := toolversions.Version{Type: \"version\", Value: \"1.1.0\"}\n\tversion2 := toolversions.Version{Type: \"version\", Value: \"2.0.0\"}\n\tconf, plugin := generateConfig(t)\n\tinstallVersion(t, conf, plugin, version.Value)\n\tinstallVersion(t, conf, plugin, version2.Value)\n\texecutables, err := ToolExecutables(conf, plugin, version)\n\texecutable := executables[0]\n\tassert.Nil(t, err)\n\n\tt.Run(\"writes a new shim file when doesn't exist\", func(t *testing.T) {\n\t\texecutable := executables[0]\n\t\terr = Write(conf, plugin, version, executable)\n\t\tassert.Nil(t, err)\n\n\t\t// shim is executable\n\t\tshimName := filepath.Base(executable)\n\t\tshimPath := Path(conf, shimName)\n\t\tassert.Nil(t, unix.Access(shimPath, unix.X_OK))\n\n\t\t// shim exists and has expected contents\n\t\tcontent, err := os.ReadFile(shimPath)\n\t\tassert.Nil(t, err)\n\t\twant := \"#!/usr/bin/env bash\\n# asdf-plugin: lua 1.1.0\\nexec asdf exec \\\"dummy\\\" \\\"$@\\\"\"\n\t\tassert.Equal(t, want, string(content))\n\t\tos.Remove(shimPath)\n\t})\n\n\tt.Run(\"updates an existing shim file when already present\", func(t *testing.T) {\n\t\t// Write same shim for two versions\n\t\tassert.Nil(t, Write(conf, plugin, version, executable))\n\t\tassert.Nil(t, Write(conf, plugin, version2, executable))\n\n\t\t// shim is still executable\n\t\tshimName := filepath.Base(executable)\n\t\tshimPath := Path(conf, shimName)\n\t\tassert.Nil(t, unix.Access(shimPath, unix.X_OK))\n\n\t\t// has expected contents\n\t\tcontent, err := os.ReadFile(shimPath)\n\t\tassert.Nil(t, err)\n\t\twant := \"#!/usr/bin/env bash\\n# asdf-plugin: lua 2.0.0\\n# asdf-plugin: lua 1.1.0\\nexec asdf exec \\\"dummy\\\" \\\"$@\\\"\"\n\t\tassert.Equal(t, want, string(content))\n\t\tos.Remove(shimPath)\n\t})\n\n\tt.Run(\"doesn't add the same version to a shim file twice\", func(t *testing.T) {\n\t\tassert.Nil(t, Write(conf, plugin, version, executable))\n\t\tassert.Nil(t, Write(conf, plugin, version, executable))\n\n\t\t// Shim doesn't contain any duplicate lines\n\t\tshimPath := Path(conf, filepath.Base(executable))\n\t\tcontent, err := os.ReadFile(shimPath)\n\t\tassert.Nil(t, err)\n\t\twant := \"#!/usr/bin/env bash\\n# asdf-plugin: lua 1.1.0\\nexec asdf exec \\\"dummy\\\" \\\"$@\\\"\"\n\t\tassert.Equal(t, want, string(content))\n\t\tos.Remove(shimPath)\n\t})\n}\n\nfunc TestToolExecutables(t *testing.T) {\n\tversion := toolversions.Version{Type: \"version\", Value: \"1.1.0\"}\n\tconf, plugin := generateConfig(t)\n\tinstallVersion(t, conf, plugin, version.Value)\n\n\tt.Run(\"returns list of executables for plugin\", func(t *testing.T) {\n\t\texecutables, err := ToolExecutables(conf, plugin, version)\n\t\tassert.Nil(t, err)\n\n\t\tvar filenames []string\n\t\tfor _, executablePath := range executables {\n\t\t\tassert.True(t, strings.HasPrefix(executablePath, conf.DataDir))\n\t\t\tfilenames = append(filenames, filepath.Base(executablePath))\n\t\t}\n\n\t\tassert.Equal(t, filenames, []string{\"dummy\"})\n\t})\n\n\tt.Run(\"returns list of executables for version installed in arbitrary directory\", func(t *testing.T) {\n\t\t// Reference regular install by path to validate this behavior\n\t\tpath := installs.InstallPath(conf, plugin, version)\n\t\texecutables, err := ToolExecutables(conf, plugin, toolversions.Version{Type: \"path\", Value: path})\n\t\tassert.Nil(t, err)\n\n\t\tvar filenames []string\n\t\tfor _, executablePath := range executables {\n\t\t\tassert.True(t, strings.HasPrefix(executablePath, conf.DataDir))\n\t\t\tfilenames = append(filenames, filepath.Base(executablePath))\n\t\t}\n\n\t\tassert.Equal(t, filenames, []string{\"dummy\"})\n\t})\n\n\tt.Run(\"returns list of executables even when one directory printed by list-bin-paths doesn't exist\", func(t *testing.T) {\n\t\t// foo is first in list returned by list-bin-paths but doesn't exist, do\n\t\t// we still get the executables in the bin/ dir?\n\t\trepotest.WritePluginCallback(plugin.Dir, \"list-bin-paths\", \"#!/usr/bin/env bash\\necho 'foo bin'\")\n\t\texecutables, err := ToolExecutables(conf, plugin, version)\n\t\tassert.Nil(t, err)\n\n\t\tvar filenames []string\n\t\tfor _, executablePath := range executables {\n\t\t\tassert.True(t, strings.HasPrefix(executablePath, conf.DataDir))\n\t\t\tfilenames = append(filenames, filepath.Base(executablePath))\n\t\t}\n\n\t\tassert.Equal(t, filenames, []string{\"dummy\"})\n\t})\n}\n\nfunc TestExecutablePaths(t *testing.T) {\n\tconf, plugin := generateConfig(t)\n\tinstallVersion(t, conf, plugin, \"1.2.3\")\n\n\tt.Run(\"returns list only containing 'bin' when list-bin-paths callback missing\", func(t *testing.T) {\n\t\texecutables, err := ExecutablePaths(conf, plugin, toolversions.Version{Type: \"version\", Value: \"1.2.3\"})\n\t\tpath := executables[0]\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, filepath.Base(filepath.Dir(path)), \"1.2.3\")\n\t\tassert.Equal(t, filepath.Base(path), \"bin\")\n\t})\n\n\tt.Run(\"returns list of executable paths for tool version\", func(t *testing.T) {\n\t\tdata := []byte(\"echo 'foo bar'\")\n\t\terr := os.WriteFile(filepath.Join(plugin.Dir, \"bin\", \"list-bin-paths\"), data, 0o777)\n\t\tassert.Nil(t, err)\n\n\t\texecutables, err := ExecutablePaths(conf, plugin, toolversions.Version{Type: \"version\", Value: \"1.2.3\"})\n\t\tpath1 := executables[0]\n\t\tpath2 := executables[1]\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, filepath.Base(path1), \"foo\")\n\t\tassert.Equal(t, filepath.Base(path2), \"bar\")\n\t})\n\n\tt.Run(\"returns list of executable paths for tool version containing shim templates\", func(t *testing.T) {\n\t\tdata := []byte(\"echo 'foo bar'\")\n\t\terr := os.WriteFile(filepath.Join(plugin.Dir, \"bin\", \"list-bin-paths\"), data, 0o777)\n\t\tassert.Nil(t, err)\n\n\t\t// write a shim template to the plugin shims dir\n\t\tsetupShimTemplate(t, plugin, \"dummy\", \"echo 'shim template'\")\n\n\t\texecutables, err := ExecutablePaths(conf, plugin, toolversions.Version{Type: \"version\", Value: \"1.2.3\"})\n\t\tassert.Nil(t, err)\n\t\tpath1 := executables[0]\n\t\tpath2 := executables[1]\n\t\tpath3 := executables[2]\n\t\tassert.Equal(t, \"foo\", filepath.Base(path2))\n\t\tassert.Equal(t, \"bar\", filepath.Base(path3))\n\n\t\trelativePath, err := filepath.Rel(conf.DataDir, path1)\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, \"plugins/lua/shims\", relativePath)\n\t})\n}\n\nfunc TestExecutableDirs(t *testing.T) {\n\tconf, plugin := generateConfig(t)\n\tinstallVersion(t, conf, plugin, \"1.2.3\")\n\n\tt.Run(\"returns list only containing 'bin' when list-bin-paths callback missing\", func(t *testing.T) {\n\t\texecutables, err := ExecutableDirs(plugin)\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, executables, []string{\"bin\"})\n\t})\n\n\tt.Run(\"returns list of executable paths for tool version\", func(t *testing.T) {\n\t\tdata := []byte(\"echo 'foo bar'\")\n\t\terr := os.WriteFile(filepath.Join(plugin.Dir, \"bin\", \"list-bin-paths\"), data, 0o777)\n\t\tassert.Nil(t, err)\n\n\t\texecutables, err := ExecutableDirs(plugin)\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, executables, []string{\"foo\", \"bar\"})\n\t})\n}\n\n// Helper functions\nfunc buildOutputs() (strings.Builder, strings.Builder) {\n\tvar stdout strings.Builder\n\tvar stderr strings.Builder\n\n\treturn stdout, stderr\n}\n\nfunc generateConfig(t *testing.T) (config.Config, plugins.Plugin) {\n\tt.Helper()\n\ttestDataDir := t.TempDir()\n\tconf, err := config.LoadConfig()\n\tassert.Nil(t, err)\n\tconf.DataDir = testDataDir\n\n\treturn conf, installPlugin(t, conf, \"dummy_plugin\", testPluginName)\n}\n\nfunc installDummyExecPathScript(t *testing.T, conf config.Config, plugin plugins.Plugin, version toolversions.Version, name, script string) {\n\tt.Helper()\n\texecPath := filepath.Join(plugin.Dir, \"bin\", \"exec-path\")\n\tcontents := fmt.Sprintf(\"#!/usr/bin/env bash\\n%s\\n\", script)\n\terr := os.WriteFile(execPath, []byte(contents), 0o777)\n\tassert.Nil(t, err)\n\n\tinstallPath := installs.InstallPath(conf, plugin, version)\n\terr = os.MkdirAll(filepath.Join(installPath, \"bin\", \"custom\"), 0o777)\n\tassert.Nil(t, err)\n\n\terr = os.WriteFile(filepath.Join(installPath, \"bin\", \"custom\", name), []byte{}, 0o777)\n\tassert.Nil(t, err)\n}\n\nfunc installPlugin(t *testing.T, conf config.Config, fixture, pluginName string) plugins.Plugin {\n\t_, err := repotest.InstallPlugin(fixture, conf.DataDir, pluginName)\n\tassert.Nil(t, err)\n\n\treturn plugins.New(conf, pluginName)\n}\n\nfunc installVersion(t *testing.T, conf config.Config, plugin plugins.Plugin, version string) {\n\tt.Helper()\n\terr := installtest.InstallOneVersion(conf, plugin, \"version\", version)\n\tassert.Nil(t, err)\n}\n\nfunc setupShimTemplate(t *testing.T, plugin plugins.Plugin, shimName string, script string) {\n\tt.Helper()\n\tshimsDirPath := filepath.Join(plugin.Dir, \"shims\")\n\tos.MkdirAll(shimsDirPath, 0o777)\n\n\tshimPath := filepath.Join(shimsDirPath, shimName)\n\tcontents := fmt.Sprintf(\"#!/usr/bin/env bash\\n%s\\n\", script)\n\terr := os.WriteFile(shimPath, []byte(contents), 0o777)\n\tassert.Nil(t, err)\n\n\tt.Cleanup(func() {\n\t\terr := os.Remove(shimPath)\n\t\tassert.Nil(t, err)\n\t})\n}\n"
  },
  {
    "path": "internal/shims/testdata/asdfrc",
    "content": "pre_asdf_reshim_lua = echo pre_reshim $@\npost_asdf_reshim_lua = echo post_reshim $@\n"
  },
  {
    "path": "internal/toolversions/toolversions.go",
    "content": "// Package toolversions handles reading and writing tools and versions from\n// asdf's .tool-versions files. It also handles parsing version strings from\n// .tool-versions files and command line arguments.\npackage toolversions\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"slices\"\n\t\"strings\"\n)\n\n// Version struct represents a single version in asdf.\ntype Version struct {\n\tType  string // Must be one of: version, ref, path, system, latest\n\tValue string // Any string\n}\n\n// ToolVersions represents a tool along with versions specified for it\ntype ToolVersions struct {\n\tName     string\n\tVersions []string\n}\n\n// WriteToolVersionsToFile takes a path to a file and writes the new tool and\n// version data to the file. It creates the file if it does not exist and\n// updates it if it does.\nfunc WriteToolVersionsToFile(filepath string, toolVersions []ToolVersions) error {\n\tcontent, err := os.ReadFile(filepath)\n\tif _, ok := err.(*os.PathError); err != nil && !ok {\n\t\treturn err\n\t}\n\n\tupdatedContent := updateContentWithToolVersions(string(content), toolVersions)\n\treturn os.WriteFile(filepath, []byte(updatedContent), 0o666)\n}\n\nfunc updateContentWithToolVersions(content string, toolVersions []ToolVersions) string {\n\tvar output strings.Builder\n\n\tif content != \"\" {\n\t\tfor _, line := range readLines(content) {\n\t\t\ttokens, comment := parseLine(line)\n\t\t\tif len(tokens) > 1 {\n\t\t\t\ttv := ToolVersions{Name: tokens[0], Versions: tokens[1:]}\n\n\t\t\t\tindexMatching := slices.IndexFunc(toolVersions, func(toolVersion ToolVersions) bool {\n\t\t\t\t\treturn toolVersion.Name == tv.Name\n\t\t\t\t})\n\n\t\t\t\tif indexMatching != -1 {\n\t\t\t\t\t// write updated version\n\t\t\t\t\tnewTv := toolVersions[indexMatching]\n\t\t\t\t\tnewTokens := toolVersionsToTokens(newTv)\n\t\t\t\t\twriteLine(&output, encodeLine(newTokens, comment))\n\t\t\t\t\ttoolVersions = slices.Delete(toolVersions, indexMatching, indexMatching+1)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// write back original line\n\t\t\twriteLine(&output, line)\n\t\t}\n\t}\n\n\t// If any ToolVersions structs remaining, write them to the end of the file\n\tif len(toolVersions) > 0 {\n\t\tfor _, toolVersion := range toolVersions {\n\t\t\tnewTokens := toolVersionsToTokens(toolVersion)\n\t\t\twriteLine(&output, encodeLine(newTokens, \"\"))\n\t\t}\n\t}\n\n\treturn output.String()\n}\n\n// FindToolVersions looks up a tool version in a tool versions file and if found\n// returns a slice of versions for it.\nfunc FindToolVersions(filepath, toolName string) (versions []string, found bool, err error) {\n\tcontent, err := os.ReadFile(filepath)\n\tif err != nil {\n\t\treturn versions, false, err\n\t}\n\n\tversions, found = findToolVersionsInContent(string(content), toolName)\n\treturn versions, found, nil\n}\n\n// GetAllToolsAndVersions returns a list of all tools and associated versions\n// contained in a .tool-versions file\nfunc GetAllToolsAndVersions(filepath string) (toolVersions []ToolVersions, err error) {\n\tcontent, err := os.ReadFile(filepath)\n\tif err != nil {\n\t\treturn toolVersions, err\n\t}\n\n\ttoolVersions = getAllToolsAndVersionsInContent(string(content))\n\treturn toolVersions, nil\n}\n\n// Intersect takes two slices of versions and returns a new slice containing\n// only the versions found in both.\nfunc Intersect(versions1 []string, versions2 []string) (versions []string) {\n\tfor _, version1 := range versions1 {\n\t\tif slices.Contains(versions2, version1) {\n\t\t\tversions = append(versions, version1)\n\t\t}\n\t}\n\treturn versions\n}\n\n// Unique takes a slice of ToolVersions and returns a slice of unique tools and\n// versions.\nfunc Unique(versions []ToolVersions) (uniques []ToolVersions) {\n\tfor _, version := range versions {\n\t\tindex := slices.IndexFunc(\n\t\t\tuniques,\n\t\t\tfunc(v ToolVersions) bool { return v.Name == version.Name },\n\t\t)\n\t\tif index < 0 {\n\t\t\tuniques = append(uniques, version)\n\t\t\tcontinue\n\t\t}\n\n\t\tunique := &uniques[index]\n\t\tfor _, versionNumber := range version.Versions {\n\t\t\tif !slices.Contains(unique.Versions, versionNumber) {\n\t\t\t\tunique.Versions = append(unique.Versions, versionNumber)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn uniques\n}\n\n// ParseFromCliArg parses a string that is passed in as an argument to one of\n// the asdf subcommands. Some subcommands allow the special version `latest` to\n// be used, with an optional filter string.\nfunc ParseFromCliArg(version string) Version {\n\tsegments := strings.Split(version, \":\")\n\tif len(segments) > 0 && segments[0] == \"latest\" {\n\t\tif len(segments) > 1 {\n\t\t\t// Must be latest with filter\n\t\t\treturn Version{Type: \"latest\", Value: segments[1]}\n\t\t}\n\t\treturn Version{Type: \"latest\", Value: \"\"}\n\t}\n\n\treturn Parse(version)\n}\n\n// Parse parses a version string into versionType and version components\nfunc Parse(version string) Version {\n\tsegments := strings.Split(version, \":\")\n\tif len(segments) >= 1 {\n\t\tremainder := strings.Join(segments[1:], \":\")\n\t\tswitch segments[0] {\n\t\tcase \"ref\":\n\t\t\treturn Version{Type: \"ref\", Value: remainder}\n\t\tcase \"path\":\n\t\t\t// This is for people who have the local source already compiled\n\t\t\t// Like those who work on the language, etc\n\t\t\t// We'll allow specifying path:/foo/bar/project in .tool-versions\n\t\t\t// And then use the binaries there\n\t\t\treturn Version{Type: \"path\", Value: remainder}\n\t\tdefault:\n\t\t}\n\t}\n\n\tif version == \"system\" {\n\t\treturn Version{Type: \"system\"}\n\t}\n\n\treturn Version{Type: \"version\", Value: version}\n}\n\n// ParseSlice takes a slice of strings and returns a slice of parsed versions.\nfunc ParseSlice(versions []string) (parsedVersions []Version) {\n\tfor _, version := range versions {\n\t\tparsedVersions = append(parsedVersions, Parse(version))\n\t}\n\treturn parsedVersions\n}\n\n// Format takes a Version struct and formats it as a string\nfunc Format(version Version) string {\n\tswitch version.Type {\n\tcase \"system\":\n\t\treturn \"system\"\n\tcase \"path\":\n\t\treturn fmt.Sprintf(\"path:%s\", version.Value)\n\tcase \"ref\":\n\t\treturn fmt.Sprintf(\"ref:%s\", version.Value)\n\tdefault:\n\t\treturn version.Value\n\t}\n}\n\n// FormatForFS takes a versionType and version strings and generate a version\n// string suitable for the file system\nfunc FormatForFS(version Version) string {\n\tswitch version.Type {\n\tcase \"ref\":\n\t\treturn fmt.Sprintf(\"ref-%s\", version.Value)\n\tdefault:\n\t\treturn version.Value\n\t}\n}\n\n// VersionStringFromFSFormat takes a version string from directory name and\n// formats it as version string that can be parsed by the Parse function.\nfunc VersionStringFromFSFormat(version string) string {\n\tif strings.HasPrefix(version, \"ref-\") {\n\t\treturn strings.Replace(version, \"ref-\", \"ref:\", 1)\n\t}\n\treturn version\n}\n\nfunc readLines(content string) (lines []string) {\n\treturn strings.Split(content, \"\\n\")\n}\n\nfunc findToolVersionsInContent(content, toolName string) (versions []string, found bool) {\n\ttoolVersions := getAllToolsAndVersionsInContent(content)\n\tfor _, tool := range toolVersions {\n\t\tif tool.Name == toolName {\n\t\t\treturn tool.Versions, true\n\t\t}\n\t}\n\n\treturn versions, found\n}\n\nfunc getAllToolsAndVersionsInContent(content string) (toolVersions []ToolVersions) {\n\tfor _, line := range readLines(content) {\n\t\ttokens, _ := parseLine(line)\n\t\tif len(tokens) > 1 {\n\t\t\tnewTool := ToolVersions{Name: tokens[0], Versions: tokens[1:]}\n\t\t\ttoolVersions = append(toolVersions, newTool)\n\t\t}\n\t}\n\n\treturn toolVersions\n}\n\n// parseLine receives a single line from a file and parses it into a list of\n// tokens and a comment. A comment may occur anywhere on the line and is started\n// by a `#` character.\nfunc parseLine(line string) (tokens []string, comment string) {\n\tpreComment, comment, _ := strings.Cut(line, \"#\")\n\tfor _, token := range strings.Split(preComment, \" \") {\n\t\ttoken = strings.TrimSpace(token)\n\t\tif len(token) > 0 {\n\t\t\ttokens = append(tokens, token)\n\t\t}\n\t}\n\n\treturn tokens, comment\n}\n\nfunc toolVersionsToTokens(tv ToolVersions) []string {\n\treturn append([]string{tv.Name}, tv.Versions...)\n}\n\nfunc encodeLine(tokens []string, comment string) string {\n\ttokensStr := strings.Join(tokens, \" \")\n\tif comment == \"\" {\n\t\tif len(tokens) == 0 {\n\t\t\treturn \"\"\n\t\t}\n\t\treturn tokensStr\n\t}\n\treturn fmt.Sprintf(\"%s #%s\", tokensStr, comment)\n}\n\nfunc writeLine(output *strings.Builder, line string) {\n\tif strings.TrimSpace(line) != \"\" {\n\t\toutput.WriteString(line + \"\\n\")\n\t}\n}\n"
  },
  {
    "path": "internal/toolversions/toolversions_test.go",
    "content": "package toolversions\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestGetAllToolsAndVersions(t *testing.T) {\n\tt.Run(\"returns error when non-existent file\", func(t *testing.T) {\n\t\ttoolVersions, err := GetAllToolsAndVersions(\"non-existent-file\")\n\t\tassert.Error(t, err)\n\t\tassert.Empty(t, toolVersions)\n\t})\n\n\tt.Run(\"returns list of tool versions when populated file\", func(t *testing.T) {\n\t\ttoolVersionsPath := filepath.Join(t.TempDir(), \".tool-versions\")\n\t\tfile, err := os.Create(toolVersionsPath)\n\t\tassert.Nil(t, err)\n\t\tdefer file.Close()\n\t\tfile.WriteString(\"ruby 2.0.0\")\n\n\t\ttoolVersions, err := GetAllToolsAndVersions(toolVersionsPath)\n\t\tassert.Nil(t, err)\n\t\texpected := []ToolVersions{{Name: \"ruby\", Versions: []string{\"2.0.0\"}}}\n\t\tassert.Equal(t, expected, toolVersions)\n\t})\n}\n\nfunc TestFindToolVersions(t *testing.T) {\n\tt.Run(\"returns error when non-existent file\", func(t *testing.T) {\n\t\tversions, found, err := FindToolVersions(\"non-existent-file\", \"nonexistent-tool\")\n\t\tassert.Error(t, err)\n\t\tassert.False(t, found)\n\t\tassert.Empty(t, versions)\n\t})\n\n\tt.Run(\"returns list of versions and found true when file contains tool versions\", func(t *testing.T) {\n\t\ttoolVersionsPath := filepath.Join(t.TempDir(), \".tool-versions\")\n\t\tfile, err := os.Create(toolVersionsPath)\n\t\tassert.Nil(t, err)\n\t\tdefer file.Close()\n\t\tfile.WriteString(\"ruby 2.0.0\")\n\n\t\tversions, found, err := FindToolVersions(toolVersionsPath, \"ruby\")\n\t\tassert.Nil(t, err)\n\t\tassert.True(t, found)\n\t\tassert.Equal(t, []string{\"2.0.0\"}, versions)\n\t})\n}\n\nfunc TestWriteToolVersionsToFile(t *testing.T) {\n\ttoolVersions := ToolVersions{Name: \"lua\", Versions: []string{\"1.2.3\"}}\n\n\tt.Run(\"writes new file when it does not exist\", func(t *testing.T) {\n\t\tpath := filepath.Join(t.TempDir(), \".tool-versions\")\n\t\tassert.Nil(t, WriteToolVersionsToFile(path, []ToolVersions{toolVersions}))\n\n\t\tfileContents, err := os.ReadFile(path)\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, string(fileContents), \"lua 1.2.3\\n\")\n\t})\n\n\tt.Run(\"writes new line to end of file when version not already set\", func(t *testing.T) {\n\t\tpath := filepath.Join(t.TempDir(), \".tool-versions\")\n\t\tassert.Nil(t, os.WriteFile(path, []byte(\"test 1.2.3\"), 0o666))\n\t\tassert.Nil(t, WriteToolVersionsToFile(path, []ToolVersions{toolVersions}))\n\n\t\tfileContents, err := os.ReadFile(path)\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, string(fileContents), \"test 1.2.3\\nlua 1.2.3\\n\")\n\t})\n\n\tt.Run(\"updates existing line when tool already has one or more versions set\", func(t *testing.T) {\n\t\tpath := filepath.Join(t.TempDir(), \".tool-versions\")\n\t\tassert.Nil(t, os.WriteFile(path, []byte(\"lua 1.1.1\"), 0o666))\n\t\tassert.Nil(t, WriteToolVersionsToFile(path, []ToolVersions{toolVersions}))\n\n\t\tfileContents, err := os.ReadFile(path)\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, string(fileContents), \"lua 1.2.3\\n\")\n\t})\n}\n\nfunc TestUpdateContentWithToolVersions(t *testing.T) {\n\ttests := []struct {\n\t\tdesc         string\n\t\tinput        string\n\t\ttoolVersions []ToolVersions\n\t\toutput       string\n\t}{\n\t\t{\n\t\t\tdesc:         \"returns content unchanged when identical tool and version already set\",\n\t\t\tinput:        \"foobar 1.2.3\",\n\t\t\ttoolVersions: []ToolVersions{{Name: \"foobar\", Versions: []string{\"1.2.3\"}}},\n\t\t\toutput:       \"foobar 1.2.3\\n\",\n\t\t},\n\t\t{\n\t\t\tdesc:         \"writes new line to end of file when version not already set\",\n\t\t\tinput:        \"foobar 1.2.3\",\n\t\t\ttoolVersions: []ToolVersions{{Name: \"test\", Versions: []string{\"4.5.6\"}}},\n\t\t\toutput:       \"foobar 1.2.3\\ntest 4.5.6\\n\",\n\t\t},\n\t\t{\n\t\t\tdesc:         \"preserves comments on all other lines\",\n\t\t\tinput:        \"foobar 1.2.3\\n# this is a test\",\n\t\t\ttoolVersions: []ToolVersions{{Name: \"foobar\", Versions: []string{\"4.5.6\"}}},\n\t\t\toutput:       \"foobar 4.5.6\\n# this is a test\\n\",\n\t\t},\n\t\t{\n\t\t\tdesc:         \"preserves comment on end of the line specifying previous version\",\n\t\t\tinput:        \"foobar 1.2.3 # this is a test\",\n\t\t\ttoolVersions: []ToolVersions{{Name: \"foobar\", Versions: []string{\"4.5.6\"}}},\n\t\t\toutput:       \"foobar 4.5.6 # this is a test\\n\",\n\t\t},\n\t\t{\n\t\t\tdesc:         \"writes multiple versions for same tool\",\n\t\t\tinput:        \"foobar 1.2.3\",\n\t\t\ttoolVersions: []ToolVersions{{Name: \"foobar\", Versions: []string{\"4.5.6\", \"1.2.3\"}}},\n\t\t\toutput:       \"foobar 4.5.6 1.2.3\\n\",\n\t\t},\n\t\t{\n\t\t\tdesc:  \"writes multiple tools\",\n\t\t\tinput: \"foobar 1.2.3\",\n\t\t\ttoolVersions: []ToolVersions{\n\t\t\t\t{Name: \"ruby\", Versions: []string{\"4.5.6\", \"1.2.3\"}},\n\t\t\t\t{Name: \"lua\", Versions: []string{\"5.2.3\"}},\n\t\t\t},\n\t\t\toutput: \"foobar 1.2.3\\nruby 4.5.6 1.2.3\\nlua 5.2.3\\n\",\n\t\t},\n\t\t{\n\t\t\tdesc:         \"writes new version when empty string\",\n\t\t\tinput:        \"\",\n\t\t\ttoolVersions: []ToolVersions{{Name: \"foobar\", Versions: []string{\"1.2.3\"}}},\n\t\t\toutput:       \"foobar 1.2.3\\n\",\n\t\t},\n\t\t{\n\t\t\tdesc:         \"writes new version when empty string\",\n\t\t\tinput:        \"# this is a test\",\n\t\t\ttoolVersions: []ToolVersions{{Name: \"foobar\", Versions: []string{\"1.2.3\"}}},\n\t\t\toutput:       \"# this is a test\\nfoobar 1.2.3\\n\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\toutput := updateContentWithToolVersions(tt.input, tt.toolVersions)\n\t\t\tassert.Equal(t, tt.output, output)\n\t\t})\n\t}\n}\n\nfunc TestIntersect(t *testing.T) {\n\ttests := []struct {\n\t\tdesc   string\n\t\tinput1 []string\n\t\tinput2 []string\n\t\twant   []string\n\t}{\n\t\t{\n\t\t\tdesc:   \"when provided two empty ToolVersions returns empty ToolVersions\",\n\t\t\tinput1: []string{},\n\t\t\tinput2: []string{},\n\t\t\twant:   []string(nil),\n\t\t},\n\t\t{\n\t\t\tdesc:   \"when provided ToolVersions with no matching versions return empty ToolVersions\",\n\t\t\tinput1: []string{\"1\", \"2\"},\n\t\t\tinput2: []string{\"3\", \"4\"},\n\t\t\twant:   []string(nil),\n\t\t},\n\t\t{\n\t\t\tdesc:   \"when provided ToolVersions with different versions return new ToolVersions only containing versions in both\",\n\t\t\tinput1: []string{\"1\", \"2\"},\n\t\t\tinput2: []string{\"2\", \"3\"},\n\t\t\twant:   []string{\"2\"},\n\t\t},\n\t\t{\n\t\t\tdesc:   \"preserves order of items in first argument\",\n\t\t\tinput1: []string{\"1\", \"3\", \"2\"},\n\t\t\tinput2: []string{\"2\", \"3\"},\n\t\t\twant:   []string{\"3\", \"2\"},\n\t\t},\n\t\t{\n\t\t\tdesc:   \"preserves order of items in first argument (variant 2)\",\n\t\t\tinput1: []string{\"1\", \"4\", \"5\", \"3\", \"2\"},\n\t\t\tinput2: []string{\"2\", \"3\", \"4\"},\n\t\t\twant:   []string{\"4\", \"3\", \"2\"},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tgot := Intersect(tt.input1, tt.input2)\n\t\t\tassert.Equal(t, tt.want, got)\n\t\t})\n\t}\n}\n\nfunc TestUnique(t *testing.T) {\n\tt.Run(\"returns unique slice of tool versions when tool appears multiple times in slice\", func(t *testing.T) {\n\t\tgot := Unique([]ToolVersions{\n\t\t\t{Name: \"foo\", Versions: []string{\"1\"}},\n\t\t\t{Name: \"foo\", Versions: []string{\"2\"}},\n\t\t})\n\n\t\twant := []ToolVersions{\n\t\t\t{Name: \"foo\", Versions: []string{\"1\", \"2\"}},\n\t\t}\n\n\t\tassert.Equal(t, got, want)\n\t})\n\n\tt.Run(\"returns unique slice of tool versions when given slice with multiple tools\", func(t *testing.T) {\n\t\tgot := Unique([]ToolVersions{\n\t\t\t{Name: \"foo\", Versions: []string{\"1\"}},\n\t\t\t{Name: \"bar\", Versions: []string{\"2\"}},\n\t\t\t{Name: \"foo\", Versions: []string{\"2\"}},\n\t\t\t{Name: \"bar\", Versions: []string{\"2\"}},\n\t\t})\n\n\t\twant := []ToolVersions{\n\t\t\t{Name: \"foo\", Versions: []string{\"1\", \"2\"}},\n\t\t\t{Name: \"bar\", Versions: []string{\"2\"}},\n\t\t}\n\n\t\tassert.Equal(t, got, want)\n\t})\n}\n\nfunc TestFindToolVersionsInContent(t *testing.T) {\n\tt.Run(\"returns empty list with found false when empty content\", func(t *testing.T) {\n\t\tversions, found := findToolVersionsInContent(\"\", \"ruby\")\n\t\tassert.False(t, found)\n\t\tassert.Empty(t, versions)\n\t})\n\n\tt.Run(\"returns empty list with found false when tool not found\", func(t *testing.T) {\n\t\tversions, found := findToolVersionsInContent(\"lua 5.4.5\", \"ruby\")\n\t\tassert.False(t, found)\n\t\tassert.Empty(t, versions)\n\t})\n\n\tt.Run(\"returns list of versions with found true when tool found\", func(t *testing.T) {\n\t\tversions, found := findToolVersionsInContent(\"lua 5.4.5 5.4.6\\nruby 2.0.0\", \"lua\")\n\t\tassert.True(t, found)\n\t\tassert.Equal(t, []string{\"5.4.5\", \"5.4.6\"}, versions)\n\t})\n}\n\nfunc TestGetAllToolsAndVersionsInContent(t *testing.T) {\n\ttests := []struct {\n\t\tdesc  string\n\t\tinput string\n\t\twant  []ToolVersions\n\t}{\n\t\t{\n\t\t\tdesc:  \"returns empty list with found true and no error when empty content\",\n\t\t\tinput: \"\",\n\t\t\twant:  []ToolVersions(nil),\n\t\t},\n\t\t{\n\t\t\tdesc:  \"returns list with one tool when single tool in content\",\n\t\t\tinput: \"lua 5.4.5 5.4.6\",\n\t\t\twant:  []ToolVersions{{Name: \"lua\", Versions: []string{\"5.4.5\", \"5.4.6\"}}},\n\t\t},\n\t\t{\n\t\t\tdesc:  \"returns list with multiple tools when multiple tools in content\",\n\t\t\tinput: \"lua 5.4.5 5.4.6\\nruby 2.0.0\",\n\t\t\twant: []ToolVersions{\n\t\t\t\t{Name: \"lua\", Versions: []string{\"5.4.5\", \"5.4.6\"}},\n\t\t\t\t{Name: \"ruby\", Versions: []string{\"2.0.0\"}},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\ttoolsAndVersions := getAllToolsAndVersionsInContent(tt.input)\n\t\t\tif len(tt.want) == 0 {\n\t\t\t\tassert.Empty(t, toolsAndVersions)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tassert.Equal(t, tt.want, toolsAndVersions)\n\t\t})\n\t}\n}\n\nfunc TestParse(t *testing.T) {\n\tt.Run(\"when passed version string returns struct with type of 'version' and version as value\", func(t *testing.T) {\n\t\tversion := Parse(\"1.2.3\")\n\t\tassert.Equal(t, version.Type, \"version\")\n\t\tassert.Equal(t, version.Value, \"1.2.3\")\n\t})\n\n\tt.Run(\"when passed ref and version returns struct with type of 'ref' and version as value\", func(t *testing.T) {\n\t\tversion := Parse(\"ref:abc123\")\n\t\tassert.Equal(t, version.Type, \"ref\")\n\t\tassert.Equal(t, version.Value, \"abc123\")\n\t})\n\n\tt.Run(\"when passed 'ref:' returns struct with type of 'ref' and empty value\", func(t *testing.T) {\n\t\tversion := Parse(\"ref:\")\n\t\tassert.Equal(t, version.Type, \"ref\")\n\t\tassert.Equal(t, version.Value, \"\")\n\t})\n\n\tt.Run(\"when passed 'system' returns struct with type of 'system'\", func(t *testing.T) {\n\t\tversion := Parse(\"system\")\n\t\tassert.Equal(t, version.Type, \"system\")\n\t\tassert.Equal(t, version.Value, \"\")\n\t})\n}\n\nfunc TestParseFromCliArg(t *testing.T) {\n\tt.Run(\"when passed 'latest' returns struct with type of 'latest'\", func(t *testing.T) {\n\t\tversion := ParseFromCliArg(\"latest\")\n\t\tassert.Equal(t, version.Type, \"latest\")\n\t\tassert.Equal(t, version.Value, \"\")\n\t})\n\n\tt.Run(\"when passed latest with filter returns struct with type of 'latest' and unmodified filter string as value\", func(t *testing.T) {\n\t\tversion := ParseFromCliArg(\"latest:1.2\")\n\t\tassert.Equal(t, version.Type, \"latest\")\n\t\tassert.Equal(t, version.Value, \"1.2\")\n\t})\n\n\tt.Run(\"when passed version string returns struct with type of 'version' and version as value\", func(t *testing.T) {\n\t\tversion := ParseFromCliArg(\"1.2.3\")\n\t\tassert.Equal(t, version.Type, \"version\")\n\t\tassert.Equal(t, version.Value, \"1.2.3\")\n\t})\n\n\tt.Run(\"when passed ref and version returns struct with type of 'ref' and version as value\", func(t *testing.T) {\n\t\tversion := ParseFromCliArg(\"ref:abc123\")\n\t\tassert.Equal(t, version.Type, \"ref\")\n\t\tassert.Equal(t, version.Value, \"abc123\")\n\t})\n\n\tt.Run(\"when passed 'ref:' returns struct with type of 'ref' and empty value\", func(t *testing.T) {\n\t\tversion := ParseFromCliArg(\"ref:\")\n\t\tassert.Equal(t, version.Type, \"ref\")\n\t\tassert.Equal(t, version.Value, \"\")\n\t})\n\n\tt.Run(\"when passed 'system' returns struct with type of 'system'\", func(t *testing.T) {\n\t\tversion := ParseFromCliArg(\"system\")\n\t\tassert.Equal(t, version.Type, \"system\")\n\t\tassert.Equal(t, version.Value, \"\")\n\t})\n}\n\nfunc TestParseSlice(t *testing.T) {\n\tt.Run(\"returns slice of parsed tool versions\", func(t *testing.T) {\n\t\tversions := ParseSlice([]string{\"1.2.3\"})\n\t\tassert.Equal(t, []Version{{Type: \"version\", Value: \"1.2.3\"}}, versions)\n\t})\n\n\tt.Run(\"returns empty slice when empty slice provided\", func(t *testing.T) {\n\t\tversions := ParseSlice([]string{})\n\t\tassert.Empty(t, versions)\n\t})\n\n\tt.Run(\"parses special versions\", func(t *testing.T) {\n\t\tversions := ParseSlice([]string{\"ref:foo\", \"system\", \"path:/foo/bar\"})\n\t\tassert.Equal(t, []Version{{Type: \"ref\", Value: \"foo\"}, {Type: \"system\"}, {Type: \"path\", Value: \"/foo/bar\"}}, versions)\n\t})\n}\n\nfunc TestFormat(t *testing.T) {\n\ttests := []struct {\n\t\tdesc   string\n\t\tinput  Version\n\t\toutput string\n\t}{\n\t\t{\n\t\t\tdesc:   \"with regular version\",\n\t\t\tinput:  Version{Type: \"version\", Value: \"foobar\"},\n\t\t\toutput: \"foobar\",\n\t\t},\n\t\t{\n\t\t\tdesc:   \"with ref version\",\n\t\t\tinput:  Version{Type: \"ref\", Value: \"foobar\"},\n\t\t\toutput: \"ref:foobar\",\n\t\t},\n\t\t{\n\t\t\tdesc:   \"with system version\",\n\t\t\tinput:  Version{Type: \"system\", Value: \"system\"},\n\t\t\toutput: \"system\",\n\t\t},\n\t\t{\n\t\t\tdesc:   \"with system version\",\n\t\t\tinput:  Version{Type: \"path\", Value: \"/foo/bar\"},\n\t\t\toutput: \"path:/foo/bar\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tgot := Format(tt.input)\n\t\t\tassert.Equal(t, got, tt.output)\n\t\t})\n\t}\n}\n\nfunc TestFormatForFS(t *testing.T) {\n\tt.Run(\"returns version when version type is not ref\", func(t *testing.T) {\n\t\tassert.Equal(t, FormatForFS(Version{Type: \"version\", Value: \"foobar\"}), \"foobar\")\n\t})\n\n\tt.Run(\"returns version prefixed with 'ref-' when version type is ref\", func(t *testing.T) {\n\t\tassert.Equal(t, FormatForFS(Version{Type: \"ref\", Value: \"foobar\"}), \"ref-foobar\")\n\t})\n}\n\nfunc TestVersionStringFromFSFormat(t *testing.T) {\n\ttests := []struct {\n\t\tdesc  string\n\t\tinput Version\n\t}{\n\t\t{\n\t\t\tdesc:  \"with regular version\",\n\t\t\tinput: Version{Type: \"version\", Value: \"foobar\"},\n\t\t},\n\t\t{\n\t\t\tdesc:  \"with ref version\",\n\t\t\tinput: Version{Type: \"ref\", Value: \"foobar\"},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.desc, func(t *testing.T) {\n\t\t\tgot := Parse(VersionStringFromFSFormat(FormatForFS(tt.input)))\n\t\t\tassert.Equal(t, tt.input, got)\n\t\t})\n\t}\n}\n\nfunc BenchmarkUnique(b *testing.B) {\n\tversions := []ToolVersions{\n\t\t{Name: \"foo\", Versions: []string{\"1\"}},\n\t\t{Name: \"bar\", Versions: []string{\"2\"}},\n\t\t{Name: \"foo\", Versions: []string{\"2\"}},\n\t\t{Name: \"bar\", Versions: []string{\"2\"}},\n\t}\n\n\tfor i := 0; i < b.N; i++ {\n\t\tUnique(versions)\n\t}\n}\n"
  },
  {
    "path": "internal/versions/testdata/asdfrc",
    "content": "pre_asdf_download_testlua = echo pre_asdf_download_lua $@\npre_asdf_install_testlua = echo pre_asdf_install_lua $@\npost_asdf_install_testlua = echo post_asdf_install_lua $@\nalways_keep_download = yes\n"
  },
  {
    "path": "internal/versions/testdata/list-all-elixir",
    "content": "0.12.4 0.12.5 0.13.0 0.13.1 0.13.2 0.13.3 0.14.0 0.14.1 0.14.2 0.14.3 0.15.0 0.15.1 1.0.0 1.0.0-otp-17 1.0.0-rc1 1.0.0-rc1-otp-17 1.0.0-rc2 1.0.0-rc2-otp-17 1.0.1 1.0.1-otp-17 1.0.2 1.0.2-otp-17 1.0.3 1.0.3-otp-17 1.0.4 1.0.4-otp-17 1.0.5 1.0.5-otp-17 1.0.5-otp-18 1.1.0 1.1.0-otp-17 1.1.0-otp-18 1.1.0-rc.0 1.1.0-rc.0-otp-17 1.1.0-rc.0-otp-18 1.1.1 1.1.1-otp-17 1.1.1-otp-18 1.2.0 1.2.0-otp-18 1.2.0-rc.0 1.2.0-rc.0-otp-18 1.2.0-rc.1 1.2.0-rc.1-otp-18 1.2.1 1.2.1-otp-18 1.2.2 1.2.2-otp-18 1.2.3 1.2.3-otp-18 1.2.4 1.2.4-otp-18 1.2.5 1.2.5-otp-18 1.2.6 1.2.6-otp-18 1.2.6-otp-19 1.3.0 1.3.0-otp-18 1.3.0-otp-19 1.3.0-rc.0 1.3.0-rc.0-otp-18 1.3.0-rc.0-otp-19 1.3.0-rc.1 1.3.0-rc.1-otp-18 1.3.0-rc.1-otp-19 1.3.1 1.3.1-otp-18 1.3.1-otp-19 1.3.2 1.3.2-otp-18 1.3.2-otp-19 1.3.3 1.3.3-otp-18 1.3.3-otp-19 1.3.4 1.3.4-otp-18 1.3.4-otp-19 1.4.0 1.4.0-otp-18 1.4.0-otp-19 1.4.0-rc.0 1.4.0-rc.0-otp-18 1.4.0-rc.0-otp-19 1.4.0-rc.0-otp-20 1.4.0-rc.1 1.4.0-rc.1-otp-18 1.4.0-rc.1-otp-19 1.4.0-rc.1-otp-20 1.4.1 1.4.1-otp-18 1.4.1-otp-19 1.4.2 1.4.2-otp-18 1.4.2-otp-19 1.4.3 1.4.3-otp-18 1.4.3-otp-19 1.4.4 1.4.4-otp-18 1.4.4-otp-19 1.4.5 1.4.5-otp-18 1.4.5-otp-19 1.4.5-otp-20 1.5.0 1.5.0-otp-18 1.5.0-otp-19 1.5.0-otp-20 1.5.0-rc.0 1.5.0-rc.0-otp-18 1.5.0-rc.0-otp-19 1.5.0-rc.0-otp-20 1.5.0-rc.1 1.5.0-rc.1-otp-18 1.5.0-rc.1-otp-19 1.5.0-rc.1-otp-20 1.5.0-rc.2 1.5.0-rc.2-otp-18 1.5.0-rc.2-otp-19 1.5.0-rc.2-otp-20 1.5.1 1.5.1-otp-18 1.5.1-otp-19 1.5.1-otp-20 1.5.2 1.5.2-otp-18 1.5.2-otp-19 1.5.2-otp-20 1.5.3 1.5.3-otp-18 1.5.3-otp-19 1.5.3-otp-20 1.6.0 1.6.0-otp-19 1.6.0-otp-20 1.6.0-rc.0 1.6.0-rc.0-otp-19 1.6.0-rc.0-otp-20 1.6.0-rc.1 1.6.0-rc.1-otp-19 1.6.0-rc.1-otp-20 1.6.1 1.6.1-otp-19 1.6.1-otp-20 1.6.2 1.6.2-otp-19 1.6.2-otp-20 1.6.3 1.6.3-otp-19 1.6.3-otp-20 1.6.4 1.6.4-otp-19 1.6.4-otp-20 1.6.5 1.6.5-otp-19 1.6.5-otp-20 1.6.5-otp-21 1.6.6 1.6.6-otp-19 1.6.6-otp-20 1.6.6-otp-21 1.7.0 1.7.0-otp-19 1.7.0-otp-20 1.7.0-otp-21 1.7.0-otp-22 1.7.0-rc.0 1.7.0-rc.0-otp-19 1.7.0-rc.0-otp-20 1.7.0-rc.0-otp-21 1.7.0-rc.0-otp-22 1.7.0-rc.1 1.7.0-rc.1-otp-19 1.7.0-rc.1-otp-20 1.7.0-rc.1-otp-21 1.7.0-rc.1-otp-22 1.7.1 1.7.1-otp-19 1.7.1-otp-20 1.7.1-otp-21 1.7.1-otp-22 1.7.2 1.7.2-otp-19 1.7.2-otp-20 1.7.2-otp-21 1.7.2-otp-22 1.7.3 1.7.3-otp-19 1.7.3-otp-20 1.7.3-otp-21 1.7.3-otp-22 1.7.4 1.7.4-otp-19 1.7.4-otp-20 1.7.4-otp-21 1.7.4-otp-22 1.8.0 1.8.0-otp-20 1.8.0-otp-21 1.8.0-otp-22 1.8.0-rc.0 1.8.0-rc.0-otp-20 1.8.0-rc.0-otp-21 1.8.0-rc.0-otp-22 1.8.0-rc.1 1.8.0-rc.1-otp-20 1.8.0-rc.1-otp-21 1.8.0-rc.1-otp-22 1.8.1 1.8.1-otp-20 1.8.1-otp-21 1.8.1-otp-22 1.8.2 1.8.2-otp-20 1.8.2-otp-21 1.8.2-otp-22 1.9.0 1.9.0-otp-20 1.9.0-otp-21 1.9.0-otp-22 1.9.0-rc.0 1.9.0-rc.0-otp-20 1.9.0-rc.0-otp-21 1.9.0-rc.0-otp-22 1.9.1 1.9.1-otp-20 1.9.1-otp-21 1.9.1-otp-22 1.9.2 1.9.2-otp-20 1.9.2-otp-21 1.9.2-otp-22 1.9.3 1.9.3-otp-20 1.9.3-otp-21 1.9.3-otp-22 1.9.4 1.9.4-otp-20 1.9.4-otp-21 1.9.4-otp-22 1.10.0 1.10.0-otp-21 1.10.0-otp-22 1.10.0-rc.0 1.10.0-rc.0-otp-21 1.10.0-rc.0-otp-22 1.10.1 1.10.1-otp-21 1.10.1-otp-22 1.10.2 1.10.2-otp-21 1.10.2-otp-22 1.10.3 1.10.3-otp-21 1.10.3-otp-22 1.10.3-otp-23 1.10.4 1.10.4-otp-21 1.10.4-otp-22 1.10.4-otp-23 1.11.0 1.11.0-otp-21 1.11.0-otp-22 1.11.0-otp-23 1.11.0-rc.0 1.11.0-rc.0-otp-21 1.11.0-rc.0-otp-22 1.11.0-rc.0-otp-23 1.11.1 1.11.1-otp-21 1.11.1-otp-22 1.11.1-otp-23 1.11.2 1.11.2-otp-21 1.11.2-otp-22 1.11.2-otp-23 1.11.3 1.11.3-otp-21 1.11.3-otp-22 1.11.3-otp-23 1.11.4 1.11.4-otp-21 1.11.4-otp-22 1.11.4-otp-23 1.11.4-otp-24 1.12.0 1.12.0-otp-22 1.12.0-otp-23 1.12.0-otp-24 1.12.0-rc.0 1.12.0-rc.0-otp-21 1.12.0-rc.0-otp-22 1.12.0-rc.0-otp-23 1.12.0-rc.0-otp-24 1.12.0-rc.1 1.12.0-rc.1-otp-22 1.12.0-rc.1-otp-23 1.12.0-rc.1-otp-24 1.12.1 1.12.1-otp-22 1.12.1-otp-23 1.12.1-otp-24 1.12.2 1.12.2-otp-22 1.12.2-otp-23 1.12.2-otp-24 1.12.3 1.12.3-otp-22 1.12.3-otp-23 1.12.3-otp-24 1.13.0 1.13.0-otp-22 1.13.0-otp-23 1.13.0-otp-24 1.13.0-otp-25 1.13.0-rc.0 1.13.0-rc.0-otp-22 1.13.0-rc.0-otp-23 1.13.0-rc.0-otp-24 1.13.0-rc.0-otp-25 1.13.0-rc.1 1.13.0-rc.1-otp-22 1.13.0-rc.1-otp-23 1.13.0-rc.1-otp-24 1.13.0-rc.1-otp-25 1.13.1 1.13.1-otp-22 1.13.1-otp-23 1.13.1-otp-24 1.13.1-otp-25 1.13.2 1.13.2-otp-22 1.13.2-otp-23 1.13.2-otp-24 1.13.2-otp-25 1.13.3 1.13.3-otp-22 1.13.3-otp-23 1.13.3-otp-24 1.13.3-otp-25 1.13.4 1.13.4-otp-22 1.13.4-otp-23 1.13.4-otp-24 1.13.4-otp-25 1.14.0 1.14.0-otp-23 1.14.0-otp-24 1.14.0-otp-25 1.14.0-rc.0 1.14.0-rc.0-otp-23 1.14.0-rc.0-otp-24 1.14.0-rc.0-otp-25 1.14.0-rc.1 1.14.0-rc.1-otp-23 1.14.0-rc.1-otp-24 1.14.0-rc.1-otp-25 1.14.1 1.14.1-otp-23 1.14.1-otp-24 1.14.1-otp-25 1.14.2 1.14.2-otp-23 1.14.2-otp-24 1.14.2-otp-25 1.14.3 1.14.3-otp-23 1.14.3-otp-24 1.14.3-otp-25 1.14.4 1.14.4-otp-23 1.14.4-otp-24 1.14.4-otp-25 1.14.4-otp-26 1.14.5 1.14.5-otp-23 1.14.5-otp-24 1.14.5-otp-25 1.14.5-otp-26 1.15.0 1.15.0-otp-24 1.15.0-otp-25 1.15.0-otp-26 1.15.0-rc.0 1.15.0-rc.0-otp-24 1.15.0-rc.0-otp-25 1.15.0-rc.0-otp-26 1.15.0-rc.1 1.15.0-rc.1-otp-24 1.15.0-rc.1-otp-25 1.15.0-rc.1-otp-26 1.15.0-rc.2 1.15.0-rc.2-otp-24 1.15.0-rc.2-otp-25 1.15.0-rc.2-otp-26 1.15.1 1.15.1-otp-24 1.15.1-otp-25 1.15.1-otp-26 1.15.2 1.15.2-otp-24 1.15.2-otp-25 1.15.2-otp-26 1.15.3 1.15.3-otp-24 1.15.3-otp-25 1.15.3-otp-26 1.15.4 1.15.4-otp-24 1.15.4-otp-25 1.15.4-otp-26 1.15.5 1.15.5-otp-24 1.15.5-otp-25 1.15.5-otp-26 1.15.6 1.15.6-otp-24 1.15.6-otp-25 1.15.6-otp-26 1.15.7 1.15.7-otp-24 1.15.7-otp-25 1.15.7-otp-26 1.15.8 1.15.8-otp-24 1.15.8-otp-25 1.15.8-otp-26 1.16.0 1.16.0-otp-24 1.16.0-otp-25 1.16.0-otp-26 1.16.0-rc.0 1.16.0-rc.0-otp-24 1.16.0-rc.0-otp-25 1.16.0-rc.0-otp-26 1.16.0-rc.1 1.16.0-rc.1-otp-24 1.16.0-rc.1-otp-25 1.16.0-rc.1-otp-26 1.16.1 1.16.1-otp-24 1.16.1-otp-25 1.16.1-otp-26 1.16.2 1.16.2-otp-24 1.16.2-otp-25 1.16.2-otp-26 1.16.3 1.16.3-otp-24 1.16.3-otp-25 1.16.3-otp-26 1.17.0 1.17.0-otp-25 1.17.0-otp-26 1.17.0-otp-27 1.17.0-rc.0 1.17.0-rc.0-otp-25 1.17.0-rc.0-otp-26 1.17.0-rc.0-otp-27 1.17.0-rc.1 1.17.0-rc.1-otp-25 1.17.0-rc.1-otp-26 1.17.0-rc.1-otp-27 1.17.1 1.17.1-otp-25 1.17.1-otp-26 1.17.1-otp-27 1.17.2 1.17.2-otp-25 1.17.2-otp-26 1.17.2-otp-27 1.17.3 1.17.3-otp-25 1.17.3-otp-26 1.17.3-otp-27 1.18.0 1.18.0-otp-25 1.18.0-otp-26 1.18.0-otp-27 1.18.0-rc.0 1.18.0-rc.0-otp-25 1.18.0-rc.0-otp-26 1.18.0-rc.0-otp-27 1.18.1 1.18.1-otp-25 1.18.1-otp-26 1.18.1-otp-27 1.18.2 1.18.2-otp-25 1.18.2-otp-26 1.18.2-otp-27 main main-otp-22 main-otp-23 main-otp-24 main-otp-25 main-otp-26 main-otp-27 master master-otp-21 master-otp-22 master-otp-23 master-otp-24 "
  },
  {
    "path": "internal/versions/testdata/list-all-python",
    "content": "2.1.3 2.2.3 2.3.7 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.5.0 2.5.1 2.5.2 2.5.3 2.5.4 2.5.5 2.5.6 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.6.5 2.6.6 2.6.7 2.6.8 2.6.9 2.7.0 2.7-dev 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 2.7.8 2.7.9 2.7.10 2.7.11 2.7.12 2.7.13 2.7.14 2.7.15 2.7.16 2.7.17 2.7.18 3.0.1 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.2.0 3.2.1 3.2.2 3.2.3 3.2.4 3.2.5 3.2.6 3.3.0 3.3.1 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 3.3.7 3.4.0 3.4-dev 3.4.1 3.4.2 3.4.3 3.4.4 3.4.5 3.4.6 3.4.7 3.4.8 3.4.9 3.4.10 3.5.0 3.5-dev 3.5.1 3.5.2 3.5.3 3.5.4 3.5.5 3.5.6 3.5.7 3.5.8 3.5.9 3.5.10 3.6.0 3.6-dev 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.6.8 3.6.9 3.6.10 3.6.11 3.6.12 3.6.13 3.6.14 3.6.15 3.7.0 3.7-dev 3.7.1 3.7.2 3.7.3 3.7.4 3.7.5 3.7.6 3.7.7 3.7.8 3.7.9 3.7.10 3.7.11 3.7.12 3.7.13 3.7.14 3.7.15 3.7.16 3.7.17 3.8.0 3.8-dev 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.8.6 3.8.7 3.8.8 3.8.9 3.8.10 3.8.11 3.8.12 3.8.13 3.8.14 3.8.15 3.8.16 3.8.17 3.8.18 3.8.19 3.8.20 3.9.0 3.9-dev 3.9.1 3.9.2 3.9.4 3.9.5 3.9.6 3.9.7 3.9.8 3.9.9 3.9.10 3.9.11 3.9.12 3.9.13 3.9.14 3.9.15 3.9.16 3.9.17 3.9.18 3.9.19 3.9.20 3.9.21 3.10.0 3.10-dev 3.10.1 3.10.2 3.10.3 3.10.4 3.10.5 3.10.6 3.10.7 3.10.8 3.10.9 3.10.10 3.10.11 3.10.12 3.10.13 3.10.14 3.10.15 3.10.16 3.11.0 3.11-dev 3.11.1 3.11.2 3.11.3 3.11.4 3.11.5 3.11.6 3.11.7 3.11.8 3.11.9 3.11.10 3.11.11 3.12.0 3.12-dev 3.12.1 3.12.2 3.12.3 3.12.4 3.12.5 3.12.6 3.12.7 3.12.8 3.12.9 3.13.0 3.13.0t 3.13-dev 3.13t-dev 3.13.1 3.13.1t 3.13.2 3.13.2t 3.14.0a5 3.14.0a5t 3.14-dev 3.14t-dev activepython-2.7.14 activepython-3.5.4 activepython-3.6.0 anaconda-1.4.0 anaconda-1.5.0 anaconda-1.5.1 anaconda-1.6.0 anaconda-1.6.1 anaconda-1.7.0 anaconda-1.8.0 anaconda-1.9.0 anaconda-1.9.1 anaconda-1.9.2 anaconda-2.0.0 anaconda-2.0.1 anaconda-2.1.0 anaconda-2.2.0 anaconda-2.3.0 anaconda-2.4.0 anaconda-4.0.0 anaconda2-2.4.0 anaconda2-2.4.1 anaconda2-2.5.0 anaconda2-4.0.0 anaconda2-4.1.0 anaconda2-4.1.1 anaconda2-4.2.0 anaconda2-4.3.0 anaconda2-4.3.1 anaconda2-4.4.0 anaconda2-5.0.0 anaconda2-5.0.1 anaconda2-5.1.0 anaconda2-5.2.0 anaconda2-5.3.0 anaconda2-5.3.1 anaconda2-2018.12 anaconda2-2019.03 anaconda2-2019.07 anaconda2-2019.10 anaconda3-2.0.0 anaconda3-2.0.1 anaconda3-2.1.0 anaconda3-2.2.0 anaconda3-2.3.0 anaconda3-2.4.0 anaconda3-2.4.1 anaconda3-2.5.0 anaconda3-4.0.0 anaconda3-4.1.0 anaconda3-4.1.1 anaconda3-4.2.0 anaconda3-4.3.0 anaconda3-4.3.1 anaconda3-4.4.0 anaconda3-5.0.0 anaconda3-5.0.1 anaconda3-5.1.0 anaconda3-5.2.0 anaconda3-5.3.0 anaconda3-5.3.1 anaconda3-2018.12 anaconda3-2019.03 anaconda3-2019.07 anaconda3-2019.10 anaconda3-2020.02 anaconda3-2020.07 anaconda3-2020.11 anaconda3-2021.04 anaconda3-2021.05 anaconda3-2021.11 anaconda3-2022.05 anaconda3-2022.10 anaconda3-2023.03-0 anaconda3-2023.03 anaconda3-2023.03-1 anaconda3-2023.07-0 anaconda3-2023.07-1 anaconda3-2023.07-2 anaconda3-2023.09-0 anaconda3-2024.02-1 anaconda3-2024.06-1 anaconda3-2024.10-1 cinder-3.8-dev cinder-3.10-dev graalpy-dev graalpy-community-23.1.0 graalpy-community-23.1.2 graalpy-community-24.0.0 graalpy-community-24.1.0 graalpy-community-24.1.1 graalpy-community-24.1.2 graalpy-22.3.0 graalpy-23.0.0 graalpy-23.1.0 graalpy-23.1.2 graalpy-24.0.0 graalpy-24.1.0 graalpy-24.1.1 graalpy-24.1.2 graalpython-20.1.0 graalpython-20.2.0 graalpython-20.3.0 graalpython-21.0.0 graalpython-21.1.0 graalpython-21.2.0 graalpython-21.3.0 graalpython-22.0.0 graalpython-22.1.0 graalpython-22.2.0 ironpython-dev ironpython-2.7.4 ironpython-2.7.5 ironpython-2.7.6.3 ironpython-2.7.7 jython-dev jython-2.5.0 jython-2.5-dev jython-2.5.1 jython-2.5.2 jython-2.5.3 jython-2.5.4-rc1 jython-2.7.0 jython-2.7.1 jython-2.7.2 jython-2.7.3 mambaforge-pypy3 mambaforge mambaforge-4.10.1-4 mambaforge-4.10.1-5 mambaforge-4.10.2-0 mambaforge-4.10.3-0 mambaforge-4.10.3-1 mambaforge-4.10.3-2 mambaforge-4.10.3-3 mambaforge-4.10.3-4 mambaforge-4.10.3-5 mambaforge-4.10.3-6 mambaforge-4.10.3-7 mambaforge-4.10.3-8 mambaforge-4.10.3-9 mambaforge-4.10.3-10 mambaforge-4.11.0-0 mambaforge-4.11.0-1 mambaforge-4.11.0-2 mambaforge-4.11.0-3 mambaforge-4.11.0-4 mambaforge-4.12.0-0 mambaforge-4.12.0-1 mambaforge-4.12.0-2 mambaforge-4.12.0-3 mambaforge-4.13.0-1 mambaforge-4.14.0-0 mambaforge-4.14.0-1 mambaforge-4.14.0-2 mambaforge-22.9.0-0 mambaforge-22.9.0-1 mambaforge-22.9.0-2 mambaforge-22.9.0-3 mambaforge-22.11.1-3 mambaforge-22.11.1-4 mambaforge-23.1.0-0 mambaforge-23.1.0-1 mambaforge-23.1.0-2 mambaforge-23.1.0-3 mambaforge-23.1.0-4 mambaforge-23.3.0-0 mambaforge-23.3.1-0 mambaforge-23.3.1-1 mambaforge-23.10.0-0 mambaforge-23.11.0-0 mambaforge-24.1.2-0 mambaforge-24.3.0-0 mambaforge-24.5.0-0 mambaforge-24.7.1-0 mambaforge-24.7.1-1 mambaforge-24.7.1-2 mambaforge-24.9.0-0 mambaforge-24.9.2-0 mambaforge-24.11.0-0 mambaforge-24.11.0-1 micropython-dev micropython-1.9.3 micropython-1.9.4 micropython-1.10 micropython-1.11 micropython-1.12 micropython-1.13 micropython-1.14 micropython-1.15 micropython-1.16 micropython-1.17 micropython-1.18 micropython-1.19.1 micropython-1.20.0 micropython-1.21.0 miniconda-latest miniconda-2.2.2 miniconda-3.0.0 miniconda-3.0.4 miniconda-3.0.5 miniconda-3.3.0 miniconda-3.4.2 miniconda-3.7.0 miniconda-3.8.3 miniconda-3.9.1 miniconda-3.10.1 miniconda-3.16.0 miniconda-3.18.3 miniconda2-latest miniconda2-2.7-4.8.3 miniconda2-3.18.3 miniconda2-3.19.0 miniconda2-4.0.5 miniconda2-4.1.11 miniconda2-4.3.14 miniconda2-4.3.21 miniconda2-4.3.27 miniconda2-4.3.30 miniconda2-4.3.31 miniconda2-4.4.10 miniconda2-4.5.1 miniconda2-4.5.4 miniconda2-4.5.11 miniconda2-4.5.12 miniconda2-4.6.14 miniconda2-4.7.10 miniconda2-4.7.12 miniconda3-latest miniconda3-2.2.2 miniconda3-3.0.0 miniconda3-3.0.4 miniconda3-3.0.5 miniconda3-3.3.0 miniconda3-3.4.2 miniconda3-3.7.0 miniconda3-3.7-4.8.2 miniconda3-3.7-4.8.3 miniconda3-3.7-4.9.2 miniconda3-3.7-4.10.1 miniconda3-3.7-4.10.3 miniconda3-3.7-4.11.0 miniconda3-3.7-4.12.0 miniconda3-3.7-22.11.1-1 miniconda3-3.7-23.1.0-1 miniconda3-3.8.3 miniconda3-3.8-4.8.2 miniconda3-3.8-4.8.3 miniconda3-3.8-4.9.2 miniconda3-3.8-4.10.1 miniconda3-3.8-4.10.3 miniconda3-3.8-4.11.0 miniconda3-3.8-4.12.0 miniconda3-3.8-22.11.1-1 miniconda3-3.8-23.1.0-1 miniconda3-3.8-23.3.1-0 miniconda3-3.8-23.5.0-3 miniconda3-3.8-23.5.1-0 miniconda3-3.8-23.5.2-0 miniconda3-3.8-23.9.0-0 miniconda3-3.8-23.10.0-1 miniconda3-3.8-23.11.0-1 miniconda3-3.8-23.11.0-2 miniconda3-3.9.1 miniconda3-3.9-4.9.2 miniconda3-3.9-4.10.1 miniconda3-3.9-4.10.3 miniconda3-3.9-4.11.0 miniconda3-3.9-4.12.0 miniconda3-3.9-22.11.1-1 miniconda3-3.9-23.1.0-1 miniconda3-3.9-23.3.1-0 miniconda3-3.9-23.5.0-3 miniconda3-3.9-23.5.1-0 miniconda3-3.9-23.5.2-0 miniconda3-3.9-23.9.0-0 miniconda3-3.9-23.10.0-1 miniconda3-3.9-23.11.0-1 miniconda3-3.9-23.11.0-2 miniconda3-3.9-24.1.2-0 miniconda3-3.9-24.3.0-0 miniconda3-3.9-24.4.0-0 miniconda3-3.9-24.5.0-0 miniconda3-3.9-24.7.1-0 miniconda3-3.9-24.9.2-0 miniconda3-3.9-24.11.1-0 miniconda3-3.9-25.1.1-0 miniconda3-3.9-25.1.1-1 miniconda3-3.9-25.1.1-2 miniconda3-3.10.1 miniconda3-3.10-22.11.1-1 miniconda3-3.10-23.1.0-1 miniconda3-3.10-23.3.1-0 miniconda3-3.10-23.5.0-3 miniconda3-3.10-23.5.1-0 miniconda3-3.10-23.5.2-0 miniconda3-3.10-23.9.0-0 miniconda3-3.10-23.10.0-1 miniconda3-3.10-23.11.0-1 miniconda3-3.10-23.11.0-2 miniconda3-3.10-24.1.2-0 miniconda3-3.10-24.3.0-0 miniconda3-3.10-24.4.0-0 miniconda3-3.10-24.5.0-0 miniconda3-3.10-24.7.1-0 miniconda3-3.10-24.9.2-0 miniconda3-3.10-24.11.1-0 miniconda3-3.10-25.1.1-0 miniconda3-3.10-25.1.1-1 miniconda3-3.10-25.1.1-2 miniconda3-3.11-23.5.0-3 miniconda3-3.11-23.5.1-0 miniconda3-3.11-23.5.2-0 miniconda3-3.11-23.9.0-0 miniconda3-3.11-23.10.0-1 miniconda3-3.11-23.11.0-1 miniconda3-3.11-23.11.0-2 miniconda3-3.11-24.1.2-0 miniconda3-3.11-24.3.0-0 miniconda3-3.11-24.4.0-0 miniconda3-3.11-24.5.0-0 miniconda3-3.11-24.7.1-0 miniconda3-3.11-24.9.2-0 miniconda3-3.11-24.11.1-0 miniconda3-3.11-25.1.1-0 miniconda3-3.11-25.1.1-1 miniconda3-3.11-25.1.1-2 miniconda3-3.12-24.1.2-0 miniconda3-3.12-24.3.0-0 miniconda3-3.12-24.4.0-0 miniconda3-3.12-24.5.0-0 miniconda3-3.12-24.7.1-0 miniconda3-3.12-24.9.2-0 miniconda3-3.12-24.11.1-0 miniconda3-3.12-25.1.1-0 miniconda3-3.12-25.1.1-1 miniconda3-3.12-25.1.1-2 miniconda3-3.16.0 miniconda3-3.18.3 miniconda3-3.19.0 miniconda3-4.0.5 miniconda3-4.1.11 miniconda3-4.2.12 miniconda3-4.3.11 miniconda3-4.3.14 miniconda3-4.3.21 miniconda3-4.3.27 miniconda3-4.3.30 miniconda3-4.3.31 miniconda3-4.4.10 miniconda3-4.5.1 miniconda3-4.5.4 miniconda3-4.5.11 miniconda3-4.5.12 miniconda3-4.6.14 miniconda3-4.7.10 miniconda3-4.7.12 miniforge-pypy3 miniforge3-latest miniforge3-4.9.2 miniforge3-4.10 miniforge3-4.10.1-1 miniforge3-4.10.1-3 miniforge3-4.10.1-5 miniforge3-4.10.2-0 miniforge3-4.10.3-0 miniforge3-4.10.3-1 miniforge3-4.10.3-2 miniforge3-4.10.3-3 miniforge3-4.10.3-4 miniforge3-4.10.3-5 miniforge3-4.10.3-6 miniforge3-4.10.3-7 miniforge3-4.10.3-8 miniforge3-4.10.3-9 miniforge3-4.10.3-10 miniforge3-4.11.0-0 miniforge3-4.11.0-1 miniforge3-4.11.0-2 miniforge3-4.11.0-3 miniforge3-4.11.0-4 miniforge3-4.12.0-0 miniforge3-4.12.0-1 miniforge3-4.12.0-2 miniforge3-4.12.0-3 miniforge3-4.13.0-0 miniforge3-4.13.0-1 miniforge3-4.14.0-0 miniforge3-4.14.0-1 miniforge3-4.14.0-2 miniforge3-22.9.0-0 miniforge3-22.9.0-1 miniforge3-22.9.0-2 miniforge3-22.9.0-3 miniforge3-22.11.1-3 miniforge3-22.11.1-4 miniforge3-23.1.0-0 miniforge3-23.1.0-1 miniforge3-23.1.0-2 miniforge3-23.1.0-3 miniforge3-23.1.0-4 miniforge3-23.3.0-0 miniforge3-23.3.1-0 miniforge3-23.3.1-1 miniforge3-23.10.0-0 miniforge3-23.11.0-0 miniforge3-24.1.2-0 miniforge3-24.3.0-0 miniforge3-24.5.0-0 miniforge3-24.7.1-0 miniforge3-24.7.1-1 miniforge3-24.7.1-2 miniforge3-24.9.0-0 miniforge3-24.9.2-0 miniforge3-24.11.0-0 miniforge3-24.11.0-1 miniforge3-24.11.2-0 miniforge3-24.11.2-1 miniforge3-24.11.3-0 miniforge3-25.1.1-0 nogil-3.9.10 nogil-3.9.10-1 pypy-c-jit-latest pypy-dev pypy-stm-2.3 pypy-stm-2.5.1 pypy-1.5-src pypy-1.6 pypy-1.7 pypy-1.8 pypy-1.9 pypy-2.0-src pypy-2.0 pypy-2.0.1-src pypy-2.0.1 pypy-2.0.2-src pypy-2.0.2 pypy-2.1-src pypy-2.1 pypy-2.2-src pypy-2.2 pypy-2.2.1-src pypy-2.2.1 pypy-2.3-src pypy-2.3 pypy-2.3.1-src pypy-2.3.1 pypy-2.4.0-src pypy-2.4.0 pypy-2.5.0-src pypy-2.5.0 pypy-2.5.1-src pypy-2.5.1 pypy-2.6.0-src pypy-2.6.0 pypy-2.6.1-src pypy-2.6.1 pypy-4.0.0-src pypy-4.0.0 pypy-4.0.1-src pypy-4.0.1 pypy-5.0.0-src pypy-5.0.0 pypy-5.0.1-src pypy-5.0.1 pypy-5.1-src pypy-5.1 pypy-5.1.1-src pypy-5.1.1 pypy-5.3-src pypy-5.3 pypy-5.3.1-src pypy-5.3.1 pypy-5.4-src pypy-5.4 pypy-5.4.1-src pypy-5.4.1 pypy-5.6.0-src pypy-5.6.0 pypy-5.7.0-src pypy-5.7.0 pypy-5.7.1-src pypy-5.7.1 pypy2-5.3-src pypy2-5.3 pypy2-5.3.1-src pypy2-5.3.1 pypy2-5.4-src pypy2-5.4 pypy2-5.4.1-src pypy2-5.4.1 pypy2-5.6.0-src pypy2-5.6.0 pypy2-5.7.0-src pypy2-5.7.0 pypy2-5.7.1-src pypy2-5.7.1 pypy2.7-5.8.0-src pypy2.7-5.8.0 pypy2.7-5.9.0-src pypy2.7-5.9.0 pypy2.7-5.10.0-src pypy2.7-5.10.0 pypy2.7-6.0.0-src pypy2.7-6.0.0 pypy2.7-7.0.0-src pypy2.7-7.0.0 pypy2.7-7.1.0-src pypy2.7-7.1.0 pypy2.7-7.1.1-src pypy2.7-7.1.1 pypy2.7-7.2.0-src pypy2.7-7.2.0 pypy2.7-7.3.0-src pypy2.7-7.3.0 pypy2.7-7.3.1-src pypy2.7-7.3.1 pypy2.7-7.3.2-src pypy2.7-7.3.2 pypy2.7-7.3.3-src pypy2.7-7.3.3 pypy2.7-7.3.4-src pypy2.7-7.3.4 pypy2.7-7.3.5-src pypy2.7-7.3.5 pypy2.7-7.3.6-src pypy2.7-7.3.6 pypy2.7-7.3.8-src pypy2.7-7.3.8 pypy2.7-7.3.9-src pypy2.7-7.3.9 pypy2.7-7.3.10-src pypy2.7-7.3.10 pypy2.7-7.3.11-src pypy2.7-7.3.11 pypy2.7-7.3.12-src pypy2.7-7.3.12 pypy2.7-7.3.13-src pypy2.7-7.3.13 pypy2.7-7.3.14-src pypy2.7-7.3.14 pypy2.7-7.3.15-src pypy2.7-7.3.15 pypy2.7-7.3.16-src pypy2.7-7.3.16 pypy2.7-7.3.17-src pypy2.7-7.3.17 pypy2.7-7.3.18-src pypy2.7-7.3.18 pypy3-2.3.1-src pypy3-2.3.1 pypy3-2.4.0-src pypy3-2.4.0 pypy3.3-5.2-alpha1-src pypy3.3-5.2-alpha1 pypy3.3-5.5-alpha-src pypy3.3-5.5-alpha pypy3.5-c-jit-latest pypy3.5-5.7-beta-src pypy3.5-5.7-beta pypy3.5-5.7.1-beta-src pypy3.5-5.7.1-beta pypy3.5-5.8.0-src pypy3.5-5.8.0 pypy3.5-5.9.0-src pypy3.5-5.9.0 pypy3.5-5.10.0-src pypy3.5-5.10.0 pypy3.5-5.10.1-src pypy3.5-5.10.1 pypy3.5-6.0.0-src pypy3.5-6.0.0 pypy3.5-7.0.0-src pypy3.5-7.0.0 pypy3.6-7.0.0-src pypy3.6-7.0.0 pypy3.6-7.1.0-src pypy3.6-7.1.0 pypy3.6-7.1.1-src pypy3.6-7.1.1 pypy3.6-7.2.0-src pypy3.6-7.2.0 pypy3.6-7.3.0-src pypy3.6-7.3.0 pypy3.6-7.3.1-src pypy3.6-7.3.1 pypy3.6-7.3.2-src pypy3.6-7.3.2 pypy3.6-7.3.3-src pypy3.6-7.3.3 pypy3.7-c-jit-latest pypy3.7-7.3.2-src pypy3.7-7.3.2 pypy3.7-7.3.3-src pypy3.7-7.3.3 pypy3.7-7.3.4-src pypy3.7-7.3.4 pypy3.7-7.3.5-src pypy3.7-7.3.5 pypy3.7-7.3.6-src pypy3.7-7.3.6 pypy3.7-7.3.7-src pypy3.7-7.3.7 pypy3.7-7.3.8-src pypy3.7-7.3.8 pypy3.7-7.3.9-src pypy3.7-7.3.9 pypy3.8-7.3.6-src pypy3.8-7.3.6 pypy3.8-7.3.7-src pypy3.8-7.3.7 pypy3.8-7.3.8-src pypy3.8-7.3.8 pypy3.8-7.3.9-src pypy3.8-7.3.9 pypy3.8-7.3.10-src pypy3.8-7.3.10 pypy3.8-7.3.11-src pypy3.8-7.3.11 pypy3.9-7.3.8-src pypy3.9-7.3.8 pypy3.9-7.3.9-src pypy3.9-7.3.9 pypy3.9-7.3.10-src pypy3.9-7.3.10 pypy3.9-7.3.11-src pypy3.9-7.3.11 pypy3.9-7.3.12-src pypy3.9-7.3.12 pypy3.9-7.3.13-src pypy3.9-7.3.13 pypy3.9-7.3.14-src pypy3.9-7.3.14 pypy3.9-7.3.15-src pypy3.9-7.3.15 pypy3.9-7.3.16-src pypy3.9-7.3.16 pypy3.10-7.3.12-src pypy3.10-7.3.12 pypy3.10-7.3.13-src pypy3.10-7.3.13 pypy3.10-7.3.14-src pypy3.10-7.3.14 pypy3.10-7.3.15-src pypy3.10-7.3.15 pypy3.10-7.3.16-src pypy3.10-7.3.16 pypy3.10-7.3.17-src pypy3.10-7.3.17 pypy3.10-7.3.18-src pypy3.10-7.3.18 pypy3.11-7.3.18-src pypy3.11-7.3.18 pyston-2.2 pyston-2.3 pyston-2.3.1 pyston-2.3.2 pyston-2.3.3 pyston-2.3.4 pyston-2.3.5 stackless-dev stackless-2.7-dev stackless-2.7.2 stackless-2.7.3 stackless-2.7.4 stackless-2.7.5 stackless-2.7.6 stackless-2.7.7 stackless-2.7.8 stackless-2.7.9 stackless-2.7.10 stackless-2.7.11 stackless-2.7.12 stackless-2.7.14 stackless-2.7.16 stackless-3.2.2 stackless-3.2.5 stackless-3.3.5 stackless-3.3.7 stackless-3.4-dev stackless-3.4.2 stackless-3.4.7 stackless-3.5.4 stackless-3.7.5 "
  },
  {
    "path": "internal/versions/testdata/list-all-ruby",
    "content": "1.8.5-p52 1.8.5-p113 1.8.5-p114 1.8.5-p115 1.8.5-p231 1.8.6 1.8.6-p36 1.8.6-p110 1.8.6-p111 1.8.6-p114 1.8.6-p230 1.8.6-p286 1.8.6-p287 1.8.6-p368 1.8.6-p369 1.8.6-p383 1.8.6-p388 1.8.6-p398 1.8.6-p399 1.8.6-p420 1.8.7-preview1 1.8.7-preview2 1.8.7-preview3 1.8.7-preview4 1.8.7 1.8.7-p17 1.8.7-p22 1.8.7-p71 1.8.7-p72 1.8.7-p160 1.8.7-p173 1.8.7-p174 1.8.7-p248 1.8.7-p249 1.8.7-p299 1.8.7-p301 1.8.7-p302 1.8.7-p330 1.8.7-p334 1.8.7-p352 1.8.7-p357 1.8.7-p358 1.8.7-p370 1.8.7-p371 1.8.7-p373 1.8.7-p374 1.9.0-0 1.9.0-1 1.9.0-2 1.9.0-3 1.9.0-4 1.9.0-5 1.9.1-preview1 1.9.1-preview2 1.9.1-rc1 1.9.1-rc2 1.9.1-p0 1.9.1-p129 1.9.1-p243 1.9.1-p376 1.9.1-p378 1.9.1-p429 1.9.1-p430 1.9.1-p431 1.9.2-preview1 1.9.2-preview3 1.9.2-rc1 1.9.2-rc2 1.9.2-p0 1.9.2-p136 1.9.2-p180 1.9.2-p290 1.9.2-p318 1.9.2-p320 1.9.2-p330 1.9.3-dev 1.9.3-preview1 1.9.3-rc1 1.9.3-p0 1.9.3-p105 1.9.3-p125 1.9.3-p194 1.9.3-p286 1.9.3-p327 1.9.3-p362 1.9.3-p374 1.9.3-p385 1.9.3-p392 1.9.3-p426 1.9.3-p429 1.9.3-p448 1.9.3-p484 1.9.3-p545 1.9.3-p547 1.9.3-p550 1.9.3-p551 2.0.0-dev 2.0.0-preview1 2.0.0-preview2 2.0.0-rc1 2.0.0-rc2 2.0.0-p0 2.0.0-p195 2.0.0-p247 2.0.0-p353 2.0.0-p451 2.0.0-p481 2.0.0-p576 2.0.0-p594 2.0.0-p598 2.0.0-p643 2.0.0-p645 2.0.0-p647 2.0.0-p648 2.1.0-preview1 2.1.0-preview2 2.1.0-rc1 2.1.0 2.1-dev 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.1.10 2.2.0-preview1 2.2.0-preview2 2.2.0-rc1 2.2.0 2.2-dev 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.2.7 2.2.8 2.2.9 2.2.10 2.3.0-preview1 2.3.0-preview2 2.3.0 2.3-dev 2.3.1 2.3.2 2.3.3 2.3.4 2.3.5 2.3.6 2.3.7 2.3.8 2.4.0-preview1 2.4.0-preview2 2.4.0-preview3 2.4.0-rc1 2.4.0 2.4-dev 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.4.7 2.4.8 2.4.9 2.4.10 2.5.0-preview1 2.5.0-rc1 2.5.0 2.5-dev 2.5.1 2.5.2 2.5.3 2.5.4 2.5.5 2.5.6 2.5.7 2.5.8 2.5.9 2.6.0-preview1 2.6.0-preview2 2.6.0-preview3 2.6.0-rc1 2.6.0-rc2 2.6.0 2.6-dev 2.6.1 2.6.2 2.6.3 2.6.4 2.6.5 2.6.6 2.6.7 2.6.8 2.6.9 2.6.10 2.7.0-preview1 2.7.0-preview2 2.7.0-preview3 2.7.0-rc1 2.7.0-rc2 2.7.0 2.7-dev 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 2.7.8 3.0.0-preview1 3.0.0-preview2 3.0.0-rc1 3.0.0 3.0-dev 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.1.0-preview1 3.1.0 3.1-dev 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.2.0-preview1 3.2.0-preview2 3.2.0-preview3 3.2.0-rc1 3.2.0 3.2-dev 3.2.1 3.2.2 3.2.3 3.2.4 3.2.5 3.2.6 3.2.7 3.3.0-preview1 3.3.0-preview2 3.3.0-preview3 3.3.0-rc1 3.3.0 3.3-dev 3.3.1 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 3.3.7 3.4.0-preview1 3.4.0-preview2 3.4.0-rc1 3.4.0 3.4-dev 3.4.1 3.4.2 3.5-dev artichoke-dev jruby-dev jruby-1.7.2 jruby-1.7.5 jruby-1.7.6 jruby-1.7.7 jruby-1.7.8 jruby-1.7.9 jruby-1.7.10 jruby-1.7.11 jruby-1.7.12 jruby-1.7.13 jruby-1.7.14 jruby-1.7.15 jruby-1.7.16 jruby-1.7.16.1 jruby-1.7.16.2 jruby-1.7.17 jruby-1.7.18 jruby-1.7.19 jruby-1.7.20 jruby-1.7.20.1 jruby-1.7.21 jruby-1.7.22 jruby-1.7.23 jruby-1.7.24 jruby-1.7.25 jruby-1.7.26 jruby-1.7.27 jruby-9.0.0.0.pre1 jruby-9.0.0.0.pre2 jruby-9.0.0.0.rc1 jruby-9.0.0.0.rc2 jruby-9.0.0.0 jruby-9.0.1.0 jruby-9.0.3.0 jruby-9.0.4.0 jruby-9.0.5.0 jruby-9.1.0.0-dev jruby-9.1.0.0 jruby-9.1.1.0 jruby-9.1.2.0 jruby-9.1.3.0 jruby-9.1.4.0 jruby-9.1.5.0 jruby-9.1.6.0 jruby-9.1.7.0 jruby-9.1.8.0 jruby-9.1.9.0 jruby-9.1.10.0 jruby-9.1.11.0 jruby-9.1.12.0 jruby-9.1.13.0 jruby-9.1.14.0 jruby-9.1.15.0 jruby-9.1.16.0 jruby-9.1.17.0 jruby-9.2.0.0-dev jruby-9.2.0.0 jruby-9.2.1.0-dev jruby-9.2.1.0 jruby-9.2.3.0 jruby-9.2.4.0 jruby-9.2.4.1 jruby-9.2.5.0 jruby-9.2.6.0 jruby-9.2.7.0 jruby-9.2.8.0 jruby-9.2.9.0 jruby-9.2.10.0 jruby-9.2.11.0 jruby-9.2.11.1 jruby-9.2.12.0 jruby-9.2.13.0 jruby-9.2.14.0 jruby-9.2.15.0 jruby-9.2.16.0 jruby-9.2.17.0 jruby-9.2.18.0 jruby-9.2.19.0 jruby-9.2.20.0 jruby-9.2.20.1 jruby-9.2.21.0 jruby-9.3.0.0 jruby-9.3.1.0 jruby-9.3.2.0 jruby-9.3.3.0 jruby-9.3.4.0 jruby-9.3.6.0 jruby-9.3.7.0 jruby-9.3.8.0 jruby-9.3.9.0 jruby-9.3.10.0 jruby-9.3.11.0 jruby-9.3.13.0 jruby-9.3.14.0 jruby-9.3.15.0 jruby-9.4.0.0 jruby-9.4.1.0 jruby-9.4.2.0 jruby-9.4.3.0 jruby-9.4.4.0 jruby-9.4.5.0 jruby-9.4.6.0 jruby-9.4.7.0 jruby-9.4.8.0 jruby-9.4.9.0 jruby-9.4.10.0 jruby-9.4.11.0 jruby-9.4.12.0 mruby-dev mruby-1.0.0 mruby-1.1.0 mruby-1.2.0 mruby-1.3.0 mruby-1.4.0 mruby-1.4.1 mruby-2.0.0 mruby-2.0.1 mruby-2.1.0 mruby-2.1.1 mruby-2.1.2 mruby-3.0.0 mruby-3.1.0 mruby-3.2.0 mruby-3.3.0 picoruby-3.0.0 rbx-2.2.2 rbx-2.2.3 rbx-2.2.4 rbx-2.2.5 rbx-2.2.6 rbx-2.2.7 rbx-2.2.8 rbx-2.2.9 rbx-2.2.10 rbx-2.3.0 rbx-2.4.0 rbx-2.4.1 rbx-2.5.0 rbx-2.5.1 rbx-2.5.2 rbx-2.5.3 rbx-2.5.4 rbx-2.5.5 rbx-2.5.6 rbx-2.5.7 rbx-2.5.8 rbx-2.6 rbx-2.7 rbx-2.8 rbx-2.9 rbx-2.10 rbx-2.11 rbx-2.71828182 rbx-3.0 rbx-3.1 rbx-3.2 rbx-3.3 rbx-3.4 rbx-3.5 rbx-3.6 rbx-3.7 rbx-3.8 rbx-3.9 rbx-3.10 rbx-3.11 rbx-3.12 rbx-3.13 rbx-3.14 rbx-3.15 rbx-3.16 rbx-3.17 rbx-3.18 rbx-3.19 rbx-3.20 rbx-3.21 rbx-3.22 rbx-3.23 rbx-3.24 rbx-3.25 rbx-3.26 rbx-3.27 rbx-3.28 rbx-3.29 rbx-3.30 rbx-3.31 rbx-3.32 rbx-3.33 rbx-3.34 rbx-3.35 rbx-3.36 rbx-3.37 rbx-3.38 rbx-3.39 rbx-3.40 rbx-3.41 rbx-3.42 rbx-3.43 rbx-3.44 rbx-3.45 rbx-3.46 rbx-3.47 rbx-3.48 rbx-3.49 rbx-3.50 rbx-3.51 rbx-3.52 rbx-3.53 rbx-3.54 rbx-3.55 rbx-3.56 rbx-3.57 rbx-3.58 rbx-3.59 rbx-3.60 rbx-3.61 rbx-3.62 rbx-3.63 rbx-3.64 rbx-3.65 rbx-3.66 rbx-3.67 rbx-3.68 rbx-3.69 rbx-3.70 rbx-3.71 rbx-3.72 rbx-3.73 rbx-3.74 rbx-3.75 rbx-3.76 rbx-3.77 rbx-3.78 rbx-3.79 rbx-3.80 rbx-3.81 rbx-3.82 rbx-3.83 rbx-3.84 rbx-3.85 rbx-3.86 rbx-3.87 rbx-3.88 rbx-3.89 rbx-3.90 rbx-3.91 rbx-3.92 rbx-3.93 rbx-3.94 rbx-3.95 rbx-3.96 rbx-3.97 rbx-3.98 rbx-3.99 rbx-3.100 rbx-3.101 rbx-3.102 rbx-3.103 rbx-3.104 rbx-3.105 rbx-3.106 rbx-3.107 rbx-4.0 rbx-4.1 rbx-4.2 rbx-4.3 rbx-4.4 rbx-4.5 rbx-4.6 rbx-4.7 rbx-4.8 rbx-4.9 rbx-4.10 rbx-4.11 rbx-4.12 rbx-4.13 rbx-4.14 rbx-4.15 rbx-4.16 rbx-4.18 rbx-4.19 rbx-4.20 rbx-5.0 ree-1.8.7-2011.03 ree-1.8.7-2011.12 ree-1.8.7-2012.01 ree-1.8.7-2012.02 ruby-dev truffleruby-dev truffleruby-1.0.0-rc10 truffleruby-1.0.0-rc11 truffleruby-1.0.0-rc12 truffleruby-1.0.0-rc13 truffleruby-1.0.0-rc14 truffleruby-1.0.0-rc15 truffleruby-1.0.0-rc16 truffleruby-1.0.0-rc2 truffleruby-1.0.0-rc3 truffleruby-1.0.0-rc5 truffleruby-1.0.0-rc6 truffleruby-1.0.0-rc7 truffleruby-1.0.0-rc8 truffleruby-1.0.0-rc9 truffleruby-19.0.0 truffleruby-19.1.0 truffleruby-19.2.0 truffleruby-19.2.0.1 truffleruby-19.3.0 truffleruby-19.3.0.2 truffleruby-19.3.1 truffleruby-20.0.0 truffleruby-20.1.0 truffleruby-20.2.0 truffleruby-20.3.0 truffleruby-21.0.0 truffleruby-21.1.0 truffleruby-21.2.0 truffleruby-21.2.0.1 truffleruby-21.3.0 truffleruby-22.0.0.2 truffleruby-22.1.0 truffleruby-22.2.0 truffleruby-22.3.0 truffleruby-22.3.1 truffleruby-23.0.0-preview1 truffleruby-23.0.0 truffleruby-23.1.0 truffleruby-23.1.1 truffleruby-23.1.2 truffleruby-24.0.0 truffleruby-24.0.1 truffleruby-24.0.2 truffleruby-24.1.0 truffleruby-24.1.1 truffleruby-24.1.2 truffleruby+graalvm-dev truffleruby+graalvm-20.1.0 truffleruby+graalvm-20.2.0 truffleruby+graalvm-20.3.0 truffleruby+graalvm-21.0.0 truffleruby+graalvm-21.1.0 truffleruby+graalvm-21.2.0 truffleruby+graalvm-21.3.0 truffleruby+graalvm-22.0.0.2 truffleruby+graalvm-22.1.0 truffleruby+graalvm-22.2.0 truffleruby+graalvm-22.3.0 truffleruby+graalvm-22.3.1 truffleruby+graalvm-23.0.0-preview1 truffleruby+graalvm-23.0.0 truffleruby+graalvm-23.1.0 truffleruby+graalvm-23.1.1 truffleruby+graalvm-23.1.2 truffleruby+graalvm-24.0.0 truffleruby+graalvm-24.0.1 truffleruby+graalvm-24.0.2 truffleruby+graalvm-24.1.0 truffleruby+graalvm-24.1.1 truffleruby+graalvm-24.1.2\n"
  },
  {
    "path": "internal/versions/testdata/uninstall-asdfrc",
    "content": "pre_asdf_uninstall_uninstall-test = echo pre_asdf_uninstall_test $@\npost_asdf_uninstall_uninstall-test = echo post_asdf_uninstall_test $@\n"
  },
  {
    "path": "internal/versions/versions.go",
    "content": "// Package versions handles all operations pertaining to specific versions.\n// Install, uninstall, etc...\npackage versions\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"regexp\"\n\t\"strings\"\n\n\t\"github.com/asdf-vm/asdf/internal/config\"\n\t\"github.com/asdf-vm/asdf/internal/hook\"\n\t\"github.com/asdf-vm/asdf/internal/installs\"\n\t\"github.com/asdf-vm/asdf/internal/plugins\"\n\t\"github.com/asdf-vm/asdf/internal/resolve\"\n\t\"github.com/asdf-vm/asdf/internal/shims\"\n\t\"github.com/asdf-vm/asdf/internal/toolversions\"\n)\n\nconst (\n\tsystemVersion           = \"system\"\n\tlatestVersion           = \"latest\"\n\tlatestFilterRegex       = \"(?i)(^Available versions:|-src|-dev|-latest|-stm|[-\\\\.]rc|-milestone|-alpha|-beta|[-\\\\.]pre|-next|(a|b|c)[0-9]+|snapshot|master|main)\"\n\tnumericStartFilterRegex = \"^\\\\s*[0-9]\"\n\tnoLatestVersionErrMsg   = \"no latest version found\"\n)\n\n// UninstallableVersionError is an error returned if someone tries to install the\n// system version.\ntype UninstallableVersionError struct {\n\ttoolName    string\n\tversionType string\n}\n\nfunc (e UninstallableVersionError) Error() string {\n\treturn fmt.Sprintf(\"uninstallable version %s of %s\", e.versionType, e.toolName)\n}\n\n// NoVersionSetError is returned whenever an operation that requires a version\n// is not able to resolve one.\ntype NoVersionSetError struct {\n\ttoolName string\n}\n\nfunc (e NoVersionSetError) Error() string {\n\t// Eventually switch this to a more friendly error message, BATS tests fail\n\t// with this improvement\n\t// return fmt.Sprintf(\"no version set for plugin %s\", e.toolName)\n\treturn \"no version set\"\n}\n\n// VersionAlreadyInstalledError is returned whenever a version is already\n// installed.\ntype VersionAlreadyInstalledError struct {\n\ttoolName string\n\tversion  toolversions.Version\n}\n\nfunc (e VersionAlreadyInstalledError) Error() string {\n\treturn fmt.Sprintf(\"version %s of %s is already installed\", e.version.Value, e.toolName)\n}\n\n// InstallAll installs all specified versions of every tool for the current\n// directory. Typically this will just be a single version, if not already\n// installed, but it may be multiple versions if multiple versions for the tool\n// are specified in the .tool-versions file.\nfunc InstallAll(conf config.Config, dir string, stdOut io.Writer, stdErr io.Writer) (failures []error) {\n\tplugins, err := plugins.List(conf, false, false)\n\tif err != nil {\n\t\treturn []error{fmt.Errorf(\"unable to list plugins: %w\", err)}\n\t}\n\n\t// Ideally we should install these in the order they are specified in the\n\t// closest .tool-versions file, but for now that is too complicated to\n\t// implement.\n\tfor _, plugin := range plugins {\n\t\terr := Install(conf, plugin, dir, stdOut, stdErr)\n\t\tif err != nil {\n\t\t\tfailures = append(failures, err)\n\t\t}\n\t}\n\n\treturn failures\n}\n\n// Install installs all specified versions of a tool for the current directory.\n// Typically this will just be a single version, if not already installed, but\n// it may be multiple versions if multiple versions for the tool are specified\n// in the .tool-versions file.\nfunc Install(conf config.Config, plugin plugins.Plugin, dir string, stdOut io.Writer, stdErr io.Writer) error {\n\terr := plugin.Exists()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tversions, found, err := resolve.Version(conf, plugin, dir)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif !found || len(versions.Versions) == 0 {\n\t\treturn NoVersionSetError{toolName: plugin.Name}\n\t}\n\n\tfor _, version := range versions.Versions {\n\t\tiErr := InstallOneVersion(conf, plugin, version, false, stdOut, stdErr)\n\t\tvar vaiErr VersionAlreadyInstalledError\n\t\tif errors.As(iErr, &vaiErr) {\n\t\t\terr = errors.Join(err, iErr)\n\t\t} else if iErr != nil {\n\t\t\treturn iErr\n\t\t}\n\t}\n\n\treturn err\n}\n\n// InstallVersion installs a version of a specific tool, the version may be an\n// exact version, or it may be `latest` or `latest` a regex query in order to\n// select the latest version matching the provided pattern.\nfunc InstallVersion(conf config.Config, plugin plugins.Plugin, version toolversions.Version, stdOut io.Writer, stdErr io.Writer) error {\n\terr := plugin.Exists()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tresolvedVersion := \"\"\n\tif version.Type == latestVersion {\n\t\tresolvedVersion, err = Latest(plugin, version.Value)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn InstallOneVersion(conf, plugin, resolvedVersion, false, stdOut, stdErr)\n}\n\n// InstallOneVersion installs a specific version of a specific tool\nfunc InstallOneVersion(conf config.Config, plugin plugins.Plugin, versionStr string, keepDownload bool, stdOut io.Writer, stdErr io.Writer) error {\n\terr := plugin.Exists()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif versionStr == systemVersion {\n\t\treturn UninstallableVersionError{toolName: plugin.Name, versionType: systemVersion}\n\t}\n\n\tversion := toolversions.Parse(versionStr)\n\n\tif version.Type == \"path\" {\n\t\treturn UninstallableVersionError{toolName: plugin.Name, versionType: \"path\"}\n\t}\n\tdownloadDir := installs.DownloadPath(conf, plugin, version)\n\tinstallDir := installs.InstallPath(conf, plugin, version)\n\n\tif installs.IsInstalled(conf, plugin, version) {\n\t\treturn VersionAlreadyInstalledError{version: version, toolName: plugin.Name}\n\t}\n\n\tconcurrency, _ := conf.Concurrency()\n\tenv := map[string]string{\n\t\t\"ASDF_INSTALL_TYPE\":    version.Type,\n\t\t\"ASDF_INSTALL_VERSION\": version.Value,\n\t\t\"ASDF_INSTALL_PATH\":    installDir,\n\t\t\"ASDF_DOWNLOAD_PATH\":   downloadDir,\n\t\t\"ASDF_CONCURRENCY\":     concurrency,\n\t}\n\n\terr = os.MkdirAll(downloadDir, 0o777)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to create download dir: %w\", err)\n\t}\n\n\terr = hook.RunWithOutput(conf, fmt.Sprintf(\"pre_asdf_download_%s\", plugin.Name), []string{version.Value}, stdOut, stdErr)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to run pre-download hook: %w\", err)\n\t}\n\n\terr = plugin.RunCallback(\"download\", []string{}, env, stdOut, stdErr)\n\tif _, ok := err.(plugins.NoCallbackError); err != nil && !ok {\n\t\treturn fmt.Errorf(\"failed to run download callback: %w\", err)\n\t}\n\n\terr = hook.RunWithOutput(conf, fmt.Sprintf(\"pre_asdf_install_%s\", plugin.Name), []string{version.Value}, stdOut, stdErr)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to run pre-install hook: %w\", err)\n\t}\n\n\terr = os.MkdirAll(installDir, 0o777)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to create install dir: %w\", err)\n\t}\n\n\terr = plugin.RunCallback(\"install\", []string{}, env, stdOut, stdErr)\n\tif err != nil {\n\t\tif rmErr := os.RemoveAll(installDir); rmErr != nil {\n\t\t\tfmt.Fprintf(stdErr, \"failed to clean up '%s' due to %s\\n\", installDir, rmErr)\n\t\t}\n\t\treturn fmt.Errorf(\"failed to run install callback: %w\", err)\n\t}\n\n\t// Reshim\n\terr = shims.GenerateAll(conf, stdOut, stdErr)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unable to generate shims post-install: %w\", err)\n\t}\n\n\terr = hook.RunWithOutput(conf, fmt.Sprintf(\"post_asdf_install_%s\", plugin.Name), []string{version.Value}, stdOut, stdErr)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to run post-install hook: %w\", err)\n\t}\n\n\t// delete download dir\n\tkeep, err := conf.AlwaysKeepDownload()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif keep || keepDownload {\n\t\treturn nil\n\t}\n\n\terr = os.RemoveAll(downloadDir)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to remove download dir: %w\", err)\n\t}\n\n\treturn nil\n}\n\n// Latest invokes the plugin's latest-stable callback if it exists and returns\n// the version it returns. If the callback is missing it invokes the list-all\n// callback and returns the last version matching the query, if a query is\n// provided.\nfunc Latest(plugin plugins.Plugin, query string) (version string, err error) {\n\tvar stdOut strings.Builder\n\tvar stdErr strings.Builder\n\n\terr = plugin.RunCallback(\"latest-stable\", []string{query}, map[string]string{}, &stdOut, &stdErr)\n\tif err == nil {\n\t\tversions := parseVersions(stdOut.String())\n\t\tif len(versions) < 1 {\n\t\t\treturn version, errors.New(noLatestVersionErrMsg)\n\t\t}\n\t\treturn versions[len(versions)-1], nil\n\t}\n\n\t// Fallback to list-all if latest-stable fails\n\tif _, ok := err.(plugins.NoCallbackError); !ok {\n\t\treturn version, err\n\t}\n\n\tallVersions, err := AllVersions(plugin)\n\tif err != nil {\n\t\treturn version, err\n\t}\n\n\tversions := filterByRegex(allVersions, latestFilterRegex, false)\n\n\t// If no query specified by user default to selecting version with numeric start\n\tif query == \"\" {\n\t\tversions = filterByRegex(versions, numericStartFilterRegex, true)\n\t} else {\n\t\tversions = filterByExactMatch(versions, query)\n\t}\n\n\tif len(versions) < 1 {\n\t\treturn version, errors.New(noLatestVersionErrMsg)\n\t}\n\n\treturn versions[len(versions)-1], nil\n}\n\n// AllVersions returns a slice of all available versions for the tool managed by\n// the given plugin by invoking the plugin's list-all callback\nfunc AllVersions(plugin plugins.Plugin) (versions []string, err error) {\n\tvar stdout strings.Builder\n\tvar stderr strings.Builder\n\n\terr = plugin.RunCallback(\"list-all\", []string{}, map[string]string{}, &stdout, &stderr)\n\tif err != nil {\n\t\treturn versions, err\n\t}\n\n\tversions = parseVersions(stdout.String())\n\n\treturn versions, err\n}\n\n// Uninstall uninstalls a specific tool version. It invokes pre and\n// post-uninstall hooks if set, and runs the plugin's uninstall callback if\n// defined.\nfunc Uninstall(conf config.Config, plugin plugins.Plugin, rawVersion string, stdout, stderr io.Writer) error {\n\tversion := toolversions.ParseFromCliArg(rawVersion)\n\n\tif version.Type == \"latest\" {\n\t\treturn errors.New(\"'latest' is a special version value that cannot be used for uninstall command\")\n\t}\n\n\tif !installs.IsInstalled(conf, plugin, version) {\n\t\treturn errors.New(\"No such version\")\n\t}\n\n\terr := hook.RunWithOutput(conf, fmt.Sprintf(\"pre_asdf_uninstall_%s\", plugin.Name), []string{version.Value}, stdout, stderr)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// invoke uninstall callback if available\n\tinstallDir := installs.InstallPath(conf, plugin, version)\n\tenv := map[string]string{\n\t\t\"ASDF_INSTALL_TYPE\":    version.Type,\n\t\t\"ASDF_INSTALL_VERSION\": version.Value,\n\t\t\"ASDF_INSTALL_PATH\":    installDir,\n\t}\n\terr = plugin.RunCallback(\"uninstall\", []string{}, env, stdout, stderr)\n\tif _, ok := err.(plugins.NoCallbackError); !ok && err != nil {\n\t\treturn err\n\t}\n\n\terr = os.RemoveAll(installDir)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = hook.RunWithOutput(conf, fmt.Sprintf(\"post_asdf_uninstall_%s\", plugin.Name), []string{version.Value}, stdout, stderr)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc filterByExactMatch(allVersions []string, pattern string) (versions []string) {\n\tfor _, version := range allVersions {\n\t\tif strings.HasPrefix(version, pattern) {\n\t\t\tversions = append(versions, version)\n\t\t}\n\t}\n\n\treturn versions\n}\n\nfunc filterByRegex(allVersions []string, pattern string, keepMatch bool) (versions []string) {\n\tregex, _ := regexp.Compile(pattern)\n\tfor _, version := range allVersions {\n\t\tmatch := regex.MatchString(version)\n\t\tif match == keepMatch {\n\t\t\tversions = append(versions, version)\n\t\t}\n\t}\n\n\treturn versions\n}\n\n// future refactoring opportunity: this function is an exact copy of\n// resolve.parseVersion\nfunc parseVersions(rawVersions string) []string {\n\tvar versions []string\n\tfor _, version := range strings.Split(rawVersions, \" \") {\n\t\tversion = strings.TrimSpace(version)\n\t\tif len(version) > 0 {\n\t\t\tversions = append(versions, version)\n\t\t}\n\t}\n\treturn versions\n}\n"
  },
  {
    "path": "internal/versions/versions_test.go",
    "content": "package versions\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/asdf-vm/asdf/internal/config\"\n\t\"github.com/asdf-vm/asdf/internal/plugins\"\n\t\"github.com/asdf-vm/asdf/internal/repotest\"\n\t\"github.com/asdf-vm/asdf/internal/toolversions\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nconst testPluginName = \"testlua\"\n\nfunc TestInstallAll(t *testing.T) {\n\tt.Run(\"installs multiple tools when multiple tool versions are specified\", func(t *testing.T) {\n\t\tconf, plugin := generateConfig(t)\n\t\tstdout, stderr := buildOutputs()\n\t\tcurrentDir := t.TempDir()\n\t\tsecondPlugin := installPlugin(t, conf, \"dummy_plugin\", \"another\")\n\t\tversion := \"1.0.0\"\n\n\t\t// write a version file\n\t\tcontent := fmt.Sprintf(\"%s %s\\n%s %s\", plugin.Name, version, secondPlugin.Name, version)\n\t\twriteVersionFile(t, currentDir, content)\n\n\t\terr := InstallAll(conf, currentDir, &stdout, &stderr)\n\t\tassert.Nil(t, err)\n\n\t\tassertVersionInstalled(t, conf.DataDir, plugin.Name, version)\n\t\tassertVersionInstalled(t, conf.DataDir, secondPlugin.Name, version)\n\t})\n\n\tt.Run(\"only installs tools with versions specified for current directory\", func(t *testing.T) {\n\t\tconf, plugin := generateConfig(t)\n\t\tstdout, stderr := buildOutputs()\n\t\tcurrentDir := t.TempDir()\n\t\tsecondPlugin := installPlugin(t, conf, \"dummy_plugin\", \"another\")\n\t\tversion := \"1.0.0\"\n\n\t\t// write a version file\n\t\tcontent := fmt.Sprintf(\"%s %s\\n\", plugin.Name, version)\n\t\twriteVersionFile(t, currentDir, content)\n\n\t\terr := InstallAll(conf, currentDir, &stdout, &stderr)\n\t\tassert.ErrorContains(t, err[0], \"no version set\")\n\n\t\tassertVersionInstalled(t, conf.DataDir, plugin.Name, version)\n\t\tassertNotInstalled(t, conf.DataDir, secondPlugin.Name, version)\n\t})\n\n\tt.Run(\"installs all tools even after one fails to install\", func(t *testing.T) {\n\t\tconf, plugin := generateConfig(t)\n\t\tstdout, stderr := buildOutputs()\n\t\tcurrentDir := t.TempDir()\n\t\tsecondPlugin := installPlugin(t, conf, \"dummy_plugin\", \"another\")\n\t\tversion := \"1.0.0\"\n\n\t\t// write a version file\n\t\tcontent := fmt.Sprintf(\"%s %s\\n%s %s\", secondPlugin.Name, \"non-existent-version\", plugin.Name, version)\n\t\twriteVersionFile(t, currentDir, content)\n\n\t\terr := InstallAll(conf, currentDir, &stdout, &stderr)\n\t\tassert.Empty(t, err)\n\n\t\tassertNotInstalled(t, conf.DataDir, secondPlugin.Name, version)\n\t\tassertVersionInstalled(t, conf.DataDir, plugin.Name, version)\n\t})\n}\n\nfunc TestInstall(t *testing.T) {\n\tconf, plugin := generateConfig(t)\n\tstdout, stderr := buildOutputs()\n\tcurrentDir := t.TempDir()\n\n\tt.Run(\"installs version of tool specified for current directory\", func(t *testing.T) {\n\t\tversion := \"1.0.0\"\n\t\t// write a version file\n\t\tdata := []byte(fmt.Sprintf(\"%s %s\", plugin.Name, version))\n\t\terr := os.WriteFile(filepath.Join(currentDir, \".tool-versions\"), data, 0o666)\n\t\tassert.Nil(t, err)\n\n\t\terr = Install(conf, plugin, currentDir, &stdout, &stderr)\n\t\tassert.Nil(t, err)\n\n\t\tassertVersionInstalled(t, conf.DataDir, plugin.Name, version)\n\t})\n\n\tt.Run(\"returns error when plugin doesn't exist\", func(t *testing.T) {\n\t\tconf, _ := generateConfig(t)\n\t\tstdout, stderr := buildOutputs()\n\t\terr := Install(conf, plugins.New(conf, \"non-existent\"), currentDir, &stdout, &stderr)\n\t\tassert.IsType(t, plugins.PluginMissing{}, err)\n\t})\n\n\tt.Run(\"returns error when no version set\", func(t *testing.T) {\n\t\tconf, _ := generateConfig(t)\n\t\tstdout, stderr := buildOutputs()\n\t\tcurrentDir := t.TempDir()\n\t\terr := Install(conf, plugin, currentDir, &stdout, &stderr)\n\t\tassert.EqualError(t, err, \"no version set\")\n\t})\n\n\tt.Run(\"if multiple versions are defined installs all of them\", func(t *testing.T) {\n\t\tconf, plugin := generateConfig(t)\n\t\tstdout, stderr := buildOutputs()\n\t\tcurrentDir := t.TempDir()\n\n\t\tversions := \"1.0.0 2.0.0\"\n\t\t// write a version file\n\t\tdata := []byte(fmt.Sprintf(\"%s %s\", plugin.Name, versions))\n\t\terr := os.WriteFile(filepath.Join(currentDir, \".tool-versions\"), data, 0o666)\n\t\tassert.Nil(t, err)\n\n\t\terr = Install(conf, plugin, currentDir, &stdout, &stderr)\n\t\tassert.Nil(t, err)\n\n\t\tassertVersionInstalled(t, conf.DataDir, plugin.Name, \"1.0.0\")\n\t\tassertVersionInstalled(t, conf.DataDir, plugin.Name, \"2.0.0\")\n\t})\n\n\tt.Run(\"if multiple versions are defined and installed returns an error\", func(t *testing.T) {\n\t\tconf, plugin := generateConfig(t)\n\t\tstdout, stderr := buildOutputs()\n\t\tcurrentDir := t.TempDir()\n\n\t\tversions := \"1.0.0 2.0.0\"\n\t\t// write a version file\n\t\tdata := []byte(fmt.Sprintf(\"%s %s\", plugin.Name, versions))\n\t\terr := os.WriteFile(filepath.Join(currentDir, \".tool-versions\"), data, 0o666)\n\t\tassert.NoError(t, err)\n\n\t\terr = Install(conf, plugin, currentDir, &stdout, &stderr)\n\t\tassert.NoError(t, err)\n\n\t\tassertVersionInstalled(t, conf.DataDir, plugin.Name, \"1.0.0\")\n\t\tassertVersionInstalled(t, conf.DataDir, plugin.Name, \"2.0.0\")\n\n\t\terr = Install(conf, plugin, currentDir, &stdout, &stderr)\n\t\tassert.Error(t, err)\n\t\t// Expect a VersionAlreadyInstalledError\n\t\tvar eerr VersionAlreadyInstalledError\n\t\tassert.ErrorAs(t, err, &eerr)\n\t})\n}\n\nfunc TestInstallVersion(t *testing.T) {\n\tt.Setenv(\"ASDF_CONFIG_FILE\", \"testdata/asdfrc\")\n\n\tt.Run(\"returns error when plugin doesn't exist\", func(t *testing.T) {\n\t\tconf, _ := generateConfig(t)\n\t\tstdout, stderr := buildOutputs()\n\t\tversion := toolversions.Version{Type: \"version\", Value: \"1.2.3\"}\n\t\terr := InstallVersion(conf, plugins.New(conf, \"non-existent\"), version, &stdout, &stderr)\n\t\tassert.IsType(t, plugins.PluginMissing{}, err)\n\t})\n\n\tt.Run(\"installs latest version of tool when version is 'latest'\", func(t *testing.T) {\n\t\tconf, plugin := generateConfig(t)\n\t\tstdout, stderr := buildOutputs()\n\t\tversion := toolversions.Version{Type: \"latest\", Value: \"\"}\n\t\terr := InstallVersion(conf, plugin, version, &stdout, &stderr)\n\t\tassert.Nil(t, err)\n\n\t\tassertVersionInstalled(t, conf.DataDir, plugin.Name, \"2.0.0\")\n\t})\n\n\tt.Run(\"installs specific version of tool\", func(t *testing.T) {\n\t\tconf, plugin := generateConfig(t)\n\t\tstdout, stderr := buildOutputs()\n\n\t\tversion := toolversions.Version{Type: \"latest\", Value: \"^1.\"}\n\t\terr := InstallVersion(conf, plugin, version, &stdout, &stderr)\n\t\tassert.Nil(t, err)\n\n\t\tassertVersionInstalled(t, conf.DataDir, plugin.Name, \"1.1.0\")\n\t})\n}\n\nfunc TestInstallOneVersion(t *testing.T) {\n\tt.Setenv(\"ASDF_CONFIG_FILE\", \"testdata/asdfrc\")\n\n\tt.Run(\"returns error when plugin doesn't exist\", func(t *testing.T) {\n\t\tconf, _ := generateConfig(t)\n\t\tstdout, stderr := buildOutputs()\n\t\terr := InstallOneVersion(conf, plugins.New(conf, \"non-existent\"), \"1.2.3\", false, &stdout, &stderr)\n\t\tassert.IsType(t, plugins.PluginMissing{}, err)\n\t})\n\n\tt.Run(\"returns error when passed a path version\", func(t *testing.T) {\n\t\tconf, plugin := generateConfig(t)\n\t\tstdout, stderr := buildOutputs()\n\t\terr := InstallOneVersion(conf, plugin, \"path:/foo/bar\", false, &stdout, &stderr)\n\n\t\tassert.ErrorContains(t, err, \"uninstallable version path of testlua\")\n\t})\n\n\tt.Run(\"returns error when plugin version is 'system'\", func(t *testing.T) {\n\t\tconf, plugin := generateConfig(t)\n\t\tstdout, stderr := buildOutputs()\n\t\terr := InstallOneVersion(conf, plugin, \"system\", false, &stdout, &stderr)\n\t\tassert.IsType(t, UninstallableVersionError{}, err)\n\t})\n\n\tt.Run(\"returns error when version doesn't exist\", func(t *testing.T) {\n\t\tversion := \"other-dummy\"\n\t\tconf, plugin := generateConfig(t)\n\t\tstdout, stderr := buildOutputs()\n\t\terr := InstallOneVersion(conf, plugin, version, false, &stdout, &stderr)\n\t\tassert.Errorf(t, err, \"failed to run install callback: exit status 1\")\n\n\t\twant := \"pre_asdf_download_lua other-dummy\\npre_asdf_install_lua other-dummy\\nDummy couldn't install version: other-dummy (on purpose)\\n\"\n\t\tassert.Equal(t, want, stdout.String())\n\n\t\tassertNotInstalled(t, conf.DataDir, plugin.Name, version)\n\t})\n\n\tt.Run(\"returns error when version already installed\", func(t *testing.T) {\n\t\tconf, plugin := generateConfig(t)\n\t\tstdout, stderr := buildOutputs()\n\t\terr := InstallOneVersion(conf, plugin, \"1.0.0\", false, &stdout, &stderr)\n\t\tassert.Nil(t, err)\n\t\tassertVersionInstalled(t, conf.DataDir, plugin.Name, \"1.0.0\")\n\n\t\t// Install a second time\n\t\terr = InstallOneVersion(conf, plugin, \"1.0.0\", false, &stdout, &stderr)\n\t\tassert.Error(t, err)\n\t\t// Expect a VersionAlreadyInstalledError\n\t\tvar eerr VersionAlreadyInstalledError\n\t\tassert.ErrorAs(t, err, &eerr)\n\t})\n\n\tt.Run(\"creates download directory\", func(t *testing.T) {\n\t\tconf, plugin := generateConfig(t)\n\t\tstdout, stderr := buildOutputs()\n\t\terr := InstallOneVersion(conf, plugin, \"1.0.0\", false, &stdout, &stderr)\n\t\tassert.Nil(t, err)\n\n\t\tdownloadPath := filepath.Join(conf.DataDir, \"downloads\", plugin.Name, \"1.0.0\")\n\t\tpathInfo, err := os.Stat(downloadPath)\n\t\tassert.Nil(t, err)\n\t\tassert.True(t, pathInfo.IsDir())\n\t})\n\n\tt.Run(\"creates install directory\", func(t *testing.T) {\n\t\tconf, plugin := generateConfig(t)\n\t\tstdout, stderr := buildOutputs()\n\t\terr := InstallOneVersion(conf, plugin, \"1.0.0\", false, &stdout, &stderr)\n\t\tassert.Nil(t, err)\n\n\t\tinstallPath := filepath.Join(conf.DataDir, \"installs\", plugin.Name, \"1.0.0\")\n\t\tpathInfo, err := os.Stat(installPath)\n\t\tassert.Nil(t, err)\n\t\tassert.True(t, pathInfo.IsDir())\n\t})\n\n\tt.Run(\"deletes install directory when install fails\", func(t *testing.T) {\n\t\tconf, plugin := generateConfig(t)\n\t\tstdout, stderr := buildOutputs()\n\n\t\tinstallScript := filepath.Join(conf.DataDir, \"plugins\", plugin.Name, \"bin\", \"install\")\n\t\tf, err := os.OpenFile(installScript, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0o777)\n\t\tassert.Nil(t, err)\n\t\t_, err = f.WriteString(\"\\nexit 1\")\n\t\tassert.Nil(t, err)\n\t\terr = f.Close()\n\t\tassert.Nil(t, err)\n\n\t\terr = InstallOneVersion(conf, plugin, \"1.0.0\", false, &stdout, &stderr)\n\t\tassert.Errorf(t, err, \"failed to run install callback: exit status 1\")\n\n\t\tinstallPath := filepath.Join(conf.DataDir, \"installs\", plugin.Name, \"1.0.0\")\n\t\t_, err = os.Stat(installPath)\n\t\tassert.True(t, os.IsNotExist(err))\n\t})\n\n\tt.Run(\"runs pre-download, pre-install and post-install hooks when installation successful\", func(t *testing.T) {\n\t\tconf, plugin := generateConfig(t)\n\t\tstdout, stderr := buildOutputs()\n\t\terr := InstallOneVersion(conf, plugin, \"1.0.0\", false, &stdout, &stderr)\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, \"\", stderr.String())\n\t\twant := \"pre_asdf_download_lua 1.0.0\\npre_asdf_install_lua 1.0.0\\npost_asdf_install_lua 1.0.0\\n\"\n\t\tassert.Equal(t, want, stdout.String())\n\t})\n\n\tt.Run(\"installs successfully when plugin exists but version does not\", func(t *testing.T) {\n\t\tconf, plugin := generateConfig(t)\n\t\tstdout, stderr := buildOutputs()\n\t\terr := InstallOneVersion(conf, plugin, \"1.0.0\", false, &stdout, &stderr)\n\t\tassert.Nil(t, err)\n\n\t\t// Check download directory\n\t\tdownloadPath := filepath.Join(conf.DataDir, \"downloads\", plugin.Name, \"1.0.0\")\n\t\tentries, err := os.ReadDir(downloadPath)\n\t\tassert.Nil(t, err)\n\t\t// mock plugin doesn't write anything\n\t\tassert.Empty(t, entries)\n\n\t\t// Check install directory\n\t\tassertVersionInstalled(t, conf.DataDir, plugin.Name, \"1.0.0\")\n\t})\n\n\tt.Run(\"install successfully when plugin lacks download callback\", func(t *testing.T) {\n\t\tconf, _ := generateConfig(t)\n\t\tstdout, stderr := buildOutputs()\n\t\ttestPluginName := \"no-download\"\n\t\t_, err := repotest.InstallPlugin(\"dummy_plugin_no_download\", conf.DataDir, testPluginName)\n\t\tassert.Nil(t, err)\n\t\tplugin := plugins.New(conf, testPluginName)\n\n\t\terr = InstallOneVersion(conf, plugin, \"1.0.0\", false, &stdout, &stderr)\n\t\tassert.Nil(t, err)\n\n\t\t// no-download install script prints 'install'\n\t\tassert.Equal(t, \"install\", stdout.String())\n\t})\n}\n\nfunc TestLatest(t *testing.T) {\n\tpluginName := \"latest_test\"\n\tconf, _ := generateConfig(t)\n\t_, err := repotest.InstallPlugin(\"dummy_legacy_plugin\", conf.DataDir, pluginName)\n\tassert.Nil(t, err)\n\tplugin := plugins.New(conf, pluginName)\n\n\tt.Run(\"when plugin has a latest-stable callback invokes it and returns version it printed\", func(t *testing.T) {\n\t\tpluginName := \"latest-with-callback\"\n\t\t_, err := repotest.InstallPlugin(\"dummy_plugin\", conf.DataDir, pluginName)\n\t\tassert.Nil(t, err)\n\t\tplugin := plugins.New(conf, pluginName)\n\n\t\tversion, err := Latest(plugin, \"\")\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, \"2.0.0\", version)\n\t})\n\n\tt.Run(\"when plugin has latest-stable callback invokes and it does not filter results\", func(t *testing.T) {\n\t\tpluginName := \"latest-with-dev-version\"\n\t\tpluginDir, err := repotest.InstallPlugin(\"dummy_plugin\", conf.DataDir, pluginName)\n\t\tassert.Nil(t, err)\n\t\tplugin := plugins.New(conf, pluginName)\n\n\t\t// Replace latest-stable script so it returns a dev version that would be otherwise filtered out\n\t\tlatestScript := filepath.Join(pluginDir, \"bin\", \"latest-stable\")\n\t\terr = os.WriteFile(latestScript, []byte(\"#!/usr/bin/env bash\\necho 1.2.3-dev\"), 0o777)\n\t\tassert.Nil(t, err)\n\n\t\tversion, err := Latest(plugin, \"\")\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, \"1.2.3-dev\", version)\n\t})\n\n\tt.Run(\"when given query matching no versions return empty slice of versions\", func(t *testing.T) {\n\t\tversion, err := Latest(plugin, \"impossible-to-satisfy-query\")\n\t\tassert.Error(t, err, \"no latest version found\")\n\t\tassert.Equal(t, version, \"\")\n\t})\n\n\tt.Run(\"when given no query returns latest version of plugin\", func(t *testing.T) {\n\t\tversion, err := Latest(plugin, \"\")\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, \"5.1.0\", version)\n\t})\n\n\tt.Run(\"when given no query returns latest version of plugin\", func(t *testing.T) {\n\t\tversion, err := Latest(plugin, \"4\")\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, \"4.0.0\", version)\n\t})\n\n\tt.Run(\"when given no query returns latest version of plugin starting with number\", func(t *testing.T) {\n\t\tpluginName := \"no-latest-non-numeric-versions\"\n\t\tpluginDir, err := repotest.InstallPlugin(\"dummy_plugin\", conf.DataDir, pluginName)\n\t\tassert.Nil(t, err)\n\t\tplugin := plugins.New(conf, pluginName)\n\n\t\t// Replace list-all script so it returns some non-numeric versions\n\t\tlistAllScript := filepath.Join(pluginDir, \"bin\", \"list-all\")\n\t\terr = os.WriteFile(listAllScript, []byte(\"#!/usr/bin/env bash\\necho foobar 3.4.5 1.2.3-dev foobar2\"), 0o777)\n\t\tassert.Nil(t, err)\n\t\tlatestScript := filepath.Join(pluginDir, \"bin\", \"latest-stable\")\n\t\terr = os.Remove(latestScript)\n\t\tassert.Nil(t, err)\n\n\t\tversion, err := Latest(plugin, \"\")\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, \"3.4.5\", version)\n\t})\n}\n\nfunc TestLatestWithSamples(t *testing.T) {\n\ttests := []struct {\n\t\ttestFile       string\n\t\texpectedOutput string\n\t}{\n\t\t{\n\t\t\ttestFile:       \"list-all-ruby\",\n\t\t\texpectedOutput: \"3.4.2\",\n\t\t},\n\t\t{\n\t\t\ttestFile:       \"list-all-python\",\n\t\t\texpectedOutput: \"3.13.2t\",\n\t\t},\n\t\t{\n\t\t\ttestFile:       \"list-all-elixir\",\n\t\t\texpectedOutput: \"1.18.2-otp-27\",\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tpluginName := \"latest_test\"\n\t\tconf, _ := generateConfig(t)\n\t\tpluginDir, err := repotest.InstallPlugin(\"dummy_legacy_plugin\", conf.DataDir, pluginName)\n\t\tassert.Nil(t, err)\n\t\tversionsFilePath, err := filepath.Abs(filepath.Join(\"testdata\", tt.testFile))\n\t\tassert.Nil(t, err)\n\t\tcontents := \"#!/usr/bin/env bash\\ncat \\\"\" + versionsFilePath + \"\\\"\"\n\t\tlistAllPath := filepath.Join(pluginDir, \"bin\", \"list-all\")\n\t\terr = os.WriteFile(listAllPath, []byte(contents), 0o777)\n\t\tassert.Nil(t, err)\n\n\t\tplugin := plugins.New(conf, pluginName)\n\t\tversion, err := Latest(plugin, \"\")\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, tt.expectedOutput, version)\n\t}\n}\n\nfunc TestAllVersions(t *testing.T) {\n\tpluginName := \"list-all-test\"\n\tconf, _ := generateConfig(t)\n\t_, err := repotest.InstallPlugin(\"dummy_plugin\", conf.DataDir, pluginName)\n\tassert.Nil(t, err)\n\tplugin := plugins.New(conf, pluginName)\n\n\tt.Run(\"returns slice of available versions from plugin\", func(t *testing.T) {\n\t\tversions, err := AllVersions(plugin)\n\t\tassert.Nil(t, err)\n\t\tassert.Equal(t, versions, []string{\"1.0.0\", \"1.1.0\", \"2.0.0\"})\n\t})\n\n\tt.Run(\"returns error when callback missing\", func(t *testing.T) {\n\t\tpluginName = \"list-all-fail\"\n\t\t_, err := repotest.InstallPlugin(\"dummy_plugin_no_download\", conf.DataDir, pluginName)\n\t\tassert.Nil(t, err)\n\t\tplugin := plugins.New(conf, pluginName)\n\n\t\tversions, err := AllVersions(plugin)\n\t\tassert.Equal(t, err.(plugins.NoCallbackError).Error(), \"Plugin named list-all-fail does not have a callback named list-all\")\n\t\tassert.Empty(t, versions)\n\t})\n}\n\nfunc TestUninstall(t *testing.T) {\n\tt.Setenv(\"ASDF_CONFIG_FILE\", \"testdata/uninstall-asdfrc\")\n\tpluginName := \"uninstall-test\"\n\tconf, _ := generateConfig(t)\n\t_, err := repotest.InstallPlugin(\"dummy_plugin\", conf.DataDir, pluginName)\n\tassert.Nil(t, err)\n\tplugin := plugins.New(conf, pluginName)\n\tstdout, stderr := buildOutputs()\n\n\tt.Run(\"returns error when version is 'latest'\", func(t *testing.T) {\n\t\tstdout, stderr := buildOutputs()\n\t\terr := Uninstall(conf, plugin, \"latest\", &stdout, &stderr)\n\t\tassert.Error(t, err, \"'latest' is a special version value that cannot be used for uninstall command\")\n\t})\n\n\tt.Run(\"returns an error when version not installed\", func(t *testing.T) {\n\t\terr := Uninstall(conf, plugin, \"4.0.0\", &stdout, &stderr)\n\t\tassert.Error(t, err, \"No such version\")\n\t})\n\n\tt.Run(\"uninstalls successfully when plugin and version are installed\", func(t *testing.T) {\n\t\terr = InstallOneVersion(conf, plugin, \"1.0.0\", false, &stdout, &stderr)\n\t\tassert.Nil(t, err)\n\n\t\terr := Uninstall(conf, plugin, \"1.0.0\", &stdout, &stderr)\n\t\tassert.Nil(t, err)\n\t\tassertNotInstalled(t, conf.DataDir, plugin.Name, \"1.0.0\")\n\t})\n\n\tt.Run(\"runs pre and post-uninstall hooks\", func(t *testing.T) {\n\t\tstdout, stderr := buildOutputs()\n\t\terr = InstallOneVersion(conf, plugin, \"1.0.0\", false, &stdout, &stderr)\n\t\tassert.Nil(t, err)\n\n\t\terr := Uninstall(conf, plugin, \"1.0.0\", &stdout, &stderr)\n\t\tassert.Nil(t, err)\n\t\twant := \"pre_asdf_uninstall_test 1.0.0\\npost_asdf_uninstall_test 1.0.0\\n\"\n\t\tassert.Equal(t, want, stdout.String())\n\t})\n\n\tt.Run(\"invokes uninstall callback when present\", func(t *testing.T) {\n\t\tstdout, stderr := buildOutputs()\n\t\terr = InstallOneVersion(conf, plugin, \"1.0.0\", false, &stdout, &stderr)\n\t\tassert.Nil(t, err)\n\n\t\tdata := []byte(\"echo custom uninstall\")\n\t\terr := os.WriteFile(filepath.Join(plugin.Dir, \"bin\", \"uninstall\"), data, 0o755)\n\t\tassert.Nil(t, err)\n\n\t\terr = Uninstall(conf, plugin, \"1.0.0\", &stdout, &stderr)\n\t\tassert.Nil(t, err)\n\t\twant := \"pre_asdf_uninstall_test 1.0.0\\ncustom uninstall\\npost_asdf_uninstall_test 1.0.0\\n\"\n\t\tassert.Equal(t, want, stdout.String())\n\t})\n}\n\n// Helper functions\nfunc buildOutputs() (strings.Builder, strings.Builder) {\n\tvar stdout strings.Builder\n\tvar stderr strings.Builder\n\n\treturn stdout, stderr\n}\n\nfunc assertVersionInstalled(t *testing.T, dataDir, pluginName, version string) {\n\tt.Helper()\n\n\tinstallDir := filepath.Join(dataDir, \"installs\", pluginName, version)\n\tinstalledVersionFile := filepath.Join(installDir, \"version\")\n\n\tbytes, err := os.ReadFile(installedVersionFile)\n\tassert.Nil(t, err, \"expected file from install to exist\")\n\n\twant := fmt.Sprintf(\"%s\\n\", version)\n\tassert.Equal(t, want, string(bytes), \"got wrong version\")\n\n\tentries, err := os.ReadDir(installDir)\n\tassert.Nil(t, err)\n\n\tvar fileNames []string\n\tfor _, e := range entries {\n\t\tfileNames = append(fileNames, e.Name())\n\t}\n\n\tassert.Equal(t, fileNames, []string{\"bin\", \"env\", \"version\"})\n}\n\nfunc assertNotInstalled(t *testing.T, dataDir, pluginName, version string) {\n\tt.Helper()\n\n\tinstallPath := filepath.Join(dataDir, \"installs\", pluginName, version)\n\tentries, err := os.ReadDir(installPath)\n\tif err != nil && !os.IsNotExist(err) {\n\t\tt.Errorf(\"failed to check directory %s due to error %s\", installPath, err)\n\t}\n\tassert.Empty(t, entries)\n}\n\nfunc generateConfig(t *testing.T) (config.Config, plugins.Plugin) {\n\tt.Helper()\n\ttestDataDir := t.TempDir()\n\tconf, err := config.LoadConfig()\n\tassert.Nil(t, err)\n\tconf.DataDir = testDataDir\n\n\t_, err = repotest.InstallPlugin(\"dummy_plugin\", testDataDir, testPluginName)\n\tassert.Nil(t, err)\n\n\treturn conf, plugins.New(conf, testPluginName)\n}\n\nfunc installPlugin(t *testing.T, conf config.Config, fixture, name string) plugins.Plugin {\n\t_, err := repotest.InstallPlugin(fixture, conf.DataDir, name)\n\tassert.Nil(t, err)\n\treturn plugins.New(conf, name)\n}\n\nfunc writeVersionFile(t *testing.T, dir, contents string) {\n\tt.Helper()\n\terr := os.WriteFile(filepath.Join(dir, \".tool-versions\"), []byte(contents), 0o666)\n\tassert.Nil(t, err)\n}\n"
  },
  {
    "path": "lib/commands/command-current.bash",
    "content": "# -*- sh -*-\n# shellcheck source=lib/functions/plugins.bash\n. \"$(dirname \"$(dirname \"$0\")\")/lib/functions/plugins.bash\"\n\n# shellcheck disable=SC2059\nplugin_current_command() {\n  local plugin_name=$1\n  local terminal_format=$2\n\n  check_if_plugin_exists \"$plugin_name\"\n\n  local search_path\n  search_path=$PWD\n  local version_and_path\n  version_and_path=$(find_versions \"$plugin_name\" \"$search_path\")\n  local full_version\n  full_version=$(cut -d '|' -f 1 <<<\"$version_and_path\")\n  local version_file_path\n  version_file_path=$(cut -d '|' -f 2 <<<\"$version_and_path\")\n  local version_not_installed\n  local description=\"\"\n\n  IFS=' ' read -r -a versions <<<\"$full_version\"\n  for version in \"${versions[@]}\"; do\n    if ! (check_if_version_exists \"$plugin_name\" \"$version\"); then\n      version_not_installed=\"$version\"\n    fi\n  done\n  check_for_deprecated_plugin \"$plugin_name\"\n\n  if [ -n \"$version_not_installed\" ]; then\n    description=\"Not installed. Run \\\"asdf install $plugin $version\\\"\"\n    printf \"$terminal_format\" \"$plugin\" \"$version\" \"$description\" 1>&2\n    return 1\n  elif [ -z \"$full_version\" ]; then\n    description=\"No version is set. Run \\\"asdf <global|shell|local> $plugin <version>\\\"\"\n    printf \"$terminal_format\" \"$plugin\" \"______\" \"$description\" 1>&2\n    return 126\n  else\n    description=\"$version_file_path\"\n    printf \"$terminal_format\" \"$plugin\" \"$full_version\" \"$description\"\n  fi\n}\n\n# shellcheck disable=SC2059\ncurrent_command() {\n  local terminal_format=\"%-15s %-15s %-10s\\n\"\n  local exit_status=0\n  local plugin\n\n  # printf \"$terminal_format\" \"PLUGIN\" \"VERSION\" \"SET BY CONFIG\" # disable this until we release headings across the board\n  if [ $# -eq 0 ]; then\n    # shellcheck disable=SC2119\n    for plugin in $(plugin_list_command); do\n      plugin_current_command \"$plugin\" \"$terminal_format\"\n    done\n  else\n    plugin=$1\n    plugin_current_command \"$plugin\" \"$terminal_format\"\n    exit_status=\"$?\"\n  fi\n\n  exit \"$exit_status\"\n}\n\n# Warn if the plugin isn't using the updated legacy file api.\ncheck_for_deprecated_plugin() {\n  local plugin_name=$1\n\n  local plugin_path\n  plugin_path=$(get_plugin_path \"$plugin_name\")\n  local legacy_config\n  legacy_config=$(get_asdf_config_value \"legacy_version_file\")\n  local deprecated_script=\"${plugin_path}/bin/get-version-from-legacy-file\"\n  local new_script=\"${plugin_path}/bin/list-legacy-filenames\"\n\n  if [ \"$legacy_config\" = \"yes\" ] && [ -f \"$deprecated_script\" ] && [ ! -f \"$new_script\" ]; then\n    printf \"Heads up! It looks like your %s plugin is out of date. You can update it with:\\n\\n\" \"$plugin_name\"\n    printf \"  asdf plugin-update %s\\n\\n\" \"$plugin_name\"\n  fi\n}\n\ncurrent_command \"$@\"\n"
  },
  {
    "path": "lib/commands/command-env.bash",
    "content": "# -*- sh -*-\n\nshim_env_command() {\n  local shim_name=\"$1\"\n  local env_cmd=\"${2}\"\n  local env_args=(\"${@:3}\")\n\n  if [ -z \"$shim_name\" ]; then\n    printf \"usage: asdf env <command>\\n\"\n    exit 1\n  fi\n\n  if [ -z \"$env_cmd\" ]; then\n    env_cmd=\"env\"\n  fi\n\n  shim_env() {\n    \"$env_cmd\" \"${env_args[@]}\"\n  }\n\n  with_shim_executable \"$shim_name\" shim_env || exit $?\n}\n\nshim_env_command \"$@\"\n"
  },
  {
    "path": "lib/commands/command-exec.bash",
    "content": "# -*- sh -*-\n\nshim_exec_command() {\n  local shim_name\n  shim_name=$(basename \"$1\")\n  local shim_args=(\"${@:2}\")\n\n  if [ -z \"$shim_name\" ]; then\n    printf \"usage: asdf exec <command>\\n\"\n    exit 1\n  fi\n\n  exec_shim() {\n    local plugin_name=\"$1\"\n    local version=\"$2\"\n    local executable_path=\"$3\"\n\n    if [ ! -x \"$executable_path\" ]; then\n      printf \"No %s executable found for %s %s\\n\" \"$shim_name\" \"$plugin_name\" \"$version\" >&2\n      exit 2\n    fi\n\n    asdf_run_hook \"pre_${plugin_name}_${shim_name}\" \"${shim_args[@]}\"\n    pre_status=$?\n    if [ \"$pre_status\" -ne 0 ]; then\n      return \"$pre_status\"\n    fi\n    exec \"$executable_path\" \"${shim_args[@]}\"\n  }\n\n  with_shim_executable \"$shim_name\" exec_shim || exit $?\n}\n\nshim_exec_command \"$@\"\n"
  },
  {
    "path": "lib/commands/command-export-shell-version.bash",
    "content": "# -*- sh -*-\n# shellcheck source=lib/functions/versions.bash\n. \"$(dirname \"$(dirname \"$0\")\")/lib/functions/versions.bash\"\n\n# Output from this command must be executable shell code\nshell_command() {\n  local asdf_shell=\"$1\"\n  shift\n\n  if [ \"$#\" -lt \"2\" ]; then\n    printf \"Usage: asdf shell <name> {<version>|--unset}\\n\" >&2\n    printf \"false\\n\"\n    exit 1\n  fi\n\n  local plugin=$1\n  local version=$2\n\n  local upcase_name\n  upcase_name=$(tr '[:lower:]-' '[:upper:]_' <<<\"$plugin\")\n  local version_env_var=\"ASDF_${upcase_name}_VERSION\"\n\n  if [ \"$version\" = \"--unset\" ]; then\n    case \"$asdf_shell\" in\n    fish)\n      printf \"set -e %s\\n\" \"$version_env_var\"\n      ;;\n    elvish)\n      # Elvish doesn't have a `source` command, and eval is banned, so the\n      # var name and value are printed on separate lines for asdf.elv to parse\n      # and pass to unset-env.\n      printf \"unset-env\\n%s\" \"$version_env_var\"\n      ;;\n    pwsh)\n      printf '%s\\n' \"if (\\$(Test-Path Env:$version_env_var) -eq 'True') { Remove-Item Env:$version_env_var }\"\n      ;;\n    *)\n      printf \"unset %s\\n\" \"$version_env_var\"\n      ;;\n    esac\n    exit 0\n  fi\n  if [ \"$version\" = \"latest\" ]; then\n    version=$(latest_command \"$plugin\")\n  fi\n  if ! (check_if_version_exists \"$plugin\" \"$version\"); then\n    version_not_installed_text \"$plugin\" \"$version\" 1>&2\n    printf \"false\\n\"\n    exit 1\n  fi\n\n  case \"$asdf_shell\" in\n  fish)\n    printf \"set -gx %s \\\"%s\\\"\\n\" \"$version_env_var\" \"$version\"\n    ;;\n  elvish)\n    # Elvish doesn't have a `source` command, and eval is banned, so the\n    # var name and value are printed on separate lines for asdf.elv to parse\n    # and pass to set-env.\n    printf \"set-env\\n%s\\n%s\" \"$version_env_var\" \"$version\"\n    ;;\n  pwsh)\n    printf '%s\\n' \"\\$Env:$version_env_var = '$version'\"\n    ;;\n  *)\n    printf \"export %s=\\\"%s\\\"\\n\" \"$version_env_var\" \"$version\"\n    ;;\n  esac\n}\n\nshell_command \"$@\"\n"
  },
  {
    "path": "lib/commands/command-global.bash",
    "content": "# -*- sh -*-\n\n# shellcheck source=lib/commands/version_commands.bash\n. \"$(dirname \"$ASDF_CMD_FILE\")/version_commands.bash\"\nversion_command global \"$@\"\n"
  },
  {
    "path": "lib/commands/command-help.bash",
    "content": "# -*- sh -*-\n# shellcheck source=lib/functions/versions.bash\n. \"$(dirname \"$(dirname \"$0\")\")/lib/functions/versions.bash\"\n\nasdf_help() {\n  printf \"version: %s\\n\\n\" \"$(asdf_version)\"\n  cat \"$(asdf_dir)/help.txt\"\n}\n\nasdf_moto() {\n  cat <<EOF\n\n\"Late but latest\"\n-- Rajinikanth\nEOF\n}\n\nasdf_extension_cmds() {\n  local plugins_path plugin_path ext_cmd_path ext_cmds plugin\n  plugins_path=\"$(get_plugin_path)\"\n  for plugin_path in \"$plugins_path\"/*/; do\n    plugin=\"$(basename \"$plugin_path\")\"\n    ext_cmd_path=\"$plugin_path/lib/commands\"\n    ext_cmds=\"$(find \"$ext_cmd_path\" -name \"command*.bash\" 2>/dev/null)\"\n    if [[ -n $ext_cmds ]]; then\n      printf \"\\nPLUGIN %s\\n\" \"$plugin\"\n      for ext_cmd in $ext_cmds; do\n        ext_cmd_name=\"$(basename \"$ext_cmd\")\"\n        sed \"s/-/ /g;s/.bash//;s/command-*/  asdf $plugin/;\" <<<\"$ext_cmd_name\"\n      done | sort\n    fi\n  done\n}\n\nhelp_command() {\n  local plugin_name=\"$1\"\n  local tool_version=\"$2\"\n  local plugin_path\n\n  # If plugin name is present as first argument output plugin help info\n  if [ -n \"$plugin_name\" ]; then\n    plugin_path=$(get_plugin_path \"$plugin_name\")\n\n    if [ -d \"$plugin_path\" ]; then\n      if [ -f \"${plugin_path}/bin/help.overview\" ]; then\n        if [ -n \"$tool_version\" ]; then\n\n          # TODO: Refactor this code out into helper functions in utils.bash\n          IFS=':' read -r -a version_info <<<\"$tool_version\"\n          if [ \"${version_info[0]}\" = \"ref\" ]; then\n            local install_type=\"${version_info[0]}\"\n            local version=\"${version_info[1]}\"\n          else\n            local install_type=\"version\"\n\n            if [ \"${version_info[0]}\" = \"latest\" ]; then\n              local version\n              version=$(latest_command \"$plugin_name\" \"${version_info[1]}\")\n            else\n              local version=\"${version_info[0]}\"\n            fi\n          fi\n\n          local install_path\n          install_path=$(get_install_path \"$plugin_name\" \"$install_type\" \"$version\")\n\n          (\n            # shellcheck disable=SC2031\n            export ASDF_INSTALL_TYPE=$install_type\n            # shellcheck disable=SC2031\n            export ASDF_INSTALL_VERSION=$version\n            # shellcheck disable=SC2031\n            export ASDF_INSTALL_PATH=$install_path\n\n            print_plugin_help \"$plugin_path\"\n          )\n        else\n          (print_plugin_help \"$plugin_path\")\n        fi\n      else\n        printf \"No documentation for plugin %s\\n\" \"$plugin_name\" >&2\n        exit 1\n      fi\n    else\n      printf \"No plugin named %s\\n\" \"$plugin_name\" >&2\n      exit 1\n    fi\n  else\n    # Otherwise output general asdf help\n    asdf_help\n    asdf_extension_cmds\n    asdf_moto\n  fi\n}\n\nprint_plugin_help() {\n  local plugin_path=$1\n\n  # Eventually @jthegedus or someone else will format the output from these\n  # scripts in a certain way.\n  \"${plugin_path}\"/bin/help.overview\n\n  if [ -f \"${plugin_path}\"/bin/help.deps ]; then\n    \"${plugin_path}\"/bin/help.deps\n  fi\n\n  if [ -f \"${plugin_path}\"/bin/help.config ]; then\n    \"${plugin_path}\"/bin/help.config\n  fi\n\n  if [ -f \"${plugin_path}\"/bin/help.links ]; then\n    \"${plugin_path}\"/bin/help.links\n  fi\n}\n\nhelp_command \"$@\"\n"
  },
  {
    "path": "lib/commands/command-info.bash",
    "content": "# -*- sh -*-\n# shellcheck source=lib/functions/plugins.bash\n. \"$(dirname \"$(dirname \"$0\")\")/lib/functions/plugins.bash\"\n\ninfo_command() {\n  printf \"%s:\\n%s\\n\\n\" \"OS\" \"$(uname -a)\"\n  printf \"%s:\\n%s\\n\\n\" \"SHELL\" \"$(\"$SHELL\" --version)\"\n  printf \"%s:\\n%s\\n\\n\" \"BASH VERSION\" \"$BASH_VERSION\"\n  printf \"%s:\\n%s\\n\\n\" \"ASDF VERSION\" \"$(asdf_version)\"\n  printf '%s\\n' 'ASDF INTERNAL VARIABLES:'\n  printf 'ASDF_DEFAULT_TOOL_VERSIONS_FILENAME=%s\\n' \"${ASDF_DEFAULT_TOOL_VERSIONS_FILENAME}\"\n  printf 'ASDF_DATA_DIR=%s\\n' \"${ASDF_DATA_DIR}\"\n  printf 'ASDF_DIR=%s\\n' \"${ASDF_DIR}\"\n  printf 'ASDF_CONFIG_FILE=%s\\n\\n' \"${ASDF_CONFIG_FILE}\"\n  printf \"%s:\\n%s\\n\\n\" \"ASDF INSTALLED PLUGINS\" \"$(plugin_list_command --urls --refs)\"\n}\n\ninfo_command \"$@\"\n"
  },
  {
    "path": "lib/commands/command-install.bash",
    "content": "# -*- sh -*-\n# shellcheck source=lib/functions/versions.bash\n. \"$(dirname \"$(dirname \"$0\")\")/lib/functions/versions.bash\"\n# shellcheck source=lib/commands/reshim.bash\n. \"$(dirname \"$ASDF_CMD_FILE\")/reshim.bash\"\n# shellcheck source=lib/functions/installs.bash\n. \"$(dirname \"$(dirname \"$0\")\")/lib/functions/installs.bash\"\n\ninstall_command \"$@\"\n"
  },
  {
    "path": "lib/commands/command-latest.bash",
    "content": "# -*- sh -*-\n# shellcheck source=lib/functions/versions.bash\n. \"$(dirname \"$(dirname \"$0\")\")/lib/functions/versions.bash\"\n\nlatest_command \"$@\"\n"
  },
  {
    "path": "lib/commands/command-list-all.bash",
    "content": "# -*- sh -*-\n# shellcheck source=lib/functions/versions.bash\n. \"$(dirname \"$(dirname \"$0\")\")/lib/functions/versions.bash\"\n\nlist_all_command \"$@\"\n"
  },
  {
    "path": "lib/commands/command-list.bash",
    "content": "# -*- sh -*-\n\nlist_command() {\n  local plugin_name=$1\n  local query=$2\n\n  if [ -z \"$plugin_name\" ]; then\n    local plugins_path\n    plugins_path=$(get_plugin_path)\n\n    if find \"$plugins_path\" -mindepth 1 -type d &>/dev/null; then\n      for plugin_path in \"$plugins_path\"/*/; do\n        plugin_name=$(basename \"$plugin_path\")\n        printf \"%s\\n\" \"$plugin_name\"\n        display_installed_versions \"$plugin_name\" \"$query\"\n      done\n    else\n      printf \"%s\\n\" 'No plugins installed'\n    fi\n  else\n    check_if_plugin_exists \"$plugin_name\"\n    display_installed_versions \"$plugin_name\" \"$query\"\n  fi\n}\n\ndisplay_installed_versions() {\n  local plugin_name=$1\n  local query=$2\n  local versions\n  local current_version\n  local flag\n\n  versions=$(list_installed_versions \"$plugin_name\")\n\n  if [[ $query ]]; then\n    versions=$(printf \"%s\\n\" \"$versions\" | grep -E \"^\\s*$query\")\n\n    if [ -z \"${versions}\" ]; then\n      display_error \"No compatible versions installed ($plugin_name $query)\"\n      exit 1\n    fi\n  fi\n\n  if [ -n \"${versions}\" ]; then\n    current_version=$(cut -d '|' -f 1 <<<\"$(find_versions \"$plugin_name\" \"$PWD\")\")\n\n    for version in $versions; do\n      flag=\"  \"\n      if [[ \"$version\" == \"$current_version\" ]]; then\n        flag=\" *\"\n      fi\n      printf \"%s%s\\n\" \"$flag\" \"$version\"\n    done\n  else\n    display_error '  No versions installed'\n  fi\n}\n\nlist_command \"$@\"\n"
  },
  {
    "path": "lib/commands/command-local.bash",
    "content": "# -*- sh -*-\n\n# shellcheck source=lib/commands/version_commands.bash\n. \"$(dirname \"$ASDF_CMD_FILE\")/version_commands.bash\"\n\nlocal_command \"$@\"\n"
  },
  {
    "path": "lib/commands/command-plugin-add.bash",
    "content": "# -*- sh -*-\n# shellcheck source=lib/functions/plugins.bash\n. \"$(dirname \"$(dirname \"$0\")\")/lib/functions/plugins.bash\"\n\nplugin_add_command \"$@\"\n"
  },
  {
    "path": "lib/commands/command-plugin-list-all.bash",
    "content": "# -*- sh -*-\n\nplugin_list_all_command() {\n  initialize_or_update_plugin_repository\n\n  local plugins_index_path\n  plugins_index_path=\"$(asdf_data_dir)/repository/plugins\"\n\n  local plugins_local_path\n  plugins_local_path=\"$(get_plugin_path)\"\n\n  if find \"$plugins_index_path\" -mindepth 1 -type d &>/dev/null; then\n    (\n      for index_plugin in \"$plugins_index_path\"/*; do\n        index_plugin_name=$(basename \"$index_plugin\")\n        source_url=$(get_plugin_source_url \"$index_plugin_name\")\n        installed_flag=\" \"\n\n        [[ -d \"${plugins_local_path}/${index_plugin_name}\" ]] && installed_flag='*'\n\n        printf \"%s\\t%s\\n\" \"$index_plugin_name\" \"$installed_flag$source_url\"\n      done\n    ) | awk '{ printf(\"%-28s\", $1); sub(/^[^*]/, \" &\", $2); $1=\"\"; print $0 }'\n  else\n    printf \"%s%s\\n\" \"error: index of plugins not found at \" \"$plugins_index_path\"\n  fi\n}\n\nplugin_list_all_command \"$@\"\n"
  },
  {
    "path": "lib/commands/command-plugin-list.bash",
    "content": "# -*- sh -*-\n# shellcheck source=lib/functions/plugins.bash\n. \"$(dirname \"$(dirname \"$0\")\")/lib/functions/plugins.bash\"\n\nplugin_list_command \"$@\"\n"
  },
  {
    "path": "lib/commands/command-plugin-push.bash",
    "content": "# -*- sh -*-\n\nplugin_push_command() {\n  local plugin_name=$1\n  if [ \"$plugin_name\" = \"--all\" ]; then\n    for dir in \"$(asdf_data_dir)\"/plugins/*/; do\n      printf \"Pushing %s...\\n\" \"$(basename \"$dir\")\"\n      (cd \"$dir\" && git push)\n    done\n  else\n    local plugin_path\n    plugin_path=$(get_plugin_path \"$plugin_name\")\n    check_if_plugin_exists \"$plugin_name\"\n    printf \"Pushing %s...\\n\" \"$plugin_name\"\n    (cd \"$plugin_path\" && git push)\n  fi\n}\n\nplugin_push_command \"$@\"\n"
  },
  {
    "path": "lib/commands/command-plugin-remove.bash",
    "content": "# -*- sh -*-\n\nplugin_remove_command() {\n  local plugin_name=$1\n  check_if_plugin_exists \"$plugin_name\"\n\n  local plugin_path\n  plugin_path=$(get_plugin_path \"$plugin_name\")\n\n  asdf_run_hook \"pre_asdf_plugin_remove\" \"$plugin_name\"\n  asdf_run_hook \"pre_asdf_plugin_remove_${plugin_name}\"\n\n  if [ -f \"${plugin_path}/bin/pre-plugin-remove\" ]; then\n    (\n      export ASDF_PLUGIN_PATH=$plugin_path\n      \"${plugin_path}/bin/pre-plugin-remove\"\n    )\n  fi\n\n  rm -rf \"$plugin_path\"\n  rm -rf \"$(asdf_data_dir)/installs/${plugin_name}\"\n  rm -rf \"$(asdf_data_dir)/downloads/${plugin_name}\"\n\n  for f in \"$(asdf_data_dir)\"/shims/*; do\n    if [ -f \"$f\" ]; then # nullglob may not be set\n      if grep -q \"asdf-plugin: ${plugin_name}\" \"$f\"; then\n        rm -f \"$f\"\n      fi\n    fi\n  done\n\n  asdf_run_hook \"post_asdf_plugin_remove\" \"$plugin_name\"\n  asdf_run_hook \"post_asdf_plugin_remove_${plugin_name}\"\n}\n\nplugin_remove_command \"$@\"\n"
  },
  {
    "path": "lib/commands/command-plugin-test.bash",
    "content": "# -*- sh -*-\n# shellcheck source=lib/functions/versions.bash\n. \"$(dirname \"$(dirname \"$0\")\")/lib/functions/versions.bash\"\n# shellcheck source=lib/functions/plugins.bash\n. \"$(dirname \"$(dirname \"$0\")\")/lib/functions/plugins.bash\"\n# shellcheck source=lib/commands/reshim.bash\n. \"$(dirname \"$ASDF_CMD_FILE\")/reshim.bash\"\n# shellcheck source=lib/functions/installs.bash\n. \"$(dirname \"$(dirname \"$0\")\")/lib/functions/installs.bash\"\n\nplugin_test_command() {\n\n  local plugin_name=\"$1\"\n  local plugin_url=\"$2\"\n  shift 2\n\n  local exit_code\n  local TEST_DIR\n\n  fail_test() {\n    printf \"FAILED: %s\\n\" \"$1\"\n    rm -rf \"$TEST_DIR\"\n    exit 1\n  }\n\n  if [ -z \"$plugin_name\" ] || [ -z \"$plugin_url\" ]; then\n    fail_test \"please provide a plugin name and url\"\n  fi\n\n  local plugin_gitref\n  local tool_version\n  local interpret_args_literally\n  local skip_next_arg\n\n  for arg; do\n    shift\n    if [ -n \"${skip_next_arg}\" ]; then\n      skip_next_arg=\n    elif [ -n \"${interpret_args_literally}\" ]; then\n      set -- \"$@\" \"${arg}\"\n    else\n      case \"${arg}\" in\n      --asdf-plugin-gitref)\n        plugin_gitref=\"$1\"\n        skip_next_arg=true\n        ;;\n      --asdf-tool-version)\n        tool_version=\"$1\"\n        skip_next_arg=true\n        ;;\n      --)\n        interpret_args_literally=true\n        ;;\n      *)\n        set -- \"$@\" \"${arg}\"\n        ;;\n      esac\n    fi\n  done\n\n  if [ -z \"$plugin_gitref\" ]; then\n    plugin_remote_default_branch=$(git ls-remote --symref \"$plugin_url\" HEAD | awk '{ sub(/refs\\/heads\\//, \"\"); print $2; exit }')\n    plugin_gitref=${3:-${plugin_remote_default_branch}}\n  fi\n\n  if [ \"$#\" -eq 1 ]; then\n    set -- \"${SHELL:-sh}\" -c \"$1\"\n  fi\n\n  TEST_DIR=$(mktemp -dt asdf.XXXXXX)\n  cp -R \"$(asdf_dir)/bin\" \"$TEST_DIR\"\n  cp -R \"$(asdf_dir)/lib\" \"$TEST_DIR\"\n  cp \"$(asdf_dir)/asdf.sh\" \"$TEST_DIR\"\n\n  plugin_test() {\n    export ASDF_DIR=$TEST_DIR\n    export ASDF_DATA_DIR=$TEST_DIR\n\n    # shellcheck disable=SC1090\n    . \"$ASDF_DIR/asdf.sh\"\n\n    if ! (plugin_add_command \"$plugin_name\" \"$plugin_url\"); then\n      fail_test \"could not install $plugin_name from $plugin_url\"\n    fi\n\n    # shellcheck disable=SC2119\n    if ! (plugin_list_command | grep -q \"^$plugin_name$\"); then\n      fail_test \"$plugin_name was not properly installed\"\n    fi\n\n    if ! (plugin_update_command \"$plugin_name\" \"$plugin_gitref\"); then\n      fail_test \"failed to checkout $plugin_name gitref: $plugin_gitref\"\n    fi\n\n    local plugin_path\n    plugin_path=$(get_plugin_path \"$plugin_name\")\n    local list_all=\"$plugin_path/bin/list-all\"\n    if grep -q api.github.com \"$list_all\"; then\n      if ! grep -q Authorization \"$list_all\"; then\n        printf \"\\nLooks like %s/bin/list-all relies on GitHub releases\\n\" \"$plugin_name\"\n        printf \"but it does not properly sets an Authorization header to prevent\\n\"\n        printf \"GitHub API rate limiting.\\n\\n\"\n        printf \"See https://github.com/asdf-vm/asdf/blob/master/docs/creating-plugins.md#github-api-rate-limiting\\n\"\n\n        fail_test \"$plugin_name/bin/list-all does not set GitHub Authorization token\"\n      fi\n\n      # test for most common token names we have on plugins. If both are empty show this warning\n      if [ -z \"$OAUTH_TOKEN\" ] && [ -z \"$GITHUB_API_TOKEN\" ]; then\n        printf \"%s/bin/list-all is using GitHub API, just be sure you provide an API Authorization token\\n\" \"$plugin_name\"\n        printf \"via your CI env GITHUB_API_TOKEN. This is the current rate_limit:\\n\\n\"\n        curl -s https://api.github.com/rate_limit\n        printf \"\\n\"\n      fi\n    fi\n\n    local versions\n    # shellcheck disable=SC2046\n    if ! read -r -a versions <<<$(list_all_command \"$plugin_name\"); then\n      fail_test \"list-all exited with an error\"\n    fi\n\n    if [ ${#versions} -eq 0 ]; then\n      fail_test \"list-all did not return any version\"\n    fi\n\n    local version\n\n    # Use the version passed in if it was set. Otherwise grab the latest\n    # version from the versions list\n    if [ -z \"$tool_version\" ] || [[ \"$tool_version\" == *\"latest\"* ]]; then\n      version=\"$(latest_command \"$plugin_name\" \"$(sed -e 's#latest##;s#^:##' <<<\"$tool_version\")\")\"\n      if [ -z \"$version\" ]; then\n        fail_test \"could not get latest version\"\n      fi\n    else\n      version=\"$tool_version\"\n    fi\n\n    if ! (install_command \"$plugin_name\" \"$version\"); then\n      fail_test \"install exited with an error\"\n    fi\n\n    cd \"$TEST_DIR\" || fail_test \"could not cd $TEST_DIR\"\n\n    if ! (local_command \"$plugin_name\" \"$version\"); then\n      fail_test \"install did not add the requested version\"\n    fi\n\n    if ! (reshim_command \"$plugin_name\"); then\n      fail_test \"could not reshim plugin\"\n    fi\n\n    if [ \"$#\" -gt 0 ]; then\n      \"$@\"\n      exit_code=$?\n      if [ $exit_code != 0 ]; then\n        fail_test \"$* failed with exit code $exit_code\"\n      fi\n    fi\n\n    # Assert the scripts in bin are executable by asdf\n    for filename in \"$ASDF_DIR/plugins/$plugin_name/bin\"/*; do\n      if [ ! -x \"$filename\" ]; then\n        fail_test \"Incorrect permissions on $filename. Must be executable by asdf\"\n      fi\n    done\n\n    # Assert that a license file exists in the plugin repo and is not empty\n    license_file=\"$ASDF_DIR/plugins/$plugin_name/LICENSE\"\n    if [ -f \"$license_file\" ]; then\n      if [ ! -s \"$license_file\" ]; then\n        fail_test \"LICENSE file in the plugin repository must not be empty\"\n      fi\n    else\n      fail_test \"LICENSE file must be present in the plugin repository\"\n    fi\n  }\n\n  # run test in a subshell\n  (plugin_test \"$@\")\n  exit_code=$?\n  rm -rf \"$TEST_DIR\"\n  exit $exit_code\n}\n\nplugin_test_command \"$@\"\n"
  },
  {
    "path": "lib/commands/command-plugin-update.bash",
    "content": "# -*- sh -*-\n# shellcheck source=lib/functions/plugins.bash\n. \"$(dirname \"$(dirname \"$0\")\")/lib/functions/plugins.bash\"\n\nplugin_update_command \"$@\"\n"
  },
  {
    "path": "lib/commands/command-reshim.bash",
    "content": "# -*- sh -*-\n\n# shellcheck source=lib/commands/reshim.bash\n. \"$(dirname \"$ASDF_CMD_FILE\")/reshim.bash\"\n\nreshim_command \"$@\"\n"
  },
  {
    "path": "lib/commands/command-shim-versions.bash",
    "content": "# -*- sh -*-\n\nshim_versions_command() {\n  local shim_name=$1\n  shim_plugin_versions \"$shim_name\"\n}\n\nshim_versions_command \"$@\"\n"
  },
  {
    "path": "lib/commands/command-uninstall.bash",
    "content": "# -*- sh -*-\n\n# shellcheck source=lib/commands/reshim.bash\n. \"$(dirname \"$ASDF_CMD_FILE\")/reshim.bash\"\n\nuninstall_command() {\n  local plugin_name=$1\n  local full_version=$2\n  local plugin_path\n  plugin_path=$(get_plugin_path \"$plugin_name\")\n\n  check_if_plugin_exists \"$plugin_name\"\n\n  IFS=':' read -r -a version_info <<<\"$full_version\"\n  if [ \"${version_info[0]}\" = \"ref\" ]; then\n    local install_type=\"${version_info[0]}\"\n    local version=\"${version_info[1]}\"\n  else\n    local install_type=\"version\"\n    local version=\"${version_info[0]}\"\n  fi\n\n  local install_path\n  install_path=$(get_install_path \"$plugin_name\" \"$install_type\" \"$version\")\n\n  if [ ! -d \"$install_path\" ]; then\n    display_error \"No such version\"\n    exit 1\n  fi\n\n  asdf_run_hook \"pre_asdf_uninstall_${plugin_name}\" \"$full_version\"\n  remove_shims_for_version \"$plugin_name\" \"$full_version\"\n\n  if [ -f \"${plugin_path}/bin/uninstall\" ]; then\n    (\n      export ASDF_INSTALL_TYPE=$install_type\n      export ASDF_INSTALL_VERSION=$version\n      export ASDF_INSTALL_PATH=$install_path\n      \"${plugin_path}/bin/uninstall\"\n    )\n  else\n    rm -rf \"$install_path\"\n  fi\n\n  asdf_run_hook \"post_asdf_uninstall_${plugin_name}\" \"$full_version\"\n}\n\nremove_shims_for_version() {\n  local plugin_name=$1\n  local full_version=$2\n  for shim_path in $(plugin_shims \"$plugin_name\" \"$full_version\"); do\n    remove_shim_for_version \"$plugin_name\" \"$full_version\" \"$shim_path\"\n  done\n}\n\nuninstall_command \"$@\"\n"
  },
  {
    "path": "lib/commands/command-update.bash",
    "content": "# -*- sh -*-\n\nupdate_command() {\n  printf \"Upgrading asdf via asdf update is no longer supported. Please use your OS\\npackage manager (Homebrew, APT, etc...) to upgrade asdf or download the\\nlatest asdf binary manually from the asdf website.\\n\\nPlease visit https://asdf-vm.com/ or https://github.com/asdf-vm/asdf for more\\ndetails.\\n\"\n\n  exit 1\n}\n\nupdate_command \"$@\"\n"
  },
  {
    "path": "lib/commands/command-version.bash",
    "content": "# -*- sh -*-\nasdf_version\n"
  },
  {
    "path": "lib/commands/command-where.bash",
    "content": "# -*- sh -*-\n\nwhere_command() {\n  local plugin_name=$1\n  local full_version=$2\n  check_if_plugin_exists \"$plugin_name\"\n\n  local version\n  local install_type=\"version\"\n  if [[ -z ${full_version} ]]; then\n    local version_and_path\n    local versions\n    version_and_path=$(find_versions \"$plugin_name\" \"$PWD\")\n    versions=$(cut -d '|' -f 1 <<<\"$version_and_path\")\n    IFS=' ' read -r -a plugin_versions <<<\"$versions\"\n    version=\"${plugin_versions[0]}\"\n  else\n    local -a version_info\n    IFS=':' read -r -a version_info <<<\"$full_version\"\n    if [ \"${version_info[0]}\" = \"ref\" ]; then\n      install_type=\"${version_info[0]}\"\n      version=\"${version_info[1]}\"\n    else\n      version=\"${version_info[0]}\"\n    fi\n  fi\n\n  if [ -z \"$version\" ]; then\n    display_no_version_set \"$plugin_name\"\n    exit 1\n  fi\n\n  local install_path\n  install_path=$(get_install_path \"$plugin_name\" \"$install_type\" \"$version\")\n\n  if [ -d \"$install_path\" ]; then\n    printf \"%s\\n\" \"$install_path\"\n    exit 0\n  else\n    if [ \"$version\" = \"system\" ]; then\n      printf \"System version is selected\\n\"\n      exit 1\n    else\n      printf \"Version not installed\\n\"\n      exit 1\n    fi\n  fi\n}\n\nwhere_command \"$@\"\n"
  },
  {
    "path": "lib/commands/command-which.bash",
    "content": "# -*- sh -*-\n\nwhich_command() {\n  local shim_name\n  shim_name=$(basename \"$1\")\n\n  if [ -z \"$shim_name\" ]; then\n    printf \"usage: asdf which <command>\\n\"\n    exit 1\n  fi\n\n  print_exec() {\n    local plugin_name=\"$1\"\n    local version=\"$2\"\n    local executable_path=\"$3\"\n\n    if [ ! -x \"$executable_path\" ]; then\n      printf \"No %s executable found for %s %s\\n\" \"$shim_name\" \"$plugin_name\" \"$version\" >&2\n      exit 1\n    fi\n\n    printf \"%s\\n\" \"$executable_path\"\n    exit 0\n  }\n\n  with_shim_executable \"$shim_name\" print_exec || exit 1\n}\n\nwhich_command \"$@\"\n"
  },
  {
    "path": "lib/commands/reshim.bash",
    "content": "remove_shim_for_version() {\n  local plugin_name=$1\n  local version=$2\n  local shim_name\n\n  shim_name=$(basename \"$3\")\n\n  local shim_path\n  shim_path=\"$(asdf_data_dir)/shims/$shim_name\"\n\n  local count_installed\n  count_installed=$(list_installed_versions \"$plugin_name\" | wc -l)\n\n  if ! grep -x \"# asdf-plugin: $plugin_name $version\" \"$shim_path\" &>/dev/null; then\n    return 0\n  fi\n\n  sed -i.bak -e \"/# asdf-plugin: $plugin_name $version\"'$/d' \"$shim_path\"\n  rm \"$shim_path\".bak\n\n  if ! grep -q \"# asdf-plugin:\" \"$shim_path\" ||\n    [ \"$count_installed\" -eq 0 ]; then\n    rm -f \"$shim_path\"\n  fi\n}\n\nreshim_command() {\n  local plugin_name=$1\n  local full_version=$2\n\n  if [ -z \"$plugin_name\" ]; then\n    local plugins_path\n    plugins_path=$(get_plugin_path)\n\n    if find \"$plugins_path\" -mindepth 1 -type d &>/dev/null; then\n      for plugin_path in \"$plugins_path\"/*/; do\n        plugin_name=$(basename \"$plugin_path\")\n        reshim_command \"$plugin_name\"\n      done\n    fi\n    return 0\n  fi\n\n  check_if_plugin_exists \"$plugin_name\"\n  ensure_shims_dir\n\n  if [ \"$full_version\" != \"\" ]; then\n    # generate for the whole package version\n    asdf_run_hook \"pre_asdf_reshim_$plugin_name\" \"$full_version\"\n    generate_shims_for_version \"$plugin_name\" \"$full_version\"\n    asdf_run_hook \"post_asdf_reshim_$plugin_name\" \"$full_version\"\n  else\n    # generate for all versions of the package\n    local plugin_installs_path\n    plugin_installs_path=\"$(asdf_data_dir)/installs/${plugin_name}\"\n\n    for install in \"${plugin_installs_path}\"/*/; do\n      local full_version_name\n      full_version_name=$(basename \"$install\" | sed 's/ref\\-/ref\\:/')\n      asdf_run_hook \"pre_asdf_reshim_$plugin_name\" \"$full_version_name\"\n      generate_shims_for_version \"$plugin_name\" \"$full_version_name\"\n      remove_obsolete_shims \"$plugin_name\" \"$full_version_name\"\n      asdf_run_hook \"post_asdf_reshim_$plugin_name\" \"$full_version_name\"\n    done\n  fi\n\n}\n\nensure_shims_dir() {\n  # Create shims dir if doesn't exist\n  if [ ! -d \"$(asdf_data_dir)/shims\" ]; then\n    mkdir \"$(asdf_data_dir)/shims\"\n  fi\n}\n\nwrite_shim_script() {\n  local plugin_name=$1\n  local version=$2\n  local executable_path=$3\n\n  if ! is_executable \"$executable_path\"; then\n    return 0\n  fi\n\n  local executable_name\n  executable_name=$(basename \"$executable_path\")\n\n  local shim_path\n  shim_path=\"$(asdf_data_dir)/shims/$executable_name\"\n\n  local temp_dir\n  temp_dir=${TMPDIR:-/tmp}\n\n  local temp_versions_path\n  temp_versions_path=$(mktemp \"$temp_dir/asdf-command-reshim-write-shims.XXXXXX\")\n  cat <<EOF >\"$temp_versions_path\"\n# asdf-plugin: ${plugin_name} ${version}\nEOF\n\n  if [ -f \"$shim_path\" ]; then\n    grep '^#\\sasdf-plugin:\\s' <\"$shim_path\" >>\"$temp_versions_path\"\n  fi\n\n  cat <<EOF >\"$shim_path\"\n#!/usr/bin/env bash\n$(sort -u <\"$temp_versions_path\")\nexec $(asdf_dir)/bin/asdf exec \"${executable_name}\" \"\\$@\" # asdf_allow: ' asdf '\nEOF\n\n  rm \"$temp_versions_path\"\n\n  chmod +x \"$shim_path\"\n}\n\ngenerate_shims_for_version() {\n  local plugin_name=$1\n  local full_version=$2\n  local all_executable_paths\n  IFS=$'\\n' read -rd '' -a all_executable_paths <<<\"$(plugin_executables \"$plugin_name\" \"$full_version\")\"\n  for executable_path in \"${all_executable_paths[@]}\"; do\n    write_shim_script \"$plugin_name\" \"$full_version\" \"$executable_path\"\n  done\n}\n\nremove_obsolete_shims() {\n  local plugin_name=$1\n  local full_version=$2\n\n  local shims\n  shims=$(plugin_shims \"$plugin_name\" \"$full_version\" | xargs -IX basename X | sort)\n\n  local exec_names\n  exec_names=$(plugin_executables \"$plugin_name\" \"$full_version\" | xargs -IX basename X | sort)\n\n  local obsolete_shims\n  local formatted_shims\n  local formatted_exec_names\n\n  local temp_dir\n  temp_dir=${TMPDIR:-/tmp}\n\n  # comm only takes to files, so we write this data to temp files so we can\n  # pass it to comm.\n  formatted_shims=\"$(mktemp \"$temp_dir/asdf-command-reshim-formatted-shims.XXXXXX\")\"\n  printf \"%s\\n\" \"$shims\" >\"$formatted_shims\"\n\n  formatted_exec_names=\"$(mktemp \"$temp_dir/asdf-command-reshim-formatted-exec-names.XXXXXX\")\"\n  printf \"%s\\n\" \"$exec_names\" >\"$formatted_exec_names\"\n\n  obsolete_shims=$(comm -23 \"$formatted_shims\" \"$formatted_exec_names\")\n  rm -f \"$formatted_exec_names\" \"$formatted_shims\"\n\n  for shim_name in $obsolete_shims; do\n    remove_shim_for_version \"$plugin_name\" \"$full_version\" \"$shim_name\"\n  done\n}\n"
  },
  {
    "path": "lib/commands/version_commands.bash",
    "content": "# -*- sh -*-\n# shellcheck source=lib/functions/versions.bash\n. \"$(dirname \"$(dirname \"$0\")\")/lib/functions/versions.bash\"\n"
  },
  {
    "path": "lib/functions/installs.bash",
    "content": "handle_failure() {\n  local install_path=\"$1\"\n  rm -rf \"$install_path\"\n  exit 1\n}\n\nhandle_cancel() {\n  local install_path=\"$1\"\n  printf \"\\nreceived sigint, cleaning up\"\n  handle_failure \"$install_path\"\n}\n\ninstall_command() {\n  local plugin_name=$1\n  local full_version=$2\n  local extra_args=\"${*:3}\"\n\n  if [ \"$plugin_name\" = \"\" ] && [ \"$full_version\" = \"\" ]; then\n    install_local_tool_versions \"$extra_args\"\n  elif [[ $# -eq 1 ]]; then\n    install_one_local_tool \"$plugin_name\"\n  else\n    install_tool_version \"$plugin_name\" \"$full_version\" \"$extra_args\"\n  fi\n}\n\nget_concurrency() {\n  local asdf_concurrency=\n\n  if [ -n \"$ASDF_CONCURRENCY\" ]; then\n    asdf_concurrency=\"$ASDF_CONCURRENCY\"\n  else\n    asdf_concurrency=$(get_asdf_config_value 'concurrency')\n  fi\n\n  if [ \"$asdf_concurrency\" = 'auto' ]; then\n    if command -v nproc &>/dev/null; then\n      asdf_concurrency=$(nproc)\n    elif command -v sysctl &>/dev/null && sysctl hw.ncpu &>/dev/null; then\n      asdf_concurrency=$(sysctl -n hw.ncpu)\n    elif [ -f /proc/cpuinfo ]; then\n      asdf_concurrency=$(grep -c processor /proc/cpuinfo)\n    else\n      asdf_concurrency=\"1\"\n    fi\n  fi\n\n  printf \"%s\\n\" \"$asdf_concurrency\"\n}\n\ninstall_one_local_tool() {\n  local plugin_name=$1\n  local search_path\n  search_path=$PWD\n\n  local plugin_versions\n\n  local plugin_version\n\n  local plugin_version_and_path\n  plugin_version_and_path=\"$(find_versions \"$plugin_name\" \"$search_path\")\"\n\n  if [ -n \"$plugin_version_and_path\" ]; then\n    local plugin_version\n    plugin_versions=$(cut -d '|' -f 1 <<<\"$plugin_version_and_path\")\n    for plugin_version in $plugin_versions; do\n      install_tool_version \"$plugin_name\" \"$plugin_version\"\n    done\n  else\n    printf \"No versions specified for %s in config files or environment\\n\" \"$plugin_name\"\n    exit 1\n  fi\n}\n\ninstall_local_tool_versions() {\n  local plugins_path\n  plugins_path=$(get_plugin_path)\n\n  local search_path\n  search_path=$PWD\n\n  local some_tools_installed\n  local some_plugin_not_installed\n\n  local tool_versions_path\n  tool_versions_path=$(find_tool_versions)\n\n  # Locate all the plugins installed in the system\n  local plugins_installed\n  if find \"$plugins_path\" -mindepth 1 -type d &>/dev/null; then\n    for plugin_path in \"$plugins_path\"/*/; do\n      local plugin_name\n      plugin_name=$(basename \"$plugin_path\")\n      plugins_installed=\"$plugins_installed $plugin_name\"\n    done\n    plugins_installed=$(printf \"%s\" \"$plugins_installed\" | tr \" \" \"\\n\")\n  fi\n\n  if [ -z \"$plugins_installed\" ]; then\n    printf \"Install plugins first to be able to install tools\\n\"\n    exit 1\n  fi\n\n  # Locate all the plugins defined in the versions file.\n  local tools_file\n  if [ -f \"$tool_versions_path\" ]; then\n    tools_file=$(strip_tool_version_comments \"$tool_versions_path\" | cut -d ' ' -f 1)\n    for plugin_name in $tools_file; do\n      if ! printf '%s\\n' \"${plugins_installed[@]}\" | grep -q \"^$plugin_name\\$\"; then\n        printf \"%s plugin is not installed\\n\" \"$plugin_name\"\n        some_plugin_not_installed='yes'\n      fi\n    done\n  fi\n\n  if [ -n \"$some_plugin_not_installed\" ]; then\n    exit 1\n  fi\n\n  if [ -n \"$plugins_installed\" ]; then\n    for plugin_name in $plugins_installed; do\n      local plugin_version_and_path\n      plugin_version_and_path=\"$(find_versions \"$plugin_name\" \"$search_path\")\"\n\n      if [ -n \"$plugin_version_and_path\" ]; then\n        local plugin_version\n        some_tools_installed='yes'\n        plugin_versions=$(cut -d '|' -f 1 <<<\"$plugin_version_and_path\")\n        for plugin_version in $plugin_versions; do\n          install_tool_version \"$plugin_name\" \"$plugin_version\"\n        done\n      fi\n    done\n  fi\n\n  if [ -z \"$some_tools_installed\" ]; then\n    printf \"Either specify a tool & version in the command\\n\"\n    printf \"OR add .tool-versions file in this directory\\n\"\n    printf \"or in a parent directory\\n\"\n    exit 1\n  fi\n}\n\ninstall_tool_version() {\n  local plugin_name=$1\n  local full_version=$2\n  local flags=$3\n  local keep_download\n  local plugin_path\n\n  plugin_path=$(get_plugin_path \"$plugin_name\")\n  check_if_plugin_exists \"$plugin_name\"\n\n  for flag in $flags; do\n    case \"$flag\" in\n    \"--keep-download\")\n      keep_download=true\n      shift\n      ;;\n    *)\n      shift\n      ;;\n    esac\n  done\n\n  if [ \"$full_version\" = \"system\" ]; then\n    return\n  fi\n\n  IFS=':' read -r -a version_info <<<\"$full_version\"\n  if [ \"${version_info[0]}\" = \"ref\" ]; then\n    local install_type=\"${version_info[0]}\"\n    local version=\"${version_info[1]}\"\n  else\n    local install_type=\"version\"\n\n    if [ \"${version_info[0]}\" = \"latest\" ]; then\n      local version\n      version=$(latest_command \"$plugin_name\" \"${version_info[1]}\")\n      full_version=$version\n    else\n      local version=\"${version_info[0]}\"\n    fi\n  fi\n\n  local install_path\n  install_path=$(get_install_path \"$plugin_name\" \"$install_type\" \"$version\")\n  local download_path\n  download_path=$(get_download_path \"$plugin_name\" \"$install_type\" \"$version\")\n  local concurrency\n  concurrency=$(get_concurrency)\n  trap 'handle_cancel $install_path' INT\n\n  if [ -d \"$install_path\" ]; then\n    printf \"%s %s is already installed\\n\" \"$plugin_name\" \"$full_version\"\n  else\n\n    if [ -f \"${plugin_path}/bin/download\" ]; then\n      # Not a legacy plugin\n      # Run the download script\n      (\n        # shellcheck disable=SC2030\n        export ASDF_INSTALL_TYPE=$install_type\n        # shellcheck disable=SC2030\n        export ASDF_INSTALL_VERSION=$version\n        # shellcheck disable=SC2030\n        export ASDF_INSTALL_PATH=$install_path\n        # shellcheck disable=SC2030\n        export ASDF_DOWNLOAD_PATH=$download_path\n        mkdir -p \"$download_path\"\n        asdf_run_hook \"pre_asdf_download_${plugin_name}\" \"$full_version\"\n        \"${plugin_path}\"/bin/download\n      )\n    fi\n\n    local download_exit_code=$?\n    if [ $download_exit_code -eq 0 ]; then\n      (\n        # shellcheck disable=SC2031\n        export ASDF_INSTALL_TYPE=$install_type\n        # shellcheck disable=SC2031\n        export ASDF_INSTALL_VERSION=$version\n        # shellcheck disable=SC2031\n        export ASDF_INSTALL_PATH=$install_path\n        # shellcheck disable=SC2031\n        export ASDF_DOWNLOAD_PATH=$download_path\n        # shellcheck disable=SC2031\n        export ASDF_CONCURRENCY=$concurrency\n        mkdir \"$install_path\"\n        asdf_run_hook \"pre_asdf_install_${plugin_name}\" \"$full_version\"\n        \"${plugin_path}\"/bin/install\n      )\n    fi\n\n    local install_exit_code=$?\n    if [ $install_exit_code -eq 0 ] && [ $download_exit_code -eq 0 ]; then\n      # If the download directory should be kept, but isn't available, warn the user\n      always_keep_download=$(get_asdf_config_value \"always_keep_download\")\n      if [ \"$keep_download\" = \"true\" ] || [ \"$always_keep_download\" = \"yes\" ]; then\n        if [ ! -d \"$download_path\" ]; then\n          printf '%s\\n' \"asdf: Warn: You have configured asdf to preserve downloaded files (with always_keep_download=yes or --keep-download). But\" >&2\n          printf '%s\\n' \"asdf: Warn: the current plugin ($plugin_name) does not support that. Downloaded files will not be preserved.\" >&2\n        fi\n      # Otherwise, remove the download directory if it exists\n      elif [ -d \"$download_path\" ]; then\n        rm -rf \"$download_path\"\n      fi\n\n      reshim_command \"$plugin_name\" \"$full_version\"\n\n      asdf_run_hook \"post_asdf_install_${plugin_name}\" \"$full_version\"\n    else\n      handle_failure \"$install_path\"\n    fi\n  fi\n}\n"
  },
  {
    "path": "lib/functions/plugins.bash",
    "content": "plugin_list_command() {\n  local plugins_path\n  plugins_path=$(get_plugin_path)\n\n  local show_repo\n  local show_ref\n\n  while [ -n \"$*\" ]; do\n    case \"$1\" in\n    \"--urls\")\n      show_repo=true\n      shift\n      ;;\n    \"--refs\")\n      show_ref=true\n      shift\n      ;;\n    *)\n      shift\n      ;;\n    esac\n  done\n\n  if find \"$plugins_path\" -mindepth 1 -type d &>/dev/null; then\n    (\n      for plugin_path in \"$plugins_path\"/*/; do\n        plugin_name=$(basename \"$plugin_path\")\n        printf \"%s\" \"$plugin_name\"\n\n        if [ -n \"$show_repo\" ]; then\n          printf \"\\t%s\" \"$(get_plugin_remote_url \"$plugin_name\")\"\n        fi\n\n        if [ -n \"$show_ref\" ]; then\n          printf \"\\t%s\\t%s\" \\\n            \"$(get_plugin_remote_branch \"$plugin_name\")\" \\\n            \"$(get_plugin_remote_gitref \"$plugin_name\")\"\n        fi\n\n        printf \"\\n\"\n      done\n    ) | awk '{ if (NF > 1) { printf(\"%-28s\", $1) ; $1=\"\" }; print $0}'\n  else\n    display_error 'No plugins installed'\n    exit 0\n  fi\n}\n\nplugin_add_command() {\n  if [[ $# -lt 1 || $# -gt 2 ]]; then\n    display_error \"usage: asdf plugin add <name> [<git-url>]\"\n    exit 1\n  fi\n\n  local plugin_name=$1\n\n  local regex=\"^[[:lower:][:digit:]_-]+$\"\n  if ! printf \"%s\" \"$plugin_name\" | grep -q -E \"$regex\"; then\n    display_error \"$plugin_name is invalid. Name may only contain lowercase letters, numbers, '_', and '-'\"\n    exit 1\n  fi\n\n  if [ -n \"$2\" ]; then\n    local source_url=$2\n  else\n    initialize_or_update_plugin_repository\n    local source_url\n    source_url=$(get_plugin_source_url \"$plugin_name\")\n  fi\n\n  if [ -z \"$source_url\" ]; then\n    display_error \"plugin $plugin_name not found in repository\"\n    exit 1\n  fi\n\n  local plugin_path\n  plugin_path=$(get_plugin_path \"$plugin_name\")\n\n  [ -d \"$(asdf_data_dir)/plugins\" ] || mkdir -p \"$(asdf_data_dir)/plugins\"\n\n  if [ -d \"$plugin_path\" ]; then\n    printf '%s\\n' \"Plugin named $plugin_name already added\"\n    exit 0\n  else\n    asdf_run_hook \"pre_asdf_plugin_add\" \"$plugin_name\"\n    asdf_run_hook \"pre_asdf_plugin_add_${plugin_name}\"\n\n    if ! git clone -q \"$source_url\" \"$plugin_path\"; then\n      exit 1\n    fi\n\n    if [ -f \"${plugin_path}/bin/post-plugin-add\" ]; then\n      (\n        export ASDF_PLUGIN_SOURCE_URL=$source_url\n        # shellcheck disable=SC2030\n        export ASDF_PLUGIN_PATH=$plugin_path\n        \"${plugin_path}/bin/post-plugin-add\"\n      )\n    fi\n\n    asdf_run_hook \"post_asdf_plugin_add\" \"$plugin_name\"\n    asdf_run_hook \"post_asdf_plugin_add_${plugin_name}\"\n  fi\n}\n\nplugin_update_command() {\n  if [ \"$#\" -lt 1 ]; then\n    display_error \"usage: asdf plugin-update {<name> [git-ref] | --all}\"\n    exit 1\n  fi\n\n  local plugin_name=\"$1\"\n  local gitref=\"${2}\"\n  local plugins=\n\n  if [ \"$plugin_name\" = \"--all\" ]; then\n    if [ -d \"$(asdf_data_dir)\"/plugins ]; then\n      plugins=$(find \"$(asdf_data_dir)\"/plugins -mindepth 1 -maxdepth 1 -type d)\n      while IFS= read -r dir; do\n        update_plugin \"$(basename \"$dir\")\" \"$dir\" \"$gitref\" &\n      done <<<\"$plugins\"\n      wait\n    fi\n  else\n    local plugin_path\n    plugin_path=\"$(get_plugin_path \"$plugin_name\")\"\n    check_if_plugin_exists \"$plugin_name\"\n    update_plugin \"$plugin_name\" \"$plugin_path\" \"$gitref\"\n  fi\n}\n\nupdate_plugin() {\n  local plugin_name=$1\n  local plugin_path=$2\n  plugin_remote_default_branch=$(git --git-dir \"$plugin_path/.git\" --work-tree \"$plugin_path\" ls-remote --symref origin HEAD | awk '{ sub(/refs\\/heads\\//, \"\"); print $2; exit }')\n  local gitref=${3:-${plugin_remote_default_branch}}\n  logfile=$(mktemp)\n\n  local common_git_options=(--git-dir \"$plugin_path/.git\" --work-tree \"$plugin_path\")\n  local prev_ref=\n  local post_ref=\n  {\n    printf \"Location of %s plugin: %s\\n\" \"$plugin_name\" \"$plugin_path\"\n    asdf_run_hook \"pre_asdf_plugin_update\" \"$plugin_name\"\n    asdf_run_hook \"pre_asdf_plugin_update_${plugin_name}\"\n\n    printf \"Updating %s to %s\\n\" \"$plugin_name\" \"$gitref\"\n\n    git \"${common_git_options[@]}\" fetch --prune --update-head-ok origin \"$gitref:$gitref\"\n    prev_ref=$(git \"${common_git_options[@]}\" rev-parse --short HEAD)\n    post_ref=$(git \"${common_git_options[@]}\" rev-parse --short \"${gitref}\")\n    git \"${common_git_options[@]}\" -c advice.detachedHead=false checkout --force \"$gitref\"\n\n    if [ -f \"${plugin_path}/bin/post-plugin-update\" ]; then\n      (\n        # shellcheck disable=SC2031\n        export ASDF_PLUGIN_PATH=$plugin_path\n        export ASDF_PLUGIN_PREV_REF=$prev_ref\n        export ASDF_PLUGIN_POST_REF=$post_ref\n        \"${plugin_path}/bin/post-plugin-update\"\n      )\n    fi\n\n    asdf_run_hook \"post_asdf_plugin_update\" \"$plugin_name\"\n    asdf_run_hook \"post_asdf_plugin_update_${plugin_name}\"\n  } >\"$logfile\" 2>&1\n  cat \"$logfile\"\n  rm \"$logfile\"\n}\n"
  },
  {
    "path": "lib/functions/versions.bash",
    "content": "version_command() {\n  local cmd=$1\n  local plugin_name=$2\n\n  if [ \"$#\" -lt \"3\" ]; then\n    if [ \"$cmd\" = \"global\" ]; then\n      printf \"Usage: asdf global <name> <version>\\n\"\n    else\n      printf \"Usage: asdf local <name> <version>\\n\"\n    fi\n    exit 1\n  fi\n\n  shift 2\n  local versions=(\"$@\")\n\n  local file_name\n  local file\n\n  file_name=\"$(asdf_tool_versions_filename)\"\n\n  if [ \"$cmd\" = \"global\" ]; then\n    file=\"$HOME/$file_name\"\n  elif [ \"$cmd\" = \"local-tree\" ]; then\n    file=$(find_tool_versions)\n  else # cmd = local\n    file=\"$PWD/$file_name\"\n  fi\n\n  if [ -L \"$file\" ]; then\n    # Resolve file path if symlink\n    file=\"$(resolve_symlink \"$file\")\"\n  fi\n\n  check_if_plugin_exists \"$plugin_name\"\n\n  declare -a resolved_versions\n  local item\n  for item in \"${!versions[@]}\"; do\n    IFS=':' read -r -a version_info <<<\"${versions[$item]}\"\n    if [ \"${version_info[0]}\" = \"latest\" ] && [ -n \"${version_info[1]}\" ]; then\n      version=$(latest_command \"$plugin_name\" \"${version_info[1]}\")\n    elif [ \"${version_info[0]}\" = \"latest\" ] && [ -z \"${version_info[1]}\" ]; then\n      version=$(latest_command \"$plugin_name\")\n    else\n      # if branch handles ref: || path: || normal versions\n      version=\"${versions[$item]}\"\n    fi\n\n    # check_if_version_exists should probably handle if either param is empty string\n    if [ -z \"$version\" ]; then\n      exit 1\n    fi\n\n    if ! (check_if_version_exists \"$plugin_name\" \"$version\"); then\n      version_not_installed_text \"$plugin_name\" \"$version\" 1>&2\n      exit 1\n    fi\n\n    resolved_versions+=(\"$version\")\n  done\n\n  if [ -f \"$file\" ] && grep -q \"^$plugin_name \" \"$file\"; then\n    local temp_dir\n    temp_dir=${TMPDIR:-/tmp}\n\n    local temp_tool_versions_file\n    temp_tool_versions_file=$(mktemp \"$temp_dir/asdf-tool-versions-file.XXXXXX\")\n\n    cp -f \"$file\" \"$temp_tool_versions_file\"\n    sed -e \"s|^$plugin_name .*$|$plugin_name ${resolved_versions[*]}|\" \"$temp_tool_versions_file\" >\"$file\"\n    rm -f \"$temp_tool_versions_file\"\n  else\n    # Add a trailing newline at the end of the file if missing\n    [[ -f \"$file\" && -n \"$(tail -c1 \"$file\")\" ]] && printf '\\n' >>\"$file\"\n\n    # Add a new version line to the end of the file\n    printf \"%s %s\\n\" \"$plugin_name\" \"${resolved_versions[*]}\" >>\"$file\"\n  fi\n}\n\nlist_all_command() {\n  local plugin_name=$1\n  local query=$2\n  local plugin_path\n  local std_out_file\n  local std_err_file\n  local output\n  plugin_path=$(get_plugin_path \"$plugin_name\")\n  check_if_plugin_exists \"$plugin_name\"\n\n  local temp_dir\n  temp_dir=${TMPDIR:-/tmp}\n\n  # Capture return code to allow error handling\n  std_out_file=\"$(mktemp \"$temp_dir/asdf-command-list-all-${plugin_name}.stdout.XXXXXX\")\"\n  std_err_file=\"$(mktemp \"$temp_dir/asdf-command-list-all-${plugin_name}.stderr.XXXXXX\")\"\n  return_code=0 && \"${plugin_path}/bin/list-all\" >\"$std_out_file\" 2>\"$std_err_file\" || return_code=$?\n\n  if [[ $return_code -ne 0 ]]; then\n    # Printing all output to allow plugin to handle error formatting\n    printf \"Plugin %s's list-all callback script failed with output:\\n\" \"${plugin_name}\" >&2\n    printf \"%s\\n\" \"$(cat \"$std_err_file\")\" >&2\n    printf \"%s\\n\" \"$(cat \"$std_out_file\")\" >&2\n    rm \"$std_out_file\" \"$std_err_file\"\n    exit 1\n  fi\n\n  if [[ $query ]]; then\n    output=$(tr ' ' '\\n' <\"$std_out_file\" |\n      grep -E \"^\\\\s*$query\" |\n      tr '\\n' ' ')\n  else\n    output=$(cat \"$std_out_file\")\n  fi\n\n  if [ -z \"$output\" ]; then\n    display_error \"No compatible versions available ($plugin_name $query)\"\n    exit 1\n  fi\n\n  IFS=' ' read -r -a versions_list <<<\"$output\"\n\n  for version in \"${versions_list[@]}\"; do\n    printf \"%s\\n\" \"${version}\"\n  done\n\n  # Remove temp files if they still exist\n  rm \"$std_out_file\" \"$std_err_file\" || true\n}\n\nlatest_command() {\n  DEFAULT_QUERY=\"[0-9]\"\n\n  local plugin_name=$1\n  local query=$2\n  local plugin_path\n\n  if [ \"$plugin_name\" = \"--all\" ]; then\n    latest_all\n  fi\n\n  [[ -z $query ]] && query=\"$DEFAULT_QUERY\"\n\n  plugin_path=$(get_plugin_path \"$plugin_name\")\n  check_if_plugin_exists \"$plugin_name\"\n\n  local versions\n\n  if [ -f \"${plugin_path}/bin/latest-stable\" ]; then\n    versions=$(\"${plugin_path}\"/bin/latest-stable \"$query\")\n    if [ -z \"${versions}\" ]; then\n      # this branch requires this print to mimic the error from the list-all branch\n      printf \"No compatible versions available (%s %s)\\n\" \"$plugin_name\" \"$query\" >&2\n      exit 1\n    fi\n  else\n    # pattern from xxenv-latest (https://github.com/momo-lab/xxenv-latest)\n    versions=$(list_all_command \"$plugin_name\" \"$query\" |\n      grep -ivE \"(^Available versions:|-src|-dev|-latest|-stm|[-\\\\.]rc|-milestone|-alpha|-beta|[-\\\\.]pre|-next|(a|b|c)[0-9]+|snapshot|master)\" |\n      sed 's/^[[:space:]]\\+//' |\n      tail -1)\n    if [ -z \"${versions}\" ]; then\n      exit 1\n    fi\n  fi\n\n  printf \"%s\\n\" \"$versions\"\n}\n\nlatest_all() {\n  local plugins_path\n  plugins_path=$(get_plugin_path)\n\n  if find \"$plugins_path\" -mindepth 1 -type d &>/dev/null; then\n    for plugin_path in \"$plugins_path\"/*/; do\n      plugin_name=$(basename \"$plugin_path\")\n\n      # Retrieve the version of the plugin\n      local version\n      if [ -f \"${plugin_path}/bin/latest-stable\" ]; then\n        # We can't filter by a concrete query because different plugins might\n        # have different queries.\n        version=$(\"${plugin_path}\"/bin/latest-stable \"\")\n        if [ -z \"${version}\" ]; then\n          version=\"unknown\"\n        fi\n      else\n        # pattern from xxenv-latest (https://github.com/momo-lab/xxenv-latest)\n        version=$(list_all_command \"$plugin_name\" |\n          grep -ivE \"(^Available version:|-src|-dev|-latest|-stm|[-\\\\.]rc|-alpha|-beta|[-\\\\.]pre|-next|(a|b|c)[0-9]+|snapshot|master)\" |\n          sed 's/^[[:space:]]\\+//' |\n          tail -1)\n        if [ -z \"${version}\" ]; then\n          version=\"unknown\"\n        fi\n      fi\n\n      local installed_status\n      installed_status=\"missing\"\n\n      local installed_versions\n      installed_versions=$(list_installed_versions \"$plugin_name\")\n\n      if [ -n \"$installed_versions\" ] && printf '%s\\n' \"$installed_versions\" | grep -q \"^$version\\$\"; then\n        installed_status=\"installed\"\n      fi\n      printf \"%s\\t%s\\t%s\\n\" \"$plugin_name\" \"$version\" \"$installed_status\"\n    done\n  else\n    printf \"%s\\n\" 'No plugins installed'\n  fi\n  exit 0\n}\n\nlocal_command() {\n  local parent=false\n  local positional=()\n\n  while [[ $# -gt 0 ]]; do\n    case $1 in\n    -p | --parent)\n      parent=\"true\"\n      shift # past value\n      ;;\n    *)\n      positional+=(\"$1\") # save it in an array for later\n      shift              # past argument\n      ;;\n    esac\n  done\n\n  set -- \"${positional[@]}\" # restore positional parameters\n\n  if [ $parent = true ]; then\n    version_command local-tree \"$@\"\n  else\n    version_command local \"$@\"\n  fi\n}\n"
  },
  {
    "path": "lib/utils.bash",
    "content": "# We shouldn't rely on the user's grep settings to be correct. If we set these\n# here anytime asdf invokes grep it will be invoked with these options\n# shellcheck disable=SC2034\nGREP_OPTIONS=\"--color=never\"\n# shellcheck disable=SC2034\nGREP_COLORS=\n\nasdf_version() {\n  local version git_rev\n  version=\"v$(cat \"$(asdf_dir)/version.txt\")\"\n  if [ -d \"$(asdf_dir)/.git\" ]; then\n    git_rev=\"$(git --git-dir \"$(asdf_dir)/.git\" rev-parse --short HEAD)\"\n    printf \"%s-%s\\n\" \"$version\" \"$git_rev\"\n  else\n    printf \"%s\\n\" \"$version\"\n  fi\n}\n\nasdf_tool_versions_filename() {\n  printf '%s\\n' \"${ASDF_DEFAULT_TOOL_VERSIONS_FILENAME:-.tool-versions}\"\n}\n\nasdf_config_file() {\n  printf '%s\\n' \"${ASDF_CONFIG_FILE:-$HOME/.asdfrc}\"\n}\n\nasdf_data_dir() {\n  local data_dir\n\n  if [ -n \"${ASDF_DATA_DIR}\" ]; then\n    data_dir=\"${ASDF_DATA_DIR}\"\n  elif [ -n \"$HOME\" ]; then\n    data_dir=\"$HOME/.asdf\"\n  else\n    data_dir=$(asdf_dir)\n  fi\n\n  printf \"%s\\n\" \"$data_dir\"\n}\n\nasdf_dir() {\n  if [ -z \"$ASDF_DIR\" ]; then\n    local current_script_path=${BASH_SOURCE[0]}\n    printf '%s\\n' \"$(\n      cd -- \"$(dirname \"$(dirname \"$current_script_path\")\")\" || exit\n      printf '%s\\n' \"$PWD\"\n    )\"\n  else\n    printf '%s\\n' \"$ASDF_DIR\"\n  fi\n}\n\nasdf_plugin_repository_url() {\n  printf \"https://github.com/asdf-vm/asdf-plugins.git\\n\"\n}\n\nget_install_path() {\n  local plugin=$1\n  local install_type=$2\n  local version=$3\n\n  local install_dir\n  install_dir=\"$(asdf_data_dir)/installs\"\n\n  [ -d \"${install_dir}/${plugin}\" ] || mkdir -p \"${install_dir}/${plugin}\"\n\n  if [ \"$install_type\" = \"version\" ]; then\n    printf \"%s/%s/%s\\n\" \"$install_dir\" \"$plugin\" \"$version\"\n  elif [ \"$install_type\" = \"path\" ]; then\n    printf \"%s\\n\" \"$version\"\n  else\n    printf \"%s/%s/%s-%s\\n\" \"$install_dir\" \"$plugin\" \"$install_type\" \"$version\"\n  fi\n}\n\nget_download_path() {\n  local plugin=$1\n  local install_type=$2\n  local version=$3\n\n  local download_dir\n  download_dir=\"$(asdf_data_dir)/downloads\"\n\n  [ -d \"${download_dir}/${plugin}\" ] || mkdir -p \"${download_dir}/${plugin}\"\n\n  if [ \"$install_type\" = \"version\" ]; then\n    printf \"%s/%s/%s\\n\" \"$download_dir\" \"$plugin\" \"$version\"\n  elif [ \"$install_type\" = \"path\" ]; then\n    return\n  else\n    printf \"%s/%s/%s-%s\\n\" \"$download_dir\" \"$plugin\" \"$install_type\" \"$version\"\n  fi\n}\n\nlist_installed_versions() {\n  local plugin_name=$1\n  local plugin_path\n  plugin_path=$(get_plugin_path \"$plugin_name\")\n\n  local plugin_installs_path\n  plugin_installs_path=\"$(asdf_data_dir)/installs/${plugin_name}\"\n\n  if [ -d \"$plugin_installs_path\" ]; then\n    for install in \"${plugin_installs_path}\"/*/; do\n      [[ -e \"$install\" ]] || break\n      basename \"$install\" | sed 's/^ref-/ref:/'\n    done\n  fi\n}\n\ncheck_if_plugin_exists() {\n  local plugin_name=$1\n\n  # Check if we have a non-empty argument\n  if [ -z \"${1}\" ]; then\n    display_error \"No plugin given\"\n    exit 1\n  fi\n\n  if [ ! -d \"$(asdf_data_dir)/plugins/$plugin_name\" ]; then\n    display_error \"No such plugin: $plugin_name\"\n    exit 1\n  fi\n}\n\ncheck_if_version_exists() {\n  local plugin_name=$1\n  local version=$2\n\n  check_if_plugin_exists \"$plugin_name\"\n\n  local install_path\n  install_path=$(find_install_path \"$plugin_name\" \"$version\")\n\n  if [ \"$version\" != \"system\" ] && [ ! -d \"$install_path\" ]; then\n    exit 1\n  fi\n}\n\nversion_not_installed_text() {\n  local plugin_name=$1\n  local version=$2\n\n  printf \"version %s is not installed for %s\\n\" \"$version\" \"$plugin_name\"\n}\n\nget_plugin_path() {\n  if [ -n \"$1\" ]; then\n    printf \"%s\\n\" \"$(asdf_data_dir)/plugins/$1\"\n  else\n    printf \"%s\\n\" \"$(asdf_data_dir)/plugins\"\n  fi\n}\n\ndisplay_error() {\n  printf \"%s\\n\" \"$1\" >&2\n}\n\nget_version_in_dir() {\n  local plugin_name=$1\n  local search_path=$2\n  local legacy_filenames=$3\n\n  local asdf_version\n\n  file_name=$(asdf_tool_versions_filename)\n  asdf_version=$(parse_asdf_version_file \"$search_path/$file_name\" \"$plugin_name\")\n\n  if [ -n \"$asdf_version\" ]; then\n    printf \"%s\\n\" \"$asdf_version|$search_path/$file_name\"\n    return 0\n  fi\n\n  for filename in $legacy_filenames; do\n    local legacy_version\n    legacy_version=$(parse_legacy_version_file \"$search_path/$filename\" \"$plugin_name\")\n\n    if [ -n \"$legacy_version\" ]; then\n      printf \"%s\\n\" \"$legacy_version|$search_path/$filename\"\n      return 0\n    fi\n  done\n}\n\nfind_versions() {\n  local plugin_name=$1\n  local search_path=$2\n\n  local version\n  version=$(get_version_from_env \"$plugin_name\")\n  if [ -n \"$version\" ]; then\n    local upcase_name\n    upcase_name=$(printf \"%s\\n\" \"$plugin_name\" | tr '[:lower:]-' '[:upper:]_')\n    local version_env_var=\"ASDF_${upcase_name}_VERSION\"\n\n    printf \"%s\\n\" \"$version|$version_env_var environment variable\"\n    return 0\n  fi\n\n  local plugin_path\n  plugin_path=$(get_plugin_path \"$plugin_name\")\n  local legacy_config\n  legacy_config=$(get_asdf_config_value \"legacy_version_file\")\n  local legacy_list_filenames_script\n  legacy_list_filenames_script=\"${plugin_path}/bin/list-legacy-filenames\"\n  local legacy_filenames=\"\"\n\n  if [ \"$legacy_config\" = \"yes\" ] && [ -f \"$legacy_list_filenames_script\" ]; then\n    legacy_filenames=$(\"$legacy_list_filenames_script\")\n  fi\n\n  while [ \"$search_path\" != \"/\" ]; do\n    version=$(get_version_in_dir \"$plugin_name\" \"$search_path\" \"$legacy_filenames\")\n    if [ -n \"$version\" ]; then\n      printf \"%s\\n\" \"$version\"\n      return 0\n    fi\n    search_path=$(dirname \"$search_path\")\n  done\n\n  get_version_in_dir \"$plugin_name\" \"$HOME\" \"$legacy_filenames\"\n\n  if [ -f \"$ASDF_DEFAULT_TOOL_VERSIONS_FILENAME\" ]; then\n    versions=$(parse_asdf_version_file \"$ASDF_DEFAULT_TOOL_VERSIONS_FILENAME\" \"$plugin_name\")\n    if [ -n \"$versions\" ]; then\n      printf \"%s\\n\" \"$versions|$ASDF_DEFAULT_TOOL_VERSIONS_FILENAME\"\n      return 0\n    fi\n  fi\n}\n\ndisplay_no_version_set() {\n  local plugin_name=$1\n  printf \"No version is set for %s; please run \\`asdf <global | shell | local> %s <version>\\`\\n\" \"$plugin_name\" \"$plugin_name\"\n}\n\nget_version_from_env() {\n  local plugin_name=$1\n  local upcase_name\n  upcase_name=$(printf \"%s\\n\" \"$plugin_name\" | tr '[:lower:]-' '[:upper:]_')\n  local version_env_var=\"ASDF_${upcase_name}_VERSION\"\n  local version=${!version_env_var:-}\n  printf \"%s\\n\" \"$version\"\n}\n\nfind_install_path() {\n  local plugin_name=$1\n  local version=$2\n\n  # shellcheck disable=SC2162\n  IFS=':' read -a version_info <<<\"$version\"\n\n  if [ \"$version\" = \"system\" ]; then\n    printf \"\\n\"\n  elif [ \"${version_info[0]}\" = \"ref\" ]; then\n    local install_type=\"${version_info[0]}\"\n    local version=\"${version_info[1]}\"\n    get_install_path \"$plugin_name\" \"$install_type\" \"$version\"\n  elif [ \"${version_info[0]}\" = \"path\" ]; then\n    # This is for people who have the local source already compiled\n    # Like those who work on the language, etc\n    # We'll allow specifying path:/foo/bar/project in .tool-versions\n    # And then use the binaries there\n    local install_type=\"path\"\n    local version=\"path\"\n\n    util_resolve_user_path \"${version_info[1]}\"\n    printf \"%s\\n\" \"${util_resolve_user_path_reply}\"\n  else\n    local install_type=\"version\"\n    local version=\"${version_info[0]}\"\n    get_install_path \"$plugin_name\" \"$install_type\" \"$version\"\n  fi\n}\n\nget_custom_executable_path() {\n  local plugin_path=$1\n  local install_path=$2\n  local executable_path=$3\n\n  # custom plugin hook for executable path\n  if [ -x \"${plugin_path}/bin/exec-path\" ]; then\n    cmd=$(basename \"$executable_path\")\n    local relative_path\n    # shellcheck disable=SC2001\n    relative_path=$(printf \"%s\\n\" \"$executable_path\" | sed -e \"s|${install_path}/||\")\n    relative_path=\"$(\"${plugin_path}/bin/exec-path\" \"$install_path\" \"$cmd\" \"$relative_path\")\"\n    executable_path=\"$install_path/$relative_path\"\n  fi\n\n  printf \"%s\\n\" \"$executable_path\"\n}\n\nget_executable_path() {\n  local plugin_name=$1\n  local version=$2\n  local executable_path=$3\n\n  check_if_version_exists \"$plugin_name\" \"$version\"\n\n  if [ \"$version\" = \"system\" ]; then\n    path=$(remove_path_from_path \"$PATH\" \"$(asdf_data_dir)/shims\")\n    cmd=$(basename \"$executable_path\")\n    cmd_path=$(PATH=$path command -v \"$cmd\" 2>&1)\n    # shellcheck disable=SC2181\n    if [ $? -ne 0 ]; then\n      return 1\n    fi\n    printf \"%s\\n\" \"$cmd_path\"\n  else\n    local install_path\n    install_path=$(find_install_path \"$plugin_name\" \"$version\")\n    printf \"%s\\n\" \"${install_path}\"/\"${executable_path}\"\n  fi\n}\n\nparse_asdf_version_file() {\n  local file_path=$1\n  local plugin_name=$2\n\n  if [ -f \"$file_path\" ]; then\n    local version\n    version=$(strip_tool_version_comments \"$file_path\" | grep \"^${plugin_name} \" | sed -e \"s/^${plugin_name} //\")\n\n    if [ -n \"$version\" ]; then\n      if [[ \"$version\" == path:* ]]; then\n        util_resolve_user_path \"${version#path:}\"\n        printf \"%s\\n\" \"path:${util_resolve_user_path_reply}\"\n      else\n        printf \"%s\\n\" \"$version\"\n      fi\n\n      return 0\n    fi\n  fi\n}\n\nparse_legacy_version_file() {\n  local file_path=$1\n  local plugin_name=$2\n\n  local plugin_path\n  plugin_path=$(get_plugin_path \"$plugin_name\")\n  local parse_legacy_script\n  parse_legacy_script=\"${plugin_path}/bin/parse-legacy-file\"\n\n  if [ -f \"$file_path\" ]; then\n    if [ -f \"$parse_legacy_script\" ]; then\n      \"$parse_legacy_script\" \"$file_path\"\n    else\n      cat \"$file_path\"\n    fi\n  fi\n}\n\nget_preset_version_for() {\n  local plugin_name=$1\n  local search_path\n  search_path=$PWD\n  local version_and_path\n  version_and_path=$(find_versions \"$plugin_name\" \"$search_path\")\n  local version\n  version=$(cut -d '|' -f 1 <<<\"$version_and_path\")\n\n  printf \"%s\\n\" \"$version\"\n}\n\nget_asdf_config_value_from_file() {\n  local config_path=$1\n  local key=$2\n\n  if [ ! -f \"$config_path\" ]; then\n    return 1\n  fi\n\n  util_validate_no_carriage_returns \"$config_path\"\n\n  local result\n  result=$(grep -E \"^\\s*$key\\s*=\\s*\" \"$config_path\" | head | sed -e 's/^[^=]*= *//' -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')\n  if [ -n \"$result\" ]; then\n    printf \"%s\\n\" \"$result\"\n    return 0\n  fi\n\n  return 2\n}\n\nget_asdf_config_value() {\n  local key=$1\n  local config_path=\n  config_path=$(asdf_config_file)\n  local default_config_path=${ASDF_CONFIG_DEFAULT_FILE:-\"$(asdf_dir)/defaults\"}\n\n  local local_config_path\n  local_config_path=\"$(find_file_upwards \".asdfrc\")\"\n\n  get_asdf_config_value_from_file \"$local_config_path\" \"$key\" ||\n    get_asdf_config_value_from_file \"$config_path\" \"$key\" ||\n    get_asdf_config_value_from_file \"$default_config_path\" \"$key\"\n}\n\n# Whether the plugin shortname repo needs to be synced\n# 0: if no sync needs to occur\n# 1: if sync needs to occur\nrepository_needs_update() {\n  local plugin_repository_last_check_duration\n  local sync_required\n\n  plugin_repository_last_check_duration=\"$(get_asdf_config_value \"plugin_repository_last_check_duration\")\"\n\n  if [ \"never\" != \"$plugin_repository_last_check_duration\" ]; then\n    local update_file_dir\n    local update_file_name\n    update_file_dir=\"$(asdf_data_dir)/tmp\"\n    update_file_name=\"repo-updated\"\n    # `find` outputs filename if it has not been modified in plugin_repository_last_check_duration setting.\n    sync_required=$(find \"$update_file_dir\" -name \"$update_file_name\" -type f -mmin +\"${plugin_repository_last_check_duration:-60}\" -print)\n  fi\n\n  [ \"$sync_required\" ]\n}\n\ninitialize_or_update_plugin_repository() {\n  local repository_url\n  local repository_path\n\n  disable_plugin_short_name_repo=\"$(get_asdf_config_value \"disable_plugin_short_name_repository\")\"\n  if [ \"yes\" = \"$disable_plugin_short_name_repo\" ]; then\n    printf \"Short-name plugin repository is disabled\\n\" >&2\n    exit 1\n  fi\n\n  repository_url=$(asdf_plugin_repository_url)\n  repository_path=$(asdf_data_dir)/repository\n\n  if [ ! -d \"$repository_path\" ]; then\n    printf \"initializing plugin repository...\"\n    git clone \"$repository_url\" \"$repository_path\"\n  elif repository_needs_update; then\n    printf \"updating plugin repository...\"\n    git -C \"$repository_path\" fetch\n    git -C \"$repository_path\" reset --hard origin/master\n  fi\n\n  [ -d \"$(asdf_data_dir)/tmp\" ] || mkdir -p \"$(asdf_data_dir)/tmp\"\n  touch \"$(asdf_data_dir)/tmp/repo-updated\"\n}\n\nget_plugin_source_url() {\n  local plugin_name=$1\n  local plugin_config\n\n  plugin_config=\"$(asdf_data_dir)/repository/plugins/$plugin_name\"\n\n  if [ -f \"$plugin_config\" ]; then\n    grep \"repository\" \"$plugin_config\" | awk -F'=' '{print $2}' | sed 's/ //'\n  fi\n}\n\nfind_tool_versions() {\n  find_file_upwards \"$(asdf_tool_versions_filename)\"\n}\n\nfind_file_upwards() {\n  local name=\"$1\"\n  local search_path\n  search_path=$PWD\n  while [ \"$search_path\" != \"/\" ]; do\n    if [ -f \"$search_path/$name\" ]; then\n      util_validate_no_carriage_returns \"$search_path/$name\"\n\n      printf \"%s\\n\" \"${search_path}/$name\"\n      return 0\n    fi\n    search_path=$(dirname \"$search_path\")\n  done\n}\n\nresolve_symlink() {\n  local symlink\n  symlink=\"$1\"\n\n  # This seems to be the only cross-platform way to resolve symlink paths to\n  # the real file path.\n  # shellcheck disable=SC2012\n  resolved_path=$(ls -l \"$symlink\" | sed -e 's|.*-> \\(.*\\)|\\1|') # asdf_allow: ls '\n\n  # Check if resolved path is relative or not by looking at the first character.\n  # If it is a slash we can assume it's root and absolute. Otherwise we treat it\n  # as relative\n  case $resolved_path in\n  /*)\n    printf \"%s\\n\" \"$resolved_path\"\n    ;;\n  *)\n    (\n      cd \"$(dirname \"$symlink\")\" || exit 1\n      printf \"%s\\n\" \"$PWD/$resolved_path\"\n    )\n    ;;\n  esac\n}\n\nlist_plugin_bin_paths() {\n  local plugin_name=$1\n  local version=$2\n  local install_type=$3\n  local plugin_path\n  plugin_path=$(get_plugin_path \"$plugin_name\")\n  local install_path\n  install_path=$(get_install_path \"$plugin_name\" \"$install_type\" \"$version\")\n\n  if [ -f \"${plugin_path}/bin/list-bin-paths\" ]; then\n    local space_separated_list_of_bin_paths\n\n    # shellcheck disable=SC2030\n    space_separated_list_of_bin_paths=$(\n      export ASDF_INSTALL_TYPE=$install_type\n      export ASDF_INSTALL_VERSION=$version\n      export ASDF_INSTALL_PATH=$install_path\n      \"${plugin_path}/bin/list-bin-paths\"\n    )\n  else\n    local space_separated_list_of_bin_paths=\"bin\"\n  fi\n  printf \"%s\\n\" \"$space_separated_list_of_bin_paths\"\n}\n\nlist_plugin_exec_paths() {\n  local plugin_name=$1\n  local full_version=$2\n  check_if_plugin_exists \"$plugin_name\"\n\n  IFS=':' read -r -a version_info <<<\"$full_version\"\n  if [ \"${version_info[0]}\" = \"ref\" ]; then\n    local install_type=\"${version_info[0]}\"\n    local version=\"${version_info[1]}\"\n  elif [ \"${version_info[0]}\" = \"path\" ]; then\n    local install_type=\"${version_info[0]}\"\n    local version=\"${version_info[1]}\"\n  else\n    local install_type=\"version\"\n    local version=\"${version_info[0]}\"\n  fi\n\n  local plugin_shims_path\n  plugin_shims_path=$(get_plugin_path \"$plugin_name\")/shims\n  if [ -d \"$plugin_shims_path\" ]; then\n    printf \"%s\\n\" \"$plugin_shims_path\"\n  fi\n\n  space_separated_list_of_bin_paths=\"$(list_plugin_bin_paths \"$plugin_name\" \"$version\" \"$install_type\")\"\n  IFS=' ' read -r -a all_bin_paths <<<\"$space_separated_list_of_bin_paths\"\n\n  local install_path\n  install_path=$(get_install_path \"$plugin_name\" \"$install_type\" \"$version\")\n\n  for bin_path in \"${all_bin_paths[@]}\"; do\n    printf \"%s\\n\" \"$install_path/$bin_path\"\n  done\n}\n\nwith_plugin_env() {\n  local plugin_name=$1\n  local full_version=$2\n  local callback=$3\n\n  IFS=':' read -r -a version_info <<<\"$full_version\"\n  if [ \"${version_info[0]}\" = \"ref\" ]; then\n    local install_type=\"${version_info[0]}\"\n    local version=\"${version_info[1]}\"\n  else\n    local install_type=\"version\"\n    local version=\"${version_info[0]}\"\n  fi\n\n  if [ \"$version\" = \"system\" ]; then\n    # execute as is for system\n    \"$callback\"\n    return $?\n  fi\n\n  local plugin_path\n  plugin_path=$(get_plugin_path \"$plugin_name\")\n\n  # add the plugin listed exec paths to PATH\n  local path exec_paths\n  exec_paths=\"$(list_plugin_exec_paths \"$plugin_name\" \"$full_version\")\"\n\n  # exec_paths contains a trailing newline which is converted to a colon, so no\n  # colon is needed between the subshell and the PATH variable in this string\n  path=\"$(tr '\\n' ':' <<<\"$exec_paths\")$PATH\"\n\n  # If no custom exec-env transform, just execute callback\n  if [ ! -f \"${plugin_path}/bin/exec-env\" ]; then\n    PATH=$path \"$callback\"\n    return $?\n  fi\n\n  # Load the plugin custom environment\n  local install_path\n  install_path=$(find_install_path \"$plugin_name\" \"$full_version\")\n\n  # shellcheck source=/dev/null\n  ASDF_INSTALL_TYPE=$install_type \\\n    ASDF_INSTALL_VERSION=$version \\\n    ASDF_INSTALL_PATH=$install_path \\\n    . \"${plugin_path}/bin/exec-env\"\n\n  PATH=$path \"$callback\"\n}\n\nplugin_executables() {\n  local plugin_name=$1\n  local full_version=$2\n  local all_bin_paths\n  IFS=$'\\n' read -rd '' -a all_bin_paths <<<\"$(list_plugin_exec_paths \"$plugin_name\" \"$full_version\")\"\n  for bin_path in \"${all_bin_paths[@]}\"; do\n    for executable_file in \"$bin_path\"/*; do\n      if is_executable \"$executable_file\"; then\n        printf \"%s\\n\" \"$executable_file\"\n      fi\n    done\n  done\n}\n\nis_executable() {\n  local executable_path=$1\n  if [[ (-f \"$executable_path\") && (-x \"$executable_path\") ]]; then\n    return 0\n  fi\n  return 1\n}\n\nplugin_shims() {\n  local plugin_name=$1\n  local full_version=$2\n  grep -lx \"# asdf-plugin: $plugin_name $full_version\" \"$(asdf_data_dir)/shims\"/* 2>/dev/null\n}\n\nshim_plugin_versions() {\n  local executable_name\n  executable_name=$(basename \"$1\")\n  local shim_path\n  shim_path=\"$(asdf_data_dir)/shims/${executable_name}\"\n  if [ -x \"$shim_path\" ]; then\n    grep \"# asdf-plugin: \" \"$shim_path\" 2>/dev/null | sed -e \"s/# asdf-plugin: //\" | uniq\n  else\n    printf \"asdf: unknown shim %s\\n\" \"$executable_name\"\n    return 1\n  fi\n}\n\nshim_plugins() {\n  local executable_name\n  executable_name=$(basename \"$1\")\n  local shim_path\n  shim_path=\"$(asdf_data_dir)/shims/${executable_name}\"\n  if [ -x \"$shim_path\" ]; then\n    grep \"# asdf-plugin: \" \"$shim_path\" 2>/dev/null | sed -e \"s/# asdf-plugin: //\" | cut -d' ' -f 1 | uniq\n  else\n    printf \"asdf: unknown shim %s\\n\" \"$executable_name\"\n    return 1\n  fi\n}\n\nstrip_tool_version_comments() {\n  local tool_version_path=\"$1\"\n  # Use sed to strip comments from the tool version file\n  # Breakdown of sed command:\n  # This command represents 3 steps, separated by a semi-colon (;), that run on each line.\n  # 1. Delete line if it starts with any blankspace and a #.\n  # 2. Find a # and delete it and everything after the #.\n  # 3. Remove any whitespace from the end of the line.\n  # Finally, the command will print the lines that are not empty.\n  sed '/^[[:blank:]]*#/d;s/#.*//;s/[[:blank:]]*$//' \"$tool_version_path\"\n}\n\nasdf_run_hook() {\n  local hook_name=$1\n  local hook_cmd\n  hook_cmd=\"$(get_asdf_config_value \"$hook_name\")\"\n  if [ -n \"$hook_cmd\" ]; then\n    asdf_hook_fun() {\n      unset asdf_hook_fun\n      ev'al' \"$hook_cmd\" # ignore banned command just here\n    }\n    asdf_hook_fun \"${@:2}\"\n  fi\n}\n\nget_shim_versions() {\n  shim_name=$1\n  shim_plugin_versions \"${shim_name}\"\n  shim_plugin_versions \"${shim_name}\" | cut -d' ' -f 1 | awk '{print$1\" system\"}'\n}\n\npreset_versions() {\n  shim_name=$1\n  shim_plugin_versions \"${shim_name}\" | cut -d' ' -f 1 | uniq | xargs -IPLUGIN bash -c \". $(asdf_dir)/lib/utils.bash; printf \\\"%s %s\\n\\\" PLUGIN \\$(get_preset_version_for PLUGIN)\"\n}\n\nselect_from_preset_version() {\n  local shim_name=$1\n  local shim_versions\n  local preset_versions\n\n  shim_versions=$(get_shim_versions \"$shim_name\")\n  if [ -n \"$shim_versions\" ]; then\n    preset_versions=$(preset_versions \"$shim_name\")\n    grep -F \"$shim_versions\" <<<\"$preset_versions\" | head -n 1 | xargs -IVERSION printf \"%s\\n\" VERSION\n  fi\n}\n\nselect_version() {\n  shim_name=$1\n  # First, we get the all the plugins where the\n  # current shim is available.\n  # Then, we iterate on all versions set for each plugin\n  # Note that multiple plugin versions can be set for a single plugin.\n  # These are separated by a space. e.g. python 3.7.2 2.7.15\n  # For each plugin/version pair, we check if it is present in the shim\n  local search_path\n  search_path=$PWD\n  local shim_versions\n  IFS=$'\\n' read -rd '' -a shim_versions <<<\"$(get_shim_versions \"$shim_name\")\"\n\n  local plugins\n  IFS=$'\\n' read -rd '' -a plugins <<<\"$(shim_plugins \"$shim_name\")\"\n\n  for plugin_name in \"${plugins[@]}\"; do\n    local version_and_path\n    local version_string\n    local usable_plugin_versions\n    local _path\n    version_and_path=$(find_versions \"$plugin_name\" \"$search_path\")\n    IFS='|' read -r version_string _path <<<\"$version_and_path\"\n    IFS=' ' read -r -a usable_plugin_versions <<<\"$version_string\"\n    for plugin_version in \"${usable_plugin_versions[@]}\"; do\n      for plugin_and_version in \"${shim_versions[@]}\"; do\n        local plugin_shim_name\n        local plugin_shim_version\n        IFS=' ' read -r plugin_shim_name plugin_shim_version <<<\"$plugin_and_version\"\n        if [[ \"$plugin_name\" == \"$plugin_shim_name\" ]]; then\n          if [[ \"$plugin_version\" == \"$plugin_shim_version\" ]]; then\n            printf \"%s\\n\" \"$plugin_name $plugin_version\"\n            return\n          elif [[ \"$plugin_version\" == \"path:\"* ]]; then\n            printf \"%s\\n\" \"$plugin_name $plugin_version\"\n            return\n          fi\n        fi\n      done\n    done\n  done\n}\n\nwith_shim_executable() {\n  local shim_name\n  shim_name=$(basename \"$1\")\n  local shim_exec=\"${2}\"\n\n  if [ ! -f \"$(asdf_data_dir)/shims/${shim_name}\" ]; then\n    printf \"%s %s %s\\n\" \"unknown command:\" \"${shim_name}.\" \"Perhaps you have to reshim?\" >&2\n    return 1\n  fi\n\n  local selected_version\n  selected_version=\"$(select_version \"$shim_name\")\"\n\n  if [ -z \"$selected_version\" ]; then\n    selected_version=\"$(select_from_preset_version \"$shim_name\")\"\n  fi\n\n  if [ -n \"$selected_version\" ]; then\n    local plugin_name\n    local full_version\n    local plugin_path\n\n    IFS=' ' read -r plugin_name full_version <<<\"$selected_version\"\n    plugin_path=$(get_plugin_path \"$plugin_name\")\n\n    # This function does get invoked, but shellcheck sees it as unused code\n    # shellcheck disable=SC2317\n    run_within_env() {\n      local path\n      path=$(remove_path_from_path \"$PATH\" \"$(asdf_data_dir)/shims\")\n\n      executable_path=$(PATH=$path command -v \"$shim_name\")\n\n      if [ -x \"${plugin_path}/bin/exec-path\" ]; then\n        install_path=$(find_install_path \"$plugin_name\" \"$full_version\")\n        executable_path=$(get_custom_executable_path \"${plugin_path}\" \"${install_path}\" \"${executable_path:-${shim_name}}\")\n      fi\n\n      \"$shim_exec\" \"$plugin_name\" \"$full_version\" \"$executable_path\"\n    }\n\n    with_plugin_env \"$plugin_name\" \"$full_version\" run_within_env\n    return $?\n  fi\n\n  (\n    local preset_plugin_versions\n    preset_plugin_versions=()\n    local closest_tool_version\n    closest_tool_version=$(find_tool_versions)\n\n    local shim_plugins\n    IFS=$'\\n' read -rd '' -a shim_plugins <<<\"$(shim_plugins \"$shim_name\")\"\n    for shim_plugin in \"${shim_plugins[@]}\"; do\n      local shim_versions\n      local version_string\n      version_string=$(get_preset_version_for \"$shim_plugin\")\n      IFS=' ' read -r -a shim_versions <<<\"$version_string\"\n      local usable_plugin_versions\n      for shim_version in \"${shim_versions[@]}\"; do\n        preset_plugin_versions+=(\"$shim_plugin $shim_version\")\n      done\n    done\n\n    if [ -n \"${preset_plugin_versions[*]}\" ]; then\n      printf \"%s %s\\n\" \"No preset version installed for command\" \"$shim_name\"\n      printf \"%s\\n\\n\" \"Please install a version by running one of the following:\"\n      for preset_plugin_version in \"${preset_plugin_versions[@]}\"; do\n        printf \"%s %s\\n\" \"asdf install\" \"$preset_plugin_version\"\n      done\n      printf \"\\n%s %s\\n\" \"or add one of the following versions in your config file at\" \"$closest_tool_version\"\n    else\n      printf \"%s %s\\n\" \"No version is set for command\" \"$shim_name\"\n      printf \"%s %s\\n\" \"Consider adding one of the following versions in your config file at\" \"$closest_tool_version\"\n    fi\n    shim_plugin_versions \"${shim_name}\"\n  ) >&2\n\n  return 126\n}\n\nsubstitute() {\n  # Use Bash substitution rather than sed as it will handle escaping of all\n  # strings for us.\n  local input=$1\n  local find_str=$2\n  local replace=$3\n  printf \"%s\" \"${input//\"$find_str\"/\"$replace\"}\"\n}\n\nremove_path_from_path() {\n  # A helper function for removing an arbitrary path from the PATH variable.\n  # Output is a new string suitable for assignment to PATH\n  local PATH=$1\n  local path=$2\n  substitute \"$PATH\" \"$path\" \"\" | sed -e \"s|::|:|g\"\n}\n\n# @description Strings that began with a ~ are always paths. In\n# that case, then ensure ~ it handled like a shell\nutil_resolve_user_path() {\n  util_resolve_user_path_reply=\n  local path=\"$1\"\n\n  # shellcheck disable=SC2088\n  if [ \"${path::2}\" = '~/' ]; then\n    util_resolve_user_path_reply=\"${HOME}/${path:2}\"\n  else\n    util_resolve_user_path_reply=\"$path\"\n  fi\n}\n\n# @description Check if a file contains carriage returns. If it does, print a warning.\nutil_validate_no_carriage_returns() {\n  local file_path=\"$1\"\n\n  if grep -qr $'\\r' \"$file_path\"; then\n    printf '%s\\n' \"asdf: Warning: File $file_path contains carriage returns. Please remove them.\" >&2\n  fi\n}\n\nget_plugin_remote_url() {\n  local plugin_name=\"$1\"\n  local plugin_path\n  plugin_path=\"$(get_plugin_path \"$plugin_name\")\"\n  git --git-dir \"$plugin_path/.git\" remote get-url origin 2>/dev/null\n}\n\nget_plugin_remote_branch() {\n  local plugin_name=\"$1\"\n  local plugin_path\n  plugin_path=\"$(get_plugin_path \"$plugin_name\")\"\n  git --git-dir \"$plugin_path/.git\" rev-parse --abbrev-ref HEAD 2>/dev/null\n}\n\nget_plugin_remote_gitref() {\n  local plugin_name=\"$1\"\n  local plugin_path\n  plugin_path=\"$(get_plugin_path \"$plugin_name\")\"\n  git --git-dir \"$plugin_path/.git\" rev-parse --short HEAD 2>/dev/null\n}\n"
  },
  {
    "path": "release-please-config.json",
    "content": "{\n  \"$schema\": \"https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json\",\n  \"packages\": {\n    \".\": {\n      \"release-type\": \"go\",\n      \"bump-minor-pre-major\": true,\n      \"changelog-types\": [\n        {\n          \"type\": \"feat\",\n          \"section\": \"Features\",\n          \"hidden\": false\n        },\n        {\n          \"type\": \"fix\",\n          \"section\": \"Patches\",\n          \"hidden\": false\n        },\n        {\n          \"type\": \"docs\",\n          \"section\": \"Documentation\",\n          \"hidden\": false\n        }\n      ],\n      \"extra-files\": [\n        \"SECURITY.md\",\n        \"docs/guide/getting-started.md\",\n        \"docs/pt-br/guide/getting-started.md\",\n        \"docs/zh-hans/guide/getting-started.md\",\n        \"docs/ko-kr/guide/getting-started.md\",\n        \"docs/ja-jp/guide/getting-started.md\",\n        \"cmd/asdf/main.go\"\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "scripts/checkstyle.py",
    "content": "#!/usr/bin/env python3\nimport re\nimport os\nimport argparse\nfrom pathlib import Path\nfrom typing import Callable, List, Dict, Any # compat\n\n# This file checks Bash and Shell scripts for violations not found with\n# shellcheck or existing methods. You can use it in several ways:\n#\n# Lint all .bash, .sh, .bats files along with 'bin/asdf' and print out violations:\n# $ ./scripts/checkstyle.py\n#\n# The former, but also fix all violations. This must be ran until there\n# are zero violations since any line can have more than one violation:\n# $ ./scripts/checkstyle.py --fix\n#\n# Lint a particular file:\n# $ ./scripts/checkstyle.py ./lib/functions/installs.bash\n#\n# Check to ensure all regular expressions are working as intended:\n# $ ./scripts/checkstyle.py --internal-test-regex\n\nRule = Dict[str, Any]\n\nclass c:\n    RED = '\\033[91m'\n    GREEN = '\\033[92m'\n    YELLOW = '\\033[93m'\n    BLUE = '\\033[94m'\n    MAGENTA = '\\033[95m'\n    CYAN = '\\033[96m'\n    RESET = '\\033[0m'\n    BOLD = '\\033[1m'\n    UNDERLINE = '\\033[4m'\n    LINK: Callable[[str, str], str] = lambda href, text: f'\\033]8;;{href}\\a{text}\\033]8;;\\a'\n\ndef utilGetStrs(line: Any, m: Any):\n    return (\n        line[0:m.start('match')],\n        line[m.start('match'):m.end('match')],\n        line[m.end('match'):]\n    )\n\n# Before: printf '%s\\\\n' '^w^'\n# After: printf '%s\\n' '^w^'\ndef noDoubleBackslashFixer(line: str, m: Any) -> str:\n    prestr, midstr, poststr = utilGetStrs(line, m)\n\n    return f'{prestr}{midstr[1:]}{poststr}'\n\n# Before: $(pwd)\n# After: $PWD\ndef noPwdCaptureFixer(line: str, m: Any) -> str:\n    prestr, _, poststr = utilGetStrs(line, m)\n\n    return f'{prestr}$PWD{poststr}'\n\n# Before: [ a == b ]\n# After: [ a = b ]\ndef noTestDoubleEqualsFixer(line: str, m: Any) -> str:\n    prestr, _, poststr = utilGetStrs(line, m)\n\n    return f'{prestr}={poststr}'\n\n# Before: function fn() { ...\n# After: fn() { ...\n# ---\n# Before: function fn { ...\n# After fn() { ...\ndef noFunctionKeywordFixer(line: str, m: Any) -> str:\n    prestr, midstr, poststr = utilGetStrs(line, m)\n\n    midstr = midstr.strip()\n    midstr = midstr[len('function'):]\n    midstr = midstr.strip()\n\n    parenIdx = midstr.find('(')\n    if parenIdx != -1: midstr = midstr[:parenIdx]\n\n    return f'{prestr}{midstr}() {poststr}'\n\n# Before: >/dev/null 2>&1\n# After: &>/dev/null\n# ---\n# Before: 2>/dev/null 1>&2\n# After: &>/dev/null\ndef noVerboseRedirectionFixer(line: str, m: Any) -> str:\n    prestr, _, poststr = utilGetStrs(line, m)\n\n    return f'{prestr}&>/dev/null{poststr}'\n\ndef lintfile(file: Path, rules: List[Rule], options: Dict[str, Any]):\n    content_arr = file.read_text().split('\\n')\n\n    for line_i, line in enumerate(content_arr):\n        if 'checkstyle-ignore' in line:\n            continue\n\n        for rule in rules:\n            should_run = False\n            if 'sh' in rule['fileTypes']:\n                if file.name.endswith('.sh') or str(file.absolute()).endswith('bin/asdf'):\n                    should_run = True\n            if 'bash' in rule['fileTypes']:\n                if file.name.endswith('.bash') or file.name.endswith('.bats'):\n                    should_run = True\n\n            if options['verbose']:\n                print(f'{str(file)}: {should_run}')\n\n            if not should_run:\n                continue\n\n            m = re.search(rule['regex'], line)\n            if m is not None and m.group('match') is not None:\n                dir = os.path.relpath(file.resolve(), Path.cwd())\n                prestr = line[0:m.start('match')]\n                midstr = line[m.start('match'):m.end('match')]\n                poststr = line[m.end('match'):]\n\n                print(f'{c.CYAN}{dir}{c.RESET}:{line_i + 1}')\n                print(f'{c.MAGENTA}{rule[\"name\"]}{c.RESET}: {rule[\"reason\"]}')\n                print(f'{prestr}{c.RED}{midstr}{c.RESET}{poststr}')\n                print()\n\n                if options['fix']:\n                    content_arr[line_i] = rule['fixerFn'](line, m)\n\n                rule['found'] += 1\n\n    if options['fix']:\n        file.write_text('\\n'.join(content_arr))\n\ndef main():\n    rules: List[Rule] = [\n        {\n            'name': 'no-double-backslash',\n            'regex': '\".*?(?P<match>\\\\\\\\\\\\\\\\[abeEfnrtv\\'\"?xuUc]).*?(?<!\\\\\\\\)\"',\n            'reason': 'Backslashes are only required if followed by a $, `, \", \\\\, or <newline>',\n            'fileTypes': ['bash', 'sh'],\n            'fixerFn': noDoubleBackslashFixer,\n            'testPositiveMatches': [\n                'printf \"%s\\\\\\\\n\" \"Hai\"',\n                'echo -n \"Hello\\\\\\\\n\"'\n            ],\n            'testNegativeMatches': [\n                'printf \"%s\\\\n\" \"Hai\"',\n                'echo -n \"Hello\\\\n\"'\n            ],\n        },\n        {\n            'name': 'no-pwd-capture',\n            'regex': '(?P<match>\\\\$\\\\(pwd\\\\))',\n            'reason': '$PWD is essentially equivalent to $(pwd) without the overhead of a subshell',\n            'fileTypes': ['bash', 'sh'],\n            'fixerFn': noPwdCaptureFixer,\n            'testPositiveMatches': [\n                '$(pwd)'\n            ],\n            'testNegativeMatches': [\n                '$PWD'\n            ],\n        },\n        {\n            'name': 'no-test-double-equals',\n            'regex': '(?<!\\\\[)\\\\[ (?:[^]]|](?=}))*?(?P<match>==).*?]',\n            'reason': 'Disallow double equals in places where they are not necessary for consistency',\n            'fileTypes': ['bash', 'sh'],\n            'fixerFn': noTestDoubleEqualsFixer,\n            'testPositiveMatches': [\n                '[ a == b ]',\n                '[ \"${lines[0]}\" == blah ]',\n            ],\n            'testNegativeMatches': [\n                '[ a = b ]',\n                '[[ a = b ]]',\n                '[[ a == b ]]',\n                '[ a = b ] || [[ a == b ]]',\n                '[[ a = b ]] || [[ a == b ]]',\n                '[[ \"${lines[0]}\" == \\'usage: \\'* ]]',\n                '[ \"${lines[0]}\" = blah ]',\n            ],\n        },\n        {\n            'name': 'no-function-keyword',\n            'regex': '^[ \\\\t]*(?P<match>function .*?(?:\\\\([ \\\\t]*\\\\))?[ \\\\t]*){',\n            'reason': 'Only allow functions declared like `fn_name() {{ :; }}` for consistency (see ' + c.LINK('https://www.shellcheck.net/wiki/SC2113', 'ShellCheck SC2113') + ')',\n            'fileTypes': ['bash', 'sh'],\n            'fixerFn': noFunctionKeywordFixer,\n            'testPositiveMatches': [\n                'function fn() { :; }',\n                'function fn { :; }',\n            ],\n            'testNegativeMatches': [\n                'fn() { :; }',\n            ],\n        },\n        {\n            'name': 'no-verbose-redirection',\n            'regex': '(?P<match>(>/dev/null 2>&1|2>/dev/null 1>&2))',\n            'reason': 'Use `&>/dev/null` instead of `>/dev/null 2>&1` or `2>/dev/null 1>&2` for consistency',\n            'fileTypes': ['bash'],\n            'fixerFn': noVerboseRedirectionFixer,\n            'testPositiveMatches': [\n                'echo woof >/dev/null 2>&1',\n                'echo woof 2>/dev/null 1>&2',\n            ],\n            'testNegativeMatches': [\n                'echo woof &>/dev/null',\n                'echo woof >&/dev/null',\n            ],\n        },\n    ]\n    [rule.update({ 'found': 0 }) for rule in rules]\n\n    parser = argparse.ArgumentParser()\n    parser.add_argument('files', metavar='FILES', nargs='*')\n    parser.add_argument('--fix', action='store_true')\n    parser.add_argument('--verbose', action='store_true')\n    parser.add_argument('--internal-test-regex', action='store_true')\n    args = parser.parse_args()\n\n    if args.internal_test_regex:\n        for rule in rules:\n            for positiveMatch in rule['testPositiveMatches']:\n                m: Any = re.search(rule['regex'], positiveMatch)\n                if m is None or m.group('match') is None:\n                    print(f'{c.MAGENTA}{rule[\"name\"]}{c.RESET}: Failed {c.CYAN}positive{c.RESET} test:')\n                    print(f'=> {positiveMatch}')\n                    print()\n\n            for negativeMatch in rule['testNegativeMatches']:\n                m: Any = re.search(rule['regex'], negativeMatch)\n                if m is not None and m.group('match') is not None:\n                    print(f'{c.MAGENTA}{rule[\"name\"]}{c.RESET}: Failed {c.YELLOW}negative{c.RESET} test:')\n                    print(f'=> {negativeMatch}')\n                    print()\n        print('Done.')\n        return\n\n    options = {\n        'fix': args.fix,\n        'verbose': args.verbose,\n    }\n\n    # parse files and print matched lints\n    if len(args.files) > 0:\n        for file in args.files:\n            p = Path(file)\n            if p.is_file():\n                lintfile(p, rules, options)\n    else:\n        for file in Path.cwd().glob('**/*'):\n            if '.git' in str(file.absolute()):\n                continue\n\n            if file.is_file():\n                lintfile(file, rules, options)\n\n    # print final results\n    print(f'{c.UNDERLINE}TOTAL ISSUES{c.RESET}')\n    for rule in rules:\n        print(f'{c.MAGENTA}{rule[\"name\"]}{c.RESET}: {rule[\"found\"]}')\n\n    grand_total = sum([rule['found'] for rule in rules])\n    print(f'GRAND TOTAL: {grand_total}')\n\n    # exit\n    if grand_total == 0:\n        exit(0)\n    else:\n        exit(2)\n\nmain()\n"
  },
  {
    "path": "scripts/install_dependencies.bash",
    "content": "#!/usr/bin/env bash\n\nset -euo pipefail\nIFS=$'\\n\\t'\n\n### Used env vars set by default in GitHub Actions\n# docs: https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables\n# GITHUB_ACTIONS\n# RUNNER_OS\n\nif [ -z \"$GITHUB_ACTIONS\" ]; then\n  printf \"%s\\n\" \"GITHUB_ACTIONS is not set. This script is only intended to be run in GitHub Actions. Exiting.\"\n  exit 1\nfi\n\nif [ -z \"$RUNNER_OS\" ]; then\n  printf \"%s\\n\" \"RUNNER_OS is not set. This script is only intended to be run in GitHub Actions. Exiting.\"\n  exit 1\nfi\n\n### Set variables for tracking versions\n# Elvish\nelvish_semver=\"v0.19.2\"\n# Nushell\nnushell_semver=\"0.86.0\"\n# Powershell\npowershell_semver=\"7.4.6\"\npowershell_apt_semver=\"${powershell_semver}-1.deb\"\n\n### Install dependencies on Linux\nif [ \"$RUNNER_OS\" = \"Linux\" ]; then\n  printf \"%s\\n\" \"Installing dependencies on Linux\"\n\n  curl -fsSLo- https://packages.microsoft.com/keys/microsoft.asc | sudo tee /etc/apt/trusted.gpg.d/microsoft.asc >/dev/null\n  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'\n  sudo add-apt-repository -y ppa:fish-shell/release-3\n  sudo apt-get update\n  sudo apt-get --allow-downgrades -y install curl parallel \\\n    fish powershell=\"${powershell_apt_semver}\"\n\n  # Create $HOME/bin\n  mkdir -p \"$HOME/bin\"\n\n  # Download elvish binary and add to path\n  curl https://dl.elv.sh/linux-amd64/elvish-${elvish_semver}.tar.gz -o elvish-${elvish_semver}.tar.gz\n  tar xzf elvish-${elvish_semver}.tar.gz\n  rm elvish-${elvish_semver}.tar.gz\n  mv elvish-${elvish_semver} \"$HOME/bin/elvish\"\n\n  # Download nushell binary and add to path\n  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\n  tar xzf nu-${nushell_semver}-x86_64-unknown-linux-gnu.tar.gz\n  rm nu-${nushell_semver}-x86_64-unknown-linux-gnu.tar.gz\n  mv nu-${nushell_semver}-x86_64-unknown-linux-gnu/* \"$HOME/bin\"\n\n  # Add $HOME/bin to path (add Elvish & Nushell to path)\n  echo \"$HOME/bin\" >>\"$GITHUB_PATH\"\nfi\n\n### Install dependencies on macOS\nif [ \"$RUNNER_OS\" = \"macOS\" ]; then\n  printf \"%s\\n\" \"Installing dependencies on macOS\"\n  brew install coreutils parallel \\\n    elvish \\\n    fish \\\n    nushell \\\n    powershell\nfi\n\n### Install bats-core\nprintf \"%s\\n\" \"Installing bats-core\"\nbats_version=$(grep -Eo \"^\\\\s*bats\\\\s*.*$\" \".tool-versions\" | cut -d ' ' -f2-)\ngit clone --depth 1 --branch \"v$bats_version\" https://github.com/bats-core/bats-core.git \"$HOME/bats-core\"\necho \"$HOME/bats-core/bin\" >>\"$GITHUB_PATH\"\n"
  },
  {
    "path": "scripts/lint.bash",
    "content": "#!/usr/bin/env bash\n\nset -euo pipefail\nIFS=$'\\n\\t'\n\nprint.info() {\n  printf '[INFO] %s\\n' \"$*\"\n}\n\nprint.error() {\n  printf '[ERROR] %s\\n' \"$*\" >&2\n}\n\nusage() {\n  printf \"%s\\n\" \"Lint script for the asdf codebase. Must be executed from the\"\n  printf \"%s\\n\\n\" \"repository root directory.\"\n  printf \"%s\\n\\n\" \"Usage: scripts/lint.bash [options]\"\n  printf \"%s\\n\" \"Options:\"\n  printf \"%s\\n\" \"  -c, --check   Error if any issues are found\"\n  printf \"%s\\n\" \"  -f, --fix     Automatically fix issues if possible\"\n  printf \"%s\\n\" \"  -h, --help    Display this help message\"\n}\n\nrun_shfmt_stylecheck() {\n  local shfmt_flag=\"\"\n  if [ \"$1\" = \"fix\" ]; then\n    shfmt_flag=\"--write\"\n  else\n    shfmt_flag=\"--diff\"\n  fi\n\n  print.info \"Checking .bash with shfmt\"\n  shfmt --language-dialect bash --indent 2 \"${shfmt_flag}\" \\\n    internal/completions/*.bash \\\n    bin/asdf \\\n    bin/private/asdf-exec \\\n    lib/utils.bash \\\n    lib/commands/*.bash \\\n    lib/functions/*.bash \\\n    scripts/*.bash \\\n    test/test_helpers.bash \\\n    test/fixtures/dummy_broken_plugin/bin/* \\\n    test/fixtures/dummy_legacy_plugin/bin/* \\\n    test/fixtures/dummy_plugin/bin/*\n\n  print.info \"Checking .bats with shfmt\"\n  shfmt --language-dialect bats --indent 2 \"${shfmt_flag}\" \\\n    test/*.bats\n}\n\nrun_shellcheck_linter() {\n  print.info \"Checking .sh files with Shellcheck\"\n  shellcheck --shell sh --external-sources \\\n    asdf.sh\n\n  print.info \"Checking .bash files with Shellcheck\"\n  shellcheck --shell bash --external-sources \\\n    internal/completions/*.bash \\\n    bin/asdf \\\n    bin/private/asdf-exec \\\n    lib/utils.bash \\\n    lib/commands/*.bash \\\n    lib/functions/*.bash \\\n    scripts/*.bash \\\n    test/test_helpers.bash \\\n    test/fixtures/dummy_broken_plugin/bin/* \\\n    test/fixtures/dummy_legacy_plugin/bin/* \\\n    test/fixtures/dummy_plugin/bin/*\n\n  print.info \"Checking .bats files with Shellcheck\"\n  shellcheck --shell bats --external-source \\\n    test/*.bats\n}\n\nrun_custom_python_stylecheck() {\n  local github_actions=${GITHUB_ACTIONS:-}\n  local flag=\n\n  if [ \"$1\" = \"fix\" ]; then\n    flag=\"--fix\"\n  fi\n\n  if [ -n \"$github_actions\" ] && ! command -v python3 &>/dev/null; then\n    # fail if CI and no python3\n    print.error \"Detected execution in GitHub Actions but python3 was not found. This is required during CI linting.\"\n    exit 1\n  fi\n\n  if ! command -v python3 &>/dev/null; then\n    # skip if local and no python3\n    printf \"%s\\n\" \"[WARNING] python3 not found. Skipping Custom Python Script.\"\n  else\n    print.info \"Checking files with Custom Python Script.\"\n    \"${0%/*}/checkstyle.py\" \"${flag}\"\n  fi\n\n}\n\n# TODO: there is no elvish linter/formatter yet\n#       see https://github.com/elves/elvish/issues/1651\n#run_elvish_linter() {\n#  printf \"%s\\n\" \"[WARNING] elvish linter/formatter not found, skipping for now.\"\n#}\n\nrun_fish_linter() {\n  local github_actions=${GITHUB_ACTIONS:-}\n  local flag=\n\n  if [ \"$1\" = \"fix\" ]; then\n    flag=\"--write\"\n  else\n    flag=\"--check\"\n  fi\n\n  if [ -n \"$github_actions\" ] && ! command -v fish_indent &>/dev/null; then\n    # fail if CI and no fish_ident\n    print.error \"Detected execution in GitHub Actions but fish_indent was not found. This is required during CI linting.\"\n    exit 1\n  fi\n\n  if ! command -v fish_indent &>/dev/null; then\n    # skip if local and no fish_ident\n    printf \"%s\\n\" \"[WARNING] fish_indent not found. Skipping .fish files.\"\n  else\n    print.info \"Checking .fish files with fish_indent\"\n    fish_indent \"${flag}\" ./internal/completions/asdf.fish\n  fi\n}\n\n# TODO: there is no nushell linter/formatter yet\n#run_nushell_linter() {\n#  printf \"%s\\n\" \"[WARNING] nushell linter/formatter not found, skipping for now.\"\n#}\n\n# TODO: select powershell linter/formatter & setup installation in CI\n#run_powershell_linter() {\n#  printf \"%s\\n\" \"[WARNING] powershell linter/formatter not found, skipping for now.\"\n#}\n\n{\n  repo_dir=$(git rev-parse --show-toplevel)\n  current_dir=$(pwd -P)\n  if [ \"$repo_dir\" != \"$current_dir\" ]; then\n    print.error \"This scripts requires execution from the repository root directory.\"\n    printf \"\\t%s\\t%s\\n\" \"Repo root dir:\" \"$repo_dir\"\n    printf \"\\t%s\\t%s\\n\\n\" \"Current dir:\" \"$current_dir\"\n    exit 1\n  fi\n}\n\nif [ $# -eq 0 ]; then\n  print.error \"At least one option required.\"\n  printf \"=%.0s\" {1..60}\n  printf \"\\n\"\n  usage\n  exit 1\nfi\n\nmode=\ncase \"$1\" in\n-h | --help)\n  usage\n  exit 0\n  ;;\n-c | --check)\n  mode=\"check\"\n  ;;\n-f | --fix)\n  mode=\"fix\"\n  ;;\n*)\n  print.error \"Invalid flag: $1\"\n  printf \"=%.0s\" {1..60}\n  printf \"\\n\"\n  usage\n  exit 1\n  ;;\nesac\n\nprintf \"%s\\\"%s\\\"\\n\" \"[INFO] Executing with mode: \" \"$mode\"\n\nrun_shfmt_stylecheck \"$mode\"\nrun_custom_python_stylecheck \"$mode\"\nrun_shellcheck_linter \"$mode\"\nrun_fish_linter \"$mode\"\n#run_elvish_linter \"$mode\"\n#run_nushell_linter \"$mode\"\n#run_powershell_linter \"$mode\"\n\nprint.info \"Success!\"\n"
  },
  {
    "path": "scripts/test.bash",
    "content": "#!/usr/bin/env bash\n\nset -euo pipefail\nIFS=$'\\n\\t'\n\nprint.info() {\n  printf '[INFO] %s\\n' \"$1\"\n}\n\nprint.error() {\n  printf '[ERROR] %s\\n' \"$1\" >&2\n}\n\n{\n  repo_dir=$(git rev-parse --show-toplevel)\n  current_dir=$(pwd -P)\n  if [ \"$repo_dir\" != \"$current_dir\" ]; then\n    print.error \"This scripts requires execution from the repository root directory.\"\n    printf \"\\t%s\\t%s\\n\" \"Repo root dir:\" \"$repo_dir\"\n    printf \"\\t%s\\t%s\\n\\n\" \"Current dir:\" \"$current_dir\"\n    exit 1\n  fi\n}\n\ntest_directory=\"./test\"\nbats_options=(--timing --print-output-on-failure)\n\nif command -v parallel >/dev/null; then\n  # Enable parallel jobs\n  bats_options+=(--jobs 2 --no-parallelize-within-files)\nelif [[ -n \"${CI-}\" ]]; then\n  print.error \"GNU parallel should be installed in the CI environment. Please install and rerun the test suite.\"\n  exit 1\nelse\n  print.info \"For faster test execution, install GNU parallel.\"\nfi\n\nprint.info \"Running Bats in directory '${test_directory}' with options:\" \"${bats_options[@]}\"\nbats \"${bats_options[@]}\" \"${test_directory}\"\n"
  },
  {
    "path": "staticcheck.conf",
    "content": "checks = [\"all\", \"-ST1005\"]\n"
  },
  {
    "path": "test/asdf_elvish.bats",
    "content": "#!/usr/bin/env bats\n# shellcheck disable=SC2030,SC2031\n\nload test_helpers\n\nsetup() {\n  export XDG_CONFIG_HOME=\n  export XDG_DATA_HOME=\n  export XDG_DATA_DIRS=\n\n  if ! command -v elvish &>/dev/null && [ -z \"$GITHUB_ACTIONS\" ]; then\n    skip 'Elvish not installed'\n  fi\n\n  local ver_major=\n  local ver_minor=\n  local ver_patch=\n  IFS='.' read -r ver_major ver_minor ver_patch <<<\"$(elvish -version)\"\n\n  if ((ver_major == 0 && ver_minor < 18)) && [ -z \"$GITHUB_ACTIONS\" ]; then\n    skip \"Elvish version is not at least 0.18. Found ${ver_major}.${ver_minor}.${ver_patch}\"\n  fi\n}\n\ncleaned_path() {\n  echo \"$PATH\" | tr ':' '\\n' | grep -v \"asdf\" | tr '\\n' ' '\n}\n\n@test \"exports ASDF_DIR\" {\n  run elvish -norc -c \"\n    unset-env ASDF_DIR\n    set paths = [$(cleaned_path)]\n    use ./asdf _asdf; var asdf~ = \\$_asdf:asdf~\n    echo \\$E:ASDF_DIR\"\n\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"$HOME/.asdf\" ]\n}\n\n@test \"retains ASDF_DIR\" {\n  run elvish -norc -c \"\n    set-env ASDF_DIR \\\"/path/to/asdf\\\"\n    set paths = [$(cleaned_path)]\n    use ./asdf _asdf; var asdf~ = \\$_asdf:asdf~\n    echo \\$E:ASDF_DIR\"\n\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"/path/to/asdf\" ]\n}\n\n@test \"retains ASDF_DATA_DIR\" {\n  run elvish -norc -c \"\n    set-env ASDF_DATA_DIR \\\"/path/to/asdf-data\\\"\n    set paths = [$(cleaned_path)]\n    use ./asdf _asdf; var asdf~ = \\$_asdf:asdf~\n    echo \\$E:ASDF_DATA_DIR\"\n\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"/path/to/asdf-data\" ]\n}\n\n@test \"adds asdf dirs to PATH\" {\n  run elvish -norc -c \"\n    unset-env ASDF_DIR\n    set paths = [$(cleaned_path)]\n    use ./asdf _asdf; var asdf~ = \\$_asdf:asdf~\n    echo \\$E:PATH\"\n\n  [ \"$status\" -eq 0 ]\n\n  result=$(echo \"$output\" | grep \"asdf\")\n  [ \"$result\" != \"\" ]\n}\n\n@test \"defines the _asdf namespace\" {\n  run elvish -norc -c \"\n    unset-env ASDF_DIR\n    set paths = [$(cleaned_path)]\n    use ./asdf _asdf; var asdf~ = \\$_asdf:asdf~\n    pprint \\$_asdf:\"\n\n  [ \"$status\" -eq 0 ]\n  [[ \"$output\" =~ \"<ns \" ]]\n}\n\n@test \"does not add paths to PATH more than once\" {\n  run elvish -norc -c \"\n    unset-env ASDF_DIR\n    set paths = [$(cleaned_path)]\n\n    use ./asdf _asdf; var asdf~ = \\$_asdf:asdf~\n    use ./asdf _asdf; var asdf~ = \\$_asdf:asdf~\n    echo \\$E:PATH\"\n\n  [ \"$status\" -eq 0 ]\n\n  result=$(echo \"$result\" | tr ':' '\\n' | grep \"asdf\" | sort | uniq -d)\n  [ \"$result\" = \"\" ]\n}\n\n@test \"defines the asdf function\" {\n  run elvish -norc -c \"\n    unset-env ASDF_DIR\n    set paths = [$(cleaned_path)]\n    use ./asdf _asdf; var asdf~ = \\$_asdf:asdf~\n    pprint \\$asdf~\"\n\n  [ \"$status\" -eq 0 ]\n  if [ \"$(uname)\" = \"Linux\" ]; then\n    [[ \"$output\" =~ \"<closure \" ]]\n  else\n    [[ \"$output\" =~ ^'[^fn ' ]]\n  fi\n}\n\n@test \"function calls asdf command\" {\n  run elvish -norc -c \"\n    set-env ASDF_DIR $(pwd) # checkstyle-ignore\n    set paths = [$(cleaned_path)]\n    use ./asdf _asdf; var asdf~ = \\$_asdf:asdf~\n    asdf info\"\n\n  [ \"$status\" -eq 0 ]\n\n  result=$(echo \"$output\" | grep \"ASDF INSTALLED PLUGINS:\")\n  [ \"$result\" != \"\" ]\n}\n"
  },
  {
    "path": "test/asdf_fish.bats",
    "content": "#!/usr/bin/env bats\n# shellcheck disable=SC2164\n\nload test_helpers\n\nsetup() {\n  cd \"$(dirname \"$BATS_TEST_DIRNAME\")\"\n\n  if ! command -v fish &>/dev/null && [ -z \"$GITHUB_ACTIONS\" ]; then\n    skip \"Fish is not installed\"\n  fi\n}\n\ncleaned_path() {\n  echo \"$PATH\" | tr ':' '\\n' | grep -v \"asdf\" | tr '\\n' ' '\n}\n\n@test \"exports ASDF_DIR\" {\n  run fish --no-config -c \"\n    set -e asdf\n    set -e ASDF_DIR\n    set -e ASDF_DATA_DIR\n    set PATH $(cleaned_path)\n\n    . asdf.fish\n    echo \\$ASDF_DIR\"\n\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" != \"\" ]\n}\n\n@test \"adds asdf dirs to PATH\" {\n  run fish --no-config -c \"\n    set -e asdf\n    set -e ASDF_DIR\n    set -e ASDF_DATA_DIR\n    set PATH $(cleaned_path)\n\n    . (pwd)/asdf.fish  # if the full path is not passed, status -f will return the relative path\n    echo \\$PATH\"\n\n  [ \"$status\" -eq 0 ]\n\n  result=$(echo \"$output\" | grep \"asdf\")\n  [ \"$result\" != \"\" ]\n}\n\n@test \"does not add paths to PATH more than once\" {\n  run fish --no-config -c \"\n    set -e asdf\n    set -e ASDF_DIR\n    set -e ASDF_DATA_DIR\n    set PATH $(cleaned_path)\n\n    . asdf.fish\n    . asdf.fish\n    echo \\$PATH\"\n\n  [ \"$status\" -eq 0 ]\n\n  result=$(echo \"$output\" | tr ' ' '\\n' | grep \"asdf\" | sort | uniq -d)\n  [ \"$result\" = \"\" ]\n}\n\n@test \"defines the asdf function\" {\n  run fish --no-config -c \"\n    set -e asdf\n    set -e ASDF_DIR\n    set PATH $(cleaned_path)\n\n    . asdf.fish\n    type asdf\"\n\n  [ \"$status\" -eq 0 ]\n  [[ \"$output\" =~ \"is a function\" ]]\n}\n\n@test \"function calls asdf command\" {\n  run fish --no-config -c \"\n    set -e asdf\n    set -x ASDF_DIR $(pwd) # checkstyle-ignore\n    set PATH $(cleaned_path)\n\n    . asdf.fish\n    asdf info\"\n\n  [ \"$status\" -eq 0 ]\n\n  result=$(echo \"$output\" | grep \"ASDF INSTALLED PLUGINS:\")\n  [ \"$result\" != \"\" ]\n}\n"
  },
  {
    "path": "test/asdf_nu.bats",
    "content": "#!/usr/bin/env bats\n# shellcheck disable=SC2164\n\nload test_helpers\n\nsetup() {\n  cd \"$(dirname \"$BATS_TEST_DIRNAME\")\"\n\n  if ! command -v nu &>/dev/null && [ -z \"$GITHUB_ACTIONS\" ]; then\n    skip \"Nu is not installed\"\n  fi\n\n  setup_asdf_dir\n}\n\nteardown() {\n  clean_asdf_dir\n}\n\ncleaned_path() {\n  echo \"$PATH\" | tr ':' '\\n' | grep -v \"asdf\" | tr '\\n' ':'\n}\n\nrun_nushell() {\n  run nu -c \"\n    hide-env -i asdf\n    hide-env -i ASDF_DIR\n    \\$env.PATH = ( '$(cleaned_path)' | split row ':' )\n    \\$env.ASDF_DIR = '$PWD'\n\n    source asdf.nu\n    $1\"\n}\n\n@test \"exports ASDF_DIR\" {\n  run_nushell \"echo \\$env.ASDF_DIR\"\n\n  [ \"$status\" -eq 0 ]\n  result=$(echo \"$output\" | grep \"asdf\")\n  [ \"$result\" = \"$PWD\" ]\n}\n\n@test \"adds asdf dirs to PATH\" {\n  run_nushell \"\\$env.PATH | to text\"\n\n  [ \"$status\" -eq 0 ]\n\n  [[ \"$output\" == *\"$PWD/bin\"* ]]\n  [[ \"$output\" == *\"$HOME/.asdf/shims\"* ]]\n}\n\n@test \"does not add paths to PATH more than once\" {\n  run_nushell \"\n    source asdf.nu\n    echo \\$env.PATH\"\n\n  [ \"$status\" -eq 0 ]\n\n  result=$(echo \"$output\" | tr ' ' '\\n' | grep \"asdf\" | sort | uniq -d)\n  [ \"$result\" = \"\" ]\n}\n\n@test \"retains ASDF_DIR (from ASDF_NU_DIR)\" {\n  run nu -c \"\n    hide-env -i asdf\n    \\$env.ASDF_DIR = ( pwd )\n    \\$env.PATH = ( '$(cleaned_path)' | split row ':' )\n    \\$env.ASDF_NU_DIR = '$PWD'\n\n    source asdf.nu\n\n    echo \\$env.ASDF_DIR\"\n\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"$PWD\" ]\n}\n\n@test \"retains ASDF_DIR (from ASDF_DIR)\" {\n  run nu -c \"\n    hide-env -i asdf\n    \\$env.ASDF_DIR = ( pwd )\n    \\$env.PATH = ( '$(cleaned_path)' | split row ':' )\n    \\$env.ASDF_DIR = '$PWD'\n\n    source asdf.nu\n\n    echo \\$env.ASDF_DIR\"\n\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"$PWD\" ]\n}\n\n@test \"defines the asdf or main function\" {\n  run_nushell \"which asdf | get path | to text\"\n\n  [ \"$status\" -eq 0 ]\n}\n\n@test \"function calls asdf command\" {\n  run_nushell \"asdf info\"\n\n  [ \"$status\" -eq 0 ]\n\n  result=$(echo \"$output\" | grep \"ASDF INSTALLED PLUGINS:\")\n  [ \"$result\" != \"\" ]\n}\n\n@test \"parses the output of asdf plugin list\" {\n  setup_repo\n  install_dummy_plugin\n  run_nushell \"asdf plugin list | to csv -n\"\n\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"dummy\" ]\n}\n\n# TODO: Fix as soon as possible https://github.com/asdf-vm/asdf/issues/1808\n#@test \"parses the output of asdf plugin list --urls\" {\n#  setup_repo\n#  install_mock_plugin_repo \"dummy\"\n#  asdf plugin add \"dummy\" \"${BASE_DIR}/repo-dummy\"\n\n#  run_nushell \"asdf plugin list --urls | to csv -n\"\n\n#  [ \"$status\" -eq 0 ]\n\n#  local repo_url\n#  repo_url=$(get_plugin_remote_url \"dummy\")\n\n#  [ \"$output\" = \"dummy,$repo_url\" ]\n#}\n\n#@test \"parses the output of asdf plugin list --refs\" {\n#  setup_repo\n#  install_mock_plugin_repo \"dummy\"\n#  asdf plugin add \"dummy\" \"${BASE_DIR}/repo-dummy\"\n\n#  run_nushell \"asdf plugin list --refs | to csv -n\"\n\n#  [ \"$status\" -eq 0 ]\n\n#  local branch gitref\n#  branch=$(get_plugin_remote_branch \"dummy\")\n#  gitref=$(get_plugin_remote_gitref \"dummy\")\n\n#  [ \"$output\" = \"dummy,$branch,$gitref\" ]\n#}\n\n#@test \"parses the output of asdf plugin list --urls --refs\" {\n#  setup_repo\n#  install_mock_plugin_repo \"dummy\"\n#  asdf plugin add \"dummy\" \"${BASE_DIR}/repo-dummy\"\n\n#  run_nushell \"asdf plugin list --urls --refs | to csv -n\"\n\n#  [ \"$status\" -eq 0 ]\n\n#  local repo_url branch gitref\n#  repo_url=$(get_plugin_remote_url \"dummy\")\n#  branch=$(get_plugin_remote_branch \"dummy\")\n#  gitref=$(get_plugin_remote_gitref \"dummy\")\n\n#  [ \"$output\" = \"dummy,$repo_url,$branch,$gitref\" ]\n#}\n\n@test \"parses the output of asdf plugin list all\" {\n  setup_repo\n  install_dummy_plugin\n  run_nushell \"asdf plugin list all | to csv -n\"\n\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"\\\nbar,false,http://example.com/bar\ndummy,true,http://example.com/dummy\nfoo,false,http://example.com/foo\" ]\n}\n"
  },
  {
    "path": "test/asdf_pwsh.bats",
    "content": "#!/usr/bin/env bats\n# shellcheck disable=SC2164\n\nload test_helpers\n\nsetup() {\n  cd \"$(dirname \"$BATS_TEST_DIRNAME\")\"\n\n  if ! command -v pwsh &>/dev/null && [ -z \"$GITHUB_ACTIONS\" ]; then\n    skip \"Powershell Core is not installed\"\n  fi\n}\n\ncleaned_path() {\n  echo \"$PATH\" | tr ':' '\\n' | grep -v \"asdf\" | tr '\\n' ':'\n}\n\n@test \"exports ASDF_DIR\" {\n  run pwsh -Command \"\n    function asdf() {} # checkstyle-ignore\n    Remove-item Function:asdf\n    \\$Env:ASDF_DIR = ''\n    \\$Env:ASDF_DATA_DIR = ''\n    \\$Env:PATH = \\\"$(cleaned_path)\\\"\n\n    . ./asdf.ps1\n    Write-Output \\\"\\$env:ASDF_DIR\\\"\"\n\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" != \"\" ]\n}\n\n@test \"adds asdf dirs to PATH\" {\n  run pwsh -Command \"\n    function asdf() {} # checkstyle-ignore\n    Remove-item Function:asdf\n    \\$Env:ASDF_DIR = ''\n    \\$Env:ASDF_DATA_DIR = ''\n    \\$Env:PATH = \\\"$(cleaned_path)\\\"\n\n    . ./asdf.ps1\n    Write-Output \\$Env:PATH\"\n\n  [ \"$status\" -eq 0 ]\n  result=$(echo \"$output\" | grep \"asdf\")\n  [ \"$result\" != \"\" ]\n}\n\n@test \"does not add paths to PATH more than once\" {\n  run pwsh -Command \"\n    function asdf() {} # checkstyle-ignore\n    Remove-item Function:asdf\n    \\$Env:ASDF_DIR = ''\n    \\$Env:ASDF_DATA_DIR = ''\n    \\$Env:PATH = \\\"$(cleaned_path)\\\"\n\n    . ./asdf.ps1\n    . ./asdf.ps1\n    Write-Output \\$Env:PATH\"\n\n  [ \"$status\" -eq 0 ]\n\n  result=$(echo \"$output\" | tr ' ' '\\n' | grep \"asdf\" | sort | uniq -d)\n  [ \"$result\" = \"\" ]\n}\n\n@test \"defines the asdf function\" {\n  run pwsh -Command \"\n    function asdf() {} # checkstyle-ignore\n    Remove-item Function:asdf\n    \\$Env:ASDF_DIR = ''\n    \\$Env:ASDF_DATA_DIR = ''\n    \\$Env:PATH = \\\"$(cleaned_path)\\\"\n\n    ./ asdf.ps1\n    \\$(Get-Command -CommandType asdf).Name\"\n\n  [ \"$status\" -eq 0 ]\n  [[ \"$output\" =~ \"asdf\" ]]\n}\n\n@test \"function calls asdf command\" {\n  run pwsh -Command \"\n    function asdf() {} # checkstyle-ignore\n    Remove-item Function:asdf\n    \\$Env:ASDF_DIR = ''\n    \\$Env:ASDF_DATA_DIR = ''\n    \\$Env:PATH = \\\"$(cleaned_path)\\\"\n\n    . ./asdf.ps1\n    asdf info\"\n\n  [ \"$status\" -eq 0 ]\n  result=$(echo \"$output\" | grep \"ASDF INSTALLED PLUGINS:\")\n  [ \"$result\" != \"\" ]\n}\n"
  },
  {
    "path": "test/asdf_sh.bats",
    "content": "#!/usr/bin/env bats\n\nload test_helpers\n\n# Helper function to handle sourcing of asdf.sh\nsource_asdf_sh() {\n  . \"$(dirname \"$BATS_TEST_DIRNAME\")/asdf.sh\"\n}\n\ncleaned_path() {\n  echo \"$PATH\" | tr ':' '\\n' | grep -v \"asdf\" | tr '\\n' ':'\n}\n\n@test \"exports ASDF_DIR\" {\n  output=$(\n    unset -f asdf\n    unset ASDF_DIR\n    PATH=$(cleaned_path)\n\n    source_asdf_sh\n    echo \"$ASDF_DIR\"\n  )\n\n  result=$(echo \"$output\" | grep \"asdf\")\n  [ \"$result\" != \"\" ]\n}\n\n@test \"does not error if nounset is enabled\" {\n  output=$(\n    unset -f asdf\n    unset ASDF_DIR\n    PATH=$(cleaned_path)\n    set -o nounset\n\n    source_asdf_sh\n    echo \"$ASDF_DIR\"\n  )\n\n  result=$(echo \"$output\" | grep \"asdf\")\n  [ \"$result\" != \"\" ]\n}\n\n@test \"adds asdf dirs to PATH\" {\n  output=$(\n    unset -f asdf\n    unset ASDF_DIR\n    PATH=$(cleaned_path)\n\n    source_asdf_sh\n    echo \"$PATH\"\n  )\n\n  result=$(echo \"$output\" | grep \"asdf\")\n  [ \"$result\" != \"\" ]\n}\n\n@test \"does not add paths to PATH more than once\" {\n  output=$(\n    unset -f asdf\n    unset ASDF_DIR\n    PATH=$(cleaned_path)\n\n    source_asdf_sh\n    source_asdf_sh\n    echo \"$PATH\"\n  )\n\n  result=$(echo \"$output\" | tr ':' '\\n' | grep \"asdf\" | sort | uniq -d)\n  [ \"$result\" = \"\" ]\n}\n\n@test \"defines the asdf function\" {\n  output=$(\n    unset -f asdf\n    unset ASDF_DIR\n    PATH=$(cleaned_path)\n\n    source_asdf_sh\n    type asdf\n  )\n\n  [[ \"$output\" =~ \"is a function\" ]]\n}\n\n@test \"function calls asdf command\" {\n  result=$(\n    unset -f asdf\n    ASDF_DIR=$PWD\n    PATH=$(cleaned_path)\n\n    source_asdf_sh\n    asdf info\n  )\n\n  output=$(echo \"$result\" | grep \"ASDF INSTALLED PLUGINS:\")\n  [ \"$output\" != \"\" ]\n}\n"
  },
  {
    "path": "test/banned_commands.bats",
    "content": "#!/usr/bin/env bats\n\nload test_helpers\n\nbanned_commands=(\n  # Process substitution isn't POSIX compliant and cause trouble\n  \"<(\"\n  # Command isn't included in the Ubuntu packages asdf depends on. Also not\n  # defined in POSIX\n  column\n  # echo isn't consistent across operating systems, and sometimes output can\n  # be confused with echo flags. printf does everything echo does and more.\n  echo\n  # It's best to avoid eval as it makes it easier to accidentally execute\n  # arbitrary strings\n  eval\n  # realpath not available by default on OSX.\n  realpath\n  # source isn't POSIX compliant. . behaves the same and is POSIX compliant\n  # Except in fish, where . is deprecated, and will be removed in the future.\n  source\n  # For consistency, [ should be used instead. There is a leading space so 'fail_test', etc. is not matched\n  ' test'\n)\n\nbanned_commands_regex=(\n  # grep -y does not work on alpine and should be \"grep -i\" either way\n  \"grep.* -y\"\n  # grep -P is not a valid option in OSX.\n  \"grep.* -P\"\n  # Ban grep long commands as they do not work on alpine\n  \"grep[^|]+--\\w{2,}\"\n  # readlink -f on OSX behaves differently from readlink -f on other Unix systems\n  'readlink.+-.*f.+[\"$]'\n  # sort --sort-version isn't supported everywhere\n  \"sort.*-V\"\n  \"sort.*--sort-versions\"\n\n  # ls often gets used when we want to glob for files that match a pattern\n  # or when we want to find all files/directories that match a pattern or are\n  # found in a certain location. Using shell globs is preferred over ls, and\n  # find is better at locating files that are in a certain location or that\n  # match certain filename patterns.\n  # https://github-wiki-see.page/m/koalaman/shellcheck/wiki/SC2012\n  '\\bls '\n\n  # Ban recursive asdf calls as they are inefficient and may introduce bugs.\n  # If you find yourself needing to invoke an `asdf` command from within\n  # asdf code, please source the appropriate file and invoke the\n  # corresponding function.\n  '\\basdf '\n)\n\nsetup() {\n  setup_asdf_dir\n}\n\nteardown() {\n  clean_asdf_dir\n}\n\n@test \"banned commands are not found in source code\" {\n  # Assert command is not used in the lib and bin dirs\n  # or expect an explicit comment at end of line, allowing it.\n  # Also ignore matches that are contained in comments or a string or\n  # followed by an underscore (indicating it's a variable and not a\n  # command).\n  for cmd in \"${banned_commands[@]}\"; do\n    run bash -c \"grep -nHR --include \\*.bash --include \\*.sh '$cmd' asdf.* lib bin\\\n        | grep -v '#.*$cmd'\\\n        | grep -v '\\\".*$cmd.*\\\"' \\\n        | grep -v '${cmd}_'\\\n        | grep -v '# asdf_allow: $cmd'\"\n\n    # Only print output if we've found a banned command\n    #if [ \"$status\" -ne 1 ]; then\n    if [ \"\" != \"$output\" ]; then\n      echo \"banned command $cmd: $output\"\n    fi\n\n    [ \"$status\" -eq 1 ]\n    [ \"\" = \"$output\" ]\n  done\n\n  for cmd in \"${banned_commands_regex[@]}\"; do\n    run bash -c \"grep -nHRE --include \\*.bash --include \\*.sh '$cmd' asdf.* lib bin\\\n        | grep -v '#.*$cmd'\\\n        | grep -v '\\\".*$cmd.*\\\"' \\\n        | grep -v '${cmd}_'\\\n        | grep -v '# asdf_allow: $cmd'\"\n\n    # Only print output if we've found a banned command\n    #if [ \"$status\" -ne 1 ]; then\n    if [ \"\" != \"$output\" ]; then\n      echo \"banned command $cmd: $output\"\n    fi\n\n    [ \"$status\" -eq 1 ]\n    [ \"\" = \"$output\" ]\n  done\n}\n"
  },
  {
    "path": "test/current_command.bats",
    "content": "#!/usr/bin/env bats\n\nload test_helpers\n\nsetup() {\n  setup_asdf_dir\n  install_dummy_plugin\n  install_dummy_version \"1.1.0\"\n  install_dummy_version \"1.2.0\"\n  install_dummy_version \"nightly-2000-01-01\"\n\n  PROJECT_DIR=\"$HOME/project\"\n  mkdir -p \"$PROJECT_DIR\"\n}\n\nteardown() {\n  clean_asdf_dir\n}\n\n@test \"current should derive from the current .tool-versions\" {\n  cd \"$PROJECT_DIR\"\n  echo 'dummy 1.1.0' >>\"$PROJECT_DIR/.tool-versions\"\n  expected=\"Name Version Source Installed\ndummy 1.1.0 $PROJECT_DIR/.tool-versions true\"\n\n  run asdf current \"dummy\"\n\n  # shellcheck disable=SC2001\n  condensed_output=\"$(sed -e 's/ [ ]*/ /g' <<<\"$output\")\"\n\n  [ \"$status\" -eq 0 ]\n  [ \"$condensed_output\" = \"$expected\" ]\n}\n\n@test \"current should handle long version name\" {\n  cd \"$PROJECT_DIR\"\n  echo \"dummy nightly-2000-01-01\" >>\"$PROJECT_DIR/.tool-versions\"\n  expected=\"Name Version Source Installed\ndummy nightly-2000-01-01 $PROJECT_DIR/.tool-versions true\"\n\n  run asdf current \"dummy\"\n\n  # shellcheck disable=SC2001\n  condensed_output=\"$(sed -e 's/ [ ]*/ /g' <<<\"$output\")\"\n\n  [ \"$status\" -eq 0 ]\n  [ \"$condensed_output\" = \"$expected\" ]\n}\n\n@test \"current should handle multiple versions\" {\n  cd \"$PROJECT_DIR\"\n  echo \"dummy 1.2.0 1.1.0\" >>\"$PROJECT_DIR/.tool-versions\"\n  expected=\"Name Version Source Installed\ndummy 1.2.0 1.1.0 $PROJECT_DIR/.tool-versions true\"\n\n  run asdf current \"dummy\"\n\n  # shellcheck disable=SC2001\n  condensed_output=\"$(sed -e 's/ [ ]*/ /g' <<<\"$output\")\"\n\n  [ \"$status\" -eq 0 ]\n  [ \"$condensed_output\" = \"$expected\" ]\n}\n\n@test \"current should derive from the legacy file if enabled\" {\n  cd \"$PROJECT_DIR\"\n  echo 'legacy_version_file = yes' >\"$HOME/.asdfrc\"\n  echo '1.2.0' >>\"$PROJECT_DIR/.dummy-version\"\n  expected=\"Name Version Source Installed\ndummy 1.2.0 $PROJECT_DIR/.dummy-version true\"\n\n  run asdf current \"dummy\"\n\n  # shellcheck disable=SC2001\n  condensed_output=\"$(sed -e 's/ [ ]*/ /g' <<<\"$output\")\"\n\n  [ \"$status\" -eq 0 ]\n  [ \"$condensed_output\" = \"$expected\" ]\n}\n\n# TODO: Need to fix plugin error as well\n@test \"current should error when the plugin doesn't exist\" {\n  expected=\"No such plugin: foobar\"\n\n  run asdf current \"foobar\"\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"$expected\" ]\n}\n\n@test \"current should error when no version is set\" {\n  cd \"$PROJECT_DIR\"\n  expected=\"Name Version Source Installed\ndummy ______ ______ \"\n\n  run asdf current \"dummy\"\n\n  # shellcheck disable=SC2001\n  condensed_output=\"$(sed -e 's/ [ ]*/ /g' <<<\"$output\")\"\n\n  [ \"$status\" -eq 126 ]\n  [ \"$condensed_output\" = \"$expected\" ]\n}\n\n@test \"current should error when a version is set that isn't installed\" {\n  cd \"$PROJECT_DIR\"\n  echo 'dummy 9.9.9' >>\"$PROJECT_DIR/.tool-versions\"\n  expected=\"Name Version Source Installed\ndummy 9.9.9 $PROJECT_DIR/.tool-versions false - Run \\`asdf install dummy 9.9.9\\`\"\n\n  asdf uninstall dummy 9.9.9 || true\n  run asdf current \"dummy\"\n\n  # shellcheck disable=SC2001\n  condensed_output=\"$(sed -e 's/ [ ]*/ /g' -e 's/ $//g' <<<\"$output\")\"\n\n  [ \"$status\" -eq 1 ]\n\n  [ \"$condensed_output\" = \"$expected\" ]\n}\n\n@test \"should output all plugins when no plugin passed\" {\n\n  #install_dummy_plugin\n  install_dummy_version \"1.1.0\"\n\n  install_mock_plugin \"foobar\"\n  install_mock_plugin_version \"foobar\" \"1.0.0\"\n\n  install_mock_plugin \"baz\"\n\n  cd \"$PROJECT_DIR\"\n  echo 'dummy 1.1.0' >>\"$PROJECT_DIR/.tool-versions\"\n  echo 'foobar 1.0.0' >>\"$PROJECT_DIR/.tool-versions\"\n  expected=\"Name Version Source Installed\nbaz ______ ______\ndummy 1.1.0 $PROJECT_DIR/.tool-versions true\nfoobar 1.0.0 $PROJECT_DIR/.tool-versions true\"\n\n  run asdf current\n\n  # shellcheck disable=SC2001\n  condensed_output=\"$(sed -e 's/ [ ]*/ /g' -e 's/ $//g' <<<\"$output\")\"\n\n  [ \"$expected\" = \"$condensed_output\" ]\n}\n\n@test \"should always match the tool name exactly\" {\n  #install_dummy_plugin\n  install_dummy_version \"1.1.0\"\n\n  install_mock_plugin \"y\"\n  install_mock_plugin_version \"y\" \"2.1.0\"\n\n  cd \"$PROJECT_DIR\"\n  echo 'dummy 1.1.0' >>\"$PROJECT_DIR/.tool-versions\"\n  echo 'y 2.1.0' >>\"$PROJECT_DIR/.tool-versions\"\n\n  run asdf current \"y\"\n  [ \"$status\" -eq 0 ]\n  [[ \"$output\" == *'2.1.0'* ]]\n}\n\n@test \"with no plugins prints an error\" {\n  clean_asdf_dir\n  expected=\"No plugins installed\"\n\n  run asdf current\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"$expected\" ]\n}\n\n@test \"current should handle comments\" {\n  cd \"$PROJECT_DIR\"\n  echo \"dummy 1.2.0  # this is a comment\" >>\"$PROJECT_DIR/.tool-versions\"\n  expected=\"Name Version Source Installed\ndummy 1.2.0 $PROJECT_DIR/.tool-versions true\"\n\n  run asdf current \"dummy\"\n  [ \"$status\" -eq 0 ]\n  # shellcheck disable=SC2001\n  condensed_output=\"$(sed -e 's/ [ ]*/ /g' <<<\"$output\")\"\n  [ \"$condensed_output\" = \"$expected\" ]\n}\n"
  },
  {
    "path": "test/fixtures/dummy_broken_plugin/bin/download",
    "content": "#!/usr/bin/env bash\n\necho \"Download failed!\"\nexit 1\n"
  },
  {
    "path": "test/fixtures/dummy_broken_plugin/bin/install",
    "content": "#!/usr/bin/env bash\n\necho \"Unused script\"\n"
  },
  {
    "path": "test/fixtures/dummy_broken_plugin/bin/list-all",
    "content": "#!/usr/bin/env bash\n\necho \"Attempting to list versions\"\necho \"List-all failed!\" >&2\nexit 1\n"
  },
  {
    "path": "test/fixtures/dummy_legacy_plugin/bin/get-version-from-legacy-file",
    "content": "#!/usr/bin/env bash\n\nget_legacy_version() {\n  current_directory=$1\n  version_file=\"$current_directory/.dummy-version\"\n\n  if [ -f \"$version_file\" ]; then\n    cat \"$version_file\"\n  fi\n}\n\nget_legacy_version \"$1\"\n"
  },
  {
    "path": "test/fixtures/dummy_legacy_plugin/bin/install",
    "content": "#!/usr/bin/env bash\n\nmkdir -p \"$ASDF_INSTALL_PATH\"\nenv >\"$ASDF_INSTALL_PATH/env\"\necho \"$ASDF_INSTALL_VERSION\" >\"$ASDF_INSTALL_PATH/version\"\n\n# create the dummy executable\nmkdir -p \"$ASDF_INSTALL_PATH/bin\"\ncat <<EOF >\"$ASDF_INSTALL_PATH/bin/dummy\"\necho This is Dummy ${ASDF_INSTALL_VERSION}! \\$2 \\$1\nEOF\nchmod +x \"$ASDF_INSTALL_PATH/bin/dummy\"\nmkdir -p \"$ASDF_INSTALL_PATH/bin/subdir\"\ncat <<EOF >\"$ASDF_INSTALL_PATH/bin/subdir/other_bin\"\necho This is Other Bin ${ASDF_INSTALL_VERSION}! \\$2 \\$1\nEOF\nchmod +x \"$ASDF_INSTALL_PATH/bin/subdir/other_bin\"\n"
  },
  {
    "path": "test/fixtures/dummy_legacy_plugin/bin/list-all",
    "content": "#!/usr/bin/env bash\n\nversions_list=(1.0.0 1.1.0 2.0.0 3.0.0-alpha1 3.0.0-beta2 4.0.0 4.1.0-pre 5.0.0-Alpha1 5.1.0 5.2.0-Alpha2)\necho \"${versions_list[@]}\"\n"
  },
  {
    "path": "test/fixtures/dummy_legacy_plugin/bin/list-legacy-filenames",
    "content": "#!/usr/bin/env bash\n\necho \".dummy-version .dummyrc\"\n"
  },
  {
    "path": "test/fixtures/dummy_legacy_plugin/bin/parse-legacy-file",
    "content": "#!/usr/bin/env bash\n\n# shellcheck disable=SC2020\ntr <\"$1\" -d \"dummy-\"\n"
  },
  {
    "path": "test/fixtures/dummy_plugin/LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2014 Akash Manohar J\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "test/fixtures/dummy_plugin/bin/debug",
    "content": "#!/usr/bin/env bash\n\necho \"$@\"\n"
  },
  {
    "path": "test/fixtures/dummy_plugin/bin/download",
    "content": "#!/usr/bin/env bash\n\nexit 0\n"
  },
  {
    "path": "test/fixtures/dummy_plugin/bin/get-version-from-legacy-file",
    "content": "#!/usr/bin/env bash\n\nget_legacy_version() {\n  current_directory=$1\n  version_file=\"$current_directory/.dummy-version\"\n\n  if [ -f \"$version_file\" ]; then\n    cat \"$version_file\"\n  fi\n}\n\nget_legacy_version \"$1\"\n"
  },
  {
    "path": "test/fixtures/dummy_plugin/bin/help.overview",
    "content": "#!/usr/bin/env bash\n\necho \"Dummy plugin documentation\"\necho\necho \"Dummy plugin is a plugin only used for unit tests\"\n\nif [ -n \"$ASDF_INSTALL_VERSION\" ]; then\n  echo\n  echo \"Details specific for version $ASDF_INSTALL_VERSION\"\nfi\n"
  },
  {
    "path": "test/fixtures/dummy_plugin/bin/install",
    "content": "#!/usr/bin/env bash\n\n# We want certain versions to fail installation for various reasons in the tests\ncheck_dummy_versions() {\n  local bad_versions=\" other-dummy \"\n  if [[ \"$bad_versions\" == *\" $ASDF_INSTALL_VERSION \"* ]]; then\n    echo \"Dummy couldn't install version: $ASDF_INSTALL_VERSION (on purpose)\"\n    exit 1\n  fi\n}\n\ncheck_dummy_versions\nmkdir -p \"$ASDF_INSTALL_PATH\"\nenv >\"$ASDF_INSTALL_PATH/env\"\necho \"$ASDF_INSTALL_VERSION\" >\"$ASDF_INSTALL_PATH/version\"\n\n# create the dummy executable\nmkdir -p \"$ASDF_INSTALL_PATH/bin\"\ncat <<EOF >\"$ASDF_INSTALL_PATH/bin/dummy\"\n#!/usr/bin/env bash\necho This is Dummy ${ASDF_INSTALL_VERSION}! \\$2 \\$1\nEOF\nchmod +x \"$ASDF_INSTALL_PATH/bin/dummy\"\nmkdir -p \"$ASDF_INSTALL_PATH/bin/subdir\"\ncat <<EOF >\"$ASDF_INSTALL_PATH/bin/subdir/other_bin\"\n#!/usr/bin/env bash\necho This is Other Bin ${ASDF_INSTALL_VERSION}! \\$2 \\$1\nEOF\nchmod +x \"$ASDF_INSTALL_PATH/bin/subdir/other_bin\"\n"
  },
  {
    "path": "test/fixtures/dummy_plugin/bin/latest-stable",
    "content": "#!/usr/bin/env bash\n\nget_latest_stable() {\n  query=$1\n\n  version_list=(1.0.0 1.1.0 2.0.0)\n  printf \"%s\\n\" \"${version_list[@]}\" | grep -E \"^\\s*$query\" | tail -1\n}\n\nget_latest_stable \"$1\"\n"
  },
  {
    "path": "test/fixtures/dummy_plugin/bin/list-all",
    "content": "#!/usr/bin/env bash\n\nversions_list=(1.0.0 1.1.0 2.0.0)\necho \"${versions_list[@]}\"\n# Sending message to STD error to ensure that it is ignored\necho \"ignore this error\" >&2\n"
  },
  {
    "path": "test/fixtures/dummy_plugin/bin/list-legacy-filenames",
    "content": "#!/usr/bin/env bash\n\necho \".dummy-version .dummyrc\"\n"
  },
  {
    "path": "test/fixtures/dummy_plugin/bin/parse-legacy-file",
    "content": "#!/usr/bin/env bash\n\n# shellcheck disable=SC2020\ntr <\"$1\" -d \"dummy-\"\n"
  },
  {
    "path": "test/fixtures/dummy_plugin/bin/post-plugin-add",
    "content": "#!/usr/bin/env bash\n\necho \"plugin add path=${ASDF_PLUGIN_PATH} source_url=${ASDF_PLUGIN_SOURCE_URL}\"\n"
  },
  {
    "path": "test/fixtures/dummy_plugin/bin/post-plugin-update",
    "content": "#!/usr/bin/env bash\n\necho \"plugin updated path=${ASDF_PLUGIN_PATH} old git-ref=${ASDF_PLUGIN_PREV_REF} new git-ref=${ASDF_PLUGIN_POST_REF}\"\n"
  },
  {
    "path": "test/fixtures/dummy_plugin/bin/pre-plugin-remove",
    "content": "#!/usr/bin/env bash\n\necho \"plugin-remove ${ASDF_PLUGIN_PATH}\"\n"
  },
  {
    "path": "test/fixtures/dummy_plugin_no_download/LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2014 Akash Manohar J\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "test/fixtures/dummy_plugin_no_download/bin/install",
    "content": "#!/usr/bin/env bash\n\nprintf '%s' 'install'\n"
  },
  {
    "path": "test/fixtures/dummy_plugins_repo/plugins/bar",
    "content": "repository = http://example.com/bar\n"
  },
  {
    "path": "test/fixtures/dummy_plugins_repo/plugins/dummy",
    "content": "repository = http://example.com/dummy\n"
  },
  {
    "path": "test/fixtures/dummy_plugins_repo/plugins/foo",
    "content": "repository = http://example.com/foo\n"
  },
  {
    "path": "test/get_asdf_config_value.bats",
    "content": "#!/usr/bin/env bats\n# shellcheck disable=SC2164\n\nload test_helpers\n\nsetup() {\n  cd \"$BATS_TMPDIR\"\n  ASDF_CONFIG_FILE=\"$BATS_TMPDIR/asdfrc\"\n  cat >\"$ASDF_CONFIG_FILE\" <<-EOM\nkey1 = value1\nlegacy_version_file = yes\nEOM\n\n  ASDF_CONFIG_DEFAULT_FILE=\"$BATS_TMPDIR/asdfrc_defaults\"\n  cat >\"$ASDF_CONFIG_DEFAULT_FILE\" <<-EOM\n# i have  a comment, it's ok\nkey2 = value2\nlegacy_version_file = no\nEOM\n}\n\nteardown() {\n  rm \"$ASDF_CONFIG_FILE\"\n  rm \"$ASDF_CONFIG_DEFAULT_FILE\"\n  unset ASDF_CONFIG_DEFAULT_FILE\n  unset ASDF_CONFIG_FILE\n}\n\n@test \"get_config returns default when config file does not exist\" {\n  result=$(ASDF_CONFIG_FILE=\"/some/fake/path\" get_asdf_config_value \"legacy_version_file\")\n  [ \"$result\" = \"no\" ]\n}\n\n@test \"get_config returns default value when the key does not exist\" {\n  [ \"$(get_asdf_config_value \"key2\")\" = \"value2\" ]\n}\n\n@test \"get_config returns config file value when key exists\" {\n  [ \"$(get_asdf_config_value \"key1\")\" = \"value1\" ]\n  [ \"$(get_asdf_config_value \"legacy_version_file\")\" = \"yes\" ]\n}\n\n@test \"get_config returns config file complete value including '=' symbols\" {\n  cat >>\"$ASDF_CONFIG_FILE\" <<-'EOM'\nkey3 = VAR=val\nEOM\n\n  [ \"$(get_asdf_config_value \"key3\")\" = \"VAR=val\" ]\n}\n"
  },
  {
    "path": "test/help_command.bats",
    "content": "#!/usr/bin/env bats\n\nload test_helpers\n\nsetup() {\n  setup_asdf_dir\n  install_dummy_plugin\n  install_dummy_legacy_plugin\n  run asdf install dummy 1.0\n  run asdf install dummy 1.1\n\n  PROJECT_DIR=\"$HOME/project\"\n  mkdir -p \"$PROJECT_DIR\"\n}\n\nteardown() {\n  clean_asdf_dir\n}\n\n@test \"help should show dummy plugin help\" {\n  cd \"$PROJECT_DIR\"\n\n  run asdf help \"dummy\"\n\n  expected_output=\"$(\n    cat <<EOF\nDummy plugin documentation\n\nDummy plugin is a plugin only used for unit tests\nEOF\n  )\"\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"$expected_output\" ]\n}\n\n@test \"help should show dummy plugin help specific to version when version is present\" {\n  cd \"$PROJECT_DIR\"\n\n  run asdf help \"dummy\" \"1.2.3\"\n\n  expected_output=\"$(\n    cat <<EOF\nDummy plugin documentation\n\nDummy plugin is a plugin only used for unit tests\n\nDetails specific for version 1.2.3\nEOF\n  )\"\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"$expected_output\" ]\n}\n\n@test \"help should fail for unknown plugins\" {\n  cd \"$PROJECT_DIR\"\n\n  run asdf help \"sunny\"\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"No plugin named sunny\" ]\n}\n\n@test \"help should fail when plugin doesn't have documentation callback\" {\n  cd \"$PROJECT_DIR\"\n\n  run asdf help \"legacy-dummy\"\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"No documentation for plugin legacy-dummy\" ]\n}\n\n@test \"help should show asdf help when no plugin name is provided\" {\n  cd \"$PROJECT_DIR\"\n\n  run asdf help\n\n  [ \"$status\" -eq 0 ]\n  [[ $output =~ $'version: '[0-9]* ]]\n  [[ $output == *$'MANAGE PLUGINS\\n'* ]]\n  [[ $output == *$'MANAGE TOOLS\\n'* ]]\n  [[ $output == *$'UTILS\\n'* ]]\n  [[ $output == *$'\"Late but latest\"\\n-- Rajinikanth' ]]\n}\n"
  },
  {
    "path": "test/info_command.bats",
    "content": "#!/usr/bin/env bats\n\nload test_helpers\n\nsetup() {\n  setup_asdf_dir\n  install_dummy_plugin\n  install_dummy_legacy_plugin\n  run asdf install dummy 1.0\n  run asdf install dummy 1.1\n\n  PROJECT_DIR=\"$HOME/project\"\n  mkdir -p \"$PROJECT_DIR\"\n}\n\nteardown() {\n  clean_asdf_dir\n}\n\n@test \"info should show os, shell and asdf debug information\" {\n  cd \"$PROJECT_DIR\"\n\n  run asdf info\n\n  [ \"$status\" -eq 0 ]\n  [[ $output == *$'OS:\\n'* ]]\n  [[ $output == *$'SHELL:\\n'* ]]\n  [[ $output == *$'BASH VERSION:\\n'* ]]\n  [[ $output == *$'ASDF VERSION:\\n'* ]]\n  [[ $output == *$'ASDF INTERNAL VARIABLES:\\n'* ]]\n  [[ $output == *$'ASDF INSTALLED PLUGINS:\\n'* ]]\n\n}\n"
  },
  {
    "path": "test/install_command.bats",
    "content": "#!/usr/bin/env bats\n\nload test_helpers\n\nsetup() {\n  setup_asdf_dir\n  install_dummy_legacy_plugin\n  install_dummy_plugin\n  install_dummy_broken_plugin\n  install_dummy_plugin_no_download\n\n  PROJECT_DIR=\"$HOME/project\"\n  mkdir -p \"$PROJECT_DIR\"\n}\n\nteardown() {\n  clean_asdf_dir\n}\n\n@test \"install_command installs the correct version\" {\n  run asdf install dummy 1.1.0\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$ASDF_DIR/installs/dummy/1.1.0/version\")\" = \"1.1.0\" ]\n}\n\n@test \"install_command installs the correct version for plugins without download script\" {\n  run asdf install legacy-dummy 1.1.0\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$ASDF_DIR/installs/legacy-dummy/1.1.0/version\")\" = \"1.1.0\" ]\n}\n\n@test \"install_command without arguments installs even if the user is terrible and does not use newlines\" {\n  cd \"$PROJECT_DIR\"\n  echo -n 'dummy 1.2.0' >\".tool-versions\"\n  run asdf install\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$ASDF_DIR/installs/dummy/1.2.0/version\")\" = \"1.2.0\" ]\n}\n\n@test \"install_command with only name installs the version in .tool-versions\" {\n  cd \"$PROJECT_DIR\"\n  echo -n 'dummy 1.2.0' >\".tool-versions\"\n  run asdf install dummy\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$ASDF_DIR/installs/dummy/1.2.0/version\")\" = \"1.2.0\" ]\n}\n\n@test \"install_command set ASDF_CONCURRENCY\" {\n  run asdf install dummy 1.0.0\n  [ \"$status\" -eq 0 ]\n  [ -f \"$ASDF_DIR/installs/dummy/1.0.0/env\" ]\n  run grep ASDF_CONCURRENCY \"$ASDF_DIR/installs/dummy/1.0.0/env\"\n  [ \"$status\" -eq 0 ]\n}\n\n@test \"install_command set ASDF_CONCURRENCY via env var\" {\n  ASDF_CONCURRENCY=-1 run asdf install dummy 1.0.0\n  [ \"$status\" -eq 0 ]\n  [ -f \"$ASDF_DIR/installs/dummy/1.0.0/env\" ]\n  run grep ASDF_CONCURRENCY=-1 \"$ASDF_DIR/installs/dummy/1.0.0/env\"\n  [ \"$status\" -eq 0 ]\n}\n\n@test \"install_command set ASDF_CONCURRENCY via asdfrc\" {\n  cat >\"$HOME/.asdfrc\" <<-'EOM'\n  concurrency = -2\nEOM\n  run asdf install dummy 1.0.0\n  [ \"$status\" -eq 0 ]\n  [ -f \"$ASDF_DIR/installs/dummy/1.0.0/env\" ]\n  run grep ASDF_CONCURRENCY=-2 \"$ASDF_DIR/installs/dummy/1.0.0/env\"\n  [ \"$status\" -eq 0 ]\n}\n\n@test \"install_command without arguments should work in directory containing whitespace\" {\n  WHITESPACE_DIR=\"$PROJECT_DIR/whitespace\\ dir\"\n  mkdir -p \"$WHITESPACE_DIR\"\n  cd \"$WHITESPACE_DIR\"\n  echo 'dummy 1.2.0' >>\"$WHITESPACE_DIR/.tool-versions\"\n\n  run asdf install\n\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$ASDF_DIR/installs/dummy/1.2.0/version\")\" = \"1.2.0\" ]\n}\n\n@test \"install_command should create a shim with asdf-plugin metadata\" {\n  run asdf install dummy 1.0.0\n  [ \"$status\" -eq 0 ]\n  [ -f \"$ASDF_DIR/installs/dummy/1.0.0/env\" ]\n  run grep \"asdf-plugin: dummy 1.0.0\" \"$ASDF_DIR/shims/dummy\"\n  [ \"$status\" -eq 0 ]\n}\n\n@test \"install_command should create a shim with asdf-plugin metadata for plugins without download script\" {\n  run asdf install legacy-dummy 1.0.0\n  [ \"$status\" -eq 0 ]\n  [ -f \"$ASDF_DIR/installs/legacy-dummy/1.0.0/env\" ]\n  run grep \"asdf-plugin: legacy-dummy 1.0.0\" \"$ASDF_DIR/shims/dummy\"\n  [ \"$status\" -eq 0 ]\n}\n\n@test \"install_command on two versions should create a shim with asdf-plugin metadata\" {\n  run asdf install dummy 1.1.0\n  [ \"$status\" -eq 0 ]\n\n  run grep \"asdf-plugin: dummy 1.1.0\" \"$ASDF_DIR/shims/dummy\"\n  [ \"$status\" -eq 0 ]\n\n  run grep \"asdf-plugin: dummy 1.0.0\" \"$ASDF_DIR/shims/dummy\"\n  [ \"$status\" -eq 1 ]\n\n  run asdf install dummy 1.0.0\n  [ \"$status\" -eq 0 ]\n  run grep \"asdf-plugin: dummy 1.0.0\" \"$ASDF_DIR/shims/dummy\"\n  [ \"$status\" -eq 0 ]\n\n  run grep \"# asdf-plugin: dummy 1.0.0\"$'\\n'\"# asdf-plugin: dummy 1.1.0\" \"$ASDF_DIR/shims/dummy\"\n  [ \"$status\" -eq 0 ]\n\n  lines_count=$(grep -c \"asdf-plugin: dummy 1.1.0\" \"$ASDF_DIR/shims/dummy\")\n  [ \"$lines_count\" -eq \"1\" ]\n}\n\n@test \"install_command without arguments should not generate shim for subdir\" {\n  cd \"$PROJECT_DIR\"\n  echo 'dummy 1.0.0' >\"$PROJECT_DIR/.tool-versions\"\n\n  run asdf install\n  [ \"$status\" -eq 0 ]\n  [ -f \"$ASDF_DIR/shims/dummy\" ]\n  [ ! -f \"$ASDF_DIR/shims/subdir\" ]\n}\n\n@test \"install_command without arguments should generate shim that passes all arguments to executable\" {\n  # asdf lib needed to run generated shims\n  cp -rf \"$BATS_TEST_DIRNAME\"/../{bin,lib} \"$ASDF_DIR/\"\n\n  cd \"$PROJECT_DIR\"\n  echo 'dummy 1.0.0' >\"$PROJECT_DIR/.tool-versions\"\n  run asdf install\n\n  # execute the generated shim\n  run \"$ASDF_DIR/shims/dummy\" world hello\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"This is Dummy 1.0.0! hello world\" ]\n}\n\n@test \"install_command fails when tool is specified but no version of the tool is configured\" {\n  run asdf install dummy\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"No versions specified for dummy in config files or environment\" ]\n  [ ! -f \"$ASDF_DIR/installs/dummy/1.1.0/version\" ]\n}\n\n# `asdf install` now enumerates installed plugins, so if a plugin defined in a\n# .tool-versions file is not installed `asdf install` now skips it.\n#@test \"install_command fails if the plugin is not installed\" {\n#  cd \"$PROJECT_DIR\"\n#  echo 'other_dummy 1.0.0' >\"$PROJECT_DIR/.tool-versions\"\n\n#  run asdf install\n#  [ \"$status\" -eq 1 ]\n#  [ \"$output\" = \"other_dummy plugin is not installed\" ]\n#}\n\n# Not clear how this test differs from those above\n#@test \"install_command fails if the plugin is not installed without collisions\" {\n#  cd \"$PROJECT_DIR\"\n#  printf \"dummy 1.0.0\\ndum 1.0.0\" >\"$PROJECT_DIR/.tool-versions\"\n\n#  run asdf install\n#  [ \"$status\" -eq 1 ]\n#  [ \"$output\" = \"dum plugin is not installed\" ]\n#}\n\n@test \"install_command fails when tool is specified but no version of the tool is configured in config file\" {\n  run asdf install dummy\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"No versions specified for dummy in config files or environment\" ]\n  [ ! -f \"$ASDF_DIR/installs/dummy/1.0.0/version\" ]\n}\n\n@test \"install_command fails when two tools are specified with no versions\" {\n  printf 'dummy 1.0.0\\nother-dummy 2.0.0' >\"$PROJECT_DIR/.tool-versions\"\n  run asdf install dummy other-dummy\n  [ \"$status\" -eq 1 ]\n  [ \"$(head -n1 <<<\"$output\")\" = \"Dummy couldn't install version: other-dummy (on purpose)\" ]\n  [ ! -f \"$ASDF_DIR/installs/dummy/1.0.0/version\" ]\n  [ ! -f \"$ASDF_DIR/installs/other-dummy/2.0.0/version\" ]\n}\n\n@test \"install_command without arguments uses a parent directory .tool-versions file if present\" {\n  # asdf lib needed to run generated shims\n  cp -rf \"$BATS_TEST_DIRNAME\"/../{bin,lib} \"$ASDF_DIR/\"\n\n  echo 'dummy 1.0.0' >\"$PROJECT_DIR/.tool-versions\"\n  mkdir -p \"$PROJECT_DIR/child\"\n\n  cd \"$PROJECT_DIR/child\"\n\n  run asdf install\n\n  # execute the generated shim\n  [ \"$(\"$ASDF_DIR/shims/dummy\" world hello)\" = \"This is Dummy 1.0.0! hello world\" ]\n  [ \"$status\" -eq 0 ]\n}\n\n@test \"install_command installs multiple tool versions when they are specified in a .tool-versions file\" {\n  echo 'dummy 1.0.0 1.2.0' >\"$PROJECT_DIR/.tool-versions\"\n  cd \"$PROJECT_DIR\"\n\n  run asdf install\n  [ \"$status\" -eq 0 ]\n\n  [ \"$(cat \"$ASDF_DIR/installs/dummy/1.0.0/version\")\" = \"1.0.0\" ]\n  [ \"$(cat \"$ASDF_DIR/installs/dummy/1.2.0/version\")\" = \"1.2.0\" ]\n}\n\n@test \"install_command doesn't install system version\" {\n  run asdf install dummy system\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"error installing version: uninstallable version system of dummy\" ]\n  [ ! -f \"$ASDF_DIR/installs/dummy/system/version\" ]\n}\n\n@test \"install command executes configured pre plugin install hook\" {\n  cat >\"$HOME/.asdfrc\" <<-'EOM'\npre_asdf_install_dummy = echo will install dummy $1\nEOM\n\n  run asdf install dummy 1.0.0\n  [ \"$output\" = \"will install dummy 1.0.0\" ]\n}\n\n# This test has been changed because variables like $version and $plugin_name\n# only worked because asdf was a Bash script and leaked those variables.\n@test \"install command executes configured post plugin install hook\" {\n  cat >\"$HOME/.asdfrc\" <<-'EOM'\npost_asdf_install_dummy = echo HEY $1 FROM dummy\nEOM\n\n  run asdf install dummy 1.0.0\n  [ \"$output\" = \"HEY 1.0.0 FROM dummy\" ]\n}\n\n@test \"install command without arguments installs versions from legacy files\" {\n  echo 'legacy_version_file = yes' >\"$HOME/.asdfrc\"\n  echo '1.2.0' >>\"$PROJECT_DIR/.dummy-version\"\n  cd \"$PROJECT_DIR\"\n\n  run asdf install\n  [ \"$status\" -eq 0 ]\n  [ -f \"$ASDF_DIR/installs/dummy/1.2.0/version\" ]\n}\n\n@test \"install command without arguments installs versions from legacy files in parent directories\" {\n  echo 'legacy_version_file = yes' >\"$HOME/.asdfrc\"\n  echo '1.2.0' >>\"$PROJECT_DIR/.dummy-version\"\n\n  mkdir -p \"$PROJECT_DIR/child\"\n  cd \"$PROJECT_DIR/child\"\n\n  run asdf install\n  [ \"$status\" -eq 0 ]\n  [ -f \"$ASDF_DIR/installs/dummy/1.2.0/version\" ]\n}\n\n@test \"install_command latest installs latest stable version\" {\n  run asdf install dummy latest\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$ASDF_DIR/installs/dummy/2.0.0/version\")\" = \"2.0.0\" ]\n}\n\n@test \"install_command latest:version installs latest stable version that matches the given string\" {\n  run asdf install dummy latest:1\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$ASDF_DIR/installs/dummy/1.1.0/version\")\" = \"1.1.0\" ]\n}\n\n@test \"install_command deletes the download directory\" {\n  run asdf install dummy 1.1.0\n  [ \"$status\" -eq 0 ]\n  [ ! -d \"$ASDF_DIR/downloads/dummy/1.1.0\" ]\n  [ \"$(cat \"$ASDF_DIR/installs/dummy/1.1.0/version\")\" = \"1.1.0\" ]\n}\n\n@test \"install_command keeps the download directory when --keep-download flag is provided\" {\n  # Original code:\n  # run asdf install dummy 1.1.0 --keep-download\n  # Flags should be allowed anywhere, but unfortunately the CLI arg parser\n  # I'm using only allows them before positional arguments. Hence I've had to\n  # update this test. But we should fix this soon.\n  run asdf install --keep-download dummy 1.1.0\n  [ \"$status\" -eq 0 ]\n  [ -d \"$ASDF_DIR/downloads/dummy/1.1.0\" ]\n  [ \"$(cat \"$ASDF_DIR/installs/dummy/1.1.0/version\")\" = \"1.1.0\" ]\n}\n\n@test \"install_command keeps the download directory when always_keep_download setting is true\" {\n  echo 'always_keep_download = yes' >\"$HOME/.asdfrc\"\n  run asdf install dummy 1.1.0\n  [ \"$status\" -eq 0 ]\n  [ -d \"$ASDF_DIR/downloads/dummy/1.1.0\" ]\n  [ \"$(cat \"$ASDF_DIR/installs/dummy/1.1.0/version\")\" = \"1.1.0\" ]\n}\n\n@test \"install_command fails when download script exits with non-zero code\" {\n  run asdf install dummy-broken 1.0.0\n  [ \"$status\" -eq 1 ]\n  [ ! -d \"$ASDF_DIR/downloads/dummy-broken/1.1.0\" ]\n  [ ! -d \"$ASDF_DIR/installs/dummy-broken/1.1.0\" ]\n  [ \"$(head -n1 <<<\"$output\")\" = \"Download failed!\" ]\n}\n\n# Download callback is now required\n#@test \"install_command prints info message if plugin does not support preserving download data if configured\" {\n#  install_dummy_plugin_no_download\n#\n#  run asdf install dummy-no-download 1.0.0\n#  [ \"$status\" -eq 0 ]\n#  [[ \"$output\" == *'asdf: Warn:'*'not be preserved'* ]]\n#}\n\n@test \"install command without arguments installs version a second time without errors\" {\n  cd \"$PROJECT_DIR\"\n  echo -n 'dummy 1.2.0' >\".tool-versions\"\n  run asdf install\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$ASDF_DIR/installs/dummy/1.2.0/version\")\" = \"1.2.0\" ]\n\n  run asdf install\n  [ \"$status\" -eq 0 ]\n}\n\n@test \"install command with tool installs version a second time without errors\" {\n  cd \"$PROJECT_DIR\"\n  echo -n 'dummy 1.2.0' >\".tool-versions\"\n  run asdf install dummy\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$ASDF_DIR/installs/dummy/1.2.0/version\")\" = \"1.2.0\" ]\n\n  run asdf install dummy\n  [ \"$status\" -eq 0 ]\n}\n\n@test \"install command with tool and version installs version a second time without errors\" {\n  run asdf install dummy 1.0.0\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$ASDF_DIR/installs/dummy/1.0.0/version\")\" = \"1.0.0\" ]\n\n  run asdf install dummy 1.0.0\n  [ \"$status\" -eq 0 ]\n}\n\n@test \"install command with tool and different version installs version a second time without errors\" {\n  cd \"$PROJECT_DIR\"\n  echo -n 'dummy 1.0.0 1.1.0' >\".tool-versions\"\n\n  run asdf install dummy 1.0.0\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$ASDF_DIR/installs/dummy/1.0.0/version\")\" = \"1.0.0\" ]\n\n  run asdf install\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$ASDF_DIR/installs/dummy/1.1.0/version\")\" = \"1.1.0\" ]\n}\n\n@test \"install command with two tools installs tool version a second time without errors\" {\n  cd \"$PROJECT_DIR\"\n  printf \"dummy 1.0.0\\nlegacy-dummy 1.0.0\" >\"$PROJECT_DIR/.tool-versions\"\n\n  run asdf install dummy 1.0.0\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$ASDF_DIR/installs/dummy/1.0.0/version\")\" = \"1.0.0\" ]\n\n  run asdf install\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$ASDF_DIR/installs/legacy-dummy/1.0.0/version\")\" = \"1.0.0\" ]\n}\n"
  },
  {
    "path": "test/latest_command.bats",
    "content": "#!/usr/bin/env bats\n\nload test_helpers\n\nsetup() {\n  setup_asdf_dir\n  install_dummy_plugin\n  install_dummy_legacy_plugin\n}\n\nteardown() {\n  clean_asdf_dir\n}\n\n####################################################\n####       plugin with bin/latest-stable        ####\n####################################################\n@test \"[latest_command - dummy_plugin] shows latest stable version\" {\n  run asdf latest dummy\n  [ \"2.0.0\" = \"$output\" ]\n  [ \"$status\" -eq 0 ]\n}\n\n@test \"[latest_command - dummy_plugin] shows latest stable version that matches the given string\" {\n  run asdf latest dummy 1\n  [ \"1.1.0\" = \"$output\" ]\n  [ \"$status\" -eq 0 ]\n}\n\n@test \"[latest_command - dummy_plugin] an invalid version should return an error\" {\n  run asdf latest dummy 3\n  [ \"No compatible versions available (dummy 3)\" = \"$output\" ]\n  [ \"$status\" -eq 1 ]\n}\n\n####################################################\n####      plugin without bin/latest-stable      ####\n####################################################\n@test \"[latest_command - dummy_legacy_plugin] shows latest stable version\" {\n  run asdf latest legacy-dummy\n  [ \"5.1.0\" = \"$output\" ]\n  [ \"$status\" -eq 0 ]\n}\n\n@test \"[latest_command - dummy_legacy_plugin] shows latest stable version that matches the given string\" {\n  run asdf latest legacy-dummy 1\n  [ \"1.1.0\" = \"$output\" ]\n  [ \"$status\" -eq 0 ]\n}\n\n@test \"[latest_command - dummy_legacy_plugin] No stable version should return an error\" {\n  run asdf latest legacy-dummy 3\n  [ \"No compatible versions available (legacy-dummy 3)\" = \"$output\" ]\n  [ \"$status\" -eq 1 ]\n}\n\n@test \"[latest_command - dummy_legacy_plugin] do not show latest unstable version that matches the given string\" {\n  run asdf latest legacy-dummy 4\n  [ \"4.0.0\" = \"$output\" ]\n  [ \"$status\" -eq 0 ]\n}\n\n@test \"[latest_command - dummy_legacy_plugin] do not show latest unstable version with capital characters that matches the given string\" {\n  run asdf latest legacy-dummy 5\n  [ \"5.1.0\" = \"$output\" ]\n  [ \"$status\" -eq 0 ]\n}\n\n@test \"[latest_command - dummy_legacy_plugin] an invalid version should return an error\" {\n  run asdf latest legacy-dummy 6\n  [ \"No compatible versions available (legacy-dummy 6)\" = \"$output\" ]\n  [ \"$status\" -eq 1 ]\n}\n\n################################\n####      latest --all      ####\n################################\n@test \"[latest_command - all plugins] shows the latest stable version of all plugins\" {\n  run asdf install dummy 2.0.0\n  run asdf install legacy-dummy 4.0.0\n  run asdf latest --all\n  [ $'dummy\\t2.0.0\\tinstalled\\nlegacy-dummy\\t5.1.0\\tmissing' = \"$output\" ]\n  [ \"$status\" -eq 0 ]\n}\n\n@test \"[latest_command - all plugins] not installed plugin should return missing\" {\n  run asdf latest --all\n  [ $'dummy\\t2.0.0\\tmissing\\nlegacy-dummy\\t5.1.0\\tmissing' = \"$output\" ]\n  [ \"$status\" -eq 0 ]\n}\n"
  },
  {
    "path": "test/list_command.bats",
    "content": "#!/usr/bin/env bats\n\nload test_helpers\n\nsetup() {\n  setup_asdf_dir\n  install_dummy_plugin\n  install_dummy_broken_plugin\n\n  PROJECT_DIR=\"$HOME/project\"\n  mkdir -p \"$PROJECT_DIR\"\n}\n\nteardown() {\n  clean_asdf_dir\n}\n\n@test \"list_command should list plugins with installed versions\" {\n  run asdf install dummy 1.0.0\n  run asdf install dummy 1.1.0\n  run asdf list\n  [[ \"$output\" == *$'dummy\\n  1.0.0\\n  1.1.0'* ]]\n  [[ \"$output\" == *$'dummy-broken\\n  No versions installed'* ]]\n  [ \"$status\" -eq 0 ]\n}\n\n@test \"list_command should list plugins with installed versions and any selected versions marked with asterisk\" {\n  cd \"$PROJECT_DIR\"\n  echo 'dummy 1.1.0' >>\"$PROJECT_DIR/.tool-versions\"\n  run asdf install dummy 1.0.0\n  run asdf install dummy 1.1.0\n\n  run asdf list\n  [[ \"$output\" == *$'dummy\\n  1.0.0\\n *1.1.0'* ]]\n  [[ \"$output\" == *$'dummy-broken\\n  No versions installed'* ]]\n  [ \"$status\" -eq 0 ]\n}\n\n@test \"list_command should continue listing even when no version is installed for any of the plugins\" {\n  run install_mock_plugin \"dummy\"\n  run install_mock_plugin \"mummy\"\n  run install_mock_plugin \"tummy\"\n  run asdf install dummy 1.0.0\n  run asdf install tummy 2.0.0\n  run asdf list\n  [[ \"$output\" == *$'dummy\\n  1.0.0'* ]]\n  [[ \"$output\" == *$'dummy-broken\\n  No versions installed'* ]]\n  [[ \"$output\" == *$'mummy\\n  No versions installed'* ]]\n  [[ \"$output\" == *$'tummy\\n  2.0.0'* ]]\n  [ \"$status\" -eq 0 ]\n}\n\n@test \"list_command with plugin should list installed versions\" {\n  run asdf install dummy 1.0.0\n  run asdf install dummy 1.1.0\n  run asdf list dummy\n  [ $'  1.0.0\\n  1.1.0' = \"$output\" ]\n  [ \"$status\" -eq 0 ]\n}\n\n@test \"list_command with version filters installed versions\" {\n  run asdf install dummy 1.0\n  run asdf install dummy 1.1\n  run asdf install dummy 2.0\n  run asdf list dummy 1\n  [ $'  1.0\\n  1.1' = \"$output\" ]\n  [ \"$status\" -eq 0 ]\n}\n\n@test \"list_command with an invalid version should return an error\" {\n  run asdf install dummy 1.0\n  run asdf install dummy 1.1\n  run asdf list dummy 2\n  [ \"No compatible versions installed (dummy 2)\" = \"$output\" ]\n  [ \"$status\" -eq 0 ]\n}\n\n@test \"list_all_command lists available versions\" {\n  run asdf list all dummy\n  [ $'1.0.0\\n1.1.0\\n2.0.0' = \"$output\" ]\n  [ \"$status\" -eq 0 ]\n}\n\n@test \"list_all_command with version filters available versions\" {\n  run asdf list all dummy 1\n  [ $'1.0.0\\n1.1.0' = \"$output\" ]\n  [ \"$status\" -eq 0 ]\n}\n\n@test \"list_all_command with an invalid version should return an error\" {\n  run asdf list all dummy 3\n  [ \"No compatible versions available (dummy 3)\" = \"$output\" ]\n  [ \"$status\" -eq 1 ]\n}\n\n@test \"list_all_command fails when list-all script exits with non-zero code\" {\n  run asdf list all dummy-broken\n  [ \"$status\" -eq 1 ]\n  [[ \"$output\" == \"Plugin dummy-broken's list-all callback script failed with output:\"* ]]\n}\n\n@test \"list_all_command displays stderr then stdout when failing\" {\n  run asdf list all dummy-broken\n  [[ \"$output\" == *\"List-all failed!\"* ]]\n  [[ \"$output\" == *\"Attempting to list versions\" ]]\n}\n\n@test \"list_all_command ignores stderr when completing successfully\" {\n  run asdf list all dummy\n  [[ \"$output\" != *\"ignore this error\"* ]]\n}\n"
  },
  {
    "path": "test/non_existent_command.bats",
    "content": "#!/usr/bin/env bats\n\nload test_helpers\n\nsetup() {\n  setup_asdf_dir\n\n  PROJECT_DIR=\"$HOME/project\"\n  mkdir -p \"$PROJECT_DIR\"\n}\n\n@test \"should show help when no valid command is provided\" {\n  cd \"$PROJECT_DIR\"\n\n  run asdf non-existent-command\n\n  [ \"$status\" -eq 1 ]\n  [[ $output == 'invalid command provided:'* ]]\n  [[ $output =~ $'version: '[0-9]* ]]\n  [[ $output == *$'MANAGE PLUGINS\\n'* ]]\n  [[ $output == *$'MANAGE TOOLS\\n'* ]]\n  [[ $output == *$'UTILS\\n'* ]]\n  [[ $output == *$'\"Late but latest\"\\n-- Rajinikanth' ]]\n}\n"
  },
  {
    "path": "test/plugin_add_command.bats",
    "content": "#!/usr/bin/env bats\n# shellcheck disable=SC2030,SC2031\n\nload test_helpers\n\nsetup() {\n  setup_asdf_dir\n}\n\nteardown() {\n  clean_asdf_dir\n}\n\n@test \"plugin_add command with plugin name matching all valid regex chars succeeds\" {\n  install_mock_plugin_repo \"plugin_with-all-valid-chars-123\"\n\n  run asdf plugin add \"plugin_with-all-valid-chars-123\" \"${BASE_DIR}/repo-plugin_with-all-valid-chars-123\"\n  [ \"$status\" -eq 0 ]\n\n  run asdf plugin list\n  [ \"$output\" = \"plugin_with-all-valid-chars-123\" ]\n}\n\n@test \"plugin_add command with LANG=sv_SE.UTF-8 and plugin name matching all valid regex chars succeeds\" {\n  ORIGINAL_LANG=\"$LANG\"\n  LANG=sv_SE.UTF-8\n\n  install_mock_plugin_repo \"plugin-with-w\"\n\n  # https://stackoverflow.com/questions/52570103/regular-expression-a-za-z-seems-to-not-include-letter-w-and-wA\n  # https://github.com/asdf-vm/asdf/issues/1237\n  run asdf plugin add \"plugin-with-w\" \"${BASE_DIR}/repo-plugin-with-w\"\n  [ \"$status\" -eq 0 ]\n\n  run asdf plugin list\n  [ \"$output\" = \"plugin-with-w\" ]\n\n  LANG=\"$ORIGINAL_LANG\"\n}\n\n@test \"plugin_add command with plugin name not matching valid regex fails 1\" {\n  run asdf plugin add \"invalid\\$plugin\\$name\"\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"invalid\\$plugin\\$name is invalid. Name may only contain lowercase letters, numbers, '_', and '-'\" ]\n}\n\n@test \"plugin_add command with plugin name not matching valid regex fails 2\" {\n  run asdf plugin add \"#invalid#plugin#name\"\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"#invalid#plugin#name is invalid. Name may only contain lowercase letters, numbers, '_', and '-'\" ]\n}\n\n@test \"plugin_add command with plugin name not matching valid regex fails 3\" {\n  run asdf plugin add \"Ruby\"\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"Ruby is invalid. Name may only contain lowercase letters, numbers, '_', and '-'\" ]\n}\n\n@test \"plugin_add command with no URL specified adds a plugin using repo\" {\n  run asdf plugin add \"elixir\"\n  [ \"$status\" -eq 0 ]\n\n  run asdf plugin list\n  [ \"$output\" = \"elixir\" ]\n}\n\n@test \"plugin_add command with no URL specified adds a plugin when short name repository is enabled\" {\n  export ASDF_CONFIG_DEFAULT_FILE=\"$HOME/.asdfrc\"\n  echo \"disable_plugin_short_name_repository=no\" >\"$ASDF_CONFIG_DEFAULT_FILE\"\n\n  run asdf plugin add \"elixir\"\n  [ \"$status\" -eq 0 ]\n\n  local expected=\"elixir\"\n  run asdf plugin list\n  [ \"$output\" = \"$expected\" ]\n}\n\n@test \"plugin_add command with no URL specified fails to add a plugin when disabled\" {\n  export ASDF_CONFIG_DEFAULT_FILE=\"$HOME/.asdfrc\"\n  echo \"disable_plugin_short_name_repository=yes\" >\"$ASDF_CONFIG_DEFAULT_FILE\"\n  local expected=\"Short-name plugin repository is disabled\"\n\n  run asdf plugin add \"elixir\"\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"$expected\" ]\n}\n\n@test \"plugin_add command with URL specified adds a plugin using repo\" {\n  install_mock_plugin_repo \"dummy\"\n\n  run asdf plugin add \"dummy\" \"${BASE_DIR}/repo-dummy\"\n  [ \"$status\" -eq 0 ]\n\n  run asdf plugin list\n  # whitespace between 'elixir' and url is from printf %-15s %s format\n  [ \"$output\" = \"dummy\" ]\n}\n\n@test \"plugin_add command with URL specified twice returns success on second time\" {\n  install_mock_plugin_repo \"dummy\"\n\n  run asdf plugin add \"dummy\" \"${BASE_DIR}/repo-dummy\"\n  run asdf plugin add \"dummy\" \"${BASE_DIR}/repo-dummy\"\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"Plugin named dummy already added\" ]\n}\n\n@test \"plugin_add command with no URL specified fails if the plugin doesn't exist\" {\n  run asdf plugin add \"does-not-exist\"\n  [ \"$status\" -eq 1 ]\n  echo \"$output\" | grep \"plugin does-not-exist not found in repository\"\n}\n\n@test \"plugin_add command executes post-plugin add script\" {\n  install_mock_plugin_repo \"dummy\"\n\n  run asdf plugin add \"dummy\" \"${BASE_DIR}/repo-dummy\"\n  [ \"$output\" = \"plugin add path=${ASDF_DIR}/plugins/dummy source_url=${BASE_DIR}/repo-dummy\" ]\n}\n\n@test \"plugin_add command executes configured pre hook (generic)\" {\n  install_mock_plugin_repo \"dummy\"\n\n  cat >\"$HOME/.asdfrc\" <<-'EOM'\npre_asdf_plugin_add = echo ADD ${@}\nEOM\n\n  run asdf plugin add \"dummy\" \"${BASE_DIR}/repo-dummy\"\n\n  local expected_output=\"ADD dummy\nplugin add path=${ASDF_DIR}/plugins/dummy source_url=${BASE_DIR}/repo-dummy\"\n  [ \"$output\" = \"${expected_output}\" ]\n}\n\n@test \"plugin_add command executes configured pre hook (specific)\" {\n  install_mock_plugin_repo \"dummy\"\n\n  cat >\"$HOME/.asdfrc\" <<-'EOM'\npre_asdf_plugin_add_dummy = echo ADD\nEOM\n\n  run asdf plugin add \"dummy\" \"${BASE_DIR}/repo-dummy\"\n\n  local expected_output=\"ADD\nplugin add path=${ASDF_DIR}/plugins/dummy source_url=${BASE_DIR}/repo-dummy\"\n  [ \"$output\" = \"${expected_output}\" ]\n}\n\n@test \"plugin_add command executes configured post hook (generic)\" {\n  install_mock_plugin_repo \"dummy\"\n\n  cat >\"$HOME/.asdfrc\" <<-'EOM'\npost_asdf_plugin_add = echo ADD ${@}\nEOM\n\n  run asdf plugin add \"dummy\" \"${BASE_DIR}/repo-dummy\"\n\n  local expected_output=\"plugin add path=${ASDF_DIR}/plugins/dummy source_url=${BASE_DIR}/repo-dummy\nADD dummy\"\n  [ \"$output\" = \"${expected_output}\" ]\n}\n\n@test \"plugin_add command executes configured post hook (specific)\" {\n  install_mock_plugin_repo \"dummy\"\n\n  cat >\"$HOME/.asdfrc\" <<-'EOM'\npost_asdf_plugin_add_dummy = echo ADD\nEOM\n\n  run asdf plugin add \"dummy\" \"${BASE_DIR}/repo-dummy\"\n\n  local expected_output=\"plugin add path=${ASDF_DIR}/plugins/dummy source_url=${BASE_DIR}/repo-dummy\nADD\"\n  [ \"$output\" = \"${expected_output}\" ]\n}\n"
  },
  {
    "path": "test/plugin_extension_command.bats",
    "content": "#!/usr/bin/env bats\n# shellcheck disable=SC2016\n\nload test_helpers\n\nsetup() {\n  setup_asdf_dir\n  install_dummy_plugin\n  local plugin_path\n  plugin_path=\"$(get_plugin_path dummy)\"\n  mkdir -p \"$plugin_path/lib/commands\"\n}\n\nteardown() {\n  clean_asdf_dir\n}\n\n@test \"asdf help shows plugin extension commands\" {\n  local plugin_path listed_cmds\n  plugin_path=\"$(get_plugin_path dummy)\"\n  touch \"$plugin_path/lib/commands/command\"\n  touch \"$plugin_path/lib/commands/command-foo\"\n  touch \"$plugin_path/lib/commands/command-foo-bar\"\n  run asdf help\n  [ \"$status\" -eq 0 ]\n  echo \"$output\" | grep \"PLUGIN dummy\" # should present plugin section\n  listed_cmds=$(echo \"$output\" | grep -c \"asdf dummy\")\n  [ \"$listed_cmds\" -eq 3 ]\n  echo \"$output\" | grep \"asdf dummy foo-bar\"\n}\n\n@test \"asdf help shows extension commands for plugin with hyphens in the name\" {\n  cd \"$PROJECT_DIR\"\n\n  plugin_name=dummy-hyphenated\n  install_mock_plugin $plugin_name\n\n  plugin_path=\"$(get_plugin_path $plugin_name)\"\n  mkdir -p \"$plugin_path/lib/commands\"\n  touch \"$plugin_path/lib/commands/command\"\n  touch \"$plugin_path/lib/commands/command-foo\"\n  touch \"$plugin_path/lib/commands/command-foo-bar\"\n\n  run asdf help\n  [ \"$status\" -eq 0 ]\n  [[ \"$output\" == *\"PLUGIN $plugin_name\"* ]]\n  listed_cmds=$(grep -c \"asdf $plugin_name\" <<<\"${output}\")\n  [[ $listed_cmds -eq 3 ]]\n  [[ \"$output\" == *\"asdf $plugin_name foo\"* ]]\n  [[ \"$output\" == *\"asdf $plugin_name foo-bar\"* ]]\n}\n\n@test \"asdf can execute plugin bin commands\" {\n  plugin_path=\"$(get_plugin_path dummy)\"\n\n  # this plugin defines a new `asdf dummy foo` command\n  cat <<'EOF' >\"$plugin_path/lib/commands/command-foo\"\n#!/usr/bin/env bash\necho this is an executable $*\nEOF\n  chmod +x \"$plugin_path/lib/commands/command-foo\"\n\n  expected=\"this is an executable bar\"\n\n  run asdf cmd dummy foo bar\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"$expected\" ]\n}\n\n# No longer supported. If you want to do this you'll need to manual source the\n# file containing the functions you want via relative path.\n#@test \"asdf can source plugin bin scripts\" {\n#  plugin_path=\"$(get_plugin_path dummy)\"\n\n#  # this plugin defines a new `asdf dummy foo` command\n#  echo '#!/usr/bin/env bash\n#  echo sourced script has asdf utils $(get_plugin_path dummy) $*' >\"$plugin_path/lib/commands/command-foo\"\n\n#  expected=\"sourced script has asdf utils $plugin_path bar\"\n\n#  run asdf cmd dummy foo bar\n#  [ \"$status\" -eq 0 ]\n#  [ \"$output\" = \"$expected\" ]\n#}\n\n@test \"asdf can execute plugin default command without arguments\" {\n  plugin_path=\"$(get_plugin_path dummy)\"\n\n  # this plugin defines a new `asdf dummy` command\n  cat <<'EOF' >\"$plugin_path/lib/commands/command\"\n#!/usr/bin/env bash\necho hello\nEOF\n  chmod +x \"$plugin_path/lib/commands/command\"\n\n  expected=\"hello\"\n\n  run asdf cmd dummy\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"$expected\" ]\n}\n\n@test \"asdf can execute plugin default command with arguments\" {\n  plugin_path=\"$(get_plugin_path dummy)\"\n\n  # this plugin defines a new `asdf dummy` command\n  cat <<'EOF' >\"$plugin_path/lib/commands/command\"\n#!/usr/bin/env bash\necho hello $*\nEOF\n  chmod +x \"$plugin_path/lib/commands/command\"\n\n  expected=\"hello world\"\n\n  run asdf cmd dummy world\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"$expected\" ]\n}\n\n@test \"asdf execute plugin default command unsets ASDF_INSTALL_TYPE ASDF_INSTALL_VERSION and ASDF_INSTALL_PATH env variables\" {\n  export ASDF_INSTALL_VERSION=1.2.3\n  export ASDF_INSTALL_TYPE=version\n  export ASDF_INSTALL_PATH=/somewhere\n\n  plugin_path=\"$(get_plugin_path dummy)\"\n\n  # this plugin defines a new `asdf dummy` command\n  cat <<'EOF' >\"$plugin_path/lib/commands/command\"\n#!/usr/bin/env bash\n/usr/bin/env\nEOF\n  chmod +x \"$plugin_path/lib/commands/command\"\n\n  run asdf cmd dummy\n  [ \"$status\" -eq 0 ]\n  [ \"0\" -eq \"$(echo \"$output\" | grep -c \"^ASDF_INSTALL_\")\" ]\n}\n"
  },
  {
    "path": "test/plugin_list_all_command.bats",
    "content": "#!/usr/bin/env bats\n# shellcheck disable=SC2030,SC2031\n\nload test_helpers\n\nsetup() {\n  setup_asdf_dir\n  setup_repo\n  install_dummy_plugin\n}\n\nteardown() {\n  clean_asdf_dir\n}\n\n@test \"plugin_list_all should exit before syncing the plugin repo if disabled\" {\n  export ASDF_CONFIG_DEFAULT_FILE=\"$HOME/.asdfrc\"\n  echo 'disable_plugin_short_name_repository=yes' >\"$ASDF_CONFIG_DEFAULT_FILE\"\n  local expected=\"Short-name plugin repository is disabled\"\n\n  run asdf plugin list all\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"$expected\" ]\n}\n\n@test \"plugin_list_all should sync repo when check_duration set to 0\" {\n  export ASDF_CONFIG_DEFAULT_FILE=\"$HOME/.asdfrc\"\n  echo 'plugin_repository_last_check_duration = 0' >\"$ASDF_CONFIG_DEFAULT_FILE\"\n  local expected_plugins_list=\"\\\nbar                           http://example.com/bar\ndummy                         http://example.com/dummy\nfoo                           http://example.com/foo\"\n\n  run asdf plugin list all\n  [ \"$status\" -eq 0 ]\n  [[ \"$output\" == *\"$expected_plugins_list\"* ]]\n}\n\n@test \"plugin_list_all no immediate repo sync expected because check_duration is greater than 0\" {\n  export ASDF_CONFIG_DEFAULT_FILE=\"$HOME/.asdfrc\"\n  echo 'plugin_repository_last_check_duration = 10' >\"$ASDF_CONFIG_DEFAULT_FILE\"\n  local expected=\"\\\nbar                           http://example.com/bar\ndummy                         http://example.com/dummy\nfoo                           http://example.com/foo\"\n\n  run asdf plugin list all\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"$expected\" ]\n}\n\n@test \"plugin_list_all skips repo sync because check_duration is set to never\" {\n  export ASDF_CONFIG_DEFAULT_FILE=\"$HOME/.asdfrc\"\n  echo 'plugin_repository_last_check_duration = never' >\"$ASDF_CONFIG_DEFAULT_FILE\"\n  local expected=\"\\\nbar                           http://example.com/bar\ndummy                         http://example.com/dummy\nfoo                           http://example.com/foo\"\n\n  run asdf plugin list all\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"$expected\" ]\n}\n\n@test \"plugin_list_all list all plugins in the repository\" {\n  local expected=\"\\\nbar                           http://example.com/bar\ndummy                         http://example.com/dummy\nfoo                           http://example.com/foo\"\n\n  run asdf plugin list all\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"$expected\" ]\n}\n"
  },
  {
    "path": "test/plugin_remove_command.bats",
    "content": "#!/usr/bin/env bats\n\nload test_helpers\n\nsetup() {\n  setup_asdf_dir\n  install_dummy_plugin\n}\n\nteardown() {\n  clean_asdf_dir\n}\n\n@test \"plugin_remove command removes the plugin directory\" {\n  run asdf install dummy 1.0\n  [ \"$status\" -eq 0 ]\n  [ -d \"$ASDF_DIR/downloads/dummy\" ]\n\n  run asdf plugin remove \"dummy\"\n  [ \"$status\" -eq 0 ]\n  [ ! -d \"$ASDF_DIR/downloads/dummy\" ]\n}\n\n@test \"plugin_remove command fails if the plugin doesn't exist\" {\n  run asdf plugin remove \"does-not-exist\"\n  [ \"$status\" -eq 1 ]\n  echo \"$output\" | grep \"No such plugin: does-not-exist\"\n}\n"
  },
  {
    "path": "test/plugin_test_command.bats",
    "content": "#!/usr/bin/env bats\n\nload test_helpers\n\nsetup() {\n  setup_asdf_dir\n  install_mock_plugin_repo \"dummy\"\n}\n\nteardown() {\n  clean_asdf_dir\n}\n\n@test \"plugin_test_command with no URL specified prints an error\" {\n  run asdf plugin test \"elixir\"\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"FAILED: please provide a plugin name and url\" ]\n}\n\n@test \"plugin_test_command with no name or URL specified prints an error\" {\n  run asdf plugin test\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"FAILED: please provide a plugin name and url\" ]\n}\n\n@test \"plugin_test_command works with no options provided\" {\n  run asdf plugin test dummy \"${BASE_DIR}/repo-dummy\"\n  [ \"$status\" -eq 0 ]\n}\n\n@test \"plugin_test_command works with all options provided\" {\n  run asdf plugin test dummy \"${BASE_DIR}/repo-dummy\" --asdf-tool-version 1.0.0 --asdf-plugin-gitref master\n  [ \"$status\" -eq 0 ]\n}\n"
  },
  {
    "path": "test/plugin_update_command.bats",
    "content": "#!/usr/bin/env bats\n\nload test_helpers\n\nsetup() {\n  setup_asdf_dir\n  install_mock_plugin_repo \"dummy\"\n  run asdf plugin add \"dummy\" \"${BASE_DIR}/repo-dummy\"\n}\n\nteardown() {\n  clean_asdf_dir\n}\n\n@test \"asdf plugin update should pull latest default branch (refs/remotes/origin/HEAD) for plugin\" {\n  run asdf plugin update dummy\n  repo_head=\"$(git --git-dir \"$ASDF_DIR/plugins/dummy/.git\" --work-tree \"$ASDF_DIR/plugins/dummy\" rev-parse --abbrev-ref HEAD)\"\n  [ \"$status\" -eq 0 ]\n  [[ \"$output\" =~ \"updated dummy to ref refs/heads/master\"* ]]\n  [ \"$repo_head\" = \"master\" ]\n}\n\n#@test \"asdf plugin update should pull latest default branch (refs/remotes/origin/HEAD) for plugin even if default branch changes\" {\n#  install_mock_plugin_repo \"dummy-remote\"\n#  remote_dir=\"$BASE_DIR/repo-dummy-remote\"\n#  # set HEAD to refs/head/main in dummy-remote\n#  git -C \"${remote_dir}\" checkout -b main\n#  # track & fetch remote repo (dummy-remote) in plugin (dummy)\n#  git --git-dir \"$ASDF_DIR/plugins/dummy/.git\" --work-tree \"$ASDF_DIR/plugins/dummy\" remote remove origin\n#  git --git-dir \"$ASDF_DIR/plugins/dummy/.git\" --work-tree \"$ASDF_DIR/plugins/dummy\" remote add origin \"$remote_dir\"\n#  git --git-dir \"$ASDF_DIR/plugins/dummy/.git\" --work-tree \"$ASDF_DIR/plugins/dummy\" fetch origin\n\n#  run asdf plugin update dummy\n#  repo_head=\"$(git --git-dir \"$ASDF_DIR/plugins/dummy/.git\" --work-tree \"$ASDF_DIR/plugins/dummy\" rev-parse --abbrev-ref HEAD)\"\n\n#  [ \"$status\" -eq 0 ]\n#  [[ \"$output\" =~ \"Updating dummy to main\"* ]]\n#  [ \"$repo_head\" = \"main\" ]\n#}\n\n#@test \"asdf plugin update should pull latest default branch (refs/remotes/origin/HEAD) for plugin even if the default branch contains a forward slash\" {\n#  install_mock_plugin_repo \"dummy-remote\"\n#  remote_dir=\"$BASE_DIR/repo-dummy-remote\"\n#  # set HEAD to refs/head/my/default in dummy-remote\n#  git -C \"${remote_dir}\" checkout -b my/default\n#  # track & fetch remote repo (dummy-remote) in plugin (dummy)\n#  git --git-dir \"$ASDF_DIR/plugins/dummy/.git\" --work-tree \"$ASDF_DIR/plugins/dummy\" remote remove origin\n#  git --git-dir \"$ASDF_DIR/plugins/dummy/.git\" --work-tree \"$ASDF_DIR/plugins/dummy\" remote add origin \"$remote_dir\"\n#  git --git-dir \"$ASDF_DIR/plugins/dummy/.git\" --work-tree \"$ASDF_DIR/plugins/dummy\" fetch origin\n\n#  run asdf plugin update dummy\n#  repo_head=\"$(git --git-dir \"$ASDF_DIR/plugins/dummy/.git\" --work-tree \"$ASDF_DIR/plugins/dummy\" rev-parse --abbrev-ref HEAD)\"\n\n#  [ \"$status\" -eq 0 ]\n#  [[ \"$output\" =~ \"Updating dummy to my/default\"* ]]\n#  [ \"$repo_head\" = \"my/default\" ]\n#}\n\n#@test \"asdf plugin update should pull latest default branch (refs/remotes/origin/HEAD) for plugin even if already set to specific ref\" {\n#  # set plugin to specific sha\n#  current_sha=\"$(git --git-dir \"${BASE_DIR}/repo-dummy/.git\" --work-tree \"$BASE_DIR/repo-dummy\" rev-parse HEAD)\"\n#  run asdf plugin update dummy \"${current_sha}\"\n\n#  # setup mock plugin remote\n#  install_mock_plugin_repo \"dummy-remote\"\n#  remote_dir=\"$BASE_DIR/repo-dummy-remote\"\n#  # set HEAD to refs/head/main in dummy-remote\n#  git -C \"${remote_dir}\" checkout -b main\n#  # track & fetch remote repo (dummy-remote) in plugin (dummy)\n#  git --git-dir \"$ASDF_DIR/plugins/dummy/.git\" --work-tree \"$ASDF_DIR/plugins/dummy\" remote remove origin\n#  git --git-dir \"$ASDF_DIR/plugins/dummy/.git\" --work-tree \"$ASDF_DIR/plugins/dummy\" remote add origin \"$remote_dir\"\n#  git --git-dir \"$ASDF_DIR/plugins/dummy/.git\" --work-tree \"$ASDF_DIR/plugins/dummy\" fetch origin\n\n#  # update plugin to the default branch\n#  run asdf plugin update dummy\n#  repo_head=\"$(git --git-dir \"$ASDF_DIR/plugins/dummy/.git\" --work-tree \"$ASDF_DIR/plugins/dummy\" rev-parse --abbrev-ref HEAD)\"\n\n#  [ \"$status\" -eq 0 ]\n#  [[ \"$output\" =~ \"Updating dummy to main\"* ]]\n#  [ \"$repo_head\" = \"main\" ]\n#}\n\n@test \"asdf plugin update should not remove plugin versions\" {\n  run asdf install dummy 1.1\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$ASDF_DIR/installs/dummy/1.1/version\")\" = \"1.1\" ]\n  run asdf plugin update dummy\n  [ \"$status\" -eq 0 ]\n  [ -f \"$ASDF_DIR/installs/dummy/1.1/version\" ]\n  run asdf plugin update --all\n  [ \"$status\" -eq 0 ]\n  [ -f \"$ASDF_DIR/installs/dummy/1.1/version\" ]\n}\n\n@test \"asdf plugin update should not remove plugins\" {\n  # dummy plugin is already installed\n  run asdf plugin update dummy\n  [ \"$status\" -eq 0 ]\n  [ -d \"$ASDF_DIR/plugins/dummy\" ]\n  run asdf plugin update --all\n  [ \"$status\" -eq 0 ]\n  [ -d \"$ASDF_DIR/plugins/dummy\" ]\n}\n\n@test \"asdf plugin update should not remove shims\" {\n  run asdf install dummy 1.1\n  [ -f \"$ASDF_DIR/shims/dummy\" ]\n  run asdf plugin update dummy\n  [ \"$status\" -eq 0 ]\n  [ -f \"$ASDF_DIR/shims/dummy\" ]\n  run asdf plugin update --all\n  [ \"$status\" -eq 0 ]\n  [ -f \"$ASDF_DIR/shims/dummy\" ]\n}\n\n# TODO: Get these tests passing\n#@test \"asdf plugin update done for all plugins\" {\n#  local command=\"asdf plugin update --all\"\n#  # Count the number of update processes remaining after the update command is completed.\n#  run bash -c \"${command} >/dev/null && ps -o 'ppid,args' | awk '{if(\\$1==1 && \\$0 ~ /${command}/ ) print}' | wc -l\"\n#  [[ 0 -eq \"$output\" ]]\n#}\n\n#@test \"asdf plugin update executes post-plugin update script\" {\n#  local plugin_path\n#  plugin_path=\"$(get_plugin_path dummy)\"\n\n#  old_ref=\"$(git --git-dir \"$plugin_path/.git\" --work-tree \"$plugin_path\" rev-parse --short HEAD)\"\n#  run asdf plugin update dummy\n#  new_ref=\"$(git --git-dir \"$plugin_path/.git\" --work-tree \"$plugin_path\" rev-parse --short HEAD)\"\n\n#  local expected_output=\"plugin updated path=${plugin_path} old git-ref=${old_ref} new git-ref=${new_ref}\"\n#  [[ \"$output\" = *\"${expected_output}\" ]]\n#}\n\n#@test \"asdf plugin update executes post-plugin update script if git-ref updated\" {\n#  local plugin_path\n#  plugin_path=\"$(get_plugin_path dummy)\"\n\n#  old_ref=\"$(git --git-dir \"$plugin_path/.git\" --work-tree \"$plugin_path\" rev-parse --short HEAD)\"\n\n#  # setup mock plugin remote\n#  install_mock_plugin_repo \"dummy-remote\"\n#  remote_dir=\"$BASE_DIR/repo-dummy-remote\"\n#  # set HEAD to refs/head/main in dummy-remote\n#  git -C \"${remote_dir}\" checkout -b main\n#  # track & fetch remote repo (dummy-remote) in plugin (dummy)\n#  git --git-dir \"$plugin_path/.git\" --work-tree \"$plugin_path\" remote remove origin\n#  git --git-dir \"$plugin_path/.git\" --work-tree \"$plugin_path\" remote add origin \"$remote_dir\"\n#  git --git-dir \"$plugin_path/.git\" --work-tree \"$plugin_path\" fetch origin\n\n#  # update plugin to the default branch\n#  run asdf plugin update dummy\n#  new_ref=\"$(git --git-dir \"$plugin_path/.git\" --work-tree \"$plugin_path\" rev-parse --short HEAD)\"\n\n#  local expected_output=\"plugin updated path=${plugin_path} old git-ref=${old_ref} new git-ref=${new_ref}\"\n#  [[ \"$output\" = *\"${expected_output}\" ]]\n#}\n\n#@test \"asdf plugin update executes configured pre hook (generic)\" {\n#  cat >\"$HOME/.asdfrc\" <<-'EOM'\n#pre_asdf_plugin_update = echo UPDATE ${@}\n#EOM\n\n#  local plugin_path\n#  plugin_path=\"$(get_plugin_path dummy)\"\n\n#  old_ref=\"$(git --git-dir \"$plugin_path/.git\" --work-tree \"$plugin_path\" rev-parse --short HEAD)\"\n#  run asdf plugin update dummy\n#  new_ref=\"$(git --git-dir \"$plugin_path/.git\" --work-tree \"$plugin_path\" rev-parse --short HEAD)\"\n\n#  local expected_output=\"plugin updated path=${plugin_path} old git-ref=${old_ref} new git-ref=${new_ref}\"\n#  [[ \"$output\" = *\"UPDATE dummy\"*\"${expected_output}\" ]]\n#}\n\n#@test \"asdf plugin update executes configured pre hook (specific)\" {\n#  cat >\"$HOME/.asdfrc\" <<-'EOM'\n#pre_asdf_plugin_update_dummy = echo UPDATE\n#EOM\n\n#  local plugin_path\n#  plugin_path=\"$(get_plugin_path dummy)\"\n\n#  old_ref=\"$(git --git-dir \"$plugin_path/.git\" --work-tree \"$plugin_path\" rev-parse --short HEAD)\"\n#  run asdf plugin update dummy\n#  new_ref=\"$(git --git-dir \"$plugin_path/.git\" --work-tree \"$plugin_path\" rev-parse --short HEAD)\"\n\n#  local expected_output=\"plugin updated path=${plugin_path} old git-ref=${old_ref} new git-ref=${new_ref}\"\n#  [[ \"$output\" = *\"UPDATE\"*\"${expected_output}\" ]]\n#}\n\n#@test \"asdf plugin update executes configured post hook (generic)\" {\n#  cat >\"$HOME/.asdfrc\" <<-'EOM'\n#post_asdf_plugin_update = echo UPDATE ${@}\n#EOM\n\n#  local plugin_path\n#  plugin_path=\"$(get_plugin_path dummy)\"\n\n#  old_ref=\"$(git --git-dir \"$plugin_path/.git\" --work-tree \"$plugin_path\" rev-parse --short HEAD)\"\n#  run asdf plugin update dummy\n#  new_ref=\"$(git --git-dir \"$plugin_path/.git\" --work-tree \"$plugin_path\" rev-parse --short HEAD)\"\n\n#  local expected_output=\"plugin updated path=${plugin_path} old git-ref=${old_ref} new git-ref=${new_ref}\n#UPDATE dummy\"\n#  [[ \"$output\" = *\"${expected_output}\" ]]\n#}\n\n#@test \"asdf plugin update executes configured post hook (specific)\" {\n#  cat >\"$HOME/.asdfrc\" <<-'EOM'\n#post_asdf_plugin_update_dummy = echo UPDATE\n#EOM\n\n#  local plugin_path\n#  plugin_path=\"$(get_plugin_path dummy)\"\n\n#  old_ref=\"$(git --git-dir \"$plugin_path/.git\" --work-tree \"$plugin_path\" rev-parse --short HEAD)\"\n#  run asdf plugin update dummy\n#  new_ref=\"$(git --git-dir \"$plugin_path/.git\" --work-tree \"$plugin_path\" rev-parse --short HEAD)\"\n\n#  local expected_output=\"plugin updated path=${plugin_path} old git-ref=${old_ref} new git-ref=${new_ref}\n#UPDATE\n#updated dummy to ref refs/heads/master\"\n#  [[ \"$output\" = *\"${expected_output}\" ]]\n#}\n\n# No longer supported\n#@test \"asdf plugin update prints the location of plugin (specific)\" {\n#  local plugin_path\n#  plugin_path=\"$(get_plugin_path dummy)\"\n#  run asdf plugin update dummy\n\n#  local expected_output=\"Location of dummy plugin: $plugin_path\"\n#  [[ \"$output\" == *\"$expected_output\"* ]]\n#}\n"
  },
  {
    "path": "test/remove_command.bats",
    "content": "#!/usr/bin/env bats\n\nload test_helpers\n\nsetup() {\n  setup_asdf_dir\n}\n\nteardown() {\n  clean_asdf_dir\n}\n\n@test \"plugin_remove_command removes a plugin\" {\n  install_dummy_plugin\n\n  run asdf plugin remove \"dummy\"\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"plugin-remove ${ASDF_DIR}/plugins/dummy\" ]\n}\n\n@test \"plugin_remove_command should exit with 1 when not passed any arguments\" {\n  run asdf plugin remove\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"No plugin given\" ]\n}\n\n@test \"plugin_remove_command should exit with 1 when passed invalid plugin name\" {\n  run asdf plugin remove \"does-not-exist\"\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"No such plugin: does-not-exist\" ]\n}\n\n@test \"plugin_remove_command should remove installed versions\" {\n  install_dummy_plugin\n  run asdf install dummy 1.0\n  [ \"$status\" -eq 0 ]\n  [ -d \"$ASDF_DIR/installs/dummy\" ]\n\n  run asdf plugin remove dummy\n  [ \"$status\" -eq 0 ]\n  [ ! -d \"$ASDF_DIR/installs/dummy\" ]\n}\n\n@test \"plugin_remove_command should also remove shims for that plugin\" {\n  install_dummy_plugin\n  run asdf install dummy 1.0\n  [ \"$status\" -eq 0 ]\n  [ -f \"$ASDF_DIR/shims/dummy\" ]\n\n  run asdf plugin remove dummy\n  [ \"$status\" -eq 0 ]\n  [ ! -f \"$ASDF_DIR/shims/dummy\" ]\n}\n\n# Disabled this test because it while the title is correct, the test code sets\n# and invalid state (shim unattached to any existing plugin) that is corrected\n# by asdf reshim removing the invalid shim, and that fails this test, even\n# though it's the correct behavior\n#@test \"plugin_remove_command should not remove unrelated shims\" {\n#  install_dummy_plugin\n#  run asdf install dummy 1.0\n\n#  # make an unrelated shim\n#  echo \"# asdf-plugin: gummy\" >\"$ASDF_DIR/shims/gummy\"\n\n#  run asdf plugin remove dummy\n#  [ \"$status\" -eq 0 ]\n\n#  # unrelated shim should exist\n#  [ -f \"$ASDF_DIR/shims/gummy\" ]\n#}\n\n@test \"plugin_remove_command executes pre-plugin-remove script\" {\n  install_dummy_plugin\n\n  run asdf plugin remove dummy\n\n  [ \"$output\" = \"plugin-remove ${ASDF_DIR}/plugins/dummy\" ]\n}\n\n@test \"plugin_remove_command executes configured pre hook (generic)\" {\n  install_dummy_plugin\n\n  cat >\"$HOME/.asdfrc\" <<-'EOM'\npre_asdf_plugin_remove = echo REMOVE ${@}\nEOM\n\n  run asdf plugin remove dummy\n\n  local expected_output=\"REMOVE dummy\nplugin-remove ${ASDF_DIR}/plugins/dummy\"\n  [ \"$output\" = \"${expected_output}\" ]\n}\n\n@test \"plugin_remove_command executes configured pre hook (specific)\" {\n  install_dummy_plugin\n\n  cat >\"$HOME/.asdfrc\" <<-'EOM'\npre_asdf_plugin_remove_dummy = echo REMOVE\nEOM\n\n  run asdf plugin remove dummy\n\n  local expected_output=\"REMOVE\nplugin-remove ${ASDF_DIR}/plugins/dummy\"\n  [ \"$output\" = \"${expected_output}\" ]\n}\n\n@test \"plugin_remove_command executes configured post hook (generic)\" {\n  install_dummy_plugin\n\n  cat >\"$HOME/.asdfrc\" <<-'EOM'\npost_asdf_plugin_remove = echo REMOVE ${@}\nEOM\n\n  run asdf plugin remove dummy\n\n  local expected_output=\"plugin-remove ${ASDF_DIR}/plugins/dummy\nREMOVE dummy\"\n  [ \"$output\" = \"${expected_output}\" ]\n}\n\n@test \"plugin_remove_command executes configured post hook (specific)\" {\n  install_dummy_plugin\n\n  cat >\"$HOME/.asdfrc\" <<-'EOM'\npost_asdf_plugin_remove_dummy = echo REMOVE\nEOM\n\n  run asdf plugin remove dummy\n\n  local expected_output=\"plugin-remove ${ASDF_DIR}/plugins/dummy\nREMOVE\"\n  [ \"$output\" = \"${expected_output}\" ]\n}\n"
  },
  {
    "path": "test/reshim_command.bats",
    "content": "#!/usr/bin/env bats\n# shellcheck disable=SC2012\n\nload test_helpers\n\nsetup() {\n  setup_asdf_dir\n  install_dummy_plugin\n\n  PROJECT_DIR=\"$HOME/project\"\n  mkdir \"$PROJECT_DIR\"\n}\n\nteardown() {\n  clean_asdf_dir\n}\n\n@test \"reshim should print error when plugin with name does not exist\" {\n  run asdf reshim non-existent 1.0\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"No such plugin: non-existent\" ]\n}\n\n@test \"reshim should allow prefixes of other versions\" {\n  run asdf install dummy 1.0.1\n  run asdf install dummy 1.0\n\n  run asdf reshim\n  [ \"$status\" -eq 0 ]\n\n  run grep \"asdf-plugin: dummy 1.0.1\" \"$ASDF_DIR/shims/dummy\"\n  [ \"$status\" -eq 0 ]\n  run grep \"asdf-plugin: dummy 1.0\\$\" \"$ASDF_DIR/shims/dummy\"\n  [ \"$status\" -eq 0 ]\n}\n\n@test \"reshim command should remove shims of removed binaries\" {\n  run asdf install dummy 1.0\n  [ \"$status\" -eq 0 ]\n  [ -f \"$ASDF_DIR/shims/dummy\" ]\n\n  run asdf reshim dummy\n  [ \"$status\" -eq 0 ]\n  [ -f \"$ASDF_DIR/shims/dummy\" ]\n\n  run rm \"$ASDF_DIR/installs/dummy/1.0/bin/dummy\"\n  run asdf reshim dummy\n  [ \"$status\" -eq 0 ]\n  [ ! -f \"$ASDF_DIR/shims/dummy\" ]\n}\n\n@test \"reshim should remove metadata of removed binaries\" {\n  run asdf install dummy 1.0\n  run asdf install dummy 1.1\n\n  run rm \"$ASDF_DIR/installs/dummy/1.0/bin/dummy\"\n  run asdf reshim dummy\n  [ \"$status\" -eq 0 ]\n  [ -f \"$ASDF_DIR/shims/dummy\" ]\n  run grep \"asdf-plugin: dummy 1.0\" \"$ASDF_DIR/shims/dummy\"\n  [ \"$status\" -eq 1 ]\n  run grep \"asdf-plugin: dummy 1.1\" \"$ASDF_DIR/shims/dummy\"\n  [ \"$status\" -eq 0 ]\n}\n\n@test \"reshim should not remove metadata of removed prefix versions\" {\n  run asdf install dummy 1.0\n  run asdf install dummy 1.0.1\n  run rm \"$ASDF_DIR/installs/dummy/1.0/bin/dummy\"\n  run asdf reshim dummy\n  [ \"$status\" -eq 0 ]\n  [ -f \"$ASDF_DIR/shims/dummy\" ]\n  run grep \"asdf-plugin: dummy 1.0.1\" \"$ASDF_DIR/shims/dummy\"\n  [ \"$status\" -eq 0 ]\n}\n\n@test \"reshim should not duplicate shims\" {\n  cd \"$PROJECT_DIR\"\n\n  run asdf install dummy 1.0\n  run asdf install dummy 1.1\n  [ \"$status\" -eq 0 ]\n  [ -f \"$ASDF_DIR/shims/dummy\" ]\n\n  run rm \"$ASDF_DIR/shims/\"*\n  [ \"$status\" -eq 0 ]\n  [ \"0\" -eq \"$(ls \"$ASDF_DIR/shims/\"dummy* | wc -l)\" ]\n\n  run asdf reshim dummy\n  [ \"$status\" -eq 0 ]\n  [ \"1\" -eq \"$(ls \"$ASDF_DIR/shims/\"dummy* | wc -l)\" ]\n\n  run asdf reshim dummy\n  [ \"$status\" -eq 0 ]\n  [ \"1\" -eq \"$(ls \"$ASDF_DIR/shims/\"dummy* | wc -l)\" ]\n}\n\n@test \"reshim should create shims only for files and not folders\" {\n  cd \"$PROJECT_DIR\"\n\n  run asdf install dummy 1.0\n  run asdf install dummy 1.1\n  [ \"$status\" -eq 0 ]\n  [ -f \"$ASDF_DIR/shims/dummy\" ]\n  [ ! -f \"$ASDF_DIR/shims/subdir\" ]\n\n  run rm \"$ASDF_DIR/shims/\"*\n  [ \"$status\" -eq 0 ]\n  [ \"0\" -eq \"$(ls \"$ASDF_DIR/shims/\"dummy* | wc -l)\" ]\n  [ \"0\" -eq \"$(ls \"$ASDF_DIR/shims/\"subdir* | wc -l)\" ]\n\n  run asdf reshim dummy\n  [ \"$status\" -eq 0 ]\n  [ \"1\" -eq \"$(ls \"$ASDF_DIR/shims/\"dummy* | wc -l)\" ]\n  [ \"0\" -eq \"$(ls \"$ASDF_DIR/shims/\"subdir* | wc -l)\" ]\n\n}\n\n@test \"reshim without arguments reshims all installed plugins\" {\n  run asdf install dummy 1.0\n  run rm \"$ASDF_DIR/shims/\"*\n  [ \"$status\" -eq 0 ]\n  [ \"0\" -eq \"$(ls \"$ASDF_DIR/shims/\"dummy* | wc -l)\" ]\n  run asdf reshim\n  [ \"$status\" -eq 0 ]\n  [ \"1\" -eq \"$(ls \"$ASDF_DIR/shims/\"dummy* | wc -l)\" ]\n}\n\n@test \"reshim command executes configured pre hook\" {\n  run asdf install dummy 1.0\n\n  cat >\"$HOME/.asdfrc\" <<-'EOM'\npre_asdf_reshim_dummy = echo RESHIM\nEOM\n\n  run asdf reshim dummy 1.0\n  [ \"$output\" = \"RESHIM\" ]\n}\n\n@test \"reshim command executes configured post hook\" {\n  run asdf install dummy 1.0\n\n  cat >\"$HOME/.asdfrc\" <<-'EOM'\npost_asdf_reshim_dummy = echo RESHIM\nEOM\n\n  run asdf reshim dummy 1.0\n  [ \"$output\" = \"RESHIM\" ]\n}\n\n# Fixes https://github.com/asdf-vm/asdf/issues/1115\n# (Issue with executable_name changing after homebre updates)\n@test \"reshim should rewrite the shim file except the version list\" {\n  run asdf install dummy 1.0\n  local dummy_shim\n  dummy_shim=\"$ASDF_DIR/shims/dummy\"\n\n  sed -i.bak -e 's/exec /exec \\/borked_path_due_to_homebrew_update/' \"$dummy_shim\"\n  run grep 'borked_path_due_to_homebrew_update' \"$dummy_shim\" # Sanity check\n  [ \"$status\" -eq 0 ]\n\n  run asdf reshim dummy \"path:$ASDF_DIR/installs/dummy/path\"\n  run grep -v 'borked_path_due_to_homebrew_update' \"$dummy_shim\"\n  [ \"$status\" -eq 0 ]\n}\n\n@test \"reshim should allow local path versions\" {\n  run asdf install dummy 1.0\n\n  mkdir -p \"$ASDF_DIR/installs/dummy/path/bin/\"\n  touch \"$ASDF_DIR/installs/dummy/path/bin/dummy\"\n  chmod +x \"$ASDF_DIR/installs/dummy/path/bin/dummy\"\n\n  run asdf reshim dummy \"path:$ASDF_DIR/installs/dummy/path\"\n\n  [ \"$status\" -eq 0 ]\n  run grep \"asdf-plugin: dummy 1.0\" \"$ASDF_DIR/shims/dummy\"\n  [ \"$status\" -eq 0 ]\n  run grep \"asdf-plugin: dummy path:$ASDF_DIR/installs/dummy\" \"$ASDF_DIR/shims/dummy\"\n  [ \"$status\" -eq 0 ]\n}\n"
  },
  {
    "path": "test/setup_suite.bash",
    "content": "setup_suite() {\n  # Unset ASDF_DIR because it may already be set by the users shell, and some\n  # tests fail when it is set to something other than the temp dir.\n  unset ASDF_DIR\n\n  # Also unset below variables, because in users shell shimmed commands\n  # (include bats) export them by determining user's real HOME.\n  unset ASDF_DATA_DIR\n  unset ASDF_CONFIG_FILE\n\n  # Set an agnostic Git configuration directory to prevent personal\n  # configuration from interfering with the tests\n  export GIT_CONFIG_GLOBAL=/dev/null\n}\n"
  },
  {
    "path": "test/shim_env_command.bats",
    "content": "#!/usr/bin/env bats\n# shellcheck disable=SC2164\n\nload test_helpers\n\nsetup() {\n  setup_asdf_dir\n  install_dummy_plugin\n\n  PROJECT_DIR=\"$HOME/project\"\n  mkdir -p \"$PROJECT_DIR\"\n  cd \"$PROJECT_DIR\"\n\n  # asdf lib needed to run generated shims\n  cp -rf \"$BATS_TEST_DIRNAME\"/../{bin,lib} \"$ASDF_DIR/\"\n}\n\nteardown() {\n  clean_asdf_dir\n}\n\n@test \"asdf env without argument should display help\" {\n  run asdf env\n  [ \"$status\" -eq 1 ]\n  echo \"$output\" | grep \"usage: asdf env <command>\"\n}\n\n@test \"asdf env should execute under the environment used for a shim\" {\n  echo \"dummy 1.0\" >\"$PROJECT_DIR/.tool-versions\"\n  run asdf install\n\n  run asdf env dummy which dummy\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"$ASDF_DIR/installs/dummy/1.0/bin/dummy\" ]\n}\n\n@test \"asdf env should execute under plugin custom environment used for a shim\" {\n  echo \"dummy 1.0\" >\"$PROJECT_DIR/.tool-versions\"\n  run asdf install\n\n  echo '#!/usr/bin/env bash\n  export FOO=bar' >\"$ASDF_DIR/plugins/dummy/bin/exec-env\"\n  chmod +x \"$ASDF_DIR/plugins/dummy/bin/exec-env\"\n\n  run asdf env dummy\n  [ \"$status\" -eq 0 ]\n  echo \"$output\" | grep 'FOO=bar'\n}\n\n@test \"asdf env should pass through any already defined environment variables to a shim\" {\n  echo \"dummy 1.0\" >\"$PROJECT_DIR/.tool-versions\"\n  export MYTESTENV=exec-env-pass-through\n  run asdf install\n\n  echo '#!/usr/bin/env bash\n  export FOO=bar' >\"$ASDF_DIR/plugins/dummy/bin/exec-env\"\n  chmod +x \"$ASDF_DIR/plugins/dummy/bin/exec-env\"\n\n  run asdf env dummy\n  [ \"$status\" -eq 0 ]\n  echo \"$output\" | grep 'FOO=bar'\n  echo \"$output\" | grep 'MYTESTENV=exec-env-pass-through'\n}\n\n@test \"asdf env should print error when plugin version lacks the specified executable\" {\n  echo \"dummy 1.0\" >\"$PROJECT_DIR/.tool-versions\"\n  run asdf install\n\n  echo '#!/usr/bin/env bash\n  export FOO=bar' >\"$ASDF_DIR/plugins/dummy/bin/exec-env\"\n  chmod +x \"$ASDF_DIR/plugins/dummy/bin/exec-env\"\n\n  echo \"dummy system\" >\"$PROJECT_DIR/.tool-versions\"\n\n  run asdf env dummy\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"No executable dummy found for current version. Please select a different version or install dummy manually for the current version\" ]\n}\n\n@test \"asdf env should ignore plugin custom environment on system version\" {\n  echo \"dummy 1.0\" >\"$PROJECT_DIR/.tool-versions\"\n  run asdf install\n\n  echo '#!/usr/bin/env bash\n  export FOO=bar' >\"$ASDF_DIR/plugins/dummy/bin/exec-env\"\n  chmod +x \"$ASDF_DIR/plugins/dummy/bin/exec-env\"\n\n  # Create a \"system\" dummy executable\n  echo '#!/usr/bin/env bash\n  echo \"system dummy\"' >\"$ASDF_BIN/dummy\"\n  chmod +x \"$ASDF_BIN/dummy\"\n\n  echo \"dummy system\" >\"$PROJECT_DIR/.tool-versions\"\n\n  run asdf env dummy\n  [ \"$status\" -eq 0 ]\n\n  run grep 'FOO=bar' <<<\"$output\"\n  [ \"$output\" = \"\" ]\n  [ \"$status\" -eq 1 ]\n\n  run asdf env dummy which dummy\n  [ \"$output\" = \"$ASDF_BIN/dummy\" ]\n  [ \"$status\" -eq 0 ]\n  # Remove \"system\" dummy executable\n  rm \"$ASDF_BIN/dummy\"\n}\n\n@test \"asdf env should set PATH correctly\" {\n  echo \"dummy 1.0\" >\"$PROJECT_DIR/.tool-versions\"\n  run asdf install\n\n  run asdf env dummy\n  [ \"$status\" -eq 0 ]\n\n  # Should set path\n  path_line=$(echo \"$output\" | grep '^PATH=')\n  [ \"$path_line\" != \"\" ]\n\n  # Should not contain duplicate colon\n  run grep -q '::' <<<\"$path_line\"\n  [ \"$status\" -ne 0 ]\n}\n"
  },
  {
    "path": "test/shim_exec.bats",
    "content": "#!/usr/bin/env bats\n# shellcheck disable=SC2016,SC2164\n\nload test_helpers\n\nsetup() {\n  setup_asdf_dir\n  install_dummy_plugin\n\n  PROJECT_DIR=\"$HOME/project\"\n  mkdir -p \"$PROJECT_DIR\"\n  cd \"$PROJECT_DIR\"\n\n  # asdf lib needed to run generated shims\n  cp -rf \"$BATS_TEST_DIRNAME\"/../{bin,lib} \"$ASDF_DIR/\"\n}\n\nteardown() {\n  clean_asdf_dir\n}\n\n@test \"asdf exec without argument should display help\" {\n  run asdf exec\n  [ \"$status\" -eq 1 ]\n  echo \"$output\" | grep \"usage: asdf exec <command>\"\n}\n\n@test \"asdf exec should pass all arguments to executable\" {\n  echo \"dummy 1.0\" >\"$PROJECT_DIR/.tool-versions\"\n  run asdf install\n\n  run asdf exec dummy world hello\n  [ \"$output\" = \"This is Dummy 1.0! hello world\" ]\n  [ \"$status\" -eq 0 ]\n}\n\n@test \"asdf exec should pass all arguments including flags to executable\" {\n  echo \"dummy 1.0\" >\"$PROJECT_DIR/.tool-versions\"\n  run asdf install\n\n  run asdf exec dummy --mytestflag hello\n  [ \"$output\" = \"This is Dummy 1.0! hello --mytestflag\" ]\n  [ \"$status\" -eq 0 ]\n}\n\n@test \"asdf exec should pass all arguments to executable even if shim is not in PATH\" {\n  echo \"dummy 1.0\" >\"$PROJECT_DIR/.tool-versions\"\n  run asdf install\n\n  path=$(echo \"$PATH\" | sed -e \"s|$(asdf_data_dir)/shims||g; s|::|:|g\")\n  run env PATH=\"$path\" which dummy\n  [ \"$output\" = \"\" ]\n  [ \"$status\" -eq 1 ]\n\n  run env PATH=\"$path\" asdf exec dummy world hello\n  [ \"$output\" = \"This is Dummy 1.0! hello world\" ]\n  [ \"$status\" -eq 0 ]\n}\n\n@test \"shim exec should pass all arguments to executable\" {\n  echo \"dummy 1.0\" >\"$PROJECT_DIR/.tool-versions\"\n  run asdf install\n\n  run \"$ASDF_DIR/shims/dummy\" world hello\n  [ \"$output\" = \"This is Dummy 1.0! hello world\" ]\n  [ \"$status\" -eq 0 ]\n}\n\n@test \"shim exec should pass stdin to executable\" {\n  echo \"dummy 1.0\" >\"$PROJECT_DIR/.tool-versions\"\n  run asdf install\n\n  echo \"#!/usr/bin/env bash\n  tr [:lower:] [:upper:]\" >\"$ASDF_DIR/installs/dummy/1.0/bin/upper\"\n  chmod +x \"$ASDF_DIR/installs/dummy/1.0/bin/upper\"\n\n  run asdf reshim dummy 1.0\n\n  run echo \"$(echo hello | \"$ASDF_DIR/shims/upper\")\"\n  [ \"$output\" = \"HELLO\" ]\n  [ \"$status\" -eq 0 ]\n}\n\n@test \"shim exec should fail if no version is selected\" {\n  run asdf install dummy 1.0\n\n  touch \"$PROJECT_DIR/.tool-versions\"\n\n  run \"$ASDF_DIR/shims/dummy\" world hello\n  [ \"$status\" -eq 126 ]\n  echo \"$output\" | grep -q \"No version is set for command dummy\" 2>/dev/null\n}\n\n@test \"shim exec should suggest which plugin to use when no version is selected\" {\n  run asdf install dummy 1.0\n  run asdf install dummy 2.0.0\n\n  touch \"$PROJECT_DIR/.tool-versions\"\n\n  run --separate-stderr \"$ASDF_DIR/shims/dummy\" world hello\n  [ \"$status\" -eq 126 ]\n\n  # shellcheck disable=SC2154\n  echo \"${stderr:?}\" | grep -q \"No version is set for command dummy\" 2>/dev/null\n  echo \"${stderr:?}\" | grep -q \"Consider adding one of the following versions in your config file at $PROJECT_DIR/.tool-versions\" 2>/dev/null\n  echo \"${stderr:?}\" | grep -q \"dummy 1.0\" 2>/dev/null\n  echo \"${stderr:?}\" | grep -q \"dummy 2.0.0\" 2>/dev/null\n}\n\n@test \"shim exec should suggest different plugins providing same tool when no version is selected\" {\n  # Another fake plugin with 'dummy' executable\n  cp -rf \"$ASDF_DIR/plugins/dummy\" \"$ASDF_DIR/plugins/mummy\"\n\n  run asdf install dummy 1.0\n  run asdf install mummy 3.0\n\n  touch \"$PROJECT_DIR/.tool-versions\"\n\n  run \"$ASDF_DIR/shims/dummy\" world hello\n  [ \"$status\" -eq 126 ]\n\n  echo \"$output\" | grep -q \"No version is set for command dummy\" 2>/dev/null\n  echo \"$output\" | grep -q \"Consider adding one of the following versions in your config file at $PROJECT_DIR/.tool-versions\" 2>/dev/null\n  echo \"$output\" | grep -q \"dummy 1.0\" 2>/dev/null\n  echo \"$output\" | grep -q \"mummy 3.0\" 2>/dev/null\n}\n\n# No longer possible for shim to specify version that isn't installed because\n# shims are re-generated after every install and uninstall.\n#@test \"shim exec should suggest to install missing version\" {\n#  run asdf install dummy 1.0\n\n#  echo \"dummy 2.0.0 1.3\" >\"$PROJECT_DIR/.tool-versions\"\n\n#  run \"$ASDF_DIR/shims/dummy\" world hello\n#  [ \"$status\" -eq 126 ]\n#  echo \"$output\" | grep -q \"No preset version installed for command dummy\" 2>/dev/null\n#  echo \"$output\" | grep -q \"Please install a version by running one of the following:\" 2>/dev/null\n#  echo \"$output\" | grep -q \"asdf install dummy 2.0.0\" 2>/dev/null\n#  echo \"$output\" | grep -q \"asdf install dummy 1.3\" 2>/dev/null\n#  echo \"$output\" | grep -q \"or add one of the following versions in your config file at $PROJECT_DIR/.tool-versions\" 2>/dev/null\n#  echo \"$output\" | grep -q \"dummy 1.0\" 2>/dev/null\n#}\n\n@test \"shim exec should execute first plugin that is installed and set\" {\n  run asdf install dummy 2.0.0\n  run asdf install dummy 3.0\n\n  echo \"dummy 1.0 3.0 2.0.0\" >\"$PROJECT_DIR/.tool-versions\"\n\n  run \"$ASDF_DIR/shims/dummy\" world hello\n  [ \"$status\" -eq 0 ]\n\n  echo \"$output\" | grep -q \"This is Dummy 3.0! hello world\" 2>/dev/null\n}\n\n@test \"shim exec should only use the first version found for a plugin\" {\n  run asdf install dummy 3.0\n\n  echo \"dummy 3.0\" >\"$PROJECT_DIR/.tool-versions\"\n  echo \"dummy 1.0\" >>\"$PROJECT_DIR/.tool-versions\"\n\n  run \"$ASDF_DIR/shims/dummy\" world hello\n  [ \"$status\" -eq 0 ]\n\n  echo \"$output\" | grep -q \"This is Dummy 3.0! hello world\" 2>/dev/null\n}\n\n@test \"shim exec should determine correct executable on two projects using different plugins that provide the same tool\" {\n  # Another fake plugin with 'dummy' executable\n  cp -rf \"$ASDF_DIR/plugins/dummy\" \"$ASDF_DIR/plugins/mummy\"\n  sed -i -e 's/Dummy/Mummy/' \"$ASDF_DIR/plugins/mummy/bin/install\"\n\n  run asdf install mummy 3.0\n  run asdf install dummy 1.0\n\n  mkdir \"$PROJECT_DIR\"/{A,B}\n  echo \"dummy 1.0\" >\"$PROJECT_DIR/A/.tool-versions\"\n  echo \"mummy 3.0\" >\"$PROJECT_DIR/B/.tool-versions\"\n\n  cd \"$PROJECT_DIR\"/A\n  run \"$ASDF_DIR/shims/dummy\" world hello\n  [ \"$output\" = \"This is Dummy 1.0! hello world\" ]\n  [ \"$status\" -eq 0 ]\n\n  cd \"$PROJECT_DIR\"/B\n  run \"$ASDF_DIR/shims/dummy\" world hello\n  [ \"$output\" = \"This is Mummy 3.0! hello world\" ]\n  [ \"$status\" -eq 0 ]\n}\n\n@test \"shim exec should determine correct executable on a project with two plugins set that provide the same tool\" {\n  # Another fake plugin with 'dummy' executable\n  cp -rf \"$ASDF_DIR/plugins/dummy\" \"$ASDF_DIR/plugins/mummy\"\n  sed -i -e 's/Dummy/Mummy/' \"$ASDF_DIR/plugins/mummy/bin/install\"\n\n  run asdf install dummy 1.0\n  run asdf install mummy 3.0\n\n  echo \"dummy 2.0.0\" >\"$PROJECT_DIR/.tool-versions\"\n  echo \"mummy 3.0\" >>\"$PROJECT_DIR/.tool-versions\"\n  echo \"dummy 1.0\" >>\"$PROJECT_DIR/.tool-versions\"\n\n  run \"$ASDF_DIR/shims/dummy\" world hello\n  [ \"$output\" = \"This is Mummy 3.0! hello world\" ]\n  [ \"$status\" -eq 0 ]\n}\n\n@test \"shim exec should fallback to system executable when specified version is system\" {\n  run asdf install dummy 1.0\n\n  echo \"dummy system\" >\"$PROJECT_DIR/.tool-versions\"\n\n  mkdir \"$PROJECT_DIR/foo/\"\n  echo \"#!/usr/bin/env bash\n  echo System\" >\"$PROJECT_DIR/foo/dummy\"\n  chmod +x \"$PROJECT_DIR/foo/dummy\"\n\n  run env \"PATH=$PATH:$PROJECT_DIR/foo\" \"$ASDF_DIR/shims/dummy\" hello\n  [ \"$output\" = \"System\" ]\n}\n\n# NOTE: The name of this test is linked to a condition in `test_helpers.bash. See\n# the 'setup_asdf_dir' function for details.\n@test \"shim exec should use path executable when specified version path:<path>\" {\n  run asdf install dummy 1.0\n\n  CUSTOM_DUMMY_PATH=\"$PROJECT_DIR/foo\"\n  CUSTOM_DUMMY_BIN_PATH=\"$CUSTOM_DUMMY_PATH/bin\"\n  mkdir -p \"$CUSTOM_DUMMY_BIN_PATH\"\n  echo \"#!/usr/bin/env bash\n  echo System\" >\"$CUSTOM_DUMMY_BIN_PATH/dummy\"\n  chmod +x \"$CUSTOM_DUMMY_BIN_PATH/dummy\"\n\n  echo \"dummy path:$CUSTOM_DUMMY_PATH\" >\"$PROJECT_DIR/.tool-versions\"\n\n  run \"$ASDF_DIR/shims/dummy\" hello\n  [ \"$output\" = \"System\" ]\n}\n\n@test \"shim exec should execute system if set first\" {\n  run asdf install dummy 2.0.0\n\n  echo \"dummy system\" >\"$PROJECT_DIR/.tool-versions\"\n  echo \"dummy 2.0.0\" >>\"$PROJECT_DIR/.tool-versions\"\n\n  mkdir \"$PROJECT_DIR/foo/\"\n  echo \"#!/usr/bin/env bash\n  echo System\" >\"$PROJECT_DIR/foo/dummy\"\n  chmod +x \"$PROJECT_DIR/foo/dummy\"\n\n  run env \"PATH=$PATH:$PROJECT_DIR/foo\" \"$ASDF_DIR/shims/dummy\" hello\n  [ \"$output\" = \"System\" ]\n}\n\n# These tests are disabled because the custom shims templates feature is no\n# longer supported.\n#\n#@test \"shim exec should use custom exec-env for tool\" {\n#  run asdf install dummy 2.0.0\n#  echo '#!/usr/bin/env bash\n#  export FOO=sourced' >\"$ASDF_DIR/plugins/dummy/bin/exec-env\"\n#  mkdir \"$ASDF_DIR/plugins/dummy/shims\"\n#  echo '#!/usr/bin/env bash\n#  echo $FOO custom' >\"$ASDF_DIR/plugins/dummy/shims/foo\"\n#  chmod +x \"$ASDF_DIR/plugins/dummy/shims/foo\"\n#  run asdf reshim dummy 2.0.0\n\n#  echo \"dummy 2.0.0\" >\"$PROJECT_DIR/.tool-versions\"\n#  run \"$ASDF_DIR/shims/foo\"\n#  [ \"$output\" = \"sourced custom\" ]\n#}\n\n#@test \"shim exec with custom exec-env using ASDF_INSTALL_PATH\" {\n#  run asdf install dummy 2.0.0\n#  echo 'export FOO=$ASDF_INSTALL_PATH/foo' >\"$ASDF_DIR/plugins/dummy/bin/exec-env\"\n#  mkdir \"$ASDF_DIR/plugins/dummy/shims\"\n#  echo 'echo $FOO custom' >\"$ASDF_DIR/plugins/dummy/shims/foo\"\n#  chmod +x \"$ASDF_DIR/plugins/dummy/shims/foo\"\n#  run asdf reshim dummy 2.0.0\n\n#  echo \"dummy 2.0.0\" >\"$PROJECT_DIR/.tool-versions\"\n#  run \"$ASDF_DIR/shims/foo\"\n#  [ \"$output\" = \"$ASDF_DIR/installs/dummy/2.0.0/foo custom\" ]\n#}\n\n#@test \"shim exec doesn't not use custom exec-env for system version\" {\n#  run asdf install dummy 2.0.0\n#  echo \"export FOO=sourced\" >\"$ASDF_DIR/plugins/dummy/bin/exec-env\"\n#  mkdir \"$ASDF_DIR/plugins/dummy/shims\"\n#  echo 'echo $FOO custom' >\"$ASDF_DIR/plugins/dummy/shims/foo\"\n#  chmod +x \"$ASDF_DIR/plugins/dummy/shims/foo\"\n#  run asdf reshim dummy 2.0.0\n\n#  echo \"dummy system\" >\"$PROJECT_DIR/.tool-versions\"\n\n#  mkdir \"$PROJECT_DIR/sys/\"\n#  echo 'echo x$FOO System' >\"$PROJECT_DIR/sys/foo\"\n#  chmod +x \"$PROJECT_DIR/sys/foo\"\n\n#  run env \"PATH=$PATH:$PROJECT_DIR/sys\" \"$ASDF_DIR/shims/foo\"\n#  [ \"$output\" = \"x System\" ]\n#}\n\n#@test \"shim exec should prepend the plugin paths on execution\" {\n#  run asdf install dummy 2.0.0\n\n#  mkdir \"$ASDF_DIR/plugins/dummy/shims\"\n#  echo 'which dummy' >\"$ASDF_DIR/plugins/dummy/shims/foo\"\n#  chmod +x \"$ASDF_DIR/plugins/dummy/shims/foo\"\n#  run asdf reshim dummy 2.0.0\n\n#  echo \"dummy 2.0.0\" >\"$PROJECT_DIR/.tool-versions\"\n\n#  run \"$ASDF_DIR/shims/foo\"\n#  [ \"$output\" = \"$ASDF_DIR/installs/dummy/2.0.0/bin/dummy\" ]\n#}\n\n#@test \"shim exec should be able to find other shims in path\" {\n#  cp -rf \"$ASDF_DIR/plugins/dummy\" \"$ASDF_DIR/plugins/gummy\"\n\n#  echo \"dummy 2.0.0\" >\"$PROJECT_DIR/.tool-versions\"\n#  echo \"gummy 2.0.0\" >>\"$PROJECT_DIR/.tool-versions\"\n\n#  run asdf install\n\n#  mkdir \"$ASDF_DIR/plugins/\"{dummy,gummy}/shims\n\n#  echo 'which dummy' >\"$ASDF_DIR/plugins/dummy/shims/foo\"\n#  chmod +x \"$ASDF_DIR/plugins/dummy/shims/foo\"\n\n#  echo 'which gummy' >\"$ASDF_DIR/plugins/dummy/shims/bar\"\n#  chmod +x \"$ASDF_DIR/plugins/dummy/shims/bar\"\n\n#  touch \"$ASDF_DIR/plugins/gummy/shims/gummy\"\n#  chmod +x \"$ASDF_DIR/plugins/gummy/shims/gummy\"\n\n#  run asdf reshim\n\n#  run \"$ASDF_DIR/shims/foo\"\n#  [ \"$output\" = \"$ASDF_DIR/installs/dummy/2.0.0/bin/dummy\" ]\n\n#  run \"$ASDF_DIR/shims/bar\"\n#  [ \"$output\" = \"$ASDF_DIR/shims/gummy\" ]\n#}\n\n@test \"shim exec should remove shim_path from path on system version execution\" {\n  run asdf install dummy 2.0.0\n\n  echo \"dummy system\" >\"$PROJECT_DIR/.tool-versions\"\n\n  mkdir \"$PROJECT_DIR/sys/\"\n  echo '#!/usr/bin/env bash\n  which dummy' >\"$PROJECT_DIR/sys/dummy\"\n  chmod +x \"$PROJECT_DIR/sys/dummy\"\n\n  run env \"PATH=$PATH:$PROJECT_DIR/sys\" \"$ASDF_DIR/shims/dummy\"\n  [ \"$output\" = \"$ASDF_DIR/shims/dummy\" ]\n}\n\n@test \"shim exec can take version from legacy file if configured\" {\n  run asdf install dummy 2.0.0\n\n  echo \"legacy_version_file = yes\" >\"$HOME/.asdfrc\"\n  echo \"2.0.0\" >\"$PROJECT_DIR/.dummy-version\"\n\n  run \"$ASDF_DIR/shims/dummy\" world hello\n  [ \"$output\" = \"This is Dummy 2.0.0! hello world\" ]\n}\n\n@test \"shim exec can take version from environment variable\" {\n  run asdf install dummy 2.0.0\n  run env ASDF_DUMMY_VERSION=2.0.0 \"$ASDF_DIR/shims/dummy\" world hello\n  [ \"$output\" = \"This is Dummy 2.0.0! hello world\" ]\n}\n\n@test \"shim exec uses plugin list-bin-paths\" {\n  exec_path=\"$ASDF_DIR/plugins/dummy/bin/list-bin-paths\"\n  custom_path=\"$ASDF_DIR/installs/dummy/1.0/custom\"\n\n  echo \"echo bin custom\" >\"$exec_path\"\n  chmod +x \"$exec_path\"\n\n  run asdf install dummy 1.0\n  echo \"dummy 1.0\" >\"$PROJECT_DIR/.tool-versions\"\n\n  mkdir \"$custom_path\"\n  echo '#!/usr/bin/env bash\n  echo CUSTOM' >\"$custom_path/foo\"\n  chmod +x \"$custom_path/foo\"\n\n  run asdf reshim dummy 1.0\n\n  run \"$ASDF_DIR/shims/foo\"\n  [ \"$output\" = \"CUSTOM\" ]\n}\n\n@test \"shim exec uses plugin custom exec-path hook\" {\n  run asdf install dummy 1.0\n\n  exec_path=\"$ASDF_DIR/plugins/dummy/bin/exec-path\"\n  custom_dummy=\"$ASDF_DIR/installs/dummy/1.0/custom/dummy\"\n\n  echo '#!/usr/bin/env bash\n  echo custom/dummy' >\"$exec_path\"\n  chmod +x \"$exec_path\"\n\n  mkdir \"$(dirname \"$custom_dummy\")\"\n  echo '#!/usr/bin/env bash\n  echo CUSTOM' >\"$custom_dummy\"\n  chmod +x \"$custom_dummy\"\n\n  echo \"dummy 1.0\" >\"$PROJECT_DIR/.tool-versions\"\n\n  run \"$ASDF_DIR/shims/dummy\"\n  [ \"$output\" = \"CUSTOM\" ]\n}\n\n@test \"shim exec uses plugin custom exec-path hook that defaults\" {\n  run asdf install dummy 1.0\n\n  exec_path=\"$ASDF_DIR/plugins/dummy/bin/exec-path\"\n  echo '#!/usr/bin/env bash\n  echo \"$3\" # always same path' >\"$exec_path\"\n  chmod +x \"$exec_path\"\n\n  echo \"dummy 1.0\" >\"$PROJECT_DIR/.tool-versions\"\n\n  run \"$ASDF_DIR/shims/dummy\"\n  [ \"$output\" = \"This is Dummy 1.0!\" ]\n}\n\n@test \"shim exec executes configured pre-hook\" {\n  run asdf install dummy 1.0\n  echo dummy 1.0 >\"$PROJECT_DIR/.tool-versions\"\n\n  cat >\"$HOME/.asdfrc\" <<-'EOM'\npre_dummy_dummy = echo PRE $1 $2\nEOM\n\n  run \"$ASDF_DIR/shims/dummy\" hello world\n  [ \"$status\" -eq 0 ]\n  echo \"$output\" | grep \"PRE hello world\"\n  echo \"$output\" | grep \"This is Dummy 1.0! world hello\"\n}\n\n@test \"shim exec doesn't execute command if pre-hook failed\" {\n  run asdf install dummy 1.0\n  echo dummy 1.0 >\"$PROJECT_DIR/.tool-versions\"\n\n  mkdir \"$HOME/hook\"\n  pre_cmd=\"$HOME/hook/pre\"\n  echo '#!/usr/bin/env bash\n  echo $* && false' >\"$pre_cmd\"\n  chmod +x \"$pre_cmd\"\n\n  cat >\"$HOME/.asdfrc\" <<'EOM'\npre_dummy_dummy = pre $1 $2\nEOM\n\n  run env PATH=\"$PATH:$HOME/hook\" \"$ASDF_DIR/shims/dummy\" hello world\n  [ \"$output\" = \"hello world\" ]\n  [ \"$status\" -eq 1 ]\n}\n\n# From @tejanium in https://github.com/asdf-vm/asdf/issues/581#issuecomment-635337727\n@test \"asdf exec should not crash when POSIXLY_CORRECT=1\" {\n  export POSIXLY_CORRECT=1\n\n  echo \"dummy 1.0\" >\"$PROJECT_DIR/.tool-versions\"\n  run asdf install\n\n  run asdf exec dummy world hello\n  [ \"$output\" = \"This is Dummy 1.0! hello world\" ]\n  [ \"$status\" -eq 0 ]\n}\n"
  },
  {
    "path": "test/shim_versions_command.bats",
    "content": "#!/usr/bin/env bats\n# shellcheck disable=SC2164\n\nload test_helpers\n\nsetup() {\n  setup_asdf_dir\n  install_dummy_plugin\n\n  PROJECT_DIR=\"$HOME/project\"\n  mkdir -p \"$PROJECT_DIR\"\n  cd \"$PROJECT_DIR\"\n}\n\nteardown() {\n  clean_asdf_dir\n}\n\n@test \"shim_versions_command should list plugins and versions where command is available\" {\n  cd \"$PROJECT_DIR\"\n  run asdf install dummy 3.0\n  run asdf install dummy 1.0\n  run asdf reshim dummy\n\n  run asdf shimversions dummy\n  [ \"$status\" -eq 0 ]\n\n  echo \"$output\" | grep \"dummy 3.0\"\n  echo \"$output\" | grep \"dummy 1.0\"\n}\n"
  },
  {
    "path": "test/test_helpers.bash",
    "content": "#!/usr/bin/env bash\n\nbats_require_minimum_version 1.7.0\n\n# shellcheck source=lib/utils.bash\n. \"$(dirname \"$BATS_TEST_DIRNAME\")\"/lib/utils.bash\n\nsetup_asdf_dir() {\n  if [ \"$BATS_TEST_NAME\" = 'test_shim_exec_should_use_path_executable_when_specified_version_path-3a-3cpath-3e' ]; then\n    BASE_DIR=\"$BASE_DIR/asdf_with_no_spaces\"\n  else\n    BASE_DIR=\"$BASE_DIR/w space${BATS_TEST_NAME}\"\n  fi\n\n  # We don't call mktemp anymore so we need to create this sub directory manually\n  mkdir \"$BASE_DIR\"\n\n  # HOME is now defined by the Golang test code in main_test.go\n  HOME=\"$BASE_DIR\"\n  export HOME\n  ASDF_DIR=\"$HOME/.asdf\"\n  mkdir -p \"$ASDF_DIR/plugins\"\n  mkdir -p \"$ASDF_DIR/installs\"\n  mkdir -p \"$ASDF_DIR/shims\"\n  mkdir -p \"$ASDF_DIR/tmp\"\n  # ASDF_BIN is now defined by the Golang test code in main_test.go\n  #ASDF_BIN=\"$(dirname \"$BATS_TEST_DIRNAME\")/bin\"\n\n  ASDF_DATA_DIR=\"$BASE_DIR/.asdf\"\n  export ASDF_DATA_DIR\n\n  # shellcheck disable=SC2031,SC2153\n  PATH=\"$ASDF_BIN:$ASDF_DIR/shims:$PATH\"\n}\n\ninstall_mock_plugin() {\n  local plugin_name=$1\n  local location=\"${2:-$ASDF_DIR}\"\n  plugin_dir=\"$location/plugins/$plugin_name\"\n  cp -r \"$BATS_TEST_DIRNAME/fixtures/dummy_plugin\" \"$plugin_dir\"\n  init_git_repo \"$plugin_dir\"\n}\n\ninstall_mock_plugin_no_download() {\n  local plugin_name=$1\n  local location=\"${2:-$ASDF_DIR}\"\n  cp -r \"$BATS_TEST_DIRNAME/fixtures/dummy_plugin_no_download\" \"$location/plugins/$plugin_name\"\n}\n\ninstall_mock_legacy_plugin() {\n  local plugin_name=$1\n  local location=\"${2:-$ASDF_DIR}\"\n  plugin_dir=\"$location/plugins/$plugin_name\"\n  cp -r \"$BATS_TEST_DIRNAME/fixtures/dummy_legacy_plugin\" \"$plugin_dir\"\n  init_git_repo \"$plugin_dir\"\n}\n\ninstall_mock_broken_plugin() {\n  local plugin_name=$1\n  local location=\"${2:-$ASDF_DIR}\"\n  cp -r \"$BATS_TEST_DIRNAME/fixtures/dummy_broken_plugin\" \"$location/plugins/$plugin_name\"\n}\n\ninstall_mock_plugin_repo() {\n  local plugin_name=$1\n  local location=\"${BASE_DIR}/repo-${plugin_name}\"\n  cp -r \"$BATS_TEST_DIRNAME/fixtures/dummy_plugin\" \"${location}\"\n  init_git_repo \"${location}\"\n}\n\ninit_git_repo() {\n  location=\"$1\"\n  remote=\"${2:-\"https://asdf-vm.com/fake-repo\"}\"\n  git -C \"${location}\" init -q --initial-branch=master\n  git -C \"${location}\" config user.name \"Test\"\n  git -C \"${location}\" config user.email \"test@example.com\"\n  git -C \"${location}\" add -A\n  git -C \"${location}\" commit -q -m \"asdf ${plugin_name} plugin\"\n  git -C \"${location}\" remote add origin \"$remote\"\n}\n\ninstall_mock_plugin_version() {\n  local plugin_name=$1\n  local plugin_version=$2\n  local location=\"${3:-$ASDF_DIR}\"\n  mkdir -p \"$location/installs/$plugin_name/$plugin_version\"\n}\n\ninstall_dummy_plugin() {\n  install_mock_plugin \"dummy\"\n}\n\ninstall_dummy_plugin_no_download() {\n  install_mock_plugin_no_download \"dummy-no-download\" \"$1\"\n}\n\ninstall_dummy_legacy_plugin() {\n  install_mock_legacy_plugin \"legacy-dummy\"\n}\n\ninstall_dummy_broken_plugin() {\n  install_mock_broken_plugin \"dummy-broken\"\n}\n\ninstall_dummy_version() {\n  install_mock_plugin_version \"dummy\" \"$1\"\n}\n\ninstall_dummy_legacy_version() {\n  install_mock_plugin_version \"legacy-dummy\" \"$1\"\n}\n\ninstall_dummy_exec_path_script() {\n  local name=$1\n  local exec_path=\"$ASDF_DIR/plugins/dummy/bin/exec-path\"\n  local custom_dir=\"$ASDF_DIR/installs/dummy/1.0/bin/custom\"\n  mkdir \"$custom_dir\"\n  touch \"$custom_dir/$name\"\n  chmod +x \"$custom_dir/$name\"\n  echo \"echo 'bin/custom/$name'\" >\"$exec_path\"\n  chmod +x \"$exec_path\"\n}\n\nclean_asdf_dir() {\n  rm -rf \"$BASE_DIR\"\n  unset ASDF_DIR\n  unset ASDF_DATA_DIR\n}\n\nsetup_repo() {\n  cp -r \"$BATS_TEST_DIRNAME/fixtures/dummy_plugins_repo\" \"$ASDF_DIR/plugin-index\"\n  cp -r \"$BATS_TEST_DIRNAME/fixtures/dummy_plugins_repo\" \"$ASDF_DIR/plugin-index-2\"\n  init_git_repo \"$ASDF_DIR/plugin-index-2\"\n  init_git_repo \"$ASDF_DIR/plugin-index\" \"$ASDF_DIR/plugin-index-2\"\n  touch \"$(asdf_dir)/tmp/repo-updated\"\n}\n"
  },
  {
    "path": "test/uninstall_command.bats",
    "content": "#!/usr/bin/env bats\n\nload test_helpers\n\nsetup() {\n  setup_asdf_dir\n  install_dummy_plugin\n\n  PROJECT_DIR=\"$HOME/project\"\n  mkdir -p \"$PROJECT_DIR\"\n}\n\nteardown() {\n  clean_asdf_dir\n}\n\n@test \"uninstall_command should fail when no such version is installed\" {\n  run asdf uninstall dummy 3.14\n  [ \"$output\" = \"No such version\" ]\n  [ \"$status\" -eq 1 ]\n}\n\n@test \"uninstall_command should remove the plugin with that version from asdf\" {\n  run asdf install dummy 1.1.0\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$ASDF_DIR/installs/dummy/1.1.0/version\")\" = \"1.1.0\" ]\n  run asdf uninstall dummy 1.1.0\n  [ ! -f \"$ASDF_DIR/installs/dummy/1.1.0/version\" ]\n}\n\n@test \"uninstall_command should invoke the plugin bin/uninstall if available\" {\n  run asdf install dummy 1.1.0\n  [ \"$status\" -eq 0 ]\n  mkdir -p \"$ASDF_DIR/plugins/dummy/bin\"\n  printf '%s\\n' \"echo custom uninstall\" >\"$ASDF_DIR/plugins/dummy/bin/uninstall\"\n  chmod 755 \"$ASDF_DIR/plugins/dummy/bin/uninstall\"\n  run asdf uninstall dummy 1.1.0\n  [ \"$output\" = \"custom uninstall\" ]\n  [ \"$status\" -eq 0 ]\n}\n\n@test \"uninstall_command should remove the plugin shims if no other version is installed\" {\n  run asdf install dummy 1.1.0\n  [ -f \"$ASDF_DIR/shims/dummy\" ]\n  run asdf uninstall dummy 1.1.0\n  [ ! -f \"$ASDF_DIR/shims/dummy\" ]\n}\n\n@test \"uninstall_command should leave the plugin shims if other version is installed\" {\n  run asdf install dummy 1.0.0\n  [ -f \"$ASDF_DIR/installs/dummy/1.0.0/bin/dummy\" ]\n\n  run asdf install dummy 1.1.0\n  [ -f \"$ASDF_DIR/installs/dummy/1.1.0/bin/dummy\" ]\n\n  [ -f \"$ASDF_DIR/shims/dummy\" ]\n  run asdf uninstall dummy 1.0.0\n  [ -f \"$ASDF_DIR/shims/dummy\" ]\n}\n\n@test \"uninstall_command should remove relevant asdf-plugin metadata\" {\n  run asdf install dummy 1.0.0\n  [ -f \"$ASDF_DIR/installs/dummy/1.0.0/bin/dummy\" ]\n\n  run asdf install dummy 1.1.0\n  [ -f \"$ASDF_DIR/installs/dummy/1.1.0/bin/dummy\" ]\n\n  run asdf uninstall dummy 1.0.0\n  run grep \"asdf-plugin: dummy 1.1.0\" \"$ASDF_DIR/shims/dummy\"\n  [ \"$status\" -eq 0 ]\n  run grep \"asdf-plugin: dummy 1.0.0\" \"$ASDF_DIR/shims/dummy\"\n  [ \"$status\" -eq 1 ]\n}\n\n# Disabled as this test represents an invalid state. A shim (`gummy`) should\n# never exist unless it referenced an existing tool and version.\n#\n#@test \"uninstall_command should not remove other unrelated shims\" {\n#  run asdf install dummy 1.0.0\n#  [ -f \"$ASDF_DIR/shims/dummy\" ]\n\n#  touch \"$ASDF_DIR/shims/gummy\"\n#  [ -f \"$ASDF_DIR/shims/gummy\" ]\n\n#  run asdf uninstall dummy 1.0.0\n#  [ -f \"$ASDF_DIR/shims/gummy\" ]\n#}\n\n@test \"uninstall command executes configured pre hook\" {\n  cat >\"$HOME/.asdfrc\" <<-'EOM'\npre_asdf_uninstall_dummy = echo will uninstall dummy $1\nEOM\n\n  run asdf install dummy 1.0.0\n  run asdf uninstall dummy 1.0.0\n  [ \"$output\" = \"will uninstall dummy 1.0.0\" ]\n}\n\n@test \"uninstall command executes configured post hook\" {\n  cat >\"$HOME/.asdfrc\" <<-'EOM'\npost_asdf_uninstall_dummy = echo removed dummy $1\nEOM\n\n  run asdf install dummy 1.0.0\n  run asdf uninstall dummy 1.0.0\n  [ \"$output\" = \"removed dummy 1.0.0\" ]\n}\n"
  },
  {
    "path": "test/update_command.bats",
    "content": "#!/usr/bin/env bats\n\n#load test_helpers\n\n#setup() {\n#  BASE_DIR=$(mktemp -dt asdf.XXXX)\n#  HOME=\"$BASE_DIR/home\"\n#  ASDF_DIR=\"$HOME/.asdf\"\n#  git clone -o local \"$(dirname \"$BATS_TEST_DIRNAME\")\" \"$ASDF_DIR\"\n#  git --git-dir \"$ASDF_DIR/.git\" remote add origin https://github.com/asdf-vm/asdf.git\n#  mkdir -p \"$ASDF_DIR/plugins\"\n#  mkdir -p \"$ASDF_DIR/installs\"\n#  mkdir -p \"$ASDF_DIR/shims\"\n#  mkdir -p \"$ASDF_DIR/tmp\"\n#  ASDF_BIN=\"$ASDF_DIR/bin\"\n\n#  # shellcheck disable=SC2031\n#  PATH=\"$ASDF_BIN:$ASDF_DIR/shims:$PATH\"\n#  install_dummy_plugin\n\n#  PROJECT_DIR=\"$HOME/project\"\n#  mkdir -p \"$PROJECT_DIR\"\n#}\n\n#teardown() {\n#  clean_asdf_dir\n#}\n\n#@test \"asdf update --head should checkout the master branch\" {\n#  run asdf update --head\n#  [ \"$status\" -eq 0 ]\n#  cd \"$ASDF_DIR\"\n#  [ \"$(git rev-parse --abbrev-ref HEAD)\" = \"master\" ]\n#}\n\n#@test \"asdf update should checkout the latest non-RC tag\" {\n#  local tag=\n#  tag=$(git tag | grep -vi \"rc\" | tail -1)\n#  if [ -n \"$tag\" ]; then\n#    run asdf update\n#    [ \"$status\" -eq 0 ]\n#    cd \"$ASDF_DIR\"\n#    git tag | grep \"$tag\"\n#  fi\n#}\n\n#@test \"asdf update should checkout the latest tag when configured with use_release_candidates = yes\" {\n#  local tag=\n#  tag=$(git tag | tail -1)\n#  if [ -n \"$tag\" ]; then\n#    export ASDF_CONFIG_DEFAULT_FILE=\"$BATS_TMPDIR/asdfrc_defaults\"\n#    echo \"use_release_candidates = yes\" >\"$ASDF_CONFIG_DEFAULT_FILE\"\n#    run asdf update\n#    [ \"$status\" -eq 0 ]\n#    cd \"$ASDF_DIR\"\n#    git tag | grep \"$tag\"\n#  fi\n#}\n\n#@test \"asdf update is a noop for when updates are disabled\" {\n#  touch \"$ASDF_DIR/asdf_updates_disabled\"\n#  run asdf update\n#  [ \"$status\" -eq 42 ]\n#  [ $'Update command disabled. Please use the package manager that you used to install asdf to upgrade asdf.' = \"$output\" ]\n#}\n\n#@test \"asdf update is a noop for non-git repos\" {\n#  rm -rf \"$ASDF_DIR/.git/\"\n#  run asdf update\n#  [ \"$status\" -eq 42 ]\n#  [ $'Update command disabled. Please use the package manager that you used to install asdf to upgrade asdf.' = \"$output\" ]\n#}\n\n#@test \"asdf update fails with exit code 1\" {\n#  git --git-dir \"$ASDF_DIR/.git\" remote set-url origin https://this-host-does-not-exist.xyz\n#  run asdf update\n#  [ \"$status\" -eq 1 ]\n#}\n\n#@test \"asdf update should not remove plugin versions\" {\n#  run asdf install dummy 1.1.0\n#  [ \"$status\" -eq 0 ]\n#  [ \"$(cat \"$ASDF_DIR/installs/dummy/1.1.0/version\")\" = \"1.1.0\" ]\n#  run asdf update\n#  [ \"$status\" -eq 0 ]\n#  [ -f \"$ASDF_DIR/installs/dummy/1.1.0/version\" ]\n#  run asdf update --head\n#  [ \"$status\" -eq 0 ]\n#  [ -f \"$ASDF_DIR/installs/dummy/1.1.0/version\" ]\n#}\n\n#@test \"asdf update should not remove plugins\" {\n#  # dummy plugin is already installed\n#  run asdf update\n#  [ \"$status\" -eq 0 ]\n#  [ -d \"$ASDF_DIR/plugins/dummy\" ]\n#  run asdf update --head\n#  [ \"$status\" -eq 0 ]\n#  [ -d \"$ASDF_DIR/plugins/dummy\" ]\n#}\n\n#@test \"asdf update should not remove shims\" {\n#  run asdf install dummy 1.1.0\n#  [ -f \"$ASDF_DIR/shims/dummy\" ]\n#  run asdf update\n#  [ \"$status\" -eq 0 ]\n#  [ -f \"$ASDF_DIR/shims/dummy\" ]\n#  run asdf update --head\n#  [ \"$status\" -eq 0 ]\n#  [ -f \"$ASDF_DIR/shims/dummy\" ]\n#}\n"
  },
  {
    "path": "test/utils.bats",
    "content": "#!/usr/bin/env bats\n# shellcheck disable=SC2030,SC2031,SC2164\n\nload test_helpers\n\nsetup() {\n  setup_asdf_dir\n  install_dummy_plugin\n  install_dummy_version \"0.1.0\"\n  install_dummy_version \"0.2.0\"\n\n  PROJECT_DIR=\"$HOME/project\"\n  mkdir -p \"$PROJECT_DIR\"\n\n  cd \"$HOME\"\n}\n\nteardown() {\n  clean_asdf_dir\n}\n\n@test \"get_install_path should output version path when version is provided\" {\n  run get_install_path foo version \"1.0.0\"\n  [ \"$status\" -eq 0 ]\n  install_path=${output#\"$HOME/\"}\n  [ \"$install_path\" = \".asdf/installs/foo/1.0.0\" ]\n}\n\n@test \"get_install_path should output custom path when custom install type is provided\" {\n  run get_install_path foo custom \"1.0.0\"\n  [ \"$status\" -eq 0 ]\n  install_path=${output#\"$HOME/\"}\n  [ \"$install_path\" = \".asdf/installs/foo/custom-1.0.0\" ]\n}\n\n@test \"get_install_path should output path when path version is provided\" {\n  run get_install_path foo path \"/some/path\"\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"/some/path\" ]\n}\n\n@test \"get_download_path should output version path when version is provided\" {\n  run get_download_path foo version \"1.0.0\"\n  [ \"$status\" -eq 0 ]\n  download_path=${output#\"$HOME/\"}\n  [ \"$download_path\" = \".asdf/downloads/foo/1.0.0\" ]\n}\n\n@test \"get_download_path should output custom path when custom download type is provided\" {\n  run get_download_path foo custom \"1.0.0\"\n  [ \"$status\" -eq 0 ]\n  download_path=${output#\"$HOME/\"}\n  [ \"$download_path\" = \".asdf/downloads/foo/custom-1.0.0\" ]\n}\n\n@test \"get_download_path should output nothing when path version is provided\" {\n  run get_download_path foo path \"/some/path\"\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"\" ]\n}\n\n@test \"check_if_version_exists should exit with 1 if plugin does not exist\" {\n  run check_if_version_exists \"inexistent\" \"1.0.0\"\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"No such plugin: inexistent\" ]\n}\n\n@test \"check_if_version_exists should exit with 1 if version does not exist\" {\n  run check_if_version_exists \"dummy\" \"1.0.0\"\n  [ \"$status\" -eq 1 ]\n}\n\n@test \"version_not_installed_text is correct\" {\n  run version_not_installed_text \"dummy\" \"1.0.0\"\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"version 1.0.0 is not installed for dummy\" ]\n}\n\n@test \"check_if_version_exists should be noop if version exists\" {\n  run check_if_version_exists \"dummy\" \"0.1.0\"\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"\" ]\n}\n\n@test \"check_if_version_exists should be noop if version is system\" {\n  mkdir -p \"$ASDF_DIR/plugins/foo\"\n  run check_if_version_exists \"foo\" \"system\"\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"\" ]\n}\n\n@test \"check_if_version_exists should be ok for ref:version install\" {\n  mkdir -p \"$ASDF_DIR/plugins/foo\"\n  mkdir -p \"$ASDF_DIR/installs/foo/ref-master\"\n  run check_if_version_exists \"foo\" \"ref:master\"\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"\" ]\n}\n\n@test \"check_if_plugin_exists should exit with 1 when plugin is empty string\" {\n  run check_if_plugin_exists\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"No plugin given\" ]\n}\n\n@test \"check_if_plugin_exists should be noop if plugin exists\" {\n  run check_if_plugin_exists \"dummy\"\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"\" ]\n}\n\n@test \"parse_asdf_version_file should output version\" {\n  echo \"dummy 0.1.0\" >\"$PROJECT_DIR/.tool-versions\"\n  run parse_asdf_version_file \"$PROJECT_DIR/.tool-versions\" dummy\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"0.1.0\" ]\n}\n\n@test \"parse_asdf_version_file should output path on project with spaces\" {\n  PROJECT_DIR=\"$PROJECT_DIR/outer space\"\n  mkdir -p \"$PROJECT_DIR\"\n\n  echo \"dummy 0.1.0\" >\"$PROJECT_DIR/.tool-versions\"\n  run parse_asdf_version_file \"$PROJECT_DIR/.tool-versions\" dummy\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"0.1.0\" ]\n}\n\n@test \"parse_asdf_version_file should output path version with spaces\" {\n  echo \"dummy path:/some/dummy path\" >\"$PROJECT_DIR/.tool-versions\"\n  run parse_asdf_version_file \"$PROJECT_DIR/.tool-versions\" dummy\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"path:/some/dummy path\" ]\n}\n\n@test \"parse_asdf_version_file should output path version with tilde\" {\n  echo \"dummy path:~/some/dummy path\" >\"$PROJECT_DIR/.tool-versions\"\n  run parse_asdf_version_file \"$PROJECT_DIR/.tool-versions\" dummy\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"path:$HOME/some/dummy path\" ]\n}\n\n@test \"find_versions should return .tool-versions if legacy is disabled\" {\n  echo \"dummy 0.1.0\" >\"$PROJECT_DIR/.tool-versions\"\n  echo \"0.2.0\" >\"$PROJECT_DIR/.dummy-version\"\n\n  run find_versions \"dummy\" \"$PROJECT_DIR\"\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"0.1.0|$PROJECT_DIR/.tool-versions\" ]\n}\n\n@test \"find_versions should return the legacy file if supported\" {\n  echo \"legacy_version_file = yes\" >\"$HOME/.asdfrc\"\n  echo \"dummy 0.1.0\" >\"$HOME/.tool-versions\"\n  echo \"0.2.0\" >\"$PROJECT_DIR/.dummy-version\"\n\n  run find_versions \"dummy\" \"$PROJECT_DIR\"\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"0.2.0|$PROJECT_DIR/.dummy-version\" ]\n}\n\n@test \"find_versions skips .tool-version file that don't list the plugin\" {\n  echo \"dummy 0.1.0\" >\"$HOME/.tool-versions\"\n  echo \"another_plugin 0.3.0\" >\"$PROJECT_DIR/.tool-versions\"\n\n  run find_versions \"dummy\" \"$PROJECT_DIR\"\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"0.1.0|$HOME/.tool-versions\" ]\n}\n\n@test \"find_versions should return .tool-versions if unsupported\" {\n  echo \"dummy 0.1.0\" >\"$HOME/.tool-versions\"\n  echo \"0.2.0\" >\"$PROJECT_DIR/.dummy-version\"\n  echo \"legacy_version_file = yes\" >\"$HOME/.asdfrc\"\n  rm \"$ASDF_DIR/plugins/dummy/bin/list-legacy-filenames\"\n\n  run find_versions \"dummy\" \"$PROJECT_DIR\"\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"0.1.0|$HOME/.tool-versions\" ]\n}\n\n@test \"find_versions should return the version set by environment variable\" {\n  export ASDF_DUMMY_VERSION=0.2.0\n\n  run find_versions \"dummy\" \"$PROJECT_DIR\"\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"0.2.0|ASDF_DUMMY_VERSION environment variable\" ]\n}\n\n@test \"asdf_data_dir should return user dir if configured\" {\n  ASDF_DATA_DIR=\"/tmp/wadus\"\n\n  run asdf_data_dir\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"$ASDF_DATA_DIR\" ]\n}\n\n@test \"asdf_data_dir should return ~/.asdf when ASDF_DATA_DIR is not set\" {\n  unset ASDF_DATA_DIR\n\n  run asdf_data_dir\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"$HOME/.asdf\" ]\n}\n\n@test \"check_if_plugin_exists should work with a custom data directory\" {\n  ASDF_DATA_DIR=$HOME/asdf-data\n\n  mkdir -p \"$ASDF_DATA_DIR/plugins\"\n  mkdir -p \"$ASDF_DATA_DIR/installs\"\n\n  install_mock_plugin \"dummy2\" \"$ASDF_DATA_DIR\"\n  install_mock_plugin_version \"dummy2\" \"0.1.0\" \"$ASDF_DATA_DIR\"\n\n  run check_if_plugin_exists \"dummy2\"\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"\" ]\n}\n\n@test \"find_versions should return \\$ASDF_TOOL_VERSIONS_FILENAME if set\" {\n  ASDF_TOOL_VERSIONS_FILENAME=\"$PROJECT_DIR/global-tool-versions\"\n  echo \"dummy 0.1.0\" >\"$ASDF_TOOL_VERSIONS_FILENAME\"\n\n  run find_versions \"dummy\" \"$PROJECT_DIR\"\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"0.1.0|$ASDF_TOOL_VERSIONS_FILENAME\" ]\n}\n\n@test \"find_versions should check \\$HOME legacy files before \\$ASDF_TOOL_VERSIONS_FILENAME\" {\n  ASDF_TOOL_VERSIONS_FILENAME=\"$PROJECT_DIR/global-tool-versions\"\n  echo \"dummy 0.2.0\" >\"$ASDF_TOOL_VERSIONS_FILENAME\"\n  echo \"dummy 0.1.0\" >\"$HOME/.dummy-version\"\n  echo \"legacy_version_file = yes\" >\"$HOME/.asdfrc\"\n\n  run find_versions \"dummy\" \"$PROJECT_DIR\"\n  [ \"$status\" -eq 0 ]\n  [[ \"$output\" == *\"0.1.0|$HOME/.dummy-version\"* ]]\n}\n\n@test \"get_preset_version_for returns the current version\" {\n  cd \"$PROJECT_DIR\"\n  echo \"dummy 0.2.0\" >.tool-versions\n  run get_preset_version_for \"dummy\"\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"0.2.0\" ]\n}\n\n@test \"get_preset_version_for returns the global version from home when project is outside of home\" {\n  echo \"dummy 0.1.0\" >\"$HOME/.tool-versions\"\n  PROJECT_DIR=$BASE_DIR/project\n  mkdir -p \"$PROJECT_DIR\"\n  run get_preset_version_for \"dummy\"\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"0.1.0\" ]\n}\n\n@test \"get_preset_version_for returns the tool version from env if ASDF_{TOOL}_VERSION is defined\" {\n  cd \"$PROJECT_DIR\"\n  echo \"dummy 0.2.0\" >.tool-versions\n  ASDF_DUMMY_VERSION=3.0.0 run get_preset_version_for \"dummy\"\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"3.0.0\" ]\n}\n\n@test \"get_preset_version_for should return branch reference version\" {\n  cd \"$PROJECT_DIR\"\n  echo \"dummy ref:master\" >\"$PROJECT_DIR/.tool-versions\"\n  run get_preset_version_for \"dummy\"\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"ref:master\" ]\n}\n\n@test \"get_preset_version_for should return path version\" {\n  cd \"$PROJECT_DIR\"\n  echo \"dummy path:/some/place with spaces\" >\"$PROJECT_DIR/.tool-versions\"\n  run get_preset_version_for \"dummy\"\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"path:/some/place with spaces\" ]\n}\n\n@test \"get_preset_version_for should return path version with tilde\" {\n  cd \"$PROJECT_DIR\"\n  echo \"dummy path:~/some/place with spaces\" >\"$PROJECT_DIR/.tool-versions\"\n  run get_preset_version_for \"dummy\"\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"path:$HOME/some/place with spaces\" ]\n}\n\n@test \"get_executable_path for system version should return system path\" {\n  mkdir -p \"$ASDF_DIR/plugins/foo\"\n  run get_executable_path \"foo\" \"system\" \"ls\"\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"$(which ls)\" ]\n}\n\n@test \"get_executable_path for system version should not use asdf shims\" {\n  mkdir -p \"$ASDF_DIR/plugins/foo\"\n  touch \"$ASDF_DIR/shims/dummy_executable\"\n  chmod +x \"$ASDF_DIR/shims/dummy_executable\"\n\n  run which dummy_executable\n  [ \"$status\" -eq 0 ]\n\n  run get_executable_path \"foo\" \"system\" \"dummy_executable\"\n  [ \"$status\" -eq 1 ]\n}\n\n@test \"get_executable_path for non system version should return relative path from plugin\" {\n  mkdir -p \"$ASDF_DIR/plugins/foo\"\n  mkdir -p \"$ASDF_DIR/installs/foo/1.0.0/bin\"\n  executable_path=\"$ASDF_DIR/installs/foo/1.0.0/bin/dummy\"\n  touch \"$executable_path\"\n  chmod +x \"$executable_path\"\n\n  run get_executable_path \"foo\" \"1.0.0\" \"bin/dummy\"\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"$executable_path\" ]\n}\n\n@test \"get_executable_path for ref:version installed version should resolve to ref-version\" {\n  mkdir -p \"$ASDF_DIR/plugins/foo\"\n  mkdir -p \"$ASDF_DIR/installs/foo/ref-master/bin\"\n  executable_path=\"$ASDF_DIR/installs/foo/ref-master/bin/dummy\"\n  touch \"$executable_path\"\n  chmod +x \"$executable_path\"\n\n  run get_executable_path \"foo\" \"ref:master\" \"bin/dummy\"\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"$executable_path\" ]\n}\n\n@test \"find_tool_versions will find a .tool-versions path if it exists in current directory\" {\n  echo \"dummy 0.1.0\" >\"$PROJECT_DIR/.tool-versions\"\n  cd \"$PROJECT_DIR\"\n\n  run find_tool_versions\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"$PROJECT_DIR/.tool-versions\" ]\n}\n\n@test \"find_tool_versions will find a .tool-versions path if it exists in parent directory\" {\n  echo \"dummy 0.1.0\" >\"$PROJECT_DIR/.tool-versions\"\n  mkdir -p \"$PROJECT_DIR/child\"\n  cd \"$PROJECT_DIR\"/child\n\n  run find_tool_versions\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"$PROJECT_DIR/.tool-versions\" ]\n}\n\n@test \"get_version_from_env returns the version set in the environment variable\" {\n  export ASDF_DUMMY_VERSION=0.1.0\n  run get_version_from_env 'dummy'\n\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = '0.1.0' ]\n}\n\n@test \"get_version_from_env returns nothing when environment variable is not set\" {\n  run get_version_from_env 'dummy'\n\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = '' ]\n}\n\n@test \"resolve_symlink converts the symlink path to the real file path\" {\n  touch foo\n  ln -s \"$PWD/foo\" bar\n\n  run resolve_symlink bar\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"$PWD/foo\" ]\n  rm -f foo bar\n}\n\n@test \"resolve_symlink converts relative symlink directory path to the real file path\" {\n  mkdir baz\n  ln -s ../foo baz/bar\n\n  run resolve_symlink baz/bar\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"$PWD/baz/../foo\" ]\n  rm -f foo bar\n}\n\n@test \"resolve_symlink converts relative symlink path to the real file path\" {\n  touch foo\n  ln -s foo bar\n\n  run resolve_symlink bar\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"$PWD/foo\" ]\n  rm -f foo bar\n}\n\n@test \"strip_tool_version_comments removes lines that only contain comments\" {\n  cat <<EOF >test_file\n# comment line\nruby 2.0.0\nEOF\n  run strip_tool_version_comments test_file\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"ruby 2.0.0\" ]\n}\n@test \"strip_tool_version_comments removes lines that only contain comments even with missing newline\" {\n  echo -n \"# comment line\" >test_file\n  run strip_tool_version_comments test_file\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"\" ]\n}\n\n@test \"strip_tool_version_comments removes trailing comments on lines containing version information\" {\n  cat <<EOF >test_file\nruby 2.0.0 # inline comment\nEOF\n  run strip_tool_version_comments test_file\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"ruby 2.0.0\" ]\n}\n\n@test \"strip_tool_version_comments removes trailing comments on lines containing version information even with missing newline\" {\n  echo -n \"ruby 2.0.0 # inline comment\" >test_file\n  run strip_tool_version_comments test_file\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"ruby 2.0.0\" ]\n}\n\n@test \"strip_tool_version_comments removes all comments from the version file\" {\n  cat <<EOF >test_file\nruby 2.0.0 # inline comment\n# comment line\nerlang 18.2.1 # inline comment\nEOF\n  expected=\"$(\n    cat <<EOF\nruby 2.0.0\nerlang 18.2.1\nEOF\n  )\"\n  run strip_tool_version_comments test_file\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"$expected\" ]\n}\n\n@test \"with_shim_executable doesn't crash when executable names contain dashes\" {\n  cd \"$PROJECT_DIR\"\n  echo \"dummy 0.1.0\" >\"$PROJECT_DIR/.tool-versions\"\n  mkdir -p \"$ASDF_DIR/installs/dummy/0.1.0/bin\"\n  touch \"$ASDF_DIR/installs/dummy/0.1.0/bin/test-dash\"\n  chmod +x \"$ASDF_DIR/installs/dummy/0.1.0/bin/test-dash\"\n  run asdf reshim dummy 0.1.0\n\n  message=\"callback invoked\"\n\n  callback() {\n    echo \"$message\"\n  }\n\n  run with_shim_executable test-dash callback\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"$message\" ]\n}\n\n@test \"prints warning if .tool-versions file has carriage returns\" {\n  ASDF_CONFIG_FILE=\"$BATS_TEST_TMPDIR/asdfrc\"\n  cat >\"$ASDF_CONFIG_FILE\" <<<$'key2 = value2\\r'\n\n  [[ \"$(get_asdf_config_value \"key1\" 2>&1)\" = *\"contains carriage returns\"* ]]\n}\n\n@test \"prints if asdfrc config file has carriage returns\" {\n  cat >\".tool-versions\" <<<$'nodejs 19.6.0\\r'\n\n  [[ \"$(find_tool_versions 2>&1)\" = *\"contains carriage returns\"* ]]\n}\n"
  },
  {
    "path": "test/version_commands.bats",
    "content": "#!/usr/bin/env bats\n# shellcheck disable=SC2012,SC2030,SC2031,SC2164\n\nload test_helpers\n\nsetup() {\n  setup_asdf_dir\n  install_dummy_plugin\n  install_dummy_version \"1.0.0\"\n  install_dummy_version \"1.1.0\"\n  install_dummy_version \"2.0.0\"\n\n  install_dummy_legacy_plugin\n  install_dummy_legacy_version \"1.0.0\"\n  install_dummy_legacy_version \"1.1.0\"\n  install_dummy_legacy_version \"2.0.0\"\n  install_dummy_legacy_version \"5.1.0\"\n\n  PROJECT_DIR=\"$HOME/project\"\n  mkdir -p \"$PROJECT_DIR\"\n\n  CHILD_DIR=\"$PROJECT_DIR/child-dir\"\n  mkdir -p \"$CHILD_DIR\"\n\n  cd \"$PROJECT_DIR\"\n\n  # asdf lib needed to run asdf.sh\n  cp -rf \"$BATS_TEST_DIRNAME\"/../{bin,lib} \"$ASDF_DIR/\"\n}\n\nteardown() {\n  clean_asdf_dir\n}\n\n# Warn users who invoke the old style command without arguments.\n@test \"local should emit an error when called with incorrect arity\" {\n  run asdf local \"dummy\"\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"Usage: asdf local <name> <version>\" ]\n}\n\n@test \"local should emit an error when plugin does not exist\" {\n  run asdf local \"inexistent\" \"1.0.0\"\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"No such plugin: inexistent\" ]\n}\n\n@test \"local should emit an error when plugin version does not exist\" {\n  run asdf local \"dummy\" \"0.0.1\"\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"version 0.0.1 is not installed for dummy\" ]\n}\n\n@test \"local should create a local .tool-versions file if it doesn't exist\" {\n  run asdf local \"dummy\" \"1.1.0\"\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$PROJECT_DIR/.tool-versions\")\" = \"dummy 1.1.0\" ]\n}\n\n@test \"[local - dummy_plugin] with latest should use the latest installed version\" {\n  run asdf local \"dummy\" \"latest\"\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$PROJECT_DIR/.tool-versions\")\" = \"dummy 2.0.0\" ]\n}\n\n@test \"[local - dummy_plugin] with latest:version should use the latest valid installed version\" {\n  run asdf local \"dummy\" \"latest:1.0\"\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$PROJECT_DIR/.tool-versions\")\" = \"dummy 1.0.0\" ]\n}\n\n@test \"[local - dummy_plugin] with latest:version should return an error for invalid versions\" {\n  run asdf local \"dummy\" \"latest:99\"\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"No compatible versions available (dummy 99)\" ]\n}\n\n@test \"[local - dummy_legacy_plugin] with latest should use the latest installed version\" {\n  run asdf local \"legacy-dummy\" \"latest\"\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$PROJECT_DIR/.tool-versions\")\" = \"legacy-dummy 5.1.0\" ]\n}\n\n@test \"[local - dummy_legacy_plugin] with latest:version should use the latest valid installed version\" {\n  run asdf local \"legacy-dummy\" \"latest:1.0\"\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$PROJECT_DIR/.tool-versions\")\" = \"legacy-dummy 1.0.0\" ]\n}\n\n@test \"[local - dummy_legacy_plugin] with latest:version should return an error for invalid versions\" {\n  run asdf local \"legacy-dummy\" \"latest:99\"\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"No compatible versions available (legacy-dummy 99)\" ]\n}\n\n@test \"local should allow multiple versions\" {\n  run asdf local \"dummy\" \"1.1.0\" \"1.0.0\"\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$PROJECT_DIR/.tool-versions\")\" = \"dummy 1.1.0 1.0.0\" ]\n}\n\n@test \"local should create a local .tool-versions file if it doesn't exist when the directory name contains whitespace\" {\n  WHITESPACE_DIR=\"$PROJECT_DIR/whitespace\\ dir\"\n  mkdir -p \"$WHITESPACE_DIR\"\n  cd \"$WHITESPACE_DIR\"\n\n  run asdf local \"dummy\" \"1.1.0\"\n\n  tool_version_contents=$(cat \"$WHITESPACE_DIR/.tool-versions\")\n  [ \"$status\" -eq 0 ]\n  [ \"$tool_version_contents\" = \"dummy 1.1.0\" ]\n}\n\n@test \"local should not create a duplicate .tool-versions file if such file exists\" {\n  echo 'dummy 1.0.0' >>\"$PROJECT_DIR/.tool-versions\"\n\n  run asdf local \"dummy\" \"1.1.0\"\n  [ \"$status\" -eq 0 ]\n  [ \"$(ls \"$PROJECT_DIR/.tool-versions\"* | wc -l)\" -eq 1 ]\n}\n\n@test \"local should overwrite the existing version if it's set\" {\n  echo 'dummy 1.0.0' >>\"$PROJECT_DIR/.tool-versions\"\n\n  run asdf local \"dummy\" \"1.1.0\"\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$PROJECT_DIR/.tool-versions\")\" = \"dummy 1.1.0\" ]\n}\n\n@test \"local should append trailing newline before appending new version when missing\" {\n  echo -n 'foobar 1.0.0' >>\"$PROJECT_DIR/.tool-versions\"\n\n  run asdf local \"dummy\" \"1.1.0\"\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$PROJECT_DIR/.tool-versions\")\" = $'foobar 1.0.0\\ndummy 1.1.0' ]\n}\n\n@test \"local should not append trailing newline before appending new version when one present\" {\n  echo 'foobar 1.0.0' >>\"$PROJECT_DIR/.tool-versions\"\n\n  run asdf local \"dummy\" \"1.1.0\"\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$PROJECT_DIR/.tool-versions\")\" = $'foobar 1.0.0\\ndummy 1.1.0' ]\n}\n\n@test \"local should fail to set a path:dir if dir does not exists \" {\n  run asdf local \"dummy\" \"path:$PROJECT_DIR/local\"\n  [ \"$output\" = \"version path:$PROJECT_DIR/local is not installed for dummy\" ]\n  [ \"$status\" -eq 1 ]\n}\n\n@test \"local should set a path:dir if dir exists \" {\n  mkdir -p \"$PROJECT_DIR/local\"\n  run asdf local \"dummy\" \"path:$PROJECT_DIR/local\"\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$PROJECT_DIR/.tool-versions\")\" = \"dummy path:$PROJECT_DIR/local\" ]\n}\n\n@test \"local -p/--parent should set should emit an error when called with incorrect arity\" {\n  run asdf local -p \"dummy\"\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"Usage: asdf local <name> <version>\" ]\n}\n\n@test \"local -p/--parent should emit an error when plugin does not exist\" {\n  run asdf local -p \"inexistent\" \"1.0.0\"\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"No such plugin: inexistent\" ]\n}\n\n@test \"local -p/--parent should emit an error when plugin version does not exist\" {\n  run asdf local -p \"dummy\" \"0.0.1\"\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"version 0.0.1 is not installed for dummy\" ]\n}\n\n@test \"local -p/--parent should allow multiple versions\" {\n  cd \"$CHILD_DIR\"\n  touch \"$PROJECT_DIR/.tool-versions\"\n  run asdf local -p \"dummy\" \"1.1.0\" \"1.0.0\"\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$PROJECT_DIR/.tool-versions\")\" = \"dummy 1.1.0 1.0.0\" ]\n}\n\n@test \"local -p/--parent should overwrite the existing version if it's set\" {\n  cd \"$CHILD_DIR\"\n  echo 'dummy 1.0.0' >>\"$PROJECT_DIR/.tool-versions\"\n  run asdf local -p \"dummy\" \"1.1.0\"\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$PROJECT_DIR/.tool-versions\")\" = \"dummy 1.1.0\" ]\n}\n\n@test \"local -p/--parent should set the version if it's unset\" {\n  cd \"$CHILD_DIR\"\n  touch \"$PROJECT_DIR/.tool-versions\"\n  run asdf local -p \"dummy\" \"1.1.0\"\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$PROJECT_DIR/.tool-versions\")\" = \"dummy 1.1.0\" ]\n}\n\n@test \"global should create a global .tool-versions file if it doesn't exist\" {\n  run asdf global \"dummy\" \"1.1.0\"\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$HOME/.tool-versions\")\" = \"dummy 1.1.0\" ]\n}\n\n@test \"[global - dummy_plugin] with latest should use the latest installed version\" {\n  run asdf global \"dummy\" \"latest\"\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$HOME/.tool-versions\")\" = \"dummy 2.0.0\" ]\n}\n\n@test \"[global - dummy_plugin] with latest:version should use the latest valid installed version\" {\n  run asdf global \"dummy\" \"latest:1.0\"\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$HOME/.tool-versions\")\" = \"dummy 1.0.0\" ]\n}\n\n@test \"[global - dummy_plugin] with latest:version should return an error for invalid versions\" {\n  run asdf global \"dummy\" \"latest:99\"\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"No compatible versions available (dummy 99)\" ]\n}\n\n@test \"[global - dummy_legacy_plugin] with latest should use the latest installed version\" {\n  run asdf global \"legacy-dummy\" \"latest\"\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$HOME/.tool-versions\")\" = \"legacy-dummy 5.1.0\" ]\n}\n\n@test \"[global - dummy_legacy_plugin] with latest:version should use the latest valid installed version\" {\n  run asdf global \"legacy-dummy\" \"latest:1.0\"\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$HOME/.tool-versions\")\" = \"legacy-dummy 1.0.0\" ]\n}\n\n@test \"[global - dummy_legacy_plugin] with latest:version should return an error for invalid versions\" {\n  run asdf global \"legacy-dummy\" \"latest:99\"\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"No compatible versions available (legacy-dummy 99)\" ]\n}\n\n@test \"global should accept multiple versions\" {\n  run asdf global \"dummy\" \"1.1.0\" \"1.0.0\"\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$HOME/.tool-versions\")\" = \"dummy 1.1.0 1.0.0\" ]\n}\n\n@test \"global should overwrite the existing version if it's set\" {\n  echo 'dummy 1.0.0' >>\"$HOME/.tool-versions\"\n  run asdf global \"dummy\" \"1.1.0\"\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$HOME/.tool-versions\")\" = \"dummy 1.1.0\" ]\n}\n\n@test \"global should append trailing newline before appending new version when missing\" {\n  echo -n 'foobar 1.0.0' >>\"$HOME/.tool-versions\"\n\n  run asdf global \"dummy\" \"1.1.0\"\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$HOME/.tool-versions\")\" = $'foobar 1.0.0\\ndummy 1.1.0' ]\n}\n\n@test \"global should not append trailing newline before appending new version when one present\" {\n  echo 'foobar 1.0.0' >>\"$HOME/.tool-versions\"\n\n  run asdf global \"dummy\" \"1.1.0\"\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$HOME/.tool-versions\")\" = $'foobar 1.0.0\\ndummy 1.1.0' ]\n}\n\n@test \"global should fail to set a path:dir if dir does not exists \" {\n  run asdf global \"dummy\" \"path:$PROJECT_DIR/local\"\n  [ \"$output\" = \"version path:$PROJECT_DIR/local is not installed for dummy\" ]\n  [ \"$status\" -eq 1 ]\n}\n\n@test \"global should set a path:dir if dir exists \" {\n  mkdir -p \"$PROJECT_DIR/local\"\n  run asdf global \"dummy\" \"path:$PROJECT_DIR/local\"\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$HOME/.tool-versions\")\" = \"dummy path:$PROJECT_DIR/local\" ]\n}\n\n@test \"local should write to ASDF_TOOL_VERSIONS_FILENAME\" {\n  export ASDF_TOOL_VERSIONS_FILENAME=\"local-tool-versions\"\n  run asdf local \"dummy\" \"1.1.0\"\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$ASDF_TOOL_VERSIONS_FILENAME\")\" = \"dummy 1.1.0\" ]\n  [ -z \"$(cat .tool-versions)\" ]\n  unset ASDF_TOOL_VERSIONS_FILENAME\n}\n\n@test \"local should overwrite contents of ASDF_TOOL_VERSIONS_FILENAME if set\" {\n  export ASDF_TOOL_VERSIONS_FILENAME=\"local-tool-versions\"\n  echo 'dummy 1.0.0' >>\"$ASDF_TOOL_VERSIONS_FILENAME\"\n  run asdf local \"dummy\" \"1.1.0\"\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$ASDF_TOOL_VERSIONS_FILENAME\")\" = \"dummy 1.1.0\" ]\n  [ -z \"$(cat .tool-versions)\" ]\n  unset ASDF_TOOL_VERSIONS_FILENAME\n}\n\n@test \"global should write to ASDF_TOOL_VERSIONS_FILENAME\" {\n  export ASDF_TOOL_VERSIONS_FILENAME=\"global-tool-versions\"\n  run asdf global \"dummy\" \"1.1.0\"\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$HOME/$ASDF_TOOL_VERSIONS_FILENAME\")\" = \"dummy 1.1.0\" ]\n  [ -z \"$(cat \"$HOME/.tool-versions\")\" ]\n  unset ASDF_TOOL_VERSIONS_FILENAME\n}\n\n@test \"global should overwrite contents of ASDF_TOOL_VERSIONS_FILENAME if set\" {\n  export ASDF_TOOL_VERSIONS_FILENAME=\"global-tool-versions\"\n  echo 'dummy 1.0.0' >>\"$ASDF_TOOL_VERSIONS_FILENAME\"\n  run asdf global \"dummy\" \"1.1.0\"\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$HOME/$ASDF_TOOL_VERSIONS_FILENAME\")\" = \"dummy 1.1.0\" ]\n  [ -z \"$(cat \"$HOME/.tool-versions\")\" ]\n  unset ASDF_TOOL_VERSIONS_FILENAME\n}\n\n@test \"local should preserve symlinks when setting versions\" {\n  mkdir other-dir\n  touch other-dir/.tool-versions\n  ln -s other-dir/.tool-versions .tool-versions\n  run asdf local \"dummy\" \"1.1.0\"\n  [ \"$status\" -eq 0 ]\n  [ -L .tool-versions ]\n  [ \"$(cat other-dir/.tool-versions)\" = \"dummy 1.1.0\" ]\n}\n\n@test \"local should preserve symlinks when updating versions\" {\n  mkdir other-dir\n  touch other-dir/.tool-versions\n  ln -s other-dir/.tool-versions .tool-versions\n  run asdf local \"dummy\" \"1.1.0\"\n  run asdf local \"dummy\" \"1.1.0\"\n  [ \"$status\" -eq 0 ]\n  [ -L .tool-versions ]\n  [ \"$(cat other-dir/.tool-versions)\" = \"dummy 1.1.0\" ]\n}\n\n@test \"global should preserve symlinks when setting versions\" {\n  mkdir \"$HOME/other-dir\"\n  touch \"$HOME/other-dir/.tool-versions\"\n  ln -s other-dir/.tool-versions \"$HOME/.tool-versions\"\n\n  run asdf global \"dummy\" \"1.1.0\"\n  [ \"$status\" -eq 0 ]\n  [ -L \"$HOME/.tool-versions\" ]\n  [ \"$(cat \"$HOME/other-dir/.tool-versions\")\" = \"dummy 1.1.0\" ]\n}\n\n@test \"global should preserve symlinks when updating versions\" {\n  mkdir \"$HOME/other-dir\"\n  touch \"$HOME/other-dir/.tool-versions\"\n  ln -s other-dir/.tool-versions \"$HOME/.tool-versions\"\n\n  run asdf global \"dummy\" \"1.1.0\"\n  run asdf global \"dummy\" \"1.1.0\"\n  [ \"$status\" -eq 0 ]\n  [ -L \"$HOME/.tool-versions\" ]\n  [ \"$(cat \"$HOME/other-dir/.tool-versions\")\" = \"dummy 1.1.0\" ]\n}\n\n@test \"shell wrapper function should export ENV var\" {\n  . \"$(dirname \"$BATS_TEST_DIRNAME\")/asdf.sh\"\n  asdf shell \"dummy\" \"1.1.0\"\n  [ \"$ASDF_DUMMY_VERSION\" = \"1.1.0\" ]\n  unset ASDF_DUMMY_VERSION\n}\n\n@test \"shell wrapper function with --unset should unset ENV var\" {\n  . \"$(dirname \"$BATS_TEST_DIRNAME\")/asdf.sh\"\n  asdf shell \"dummy\" \"1.1.0\"\n  [ \"$ASDF_DUMMY_VERSION\" = \"1.1.0\" ]\n  asdf shell \"dummy\" --unset\n  [ -z \"$ASDF_DUMMY_VERSION\" ]\n  unset ASDF_DUMMY_VERSION\n}\n\n@test \"shell wrapper function should return an error for missing plugins\" {\n  . \"$(dirname \"$BATS_TEST_DIRNAME\")/asdf.sh\"\n  expected=\"No such plugin: nonexistent\nversion 1.0.0 is not installed for nonexistent\"\n\n  run asdf shell \"nonexistent\" \"1.0.0\"\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"$expected\" ]\n}\n\n@test \"shell should emit an error when wrapper function is not loaded\" {\n  run asdf shell \"dummy\" \"1.1.0\"\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"Shell integration is not enabled. Please ensure you source asdf in your shell setup.\" ]\n}\n\n@test \"export-shell-version should emit an error when plugin does not exist\" {\n  expected=\"No such plugin: nonexistent\nversion 1.0.0 is not installed for nonexistent\nfalse\"\n\n  run asdf export-shell-version sh \"nonexistent\" \"1.0.0\"\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"$expected\" ]\n}\n\n@test \"export-shell-version should emit an error when version does not exist\" {\n  expected=\"version nonexistent is not installed for dummy\nfalse\"\n\n  run asdf export-shell-version sh \"dummy\" \"nonexistent\"\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"$expected\" ]\n}\n\n@test \"export-shell-version should export version if it exists\" {\n  run asdf export-shell-version sh \"dummy\" \"1.1.0\"\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"export ASDF_DUMMY_VERSION=\\\"1.1.0\\\"\" ]\n}\n\n@test \"export-shell-version should use set when shell is fish\" {\n  run asdf export-shell-version fish \"dummy\" \"1.1.0\"\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"set -gx ASDF_DUMMY_VERSION \\\"1.1.0\\\"\" ]\n}\n\n@test \"export-shell-version should use set-env when shell is elvish\" {\n  run asdf export-shell-version elvish \"dummy\" \"1.1.0\"\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = $'set-env\\nASDF_DUMMY_VERSION\\n1.1.0' ]\n}\n\n@test \"export-shell-version should unset when --unset flag is passed\" {\n  run asdf export-shell-version sh \"dummy\" \"--unset\"\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"unset ASDF_DUMMY_VERSION\" ]\n}\n\n@test \"export-shell-version should use set -e when --unset flag is passed and shell is fish\" {\n  run asdf export-shell-version fish \"dummy\" \"--unset\"\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"set -e ASDF_DUMMY_VERSION\" ]\n}\n\n@test \"export-shell-version should use unset-env when --unset flag is passed and shell is elvish\" {\n  run asdf export-shell-version elvish \"dummy\" \"--unset\"\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = $'unset-env\\nASDF_DUMMY_VERSION' ]\n}\n\n@test \"[shell - dummy_plugin] wrapper function should support latest\" {\n  . \"$(dirname \"$BATS_TEST_DIRNAME\")/asdf.sh\"\n  asdf shell \"dummy\" \"latest\"\n  [ \"$ASDF_DUMMY_VERSION\" = \"2.0.0\" ]\n  unset ASDF_DUMMY_VERSION\n}\n\n@test \"[shell - dummy_legacy_plugin] wrapper function should support latest\" {\n  . \"$(dirname \"$BATS_TEST_DIRNAME\")/asdf.sh\"\n  asdf shell \"legacy-dummy\" \"latest\"\n  [ \"$ASDF_LEGACY_DUMMY_VERSION\" = \"5.1.0\" ]\n  unset ASDF_LEGACY_DUMMY_VERSION\n}\n\n@test \"[global - dummy_plugin] should support latest\" {\n  echo 'dummy 1.0.0' >>\"$HOME/.tool-versions\"\n  run asdf global \"dummy\" \"1.0.0\" \"latest\"\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$HOME/.tool-versions\")\" = \"dummy 1.0.0 2.0.0\" ]\n}\n\n@test \"[global - dummy_legacy_plugin] should support latest\" {\n  echo 'legacy-dummy 1.0.0' >>\"$HOME/.tool-versions\"\n  run asdf global \"legacy-dummy\" \"1.0.0\" \"latest\"\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$HOME/.tool-versions\")\" = \"legacy-dummy 1.0.0 5.1.0\" ]\n}\n\n@test \"[local - dummy_plugin] should support latest\" {\n  echo 'dummy 1.0.0' >>\"$PROJECT_DIR/.tool-versions\"\n  run asdf local \"dummy\" \"1.0.0\" \"latest\"\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$PROJECT_DIR/.tool-versions\")\" = \"dummy 1.0.0 2.0.0\" ]\n}\n\n@test \"[local - dummy_legacy_plugin] should support latest\" {\n  echo 'legacy-dummy 1.0.0' >>\"$PROJECT_DIR/.tool-versions\"\n  run asdf local \"legacy-dummy\" \"1.0.0\" \"latest\"\n  [ \"$status\" -eq 0 ]\n  [ \"$(cat \"$PROJECT_DIR/.tool-versions\")\" = \"legacy-dummy 1.0.0 5.1.0\" ]\n}\n"
  },
  {
    "path": "test/where_command.bats",
    "content": "#!/usr/bin/env bats\n\nload test_helpers\n\nsetup() {\n  setup_asdf_dir\n  install_dummy_plugin\n  install_dummy_version 1.0\n  install_dummy_version 2.1\n  install_dummy_version ref-master\n  cd \"$HOME\" || exit\n}\n\nteardown() {\n  clean_asdf_dir\n}\n\n@test \"where shows install location of selected version\" {\n  run asdf where 'dummy' '1.0'\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"$ASDF_DIR/installs/dummy/1.0\" ]\n}\n\n@test \"where understands versions installed by ref\" {\n  run asdf where 'dummy' 'ref:master'\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"$ASDF_DIR/installs/dummy/ref-master\" ]\n}\n\n@test \"where shows install location of current version if no version specified\" {\n  echo 'dummy 2.1' >>\"$HOME/.tool-versions\"\n\n  run asdf where 'dummy'\n\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"$ASDF_DIR/installs/dummy/2.1\" ]\n}\n\n@test \"where shows install location of first current version if not version specified and multiple current versions\" {\n  echo 'dummy 2.1 1.0' >>\"$HOME/.tool-versions\"\n  run asdf where 'dummy'\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"$ASDF_DIR/installs/dummy/2.1\" ]\n}\n\n@test \"where should error when the plugin doesn't exist\" {\n  run asdf where \"foobar\"\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"No such plugin: foobar\" ]\n}\n\n@test \"where should error when version is not installed\" {\n  run asdf where 'dummy' '1.6'\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"Version not installed\" ]\n}\n\n@test \"where should error when system version is set\" {\n  run asdf where 'dummy' 'system'\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"System version is selected\" ]\n}\n\n@test \"where should error when no current version selected and version not specified\" {\n  run asdf where 'dummy'\n\n  local expected\n  expected=\"No version is set for dummy; please run \\`asdf set [options] dummy <version>\\`\"\n\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"$expected\" ]\n}\n"
  },
  {
    "path": "test/which_command.bats",
    "content": "#!/usr/bin/env bats\n\nload test_helpers\n\nsetup() {\n  setup_asdf_dir\n  install_dummy_plugin\n  run asdf install dummy 1.0\n  run asdf install dummy 1.1\n\n  PROJECT_DIR=\"$HOME/project\"\n  mkdir -p \"$PROJECT_DIR\"\n  echo 'dummy 1.0' >>\"$PROJECT_DIR/.tool-versions\"\n}\n\nteardown() {\n  clean_asdf_dir\n}\n\n@test \"which should show dummy 1.0 main binary\" {\n  cd \"$PROJECT_DIR\"\n\n  run asdf which \"dummy\"\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"$ASDF_DIR/installs/dummy/1.0/bin/dummy\" ]\n}\n\n@test \"which should fail for unknown binary\" {\n  cd \"$PROJECT_DIR\"\n\n  run asdf which \"sunny\"\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"unknown command: sunny. Perhaps you have to reshim?\" ]\n}\n\n@test \"which should show dummy 1.0 other binary\" {\n  cd \"$PROJECT_DIR\"\n\n  echo \"echo bin bin/subdir\" >\"$ASDF_DIR/plugins/dummy/bin/list-bin-paths\"\n  chmod +x \"$ASDF_DIR/plugins/dummy/bin/list-bin-paths\"\n  run asdf reshim dummy 1.0\n\n  run asdf which \"other_bin\"\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"$ASDF_DIR/installs/dummy/1.0/bin/subdir/other_bin\" ]\n}\n\n@test \"which should show path of system version\" {\n  echo 'dummy system' >\"$PROJECT_DIR/.tool-versions\"\n  cd \"$PROJECT_DIR\"\n\n  mkdir \"$PROJECT_DIR/sys\"\n  touch \"$PROJECT_DIR/sys/dummy\"\n  chmod +x \"$PROJECT_DIR/sys/dummy\"\n\n  run env \"PATH=$PATH:$PROJECT_DIR/sys\" asdf which \"dummy\"\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"$PROJECT_DIR/sys/dummy\" ]\n}\n\n@test \"which report when missing executable on system version\" {\n  echo 'dummy system' >\"$PROJECT_DIR/.tool-versions\"\n  cd \"$PROJECT_DIR\"\n\n  run asdf which \"dummy\"\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"No dummy executable found for dummy system\" ]\n}\n\n@test \"which should inform when no binary is found\" {\n  cd \"$PROJECT_DIR\"\n\n  run asdf which \"bazbat\"\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"unknown command: bazbat. Perhaps you have to reshim?\" ]\n}\n\n@test \"which should use path returned by exec-path when present\" {\n  cd \"$PROJECT_DIR\"\n  install_dummy_exec_path_script \"dummy\"\n\n  run asdf which \"dummy\"\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"$ASDF_DIR/installs/dummy/1.0/bin/custom/dummy\" ]\n}\n\n@test \"which should return the path set by the legacy file\" {\n  cd \"$PROJECT_DIR\"\n\n  echo 'dummy 1.0' >>\"$HOME/.tool-versions\"\n  echo '1.1' >>\"$PROJECT_DIR/.dummy-version\"\n  rm \"$PROJECT_DIR/.tool-versions\"\n  echo 'legacy_version_file = yes' >\"$HOME/.asdfrc\"\n\n  run asdf which \"dummy\"\n  [ \"$status\" -eq 0 ]\n  [ \"$output\" = \"$ASDF_DIR/installs/dummy/1.1/bin/dummy\" ]\n}\n\n@test \"which should not return shim path\" {\n  cd \"$PROJECT_DIR\"\n  echo 'dummy 1.0' >\"$PROJECT_DIR/.tool-versions\"\n  rm \"$ASDF_DIR/installs/dummy/1.0/bin/dummy\"\n\n  run env PATH=\"$PATH:$ASDF_DIR/shims\" asdf which dummy\n  [ \"$status\" -eq 1 ]\n  [ \"$output\" = \"No dummy executable found for dummy 1.0\" ]\n}\n"
  },
  {
    "path": "tools.go",
    "content": "//go:build tools\n\npackage tools\n\nimport (\n\t_ \"github.com/mgechev/revive\"\n\t_ \"honnef.co/go/tools/cmd/staticcheck\"\n\t_ \"mvdan.cc/gofumpt\"\n)\n"
  },
  {
    "path": "version.txt",
    "content": "0.15.0\n"
  }
]