Full Code of sdkman/sdkman-cli for AI

master 532356b8e8a4 cached
117 files
268.7 KB
76.5k tokens
1 requests
Download .txt
Showing preview only (297K chars total). Download the full file or copy to clipboard to get everything.
Repository: sdkman/sdkman-cli
Branch: master
Commit: 532356b8e8a4
Files: 117
Total size: 268.7 KB

Directory structure:
gitextract_5nup9gj_/

├── .editorconfig
├── .github/
│   ├── CODEOWNERS
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   ├── config.yml
│   │   ├── feature_request.md
│   │   └── support_request.md
│   ├── auto_assign.yml
│   ├── dependabot.yml
│   ├── pull_request_template.md
│   └── workflows/
│       ├── beta.yml
│       ├── chloe-triage.yml
│       ├── notify-on-failure.yml
│       ├── pr.yml
│       └── release.yml
├── .gitignore
├── .sdkmanrc
├── CLAUDE.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Dockerfile
├── LICENSE
├── README.md
├── bin/
│   └── release-binary.sh
├── build.gradle
├── contrib/
│   └── completion/
│       └── bash/
│           └── sdk
├── dco.txt
├── gradle/
│   ├── archive.gradle
│   ├── release.gradle
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── settings.gradle
└── src/
    ├── jreleaser/
    │   └── changelog.tpl
    ├── main/
    │   └── bash/
    │       ├── sdkman-availability.sh
    │       ├── sdkman-cache.sh
    │       ├── sdkman-config.sh
    │       ├── sdkman-current.sh
    │       ├── sdkman-default.sh
    │       ├── sdkman-env-helpers.sh
    │       ├── sdkman-env.sh
    │       ├── sdkman-flush.sh
    │       ├── sdkman-help.sh
    │       ├── sdkman-home.sh
    │       ├── sdkman-init.sh
    │       ├── sdkman-install.sh
    │       ├── sdkman-list.sh
    │       ├── sdkman-main.sh
    │       ├── sdkman-offline.sh
    │       ├── sdkman-path-helpers.sh
    │       ├── sdkman-selfupdate.sh
    │       ├── sdkman-uninstall.sh
    │       ├── sdkman-update.sh
    │       ├── sdkman-upgrade.sh
    │       ├── sdkman-use.sh
    │       ├── sdkman-utils.sh
    │       └── sdkman-version.sh
    └── test/
        ├── groovy/
        │   └── sdkman/
        │       ├── cucumber/
        │       │   └── RunCukeTests.groovy
        │       ├── env/
        │       │   ├── BashEnv.groovy
        │       │   ├── CleanBashEnvBuilder.groovy
        │       │   └── SdkmanBashEnvBuilder.groovy
        │       ├── specs/
        │       │   ├── CandidatesCacheUpdateFailureSpec.groovy
        │       │   ├── CandidatesCacheUpdateSpec.groovy
        │       │   ├── CompletionSpec.groovy
        │       │   ├── ConfigCommandSpec.groovy
        │       │   ├── CurrentCommandSpec.groovy
        │       │   ├── EnvCommandSpec.groovy
        │       │   ├── InitialisationSpec.groovy
        │       │   ├── SdkCompatibilitySpec.groovy
        │       │   └── SelfupdateFeatureSpec.groovy
        │       ├── steps/
        │       │   ├── command_line_interop_steps.groovy
        │       │   ├── env.groovy
        │       │   ├── env_steps.groovy
        │       │   ├── flush_steps.groovy
        │       │   ├── initialisation_steps.groovy
        │       │   ├── installation_steps.groovy
        │       │   ├── stub_steps.groovy
        │       │   ├── update_steps.groovy
        │       │   └── use_steps.groovy
        │       ├── stubs/
        │       │   ├── CurlStub.groovy
        │       │   ├── HookResponses.groovy
        │       │   ├── UnameStub.groovy
        │       │   └── WebServiceStub.groovy
        │       └── support/
        │           ├── BashEnvSpecification.groovy
        │           ├── FilesystemUtils.groovy
        │           ├── SdkmanEnvSpecification.groovy
        │           ├── UnixUtils.groovy
        │           └── WireMockServerProvider.groovy
        ├── jmeter/
        │   ├── SDKman.jmx
        │   └── candidates.csv
        └── resources/
            ├── __files/
            │   └── hooks/
            │       ├── post_hook_java_8.0.101_linuxx64.sh
            │       ├── post_hook_java_8.0.111_linuxx64.sh
            │       └── post_hook_java_8.0.111_universal.sh
            └── features/
                ├── checksum_verification.feature
                ├── command_line_interop.feature
                ├── current_candidate.feature
                ├── default_version.feature
                ├── flush.feature
                ├── home.feature
                ├── hooks.feature
                ├── install_candidate.feature
                ├── install_sdkman.feature
                ├── java_installation.feature
                ├── list_candidate_versions.feature
                ├── list_candidates.feature
                ├── local_developement_versions.feature
                ├── mnemonics.feature
                ├── offline_mode.feature
                ├── path_initialisation.feature
                ├── per_project_configuration.feature
                ├── self_update.feature
                ├── service_unavailable.feature
                ├── uninstall_candidate.feature
                ├── update.feature
                ├── upgrade_candidate.feature
                ├── use_version.feature
                └── version.feature

================================================
FILE CONTENTS
================================================

================================================
FILE: .editorconfig
================================================
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = tab
insert_final_newline = false
max_line_length = 120
tab_width = 4
trim_trailing_whitespace = false
ij_continuation_indent_size = 8
ij_formatter_off_tag = @formatter:off
ij_formatter_on_tag = @formatter:on
ij_formatter_tags_enabled = false
ij_smart_tabs = false
ij_wrap_on_typing = false

[{*.yaml,*.yml}]
indent_size = 2

[{*.bash,*.sh,*.zsh}]
ij_shell_binary_ops_start_line = false
ij_shell_keep_column_alignment_padding = false
ij_shell_minify_program = false
ij_shell_redirect_followed_by_space = true
ij_shell_switch_cases_indented = false

[*.feature]
ij_gherkin_keep_indents_on_empty_lines = false

[{*.gant,*.gradle,*.groovy,*.gy}]
ij_groovy_align_group_field_declarations = false
ij_groovy_align_multiline_array_initializer_expression = false
ij_groovy_align_multiline_assignment = false
ij_groovy_align_multiline_binary_operation = false
ij_groovy_align_multiline_chained_methods = false
ij_groovy_align_multiline_extends_list = false
ij_groovy_align_multiline_for = true
ij_groovy_align_multiline_list_or_map = true
ij_groovy_align_multiline_method_parentheses = false
ij_groovy_align_multiline_parameters = true
ij_groovy_align_multiline_parameters_in_calls = false
ij_groovy_align_multiline_resources = true
ij_groovy_align_multiline_ternary_operation = false
ij_groovy_align_multiline_throws_list = false
ij_groovy_align_named_args_in_map = true
ij_groovy_align_throws_keyword = false
ij_groovy_array_initializer_new_line_after_left_brace = false
ij_groovy_array_initializer_right_brace_on_new_line = false
ij_groovy_array_initializer_wrap = off
ij_groovy_assert_statement_wrap = off
ij_groovy_assignment_wrap = off
ij_groovy_binary_operation_wrap = off
ij_groovy_blank_lines_after_class_header = 0
ij_groovy_blank_lines_after_imports = 1
ij_groovy_blank_lines_after_package = 1
ij_groovy_blank_lines_around_class = 1
ij_groovy_blank_lines_around_field = 0
ij_groovy_blank_lines_around_field_in_interface = 0
ij_groovy_blank_lines_around_method = 1
ij_groovy_blank_lines_around_method_in_interface = 1
ij_groovy_blank_lines_before_imports = 1
ij_groovy_blank_lines_before_method_body = 0
ij_groovy_blank_lines_before_package = 0
ij_groovy_block_brace_style = end_of_line
ij_groovy_block_comment_at_first_column = true
ij_groovy_call_parameters_new_line_after_left_paren = false
ij_groovy_call_parameters_right_paren_on_new_line = false
ij_groovy_call_parameters_wrap = off
ij_groovy_catch_on_new_line = false
ij_groovy_class_annotation_wrap = split_into_lines
ij_groovy_class_brace_style = end_of_line
ij_groovy_class_count_to_use_import_on_demand = 5
ij_groovy_do_while_brace_force = never
ij_groovy_else_on_new_line = false
ij_groovy_enum_constants_wrap = off
ij_groovy_extends_keyword_wrap = off
ij_groovy_extends_list_wrap = off
ij_groovy_field_annotation_wrap = split_into_lines
ij_groovy_finally_on_new_line = false
ij_groovy_for_brace_force = never
ij_groovy_for_statement_new_line_after_left_paren = false
ij_groovy_for_statement_right_paren_on_new_line = false
ij_groovy_for_statement_wrap = off
ij_groovy_if_brace_force = never
ij_groovy_import_annotation_wrap = 2
ij_groovy_indent_case_from_switch = true
ij_groovy_indent_label_blocks = true
ij_groovy_insert_inner_class_imports = false
ij_groovy_keep_blank_lines_before_right_brace = 2
ij_groovy_keep_blank_lines_in_code = 2
ij_groovy_keep_blank_lines_in_declarations = 2
ij_groovy_keep_control_statement_in_one_line = true
ij_groovy_keep_first_column_comment = true
ij_groovy_keep_indents_on_empty_lines = false
ij_groovy_keep_line_breaks = true
ij_groovy_keep_multiple_expressions_in_one_line = false
ij_groovy_keep_simple_blocks_in_one_line = false
ij_groovy_keep_simple_classes_in_one_line = true
ij_groovy_keep_simple_lambdas_in_one_line = true
ij_groovy_keep_simple_methods_in_one_line = true
ij_groovy_label_indent_absolute = false
ij_groovy_label_indent_size = 0
ij_groovy_lambda_brace_style = end_of_line
ij_groovy_layout_static_imports_separately = true
ij_groovy_line_comment_add_space = false
ij_groovy_line_comment_at_first_column = true
ij_groovy_method_annotation_wrap = split_into_lines
ij_groovy_method_brace_style = end_of_line
ij_groovy_method_call_chain_wrap = off
ij_groovy_method_parameters_new_line_after_left_paren = false
ij_groovy_method_parameters_right_paren_on_new_line = false
ij_groovy_method_parameters_wrap = off
ij_groovy_modifier_list_wrap = false
ij_groovy_names_count_to_use_import_on_demand = 3
ij_groovy_parameter_annotation_wrap = off
ij_groovy_parentheses_expression_new_line_after_left_paren = false
ij_groovy_parentheses_expression_right_paren_on_new_line = false
ij_groovy_prefer_parameters_wrap = false
ij_groovy_resource_list_new_line_after_left_paren = false
ij_groovy_resource_list_right_paren_on_new_line = false
ij_groovy_resource_list_wrap = off
ij_groovy_space_after_assert_separator = true
ij_groovy_space_after_colon = true
ij_groovy_space_after_comma = true
ij_groovy_space_after_comma_in_type_arguments = true
ij_groovy_space_after_for_semicolon = true
ij_groovy_space_after_quest = true
ij_groovy_space_after_type_cast = true
ij_groovy_space_before_annotation_parameter_list = false
ij_groovy_space_before_array_initializer_left_brace = false
ij_groovy_space_before_assert_separator = false
ij_groovy_space_before_catch_keyword = true
ij_groovy_space_before_catch_left_brace = true
ij_groovy_space_before_catch_parentheses = true
ij_groovy_space_before_class_left_brace = true
ij_groovy_space_before_closure_left_brace = true
ij_groovy_space_before_colon = true
ij_groovy_space_before_comma = false
ij_groovy_space_before_do_left_brace = true
ij_groovy_space_before_else_keyword = true
ij_groovy_space_before_else_left_brace = true
ij_groovy_space_before_finally_keyword = true
ij_groovy_space_before_finally_left_brace = true
ij_groovy_space_before_for_left_brace = true
ij_groovy_space_before_for_parentheses = true
ij_groovy_space_before_for_semicolon = false
ij_groovy_space_before_if_left_brace = true
ij_groovy_space_before_if_parentheses = true
ij_groovy_space_before_method_call_parentheses = false
ij_groovy_space_before_method_left_brace = true
ij_groovy_space_before_method_parentheses = false
ij_groovy_space_before_quest = true
ij_groovy_space_before_switch_left_brace = true
ij_groovy_space_before_switch_parentheses = true
ij_groovy_space_before_synchronized_left_brace = true
ij_groovy_space_before_synchronized_parentheses = true
ij_groovy_space_before_try_left_brace = true
ij_groovy_space_before_try_parentheses = true
ij_groovy_space_before_while_keyword = true
ij_groovy_space_before_while_left_brace = true
ij_groovy_space_before_while_parentheses = true
ij_groovy_space_in_named_argument = true
ij_groovy_space_in_named_argument_before_colon = false
ij_groovy_space_within_empty_array_initializer_braces = false
ij_groovy_space_within_empty_method_call_parentheses = false
ij_groovy_spaces_around_additive_operators = true
ij_groovy_spaces_around_assignment_operators = true
ij_groovy_spaces_around_bitwise_operators = true
ij_groovy_spaces_around_equality_operators = true
ij_groovy_spaces_around_lambda_arrow = true
ij_groovy_spaces_around_logical_operators = true
ij_groovy_spaces_around_multiplicative_operators = true
ij_groovy_spaces_around_regex_operators = true
ij_groovy_spaces_around_relational_operators = true
ij_groovy_spaces_around_shift_operators = true
ij_groovy_spaces_within_annotation_parentheses = false
ij_groovy_spaces_within_array_initializer_braces = false
ij_groovy_spaces_within_braces = true
ij_groovy_spaces_within_brackets = false
ij_groovy_spaces_within_cast_parentheses = false
ij_groovy_spaces_within_catch_parentheses = false
ij_groovy_spaces_within_for_parentheses = false
ij_groovy_spaces_within_gstring_injection_braces = false
ij_groovy_spaces_within_if_parentheses = false
ij_groovy_spaces_within_list_or_map = false
ij_groovy_spaces_within_method_call_parentheses = false
ij_groovy_spaces_within_method_parentheses = false
ij_groovy_spaces_within_parentheses = false
ij_groovy_spaces_within_switch_parentheses = false
ij_groovy_spaces_within_synchronized_parentheses = false
ij_groovy_spaces_within_try_parentheses = false
ij_groovy_spaces_within_tuple_expression = false
ij_groovy_spaces_within_while_parentheses = false
ij_groovy_special_else_if_treatment = true
ij_groovy_ternary_operation_wrap = off
ij_groovy_throws_keyword_wrap = off
ij_groovy_throws_list_wrap = off
ij_groovy_use_flying_geese_braces = false
ij_groovy_use_fq_class_names = false
ij_groovy_use_fq_class_names_in_javadoc = true
ij_groovy_use_relative_indents = false
ij_groovy_use_single_class_imports = true
ij_groovy_variable_annotation_wrap = off
ij_groovy_while_brace_force = never
ij_groovy_while_on_new_line = false
ij_groovy_wrap_long_lines = false


================================================
FILE: .github/CODEOWNERS
================================================
* @sdkman/sdkman-cli


================================================
FILE: .github/FUNDING.yml
================================================
open_collective: sdkman


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: You found a bug in SDKMAN!
title: "Bug: [A concise title]"
labels: 'bug'
assignees: ''

---
<!-- DO NOT DELETE THIS TEMPLATE AND PLEASE READ IT WITH CARE! -->
<!-- Thank you for using the SDKMAN! issue tracker. Please fill in everything to the best of your ability. If you need clarification on whether it is a bug, please use our #help Discord channel before creating a new issue. Please know that we cannot offer regular support through GitHub issues and will close the issue without warning if it is not a bug. -->

**Bug report**
<!-- A clear and concise description of the bug you encountered  -->

**To reproduce**
<!-- Steps to reproduce the behaviour or verify the issue -->

**System info**
<!-- Please add relevant information about your system:
- OS (e.g. Windows, Linux, Mac, Cygwin, WSL, etc.) and version
- Shell and version (e.g. `bash --version`/`zsh --version`)
- The output of `sdk version`
-->


================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
  - name: SDKMAN! usage documentation
    url: https://sdkman.io/usage
    about: Find documentation about the available commands here.
  - name: SDKMAN! Discord server
    url: https://discord.gg/y9mVJYVyu4
    about: Please ask and answer questions about the usage of SDKMAN! here.
  - name: SDKMAN! on Stack Overflow
    url: https://stackoverflow.com/questions/tagged/sdkman
    about: You might find help to common issues here.


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for SDKMAN!
title: 'Feature: [A concise title]'
labels: 'enhancement'
assignees: ''
---

<!-- Thank you for suggesting a new feature. Please discuss your idea on our #discussions Discord channel before requesting a new feature. -->

- [ ] I have read the [CONTRIBUTING guidelines](CONTRIBUTING.md)
- [ ] I've had a conversation on our community [Discord](https://discord.gg/y9mVJYVyu4) server.

**Feature request**
<!-- A clear and precise description of your desired new or changed feature. Please include the reason why you would need the feature. E.g. what problem does it solve? Or which workflow is currently frustrating and will be improved by this? -->



================================================
FILE: .github/ISSUE_TEMPLATE/support_request.md
================================================
---
name: Support request or question
about: You need help using SDKMAN!
title: "Question: [Short summary]"
labels: 'support'
assignees: ''

---
<!-- Thank you for using SDKMAN!. We want to help users using the software but also avoid filling the issue tracker with too many support requests. Please talk to us on our Discord server before posting.

Please consider the following things. Just so you know, ignoring any of these might lead to the issue being closed abruptly. -->

