[
  {
    "path": ".editorconfig",
    "content": "[*]\ncharset = utf-8\nend_of_line = lf\nindent_size = 4\nindent_style = tab\ninsert_final_newline = false\nmax_line_length = 120\ntab_width = 4\ntrim_trailing_whitespace = false\nij_continuation_indent_size = 8\nij_formatter_off_tag = @formatter:off\nij_formatter_on_tag = @formatter:on\nij_formatter_tags_enabled = false\nij_smart_tabs = false\nij_wrap_on_typing = false\n\n[{*.yaml,*.yml}]\nindent_size = 2\n\n[{*.bash,*.sh,*.zsh}]\nij_shell_binary_ops_start_line = false\nij_shell_keep_column_alignment_padding = false\nij_shell_minify_program = false\nij_shell_redirect_followed_by_space = true\nij_shell_switch_cases_indented = false\n\n[*.feature]\nij_gherkin_keep_indents_on_empty_lines = false\n\n[{*.gant,*.gradle,*.groovy,*.gy}]\nij_groovy_align_group_field_declarations = false\nij_groovy_align_multiline_array_initializer_expression = false\nij_groovy_align_multiline_assignment = false\nij_groovy_align_multiline_binary_operation = false\nij_groovy_align_multiline_chained_methods = false\nij_groovy_align_multiline_extends_list = false\nij_groovy_align_multiline_for = true\nij_groovy_align_multiline_list_or_map = true\nij_groovy_align_multiline_method_parentheses = false\nij_groovy_align_multiline_parameters = true\nij_groovy_align_multiline_parameters_in_calls = false\nij_groovy_align_multiline_resources = true\nij_groovy_align_multiline_ternary_operation = false\nij_groovy_align_multiline_throws_list = false\nij_groovy_align_named_args_in_map = true\nij_groovy_align_throws_keyword = false\nij_groovy_array_initializer_new_line_after_left_brace = false\nij_groovy_array_initializer_right_brace_on_new_line = false\nij_groovy_array_initializer_wrap = off\nij_groovy_assert_statement_wrap = off\nij_groovy_assignment_wrap = off\nij_groovy_binary_operation_wrap = off\nij_groovy_blank_lines_after_class_header = 0\nij_groovy_blank_lines_after_imports = 1\nij_groovy_blank_lines_after_package = 1\nij_groovy_blank_lines_around_class = 1\nij_groovy_blank_lines_around_field = 0\nij_groovy_blank_lines_around_field_in_interface = 0\nij_groovy_blank_lines_around_method = 1\nij_groovy_blank_lines_around_method_in_interface = 1\nij_groovy_blank_lines_before_imports = 1\nij_groovy_blank_lines_before_method_body = 0\nij_groovy_blank_lines_before_package = 0\nij_groovy_block_brace_style = end_of_line\nij_groovy_block_comment_at_first_column = true\nij_groovy_call_parameters_new_line_after_left_paren = false\nij_groovy_call_parameters_right_paren_on_new_line = false\nij_groovy_call_parameters_wrap = off\nij_groovy_catch_on_new_line = false\nij_groovy_class_annotation_wrap = split_into_lines\nij_groovy_class_brace_style = end_of_line\nij_groovy_class_count_to_use_import_on_demand = 5\nij_groovy_do_while_brace_force = never\nij_groovy_else_on_new_line = false\nij_groovy_enum_constants_wrap = off\nij_groovy_extends_keyword_wrap = off\nij_groovy_extends_list_wrap = off\nij_groovy_field_annotation_wrap = split_into_lines\nij_groovy_finally_on_new_line = false\nij_groovy_for_brace_force = never\nij_groovy_for_statement_new_line_after_left_paren = false\nij_groovy_for_statement_right_paren_on_new_line = false\nij_groovy_for_statement_wrap = off\nij_groovy_if_brace_force = never\nij_groovy_import_annotation_wrap = 2\nij_groovy_indent_case_from_switch = true\nij_groovy_indent_label_blocks = true\nij_groovy_insert_inner_class_imports = false\nij_groovy_keep_blank_lines_before_right_brace = 2\nij_groovy_keep_blank_lines_in_code = 2\nij_groovy_keep_blank_lines_in_declarations = 2\nij_groovy_keep_control_statement_in_one_line = true\nij_groovy_keep_first_column_comment = true\nij_groovy_keep_indents_on_empty_lines = false\nij_groovy_keep_line_breaks = true\nij_groovy_keep_multiple_expressions_in_one_line = false\nij_groovy_keep_simple_blocks_in_one_line = false\nij_groovy_keep_simple_classes_in_one_line = true\nij_groovy_keep_simple_lambdas_in_one_line = true\nij_groovy_keep_simple_methods_in_one_line = true\nij_groovy_label_indent_absolute = false\nij_groovy_label_indent_size = 0\nij_groovy_lambda_brace_style = end_of_line\nij_groovy_layout_static_imports_separately = true\nij_groovy_line_comment_add_space = false\nij_groovy_line_comment_at_first_column = true\nij_groovy_method_annotation_wrap = split_into_lines\nij_groovy_method_brace_style = end_of_line\nij_groovy_method_call_chain_wrap = off\nij_groovy_method_parameters_new_line_after_left_paren = false\nij_groovy_method_parameters_right_paren_on_new_line = false\nij_groovy_method_parameters_wrap = off\nij_groovy_modifier_list_wrap = false\nij_groovy_names_count_to_use_import_on_demand = 3\nij_groovy_parameter_annotation_wrap = off\nij_groovy_parentheses_expression_new_line_after_left_paren = false\nij_groovy_parentheses_expression_right_paren_on_new_line = false\nij_groovy_prefer_parameters_wrap = false\nij_groovy_resource_list_new_line_after_left_paren = false\nij_groovy_resource_list_right_paren_on_new_line = false\nij_groovy_resource_list_wrap = off\nij_groovy_space_after_assert_separator = true\nij_groovy_space_after_colon = true\nij_groovy_space_after_comma = true\nij_groovy_space_after_comma_in_type_arguments = true\nij_groovy_space_after_for_semicolon = true\nij_groovy_space_after_quest = true\nij_groovy_space_after_type_cast = true\nij_groovy_space_before_annotation_parameter_list = false\nij_groovy_space_before_array_initializer_left_brace = false\nij_groovy_space_before_assert_separator = false\nij_groovy_space_before_catch_keyword = true\nij_groovy_space_before_catch_left_brace = true\nij_groovy_space_before_catch_parentheses = true\nij_groovy_space_before_class_left_brace = true\nij_groovy_space_before_closure_left_brace = true\nij_groovy_space_before_colon = true\nij_groovy_space_before_comma = false\nij_groovy_space_before_do_left_brace = true\nij_groovy_space_before_else_keyword = true\nij_groovy_space_before_else_left_brace = true\nij_groovy_space_before_finally_keyword = true\nij_groovy_space_before_finally_left_brace = true\nij_groovy_space_before_for_left_brace = true\nij_groovy_space_before_for_parentheses = true\nij_groovy_space_before_for_semicolon = false\nij_groovy_space_before_if_left_brace = true\nij_groovy_space_before_if_parentheses = true\nij_groovy_space_before_method_call_parentheses = false\nij_groovy_space_before_method_left_brace = true\nij_groovy_space_before_method_parentheses = false\nij_groovy_space_before_quest = true\nij_groovy_space_before_switch_left_brace = true\nij_groovy_space_before_switch_parentheses = true\nij_groovy_space_before_synchronized_left_brace = true\nij_groovy_space_before_synchronized_parentheses = true\nij_groovy_space_before_try_left_brace = true\nij_groovy_space_before_try_parentheses = true\nij_groovy_space_before_while_keyword = true\nij_groovy_space_before_while_left_brace = true\nij_groovy_space_before_while_parentheses = true\nij_groovy_space_in_named_argument = true\nij_groovy_space_in_named_argument_before_colon = false\nij_groovy_space_within_empty_array_initializer_braces = false\nij_groovy_space_within_empty_method_call_parentheses = false\nij_groovy_spaces_around_additive_operators = true\nij_groovy_spaces_around_assignment_operators = true\nij_groovy_spaces_around_bitwise_operators = true\nij_groovy_spaces_around_equality_operators = true\nij_groovy_spaces_around_lambda_arrow = true\nij_groovy_spaces_around_logical_operators = true\nij_groovy_spaces_around_multiplicative_operators = true\nij_groovy_spaces_around_regex_operators = true\nij_groovy_spaces_around_relational_operators = true\nij_groovy_spaces_around_shift_operators = true\nij_groovy_spaces_within_annotation_parentheses = false\nij_groovy_spaces_within_array_initializer_braces = false\nij_groovy_spaces_within_braces = true\nij_groovy_spaces_within_brackets = false\nij_groovy_spaces_within_cast_parentheses = false\nij_groovy_spaces_within_catch_parentheses = false\nij_groovy_spaces_within_for_parentheses = false\nij_groovy_spaces_within_gstring_injection_braces = false\nij_groovy_spaces_within_if_parentheses = false\nij_groovy_spaces_within_list_or_map = false\nij_groovy_spaces_within_method_call_parentheses = false\nij_groovy_spaces_within_method_parentheses = false\nij_groovy_spaces_within_parentheses = false\nij_groovy_spaces_within_switch_parentheses = false\nij_groovy_spaces_within_synchronized_parentheses = false\nij_groovy_spaces_within_try_parentheses = false\nij_groovy_spaces_within_tuple_expression = false\nij_groovy_spaces_within_while_parentheses = false\nij_groovy_special_else_if_treatment = true\nij_groovy_ternary_operation_wrap = off\nij_groovy_throws_keyword_wrap = off\nij_groovy_throws_list_wrap = off\nij_groovy_use_flying_geese_braces = false\nij_groovy_use_fq_class_names = false\nij_groovy_use_fq_class_names_in_javadoc = true\nij_groovy_use_relative_indents = false\nij_groovy_use_single_class_imports = true\nij_groovy_variable_annotation_wrap = off\nij_groovy_while_brace_force = never\nij_groovy_while_on_new_line = false\nij_groovy_wrap_long_lines = false\n"
  },
  {
    "path": ".github/CODEOWNERS",
    "content": "* @sdkman/sdkman-cli\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "open_collective: sdkman\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: You found a bug in SDKMAN!\ntitle: \"Bug: [A concise title]\"\nlabels: 'bug'\nassignees: ''\n\n---\n<!-- DO NOT DELETE THIS TEMPLATE AND PLEASE READ IT WITH CARE! -->\n<!-- 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. -->\n\n**Bug report**\n<!-- A clear and concise description of the bug you encountered  -->\n\n**To reproduce**\n<!-- Steps to reproduce the behaviour or verify the issue -->\n\n**System info**\n<!-- Please add relevant information about your system:\n- OS (e.g. Windows, Linux, Mac, Cygwin, WSL, etc.) and version\n- Shell and version (e.g. `bash --version`/`zsh --version`)\n- The output of `sdk version`\n-->\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: SDKMAN! usage documentation\n    url: https://sdkman.io/usage\n    about: Find documentation about the available commands here.\n  - name: SDKMAN! Discord server\n    url: https://discord.gg/y9mVJYVyu4\n    about: Please ask and answer questions about the usage of SDKMAN! here.\n  - name: SDKMAN! on Stack Overflow\n    url: https://stackoverflow.com/questions/tagged/sdkman\n    about: You might find help to common issues here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for SDKMAN!\ntitle: 'Feature: [A concise title]'\nlabels: 'enhancement'\nassignees: ''\n---\n\n<!-- Thank you for suggesting a new feature. Please discuss your idea on our #discussions Discord channel before requesting a new feature. -->\n\n- [ ] I have read the [CONTRIBUTING guidelines](CONTRIBUTING.md)\n- [ ] I've had a conversation on our community [Discord](https://discord.gg/y9mVJYVyu4) server.\n\n**Feature request**\n<!-- 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? -->\n\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/support_request.md",
    "content": "---\nname: Support request or question\nabout: You need help using SDKMAN!\ntitle: \"Question: [Short summary]\"\nlabels: 'support'\nassignees: ''\n\n---\n<!-- 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.\n\nPlease consider the following things. Just so you know, ignoring any of these might lead to the issue being closed abruptly. -->\n\n- [ ] I have checked [the documentation](https://sdkman.io/usage)\n- [ ] 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\n- [ ] I have brought up a conversation in the community [Discord](https://discord.gg/y9mVJYVyu4) server\n\n**Question**\n<!-- A clear and concise description of the issue you are encountering -->\n\n**System info**\n<!-- Please add relevant information about your system:\n- OS (e.g. Windows, Linux, Mac, Cygwin, WSL, etc.) and version\n- Shell and version (e.g. `bash --version`/`zsh --version`)\n- The output of `sdk version`\n-->\n"
  },
  {
    "path": ".github/auto_assign.yml",
    "content": "---\naddReviewers: true\naddAssignees: author\n\nreviewers:\n  - marc0der\n  - helpermethod\n\nnumberOfReviewers: 0\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: github-actions\n    directory: /\n    schedule:\n      interval: daily\n"
  },
  {
    "path": ".github/pull_request_template.md",
    "content": "<!-- To raise a new Pull Request, the following prerequisites need to be met. Please tick before proceeding: -->\n\n- [ ] a GitHub Issue was opened for this feature / bug.\n- [ ] test coverage was added (Cucumber or Spock as appropriate).\n\nFixes #XXX\n"
  },
  {
    "path": ".github/workflows/beta.yml",
    "content": "name: Beta Release\non:\n  push:\n    branches:\n      - master\n    paths-ignore:\n      - .github/workflows/*\n\njobs:\n  pre-release:\n    name: \"Beta Release\"\n    environment: production\n    runs-on: \"ubuntu-latest\"\n    services:\n      mongodb:\n        image: mongo:3.2\n        ports:\n          - 27017:27017\n    steps:\n      - uses: actions/checkout@v3\n      - uses: actions/setup-java@v4\n        with:\n          distribution: 'temurin'\n          java-version: '11'\n          cache: 'gradle'\n      - name: Run tests\n        run: ./gradlew clean test --info\n      - name: Set short git hash\n        id: var\n        run: echo \"::set-output name=sha_short::$(git rev-parse --short HEAD)\"\n      - name: Build artifacts\n        run: ./gradlew -Penv=beta -Phash=${{ steps.var.outputs.sha_short }} clean assemble\n      - name: Release\n        if: github.repository == 'sdkman/sdkman-cli'\n        uses: \"marvinpinto/action-automatic-releases@latest\"\n        with:\n          repo_token: \"${{ secrets.GITHUB_TOKEN }}\"\n          automatic_release_tag: \"latest\"\n          prerelease: true\n          title: \"Beta release\"\n          files: |\n            build/distributions/sdkman-cli-*.zip\n      - name: Update MongoDB\n        if: github.repository == 'sdkman/sdkman-cli'\n        env:\n          MONGO_URL: ${{ secrets.MONGO_URL }}\n          MONGO_USERNAME: ${{ secrets.MONGO_USERNAME }}\n          MONGO_PASSWORD: ${{ secrets.MONGO_PASSWORD }}\n          SHORT_HASH: ${{ steps.var.outputs.sha_short }}\n        run: bin/release-binary.sh \"$MONGO_URL\" \"$MONGO_USERNAME\" \"$MONGO_PASSWORD\" \"$SHORT_HASH\" \"latest\"\n\n"
  },
  {
    "path": ".github/workflows/chloe-triage.yml",
    "content": "name: Notify Chloé (Issue Triage)\n\non:\n  issues:\n    types: [opened]\n  issue_comment:\n    types: [created]\n  pull_request:\n    types: [opened]\n\njobs:\n  notify:\n    uses: sdkman/.github/.github/workflows/reusable-triage.yml@main\n    secrets: inherit\n"
  },
  {
    "path": ".github/workflows/notify-on-failure.yml",
    "content": "name: Notify on Failure\n\non:\n  workflow_run:\n    workflows: [\"*\"]\n    branches: [master, main]\n    types: [completed]\n\njobs:\n  notify:\n    uses: sdkman/.github/.github/workflows/reusable-notify-on-failure.yml@main\n    secrets: inherit\n"
  },
  {
    "path": ".github/workflows/pr.yml",
    "content": "name: Pull Requests\n#run only on PR, not fork. Pull request that way the PR submitter will not have access to the repo credentials \non: pull_request\njobs:\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout source code\n        uses: actions/checkout@v3\n        with:\n            ref: ${{ github.event.pull_request.head.sha }}\n      - uses: actions/setup-java@v4\n        with:\n          distribution: 'temurin'\n          java-version: '11'\n          cache: 'gradle'\n      - name: Run with Gradle\n        run: ./gradlew clean test --info\n      - uses: kentaro-m/auto-assign-action@v1.2.5\n        with:\n          configuration-path: \".github/auto_assign.yml\"\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Release\non:\n  workflow_dispatch:\n    inputs:\n      version:\n        description: \"Release version\"\n        required: true\njobs:\n  build:\n    name: \"Stable Release\"\n    if: github.repository == 'sdkman/sdkman-cli'\n    runs-on: ubuntu-latest\n    environment: production\n    env:\n      JRELEASER_GITHUB_TOKEN: ${{ github.token }}\n      JRELEASER_TWITTER_CONSUMER_KEY: ${{ secrets.TWITTER_CONSUMER_API_KEY }}\n      JRELEASER_TWITTER_CONSUMER_SECRET: ${{ secrets.TWITTER_CONSUMER_API_SECRET }}\n      JRELEASER_TWITTER_ACCESS_TOKEN: ${{ secrets.TWITTER_ACCESS_TOKEN }}\n      JRELEASER_TWITTER_ACCESS_TOKEN_SECRET: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }}\n      JRELEASER_MASTODON_ACCESS_TOKEN: ${{ secrets.MASTODON_ACCESS_TOKEN}}\n    services:\n      mongodb:\n        image: mongo:3.2\n        ports:\n          - 27017:27017\n    steps:\n    - uses: actions/checkout@v3\n      with:\n        fetch-depth: 0\n    - uses: actions/setup-java@v4\n      with:\n        distribution: 'temurin'\n        java-version: '11'\n        cache: 'gradle'\n    - name: Run tests\n      run: ./gradlew clean test --info\n    - name: Build artifacts\n      run: ./gradlew -Penv=stable -Prelease=${{ github.event.inputs.version }} clean assemble\n    - name: Release\n      run: ./gradlew -Penv=stable -Prelease=${{ github.event.inputs.version }} jreleaserFullRelease --exclude-announcer=twitter\n    - name: Update MongoDB\n      env:\n        MONGO_URL: ${{ secrets.MONGO_URL }}\n        MONGO_USERNAME: ${{ secrets.MONGO_USERNAME }}\n        MONGO_PASSWORD: ${{ secrets.MONGO_PASSWORD }}\n        RELEASE_TAG: ${{ github.event.inputs.version }}\n      run: bin/release-binary.sh \"$MONGO_URL\" \"$MONGO_USERNAME\" \"$MONGO_PASSWORD\" \"$RELEASE_TAG\" \"stable\"\n    - name: Tweet about it\n      run: ./gradlew -Penv=stable -Prelease=${{ github.event.inputs.version }} jreleaserAnnounce\n\n"
  },
  {
    "path": ".gitignore",
    "content": "*~\n.#*\n.classpath\n.gradle/\n.gradletasknamecache\n.idea/\n.pid.lock\n.project\n.settings/\n.vscode/\n\\#*\nbuild/\nclasses\nsdkman-cli.iml\nsdkman.iml\nsdkman.ipr\nsdkman.iws\nmongo.json\nout/\nsdkman-cli.iml\ntarget/\nbin/test"
  },
  {
    "path": ".sdkmanrc",
    "content": "# 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",
    "content": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Project Overview\n\nSDKMAN! 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.\n\n**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.\n\n## Development Environment\n\n- Requires JDK 11 (specified in `.sdkmanrc`)\n- Uses Gradle 8.0.1 for build management\n- Run `sdk env install` to install the correct JDK version\n- Run `sdk env` to switch to the appropriate SDK versions\n\n## Build and Test Commands\n\n### Core Commands\n- `./gradlew test` - Run all Cucumber BDD tests\n- `./gradlew clean` - Clean build artifacts\n- `./gradlew build` - Build the project\n\n### Running Specific Tests\n- Tests can be run with tags: use `@manual` and `@review` tags to exclude certain tests\n- Individual feature files can be run by specifying the feature file path\n- Environment can be set via `env` property (local/beta/stable) - defaults to local\n\n### Development Setup\nBefore starting development, ensure the correct Java version:\n```bash\nsdk env install  # Install JDK from .sdkmanrc\nsdk env          # Switch to correct SDK versions\n```\n\n## Architecture\n\n### Code Structure\n- `src/main/bash/` - Core bash scripts implementing SDKMAN commands\n  - `sdkman-main.sh` - Main entry point and command routing\n  - `sdkman-init.sh` - Environment initialization and platform detection\n  - Individual command scripts (e.g., `sdkman-install.sh`, `sdkman-list.sh`)\n- `src/test/groovy/` - Groovy test code using Spock framework\n- `src/test/resources/features/` - Cucumber feature files (BDD tests)\n\n### Core Components\n- **Command Router**: `sdkman-main.sh` handles command aliases and routing\n- **Environment Setup**: `sdkman-init.sh` manages platform detection and configuration\n- **Modular Commands**: Each SDKMAN command is implemented in its own bash script\n- **API Integration**: Commands interact with SDKMAN API for candidate information\n\n### Testing Strategy\n- **Cucumber BDD Tests**: Primary testing approach using Gherkin feature files\n- **Spock Unit Tests**: Some Groovy unit tests for specific components\n- Tests cover both happy path and edge cases\n- Mock external dependencies using WireMock\n- Test environment uses bash process execution\n\n### Configuration\n- `.sdkmanrc` file specifies required Java version (11.0.17-tem)\n- Environment-specific API endpoints configured in `build.gradle`:\n  - **local**: `http://localhost:8080/2` (for development)\n  - **beta**: `https://beta.sdkman.io/2`\n  - **stable**: `https://api.sdkman.io/2` (production)\n- Pass environment via `-Penv=<environment>` to gradle commands\n\n## Key Files\n- `build.gradle` - Main build configuration with test dependencies\n- `src/main/bash/sdkman-main.sh` - Command entry point and routing\n- `src/test/groovy/sdkman/cucumber/RunCukeTests.groovy` - Cucumber test runner\n- `src/test/resources/features/` - BDD feature specifications\n\n## Testing Guidelines\n- All features should have Cucumber tests covering happy and unhappy paths\n- Tests should be written before implementation (TDD approach)\n- Use `@manual` and `@review` tags to exclude certain tests from automation\n- Mock external API calls using WireMock stubs"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe 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.\n\nWe pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.\n\n## Our Standards\n\nExamples of behavior that contributes to a positive environment for our community include:\n\n* Demonstrating empathy and kindness toward other people\n* Being respectful of differing opinions, viewpoints, and experiences\n* Giving and gracefully accepting constructive feedback\n* Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience\n* Focusing on what is best not just for us as individuals, but for the overall community\n\nExamples of unacceptable behavior include:\n\n* The use of sexualized language or imagery, and sexual attention or advances of any kind\n* Trolling, insulting or derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or email address, without their explicit permission\n* Other conduct which could reasonably be considered inappropriate in a professional setting\n\n## Enforcement Responsibilities\n\nCommunity 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.\n\nCommunity 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.\n\n## Scope\n\nThis 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.\n\n## Enforcement\n\nInstances 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.\n\nAll community leaders are obligated to respect the privacy and security of the reporter of any incident.\n\n## Enforcement Guidelines\n\nCommunity leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:\n\n### 1. Correction\n\n**Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.\n\n**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.\n\n### 2. Warning\n\n**Community Impact**: A violation through a single incident or series of actions.\n\n**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.\n\n### 3. Temporary Ban\n\n**Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.\n\n**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.\n\n### 4. Permanent Ban\n\n**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.\n\n**Consequence**: A permanent ban from any sort of public interaction within the community.\n\n## Attribution\n\nThis 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].\n\nCommunity Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][Mozilla CoC].\n\nFor 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].\n\n[homepage]: https://www.contributor-covenant.org\n[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html\n[Mozilla CoC]: https://github.com/mozilla/diversity\n[FAQ]: https://www.contributor-covenant.org/faq\n[translations]: https://www.contributor-covenant.org/translations\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to SDKMAN! CLI\n\nThank you for your interest in contributing to SDKMAN! CLI. We greatly value the feedback and contributions of our users.\n\n## Important Notice\n\n**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.\n\n## Code of Conduct\n\nThis 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.\n\n## How to Contribute\n\n### Before You Start\n\nWe distinguish between:\n- **Bug Reports**: Issues with existing functionality\n- **Support Requests**: Questions about usage\n\nWe 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.\n\n### Reporting Bugs\n\n1. **Search existing issues** to avoid duplicates\n2. **Use the issue template** when creating a new issue via our [GitHub Issue Tracker](https://github.com/sdkman/sdkman-cli/issues/new)\n3. **Provide detailed information** including:\n   - Steps to reproduce the issue\n   - Expected behavior\n   - Actual behavior\n   - Your environment (OS, shell, Java version)\n   - Relevant logs or error messages\n\n**Note:** Issues that don't follow the template may be closed.\n\n### Submitting Pull Requests\n\nPull requests are always welcome but must follow these guidelines:\n\n#### Prerequisites\n\n1. **Link to an issue**: Every PR must reference a valid GitHub issue\n2. **Discuss first**: Talk about your proposed fix on Discord or in the issue before starting work\n3. **Keep it small**: Small, focused PRs are strongly preferred over large changes\n4. **Include tests**: Each PR should include passing tests that prove its validity (where feasible)\n\n#### Development Setup\n\n1. **Install required tools**:\n   ```bash\n   sdk env install  # Install JDK 11 from .sdkmanrc\n   sdk env          # Switch to correct SDK versions\n   ```\n\n2. **Verify your setup**:\n   ```bash\n   ./gradlew test\n   ```\n\n#### Development Workflow\n\n1. **Fork the repository** and create a new branch from `master`\n2. **Make your changes** following our code standards\n3. **Write or update tests** to cover your changes\n4. **Run the test suite** to ensure everything passes:\n   ```bash\n   ./gradlew test\n   ```\n5. **Commit your changes** using clear, descriptive commit messages\n6. **Push to your fork** and submit a pull request\n\n#### Pull Request Guidelines\n\n- Fill in the PR template completely\n- Link back to the GitHub issue by replacing `#XXX` with the issue number\n- Ensure all tests pass\n- Keep commits focused and atomic\n- Write clear commit messages in imperative mood (e.g., \"fix user login bug\")\n- Be responsive to feedback during code review\n\n#### Testing Requirements\n\n- All bug fixes must include a test that would have caught the bug\n- Tests should follow the existing Cucumber BDD pattern\n- Tests should cover both happy path and edge cases\n- Run `./gradlew test` to verify all tests pass\n\n### Code Standards\n\n- Follow the existing code style in bash scripts\n- Use meaningful variable and function names\n- Add comments for complex logic\n- Avoid introducing new dependencies without discussion\n- Ensure backward compatibility\n\n### Getting Help\n\n- Join our [Discord server](https://discord.gg/y9mVJYVyu4) for questions\n- Check existing issues and discussions\n- Read the documentation in the repository\n\n## Project Structure\n\n- `src/main/bash/` - Core bash scripts implementing SDKMAN commands\n- `src/test/groovy/` - Groovy test code using Spock framework\n- `src/test/resources/features/` - Cucumber feature files (BDD tests)\n\n## Recognition\n\nContributors will be recognized in our release notes and commit history. We appreciate your efforts to make SDKMAN! better!\n\n## Developer Certificate of Origin\n\nBy 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.\n\n## Questions?\n\nIf you have questions about contributing, feel free to ask on [Discord](https://discord.gg/y9mVJYVyu4) or open a discussion on GitHub.\n"
  },
  {
    "path": "Dockerfile",
    "content": "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/src/app\nWORKDIR /usr/src/app\n\nENTRYPOINT [\"./gradlew\"]\n"
  },
  {
    "path": "LICENSE",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n"
  },
  {
    "path": "README.md",
    "content": "# SDKMAN! CLI\n### The Software Development Kit Manager Command Line Interface\n\n[![Backers on Open Collective](https://img.shields.io/opencollective/backers/sdkman)](#backers) \n[![Sponsors on Open Collective](https://img.shields.io/opencollective/sponsors/sdkman)](#sponsors)\n[![Discord](https://img.shields.io/discord/1245471991117512754)](https://discord.gg/y9mVJYVyu4)\n\nSDKMAN 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.\n\nSee documentation on the [SDKMAN! website](https://sdkman.io).\n\n## NOTICE\n\n**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\naccepted 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\nwrapper/launcher for the replacement Rust commands.**\n\n## Installation\n\nOpen your favourite terminal and enter the following:\n\n    $ curl -s https://get.sdkman.io | bash\n\nIf the environment needs tweaking for SDKMAN to be installed, the installer will prompt you accordingly and ask you to restart.\n\n## Running the Cucumber Features\n\nAll 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:\n\n    $ ./gradlew test\n\nTo perform development, you will need to have a JDK 11 installed which can be obtained by running the following after installing SDKMAN:\n\n    $ sdk env install\n\n## Hosting\n\nWe're proud to host our backend services on DigitalOcean as a sponsored partner.\n\n[![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)\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. \n<a href=\"https://github.com/sdkman/sdkman-cli/graphs/contributors\"><img src=\"https://opencollective.com/sdkman/contributors.svg?width=890&button=false\" /></a>\n\n## Backers\n\nThank you to all our backers! [[Become a backer](https://opencollective.com/sdkman#backer)]\n\n<a href=\"https://opencollective.com/sdkman#backers\" target=\"_blank\"><img src=\"https://opencollective.com/sdkman/backers.svg?width=890\"></a>\n\n\n## Sponsors\n\nSupport 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)]\n\n<a href=\"https://opencollective.com/sdkman/sponsor/0/website\" target=\"_blank\"><img src=\"https://opencollective.com/sdkman/sponsor/0/avatar.svg\"></a>\n<a href=\"https://opencollective.com/sdkman/sponsor/1/website\" target=\"_blank\"><img src=\"https://opencollective.com/sdkman/sponsor/1/avatar.svg\"></a>\n<a href=\"https://opencollective.com/sdkman/sponsor/2/website\" target=\"_blank\"><img src=\"https://opencollective.com/sdkman/sponsor/2/avatar.svg\"></a>\n<a href=\"https://opencollective.com/sdkman/sponsor/3/website\" target=\"_blank\"><img src=\"https://opencollective.com/sdkman/sponsor/3/avatar.svg\"></a>\n<a href=\"https://opencollective.com/sdkman/sponsor/4/website\" target=\"_blank\"><img src=\"https://opencollective.com/sdkman/sponsor/4/avatar.svg\"></a>\n<a href=\"https://opencollective.com/sdkman/sponsor/5/website\" target=\"_blank\"><img src=\"https://opencollective.com/sdkman/sponsor/5/avatar.svg\"></a>\n<a href=\"https://opencollective.com/sdkman/sponsor/6/website\" target=\"_blank\"><img src=\"https://opencollective.com/sdkman/sponsor/6/avatar.svg\"></a>\n<a href=\"https://opencollective.com/sdkman/sponsor/7/website\" target=\"_blank\"><img src=\"https://opencollective.com/sdkman/sponsor/7/avatar.svg\"></a>\n<a href=\"https://opencollective.com/sdkman/sponsor/8/website\" target=\"_blank\"><img src=\"https://opencollective.com/sdkman/sponsor/8/avatar.svg\"></a>\n<a href=\"https://opencollective.com/sdkman/sponsor/9/website\" target=\"_blank\"><img src=\"https://opencollective.com/sdkman/sponsor/9/avatar.svg\"></a>\n"
  },
  {
    "path": "bin/release-binary.sh",
    "content": "#!/usr/bin/env bash\n\nMONGO_URL=\"$1\"\nMONGO_USERNAME=\"$2\"\nMONGO_PASSWORD=\"$3\"\nPARAM_1=\"$4\"\nPARAM_2=\"$5\"\n\necho \"Mongo URL: $MONGO_URL\"\n\nif [[ -z \"$MONGO_USERNAME\" || -z \"$MONGO_PASSWORD\" ]]; then\n\techo \"No mongo credentials so doing nothing...\"\n\treturn 1\nfi\n\nif [[ \"$PARAM_2\" == 'stable' ]]; then\n\tFIELD=\"stableCliVersion\"\n\tVERSION=\"$PARAM_1\"\nelif [[ \"$PARAM_2\" == 'latest' ]]; then\n\tFIELD=\"betaCliVersion\"\n\tVERSION=\"latest+$PARAM_1\"\nelse\n\treturn 1\nfi\n\necho \"Release: $FIELD as $VERSION\"\n\ndocker run mongo:3.2 mongo \"${MONGO_URL}\" \\\n\t--username=\"${MONGO_USERNAME}\" \\\n\t--password=\"${MONGO_PASSWORD}\" \\\n\t--quiet \\\n\t--eval \"db.application.updateOne({}, {\\$set: { \\\"$FIELD\\\": \\\"$VERSION\\\"}});\"\n\n"
  },
  {
    "path": "build.gradle",
    "content": "plugins {\n\tid('groovy')\n\tid('org.jreleaser').version('1.4.0').apply(false)\n}\n\nString userHome = System.getProperty('user.home')\next.installBinDir = \"${userHome}/.sdkman/bin\"\next.installSrcDir = \"${userHome}/.sdkman/src\"\next.installContribDir = \"${userHome}/.sdkman/contrib\"\n\next.environment = hasProperty('env') ? env : 'local'\next.hash = hasProperty('hash') ? hash : 'hashme'\next.release = hasProperty('release') ? release : 'latest'\n\nif (\"$environment\" == 'stable') {\n\text.candidatesApi = 'https://api.sdkman.io/2'\n\text.brokerApi = 'https://broker.sdkman.io'\n} else if (\"$environment\" == 'beta') {\n\text.candidatesApi = 'https://beta.sdkman.io/2'\n\text.brokerApi = 'https://broker.sdkman.io'\n} else {\n\text.candidatesApi = 'http://localhost:8080/2'\n\text.brokerApi = 'https://localhost:8080'\n}\n\next.sdkmanVersion = ext.release == 'latest' ? \"latest+${ext.hash}\".toString() : ext.release\n\nprintln(\"Environment is set to: $environment\")\nprintln(\"Short git hash: $hash\")\nprintln(\"Release set to: $release\")\nprintln(\"Candidates API: $candidatesApi\")\nprintln(\"Broker API: $brokerApi\")\nprintln(\"Version: $sdkmanVersion\")\n\napply from: 'gradle/archive.gradle'\napply from: 'gradle/release.gradle'\n\nrepositories {\n\tmavenCentral()\n}\n\ndependencies {\n\ttestImplementation('com.github.tomakehurst:wiremock:2.27.2') {\n\t\texclude module: 'junit'\n\t}\n\ttestImplementation('io.cucumber:cucumber-groovy:4.7.1')\n\ttestImplementation('io.cucumber:cucumber-junit:4.7.4')\n\ttestImplementation('io.cucumber:gherkin:5.2.0')\n\ttestImplementation('junit:junit:4.13.2')\n\ttestImplementation('org.codehaus.groovy:groovy:2.4.21')\n\ttestImplementation('org.codehaus.groovy:groovy-json:2.4.21')\n\ttestImplementation('org.codehaus.groovy:groovy-templates:2.4.21')\n\ttestImplementation('org.slf4j:slf4j-api:1.7.32')\n\ttestImplementation('org.slf4j:slf4j-simple:1.7.32')\n\ttestImplementation('org.spockframework:spock-core:1.3-groovy-2.4') {\n\t\texclude module: 'groovy-all'\n\t}\n}\n"
  },
  {
    "path": "contrib/completion/bash/sdk",
    "content": "#!/usr/bin/bash\n\n_sdk() {\n\tlocal -r previous_word=${COMP_WORDS[COMP_CWORD - 1]}\n\tlocal -r current_word=${COMP_WORDS[COMP_CWORD]}\n\n\tif ((COMP_CWORD == 3)); then\n\t\tlocal -r before_previous_word=${COMP_WORDS[COMP_CWORD - 2]}\n\n\t\t__sdkman_complete_candidate_version \"$before_previous_word\" \"$previous_word\" \"$current_word\"\n\n\t\treturn\n\tfi\n\n\t__sdkman_complete_command \"$previous_word\" \"$current_word\"\n}\n\n__sdkman_complete_command() {\n\tlocal -r command=$1\n\tlocal -r current_word=$2\n\n\tlocal -a candidates\n\n\tcase $command in\n\t\tsdk)\n\t\t\tcandidates=(\"install\" \"uninstall\" \"list\" \"use\" \"config\" \"default\" \"home\" \"env\" \"current\" \"upgrade\" \"version\" \"help\" \"offline\" \"selfupdate\" \"update\" \"flush\")\n\t\t\t;;\n\t\tcurrent|c|default|d|home|h|uninstall|rm|upgrade|ug|use|u)\n\t\t\tlocal -r candidate_paths=(\"${SDKMAN_CANDIDATES_DIR}\"/*)\n\n\t\t\tfor candidate_path in \"${candidate_paths[@]}\"; do\n\t\t\t\tcandidates+=(\"${candidate_path##*/}\")\n\t\t\tdone\n\t\t\t;;\n\t\tinstall|i|list|ls)\n\t\t\tcandidates=${SDKMAN_CANDIDATES[@]}\n\t\t\t;;\n\t\tenv|e)\n\t\t\tcandidates=(\"init\" \"install\" \"clear\")\n\t\t\t;;\n\t\toffline)\n\t\t\tcandidates=(\"enable\" \"disable\")\n\t\t\t;;\n\t\tselfupdate)\n\t\t\tcandidates=(\"force\")\n\t\t\t;;\n\t\tflush)\n\t\t\tcandidates=(\"temp\" \"version\")\n\t\t\t;;\n\tesac\n\n\tCOMPREPLY=($(compgen -W \"${candidates[*]}\" -- \"$current_word\"))\n}\n\n__sdkman_complete_candidate_version() {\n\tlocal -r command=$1\n\tlocal -r candidate=$2\n\tlocal -r candidate_version=$3\n\n\tlocal -a candidates\n\n\tcase $command in\n\t\tdefault|d|home|h|uninstall|rm|use|u)\n\t\t\tlocal -r version_paths=(\"${SDKMAN_CANDIDATES_DIR}/${candidate}\"/*)\n\n\t\t\tfor version_path in \"${version_paths[@]}\"; do\n\t\t\t\t[[ $version_path = *current ]] && continue\n\n\t\t\t\tcandidates+=(\"${version_path##*/}\")\n\t\t\tdone\n\t\t\t;;\n\t\tinstall|i)\n\t\t\twhile IFS= read -r -d, version || [[ -n \"$version\" ]]; do\n\t\t\t\tcandidates+=(\"$version\")\n\t\t\tdone <<< \"$(curl --silent \"${SDKMAN_CANDIDATES_API}/candidates/$candidate/${SDKMAN_PLATFORM}/versions/all\")\"\n\t\t\t;;\n\tesac\n\n\tCOMPREPLY=($(compgen -W \"${candidates[*]}\" -- \"$candidate_version\"))\n}\n\ncomplete -o default -F _sdk sdk\n\n# Set 'sdkman_auto_complete' to 'true' in .sdkman/etc/config to enable completion\n\n"
  },
  {
    "path": "dco.txt",
    "content": "Developer Certificate of Origin\nVersion 1.1\n\nCopyright (C) 2004, 2006 The Linux Foundation and its contributors.\n1 Letterman Drive\nSuite D4700\nSan Francisco, CA, 94129\n\nEveryone is permitted to copy and distribute verbatim copies of this\nlicense document, but changing it is not allowed.\n\n\nDeveloper's Certificate of Origin 1.1\n\nBy making a contribution to this project, I certify that:\n\n(a) The contribution was created in whole or in part by me and I\n    have the right to submit it under the open source license\n    indicated in the file; or\n\n(b) The contribution is based upon previous work that, to the best\n    of my knowledge, is covered under an appropriate open source\n    license and I have the right under that license to submit that\n    work with modifications, whether created in whole or in part\n    by me, under the same open source license (unless I am\n    permitted to submit under a different license), as indicated\n    in the file; or\n\n(c) The contribution was provided directly to me by some other\n    person who certified (a), (b) or (c) and I have not modified\n    it.\n\n(d) I understand and agree that this project and the contribution\n    are public and that a record of the contribution (including all\n    personal information I submit with it, including my sign-off) is\n    maintained indefinitely and may be redistributed consistent with\n    this project or the open source license(s) involved.\n"
  },
  {
    "path": "gradle/archive.gradle",
    "content": "import org.apache.tools.ant.filters.ReplaceTokens\nimport org.gradle.api.tasks.testing.logging.TestExceptionFormat\n\ndef baseDir = \"$buildDir/stage/sdkman-${ext.sdkmanVersion}\"\n\ntask prepareBin(type: Copy) {\n\tfrom('src/main/bash')\n\tinto(\"$baseDir/bin\")\n\tinclude('**/sdkman-init.sh')\n\tfilter(\n\t\t\tReplaceTokens,\n\t\t\ttokens: [\n\t\t\t\t\tSDKMAN_CANDIDATES_API: candidatesApi,\n\t\t\t\t\tSDKMAN_BROKER_API: brokerApi\n\t\t\t]\n\t)\n}\n\ntask prepareScripts(type: Copy) {\n\tfrom('src/main/bash')\n\tinto(\"$baseDir/src\")\n\tinclude('**/*')\n\texclude('**/sdkman-init.sh')\n}\n\ntask prepareContrib(type: Copy) {\n\tfrom('contrib')\n\tinto(\"$baseDir/contrib\")\n}\n\ntasks.test.configure {\n\tdependsOn(prepareScripts)\n\ttestLogging.exceptionFormat = TestExceptionFormat.FULL\n}\n\ntask assembleArchive(type: Zip, dependsOn: [prepareBin, prepareScripts, prepareContrib]) {\n\tarchiveVersion = sdkmanVersion\n\tfrom('build/stage') {\n\t\tinclude('**/*')\n\t}\n}\n\ntasks.assemble.configure {\n\tdependsOn(assembleArchive)\n}\n\ntasks.test.configure {\n\tdependsOn(prepareBin, prepareScripts, prepareContrib)\n}\n"
  },
  {
    "path": "gradle/release.gradle",
    "content": "apply plugin: 'org.jreleaser'\n\njreleaser {\n\tproject {\n\t\tname = 'sdkman-cli'\n\t\tversion = sdkmanVersion\n\t\tdescription = 'Sdkman - The Software Development Kit Manager'\n\t\twebsite = 'https://sdkman.io'\n\t\tauthors = ['Marco Vermeulen']\n\t\tlicense = 'Apache-2.0'\n\t\textraProperties.put('inceptionYear', '2012')\n\t}\n\t\n\trelease {\n\t\tgithub {\n\t\t\toverwrite = true\n\t\t\ttagName = '{{projectVersion}}'\n\t\t\tchangelog {\n\t\t\t\tformatted = 'ALWAYS'\n\t\t\t\tformat = '- {{commitShortHash}} {{commitTitle}}'\n\t\t\t\tcontributors {\n\t\t\t\t\tformat = '- {{contributorName}}'\n\t\t\t\t}\n\t\t\t\tcontentTemplate = file('src/jreleaser/changelog.tpl')\n\t\t\t\thide {\n\t\t\t\t\tcontributors = ['GitHub']\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\tdistributions {\n\t\t'sdkman-cli' {\n\t\t\tdistributionType = 'BINARY'\n\t\t\tartifact {\n\t\t\t\tpath = \"build/distributions/{{distributionName}}-${sdkmanVersion}.zip\"\n\t\t\t\textraProperties.put(\"universal\", \"true\")\n\t\t\t}\n\t\t}\n\t}\n\t\n\tannounce {\n\t\ttwitter {\n\t\t\tactive = 'RELEASE'\n\t\t\tstatus = 'Released version {{tagName}} of SDKMAN! {{releaseNotesUrl}}'\n\t\t}\n\t\tmastodon {\n\t\t\tactive = 'RELEASE'\n\t\t\tstatus = 'Released version {{tagName}} of SDKMAN! {{releaseNotesUrl}}'\n\t\t\thost = 'https://fosstodon.org'\n\t\t}\n\t}\n}"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.0.1-bin.zip\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env sh\n\n#\n# Copyright 2015 the original author or authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn () {\n    echo \"$*\"\n}\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" -a \"$nonstop\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif [ \"$cygwin\" = \"true\" -o \"$msys\" = \"true\" ] ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=`expr $i + 1`\n    done\n    case $i in\n        0) set -- ;;\n        1) set -- \"$args0\" ;;\n        2) set -- \"$args0\" \"$args1\" ;;\n        3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Escape application args\nsave () {\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\n    echo \" \"\n}\nAPP_ARGS=`save \"$@\"`\n\n# Collect all arguments for the java command, following the shell quoting and substitution rules\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "settings.gradle",
    "content": "pluginManagement {\n\trepositories {\n\t\tmavenLocal()\n\t\tgradlePluginPortal()\n\t\tmavenCentral()\n\t}\n}\n\nrootProject.name = \"sdkman-cli\"\n"
  },
  {
    "path": "src/jreleaser/changelog.tpl",
    "content": "## Changelog\n\n{{changelogChanges}}\n{{changelogContributors}}\n"
  },
  {
    "path": "src/main/bash/sdkman-availability.sh",
    "content": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#       http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n\nfunction __sdkman_update_service_availability() {\n\tlocal healthcheck_status=$(__sdkman_determine_healthcheck_status)\n\t__sdkman_set_availability \"$healthcheck_status\"\n}\n\nfunction __sdkman_determine_healthcheck_status() {\n\tif [[ \"$SDKMAN_OFFLINE_MODE\" == \"true\" || \"$COMMAND\" == \"offline\" && \"$QUALIFIER\" == \"enable\" ]]; then\n\t\techo \"\"\n\telse\n\t\techo $(__sdkman_secure_curl_with_timeouts \"${SDKMAN_CANDIDATES_API}/healthcheck\")\n\tfi\n}\n\nfunction __sdkman_set_availability() {\n\tlocal healthcheck_status=\"$1\"\n\tlocal detect_html=\"$(echo \"$healthcheck_status\" | tr '[:upper:]' '[:lower:]' | grep 'html')\"\n\tif [[ -z \"$healthcheck_status\" ]]; then\n\t\tSDKMAN_AVAILABLE=\"false\"\n\t\t__sdkman_display_offline_warning \"$healthcheck_status\"\n\telif [[ -n \"$detect_html\" ]]; then\n\t\tSDKMAN_AVAILABLE=\"false\"\n\t\t__sdkman_display_proxy_warning\n\telse\n\t\tSDKMAN_AVAILABLE=\"true\"\n\tfi\n}\n\nfunction __sdkman_display_offline_warning() {\n\tlocal healthcheck_status=\"$1\"\n\tif [[ -z \"$healthcheck_status\" && \"$COMMAND\" != \"offline\" && \"$SDKMAN_OFFLINE_MODE\" != \"true\" ]]; then\n\t\t__sdkman_echo_red \"==== INTERNET NOT REACHABLE! ===================================================\"\n\t\t__sdkman_echo_red \"\"\n\t\t__sdkman_echo_red \" Some functionality is disabled or only partially available.\"\n\t\t__sdkman_echo_red \" If this persists, please enable the offline mode:\"\n\t\t__sdkman_echo_red \"\"\n\t\t__sdkman_echo_red \"   $ sdk offline\"\n\t\t__sdkman_echo_red \"\"\n\t\t__sdkman_echo_red \"================================================================================\"\n\t\techo \"\"\n\tfi\n}\n\nfunction __sdkman_display_proxy_warning() {\n\t__sdkman_echo_red \"==== PROXY DETECTED! ===========================================================\"\n\t__sdkman_echo_red \"Please ensure you have open internet access to continue.\"\n\t__sdkman_echo_red \"================================================================================\"\n\techo \"\"\n}\n"
  },
  {
    "path": "src/main/bash/sdkman-cache.sh",
    "content": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#       http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n\nfunction ___sdkman_check_candidates_cache() {\n\tlocal candidates_cache=\"$1\"\n\tif [[ -f \"$candidates_cache\" && -z \"$(< \"$candidates_cache\")\" ]]; then\n\t\t__sdkman_echo_red 'WARNING: Cache is corrupt. SDKMAN cannot be used until updated.'\n\t\techo ''\n\t\t__sdkman_echo_no_colour '  $ sdk update'\n\t\techo ''\n\t\treturn 1\n\telse\n\t\t__sdkman_echo_debug \"Using existing cache: $SDKMAN_CANDIDATES_CSV\"\n\t\treturn 0\n\tfi\n}\n"
  },
  {
    "path": "src/main/bash/sdkman-config.sh",
    "content": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#       http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n\nfunction __sdk_config() {\n\tlocal -r editor=(${EDITOR:=vi})\n\n\tif ! command -v \"${editor[@]}\" > /dev/null; then\n\t\t__sdkman_echo_red \"No default editor configured.\"\n\t\t__sdkman_echo_yellow \"Please set the default editor with the EDITOR environment variable.\"\n\n\t\treturn 1\n\tfi\n\n\t\"${editor[@]}\" \"${SDKMAN_DIR}/etc/config\"\n}"
  },
  {
    "path": "src/main/bash/sdkman-current.sh",
    "content": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#       http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n\nfunction __sdk_current() {\n\tlocal candidate=\"$1\"\n\n\techo \"\"\n\tif [ -n \"$candidate\" ]; then\n\t\t__sdkman_determine_current_version \"$candidate\"\n\t\tif [ -n \"$CURRENT\" ]; then\n\t\t\t__sdkman_echo_no_colour \"Using ${candidate} version ${CURRENT}\"\n\t\telse\n\t\t\t__sdkman_echo_red \"Not using any version of ${candidate}\"\n\t\tfi\n\telse\n\t\tlocal installed_count=0\n\t\tfor ((i = 0; i <= ${#SDKMAN_CANDIDATES[*]}; i++)); do\n\t\t\t# Eliminate empty entries due to incompatibility\n\t\t\tif [[ -n ${SDKMAN_CANDIDATES[${i}]} ]]; then\n\t\t\t\t__sdkman_determine_current_version \"${SDKMAN_CANDIDATES[${i}]}\"\n\t\t\t\tif [ -n \"$CURRENT\" ]; then\n\t\t\t\t\tif [ ${installed_count} -eq 0 ]; then\n\t\t\t\t\t\t__sdkman_echo_no_colour 'Using:'\n\t\t\t\t\t\techo \"\"\n\t\t\t\t\tfi\n\t\t\t\t\t__sdkman_echo_no_colour \"${SDKMAN_CANDIDATES[${i}]}: ${CURRENT}\"\n\t\t\t\t\t((installed_count += 1))\n\t\t\t\tfi\n\t\t\tfi\n\t\tdone\n\n\t\tif [ ${installed_count} -eq 0 ]; then\n\t\t\t__sdkman_echo_no_colour 'No candidates are in use'\n\t\tfi\n\tfi\n}\n\nfunction __sdkman_determine_current_version() {\n\tlocal candidate present\n\n\tcandidate=\"$1\"\n\tpresent=$(__sdkman_path_contains \"${SDKMAN_CANDIDATES_DIR}/${candidate}\")\n\tif [[ \"$present\" == 'true' ]]; then\n\t\tif [[ $PATH =~ ${SDKMAN_CANDIDATES_DIR}/${candidate}/([^/]+)/bin ]]; then\n\t\t\tif [[ \"$zsh_shell\" == \"true\" ]]; then\n\t\t\t\tCURRENT=${match[1]}\n\t\t\telse\n\t\t\t\tCURRENT=${BASH_REMATCH[1]}\n\t\t\tfi\n\t\tfi\n\n\t\tif [[ \"$CURRENT\" == \"current\" ]]; then\n\t\t\tCURRENT=$(readlink \"${SDKMAN_CANDIDATES_DIR}/${candidate}/current\" | sed \"s!${SDKMAN_CANDIDATES_DIR}/${candidate}/!!g\")\n\t\tfi\n\telse\n\t\tCURRENT=\"\"\n\tfi\n}\n"
  },
  {
    "path": "src/main/bash/sdkman-default.sh",
    "content": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#       http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n\nfunction __sdk_default() {\n\t__sdkman_deprecation_notice \"default\"\n\tlocal candidate version\n\n\tcandidate=\"$1\"\n\tversion=\"$2\"\n\n\t__sdkman_check_candidate_present \"$candidate\" || return 1\n\t__sdkman_determine_version \"$candidate\" \"$version\" || return 1\n\n\tif [ ! -d \"${SDKMAN_CANDIDATES_DIR}/${candidate}/${VERSION}\" ]; then\n\t\techo \"\"\n\t\t__sdkman_echo_red \"Stop! Candidate version is not installed.\"\n\t\techo \"\"\n\t\t__sdkman_echo_yellow \"Tip: Run the following to install this version\"\n\t\techo \"\"\n\t\t__sdkman_echo_yellow \"$ sdk install ${candidate} ${VERSION}\"\n\t\treturn 1\n\tfi\n\n\t__sdkman_link_candidate_version \"$candidate\" \"$VERSION\"\n\n\techo \"\"\n\t__sdkman_echo_green \"Default ${candidate} version set to ${VERSION}\"\n}\n"
  },
  {
    "path": "src/main/bash/sdkman-env-helpers.sh",
    "content": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#       http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n\nfunction __sdkman_check_candidate_present() {\n\tlocal candidate=\"$1\"\n\n\tif [ -z \"$candidate\" ]; then\n\t\techo \"\"\n\t\t__sdkman_echo_red \"No candidate provided.\"\n\t\t__sdk_help\n\t\treturn 1\n\tfi\n}\n\nfunction __sdkman_check_version_present() {\n\tlocal version=\"$1\"\n\n\tif [ -z \"$version\" ]; then\n\t\techo \"\"\n\t\t__sdkman_echo_red \"No candidate version provided.\"\n\t\t__sdk_help\n\t\treturn 1\n\tfi\n}\n\nfunction __sdkman_determine_version() {\n\tlocal candidate version folder\n\n\tcandidate=\"$1\"\n\tversion=\"$2\"\n\tfolder=\"$3\"\n\n\tif [[ \"$SDKMAN_AVAILABLE\" == \"false\" && -n \"$version\" && -d \"${SDKMAN_CANDIDATES_DIR}/${candidate}/${version}\" ]]; then\n\t\tVERSION=\"$version\"\n\n\telif [[ \"$SDKMAN_AVAILABLE\" == \"false\" && -z \"$version\" && -L \"${SDKMAN_CANDIDATES_DIR}/${candidate}/current\" ]]; then\n\t\tVERSION=$(readlink \"${SDKMAN_CANDIDATES_DIR}/${candidate}/current\" | sed \"s!${SDKMAN_CANDIDATES_DIR}/${candidate}/!!g\")\n\n\telif [[ \"$SDKMAN_AVAILABLE\" == \"false\" && -n \"$version\" ]]; then\n\t\t__sdkman_echo_red \"Stop! ${candidate} ${version} is not available while offline.\"\n\t\treturn 1\n\n\telif [[ \"$SDKMAN_AVAILABLE\" == \"false\" && -z \"$version\" ]]; then\n\t\t__sdkman_echo_red \"This command is not available while offline.\"\n\t\treturn 1\n\n\telse\n\t\tif [[ -z \"$version\" ]]; then\n\t\t\tversion=$(__sdkman_secure_curl \"${SDKMAN_CANDIDATES_API}/candidates/default/${candidate}\")\n\t\tfi\n\n\t\tlocal validation_url=\"${SDKMAN_CANDIDATES_API}/candidates/validate/${candidate}/${version}/${SDKMAN_PLATFORM}\"\n\t\tVERSION_VALID=$(__sdkman_secure_curl \"$validation_url\")\n\t\t__sdkman_echo_debug \"Validate $candidate $version for $SDKMAN_PLATFORM: $VERSION_VALID\"\n\t\t__sdkman_echo_debug \"Validation URL: $validation_url\"\n\n\t\tif [[ \"$VERSION_VALID\" == 'valid' || \"$VERSION_VALID\" == 'invalid' && -n \"$folder\" ]]; then\n\t\t\tVERSION=\"$version\"\n\n\t\telif [[ \"$VERSION_VALID\" == 'invalid' && -L \"${SDKMAN_CANDIDATES_DIR}/${candidate}/${version}\" ]]; then\n\t\t\tVERSION=\"$version\"\n\n\t\telif [[ \"$VERSION_VALID\" == 'invalid' && -d \"${SDKMAN_CANDIDATES_DIR}/${candidate}/${version}\" ]]; then\n\t\t\tVERSION=\"$version\"\n\n\t\telse\n\t\t\tif [[ -z \"$version\" ]]; then\n\t\t\t\tversion=\"\\b\"\n\t\t\tfi\n\n\t\t\techo \"\"\n\t\t\t__sdkman_echo_red \"Stop! $candidate $version is not available. Possible causes:\"\n\t\t\t__sdkman_echo_red \" * $version is an invalid version\"\n\t\t\t__sdkman_echo_red \" * $candidate binaries are incompatible with your platform\"\n\t\t\t__sdkman_echo_red \" * $candidate has not been released yet\"\n\t\t\techo \"\"\n\t\t\t__sdkman_echo_yellow \"Tip: see all available versions for your platform:\"\n\t\t\techo \"\"\n\t\t\t__sdkman_echo_yellow \"  $ sdk list $candidate\"\n\t\t\treturn 1\n\t\tfi\n\tfi\n}\n"
  },
  {
    "path": "src/main/bash/sdkman-env.sh",
    "content": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#       http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n\nfunction __sdk_env() {\n\tlocal -r sdkmanrc=\".sdkmanrc\"\n\tlocal -r subcommand=\"$1\"\n\n\tcase $subcommand in\n\t\t\"\")    __sdkman_load_env \"$sdkmanrc\" ;;\n\t\tinit)  __sdkman_create_env_file \"$sdkmanrc\";;\n\t\tinstall) __sdkman_setup_env \"$sdkmanrc\";;\n\t\tclear) __sdkman_clear_env \"$sdkmanrc\";;\n\tesac\n}\n\nfunction __sdkman_setup_env() {\n\tlocal sdkmanrc=\"$1\"\n\n\tif [[ ! -f \"$sdkmanrc\" ]]; then\n\t\t__sdkman_echo_red \"Could not find $sdkmanrc in the current directory.\"\n\t\techo \"\"\n\t\t__sdkman_echo_yellow \"Run 'sdk env init' to create it.\"\n\n\t\treturn 1\n\tfi\n\n\tsdkman_auto_answer=\"true\" USE=\"n\" __sdkman_env_each_candidate \"$sdkmanrc\" \"__sdk_install\"\n\t__sdkman_load_env \"$sdkmanrc\"\n}\n\nfunction __sdkman_load_env() {\n\tlocal sdkmanrc=\"$1\"\n\t\n\tif [[ ! -f \"$sdkmanrc\" ]]; then\n\t\t__sdkman_echo_red \"Could not find $sdkmanrc in the current directory.\"\n\t\techo \"\"\n\t\t__sdkman_echo_yellow \"Run 'sdk env init' to create it.\"\n\n\t\treturn 1\n\tfi\n\n\t__sdkman_env_each_candidate \"$sdkmanrc\" \"__sdkman_check_and_use\" && \n\t\tSDKMAN_ENV=$PWD\n}\n\nfunction __sdkman_check_and_use() {\n\tlocal -r candidate=$1\n\tlocal -r version=$2\n\n\tif [[ ! -d \"${SDKMAN_CANDIDATES_DIR}/${candidate}/${version}\" ]]; then\n\t\t__sdkman_echo_red \"Stop! $candidate $version is not installed.\"\n\t\techo \"\"\n\t\t__sdkman_echo_yellow \"Run 'sdk env install' to install it.\"\n\n\t\treturn 1\n\tfi\n\n\t__sdk_use \"$candidate\" \"$version\"\n}\n\nfunction __sdkman_create_env_file() {\n\tlocal sdkmanrc=\"$1\"\n\t\n\tif [[ -f \"$sdkmanrc\" ]]; then\n\t\t__sdkman_echo_red \"$sdkmanrc already exists!\"\n\n\t\treturn 1\n\tfi\n\n\t__sdkman_determine_current_version \"java\"\n\n\tlocal version\n\t[[ -n \"$CURRENT\" ]] && version=\"$CURRENT\" || version=\"$(__sdkman_secure_curl \"${SDKMAN_CANDIDATES_API}/candidates/default/java\")\"\n\n\tcat <<-eof >|\"$sdkmanrc\"\n\t# Enable auto-env through the sdkman_auto_env config\n\t# Add key=value pairs of SDKs to use below\n\tjava=$version\n\teof\n\n\t__sdkman_echo_green \"$sdkmanrc created.\"\n}\n\nfunction __sdkman_clear_env() {\n\tlocal sdkmanrc=\"$1\"\n\n\tif [[ -z $SDKMAN_ENV ]]; then\n\t\t__sdkman_echo_red \"No environment currently set!\"\n\t\treturn 1\n\tfi\n\n\tif [[ ! -f ${SDKMAN_ENV}/${sdkmanrc} ]]; then\n\t\t__sdkman_echo_red \"Could not find ${SDKMAN_ENV}/${sdkmanrc}.\"\n\t\treturn 1\n\tfi\n\n\t__sdkman_env_each_candidate \"${SDKMAN_ENV}/${sdkmanrc}\" \"__sdkman_env_restore_default_version\"\n\tunset SDKMAN_ENV\n}\n\nfunction __sdkman_env_restore_default_version() {\n\tlocal -r candidate=\"$1\"\n\n\tlocal candidate_dir default_version\n\tcandidate_dir=\"${SDKMAN_CANDIDATES_DIR}/${candidate}/current\"\n\tif __sdkman_is_symlink $candidate_dir; then\n\t\tdefault_version=$(basename $(readlink ${candidate_dir}))\n\t\t__sdk_use \"$candidate\" \"$default_version\" >/dev/null &&\n\t\t\t__sdkman_echo_yellow \"Restored $candidate version to $default_version (default)\"\n\telse\n\t\t__sdkman_echo_yellow \"No default version of $candidate was found\"\n\tfi\n}\n\nfunction __sdkman_env_each_candidate() {\n\tlocal -r filepath=$1\n\tlocal -r func=$2\n\n\tlocal normalised_line\n\twhile IFS= read -r line || [[ -n \"$line\" ]]; do\n\t\tnormalised_line=\"$(__sdkman_normalise \"$line\")\"\n\n\t\t__sdkman_is_blank_line \"$normalised_line\" && continue\n\n\t\tif ! __sdkman_matches_candidate_format \"$normalised_line\"; then\n\t\t\t__sdkman_echo_red \"Invalid candidate format!\"\n\t\t\techo \"\"\n\t\t\t__sdkman_echo_yellow \"Expected 'candidate=version' but found '$normalised_line'\"\n\n\t\t\treturn 1\n\t\tfi\n\n\t\t$func \"${normalised_line%=*}\" \"${normalised_line#*=}\" || return\n\tdone < \"$filepath\"\n}\n\nfunction __sdkman_is_symlink() {\n\t[[ -h \"$1\" ]]\n}\n\nfunction __sdkman_is_blank_line() {\n\t[[ -z \"$1\" ]]\n}\n\nfunction __sdkman_normalise() {\n\tlocal -r line_without_comments=\"${1/\\#*/}\"\n\n\techo \"${line_without_comments//[[:space:]]/}\"\n}\n\nfunction __sdkman_matches_candidate_format() {\n\t[[ \"$1\" =~ ^[[:lower:]]+\\=.+$ ]]\n}\n"
  },
  {
    "path": "src/main/bash/sdkman-flush.sh",
    "content": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#       http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n\nfunction __sdk_flush() {\n\tlocal qualifier=\"$1\"\n\n\tcase \"$qualifier\" in\n\tversion)\n\t\tif [[ -f \"${SDKMAN_DIR}/var/version\" ]]; then\n\t\t\trm -f \"${SDKMAN_DIR}/var/version\"\n\t\t\t__sdkman_echo_green \"Version file has been flushed.\"\n\t\tfi\n\t\t;;\n\ttemp)\n\t\t__sdkman_cleanup_folder \"tmp\"\n\t\t;;\n\ttmp)\n\t\t__sdkman_cleanup_folder \"tmp\"\n\t\t;;\n\tmetadata)\n    \t__sdkman_cleanup_folder \"var/metadata\"\n    \t;;\n\t*)\n\t\t__sdkman_cleanup_folder \"tmp\"\n\t\t__sdkman_cleanup_folder \"var/metadata\"\n\t\t;;\n\tesac\n}\n\nfunction __sdkman_cleanup_folder() {\n\tlocal folder=\"$1\"\n\tlocal sdkman_cleanup_dir\n\tlocal sdkman_cleanup_disk_usage\n\tlocal sdkman_cleanup_count\n\n\tsdkman_cleanup_dir=\"${SDKMAN_DIR}/${folder}\"\n\tsdkman_cleanup_disk_usage=$(du -sh \"$sdkman_cleanup_dir\")\n\tsdkman_cleanup_count=$(ls -1 \"$sdkman_cleanup_dir\" | wc -l)\n\n\trm -rf \"$sdkman_cleanup_dir\"\n\tmkdir \"$sdkman_cleanup_dir\"\n\n\t__sdkman_echo_green \"${sdkman_cleanup_count} archive(s) flushed, freeing ${sdkman_cleanup_disk_usage}.\"\n}\n"
  },
  {
    "path": "src/main/bash/sdkman-help.sh",
    "content": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#       http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n\nfunction __sdk_help() {\n\t__sdkman_deprecation_notice \"help\"\n\t__sdkman_echo_no_colour \"\"\n\t__sdkman_echo_no_colour \"Usage: sdk <command> [candidate] [version]\"\n\t__sdkman_echo_no_colour \"       sdk offline <enable|disable>\"\n\t__sdkman_echo_no_colour \"\"\n\t__sdkman_echo_no_colour \"   commands:\"\n\t__sdkman_echo_no_colour \"       install   or i    <candidate> [version] [local-path]\"\n\t__sdkman_echo_no_colour \"       uninstall or rm   <candidate> <version>\"\n\t__sdkman_echo_no_colour \"       list      or ls   [candidate]\"\n\t__sdkman_echo_no_colour \"       use       or u    <candidate> <version>\"\n\t__sdkman_echo_no_colour \"       config\"\n\t__sdkman_echo_no_colour \"       default   or d    <candidate> [version]\"\n\t__sdkman_echo_no_colour \"       home      or h    <candidate> <version>\"\n\t__sdkman_echo_no_colour \"       env       or e    [init|install|clear]\"\n\t__sdkman_echo_no_colour \"       current   or c    [candidate]\"\n\t__sdkman_echo_no_colour \"       upgrade   or ug   [candidate]\"\n\t__sdkman_echo_no_colour \"       version   or v\"\n\t__sdkman_echo_no_colour \"       help\"\n\t__sdkman_echo_no_colour \"       offline           [enable|disable]\"\n\n\tif [[ \"$sdkman_selfupdate_feature\" == \"true\" ]]; then\n\t\t__sdkman_echo_no_colour \"       selfupdate        [force]\"\n\tfi\n\n\t__sdkman_echo_no_colour \"       update\"\n\t__sdkman_echo_no_colour \"       flush             [tmp|metadata|version]\"\n\t__sdkman_echo_no_colour \"\"\n\t__sdkman_echo_no_colour \"   candidate  :  the SDK to install: groovy, scala, grails, gradle, kotlin, etc.\"\n\t__sdkman_echo_no_colour \"                 use list command for comprehensive list of candidates\"\n\t__sdkman_echo_no_colour \"                 eg: \\$ sdk list\"\n\t__sdkman_echo_no_colour \"   version    :  where optional, defaults to latest stable if not provided\"\n\t__sdkman_echo_no_colour \"                 eg: \\$ sdk install groovy\"\n\t__sdkman_echo_no_colour \"   local-path :  optional path to an existing local installation\"\n\t__sdkman_echo_no_colour \"                 eg: \\$ sdk install groovy 2.4.13-local /opt/groovy-2.4.13\"\n\t__sdkman_echo_no_colour \"\"\n}\n"
  },
  {
    "path": "src/main/bash/sdkman-home.sh",
    "content": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#       http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n\nfunction __sdk_home() {\n\t__sdkman_deprecation_notice \"home\"\n\tlocal candidate version\n\n\tcandidate=\"$1\"\n\tversion=\"$2\"\n\t__sdkman_check_version_present \"$version\" || return 1\n\t__sdkman_check_candidate_present \"$candidate\" || return 1\n\t__sdkman_determine_version \"$candidate\" \"$version\" || return 1\n\n\tif [[ ! -d \"${SDKMAN_CANDIDATES_DIR}/${candidate}/${version}\" ]]; then\n\t\techo \"\"\n\t\t__sdkman_echo_red \"Stop! Candidate version is not installed.\"\n\t\techo \"\"\n\t\t__sdkman_echo_yellow \"Tip: Run the following to install this version\"\n\t\techo \"\"\n\t\t__sdkman_echo_yellow \"$ sdk install ${candidate} ${version}\"\n\t\treturn 1\n\tfi\n\n\techo -n \"${SDKMAN_CANDIDATES_DIR}/${candidate}/${version}\"\n}\n"
  },
  {
    "path": "src/main/bash/sdkman-init.sh",
    "content": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#       http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n\n# set env vars if not set\nif [ -z \"$SDKMAN_CANDIDATES_API\" ]; then\n\texport SDKMAN_CANDIDATES_API=\"@SDKMAN_CANDIDATES_API@\"\nfi\n\nif [ -z \"$SDKMAN_BROKER_API\" ]; then\n\texport SDKMAN_BROKER_API=\"@SDKMAN_BROKER_API@\"\nfi\n\nif [ -z \"$SDKMAN_DIR\" ]; then\n\texport SDKMAN_DIR=\"$HOME/.sdkman\"\nfi\n\n# Load the sdkman config if it exists.\nif [ -f \"${SDKMAN_DIR}/etc/config\" ]; then\n\tsource \"${SDKMAN_DIR}/etc/config\"\nfi\n\n# Read the platform file\nSDKMAN_PLATFORM=\"$(cat \"${SDKMAN_DIR}/var/platform\")\"\nexport SDKMAN_PLATFORM\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\ndarwin=false\nsolaris=false\nfreebsd=false\nSDKMAN_KERNEL=\"$(uname -s)\"\ncase \"${SDKMAN_KERNEL}\" in\n\tCYGWIN*)\n\t\tcygwin=true\n\t\t;;\n\tDarwin*)\n\t\tdarwin=true\n\t\t;;\n\tSunOS*)\n\t\tsolaris=true\n\t\t;;\n\tFreeBSD*)\n\t\tfreebsd=true\nesac\n\n# Determine shell\nzsh_shell=false\nbash_shell=false\n\nif [[ -n \"$ZSH_VERSION\" ]]; then\n\tzsh_shell=true\nelif [[ -n \"$BASH_VERSION\" ]]; then\n\tbash_shell=true\nfi\n\n# Source sdkman module scripts and extension files.\n#\n# Extension files are prefixed with 'sdkman-' and found in the ext/ folder.\n# Use this if extensions are written with the functional approach and want\n# to use functions in the main sdkman script. For more details, refer to\n# <https://github.com/sdkman/sdkman-extensions>.\nOLD_IFS=\"$IFS\"\nIFS=$'\\n'\nscripts=($(find \"${SDKMAN_DIR}/src\" \"${SDKMAN_DIR}/ext\" -type f -name 'sdkman-*.sh'))\nfor f in \"${scripts[@]}\"; do\n\tsource \"$f\"\ndone\nIFS=\"$OLD_IFS\"\nunset OLD_IFS scripts f\n\n# Create upgrade delay file if it doesn't exist\nif [[ ! -f \"${SDKMAN_DIR}/var/delay_upgrade\" ]]; then\n\ttouch \"${SDKMAN_DIR}/var/delay_upgrade\"\nfi\n\n# set curl connect-timeout and max-time\nif [[ -z \"$sdkman_curl_connect_timeout\" ]]; then sdkman_curl_connect_timeout=7; fi\nif [[ -z \"$sdkman_curl_max_time\" ]]; then sdkman_curl_max_time=10; fi\n\n# set curl retry\nif [[ -z \"${sdkman_curl_retry}\" ]]; then sdkman_curl_retry=0; fi\n\n# set curl retry max time in seconds\nif [[ -z \"${sdkman_curl_retry_max_time}\" ]]; then sdkman_curl_retry_max_time=60; fi\n\n# set curl to continue downloading automatically\nif [[ -z \"${sdkman_curl_continue}\" ]]; then sdkman_curl_continue=true; fi\n\n# read list of candidates and set array\nSDKMAN_CANDIDATES_CACHE=\"${SDKMAN_DIR}/var/candidates\"\nSDKMAN_CANDIDATES_CSV=$(<\"$SDKMAN_CANDIDATES_CACHE\")\n__sdkman_echo_debug \"Setting candidates csv: $SDKMAN_CANDIDATES_CSV\"\nif [[ \"$zsh_shell\" == 'true' ]]; then\n\tSDKMAN_CANDIDATES=(${(s:,:)SDKMAN_CANDIDATES_CSV})\nelse\n\tIFS=',' read -a SDKMAN_CANDIDATES <<< \"${SDKMAN_CANDIDATES_CSV}\"\nfi\n\nexport SDKMAN_CANDIDATES_DIR=\"${SDKMAN_DIR}/candidates\"\n\nfor candidate_name in \"${SDKMAN_CANDIDATES[@]}\"; do\n\tcandidate_dir=\"${SDKMAN_CANDIDATES_DIR}/${candidate_name}/current\"\n\tif [[ -h \"$candidate_dir\" || -d \"${candidate_dir}\" ]]; then\n\t\t__sdkman_export_candidate_home \"$candidate_name\" \"$candidate_dir\"\n\t\t__sdkman_prepend_candidate_to_path \"$candidate_dir\"\n\tfi\ndone\nunset candidate_name candidate_dir\nexport PATH\n\n# source completion scripts\nif [[ \"$sdkman_auto_complete\" == 'true' ]]; then\n\tif [[ \"$zsh_shell\" == 'true' ]]; then\n\t\t# initialize zsh completions (if not already done)\n\t\tif ! (( $+functions[compdef] )) ; then\n\t\t\tautoload -Uz compinit\n\t\t\tif [[ $ZSH_DISABLE_COMPFIX == 'true' ]]; then\n\t\t\t\tcompinit -u -C\n\t\t\telse\n\t\t\t\tcompinit\n\t\t\tfi\n\t\tfi\n\t\tautoload -U bashcompinit\n\t\tbashcompinit\n\t\tsource \"${SDKMAN_DIR}/contrib/completion/bash/sdk\"\n\t\t__sdkman_echo_debug \"ZSH completion script loaded...\"\n\telif [[ \"$bash_shell\" == 'true' ]]; then\n\t\tsource \"${SDKMAN_DIR}/contrib/completion/bash/sdk\"\n\t\t__sdkman_echo_debug \"Bash completion script loaded...\"\n\telse\n\t\t__sdkman_echo_debug \"No completion scripts found for $SHELL\"\n\tfi\nfi\n\nif [[ \"$sdkman_auto_env\" == \"true\" ]]; then\n\tif [[ \"$zsh_shell\" == \"true\" ]]; then\n\t\tfunction sdkman_auto_env() {\n\t\t\tif [[ -n $SDKMAN_ENV ]] && [[ ! $PWD =~ ^$SDKMAN_ENV ]]; then\n\t\t\t\tsdk env clear\n\t\t\tfi\n\t\t\tif [[ -f .sdkmanrc ]]; then\n\t\t\t\tsdk env\n\t\t\tfi\n\t\t}\n\n\t\tchpwd_functions+=(sdkman_auto_env)\n\telse\n\t\tfunction sdkman_auto_env() {\n\t\t\tif [[ -n $SDKMAN_ENV ]] && [[ ! $PWD =~ ^$SDKMAN_ENV ]]; then\n\t\t\t\tsdk env clear\n\t\t\tfi\n\t\t\tif [[ \"$SDKMAN_OLD_PWD\" != \"$PWD\" ]] && [[ -f \".sdkmanrc\" ]]; then\n\t\t\t\tsdk env\n\t\t\tfi\n\n\t\t\texport SDKMAN_OLD_PWD=\"$PWD\"\n\t\t}\n\t\t\n\t\ttrimmed_prompt_command=\"${PROMPT_COMMAND%\"${PROMPT_COMMAND##*[![:space:]]}\"}\"\n\t\t[[ -z \"$trimmed_prompt_command\" ]] && PROMPT_COMMAND=\"sdkman_auto_env\" || PROMPT_COMMAND=\"${trimmed_prompt_command%\\;};sdkman_auto_env\"\n\tfi\n\n\tsdkman_auto_env\nfi\n"
  },
  {
    "path": "src/main/bash/sdkman-install.sh",
    "content": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#       http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n\nfunction __sdk_install() {\n\tlocal candidate version folder\n\n\tcandidate=\"$1\"\n\tversion=\"$2\"\n\tfolder=\"$3\"\n\n\t__sdkman_check_candidate_present \"$candidate\" || return 1\n\t__sdkman_determine_version \"$candidate\" \"$version\" \"$folder\" || return 1\n\n\tif [[ -d \"${SDKMAN_CANDIDATES_DIR}/${candidate}/${VERSION}\" || -L \"${SDKMAN_CANDIDATES_DIR}/${candidate}/${VERSION}\" ]]; then\n\t\techo \"\"\n\t\t__sdkman_echo_yellow \"${candidate} ${VERSION} is already installed.\"\n\t\treturn 0\n\tfi\n\n\tif [[ ${VERSION_VALID} == 'valid' ]]; then\n\t\t__sdkman_determine_current_version \"$candidate\"\n\t\t__sdkman_install_candidate_version \"$candidate\" \"$VERSION\" || return 1\n\n\t\tif [[ \"$sdkman_auto_answer\" != 'true' && \"$auto_answer_upgrade\" != 'true' && -n \"$CURRENT\" ]]; then\n\t\t\t__sdkman_echo_confirm \"Do you want ${candidate} ${VERSION} to be set as default? (Y/n): \"\n\t\t\tread USE\n\t\tfi\n\n\t\tif [[ -z \"$USE\" || \"$USE\" == \"y\" || \"$USE\" == \"Y\" ]]; then\n\t\t\techo \"\"\n\t\t\t__sdkman_echo_green \"Setting ${candidate} ${VERSION} as default.\"\n\t\t\t__sdkman_link_candidate_version \"$candidate\" \"$VERSION\"\n\t\t\t__sdkman_add_to_path \"$candidate\"\n\t\tfi\n\n\t\treturn 0\n\telif [[ \"$VERSION_VALID\" == 'invalid' && -n \"$folder\" ]]; then\n\t\t__sdkman_install_local_version \"$candidate\" \"$VERSION\" \"$folder\" || return 1\n\telse\n\t\techo \"\"\n\t\t__sdkman_echo_red \"Stop! $1 is not a valid ${candidate} version.\"\n\t\treturn 1\n\tfi\n}\n\nfunction __sdkman_install_candidate_version() {\n\tlocal candidate version\n\n\tcandidate=\"$1\"\n\tversion=\"$2\"\n\n\t__sdkman_download \"$candidate\" \"$version\" || return 1\n\t__sdkman_echo_green \"Installing: ${candidate} ${version}\"\n\n\tmkdir -p \"${SDKMAN_CANDIDATES_DIR}/${candidate}\"\n\n\trm -rf \"${SDKMAN_DIR}/tmp/out\"\n\tunzip -oq \"${SDKMAN_DIR}/tmp/${candidate}-${version}.zip\" -d \"${SDKMAN_DIR}/tmp/out\"\n\tmv -f \"$SDKMAN_DIR\"/tmp/out/* \"${SDKMAN_CANDIDATES_DIR}/${candidate}/${version}\"\n\t__sdkman_echo_green \"Done installing!\"\n\techo \"\"\n}\n\nfunction __sdkman_install_local_version() {\n\tlocal candidate version folder version_length version_length_max\n\n\tversion_length_max=15\n\n\tcandidate=\"$1\"\n\tversion=\"$2\"\n\tfolder=\"$3\"\n\n\t# Validate max length of version\n\tversion_length=${#version}\n\t__sdkman_echo_debug \"Validating that actual version length ($version_length) does not exceed max ($version_length_max)\"\n\n\tif [[ $version_length -gt $version_length_max ]]; then\n\t\t__sdkman_echo_red \"Invalid version! ${version} with length ${version_length} exceeds max of ${version_length_max}!\"\n\t\treturn 1\n\tfi\n\n\tmkdir -p \"${SDKMAN_CANDIDATES_DIR}/${candidate}\"\n\n\t# handle relative paths\n\tif [[ \"$folder\" != /* ]]; then\n\t\tfolder=\"$(pwd)/$folder\"\n\tfi\n\n\tif [[ -d \"$folder\" ]]; then\n\t\t__sdkman_echo_green \"Linking ${candidate} ${version} to ${folder}\"\n\t\tln -s \"$folder\" \"${SDKMAN_CANDIDATES_DIR}/${candidate}/${version}\"\n\t\t__sdkman_echo_green \"Done installing!\"\n\telse\n\t\t__sdkman_echo_red \"Invalid path! Refusing to link ${candidate} ${version} to ${folder}.\"\n\t\treturn 1\n\tfi\n\n\techo \"\"\n}\n\nfunction __sdkman_download() {\n\tlocal candidate version\n\n\tcandidate=\"$1\"\n\tversion=\"$2\"\n\n\tmetadata_folder=\"${SDKMAN_DIR}/var/metadata\"\n\tmkdir -p \"${metadata_folder}\"\n\t\t\n\tlocal platform_parameter=\"$SDKMAN_PLATFORM\"\n\tlocal download_url=\"${SDKMAN_BROKER_API}/download/${candidate}/${version}/${platform_parameter}\"\n\tlocal base_name=\"${candidate}-${version}\"\n\tlocal tmp_headers_file=\"${SDKMAN_DIR}/tmp/${base_name}.headers.tmp\"\n\tlocal headers_file=\"${metadata_folder}/${base_name}.headers\"\n\n\texport local binary_input=\"${SDKMAN_DIR}/tmp/${base_name}.bin\"\n\texport local zip_output=\"${SDKMAN_DIR}/tmp/${base_name}.zip\"\n\n\techo \"\"\n\t__sdkman_echo_no_colour \"Downloading: ${candidate} ${version}\"\n\techo \"\"\n\t__sdkman_echo_no_colour \"In progress...\"\n\techo \"\"\n\n\t# download binary\n\t__sdkman_secure_curl_download \"${download_url}\" --output \"${binary_input}\" --dump-header \"${tmp_headers_file}\"\n\tgrep '^X-Sdkman' \"${tmp_headers_file}\" > \"${headers_file}\"\n\t__sdkman_echo_debug \"Downloaded binary to: ${binary_input} (HTTP headers written to: ${headers_file})\"\n\n\t# post-installation hook: implements function __sdkman_post_installation_hook\n\t# responsible for taking `binary_input` and producing `zip_output`\n\tlocal post_installation_hook=\"${SDKMAN_DIR}/tmp/hook_post_${candidate}_${version}.sh\"\n\t__sdkman_echo_debug \"Get post-installation hook: ${SDKMAN_CANDIDATES_API}/hooks/post/${candidate}/${version}/${platform_parameter}\"\n\t__sdkman_secure_curl \"${SDKMAN_CANDIDATES_API}/hooks/post/${candidate}/${version}/${platform_parameter}\" >| \"$post_installation_hook\"\n\t__sdkman_echo_debug \"Copy remote post-installation hook: ${post_installation_hook}\"\n\tsource \"$post_installation_hook\"\n\t__sdkman_post_installation_hook || return 1\n\t__sdkman_echo_debug \"Processed binary as: $zip_output\"\n\t__sdkman_echo_debug \"Completed post-installation hook...\"\n\t\t\n\t__sdkman_validate_zip \"${zip_output}\" || return 1\n\t__sdkman_checksum_zip \"${zip_output}\" \"${headers_file}\" || return 1\n\techo \"\"\n}\n\nfunction __sdkman_validate_zip() {\n\tlocal zip_archive zip_ok\n\n\tzip_archive=\"$1\"\n\tzip_ok=$(unzip -t \"$zip_archive\" | grep 'No errors detected in compressed data')\n\tif [ -z \"$zip_ok\" ]; then\n\t\trm -f \"$zip_archive\"\n\t\techo \"\"\n\t\t__sdkman_echo_red \"Stop! The archive was corrupt and has been removed! Please try installing again.\"\n\t\treturn 1\n\tfi\n}\n\nfunction __sdkman_checksum_zip() {\n\tlocal -r zip_archive=\"$1\"\n\tlocal -r headers_file=\"$2\"\n\tlocal algorithm checksum cmd\n\tlocal shasum_avail=false\n\tlocal md5sum_avail=false\n\t\n\tif [ -z \"${headers_file}\" ]; then\n\t\techo \"\"\n\t\t__sdkman_echo_debug \"Skipping checksum for cached artifact\"\n\t\treturn\n\telif [ ! -f \"${headers_file}\" ]; then\n\t\techo \"\"\n\t\t__sdkman_echo_yellow \"Metadata file not found at '${headers_file}', skipping checksum...\"\n\t\treturn\n\tfi\n\t\n\tif [[ \"$sdkman_checksum_enable\" != \"true\" ]]; then\n\t\techo \"\"\n\t\t__sdkman_echo_yellow \"Checksums are disabled, skipping verification...\"\n\t\treturn\n\tfi\n\t\n\t#Check for the appropriate checksum tools\n\tif command -v shasum > /dev/null 2>&1; then\n\t\tshasum_avail=true\n\tfi\n\tif command -v md5sum > /dev/null 2>&1; then\n\t\tmd5sum_avail=true\n\tfi\n\t\n\twhile IFS= read -r line; do\n\t\talgorithm=$(echo $line | sed -n 's/^X-Sdkman-Checksum-\\(.*\\):.*$/\\1/p' | tr '[:lower:]' '[:upper:]')\n\t\tchecksum=$(echo $line | sed -n 's/^X-Sdkman-Checksum-.*:\\(.*\\)$/\\1/p' | tr -cd '[:alnum:]')\n\t\t\n\t\tif [[ -n ${algorithm} && -n ${checksum} ]]; then\n\t\t\t\n\t\t\tif [[ \"$algorithm\" =~ 'SHA' && \"$shasum_avail\" == 'true' ]]; then\n\t\t\t\tcmd=\"echo \\\"${checksum} *${zip_archive}\\\" | shasum --check --quiet\"\n\t\t\t\t\n\t\t\telif [[ \"$algorithm\" =~ 'MD5' && \"$md5sum_avail\" == 'true' ]]; then\n\t\t\t\tcmd=\"echo \\\"${checksum} ${zip_archive}\\\" | md5sum --check --quiet\"\n\t\t\tfi\n\t\t\t\n\t\t\tif [[ -n $cmd ]]; then\n\t\t\t\t__sdkman_echo_no_colour \"Verifying artifact: ${zip_archive} (${algorithm}:${checksum})\"\n\n\t\t\t\tif ! eval \"$cmd\"; then\n\t\t\t\t\trm -f \"$zip_archive\"\n\t\t\t\t\techo \"\"\n\t\t\t\t\t__sdkman_echo_red \"Stop! An invalid checksum was detected and the archive removed! Please try re-installing.\"\n\t\t\t\t\treturn 1\n\t\t\t\tfi\n\t\t\telse\n\t\t\t\t__sdkman_echo_no_colour \"Not able to perform checksum verification at this time.\"\n\t\t\tfi\n\t\tfi\n  \tdone < \"${headers_file}\"\n}\n"
  },
  {
    "path": "src/main/bash/sdkman-list.sh",
    "content": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#       http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n\nfunction __sdk_list() {\n\tlocal candidate=\"$1\"\n\n\tif [[ -z \"$candidate\" ]]; then\n\t\t__sdkman_list_candidates\n\telse\n\t\t__sdkman_list_versions \"$candidate\"\n\tfi\n}\n\nfunction __sdkman_list_candidates() {\n\tif [[ \"$SDKMAN_AVAILABLE\" == \"false\" ]]; then\n\t\t__sdkman_echo_red \"This command is not available while offline.\"\n\telse\n\t\t__sdkman_echo_paged \"$(__sdkman_secure_curl \"${SDKMAN_CANDIDATES_API}/candidates/list\")\"\n\tfi\n}\n\nfunction __sdkman_list_versions() {\n\tlocal candidate versions_csv\n\n\tcandidate=\"$1\"\n\tversions_csv=\"$(__sdkman_build_version_csv \"$candidate\")\"\n\t__sdkman_determine_current_version \"$candidate\"\n\n\tif [[ \"$SDKMAN_AVAILABLE\" == \"false\" ]]; then\n\t\t__sdkman_offline_list \"$candidate\" \"$versions_csv\"\n\telse\n\t\t__sdkman_echo_paged \"$(__sdkman_secure_curl \"${SDKMAN_CANDIDATES_API}/candidates/${candidate}/${SDKMAN_PLATFORM}/versions/list?current=${CURRENT}&installed=${versions_csv}\")\"\n\tfi\n}\n\nfunction __sdkman_build_version_csv() {\n\tlocal candidate versions_csv\n\n\tcandidate=\"$1\"\n\tversions_csv=\"\"\n\n\tif [[ -d \"${SDKMAN_CANDIDATES_DIR}/${candidate}\" ]]; then\n\t\tfor version in $(find \"${SDKMAN_CANDIDATES_DIR}/${candidate}\" -maxdepth 1 -mindepth 1 \\( -type l -o -type d \\) -exec basename '{}' \\; | sort -r); do\n\t\t\tif [[ \"$version\" != 'current' ]]; then\n\t\t\t\tversions_csv=\"${version},${versions_csv}\"\n\t\t\tfi\n\t\tdone\n\t\tversions_csv=${versions_csv%?}\n\tfi\n\techo \"$versions_csv\"\n}\n\nfunction __sdkman_offline_list() {\n\tlocal candidate versions_csv\n\n\tcandidate=\"$1\"\n\tversions_csv=\"$2\"\n\n\t__sdkman_echo_no_colour \"--------------------------------------------------------------------------------\"\n\t__sdkman_echo_yellow \"Offline: only showing installed ${candidate} versions\"\n\t__sdkman_echo_no_colour \"--------------------------------------------------------------------------------\"\n\n\tlocal versions=($(echo ${versions_csv//,/ }))\n\tfor ((i = ${#versions} - 1; i >= 0; i--)); do\n\t\tif [[ -n \"${versions[${i}]}\" ]]; then\n\t\t\tif [[ \"${versions[${i}]}\" == \"$CURRENT\" ]]; then\n\t\t\t\t__sdkman_echo_no_colour \" > ${versions[${i}]}\"\n\t\t\telse\n\t\t\t\t__sdkman_echo_no_colour \" * ${versions[${i}]}\"\n\t\t\tfi\n\t\tfi\n\tdone\n\n\tif [[ -z \"${versions[@]}\" ]]; then\n\t\t__sdkman_echo_yellow \"   None installed!\"\n\tfi\n\n\t__sdkman_echo_no_colour \"--------------------------------------------------------------------------------\"\n\t__sdkman_echo_no_colour \"* - installed                                                                   \"\n\t__sdkman_echo_no_colour \"> - currently in use                                                            \"\n\t__sdkman_echo_no_colour \"--------------------------------------------------------------------------------\"\n}\n"
  },
  {
    "path": "src/main/bash/sdkman-main.sh",
    "content": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#       http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n\nfunction ___sdkman_help() {\n\tif [[ -f \"$SDKMAN_DIR/libexec/help\" ]]; then\n\t\t\"$SDKMAN_DIR/libexec/help\"\n\telse\n\t\t__sdk_help\n\tfi\n}\n\nfunction sdk() {\n\n\tCOMMAND=\"$1\"\n\tQUALIFIER=\"$2\"\n\n\tcase \"$COMMAND\" in\n\tl)\n\t\tCOMMAND=\"list\"\n\t\t;;\n\tls)\n\t\tCOMMAND=\"list\"\n\t\t;;\n\tv)\n\t\tCOMMAND=\"version\"\n\t\t;;\n\tu)\n\t\tCOMMAND=\"use\"\n\t\t;;\n\ti)\n\t\tCOMMAND=\"install\"\n\t\t;;\n\trm)\n\t\tCOMMAND=\"uninstall\"\n\t\t;;\n\tc)\n\t\tCOMMAND=\"current\"\n\t\t;;\n\tug)\n\t\tCOMMAND=\"upgrade\"\n\t\t;;\n\td)\n\t\tCOMMAND=\"default\"\n\t\t;;\n\th)\n\t\tCOMMAND=\"home\"\n\t\t;;\n\te)\n\t\tCOMMAND=\"env\"\n\t\t;;\n\tesac\n\n\t#\n\t# Various sanity checks and default settings\n\t#\n\n\t# Check candidates cache\n\tif [[ \"$COMMAND\" != \"update\" ]]; then\n\t\t___sdkman_check_candidates_cache \"$SDKMAN_CANDIDATES_CACHE\" || return 1\n\tfi\n\n\t# Always presume internet availability\n\tSDKMAN_AVAILABLE=\"true\"\n\tif [ -z \"$SDKMAN_OFFLINE_MODE\" ]; then\n\t\tSDKMAN_OFFLINE_MODE=\"false\"\n\tfi\n\n\t# ...unless proven otherwise\n\t__sdkman_update_service_availability\n\n\t# Load the sdkman config if it exists.\n\tif [ -f \"${SDKMAN_DIR}/etc/config\" ]; then\n\t\tsource \"${SDKMAN_DIR}/etc/config\"\n\tfi\n\n\t# no command provided\n\tif [[ -z \"$COMMAND\" ]]; then\n\t\t___sdkman_help\n\t\treturn 1\n\tfi\n\n\t# Check if it is a valid command\n\tCMD_FOUND=\"\"\n\tif [[ \"$COMMAND\" != \"selfupdate\" || \"$sdkman_selfupdate_feature\" == \"true\" ]]; then\n\t\tCMD_TARGET=\"${SDKMAN_DIR}/src/sdkman-${COMMAND}.sh\"\n\t\tif [[ -f \"$CMD_TARGET\" ]]; then\n\t\t\tCMD_FOUND=\"$CMD_TARGET\"\n\t\tfi\n\tfi\n\n\t# Check if it is a sourced function\n\tCMD_TARGET=\"${SDKMAN_DIR}/ext/sdkman-${COMMAND}.sh\"\n\tif [[ -f \"$CMD_TARGET\" ]]; then\n\t\tCMD_FOUND=\"$CMD_TARGET\"\n\tfi\n\n\t# couldn't find the command\n\tif [[ -z \"$CMD_FOUND\" ]]; then\n\t\techo \"\"\n\t\t__sdkman_echo_red \"Invalid command: $COMMAND\"\n\t\techo \"\"\n\t\t___sdkman_help\n\tfi\n\n\t# Validate offline qualifier\n\tif [[ \"$COMMAND\" == \"offline\" && -n \"$QUALIFIER\" && -z $(echo \"enable disable\" | grep -w \"$QUALIFIER\") ]]; then\n\t\techo \"\"\n\t\t__sdkman_echo_red \"Stop! $QUALIFIER is not a valid offline mode.\"\n\tfi\n\n\t# Store the return code of the requested command\n\tlocal final_rc=0\n\n\t# Native commands found under libexec\n\tlocal native_command=\"${SDKMAN_DIR}/libexec/${COMMAND}\"\n\t\n\tif [[ \"$sdkman_native_enable\" == 'true' && -f \"$native_command\" ]]; then\n\t\t\"$native_command\" \"${@:2}\"\n\n\telif [ -n \"$CMD_FOUND\" ]; then\n\n\t\t# Check whether the candidate exists\n\t\tif [[ -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\n\t\t\techo \"\"\n\t\t\t__sdkman_echo_red \"Stop! $QUALIFIER is not a valid candidate.\"\n\t\t\treturn 1\n\t\tfi\n\n\t\t# Internal commands use underscores rather than hyphens\n\t\tlocal converted_command_name=$(echo \"$COMMAND\" | tr '-' '_')\n\n\t\t# Available as a shell function\n\t\t__sdk_\"$converted_command_name\" \"${@:2}\"\n\tfi\n\tfinal_rc=$?\n\treturn $final_rc\n}\n"
  },
  {
    "path": "src/main/bash/sdkman-offline.sh",
    "content": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#       http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n\nfunction __sdk_offline() {\n\tlocal mode=\"$1\"\n\tif [[ -z \"$mode\" || \"$mode\" == \"enable\" ]]; then\n\t\tSDKMAN_OFFLINE_MODE=\"true\"\n\t\t__sdkman_echo_green \"Offline mode enabled.\"\n\tfi\n\n\tif [[ \"$mode\" == \"disable\" ]]; then\n\t\tSDKMAN_OFFLINE_MODE=\"false\"\n\t\t__sdkman_echo_green \"Online mode re-enabled!\"\n\tfi\n}\n"
  },
  {
    "path": "src/main/bash/sdkman-path-helpers.sh",
    "content": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#       http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n\nfunction __sdkman_path_contains() {\n\tlocal candidate exists\n\n\tcandidate=\"$1\"\n\texists=\"$(echo \"$PATH\" | grep \"$candidate\")\"\n\tif [[ -n \"$exists\" ]]; then\n\t\techo 'true'\n\telse\n\t\techo 'false'\n\tfi\n}\n\nfunction __sdkman_add_to_path() {\n\tlocal candidate present\n\n\tcandidate=\"$1\"\n\n\tpresent=$(__sdkman_path_contains \"$candidate\")\n\tif [[ \"$present\" == 'false' ]]; then\n\t\tPATH=\"$SDKMAN_CANDIDATES_DIR/$candidate/current/bin:$PATH\"\n\tfi\n}\n\nfunction __sdkman_set_candidate_home() {\n\tlocal candidate version upper_candidate\n\n\tcandidate=\"$1\"\n\tversion=\"$2\"\n\n\tupper_candidate=$(echo \"$candidate\" | tr '[:lower:]' '[:upper:]')\n\texport \"${upper_candidate}_HOME\"=\"${SDKMAN_CANDIDATES_DIR}/${candidate}/${version}\"\n}\n\nfunction __sdkman_export_candidate_home() {\n\tlocal candidate_name candidate_dir ucase_candidate_name\n\n\tcandidate_name=\"$1\"\n\tcandidate_dir=\"$2\"\n\n\tif [ \"$zsh_shell\" = true ]; then\n\t\tucase_candidate_name=\"${candidate_name:u}\"\n\telif [ \"$bash_shell\" = true ]; then\n\t\tucase_candidate_name=\"${candidate_name^^}\"\n\telse\n\t\tucase_candidate_name=\"$(printf %s \"$candidate_name\" | tr '[:lower:]' '[:upper:]')\"\n\tfi\n\n\texport \"${ucase_candidate_name}_HOME=$candidate_dir\"\n}\n\nfunction __sdkman_prepend_candidate_to_path() {\n\tlocal candidate_dir\n\n\tcandidate_dir=\"$1\"\n\n\tif [ -d \"${candidate_dir}/bin\" ]; then\n\t\tcandidate_dir=\"${candidate_dir}/bin\"\n\tfi\n\t[[ \":$PATH:\" == *\":$candidate_dir:\"* ]] || PATH=\"${candidate_dir}:${PATH}\"\n}\n\nfunction __sdkman_link_candidate_version() {\n\tlocal candidate version\n\n\tcandidate=\"$1\"\n\tversion=\"$2\"\n\n\t# Change the 'current' symlink for the candidate, hence affecting all shells.\n\tif [[ -L \"${SDKMAN_CANDIDATES_DIR}/${candidate}/current\" || -d \"${SDKMAN_CANDIDATES_DIR}/${candidate}/current\" ]]; then\n\t\trm -rf \"${SDKMAN_CANDIDATES_DIR}/${candidate}/current\"\n\tfi\n\n\tln -s \"${version}\" \"${SDKMAN_CANDIDATES_DIR}/${candidate}/current\"\n}\n"
  },
  {
    "path": "src/main/bash/sdkman-selfupdate.sh",
    "content": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#       http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n\nfunction __sdk_selfupdate() {\n\tlocal force_selfupdate\n\tlocal sdkman_script_version_api\n\tlocal sdkman_native_version_api\n\n\tif [[ \"$SDKMAN_AVAILABLE\" == \"false\" ]]; then\n\t\techo \"This command is not available while offline.\"\n\t\treturn 1\n\tfi\n\n\tif [[ \"$sdkman_beta_channel\" == \"true\" ]]; then\n\t\tsdkman_script_version_api=\"${SDKMAN_CANDIDATES_API}/broker/version/sdkman/script/beta\"\n\t\tsdkman_native_version_api=\"${SDKMAN_CANDIDATES_API}/broker/version/sdkman/native/beta\"\n\telse\n\t\tsdkman_script_version_api=\"${SDKMAN_CANDIDATES_API}/broker/version/sdkman/script/stable\"\n\t\tsdkman_native_version_api=\"${SDKMAN_CANDIDATES_API}/broker/version/sdkman/native/stable\"\n\tfi\n\n\tsdkman_remote_script_version=$(__sdkman_secure_curl \"$sdkman_script_version_api\")\n\tsdkman_remote_native_version=$(__sdkman_secure_curl \"$sdkman_native_version_api\")\n\n\tsdkman_local_script_version=$(< \"$SDKMAN_DIR/var/version\")\n\tsdkman_local_native_version=$(< \"$SDKMAN_DIR/var/version_native\")\n\n\t__sdkman_echo_debug \"Script: local version: $sdkman_local_script_version; remote version: $sdkman_remote_script_version\"\n\t__sdkman_echo_debug \"Native: local version: $sdkman_local_native_version; remote version: $sdkman_remote_native_version\"\n\n\tforce_selfupdate=\"$1\"\n\texport sdkman_debug_mode\n\tif [[ \"$sdkman_local_script_version\" == \"$sdkman_remote_script_version\" && \"$sdkman_local_native_version\" == \"$sdkman_remote_native_version\" && \"$force_selfupdate\" != \"force\" ]]; then\n\t\techo \"No update available at this time.\"\n\telif [[ \"$sdkman_beta_channel\" == \"true\" ]]; then\n\t\t__sdkman_secure_curl \"${SDKMAN_CANDIDATES_API}/selfupdate/beta/${SDKMAN_PLATFORM}\" | bash\n\telse\n\t\t__sdkman_secure_curl \"${SDKMAN_CANDIDATES_API}/selfupdate/stable/${SDKMAN_PLATFORM}\" | bash\n\tfi\n}\n"
  },
  {
    "path": "src/main/bash/sdkman-uninstall.sh",
    "content": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#       http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n\nfunction __sdk_uninstall() {\n\t__sdkman_deprecation_notice \"uninstall\"\n\tlocal candidate version current\n\n\tcandidate=\"$1\"\n\tversion=\"$2\"\n\t__sdkman_check_candidate_present \"$candidate\" || return 1\n\t__sdkman_check_version_present \"$version\" || return 1\n\n\tcurrent=$(readlink \"${SDKMAN_CANDIDATES_DIR}/${candidate}/current\" | sed \"s!${SDKMAN_CANDIDATES_DIR}/${candidate}/!!g\")\n\tif [[ -L \"${SDKMAN_CANDIDATES_DIR}/${candidate}/current\" && \"$version\" == \"$current\" ]]; then\n\t\techo \"\"\n\t\t__sdkman_echo_green \"Deselecting ${candidate} ${version}...\"\n\t\tunlink \"${SDKMAN_CANDIDATES_DIR}/${candidate}/current\"\n\tfi\n\n\techo \"\"\n\n\tif [ -d \"${SDKMAN_CANDIDATES_DIR}/${candidate}/${version}\" ]; then\n\t\t__sdkman_echo_green \"Uninstalling ${candidate} ${version}...\"\n\t\trm -rf \"${SDKMAN_CANDIDATES_DIR}/${candidate}/${version}\"\n\telse\n\t\t__sdkman_echo_red \"${candidate} ${version} is not installed.\"\n\tfi\n}\n"
  },
  {
    "path": "src/main/bash/sdkman-update.sh",
    "content": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#       http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n\nfunction __sdk_update() {\n\tlocal candidates_uri=\"${SDKMAN_CANDIDATES_API}/candidates/all\"\n\t__sdkman_echo_debug \"Using candidates endpoint: $candidates_uri\"\n\n\tlocal fresh_candidates_csv=$(__sdkman_secure_curl_with_timeouts \"$candidates_uri\")\n\n\t__sdkman_echo_debug \"Local candidates: $SDKMAN_CANDIDATES_CSV\"\n\t__sdkman_echo_debug \"Fetched candidates: $fresh_candidates_csv\"\n\n\tif [[ -n \"${fresh_candidates_csv}\" ]] && ! grep -iq 'html' <<< \"${fresh_candidates_csv}\"; then\n\t\t__sdkman_echo_debug \"Fresh and cached candidate lengths: ${#fresh_candidates_csv} ${#SDKMAN_CANDIDATES_CSV}\"\n\n\t\tlocal fresh_candidates combined_candidates diff_candidates\n\n\t\tif [[ \"${zsh_shell}\" == 'true' ]]; then\n\t\t\tfresh_candidates=(${(s:,:)fresh_candidates_csv})\n\t\telse\n\t\t\tIFS=',' read -a fresh_candidates <<< \"${fresh_candidates_csv}\"\n\t\tfi\n\n\t\tcombined_candidates=(\"${fresh_candidates[@]}\" \"${SDKMAN_CANDIDATES[@]}\")\n\n\t\tdiff_candidates=($(printf $'%s\\n' \"${combined_candidates[@]}\" | sort | uniq -u))\n\n\t\tif ((${#diff_candidates[@]})); then\n\t\t\tlocal delta\n\n\t\t\tdelta=(\"${fresh_candidates[@]}\" \"${diff_candidates[@]}\")\n\t\t\tdelta=($(printf $'%s\\n' \"${delta[@]}\" | sort | uniq -d))\n\t\t\tif ((${#delta[@]})); then\n\t\t\t\t__sdkman_echo_green \"\\nAdding new candidates(s): ${delta[*]}\"\n\t\t\tfi\n\n\t\t\tdelta=(\"${SDKMAN_CANDIDATES[@]}\" \"${diff_candidates[@]}\")\n\t\t\tdelta=($(printf $'%s\\n' \"${delta[@]}\" | sort | uniq -d))\n\t\t\tif ((${#delta[@]})); then\n\t\t\t\t__sdkman_echo_green \"\\nRemoving obsolete candidates(s): ${delta[*]}\"\n\t\t\tfi\n\n\t\t\techo \"${fresh_candidates_csv}\" >| \"${SDKMAN_CANDIDATES_CACHE}\"\n\t\t\t__sdkman_echo_yellow $'\\nPlease open a new terminal now...'\n\t\telse\n\t\t\ttouch \"${SDKMAN_CANDIDATES_CACHE}\"\n\t\t\t__sdkman_echo_green $'\\nNo new candidates found at this time.'\n\t\tfi\n\tfi\n}\n"
  },
  {
    "path": "src/main/bash/sdkman-upgrade.sh",
    "content": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#       http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n\nfunction __sdk_upgrade() {\n\tlocal all candidates candidate upgradable installed_count upgradable_count upgradable_candidates\n\n\tif [ -n \"$1\" ]; then\n\t\tall=false\n\t\tcandidates=$1\n\telse\n\t\tall=true\n\t\tif [[ \"$zsh_shell\" == 'true' ]]; then\n\t\t\tcandidates=(${SDKMAN_CANDIDATES[@]})\n\t\telse\n\t\t\tcandidates=${SDKMAN_CANDIDATES[@]}\n\t\tfi\n\tfi\n\n\tinstalled_count=0\n\tupgradable_count=0\n\techo \"\"\n\n\tfor candidate in ${candidates}; do\n\t\tupgradable=\"$(__sdkman_determine_upgradable_version \"$candidate\")\"\n\t\tcase $? in\n\t\t1)\n\t\t\t$all || __sdkman_echo_red \"Not using any version of ${candidate}\"\n\t\t\t;;\n\t\t2)\n\t\t\techo \"\"\n\t\t\t__sdkman_echo_red \"Stop! Could not get remote version of ${candidate}\"\n\t\t\treturn 1\n\t\t\t;;\n\t\t*)\n\t\t\tif [ -n \"$upgradable\" ]; then\n\t\t\t\t[ ${upgradable_count} -eq 0 ] && __sdkman_echo_no_colour \"Available defaults:\"\n\t\t\t\t__sdkman_echo_no_colour \"$upgradable\"\n\t\t\t\t((upgradable_count += 1))\n\t\t\t\tupgradable_candidates=(${upgradable_candidates[@]} $candidate)\n\t\t\tfi\n\t\t\t((installed_count += 1))\n\t\t\t;;\n\t\tesac\n\tdone\n\n\tif $all; then\n\t\tif [ ${installed_count} -eq 0 ]; then\n\t\t\t__sdkman_echo_no_colour 'No candidates are in use'\n\t\telif [ ${upgradable_count} -eq 0 ]; then\n\t\t\t__sdkman_echo_no_colour \"All candidates are up-to-date\"\n\t\tfi\n\telif [ ${upgradable_count} -eq 0 ]; then\n\t\t__sdkman_echo_no_colour \"${candidate} is up-to-date\"\n\tfi\n\n\tif [ ${upgradable_count} -gt 0 ]; then\n\t\techo \"\"\n\n\t\tif [[ \"$sdkman_auto_answer\" != 'true' ]]; then\n\t\t\t__sdkman_echo_confirm \"Use prescribed default version(s)? (Y/n): \"\n\t\t\tread UPGRADE_ALL\n\t\tfi\n\n\t\texport auto_answer_upgrade='true'\n\t\tif [[ -z \"$UPGRADE_ALL\" || \"$UPGRADE_ALL\" == \"y\" || \"$UPGRADE_ALL\" == \"Y\" ]]; then\n\t\t\t# Using array for bash & zsh compatibility\n\t\t\tfor ((i = 0; i <= ${#upgradable_candidates[*]}; i++)); do\n\t\t\t\tupgradable_candidate=\"${upgradable_candidates[${i}]}\"\n\t\t\t\t# Filter empty elements (in bash arrays are zero index based, in zsh they are 1 based)\n\t\t\t\tif [[ -n \"$upgradable_candidate\" ]]; then\n\t\t\t\t\t__sdk_install $upgradable_candidate\n\t\t\t\tfi\n\t\t\tdone\n\t\tfi\n\t\tunset auto_answer_upgrade\n\tfi\n}\n\nfunction __sdkman_determine_upgradable_version() {\n\tlocal candidate local_versions remote_default_version\n\n\tcandidate=\"$1\"\n\n\t# Resolve local versions\n\tlocal_versions=\"$(echo $(find \"${SDKMAN_CANDIDATES_DIR}/${candidate}\" -maxdepth 1 -mindepth 1 -type d -exec basename '{}' \\; 2> /dev/null) | sed -e \"s/ /, /g\")\"\n\tif [ ${#local_versions} -eq 0 ]; then\n\t\treturn 1\n\tfi\n\n\t# Resolve remote default version\n\tremote_default_version=\"$(__sdkman_secure_curl \"${SDKMAN_CANDIDATES_API}/candidates/default/${candidate}\")\"\n\tif [ -z \"$remote_default_version\" ]; then\n\t\treturn 2\n\tfi\n\n\t# Check upgradable or not\n\tif [ ! -d \"${SDKMAN_CANDIDATES_DIR}/${candidate}/${remote_default_version}\" ]; then\n\t\t__sdkman_echo_yellow \"${candidate} (local: ${local_versions}; default: ${remote_default_version})\"\n\tfi\n}\n"
  },
  {
    "path": "src/main/bash/sdkman-use.sh",
    "content": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#       http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n\nfunction __sdk_use() {\n\tlocal candidate version install\n\n\tcandidate=\"$1\"\n\tversion=\"$2\"\n\t__sdkman_check_version_present \"$version\" || return 1\n\t__sdkman_check_candidate_present \"$candidate\" || return 1\n\n\tif [[ ! -d \"${SDKMAN_CANDIDATES_DIR}/${candidate}/${version}\" ]]; then\n\t\techo \"\"\n\t\t__sdkman_echo_red \"Stop! Candidate version is not installed.\"\n\t\techo \"\"\n\t\t__sdkman_echo_yellow \"Tip: Run the following to install this version\"\n\t\techo \"\"\n\t\t__sdkman_echo_yellow \"$ sdk install ${candidate} ${version}\"\n\t\treturn 1\n\tfi\n\n\t# Just update the *_HOME and PATH for this shell.\n\t__sdkman_set_candidate_home \"$candidate\" \"$version\"\n\n\tif [[ $PATH =~ ${SDKMAN_CANDIDATES_DIR}/${candidate}/([^/]+) ]]; then\n\t\tlocal matched_version\n\n\t\tif [[ \"$zsh_shell\" == \"true\" ]]; then\n\t\t\tmatched_version=${match[1]}\n\t\telse\n\t\t\tmatched_version=${BASH_REMATCH[1]}\n\t\tfi\n\n\t\texport PATH=${PATH//${SDKMAN_CANDIDATES_DIR}\\/${candidate}\\/${matched_version}/${SDKMAN_CANDIDATES_DIR}\\/${candidate}\\/${version}}\n\tfi\n\n\tif [[ ! (-L \"${SDKMAN_CANDIDATES_DIR}/${candidate}/current\" || -d \"${SDKMAN_CANDIDATES_DIR}/${candidate}/current\") ]]; then\n\t\t__sdkman_echo_green \"Setting ${candidate} version ${version} as default.\"\n\t\t__sdkman_link_candidate_version \"$candidate\" \"$version\"\n\tfi\n\n\techo \"\"\n\t__sdkman_echo_green \"Using ${candidate} version ${version} in this shell.\"\n}\n"
  },
  {
    "path": "src/main/bash/sdkman-utils.sh",
    "content": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#       http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n\nfunction __sdkman_echo_debug() {\n\tif [[ \"$sdkman_debug_mode\" == 'true' ]]; then\n\t\techo \"$1\"\n\tfi\n}\n\nfunction __sdkman_secure_curl() {\n\tif [[ \"${sdkman_insecure_ssl}\" == 'true' ]]; then\n\t\tcurl --insecure --silent --location \"$1\"\n\telse\n\t\tcurl --silent --location \"$1\"\n\tfi\n}\n\nfunction __sdkman_secure_curl_download() {\n\tlocal curl_params\n\tcurl_params=('--progress-bar' '--location')\n\n\tif [[ \"${sdkman_debug_mode}\" == 'true' ]]; then\n\t\tcurl_params+=('--verbose')\n\tfi\n\n\tif [[ \"${sdkman_curl_continue}\" == 'true' ]]; then\n\t\tcurl_params+=('-C' '-')\n\tfi\n\n\tif [[ -n \"${sdkman_curl_retry_max_time}\" ]]; then\n\t\tcurl_params+=('--retry-max-time' \"${sdkman_curl_retry_max_time}\")\n\tfi\n\n\tif [[ -n \"${sdkman_curl_retry}\" ]]; then\n\t\tcurl_params+=('--retry' \"${sdkman_curl_retry}\")\n\tfi\n\n\tif [[ \"${sdkman_insecure_ssl}\" == 'true' ]]; then\n\t\tcurl_params+=('--insecure')\n\tfi\n\n\tcurl \"${curl_params[@]}\" \"${@}\"\n}\n\nfunction __sdkman_secure_curl_with_timeouts() {\n\tif [[ \"${sdkman_insecure_ssl}\" == 'true' ]]; then\n\t\tcurl --insecure --silent --location --connect-timeout ${sdkman_curl_connect_timeout} --max-time ${sdkman_curl_max_time} \"$1\"\n\telse\n\t\tcurl --silent --location --connect-timeout ${sdkman_curl_connect_timeout} --max-time ${sdkman_curl_max_time} \"$1\"\n\tfi\n}\n\nfunction __sdkman_echo_paged() {\n\tif [[ -n \"$PAGER\" ]]; then\n\t\techo \"$@\" | eval \"$PAGER\"\n\telif command -v less >& /dev/null; then\n\t\techo \"$@\" | less\n\telse\n\t\techo \"$@\"\n\tfi\n}\n\nfunction __sdkman_echo() {\n\tif [[ \"$sdkman_colour_enable\" == 'false' ]]; then\n\t\techo -e \"$2\"\n\telse\n\t\techo -e \"\\033[1;$1$2\\033[0m\"\n\tfi\n}\n\nfunction __sdkman_echo_red() {\n\t__sdkman_echo \"31m\" \"$1\"\n}\n\nfunction __sdkman_echo_no_colour() {\n\techo \"$1\"\n}\n\nfunction __sdkman_echo_yellow() {\n\t__sdkman_echo \"33m\" \"$1\"\n}\n\nfunction __sdkman_echo_green() {\n\t__sdkman_echo \"32m\" \"$1\"\n}\n\nfunction __sdkman_echo_cyan() {\n\t__sdkman_echo \"36m\" \"$1\"\n}\n\nfunction __sdkman_echo_confirm() {\n\tif [[ \"$sdkman_colour_enable\" == 'false' ]]; then\n\t\techo -n \"$1\"\n\telse\n\t\techo -e -n \"\\033[1;33m$1\\033[0m\"\n\tfi\n}\n\nfunction __sdkman_deprecation_notice() {\n\tlocal message=\"\n[Deprecation Notice]:\nThis legacy '$1' command is replaced by a native implementation\nand it will be removed in a future release.\nPlease follow the discussion here:\nhttps://github.com/sdkman/sdkman-cli/discussions/1332\"\n\n\tif [[ \"$sdkman_colour_enable\" == 'false' ]]; then\n\t\t__sdkman_echo_no_colour \"$message\"\n\telse\n\t\t__sdkman_echo_yellow \"$message\"\n\tfi\n}\n\n"
  },
  {
    "path": "src/main/bash/sdkman-version.sh",
    "content": "#!/usr/bin/env bash\n\n#\n#   Copyright 2021 Marco Vermeulen\n#\n#   Licensed under the Apache License, Version 2.0 (the \"License\");\n#   you may not use this file except in compliance with the License.\n#   You may obtain a copy of the License at\n#\n#       http://www.apache.org/licenses/LICENSE-2.0\n#\n#   Unless required by applicable law or agreed to in writing, software\n#   distributed under the License is distributed on an \"AS IS\" BASIS,\n#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n#   See the License for the specific language governing permissions and\n#   limitations under the License.\n#\n\nfunction __sdk_version() {\n\t__sdkman_deprecation_notice \"version\"\n    local version=$(cat $SDKMAN_DIR/var/version)\n\techo \"\"\n\t__sdkman_echo_yellow \"SDKMAN $version\"\n}\n"
  },
  {
    "path": "src/test/groovy/sdkman/cucumber/RunCukeTests.groovy",
    "content": "package sdkman.cucumber\n\nimport io.cucumber.junit.Cucumber\nimport io.cucumber.junit.CucumberOptions\nimport org.junit.runner.RunWith\n\n@RunWith(Cucumber)\n@CucumberOptions(\n\t\tstrict = true,\n\t\tfeatures = [\"src/test/resources/features\"],\n\t\tglue = [\"sdkman.steps\"],\n\t\ttags = [\"not @manual\", \"not @review\"]\n)\nclass RunCukeTests {}\n"
  },
  {
    "path": "src/test/groovy/sdkman/env/BashEnv.groovy",
    "content": "package sdkman.env\n\nimport groovy.transform.ToString\n\n/**\n * <p>As part of the sdkman test suite we need to launch a bash shell and execute\n * multiple commands in it. This is tricky to do using Java's support for\n * working with external processes as the API can't tell you when a command\n * has finished executing.</p>\n * <p>This class provides some hacks that allow you to serially execute commands\n * in an external bash process in a fairly reliable manner and to retrieve the\n * output of those commands.</p>\n */\n@ToString(includeNames = true)\nclass BashEnv {\n\n\tstatic final PROMPT = \"\"\n\tstatic final EXIT_CODE_CMD = 'echo \"Exit code is: $?\"'\n\tstatic final EXIT_CODE_PATTERN = ~/Exit code is: (\\d+)\\s*${PROMPT}?$/\n\n\tprivate final Object outputLock = new Object()\n\n\tdef exitCode\n\tdef process\n\tdef processOutput = new StringBuilder()\n\tdef commandOutput\n\n\t// Command timeout in milliseconds\n\tdef timeout = 5000\n\tdef workDir\n\tdef env\n\n\tBashEnv(workDir, Map env) {\n\t\tthis.workDir = workDir as File\n\n\t\tdef basicPath = \"/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/run/current-system/sw/bin\"\n\t\tdef localBinDir = \"${workDir}/bin\"\n\n\t\tdef modifiedPath = \"$localBinDir:$basicPath\"\n\n\t\tenv = env + [PS1: PROMPT, PATH: modifiedPath]\n\t\tthis.env = env.collect { k, v -> k + '=' + v }\n\t}\n\n\t/**\n\t * Starts the external bash process.\n\t */\n\tvoid start() {\n\t\tprocess = [\"bash\", \"--noprofile\", \"--norc\", \"--posix\", \"-i\", \"-o\", \"noclobber\"].execute(env, workDir)\n\n\t\tconsumeProcessStream(process.inputStream)\n\t\tconsumeProcessStream(process.errorStream)\n\t}\n\n\t/**\n\t * Stops the external bash process and waits for it to finish.\n\t */\n\tvoid stop() {\n\t\texecute(\"exit\")\n\t\tprocess.waitFor()\n\t}\n\n\t/**\n\t * Sends a command line to the external bash process and returns once the\n\t * command has finished executing. If the command is interactive and requires\n\t * input during it's execution (for example a y/n answer to a question) you\n\t * can provide that input as a list of strings.\n\t */\n\tvoid execute(String cmdline, List inputs = []) {\n\t\tresetOutput()\n\n\t\tif (cmdline != \"exit\") {\n\t\t\texitCode = null\n\t\t}\n\n\t\tprocess.outputStream << cmdline << \"\\n\"\n\t\tprocess.outputStream.flush()\n\n\t\tif (cmdline != \"exit\") {\n\t\t\tfor (input in inputs) {\n\t\t\t\tprocess.outputStream << input << \"\\n\"\n\t\t\t}\n\t\t\tprocess.outputStream << EXIT_CODE_CMD << \"\\n\"\n\t\t\tprocess.outputStream.flush()\n\t\t}\n\n\t\tdef start = System.currentTimeMillis()\n\t\twhile (cmdline != \"exit\") {\n\t\t\tThread.sleep 100\n\n\t\t\tsynchronized (outputLock) {\n\t\t\t\t// Remove all the extraneous text that's not related to the\n\t\t\t\t// command's output. This includes the command string itself,\n\t\t\t\t// the 'echo' command to display the command's exit code, and\n\t\t\t\t// the exit code line.\n\t\t\t\tremoveFromOutput(cmdline + \"\\n\")\n\t\t\t\tremoveFromOutput(PROMPT + EXIT_CODE_CMD + \"\\n\")\n\n\t\t\t\tdef str = processOutput.toString()\n\t\t\t\tdef m = EXIT_CODE_PATTERN.matcher(str)\n\t\t\t\tif (m) {\n\t\t\t\t\texitCode = m[0][1]\n\n\t\t\t\t\t// Remove this exit code line from the output.\n\t\t\t\t\tcommandOutput = m.replaceAll('')\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\t// If the command times out, we should break out of the loop and\n\t\t\t\t// display whatever output has already been produced.\n\t\t\t\tif (System.currentTimeMillis() - start > timeout) {\n\t\t\t\t\tcommandOutput = \"ALERT! Command timed out. Last output was:\\n\\n${processOutput}\"\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Returns the exit code of the last command that was executed.\n\t */\n\tint getStatus() {\n\t\tif (!exitCode) throw new IllegalStateException(\"Did you run execute() before getting the status?\")\n\t\treturn exitCode.toInteger()\n\t}\n\n\t/**\n\t * Returns the text output (both stdout and stderr) of the last command\n\t * that was executed.\n\t */\n\tString getOutput() {\n\t\treturn commandOutput\n\t}\n\n\t/**\n\t * Clears the saved command output.\n\t */\n\tvoid resetOutput() {\n\t\tsynchronized (outputLock) {\n\t\t\tprocessOutput = new StringBuilder()\n\t\t}\n\t}\n\n\tprivate void consumeProcessStream(final InputStream stream) {\n\t\tchar[] buffer = new char[256]\n\t\tThread.start {\n\t\t\tdef reader = new InputStreamReader(stream)\n\t\t\tdef charsRead = 0\n\t\t\twhile (charsRead != -1) {\n\t\t\t\tcharsRead = reader.read(buffer, 0, 256)\n\t\t\t\tif (charsRead > 0) {\n\t\t\t\t\tsynchronized (outputLock) {\n\t\t\t\t\t\tprocessOutput.append(buffer, 0, charsRead)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void removeFromOutput(String line) {\n\t\tsynchronized (outputLock) {\n\t\t\tdef pos = processOutput.indexOf(line)\n\t\t\tif (pos != -1) {\n\t\t\t\tprocessOutput.delete(pos, pos + line.size() - 1)\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/test/groovy/sdkman/env/CleanBashEnvBuilder.groovy",
    "content": "package sdkman.env\n\nclass CleanBashEnvBuilder {\n\n\tprivate final File baseFolder\n\tString httpProxy\n\n\tstatic CleanBashEnvBuilder create(File baseFolder) {\n\t\tnew CleanBashEnvBuilder(baseFolder)\n\t}\n\n\tprivate CleanBashEnvBuilder(File baseFolder) {\n\t\tthis.baseFolder = baseFolder\n\t}\n\n\tCleanBashEnvBuilder withHttpProxy(String httpProxy) {\n\t\tthis.httpProxy = httpProxy\n\t\tthis\n\t}\n\n\tBashEnv build() {\n\t\tdef env = [HOME: baseFolder.absolutePath]\n\t\tif (httpProxy) {\n\t\t\tenv.put(\"http_proxy\", httpProxy)\n\t\t}\n\t\tnew BashEnv(baseFolder.absolutePath, env)\n\t}\n}\n"
  },
  {
    "path": "src/test/groovy/sdkman/env/SdkmanBashEnvBuilder.groovy",
    "content": "package sdkman.env\n\nimport groovy.transform.ToString\nimport sdkman.stubs.CurlStub\nimport sdkman.stubs.UnameStub\nimport sdkman.support.UnixUtils\n\n@ToString(includeNames = true)\nclass SdkmanBashEnvBuilder {\n\n\tfinal BUILD_STAGE_DIR = \"build/stage/sdkman-latest+hashme\"\n\tfinal BUILD_BIN_DIR = \"$BUILD_STAGE_DIR/bin\"\n\tfinal BUILD_SRC_DIR = \"$BUILD_STAGE_DIR/src\"\n\tfinal BUILD_COMPLETION_DIR = \"$BUILD_STAGE_DIR/contrib/completion/bash\"\n\n\t//mandatory fields\n\tprivate final File baseFolder\n\n\t//optional fields with sensible defaults\n\tprivate Optional<CurlStub> curlStub = Optional.empty()\n\tprivate Optional<UnameStub> unameStub = Optional.empty()\n\tprivate List candidates = ['groovy', 'grails', 'java']\n\tprivate String platform = UnixUtils.inferPlatform()\n\tprivate boolean offlineMode = false\n\tprivate String candidatesApi = \"http://localhost:8080/2\"\n\tprivate String brokerApi = \"http://localhost:8080/2\"\n\tprivate String jdkHome = \"/path/to/my/jdk\"\n\tprivate String httpProxy\n\tprivate String scriptVersion\n\tprivate String nativeVersion\n\tprivate boolean debugMode = true\n\n\tMap config = [\n\t\t\tsdkman_auto_answer : 'false',\n\t\t\tsdkman_beta_channel: 'false',\n\t\t\tsdkman_native_enable: 'false',\n\t\t\tsdkman_selfupdate_feature: 'true'\n\t]\n\n\tFile sdkmanDir, sdkmanBinDir, sdkmanVarDir, sdkmanSrcDir, sdkmanEtcDir, sdkmanExtDir,\n\t\t sdkmanTmpDir, sdkmanCandidatesDir, sdkmanMetadataDir, sdkmanContribDir\n\t\n\tstatic SdkmanBashEnvBuilder create(File baseFolder) {\n\t\tnew SdkmanBashEnvBuilder(baseFolder)\n\t}\n\n\tprivate SdkmanBashEnvBuilder(File baseFolder) {\n\t\tthis.baseFolder = baseFolder\n\t}\n\n\tSdkmanBashEnvBuilder withCurlStub(CurlStub curlStub) {\n\t\tthis.curlStub = Optional.of(curlStub)\n\t\tthis\n\t}\n\n\tSdkmanBashEnvBuilder withUnameStub(UnameStub unameStub) {\n\t\tthis.unameStub = Optional.of(unameStub)\n\t\tthis\n\t}\n\t\n\tSdkmanBashEnvBuilder withPlatform(String platform) {\n\t\tthis.platform = platform\n\t\tthis\n\t}\n\n\tSdkmanBashEnvBuilder withCandidates(List candidates) {\n\t\tthis.candidates = candidates\n\t\tthis\n\t}\n\n\tSdkmanBashEnvBuilder withConfiguration(String key, String value) {\n\t\tconfig.put key, value\n\t\tthis\n\t}\n\n\tSdkmanBashEnvBuilder withOfflineMode(boolean offlineMode) {\n\t\tthis.offlineMode = offlineMode\n\t\tthis\n\t}\n\n\tSdkmanBashEnvBuilder withCandidatesApi(String service) {\n\t\tthis.candidatesApi = service\n\t\tthis\n\t}\n\n\tSdkmanBashEnvBuilder withBrokerApi(String service) {\n\t\tthis.brokerApi = service\n\t\tthis\n\t}\n\n\tSdkmanBashEnvBuilder withJdkHome(String jdkHome) {\n\t\tthis.jdkHome = jdkHome\n\t\tthis\n\t}\n\n\tSdkmanBashEnvBuilder withHttpProxy(String httpProxy) {\n\t\tthis.httpProxy = httpProxy\n\t\tthis\n\t}\n\n\tSdkmanBashEnvBuilder withScriptVersion(String version) {\n\t\tthis.scriptVersion = version\n\t\tthis\n\t}\n\n\tSdkmanBashEnvBuilder withNativeVersion(String version) {\n\t\tthis.nativeVersion = version\n\t\tthis\n\t}\n\n\tSdkmanBashEnvBuilder withDebugMode(boolean debugMode) {\n\t\tthis.debugMode = debugMode\n\t\tthis\n\t}\n\n\tBashEnv build() {\n\t\tsdkmanDir = prepareDirectory(baseFolder, \".sdkman\")\n\t\tsdkmanBinDir = prepareDirectory(sdkmanDir, \"bin\")\n\t\tsdkmanVarDir = prepareDirectory(sdkmanDir, \"var\")\n\t\tsdkmanSrcDir = prepareDirectory(sdkmanDir, \"src\")\n\t\tsdkmanEtcDir = prepareDirectory(sdkmanDir, \"etc\")\n\t\tsdkmanExtDir = prepareDirectory(sdkmanDir, \"ext\")\n\t\tsdkmanTmpDir = prepareDirectory(sdkmanDir, \"tmp\")\n\t\tsdkmanCandidatesDir = prepareDirectory(sdkmanDir, \"candidates\")\n\t\tsdkmanMetadataDir = prepareDirectory(sdkmanVarDir, \"metadata\")\n\t\tsdkmanContribDir = prepareDirectory(sdkmanDir, \"contrib\")\n\n\t\tcurlStub.map { it.build() }\n\t\tunameStub.map { it.build() }\n\n\t\tinitializeConfiguration(sdkmanEtcDir, config)\n\t\tinitializeCandidates(sdkmanCandidatesDir, candidates)\n\t\tinitializeCandidatesCache(sdkmanVarDir, candidates)\n\t\tinitializePlatformDescriptor(sdkmanVarDir, platform)\n\t\tinitializeScriptVersionFile(sdkmanVarDir, scriptVersion)\n\t\tinitializeNativeVersionFile(sdkmanVarDir, nativeVersion)\n\n\t\tprimeInitScript(sdkmanBinDir)\n\t\tprimeModuleScripts(sdkmanSrcDir)\n\t\tprimeBashCompletionScript(sdkmanContribDir)\n\n\t\tdef env = [\n\t\t\t\tSDKMAN_DIR           : sdkmanDir.absolutePath,\n\t\t\t\tSDKMAN_CANDIDATES_DIR: sdkmanCandidatesDir.absolutePath,\n\t\t\t\tSDKMAN_OFFLINE_MODE  : \"$offlineMode\",\n\t\t\t\tSDKMAN_CANDIDATES_API: candidatesApi,\n\t\t\t\tSDKMAN_BROKER_API    : brokerApi,\n\t\t\t\tsdkman_debug_mode    : Boolean.toString(debugMode),\n\t\t\t\tJAVA_HOME            : jdkHome\n\t\t]\n\n\t\tif (httpProxy) {\n\t\t\tenv.put(\"http_proxy\", httpProxy)\n\t\t}\n\n\t\tnew BashEnv(baseFolder.absolutePath, env)\n\t}\n\n\tprivate prepareDirectory(File target, String directoryName) {\n\t\tdef directory = new File(target, directoryName)\n\t\tdirectory.mkdirs()\n\t\tdirectory\n\t}\n\n\tprivate initializeScriptVersionFile(File folder, String version) {\n\t\tif (version) {\n\t\t\tnew File(folder, \"version\") << version\n\t\t}\n\t}\n\n\tprivate initializeNativeVersionFile(File folder, String version) {\n\t\tif (version) {\n\t\t\tnew File(folder, \"version_native\") << version\n\t\t}\n\t}\n\n\tprivate initializeCandidates(File folder, List candidates) {\n\t\tcandidates.each { candidate ->\n\t\t\tnew File(folder, candidate).mkdirs()\n\t\t}\n\t}\n\n\tprivate initializeCandidatesCache(File folder, List candidates) {\n\t\tdef candidatesCache = new File(folder, \"candidates\")\n\t\tif (candidates) {\n\t\t\tcandidatesCache << candidates.join(\",\")\n\t\t} else {\n\t\t\tcandidatesCache << \"\"\n\t\t}\n\t}\n\t\n\tprivate initializePlatformDescriptor(File folder, String platform) {\n\t\tdef platformDescriptor = new File(folder, \"platform\")\n\t\tplatformDescriptor << platform\n\t}\n\n\tprivate initializeConfiguration(File targetFolder, Map config) {\n\t\tdef configFile = new File(targetFolder, \"config\")\n\t\tconfig.each { key, value ->\n\t\t\tconfigFile << \"$key=$value\\n\"\n\t\t}\n\t}\n\n\tprivate primeInitScript(File targetFolder) {\n\t\tdef sourceInitScript = new File(BUILD_BIN_DIR, 'sdkman-init.sh')\n\n\t\tif (!sourceInitScript.exists())\n\t\t\tthrow new IllegalStateException(\"sdkman-init.sh has not been prepared for consumption.\")\n\n\t\tdef destInitScript = new File(targetFolder, \"sdkman-init.sh\")\n\t\tdestInitScript << sourceInitScript.text\n\t\tdestInitScript\n\t}\n\n\tprivate primeBashCompletionScript(File targetFolder) {\n\t\tdef sourceCompletionScript = new File(BUILD_COMPLETION_DIR, 'sdk')\n\n\t\tif (!sourceCompletionScript.exists())\n\t\t\tthrow new IllegalStateException(\"sdk has not been prepared for consumption.\")\n\n\t\tnew FileTreeBuilder(targetFolder).with {\n\t\t\tcompletion {\n\t\t\t\tbash {\n\t\t\t\t\tsdk(sourceCompletionScript.text)\n\t\t\t\t}\n\t\t\t}\t\t\t\n\t\t}\n\t}\n\n\tprivate primeModuleScripts(File targetFolder) {\n\t\tfor (f in new File(BUILD_SRC_DIR).listFiles()) {\n\t\t\tif (!(f.name in ['selfupdate.sh', 'install.sh', 'sdkman-init.sh'])) {\n\t\t\t\tnew File(targetFolder, f.name) << f.text\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/test/groovy/sdkman/specs/CandidatesCacheUpdateFailureSpec.groovy",
    "content": "package sdkman.specs\n\nimport sdkman.support.SdkmanEnvSpecification\n\nclass CandidatesCacheUpdateFailureSpec extends SdkmanEnvSpecification {\n\n\tstatic final String CANDIDATES_API = \"http://localhost:8080/2\"\n\n\tstatic final String HEALTHCHECK_ENDPOINT = \"$CANDIDATES_API/healthcheck\"\n\tstatic final String CANDIDATES_ALL_ENDPOINT = \"$CANDIDATES_API/candidates/all\"\n\n\tFile candidatesCache\n\n\tdef setup() {\n\t\tcandidatesCache = new File(\"${sdkmanDotDirectory}/var\", \"candidates\")\n\t\tcurlStub.primeWith(HEALTHCHECK_ENDPOINT, \"echo dbfb025be9f97fda2052b5febcca0155\")\n\t\t\t\t.primeWith(CANDIDATES_ALL_ENDPOINT, \"echo html\")\n\t\tsdkmanBashEnvBuilder.withConfiguration(\"sdkman_debug_mode\", \"true\")\n\t}\n\n\tvoid \"should not update candidates if error downloading candidate list\"() {\n\t\tgiven:\n\t\tbash = sdkmanBashEnvBuilder\n\t\t\t\t.withCandidates([])\n\t\t\t\t.build()\n\n\t\tand:\n\t\tbash.start()\n\n\t\twhen:\n\t\tbash.execute(\"source $bootstrapScript\")\n\t\tbash.execute(\"sdk update\")\n\n\t\tthen:\n\t\t!bash.output.contains('Fresh and cached candidate lengths')\n\t}\n}\n"
  },
  {
    "path": "src/test/groovy/sdkman/specs/CandidatesCacheUpdateSpec.groovy",
    "content": "package sdkman.specs\n\nimport sdkman.support.SdkmanEnvSpecification\n\nclass CandidatesCacheUpdateSpec extends SdkmanEnvSpecification {\n\n\tstatic final String CANDIDATES_API = \"http://localhost:8080/2\"\n\n\tstatic final String HEALTHCHECK_ENDPOINT = \"$CANDIDATES_API/healthcheck\"\n\tstatic final String CANDIDATES_ALL_ENDPOINT = \"$CANDIDATES_API/candidates/all\"\n\n\tFile candidatesCache\n\n\tdef setup() {\n\t\tcandidatesCache = new File(\"${sdkmanDotDirectory}/var\", \"candidates\")\n\t\tcurlStub.primeWith(HEALTHCHECK_ENDPOINT, \"echo dbfb025be9f97fda2052b5febcca0155\")\n\t\t\t\t.primeWith(CANDIDATES_ALL_ENDPOINT, \"echo groovy,scala\")\n\t\tsdkmanBashEnvBuilder.withConfiguration(\"sdkman_debug_mode\", \"true\")\n\t}\n\n\tvoid \"should issue a warning and escape if cache is empty\"() {\n\t\tgiven:\n\t\tbash = sdkmanBashEnvBuilder\n\t\t\t\t.withCandidates([])\n\t\t\t\t.build()\n\n\t\tand:\n\t\tbash.start()\n\n\t\twhen:\n\t\tbash.execute(\"source $bootstrapScript\")\n\t\tbash.execute(\"sdk version\")\n\n\t\tthen:\n\t\tbash.output.contains('WARNING: Cache is corrupt. SDKMAN cannot be used until updated.')\n\t\tbash.output.contains('$ sdk update')\n\n\t\tand:\n\t\t!bash.output.contains(\"SDKMAN 5.0.0\")\n\t}\n\n\tvoid \"should log a success message if cache exists\"() {\n\t\tgiven:\n\t\tbash = sdkmanBashEnvBuilder\n\t\t\t\t.withCandidates(['groovy'])\n\t\t\t\t.build()\n\n\t\tand:\n\t\tbash.start()\n\n\t\twhen:\n\t\tbash.execute(\"source $bootstrapScript\")\n\t\tbash.execute(\"sdk help\")\n\n\t\tthen:\n\t\tbash.output.contains('Using existing cache')\n\t}\n\n\tvoid \"should bypass cache check if update command issued\"() {\n\t\tgiven:\n\t\tbash = sdkmanBashEnvBuilder\n\t\t\t\t.withCandidates([])\n\t\t\t\t.build()\n\n\t\tand:\n\t\tbash.start()\n\n\t\twhen:\n\t\tbash.execute(\"source $bootstrapScript\")\n\t\tbash.execute(\"sdk update\")\n\n\t\tthen:\n\t\tbash.output.contains('Adding new candidates(s): groovy scala')\n\n\t\tand:\n\t\tcandidatesCache.text.trim() == \"groovy,scala\"\n\t}\n}\n"
  },
  {
    "path": "src/test/groovy/sdkman/specs/CompletionSpec.groovy",
    "content": "package sdkman.specs\n\nimport sdkman.support.SdkmanEnvSpecification\n\nclass CompletionSpec extends SdkmanEnvSpecification {\n\tstatic final String CANDIDATES_API = \"http://localhost:8080/2\"\n\n\tdef \"should complete the list of commands\"() {\n\t\tgiven:\n\t\tbash = sdkmanBashEnvBuilder\n\t\t\t\t.withConfiguration(\"sdkman_auto_complete\", \"true\")\n\t\t\t\t.build()\n\n\t\tbash.start()\n\t\tbash.execute(\"source $bootstrapScript\")\n\n\t\twhen:\n\t\tbash.execute(\"COMP_CWORD=1; COMP_WORDS=(sdk); _sdk\")\n\t\tbash.execute('echo \"\\${COMPREPLY[@]}\"')\n\n\t\tthen:\n\t\tbash.output.contains(\"install uninstall list use config default home env current upgrade version help offline selfupdate update flush\")\n\t}\n\n\tdef \"should complete the list of candidates\"() {\n\t\tgiven:\n\t\tbash = sdkmanBashEnvBuilder\n\t\t\t\t.withCandidates([\"java\", \"groovy\"])\n\t\t\t\t.withConfiguration(\"sdkman_auto_complete\", \"true\")\n\t\t\t\t.build()\n\n\t\tbash.start()\n\t\tbash.execute(\"source $bootstrapScript\")\n\n\t\twhen:\n\t\tbash.execute(\"COMP_CWORD=2; COMP_WORDS=(sdk install); _sdk\")\n\t\tbash.execute('echo \"\\${COMPREPLY[@]}\"')\n\n\t\tthen:\n\t\tbash.output.contains(\"java groovy\")\n\t}\n\n\tdef \"should complete the list of Java versions\"() {\n\t\tgiven:\n\t\tcurlStub.primeWith(\"$CANDIDATES_API/candidates/java/darwinx64/versions/all\", \"echo 16.0.1.hs-adpt,17.0.0-tem\")\n\n\t\tunameStub.forKernel(\"Darwin\").forMachine(\"x86_64\")\n\n\t\tbash = sdkmanBashEnvBuilder\n\t\t\t\t.withConfiguration(\"sdkman_auto_complete\", \"true\")\n\t\t\t\t.withUnameStub(unameStub)\n\t\t\t\t.withPlatform(\"darwinx64\")\n\t\t\t\t.build()\n\n\t\tbash.start()\n\t\tbash.execute(\"source $bootstrapScript\")\n\n\t\twhen:\n\t\tbash.execute(\"COMP_CWORD=3; COMP_WORDS=(sdk install java); _sdk\")\n\t\tbash.execute('echo \"\\${COMPREPLY[@]}\"')\n\n\t\tthen:\n\t\tbash.output.contains(\"16.0.1.hs-adpt 17.0.0-tem\")\n\t}\n}\n"
  },
  {
    "path": "src/test/groovy/sdkman/specs/ConfigCommandSpec.groovy",
    "content": "package sdkman.specs\n\nimport sdkman.support.SdkmanEnvSpecification\n\nclass ConfigCommandSpec extends SdkmanEnvSpecification {\n\tdef \"it should open the config in the system's default editor\"() {\n\t\tgiven:\n\t\tbash = sdkmanBashEnvBuilder\n\t\t\t\t.withOfflineMode(true)\n\t\t\t\t.build()\n\n\t\tbash.start()\n\t\tbash.execute(\"source $bootstrapScript\")\n\n\t\twhen:\n\t\tsetupEnv(bash)\n\t\tbash.execute(\"sdk config\")\n\n\t\tthen:\n\t\tverifyOutput(bash.output, sdkmanBaseDirectory)\n\n\t\twhere:\n\t\tsetupEnv << [\n\t\t\t{\n\t\t\t\tit.execute('nano() { echo \"nano was called with $*\"; }')\n\t\t\t\tit.execute(\"EDITOR=nano\")\n\t\t\t},\n\t\t\t{\n\t\t\t\tit.execute('code() { echo \"code was called with $*\"; }')\n\t\t\t\tit.execute(\"EDITOR='code --wait'\")\n\t\t\t},\n\t\t\t{\n\t\t\t\tit.execute('vi() { echo \"vi was called with $*\"; }')\n\t\t\t\tit.execute(\"unset EDITOR\")\n\t\t\t},\n\t\t\t{\n\t\t\t\tit.execute(\"EDITOR=/does/not/exist\")\n\t\t\t}\n\t\t]\n\t\tverifyOutput << [\n\t\t\t{ output, baseDirectory -> output.contains(\"nano was called with ${baseDirectory}/.sdkman/etc/config\") },\n\t\t\t{ output, baseDirectory -> output.contains(\"code was called with --wait ${baseDirectory}/.sdkman/etc/config\") },\n\t\t\t{ output, baseDirectory -> output.contains(\"vi was called with ${baseDirectory}/.sdkman/etc/config\") },\n\t\t\t{ output, _ -> output.contains(\"No default editor configured.\") }\n\t\t]\n\t}\n}\n"
  },
  {
    "path": "src/test/groovy/sdkman/specs/CurrentCommandSpec.groovy",
    "content": "package sdkman.specs\n\nimport sdkman.support.SdkmanEnvSpecification\n\nimport java.nio.file.Paths\n\nimport static java.nio.file.Files.createSymbolicLink\n\nclass CurrentCommandSpec extends SdkmanEnvSpecification {\n\n\tstatic final CANDIDATES_API = \"http://localhost:8080/2\"\n\tstatic final HEALTHCHECK_ENDPOINT = \"$CANDIDATES_API/healthcheck\"\n\n\tdef setup() {\n\t\tcurlStub.primeWith(HEALTHCHECK_ENDPOINT, \"echo dbfb025be9f97fda2052b5febcca0155\")\n\t}\n\n\tvoid \"should display current version of all candidates installed\"() {\n\t\tgiven:\n\t\tdef installedCandidates = [\n\t\t\t\t\"gradle\": \"2.7\",\n\t\t\t\t\"groovy\": \"2.4.4\",\n\t\t\t\t\"vertx\" : \"3.0.0\"\n\t\t]\n\t\tdef allCandidates = [\n\t\t\t\t\"asciidoctorj\",\n\t\t\t\t\"crash\",\n\t\t\t\t\"gaiden\",\n\t\t\t\t\"glide\",\n\t\t\t\t\"gradle\",\n\t\t\t\t\"grails\",\n\t\t\t\t\"griffon\",\n\t\t\t\t\"groovy\",\n\t\t\t\t\"groovyserv\",\n\t\t\t\t\"jbake\",\n\t\t\t\t\"jbossforge\",\n\t\t\t\t\"lazybones\",\n\t\t\t\t\"springboot\",\n\t\t\t\t\"vertx\"\n\t\t]\n\n\t\tbash = sdkmanBashEnvBuilder\n\t\t\t\t.withOfflineMode(false)\n\t\t\t\t.withCandidates(installedCandidates.keySet().toList())\n\t\t\t\t.build()\n\n\t\tprepareFoldersFor(installedCandidates)\n\n\t\tbash.start()\n\t\tbash.execute(\"source $bootstrapScript\")\n\n\t\twhen:\n\t\tbash.execute('sdk current')\n\n\t\tthen:\n\t\tbash.output.contains(\"Using:\")\n\t\tbash.output.contains(\"groovy: 2.4.4\")\n\t\tbash.output.contains(\"gradle: 2.7\")\n\t\tbash.output.contains(\"vertx: 3.0.0\")\n\t}\n\n\tprivate prepareFoldersFor(Map installedCandidates) {\n\t\tinstalledCandidates.forEach { candidate, version ->\n\t\t\tdef candidateVersionDirectory = \"$candidatesDirectory/$candidate/$version\"\n\t\t\tdef candidateVersionBinDirectory = \"$candidateVersionDirectory/bin\"\n\t\t\tnew File(candidateVersionBinDirectory).mkdirs()\n\t\t\tdef candidateVersionPath = Paths.get(candidateVersionDirectory)\n\t\t\tdef symlink = Paths.get(\"$candidatesDirectory/$candidate/current\")\n\t\t\tcreateSymbolicLink(symlink, candidateVersionPath)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/test/groovy/sdkman/specs/EnvCommandSpec.groovy",
    "content": "package sdkman.specs\n\nimport sdkman.support.SdkmanEnvSpecification\n\nimport java.nio.file.Paths\n\nimport static java.nio.file.Files.createSymbolicLink\n\nclass EnvCommandSpec extends SdkmanEnvSpecification {\n\tstatic final String CANDIDATES_API = \"http://localhost:8080/2\"\n\n\tstatic final String CANDIDATES_DEFAULT_JAVA = \"$CANDIDATES_API/candidates/default/java\"\n\n\tdef \"should generate .sdkmanrc when called with 'init'\"() {\n\t\tgiven:\n\t\tcurlStub.primeWith(CANDIDATES_DEFAULT_JAVA, \"echo 11.0.6.hs-adpt\")\n\n\t\tsetupCandidates(candidatesDirectory)\n\n\t\tbash = sdkmanBashEnvBuilder\n\t\t\t.withOfflineMode(true)\n\t\t\t.build()\n\n\t\tbash.start()\n\t\tbash.execute(\"source $bootstrapScript\")\n\n\t\twhen:\n\t\tbash.execute(\"sdk env init\")\n\n\t\tthen:\n\t\tnew File(bash.workDir, '.sdkmanrc').text.contains(expected)\n\n\t\twhere:\n\t\tsetupCandidates << [\n\t\t\t{ directory ->\n\t\t\t\tnew FileTreeBuilder(directory).with {\n\t\t\t\t\t\"java\" {\n\t\t\t\t\t\t\"8.0.252.hs\" {\n\t\t\t\t\t\t\t\"bin\" {}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tcreateSymbolicLink(Paths.get(\"$directory/java/current\"), Paths.get(\"$directory/java/8.0.252.hs\"))\n\t\t\t},\n\t\t\t{} // NOOP\n\t\t]\n\t\texpected << [\"java=8.0.252.hs\\n\", \"java=11.0.6.hs-adpt\\n\"]\n\t}\n\n\tdef \"should use the candidates contained in .sdkmanrc\"() {\n\t\tgiven:\n\t\tnew FileTreeBuilder(candidatesDirectory).with {\n\t\t\t\"grails\" {\n\t\t\t\t\"2.1.0\" {}\n\t\t\t}\n\t\t\t\"groovy\" {\n\t\t\t\t\"2.4.1\" {}\n\t\t\t}\n\t\t}\n\n\t\tbash = sdkmanBashEnvBuilder\n\t\t\t.withOfflineMode(true)\n\t\t\t.build()\n\n\t\tnew File(bash.workDir, '.sdkmanrc').text = sdkmanrc\n\n\t\tbash.start()\n\t\tbash.execute(\"source $bootstrapScript\")\n\n\t\twhen:\n\t\tbash.execute(\"sdk env\")\n\n\t\tthen:\n\t\tverifyAll(bash.output) {\n\t\t\tcontains(\"Using groovy version 2.4.1 in this shell.\")\n\t\t\tcontains(\"Using grails version 2.1.0 in this shell.\")\n\t\t}\n\n\t\twhere:\n\t\tsdkmanrc << [\n\t\t\t\"grails=2.1.0\\ngroovy=2.4.1\",\n\t\t\t\"grails=2.1.0\\ngroovy=2.4.1\\n\",\n\t\t\t\"  grails=2.1.0\\ngroovy=2.4.1\\n\",\n\t\t\t\"grails=2.1.0\t\\ngroovy=2.4.1\\n\",\n\t\t\t\"grails=2.1.0\\ngroovy = 2.4.1\\n\",\n\t\t]\n\t}\n\n\tdef \"should execute 'sdk env' when entering a directory with an .sdkmanrc\"() {\n\t\tgiven:\n\t\tnew FileTreeBuilder(candidatesDirectory).with {\n\t\t\t\"groovy\" {\n\t\t\t\t\"2.4.1\" {}\n\t\t\t}\n\t\t}\n\n\t\tbash = sdkmanBashEnvBuilder\n\t\t\t.withOfflineMode(true)\n\t\t\t.withConfiguration(\"sdkman_auto_env\", sdkmanAutoEnv)\n\t\t\t.build()\n\n\t\tnew FileTreeBuilder(bash.workDir).with {\n\t\t\t\"project\" {\n\t\t\t\t\".sdkmanrc\"(\"groovy=2.4.1\\n\")\t\n\t\t\t}\n\t\t}\n\n\t\tbash.start()\n\t\tbash.execute(\"source $bootstrapScript\")\t\t\n\n\t\twhen:\n\t\tbash.execute(\"cd project\")\n\n\t\tthen:\n\t\tverifyOutput(bash.output)\n\n\t\twhere:\n\t\tsdkmanAutoEnv | verifyOutput\n\t\t'true'\t\t  | { it.contains(\"Using groovy version 2.4.1 in this shell\") }\n\t\t'false'       | { !it.contains(\"Using groovy version 2.4.1 in this shell\") }\n\t}\n\t\n\tdef \"should not execute 'sdk env' when already being in a directory with an .sdkmanrc\"() {\n\t\tgiven:\n\t\tnew FileTreeBuilder(candidatesDirectory).with {\n\t\t\t\"groovy\" {\n\t\t\t\t\"2.4.1\" {}\n\t\t\t}\n\t\t}\n\n\t\tbash = sdkmanBashEnvBuilder\n\t\t\t.withOfflineMode(true)\n\t\t\t.withConfiguration(\"sdkman_auto_env\", \"true\")\n\t\t\t.build()\n\n\t\tnew FileTreeBuilder(bash.workDir).with {\n\t\t\t\"project\" {\n\t\t\t\t\".sdkmanrc\"(\"groovy=2.4.1\\n\")\n\t\t\t}\n\t\t}\n\n\t\tbash.start()\n\t\tbash.execute(\"source $bootstrapScript\")\n\n\t\twhen:\n\t\tbash.execute(\"cd project\")\n\t\tbash.execute(\"ls\")\n\n\t\tthen:\n\t\t!bash.output.contains(\"Using groovy version 2.4.1 in this shell\")\n\t}\n\n\tdef \"should execute 'sdk env' when opening a new terminal in a directory with an .sdkmanrc\"() {\n\t\tgiven:\n\t\tnew FileTreeBuilder(candidatesDirectory).with {\n\t\t\t\"groovy\" {\n\t\t\t\t\"2.4.1\" {}\n\t\t\t}\n\t\t}\n\n\t\tbash = sdkmanBashEnvBuilder\n\t\t\t.withOfflineMode(true)\n\t\t\t.withConfiguration(\"sdkman_auto_env\", \"true\")\n\t\t\t.build()\n\n\t\tnew FileTreeBuilder(bash.workDir).with {\n\t\t\t\".sdkmanrc\"(\"groovy=2.4.1\\n\")\n\t\t}\n\n\t\tbash.start()\n\n\t\twhen:\n\t\tbash.execute(\"source $bootstrapScript\")\n\n\t\tthen:\n\t\tbash.output.contains(\"Using groovy version 2.4.1 in this shell\")\n\t}\n\n\tdef \"should execute 'sdk env' after executing 'sdk env clear'\"() {\n\t\tgiven:\n\t\tnew FileTreeBuilder(candidatesDirectory).with {\n\t\t\t\"groovy\" {\n\t\t\t\t\"2.4.1\" {}\n\t\t\t}\n\t\t}\n\n\t\tbash = sdkmanBashEnvBuilder\n\t\t\t.withOfflineMode(true)\n\t\t\t.withConfiguration(\"sdkman_auto_env\", \"true\")\n\t\t\t.build()\n\n\t\tnew FileTreeBuilder(bash.workDir).with {\n\t\t\t\"project\" {\n\t\t\t\t\".sdkmanrc\"(\"groovy=2.4.1\\n\")\n\t\t\t}\n\t\t}\n\n\t\tbash.start()\n\t\tbash.execute(\"source $bootstrapScript\")\n\n\t\twhen:\n\t\tbash.execute(\"cd project\")\n\t\tbash.execute(\"cd ..\")\n\t\tbash.execute(\"cd project\")\n\n\t\tthen:\n\t\tbash.output.contains('Using groovy version 2.4.1 in this shell')\n\t}\n\n\tdef \"should execute 'sdk env clear' when exiting from a directory with an .sdkmanrc\"() {\n\t\tgiven:\n\t\tnew FileTreeBuilder(candidatesDirectory).with {\n\t\t\t\"groovy\" {\n\t\t\t\t\"2.4.1\" {}\n\t\t\t\t\"2.4.6\" {}\n\t\t\t}\n\t\t}\n\t\tcreateSymbolicLink(Paths.get(\"$candidatesDirectory/groovy/current\"), Paths.get(\"$candidatesDirectory/groovy/2.4.6\"))\n\n\t\tbash = sdkmanBashEnvBuilder\n\t\t\t\t.withOfflineMode(true)\n\t\t\t\t.withConfiguration(\"sdkman_auto_env\", \"true\")\n\t\t\t\t.build()\n\n\t\tnew FileTreeBuilder(bash.workDir).with {\n\t\t\t\"project\" {\n\t\t\t\t\".sdkmanrc\"(\"groovy=2.4.1\\n\")\n\t\t\t}\n\t\t}\n\n\t\tbash.start()\n\t\tbash.execute(\"source $bootstrapScript\")\n\n\t\twhen:\n\t\tbash.execute(\"cd project\")\n\t\tbash.execute(\"cd ..\")\n\n\t\tthen:\n\t\tbash.output.contains(\"Restored groovy version to 2.4.6\")\n\t}\n\n\tdef \"should execute 'sdk env clear; sdk env' when switching to another directory with an .sdkmanrc\"() {\n\t\tgiven:\n\t\tnew FileTreeBuilder(candidatesDirectory).with {\n\t\t\t\"groovy\" {\n\t\t\t\t\"2.4.1\" {}\n\t\t\t\t\"2.4.6\" {}\n\t\t\t\t\"2.5.14\" {}\n\t\t\t}\n\t\t\t\"ant\" {\n\t\t\t\t\"1.9.15\" {}\n\t\t\t\t\"1.10.8\" {}\n\t\t\t}\n\t\t}\n\t\tcreateSymbolicLink(Paths.get(\"$candidatesDirectory/groovy/current\"), Paths.get(\"$candidatesDirectory/groovy/2.5.14\"))\n\t\tcreateSymbolicLink(Paths.get(\"$candidatesDirectory/ant/current\"), Paths.get(\"$candidatesDirectory/ant/1.10.8\"))\n\n\t\tbash = sdkmanBashEnvBuilder\n\t\t\t\t.withOfflineMode(true)\n\t\t\t\t.withConfiguration(\"sdkman_auto_env\", \"true\")\n\t\t\t\t.build()\n\n\t\tnew FileTreeBuilder(bash.workDir).with {\n\t\t\t\"projectA\" {\n\t\t\t\t\".sdkmanrc\"(\"groovy=2.4.1\\nant=1.9.15\\n\")\n\t\t\t}\n\t\t\t\"projectB\" {\n\t\t\t\t\".sdkmanrc\"(\"groovy=2.4.6\\n\")\n\t\t\t}\n\t\t}\n\n\t\tbash.start()\n\t\tbash.execute(\"source $bootstrapScript\")\n\n\t\twhen:\n\t\tbash.execute(\"cd projectA\")\n\t\t\n\t\tthen:\n\t\tbash.output.contains('Using groovy version 2.4.1 in this shell')\n\t\tbash.output.contains('Using ant version 1.9.15 in this shell')\n\t\t\n\t\twhen:\n\t\tbash.execute(\"cd ../projectB\")\n\n\t\tthen:\n\t\tbash.output.contains(\"Restored ant version to 1.10.8\")\n\t\tbash.output.contains('Using groovy version 2.4.6 in this shell')\n\t\t\n\t\twhen:\n\t\tbash.execute(\"cd ..\")\n\n\t\tthen:\n\t\tbash.output.contains('Restored groovy version to 2.5.14')\n\t\t!bash.output.contains('ant')\n\t}\n\n\tdef \"should not execute 'sdk env clear' when entering a subdirectory within the current active configuration\"() {\n\t\tgiven:\n\t\tnew FileTreeBuilder(candidatesDirectory).with {\n\t\t\t\"groovy\" {\n\t\t\t\t\"2.4.1\" {}\n\t\t\t}\n\t\t}\n\n\t\tbash = sdkmanBashEnvBuilder\n\t\t\t\t.withOfflineMode(true)\n\t\t\t\t.withConfiguration(\"sdkman_auto_env\", \"true\")\n\t\t\t\t.build()\n\n\t\tnew FileTreeBuilder(bash.workDir).with {\n\t\t\t\"project\" {\n\t\t\t\t\".sdkmanrc\"(\"groovy=2.4.1\\n\")\n\t\t\t}\n\t\t\t\"src\" {}\n\t\t}\n\n\t\tbash.start()\n\t\tbash.execute(\"source $bootstrapScript\")\n\n\t\twhen:\n\t\tbash.execute(\"cd project\")\n\t\tbash.execute(\"cd src\")\n\n\t\tthen:\n\t\t!bash.output.contains(\"Restored groovy version to 2.4.6\")\n\t}\n\n\tdef \"should issue an error if .sdkmanrc contains a malformed candidate version\"() {\n\t\tgiven:\n\t\tbash = sdkmanBashEnvBuilder\n\t\t\t.withOfflineMode(true)\n\t\t\t.build()\n\n\t\tnew File(bash.workDir, \".sdkmanrc\").text = \"groovy 2.4.1\"\n\n\t\tbash.start()\n\t\tbash.execute(\"source $bootstrapScript\")\n\n\t\twhen:\n\t\tbash.execute(\"sdk env\")\n\n\t\tthen:\n\t\tverifyAll(bash) {\n\t\t\tstatus == 1\n\t\t\toutput.contains(\"Invalid candidate format!\")\n\t\t}\n\t}\n\n\tdef \"should issue an error when .sdkmanrc contains a candidate version which is not installed\"() {\n\t\tgiven:\n\t\tbash = sdkmanBashEnvBuilder\n\t\t\t\t.withOfflineMode(true)\n\t\t\t\t.build()\n\n\t\tnew File(bash.workDir, \".sdkmanrc\").text = \"groovy=2.4.1\"\n\n\t\tbash.start()\n\t\tbash.execute(\"source $bootstrapScript\")\n\n\t\twhen:\n\t\tbash.execute(\"sdk env\")\n\n\t\tthen:\n\t\tverifyAll(bash) {\n\t\t\tstatus == 1\n\t\t\toutput.contains(\"Stop! groovy 2.4.1 is not installed.\")\n\t\t\toutput.contains(\"Run 'sdk env install' to install it.\")\n\t\t}\n\t}\n\n\tdef \"should support blank lines, comments and inline comments\"() {\n\t\tgiven:\n\t\tnew FileTreeBuilder(candidatesDirectory).with {\n\t\t\t\"groovy\" {\n\t\t\t\t\"2.4.1\" {}\n\t\t\t}\n\t\t}\n\n\t\tbash = sdkmanBashEnvBuilder\n\t\t\t.withOfflineMode(true)\n\t\t\t.build()\n\n\t\tnew File(bash.workDir, \".sdkmanrc\").text = sdkmanrc\n\n\t\tbash.start()\n\t\tbash.execute(\"source $bootstrapScript\")\n\n\t\twhen:\n\t\tbash.execute(\"sdk env\")\n\n\t\tthen:\n\t\tbash.output.contains(\"Using groovy version 2.4.1 in this shell.\")\n\n\t\twhere:\n\t\tsdkmanrc << [\n\t\t\t\"\\ngroovy=2.4.1\\n\",\n\t\t\t\"# this is a comment\\ngroovy=2.4.1\\n\",\n\t\t\t\"groovy=2.4.1 # this is a comment too\\n\"\n\t\t]\n\t}\n}\n"
  },
  {
    "path": "src/test/groovy/sdkman/specs/InitialisationSpec.groovy",
    "content": "package sdkman.specs\n\nimport sdkman.support.SdkmanEnvSpecification\n\nimport java.nio.file.Files\nimport java.nio.file.Paths\n\nclass InitialisationSpec extends SdkmanEnvSpecification {\n\n\tstatic final allCandidates = [\n\t\t\t\"asciidoctorj\", \"crash\", \"gaiden\", \"glide\", \"gradle\", \"grails\", \"griffon\", \"groovy\",\n\t\t\t\"groovyserv\", \"jbake\", \"jbossforge\", \"lazybones\", \"springboot\", \"vertx\"\n\t]\n\n\tdef setup() {\n\t\tbash = sdkmanBashEnvBuilder\n\t\t\t\t.withCandidates(allCandidates)\n\t\t\t\t.build()\n\t\tprepareCandidateDirectories(allCandidates)\n\t}\n\n\tvoid \"should include all candidates in PATH\"() {\n\t\tgiven:\n\t\tbash.start()\n\t\tbash.execute(\"source $bootstrapScript\")\n\t\tbash.resetOutput()\n\n\t\twhen:\n\t\tbash.execute('echo \"$PATH\"')\n\t\tdef pathParts = bash.output.split(':')\n\t\tdef pathElementMatcher = ~/$candidatesDirectory\\/([^\\/]+)\\/.*/\n\t\tdef includedCandidates = pathParts\n\t\t\t\t.collect { it.replace(\"\\n\", \"\") }\n\t\t\t\t.collect { it =~ pathElementMatcher }\n\t\t\t\t.findAll { it }\n\t\t\t\t.collect { it[0][1] }\n\t\t\t\t.sort()\n\n\t\tprintln(\"Available: ${allCandidates}\")\n\t\tprintln(\"Included : $includedCandidates\")\n\n\t\tand:\n\t\tdef missingCandidates = allCandidates - includedCandidates\n\n\t\tthen:\n\t\tmissingCandidates.isEmpty()\n\t}\n\n\tvoid \"should reinitialize candidates in PATH if necessary\"() {\n\t\tgiven:\n\t\tbash.start()\n\t\tbash.execute(\"source $bootstrapScript\")\n\t\tbash.resetOutput()\n\n\t\tand:\n\t\tdef originalPath = bash.env.grep { it =~ /^PATH=/ }.first() as String\n\t\tbash.execute(originalPath)\n\n\t\twhen:\n\t\tbash.execute(\"source $bootstrapScript\")\n\t\tbash.execute('echo \"$PATH\"')\n\n\t\tthen:\n\t\tdef pathParts = bash.output.split(':')\n\t\tdef pathElementMatcher = ~/$candidatesDirectory\\/([^\\/]+)\\/.*/\n\t\tdef includedCandidates = pathParts\n\t\t\t\t.collect { it.replace(\"\\n\", \"\") }\n\t\t\t\t.collect { it =~ pathElementMatcher }\n\t\t\t\t.findAll { it }\n\t\t\t\t.collect { it[0][1] }\n\t\t\t\t.sort()\n\n\t\tprintln(\"Available: $allCandidates\")\n\t\tprintln(\"Included : $includedCandidates\")\n\n\t\tdef missingCandidates = allCandidates - includedCandidates\n\t\tmissingCandidates.isEmpty()\n\t}\n\n\tvoid \"should not duplicate PATH entries if re-sourced\"() {\n\t\tgiven:\n\t\tbash.start()\n\t\tbash.execute(\"source $bootstrapScript\")\n\t\tbash.resetOutput()\n\n\t\twhen:\n\t\tbash.execute(\"source $bootstrapScript\")\n\t\tbash.execute('echo \"$PATH\"')\n\n\t\tdef pathParts = bash.output.split(':')\n\t\tdef pathElementMatcher = ~/$candidatesDirectory\\/([^\\/]+)\\/.*/\n\t\tdef includedCandidates = pathParts\n\t\t\t\t.collect { it.replace(\"\\n\", \"\") }\n\t\t\t\t.collect { it =~ pathElementMatcher }\n\t\t\t\t.findAll { it }\n\t\t\t\t.collect { it[0][1] }\n\t\t\t\t.sort()\n\n\t\tprintln(\"Available: $allCandidates\")\n\t\tprintln(\"Included : $includedCandidates\")\n\n\t\tand:\n\t\tdef duplicateCandidates = includedCandidates - allCandidates\n\n\t\tthen:\n\t\tduplicateCandidates.isEmpty()\n\t}\n\n\tprivate prepareCandidateDirectories(List candidates) {\n\t\tcandidates.forEach {\n\t\t\tdef current = Paths.get(\"$candidatesDirectory/$it/current\")\n\t\t\tdef targetFilename = \"$candidatesDirectory/$it/xxx\"\n\n\t\t\tnew File(targetFilename).createNewFile()\n\t\t\tdef target = Paths.get(targetFilename)\n\n\t\t\tFiles.createSymbolicLink(current, target)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/test/groovy/sdkman/specs/SdkCompatibilitySpec.groovy",
    "content": "package sdkman.specs\n\nimport sdkman.support.SdkmanEnvSpecification\n\nimport java.nio.file.Files\nimport java.nio.file.Paths\n\nclass SdkCompatibilitySpec extends SdkmanEnvSpecification {\n\n\tdef allCandidates = [\"groovy\", \"grails\", \"scala\", \"sbt\"]\n\n\tdef setup() {\n\t\tbash = sdkmanBashEnvBuilder\n\t\t\t\t.withCandidates(allCandidates)\n\t\t\t\t.build()\n\t}\n\n\tvoid \"should add candidate bin folder to the path if present\"() {\n\t\tgiven:\n\t\tdef candidateFolder = prepareCandidateFolder(\"scala\", \"2.11.7\", true)\n\n\t\tand:\n\t\tbash.start()\n\t\tbash.execute(\"source $bootstrapScript\")\n\t\tbash.resetOutput()\n\n\t\twhen:\n\t\tbash.execute('echo $PATH')\n\t\tdef pathEntries = bash.output.split(':')\n\t\tdef firstPathEntry = pathEntries.first()\n\n\t\tthen:\n\t\tfirstPathEntry.contains(\"$candidateFolder/bin\")\n\t}\n\n\tvoid \"should add candidate base folder to the path if no bin folder present\"() {\n\t\tgiven:\n\t\tdef candidateFolder = prepareCandidateFolder(\"sbt\", \"1.0.3\", false)\n\n\t\tand:\n\t\tbash.start()\n\t\tbash.execute(\"source $bootstrapScript\")\n\t\tbash.resetOutput()\n\n\t\twhen:\n\t\tbash.execute('echo $PATH')\n\t\tdef pathEntries = bash.output.split(':')\n\t\tdef firstPathEntry = pathEntries.first()\n\n\t\tthen:\n\t\tfirstPathEntry.contains(\"$candidateFolder\")\n\n\t\tand:\n\t\t!firstPathEntry.contains(\"$candidateFolder/bin\")\n\t}\n\n\tprivate prepareCandidateFolder(String candidate, String version, boolean hasBinFolder) {\n\t\tdef candidateBaseDir = \"${candidatesDirectory.absolutePath}/$candidate\"\n\t\tdef candidateCurrentDir = Paths.get(\"$candidateBaseDir/current\")\n\n\t\tdef candidateLocation = \"${candidatesDirectory.absolutePath}/$candidate/$version\"\n\t\tdef candidatePath = Paths.get(candidateLocation)\n\n\t\tdef binLocation = hasBinFolder ? \"$candidateLocation/bin/\" : \"$candidateLocation\"\n\t\tdef executableFilename = \"run.sh\"\n\t\tdef executableFile = \"$binLocation/$executableFilename\" as File\n\n\t\tnew File(binLocation).mkdirs()\n\t\texecutableFile.createNewFile()\n\n\t\tFiles.createSymbolicLink(candidateCurrentDir, candidatePath)\n\t\tcandidateCurrentDir.toString()\n\t}\n}\n"
  },
  {
    "path": "src/test/groovy/sdkman/specs/SelfupdateFeatureSpec.groovy",
    "content": "package sdkman.specs\n\nimport sdkman.support.SdkmanEnvSpecification\n\nimport java.time.Instant\n\nimport static java.time.temporal.ChronoUnit.DAYS\n\nclass SelfupdateFeatureSpec extends SdkmanEnvSpecification {\n\tstatic final String CANDIDATES_API = \"http://localhost:8080/2\"\n\tstatic final String HEALTHCHECK_ENDPOINT = \"$CANDIDATES_API/healthcheck\"\n\tstatic final String VERSION_ENDPOINT = \"$CANDIDATES_API/broker/download/sdkman/version/stable\"\n \n\tdef setup() {\n\t\tcurlStub.primeWith(HEALTHCHECK_ENDPOINT, \"echo dbfb025be9f97fda2052b5febcca0155\")\n\t\tcurlStub.primeWith(VERSION_ENDPOINT, \"echo 5.0.0\")\n\t}\n\n\tdef \"should list selfupdate as a valid command when the selfupdate feature is toggled on\"() {\n\t\tgiven:\n\t\tbash = sdkmanBashEnvBuilder\n\t\t\t.withConfiguration(\"sdkman_selfupdate_feature\", selfUpdateFeature)\n\t\t\t.build()\n\n\t\tbash.start()\n\t\tbash.execute(\"source $bootstrapScript\")\n\n\t\twhen:\n\t\tbash.execute(\"sdk help\")\n\n\t\tthen:\n\t\tverifyOutput(bash.output)\n\n\t\twhere:\n\t\tselfUpdateFeature | verifyOutput\n\t\t\"false\"           | { !it.contains(\"selfupdate\") }\n\t\t\"true\"            | { it.contains(\"selfupdate\") }\n\t}\n\n\tdef \"should source sdkman-selfupdate.sh when the selfupdate feature is toggled on\"() {\n\t\tgiven:\n\t\tbash = sdkmanBashEnvBuilder\n\t\t\t.withConfiguration(\"sdkman_selfupdate_feature\", selfupdateFeature)\n\t\t\t.build()\n\n\t\tbash.start()\n\t\tbash.execute(\"source $bootstrapScript\")\n\n\t\twhen:\n\t\tbash.execute(\"sdk selfupdate\")\n\n\t\tthen:\n\t\tverifyOutput(bash.output)\n\n\t\twhere:\n\t\tselfupdateFeature | verifyOutput\n\t\t\"false\"           | { it.contains(\"Invalid command: selfupdate\") }\n\t\t\"true\"            | { !it.contains(\"Invalid command: selfupdate\") }\n\t}\n}\n"
  },
  {
    "path": "src/test/groovy/sdkman/steps/command_line_interop_steps.groovy",
    "content": "package sdkman.steps\n\nimport static cucumber.api.groovy.EN.And\n\nAnd(~'^I enter \\\"([^\\\"]*)\\\"$') { String command ->\n\tbash.execute(command)\n\tresult = bash.output\n}\n\nAnd(~'^I enter \"([^\"]*)\" and answer \"([^\"]*)\"$') { String command, String answer ->\n\tbash.execute(command, [answer])\n\tresult = bash.output\n}\n\nAnd(~'^I see \\\"([^\\\"]*)\\\"$') { String output ->\n\tassert result.contains(output)\n}\n\nAnd(~'^I do not see \"([^\"]*)\"$') { String output ->\n\tassert !result.contains(output)\n}\n\nAnd(~'^I see only \\\"([^\\\"]*)\\\"$') { String output ->\n\tassert result?.replaceAll(\"\\\\n\", \"\") == output\n}\n\nAnd(~'^I see the current sdkman version$') { ->\n\tassert result.contains(\"SDKMAN\")\n}\n\nAnd(~'^I see a single occurrence of \\\"([^\\\"]*)\\\"$') { String occurrence ->\n\tassert result.count(occurrence) == 1\n}\n\nAnd(~'^I see no occurrences of \\\"([^\\\"]*)\\\"$') { String occurrence ->\n\tassert result.count(occurrence) == 0\n}\n\nAnd(~'the \"(.*)\" variable contains \"(.*)\"') { String home, String segment ->\n\tbash.execute(\"echo \\$$home\")\n\tassert bash.output.contains(segment)\n}\n\nAnd(~'the \"(.*)\" variable is not set') { String home ->\n\tbash.execute(\"echo \\$$home\")\n\tassert !bash.output.contains(\".sdkman/\")\n}\n\nAnd(~'^the home path ends with \\\"([^\\\"]*)\\\"$') { String suffix ->\n\tdef path = sdkmanBaseDir.absolutePath + \"/\" + suffix\n\tassert result.trim().endsWith(path)\n}\n"
  },
  {
    "path": "src/test/groovy/sdkman/steps/env.groovy",
    "content": "package sdkman.steps\n\nimport com.github.tomakehurst.wiremock.client.WireMock\nimport sdkman.support.FilesystemUtils\nimport sdkman.support.WireMockServerProvider\n\nimport static cucumber.api.groovy.Hooks.After\nimport static cucumber.api.groovy.Hooks.Before\n\nHTTP_PROXY = System.getProperty(\"httpProxy\") ?: \"\"\n\nFAKE_JDK_PATH = \"/path/to/my/openjdk\"\nSERVICE_UP_HOST = \"localhost\"\nSERVICE_UP_PORT = 8080\nSERVICE_UP_URL = \"http://$SERVICE_UP_HOST:$SERVICE_UP_PORT\"\nSERVICE_DOWN_URL = \"http://localhost:0\"\n\ncounter = \"${(Math.random() * 10000).toInteger()}\".padLeft(4, \"0\")\n\nlocalGroovyCandidate = \"/tmp/groovy-core\" as File\n\nsdkmanScriptVersion = \"5.0.0\"\nsdkmanNativeVersion = \"0.0.1\"\n\nsdkmanBaseEnv = FilesystemUtils.prepareBaseDir().absolutePath\nsdkmanBaseDir = sdkmanBaseEnv as File\n\nsdkmanDirEnv = \"$sdkmanBaseEnv/.sdkman\"\nsdkmanDir = sdkmanDirEnv as File\ncandidatesDir = \"${sdkmanDirEnv}/candidates\" as File\nbinDir = \"${sdkmanDirEnv}/bin\" as File\nsrcDir = \"${sdkmanDirEnv}/src\" as File\nvarDir = \"${sdkmanDirEnv}/var\" as File\nmetadataDir = \"${varDir}/metadata\" as File\netcDir = \"${sdkmanDirEnv}/etc\" as File\nextDir = \"${sdkmanDirEnv}/ext\" as File\ntmpDir = \"${sdkmanDir}/tmp\" as File\n\nhealthcheckFile = new File(varDir, \"healthcheck\")\ncandidatesFile = new File(varDir, \"candidates\")\nversionFile = new File(varDir, \"version\")\ninitScript = new File(binDir, \"sdkman-init.sh\")\n\nlocalCandidates = ['groovy', 'grails', 'java', 'kotlin', 'scala']\n\nbash = null\n\nif (!binding.hasVariable(\"wireMock\")) {\n\twireMock = WireMockServerProvider.wireMockServer()\n}\n\naddShutdownHook {\n\twireMock.stop()\n}\n\nBefore() {\n\tWireMock.reset()\n\tcleanUp()\n}\n\nprivate cleanUp() {\n\tsdkmanBaseDir.deleteDir()\n\tlocalGroovyCandidate.deleteDir()\n}\n\nAfter() { scenario ->\n\tdef output = bash?.output\n\tif (output) {\n\t\tscenario.write(\"\\nOutput: \\n${output}\")\n\t}\n\tbash?.stop()\n}\n"
  },
  {
    "path": "src/test/groovy/sdkman/steps/env_steps.groovy",
    "content": "package sdkman.steps\n\nimport static cucumber.api.groovy.EN.And\n\nAnd(~/^the file \"([^\"]+)\" exists and contains \"([^\"]+)\"$/) { String filename, String content ->\n\tnew File(sdkmanBaseEnv, filename).withWriter {\n\t\tit.writeLine(content)\n\t}\n}\n"
  },
  {
    "path": "src/test/groovy/sdkman/steps/flush_steps.groovy",
    "content": "package sdkman.steps\n\nimport static cucumber.api.groovy.EN.And\n\nAnd(~'^the candidate \"([^\"]*)\" is known locally$') { String candidate ->\n\tassert candidatesFile.text.contains(candidate)\n}\n\nAnd(~'^no candidates are know locally$') { ->\n\tassert !candidatesFile.exists()\n}\n\nAnd(~'^the file \"([^\"]*)\" in temporary storage$') { String fileName ->\n\tnew File(tmpDir, fileName).createNewFile()\n}\n\nAnd(~'^no \"([^\"]*)\" file is present in temporary storage$') { String fileName ->\n\tassert !new File(tmpDir, fileName).exists()\n}\n\nAnd(~'^a prior version \"([^\"]*)\" was detected$') { String version ->\n\tassert versionFile.exists()\n\tassert versionFile.text.contains(version)\n}\n\nAnd(~'^no version file can be found$') { ->\n\tassert !versionFile.exists()\n}\n\nAnd(~'^the Remote Version has been flushed$') { ->\n\tassert versionFile.delete()\n}\n\nAnd(~'^a headers file \"([^\"]*)\" in metadata directory with checksum \"([^\"]*)\" using algorithm \"([^\"]*)\"$') { \n\tString fileName, String checksum, String algorithm ->\n\t\t\n\tnew File(metadataDir, fileName).withWriter { out ->\n\t\tout.println \"X-Sdkman-Checksum-${algorithm}: ${checksum}\"\n\t}\n}\n\nAnd(~'^no metadata is cached$') { ->\n\tassert !metadataDir.listFiles()\n}\n"
  },
  {
    "path": "src/test/groovy/sdkman/steps/initialisation_steps.groovy",
    "content": "package sdkman.steps\n\nimport sdkman.env.SdkmanBashEnvBuilder\nimport sdkman.stubs.UnameStub\n\nimport java.util.zip.ZipException\nimport java.util.zip.ZipFile\n\nimport static cucumber.api.groovy.EN.And\nimport static sdkman.stubs.WebServiceStub.primeEndpointWithString\nimport static sdkman.stubs.WebServiceStub.primeSelfupdate\n\nAnd(~'^the sdkman work folder is created$') { ->\n\tassert sdkmanDir.isDirectory(), \"The SDKMAN directory does not exist.\"\n}\n\nAnd(~'^the \"([^\"]*)\" folder exists in user home$') { String arg1 ->\n\tassert sdkmanDir.isDirectory(), \"The SDKMAN directory does not exist.\"\n}\n\nAnd(~'^the archive for candidate \"([^\"]*)\" version \"([^\"]*)\" is corrupt$') { String candidate, String version ->\n\ttry {\n\t\tnew ZipFile(new File(\"src/test/resources/__files/${candidate}-${version}.zip\"))\n\t\tassert false, \"Archive was not corrupt!\"\n\t} catch (ZipException ze) {\n\t\t//expected behaviour\n\t}\n}\n\nAnd(~'^the archive for candidate \"([^\"]*)\" version \"([^\"]*)\" is removed$') { String candidate, String version ->\n\tdef archive = new File(\"${sdkmanDir}/tmp/${candidate}-${version}.zip\")\n\tassert !archive.exists()\n}\n\nAnd(~'^the sdkman (.*) version \"(.*)\" is available for download$') { format, version ->\n\tprimeEndpointWithString(\"/broker/version/sdkman/${format}/stable\", version)\n}\n\nAnd(~'^the internet is reachable$') { ->\n\tprimeEndpointWithString(\"/healthcheck\", \"12345\")\n\tprimeSelfupdate()\n\n\tofflineMode = false\n\tserviceUrlEnv = SERVICE_UP_URL\n\tjavaHome = FAKE_JDK_PATH\n}\n\nAnd(~'^the internet is not reachable$') { ->\n\tofflineMode = false\n\tserviceUrlEnv = SERVICE_DOWN_URL\n\tjavaHome = FAKE_JDK_PATH\n}\n\nAnd(~'^offline mode is disabled with reachable internet$') { ->\n\tprimeEndpointWithString(\"/healthcheck\", \"12345\")\n\n\tofflineMode = false\n\tserviceUrlEnv = SERVICE_UP_URL\n\tjavaHome = FAKE_JDK_PATH\n}\n\nAnd(~'^offline mode is enabled with reachable internet$') { ->\n\tprimeEndpointWithString(\"/healthcheck\", \"12345\")\n\n\tofflineMode = true\n\tserviceUrlEnv = SERVICE_UP_URL\n\tjavaHome = FAKE_JDK_PATH\n}\n\nAnd(~'^offline mode is enabled with unreachable internet$') { ->\n\tofflineMode = true\n\tserviceUrlEnv = SERVICE_DOWN_URL\n\tjavaHome = FAKE_JDK_PATH\n}\n\nAnd(~'^an \"(.*)\" machine with \"(.*)\" installed$') { String machine, String kernel ->\n\tdef binFolder = \"$sdkmanBaseDir/bin\" as File\n\tUnameStub.prepareIn(binFolder)\n\t\t\t.forKernel(kernel)\n\t\t\t.forMachine(machine)\n\t\t\t.build()\n}\n\nAnd(~'^an initialised environment$') { ->\n\tbash = SdkmanBashEnvBuilder.create(sdkmanBaseDir)\n\t\t\t.withOfflineMode(offlineMode)\n\t\t\t.withCandidatesApi(serviceUrlEnv)\n\t\t\t.withBrokerApi(serviceUrlEnv)\n\t\t\t.withJdkHome(javaHome)\n\t\t\t.withHttpProxy(HTTP_PROXY)\n\t\t\t.withScriptVersion(sdkmanScriptVersion)\n\t\t\t.withNativeVersion(sdkmanNativeVersion)\n\t\t\t.withCandidates(localCandidates)\n\t\t\t.build()\n}\n\nAnd(~'^an initialised environment without debug prints$') { ->\n\tbash = SdkmanBashEnvBuilder.create(sdkmanBaseDir)\n\t\t\t.withOfflineMode(offlineMode)\n\t\t\t.withCandidatesApi(serviceUrlEnv)\n\t\t\t.withBrokerApi(serviceUrlEnv)\n\t\t\t.withJdkHome(javaHome)\n\t\t\t.withHttpProxy(HTTP_PROXY)\n\t\t\t.withScriptVersion(sdkmanScriptVersion)\n\t\t\t.withNativeVersion(sdkmanNativeVersion)\n\t\t\t.withCandidates(localCandidates)\n\t\t\t.withDebugMode(false)\n\t\t\t.build()\n}\n\nAnd(~'^the system is bootstrapped$') { ->\n\tbash.start()\n\tbash.execute(\"source $sdkmanDirEnv/bin/sdkman-init.sh\")\n}\n\nAnd(~'^the system is bootstrapped again$') { ->\n\tbash.execute(\"source $sdkmanDirEnv/bin/sdkman-init.sh\")\n}\n\nAnd(~/^the sdkman scripts version is \"([^\"]*)\"$/) { String version ->\n\tsdkmanScriptVersion = version\n}\n\nAnd(~/^the sdkman native version is \"([^\"]*)\"$/) { String version ->\n\tsdkmanNativeVersion = version\n}\n\nAnd(~/^the candidates cache is initialised with \"(.*)\"$/) { String candidate ->\n\tlocalCandidates << candidate\n}\n\nAnd(~/^a project configuration is active$/) { ->\n\tbash.execute(\"SDKMAN_ENV=\" + sdkmanBaseEnv)\n}\n\nAnd(~/^a project configuration is active but points to a directory without configuration$/) { ->\n\tdef emptyDir = tmpDir.getPath() + \"/empty\"\n\tbash.execute(\"mkdir $emptyDir\")\n\tbash.execute(\"SDKMAN_ENV=$emptyDir\")\n}"
  },
  {
    "path": "src/test/groovy/sdkman/steps/installation_steps.groovy",
    "content": "package sdkman.steps\n\nimport java.nio.file.FileSystems\nimport java.nio.file.Files\nimport java.nio.file.Path\nimport java.nio.file.Paths\n\nimport static cucumber.api.groovy.EN.And\nimport static java.nio.file.Files.isSameFile\nimport static java.nio.file.Files.isSymbolicLink\nimport static sdkman.support.FilesystemUtils.prepareCandidateBinFolder\nimport static sdkman.support.FilesystemUtils.prepareCandidateWithVersionFolder\n\nAnd(~'^the candidate \"([^\"]*)\" version \"([^\"]*)\" is installed$') { String candidate, String version ->\n\tdef file = \"${candidatesDir}/${candidate}/${version}\" as File\n\tif (!file.exists()) println bash.output\n\tassert file.exists()\n}\n\nAnd(~'^the candidate \"([^\"]*)\" version \"([^\"]*)\" is not installed$') { String candidate, String version ->\n\tdef directory = FileSystems.default.getPath(\"$candidatesDir/$candidate/$version\")\n\tif (Files.exists(directory)) println bash.output\n\tassert !Files.exists(directory)\n}\n\nAnd(~'^the candidate \"([^\"]*)\" version \"([^\"]*)\" is already installed and default$') { String candidate, String version ->\n\tdef candidateVersion = prepareCandidateWithVersionFolder(\"$candidatesDir\", candidate, version)\n\tdef currentLink = FileSystems.default.getPath(\"$candidatesDir/$candidate/current\")\n\tFiles.createSymbolicLink currentLink, candidateVersion\n}\n\nAnd(~'^the candidate \"([^\"]*)\" version \"([^\"]*)\" is the default$') { String candidate, String version ->\n\tdef localVersion = FileSystems.default.getPath(\"$candidatesDir/$candidate/$version\")\n\tdef currentLink = FileSystems.default.getPath(\"$candidatesDir/$candidate/current\")\n\tFiles.createSymbolicLink currentLink, localVersion\n}\n\nAnd(~'^the candidate \"([^\"]*)\" version \"([^\"]*)\" is already installed but not default$') { String candidate, String version ->\n\tprepareCandidateWithVersionFolder \"$candidatesDir\", candidate, version\n}\n\nAnd(~'^I do not have a \"([^\"]*)\" candidate installed$') { String candidate ->\n\tdef candidateDir = FileSystems.default.getPath(\"${candidatesDir}/${candidate}\")\n\tassert !candidateDir.toFile().listFiles()\n}\n\nAnd(~'^the candidate \"([^\"]*)\" does not exist locally$') { String candidate ->\n\tdef candidateDir = \"${candidatesDir}/${candidate}\" as File\n\tcandidateDir.deleteDir()\n\tassert !candidateDir.exists()\n}\n\nAnd(~'^I have a local candidate \"([^\"]*)\" version \"([^\"]*)\" at \"([^\"]*)\"$') { String candidate, String version, String directory ->\n\tprepareCandidateBinFolder directory, candidate, version\n}\n\nAnd(~'^I have a local candidate \"([^\"]*)\" version \"([^\"]*)\" at relative path \"([^\"]*)\"$') { String candidate, String version, String relativePath ->\n\tdef fullPath = new File(sdkmanBaseDir.absolutePath, relativePath)\n\tprepareCandidateBinFolder fullPath.absolutePath, candidate, version\n}\n\nAnd(~'^the candidate \"([^\"]*)\" version \"([^\"]*)\" is linked to the relative path \"([^\"]*)\"$') { String candidate, String version, String relativePath ->\n\tdef fullPath = new File(sdkmanBaseDir.absolutePath, relativePath)\n\tassertLinkedCandidate(fullPath.absolutePath, candidate, version)\n}\n\nAnd(~'^the candidate \"([^\"]*)\" version \"([^\"]*)\" is linked to \"([^\"]*)\"$') { String candidate, String version, String directory ->\n\tassertLinkedCandidate(directory, candidate, version)\n}\n\ndef assertLinkedCandidate(String directory, String candidate, String version) {\n\tPath versionFolder = Paths.get(\"$candidatesDir/$candidate/$version\")\n\n\tassert isSymbolicLink(versionFolder)\n\n\tassert isSameFile(versionFolder, Paths.get(directory))\n}\n\nAnd(~'^the candidate \"([^\"]*)\" version \"([^\"]*)\" is already linked to \"([^\"]*)\"$') { String candidate, String version, String folder ->\n\tdef fileSystem = FileSystems.default\n\n\tdef candidateFolder = \"$candidatesDir/$candidate\" as File\n\tcandidateFolder.mkdirs()\n\n\tdef link = fileSystem.getPath(\"$candidatesDir/$candidate/$version\")\n\tdef target = prepareCandidateBinFolder(folder, candidate, version)\n\n\tFiles.createSymbolicLink(link, target)\n}\n\nAnd(~'^I have configured \"([^\"]*)\" to \"([^\"]*)\"$') { String configName, String flag ->\n\tdef configFile = new File(\"$sdkmanDir/etc/config\")\n\tconfigFile.write \"${configName}=${flag}\"\n}\n\nAnd(~/^the exit code is (\\d+)$/) { Integer rc ->\n\tassert bash.getStatus() == rc\n}\n\nAnd(~'^the response headers file is created for candidate \"([^\"]*)\" and version \"([^\"]*)\"$') { String candidate, String version ->\n\tdef headersFile = \"${metadataDir}/${candidate}-${version}.headers\" as File\n\tassert headersFile.exists()\n}\n\nAnd(~'^no response headers are written for candidate \"([^\"]*)\" and version \"([^\"]*)\"$') { String candidate, String version ->\n\tdef headersFile = \"${metadataDir}/${candidate}-${version}.headers\" as File\n\tassert !headersFile.exists()\n}\n"
  },
  {
    "path": "src/test/groovy/sdkman/steps/stub_steps.groovy",
    "content": "package sdkman.steps\n\nimport io.cucumber.datatable.DataTable\nimport sdkman.support.UnixUtils\n\nimport static cucumber.api.groovy.EN.And\nimport static sdkman.stubs.HookResponses.*\nimport static sdkman.stubs.WebServiceStub.*\nimport static sdkman.support.FilesystemUtils.readCurrentFromCandidateFolder\nimport static sdkman.support.FilesystemUtils.readVersionsCsvFromCandidateFolder\n\nAnd(~'^the default \"([^\"]*)\" version is \"([^\"]*)\"$') { String candidate, String version ->\n\tprimeEndpointWithString(\"/candidates/default/${candidate}\", version)\n\tprimeDownloadFor(SERVICE_UP_URL, candidate, version, UnixUtils.inferPlatform())\n\tprimeEndpointWithString(\"/hooks/post/${candidate}/${version}/${UnixUtils.inferPlatform()}\", postInstallationHookSuccess())\n}\n\nAnd(~'^an available selfupdate endpoint$') { ->\n\tprimeEndpointWithString(\"/selfupdate/stable/${UnixUtils.inferPlatform()}\", 'echo \"Successfully upgraded SDKMAN.\"')\n\tprimeEndpointWithString(\"/selfupdate/beta/${UnixUtils.inferPlatform()}\", 'echo \"Successfully upgraded SDKMAN.\"')\n}\n\nAnd(~'^the candidate \"([^\"]*)\" version \"([^\"]*)\" is available for download$') { String candidate, String version ->\n\tprimeEndpointWithString(\"/candidates/validate/${candidate}/${version}/${UnixUtils.inferPlatform()}\", \"valid\")\n\tprimeDownloadFor(SERVICE_UP_URL, candidate, version, UnixUtils.inferPlatform())\n\tprimeEndpointWithString(\"/hooks/post/${candidate}/${version}/${UnixUtils.inferPlatform()}\", postInstallationHookSuccess())\n}\n\nAnd(~'^the candidate \"([^\"]*)\" version \"([^\"]*)\" is available for download with checksum \"([^\"]*)\" using algorithm \"([^\"]*)\"$') { \n\tString candidate, String version, String checksum, String algorithm ->\n\tprimeEndpointWithString(\"/candidates/validate/${candidate}/${version}/${UnixUtils.inferPlatform()}\", \"valid\")\n\tprimeDownloadFor(SERVICE_UP_URL, candidate, version, UnixUtils.inferPlatform(), [\"X-Sdkman-Checksum-${algorithm}\": \"${checksum}\"])\n\tprimeEndpointWithString(\"/hooks/post/${candidate}/${version}/${UnixUtils.inferPlatform()}\", postInstallationHookSuccess())\n}\n\nAnd(~/^the appropriate universal hook is available for \"([^\"]*)\" version \"([^\"]*)\" on \"([^\"]*)\"$/) { String candidate, String version, String os ->\n\tString lcPlatform = UnixUtils.inferPlatform(os)\n\tprimeUniversalHookFor(candidate, version, lcPlatform)\n}\n\nAnd(~/^the appropriate multi-platform hook is available for \"([^\"]*)\" version \"([^\"]*)\" on \"([^\"]*)\" with architecture \"(.*)\"$/) { String candidate, String version, String os, String architecture ->\n\tString lcPlatform = UnixUtils.inferPlatform(os, architecture)\n\tprimePlatformSpecificHookFor(candidate, version, lcPlatform)\n}\n\nAnd(~'^the candidate \"([^\"]*)\" version \"([^\"]*)\" is not available for download$') { String candidate, String version ->\n\tprimeEndpointWithString(\"/candidates/validate/${candidate}/${version}/${UnixUtils.inferPlatform()}\", \"invalid\")\n}\n\nAnd(~/^the candidate \"(.*)\" version \"(.*)\" is available for download on \"(.*)\" with architecture \"(.*)\"$/) { String candidate, String version, String os, String architecture ->\n\tString lcPlatform = UnixUtils.inferPlatform(os, architecture)\n\tprimeEndpointWithString(\"/candidates/validate/${candidate}/${version}/${lcPlatform}\", \"valid\")\n\tprimeDownloadFor(SERVICE_UP_URL, candidate, version, lcPlatform)\n}\n\nAnd(~/^a post-installation hook is served for \"([^\"]*)\" \"([^\"]*)\" on \"([^\"]*)\" with architecture \"([^\"]*)\" that returns successfully$/) { String candidate, String version, String os, String architecture ->\n\tString lcPlatform = UnixUtils.inferPlatform(os, architecture)\n\tprimeEndpointWithString(\"/hooks/post/${candidate}/${version}/${lcPlatform}\", postInstallationHookSuccess())\n}\n\nAnd(~/^a post-installation hook is served for \"([^\"]*)\" \"([^\"]*)\" on \"([^\"]*)\" with architecture \"([^\"]*)\" that returns a failure$/) { String candidate, String version, String os, String architecture ->\n\tString lcPlatform = UnixUtils.inferPlatform(os, architecture)\n\tprimeEndpointWithString(\"/hooks/post/${candidate}/${version}/${lcPlatform}\", postInstallationHookFailure())\n}\n\nAnd(~/^the candidate \"(.*?)\" version \"(.*?)\" is not available for download on \"(.*?)\"$/) { String candidate, String version, String os ->\n\tString lcPlatform = UnixUtils.inferPlatform(os)\n\tprimeEndpointWithString(\"/candidates/validate/${candidate}/${version}/${lcPlatform}\", \"invalid\")\n}\n\nAnd(~'^a \"([^\"]*)\" list view is available for consumption$') { String candidate ->\n\tprimeEndpointWithString(\"/candidates/${candidate}/${UnixUtils.inferPlatform()}/versions/list?current=&installed=\", \"Available ${candidate.capitalize()} Versions\")\n}\n\nAnd(~'^the candidate \"([^\"]*)\" version \"([^\"]*)\" is a valid candidate version$') { String candidate, String version ->\n\tprimeEndpointWithString(\"/candidates/validate/${candidate}/${version}/${UnixUtils.inferPlatform()}\", \"valid\")\n}\n\nAnd(~'^the candidate \"([^\"]*)\" version \"([^\"]*)\" is not a valid candidate version$') { String candidate, String version ->\n\tprimeEndpointWithString(\"/candidates/validate/${candidate}/${version}/${UnixUtils.inferPlatform()}\", \"invalid\")\n}\n\nAnd(~/^the candidate \"(.*?)\" has a version list available$/) { String candidate ->\n\tdef current = readCurrentFromCandidateFolder(candidatesDir, candidate)\n\tdef versions = readVersionsCsvFromCandidateFolder(candidatesDir, candidate)\n\tdef url = \"/candidates/${candidate}/${UnixUtils.inferPlatform()}/versions/list?current=${current}&installed=${versions}\"\n\tprintln(\"Priming url: $url\")\n\tprimeEndpointWithString(url, \"Candidate: $candidate; Versions: $versions; Current: $current; Platform: ${UnixUtils.inferPlatform()}\")\n}\n\nAnd(~/^The candidate list is available$/) { ->\n\tprimeEndpointWithString(\"/candidates/list\", \"Candidate List\")\n}\n\nAnd(~/^the following candidates are currently available from remote API:$/) { DataTable dt ->\n\tprimeEndpointWithString(\"/candidates/all\", dt.asList(String).drop(1).join(\",\"))\n}\n"
  },
  {
    "path": "src/test/groovy/sdkman/steps/update_steps.groovy",
    "content": "package sdkman.steps\n\nimport io.cucumber.datatable.DataTable\n\nimport static cucumber.api.groovy.EN.And\n\nAnd(~/^the following candidates are available for installation in local cache:$/) { DataTable dt ->\n\tlocalCandidates = dt.asList(String).drop(1)\n}\n\nAnd(~/^the Candidates cache should contain \"(.*)\"$/) { String candidates ->\n\tassert candidatesFile.text.trim() == candidates\n}\n"
  },
  {
    "path": "src/test/groovy/sdkman/steps/use_steps.groovy",
    "content": "package sdkman.steps\n\nimport java.nio.file.FileSystems\nimport java.nio.file.Files\n\nimport static cucumber.api.groovy.EN.And\n\nAnd(~'^the candidate \"([^\"]*)\" version \"([^\"]*)\" is in use$') { String candidate, String version ->\n\tdef directory = FileSystems.default.getPath(\"$candidatesDir/$candidate/$version\")\n\tdef current = FileSystems.default.getPath(\"$candidatesDir/$candidate/current\")\n\tdef symlinkFile = current.toFile()\n\tif (!symlinkFile.exists()) {\n\t\tassert Files.createSymbolicLink(current, directory)\n\t}\n}\n\nAnd(~'^the candidate \"([^\"]*)\" version \"([^\"]*)\" is not in use$') { String candidate, String version ->\n\tdef directory = FileSystems.default.getPath(\"$candidatesDir/$candidate/$version\")\n\tdef current = FileSystems.default.getPath(\"$candidatesDir/$candidate/current\")\n\tdef symlinkFile = current.toFile()\n\tif (symlinkFile.exists()) {\n\t\tassert !Files.isSameFile(current, directory)\n\t}\n}\n\nAnd(~'^the candidate \"([^\"]*)\" version \"([^\"]*)\" should be in use$') { String candidate, String version ->\n\tbash.execute(\"$candidate --version\")\n\tassert bash.output.contains(version)\n}\n\nAnd(~'^the candidate \"([^\"]*)\" version \"([^\"]*)\" should be the default$') { String candidate, String version ->\n\tdef directory = FileSystems.default.getPath(\"$candidatesDir/$candidate/$version\")\n\tdef current = FileSystems.default.getPath(\"$candidatesDir/$candidate/current\")\n\tassert Files.isSameFile(current, directory)\n}\n\nAnd(~'^the candidate \"([^\"]*)\" version \"([^\"]*)\" should not be the default$') { String candidate, String version ->\n\tdef directory = FileSystems.default.getPath(\"$candidatesDir/$candidate/$version\")\n\tdef current = FileSystems.default.getPath(\"$candidatesDir/$candidate/current\")\n\tassert (!Files.isSymbolicLink(current) || (Files.isSymbolicLink(current) && !Files.isSameFile(current, directory)))\n}\n\nAnd(~'^the candidate \"([^\"]*)\" is no longer selected$') { String candidate ->\n\tdef symlink = new File(\"$candidatesDir/$candidate/current\")\n\tassert !symlink.exists()\n}\n"
  },
  {
    "path": "src/test/groovy/sdkman/stubs/CurlStub.groovy",
    "content": "package sdkman.stubs\n\nclass CurlStub {\n\n\tprivate File file\n\tprivate commands = [:]\n\n\tstatic CurlStub prepareIn(File folder) {\n\t\tfolder.mkdirs()\n\n\t\tdef file = new File(folder, \"curl\")\n\t\tfile.createNewFile()\n\t\tfile.write \"#!/usr/bin/env bash\\n\"\n\t\tfile.executable = true\n\n\t\tnew CurlStub(file: file)\n\t}\n\n\tCurlStub primeWith(String request, String snippet) {\n\t\tcommands.put request, snippet\n\t\tthis\n\t}\n\n\tvoid build() {\n\t\tcommands.each { request, snippet ->\n\t\t\t//use last arg because we use curl with options\n\t\t\tfile << 'if [[ \"${@: -1}\" == \"'\n\t\t\tfile << \"$request\"\n\t\t\tfile << '\" ]]; then\\n'\n\t\t\tfile << \"    $snippet\\n\"\n\t\t\tfile << 'fi\\n'\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/test/groovy/sdkman/stubs/HookResponses.groovy",
    "content": "package sdkman.stubs\n\nclass HookResponses {\n\t\n\tstatic postInstallationHookSuccess() {\n\t\t'''\\\n#!/usr/bin/env bash\nfunction __sdkman_post_installation_hook {\n\tmv -f $binary_input $zip_output\n\techo \"Post-installation hook success\"\n\treturn 0\n}\n'''\n\t}\n\n\tstatic postInstallationHookFailure() {\n\t\t'''\\\n#!/usr/bin/env bash\nfunction __sdkman_post_installation_hook {\n\techo \"Post-installation hook failure\"\n\treturn 1\n}\n'''\n\t}\n}\n"
  },
  {
    "path": "src/test/groovy/sdkman/stubs/UnameStub.groovy",
    "content": "package sdkman.stubs\n\nclass UnameStub {\n\n\tprivate File file\n\tdef kernel = \"Linux\"\n\tdef machine = \"x86_64\"\n\n\tstatic UnameStub prepareIn(File folder) {\n\t\tfolder.mkdirs()\n\n\t\tdef file = new File(folder, \"uname\")\n\t\tfile.createNewFile()\n\t\tfile.write \"#!/usr/bin/env bash\\n\"\n\t\tfile.executable = true\n\n\t\tnew UnameStub(file: file)\n\t}\n\n\tUnameStub forKernel(String kernel) {\n\t\tthis.kernel = kernel\n\t\tthis\n\t}\n\t\n\tUnameStub forMachine(String machine) {\n\t\tthis.machine = machine\n\t\tthis\n\t}\n\t\n\tvoid build() {\n\t\tfile << \"\"\"\n\t\t\t|if [[ \"\\$1\" == '-m' ]]; then\n\t\t\t|\techo \"$machine\"\n\t\t\t|elif [[ \"\\$1\" == '-s' ]]; then\n\t\t\t|\techo \"$kernel\"\n\t\t\t|else\n\t\t\t|\techo \"$machine\"\n\t\t\t|fi\n\t\t\t\"\"\".stripMargin('|')\n\t}\n}\n"
  },
  {
    "path": "src/test/groovy/sdkman/stubs/WebServiceStub.groovy",
    "content": "package sdkman.stubs\n\nimport static com.github.tomakehurst.wiremock.client.WireMock.*\n\nclass WebServiceStub {\n\n\tstatic primeEndpointWithString(String endpoint, String body) {\n\t\tstubFor(get(urlEqualTo(endpoint)).willReturn(\n\t\t\t\taResponse()\n\t\t\t\t\t\t.withStatus(200)\n\t\t\t\t\t\t.withHeader(\"Content-Type\", \"text/plain\")\n\t\t\t\t\t\t.withBody(body)))\n\t}\n\n\tstatic primeUniversalHookFor(String candidate, String version, String platform) {\n\t\tprimeHookFor(candidate, version, platform, true)\n\t}\n\n\tstatic primePlatformSpecificHookFor(String candidate, String version, String platform) {\n\t\tprimeHookFor(candidate, version, platform, false)\n\t}\n\n\tprivate static primeHookFor(String candidate, String version, String platform, boolean universal = true) {\n\t\tdef hookFile = \"hooks/post_hook_${candidate}_${version}_${universal ? 'universal' : platform}.sh\"\n\t\tstubFor(get(urlEqualTo(\"/hooks/post/$candidate/$version/$platform\")).willReturn(\n\t\t\t\taResponse()\n\t\t\t\t\t\t.withStatus(200)\n\t\t\t\t\t\t.withHeader(\"Content-Type\", \"text/plain\")\n\t\t\t\t\t\t.withBodyFile(hookFile)))\n\t}\n\n\tstatic primeDownloadFor(String host, String candidate, String version, String platform) {\n\t\tprimeDownloadFor(host, candidate, version, platform, [:])\n\t}\n\t\n\tstatic primeDownloadFor(String host, String candidate, String version, String platform, Map<String, String> headers) {\n\t\tdef binary = (candidate == \"java\") ? \"jdk-${version}-${platform}.tar.gz\" : \"${candidate}-${version}.zip\"\n\t\tdef responseBuilder = aResponse()\n\t\t\t\t.withHeader(\"Location\", \"${host}/${binary}\")\n\t\t\t\t.withStatus(302)\n\t\theaders.each { responseBuilder.withHeader(it.key, it.value) }\n\n\t\tstubFor(get(urlEqualTo(\"/download/${candidate}/${version}/${platform}\"))\n\t\t\t\t.willReturn(responseBuilder))\n\n\t\tstubFor(get(urlEqualTo(\"/$binary\")).willReturn(\n\t\t\t\taResponse()\n\t\t\t\t\t\t.withStatus(200)\n\t\t\t\t\t\t.withHeader(\"Content-Type\", \"application/zip\")\n\t\t\t\t\t\t.withBodyFile(binary)))\n\t}\n\n\tstatic primeSelfupdate() {\n\t\tstubFor(get(urlEqualTo(\"/selfupdate\")).willReturn(\n\t\t\t\taResponse()\n\t\t\t\t\t\t.withStatus(200)\n\t\t\t\t\t\t.withHeader(\"Content-Type\", \"text/plain\")\n\t\t\t\t\t\t.withBodyFile(\"selfupdate.sh\")))\n\t}\n}\n"
  },
  {
    "path": "src/test/groovy/sdkman/support/BashEnvSpecification.groovy",
    "content": "package sdkman.support\n\nimport sdkman.env.BashEnv\nimport spock.lang.Specification\n\nabstract class BashEnvSpecification extends Specification {\n\n\tBashEnv bash\n\n\tvoid cleanup() {\n\t\tprintln bash.output\n\t\tbash.stop()\n\t}\n}\n"
  },
  {
    "path": "src/test/groovy/sdkman/support/FilesystemUtils.groovy",
    "content": "package sdkman.support\n\nimport java.nio.file.FileSystems\nimport java.nio.file.Files\nimport java.nio.file.Path\nimport java.nio.file.Paths\n\nclass FilesystemUtils {\n\n\tstatic final DEFAULT_BASE_DIR = \"/tmp/sdkman-test\"\n\n\tstatic readVersionsCsvFromCandidateFolder(File baseDir, String candidate) {\n\t\tdef versionFiles = new File(baseDir, candidate).listFiles()\n\t\tversionFiles?.findAll { it.name != \"current\" }?.sort()?.collect { it.name }?.join(\",\") ?: \"\"\n\t}\n\n\tstatic readCurrentFromCandidateFolder(File baseDir, String candidate) {\n\t\tdef versionFiles = new File(baseDir, candidate).listFiles()\n\t\tdef currentSymlinkPath = versionFiles.find { it.name == \"current\" }?.absolutePath\n\t\tcurrentSymlinkPath ? Files.readSymbolicLink(Paths.get(currentSymlinkPath)).fileName : \"\"\n\t}\n\n\tstatic prepareBaseDir() {\n\t\tdef baseDir = \"$DEFAULT_BASE_DIR/${UUID.randomUUID()}\" as File\n\t\tbaseDir.mkdirs()\n\t\tbaseDir\n\t}\n\n\tstatic prepareCandidateWithVersionFolder(String baseDir, String candidate, String version) {\n\t\tdef directory = \"$baseDir/$candidate/$version\"\n\t\tprepareCandidateBinFolder directory, candidate, version\n\t}\n\n\tstatic prepareCandidateBinFolder(String folder, String candidate, String version) {\n\t\tdef fileSystem = FileSystems.default\n\n\t\tdef binFolderPath = fileSystem.getPath(\"$folder/bin\")\n\t\tFiles.createDirectories binFolderPath\n\t\tprepareCandidateExecutable binFolderPath, candidate, version\n\n\t\treturn fileSystem.getPath(\"$folder\")\n\t}\n\n\tprivate static prepareCandidateExecutable(Path binFolder, String candidate, String version) {\n\t\tdef candidateFile = new File(\"$binFolder/$candidate\")\n\t\tcandidateFile.write \"echo ${candidate.capitalize()} Version: ${version}\"\n\t\tcandidateFile.executable = true\n\t}\n}\n"
  },
  {
    "path": "src/test/groovy/sdkman/support/SdkmanEnvSpecification.groovy",
    "content": "package sdkman.support\n\nimport sdkman.env.SdkmanBashEnvBuilder\nimport sdkman.stubs.CurlStub\nimport sdkman.stubs.UnameStub\n\nimport static sdkman.support.FilesystemUtils.prepareBaseDir\n\nabstract class SdkmanEnvSpecification extends BashEnvSpecification {\n\n\tSdkmanBashEnvBuilder sdkmanBashEnvBuilder\n\n\tCurlStub curlStub\n\tUnameStub unameStub\n\n\tFile sdkmanBaseDirectory\n\tFile sdkmanDotDirectory\n\tFile candidatesDirectory\n\n\tString bootstrapScript\n\n\tdef setup() {\n\t\tsdkmanBaseDirectory = prepareBaseDir()\n\t\tcurlStub = CurlStub.prepareIn(new File(sdkmanBaseDirectory, \"bin\"))\n\t\tunameStub = UnameStub.prepareIn(new File(sdkmanBaseDirectory, \"bin\"))\n\t\tsdkmanBashEnvBuilder = SdkmanBashEnvBuilder\n\t\t\t\t.create(sdkmanBaseDirectory)\n\t\t\t\t.withUnameStub(unameStub)\n\t\t\t\t.withCurlStub(curlStub)\n\n\t\tsdkmanDotDirectory = new File(sdkmanBaseDirectory, \".sdkman\")\n\t\tcandidatesDirectory = new File(sdkmanDotDirectory, \"candidates\")\n\t\tbootstrapScript = \"${sdkmanDotDirectory}/bin/sdkman-init.sh\"\n\t}\n\n\tdef cleanup() {\n\t\tassert sdkmanBaseDirectory.deleteDir()\n\t}\n}\n"
  },
  {
    "path": "src/test/groovy/sdkman/support/UnixUtils.groovy",
    "content": "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\t\t\"aarch64\": \"linuxarm64\",\n\t\t\t\t\t\n\t\t\t],\n\t\t\t\"Darwin\": [\n\t\t\t\t\t\"x86_64\": \"darwinx64\",\n\t\t\t\t\t\"arm64\": \"darwinarm64\",\n\t\t\t]\n\t]\n\n\tstatic execute(String command) {\n\t\tcommand.execute().text.trim()\n\t}\n\n\tstatic osName() { execute(\"uname -s\") }\n\n\tstatic osArch() { execute(\"uname -m\") }\n\n\tstatic inferPlatform(\n\t\t\tString osName = osName(),\n\t\t\tString architecture = osArch()) {\n\t\tplatforms[osName][architecture] ?: osName.toLowerCase()\n\t}\n}\n"
  },
  {
    "path": "src/test/groovy/sdkman/support/WireMockServerProvider.groovy",
    "content": "package sdkman.support\n\nimport com.github.tomakehurst.wiremock.WireMockServer\nimport com.github.tomakehurst.wiremock.client.WireMock\n\nimport static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig\n\nclass WireMockServerProvider {\n\n\tstatic SERVICE_UP_HOST = \"localhost\"\n\tstatic SERVICE_UP_PORT = 8080\n\tstatic WireMockServer wireMockServer\n\n\tstatic def wireMockServer() {\n\t\twireMockServer ?: createWireMockServer()\n\t}\n\n\tprivate static def createWireMockServer() {\n\t\twireMockServer = new WireMockServer(wireMockConfig().port(SERVICE_UP_PORT))\n\t\twireMockServer.start()\n\t\tWireMock.configureFor(SERVICE_UP_HOST, SERVICE_UP_PORT)\n\t\twireMockServer\n\t}\n}\n"
  },
  {
    "path": "src/test/jmeter/SDKman.jmx",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<jmeterTestPlan version=\"1.2\" properties=\"2.5\" jmeter=\"2.10 r1533061\">\n  <hashTree>\n    <TestPlan guiclass=\"TestPlanGui\" testclass=\"TestPlan\" testname=\"SDKman\" enabled=\"true\">\n      <stringProp name=\"TestPlan.comments\"></stringProp>\n      <boolProp name=\"TestPlan.functional_mode\">false</boolProp>\n      <boolProp name=\"TestPlan.serialize_threadgroups\">false</boolProp>\n      <elementProp name=\"TestPlan.user_defined_variables\" elementType=\"Arguments\" guiclass=\"ArgumentsPanel\" testclass=\"Arguments\" testname=\"User Defined Variables\" enabled=\"true\">\n        <collectionProp name=\"Arguments.arguments\"/>\n      </elementProp>\n      <stringProp name=\"TestPlan.user_define_classpath\"></stringProp>\n    </TestPlan>\n    <hashTree>\n      <ThreadGroup guiclass=\"ThreadGroupGui\" testclass=\"ThreadGroup\" testname=\"Thread Group\" enabled=\"true\">\n        <stringProp name=\"ThreadGroup.on_sample_error\">continue</stringProp>\n        <elementProp name=\"ThreadGroup.main_controller\" elementType=\"LoopController\" guiclass=\"LoopControlPanel\" testclass=\"LoopController\" testname=\"Loop Controller\" enabled=\"true\">\n          <boolProp name=\"LoopController.continue_forever\">false</boolProp>\n          <intProp name=\"LoopController.loops\">-1</intProp>\n        </elementProp>\n        <stringProp name=\"ThreadGroup.num_threads\">1</stringProp>\n        <stringProp name=\"ThreadGroup.ramp_time\">1</stringProp>\n        <longProp name=\"ThreadGroup.start_time\">1382782760000</longProp>\n        <longProp name=\"ThreadGroup.end_time\">1382782760000</longProp>\n        <boolProp name=\"ThreadGroup.scheduler\">false</boolProp>\n        <stringProp name=\"ThreadGroup.duration\"></stringProp>\n        <stringProp name=\"ThreadGroup.delay\"></stringProp>\n      </ThreadGroup>\n      <hashTree>\n        <CounterConfig guiclass=\"CounterConfigGui\" testclass=\"CounterConfig\" testname=\"Counter\" enabled=\"true\">\n          <stringProp name=\"CounterConfig.start\">0</stringProp>\n          <stringProp name=\"CounterConfig.end\">100000</stringProp>\n          <stringProp name=\"CounterConfig.incr\">1</stringProp>\n          <stringProp name=\"CounterConfig.name\">counter</stringProp>\n          <stringProp name=\"CounterConfig.format\">00000</stringProp>\n          <boolProp name=\"CounterConfig.per_user\">true</boolProp>\n        </CounterConfig>\n        <hashTree/>\n        <Arguments guiclass=\"ArgumentsPanel\" testclass=\"Arguments\" testname=\"User Defined Variables\" enabled=\"true\">\n          <collectionProp name=\"Arguments.arguments\">\n            <elementProp name=\"host\" elementType=\"Argument\">\n              <stringProp name=\"Argument.name\">host</stringProp>\n              <stringProp name=\"Argument.value\">localhost</stringProp>\n              <stringProp name=\"Argument.metadata\">=</stringProp>\n            </elementProp>\n            <elementProp name=\"port\" elementType=\"Argument\">\n              <stringProp name=\"Argument.name\">port</stringProp>\n              <stringProp name=\"Argument.value\">8080</stringProp>\n              <stringProp name=\"Argument.metadata\">=</stringProp>\n            </elementProp>\n          </collectionProp>\n        </Arguments>\n        <hashTree/>\n        <CSVDataSet guiclass=\"TestBeanGUI\" testclass=\"CSVDataSet\" testname=\"CSV Data Set Config\" enabled=\"true\">\n          <stringProp name=\"delimiter\">,</stringProp>\n          <stringProp name=\"fileEncoding\"></stringProp>\n          <stringProp name=\"filename\">candidates.csv</stringProp>\n          <boolProp name=\"quotedData\">false</boolProp>\n          <boolProp name=\"recycle\">true</boolProp>\n          <stringProp name=\"shareMode\">shareMode.thread</stringProp>\n          <boolProp name=\"stopThread\">false</boolProp>\n          <stringProp name=\"variableNames\">candidate,version</stringProp>\n        </CSVDataSet>\n        <hashTree/>\n        <HTTPSamplerProxy guiclass=\"HttpTestSampleGui\" testclass=\"HTTPSamplerProxy\" testname=\"API Alive\" enabled=\"true\">\n          <elementProp name=\"HTTPsampler.Arguments\" elementType=\"Arguments\" guiclass=\"HTTPArgumentsPanel\" testclass=\"Arguments\" testname=\"User Defined Variables\" enabled=\"true\">\n            <collectionProp name=\"Arguments.arguments\"/>\n          </elementProp>\n          <stringProp name=\"HTTPSampler.domain\">${host}</stringProp>\n          <stringProp name=\"HTTPSampler.port\">${port}</stringProp>\n          <stringProp name=\"HTTPSampler.connect_timeout\"></stringProp>\n          <stringProp name=\"HTTPSampler.response_timeout\"></stringProp>\n          <stringProp name=\"HTTPSampler.protocol\"></stringProp>\n          <stringProp name=\"HTTPSampler.contentEncoding\"></stringProp>\n          <stringProp name=\"HTTPSampler.path\">/alive</stringProp>\n          <stringProp name=\"HTTPSampler.method\">GET</stringProp>\n          <boolProp name=\"HTTPSampler.follow_redirects\">false</boolProp>\n          <boolProp name=\"HTTPSampler.auto_redirects\">false</boolProp>\n          <boolProp name=\"HTTPSampler.use_keepalive\">true</boolProp>\n          <boolProp name=\"HTTPSampler.DO_MULTIPART_POST\">false</boolProp>\n          <stringProp name=\"HTTPSampler.implementation\">Java</stringProp>\n          <boolProp name=\"HTTPSampler.monitor\">false</boolProp>\n          <stringProp name=\"HTTPSampler.embedded_url_re\"></stringProp>\n        </HTTPSamplerProxy>\n        <hashTree>\n          <ResponseAssertion guiclass=\"AssertionGui\" testclass=\"ResponseAssertion\" testname=\"Response Assertion\" enabled=\"true\">\n            <collectionProp name=\"Asserion.test_strings\">\n              <stringProp name=\"2524\">OK</stringProp>\n            </collectionProp>\n            <stringProp name=\"Assertion.test_field\">Assertion.response_data</stringProp>\n            <boolProp name=\"Assertion.assume_success\">false</boolProp>\n            <intProp name=\"Assertion.test_type\">2</intProp>\n          </ResponseAssertion>\n          <hashTree/>\n        </hashTree>\n        <HTTPSamplerProxy guiclass=\"HttpTestSampleGui\" testclass=\"HTTPSamplerProxy\" testname=\"Candidates\" enabled=\"true\">\n          <elementProp name=\"HTTPsampler.Arguments\" elementType=\"Arguments\" guiclass=\"HTTPArgumentsPanel\" testclass=\"Arguments\" testname=\"User Defined Variables\" enabled=\"true\">\n            <collectionProp name=\"Arguments.arguments\"/>\n          </elementProp>\n          <stringProp name=\"HTTPSampler.domain\">${host}</stringProp>\n          <stringProp name=\"HTTPSampler.port\">${port}</stringProp>\n          <stringProp name=\"HTTPSampler.connect_timeout\"></stringProp>\n          <stringProp name=\"HTTPSampler.response_timeout\"></stringProp>\n          <stringProp name=\"HTTPSampler.protocol\"></stringProp>\n          <stringProp name=\"HTTPSampler.contentEncoding\"></stringProp>\n          <stringProp name=\"HTTPSampler.path\">/candidates</stringProp>\n          <stringProp name=\"HTTPSampler.method\">GET</stringProp>\n          <boolProp name=\"HTTPSampler.follow_redirects\">false</boolProp>\n          <boolProp name=\"HTTPSampler.auto_redirects\">false</boolProp>\n          <boolProp name=\"HTTPSampler.use_keepalive\">true</boolProp>\n          <boolProp name=\"HTTPSampler.DO_MULTIPART_POST\">false</boolProp>\n          <stringProp name=\"HTTPSampler.implementation\">Java</stringProp>\n          <boolProp name=\"HTTPSampler.monitor\">false</boolProp>\n          <stringProp name=\"HTTPSampler.embedded_url_re\"></stringProp>\n        </HTTPSamplerProxy>\n        <hashTree>\n          <ResponseAssertion guiclass=\"AssertionGui\" testclass=\"ResponseAssertion\" testname=\"Response Assertion\" enabled=\"true\">\n            <collectionProp name=\"Asserion.test_strings\">\n              <stringProp name=\"-1237466098\">groovy</stringProp>\n              <stringProp name=\"-1237889254\">grails</stringProp>\n              <stringProp name=\"287431805\">griffon</stringProp>\n              <stringProp name=\"-1237894073\">gradle</stringProp>\n            </collectionProp>\n            <stringProp name=\"Assertion.test_field\">Assertion.response_data</stringProp>\n            <boolProp name=\"Assertion.assume_success\">false</boolProp>\n            <intProp name=\"Assertion.test_type\">2</intProp>\n          </ResponseAssertion>\n          <hashTree/>\n        </hashTree>\n        <HTTPSamplerProxy guiclass=\"HttpTestSampleGui\" testclass=\"HTTPSamplerProxy\" testname=\"Candidate Validate\" enabled=\"true\">\n          <elementProp name=\"HTTPsampler.Arguments\" elementType=\"Arguments\" guiclass=\"HTTPArgumentsPanel\" testclass=\"Arguments\" testname=\"User Defined Variables\" enabled=\"true\">\n            <collectionProp name=\"Arguments.arguments\"/>\n          </elementProp>\n          <stringProp name=\"HTTPSampler.domain\">${host}</stringProp>\n          <stringProp name=\"HTTPSampler.port\">${port}</stringProp>\n          <stringProp name=\"HTTPSampler.connect_timeout\"></stringProp>\n          <stringProp name=\"HTTPSampler.response_timeout\"></stringProp>\n          <stringProp name=\"HTTPSampler.protocol\"></stringProp>\n          <stringProp name=\"HTTPSampler.contentEncoding\"></stringProp>\n          <stringProp name=\"HTTPSampler.path\">/candidates/${candidate}/${version}/validate</stringProp>\n          <stringProp name=\"HTTPSampler.method\">GET</stringProp>\n          <boolProp name=\"HTTPSampler.follow_redirects\">false</boolProp>\n          <boolProp name=\"HTTPSampler.auto_redirects\">false</boolProp>\n          <boolProp name=\"HTTPSampler.use_keepalive\">true</boolProp>\n          <boolProp name=\"HTTPSampler.DO_MULTIPART_POST\">false</boolProp>\n          <stringProp name=\"HTTPSampler.implementation\">Java</stringProp>\n          <boolProp name=\"HTTPSampler.monitor\">false</boolProp>\n          <stringProp name=\"HTTPSampler.embedded_url_re\"></stringProp>\n        </HTTPSamplerProxy>\n        <hashTree>\n          <ResponseAssertion guiclass=\"AssertionGui\" testclass=\"ResponseAssertion\" testname=\"Response Assertion\" enabled=\"true\">\n            <collectionProp name=\"Asserion.test_strings\">\n              <stringProp name=\"111972348\">valid</stringProp>\n            </collectionProp>\n            <stringProp name=\"Assertion.test_field\">Assertion.response_data</stringProp>\n            <boolProp name=\"Assertion.assume_success\">false</boolProp>\n            <intProp name=\"Assertion.test_type\">8</intProp>\n          </ResponseAssertion>\n          <hashTree/>\n        </hashTree>\n        <HTTPSamplerProxy guiclass=\"HttpTestSampleGui\" testclass=\"HTTPSamplerProxy\" testname=\"API Version\" enabled=\"true\">\n          <elementProp name=\"HTTPsampler.Arguments\" elementType=\"Arguments\" guiclass=\"HTTPArgumentsPanel\" testclass=\"Arguments\" testname=\"User Defined Variables\" enabled=\"true\">\n            <collectionProp name=\"Arguments.arguments\"/>\n          </elementProp>\n          <stringProp name=\"HTTPSampler.domain\">${host}</stringProp>\n          <stringProp name=\"HTTPSampler.port\">${port}</stringProp>\n          <stringProp name=\"HTTPSampler.connect_timeout\"></stringProp>\n          <stringProp name=\"HTTPSampler.response_timeout\"></stringProp>\n          <stringProp name=\"HTTPSampler.protocol\"></stringProp>\n          <stringProp name=\"HTTPSampler.contentEncoding\"></stringProp>\n          <stringProp name=\"HTTPSampler.path\">/api/version</stringProp>\n          <stringProp name=\"HTTPSampler.method\">GET</stringProp>\n          <boolProp name=\"HTTPSampler.follow_redirects\">false</boolProp>\n          <boolProp name=\"HTTPSampler.auto_redirects\">false</boolProp>\n          <boolProp name=\"HTTPSampler.use_keepalive\">true</boolProp>\n          <boolProp name=\"HTTPSampler.DO_MULTIPART_POST\">false</boolProp>\n          <stringProp name=\"HTTPSampler.implementation\">Java</stringProp>\n          <boolProp name=\"HTTPSampler.monitor\">false</boolProp>\n          <stringProp name=\"HTTPSampler.embedded_url_re\"></stringProp>\n        </HTTPSamplerProxy>\n        <hashTree>\n          <ResponseAssertion guiclass=\"AssertionGui\" testclass=\"ResponseAssertion\" testname=\"Response Assertion\" enabled=\"true\">\n            <collectionProp name=\"Asserion.test_strings\">\n              <stringProp name=\"46670517\">1.0.0</stringProp>\n              <stringProp name=\"0\"></stringProp>\n            </collectionProp>\n            <stringProp name=\"Assertion.test_field\">Assertion.response_data</stringProp>\n            <boolProp name=\"Assertion.assume_success\">false</boolProp>\n            <intProp name=\"Assertion.test_type\">2</intProp>\n          </ResponseAssertion>\n          <hashTree/>\n        </hashTree>\n        <HTTPSamplerProxy guiclass=\"HttpTestSampleGui\" testclass=\"HTTPSamplerProxy\" testname=\"Candidate Default\" enabled=\"true\">\n          <elementProp name=\"HTTPsampler.Arguments\" elementType=\"Arguments\" guiclass=\"HTTPArgumentsPanel\" testclass=\"Arguments\" testname=\"User Defined Variables\" enabled=\"true\">\n            <collectionProp name=\"Arguments.arguments\"/>\n          </elementProp>\n          <stringProp name=\"HTTPSampler.domain\">${host}</stringProp>\n          <stringProp name=\"HTTPSampler.port\">${port}</stringProp>\n          <stringProp name=\"HTTPSampler.connect_timeout\"></stringProp>\n          <stringProp name=\"HTTPSampler.response_timeout\"></stringProp>\n          <stringProp name=\"HTTPSampler.protocol\"></stringProp>\n          <stringProp name=\"HTTPSampler.contentEncoding\"></stringProp>\n          <stringProp name=\"HTTPSampler.path\">/candidates/${candidate}/default</stringProp>\n          <stringProp name=\"HTTPSampler.method\">GET</stringProp>\n          <boolProp name=\"HTTPSampler.follow_redirects\">false</boolProp>\n          <boolProp name=\"HTTPSampler.auto_redirects\">false</boolProp>\n          <boolProp name=\"HTTPSampler.use_keepalive\">true</boolProp>\n          <boolProp name=\"HTTPSampler.DO_MULTIPART_POST\">false</boolProp>\n          <stringProp name=\"HTTPSampler.implementation\">Java</stringProp>\n          <boolProp name=\"HTTPSampler.monitor\">false</boolProp>\n          <stringProp name=\"HTTPSampler.embedded_url_re\"></stringProp>\n        </HTTPSamplerProxy>\n        <hashTree>\n          <ResponseAssertion guiclass=\"AssertionGui\" testclass=\"ResponseAssertion\" testname=\"Response Assertion\" enabled=\"true\">\n            <collectionProp name=\"Asserion.test_strings\">\n              <stringProp name=\"551795836\">${version}</stringProp>\n            </collectionProp>\n            <stringProp name=\"Assertion.test_field\">Assertion.response_data</stringProp>\n            <boolProp name=\"Assertion.assume_success\">false</boolProp>\n            <intProp name=\"Assertion.test_type\">2</intProp>\n          </ResponseAssertion>\n          <hashTree/>\n        </hashTree>\n        <HTTPSamplerProxy guiclass=\"HttpTestSampleGui\" testclass=\"HTTPSamplerProxy\" testname=\"Candidate List\" enabled=\"true\">\n          <elementProp name=\"HTTPsampler.Arguments\" elementType=\"Arguments\" guiclass=\"HTTPArgumentsPanel\" testclass=\"Arguments\" testname=\"User Defined Variables\" enabled=\"true\">\n            <collectionProp name=\"Arguments.arguments\">\n              <elementProp name=\"platform\" elementType=\"HTTPArgument\">\n                <boolProp name=\"HTTPArgument.always_encode\">false</boolProp>\n                <stringProp name=\"Argument.value\">Linux</stringProp>\n                <stringProp name=\"Argument.metadata\">=</stringProp>\n                <boolProp name=\"HTTPArgument.use_equals\">true</boolProp>\n                <stringProp name=\"Argument.name\">platform</stringProp>\n              </elementProp>\n              <elementProp name=\"current\" elementType=\"HTTPArgument\">\n                <boolProp name=\"HTTPArgument.always_encode\">false</boolProp>\n                <stringProp name=\"Argument.value\">${version}</stringProp>\n                <stringProp name=\"Argument.metadata\">=</stringProp>\n                <boolProp name=\"HTTPArgument.use_equals\">true</boolProp>\n                <stringProp name=\"Argument.name\">current</stringProp>\n              </elementProp>\n              <elementProp name=\"installed\" elementType=\"HTTPArgument\">\n                <boolProp name=\"HTTPArgument.always_encode\">false</boolProp>\n                <stringProp name=\"Argument.value\">${version},local${counter}</stringProp>\n                <stringProp name=\"Argument.metadata\">=</stringProp>\n                <boolProp name=\"HTTPArgument.use_equals\">true</boolProp>\n                <stringProp name=\"Argument.name\">installed</stringProp>\n              </elementProp>\n            </collectionProp>\n          </elementProp>\n          <stringProp name=\"HTTPSampler.domain\">${host}</stringProp>\n          <stringProp name=\"HTTPSampler.port\">${port}</stringProp>\n          <stringProp name=\"HTTPSampler.connect_timeout\"></stringProp>\n          <stringProp name=\"HTTPSampler.response_timeout\"></stringProp>\n          <stringProp name=\"HTTPSampler.protocol\"></stringProp>\n          <stringProp name=\"HTTPSampler.contentEncoding\"></stringProp>\n          <stringProp name=\"HTTPSampler.path\">/candidates/${candidate}/list</stringProp>\n          <stringProp name=\"HTTPSampler.method\">GET</stringProp>\n          <boolProp name=\"HTTPSampler.follow_redirects\">false</boolProp>\n          <boolProp name=\"HTTPSampler.auto_redirects\">false</boolProp>\n          <boolProp name=\"HTTPSampler.use_keepalive\">true</boolProp>\n          <boolProp name=\"HTTPSampler.DO_MULTIPART_POST\">false</boolProp>\n          <stringProp name=\"HTTPSampler.implementation\">Java</stringProp>\n          <boolProp name=\"HTTPSampler.monitor\">false</boolProp>\n          <stringProp name=\"HTTPSampler.embedded_url_re\"></stringProp>\n        </HTTPSamplerProxy>\n        <hashTree>\n          <ResponseAssertion guiclass=\"AssertionGui\" testclass=\"ResponseAssertion\" testname=\"Response Assertion\" enabled=\"true\">\n            <collectionProp name=\"Asserion.test_strings\">\n              <stringProp name=\"1270065833\">Available</stringProp>\n              <stringProp name=\"0\"></stringProp>\n            </collectionProp>\n            <stringProp name=\"Assertion.test_field\">Assertion.response_data</stringProp>\n            <boolProp name=\"Assertion.assume_success\">false</boolProp>\n            <intProp name=\"Assertion.test_type\">2</intProp>\n          </ResponseAssertion>\n          <hashTree/>\n        </hashTree>\n        <HTTPSamplerProxy guiclass=\"HttpTestSampleGui\" testclass=\"HTTPSamplerProxy\" testname=\"API Broadcast\" enabled=\"true\">\n          <elementProp name=\"HTTPsampler.Arguments\" elementType=\"Arguments\" guiclass=\"HTTPArgumentsPanel\" testclass=\"Arguments\" testname=\"User Defined Variables\" enabled=\"true\">\n            <collectionProp name=\"Arguments.arguments\"/>\n          </elementProp>\n          <stringProp name=\"HTTPSampler.domain\">${host}</stringProp>\n          <stringProp name=\"HTTPSampler.port\">${port}</stringProp>\n          <stringProp name=\"HTTPSampler.connect_timeout\"></stringProp>\n          <stringProp name=\"HTTPSampler.response_timeout\"></stringProp>\n          <stringProp name=\"HTTPSampler.protocol\"></stringProp>\n          <stringProp name=\"HTTPSampler.contentEncoding\"></stringProp>\n          <stringProp name=\"HTTPSampler.path\">/api/broadcast</stringProp>\n          <stringProp name=\"HTTPSampler.method\">GET</stringProp>\n          <boolProp name=\"HTTPSampler.follow_redirects\">false</boolProp>\n          <boolProp name=\"HTTPSampler.auto_redirects\">false</boolProp>\n          <boolProp name=\"HTTPSampler.use_keepalive\">true</boolProp>\n          <boolProp name=\"HTTPSampler.DO_MULTIPART_POST\">false</boolProp>\n          <stringProp name=\"HTTPSampler.implementation\">Java</stringProp>\n          <boolProp name=\"HTTPSampler.monitor\">false</boolProp>\n          <stringProp name=\"HTTPSampler.embedded_url_re\"></stringProp>\n        </HTTPSamplerProxy>\n        <hashTree>\n          <ResponseAssertion guiclass=\"AssertionGui\" testclass=\"ResponseAssertion\" testname=\"Response Assertion\" enabled=\"true\">\n            <collectionProp name=\"Asserion.test_strings\">\n              <stringProp name=\"-302697722\">Report issues</stringProp>\n            </collectionProp>\n            <stringProp name=\"Assertion.test_field\">Assertion.response_data</stringProp>\n            <boolProp name=\"Assertion.assume_success\">false</boolProp>\n            <intProp name=\"Assertion.test_type\">2</intProp>\n          </ResponseAssertion>\n          <hashTree/>\n        </hashTree>\n        <HTTPSamplerProxy guiclass=\"HttpTestSampleGui\" testclass=\"HTTPSamplerProxy\" testname=\"Candidate Versions\" enabled=\"true\">\n          <elementProp name=\"HTTPsampler.Arguments\" elementType=\"Arguments\" guiclass=\"HTTPArgumentsPanel\" testclass=\"Arguments\" testname=\"User Defined Variables\" enabled=\"true\">\n            <collectionProp name=\"Arguments.arguments\"/>\n          </elementProp>\n          <stringProp name=\"HTTPSampler.domain\">${host}</stringProp>\n          <stringProp name=\"HTTPSampler.port\">${port}</stringProp>\n          <stringProp name=\"HTTPSampler.connect_timeout\"></stringProp>\n          <stringProp name=\"HTTPSampler.response_timeout\"></stringProp>\n          <stringProp name=\"HTTPSampler.protocol\"></stringProp>\n          <stringProp name=\"HTTPSampler.contentEncoding\"></stringProp>\n          <stringProp name=\"HTTPSampler.path\">/candidates/${candidate}</stringProp>\n          <stringProp name=\"HTTPSampler.method\">GET</stringProp>\n          <boolProp name=\"HTTPSampler.follow_redirects\">false</boolProp>\n          <boolProp name=\"HTTPSampler.auto_redirects\">false</boolProp>\n          <boolProp name=\"HTTPSampler.use_keepalive\">true</boolProp>\n          <boolProp name=\"HTTPSampler.DO_MULTIPART_POST\">false</boolProp>\n          <stringProp name=\"HTTPSampler.implementation\">Java</stringProp>\n          <boolProp name=\"HTTPSampler.monitor\">false</boolProp>\n          <stringProp name=\"HTTPSampler.embedded_url_re\"></stringProp>\n        </HTTPSamplerProxy>\n        <hashTree>\n          <ResponseAssertion guiclass=\"AssertionGui\" testclass=\"ResponseAssertion\" testname=\"Response Assertion\" enabled=\"true\">\n            <collectionProp name=\"Asserion.test_strings\">\n              <stringProp name=\"551795836\">${version}</stringProp>\n            </collectionProp>\n            <stringProp name=\"Assertion.test_field\">Assertion.response_data</stringProp>\n            <boolProp name=\"Assertion.assume_success\">false</boolProp>\n            <intProp name=\"Assertion.test_type\">2</intProp>\n          </ResponseAssertion>\n          <hashTree/>\n        </hashTree>\n        <HTTPSamplerProxy guiclass=\"HttpTestSampleGui\" testclass=\"HTTPSamplerProxy\" testname=\"Candidate Download\" enabled=\"true\">\n          <elementProp name=\"HTTPsampler.Arguments\" elementType=\"Arguments\" guiclass=\"HTTPArgumentsPanel\" testclass=\"Arguments\" testname=\"User Defined Variables\" enabled=\"true\">\n            <collectionProp name=\"Arguments.arguments\"/>\n          </elementProp>\n          <stringProp name=\"HTTPSampler.domain\">${host}</stringProp>\n          <stringProp name=\"HTTPSampler.port\">${port}</stringProp>\n          <stringProp name=\"HTTPSampler.connect_timeout\"></stringProp>\n          <stringProp name=\"HTTPSampler.response_timeout\"></stringProp>\n          <stringProp name=\"HTTPSampler.protocol\"></stringProp>\n          <stringProp name=\"HTTPSampler.contentEncoding\"></stringProp>\n          <stringProp name=\"HTTPSampler.path\">/candidates/${candidate}/${version}/download</stringProp>\n          <stringProp name=\"HTTPSampler.method\">GET</stringProp>\n          <boolProp name=\"HTTPSampler.follow_redirects\">false</boolProp>\n          <boolProp name=\"HTTPSampler.auto_redirects\">false</boolProp>\n          <boolProp name=\"HTTPSampler.use_keepalive\">true</boolProp>\n          <boolProp name=\"HTTPSampler.DO_MULTIPART_POST\">false</boolProp>\n          <stringProp name=\"HTTPSampler.implementation\">Java</stringProp>\n          <boolProp name=\"HTTPSampler.monitor\">false</boolProp>\n          <stringProp name=\"HTTPSampler.embedded_url_re\"></stringProp>\n        </HTTPSamplerProxy>\n        <hashTree>\n          <ResponseAssertion guiclass=\"AssertionGui\" testclass=\"ResponseAssertion\" testname=\"Response Assertion\" enabled=\"true\">\n            <collectionProp name=\"Asserion.test_strings\">\n              <stringProp name=\"50549\">302</stringProp>\n            </collectionProp>\n            <stringProp name=\"Assertion.test_field\">Assertion.response_code</stringProp>\n            <boolProp name=\"Assertion.assume_success\">false</boolProp>\n            <intProp name=\"Assertion.test_type\">8</intProp>\n          </ResponseAssertion>\n          <hashTree/>\n        </hashTree>\n        <ResultCollector guiclass=\"ViewResultsFullVisualizer\" testclass=\"ResultCollector\" testname=\"View Results Tree\" enabled=\"true\">\n          <boolProp name=\"ResultCollector.error_logging\">false</boolProp>\n          <objProp>\n            <name>saveConfig</name>\n            <value class=\"SampleSaveConfiguration\">\n              <time>true</time>\n              <latency>true</latency>\n              <timestamp>true</timestamp>\n              <success>true</success>\n              <label>true</label>\n              <code>true</code>\n              <message>true</message>\n              <threadName>true</threadName>\n              <dataType>true</dataType>\n              <encoding>false</encoding>\n              <assertions>true</assertions>\n              <subresults>true</subresults>\n              <responseData>false</responseData>\n              <samplerData>false</samplerData>\n              <xml>false</xml>\n              <fieldNames>false</fieldNames>\n              <responseHeaders>false</responseHeaders>\n              <requestHeaders>false</requestHeaders>\n              <responseDataOnError>false</responseDataOnError>\n              <saveAssertionResultsFailureMessage>false</saveAssertionResultsFailureMessage>\n              <assertionsResultsToSave>0</assertionsResultsToSave>\n              <bytes>true</bytes>\n            </value>\n          </objProp>\n          <stringProp name=\"filename\"></stringProp>\n        </ResultCollector>\n        <hashTree/>\n        <ResultCollector guiclass=\"StatVisualizer\" testclass=\"ResultCollector\" testname=\"Aggregate Report\" enabled=\"true\">\n          <boolProp name=\"ResultCollector.error_logging\">false</boolProp>\n          <objProp>\n            <name>saveConfig</name>\n            <value class=\"SampleSaveConfiguration\">\n              <time>true</time>\n              <latency>true</latency>\n              <timestamp>true</timestamp>\n              <success>true</success>\n              <label>true</label>\n              <code>true</code>\n              <message>true</message>\n              <threadName>true</threadName>\n              <dataType>true</dataType>\n              <encoding>false</encoding>\n              <assertions>true</assertions>\n              <subresults>true</subresults>\n              <responseData>false</responseData>\n              <samplerData>false</samplerData>\n              <xml>false</xml>\n              <fieldNames>false</fieldNames>\n              <responseHeaders>false</responseHeaders>\n              <requestHeaders>false</requestHeaders>\n              <responseDataOnError>false</responseDataOnError>\n              <saveAssertionResultsFailureMessage>false</saveAssertionResultsFailureMessage>\n              <assertionsResultsToSave>0</assertionsResultsToSave>\n              <bytes>true</bytes>\n            </value>\n          </objProp>\n          <stringProp name=\"filename\"></stringProp>\n        </ResultCollector>\n        <hashTree/>\n        <ConstantTimer guiclass=\"ConstantTimerGui\" testclass=\"ConstantTimer\" testname=\"Constant Timer\" enabled=\"true\">\n          <stringProp name=\"ConstantTimer.delay\">250</stringProp>\n        </ConstantTimer>\n        <hashTree/>\n      </hashTree>\n    </hashTree>\n  </hashTree>\n</jmeterTestPlan>\n"
  },
  {
    "path": "src/test/jmeter/candidates.csv",
    "content": "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",
    "content": "#!/usr/bin/env bash\n# failed download with non-zero exit code\nfunction __sdkman_post_installation_hook() {\n\techo \"POST: fails with non-zero exit code\"\n\techo \"Cannot install java 8.0.101 at this time...\"\n\techo \"Download has failed, aborting!\"\n\treturn 1\n}\n"
  },
  {
    "path": "src/test/resources/__files/hooks/post_hook_java_8.0.111_linuxx64.sh",
    "content": "#!/usr/bin/env bash\n# convert tar.gz to zip\nfunction __sdkman_post_installation_hook() {\n\techo \"POST: converting $binary_input to $zip_output\"\n\tmkdir -p \"$SDKMAN_DIR/tmp/out\"\n\t/usr/bin/env tar zxvf \"$binary_input\" -C \"${SDKMAN_DIR}/tmp/out\"\n\tcd \"${SDKMAN_DIR}/tmp/out\"\n\t/usr/bin/env zip -r \"$zip_output\" .\n}\n"
  },
  {
    "path": "src/test/resources/__files/hooks/post_hook_java_8.0.111_universal.sh",
    "content": "#!/usr/bin/env bash\n# passthrough used for zip binaries\nfunction __sdkman_post_installation_hook() {\n\techo \"POST: passthrough $binary_input to $zip_output\"\n\tmv -f \"$binary_input\" \"$zip_output\"\n}\n"
  },
  {
    "path": "src/test/resources/features/checksum_verification.feature",
    "content": "Feature: Verify checksums\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\t\tAnd I have configured \"sdkman_checksum_enable\" to \"true\"\n\n\tScenario: Install a specific Version with a valid SHA-256 checksum\n\t\tGiven the system is bootstrapped\n\t\tAnd the candidate \"grails\" version \"1.3.9\" is available for download with checksum \"1f9234c8e622ec46d33883ea45b39ede768b92d478fe08f6952548247f7fbb65\" using algorithm \"SHA-256\"\n\t\tWhen I enter \"sdk install grails 1.3.9\"\n\t\tThen I see \"Done installing!\"\n\t\tAnd the candidate \"grails\" version \"1.3.9\" is installed\n\t\tAnd the response headers file is created for candidate \"grails\" and version \"1.3.9\"\n\t\tAnd the exit code is 0\n\n\tScenario: Install a specific Version with a valid SHA1 checksum\n\t\tGiven the system is bootstrapped\n\t\tAnd the candidate \"grails\" version \"1.3.9\" is available for download with checksum \"c68e386a6deec9fc4c1e18df21f92739ba2ab36e\" using algorithm \"SHA1\"\n\t\tWhen I enter \"sdk install grails 1.3.9\"\n\t\tThen I see \"Done installing!\"\n\t\tAnd the candidate \"grails\" version \"1.3.9\" is installed\n\t\tAnd the response headers file is created for candidate \"grails\" and version \"1.3.9\"\n\t\tAnd the exit code is 0\n\n\tScenario: Install a specific Version with a valid MD5 checksum\n\t\tGiven the system is bootstrapped\n\t\tAnd the candidate \"grails\" version \"1.3.9\" is available for download with checksum \"1e87a7d982a2f41da96fdec289908552\" using algorithm \"MD5\"\n\t\tWhen I enter \"sdk install grails 1.3.9\"\n\t\tThen I see \"Done installing!\"\n\t\tAnd the candidate \"grails\" version \"1.3.9\" is installed\n\t\tAnd the response headers file is created for candidate \"grails\" and version \"1.3.9\"\n\t\tAnd the exit code is 0\n\n\tScenario: Do not fail if an unknown algorithm is used\n\t\tGiven the system is bootstrapped\n\t\tAnd the candidate \"grails\" version \"1.3.9\" is available for download with checksum \"abc-checksum-00000\" using algorithm \"ABC\"\n\t\tWhen I enter \"sdk install grails 1.3.9\"\n\t\tThen I see \"Done installing!\"\n\t\tAnd the candidate \"grails\" version \"1.3.9\" is installed\n\t\tAnd the response headers file is created for candidate \"grails\" and version \"1.3.9\"\n\t\tAnd the exit code is 0\n\n\tScenario: Do not fail if no algorithm is detected\n\t\tGiven the system is bootstrapped\n\t\tAnd the candidate \"grails\" version \"1.3.9\" is available for download\n\t\tWhen I enter \"sdk install grails 1.3.9\"\n\t\tThen I see \"Done installing!\"\n\t\tAnd I do not see \"Stop! An invalid checksum was detected and the archive removed! Please try re-installing.\"\n\t\tAnd the candidate \"grails\" version \"1.3.9\" is installed\n\t\tAnd the response headers file is created for candidate \"grails\" and version \"1.3.9\"\n\t\tAnd the exit code is 0\n\n\tScenario: Do not fail if checksums are disabled\n\t\tGiven the system is bootstrapped\n\t\tAnd I have configured \"sdkman_checksum_enable\" to \"false\"\n\t\tAnd the candidate \"grails\" version \"1.3.9\" is available for download with checksum \"abc-checksum-00000\" using algorithm \"SHA-256\"\n\t\tWhen I enter \"sdk install grails 1.3.9\"\n\t\tThen I see \"Checksums are disabled, skipping verification\"\n\t\tAnd I see \"Done installing!\"\n\t\tAnd the candidate \"grails\" version \"1.3.9\" is installed\n\t\tAnd the response headers file is created for candidate \"grails\" and version \"1.3.9\"\n\t\tAnd the exit code is 0\n\t\t\n\t@manual\n\tScenario: Abort installation after download of a binary with invalid SHA checksum\n\t\tGiven the system is bootstrapped\n\t\tAnd the candidate \"grails\" version \"1.3.9\" is available for download with checksum \"c68e386a6deec9fc4c1e18df21f927000000000e\" using algorithm \"SHA-256\"\n\t\tWhen I enter \"sdk install grails 1.3.9\"\n\t\tThen I see \"Stop! An invalid checksum was detected and the archive removed! Please try re-installing.\"\n\t\tAnd the candidate \"grails\" version \"1.3.9\" is not installed\n\t\tAnd the archive for candidate \"grails\" version \"1.3.9\" is removed\n\t\tAnd the exit code is 1\n\n\tScenario: Abort installation after download of a binary with invalid MD5 checksum\n\t\tGiven the system is bootstrapped\n\t\tAnd the candidate \"grails\" version \"1.3.9\" is available for download with checksum \"1e87a7d982a2f41da96fdec289908533\" using algorithm \"MD5\"\n\t\tWhen I enter \"sdk install grails 1.3.9\"\n\t\tThen I see \"Stop! An invalid checksum was detected and the archive removed! Please try re-installing.\"\n\t\tAnd the candidate \"grails\" version \"1.3.9\" is not installed\n\t\tAnd the archive for candidate \"grails\" version \"1.3.9\" is removed\n\t\tAnd the exit code is 1\n"
  },
  {
    "path": "src/test/resources/features/command_line_interop.feature",
    "content": "Feature: Command Line Interop\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\t\tAnd the system is bootstrapped\n\n\tScenario: Enter sdk\n\t\tWhen I enter \"sdk\"\n\t\tThen I see \"Usage: sdk <command> [candidate] [version]\"\n\t\tAnd I see \"sdk offline <enable|disable>\"\n\n\tScenario: Ask for help\n\t\tWhen I enter \"sdk help\"\n\t\tThen I see \"Usage: sdk <command> [candidate] [version]\"\n\n\tScenario: Enter an invalid Command\n\t\tWhen I enter \"sdk goopoo grails\"\n\t\tThen I see \"Invalid command: goopoo\"\n\t\tAnd I see \"Usage: sdk <command> [candidate] [version]\"\n\n\tScenario: Enter an invalid Candidate\n\t\tWhen I enter \"sdk install groffle\"\n\t\tThen I see \"Stop! groffle is not a valid candidate.\"\n"
  },
  {
    "path": "src/test/resources/features/current_candidate.feature",
    "content": "Feature: Current Candidate\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\n\tScenario: Display current candidate version in use\n\t\tGiven the candidate \"grails\" version \"1.3.9\" is already installed and default\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk current grails\"\n\t\tThen I see \"Using grails version 1.3.9\"\n\n\tScenario: Display current candidate version when none is in use\n\t\tGiven the candidate \"grails\" version \"1.3.9\" is already installed but not default\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk current grails\"\n\t\tThen I see \"Not using any version of grails\"\n\n\tScenario: Display current candidate versions when none is specified and none is in use\n\t\tGiven the candidate \"grails\" version \"1.3.9\" is already installed but not default\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk current\"\n\t\tThen I see \"No candidates are in use\"\n\n\tScenario: Display current candidate versions when none is specified and one is in use\n\t\tGiven the candidate \"grails\" version \"2.1.0\" is already installed and default\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk current\"\n\t\tThen I see \"Using:\"\n\t\tAnd I see \"grails: 2.1.0\"\n\n\tScenario: Display current candidate versions when none is specified and multiple are in use\n\t\tGiven the candidate \"groovy\" version \"2.0.5\" is already installed and default\n\t\tAnd the candidate \"grails\" version \"2.1.0\" is already installed and default\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk current\"\n\t\tThen I see \"Using:\"\n\t\tAnd I see \"grails: 2.1.0\"\n\t\tAnd I see \"groovy: 2.0.5\"\n"
  },
  {
    "path": "src/test/resources/features/default_version.feature",
    "content": "Feature: Default Version\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\n\tScenario: Default a candidate version that is not installed\n\t\tGiven the candidate \"groovy\" version \"2.0.5\" is a valid candidate version\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk default groovy 2.0.5\"\n\t\tThen I see \"Stop! Candidate version is not installed.\"\n\t\tAnd I see \"Tip: Run the following to install this version\"\n\t\tAnd I see \"$ sdk install groovy 2.0.5\"\n\n\tScenario: Default a candidate version that is installed and not default\n\t\tGiven the candidate \"groovy\" version \"2.0.5\" is a valid candidate version\n\t\tAnd the candidate \"groovy\" version \"2.0.5\" is already installed but not default\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk default groovy 2.0.5\"\n\t\tThen I see \"Default groovy version set to 2.0.5\"\n\t\tAnd the candidate \"groovy\" version \"2.0.5\" should be the default\n\n\tScenario: Default a candidate version that is installed and already default\n\t\tGiven the candidate \"groovy\" version \"2.0.5\" is a valid candidate version\n\t\tAnd the candidate \"groovy\" version \"2.0.5\" is already installed and default\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk default groovy 2.0.5\"\n\t\tThen I see \"Default groovy version set to 2.0.5\"\n\t\tAnd the candidate \"groovy\" version \"2.0.5\" should be the default\n\n\tScenario: Default a candidate version that does not exist\n\t\tGiven the candidate \"groovy\" version \"2.9.9\" is not available for download\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk default groovy 2.9.9\"\n\t\tThen I see \"Stop! groovy 2.9.9 is not available.\"\n"
  },
  {
    "path": "src/test/resources/features/flush.feature",
    "content": "Feature: Flush\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\t\tAnd the system is bootstrapped\n\n\tScenario: Clear out the temporary storage and metadata\n\t\tAnd the file \"res-1.2.0.zip\" in temporary storage\n\t\tAnd a headers file \"grails-1.3.9.headers\" in metadata directory with checksum \"c68e386a6deec9fc4c1e18df21f92739ba2ab36e\" using algorithm \"SHA1\"\n\t\tWhen I enter \"sdk flush\"\n\t\tAnd no \"res-1.2.0.zip\" file is present in temporary storage\n\t\tAnd no metadata is cached\n\t\tAnd I see \"1 archive(s) flushed\"\n\t\tAnd I see \"1 archive(s) flushed\"\n\n\tScenario: Clean up the last known Remote Version\n\t\tGiven a prior version \"5.0.0\" was detected\n\t\tWhen I enter \"sdk flush version\"\n\t\tThen no version file can be found\n\t\tAnd I see \"Version file has been flushed.\"\n\n\tScenario: Clear out the temporary storage\n\t\tGiven the file \"res-1.2.0.zip\" in temporary storage\n\t\tWhen I enter \"sdk flush temp\"\n\t\tThen no \"res-1.2.0.zip\" file is present in temporary storage\n\t\tAnd I see \"1 archive(s) flushed\"\n\n\tScenario: Clear out the metadata\n\t\tGiven a headers file \"grails-1.3.9.headers\" in metadata directory with checksum \"c68e386a6deec9fc4c1e18df21f92739ba2ab36e\" using algorithm \"SHA1\"\n\t\tWhen I enter \"sdk flush metadata\"\n\t\tThen no metadata is cached\n\t\tAnd I see \"1 archive(s) flushed\"\t\n"
  },
  {
    "path": "src/test/resources/features/home.feature",
    "content": "Feature: Print home path\n\n* Print home directory\n* Printing the home directory does not require reaching out to the internet.\n* Printing the home directory also has the hard requirement of not printing\nanything else unless it is an actual error.\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment without debug prints\n\n\tScenario: Home without providing a Candidate\n\t\tGiven the system is bootstrapped\n\t\tWhen I enter \"sdk home\"\n\t\tThen I see \"Usage: sdk <command> [candidate] [version]\"\n\n\tScenario: Home for a candidate version that is installed\n\t\tGiven the candidate \"grails\" version \"2.1.0\" is already installed and default\n\t\tAnd the candidate \"grails\" version \"1.3.9\" is a valid candidate version\n\t\tAnd the candidate \"grails\" version \"1.3.9\" is already installed but not default\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk home grails 1.3.9\"\n\t\tThen the home path ends with \".sdkman/candidates/grails/1.3.9\"\n\n\tScenario: Home for a candidate version that is not installed\n\t\tGiven the candidate \"grails\" version \"1.3.9\" is available for download\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk home grails 1.3.9\"\n\t\tThen I see \"Stop! Candidate version is not installed.\"\n\t\tAnd I see \"Tip: Run the following to install this version\"\n\t\tAnd I see \"$ sdk install grails 1.3.9\"\n\t\tAnd the exit code is 1\n\n\tScenario: Home for a candidate version that does not exist\n\t\tGiven the candidate \"groovy\" version \"1.9.9\" is not available for download\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk home groovy 1.9.9\"\n\t\tThen I see \"Stop! groovy 1.9.9 is not available.\"\n\n\tScenario: Home for a candidate version that only exists locally\n\t\tGiven the candidate \"grails\" version \"2.0.0.M1\" is not available for download\n\t\tAnd the candidate \"grails\" version \"2.0.0.M1\" is already installed but not default\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk home grails 2.0.0.M1\"\n\t\tThen the home path ends with \".sdkman/candidates/grails/2.0.0.M1\"\n"
  },
  {
    "path": "src/test/resources/features/hooks.feature",
    "content": "Feature: Hooks\n\n\tWe can safely remove this feature when `.tar.gz` and `.zip` are supported directly by the backend.\n\t\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\n\tScenario: Post-installation Hook returns successfully\n\t\tAnd an \"x86_64\" machine with \"Linux\" installed\n\t\tAnd the system is bootstrapped\n\t\tAnd the candidate \"grails\" version \"2.1.0\" is available for download on \"Linux\" with architecture \"x86_64\"\n\t\tAnd a post-installation hook is served for \"grails\" \"2.1.0\" on \"Linux\" with architecture \"x86_64\" that returns successfully\n\t\tWhen I enter \"sdk install grails 2.1.0\"\n\t\tAnd I see \"Post-installation hook success\"\n\t\tAnd the exit code is 0\n\n\tScenario: Post-install Hook returns a non-zero code\n\t\tAnd an \"x86_64\" machine with \"Linux\" installed\n\t\tAnd the system is bootstrapped\n\t\tAnd the candidate \"grails\" version \"2.1.0\" is available for download on \"Linux\" with architecture \"x86_64\"\n\t\tAnd a post-installation hook is served for \"grails\" \"2.1.0\" on \"Linux\" with architecture \"x86_64\" that returns a failure\n\t\tWhen I enter \"sdk install grails 2.1.0\"\n\t\tThen I see \"Post-installation hook failure\"\n\t\tAnd the exit code is 1\n"
  },
  {
    "path": "src/test/resources/features/install_candidate.feature",
    "content": "Feature: Install Candidate\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\n\tScenario: Install a default Candidate and set to default\n\t\tGiven the system is bootstrapped\n\t\tAnd the candidate \"grails\" version \"2.1.0\" is a valid candidate version\n\t\tAnd the default \"grails\" version is \"2.1.0\"\n\t\tWhen I enter \"sdk install grails\"\n\t\tThen I see \"Done installing!\"\n\t\tAnd I do not see \"Do you want grails 2.1.0 to be set as default? (Y/n)\"\n\t\tAnd the candidate \"grails\" version \"2.1.0\" is installed\n\t\tAnd the response headers file is created for candidate \"grails\" and version \"2.1.0\"\n\t\tAnd the exit code is 0\n\n\tScenario: Install a specific Candidate and set to default\n\t\tGiven the system is bootstrapped\n\t\tAnd the candidate \"grails\" version \"1.3.9\" is available for download\n\t\tWhen I enter \"sdk install grails 1.3.9\"\n\t\tThen I see \"Done installing!\"\n\t\tAnd I do not see \"Do you want grails 1.3.9 to be set as default? (Y/n)\"\n\t\tAnd the candidate \"grails\" version \"1.3.9\" is installed\n\t\tAnd the response headers file is created for candidate \"grails\" and version \"1.3.9\"\n\t\tAnd the exit code is 0\n\n\tScenario: Install a Candidate version that does not exist\n\t\tGiven the system is bootstrapped\n\t\tAnd the candidate \"grails\" version \"1.4.4\" is not available for download\n\t\tWhen I enter \"sdk install grails 1.4.4\"\n\t\tThen I see \"Stop! grails 1.4.4 is not available.\"\n\t\tAnd the exit code is 1\n\n\tScenario: Install a Candidate version that is already installed\n\t\tGiven the system is bootstrapped\n\t\tAnd the candidate \"grails\" version \"1.3.9\" is available for download\n\t\tAnd the candidate \"grails\" version \"1.3.9\" is already installed and default\n\t\tWhen I enter \"sdk install grails 1.3.9\"\n\t\tThen I see \"grails 1.3.9 is already installed.\"\n\t\tAnd no response headers are written for candidate \"grails\" and version \"1.3.9\"\n\t\tAnd the exit code is 0\n\n\tScenario: Install a candidate and auto-answer to make it default\n\t\tGiven the system is bootstrapped\n\t\tAnd the candidate \"grails\" version \"2.1.0\" is available for download\n\t\tAnd I have configured \"sdkman_auto_answer\" to \"true\"\n\t\tWhen I enter \"sdk install grails 2.1.0\"\n\t\tThen the candidate \"grails\" version \"2.1.0\" is installed\n\t\tAnd the response headers file is created for candidate \"grails\" and version \"2.1.0\"\n\t\tAnd I do not see \"Do you want grails 2.1.0 to be set as default?\"\n\t\tAnd I see \"Done installing!\"\n\t\tAnd I see \"Setting grails 2.1.0 as default.\"\n\t\tAnd the candidate \"grails\" version \"2.1.0\" should be the default\n\t\tAnd the exit code is 0\n\n\tScenario: Install a candidate and choose to make it default\n\t\tGiven the candidate \"grails\" version \"1.3.9\" is already installed and default\n\t\tAnd the system is bootstrapped\n\t\tAnd the candidate \"grails\" version \"2.1.0\" is available for download\n\t\tWhen I enter \"sdk install grails 2.1.0\" and answer \"Y\"\n\t\tThen the candidate \"grails\" version \"2.1.0\" is installed\n\t\tAnd the response headers file is created for candidate \"grails\" and version \"2.1.0\"\n\t\tAnd I see \"Done installing!\"\n\t\tAnd I see \"Do you want grails 2.1.0 to be set as default? (Y/n)\"\n\t\tAnd I see \"Setting grails 2.1.0 as default.\"\n\t\tAnd the candidate \"grails\" version \"2.1.0\" should be the default\n\t\tAnd the candidate \"grails\" version \"1.3.9\" should not be the default\n\t\tAnd the exit code is 0\n\n\tScenario: Install a candidate and choose not to make it default\n\t\tGiven the candidate \"grails\" version \"1.3.9\" is already installed and default\n\t\tAnd the system is bootstrapped\n\t\tAnd the candidate \"grails\" version \"2.1.0\" is available for download\n\t\tWhen I enter \"sdk install grails 2.1.0\" and answer \"n\"\n\t\tThen the candidate \"grails\" version \"2.1.0\" is installed\n\t\tAnd the response headers file is created for candidate \"grails\" and version \"2.1.0\"\n\t\tAnd I see \"Done installing!\"\n\t\tAnd I see \"Do you want grails 2.1.0 to be set as default? (Y/n)\"\n\t\tAnd I do not see \"Setting grails 2.1.0 as default.\"\n\t\tAnd the candidate \"grails\" version \"2.1.0\" should not be the default\n\t\tAnd the candidate \"grails\" version \"1.3.9\" should be the default\n\t\tAnd the exit code is 0\n\n\t# revisit to redownload automatically\n\t\n\tScenario: Abort installation on download of a corrupt Candidate archive\n\t\tGiven the system is bootstrapped\n\t\tAnd the candidate \"grails\" version \"1.3.6\" is available for download\n\t\tAnd the archive for candidate \"grails\" version \"1.3.6\" is corrupt\n\t\tWhen I enter \"sdk install grails 1.3.6\"\n\t\tThen I see \"Stop! The archive was corrupt and has been removed! Please try installing again.\"\n\t\tAnd the candidate \"grails\" version \"1.3.6\" is not installed\n\t\tAnd the archive for candidate \"grails\" version \"1.3.6\" is removed\n\t\tAnd the exit code is 1\n"
  },
  {
    "path": "src/test/resources/features/install_sdkman.feature",
    "content": "@manual\nFeature: Install SDKMAN\n\n\tPlatform defaults as follows:\n\t* Ubuntu:  .profile, .bashrc\n\t* Fedora:  .bash_profile, .bashrc\n\t* OS X:    no skeleton files in user home (nice one Apple!)\n\t* Cygwin:  .bash_profile, .profile and .bashrc\n\t* Solaris: .profile, .bashrc\n\t* FreeBSD: .profile with NO .bashrc or bash pre-installed\n\n\tOrder of precedence:\n\tLogin shells (all new terminals) are initialised with the first file found in this order:\n\t* .bash_profile\n\t* .bash_login\n\t* .profile\n\tNon-login shells (like xterm shells) read the .bashrc file.\n\n\tAlso important to note that usually the .bash_profile or .profile login shell files\n\twill check for the .bashrc file and invoke if present!\n\n\tBackground:\n\t\tGiven a user home exists\n\n\tScenario: Creates and initialises .bash_profile on absence of login shell dot files\n\t\tGiven the user home directory contains no \".bash_profile\" file\n\t\tAnd the user home directory contains no \".profile\" file\n\t\tWhen I run the installation script\n\t\tThen the user home contains a \".bash_profile\" file\n\t\tAnd the \".bash_profile\" contains an Initialisation Snippet\n\n\tScenario: Add Init Snippet to the .bash_profile if present\n\t\tGiven the user home contains a \".bash_profile\" file\n\t\tWhen I run the installation script\n\t\tThen the \".bash_profile\" contains an Initialisation Snippet\n\n\tScenario: Add Init Snippet to the .profile if present\n\t\tGiven the user home contains a \".profile\" file\n\t\tWhen I run the installation script\n\t\tThen the \".profile\" contains an Initialisation Snippet\n\n\tScenario: Creates and initialises .bashrc on absence of non-login dot files\n\t\tGiven the user home directory contains no \".bashrc\" file\n\t\tWhen I run the installation script\n\t\tThen the user home contains a \".bashrc\" file\n\t\tAnd the \".bashrc\" contains an Initialisation Snippet\n\n\tScenario: Always adds Init Snippet to the .bashrc\n\t\tGiven the user home contains a \".bashrc\" file\n\t\tWhen I run the installation script\n\t\tThen the \".bashrc\" contains an Initialisation Snippet\n\n\tScenario: Creates and initialises .zshrc on absence of the file\n\t\tGiven the user home directory contains no \".zshrc\" file\n\t\tWhen I run the installation script\n\t\tThen the user home contains a \".zshrc\" file\n\t\tAnd the \".zshrc\" contains an Initialisation Snippet\n\n\tScenario: Always adds Init Snippet to the .zshrc\n\t\tGiven the user home contains a \".zshrc\" file\n\t\tWhen I run the installation script\n\t\tThen the \".zshrc\" contains an Initialisation Snippet\n\n\tScenario: Source the Initialisation Script on first invokation of the Init Snippet\n\t\tGiven the user home contains a \".bash_profile\" file\n\t\tAnd the \".bash_profile\" contains an Initialisation Snippet\n\t\tWhen I open a new Login Shell\n\t\tThen the \"sdkman-init.sh\" script is sourced once only\n\n\tScenario: Do not Source the Initialisation Script on subsequent invocation of the Init Snippet\n\t\tGiven the user home contains a \".bash_profile\" file\n\t\tAnd the \".bash_profile\" contains an Initialisation Snippet\n\t\tAnd the user home contains a \".bashrc\" file\n\t\tAnd the \".bashrc\" contains an Initialisation Snippet\n\t\tWhen I open a new Login Shell\n\t\tThen the \"sdkman-init.sh\" script is sourced once only\n\n\tScenario: Upgrade an installation without configuration\n\t\tGiven an uninitialised system\n\t\tAnd the configuration file has not been primed\n\t\tWhen I run the installation script\n\t\tThen the configuration file is present\n"
  },
  {
    "path": "src/test/resources/features/java_installation.feature",
    "content": "Feature: Java Multi Platform Binary Distribution\n\n\tThree versions of Java are used to test various installation scenarios.\n\tThis feature uses real hooks found in the resources folder under /hooks.\n\n\tThe following hooks are available:\n\t8.0.111: post-hook prepared for successful installation\n\t8.0.101: post-hook aborts with non-zero return code\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\n\tScenario: Platform is supported and a specific version of compatible binary is installed\n\t\tGiven an \"x86_64\" machine with \"Linux\" installed\n\t\tAnd the system is bootstrapped\n\t\tAnd the candidate \"java\" version \"8.0.111\" is available for download on \"Linux\" with architecture \"x86_64\"\n\t\tAnd the appropriate multi-platform hook is available for \"java\" version \"8.0.111\" on \"Linux\" with architecture \"x86_64\"\n\t\tWhen I enter \"sdk install java 8.0.111\"\n\t\tAnd I see \"Done installing!\"\n\t\tAnd the candidate \"java\" version \"8.0.111\" is installed\n\n\tScenario: Platform is supported and a default version of compatible binary is installed\n\t\tGiven an \"x86_64\" machine with \"Linux\" installed\n\t\tAnd the system is bootstrapped\n\t\tAnd the default \"java\" version is \"8.0.111\"\n\t\tAnd the candidate \"java\" version \"8.0.111\" is available for download on \"Linux\" with architecture \"x86_64\"\n\t\tAnd the appropriate multi-platform hook is available for \"java\" version \"8.0.111\" on \"Linux\" with architecture \"x86_64\"\n\t\tWhen I enter \"sdk install java\"\n\t\tAnd I see \"Done installing!\"\n\t\tAnd the candidate \"java\" version \"8.0.111\" is installed\n\n\tScenario: Platform is supported but download fails\n\t\tGiven an \"x86_64\" machine with \"Linux\" installed\n\t\tAnd the system is bootstrapped\n\t\tAnd the candidate \"java\" version \"8.0.101\" is available for download on \"Linux\" with architecture \"x86_64\"\n\t\tAnd the appropriate multi-platform hook is available for \"java\" version \"8.0.101\" on \"Linux\" with architecture \"x86_64\"\n\t\tWhen I enter \"sdk install java 8.0.101\"\n\t\tAnd I see \"Download has failed, aborting!\"\n\t\tAnd the candidate \"java\" version \"8.0.101\" is not installed\n\t\tAnd I see \"Cannot install java 8.0.101 at this time...\"\n\n\tScenario: Platform is not supported for specific version and user is notified\n\t\tAnd an \"x86_64\" machine with \"Linux\" installed\n\t\tAnd the system is bootstrapped\n\t\tAnd the candidate \"java\" version \"8.0.111\" is not available for download on \"Linux\"\n\t\tWhen I enter \"sdk install java 8.0.111\"\n\t\tThen I see \"Stop! java 8.0.111 is not available. Possible causes:\"\n\t\tAnd I see \" * 8.0.111 is an invalid version\"\n\t\tAnd I see \" * java binaries are incompatible with your platform\"\n\t\tAnd I see \" * java has not been released yet\"\n\t\tAnd I see \"Tip: see all available versions for your platform:\"\n\t\tAnd I see \"$ sdk list java\"\n\t\tAnd the candidate \"java\" version \"8.0.111\" is not installed\n\n\tScenario: Platform is not supported for default version and user is notified\n\t\tAnd an \"x86_64\" machine with \"Linux\" installed\n\t\tAnd the system is bootstrapped\n\t\tAnd the default \"java\" version is \"8.0.111\"\n\t\tAnd the candidate \"java\" version \"8.0.111\" is not available for download on \"Linux\"\n\t\tWhen I enter \"sdk install java\"\n\t\tThen I see \"Stop! java 8.0.111 is not available. Possible causes:\"\n\t\tAnd I see \" * 8.0.111 is an invalid version\"\n\t\tAnd I see \" * java binaries are incompatible with your platform\"\n\t\tAnd I see \" * java has not been released yet\"\n\t\tAnd I see \"Tip: see all available versions for your platform:\"\n\t\tAnd I see \"$ sdk list java\"\n\t\tAnd the candidate \"java\" version \"8.0.111\" is not installed\n"
  },
  {
    "path": "src/test/resources/features/list_candidate_versions.feature",
    "content": "Feature: List Candidate Versions\n\n\tA dummy template to be served back that has the following information:\n\t* Candidate: grails\n\t* Current: 2.1.0\n\t* Versions: 2.1.0,2.1.1,2.1.2 (CSV)\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\n\tScenario: List an uninstalled available Version\n\t\tGiven I do not have a \"grails\" candidate installed\n\t\tAnd the candidate \"grails\" has a version list available\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk list grails\"\n\t\tThen I see \"Candidate: grails\"\n\n\tScenario: List an installed available Version not in use\n\t\tGiven the candidate \"grails\" version \"2.1.0\" is already installed but not default\n\t\tAnd the candidate \"grails\" has a version list available\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk list grails\"\n\t\tThen I see \"Versions: 2.1.0\"\n\t\tAnd I do not see \"Current: 2.1.0\"\n\n\tScenario: List an installed available Version in use\n\t\tGiven the candidate \"grails\" version \"2.1.0\" is already installed and default\n\t\tAnd the candidate \"grails\" has a version list available\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk list grails\"\n\t\tThen I see \"Current: 2.1.0\"\n\t\tAnd I see \"Versions: 2.1.0\"\n\n\tScenario: List installed multiple Versions\n\t\tGiven the candidate \"grails\" version \"2.1.0\" is already installed and default\n\t\tAnd the candidate \"grails\" version \"2.0.9\" is already installed but not default\n\t\tAnd the candidate \"grails\" has a version list available\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk list grails\"\n\t\tThen I see \"Current: 2.1.0\"\n\t\tAnd I see \"Versions: 2.0.9,2.1.0\"\n\n\tScenario: List an installed local version not in use\n\t\tGiven I have a local candidate \"grails\" version \"2.3-SNAPSHOT\" at \"/tmp/groovy-core\"\n\t\tAnd the candidate \"groovy\" version \"2.3-SNAPSHOT\" is already linked to \"/tmp/groovy-core\"\n\t\tAnd the candidate \"groovy\" has a version list available\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk list groovy\"\n\t\tThen I see \"Versions: 2.3-SNAPSHOT\"\n\t\tAnd I do not see \"Current: 2.3-SNAPSHOT\"\n\n\tScenario: List an installed local Version in use\n\t\tGiven I have a local candidate \"groovy\" version \"2.2-SNAPSHOT\" at \"/tmp/groovy-core\"\n\t\tAnd the candidate \"groovy\" version \"2.2-SNAPSHOT\" is already linked to \"/tmp/groovy-core\"\n\t\tAnd the candidate \"groovy\" version \"2.2-SNAPSHOT\" is the default\n\t\tAnd the candidate \"groovy\" has a version list available\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk list groovy\"\n\t\tThen I see \"Current: 2.2-SNAPSHOT\"\n\t\tAnd I see \"Versions: 2.2-SNAPSHOT\"\n"
  },
  {
    "path": "src/test/resources/features/list_candidates.feature",
    "content": "Feature: List Candidates\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\n\tScenario: A List of Available Candidates can be viewed\n\t\tGiven the system is bootstrapped\n\t\tAnd The candidate list is available\n\t\tWhen I enter \"sdk list\"\n\t\tThen I see \"Candidate List\"\n"
  },
  {
    "path": "src/test/resources/features/local_developement_versions.feature",
    "content": "Feature: Local Development Versions\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\n\tScenario: Install a new local development version\n\t\tGiven the candidate \"groovy\" version \"2.1-SNAPSHOT\" is not available for download\n\t\tAnd I have a local candidate \"groovy\" version \"2.1-SNAPSHOT\" at \"/tmp/groovy-core\"\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk install groovy 2.1-SNAPSHOT /tmp/groovy-core\"\n\t\tThen I see \"Linking groovy 2.1-SNAPSHOT to /tmp/groovy-core\"\n\t\tAnd the candidate \"groovy\" version \"2.1-SNAPSHOT\" is linked to \"/tmp/groovy-core\"\n\n\tScenario: Attempt installing a local development version that already exists\n\t\tGiven the candidate \"groovy\" version \"2.1-SNAPSHOT\" is not available for download\n\t\tAnd the candidate \"groovy\" version \"2.1-SNAPSHOT\" is already linked to \"/tmp/groovy-core\"\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk install groovy 2.1-SNAPSHOT /tmp/groovy-core\"\n\t\tThen I see \"groovy 2.1-SNAPSHOT is already installed.\"\n\t\tAnd the exit code is 0\n\t\tAnd the candidate \"groovy\" version \"2.1-SNAPSHOT\" is linked to \"/tmp/groovy-core\"\n\n\tScenario: Uninstall a local development version\n\t\tGiven the candidate \"groovy\" version \"2.1-SNAPSHOT\" is already linked to \"/tmp/groovy-core\"\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk uninstall groovy 2.1-SNAPSHOT\"\n\t\tThen I see \"Uninstalling groovy 2.1-SNAPSHOT\"\n\t\tAnd the candidate \"groovy\" version \"2.1-SNAPSHOT\" is not installed\n\n\tScenario: Attempt uninstalling a local development version that is not installed\n\t\tGiven the candidate \"groovy\" version \"2.1-SNAPSHOT\" is not installed\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk uninstall groovy 2.1-SNAPSHOT\"\n\t\tThen I see \"groovy 2.1-SNAPSHOT is not installed.\"\n\n\tScenario: Make the local development version the default for the candidate\n\t\tGiven the candidate \"groovy\" version \"2.0.6\" is already installed and default\n\t\tAnd the candidate \"groovy\" version \"2.1-SNAPSHOT\" is not available for download\n\t\tAnd the candidate \"groovy\" version \"2.1-SNAPSHOT\" is already linked to \"/tmp/groovy-core\"\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk default groovy 2.1-SNAPSHOT\"\n\t\tThen I see \"Default groovy version set to 2.1-SNAPSHOT\"\n\t\tAnd the candidate \"groovy\" version \"2.1-SNAPSHOT\" should be the default\n\n\tScenario: Use a local development version\n\t\tGiven the candidate \"groovy\" version \"2.0.6\" is already installed and default\n\t\tAnd the candidate \"groovy\" version \"2.1-SNAPSHOT\" is not available for download\n\t\tAnd the candidate \"groovy\" version \"2.1-SNAPSHOT\" is already linked to \"/tmp/groovy-core\"\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk use groovy 2.1-SNAPSHOT\"\n\t\tThen I see \"Using groovy version 2.1-SNAPSHOT in this shell\"\n\t\tAnd the candidate \"groovy\" version \"2.1-SNAPSHOT\" should be in use\n\n\tScenario: Install a local development version from a valid relative path\n\t\tGiven the candidate \"groovy\" version \"2.1-SNAPSHOT\" is not available for download\n\t\tAnd I have a local candidate \"groovy\" version \"2.1-SNAPSHOT\" at relative path \"some/relative/path/to/groovy\"\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk install groovy 2.1-SNAPSHOT some/relative/path/to/groovy\"\n\t\tThen I see \"Linking groovy 2.1-SNAPSHOT\"\n\t\tAnd the candidate \"groovy\" version \"2.1-SNAPSHOT\" is linked to the relative path \"some/relative/path/to/groovy\"\n\n\tScenario: Prevent installation of a local development version for an invalid path\n\t\tGiven the candidate \"groovy\" version \"2.1-SNAPSHOT\" is not available for download\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk install groovy 2.1-SNAPSHOT /some/bogus/path/to/groovy\"\n\t\tThen I see \"Invalid path! Refusing to link groovy 2.1-SNAPSHOT to /some/bogus/path/to/groovy.\"\n\t\tAnd the candidate \"groovy\" version \"2.1-SNAPSHOT\" is not installed\n\n\tScenario: Prevent installation of a local development version for a long version\n\t\tGiven the candidate \"groovy\" version \"2.1-SNAPSHOTLONG\" is not available for download\n\t\tAnd I have a local candidate \"groovy\" version \"2.1-SNAPSHOTLONG\" at relative path \"some/relative/path/to/groovy\"\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk install groovy 2.1-SNAPSHOTLONG some/relative/path/to/groovy\"\n\t\tThen I see \"Invalid version! 2.1-SNAPSHOTLONG with length 16 exceeds max of 15!\"\n\t\tAnd the candidate \"groovy\" version \"2.1-SNAPSHOTLONG\" is not installed\n\n\tScenario: Allow installation of a local development version for longest possible version\n\t\tGiven the candidate \"groovy\" version \"2.1-SNAPSHOT-XX\" is not available for download\n\t\tAnd I have a local candidate \"groovy\" version \"2.1-SNAPSHOT-XX\" at \"/tmp/groovy-core\"\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk install groovy 2.1-SNAPSHOT-XX /tmp/groovy-core\"\n\t\tThen I see \"Linking groovy 2.1-SNAPSHOT-XX to /tmp/groovy-core\"\n\t\tAnd the candidate \"groovy\" version \"2.1-SNAPSHOT-XX\" is linked to \"/tmp/groovy-core\"\n\n\tScenario: Allow installation of a local development version for a short version\n\t\tGiven the candidate \"java\" version \"graal\" is not available for download\n\t\tAnd I have a local candidate \"java\" version \"graal\" at \"/tmp/graalvm-1.0.0-rc4-graal\"\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk install java graal /tmp/graalvm-1.0.0-rc4-graal\"\n\t\tThen I see \"Linking java graal to /tmp/graalvm-1.0.0-rc4-graal\"\n\t\tAnd the candidate \"java\" version \"graal\" is linked to \"/tmp/graalvm-1.0.0-rc4-graal\"\n"
  },
  {
    "path": "src/test/resources/features/mnemonics.feature",
    "content": "Feature: Mnemonics\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\n\tScenario: Shortcut for listing an uninstalled available Version\n\t\tGiven I do not have a \"grails\" candidate installed\n\t\tAnd a \"grails\" list view is available for consumption\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk l grails\"\n\t\tThen I see \"Available Grails Versions\"\n\n\tScenario: Alternate shortcut for listing uninstalled available Version\n\t\tGiven I do not have a \"grails\" candidate installed\n\t\tAnd a \"grails\" list view is available for consumption\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk ls grails\"\n\t\tThen I see \"Available Grails Versions\"\n\n\tScenario: Shortcut for displaying current Candidate Version in use\n\t\tGiven the candidate \"grails\" version \"1.3.9\" is already installed and default\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk c grails\"\n\t\tThen I see \"Using grails version 1.3.9\"\n\n\tScenario: Shortcut for displaying current Candidate Versions\n\t\tGiven the candidate \"groovy\" version \"2.0.5\" is already installed and default\n\t\tAnd the candidate \"grails\" version \"2.1.0\" is already installed and default\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk c\"\n\t\tThen I see \"Using:\"\n\t\tAnd I see \"grails: 2.1.0\"\n\t\tAnd I see \"groovy: 2.0.5\"\n\n\tScenario: Shortcut for displaying upgradable Candidate Version in use\n\t\tGiven the candidate \"grails\" version \"1.3.9\" is already installed and default\n\t\tAnd the default \"grails\" version is \"2.4.4\"\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk ug grails\" and answer \"n\"\n\t\tThen I see \"Available defaults:\"\n\t\tAnd I see \"grails (local: 1.3.9; default: 2.4.4)\"\n\n\tScenario: Shortcut for installing a Candidate Version\n\t\tGiven the candidate \"grails\" version \"2.1.0\" is not installed\n\t\tAnd the candidate \"grails\" version \"2.1.0\" is available for download\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk i grails 2.1.0\" and answer \"Y\"\n\t\tThen I see \"Installing: grails 2.1.0\"\n\t\tAnd the candidate \"grails\" version \"2.1.0\" is installed\n\n\tScenario: Shortcut for uninstalling a Candidate Version\n\t\tGiven the candidate \"groovy\" version \"2.0.5\" is already installed and default\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk rm groovy 2.0.5\"\n\t\tThen I see \"Uninstalling groovy 2.0.5\"\n\t\tAnd the candidate \"groovy\" version \"2.0.5\" is not installed\n\n\tScenario: Shortcut for showing the current Version of sdkman\n\t\tGiven the system is bootstrapped\n\t\tWhen I enter \"sdk v\"\n\t\tThen I see \"SDKMAN 5.0.0\"\n\n\tScenario: Shortcut for using a candidate version that is installed\n\t\tGiven the candidate \"grails\" version \"2.1.0\" is already installed and default\n\t\tAnd the candidate \"grails\" version \"2.1.0\" is a valid candidate version\n\t\tAnd the candidate \"grails\" version \"1.3.9\" is already installed but not default\n\t\tAnd the candidate \"grails\" version \"1.3.9\" is a valid candidate version\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk u grails 1.3.9\"\n\t\tThen I see \"Using grails version 1.3.9 in this shell.\"\n\t\tThen the candidate \"grails\" version \"1.3.9\" should be in use\n\t\tAnd the candidate \"grails\" version \"2.1.0\" should be the default\n\n\tScenario: Shortcut for defaulting a Candidate Version that is installed and not default\n\t\tGiven the candidate \"groovy\" version \"2.0.5\" is already installed but not default\n\t\tAnd the candidate \"groovy\" version \"2.0.5\" is a valid candidate version\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk d groovy 2.0.5\"\n\t\tThen I see \"Default groovy version set to 2.0.5\"\n\t\tAnd the candidate \"groovy\" version \"2.0.5\" should be the default\n\n\tScenario: Shortcut for displaying Home directory\n\t\tGiven the candidate \"grails\" version \"2.1.0\" is already installed and default\n\t\tAnd the candidate \"grails\" version \"2.1.0\" is a valid candidate version\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk h grails 2.1.0\"\n\t\tThen the home path ends with \".sdkman/candidates/grails/2.1.0\"\n"
  },
  {
    "path": "src/test/resources/features/offline_mode.feature",
    "content": "Feature: Offline Mode\n\n\t# offline modes\n\n\tScenario: Enter an invalid offline mode\n\t\tGiven offline mode is disabled with reachable internet\n\t\tAnd an initialised environment\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk offline grails\"\n\t\tThen I see \"Stop! grails is not a valid offline mode.\"\n\n\tScenario: Issue Offline command without qualification\n\t\tGiven offline mode is disabled with reachable internet\n\t\tAnd an initialised environment\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk offline\"\n\t\tThen I see \"Offline mode enabled.\"\n\n\tScenario: Enable Offline Mode with internet reachable\n\t\tGiven offline mode is disabled with reachable internet\n\t\tAnd an initialised environment\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk offline enable\"\n\t\tThen I see \"Offline mode enabled.\"\n\t\tAnd I do not see \"INTERNET NOT REACHABLE!\"\n\t\tWhen I enter \"sdk install grails 2.1.0\"\n\t\tThen I do not see \"INTERNET NOT REACHABLE!\"\n\t\tAnd I see \"Stop! grails 2.1.0 is not available while offline.\"\n\n\tScenario: Disable Offline Mode with internet reachable\n\t\tGiven offline mode is enabled with reachable internet\n\t\tAnd the candidate \"grails\" version \"2.1.0\" is available for download\n\t\tAnd an initialised environment\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk offline disable\"\n\t\tThen I see \"Online mode re-enabled!\"\n\t\tWhen I enter \"sdk install grails 2.1.0\" and answer \"Y\"\n\t\tThen I see \"Done installing!\"\n\t\tAnd the candidate \"grails\" version \"2.1.0\" is installed\n\n\tScenario: Disable Offline Mode with internet unreachable\n\t\tGiven offline mode is enabled with unreachable internet\n\t\tAnd an initialised environment\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk offline disable\"\n\t\tThen I see \"Online mode re-enabled!\"\n\t\tWhen I enter \"sdk install grails 2.1.0\"\n\t\tThen I see \"INTERNET NOT REACHABLE!\"\n\t\tAnd I see \"Stop! grails 2.1.0 is not available while offline.\"\n\n\t# sdk version\n\n\tScenario: Determine the sdkman version while in Offline Mode\n\t\tGiven offline mode is enabled with reachable internet\n\t\tAnd an initialised environment\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk version\"\n\t\tThen I see the current sdkman version\n\n\t# list candidate version\n\n\tScenario: List candidate versions found while in Offline Mode\n\t\tGiven offline mode is enabled with reachable internet\n\t\tAnd an initialised environment\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk list grails\"\n\t\tThen I see \"Offline: only showing installed grails versions\"\n\n\t# default version\n\n\tScenario: Set the default to an uninstalled candidate version while in Offline Mode\n\t\tGiven offline mode is enabled with reachable internet\n\t\tAnd the candidate \"grails\" version \"1.3.9\" is already installed and default\n\t\tAnd an initialised environment\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk default grails 2.1.0\"\n\t\tThen I see \"Stop! grails 2.1.0 is not available while offline.\"\n\n\t# install command\n\n\tScenario: Install a candidate version that is not installed while in Offline Mode\n\t\tGiven offline mode is enabled with reachable internet\n\t\tAnd the candidate \"grails\" version \"2.1.0\" is not installed\n\t\tAnd an initialised environment\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk install grails 2.1.0\"\n\t\tThen I see \"Stop! grails 2.1.0 is not available while offline.\"\n\n\t# uninstall command\n\n\tScenario: Uninstall a candidate version while in Offline Mode\n\t\tGiven offline mode is enabled with reachable internet\n\t\tAnd the candidate \"grails\" version \"2.1.0\" is already installed and default\n\t\tAnd an initialised environment\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk uninstall grails 2.1.0\"\n\t\tAnd the candidate \"grails\" version \"2.1.0\" is not installed\n\n\t# current command\n\n\tScenario: Display the current version of a candidate while in Offline Mode\n\t\tGiven offline mode is enabled with reachable internet\n\t\tAnd the candidate \"grails\" version \"2.1.0\" is already installed and default\n\t\tAnd an initialised environment\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk current grails\"\n\t\tThen I see \"Using grails version 2.1.0\"\n\n\t# help command\n\n\tScenario: Request help while in Offline Mode\n\t\tGiven offline mode is enabled with reachable internet\n\t\tAnd an initialised environment\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk help\"\n\t\tThen I see \"Usage: sdk <command> [candidate] [version]\"\n\n\t# selfupdate command\n\n\tScenario: Attempt self-update while in Offline Mode\n\t\tGiven offline mode is enabled with reachable internet\n\t\tAnd an initialised environment\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk selfupdate\"\n\t\tThen I see \"This command is not available while offline.\"\n"
  },
  {
    "path": "src/test/resources/features/path_initialisation.feature",
    "content": "Feature: Path Initialisation\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\n\tScenario: sdkman is initialised for the first time\n\t\tGiven the candidate \"grails\" version \"2.1.0\" is already installed and default\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"echo $PATH\"\n\t\tThen I see a single occurrence of \"grails\"\n\n\tScenario: sdkman is initialised a subsequent time\n\t\tGiven the candidate \"grails\" version \"2.1.0\" is already installed and default\n\t\tAnd the system is bootstrapped\n\t\tAnd the system is bootstrapped again\n\t\tAnd I enter \"echo $PATH\"\n\t\tThen I see a single occurrence of \"grails\"\n\n\tScenario: sdkman is initialised without candidates\n\t\tGiven the system is bootstrapped\n\t\tWhen I enter \"echo $PATH\"\n\t\tThen I see no occurrences of \"grails\"\n\n\tScenario: Install a candidate and see it on the PATH\n\t\tAnd the candidate \"grails\" version \"2.1.0\" is available for download\n\t\tAnd the system is bootstrapped\n\t\tAnd I enter \"sdk install grails 2.1.0\" and answer \"Y\"\n\t\tWhen I enter \"echo $PATH\"\n\t\tThen I see a single occurrence of \"grails\"\n\n\tScenario: Install multiple candidate versions and see it once on the PATH\n\t\tGiven the candidate \"grails\" version \"1.3.9\" is available for download\n\t\tAnd the candidate \"grails\" version \"2.1.0\" is available for download\n\t\tAnd the system is bootstrapped\n\t\tAnd I enter \"sdk install grails 1.3.9\" and answer \"Y\"\n\t\tAnd I enter \"sdk install grails 2.1.0\" and answer \"Y\"\n\t\tWhen I enter \"echo $PATH\"\n\t\tThen I see a single occurrence of \"grails\"\n"
  },
  {
    "path": "src/test/resources/features/per_project_configuration.feature",
    "content": "Feature: Per-project configuration\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\n\tScenario: An sdkman project configuration is generated\n\t\tGiven the system is bootstrapped\n\t\tWhen I enter \"sdk env init\"\n\t\tThen I see \".sdkmanrc created.\"\n\n\tScenario: The env command is issued without an sdkman project configuration present\n\t\tGiven the system is bootstrapped\n\t\tWhen I enter \"sdk env\"\n\t\tThen I see \"Could not find .sdkmanrc in the current directory.\"\n\t\tAnd I see \"Run 'sdk env init' to create it.\"\n\t\tAnd the exit code is 1\n\n\tScenario: The env command is issued with an sdkman project configuration present\n\t\tGiven the file \".sdkmanrc\" exists and contains \"groovy=2.4.1\"\n\t\tAnd the candidate \"groovy\" version \"2.0.5\" is already installed and default\n\t\tAnd the candidate \"groovy\" version \"2.4.1\" is a valid candidate version\n\t\tAnd the candidate \"groovy\" version \"2.4.1\" is already installed but not default\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk env\"\n\t\tThen I see \"Using groovy version 2.4.1 in this shell.\"\n\t\tAnd the candidate \"groovy\" version \"2.4.1\" should be in use\n\t\tAnd the candidate \"groovy\" version \"2.0.5\" should be the default\n\t\t\n\tScenario: The env install subcommand is issued with an sdkman project configuration present\n\t\tGiven the system is bootstrapped\n\t\tAnd the file \".sdkmanrc\" exists and contains \"groovy=2.4.1\"\n\t\tAnd the candidate \"groovy\" version \"2.0.5\" is already installed and default\n\t\tAnd the candidate \"groovy\" version \"2.4.1\" is available for download\n\t\tWhen I enter \"sdk env install\"\n\t\tThen I see \"Done installing!\"\n\t\tAnd the candidate \"groovy\" version \"2.4.1\" is installed\n\t\tAnd the candidate \"groovy\" version \"2.4.1\" is in use\n\t\tAnd the candidate \"groovy\" version \"2.0.5\" should be the default\n\n\tScenario: The env install subcommand is issued without an sdkman project configuration present\n\t\tGiven the system is bootstrapped\n\t\tWhen I enter \"sdk env install\"\n\t\tThen I see \"Could not find .sdkmanrc in the current directory.\"\n\t\tAnd I see \"Run 'sdk env init' to create it.\"\n\t\tAnd the exit code is 1\n\n\tScenario: The env clear subcommand is issued without an active project configuration\n\t\tGiven the system is bootstrapped\n\t\tWhen I enter \"sdk env clear\"\n\t\tThen I see \"No environment currently set!\"\n\t\tAnd the exit code is 1\n\n\tScenario: The env clear subcommand is issued and the active project configuration is missing\n\t\tGiven the system is bootstrapped\n\t\tAnd a project configuration is active but points to a directory without configuration\n\t\tWhen I enter \"sdk env clear\"\n\t\tThen I see \"Could not find\"\n\t\tAnd the exit code is 1\n\n\tScenario: The current project configuration is cleared and the default versions restored\n\t\tGiven the file \".sdkmanrc\" exists and contains \"groovy=2.0.5\"\n\t\tAnd the candidate \"groovy\" version \"2.4.1\" is already installed and default\n\t\tAnd the candidate \"groovy\" version \"2.0.5\" is in use\n\t\tAnd the system is bootstrapped\n\t\tAnd a project configuration is active\n\t\tWhen I enter \"sdk env clear\"\n\t\tThen I see \"Restored groovy version to 2.4.1 (default)\"\n\t\tAnd the candidate \"groovy\" version \"2.4.1\" should be in use\n"
  },
  {
    "path": "src/test/resources/features/self_update.feature",
    "content": "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 the sdkman native version is \"0.0.1\"\n\t\tAnd an initialised environment\n\t\tAnd the system is bootstrapped\n\t\tAnd an available selfupdate endpoint\n\n\tScenario: Attempt Self Update with out dated scripts components\n\t\tGiven the sdkman script version \"6.0.0\" is available for download\n\t\tAnd the sdkman native version \"0.0.1\" is available for download\n\t\tWhen I enter \"sdk selfupdate\"\n\t\tThen I see \"Successfully upgraded SDKMAN.\"\n\n\tScenario: Attempt Self Update with out dated native components\n\t\tGiven the sdkman script version \"5.0.0\" is available for download\n\t\tAnd the sdkman native version \"0.0.2\" is available for download\n\t\tWhen I enter \"sdk selfupdate\"\n\t\tThen I see \"Successfully upgraded SDKMAN.\"\n\n\tScenario: Attempt Self Update on an up to date system\n\t\tGiven the sdkman script version \"5.0.0\" is available for download\n\t\tAnd the sdkman native version \"0.0.1\" is available for download\n\t\tWhen I enter \"sdk selfupdate\"\n\t\tThen I see \"No update available at this time.\"\n\n\tScenario: Force Self Update on an up to date system\n\t\tGiven the sdkman script version \"5.0.0\" is available for download\n\t\tAnd the sdkman native version \"0.0.1\" is available for download\n\t\tWhen I enter \"sdk selfupdate force\"\n\t\tThen I see \"Successfully upgraded SDKMAN.\"\n\t"
  },
  {
    "path": "src/test/resources/features/service_unavailable.feature",
    "content": "Feature: Service Unavailable\n\n\tBackground:\n\t\tGiven the internet is not reachable\n\t\tAnd an initialised environment\n\n\t# list commands\n\n\tScenario: List candidate versions found while Offline\n\t\tGiven the candidate \"grails\" version \"2.1.0\" is already installed and default\n\t\tAnd the candidate \"grails\" version \"1.3.9\" is already installed but not default\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk list grails\"\n\t\tThen I see \"Offline: only showing installed grails versions\"\n\t\tAnd I see \"> 2.1.0\"\n\t\tAnd I see \"* 1.3.9\"\n\n\tScenario: List candidate versions not found while Offline\n\t\tGiven the system is bootstrapped\n\t\tWhen I enter \"sdk list grails\"\n\t\tThen I see \"Offline: only showing installed grails versions\"\n\t\tAnd I see \"None installed!\"\n\n\tScenario: List Available Candidates while Offline\n\t\tGiven the system is bootstrapped\n\t\tWhen I enter \"sdk list\"\n\t\tThen I see \"This command is not available while offline.\"\n\n\t# use command\n\n\tScenario: Use an installed candidate version while Offline\n\t\tGiven the candidate \"grails\" version \"2.1.0\" is already installed and default\n\t\tAnd the candidate \"grails\" version \"1.3.9\" is already installed but not default\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk use grails 1.3.9\"\n\t\tThen I see \"Using grails version 1.3.9 in this shell.\"\n\n\t# default command\n\n\tScenario: Set the default to an uninstalled candidate version while Offline\n\t\tGiven the candidate \"grails\" version \"1.3.9\" is already installed and default\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk default grails 2.1.0\"\n\t\tThen I see \"Stop! grails 2.1.0 is not available while offline.\"\n\n\tScenario: Set the default to an invalid candidate version while Offline\n\t\tGiven the candidate \"grails\" version \"1.3.9\" is already installed and default\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk default grails 999\"\n\t\tThen I see \"Stop! grails 999 is not available while offline.\"\n\n\tScenario: Set the default to an installed candidate version while Offline\n\t\tGiven the candidate \"grails\" version \"2.1.0\" is already installed and default\n\t\tAnd the candidate \"grails\" version \"1.3.9\" is already installed but not default\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk default grails 1.3.9\"\n\t\tThen I see \"Default grails version set to 1.3.9\"\n\n\t# install command\n\n\tScenario: Install a candidate version that is not installed while Offline\n\t\tGiven the candidate \"grails\" version \"2.1.0\" is not installed\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk install grails 2.1.0\"\n\t\tThen I see \"Stop! grails 2.1.0 is not available while offline.\"\n\n\tScenario: Install a candidate version that is already installed while Offline\n\t\tGiven the candidate \"grails\" version \"2.1.0\" is already installed and default\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk install grails 2.1.0\"\n\t\tThen I see \"grails 2.1.0 is already installed.\"\n\t\tAnd the exit code is 0\n\n\t# uninstall command\n\n\tScenario: Uninstall a candidate version while Offline\n\t\tGiven the candidate \"grails\" version \"2.1.0\" is already installed and default\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk uninstall grails 2.1.0\"\n\t\tThen I see \"Deselecting grails 2.1.0...\"\n\t\tAnd I see \"Uninstalling grails 2.1.0...\"\n\t\tAnd the candidate \"grails\" version \"2.1.0\" is not in use\n\t\tAnd the candidate \"grails\" version \"2.1.0\" is not installed\n\n\tScenario: Uninstall a candidate version that is not installed while Offline\n\t\tGiven the candidate \"grails\" version \"2.1.0\" is not installed\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk uninstall grails 2.1.0\"\n\t\tThen I see \"grails 2.1.0 is not installed.\"\n\n\t# current command\n\n\tScenario: Display the current version of a candidate while Offline\n\t\tGiven the candidate \"grails\" version \"2.1.0\" is already installed and default\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk current grails\"\n\t\tThen I see \"Using grails version 2.1.0\"\n\n\tScenario: Display the current version of all candidates while Offline\n\t\tGiven the candidate \"grails\" version \"2.1.0\" is already installed and default\n\t\tAnd the candidate \"groovy\" version \"2.0.5\" is already installed and default\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk current\"\n\t\tThen I see \"Using:\"\n\t\tAnd I see \"grails: 2.1.0\"\n\t\tAnd I see \"groovy: 2.0.5\"\n\n\t# version command\n\n\tScenario: Determine the sdkman version when Offline\n\t\tGiven the system is bootstrapped\n\t\tWhen I enter \"sdk version\"\n\t\tThen I see the current sdkman version\n\n\t# help command\n\n\tScenario: Request help while Offline\n\t\tGiven the system is bootstrapped\n\t\tWhen I enter \"sdk help\"\n\t\tThen I see \"Usage: sdk <command> [candidate] [version]\"\n\n\t# selfupdate command\n\n\tScenario: Attempt self-update while Offline\n\t\tGiven the system is bootstrapped\n\t\tWhen I enter \"sdk selfupdate\"\n\t\tThen I see \"This command is not available while offline.\"\n"
  },
  {
    "path": "src/test/resources/features/uninstall_candidate.feature",
    "content": "Feature: Uninstall Candidate\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\n\tScenario: Uninstall an installed Candidate Version not in use\n\t\tGiven the candidate \"grails\" version \"2.1.0\" is already installed but not default\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk uninstall grails 2.1.0\"\n\t\tThen I do not see \"Deselecting grails 2.1.0\"\n\t\tThen I see \"Uninstalling grails 2.1.0\"\n\t\tAnd the candidate \"grails\" version \"2.1.0\" is not installed\n\n\tScenario: Uninstall a Candidate Version in use\n\t\tGiven the candidate \"grails\" version \"2.1.0\" is already installed and default\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk uninstall grails 2.1.0\"\n\t\tThen I see \"Deselecting grails 2.1.0\"\n\t\tAnd I see \"Uninstalling grails 2.1.0\"\n\t\tAnd the candidate \"grails\" version \"2.1.0\" is not installed\n\t\tAnd the candidate \"grails\" is no longer selected\n\n\tScenario: Attempt uninstalling a Candidate Version that is not installed\n\t\tGiven the candidate \"grails\" version \"1.3.9\" is not installed\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk uninstall grails 1.3.9\"\n\t\tThen I see \"grails 1.3.9 is not installed.\"\n\n\tScenario: Attempt uninstalling with no Candidate specified\n\t\tGiven the system is bootstrapped\n\t\tWhen I enter \"sdk uninstall\"\n\t\tThen I see \"No candidate provided.\"\n\n\tScenario: Attempt uninstalling with an invalid Candidate specified\n\t\tGiven the system is bootstrapped\n\t\tWhen I enter \"sdk uninstall groffle\"\n\t\tThen I see \"Stop! groffle is not a valid candidate.\"\n\n\tScenario: Attempt uninstalling without a version provided\n\t\tGiven the system is bootstrapped\n\t\tWhen I enter \"sdk uninstall grails\"\n\t\tThen I see \"No candidate version provided.\"\n"
  },
  {
    "path": "src/test/resources/features/update.feature",
    "content": "Feature: Update\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd the following candidates are available for installation in local cache:\n\t\t\t| candidate |\n\t\t\t| activator |\n\t\t\t| groovy    |\n\t\t\t| scala     |\n\t\tAnd an initialised environment\n\t\tAnd the system is bootstrapped\n\n\tScenario: A new candidate is available\n\t\tAnd the following candidates are currently available from remote API:\n\t\t\t| candidate |\n\t\t\t| activator |\n\t\t\t| groovy    |\n\t\t\t| kotlin    |\n\t\t\t| scala     |\n\t\tWhen I enter \"sdk update\"\n\t\tThen I see \"Adding new candidates(s): kotlin\"\n\t\tAnd the Candidates cache should contain \"activator,groovy,kotlin,scala\"\n\n\tScenario: A candidate has been removed\n\t\tAnd the following candidates are currently available from remote API:\n\t\t\t| candidate |\n\t\t\t| groovy    |\n\t\t\t| scala     |\n\t\tWhen I enter \"sdk update\"\n\t\tThen I see \"Removing obsolete candidates(s): activator\"\n\t\tAnd the Candidates cache should contain \"groovy,scala\"\n\n\tScenario: A new candidate is available and a candidate has been removed\n\t\tAnd the following candidates are currently available from remote API:\n\t\t\t| candidate |\n\t\t\t| groovy    |\n\t\t\t| kotlin    |\n\t\t\t| scala     |\n\t\tWhen I enter \"sdk update\"\n\t\tThen I see \"Removing obsolete candidates(s): activator\"\n\t\tAnd I see \"Adding new candidates(s): kotlin\"\n\t\tAnd the Candidates cache should contain \"groovy,kotlin,scala\"\n\n\tScenario: No new candidate is available\n\t\tAnd the following candidates are currently available from remote API:\n\t\t\t| candidate |\n\t\t\t| activator |\n\t\t\t| groovy    |\n\t\t\t| scala     |\n\t\tWhen I enter \"sdk update\"\n\t\tThen I see \"No new candidates found at this time.\"\n\t\tAnd the Candidates cache should contain \"activator,groovy,scala\"\n"
  },
  {
    "path": "src/test/resources/features/upgrade_candidate.feature",
    "content": "Feature: Upgrade Candidate\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd the candidates cache is initialised with \"grails\"\n\t\tAnd an initialised environment\n\n\tScenario: Display upgradable candidate version in use when it is upgradable\n\t\tGiven the candidate \"grails\" version \"1.3.9\" is already installed and default\n\t\tAnd the default \"grails\" version is \"2.4.4\"\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk upgrade grails\" and answer \"n\"\n\t\tThen I see \"Available defaults:\"\n\t\tAnd I see \"grails (local: 1.3.9; default: 2.4.4)\"\n\n\tScenario: Display upgradable candidate version in use when it is not upgradable\n\t\tGiven the candidate \"grails\" version \"1.3.9\" is already installed and default\n\t\tAnd the default \"grails\" version is \"1.3.9\"\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk upgrade grails\"\n\t\tThen I see \"grails is up-to-date\"\n\n\tScenario: Display upgradable candidate version when none is in use\n\t\tGiven the candidate \"grails\" does not exist locally\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk upgrade grails\"\n\t\tThen I see \"Not using any version of grails\"\n\n\tScenario: Display upgradable candidate versions when none is specified and none is in use\n\t\tGiven the candidate \"grails\" does not exist locally\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk upgrade\"\n\t\tThen I see \"No candidates are in use\"\n\n\tScenario: Display upgradable candidate versions when none is specified and one is in use\n\t\tGiven the candidate \"grails\" version \"1.3.9\" is already installed and default\n\t\tAnd the default \"grails\" version is \"2.4.4\"\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk upgrade\" and answer \"n\"\n\t\tThen I see \"Available defaults:\"\n\t\tAnd I see \"grails (local: 1.3.9; default: 2.4.4)\"\n\n\tScenario: Display upgradable candidate versions when none is specified and multiple are in use\n\t\tGiven the candidate \"grails\" version \"1.3.9\" is already installed and default\n\t\tAnd the default \"grails\" version is \"2.4.4\"\n\t\tAnd the candidate \"groovy\" version \"2.0.5\" is already installed and default\n\t\tAnd the default \"groovy\" version is \"2.4.1\"\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk upgrade\" and answer \"n\"\n\t\tThen I see \"Available defaults:\"\n\t\tAnd I see \"grails (local: 1.3.9; default: 2.4.4)\"\n\t\tAnd I see \"groovy (local: 2.0.5; default: 2.4.1)\"\n\n\tScenario: Display upgradable candidate versions when none specified and multiple in use but not upgradable\n\t\tGiven the candidate \"grails\" version \"1.3.9\" is already installed and default\n\t\tAnd the default \"grails\" version is \"1.3.9\"\n\t\tAnd the candidate \"groovy\" version \"2.0.5\" is already installed and default\n\t\tAnd the default \"groovy\" version is \"2.0.5\"\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk upgrade\"\n\t\tThen I see \"All candidates are up-to-date\"\n\n\tScenario: Update all upgradable candidates versions and set them as default\n\t\tGiven the candidate \"grails\" version \"1.3.9\" is already installed and default\n\t\tAnd the default \"grails\" version is \"2.1.0\"\n\t\tAnd the candidate \"grails\" version \"2.1.0\" is available for download\n\t\tAnd the candidate \"groovy\" version \"2.0.5\" is already installed and default\n\t\tAnd the default \"groovy\" version is \"2.4.1\"\n\t\tAnd the candidate \"groovy\" version \"2.4.1\" is available for download\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk upgrade\" and answer \"Y\"\n\t\tThen I see \"Available defaults:\"\n\t\tAnd I see \"grails (local: 1.3.9; default: 2.1.0)\"\n\t\tAnd I see \"groovy (local: 2.0.5; default: 2.4.1)\"\n\t\tAnd I see \"Use prescribed default version(s)? (Y/n)\"\n\t\tAnd I do not see \"Do you want grails 2.1.0 to be set as default? (Y/n)\"\n\t\tAnd I see \"Setting grails 2.1.0 as default.\"\n\t\tAnd I do not see \"Do you want groovy 2.4.1 to be set as default? (Y/n)\"\n\t\tAnd I see \"Setting groovy 2.4.1 as default.\"\n\t\tThen the candidate \"grails\" version \"2.1.0\" should be the default\n\t\tAnd the candidate \"groovy\" version \"2.4.1\" should be the default\n\n\tScenario: Don't update all upgradable candidates versions and set them as default\n\t\tGiven the candidate \"grails\" version \"1.3.9\" is already installed and default\n\t\tAnd the default \"grails\" version is \"2.1.0\"\n\t\tAnd the candidate \"grails\" version \"2.1.0\" is available for download\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk upgrade\" and answer \"N\"\n\t\tThen I see \"Available defaults:\"\n\t\tAnd I see \"grails (local: 1.3.9; default: 2.1.0)\"\n\t\tAnd I see \"Use prescribed default version(s)? (Y/n)\"\n\t\tThen the candidate \"grails\" version \"1.3.9\" should be the default\n\n\tScenario: Update upgradable candidate version and set it as default\n\t\tGiven the candidate \"grails\" version \"1.3.9\" is already installed and default\n\t\tAnd the default \"grails\" version is \"2.1.0\"\n\t\tAnd the candidate \"grails\" version \"2.1.0\" is available for download\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk upgrade grails\" and answer \"Y\"\n\t\tThen I see \"Available defaults:\"\n\t\tAnd I see \"grails (local: 1.3.9; default: 2.1.0)\"\n\t\tAnd I see \"Use prescribed default version(s)? (Y/n): \"\n\t\tAnd I do not see \"Do you want grails 2.1.0 to be set as default? (Y/n)\"\n\t\tAnd I see \"Setting grails 2.1.0 as default.\"\n\t\tThen the candidate \"grails\" version \"2.1.0\" should be the default\n\n\tScenario: Update upgradable candidate version and auto-answer to make it default\n\t\tGiven the candidate \"grails\" version \"1.3.9\" is already installed and default\n\t\tAnd the default \"grails\" version is \"2.1.0\"\n\t\tAnd the candidate \"grails\" version \"2.1.0\" is available for download\n\t\tAnd I have configured \"sdkman_auto_answer\" to \"true\"\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk upgrade grails\"\n\t\tThen I see \"Available defaults:\"\n\t\tAnd I see \"grails (local: 1.3.9; default: 2.1.0)\"\n\t\tAnd I do not see \"Use prescribed default version(s)? (Y/n): \"\n\t\tAnd I do not see \"Do you want grails 2.1.0 to be set as default? (Y/n)\"\n\t\tAnd I see \"Setting grails 2.1.0 as default.\"\n\t\tThen the candidate \"grails\" version \"2.1.0\" should be the default\n\n\tScenario: Don't update upgradable candidate version and set it as default\n\t\tGiven the candidate \"grails\" version \"1.3.9\" is already installed and default\n\t\tAnd the default \"grails\" version is \"2.1.0\"\n\t\tAnd the candidate \"grails\" version \"2.1.0\" is available for download\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk upgrade grails\" and answer \"N\"\n\t\tThen I see \"Available defaults:\"\n\t\tAnd I see \"grails (local: 1.3.9; default: 2.1.0)\"\n\t\tAnd I see \"Use prescribed default version(s)? (Y/n)\"\n\t\tThen the candidate \"grails\" version \"1.3.9\" should be the default\n"
  },
  {
    "path": "src/test/resources/features/use_version.feature",
    "content": "Feature: Use Version\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\n\tScenario: Use without providing a Candidate\n\t\tGiven the system is bootstrapped\n\t\tWhen I enter \"sdk use\"\n\t\tThen I see \"Usage: sdk <command> [candidate] [version]\"\n\n\tScenario: Use a candidate version that is installed\n\t\tGiven the candidate \"grails\" version \"2.1.0\" is already installed and default\n\t\tAnd the candidate \"grails\" version \"1.3.9\" is a valid candidate version\n\t\tAnd the candidate \"grails\" version \"1.3.9\" is already installed but not default\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk use grails 1.3.9\"\n\t\tThen I see \"Using grails version 1.3.9 in this shell.\"\n\t\tThen the candidate \"grails\" version \"1.3.9\" should be in use\n\t\tAnd the candidate \"grails\" version \"2.1.0\" should be the default\n\n\tScenario: Use a candidate version that is not installed\n\t\tGiven the candidate \"groovy\" version \"1.9.9\" is not available for download\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk use groovy 1.9.9\"\n\t\tThen I see \"Stop! Candidate version is not installed.\"\n\t\tAnd I see \"Tip: Run the following to install this version\"\n\t\tAnd I see \"$ sdk install groovy 1.9.9\"\n\n\tScenario: Use a candidate version that only exists locally\n\t\tGiven the candidate \"grails\" version \"2.0.0.M1\" is not available for download\n\t\tAnd the candidate \"grails\" version \"2.0.0.M1\" is already installed but not default\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk use grails 2.0.0.M1\"\n\t\tThen I see \"Using grails version 2.0.0.M1 in this shell.\"\n\n\t# scenarios related to updating _HOME variable\n\n\tScenario: Use an installed version of an installed candidate updates the candidate _HOME variable\n\t\tGiven the candidate \"grails\" version \"1.3.9\" is already installed and default\n\t\tAnd the candidate \"grails\" version \"2.1.0\" is already installed but not default\n\t\tAnd the candidate \"grails\" version \"2.1.0\" is available for download\n\t\tAnd the system is bootstrapped\n\t\tAnd the \"GRAILS_HOME\" variable contains \"grails/current\"\n\t\tWhen I enter \"sdk use grails 2.1.0\"\n\t\tAnd I see \"Using grails version 2.1.0 in this shell.\"\n\t\tThen the \"GRAILS_HOME\" variable contains \"grails/2.1.0\"\n"
  },
  {
    "path": "src/test/resources/features/version.feature",
    "content": "Feature: Version\n\n\tScenario: Show the current version of sdkman\n\t\tGiven the internet is reachable\n\t\tAnd the sdkman scripts version is \"3.2.1\"\n\t\tAnd an initialised environment\n\t\tAnd the system is bootstrapped\n\t\tWhen I enter \"sdk version\"\n\t\tThen I see \"SDKMAN 3.2.1\"\n"
  }
]