- [ ] I have checked [the documentation](https://sdkman.io/usage)
- [ ] I have performed a quick search in [past issues](https://github.com/sdkman/sdkman-cli/issues?q=) or [Stack Overflow](https://stackoverflow.com/questions/tagged/sdkman) for similar problems
- [ ] I have brought up a conversation in the community [Discord](https://discord.gg/y9mVJYVyu4) server

**Question**
<!-- A clear and concise description of the issue you are encountering -->

**System info**
<!-- Please add relevant information about your system:
- OS (e.g. Windows, Linux, Mac, Cygwin, WSL, etc.) and version
- Shell and version (e.g. `bash --version`/`zsh --version`)
- The output of `sdk version`
-->


================================================
FILE: .github/auto_assign.yml
================================================
---
addReviewers: true
addAssignees: author

reviewers:
  - marc0der
  - helpermethod

numberOfReviewers: 0


================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
  - package-ecosystem: github-actions
    directory: /
    schedule:
      interval: daily


================================================
FILE: .github/pull_request_template.md
================================================
<!-- To raise a new Pull Request, the following prerequisites need to be met. Please tick before proceeding: -->

- [ ] a GitHub Issue was opened for this feature / bug.
- [ ] test coverage was added (Cucumber or Spock as appropriate).

Fixes #XXX


================================================
FILE: .github/workflows/beta.yml
================================================
name: Beta Release
on:
  push:
    branches:
      - master
    paths-ignore:
      - .github/workflows/*

jobs:
  pre-release:
    name: "Beta Release"
    environment: production
    runs-on: "ubuntu-latest"
    services:
      mongodb:
        image: mongo:3.2
        ports:
          - 27017:27017
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-java@v4
        with:
          distribution: 'temurin'
          java-version: '11'
          cache: 'gradle'
      - name: Run tests
        run: ./gradlew clean test --info
      - name: Set short git hash
        id: var
        run: echo "::set-output name=sha_short::$(git rev-parse --short HEAD)"
      - name: Build artifacts
        run: ./gradlew -Penv=beta -Phash=${{ steps.var.outputs.sha_short }} clean assemble
      - name: Release
        if: github.repository == 'sdkman/sdkman-cli'
        uses: "marvinpinto/action-automatic-releases@latest"
        with:
          repo_token: "${{ secrets.GITHUB_TOKEN }}"
          automatic_release_tag: "latest"
          prerelease: true
          title: "Beta release"
          files: |
            build/distributions/sdkman-cli-*.zip
      - name: Update MongoDB
        if: github.repository == 'sdkman/sdkman-cli'
        env:
          MONGO_URL: ${{ secrets.MONGO_URL }}
          MONGO_USERNAME: ${{ secrets.MONGO_USERNAME }}
          MONGO_PASSWORD: ${{ secrets.MONGO_PASSWORD }}
          SHORT_HASH: ${{ steps.var.outputs.sha_short }}
        run: bin/release-binary.sh "$MONGO_URL" "$MONGO_USERNAME" "$MONGO_PASSWORD" "$SHORT_HASH" "latest"



================================================
FILE: .github/workflows/chloe-triage.yml
================================================
name: Notify Chloé (Issue Triage)

on:
  issues:
    types: [opened]
  issue_comment:
    types: [created]
  pull_request:
    types: [opened]

jobs:
  notify:
    uses: sdkman/.github/.github/workflows/reusable-triage.yml@main
    secrets: inherit


================================================
FILE: .github/workflows/notify-on-failure.yml
================================================
name: Notify on Failure

on:
  workflow_run:
    workflows: ["*"]
    branches: [master, main]
    types: [completed]

jobs:
  notify:
    uses: sdkman/.github/.github/workflows/reusable-notify-on-failure.yml@main
    secrets: inherit


================================================
FILE: .github/workflows/pr.yml
================================================
name: Pull Requests
#run only on PR, not fork. Pull request that way the PR submitter will not have access to the repo credentials 
on: pull_request
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout source code
        uses: actions/checkout@v3
        with:
            ref: ${{ github.event.pull_request.head.sha }}
      - uses: actions/setup-java@v4
        with:
          distribution: 'temurin'
          java-version: '11'
          cache: 'gradle'
      - name: Run with Gradle
        run: ./gradlew clean test --info
      - uses: kentaro-m/auto-assign-action@v1.2.5
        with:
          configuration-path: ".github/auto_assign.yml"


================================================
FILE: .github/workflows/release.yml
================================================
name: Release
on:
  workflow_dispatch:
    inputs:
      version:
        description: "Release version"
        required: true
jobs:
  build:
    name: "Stable Release"
    if: github.repository == 'sdkman/sdkman-cli'
    runs-on: ubuntu-latest
    environment: production
    env:
      JRELEASER_GITHUB_TOKEN: ${{ github.token }}
      JRELEASER_TWITTER_CONSUMER_KEY: ${{ secrets.TWITTER_CONSUMER_API_KEY }}
      JRELEASER_TWITTER_CONSUMER_SECRET: ${{ secrets.TWITTER_CONSUMER_API_SECRET }}
      JRELEASER_TWITTER_ACCESS_TOKEN: ${{ secrets.TWITTER_ACCESS_TOKEN }}
      JRELEASER_TWITTER_ACCESS_TOKEN_SECRET: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }}
      JRELEASER_MASTODON_ACCESS_TOKEN: ${{ secrets.MASTODON_ACCESS_TOKEN}}
    services:
      mongodb:
        image: mongo:3.2
        ports:
          - 27017:27017
    steps:
    - uses: actions/checkout@v3
      with:
        fetch-depth: 0
    - uses: actions/setup-java@v4
      with:
        distribution: 'temurin'
        java-version: '11'
        cache: 'gradle'
    - name: Run tests
      run: ./gradlew clean test --info
    - name: Build artifacts
      run: ./gradlew -Penv=stable -Prelease=${{ github.event.inputs.version }} clean assemble
    - name: Release
      run: ./gradlew -Penv=stable -Prelease=${{ github.event.inputs.version }} jreleaserFullRelease --exclude-announcer=twitter
    - name: Update MongoDB
      env:
        MONGO_URL: ${{ secrets.MONGO_URL }}
        MONGO_USERNAME: ${{ secrets.MONGO_USERNAME }}
        MONGO_PASSWORD: ${{ secrets.MONGO_PASSWORD }}
        RELEASE_TAG: ${{ github.event.inputs.version }}
      run: bin/release-binary.sh "$MONGO_URL" "$MONGO_USERNAME" "$MONGO_PASSWORD" "$RELEASE_TAG" "stable"
    - name: Tweet about it
      run: ./gradlew -Penv=stable -Prelease=${{ github.event.inputs.version }} jreleaserAnnounce



================================================
FILE: .gitignore
================================================
*~
.#*
.classpath
.gradle/
.gradletasknamecache
.idea/
.pid.lock
.project
.settings/
.vscode/
\#*
build/
classes
sdkman-cli.iml
sdkman.iml
sdkman.ipr
sdkman.iws
mongo.json
out/
sdkman-cli.iml
target/
bin/test

================================================
FILE: .sdkmanrc
================================================
# Enable auto-env through the sdkman_auto_env config
# Add key=value pairs of SDKs to use below
java=11.0.17-tem


================================================
FILE: CLAUDE.md
================================================
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

SDKMAN! CLI is a bash-based command-line tool for managing parallel versions of multiple Software Development Kits on Unix-based systems. The project is primarily written in bash scripts with Groovy/Spock for testing using Cucumber BDD tests.

**Important**: This project is in maintenance mode. Only bug fixes are accepted as the project is being rewritten in Rust under a new project. No new enhancements to existing commands will be accepted.

## Development Environment

- Requires JDK 11 (specified in `.sdkmanrc`)
- Uses Gradle 8.0.1 for build management
- Run `sdk env install` to install the correct JDK version
- Run `sdk env` to switch to the appropriate SDK versions

## Build and Test Commands

### Core Commands
- `./gradlew test` - Run all Cucumber BDD tests
- `./gradlew clean` - Clean build artifacts
- `./gradlew build` - Build the project

### Running Specific Tests
- Tests can be run with tags: use `@manual` and `@review` tags to exclude certain tests
- Individual feature files can be run by specifying the feature file path
- Environment can be set via `env` property (local/beta/stable) - defaults to local

### Development Setup
Before starting development, ensure the correct Java version:
```bash
sdk env install  # Install JDK from .sdkmanrc
sdk env          # Switch to correct SDK versions
```

## Architecture

### Code Structure
- `src/main/bash/` - Core bash scripts implementing SDKMAN commands
  - `sdkman-main.sh` - Main entry point and command routing
  - `sdkman-init.sh` - Environment initialization and platform detection
  - Individual command scripts (e.g., `sdkman-install.sh`, `sdkman-list.sh`)
- `src/test/groovy/` - Groovy test code using Spock framework
- `src/test/resources/features/` - Cucumber feature files (BDD tests)

### Core Components
- **Command Router**: `sdkman-main.sh` handles command aliases and routing
- **Environment Setup**: `sdkman-init.sh` manages platform detection and configuration
- **Modular Commands**: Each SDKMAN command is implemented in its own bash script
- **API Integration**: Commands interact with SDKMAN API for candidate information

### Testing Strategy
- **Cucumber BDD Tests**: Primary testing approach using Gherkin feature files
- **Spock Unit Tests**: Some Groovy unit tests for specific components
- Tests cover both happy path and edge cases
- Mock external dependencies using WireMock
- Test environment uses bash process execution

### Configuration
- `.sdkmanrc` file specifies required Java version (11.0.17-tem)
- Environment-specific API endpoints configured in `build.gradle`:
  - **local**: `http://localhost:8080/2` (for development)
  - **beta**: `https://beta.sdkman.io/2`
  - **stable**: `https://api.sdkman.io/2` (production)
- Pass environment via `-Penv=<environment>` to gradle commands

## Key Files
- `build.gradle` - Main build configuration with test dependencies
- `src/main/bash/sdkman-main.sh` - Command entry point and routing
- `src/test/groovy/sdkman/cucumber/RunCukeTests.groovy` - Cucumber test runner
- `src/test/resources/features/` - BDD feature specifications

## Testing Guidelines
- All features should have Cucumber tests covering happy and unhappy paths
- Tests should be written before implementation (TDD approach)
- Use `@manual` and `@review` tags to exclude certain tests from automation
- Mock external API calls using WireMock stubs

================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct

## Our Pledge

We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation.

We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.

## Our Standards

Examples of behavior that contributes to a positive environment for our community include:

* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall community

Examples of unacceptable behavior include:

* The use of sexualized language or imagery, and sexual attention or advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting

## Enforcement Responsibilities

Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.

Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.

## Scope

This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at info@sdkman.io. All complaints will be reviewed and investigated promptly and fairly.

All community leaders are obligated to respect the privacy and security of the reporter of any incident.

## Enforcement Guidelines

Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:

### 1. Correction

**Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.

**Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.

### 2. Warning

**Community Impact**: A violation through a single incident or series of actions.

**Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.

### 3. Temporary Ban

**Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.

**Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.

### 4. Permanent Ban

**Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.

**Consequence**: A permanent ban from any sort of public interaction within the community.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].

Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][Mozilla CoC].

For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at [https://www.contributor-covenant.org/translations][translations].

[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to SDKMAN! CLI

Thank you for your interest in contributing to SDKMAN! CLI. We greatly value the feedback and contributions of our users.

## Important Notice

**This project is in maintenance mode.** We are only accepting bug fixes at this time, as SDKMAN! CLI is being rewritten in Rust. New features and enhancements will not be accepted.

## Code of Conduct

This project adheres to the [Contributor Covenant Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to info@sdkman.io.

## How to Contribute

### Before You Start

We distinguish between:
- **Bug Reports**: Issues with existing functionality
- **Support Requests**: Questions about usage

We prefer to have a conversation on [SDKMAN Discord](https://discord.gg/y9mVJYVyu4) before creating new issues if you're unsure how to categorize your request. Join our Help channel to discuss.

### Reporting Bugs

1. **Search existing issues** to avoid duplicates
2. **Use the issue template** when creating a new issue via our [GitHub Issue Tracker](https://github.com/sdkman/sdkman-cli/issues/new)
3. **Provide detailed information** including:
   - Steps to reproduce the issue
   - Expected behavior
   - Actual behavior
   - Your environment (OS, shell, Java version)
   - Relevant logs or error messages

**Note:** Issues that don't follow the template may be closed.

### Submitting Pull Requests

Pull requests are always welcome but must follow these guidelines:

#### Prerequisites

1. **Link to an issue**: Every PR must reference a valid GitHub issue
2. **Discuss first**: Talk about your proposed fix on Discord or in the issue before starting work
3. **Keep it small**: Small, focused PRs are strongly preferred over large changes
4. **Include tests**: Each PR should include passing tests that prove its validity (where feasible)

#### Development Setup

1. **Install required tools**:
   ```bash
   sdk env install  # Install JDK 11 from .sdkmanrc
   sdk env          # Switch to correct SDK versions
   ```

2. **Verify your setup**:
   ```bash
   ./gradlew test
   ```

#### Development Workflow

1. **Fork the repository** and create a new branch from `master`
2. **Make your changes** following our code standards
3. **Write or update tests** to cover your changes
4. **Run the test suite** to ensure everything passes:
   ```bash
   ./gradlew test
   ```
5. **Commit your changes** using clear, descriptive commit messages
6. **Push to your fork** and submit a pull request

#### Pull Request Guidelines

- Fill in the PR template completely
- Link back to the GitHub issue by replacing `#XXX` with the issue number
- Ensure all tests pass
- Keep commits focused and atomic
- Write clear commit messages in imperative mood (e.g., "fix user login bug")
- Be responsive to feedback during code review

#### Testing Requirements

- All bug fixes must include a test that would have caught the bug
- Tests should follow the existing Cucumber BDD pattern
- Tests should cover both happy path and edge cases
- Run `./gradlew test` to verify all tests pass

### Code Standards

- Follow the existing code style in bash scripts
- Use meaningful variable and function names
- Add comments for complex logic
- Avoid introducing new dependencies without discussion
- Ensure backward compatibility

### Getting Help

- Join our [Discord server](https://discord.gg/y9mVJYVyu4) for questions
- Check existing issues and discussions
- Read the documentation in the repository

## Project Structure

- `src/main/bash/` - Core bash scripts implementing SDKMAN commands
- `src/test/groovy/` - Groovy test code using Spock framework
- `src/test/resources/features/` - Cucumber feature files (BDD tests)

## Recognition

Contributors will be recognized in our release notes and commit history. We appreciate your efforts to make SDKMAN! better!

## Developer Certificate of Origin

By contributing to this project, you agree to the terms of the [Developer Certificate of Origin](dco.txt). This certifies that you have the right to submit your contribution under the project's open source license.

## Questions?

If you have questions about contributing, feel free to ask on [Discord](https://discord.gg/y9mVJYVyu4) or open a discussion on GitHub.


================================================
FILE: Dockerfile
================================================
FROM openjdk:11

RUN apt-get update && \
  apt-get -y install zip

# Copy all here
RUN mkdir -p /usr/src/app
ADD . /usr/src/app
WORKDIR /usr/src/app

ENTRYPOINT ["./gradlew"]


================================================
FILE: LICENSE
================================================

                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS


================================================
FILE: README.md
================================================
# SDKMAN! CLI
### The Software Development Kit Manager Command Line Interface

[![Backers on Open Collective](https://img.shields.io/opencollective/backers/sdkman)](#backers) 
[![Sponsors on Open Collective](https://img.shields.io/opencollective/sponsors/sdkman)](#sponsors)
[![Discord](https://img.shields.io/discord/1245471991117512754)](https://discord.gg/y9mVJYVyu4)

SDKMAN is a tool for managing parallel Versions of multiple Software Development Kits on any Unix-based system. It provides a convenient command-line interface for installing, switching, removing, and listing Candidates.

See documentation on the [SDKMAN! website](https://sdkman.io).

## NOTICE

**We are rewriting all the commands for SDKMAN! in [Rust](https://www.rust-lang.org/) under a [new project](https://github.com/sdkman/sdkman-cli-native) that supplements this one. Only bug fixes to supporting code will be
accepted in this project. As a result, no further enhancements will be accepted on the commands in this project, and the commands here will be phased out in due course. This project will eventually form a lightweight
wrapper/launcher for the replacement Rust commands.**

## Installation

Open your favourite terminal and enter the following:

    $ curl -s https://get.sdkman.io | bash

If the environment needs tweaking for SDKMAN to be installed, the installer will prompt you accordingly and ask you to restart.

## Running the Cucumber Features

All SDKMAN's BDD tests describing the CLI behaviour are written in Cucumber and can be found under `src/test/resources/features`. These can be run with Gradle by running the following command:

    $ ./gradlew test

To perform development, you will need to have a JDK 11 installed which can be obtained by running the following after installing SDKMAN:

    $ sdk env install

## Hosting

We're proud to host our backend services on DigitalOcean as a sponsored partner.

[![DigitalOcean Referral Badge](https://web-platforms.sfo2.cdn.digitaloceanspaces.com/WWW/Badge%203.svg)](https://www.digitalocean.com/?refcode=d99e5747251d&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge)

## Contributors

This project exists thanks to all the people who contribute. 
<a href="https://github.com/sdkman/sdkman-cli/graphs/contributors"><img src="https://opencollective.com/sdkman/contributors.svg?width=890&button=false" /></a>

## Backers

Thank you to all our backers! [[Become a backer](https://opencollective.com/sdkman#backer)]

<a href="https://opencollective.com/sdkman#backers" target="_blank"><img src="https://opencollective.com/sdkman/backers.svg?width=890"></a>


## Sponsors

Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/sdkman#sponsor)]

<a href="https://opencollective.com/sdkman/sponsor/0/website" target="_blank"><img src="https://opencollective.com/sdkman/sponsor/0/avatar.svg"></a>
<a href="https://opencollective.com/sdkman/sponsor/1/website" target="_blank"><img src="https://opencollective.com/sdkman/sponsor/1/avatar.svg"></a>
<a href="https://opencollective.com/sdkman/sponsor/2/website" target="_blank"><img src="https://opencollective.com/sdkman/sponsor/2/avatar.svg"></a>
<a href="https://opencollective.com/sdkman/sponsor/3/website" target="_blank"><img src="https://opencollective.com/sdkman/sponsor/3/avatar.svg"></a>
<a href="https://opencollective.com/sdkman/sponsor/4/website" target="_blank"><img src="https://opencollective.com/sdkman/sponsor/4/avatar.svg"></a>
<a href="https://opencollective.com/sdkman/sponsor/5/website" target="_blank"><img src="https://opencollective.com/sdkman/sponsor/5/avatar.svg"></a>
<a href="https://opencollective.com/sdkman/sponsor/6/website" target="_blank"><img src="https://opencollective.com/sdkman/sponsor/6/avatar.svg"></a>
<a href="https://opencollective.com/sdkman/sponsor/7/website" target="_blank"><img src="https://opencollective.com/sdkman/sponsor/7/avatar.svg"></a>
<a href="https://opencollective.com/sdkman/sponsor/8/website" target="_blank"><img src="https://opencollective.com/sdkman/sponsor/8/avatar.svg"></a>
<a href="https://opencollective.com/sdkman/sponsor/9/website" target="_blank"><img src="https://opencollective.com/sdkman/sponsor/9/avatar.svg"></a>


================================================
FILE: bin/release-binary.sh
================================================
#!/usr/bin/env bash

MONGO_URL="$1"
MONGO_USERNAME="$2"
MONGO_PASSWORD="$3"
PARAM_1="$4"
PARAM_2="$5"

echo "Mongo URL: $MONGO_URL"

if [[ -z "$MONGO_USERNAME" || -z "$MONGO_PASSWORD" ]]; then
	echo "No mongo credentials so doing nothing..."
	return 1
fi

if [[ "$PARAM_2" == 'stable' ]]; then
	FIELD="stableCliVersion"
	VERSION="$PARAM_1"
elif [[ "$PARAM_2" == 'latest' ]]; then
	FIELD="betaCliVersion"
	VERSION="latest+$PARAM_1"
else
	return 1
fi

echo "Release: $FIELD as $VERSION"

docker run mongo:3.2 mongo "${MONGO_URL}" \
	--username="${MONGO_USERNAME}" \
	--password="${MONGO_PASSWORD}" \
	--quiet \
	--eval "db.application.updateOne({}, {\$set: { \"$FIELD\": \"$VERSION\"}});"



================================================
FILE: build.gradle
================================================
plugins {
	id('groovy')
	id('org.jreleaser').version('1.4.0').apply(false)
}

String userHome = System.getProperty('user.home')
ext.installBinDir = "${userHome}/.sdkman/bin"
ext.installSrcDir = "${userHome}/.sdkman/src"
ext.installContribDir = "${userHome}/.sdkman/contrib"

ext.environment = hasProperty('env') ? env : 'local'
ext.hash = hasProperty('hash') ? hash : 'hashme'
ext.release = hasProperty('release') ? release : 'latest'

if ("$environment" == 'stable') {
	ext.candidatesApi = 'https://api.sdkman.io/2'
	ext.brokerApi = 'https://broker.sdkman.io'
} else if ("$environment" == 'beta') {
	ext.candidatesApi = 'https://beta.sdkman.io/2'
	ext.brokerApi = 'https://broker.sdkman.io'
} else {
	ext.candidatesApi = 'http://localhost:8080/2'
	ext.brokerApi = 'https://localhost:8080'
}

ext.sdkmanVersion = ext.release == 'latest' ? "latest+${ext.hash}".toString() : ext.release

println("Environment is set to: $environment")
println("Short git hash: $hash")
println("Release set to: $release")
println("Candidates API: $candidatesApi")
println("Broker API: $brokerApi")
println("Version: $sdkmanVersion")

apply from: 'gradle/archive.gradle'
apply from: 'gradle/release.gradle'

repositories {
	mavenCentral()
}

dependencies {
	testImplementation('com.github.tomakehurst:wiremock:2.27.2') {
		exclude module: 'junit'
	}
	testImplementation('io.cucumber:cucumber-groovy:4.7.1')
	testImplementation('io.cucumber:cucumber-junit:4.7.4')
	testImplementation('io.cucumber:gherkin:5.2.0')
	testImplementation('junit:junit:4.13.2')
	testImplementation('org.codehaus.groovy:groovy:2.4.21')
	testImplementation('org.codehaus.groovy:groovy-json:2.4.21')
	testImplementation('org.codehaus.groovy:groovy-templates:2.4.21')
	testImplementation('org.slf4j:slf4j-api:1.7.32')
	testImplementation('org.slf4j:slf4j-simple:1.7.32')
	testImplementation('org.spockframework:spock-core:1.3-groovy-2.4') {
		exclude module: 'groovy-all'
	}
}


================================================
FILE: contrib/completion/bash/sdk
================================================
#!/usr/bin/bash

_sdk() {
	local -r previous_word=${COMP_WORDS[COMP_CWORD - 1]}
	local -r current_word=${COMP_WORDS[COMP_CWORD]}

	if ((COMP_CWORD == 3)); then
		local -r before_previous_word=${COMP_WORDS[COMP_CWORD - 2]}

		__sdkman_complete_candidate_version "$before_previous_word" "$previous_word" "$current_word"

		return
	fi

	__sdkman_complete_command "$previous_word" "$current_word"
}

__sdkman_complete_command() {
	local -r command=$1
	local -r current_word=$2

	local -a candidates

	case $command in
		sdk)
			candidates=("install" "uninstall" "list" "use" "config" "default" "home" "env" "current" "upgrade" "version" "help" "offline" "selfupdate" "update" "flush")
			;;
		current|c|default|d|home|h|uninstall|rm|upgrade|ug|use|u)
			local -r candidate_paths=("${SDKMAN_CANDIDATES_DIR}"/*)

			for candidate_path in "${candidate_paths[@]}"; do
				candidates+=("${candidate_path##*/}")
			done
			;;
		install|i|list|ls)
			candidates=${SDKMAN_CANDIDATES[@]}
			;;
		env|e)
			candidates=("init" "install" "clear")
			;;
		offline)
			candidates=("enable" "disable")
			;;
		selfupdate)
			candidates=("force")
			;;
		flush)
			candidates=("temp" "version")
			;;
	esac

	COMPREPLY=($(compgen -W "${candidates[*]}" -- "$current_word"))
}

__sdkman_complete_candidate_version() {
	local -r command=$1
	local -r candidate=$2
	local -r candidate_version=$3

	local -a candidates

	case $command in
		default|d|home|h|uninstall|rm|use|u)
			local -r version_paths=("${SDKMAN_CANDIDATES_DIR}/${candidate}"/*)

			for version_path in "${version_paths[@]}"; do
				[[ $version_path = *current ]] && continue

				candidates+=("${version_path##*/}")
			done
			;;
		install|i)
			while IFS= read -r -d, version || [[ -n "$version" ]]; do
				candidates+=("$version")
			done <<< "$(curl --silent "${SDKMAN_CANDIDATES_API}/candidates/$candidate/${SDKMAN_PLATFORM}/versions/all")"
			;;
	esac

	COMPREPLY=($(compgen -W "${candidates[*]}" -- "$candidate_version"))
}

complete -o default -F _sdk sdk

# Set 'sdkman_auto_complete' to 'true' in .sdkman/etc/config to enable completion



================================================
FILE: dco.txt
================================================
Developer Certificate of Origin
Version 1.1

Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
1 Letterman Drive
Suite D4700
San Francisco, CA, 94129

Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.


Developer's Certificate of Origin 1.1

By making a contribution to this project, I certify that:

(a) The contribution was created in whole or in part by me and I
    have the right to submit it under the open source license
    indicated in the file; or

(b) The contribution is based upon previous work that, to the best
    of my knowledge, is covered under an appropriate open source
    license and I have the right under that license to submit that
    work with modifications, whether created in whole or in part
    by me, under the same open source license (unless I am
    permitted to submit under a different license), as indicated
    in the file; or

(c) The contribution was provided directly to me by some other
    person who certified (a), (b) or (c) and I have not modified
    it.

(d) I understand and agree that this project and the contribution
    are public and that a record of the contribution (including all
    personal information I submit with it, including my sign-off) is
    maintained indefinitely and may be redistributed consistent with
    this project or the open source license(s) involved.


================================================
FILE: gradle/archive.gradle
================================================
import org.apache.tools.ant.filters.ReplaceTokens
import org.gradle.api.tasks.testing.logging.TestExceptionFormat

def baseDir = "$buildDir/stage/sdkman-${ext.sdkmanVersion}"

task prepareBin(type: Copy) {
	from('src/main/bash')
	into("$baseDir/bin")
	include('**/sdkman-init.sh')
	filter(
			ReplaceTokens,
			tokens: [
					SDKMAN_CANDIDATES_API: candidatesApi,
					SDKMAN_BROKER_API: brokerApi
			]
	)
}

task prepareScripts(type: Copy) {
	from('src/main/bash')
	into("$baseDir/src")
	include('**/*')
	exclude('**/sdkman-init.sh')
}

task prepareContrib(type: Copy) {
	from('contrib')
	into("$baseDir/contrib")
}

tasks.test.configure {
	dependsOn(prepareScripts)
	testLogging.exceptionFormat = TestExceptionFormat.FULL
}

task assembleArchive(type: Zip, dependsOn: [prepareBin, prepareScripts, prepareContrib]) {
	archiveVersion = sdkmanVersion
	from('build/stage') {
		include('**/*')
	}
}

tasks.assemble.configure {
	dependsOn(assembleArchive)
}

tasks.test.configure {
	dependsOn(prepareBin, prepareScripts, prepareContrib)
}


================================================
FILE: gradle/release.gradle
================================================
apply plugin: 'org.jreleaser'

jreleaser {
	project {
		name = 'sdkman-cli'
		version = sdkmanVersion
		description = 'Sdkman - The Software Development Kit Manager'
		website = 'https://sdkman.io'
		authors = ['Marco Vermeulen']
		license = 'Apache-2.0'
		extraProperties.put('inceptionYear', '2012')
	}
	
	release {
		github {
			overwrite = true
			tagName = '{{projectVersion}}'
			changelog {
				formatted = 'ALWAYS'
				format = '- {{commitShortHash}} {{commitTitle}}'
				contributors {
					format = '- {{contributorName}}'
				}
				contentTemplate = file('src/jreleaser/changelog.tpl')
				hide {
					contributors = ['GitHub']
				}
			}
		}
	}
	
	distributions {
		'sdkman-cli' {
			distributionType = 'BINARY'
			artifact {
				path = "build/distributions/{{distributionName}}-${sdkmanVersion}.zip"
				extraProperties.put("universal", "true")
			}
		}
	}
	
	announce {
		twitter {
			active = 'RELEASE'
			status = 'Released version {{tagName}} of SDKMAN! {{releaseNotesUrl}}'
		}
		mastodon {
			active = 'RELEASE'
			status = 'Released version {{tagName}} of SDKMAN! {{releaseNotesUrl}}'
			host = 'https://fosstodon.org'
		}
	}
}

================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists


================================================
FILE: gradlew
================================================
#!/usr/bin/env sh

#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

##############################################################################
##
##  Gradle start up script for UN*X
##
##############################################################################

# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
    ls=`ls -ld "$PRG"`
    link=`expr "$ls" : '.*-> \(.*\)$'`
    if expr "$link" : '/.*' > /dev/null; then
        PRG="$link"
    else
        PRG=`dirname "$PRG"`"/$link"
    fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null

APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`

# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'

# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"

warn () {
    echo "$*"
}

die () {
    echo
    echo "$*"
    echo
    exit 1
}

# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
  CYGWIN* )
    cygwin=true
    ;;
  Darwin* )
    darwin=true
    ;;
  MINGW* )
    msys=true
    ;;
  NONSTOP* )
    nonstop=true
    ;;
esac

CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar


# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
        # IBM's JDK on AIX uses strange locations for the executables
        JAVACMD="$JAVA_HOME/jre/sh/java"
    else
        JAVACMD="$JAVA_HOME/bin/java"
    fi
    if [ ! -x "$JAVACMD" ] ; then
        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
    fi
else
    JAVACMD="java"
    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi

# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
    MAX_FD_LIMIT=`ulimit -H -n`
    if [ $? -eq 0 ] ; then
        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
            MAX_FD="$MAX_FD_LIMIT"
        fi
        ulimit -n $MAX_FD
        if [ $? -ne 0 ] ; then
            warn "Could not set maximum file descriptor limit: $MAX_FD"
        fi
    else
        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
    fi
fi

# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi

# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`

    JAVACMD=`cygpath --unix "$JAVACMD"`

    # We build the pattern for arguments to be converted via cygpath
    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
    SEP=""
    for dir in $ROOTDIRSRAW ; do
        ROOTDIRS="$ROOTDIRS$SEP$dir"
        SEP="|"
    done
    OURCYGPATTERN="(^($ROOTDIRS))"
    # Add a user-defined pattern to the cygpath arguments
    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
    fi
    # Now convert the arguments - kludge to limit ourselves to /bin/sh
    i=0
    for arg in "$@" ; do
        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option

        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
        else
            eval `echo args$i`="\"$arg\""
        fi
        i=`expr $i + 1`
    done
    case $i in
        0) set -- ;;
        1) set -- "$args0" ;;
        2) set -- "$args0" "$args1" ;;
        3) set -- "$args0" "$args1" "$args2" ;;
        4) set -- "$args0" "$args1" "$args2" "$args3" ;;
        5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
        6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
        7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
        8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
        9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
    esac
fi

# Escape application args
save () {
    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
    echo " "
}
APP_ARGS=`save "$@"`

# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"

exec "$JAVACMD" "$@"


================================================
FILE: settings.gradle
================================================
pluginManagement {
	repositories {
		mavenLocal()
		gradlePluginPortal()
		mavenCentral()
	}
}

rootProject.name = "sdkman-cli"


================================================
FILE: src/jreleaser/changelog.tpl
================================================
## Changelog

{{changelogChanges}}
{{changelogContributors}}


================================================
FILE: src/main/bash/sdkman-availability.sh
================================================
#!/usr/bin/env bash

#
#   Copyright 2021 Marco Vermeulen
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#

function __sdkman_update_service_availability() {
	local healthcheck_status=$(__sdkman_determine_healthcheck_status)
	__sdkman_set_availability "$healthcheck_status"
}

function __sdkman_determine_healthcheck_status() {
	if [[ "$SDKMAN_OFFLINE_MODE" == "true" || "$COMMAND" == "offline" && "$QUALIFIER" == "enable" ]]; then
		echo ""
	else
		echo $(__sdkman_secure_curl_with_timeouts "${SDKMAN_CANDIDATES_API}/healthcheck")
	fi
}

function __sdkman_set_availability() {
	local healthcheck_status="$1"
	local detect_html="$(echo "$healthcheck_status" | tr '[:upper:]' '[:lower:]' | grep 'html')"
	if [[ -z "$healthcheck_status" ]]; then
		SDKMAN_AVAILABLE="false"
		__sdkman_display_offline_warning "$healthcheck_status"
	elif [[ -n "$detect_html" ]]; then
		SDKMAN_AVAILABLE="false"
		__sdkman_display_proxy_warning
	else
		SDKMAN_AVAILABLE="true"
	fi
}

function __sdkman_display_offline_warning() {
	local healthcheck_status="$1"
	if [[ -z "$healthcheck_status" && "$COMMAND" != "offline" && "$SDKMAN_OFFLINE_MODE" != "true" ]]; then
		__sdkman_echo_red "==== INTERNET NOT REACHABLE! ==================================================="
		__sdkman_echo_red ""
		__sdkman_echo_red " Some functionality is disabled or only partially available."
		__sdkman_echo_red " If this persists, please enable the offline mode:"
		__sdkman_echo_red ""
		__sdkman_echo_red "   $ sdk offline"
		__sdkman_echo_red ""
		__sdkman_echo_red "================================================================================"
		echo ""
	fi
}

function __sdkman_display_proxy_warning() {
	__sdkman_echo_red "==== PROXY DETECTED! ==========================================================="
	__sdkman_echo_red "Please ensure you have open internet access to continue."
	__sdkman_echo_red "================================================================================"
	echo ""
}


================================================
FILE: src/main/bash/sdkman-cache.sh
================================================
#!/usr/bin/env bash

#
#   Copyright 2021 Marco Vermeulen
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#

function ___sdkman_check_candidates_cache() {
	local candidates_cache="$1"
	if [[ -f "$candidates_cache" && -z "$(< "$candidates_cache")" ]]; then
		__sdkman_echo_red 'WARNING: Cache is corrupt. SDKMAN cannot be used until updated.'
		echo ''
		__sdkman_echo_no_colour '  $ sdk update'
		echo ''
		return 1
	else
		__sdkman_echo_debug "Using existing cache: $SDKMAN_CANDIDATES_CSV"
		return 0
	fi
}


================================================
FILE: src/main/bash/sdkman-config.sh
================================================
#!/usr/bin/env bash

#
#   Copyright 2021 Marco Vermeulen
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#

function __sdk_config() {
	local -r editor=(${EDITOR:=vi})

	if ! command -v "${editor[@]}" > /dev/null; then
		__sdkman_echo_red "No default editor configured."
		__sdkman_echo_yellow "Please set the default editor with the EDITOR environment variable."

		return 1
	fi

	"${editor[@]}" "${SDKMAN_DIR}/etc/config"
}

================================================
FILE: src/main/bash/sdkman-current.sh
================================================
#!/usr/bin/env bash

#
#   Copyright 2021 Marco Vermeulen
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#

function __sdk_current() {
	local candidate="$1"

	echo ""
	if [ -n "$candidate" ]; then
		__sdkman_determine_current_version "$candidate"
		if [ -n "$CURRENT" ]; then
			__sdkman_echo_no_colour "Using ${candidate} version ${CURRENT}"
		else
			__sdkman_echo_red "Not using any version of ${candidate}"
		fi
	else
		local installed_count=0
		for ((i = 0; i <= ${#SDKMAN_CANDIDATES[*]}; i++)); do
			# Eliminate empty entries due to incompatibility
			if [[ -n ${SDKMAN_CANDIDATES[${i}]} ]]; then
				__sdkman_determine_current_version "${SDKMAN_CANDIDATES[${i}]}"
				if [ -n "$CURRENT" ]; then
					if [ ${installed_count} -eq 0 ]; then
						__sdkman_echo_no_colour 'Using:'
						echo ""
					fi
					__sdkman_echo_no_colour "${SDKMAN_CANDIDATES[${i}]}: ${CURRENT}"
					((installed_count += 1))
				fi
			fi
		done

		if [ ${installed_count} -eq 0 ]; then
			__sdkman_echo_no_colour 'No candidates are in use'
		fi
	fi
}

function __sdkman_determine_current_version() {
	local candidate present

	candidate="$1"
	present=$(__sdkman_path_contains "${SDKMAN_CANDIDATES_DIR}/${candidate}")
	if [[ "$present" == 'true' ]]; then
		if [[ $PATH =~ ${SDKMAN_CANDIDATES_DIR}/${candidate}/([^/]+)/bin ]]; then
			if [[ "$zsh_shell" == "true" ]]; then
				CURRENT=${match[1]}
			else
				CURRENT=${BASH_REMATCH[1]}
			fi
		fi

		if [[ "$CURRENT" == "current" ]]; then
			CURRENT=$(readlink "${SDKMAN_CANDIDATES_DIR}/${candidate}/current" | sed "s!${SDKMAN_CANDIDATES_DIR}/${candidate}/!!g")
		fi
	else
		CURRENT=""
	fi
}


================================================
FILE: src/main/bash/sdkman-default.sh
================================================
#!/usr/bin/env bash

#
#   Copyright 2021 Marco Vermeulen
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#

function __sdk_default() {
	__sdkman_deprecation_notice "default"
	local candidate version

	candidate="$1"
	version="$2"

	__sdkman_check_candidate_present "$candidate" || return 1
	__sdkman_determine_version "$candidate" "$version" || return 1

	if [ ! -d "${SDKMAN_CANDIDATES_DIR}/${candidate}/${VERSION}" ]; then
		echo ""
		__sdkman_echo_red "Stop! Candidate version is not installed."
		echo ""
		__sdkman_echo_yellow "Tip: Run the following to install this version"
		echo ""
		__sdkman_echo_yellow "$ sdk install ${candidate} ${VERSION}"
		return 1
	fi

	__sdkman_link_candidate_version "$candidate" "$VERSION"

	echo ""
	__sdkman_echo_green "Default ${candidate} version set to ${VERSION}"
}


================================================
FILE: src/main/bash/sdkman-env-helpers.sh
================================================
#!/usr/bin/env bash

#
#   Copyright 2021 Marco Vermeulen
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#

function __sdkman_check_candidate_present() {
	local candidate="$1"

	if [ -z "$candidate" ]; then
		echo ""
		__sdkman_echo_red "No candidate provided."
		__sdk_help
		return 1
	fi
}

function __sdkman_check_version_present() {
	local version="$1"

	if [ -z "$version" ]; then
		echo ""
		__sdkman_echo_red "No candidate version provided."
		__sdk_help
		return 1
	fi
}

function __sdkman_determine_version() {
	local candidate version folder

	candidate="$1"
	version="$2"
	folder="$3"

	if [[ "$SDKMAN_AVAILABLE" == "false" && -n "$version" && -d "${SDKMAN_CANDIDATES_DIR}/${candidate}/${version}" ]]; then
		VERSION="$version"

	elif [[ "$SDKMAN_AVAILABLE" == "false" && -z "$version" && -L "${SDKMAN_CANDIDATES_DIR}/${candidate}/current" ]]; then
		VERSION=$(readlink "${SDKMAN_CANDIDATES_DIR}/${candidate}/current" | sed "s!${SDKMAN_CANDIDATES_DIR}/${candidate}/!!g")

	elif [[ "$SDKMAN_AVAILABLE" == "false" && -n "$version" ]]; then
		__sdkman_echo_red "Stop! ${candidate} ${version} is not available while offline."
		return 1

	elif [[ "$SDKMAN_AVAILABLE" == "false" && -z "$version" ]]; then
		__sdkman_echo_red "This command is not available while offline."
		return 1

	else
		if [[ -z "$version" ]]; then
			version=$(__sdkman_secure_curl "${SDKMAN_CANDIDATES_API}/candidates/default/${candidate}")
		fi

		local validation_url="${SDKMAN_CANDIDATES_API}/candidates/validate/${candidate}/${version}/${SDKMAN_PLATFORM}"
		VERSION_VALID=$(__sdkman_secure_curl "$validation_url")
		__sdkman_echo_debug "Validate $candidate $version for $SDKMAN_PLATFORM: $VERSION_VALID"
		__sdkman_echo_debug "Validation URL: $validation_url"

		if [[ "$VERSION_VALID" == 'valid' || "$VERSION_VALID" == 'invalid' && -n "$folder" ]]; then
			VERSION="$version"

		elif [[ "$VERSION_VALID" == 'invalid' && -L "${SDKMAN_CANDIDATES_DIR}/${candidate}/${version}" ]]; then
			VERSION="$version"

		elif [[ "$VERSION_VALID" == 'invalid' && -d "${SDKMAN_CANDIDATES_DIR}/${candidate}/${version}" ]]; then
			VERSION="$version"

		else
			if [[ -z "$version" ]]; then
				version="\b"
			fi

			echo ""
			__sdkman_echo_red "Stop! $candidate $version is not available. Possible causes:"
			__sdkman_echo_red " * $version is an invalid version"
			__sdkman_echo_red " * $candidate binaries are incompatible with your platform"
			__sdkman_echo_red " * $candidate has not been released yet"
			echo ""
			__sdkman_echo_yellow "Tip: see all available versions for your platform:"
			echo ""
			__sdkman_echo_yellow "  $ sdk list $candidate"
			return 1
		fi
	fi
}


================================================
FILE: src/main/bash/sdkman-env.sh
================================================
#!/usr/bin/env bash

#
#   Copyright 2021 Marco Vermeulen
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#

function __sdk_env() {
	local -r sdkmanrc=".sdkmanrc"
	local -r subcommand="$1"

	case $subcommand in
		"")    __sdkman_load_env "$sdkmanrc" ;;
		init)  __sdkman_create_env_file "$sdkmanrc";;
		install) __sdkman_setup_env "$sdkmanrc";;
		clear) __sdkman_clear_env "$sdkmanrc";;
	esac
}

function __sdkman_setup_env() {
	local sdkmanrc="$1"

	if [[ ! -f "$sdkmanrc" ]]; then
		__sdkman_echo_red "Could not find $sdkmanrc in the current directory."
		echo ""
		__sdkman_echo_yellow "Run 'sdk env init' to create it."

		return 1
	fi

	sdkman_auto_answer="true" USE="n" __sdkman_env_each_candidate "$sdkmanrc" "__sdk_install"
	__sdkman_load_env "$sdkmanrc"
}

function __sdkman_load_env() {
	local sdkmanrc="$1"
	
	if [[ ! -f "$sdkmanrc" ]]; then
		__sdkman_echo_red "Could not find $sdkmanrc in the current directory."
		echo ""
		__sdkman_echo_yellow "Run 'sdk env init' to create it."

		return 1
	fi

	__sdkman_env_each_candidate "$sdkmanrc" "__sdkman_check_and_use" && 
		SDKMAN_ENV=$PWD
}

function __sdkman_check_and_use() {
	local -r candidate=$1
	local -r version=$2

	if [[ ! -d "${SDKMAN_CANDIDATES_DIR}/${candidate}/${version}" ]]; then
		__sdkman_echo_red "Stop! $candidate $version is not installed."
		echo ""
		__sdkman_echo_yellow "Run 'sdk env install' to install it."

		return 1
	fi

	__sdk_use "$candidate" "$version"
}

function __sdkman_create_env_file() {
	local sdkmanrc="$1"
	
	if [[ -f "$sdkmanrc" ]]; then
		__sdkman_echo_red "$sdkmanrc already exists!"

		return 1
	fi

	__sdkman_determine_current_version "java"

	local version
	[[ -n "$CURRENT" ]] && version="$CURRENT" || version="$(__sdkman_secure_curl "${SDKMAN_CANDIDATES_API}/candidates/default/java")"

	cat <<-eof >|"$sdkmanrc"
	# Enable auto-env through the sdkman_auto_env config
	# Add key=value pairs of SDKs to use below
	java=$version
	eof

	__sdkman_echo_green "$sdkmanrc created."
}

function __sdkman_clear_env() {
	local sdkmanrc="$1"

	if [[ -z $SDKMAN_ENV ]]; then
		__sdkman_echo_red "No environment currently set!"
		return 1
	fi

	if [[ ! -f ${SDKMAN_ENV}/${sdkmanrc} ]]; then
		__sdkman_echo_red "Could not find ${SDKMAN_ENV}/${sdkmanrc}."
		return 1
	fi

	__sdkman_env_each_candidate "${SDKMAN_ENV}/${sdkmanrc}" "__sdkman_env_restore_default_version"
	unset SDKMAN_ENV
}

function __sdkman_env_restore_default_version() {
	local -r candidate="$1"

	local candidate_dir default_version
	candidate_dir="${SDKMAN_CANDIDATES_DIR}/${candidate}/current"
	if __sdkman_is_symlink $candidate_dir; then
		default_version=$(basename $(readlink ${candidate_dir}))
		__sdk_use "$candidate" "$default_version" >/dev/null &&
			__sdkman_echo_yellow "Restored $candidate version to $default_version (default)"
	else
		__sdkman_echo_yellow "No default version of $candidate was found"
	fi
}

function __sdkman_env_each_candidate() {
	local -r filepath=$1
	local -r func=$2

	local normalised_line
	while IFS= read -r line || [[ -n "$line" ]]; do
		normalised_line="$(__sdkman_normalise "$line")"

		__sdkman_is_blank_line "$normalised_line" && continue

		if ! __sdkman_matches_candidate_format "$normalised_line"; then
			__sdkman_echo_red "Invalid candidate format!"
			echo ""
			__sdkman_echo_yellow "Expected 'candidate=version' but found '$normalised_line'"

			return 1
		fi

		$func "${normalised_line%=*}" "${normalised_line#*=}" || return
	done < "$filepath"
}

function __sdkman_is_symlink() {
	[[ -h "$1" ]]
}

function __sdkman_is_blank_line() {
	[[ -z "$1" ]]
}

function __sdkman_normalise() {
	local -r line_without_comments="${1/\#*/}"

	echo "${line_without_comments//[[:space:]]/}"
}

function __sdkman_matches_candidate_format() {
	[[ "$1" =~ ^[[:lower:]]+\=.+$ ]]
}


================================================
FILE: src/main/bash/sdkman-flush.sh
================================================
#!/usr/bin/env bash

#
#   Copyright 2021 Marco Vermeulen
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#

function __sdk_flush() {
	local qualifier="$1"

	case "$qualifier" in
	version)
		if [[ -f "${SDKMAN_DIR}/var/version" ]]; then
			rm -f "${SDKMAN_DIR}/var/version"
			__sdkman_echo_green "Version file has been flushed."
		fi
		;;
	temp)
		__sdkman_cleanup_folder "tmp"
		;;
	tmp)
		__sdkman_cleanup_folder "tmp"
		;;
	metadata)
    	__sdkman_cleanup_folder "var/metadata"
    	;;
	*)
		__sdkman_cleanup_folder "tmp"
		__sdkman_cleanup_folder "var/metadata"
		;;
	esac
}

function __sdkman_cleanup_folder() {
	local folder="$1"
	local sdkman_cleanup_dir
	local sdkman_cleanup_disk_usage
	local sdkman_cleanup_count

	sdkman_cleanup_dir="${SDKMAN_DIR}/${folder}"
	sdkman_cleanup_disk_usage=$(du -sh "$sdkman_cleanup_dir")
	sdkman_cleanup_count=$(ls -1 "$sdkman_cleanup_dir" | wc -l)

	rm -rf "$sdkman_cleanup_dir"
	mkdir "$sdkman_cleanup_dir"

	__sdkman_echo_green "${sdkman_cleanup_count} archive(s) flushed, freeing ${sdkman_cleanup_disk_usage}."
}


================================================
FILE: src/main/bash/sdkman-help.sh
================================================
#!/usr/bin/env bash

#
#   Copyright 2021 Marco Vermeulen
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#

function __sdk_help() {
	__sdkman_deprecation_notice "help"
	__sdkman_echo_no_colour ""
	__sdkman_echo_no_colour "Usage: sdk <command> [candidate] [version]"
	__sdkman_echo_no_colour "       sdk offline <enable|disable>"
	__sdkman_echo_no_colour ""
	__sdkman_echo_no_colour "   commands:"
	__sdkman_echo_no_colour "       install   or i    <candidate> [version] [local-path]"
	__sdkman_echo_no_colour "       uninstall or rm   <candidate> <version>"
	__sdkman_echo_no_colour "       list      or ls   [candidate]"
	__sdkman_echo_no_colour "       use       or u    <candidate> <version>"
	__sdkman_echo_no_colour "       config"
	__sdkman_echo_no_colour "       default   or d    <candidate> [version]"
	__sdkman_echo_no_colour "       home      or h    <candidate> <version>"
	__sdkman_echo_no_colour "       env       or e    [init|install|clear]"
	__sdkman_echo_no_colour "       current   or c    [candidate]"
	__sdkman_echo_no_colour "       upgrade   or ug   [candidate]"
	__sdkman_echo_no_colour "       version   or v"
	__sdkman_echo_no_colour "       help"
	__sdkman_echo_no_colour "       offline           [enable|disable]"

	if [[ "$sdkman_selfupdate_feature" == "true" ]]; then
		__sdkman_echo_no_colour "       selfupdate        [force]"
	fi

	__sdkman_echo_no_colour "       update"
	__sdkman_echo_no_colour "       flush             [tmp|metadata|version]"
	__sdkman_echo_no_colour ""
	__sdkman_echo_no_colour "   candidate  :  the SDK to install: groovy, scala, grails, gradle, kotlin, etc."
	__sdkman_echo_no_colour "                 use list command for comprehensive list of candidates"
	__sdkman_echo_no_colour "                 eg: \$ sdk list"
	__sdkman_echo_no_colour "   version    :  where optional, defaults to latest stable if not provided"
	__sdkman_echo_no_colour "                 eg: \$ sdk install groovy"
	__sdkman_echo_no_colour "   local-path :  optional path to an existing local installation"
	__sdkman_echo_no_colour "                 eg: \$ sdk install groovy 2.4.13-local /opt/groovy-2.4.13"
	__sdkman_echo_no_colour ""
}


================================================
FILE: src/main/bash/sdkman-home.sh
================================================
#!/usr/bin/env bash

#
#   Copyright 2021 Marco Vermeulen
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#

function __sdk_home() {
	__sdkman_deprecation_notice "home"
	local candidate version

	candidate="$1"
	version="$2"
	__sdkman_check_version_present "$version" || return 1
	__sdkman_check_candidate_present "$candidate" || return 1
	__sdkman_determine_version "$candidate" "$version" || return 1

	if [[ ! -d "${SDKMAN_CANDIDATES_DIR}/${candidate}/${version}" ]]; then
		echo ""
		__sdkman_echo_red "Stop! Candidate version is not installed."
		echo ""
		__sdkman_echo_yellow "Tip: Run the following to install this version"
		echo ""
		__sdkman_echo_yellow "$ sdk install ${candidate} ${version}"
		return 1
	fi

	echo -n "${SDKMAN_CANDIDATES_DIR}/${candidate}/${version}"
}


================================================
FILE: src/main/bash/sdkman-init.sh
================================================
#!/usr/bin/env bash

#
#   Copyright 2021 Marco Vermeulen
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#

# set env vars if not set
if [ -z "$SDKMAN_CANDIDATES_API" ]; then
	export SDKMAN_CANDIDATES_API="@SDKMAN_CANDIDATES_API@"
fi

if [ -z "$SDKMAN_BROKER_API" ]; then
	export SDKMAN_BROKER_API="@SDKMAN_BROKER_API@"
fi

if [ -z "$SDKMAN_DIR" ]; then
	export SDKMAN_DIR="$HOME/.sdkman"
fi

# Load the sdkman config if it exists.
if [ -f "${SDKMAN_DIR}/etc/config" ]; then
	source "${SDKMAN_DIR}/etc/config"
fi

# Read the platform file
SDKMAN_PLATFORM="$(cat "${SDKMAN_DIR}/var/platform")"
export SDKMAN_PLATFORM

# OS specific support (must be 'true' or 'false').
cygwin=false
darwin=false
solaris=false
freebsd=false
SDKMAN_KERNEL="$(uname -s)"
case "${SDKMAN_KERNEL}" in
	CYGWIN*)
		cygwin=true
		;;
	Darwin*)
		darwin=true
		;;
	SunOS*)
		solaris=true
		;;
	FreeBSD*)
		freebsd=true
esac

# Determine shell
zsh_shell=false
bash_shell=false

if [[ -n "$ZSH_VERSION" ]]; then
	zsh_shell=true
elif [[ -n "$BASH_VERSION" ]]; then
	bash_shell=true
fi

# Source sdkman module scripts and extension files.
#
# Extension files are prefixed with 'sdkman-' and found in the ext/ folder.
# Use this if extensions are written with the functional approach and want
# to use functions in the main sdkman script. For more details, refer to
# <https://github.com/sdkman/sdkman-extensions>.
OLD_IFS="$IFS"
IFS=$'\n'
scripts=($(find "${SDKMAN_DIR}/src" "${SDKMAN_DIR}/ext" -type f -name 'sdkman-*.sh'))
for f in "${scripts[@]}"; do
	source "$f"
done
IFS="$OLD_IFS"
unset OLD_IFS scripts f

# Create upgrade delay file if it doesn't exist
if [[ ! -f "${SDKMAN_DIR}/var/delay_upgrade" ]]; then
	touch "${SDKMAN_DIR}/var/delay_upgrade"
fi

# set curl connect-timeout and max-time
if [[ -z "$sdkman_curl_connect_timeout" ]]; then sdkman_curl_connect_timeout=7; fi
if [[ -z "$sdkman_curl_max_time" ]]; then sdkman_curl_max_time=10; fi

# set curl retry
if [[ -z "${sdkman_curl_retry}" ]]; then sdkman_curl_retry=0; fi

# set curl retry max time in seconds
if [[ -z "${sdkman_curl_retry_max_time}" ]]; then sdkman_curl_retry_max_time=60; fi

# set curl to continue downloading automatically
if [[ -z "${sdkman_curl_continue}" ]]; then sdkman_curl_continue=true; fi

# read list of candidates and set array
SDKMAN_CANDIDATES_CACHE="${SDKMAN_DIR}/var/candidates"
SDKMAN_CANDIDATES_CSV=$(<"$SDKMAN_CANDIDATES_CACHE")
__sdkman_echo_debug "Setting candidates csv: $SDKMAN_CANDIDATES_CSV"
if [[ "$zsh_shell" == 'true' ]]; then
	SDKMAN_CANDIDATES=(${(s:,:)SDKMAN_CANDIDATES_CSV})
else
	IFS=',' read -a SDKMAN_CANDIDATES <<< "${SDKMAN_CANDIDATES_CSV}"
fi

export SDKMAN_CANDIDATES_DIR="${SDKMAN_DIR}/candidates"

for candidate_name in "${SDKMAN_CANDIDATES[@]}"; do
	candidate_dir="${SDKMAN_CANDIDATES_DIR}/${candidate_name}/current"
	if [[ -h "$candidate_dir" || -d "${candidate_dir}" ]]; then
		__sdkman_export_candidate_home "$candidate_name" "$candidate_dir"
		__sdkman_prepend_candidate_to_path "$candidate_dir"
	fi
done
unset candidate_name candidate_dir
export PATH

# source completion scripts
if [[ "$sdkman_auto_complete" == 'true' ]]; then
	if [[ "$zsh_shell" == 'true' ]]; then
		# initialize zsh completions (if not already done)
		if ! (( $+functions[compdef] )) ; then
			autoload -Uz compinit
			if [[ $ZSH_DISABLE_COMPFIX == 'true' ]]; then
				compinit -u -C
			else
				compinit
			fi
		fi
		autoload -U bashcompinit
		bashcompinit
		source "${SDKMAN_DIR}/contrib/completion/bash/sdk"
		__sdkman_echo_debug "ZSH completion script loaded..."
	elif [[ "$bash_shell" == 'true' ]]; then
		source "${SDKMAN_DIR}/contrib/completion/bash/sdk"
		__sdkman_echo_debug "Bash completion script loaded..."
	else
		__sdkman_echo_debug "No completion scripts found for $SHELL"
	fi
fi

if [[ "$sdkman_auto_env" == "true" ]]; then
	if [[ "$zsh_shell" == "true" ]]; then
		function sdkman_auto_env() {
			if [[ -n $SDKMAN_ENV ]] && [[ ! $PWD =~ ^$SDKMAN_ENV ]]; then
				sdk env clear
			fi
			if [[ -f .sdkmanrc ]]; then
				sdk env
			fi
		}

		chpwd_functions+=(sdkman_auto_env)
	else
		function sdkman_auto_env() {
			if [[ -n $SDKMAN_ENV ]] && [[ ! $PWD =~ ^$SDKMAN_ENV ]]; then
				sdk env clear
			fi
			if [[ "$SDKMAN_OLD_PWD" != "$PWD" ]] && [[ -f ".sdkmanrc" ]]; then
				sdk env
			fi

			export SDKMAN_OLD_PWD="$PWD"
		}
		
		trimmed_prompt_command="${PROMPT_COMMAND%"${PROMPT_COMMAND##*[![:space:]]}"}"
		[[ -z "$trimmed_prompt_command" ]] && PROMPT_COMMAND="sdkman_auto_env" || PROMPT_COMMAND="${trimmed_prompt_command%\;};sdkman_auto_env"
	fi

	sdkman_auto_env
fi


================================================
FILE: src/main/bash/sdkman-install.sh
================================================
#!/usr/bin/env bash

#
#   Copyright 2021 Marco Vermeulen
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#

function __sdk_install() {
	local candidate version folder

	candidate="$1"
	version="$2"
	folder="$3"

	__sdkman_check_candidate_present "$candidate" || return 1
	__sdkman_determine_version "$candidate" "$version" "$folder" || return 1

	if [[ -d "${SDKMAN_CANDIDATES_DIR}/${candidate}/${VERSION}" || -L "${SDKMAN_CANDIDATES_DIR}/${candidate}/${VERSION}" ]]; then
		echo ""
		__sdkman_echo_yellow "${candidate} ${VERSION} is already installed."
		return 0
	fi

	if [[ ${VERSION_VALID} == 'valid' ]]; then
		__sdkman_determine_current_version "$candidate"
		__sdkman_install_candidate_version "$candidate" "$VERSION" || return 1

		if [[ "$sdkman_auto_answer" != 'true' && "$auto_answer_upgrade" != 'true' && -n "$CURRENT" ]]; then
			__sdkman_echo_confirm "Do you want ${candidate} ${VERSION} to be set as default? (Y/n): "
			read USE
		fi

		if [[ -z "$USE" || "$USE" == "y" || "$USE" == "Y" ]]; then
			echo ""
			__sdkman_echo_green "Setting ${candidate} ${VERSION} as default."
			__sdkman_link_candidate_version "$candidate" "$VERSION"
			__sdkman_add_to_path "$candidate"
		fi

		return 0
	elif [[ "$VERSION_VALID" == 'invalid' && -n "$folder" ]]; then
		__sdkman_install_local_version "$candidate" "$VERSION" "$folder" || return 1
	else
		echo ""
		__sdkman_echo_red "Stop! $1 is not a valid ${candidate} version."
		return 1
	fi
}

function __sdkman_install_candidate_version() {
	local candidate version

	candidate="$1"
	version="$2"

	__sdkman_download "$candidate" "$version" || return 1
	__sdkman_echo_green "Installing: ${candidate} ${version}"

	mkdir -p "${SDKMAN_CANDIDATES_DIR}/${candidate}"

	rm -rf "${SDKMAN_DIR}/tmp/out"
	unzip -oq "${SDKMAN_DIR}/tmp/${candidate}-${version}.zip" -d "${SDKMAN_DIR}/tmp/out"
	mv -f "$SDKMAN_DIR"/tmp/out/* "${SDKMAN_CANDIDATES_DIR}/${candidate}/${version}"
	__sdkman_echo_green "Done installing!"
	echo ""
}

function __sdkman_install_local_version() {
	local candidate version folder version_length version_length_max

	version_length_max=15

	candidate="$1"
	version="$2"
	folder="$3"

	# Validate max length of version
	version_length=${#version}
	__sdkman_echo_debug "Validating that actual version length ($version_length) does not exceed max ($version_length_max)"

	if [[ $version_length -gt $version_length_max ]]; then
		__sdkman_echo_red "Invalid version! ${version} with length ${version_length} exceeds max of ${version_length_max}!"
		return 1
	fi

	mkdir -p "${SDKMAN_CANDIDATES_DIR}/${candidate}"

	# handle relative paths
	if [[ "$folder" != /* ]]; then
		folder="$(pwd)/$folder"
	fi

	if [[ -d "$folder" ]]; then
		__sdkman_echo_green "Linking ${candidate} ${version} to ${folder}"
		ln -s "$folder" "${SDKMAN_CANDIDATES_DIR}/${candidate}/${version}"
		__sdkman_echo_green "Done installing!"
	else
		__sdkman_echo_red "Invalid path! Refusing to link ${candidate} ${version} to ${folder}."
		return 1
	fi

	echo ""
}

function __sdkman_download() {
	local candidate version

	candidate="$1"
	version="$2"

	metadata_folder="${SDKMAN_DIR}/var/metadata"
	mkdir -p "${metadata_folder}"
		
	local platform_parameter="$SDKMAN_PLATFORM"
	local download_url="${SDKMAN_BROKER_API}/download/${candidate}/${version}/${platform_parameter}"
	local base_name="${candidate}-${version}"
	local tmp_headers_file="${SDKMAN_DIR}/tmp/${base_name}.headers.tmp"
	local headers_file="${metadata_folder}/${base_name}.headers"

	export local binary_input="${SDKMAN_DIR}/tmp/${base_name}.bin"
	export local zip_output="${SDKMAN_DIR}/tmp/${base_name}.zip"

	echo ""
	__sdkman_echo_no_colour "Downloading: ${candidate} ${version}"
	echo ""
	__sdkman_echo_no_colour "In progress..."
	echo ""

	# download binary
	__sdkman_secure_curl_download "${download_url}" --output "${binary_input}" --dump-header "${tmp_headers_file}"
	grep '^X-Sdkman' "${tmp_headers_file}" > "${headers_file}"
	__sdkman_echo_debug "Downloaded binary to: ${binary_input} (HTTP headers written to: ${headers_file})"

	# post-installation hook: implements function __sdkman_post_installation_hook
	# responsible for taking `binary_input` and producing `zip_output`
	local post_installation_hook="${SDKMAN_DIR}/tmp/hook_post_${candidate}_${version}.sh"
	__sdkman_echo_debug "Get post-installation hook: ${SDKMAN_CANDIDATES_API}/hooks/post/${candidate}/${version}/${platform_parameter}"
	__sdkman_secure_curl "${SDKMAN_CANDIDATES_API}/hooks/post/${candidate}/${version}/${platform_parameter}" >| "$post_installation_hook"
	__sdkman_echo_debug "Copy remote post-installation hook: ${post_installation_hook}"
	source "$post_installation_hook"
	__sdkman_post_installation_hook || return 1
	__sdkman_echo_debug "Processed binary as: $zip_output"
	__sdkman_echo_debug "Completed post-installation hook..."
		
	__sdkman_validate_zip "${zip_output}" || return 1
	__sdkman_checksum_zip "${zip_output}" "${headers_file}" || return 1
	echo ""
}

function __sdkman_validate_zip() {
	local zip_archive zip_ok

	zip_archive="$1"
	zip_ok=$(unzip -t "$zip_archive" | grep 'No errors detected in compressed data')
	if [ -z "$zip_ok" ]; then
		rm -f "$zip_archive"
		echo ""
		__sdkman_echo_red "Stop! The archive was corrupt and has been removed! Please try installing again."
		return 1
	fi
}

function __sdkman_checksum_zip() {
	local -r zip_archive="$1"
	local -r headers_file="$2"
	local algorithm checksum cmd
	local shasum_avail=false
	local md5sum_avail=false
	
	if [ -z "${headers_file}" ]; then
		echo ""
		__sdkman_echo_debug "Skipping checksum for cached artifact"
		return
	elif [ ! -f "${headers_file}" ]; then
		echo ""
		__sdkman_echo_yellow "Metadata file not found at '${headers_file}', skipping checksum..."
		return
	fi
	
	if [[ "$sdkman_checksum_enable" != "true" ]]; then
		echo ""
		__sdkman_echo_yellow "Checksums are disabled, skipping verification..."
		return
	fi
	
	#Check for the appropriate checksum tools
	if command -v shasum > /dev/null 2>&1; then
		shasum_avail=true
	fi
	if command -v md5sum > /dev/null 2>&1; then
		md5sum_avail=true
	fi
	
	while IFS= read -r line; do
		algorithm=$(echo $line | sed -n 's/^X-Sdkman-Checksum-\(.*\):.*$/\1/p' | tr '[:lower:]' '[:upper:]')
		checksum=$(echo $line | sed -n 's/^X-Sdkman-Checksum-.*:\(.*\)$/\1/p' | tr -cd '[:alnum:]')
		
		if [[ -n ${algorithm} && -n ${checksum} ]]; then
			
			if [[ "$algorithm" =~ 'SHA' && "$shasum_avail" == 'true' ]]; then
				cmd="echo \"${checksum} *${zip_archive}\" | shasum --check --quiet"
				
			elif [[ "$algorithm" =~ 'MD5' && "$md5sum_avail" == 'true' ]]; then
				cmd="echo \"${checksum} ${zip_archive}\" | md5sum --check --quiet"
			fi
			
			if [[ -n $cmd ]]; then
				__sdkman_echo_no_colour "Verifying artifact: ${zip_archive} (${algorithm}:${checksum})"

				if ! eval "$cmd"; then
					rm -f "$zip_archive"
					echo ""
					__sdkman_echo_red "Stop! An invalid checksum was detected and the archive removed! Please try re-installing."
					return 1
				fi
			else
				__sdkman_echo_no_colour "Not able to perform checksum verification at this time."
			fi
		fi
  	done < "${headers_file}"
}


================================================
FILE: src/main/bash/sdkman-list.sh
================================================
#!/usr/bin/env bash

#
#   Copyright 2021 Marco Vermeulen
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#

function __sdk_list() {
	local candidate="$1"

	if [[ -z "$candidate" ]]; then
		__sdkman_list_candidates
	else
		__sdkman_list_versions "$candidate"
	fi
}

function __sdkman_list_candidates() {
	if [[ "$SDKMAN_AVAILABLE" == "false" ]]; then
		__sdkman_echo_red "This command is not available while offline."
	else
		__sdkman_echo_paged "$(__sdkman_secure_curl "${SDKMAN_CANDIDATES_API}/candidates/list")"
	fi
}

function __sdkman_list_versions() {
	local candidate versions_csv

	candidate="$1"
	versions_csv="$(__sdkman_build_version_csv "$candidate")"
	__sdkman_determine_current_version "$candidate"

	if [[ "$SDKMAN_AVAILABLE" == "false" ]]; then
		__sdkman_offline_list "$candidate" "$versions_csv"
	else
		__sdkman_echo_paged "$(__sdkman_secure_curl "${SDKMAN_CANDIDATES_API}/candidates/${candidate}/${SDKMAN_PLATFORM}/versions/list?current=${CURRENT}&installed=${versions_csv}")"
	fi
}

function __sdkman_build_version_csv() {
	local candidate versions_csv

	candidate="$1"
	versions_csv=""

	if [[ -d "${SDKMAN_CANDIDATES_DIR}/${candidate}" ]]; then
		for version in $(find "${SDKMAN_CANDIDATES_DIR}/${candidate}" -maxdepth 1 -mindepth 1 \( -type l -o -type d \) -exec basename '{}' \; | sort -r); do
			if [[ "$version" != 'current' ]]; then
				versions_csv="${version},${versions_csv}"
			fi
		done
		versions_csv=${versions_csv%?}
	fi
	echo "$versions_csv"
}

function __sdkman_offline_list() {
	local candidate versions_csv

	candidate="$1"
	versions_csv="$2"

	__sdkman_echo_no_colour "--------------------------------------------------------------------------------"
	__sdkman_echo_yellow "Offline: only showing installed ${candidate} versions"
	__sdkman_echo_no_colour "--------------------------------------------------------------------------------"

	local versions=($(echo ${versions_csv//,/ }))
	for ((i = ${#versions} - 1; i >= 0; i--)); do
		if [[ -n "${versions[${i}]}" ]]; then
			if [[ "${versions[${i}]}" == "$CURRENT" ]]; then
				__sdkman_echo_no_colour " > ${versions[${i}]}"
			else
				__sdkman_echo_no_colour " * ${versions[${i}]}"
			fi
		fi
	done

	if [[ -z "${versions[@]}" ]]; then
		__sdkman_echo_yellow "   None installed!"
	fi

	__sdkman_echo_no_colour "--------------------------------------------------------------------------------"
	__sdkman_echo_no_colour "* - installed                                                                   "
	__sdkman_echo_no_colour "> - currently in use                                                            "
	__sdkman_echo_no_colour "--------------------------------------------------------------------------------"
}


================================================
FILE: src/main/bash/sdkman-main.sh
================================================
#!/usr/bin/env bash

#
#   Copyright 2021 Marco Vermeulen
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#

function ___sdkman_help() {
	if [[ -f "$SDKMAN_DIR/libexec/help" ]]; then
		"$SDKMAN_DIR/libexec/help"
	else
		__sdk_help
	fi
}

function sdk() {

	COMMAND="$1"
	QUALIFIER="$2"

	case "$COMMAND" in
	l)
		COMMAND="list"
		;;
	ls)
		COMMAND="list"
		;;
	v)
		COMMAND="version"
		;;
	u)
		COMMAND="use"
		;;
	i)
		COMMAND="install"
		;;
	rm)
		COMMAND="uninstall"
		;;
	c)
		COMMAND="current"
		;;
	ug)
		COMMAND="upgrade"
		;;
	d)
		COMMAND="default"
		;;
	h)
		COMMAND="home"
		;;
	e)
		COMMAND="env"
		;;
	esac

	#
	# Various sanity checks and default settings
	#

	# Check candidates cache
	if [[ "$COMMAND" != "update" ]]; then
		___sdkman_check_candidates_cache "$SDKMAN_CANDIDATES_CACHE" || return 1
	fi

	# Always presume internet availability
	SDKMAN_AVAILABLE="true"
	if [ -z "$SDKMAN_OFFLINE_MODE" ]; then
		SDKMAN_OFFLINE_MODE="false"
	fi

	# ...unless proven otherwise
	__sdkman_update_service_availability

	# Load the sdkman config if it exists.
	if [ -f "${SDKMAN_DIR}/etc/config" ]; then
		source "${SDKMAN_DIR}/etc/config"
	fi

	# no command provided
	if [[ -z "$COMMAND" ]]; then
		___sdkman_help
		return 1
	fi

	# Check if it is a valid command
	CMD_FOUND=""
	if [[ "$COMMAND" != "selfupdate" || "$sdkman_selfupdate_feature" == "true" ]]; then
		CMD_TARGET="${SDKMAN_DIR}/src/sdkman-${COMMAND}.sh"
		if [[ -f "$CMD_TARGET" ]]; then
			CMD_FOUND="$CMD_TARGET"
		fi
	fi

	# Check if it is a sourced function
	CMD_TARGET="${SDKMAN_DIR}/ext/sdkman-${COMMAND}.sh"
	if [[ -f "$CMD_TARGET" ]]; then
		CMD_FOUND="$CMD_TARGET"
	fi

	# couldn't find the command
	if [[ -z "$CMD_FOUND" ]]; then
		echo ""
		__sdkman_echo_red "Invalid command: $COMMAND"
		echo ""
		___sdkman_help
	fi

	# Validate offline qualifier
	if [[ "$COMMAND" == "offline" && -n "$QUALIFIER" && -z $(echo "enable disable" | grep -w "$QUALIFIER") ]]; then
		echo ""
		__sdkman_echo_red "Stop! $QUALIFIER is not a valid offline mode."
	fi

	# Store the return code of the requested command
	local final_rc=0

	# Native commands found under libexec
	local native_command="${SDKMAN_DIR}/libexec/${COMMAND}"
	
	if [[ "$sdkman_native_enable" == 'true' && -f "$native_command" ]]; then
		"$native_command" "${@:2}"

	elif [ -n "$CMD_FOUND" ]; then

		# Check whether the candidate exists
		if [[ -n "$QUALIFIER" && "$COMMAND" != "help" && "$COMMAND" != "offline" && "$COMMAND" != "flush" && "$COMMAND" != "selfupdate" && "$COMMAND" != "env" && "$COMMAND" != "completion" && "$COMMAND" != "edit" && "$COMMAND" != "home" && -z $(echo ${SDKMAN_CANDIDATES[@]} | grep -w "$QUALIFIER") ]]; then
			echo ""
			__sdkman_echo_red "Stop! $QUALIFIER is not a valid candidate."
			return 1
		fi

		# Internal commands use underscores rather than hyphens
		local converted_command_name=$(echo "$COMMAND" | tr '-' '_')

		# Available as a shell function
		__sdk_"$converted_command_name" "${@:2}"
	fi
	final_rc=$?
	return $final_rc
}


================================================
FILE: src/main/bash/sdkman-offline.sh
================================================
#!/usr/bin/env bash

#
#   Copyright 2021 Marco Vermeulen
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#

function __sdk_offline() {
	local mode="$1"
	if [[ -z "$mode" || "$mode" == "enable" ]]; then
		SDKMAN_OFFLINE_MODE="true"
		__sdkman_echo_green "Offline mode enabled."
	fi

	if [[ "$mode" == "disable" ]]; then
		SDKMAN_OFFLINE_MODE="false"
		__sdkman_echo_green "Online mode re-enabled!"
	fi
}


================================================
FILE: src/main/bash/sdkman-path-helpers.sh
================================================
#!/usr/bin/env bash

#
#   Copyright 2021 Marco Vermeulen
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#

function __sdkman_path_contains() {
	local candidate exists

	candidate="$1"
	exists="$(echo "$PATH" | grep "$candidate")"
	if [[ -n "$exists" ]]; then
		echo 'true'
	else
		echo 'false'
	fi
}

function __sdkman_add_to_path() {
	local candidate present

	candidate="$1"

	present=$(__sdkman_path_contains "$candidate")
	if [[ "$present" == 'false' ]]; then
		PATH="$SDKMAN_CANDIDATES_DIR/$candidate/current/bin:$PATH"
	fi
}

function __sdkman_set_candidate_home() {
	local candidate version upper_candidate

	candidate="$1"
	version="$2"

	upper_candidate=$(echo "$candidate" | tr '[:lower:]' '[:upper:]')
	export "${upper_candidate}_HOME"="${SDKMAN_CANDIDATES_DIR}/${candidate}/${version}"
}

function __sdkman_export_candidate_home() {
	local candidate_name candidate_dir ucase_candidate_name

	candidate_name="$1"
	candidate_dir="$2"

	if [ "$zsh_shell" = true ]; then
		ucase_candidate_name="${candidate_name:u}"
	elif [ "$bash_shell" = true ]; then
		ucase_candidate_name="${candidate_name^^}"
	else
		ucase_candidate_name="$(printf %s "$candidate_name" | tr '[:lower:]' '[:upper:]')"
	fi

	export "${ucase_candidate_name}_HOME=$candidate_dir"
}

function __sdkman_prepend_candidate_to_path() {
	local candidate_dir

	candidate_dir="$1"

	if [ -d "${candidate_dir}/bin" ]; then
		candidate_dir="${candidate_dir}/bin"
	fi
	[[ ":$PATH:" == *":$candidate_dir:"* ]] || PATH="${candidate_dir}:${PATH}"
}

function __sdkman_link_candidate_version() {
	local candidate version

	candidate="$1"
	version="$2"

	# Change the 'current' symlink for the candidate, hence affecting all shells.
	if [[ -L "${SDKMAN_CANDIDATES_DIR}/${candidate}/current" || -d "${SDKMAN_CANDIDATES_DIR}/${candidate}/current" ]]; then
		rm -rf "${SDKMAN_CANDIDATES_DIR}/${candidate}/current"
	fi

	ln -s "${version}" "${SDKMAN_CANDIDATES_DIR}/${candidate}/current"
}


================================================
FILE: src/main/bash/sdkman-selfupdate.sh
================================================
#!/usr/bin/env bash

#
#   Copyright 2021 Marco Vermeulen
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#

function __sdk_selfupdate() {
	local force_selfupdate
	local sdkman_script_version_api
	local sdkman_native_version_api

	if [[ "$SDKMAN_AVAILABLE" == "false" ]]; then
		echo "This command is not available while offline."
		return 1
	fi

	if [[ "$sdkman_beta_channel" == "true" ]]; then
		sdkman_script_version_api="${SDKMAN_CANDIDATES_API}/broker/version/sdkman/script/beta"
		sdkman_native_version_api="${SDKMAN_CANDIDATES_API}/broker/version/sdkman/native/beta"
	else
		sdkman_script_version_api="${SDKMAN_CANDIDATES_API}/broker/version/sdkman/script/stable"
		sdkman_native_version_api="${SDKMAN_CANDIDATES_API}/broker/version/sdkman/native/stable"
	fi

	sdkman_remote_script_version=$(__sdkman_secure_curl "$sdkman_script_version_api")
	sdkman_remote_native_version=$(__sdkman_secure_curl "$sdkman_native_version_api")

	sdkman_local_script_version=$(< "$SDKMAN_DIR/var/version")
	sdkman_local_native_version=$(< "$SDKMAN_DIR/var/version_native")

	__sdkman_echo_debug "Script: local version: $sdkman_local_script_version; remote version: $sdkman_remote_script_version"
	__sdkman_echo_debug "Native: local version: $sdkman_local_native_version; remote version: $sdkman_remote_native_version"

	force_selfupdate="$1"
	export sdkman_debug_mode
	if [[ "$sdkman_local_script_version" == "$sdkman_remote_script_version" && "$sdkman_local_native_version" == "$sdkman_remote_native_version" && "$force_selfupdate" != "force" ]]; then
		echo "No update available at this time."
	elif [[ "$sdkman_beta_channel" == "true" ]]; then
		__sdkman_secure_curl "${SDKMAN_CANDIDATES_API}/selfupdate/beta/${SDKMAN_PLATFORM}" | bash
	else
		__sdkman_secure_curl "${SDKMAN_CANDIDATES_API}/selfupdate/stable/${SDKMAN_PLATFORM}" | bash
	fi
}


================================================
FILE: src/main/bash/sdkman-uninstall.sh
================================================
#!/usr/bin/env bash

#
#   Copyright 2021 Marco Vermeulen
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#

function __sdk_uninstall() {
	__sdkman_deprecation_notice "uninstall"
	local candidate version current

	candidate="$1"
	version="$2"
	__sdkman_check_candidate_present "$candidate" || return 1
	__sdkman_check_version_present "$version" || return 1

	current=$(readlink "${SDKMAN_CANDIDATES_DIR}/${candidate}/current" | sed "s!${SDKMAN_CANDIDATES_DIR}/${candidate}/!!g")
	if [[ -L "${SDKMAN_CANDIDATES_DIR}/${candidate}/current" && "$version" == "$current" ]]; then
		echo ""
		__sdkman_echo_green "Deselecting ${candidate} ${version}..."
		unlink "${SDKMAN_CANDIDATES_DIR}/${candidate}/current"
	fi

	echo ""

	if [ -d "${SDKMAN_CANDIDATES_DIR}/${candidate}/${version}" ]; then
		__sdkman_echo_green "Uninstalling ${candidate} ${version}..."
		rm -rf "${SDKMAN_CANDIDATES_DIR}/${candidate}/${version}"
	else
		__sdkman_echo_red "${candidate} ${version} is not installed."
	fi
}


================================================
FILE: src/main/bash/sdkman-update.sh
================================================
#!/usr/bin/env bash

#
#   Copyright 2021 Marco Vermeulen
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#

function __sdk_update() {
	local candidates_uri="${SDKMAN_CANDIDATES_API}/candidates/all"
	__sdkman_echo_debug "Using candidates endpoint: $candidates_uri"

	local fresh_candidates_csv=$(__sdkman_secure_curl_with_timeouts "$candidates_uri")

	__sdkman_echo_debug "Local candidates: $SDKMAN_CANDIDATES_CSV"
	__sdkman_echo_debug "Fetched candidates: $fresh_candidates_csv"

	if [[ -n "${fresh_candidates_csv}" ]] && ! grep -iq 'html' <<< "${fresh_candidates_csv}"; then
		__sdkman_echo_debug "Fresh and cached candidate lengths: ${#fresh_candidates_csv} ${#SDKMAN_CANDIDATES_CSV}"

		local fresh_candidates combined_candidates diff_candidates

		if [[ "${zsh_shell}" == 'true' ]]; then
			fresh_candidates=(${(s:,:)fresh_candidates_csv})
		else
			IFS=',' read -a fresh_candidates <<< "${fresh_candidates_csv}"
		fi

		combined_candidates=("${fresh_candidates[@]}" "${SDKMAN_CANDIDATES[@]}")

		diff_candidates=($(printf $'%s\n' "${combined_candidates[@]}" | sort | uniq -u))

		if ((${#diff_candidates[@]})); then
			local delta

			delta=("${fresh_candidates[@]}" "${diff_candidates[@]}")
			delta=($(printf $'%s\n' "${delta[@]}" | sort | uniq -d))
			if ((${#delta[@]})); then
				__sdkman_echo_green "\nAdding new candidates(s): ${delta[*]}"
			fi

			delta=("${SDKMAN_CANDIDATES[@]}" "${diff_candidates[@]}")
			delta=($(printf $'%s\n' "${delta[@]}" | sort | uniq -d))
			if ((${#delta[@]})); then
				__sdkman_echo_green "\nRemoving obsolete candidates(s): ${delta[*]}"
			fi

			echo "${fresh_candidates_csv}" >| "${SDKMAN_CANDIDATES_CACHE}"
			__sdkman_echo_yellow $'\nPlease open a new terminal now...'
		else
			touch "${SDKMAN_CANDIDATES_CACHE}"
			__sdkman_echo_green $'\nNo new candidates found at this time.'
		fi
	fi
}


================================================
FILE: src/main/bash/sdkman-upgrade.sh
================================================
#!/usr/bin/env bash

#
#   Copyright 2021 Marco Vermeulen
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#

function __sdk_upgrade() {
	local all candidates candidate upgradable installed_count upgradable_count upgradable_candidates

	if [ -n "$1" ]; then
		all=false
		candidates=$1
	else
		all=true
		if [[ "$zsh_shell" == 'true' ]]; then
			candidates=(${SDKMAN_CANDIDATES[@]})
		else
			candidates=${SDKMAN_CANDIDATES[@]}
		fi
	fi

	installed_count=0
	upgradable_count=0
	echo ""

	for candidate in ${candidates}; do
		upgradable="$(__sdkman_determine_upgradable_version "$candidate")"
		case $? in
		1)
			$all || __sdkman_echo_red "Not using any version of ${candidate}"
			;;
		2)
			echo ""
			__sdkman_echo_red "Stop! Could not get remote version of ${candidate}"
			return 1
			;;
		*)
			if [ -n "$upgradable" ]; then
				[ ${upgradable_count} -eq 0 ] && __sdkman_echo_no_colour "Available defaults:"
				__sdkman_echo_no_colour "$upgradable"
				((upgradable_count += 1))
				upgradable_candidates=(${upgradable_candidates[@]} $candidate)
			fi
			((installed_count += 1))
			;;
		esac
	done

	if $all; then
		if [ ${installed_count} -eq 0 ]; then
			__sdkman_echo_no_colour 'No candidates are in use'
		elif [ ${upgradable_count} -eq 0 ]; then
			__sdkman_echo_no_colour "All candidates are up-to-date"
		fi
	elif [ ${upgradable_count} -eq 0 ]; then
		__sdkman_echo_no_colour "${candidate} is up-to-date"
	fi

	if [ ${upgradable_count} -gt 0 ]; then
		echo ""

		if [[ "$sdkman_auto_answer" != 'true' ]]; then
			__sdkman_echo_confirm "Use prescribed default version(s)? (Y/n): "
			read UPGRADE_ALL
		fi

		export auto_answer_upgrade='true'
		if [[ -z "$UPGRADE_ALL" || "$UPGRADE_ALL" == "y" || "$UPGRADE_ALL" == "Y" ]]; then
			# Using array for bash & zsh compatibility
			for ((i = 0; i <= ${#upgradable_candidates[*]}; i++)); do
				upgradable_candidate="${upgradable_candidates[${i}]}"
				# Filter empty elements (in bash arrays are zero index based, in zsh they are 1 based)
				if [[ -n "$upgradable_candidate" ]]; then
					__sdk_install $upgradable_candidate
				fi
			done
		fi
		unset auto_answer_upgrade
	fi
}

function __sdkman_determine_upgradable_version() {
	local candidate local_versions remote_default_version

	candidate="$1"

	# Resolve local versions
	local_versions="$(echo $(find "${SDKMAN_CANDIDATES_DIR}/${candidate}" -maxdepth 1 -mindepth 1 -type d -exec basename '{}' \; 2> /dev/null) | sed -e "s/ /, /g")"
	if [ ${#local_versions} -eq 0 ]; then
		return 1
	fi

	# Resolve remote default version
	remote_default_version="$(__sdkman_secure_curl "${SDKMAN_CANDIDATES_API}/candidates/default/${candidate}")"
	if [ -z "$remote_default_version" ]; then
		return 2
	fi

	# Check upgradable or not
	if [ ! -d "${SDKMAN_CANDIDATES_DIR}/${candidate}/${remote_default_version}" ]; then
		__sdkman_echo_yellow "${candidate} (local: ${local_versions}; default: ${remote_default_version})"
	fi
}


================================================
FILE: src/main/bash/sdkman-use.sh
================================================
#!/usr/bin/env bash

#
#   Copyright 2021 Marco Vermeulen
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#

function __sdk_use() {
	local candidate version install

	candidate="$1"
	version="$2"
	__sdkman_check_version_present "$version" || return 1
	__sdkman_check_candidate_present "$candidate" || return 1

	if [[ ! -d "${SDKMAN_CANDIDATES_DIR}/${candidate}/${version}" ]]; then
		echo ""
		__sdkman_echo_red "Stop! Candidate version is not installed."
		echo ""
		__sdkman_echo_yellow "Tip: Run the following to install this version"
		echo ""
		__sdkman_echo_yellow "$ sdk install ${candidate} ${version}"
		return 1
	fi

	# Just update the *_HOME and PATH for this shell.
	__sdkman_set_candidate_home "$candidate" "$version"

	if [[ $PATH =~ ${SDKMAN_CANDIDATES_DIR}/${candidate}/([^/]+) ]]; then
		local matched_version

		if [[ "$zsh_shell" == "true" ]]; then
			matched_version=${match[1]}
		else
			matched_version=${BASH_REMATCH[1]}
		fi

		export PATH=${PATH//${SDKMAN_CANDIDATES_DIR}\/${candidate}\/${matched_version}/${SDKMAN_CANDIDATES_DIR}\/${candidate}\/${version}}
	fi

	if [[ ! (-L "${SDKMAN_CANDIDATES_DIR}/${candidate}/current" || -d "${SDKMAN_CANDIDATES_DIR}/${candidate}/current") ]]; then
		__sdkman_echo_green "Setting ${candidate} version ${version} as default."
		__sdkman_link_candidate_version "$candidate" "$version"
	fi

	echo ""
	__sdkman_echo_green "Using ${candidate} version ${version} in this shell."
}


================================================
FILE: src/main/bash/sdkman-utils.sh
================================================
#!/usr/bin/env bash

#
#   Copyright 2021 Marco Vermeulen
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#

function __sdkman_echo_debug() {
	if [[ "$sdkman_debug_mode" == 'true' ]]; then
		echo "$1"
	fi
}

function __sdkman_secure_curl() {
	if [[ "${sdkman_insecure_ssl}" == 'true' ]]; then
		curl --insecure --silent --location "$1"
	else
		curl --silent --location "$1"
	fi
}

function __sdkman_secure_curl_download() {
	local curl_params
	curl_params=('--progress-bar' '--location')

	if [[ "${sdkman_debug_mode}" == 'true' ]]; then
		curl_params+=('--verbose')
	fi

	if [[ "${sdkman_curl_continue}" == 'true' ]]; then
		curl_params+=('-C' '-')
	fi

	if [[ -n "${sdkman_curl_retry_max_time}" ]]; then
		curl_params+=('--retry-max-time' "${sdkman_curl_retry_max_time}")
	fi

	if [[ -n "${sdkman_curl_retry}" ]]; then
		curl_params+=('--retry' "${sdkman_curl_retry}")
	fi

	if [[ "${sdkman_insecure_ssl}" == 'true' ]]; then
		curl_params+=('--insecure')
	fi

	curl "${curl_params[@]}" "${@}"
}

function __sdkman_secure_curl_with_timeouts() {
	if [[ "${sdkman_insecure_ssl}" == 'true' ]]; then
		curl --insecure --silent --location --connect-timeout ${sdkman_curl_connect_timeout} --max-time ${sdkman_curl_max_time} "$1"
	else
		curl --silent --location --connect-timeout ${sdkman_curl_connect_timeout} --max-time ${sdkman_curl_max_time} "$1"
	fi
}

function __sdkman_echo_paged() {
	if [[ -n "$PAGER" ]]; then
		echo "$@" | eval "$PAGER"
	elif command -v less >& /dev/null; then
		echo "$@" | less
	else
		echo "$@"
	fi
}

function __sdkman_echo() {
	if [[ "$sdkman_colour_enable" == 'false' ]]; then
		echo -e "$2"
	else
		echo -e "\033[1;$1$2\033[0m"
	fi
}

function __sdkman_echo_red() {
	__sdkman_echo "31m" "$1"
}

function __sdkman_echo_no_colour() {
	echo "$1"
}

function __sdkman_echo_yellow() {
	__sdkman_echo "33m" "$1"
}

function __sdkman_echo_green() {
	__sdkman_echo "32m" "$1"
}

function __sdkman_echo_cyan() {
	__sdkman_echo "36m" "$1"
}

function __sdkman_echo_confirm() {
	if [[ "$sdkman_colour_enable" == 'false' ]]; then
		echo -n "$1"
	else
		echo -e -n "\033[1;33m$1\033[0m"
	fi
}

function __sdkman_deprecation_notice() {
	local message="
[Deprecation Notice]:
This legacy '$1' command is replaced by a native implementation
and it will be removed in a future release.
Please follow the discussion here:
https://github.com/sdkman/sdkman-cli/discussions/1332"

	if [[ "$sdkman_colour_enable" == 'false' ]]; then
		__sdkman_echo_no_colour "$message"
	else
		__sdkman_echo_yellow "$message"
	fi
}



================================================
FILE: src/main/bash/sdkman-version.sh
================================================
#!/usr/bin/env bash

#
#   Copyright 2021 Marco Vermeulen
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#

function __sdk_version() {
	__sdkman_deprecation_notice "version"
    local version=$(cat $SDKMAN_DIR/var/version)
	echo ""
	__sdkman_echo_yellow "SDKMAN $version"
}


================================================
FILE: src/test/groovy/sdkman/cucumber/RunCukeTests.groovy
================================================
package sdkman.cucumber

import io.cucumber.junit.Cucumber
import io.cucumber.junit.CucumberOptions
import org.junit.runner.RunWith

@RunWith(Cucumber)
@CucumberOptions(
		strict = true,
		features = ["src/test/resources/features"],
		glue = ["sdkman.steps"],
		tags = ["not @manual", "not @review"]
)
class RunCukeTests {}


================================================
FILE: src/test/groovy/sdkman/env/BashEnv.groovy
================================================
package sdkman.env

import groovy.transform.ToString

/**
 * <p>As part of the sdkman test suite we need to launch a bash shell and execute
 * multiple commands in it. This is tricky to do using Java's support for
 * working with external processes as the API can't tell you when a command
 * has finished executing.</p>
 * <p>This class provides some hacks that allow you to serially execute commands
 * in an external bash process in a fairly reliable manner and to retrieve the
 * output of those commands.</p>
 */
@ToString(includeNames = true)
class BashEnv {

	static final PROMPT = ""
	static final EXIT_CODE_CMD = 'echo "Exit code is: $?"'
	static final EXIT_CODE_PATTERN = ~/Exit code is: (\d+)\s*${PROMPT}?$/

	private final Object outputLock = new Object()

	def exitCode
	def process
	def processOutput = new StringBuilder()
	def commandOutput

	// Command timeout in milliseconds
	def timeout = 5000
	def workDir
	def env

	BashEnv(workDir, Map env) {
		this.workDir = workDir as File

		def basicPath = "/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/run/current-system/sw/bin"
		def localBinDir = "${workDir}/bin"

		def modifiedPath = "$localBinDir:$basicPath"

		env = env + [PS1: PROMPT, PATH: modifiedPath]
		this.env = env.collect { k, v -> k + '=' + v }
	}

	/**
	 * Starts the external bash process.
	 */
	void start() {
		process = ["bash", "--noprofile", "--norc", "--posix", "-i", "-o", "noclobber"].execute(env, workDir)

		consumeProcessStream(process.inputStream)
		consumeProcessStream(process.errorStream)
	}

	/**
	 * Stops the external bash process and waits for it to finish.
	 */
	void stop() {
		execute("exit")
		process.waitFor()
	}

	/**
	 * Sends a command line to the external bash process and returns once the
	 * command has finished executing. If the command is interactive and requires
	 * input during it's execution (for example a y/n answer to a question) you
	 * can provide that input as a list of strings.
	 */
	void execute(String cmdline, List inputs = []) {
		resetOutput()

		if (cmdline != "exit") {
			exitCode = null
		}

		process.outputStream << cmdline << "\n"
		process.outputStream.flush()

		if (cmdline != "exit") {
			for (input in inputs) {
				process.outputStream << input << "\n"
			}
			process.outputStream << EXIT_CODE_CMD << "\n"
			process.outputStream.flush()
		}

		def start = System.currentTimeMillis()
		while (cmdline != "exit") {
			Thread.sleep 100

			synchronized (outputLock) {
				// Remove all the extraneous text that's not related to the
				// command's output. This includes the command string itself,
				// the 'echo' command to display the command's exit code, and
				// the exit code line.
				removeFromOutput(cmdline + "\n")
				removeFromOutput(PROMPT + EXIT_CODE_CMD + "\n")

				def str = processOutput.toString()
				def m = EXIT_CODE_PATTERN.matcher(str)
				if (m) {
					exitCode = m[0][1]

					// Remove this exit code line from the output.
					commandOutput = m.replaceAll('')
					break
				}

				// If the command times out, we should break out of the loop and
				// display whatever output has already been produced.
				if (System.currentTimeMillis() - start > timeout) {
					commandOutput = "ALERT! Command timed out. Last output was:\n\n${processOutput}"
					break
				}
			}
		}
	}

	/**
	 * Returns the exit code of the last command that was executed.
	 */
	int getStatus() {
		if (!exitCode) throw new IllegalStateException("Did you run execute() before getting the status?")
		return exitCode.toInteger()
	}

	/**
	 * Returns the text output (both stdout and stderr) of the last command
	 * that was executed.
	 */
	String getOutput() {
		return commandOutput
	}

	/**
	 * Clears the saved command output.
	 */
	void resetOutput() {
		synchronized (outputLock) {
			processOutput = new StringBuilder()
		}
	}

	private void consumeProcessStream(final InputStream stream) {
		char[] buffer = new char[256]
		Thread.start {
			def reader = new InputStreamReader(stream)
			def charsRead = 0
			while (charsRead != -1) {
				charsRead = reader.read(buffer, 0, 256)
				if (charsRead > 0) {
					synchronized (outputLock) {
						processOutput.append(buffer, 0, charsRead)
					}
				}
			}
		}
	}

	private void removeFromOutput(String line) {
		synchronized (outputLock) {
			def pos = processOutput.indexOf(line)
			if (pos != -1) {
				processOutput.delete(pos, pos + line.size() - 1)
			}
		}
	}
}


================================================
FILE: src/test/groovy/sdkman/env/CleanBashEnvBuilder.groovy
================================================
package sdkman.env

class CleanBashEnvBuilder {

	private final File baseFolder
	String httpProxy

	static CleanBashEnvBuilder create(File baseFolder) {
		new CleanBashEnvBuilder(baseFolder)
	}

	private CleanBashEnvBuilder(File baseFolder) {
		this.baseFolder = baseFolder
	}

	CleanBashEnvBuilder withHttpProxy(String httpProxy) {
		this.httpProxy = httpProxy
		this
	}

	BashEnv build() {
		def env = [HOME: baseFolder.absolutePath]
		if (httpProxy) {
			env.put("http_proxy", httpProxy)
		}
		new BashEnv(baseFolder.absolutePath, env)
	}
}


================================================
FILE: src/test/groovy/sdkman/env/SdkmanBashEnvBuilder.groovy
================================================
package sdkman.env

import groovy.transform.ToString
import sdkman.stubs.CurlStub
import sdkman.stubs.UnameStub
import sdkman.support.UnixUtils

@ToString(includeNames = true)
class SdkmanBashEnvBuilder {

	final BUILD_STAGE_DIR = "build/stage/sdkman-latest+hashme"
	final BUILD_BIN_DIR = "$BUILD_STAGE_DIR/bin"
	final BUILD_SRC_DIR = "$BUILD_STAGE_DIR/src"
	final BUILD_COMPLETION_DIR = "$BUILD_STAGE_DIR/contrib/completion/bash"

	//mandatory fields
	private final File baseFolder

	//optional fields with sensible defaults
	private Optional<CurlStub> curlStub = Optional.empty()
	private Optional<UnameStub> unameStub = Optional.empty()
	private List candidates = ['groovy', 'grails', 'java']
	private String platform = UnixUtils.inferPlatform()
	private boolean offlineMode = false
	private String candidatesApi = "http://localhost:8080/2"
	private String brokerApi = "http://localhost:8080/2"
	private String jdkHome = "/path/to/my/jdk"
	private String httpProxy
	private String scriptVersion
	private String nativeVersion
	private boolean debugMode = true

	Map config = [
			sdkman_auto_answer : 'false',
			sdkman_beta_channel: 'false',
			sdkman_native_enable: 'false',
			sdkman_selfupdate_feature: 'true'
	]

	File sdkmanDir, sdkmanBinDir, sdkmanVarDir, sdkmanSrcDir, sdkmanEtcDir, sdkmanExtDir,
		 sdkmanTmpDir, sdkmanCandidatesDir, sdkmanMetadataDir, sdkmanContribDir
	
	static SdkmanBashEnvBuilder create(File baseFolder) {
		new SdkmanBashEnvBuilder(baseFolder)
	}

	private SdkmanBashEnvBuilder(File baseFolder) {
		this.baseFolder = baseFolder
	}

	SdkmanBashEnvBuilder withCurlStub(CurlStub curlStub) {
		this.curlStub = Optional.of(curlStub)
		this
	}

	SdkmanBashEnvBuilder withUnameStub(UnameStub unameStub) {
		this.unameStub = Optional.of(unameStub)
		this
	}
	
	SdkmanBashEnvBuilder withPlatform(String platform) {
		this.platform = platform
		this
	}

	SdkmanBashEnvBuilder withCandidates(List candidates) {
		this.candidates = candidates
		this
	}

	SdkmanBashEnvBuilder withConfiguration(String key, String value) {
		config.put key, value
		this
	}

	SdkmanBashEnvBuilder withOfflineMode(boolean offlineMode) {
		this.offlineMode = offlineMode
		this
	}

	SdkmanBashEnvBuilder withCandidatesApi(String service) {
		this.candidatesApi = service
		this
	}

	SdkmanBashEnvBuilder withBrokerApi(String service) {
		this.brokerApi = service
		this
	}

	SdkmanBashEnvBuilder withJdkHome(String jdkHome) {
		this.jdkHome = jdkHome
		this
	}

	SdkmanBashEnvBuilder withHttpProxy(String httpProxy) {
		this.httpProxy = httpProxy
		this
	}

	SdkmanBashEnvBuilder withScriptVersion(String version) {
		this.scriptVersion = version
		this
	}

	SdkmanBashEnvBuilder withNativeVersion(String version) {
		this.nativeVersion = version
		this
	}

	SdkmanBashEnvBuilder withDebugMode(boolean debugMode) {
		this.debugMode = debugMode
		this
	}

	BashEnv build() {
		sdkmanDir = prepareDirectory(baseFolder, ".sdkman")
		sdkmanBinDir = prepareDirectory(sdkmanDir, "bin")
		sdkmanVarDir = prepareDirectory(sdkmanDir, "var")
		sdkmanSrcDir = prepareDirectory(sdkmanDir, "src")
		sdkmanEtcDir = prepareDirectory(sdkmanDir, "etc")
		sdkmanExtDir = prepareDirectory(sdkmanDir, "ext")
		sdkmanTmpDir = prepareDirectory(sdkmanDir, "tmp")
		sdkmanCandidatesDir = prepareDirectory(sdkmanDir, "candidates")
		sdkmanMetadataDir = prepareDirectory(sdkmanVarDir, "metadata")
		sdkmanContribDir = prepareDirectory(sdkmanDir, "contrib")

		curlStub.map { it.build() }
		unameStub.map { it.build() }

		initializeConfiguration(sdkmanEtcDir, config)
		initializeCandidates(sdkmanCandidatesDir, candidates)
		initializeCandidatesCache(sdkmanVarDir, candidates)
		initializePlatformDescriptor(sdkmanVarDir, platform)
		initializeScriptVersionFile(sdkmanVarDir, scriptVersion)
		initializeNativeVersionFile(sdkmanVarDir, nativeVersion)

		primeInitScript(sdkmanBinDir)
		primeModuleScripts(sdkmanSrcDir)
		primeBashCompletionScript(sdkmanContribDir)

		def env = [
				SDKMAN_DIR           : sdkmanDir.absolutePath,
				SDKMAN_CANDIDATES_DIR: sdkmanCandidatesDir.absolutePath,
				SDKMAN_OFFLINE_MODE  : "$offlineMode",
				SDKMAN_CANDIDATES_API: candidatesApi,
				SDKMAN_BROKER_API    : brokerApi,
				sdkman_debug_mode    : Boolean.toString(debugMode),
				JAVA_HOME            : jdkHome
		]

		if (httpProxy) {
			env.put("http_proxy", httpProxy)
		}

		new BashEnv(baseFolder.absolutePath, env)
	}

	private prepareDirectory(File target, String directoryName) {
		def directory = new File(target, directoryName)
		directory.mkdirs()
		directory
	}

	private initializeScriptVersionFile(File folder, String version) {
		if (version) {
			new File(folder, "version") << version
		}
	}

	private initializeNativeVersionFile(File folder, String version) {
		if (version) {
			new File(folder, "version_native") << version
		}
	}

	private initializeCandidates(File folder, List candidates) {
		candidates.each { candidate ->
			new File(folder, candidate).mkdirs()
		}
	}

	private initializeCandidatesCache(File folder, List candidates) {
		def candidatesCache = new File(folder, "candidates")
		if (candidates) {
			candidatesCache << candidates.join(",")
		} else {
			candidatesCache << ""
		}
	}
	
	private initializePlatformDescriptor(File folder, String platform) {
		def platformDescriptor = new File(folder, "platform")
		platformDescriptor << platform
	}

	private initializeConfiguration(File targetFolder, Map config) {
		def configFile = new File(targetFolder, "config")
		config.each { key, value ->
			configFile << "$key=$value\n"
		}
	}

	private primeInitScript(File targetFolder) {
		def sourceInitScript = new File(BUILD_BIN_DIR, 'sdkman-init.sh')

		if (!sourceInitScript.exists())
			throw new IllegalStateException("sdkman-init.sh has not been prepared for consumption.")

		def destInitScript = new File(targetFolder, "sdkman-init.sh")
		destInitScript << sourceInitScript.text
		destInitScript
	}

	private primeBashCompletionScript(File targetFolder) {
		def sourceCompletionScript = new File(BUILD_COMPLETION_DIR, 'sdk')

		if (!sourceCompletionScript.exists())
			throw new IllegalStateException("sdk has not been prepared for consumption.")

		new FileTreeBuilder(targetFolder).with {
			completion {
				bash {
					sdk(sourceCompletionScript.text)
				}
			}			
		}
	}

	private primeModuleScripts(File targetFolder) {
		for (f in new File(BUILD_SRC_DIR).listFiles()) {
			if (!(f.name in ['selfupdate.sh', 'install.sh', 'sdkman-init.sh'])) {
				new File(targetFolder, f.name) << f.text
			}
		}
	}
}


================================================
FILE: src/test/groovy/sdkman/specs/CandidatesCacheUpdateFailureSpec.groovy
================================================
package sdkman.specs

import sdkman.support.SdkmanEnvSpecification

class CandidatesCacheUpdateFailureSpec extends SdkmanEnvSpecification {

	static final String CANDIDATES_API = "http://localhost:8080/2"

	static final String HEALTHCHECK_ENDPOINT = "$CANDIDATES_API/healthcheck"
	static final String CANDIDATES_ALL_ENDPOINT = "$CANDIDATES_API/candidates/all"

	File candidatesCache

	def setup() {
		candidatesCache = new File("${sdkmanDotDirectory}/var", "candidates")
		curlStub.primeWith(HEALTHCHECK_ENDPOINT, "echo dbfb025be9f97fda2052b5febcca0155")
				.primeWith(CANDIDATES_ALL_ENDPOINT, "echo html")
		sdkmanBashEnvBuilder.withConfiguration("sdkman_debug_mode", "true")
	}

	void "should not update candidates if error downloading candidate list"() {
		given:
		bash = sdkmanBashEnvBuilder
				.withCandidates([])
				.build()

		and:
		bash.start()

		when:
		bash.execute("source $bootstrapScript")
		bash.execute("sdk update")

		then:
		!bash.output.contains('Fresh and cached candidate lengths')
	}
}


================================================
FILE: src/test/groovy/sdkman/specs/CandidatesCacheUpdateSpec.groovy
================================================
package sdkman.specs

import sdkman.support.SdkmanEnvSpecification

class CandidatesCacheUpdateSpec extends SdkmanEnvSpecification {

	static final String CANDIDATES_API = "http://localhost:8080/2"

	static final String HEALTHCHECK_ENDPOINT = "$CANDIDATES_API/healthcheck"
	static final String CANDIDATES_ALL_ENDPOINT = "$CANDIDATES_API/candidates/all"

	File candidatesCache

	def setup() {
		candidatesCache = new File("${sdkmanDotDirectory}/var", "candidates")
		curlStub.primeWith(HEALTHCHECK_ENDPOINT, "echo dbfb025be9f97fda2052b5febcca0155")
				.primeWith(CANDIDATES_ALL_ENDPOINT, "echo groovy,scala")
		sdkmanBashEnvBuilder.withConfiguration("sdkman_debug_mode", "true")
	}

	void "should issue a warning and escape if cache is empty"() {
		given:
		bash = sdkmanBashEnvBuilder
				.withCandidates([])
				.build()

		and:
		bash.start()

		when:
		bash.execute("source $bootstrapScript")
		bash.execute("sdk version")

		then:
		bash.output.contains('WARNING: Cache is corrupt. SDKMAN cannot be used until updated.')
		bash.output.contains('$ sdk update')

		and:
		!bash.output.contains("SDKMAN 5.0.0")
	}

	void "should log a success message if cache exists"() {
		given:
		bash = sdkmanBashEnvBuilder
				.withCandidates(['groovy'])
				.build()

		and:
		bash.start()

		when:
		bash.execute("source $bootstrapScript")
		bash.execute("sdk help")

		then:
		bash.output.contains('Using existing cache')
	}

	void "should bypass cache check if update command issued"() {
		given:
		bash = sdkmanBashEnvBuilder
				.withCandidates([])
				.build()

		and:
		bash.start()

		when:
		bash.execute("source $bootstrapScript")
		bash.execute("sdk update")

		then:
		bash.output.contains('Adding new candidates(s): groovy scala')

		and:
		candidatesCache.text.trim() == "groovy,scala"
	}
}


================================================
FILE: src/test/groovy/sdkman/specs/CompletionSpec.groovy
================================================
package sdkman.specs

import sdkman.support.SdkmanEnvSpecification

class CompletionSpec extends SdkmanEnvSpecification {
	static final String CANDIDATES_API = "http://localhost:8080/2"

	def "should complete the list of commands"() {
		given:
		bash = sdkmanBashEnvBuilder
				.withConfiguration("sdkman_auto_complete", "true")
				.build()

		bash.start()
		bash.execute("source $bootstrapScript")

		when:
		bash.execute("COMP_CWORD=1; COMP_WORDS=(sdk); _sdk")
		bash.execute('echo "\${COMPREPLY[@]}"')

		then:
		bash.output.contains("install uninstall list use config default home env current upgrade version help offline selfupdate update flush")
	}

	def "should complete the list of candidates"() {
		given:
		bash = sdkmanBashEnvBuilder
				.withCandidates(["java", "groovy"])
				.withConfiguration("sdkman_auto_complete", "true")
				.build()

		bash.start()
		bash.execute("source $bootstrapScript")

		when:
		bash.execute("COMP_CWORD=2; COMP_WORDS=(sdk install); _sdk")
		bash.execute('echo "\${COMPREPLY[@]}"')

		then:
		bash.output.contains("java groovy")
	}

	def "should complete the list of Java versions"() {
		given:
		curlStub.primeWith("$CANDIDATES_API/candidates/java/darwinx64/versions/all", "echo 16.0.1.hs-adpt,17.0.0-tem")

		unameStub.forKernel("Darwin").forMachine("x86_64")

		bash = sdkmanBashEnvBuilder
				.withConfiguration("sdkman_auto_complete", "true")
				.withUnameStub(unameStub)
				.withPlatform("darwinx64")
				.build()

		bash.start()
		bash.execute("source $bootstrapScript")

		when:
		bash.execute("COMP_CWORD=3; COMP_WORDS=(sdk install java); _sdk")
		bash.execute('echo "\${COMPREPLY[@]}"')

		then:
		bash.output.contains("16.0.1.hs-adpt 17.0.0-tem")
	}
}


================================================
FILE: src/test/groovy/sdkman/specs/ConfigCommandSpec.groovy
================================================
package sdkman.specs

import sdkman.support.SdkmanEnvSpecification

class ConfigCommandSpec extends SdkmanEnvSpecification {
	def "it should open the config in the system's default editor"() {
		given:
		bash = sdkmanBashEnvBuilder
				.withOfflineMode(true)
				.build()

		bash.start()
		bash.execute("source $bootstrapScript")

		when:
		setupEnv(bash)
		bash.execute("sdk config")

		then:
		verifyOutput(bash.output, sdkmanBaseDirectory)

		where:
		setupEnv << [
			{
				it.execute('nano() { echo "nano was called with $*"; }')
				it.execute("EDITOR=nano")
			},
			{
				it.execute('code() { echo "code was called with $*"; }')
				it.execute("EDITOR='code --wait'")
			},
			{
				it.execute('vi() { echo "vi was called with $*"; }')
				it.execute("unset EDITOR")
			},
			{
				it.execute("EDITOR=/does/not/exist")
			}
		]
		verifyOutput << [
			{ output, baseDirectory -> output.contains("nano was called with ${baseDirectory}/.sdkman/etc/config") },
			{ output, baseDirectory -> output.contains("code was called with --wait ${baseDirectory}/.sdkman/etc/config") },
			{ output, baseDirectory -> output.contains("vi was called with ${baseDirectory}/.sdkman/etc/config") },
			{ output, _ -> output.contains("No default editor configured.") }
		]
	}
}


================================================
FILE: src/test/groovy/sdkman/specs/CurrentCommandSpec.groovy
================================================
package sdkman.specs

import sdkman.support.SdkmanEnvSpecification

import java.nio.file.Paths

import static java.nio.file.Files.createSymbolicLink

class CurrentCommandSpec extends SdkmanEnvSpecification {

	static final CANDIDATES_API = "http://localhost:8080/2"
	static final HEALTHCHECK_ENDPOINT = "$CANDIDATES_API/healthcheck"

	def setup() {
		curlStub.primeWith(HEALTHCHECK_ENDPOINT, "echo dbfb025be9f97fda2052b5febcca0155")
	}

	void "should display current version of all candidates installed"() {
		given:
		def installedCandidates = [
				"gradle": "2.7",
				"groovy": "2.4.4",
				"vertx" : "3.0.0"
		]
		def allCandidates = [
				"asciidoctorj",
				"crash",
				"gaiden",
				"glide",
				"gradle",
				"grails",
				"griffon",
				"groovy",
				"groovyserv",
				"jbake",
				"jbossforge",
				"lazybones",
				"springboot",
				"vertx"
		]

		bash = sdkmanBashEnvBuilder
				.withOfflineMode(false)
				.withCandidates(installedCandidates.keySet().toList())
				.build()

		prepareFoldersFor(installedCandidates)

		bash.start()
		bash.execute("source $bootstrapScript")

		when:
		bash.execute('sdk current')

		then:
		bash.output.contains("Using:")
		bash.output.contains("groovy: 2.4.4")
		bash.output.contains("gradle: 2.7")
		bash.output.contains("vertx: 3.0.0")
	}

	private prepareFoldersFor(Map installedCandidates) {
		installedCandidates.forEach { candidate, version ->
			def candidateVersionDirectory = "$candidatesDirectory/$candidate/$version"
			def candidateVersionBinDirectory = "$candidateVersionDirectory/bin"
			new File(candidateVersionBinDirectory).mkdirs()
			def candidateVersionPath = Paths.get(candidateVersionDirectory)
			def symlink = Paths.get("$candidatesDirectory/$candidate/current")
			createSymbolicLink(symlink, candidateVersionPath)
		}
	}
}


================================================
FILE: src/test/groovy/sdkman/specs/EnvCommandSpec.groovy
================================================
package sdkman.specs

import sdkman.support.SdkmanEnvSpecification

import java.nio.file.Paths

import static java.nio.file.Files.createSymbolicLink

class EnvCommandSpec extends SdkmanEnvSpecification {
	static final String CANDIDATES_API = "http://localhost:8080/2"

	static final String CANDIDATES_DEFAULT_JAVA = "$CANDIDATES_API/candidates/default/java"

	def "should generate .sdkmanrc when called with 'init'"() {
		given:
		curlStub.primeWith(CANDIDATES_DEFAULT_JAVA, "echo 11.0.6.hs-adpt")

		setupCandidates(candidatesDirectory)

		bash = sdkmanBashEnvBuilder
			.withOfflineMode(true)
			.build()

		bash.start()
		bash.execute("source $bootstrapScript")

		when:
		bash.execute("sdk env init")

		then:
		new File(bash.workDir, '.sdkmanrc').text.contains(expected)

		where:
		setupCandidates << [
			{ directory ->
				new FileTreeBuilder(directory).with {
					"java" {
						"8.0.252.hs" {
							"bin" {}
						}
					}
				}

				createSymbolicLink(Paths.get("$directory/java/current"), Paths.get("$directory/java/8.0.252.hs"))
			},
			{} // NOOP
		]
		expected << ["java=8.0.252.hs\n", "java=11.0.6.hs-adpt\n"]
	}

	def "should use the candidates contained in .sdkmanrc"() {
		given:
		new FileTreeBuilder(candidatesDirectory).with {
			"grails" {
				"2.1.0" {}
			}
			"groovy" {
				"2.4.1" {}
			}
		}

		bash = sdkmanBashEnvBuilder
			.withOfflineMode(true)
			.build()

		new File(bash.workDir, '.sdkmanrc').text = sdkmanrc

		bash.start()
		bash.execute("source $bootstrapScript")

		when:
		bash.execute("sdk env")

		then:
		verifyAll(bash.output) {
			contains("Using groovy version 2.4.1 in this shell.")
			contains("Using grails version 2.1.0 in this shell.")
		}

		where:
		sdkmanrc << [
			"grails=2.1.0\ngroovy=2.4.1",
			"grails=2.1.0\ngroovy=2.4.1\n",
			"  grails=2.1.0\ngroovy=2.4.1\n",
			"grails=2.1.0	\ngroovy=2.4.1\n",
			"grails=2.1.0\ngroovy = 2.4.1\n",
		]
	}

	def "should execute 'sdk env' when entering a directory with an .sdkmanrc"() {
		given:
		new FileTreeBuilder(candidatesDirectory).with {
			"groovy" {
				"2.4.1" {}
			}
		}

		bash = sdkmanBashEnvBuilder
			.withOfflineMode(true)
			.withConfiguration("sdkman_auto_env", sdkmanAutoEnv)
			.build()

		new FileTreeBuilder(bash.workDir).with {
			"project" {
				".sdkmanrc"("groovy=2.4.1\n")	
			}
		}

		bash.start()
		bash.execute("source $bootstrapScript")		

		when:
		bash.execute("cd project")

		then:
		verifyOutput(bash.output)

		where:
		sdkmanAutoEnv | verifyOutput
		'true'		  | { it.contains("Using groovy version 2.4.1 in this shell") }
		'false'       | { !it.contains("Using groovy version 2.4.1 in this shell") }
	}
	
	def "should not execute 'sdk env' when already being in a directory with an .sdkmanrc"() {
		given:
		new FileTreeBuilder(candidatesDirectory).with {
			"groovy" {
				"2.4.1" {}
			}
		}

		bash = sdkmanBashEnvBuilder
			.withOfflineMode(true)
			.withConfiguration("sdkman_auto_env", "true")
			.build()

		new FileTreeBuilder(bash.workDir).with {
			"project" {
				".sdkmanrc"("groovy=2.4.1\n")
			}
		}

		bash.start()
		bash.execute("source $bootstrapScript")

		when:
		bash.execute("cd project")
		bash.execute("ls")

		then:
		!bash.output.contains("Using groovy version 2.4.1 in this shell")
	}

	def "should execute 'sdk env' when opening a new terminal in a directory with an .sdkmanrc"() {
		given:
		new FileTreeBuilder(candidatesDirectory).with {
			"groovy" {
				"2.4.1" {}
			}
		}

		bash = sdkmanBashEnvBuilder
			.withOfflineMode(true)
			.withConfiguration("sdkman_auto_env", "true")
			.build()

		new FileTreeBuilder(bash.workDir).with {
			".sdkmanrc"("groovy=2.4.1\n")
		}

		bash.start()

		when:
		bash.execute("source $bootstrapScript")

		then:
		bash.output.contains("Using groovy version 2.4.1 in this shell")
	}

	def "should execute 'sdk env' after executing 'sdk env clear'"() {
		given:
		new FileTreeBuilder(candidatesDirectory).with {
			"groovy" {
				"2.4.1" {}
			}
		}

		bash = sdkmanBashEnvBuilder
			.withOfflineMode(true)
			.withConfiguration("sdkman_auto_env", "true")
			.build()

		new FileTreeBuilder(bash.workDir).with {
			"project" {
				".sdkmanrc"("groovy=2.4.1\n")
			}
		}

		bash.start()
		bash.execute("source $bootstrapScript")

		when:
		bash.execute("cd project")
		bash.execute("cd ..")
		bash.execute("cd project")

		then:
		bash.output.contains('Using groovy version 2.4.1 in this shell')
	}

	def "should execute 'sdk env clear' when exiting from a directory with an .sdkmanrc"() {
		given:
		new FileTreeBuilder(candidatesDirectory).with {
			"groovy" {
				"2.4.1" {}
				"2.4.6" {}
			}
		}
		createSymbolicLink(Paths.get("$candidatesDirectory/groovy/current"), Paths.get("$candidatesDirectory/groovy/2.4.6"))

		bash = sdkmanBashEnvBuilder
				.withOfflineMode(true)
				.withConfiguration("sdkman_auto_env", "true")
				.build()

		new FileTreeBuilder(bash.workDir).with {
			"project" {
				".sdkmanrc"("groovy=2.4.1\n")
			}
		}

		bash.start()
		bash.execute("source $bootstrapScript")

		when:
		bash.execute("cd project")
		bash.execute("cd ..")

		then:
		bash.output.contains("Restored groovy version to 2.4.6")
	}

	def "should execute 'sdk env clear; sdk env' when switching to another directory with an .sdkmanrc"() {
		given:
		new FileTreeBuilder(candidatesDirectory).with {
			"groovy" {
				"2.4.1" {}
				"2.4.6" {}
				"2.5.14" {}
			}
			"ant" {
				"1.9.15" {}
				"1.10.8" {}
			}
		}
		createSymbolicLink(Paths.get("$candidatesDirectory/groovy/current"), Paths.get("$candidatesDirectory/groovy/2.5.14"))
		createSymbolicLink(Paths.get("$candidatesDirectory/ant/current"), Paths.get("$candidatesDirectory/ant/1.10.8"))

		bash = sdkmanBashEnvBuilder
				.withOfflineMode(true)
				.withConfiguration("sdkman_auto_env", "true")
				.build()

		new FileTreeBuilder(bash.workDir).with {
			"projectA" {
				".sdkmanrc"("groovy=2.4.1\nant=1.9.15\n")
			}
			"projectB" {
				".sdkmanrc"("groovy=2.4.6\n")
			}
		}

		bash.start()
		bash.execute("source $bootstrapScript")

		when:
		bash.execute("cd projectA")
		
		then:
		bash.output.contains('Using groovy version 2.4.1 in this shell')
		bash.output.contains('Using ant version 1.9.15 in this shell')
		
		when:
		bash.execute("cd ../projectB")

		then:
		bash.output.contains("Restored ant version to 1.10.8")
		bash.output.contains('Using groovy version 2.4.6 in this shell')
		
		when:
		bash.execute("cd ..")

		then:
		bash.output.contains('Restored groovy version to 2.5.14')
		!bash.output.contains('ant')
	}

	def "should not execute 'sdk env clear' when entering a subdirectory within the current active configuration"() {
		given:
		new FileTreeBuilder(candidatesDirectory).with {
			"groovy" {
				"2.4.1" {}
			}
		}

		bash = sdkmanBashEnvBuilder
				.withOfflineMode(true)
				.withConfiguration("sdkman_auto_env", "true")
				.build()

		new FileTreeBuilder(bash.workDir).with {
			"project" {
				".sdkmanrc"("groovy=2.4.1\n")
			}
			"src" {}
		}

		bash.start()
		bash.execute("source $bootstrapScript")

		when:
		bash.execute("cd project")
		bash.execute("cd src")

		then:
		!bash.output.contains("Restored groovy version to 2.4.6")
	}

	def "should issue an error if .sdkmanrc contains a malformed candidate version"() {
		given:
		bash = sdkmanBashEnvBuilder
			.withOfflineMode(true)
			.build()

		new File(bash.workDir, ".sdkmanrc").text = "groovy 2.4.1"

		bash.start()
		bash.execute("source $bootstrapScript")

		when:
		bash.execute("sdk env")

		then:
		verifyAll(bash) {
			status == 1
			output.contains("Invalid candidate format!")
		}
	}

	def "should issue an error when .sdkmanrc contains a candidate version which is not installed"() {
		given:
		bash = sdkmanBashEnvBuilder
				.withOfflineMode(true)
				.build()

		new File(bash.workDir, ".sdkmanrc").text = "groovy=2.4.1"

		bash.start()
		bash.execute("source $bootstrapScript")

		when:
		bash.execute("sdk env")

		then:
		verifyAll(bash) {
			status == 1
			output.contains("Stop! groovy 2.4.1 is not installed.")
			output.contains("Run 'sdk env install' to install it.")
		}
	}

	def "should support blank lines, comments and inline comments"() {
		given:
		new FileTreeBuilder(candidatesDirectory).with {
			"groovy" {
				"2.4.1" {}
			}
		}

		bash = sdkmanBashEnvBuilder
			.withOfflineMode(true)
			.build()

		new File(bash.workDir, ".sdkmanrc").text = sdkmanrc

		bash.start()
		bash.execute("source $bootstrapScript")

		when:
		bash.execute("sdk env")

		then:
		bash.output.contains("Using groovy version 2.4.1 in this shell.")

		where:
		sdkmanrc << [
			"\ngroovy=2.4.1\n",
			"# this is a comment\ngroovy=2.4.1\n",
			"groovy=2.4.1 # this is a comment too\n"
		]
	}
}


================================================
FILE: src/test/groovy/sdkman/specs/InitialisationSpec.groovy
================================================
package sdkman.specs

import sdkman.support.SdkmanEnvSpecification

import java.nio.file.Files
import java.nio.file.Paths

class InitialisationSpec extends SdkmanEnvSpecification {

	static final allCandidates = [
			"asciidoctorj", "crash", "gaiden", "glide", "gradle", "grails", "griffon", "groovy",
			"groovyserv", "jbake", "jbossforge", "lazybones", "springboot", "vertx"
	]

	def setup() {
		bash = sdkmanBashEnvBuilder
				.withCandidates(allCandidates)
				.build()
		prepareCandidateDirectories(allCandidates)
	}

	void "should include all candidates in PATH"() {
		given:
		bash.start()
		bash.execute("source $bootstrapScript")
		bash.resetOutput()

		when:
		bash.execute('echo "$PATH"')
		def pathParts = bash.output.split(':')
		def pathElementMatcher = ~/$candidatesDirectory\/([^\/]+)\/.*/
		def includedCandidates = pathParts
				.collect { it.replace("\n", "") }
				.collect { it =~ pathElementMatcher }
				.findAll { it }
				.collect { it[0][1] }
				.sort()

		println("Available: ${allCandidates}")
		println("Included : $includedCandidates")

		and:
		def missingCandidates = allCandidates - includedCandidates

		then:
		missingCandidates.isEmpty()
	}

	void "should reinitialize candidates in PATH if necessary"() {
		given:
		bash.start()
		bash.execute("source $bootstrapScript")
		bash.resetOutput()

		and:
		def originalPath = bash.env.grep { it =~ /^PATH=/ }.first() as String
		bash.execute(originalPath)

		when:
		bash.execute("source $bootstrapScript")
		bash.execute('echo "$PATH"')

		then:
		def pathParts = bash.output.split(':')
		def pathElementMatcher = ~/$candidatesDirectory\/([^\/]+)\/.*/
		def includedCandidates = pathParts
				.collect { it.replace("\n", "") }
				.collect { it =~ pathElementMatcher }
				.findAll { it }
				.collect { it[0][1] }
				.sort()

		println("Available: $allCandidates")
		println("Included : $includedCandidates")

		def missingCandidates = allCandidates - includedCandidates
		missingCandidates.isEmpty()
	}

	void "should not duplicate PATH entries if re-sourced"() {
		given:
		bash.start()
		bash.execute("source $bootstrapScript")
		bash.resetOutput()

		when:
		bash.execute("source $bootstrapScript")
		bash.execute('echo "$PATH"')

		def pathParts = bash.output.split(':')
		def pathElementMatcher = ~/$candidatesDirectory\/([^\/]+)\/.*/
		def includedCandidates = pathParts
				.collect { it.replace("\n", "") }
				.collect { it =~ pathElementMatcher }
				.findAll { it }
				.collect { it[0][1] }
				.sort()

		println("Available: $allCandidates")
		println("Included : $includedCandidates")

		and:
		def duplicateCandidates = includedCandidates - allCandidates

		then:
		duplicateCandidates.isEmpty()
	}

	private prepareCandidateDirectories(List candidates) {
		candidates.forEach {
			def current = Paths.get("$candidatesDirectory/$it/current")
			def targetFilename = "$candidatesDirectory/$it/xxx"

			new File(targetFilename).createNewFile()
			def target = Paths.get(targetFilename)

			Files.createSymbolicLink(current, target)
		}
	}
}


================================================
FILE: src/test/groovy/sdkman/specs/SdkCompatibilitySpec.groovy
================================================
package sdkman.specs

import sdkman.support.SdkmanEnvSpecification

import java.nio.file.Files
import java.nio.file.Paths

class SdkCompatibilitySpec extends SdkmanEnvSpecification {

	def allCandidates = ["groovy", "grails", "scala", "sbt"]

	def setup() {
		bash = sdkmanBashEnvBuilder
				.withCandidates(allCandidates)
				.build()
	}

	void "should add candidate bin folder to the path if present"() {
		given:
		def candidateFolder = prepareCandidateFolder("scala", "2.11.7", true)

		and:
		bash.start()
		bash.execute("source $bootstrapScript")
		bash.resetOutput()

		when:
		bash.execute('echo $PATH')
		def pathEntries = bash.output.split(':')
		def firstPathEntry = pathEntries.first()

		then:
		firstPathEntry.contains("$candidateFolder/bin")
	}

	void "should add candidate base folder to the path if no bin folder present"() {
		given:
		def candidateFolder = prepareCandidateFolder("sbt", "1.0.3", false)

		and:
		bash.start()
		bash.execute("source $bootstrapScript")
		bash.resetOutput()

		when:
		bash.execute('echo $PATH')
		def pathEntries = bash.output.split(':')
		def firstPathEntry = pathEntries.first()

		then:
		firstPathEntry.contains("$candidateFolder")

		and:
		!firstPathEntry.contains("$candidateFolder/bin")
	}

	private prepareCandidateFolder(String candidate, String version, boolean hasBinFolder) {
		def candidateBaseDir = "${candidatesDirectory.absolutePath}/$candidate"
		def candidateCurrentDir = Paths.get("$candidateBaseDir/current")

		def candidateLocation = "${candidatesDirectory.absolutePath}/$candidate/$version"
		def candidatePath = Paths.get(candidateLocation)

		def binLocation = hasBinFolder ? "$candidateLocation/bin/" : "$candidateLocation"
		def executableFilename = "run.sh"
		def executableFile = "$binLocation/$executableFilename" as File

		new File(binLocation).mkdirs()
		executableFile.createNewFile()

		Files.createSymbolicLink(candidateCurrentDir, candidatePath)
		candidateCurrentDir.toString()
	}
}


================================================
FILE: src/test/groovy/sdkman/specs/SelfupdateFeatureSpec.groovy
================================================
package sdkman.specs

import sdkman.support.SdkmanEnvSpecification

import java.time.Instant

import static java.time.temporal.ChronoUnit.DAYS

class SelfupdateFeatureSpec extends SdkmanEnvSpecification {
	static final String CANDIDATES_API = "http://localhost:8080/2"
	static final String HEALTHCHECK_ENDPOINT = "$CANDIDATES_API/healthcheck"
	static final String VERSION_ENDPOINT = "$CANDIDATES_API/broker/download/sdkman/version/stable"
 
	def setup() {
		curlStub.primeWith(HEALTHCHECK_ENDPOINT, "echo dbfb025be9f97fda2052b5febcca0155")
		curlStub.primeWith(VERSION_ENDPOINT, "echo 5.0.0")
	}

	def "should list selfupdate as a valid command when the selfupdate feature is toggled on"() {
		given:
		bash = sdkmanBashEnvBuilder
			.withConfiguration("sdkman_selfupdate_feature", selfUpdateFeature)
			.build()

		bash.start()
		bash.execute("source $bootstrapScript")

		when:
		bash.execute("sdk help")

		then:
		verifyOutput(bash.output)

		where:
		selfUpdateFeature | verifyOutput
		"false"           | { !it.contains("selfupdate") }
		"true"            | { it.contains("selfupdate") }
	}

	def "should source sdkman-selfupdate.sh when the selfupdate feature is toggled on"() {
		given:
		bash = sdkmanBashEnvBuilder
			.withConfiguration("sdkman_selfupdate_feature", selfupdateFeature)
			.build()

		bash.start()
		bash.execute("source $bootstrapScript")

		when:
		bash.execute("sdk selfupdate")

		then:
		verifyOutput(bash.output)

		where:
		selfupdateFeature | verifyOutput
		"false"           | { it.contains("Invalid command: selfupdate") }
		"true"            | { !it.contains("Invalid command: selfupdate") }
	}
}


================================================
FILE: src/test/groovy/sdkman/steps/command_line_interop_steps.groovy
================================================
package sdkman.steps

import static cucumber.api.groovy.EN.And

And(~'^I enter \"([^\"]*)\"$') { String command ->
	bash.execute(command)
	result = bash.output
}

And(~'^I enter "([^"]*)" and answer "([^"]*)"$') { String command, String answer ->
	bash.execute(command, [answer])
	result = bash.output
}

And(~'^I see \"([^\"]*)\"$') { String output ->
	assert result.contains(output)
}

And(~'^I do not see "([^"]*)"$') { String output ->
	assert !result.contains(output)
}

And(~'^I see only \"([^\"]*)\"$') { String output ->
	assert result?.replaceAll("\\n", "") == output
}

And(~'^I see the current sdkman version$') { ->
	assert result.contains("SDKMAN")
}

And(~'^I see a single occurrence of \"([^\"]*)\"$') { String occurrence ->
	assert result.count(occurrence) == 1
}

And(~'^I see no occurrences of \"([^\"]*)\"$') { String occurrence ->
	assert result.count(occurrence) == 0
}

And(~'the "(.*)" variable contains "(.*)"') { String home, String segment ->
	bash.execute("echo \$$home")
	assert bash.output.contains(segment)
}

And(~'the "(.*)" variable is not set') { String home ->
	bash.execute("echo \$$home")
	assert !bash.output.contains(".sdkman/")
}

And(~'^the home path ends with \"([^\"]*)\"$') { String suffix ->
	def path = sdkmanBaseDir.absolutePath + "/" + suffix
	assert result.trim().endsWith(path)
}


================================================
FILE: src/test/groovy/sdkman/steps/env.groovy
================================================
package sdkman.steps

import com.github.tomakehurst.wiremock.client.WireMock
import sdkman.support.FilesystemUtils
import sdkman.support.WireMockServerProvider

import static cucumber.api.groovy.Hooks.After
import static cucumber.api.groovy.Hooks.Before

HTTP_PROXY = System.getProperty("httpProxy") ?: ""

FAKE_JDK_PATH = "/path/to/my/openjdk"
SERVICE_UP_HOST = "localhost"
SERVICE_UP_PORT = 8080
SERVICE_UP_URL = "http://$SERVICE_UP_HOST:$SERVICE_UP_PORT"
SERVICE_DOWN_URL = "http://localhost:0"

counter = "${(Math.random() * 10000).toInteger()}".padLeft(4, "0")

localGroovyCandidate = "/tmp/groovy-core" as File

sdkmanScriptVersion = "5.0.0"
sdkmanNativeVersion = "0.0.1"

sdkmanBaseEnv = FilesystemUtils.prepareBaseDir().absolutePath
sdkmanBaseDir = sdkmanBaseEnv as File

sdkmanDirEnv = "$sdkmanBaseEnv/.sdkman"
sdkmanDir = sdkmanDirEnv as File
candidatesDir = "${sdkmanDirEnv}/candidates" as File
binDir = "${sdkmanDirEnv}/bin" as File
srcDir = "${sdkmanDirEnv}/src" as File
varDir = "${sdkmanDirEnv}/var" as File
metadataDir = "${varDir}/metadata" as File
etcDir = "${sdkmanDirEnv}/etc" as File
extDir = "${sdkmanDirEnv}/ext" as File
tmpDir = "${sdkmanDir}/tmp" as File

healthcheckFile = new File(varDir, "healthcheck")
candidatesFile = new File(varDir, "candidates")
versionFile = new File(varDir, "version")
initScript = new File(binDir, "sdkman-init.sh")

localCandidates = ['groovy', 'grails', 'java', 'kotlin', 'scala']

bash = null

if (!binding.hasVariable("wireMock")) {
	wireMock = WireMockServerProvider.wireMockServer()
}

addShutdownHook {
	wireMock.stop()
}

Before() {
	WireMock.reset()
	cleanUp()
}

private cleanUp() {
	sdkmanBaseDir.deleteDir()
	localGroovyCandidate.deleteDir()
}

After() { scenario ->
	def output = bash?.output
	if (output) {
		scenario.write("\nOutput: \n${output}")
	}
	bash?.stop()
}


================================================
FILE: src/test/groovy/sdkman/steps/env_steps.groovy
================================================
package sdkman.steps

import static cucumber.api.groovy.EN.And

And(~/^the file "([^"]+)" exists and contains "([^"]+)"$/) { String filename, String content ->
	new File(sdkmanBaseEnv, filename).withWriter {
		it.writeLine(content)
	}
}


================================================
FILE: src/test/groovy/sdkman/steps/flush_steps.groovy
================================================
package sdkman.steps

import static cucumber.api.groovy.EN.And

And(~'^the candidate "([^"]*)" is known locally$') { String candidate ->
	assert candidatesFile.text.contains(candidate)
}

And(~'^no candidates are know locally$') { ->
	assert !candidatesFile.exists()
}

And(~'^the file "([^"]*)" in temporary storage$') { String fileName ->
	new File(tmpDir, fileName).createNewFile()
}

And(~'^no "([^"]*)" file is present in temporary storage$') { String fileName ->
	assert !new File(tmpDir, fileName).exists()
}

And(~'^a prior version "([^"]*)" was detected$') { String version ->
	assert versionFile.exists()
	assert versionFile.text.contains(version)
}

And(~'^no version file can be found$') { ->
	assert !versionFile.exists()
}

And(~'^the Remote Version has been flushed$') { ->
	assert versionFile.delete()
}

And(~'^a headers file "([^"]*)" in metadata directory with checksum "([^"]*)" using algorithm "([^"]*)"$') { 
	String fileName, String checksum, String algorithm ->
		
	new File(metadataDir, fileName).withWriter { out ->
		out.println "X-Sdkman-Checksum-${algorithm}: ${checksum}"
	}
}

And(~'^no metadata is cached$') { ->
	assert !metadataDir.listFiles()
}


================================================
FILE: src/test/groovy/sdkman/steps/initialisation_steps.groovy
================================================
package sdkman.steps

import sdkman.env.SdkmanBashEnvBuilder
import sdkman.stubs.UnameStub

import java.util.zip.ZipException
import java.util.zip.ZipFile

import static cucumber.api.groovy.EN.And
import static sdkman.stubs.WebServiceStub.primeEndpointWithString
import static sdkman.stubs.WebServiceStub.primeSelfupdate

And(~'^the sdkman work folder is created$') { ->
	assert sdkmanDir.isDirectory(), "The SDKMAN directory does not exist."
}

And(~'^the "([^"]*)" folder exists in user home$') { String arg1 ->
	assert sdkmanDir.isDirectory(), "The SDKMAN directory does not exist."
}

And(~'^the archive for candidate "([^"]*)" version "([^"]*)" is corrupt$') { String candidate, String version ->
	try {
		new ZipFile(new File("src/test/resources/__files/${candidate}-${version}.zip"))
		assert false, "Archive was not corrupt!"
	} catch (ZipException ze) {
		//expected behaviour
	}
}

And(~'^the archive for candidate "([^"]*)" version "([^"]*)" is removed$') { String candidate, String version ->
	def archive = new File("${sdkmanDir}/tmp/${candidate}-${version}.zip")
	assert !archive.exists()
}

And(~'^the sdkman (.*) version "(.*)" is available for download$') { format, version ->
	primeEndpointWithString("/broker/version/sdkman/${format}/stable", version)
}

And(~'^the internet is reachable$') { ->
	primeEndpointWithString("/healthcheck", "12345")
	primeSelfupdate()

	offlineMode = false
	serviceUrlEnv = SERVICE_UP_URL
	javaHome = FAKE_JDK_PATH
}

And(~'^the internet is not reachable$') { ->
	offlineMode = false
	serviceUrlEnv = SERVICE_DOWN_URL
	javaHome = FAKE_JDK_PATH
}

And(~'^offline mode is disabled with reachable internet$') { ->
	primeEndpointWithString("/healthcheck", "12345")

	offlineMode = false
	serviceUrlEnv = SERVICE_UP_URL
	javaHome = FAKE_JDK_PATH
}

And(~'^offline mode is enabled with reachable internet$') { ->
	primeEndpointWithString("/healthcheck", "12345")

	offlineMode = true
	serviceUrlEnv = SERVICE_UP_URL
	javaHome = FAKE_JDK_PATH
}

And(~'^offline mode is enabled with unreachable internet$') { ->
	offlineMode = true
	serviceUrlEnv = SERVICE_DOWN_URL
	javaHome = FAKE_JDK_PATH
}

And(~'^an "(.*)" machine with "(.*)" installed$') { String machine, String kernel ->
	def binFolder = "$sdkmanBaseDir/bin" as File
	UnameStub.prepareIn(binFolder)
			.forKernel(kernel)
			.forMachine(machine)
			.build()
}

And(~'^an initialised environment$') { ->
	bash = SdkmanBashEnvBuilder.create(sdkmanBaseDir)
			.withOfflineMode(offlineMode)
			.withCandidatesApi(serviceUrlEnv)
			.withBrokerApi(serviceUrlEnv)
			.withJdkHome(javaHome)
			.withHttpProxy(HTTP_PROXY)
			.withScriptVersion(sdkmanScriptVersion)
			.withNativeVersion(sdkmanNativeVersion)
			.withCandidates(localCandidates)
			.build()
}

And(~'^an initialised environment without debug prints$') { ->
	bash = SdkmanBashEnvBuilder.create(sdkmanBaseDir)
			.withOfflineMode(offlineMode)
			.withCandidatesApi(serviceUrlEnv)
			.withBrokerApi(serviceUrlEnv)
			.withJdkHome(javaHome)
			.withHttpProxy(HTTP_PROXY)
			.withScriptVersion(sdkmanScriptVersion)
			.withNativeVersion(sdkmanNativeVersion)
			.withCandidates(localCandidates)
			.withDebugMode(false)
			.build()
}

And(~'^the system is bootstrapped$') { ->
	bash.start()
	bash.execute("source $sdkmanDirEnv/bin/sdkman-init.sh")
}

And(~'^the system is bootstrapped again$') { ->
	bash.execute("source $sdkmanDirEnv/bin/sdkman-init.sh")
}

And(~/^the sdkman scripts version is "([^"]*)"$/) { String version ->
	sdkmanScriptVersion = version
}

And(~/^the sdkman native version is "([^"]*)"$/) { String version ->
	sdkmanNativeVersion = version
}

And(~/^the candidates cache is initialised with "(.*)"$/) { String candidate ->
	localCandidates << candidate
}

And(~/^a project configuration is active$/) { ->
	bash.execute("SDKMAN_ENV=" + sdkmanBaseEnv)
}

And(~/^a project configuration is active but points to a directory without configuration$/) { ->
	def emptyDir = tmpDir.getPath() + "/empty"
	bash.execute("mkdir $emptyDir")
	bash.execute("SDKMAN_ENV=$emptyDir")
}

================================================
FILE: src/test/groovy/sdkman/steps/installation_steps.groovy
================================================
package sdkman.steps

import java.nio.file.FileSystems
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths

import static cucumber.api.groovy.EN.And
import static java.nio.file.Files.isSameFile
import static java.nio.file.Files.isSymbolicLink
import static sdkman.support.FilesystemUtils.prepareCandidateBinFolder
import static sdkman.support.FilesystemUtils.prepareCandidateWithVersionFolder

And(~'^the candidate "([^"]*)" version "([^"]*)" is installed$') { String candidate, String version ->
	def file = "${candidatesDir}/${candidate}/${version}" as File
	if (!file.exists()) println bash.output
	assert file.exists()
}

And(~'^the candidate "([^"]*)" version "([^"]*)" is not installed$') { String candidate, String version ->
	def directory = FileSystems.default.getPath("$candidatesDir/$candidate/$version")
	if (Files.exists(directory)) println bash.output
	assert !Files.exists(directory)
}

And(~'^the candidate "([^"]*)" version "([^"]*)" is already installed and default$') { String candidate, String version ->
	def candidateVersion = prepareCandidateWithVersionFolder("$candidatesDir", candidate, version)
	def currentLink = FileSystems.default.getPath("$candidatesDir/$candidate/current")
	Files.createSymbolicLink currentLink, candidateVersion
}

And(~'^the candidate "([^"]*)" version "([^"]*)" is the default$') { String candidate, String version ->
	def localVersion = FileSystems.default.getPath("$candidatesDir/$candidate/$version")
	def currentLink = FileSystems.default.getPath("$candidatesDir/$candidate/current")
	Files.createSymbolicLink currentLink, localVersion
}

And(~'^the candidate "([^"]*)" version "([^"]*)" is already installed but not default$') { String candidate, String version ->
	prepareCandidateWithVersionFolder "$candidatesDir", candidate, version
}

And(~'^I do not have a "([^"]*)" candidate installed$') { String candidate ->
	def candidateDir = FileSystems.default.getPath("${candidatesDir}/${candidate}")
	assert !candidateDir.toFile().listFiles()
}

And(~'^the candidate "([^"]*)" does not exist locally$') { String candidate ->
	def candidateDir = "${candidatesDir}/${candidate}" as File
	candidateDir.deleteDir()
	assert !candidateDir.exists()
}

And(~'^I have a local candidate "([^"]*)" version "([^"]*)" at "([^"]*)"$') { String candidate, String version, String directory ->
	prepareCandidateBinFolder directory, candidate, version
}

And(~'^I have a local candidate "([^"]*)" version "([^"]*)" at relative path "([^"]*)"$') { String candidate, String version, String relativePath ->
	def fullPath = new File(sdkmanBaseDir.absolutePath, relativePath)
	prepareCandidateBinFolder fullPath.absolutePath, candidate, version
}

And(~'^the candidate "([^"]*)" version "([^"]*)" is linked to the relative path "([^"]*)"$') { String candidate, String version, String relativePath ->
	def fullPath = new File(sdkmanBaseDir.absolutePath, relativePath)
	assertLinkedCandidate(fullPath.absolutePath, candidate, version)
}

And(~'^the candidate "([^"]*)" version "([^"]*)" is linked to "([^"]*)"$') { String candidate, String version, String directory ->
	assertLinkedCandidate(directory, candidate, version)
}

def assertLinkedCandidate(String directory, String candidate, String version) {
	Path versionFolder = Paths.get("$candidatesDir/$candidate/$version")

	assert isSymbolicLink(versionFolder)

	assert isSameFile(versionFolder, Paths.get(directory))
}

And(~'^the candidate "([^"]*)" version "([^"]*)" is already linked to "([^"]*)"$') { String candidate, String version, String folder ->
	def fileSystem = FileSystems.default

	def candidateFolder = "$candidatesDir/$candidate" as File
	candidateFolder.mkdirs()

	def link = fileSystem.getPath("$candidatesDir/$candidate/$version")
	def target = prepareCandidateBinFolder(folder, candidate, version)

	Files.createSymbolicLink(link, target)
}

And(~'^I have configured "([^"]*)" to "([^"]*)"$') { String configName, String flag ->
	def configFile = new File("$sdkmanDir/etc/config")
	configFile.write "${configName}=${flag}"
}

And(~/^the exit code is (\d+)$/) { Integer rc ->
	assert bash.getStatus() == rc
}

And(~'^the response headers file is created for candidate "([^"]*)" and version "([^"]*)"$') { String candidate, String version ->
	def headersFile = "${metadataDir}/${candidate}-${version}.headers" as File
	assert headersFile.exists()
}

And(~'^no response headers are written for candidate "([^"]*)" and version "([^"]*)"$') { String candidate, String version ->
	def headersFile = "${metadataDir}/${candidate}-${version}.headers" as File
	assert !headersFile.exists()
}


================================================
FILE: src/test/groovy/sdkman/steps/stub_steps.groovy
================================================
package sdkman.steps

import io.cucumber.datatable.DataTable
import sdkman.support.UnixUtils

import static cucumber.api.groovy.EN.And
import static sdkman.stubs.HookResponses.*
import static sdkman.stubs.WebServiceStub.*
import static sdkman.support.FilesystemUtils.readCurrentFromCandidateFolder
import static sdkman.support.FilesystemUtils.readVersionsCsvFromCandidateFolder

And(~'^the default "([^"]*)" version is "([^"]*)"$') { String candidate, String version ->
	primeEndpointWithString("/candidates/default/${candidate}", version)
	primeDownloadFor(SERVICE_UP_URL, candidate, version, UnixUtils.inferPlatform())
	primeEndpointWithString("/hooks/post/${candidate}/${version}/${UnixUtils.inferPlatform()}", postInstallationHookSuccess())
}

And(~'^an available selfupdate endpoint$') { ->
	primeEndpointWithString("/selfupdate/stable/${UnixUtils.inferPlatform()}", 'echo "Successfully upgraded SDKMAN."')
	primeEndpointWithString("/selfupdate/beta/${UnixUtils.inferPlatform()}", 'echo "Successfully upgraded SDKMAN."')
}

And(~'^the candidate "([^"]*)" version "([^"]*)" is available for download$') { String candidate, String version ->
	primeEndpointWithString("/candidates/validate/${candidate}/${version}/${UnixUtils.inferPlatform()}", "valid")
	primeDownloadFor(SERVICE_UP_URL, candidate, version, UnixUtils.inferPlatform())
	primeEndpointWithString("/hooks/post/${candidate}/${version}/${UnixUtils.inferPlatform()}", postInstallationHookSuccess())
}

And(~'^the candidate "([^"]*)" version "([^"]*)" is available for download with checksum "([^"]*)" using algorithm "([^"]*)"$') { 
	String candidate, String version, String checksum, String algorithm ->
	primeEndpointWithString("/candidates/validate/${candidate}/${version}/${UnixUtils.inferPlatform()}", "valid")
	primeDownloadFor(SERVICE_UP_URL, candidate, version, UnixUtils.inferPlatform(), ["X-Sdkman-Checksum-${algorithm}": "${checksum}"])
	primeEndpointWithString("/hooks/post/${candidate}/${version}/${UnixUtils.inferPlatform()}", postInstallationHookSuccess())
}

And(~/^the appropriate universal hook is available for "([^"]*)" version "([^"]*)" on "([^"]*)"$/) { String candidate, String version, String os ->
	String lcPlatform = UnixUtils.inferPlatform(os)
	primeUniversalHookFor(candidate, version, lcPlatform)
}

And(~/^the appropriate multi-platform hook is available for "([^"]*)" version "([^"]*)" on "([^"]*)" with architecture "(.*)"$/) { String candidate, String version, String os, String architecture ->
	String lcPlatform = UnixUtils.inferPlatform(os, architecture)
	primePlatformSpecificHookFor(candidate, version, lcPlatform)
}

And(~'^the candidate "([^"]*)" version "([^"]*)" is not available for download$') { String candidate, String version ->
	primeEndpointWithString("/candidates/validate/${candidate}/${version}/${UnixUtils.inferPlatform()}", "invalid")
}

And(~/^the candidate "(.*)" version "(.*)" is available for download on "(.*)" with architecture "(.*)"$/) { String candidate, String version, String os, String architecture ->
	String lcPlatform = UnixUtils.inferPlatform(os, architecture)
	primeEndpointWithString("/candidates/validate/${candidate}/${version}/${lcPlatform}", "valid")
	primeDownloadFor(SERVICE_UP_URL, candidate, version, lcPlatform)
}

And(~/^a post-installation hook is served for "([^"]*)" "([^"]*)" on "([^"]*)" with architecture "([^"]*)" that returns successfully$/) { String candidate, String version, String os, String architecture ->
	String lcPlatform = UnixUtils.inferPlatform(os, architecture)
	primeEndpointWithString("/hooks/post/${candidate}/${version}/${lcPlatform}", postInstallationHookSuccess())
}

And(~/^a post-installation hook is served for "([^"]*)" "([^"]*)" on "([^"]*)" with architecture "([^"]*)" that returns a failure$/) { String candidate, String version, String os, String architecture ->
	String lcPlatform = UnixUtils.inferPlatform(os, architecture)
	primeEndpointWithString("/hooks/post/${candidate}/${version}/${lcPlatform}", postInstallationHookFailure())
}

And(~/^the candidate "(.*?)" version "(.*?)" is not available for download on "(.*?)"$/) { String candidate, String version, String os ->
	String lcPlatform = UnixUtils.inferPlatform(os)
	primeEndpointWithString("/candidates/validate/${candidate}/${version}/${lcPlatform}", "invalid")
}

And(~'^a "([^"]*)" list view is available for consumption$') { String candidate ->
	primeEndpointWithString("/candidates/${candidate}/${UnixUtils.inferPlatform()}/versions/list?current=&installed=", "Available ${candidate.capitalize()} Versions")
}

And(~'^the candidate "([^"]*)" version "([^"]*)" is a valid candidate version$') { String candidate, String version ->
	primeEndpointWithString("/candidates/validate/${candidate}/${version}/${UnixUtils.inferPlatform()}", "valid")
}

And(~'^the candidate "([^"]*)" version "([^"]*)" is not a valid candidate version$') { String candidate, String version ->
	primeEndpointWithString("/candidates/validate/${candidate}/${version}/${UnixUtils.inferPlatform()}", "invalid")
}

And(~/^the candidate "(.*?)" has a version list available$/) { String candidate ->
	def current = readCurrentFromCandidateFolder(candidatesDir, candidate)
	def versions = readVersionsCsvFromCandidateFolder(candidatesDir, candidate)
	def url = "/candidates/${candidate}/${UnixUtils.inferPlatform()}/versions/list?current=${current}&installed=${versions}"
	println("Priming url: $url")
	primeEndpointWithString(url, "Candidate: $candidate; Versions: $versions; Current: $current; Platform: ${UnixUtils.inferPlatform()}")
}

And(~/^The candidate list is available$/) { ->
	primeEndpointWithString("/candidates/list", "Candidate List")
}

And(~/^the following candidates are currently available from remote API:$/) { DataTable dt ->
	primeEndpointWithString("/candidates/all", dt.asList(String).drop(1).join(","))
}


================================================
FILE: src/test/groovy/sdkman/steps/update_steps.groovy
================================================
package sdkman.steps

import io.cucumber.datatable.DataTable

import static cucumber.api.groovy.EN.And

And(~/^the following candidates are available for installation in local cache:$/) { DataTable dt ->
	localCandidates = dt.asList(String).drop(1)
}

And(~/^the Candidates cache should contain "(.*)"$/) { String candidates ->
	assert candidatesFile.text.trim() == candidates
}


================================================
FILE: src/test/groovy/sdkman/steps/use_steps.groovy
================================================
package sdkman.steps

import java.nio.file.FileSystems
import java.nio.file.Files

import static cucumber.api.groovy.EN.And

And(~'^the candidate "([^"]*)" version "([^"]*)" is in use$') { String candidate, String version ->
	def directory = FileSystems.default.getPath("$candidatesDir/$candidate/$version")
	def current = FileSystems.default.getPath("$candidatesDir/$candidate/current")
	def symlinkFile = current.toFile()
	if (!symlinkFile.exists()) {
		assert Files.createSymbolicLink(current, directory)
	}
}

And(~'^the candidate "([^"]*)" version "([^"]*)" is not in use$') { String candidate, String version ->
	def directory = FileSystems.default.getPath("$candidatesDir/$candidate/$version")
	def current = FileSystems.default.getPath("$candidatesDir/$candidate/current")
	def symlinkFile = current.toFile()
	if (symlinkFile.exists()) {
		assert !Files.isSameFile(current, directory)
	}
}

And(~'^the candidate "([^"]*)" version "([^"]*)" should be in use$') { String candidate, String version ->
	bash.execute("$candidate --version")
	assert bash.output.contains(version)
}

And(~'^the candidate "([^"]*)" version "([^"]*)" should be the default$') { String candidate, String version ->
	def directory = FileSystems.default.getPath("$candidatesDir/$candidate/$version")
	def current = FileSystems.default.getPath("$candidatesDir/$candidate/current")
	assert Files.isSameFile(current, directory)
}

And(~'^the candidate "([^"]*)" version "([^"]*)" should not be the default$') { String candidate, String version ->
	def directory = FileSystems.default.getPath("$candidatesDir/$candidate/$version")
	def current = FileSystems.default.getPath("$candidatesDir/$candidate/current")
	assert (!Files.isSymbolicLink(current) || (Files.isSymbolicLink(current) && !Files.isSameFile(current, directory)))
}

And(~'^the candidate "([^"]*)" is no longer selected$') { String candidate ->
	def symlink = new File("$candidatesDir/$candidate/current")
	assert !symlink.exists()
}


================================================
FILE: src/test/groovy/sdkman/stubs/CurlStub.groovy
================================================
package sdkman.stubs

class CurlStub {

	private File file
	private commands = [:]

	static CurlStub prepareIn(File folder) {
		folder.mkdirs()

		def file = new File(folder, "curl")
		file.createNewFile()
		file.write "#!/usr/bin/env bash\n"
		file.executable = true

		new CurlStub(file: file)
	}

	CurlStub primeWith(String request, String snippet) {
		commands.put request, snippet
		this
	}

	void build() {
		commands.each { request, snippet ->
			//use last arg because we use curl with options
			file << 'if [[ "${@: -1}" == "'
			file << "$request"
			file << '" ]]; then\n'
			file << "    $snippet\n"
			file << 'fi\n'
		}
	}
}


================================================
FILE: src/test/groovy/sdkman/stubs/HookResponses.groovy
================================================
package sdkman.stubs

class HookResponses {
	
	static postInstallationHookSuccess() {
		'''\
#!/usr/bin/env bash
function __sdkman_post_installation_hook {
	mv -f $binary_input $zip_output
	echo "Post-installation hook success"
	return 0
}
'''
	}

	static postInstallationHookFailure() {
		'''\
#!/usr/bin/env bash
function __sdkman_post_installation_hook {
	echo "Post-installation hook failure"
	return 1
}
'''
	}
}


================================================
FILE: src/test/groovy/sdkman/stubs/UnameStub.groovy
================================================
package sdkman.stubs

class UnameStub {

	private File file
	def kernel = "Linux"
	def machine = "x86_64"

	static UnameStub prepareIn(File folder) {
		folder.mkdirs()

		def file = new File(folder, "uname")
		file.createNewFile()
		file.write "#!/usr/bin/env bash\n"
		file.executable = true

		new UnameStub(file: file)
	}

	UnameStub forKernel(String kernel) {
		this.kernel = kernel
		this
	}
	
	UnameStub forMachine(String machine) {
		this.machine = machine
		this
	}
	
	void build() {
		file << """
			|if [[ "\$1" == '-m' ]]; then
			|	echo "$machine"
			|elif [[ "\$1" == '-s' ]]; then
			|	echo "$kernel"
			|else
			|	echo "$machine"
			|fi
			""".stripMargin('|')
	}
}


================================================
FILE: src/test/groovy/sdkman/stubs/WebServiceStub.groovy
================================================
package sdkman.stubs

import static com.github.tomakehurst.wiremock.client.WireMock.*

class WebServiceStub {

	static primeEndpointWithString(String endpoint, String body) {
		stubFor(get(urlEqualTo(endpoint)).willReturn(
				aResponse()
						.withStatus(200)
						.withHeader("Content-Type", "text/plain")
						.withBody(body)))
	}

	static primeUniversalHookFor(String candidate, String version, String platform) {
		primeHookFor(candidate, version, platform, true)
	}

	static primePlatformSpecificHookFor(String candidate, String version, String platform) {
		primeHookFor(candidate, version, platform, false)
	}

	private static primeHookFor(String candidate, String version, String platform, boolean universal = true) {
		def hookFile = "hooks/post_hook_${candidate}_${version}_${universal ? 'universal' : platform}.sh"
		stubFor(get(urlEqualTo("/hooks/post/$candidate/$version/$platform")).willReturn(
				aResponse()
						.withStatus(200)
						.withHeader("Content-Type", "text/plain")
						.withBodyFile(hookFile)))
	}

	static primeDownloadFor(String host, String candidate, String version, String platform) {
		primeDownloadFor(host, candidate, version, platform, [:])
	}
	
	static primeDownloadFor(String host, String candidate, String version, String platform, Map<String, String> headers) {
		def binary = (candidate == "java") ? "jdk-${version}-${platform}.tar.gz" : "${candidate}-${version}.zip"
		def responseBuilder = aResponse()
				.withHeader("Location", "${host}/${binary}")
				.withStatus(302)
		headers.each { responseBuilder.withHeader(it.key, it.value) }

		stubFor(get(urlEqualTo("/download/${candidate}/${version}/${platform}"))
				.willReturn(responseBuilder))

		stubFor(get(urlEqualTo("/$binary")).willReturn(
				aResponse()
						.withStatus(200)
						.withHeader("Content-Type", "application/zip")
						.withBodyFile(binary)))
	}

	static primeSelfupdate() {
		stubFor(get(urlEqualTo("/selfupdate")).willReturn(
				aResponse()
						.withStatus(200)
						.withHeader("Content-Type", "text/plain")
						.withBodyFile("selfupdate.sh")))
	}
}


================================================
FILE: src/test/groovy/sdkman/support/BashEnvSpecification.groovy
================================================
package sdkman.support

import sdkman.env.BashEnv
import spock.lang.Specification

abstract class BashEnvSpecification extends Specification {

	BashEnv bash

	void cleanup() {
		println bash.output
		bash.stop()
	}
}


================================================
FILE: src/test/groovy/sdkman/support/FilesystemUtils.groovy
================================================
package sdkman.support

import java.nio.file.FileSystems
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths

class FilesystemUtils {

	static final DEFAULT_BASE_DIR = "/tmp/sdkman-test"

	static readVersionsCsvFromCandidateFolder(File baseDir, String candidate) {
		def versionFiles = new File(baseDir, candidate).listFiles()
		versionFiles?.findAll { it.name != "current" }?.sort()?.collect { it.name }?.join(",") ?: ""
	}

	static readCurrentFromCandidateFolder(File baseDir, String candidate) {
		def versionFiles = new File(baseDir, candidate).listFiles()
		def currentSymlinkPath = versionFiles.find { it.name == "current" }?.absolutePath
		currentSymlinkPath ? Files.readSymbolicLink(Paths.get(currentSymlinkPath)).fileName : ""
	}

	static prepareBaseDir() {
		def baseDir = "$DEFAULT_BASE_DIR/${UUID.randomUUID()}" as File
		baseDir.mkdirs()
		baseDir
	}

	static prepareCandidateWithVersionFolder(String baseDir, String candidate, String version) {
		def directory = "$baseDir/$candidate/$version"
		prepareCandidateBinFolder directory, candidate, version
	}

	static prepareCandidateBinFolder(String folder, String candidate, String version) {
		def fileSystem = FileSystems.default

		def binFolderPath = fileSystem.getPath("$folder/bin")
		Files.createDirectories binFolderPath
		prepareCandidateExecutable binFolderPath, candidate, version

		return fileSystem.getPath("$folder")
	}

	private static prepareCandidateExecutable(Path binFolder, String candidate, String version) {
		def candidateFile = new File("$binFolder/$candidate")
		candidateFile.write "echo ${candidate.capitalize()} Version: ${version}"
		candidateFile.executable = true
	}
}


================================================
FILE: src/test/groovy/sdkman/support/SdkmanEnvSpecification.groovy
================================================
package sdkman.support

import sdkman.env.SdkmanBashEnvBuilder
import sdkman.stubs.CurlStub
import sdkman.stubs.UnameStub

import static sdkman.support.FilesystemUtils.prepareBaseDir

abstract class SdkmanEnvSpecification extends BashEnvSpecification {

	SdkmanBashEnvBuilder sdkmanBashEnvBuilder

	CurlStub curlStub
	UnameStub unameStub

	File sdkmanBaseDirectory
	File sdkmanDotDirectory
	File candidatesDirectory

	String bootstrapScript

	def setup() {
		sdkmanBaseDirectory = prepareBaseDir()
		curlStub = CurlStub.prepareIn(new File(sdkmanBaseDirectory, "bin"))
		unameStub = UnameStub.prepareIn(new File(sdkmanBaseDirectory, "bin"))
		sdkmanBashEnvBuilder = SdkmanBashEnvBuilder
				.create(sdkmanBaseDirectory)
				.withUnameStub(unameStub)
				.withCurlStub(curlStub)

		sdkmanDotDirectory = new File(sdkmanBaseDirectory, ".sdkman")
		candidatesDirectory = new File(sdkmanDotDirectory, "candidates")
		bootstrapScript = "${sdkmanDotDirectory}/bin/sdkman-init.sh"
	}

	def cleanup() {
		assert sdkmanBaseDirectory.deleteDir()
	}
}


================================================
FILE: src/test/groovy/sdkman/support/UnixUtils.groovy
================================================
package sdkman.support

class UnixUtils {

	private static platforms = [
			"Linux"   : [
					"x86_64": "linuxx64",
					"aarch64": "linuxarm64",
					
			],
			"Darwin": [
					"x86_64": "darwinx64",
					"arm64": "darwinarm64",
			]
	]

	static execute(String command) {
		command.execute().text.trim()
	}

	static osName() { execute("uname -s") }

	static osArch() { execute("uname -m") }

	static inferPlatform(
			String osName = osName(),
			String architecture = osArch()) {
		platforms[osName][architecture] ?: osName.toLowerCase()
	}
}


================================================
FILE: src/test/groovy/sdkman/support/WireMockServerProvider.groovy
================================================
package sdkman.support

import com.github.tomakehurst.wiremock.WireMockServer
import com.github.tomakehurst.wiremock.client.WireMock

import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig

class WireMockServerProvider {

	static SERVICE_UP_HOST = "localhost"
	static SERVICE_UP_PORT = 8080
	static WireMockServer wireMockServer

	static def wireMockServer() {
		wireMockServer ?: createWireMockServer()
	}

	private static def createWireMockServer() {
		wireMockServer = new WireMockServer(wireMockConfig().port(SERVICE_UP_PORT))
		wireMockServer.start()
		WireMock.configureFor(SERVICE_UP_HOST, SERVICE_UP_PORT)
		wireMockServer
	}
}


================================================
FILE: src/test/jmeter/SDKman.jmx
================================================
<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="2.5" jmeter="2.10 r1533061">
  <hashTree>
    <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="SDKman" enabled="true">
      <stringProp name="TestPlan.comments"></stringProp>
      <boolProp name="TestPlan.functional_mode">false</boolProp>
      <boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
      <elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
        <collectionProp name="Arguments.arguments"/>
      </elementProp>
      <stringProp name="TestPlan.user_define_classpath"></stringProp>
    </TestPlan>
    <hashTree>
      <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Thread Group" enabled="true">
        <stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
        <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
          <boolProp name="LoopController.continue_forever">false</boolProp>
          <intProp name="LoopController.loops">-1</intProp>
        </elementProp>
        <stringProp name="ThreadGroup.num_threads">1</stringProp>
        <stringProp name="ThreadGroup.ramp_time">1</stringProp>
        <longProp name="ThreadGroup.start_time">1382782760000</longProp>
        <longProp name="ThreadGroup.end_time">1382782760000</longProp>
        <boolProp name="ThreadGroup.scheduler">false</boolProp>
        <stringProp name="ThreadGroup.duration"></stringProp>
        <stringProp name="ThreadGroup.delay"></stringProp>
      </ThreadGroup>
      <hashTree>
        <CounterConfig guiclass="CounterConfigGui" testclass="CounterConfig" testname="Counter" enabled="true">
          <stringProp name="CounterConfig.start">0</stringProp>
          <stringProp name="CounterConfig.end">100000</stringProp>
          <stringProp name="CounterConfig.incr">1</stringProp>
          <stringProp name="CounterConfig.name">counter</stringProp>
          <stringProp name="CounterConfig.format">00000</stringProp>
          <boolProp name="CounterConfig.per_user">true</boolProp>
        </CounterConfig>
        <hashTree/>
        <Arguments guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
          <collectionProp name="Arguments.arguments">
            <elementProp name="host" elementType="Argument">
              <stringProp name="Argument.name">host</stringProp>
              <stringProp name="Argument.value">localhost</stringProp>
              <stringProp name="Argument.metadata">=</stringProp>
            </elementProp>
            <elementProp name="port" elementType="Argument">
              <stringProp name="Argument.name">port</stringProp>
              <stringProp name="Argument.value">8080</stringProp>
              <stringProp name="Argument.metadata">=</stringProp>
            </elementProp>
          </collectionProp>
        </Arguments>
        <hashTree/>
        <CSVDataSet guiclass="TestBeanGUI" testclass="CSVDataSet" testname="CSV Data Set Config" enabled="true">
          <stringProp name="delimiter">,</stringProp>
          <stringProp name="fileEncoding"></stringProp>
          <stringProp name="filename">candidates.csv</stringProp>
          <boolProp name="quotedData">false</boolProp>
          <boolProp name="recycle">true</boolProp>
          <stringProp name="shareMode">shareMode.thread</stringProp>
          <boolProp name="stopThread">false</boolProp>
          <stringProp name="variableNames">candidate,version</stringProp>
        </CSVDataSet>
        <hashTree/>
        <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="API Alive" enabled="true">
          <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
            <collectionProp name="Arguments.arguments"/>
          </elementProp>
          <stringProp name="HTTPSampler.domain">${host}</stringProp>
          <stringProp name="HTTPSampler.port">${po
Download .txt
gitextract_5nup9gj_/

├── .editorconfig
├── .github/
│   ├── CODEOWNERS
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   ├── config.yml
│   │   ├── feature_request.md
│   │   └── support_request.md
│   ├── auto_assign.yml
│   ├── dependabot.yml
│   ├── pull_request_template.md
│   └── workflows/
│       ├── beta.yml
│       ├── chloe-triage.yml
│       ├── notify-on-failure.yml
│       ├── pr.yml
│       └── release.yml
├── .gitignore
├── .sdkmanrc
├── CLAUDE.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Dockerfile
├── LICENSE
├── README.md
├── bin/
│   └── release-binary.sh
├── build.gradle
├── contrib/
│   └── completion/
│       └── bash/
│           └── sdk
├── dco.txt
├── gradle/
│   ├── archive.gradle
│   ├── release.gradle
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── settings.gradle
└── src/
    ├── jreleaser/
    │   └── changelog.tpl
    ├── main/
    │   └── bash/
    │       ├── sdkman-availability.sh
    │       ├── sdkman-cache.sh
    │       ├── sdkman-config.sh
    │       ├── sdkman-current.sh
    │       ├── sdkman-default.sh
    │       ├── sdkman-env-helpers.sh
    │       ├── sdkman-env.sh
    │       ├── sdkman-flush.sh
    │       ├── sdkman-help.sh
    │       ├── sdkman-home.sh
    │       ├── sdkman-init.sh
    │       ├── sdkman-install.sh
    │       ├── sdkman-list.sh
    │       ├── sdkman-main.sh
    │       ├── sdkman-offline.sh
    │       ├── sdkman-path-helpers.sh
    │       ├── sdkman-selfupdate.sh
    │       ├── sdkman-uninstall.sh
    │       ├── sdkman-update.sh
    │       ├── sdkman-upgrade.sh
    │       ├── sdkman-use.sh
    │       ├── sdkman-utils.sh
    │       └── sdkman-version.sh
    └── test/
        ├── groovy/
        │   └── sdkman/
        │       ├── cucumber/
        │       │   └── RunCukeTests.groovy
        │       ├── env/
        │       │   ├── BashEnv.groovy
        │       │   ├── CleanBashEnvBuilder.groovy
        │       │   └── SdkmanBashEnvBuilder.groovy
        │       ├── specs/
        │       │   ├── CandidatesCacheUpdateFailureSpec.groovy
        │       │   ├── CandidatesCacheUpdateSpec.groovy
        │       │   ├── CompletionSpec.groovy
        │       │   ├── ConfigCommandSpec.groovy
        │       │   ├── CurrentCommandSpec.groovy
        │       │   ├── EnvCommandSpec.groovy
        │       │   ├── InitialisationSpec.groovy
        │       │   ├── SdkCompatibilitySpec.groovy
        │       │   └── SelfupdateFeatureSpec.groovy
        │       ├── steps/
        │       │   ├── command_line_interop_steps.groovy
        │       │   ├── env.groovy
        │       │   ├── env_steps.groovy
        │       │   ├── flush_steps.groovy
        │       │   ├── initialisation_steps.groovy
        │       │   ├── installation_steps.groovy
        │       │   ├── stub_steps.groovy
        │       │   ├── update_steps.groovy
        │       │   └── use_steps.groovy
        │       ├── stubs/
        │       │   ├── CurlStub.groovy
        │       │   ├── HookResponses.groovy
        │       │   ├── UnameStub.groovy
        │       │   └── WebServiceStub.groovy
        │       └── support/
        │           ├── BashEnvSpecification.groovy
        │           ├── FilesystemUtils.groovy
        │           ├── SdkmanEnvSpecification.groovy
        │           ├── UnixUtils.groovy
        │           └── WireMockServerProvider.groovy
        ├── jmeter/
        │   ├── SDKman.jmx
        │   └── candidates.csv
        └── resources/
            ├── __files/
            │   └── hooks/
            │       ├── post_hook_java_8.0.101_linuxx64.sh
            │       ├── post_hook_java_8.0.111_linuxx64.sh
            │       └── post_hook_java_8.0.111_universal.sh
            └── features/
                ├── checksum_verification.feature
                ├── command_line_interop.feature
                ├── current_candidate.feature
                ├── default_version.feature
                ├── flush.feature
                ├── home.feature
                ├── hooks.feature
                ├── install_candidate.feature
                ├── install_sdkman.feature
                ├── java_installation.feature
                ├── list_candidate_versions.feature
                ├── list_candidates.feature
                ├── local_developement_versions.feature
                ├── mnemonics.feature
                ├── offline_mode.feature
                ├── path_initialisation.feature
                ├── per_project_configuration.feature
                ├── self_update.feature
                ├── service_unavailable.feature
                ├── uninstall_candidate.feature
                ├── update.feature
                ├── upgrade_candidate.feature
                ├── use_version.feature
                └── version.feature
Condensed preview — 117 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (305K chars).
[
  {
    "path": ".editorconfig",
    "chars": 8792,
    "preview": "[*]\ncharset = utf-8\nend_of_line = lf\nindent_size = 4\nindent_style = tab\ninsert_final_newline = false\nmax_line_length = 1"
  },
  {
    "path": ".github/CODEOWNERS",
    "chars": 21,
    "preview": "* @sdkman/sdkman-cli\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 24,
    "preview": "open_collective: sdkman\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 941,
    "preview": "---\nname: Bug report\nabout: You found a bug in SDKMAN!\ntitle: \"Bug: [A concise title]\"\nlabels: 'bug'\nassignees: ''\n\n---\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 476,
    "preview": "blank_issues_enabled: false\ncontact_links:\n  - name: SDKMAN! usage documentation\n    url: https://sdkman.io/usage\n    ab"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 710,
    "preview": "---\nname: Feature request\nabout: Suggest an idea for SDKMAN!\ntitle: 'Feature: [A concise title]'\nlabels: 'enhancement'\na"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/support_request.md",
    "chars": 1164,
    "preview": "---\nname: Support request or question\nabout: You need help using SDKMAN!\ntitle: \"Question: [Short summary]\"\nlabels: 'sup"
  },
  {
    "path": ".github/auto_assign.yml",
    "chars": 108,
    "preview": "---\naddReviewers: true\naddAssignees: author\n\nreviewers:\n  - marc0der\n  - helpermethod\n\nnumberOfReviewers: 0\n"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 111,
    "preview": "version: 2\nupdates:\n  - package-ecosystem: github-actions\n    directory: /\n    schedule:\n      interval: daily\n"
  },
  {
    "path": ".github/pull_request_template.md",
    "chars": 248,
    "preview": "<!-- To raise a new Pull Request, the following prerequisites need to be met. Please tick before proceeding: -->\n\n- [ ] "
  },
  {
    "path": ".github/workflows/beta.yml",
    "chars": 1590,
    "preview": "name: Beta Release\non:\n  push:\n    branches:\n      - master\n    paths-ignore:\n      - .github/workflows/*\n\njobs:\n  pre-r"
  },
  {
    "path": ".github/workflows/chloe-triage.yml",
    "chars": 249,
    "preview": "name: Notify Chloé (Issue Triage)\n\non:\n  issues:\n    types: [opened]\n  issue_comment:\n    types: [created]\n  pull_reques"
  },
  {
    "path": ".github/workflows/notify-on-failure.yml",
    "chars": 235,
    "preview": "name: Notify on Failure\n\non:\n  workflow_run:\n    workflows: [\"*\"]\n    branches: [master, main]\n    types: [completed]\n\nj"
  },
  {
    "path": ".github/workflows/pr.yml",
    "chars": 673,
    "preview": "name: Pull Requests\n#run only on PR, not fork. Pull request that way the PR submitter will not have access to the repo c"
  },
  {
    "path": ".github/workflows/release.yml",
    "chars": 1840,
    "preview": "name: Release\non:\n  workflow_dispatch:\n    inputs:\n      version:\n        description: \"Release version\"\n        require"
  },
  {
    "path": ".gitignore",
    "chars": 208,
    "preview": "*~\n.#*\n.classpath\n.gradle/\n.gradletasknamecache\n.idea/\n.pid.lock\n.project\n.settings/\n.vscode/\n\\#*\nbuild/\nclasses\nsdkman-"
  },
  {
    "path": ".sdkmanrc",
    "chars": 113,
    "preview": "# Enable auto-env through the sdkman_auto_env config\n# Add key=value pairs of SDKs to use below\njava=11.0.17-tem\n"
  },
  {
    "path": "CLAUDE.md",
    "chars": 3507,
    "preview": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## "
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 5469,
    "preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participa"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 4297,
    "preview": "# Contributing to SDKMAN! CLI\n\nThank you for your interest in contributing to SDKMAN! CLI. We greatly value the feedback"
  },
  {
    "path": "Dockerfile",
    "chars": 175,
    "preview": "FROM openjdk:11\n\nRUN apt-get update && \\\n  apt-get -y install zip\n\n# Copy all here\nRUN mkdir -p /usr/src/app\nADD . /usr/"
  },
  {
    "path": "LICENSE",
    "chars": 10174,
    "preview": "\n                                 Apache License\n                           Version 2.0, January 2004\n                  "
  },
  {
    "path": "README.md",
    "chars": 4301,
    "preview": "# SDKMAN! CLI\n### The Software Development Kit Manager Command Line Interface\n\n[![Backers on Open Collective](https://im"
  },
  {
    "path": "bin/release-binary.sh",
    "chars": 688,
    "preview": "#!/usr/bin/env bash\n\nMONGO_URL=\"$1\"\nMONGO_USERNAME=\"$2\"\nMONGO_PASSWORD=\"$3\"\nPARAM_1=\"$4\"\nPARAM_2=\"$5\"\n\necho \"Mongo URL: "
  },
  {
    "path": "build.gradle",
    "chars": 1928,
    "preview": "plugins {\n\tid('groovy')\n\tid('org.jreleaser').version('1.4.0').apply(false)\n}\n\nString userHome = System.getProperty('user"
  },
  {
    "path": "contrib/completion/bash/sdk",
    "chars": 2090,
    "preview": "#!/usr/bin/bash\n\n_sdk() {\n\tlocal -r previous_word=${COMP_WORDS[COMP_CWORD - 1]}\n\tlocal -r current_word=${COMP_WORDS[COMP"
  },
  {
    "path": "dco.txt",
    "chars": 1421,
    "preview": "Developer Certificate of Origin\nVersion 1.1\n\nCopyright (C) 2004, 2006 The Linux Foundation and its contributors.\n1 Lette"
  },
  {
    "path": "gradle/archive.gradle",
    "chars": 1035,
    "preview": "import org.apache.tools.ant.filters.ReplaceTokens\nimport org.gradle.api.tasks.testing.logging.TestExceptionFormat\n\ndef b"
  },
  {
    "path": "gradle/release.gradle",
    "chars": 1147,
    "preview": "apply plugin: 'org.jreleaser'\n\njreleaser {\n\tproject {\n\t\tname = 'sdkman-cli'\n\t\tversion = sdkmanVersion\n\t\tdescription = 'S"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "chars": 202,
    "preview": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributi"
  },
  {
    "path": "gradlew",
    "chars": 5766,
    "preview": "#!/usr/bin/env sh\n\n#\n# Copyright 2015 the original author or authors.\n#\n# Licensed under the Apache License, Version 2.0"
  },
  {
    "path": "settings.gradle",
    "chars": 128,
    "preview": "pluginManagement {\n\trepositories {\n\t\tmavenLocal()\n\t\tgradlePluginPortal()\n\t\tmavenCentral()\n\t}\n}\n\nrootProject.name = \"sdkm"
  },
  {
    "path": "src/jreleaser/changelog.tpl",
    "chars": 61,
    "preview": "## Changelog\n\n{{changelogChanges}}\n{{changelogContributors}}\n"
  },
  {
    "path": "src/main/bash/sdkman-availability.sh",
    "chars": 2485,
    "preview": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/bash/sdkman-cache.sh",
    "chars": 1024,
    "preview": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/bash/sdkman-config.sh",
    "chars": 941,
    "preview": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/bash/sdkman-current.sh",
    "chars": 2139,
    "preview": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/bash/sdkman-default.sh",
    "chars": 1326,
    "preview": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/bash/sdkman-env-helpers.sh",
    "chars": 3169,
    "preview": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/bash/sdkman-env.sh",
    "chars": 4297,
    "preview": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/bash/sdkman-flush.sh",
    "chars": 1575,
    "preview": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/bash/sdkman-help.sh",
    "chars": 2688,
    "preview": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/bash/sdkman-home.sh",
    "chars": 1299,
    "preview": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/bash/sdkman-init.sh",
    "chars": 5071,
    "preview": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/bash/sdkman-install.sh",
    "chars": 7635,
    "preview": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/bash/sdkman-list.sh",
    "chars": 3228,
    "preview": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/bash/sdkman-main.sh",
    "chars": 3518,
    "preview": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/bash/sdkman-offline.sh",
    "chars": 920,
    "preview": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/bash/sdkman-path-helpers.sh",
    "chars": 2464,
    "preview": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/bash/sdkman-selfupdate.sh",
    "chars": 2349,
    "preview": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/bash/sdkman-uninstall.sh",
    "chars": 1503,
    "preview": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/bash/sdkman-update.sh",
    "chars": 2357,
    "preview": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/bash/sdkman-upgrade.sh",
    "chars": 3443,
    "preview": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/bash/sdkman-use.sh",
    "chars": 1956,
    "preview": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/bash/sdkman-utils.sh",
    "chars": 3040,
    "preview": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/main/bash/sdkman-version.sh",
    "chars": 791,
    "preview": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"Lic"
  },
  {
    "path": "src/test/groovy/sdkman/cucumber/RunCukeTests.groovy",
    "chars": 324,
    "preview": "package sdkman.cucumber\n\nimport io.cucumber.junit.Cucumber\nimport io.cucumber.junit.CucumberOptions\nimport org.junit.run"
  },
  {
    "path": "src/test/groovy/sdkman/env/BashEnv.groovy",
    "chars": 4421,
    "preview": "package sdkman.env\n\nimport groovy.transform.ToString\n\n/**\n * <p>As part of the sdkman test suite we need to launch a bas"
  },
  {
    "path": "src/test/groovy/sdkman/env/CleanBashEnvBuilder.groovy",
    "chars": 544,
    "preview": "package sdkman.env\n\nclass CleanBashEnvBuilder {\n\n\tprivate final File baseFolder\n\tString httpProxy\n\n\tstatic CleanBashEnvB"
  },
  {
    "path": "src/test/groovy/sdkman/env/SdkmanBashEnvBuilder.groovy",
    "chars": 6524,
    "preview": "package sdkman.env\n\nimport groovy.transform.ToString\nimport sdkman.stubs.CurlStub\nimport sdkman.stubs.UnameStub\nimport s"
  },
  {
    "path": "src/test/groovy/sdkman/specs/CandidatesCacheUpdateFailureSpec.groovy",
    "chars": 1014,
    "preview": "package sdkman.specs\n\nimport sdkman.support.SdkmanEnvSpecification\n\nclass CandidatesCacheUpdateFailureSpec extends Sdkma"
  },
  {
    "path": "src/test/groovy/sdkman/specs/CandidatesCacheUpdateSpec.groovy",
    "chars": 1796,
    "preview": "package sdkman.specs\n\nimport sdkman.support.SdkmanEnvSpecification\n\nclass CandidatesCacheUpdateSpec extends SdkmanEnvSpe"
  },
  {
    "path": "src/test/groovy/sdkman/specs/CompletionSpec.groovy",
    "chars": 1709,
    "preview": "package sdkman.specs\n\nimport sdkman.support.SdkmanEnvSpecification\n\nclass CompletionSpec extends SdkmanEnvSpecification "
  },
  {
    "path": "src/test/groovy/sdkman/specs/ConfigCommandSpec.groovy",
    "chars": 1266,
    "preview": "package sdkman.specs\n\nimport sdkman.support.SdkmanEnvSpecification\n\nclass ConfigCommandSpec extends SdkmanEnvSpecificati"
  },
  {
    "path": "src/test/groovy/sdkman/specs/CurrentCommandSpec.groovy",
    "chars": 1798,
    "preview": "package sdkman.specs\n\nimport sdkman.support.SdkmanEnvSpecification\n\nimport java.nio.file.Paths\n\nimport static java.nio.f"
  },
  {
    "path": "src/test/groovy/sdkman/specs/EnvCommandSpec.groovy",
    "chars": 8668,
    "preview": "package sdkman.specs\n\nimport sdkman.support.SdkmanEnvSpecification\n\nimport java.nio.file.Paths\n\nimport static java.nio.f"
  },
  {
    "path": "src/test/groovy/sdkman/specs/InitialisationSpec.groovy",
    "chars": 3041,
    "preview": "package sdkman.specs\n\nimport sdkman.support.SdkmanEnvSpecification\n\nimport java.nio.file.Files\nimport java.nio.file.Path"
  },
  {
    "path": "src/test/groovy/sdkman/specs/SdkCompatibilitySpec.groovy",
    "chars": 1973,
    "preview": "package sdkman.specs\n\nimport sdkman.support.SdkmanEnvSpecification\n\nimport java.nio.file.Files\nimport java.nio.file.Path"
  },
  {
    "path": "src/test/groovy/sdkman/specs/SelfupdateFeatureSpec.groovy",
    "chars": 1633,
    "preview": "package sdkman.specs\n\nimport sdkman.support.SdkmanEnvSpecification\n\nimport java.time.Instant\n\nimport static java.time.te"
  },
  {
    "path": "src/test/groovy/sdkman/steps/command_line_interop_steps.groovy",
    "chars": 1330,
    "preview": "package sdkman.steps\n\nimport static cucumber.api.groovy.EN.And\n\nAnd(~'^I enter \\\"([^\\\"]*)\\\"$') { String command ->\n\tbash"
  },
  {
    "path": "src/test/groovy/sdkman/steps/env.groovy",
    "chars": 1835,
    "preview": "package sdkman.steps\n\nimport com.github.tomakehurst.wiremock.client.WireMock\nimport sdkman.support.FilesystemUtils\nimpor"
  },
  {
    "path": "src/test/groovy/sdkman/steps/env_steps.groovy",
    "chars": 237,
    "preview": "package sdkman.steps\n\nimport static cucumber.api.groovy.EN.And\n\nAnd(~/^the file \"([^\"]+)\" exists and contains \"([^\"]+)\"$"
  },
  {
    "path": "src/test/groovy/sdkman/steps/flush_steps.groovy",
    "chars": 1180,
    "preview": "package sdkman.steps\n\nimport static cucumber.api.groovy.EN.And\n\nAnd(~'^the candidate \"([^\"]*)\" is known locally$') { Str"
  },
  {
    "path": "src/test/groovy/sdkman/steps/initialisation_steps.groovy",
    "chars": 4038,
    "preview": "package sdkman.steps\n\nimport sdkman.env.SdkmanBashEnvBuilder\nimport sdkman.stubs.UnameStub\n\nimport java.util.zip.ZipExce"
  },
  {
    "path": "src/test/groovy/sdkman/steps/installation_steps.groovy",
    "chars": 4615,
    "preview": "package sdkman.steps\n\nimport java.nio.file.FileSystems\nimport java.nio.file.Files\nimport java.nio.file.Path\nimport java."
  },
  {
    "path": "src/test/groovy/sdkman/steps/stub_steps.groovy",
    "chars": 5841,
    "preview": "package sdkman.steps\n\nimport io.cucumber.datatable.DataTable\nimport sdkman.support.UnixUtils\n\nimport static cucumber.api"
  },
  {
    "path": "src/test/groovy/sdkman/steps/update_steps.groovy",
    "chars": 379,
    "preview": "package sdkman.steps\n\nimport io.cucumber.datatable.DataTable\n\nimport static cucumber.api.groovy.EN.And\n\nAnd(~/^the follo"
  },
  {
    "path": "src/test/groovy/sdkman/steps/use_steps.groovy",
    "chars": 1974,
    "preview": "package sdkman.steps\n\nimport java.nio.file.FileSystems\nimport java.nio.file.Files\n\nimport static cucumber.api.groovy.EN."
  },
  {
    "path": "src/test/groovy/sdkman/stubs/CurlStub.groovy",
    "chars": 640,
    "preview": "package sdkman.stubs\n\nclass CurlStub {\n\n\tprivate File file\n\tprivate commands = [:]\n\n\tstatic CurlStub prepareIn(File fold"
  },
  {
    "path": "src/test/groovy/sdkman/stubs/HookResponses.groovy",
    "chars": 418,
    "preview": "package sdkman.stubs\n\nclass HookResponses {\n\t\n\tstatic postInstallationHookSuccess() {\n\t\t'''\\\n#!/usr/bin/env bash\nfunctio"
  },
  {
    "path": "src/test/groovy/sdkman/stubs/UnameStub.groovy",
    "chars": 681,
    "preview": "package sdkman.stubs\n\nclass UnameStub {\n\n\tprivate File file\n\tdef kernel = \"Linux\"\n\tdef machine = \"x86_64\"\n\n\tstatic Uname"
  },
  {
    "path": "src/test/groovy/sdkman/stubs/WebServiceStub.groovy",
    "chars": 2088,
    "preview": "package sdkman.stubs\n\nimport static com.github.tomakehurst.wiremock.client.WireMock.*\n\nclass WebServiceStub {\n\n\tstatic p"
  },
  {
    "path": "src/test/groovy/sdkman/support/BashEnvSpecification.groovy",
    "chars": 218,
    "preview": "package sdkman.support\n\nimport sdkman.env.BashEnv\nimport spock.lang.Specification\n\nabstract class BashEnvSpecification e"
  },
  {
    "path": "src/test/groovy/sdkman/support/FilesystemUtils.groovy",
    "chars": 1691,
    "preview": "package sdkman.support\n\nimport java.nio.file.FileSystems\nimport java.nio.file.Files\nimport java.nio.file.Path\nimport jav"
  },
  {
    "path": "src/test/groovy/sdkman/support/SdkmanEnvSpecification.groovy",
    "chars": 1039,
    "preview": "package sdkman.support\n\nimport sdkman.env.SdkmanBashEnvBuilder\nimport sdkman.stubs.CurlStub\nimport sdkman.stubs.UnameStu"
  },
  {
    "path": "src/test/groovy/sdkman/support/UnixUtils.groovy",
    "chars": 546,
    "preview": "package sdkman.support\n\nclass UnixUtils {\n\n\tprivate static platforms = [\n\t\t\t\"Linux\"   : [\n\t\t\t\t\t\"x86_64\": \"linuxx64\",\n\t\t\t"
  },
  {
    "path": "src/test/groovy/sdkman/support/WireMockServerProvider.groovy",
    "chars": 669,
    "preview": "package sdkman.support\n\nimport com.github.tomakehurst.wiremock.WireMockServer\nimport com.github.tomakehurst.wiremock.cli"
  },
  {
    "path": "src/test/jmeter/SDKman.jmx",
    "chars": 27313,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<jmeterTestPlan version=\"1.2\" properties=\"2.5\" jmeter=\"2.10 r1533061\">\n  <hashTre"
  },
  {
    "path": "src/test/jmeter/candidates.csv",
    "chars": 112,
    "preview": "gaiden,0.3\ngradle,1.8\ngrails,2.3.1\ngriffon,1.4.0\ngroovy,2.1.8\ngroovyserv,0.13\nlazybones,0.4\nspringboot,0.5.0.M5\n"
  },
  {
    "path": "src/test/resources/__files/hooks/post_hook_java_8.0.101_linuxx64.sh",
    "chars": 254,
    "preview": "#!/usr/bin/env bash\n# failed download with non-zero exit code\nfunction __sdkman_post_installation_hook() {\n\techo \"POST: "
  },
  {
    "path": "src/test/resources/__files/hooks/post_hook_java_8.0.111_linuxx64.sh",
    "chars": 308,
    "preview": "#!/usr/bin/env bash\n# convert tar.gz to zip\nfunction __sdkman_post_installation_hook() {\n\techo \"POST: converting $binary"
  },
  {
    "path": "src/test/resources/__files/hooks/post_hook_java_8.0.111_universal.sh",
    "chars": 195,
    "preview": "#!/usr/bin/env bash\n# passthrough used for zip binaries\nfunction __sdkman_post_installation_hook() {\n\techo \"POST: passth"
  },
  {
    "path": "src/test/resources/features/checksum_verification.feature",
    "chars": 4374,
    "preview": "Feature: Verify checksums\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\t\tAnd I have "
  },
  {
    "path": "src/test/resources/features/command_line_interop.feature",
    "chars": 696,
    "preview": "Feature: Command Line Interop\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\t\tAnd the"
  },
  {
    "path": "src/test/resources/features/current_candidate.feature",
    "chars": 1565,
    "preview": "Feature: Current Candidate\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\n\tScenario: "
  },
  {
    "path": "src/test/resources/features/default_version.feature",
    "chars": 1592,
    "preview": "Feature: Default Version\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\n\tScenario: De"
  },
  {
    "path": "src/test/resources/features/flush.feature",
    "chars": 1295,
    "preview": "Feature: Flush\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\t\tAnd the system is boot"
  },
  {
    "path": "src/test/resources/features/home.feature",
    "chars": 1974,
    "preview": "Feature: Print home path\n\n* Print home directory\n* Printing the home directory does not require reaching out to the inte"
  },
  {
    "path": "src/test/resources/features/hooks.feature",
    "chars": 1164,
    "preview": "Feature: Hooks\n\n\tWe can safely remove this feature when `.tar.gz` and `.zip` are supported directly by the backend.\n\t\n\tB"
  },
  {
    "path": "src/test/resources/features/install_candidate.feature",
    "chars": 4611,
    "preview": "Feature: Install Candidate\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\n\tScenario: "
  },
  {
    "path": "src/test/resources/features/install_sdkman.feature",
    "chars": 3328,
    "preview": "@manual\nFeature: Install SDKMAN\n\n\tPlatform defaults as follows:\n\t* Ubuntu:  .profile, .bashrc\n\t* Fedora:  .bash_profile,"
  },
  {
    "path": "src/test/resources/features/java_installation.feature",
    "chars": 3504,
    "preview": "Feature: Java Multi Platform Binary Distribution\n\n\tThree versions of Java are used to test various installation scenario"
  },
  {
    "path": "src/test/resources/features/list_candidate_versions.feature",
    "chars": 2507,
    "preview": "Feature: List Candidate Versions\n\n\tA dummy template to be served back that has the following information:\n\t* Candidate: "
  },
  {
    "path": "src/test/resources/features/list_candidates.feature",
    "chars": 292,
    "preview": "Feature: List Candidates\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\n\tScenario: A "
  },
  {
    "path": "src/test/resources/features/local_developement_versions.feature",
    "chars": 5352,
    "preview": "Feature: Local Development Versions\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\n\tS"
  },
  {
    "path": "src/test/resources/features/mnemonics.feature",
    "chars": 3881,
    "preview": "Feature: Mnemonics\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\n\tScenario: Shortcut"
  },
  {
    "path": "src/test/resources/features/offline_mode.feature",
    "chars": 4588,
    "preview": "Feature: Offline Mode\n\n\t# offline modes\n\n\tScenario: Enter an invalid offline mode\n\t\tGiven offline mode is disabled with "
  },
  {
    "path": "src/test/resources/features/path_initialisation.feature",
    "chars": 1507,
    "preview": "Feature: Path Initialisation\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\n\tScenario"
  },
  {
    "path": "src/test/resources/features/per_project_configuration.feature",
    "chars": 3110,
    "preview": "Feature: Per-project configuration\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\n\tSc"
  },
  {
    "path": "src/test/resources/features/self_update.feature",
    "chars": 1357,
    "preview": "Feature: Self Update\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd the sdkman scripts version is \"5.0.0\"\n\t\tAnd t"
  },
  {
    "path": "src/test/resources/features/service_unavailable.feature",
    "chars": 4775,
    "preview": "Feature: Service Unavailable\n\n\tBackground:\n\t\tGiven the internet is not reachable\n\t\tAnd an initialised environment\n\n\t# li"
  },
  {
    "path": "src/test/resources/features/uninstall_candidate.feature",
    "chars": 1688,
    "preview": "Feature: Uninstall Candidate\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\n\tScenario"
  },
  {
    "path": "src/test/resources/features/update.feature",
    "chars": 1672,
    "preview": "Feature: Update\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd the following candidates are available for install"
  },
  {
    "path": "src/test/resources/features/upgrade_candidate.feature",
    "chars": 6430,
    "preview": "Feature: Upgrade Candidate\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd the candidates cache is initialised wit"
  },
  {
    "path": "src/test/resources/features/use_version.feature",
    "chars": 2165,
    "preview": "Feature: Use Version\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\n\tScenario: Use wi"
  },
  {
    "path": "src/test/resources/features/version.feature",
    "chars": 265,
    "preview": "Feature: Version\n\n\tScenario: Show the current version of sdkman\n\t\tGiven the internet is reachable\n\t\tAnd the sdkman scrip"
  }
]

// ... and 1 more files (download for full content)

About this extraction

This page contains the full source code of the sdkman/sdkman-cli GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 117 files (268.7 KB), approximately 76.5k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!