Showing preview only (297K chars total). Download the full file or copy to clipboard to get everything.
Repository: sdkman/sdkman-cli
Branch: master
Commit: 532356b8e8a4
Files: 117
Total size: 268.7 KB
Directory structure:
gitextract_5nup9gj_/
├── .editorconfig
├── .github/
│ ├── CODEOWNERS
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ ├── config.yml
│ │ ├── feature_request.md
│ │ └── support_request.md
│ ├── auto_assign.yml
│ ├── dependabot.yml
│ ├── pull_request_template.md
│ └── workflows/
│ ├── beta.yml
│ ├── chloe-triage.yml
│ ├── notify-on-failure.yml
│ ├── pr.yml
│ └── release.yml
├── .gitignore
├── .sdkmanrc
├── CLAUDE.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Dockerfile
├── LICENSE
├── README.md
├── bin/
│ └── release-binary.sh
├── build.gradle
├── contrib/
│ └── completion/
│ └── bash/
│ └── sdk
├── dco.txt
├── gradle/
│ ├── archive.gradle
│ ├── release.gradle
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── settings.gradle
└── src/
├── jreleaser/
│ └── changelog.tpl
├── main/
│ └── bash/
│ ├── sdkman-availability.sh
│ ├── sdkman-cache.sh
│ ├── sdkman-config.sh
│ ├── sdkman-current.sh
│ ├── sdkman-default.sh
│ ├── sdkman-env-helpers.sh
│ ├── sdkman-env.sh
│ ├── sdkman-flush.sh
│ ├── sdkman-help.sh
│ ├── sdkman-home.sh
│ ├── sdkman-init.sh
│ ├── sdkman-install.sh
│ ├── sdkman-list.sh
│ ├── sdkman-main.sh
│ ├── sdkman-offline.sh
│ ├── sdkman-path-helpers.sh
│ ├── sdkman-selfupdate.sh
│ ├── sdkman-uninstall.sh
│ ├── sdkman-update.sh
│ ├── sdkman-upgrade.sh
│ ├── sdkman-use.sh
│ ├── sdkman-utils.sh
│ └── sdkman-version.sh
└── test/
├── groovy/
│ └── sdkman/
│ ├── cucumber/
│ │ └── RunCukeTests.groovy
│ ├── env/
│ │ ├── BashEnv.groovy
│ │ ├── CleanBashEnvBuilder.groovy
│ │ └── SdkmanBashEnvBuilder.groovy
│ ├── specs/
│ │ ├── CandidatesCacheUpdateFailureSpec.groovy
│ │ ├── CandidatesCacheUpdateSpec.groovy
│ │ ├── CompletionSpec.groovy
│ │ ├── ConfigCommandSpec.groovy
│ │ ├── CurrentCommandSpec.groovy
│ │ ├── EnvCommandSpec.groovy
│ │ ├── InitialisationSpec.groovy
│ │ ├── SdkCompatibilitySpec.groovy
│ │ └── SelfupdateFeatureSpec.groovy
│ ├── steps/
│ │ ├── command_line_interop_steps.groovy
│ │ ├── env.groovy
│ │ ├── env_steps.groovy
│ │ ├── flush_steps.groovy
│ │ ├── initialisation_steps.groovy
│ │ ├── installation_steps.groovy
│ │ ├── stub_steps.groovy
│ │ ├── update_steps.groovy
│ │ └── use_steps.groovy
│ ├── stubs/
│ │ ├── CurlStub.groovy
│ │ ├── HookResponses.groovy
│ │ ├── UnameStub.groovy
│ │ └── WebServiceStub.groovy
│ └── support/
│ ├── BashEnvSpecification.groovy
│ ├── FilesystemUtils.groovy
│ ├── SdkmanEnvSpecification.groovy
│ ├── UnixUtils.groovy
│ └── WireMockServerProvider.groovy
├── jmeter/
│ ├── SDKman.jmx
│ └── candidates.csv
└── resources/
├── __files/
│ └── hooks/
│ ├── post_hook_java_8.0.101_linuxx64.sh
│ ├── post_hook_java_8.0.111_linuxx64.sh
│ └── post_hook_java_8.0.111_universal.sh
└── features/
├── checksum_verification.feature
├── command_line_interop.feature
├── current_candidate.feature
├── default_version.feature
├── flush.feature
├── home.feature
├── hooks.feature
├── install_candidate.feature
├── install_sdkman.feature
├── java_installation.feature
├── list_candidate_versions.feature
├── list_candidates.feature
├── local_developement_versions.feature
├── mnemonics.feature
├── offline_mode.feature
├── path_initialisation.feature
├── per_project_configuration.feature
├── self_update.feature
├── service_unavailable.feature
├── uninstall_candidate.feature
├── update.feature
├── upgrade_candidate.feature
├── use_version.feature
└── version.feature
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = tab
insert_final_newline = false
max_line_length = 120
tab_width = 4
trim_trailing_whitespace = false
ij_continuation_indent_size = 8
ij_formatter_off_tag = @formatter:off
ij_formatter_on_tag = @formatter:on
ij_formatter_tags_enabled = false
ij_smart_tabs = false
ij_wrap_on_typing = false
[{*.yaml,*.yml}]
indent_size = 2
[{*.bash,*.sh,*.zsh}]
ij_shell_binary_ops_start_line = false
ij_shell_keep_column_alignment_padding = false
ij_shell_minify_program = false
ij_shell_redirect_followed_by_space = true
ij_shell_switch_cases_indented = false
[*.feature]
ij_gherkin_keep_indents_on_empty_lines = false
[{*.gant,*.gradle,*.groovy,*.gy}]
ij_groovy_align_group_field_declarations = false
ij_groovy_align_multiline_array_initializer_expression = false
ij_groovy_align_multiline_assignment = false
ij_groovy_align_multiline_binary_operation = false
ij_groovy_align_multiline_chained_methods = false
ij_groovy_align_multiline_extends_list = false
ij_groovy_align_multiline_for = true
ij_groovy_align_multiline_list_or_map = true
ij_groovy_align_multiline_method_parentheses = false
ij_groovy_align_multiline_parameters = true
ij_groovy_align_multiline_parameters_in_calls = false
ij_groovy_align_multiline_resources = true
ij_groovy_align_multiline_ternary_operation = false
ij_groovy_align_multiline_throws_list = false
ij_groovy_align_named_args_in_map = true
ij_groovy_align_throws_keyword = false
ij_groovy_array_initializer_new_line_after_left_brace = false
ij_groovy_array_initializer_right_brace_on_new_line = false
ij_groovy_array_initializer_wrap = off
ij_groovy_assert_statement_wrap = off
ij_groovy_assignment_wrap = off
ij_groovy_binary_operation_wrap = off
ij_groovy_blank_lines_after_class_header = 0
ij_groovy_blank_lines_after_imports = 1
ij_groovy_blank_lines_after_package = 1
ij_groovy_blank_lines_around_class = 1
ij_groovy_blank_lines_around_field = 0
ij_groovy_blank_lines_around_field_in_interface = 0
ij_groovy_blank_lines_around_method = 1
ij_groovy_blank_lines_around_method_in_interface = 1
ij_groovy_blank_lines_before_imports = 1
ij_groovy_blank_lines_before_method_body = 0
ij_groovy_blank_lines_before_package = 0
ij_groovy_block_brace_style = end_of_line
ij_groovy_block_comment_at_first_column = true
ij_groovy_call_parameters_new_line_after_left_paren = false
ij_groovy_call_parameters_right_paren_on_new_line = false
ij_groovy_call_parameters_wrap = off
ij_groovy_catch_on_new_line = false
ij_groovy_class_annotation_wrap = split_into_lines
ij_groovy_class_brace_style = end_of_line
ij_groovy_class_count_to_use_import_on_demand = 5
ij_groovy_do_while_brace_force = never
ij_groovy_else_on_new_line = false
ij_groovy_enum_constants_wrap = off
ij_groovy_extends_keyword_wrap = off
ij_groovy_extends_list_wrap = off
ij_groovy_field_annotation_wrap = split_into_lines
ij_groovy_finally_on_new_line = false
ij_groovy_for_brace_force = never
ij_groovy_for_statement_new_line_after_left_paren = false
ij_groovy_for_statement_right_paren_on_new_line = false
ij_groovy_for_statement_wrap = off
ij_groovy_if_brace_force = never
ij_groovy_import_annotation_wrap = 2
ij_groovy_indent_case_from_switch = true
ij_groovy_indent_label_blocks = true
ij_groovy_insert_inner_class_imports = false
ij_groovy_keep_blank_lines_before_right_brace = 2
ij_groovy_keep_blank_lines_in_code = 2
ij_groovy_keep_blank_lines_in_declarations = 2
ij_groovy_keep_control_statement_in_one_line = true
ij_groovy_keep_first_column_comment = true
ij_groovy_keep_indents_on_empty_lines = false
ij_groovy_keep_line_breaks = true
ij_groovy_keep_multiple_expressions_in_one_line = false
ij_groovy_keep_simple_blocks_in_one_line = false
ij_groovy_keep_simple_classes_in_one_line = true
ij_groovy_keep_simple_lambdas_in_one_line = true
ij_groovy_keep_simple_methods_in_one_line = true
ij_groovy_label_indent_absolute = false
ij_groovy_label_indent_size = 0
ij_groovy_lambda_brace_style = end_of_line
ij_groovy_layout_static_imports_separately = true
ij_groovy_line_comment_add_space = false
ij_groovy_line_comment_at_first_column = true
ij_groovy_method_annotation_wrap = split_into_lines
ij_groovy_method_brace_style = end_of_line
ij_groovy_method_call_chain_wrap = off
ij_groovy_method_parameters_new_line_after_left_paren = false
ij_groovy_method_parameters_right_paren_on_new_line = false
ij_groovy_method_parameters_wrap = off
ij_groovy_modifier_list_wrap = false
ij_groovy_names_count_to_use_import_on_demand = 3
ij_groovy_parameter_annotation_wrap = off
ij_groovy_parentheses_expression_new_line_after_left_paren = false
ij_groovy_parentheses_expression_right_paren_on_new_line = false
ij_groovy_prefer_parameters_wrap = false
ij_groovy_resource_list_new_line_after_left_paren = false
ij_groovy_resource_list_right_paren_on_new_line = false
ij_groovy_resource_list_wrap = off
ij_groovy_space_after_assert_separator = true
ij_groovy_space_after_colon = true
ij_groovy_space_after_comma = true
ij_groovy_space_after_comma_in_type_arguments = true
ij_groovy_space_after_for_semicolon = true
ij_groovy_space_after_quest = true
ij_groovy_space_after_type_cast = true
ij_groovy_space_before_annotation_parameter_list = false
ij_groovy_space_before_array_initializer_left_brace = false
ij_groovy_space_before_assert_separator = false
ij_groovy_space_before_catch_keyword = true
ij_groovy_space_before_catch_left_brace = true
ij_groovy_space_before_catch_parentheses = true
ij_groovy_space_before_class_left_brace = true
ij_groovy_space_before_closure_left_brace = true
ij_groovy_space_before_colon = true
ij_groovy_space_before_comma = false
ij_groovy_space_before_do_left_brace = true
ij_groovy_space_before_else_keyword = true
ij_groovy_space_before_else_left_brace = true
ij_groovy_space_before_finally_keyword = true
ij_groovy_space_before_finally_left_brace = true
ij_groovy_space_before_for_left_brace = true
ij_groovy_space_before_for_parentheses = true
ij_groovy_space_before_for_semicolon = false
ij_groovy_space_before_if_left_brace = true
ij_groovy_space_before_if_parentheses = true
ij_groovy_space_before_method_call_parentheses = false
ij_groovy_space_before_method_left_brace = true
ij_groovy_space_before_method_parentheses = false
ij_groovy_space_before_quest = true
ij_groovy_space_before_switch_left_brace = true
ij_groovy_space_before_switch_parentheses = true
ij_groovy_space_before_synchronized_left_brace = true
ij_groovy_space_before_synchronized_parentheses = true
ij_groovy_space_before_try_left_brace = true
ij_groovy_space_before_try_parentheses = true
ij_groovy_space_before_while_keyword = true
ij_groovy_space_before_while_left_brace = true
ij_groovy_space_before_while_parentheses = true
ij_groovy_space_in_named_argument = true
ij_groovy_space_in_named_argument_before_colon = false
ij_groovy_space_within_empty_array_initializer_braces = false
ij_groovy_space_within_empty_method_call_parentheses = false
ij_groovy_spaces_around_additive_operators = true
ij_groovy_spaces_around_assignment_operators = true
ij_groovy_spaces_around_bitwise_operators = true
ij_groovy_spaces_around_equality_operators = true
ij_groovy_spaces_around_lambda_arrow = true
ij_groovy_spaces_around_logical_operators = true
ij_groovy_spaces_around_multiplicative_operators = true
ij_groovy_spaces_around_regex_operators = true
ij_groovy_spaces_around_relational_operators = true
ij_groovy_spaces_around_shift_operators = true
ij_groovy_spaces_within_annotation_parentheses = false
ij_groovy_spaces_within_array_initializer_braces = false
ij_groovy_spaces_within_braces = true
ij_groovy_spaces_within_brackets = false
ij_groovy_spaces_within_cast_parentheses = false
ij_groovy_spaces_within_catch_parentheses = false
ij_groovy_spaces_within_for_parentheses = false
ij_groovy_spaces_within_gstring_injection_braces = false
ij_groovy_spaces_within_if_parentheses = false
ij_groovy_spaces_within_list_or_map = false
ij_groovy_spaces_within_method_call_parentheses = false
ij_groovy_spaces_within_method_parentheses = false
ij_groovy_spaces_within_parentheses = false
ij_groovy_spaces_within_switch_parentheses = false
ij_groovy_spaces_within_synchronized_parentheses = false
ij_groovy_spaces_within_try_parentheses = false
ij_groovy_spaces_within_tuple_expression = false
ij_groovy_spaces_within_while_parentheses = false
ij_groovy_special_else_if_treatment = true
ij_groovy_ternary_operation_wrap = off
ij_groovy_throws_keyword_wrap = off
ij_groovy_throws_list_wrap = off
ij_groovy_use_flying_geese_braces = false
ij_groovy_use_fq_class_names = false
ij_groovy_use_fq_class_names_in_javadoc = true
ij_groovy_use_relative_indents = false
ij_groovy_use_single_class_imports = true
ij_groovy_variable_annotation_wrap = off
ij_groovy_while_brace_force = never
ij_groovy_while_on_new_line = false
ij_groovy_wrap_long_lines = false
================================================
FILE: .github/CODEOWNERS
================================================
* @sdkman/sdkman-cli
================================================
FILE: .github/FUNDING.yml
================================================
open_collective: sdkman
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: You found a bug in SDKMAN!
title: "Bug: [A concise title]"
labels: 'bug'
assignees: ''
---
<!-- DO NOT DELETE THIS TEMPLATE AND PLEASE READ IT WITH CARE! -->
<!-- Thank you for using the SDKMAN! issue tracker. Please fill in everything to the best of your ability. If you need clarification on whether it is a bug, please use our #help Discord channel before creating a new issue. Please know that we cannot offer regular support through GitHub issues and will close the issue without warning if it is not a bug. -->
**Bug report**
<!-- A clear and concise description of the bug you encountered -->
**To reproduce**
<!-- Steps to reproduce the behaviour or verify the issue -->
**System info**
<!-- Please add relevant information about your system:
- OS (e.g. Windows, Linux, Mac, Cygwin, WSL, etc.) and version
- Shell and version (e.g. `bash --version`/`zsh --version`)
- The output of `sdk version`
-->
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
- name: SDKMAN! usage documentation
url: https://sdkman.io/usage
about: Find documentation about the available commands here.
- name: SDKMAN! Discord server
url: https://discord.gg/y9mVJYVyu4
about: Please ask and answer questions about the usage of SDKMAN! here.
- name: SDKMAN! on Stack Overflow
url: https://stackoverflow.com/questions/tagged/sdkman
about: You might find help to common issues here.
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for SDKMAN!
title: 'Feature: [A concise title]'
labels: 'enhancement'
assignees: ''
---
<!-- Thank you for suggesting a new feature. Please discuss your idea on our #discussions Discord channel before requesting a new feature. -->
- [ ] I have read the [CONTRIBUTING guidelines](CONTRIBUTING.md)
- [ ] I've had a conversation on our community [Discord](https://discord.gg/y9mVJYVyu4) server.
**Feature request**
<!-- A clear and precise description of your desired new or changed feature. Please include the reason why you would need the feature. E.g. what problem does it solve? Or which workflow is currently frustrating and will be improved by this? -->
================================================
FILE: .github/ISSUE_TEMPLATE/support_request.md
================================================
---
name: Support request or question
about: You need help using SDKMAN!
title: "Question: [Short summary]"
labels: 'support'
assignees: ''
---
<!-- Thank you for using SDKMAN!. We want to help users using the software but also avoid filling the issue tracker with too many support requests. Please talk to us on our Discord server before posting.
Please consider the following things. Just so you know, ignoring any of these might lead to the issue being closed abruptly. -->
- [ ] I have checked [the documentation](https://sdkman.io/usage)
- [ ] I have performed a quick search in [past issues](https://github.com/sdkman/sdkman-cli/issues?q=) or [Stack Overflow](https://stackoverflow.com/questions/tagged/sdkman) for similar problems
- [ ] I have brought up a conversation in the community [Discord](https://discord.gg/y9mVJYVyu4) server
**Question**
<!-- A clear and concise description of the issue you are encountering -->
**System info**
<!-- Please add relevant information about your system:
- OS (e.g. Windows, Linux, Mac, Cygwin, WSL, etc.) and version
- Shell and version (e.g. `bash --version`/`zsh --version`)
- The output of `sdk version`
-->
================================================
FILE: .github/auto_assign.yml
================================================
---
addReviewers: true
addAssignees: author
reviewers:
- marc0der
- helpermethod
numberOfReviewers: 0
================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: daily
================================================
FILE: .github/pull_request_template.md
================================================
<!-- To raise a new Pull Request, the following prerequisites need to be met. Please tick before proceeding: -->
- [ ] a GitHub Issue was opened for this feature / bug.
- [ ] test coverage was added (Cucumber or Spock as appropriate).
Fixes #XXX
================================================
FILE: .github/workflows/beta.yml
================================================
name: Beta Release
on:
push:
branches:
- master
paths-ignore:
- .github/workflows/*
jobs:
pre-release:
name: "Beta Release"
environment: production
runs-on: "ubuntu-latest"
services:
mongodb:
image: mongo:3.2
ports:
- 27017:27017
steps:
- uses: actions/checkout@v3
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '11'
cache: 'gradle'
- name: Run tests
run: ./gradlew clean test --info
- name: Set short git hash
id: var
run: echo "::set-output name=sha_short::$(git rev-parse --short HEAD)"
- name: Build artifacts
run: ./gradlew -Penv=beta -Phash=${{ steps.var.outputs.sha_short }} clean assemble
- name: Release
if: github.repository == 'sdkman/sdkman-cli'
uses: "marvinpinto/action-automatic-releases@latest"
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
automatic_release_tag: "latest"
prerelease: true
title: "Beta release"
files: |
build/distributions/sdkman-cli-*.zip
- name: Update MongoDB
if: github.repository == 'sdkman/sdkman-cli'
env:
MONGO_URL: ${{ secrets.MONGO_URL }}
MONGO_USERNAME: ${{ secrets.MONGO_USERNAME }}
MONGO_PASSWORD: ${{ secrets.MONGO_PASSWORD }}
SHORT_HASH: ${{ steps.var.outputs.sha_short }}
run: bin/release-binary.sh "$MONGO_URL" "$MONGO_USERNAME" "$MONGO_PASSWORD" "$SHORT_HASH" "latest"
================================================
FILE: .github/workflows/chloe-triage.yml
================================================
name: Notify Chloé (Issue Triage)
on:
issues:
types: [opened]
issue_comment:
types: [created]
pull_request:
types: [opened]
jobs:
notify:
uses: sdkman/.github/.github/workflows/reusable-triage.yml@main
secrets: inherit
================================================
FILE: .github/workflows/notify-on-failure.yml
================================================
name: Notify on Failure
on:
workflow_run:
workflows: ["*"]
branches: [master, main]
types: [completed]
jobs:
notify:
uses: sdkman/.github/.github/workflows/reusable-notify-on-failure.yml@main
secrets: inherit
================================================
FILE: .github/workflows/pr.yml
================================================
name: Pull Requests
#run only on PR, not fork. Pull request that way the PR submitter will not have access to the repo credentials
on: pull_request
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout source code
uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }}
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '11'
cache: 'gradle'
- name: Run with Gradle
run: ./gradlew clean test --info
- uses: kentaro-m/auto-assign-action@v1.2.5
with:
configuration-path: ".github/auto_assign.yml"
================================================
FILE: .github/workflows/release.yml
================================================
name: Release
on:
workflow_dispatch:
inputs:
version:
description: "Release version"
required: true
jobs:
build:
name: "Stable Release"
if: github.repository == 'sdkman/sdkman-cli'
runs-on: ubuntu-latest
environment: production
env:
JRELEASER_GITHUB_TOKEN: ${{ github.token }}
JRELEASER_TWITTER_CONSUMER_KEY: ${{ secrets.TWITTER_CONSUMER_API_KEY }}
JRELEASER_TWITTER_CONSUMER_SECRET: ${{ secrets.TWITTER_CONSUMER_API_SECRET }}
JRELEASER_TWITTER_ACCESS_TOKEN: ${{ secrets.TWITTER_ACCESS_TOKEN }}
JRELEASER_TWITTER_ACCESS_TOKEN_SECRET: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }}
JRELEASER_MASTODON_ACCESS_TOKEN: ${{ secrets.MASTODON_ACCESS_TOKEN}}
services:
mongodb:
image: mongo:3.2
ports:
- 27017:27017
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '11'
cache: 'gradle'
- name: Run tests
run: ./gradlew clean test --info
- name: Build artifacts
run: ./gradlew -Penv=stable -Prelease=${{ github.event.inputs.version }} clean assemble
- name: Release
run: ./gradlew -Penv=stable -Prelease=${{ github.event.inputs.version }} jreleaserFullRelease --exclude-announcer=twitter
- name: Update MongoDB
env:
MONGO_URL: ${{ secrets.MONGO_URL }}
MONGO_USERNAME: ${{ secrets.MONGO_USERNAME }}
MONGO_PASSWORD: ${{ secrets.MONGO_PASSWORD }}
RELEASE_TAG: ${{ github.event.inputs.version }}
run: bin/release-binary.sh "$MONGO_URL" "$MONGO_USERNAME" "$MONGO_PASSWORD" "$RELEASE_TAG" "stable"
- name: Tweet about it
run: ./gradlew -Penv=stable -Prelease=${{ github.event.inputs.version }} jreleaserAnnounce
================================================
FILE: .gitignore
================================================
*~
.#*
.classpath
.gradle/
.gradletasknamecache
.idea/
.pid.lock
.project
.settings/
.vscode/
\#*
build/
classes
sdkman-cli.iml
sdkman.iml
sdkman.ipr
sdkman.iws
mongo.json
out/
sdkman-cli.iml
target/
bin/test
================================================
FILE: .sdkmanrc
================================================
# Enable auto-env through the sdkman_auto_env config
# Add key=value pairs of SDKs to use below
java=11.0.17-tem
================================================
FILE: CLAUDE.md
================================================
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
SDKMAN! CLI is a bash-based command-line tool for managing parallel versions of multiple Software Development Kits on Unix-based systems. The project is primarily written in bash scripts with Groovy/Spock for testing using Cucumber BDD tests.
**Important**: This project is in maintenance mode. Only bug fixes are accepted as the project is being rewritten in Rust under a new project. No new enhancements to existing commands will be accepted.
## Development Environment
- Requires JDK 11 (specified in `.sdkmanrc`)
- Uses Gradle 8.0.1 for build management
- Run `sdk env install` to install the correct JDK version
- Run `sdk env` to switch to the appropriate SDK versions
## Build and Test Commands
### Core Commands
- `./gradlew test` - Run all Cucumber BDD tests
- `./gradlew clean` - Clean build artifacts
- `./gradlew build` - Build the project
### Running Specific Tests
- Tests can be run with tags: use `@manual` and `@review` tags to exclude certain tests
- Individual feature files can be run by specifying the feature file path
- Environment can be set via `env` property (local/beta/stable) - defaults to local
### Development Setup
Before starting development, ensure the correct Java version:
```bash
sdk env install # Install JDK from .sdkmanrc
sdk env # Switch to correct SDK versions
```
## Architecture
### Code Structure
- `src/main/bash/` - Core bash scripts implementing SDKMAN commands
- `sdkman-main.sh` - Main entry point and command routing
- `sdkman-init.sh` - Environment initialization and platform detection
- Individual command scripts (e.g., `sdkman-install.sh`, `sdkman-list.sh`)
- `src/test/groovy/` - Groovy test code using Spock framework
- `src/test/resources/features/` - Cucumber feature files (BDD tests)
### Core Components
- **Command Router**: `sdkman-main.sh` handles command aliases and routing
- **Environment Setup**: `sdkman-init.sh` manages platform detection and configuration
- **Modular Commands**: Each SDKMAN command is implemented in its own bash script
- **API Integration**: Commands interact with SDKMAN API for candidate information
### Testing Strategy
- **Cucumber BDD Tests**: Primary testing approach using Gherkin feature files
- **Spock Unit Tests**: Some Groovy unit tests for specific components
- Tests cover both happy path and edge cases
- Mock external dependencies using WireMock
- Test environment uses bash process execution
### Configuration
- `.sdkmanrc` file specifies required Java version (11.0.17-tem)
- Environment-specific API endpoints configured in `build.gradle`:
- **local**: `http://localhost:8080/2` (for development)
- **beta**: `https://beta.sdkman.io/2`
- **stable**: `https://api.sdkman.io/2` (production)
- Pass environment via `-Penv=<environment>` to gradle commands
## Key Files
- `build.gradle` - Main build configuration with test dependencies
- `src/main/bash/sdkman-main.sh` - Command entry point and routing
- `src/test/groovy/sdkman/cucumber/RunCukeTests.groovy` - Cucumber test runner
- `src/test/resources/features/` - BDD feature specifications
## Testing Guidelines
- All features should have Cucumber tests covering happy and unhappy paths
- Tests should be written before implementation (TDD approach)
- Use `@manual` and `@review` tags to exclude certain tests from automation
- Mock external API calls using WireMock stubs
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at info@sdkman.io. All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series of actions.
**Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][Mozilla CoC].
For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at [https://www.contributor-covenant.org/translations][translations].
[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to SDKMAN! CLI
Thank you for your interest in contributing to SDKMAN! CLI. We greatly value the feedback and contributions of our users.
## Important Notice
**This project is in maintenance mode.** We are only accepting bug fixes at this time, as SDKMAN! CLI is being rewritten in Rust. New features and enhancements will not be accepted.
## Code of Conduct
This project adheres to the [Contributor Covenant Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to info@sdkman.io.
## How to Contribute
### Before You Start
We distinguish between:
- **Bug Reports**: Issues with existing functionality
- **Support Requests**: Questions about usage
We prefer to have a conversation on [SDKMAN Discord](https://discord.gg/y9mVJYVyu4) before creating new issues if you're unsure how to categorize your request. Join our Help channel to discuss.
### Reporting Bugs
1. **Search existing issues** to avoid duplicates
2. **Use the issue template** when creating a new issue via our [GitHub Issue Tracker](https://github.com/sdkman/sdkman-cli/issues/new)
3. **Provide detailed information** including:
- Steps to reproduce the issue
- Expected behavior
- Actual behavior
- Your environment (OS, shell, Java version)
- Relevant logs or error messages
**Note:** Issues that don't follow the template may be closed.
### Submitting Pull Requests
Pull requests are always welcome but must follow these guidelines:
#### Prerequisites
1. **Link to an issue**: Every PR must reference a valid GitHub issue
2. **Discuss first**: Talk about your proposed fix on Discord or in the issue before starting work
3. **Keep it small**: Small, focused PRs are strongly preferred over large changes
4. **Include tests**: Each PR should include passing tests that prove its validity (where feasible)
#### Development Setup
1. **Install required tools**:
```bash
sdk env install # Install JDK 11 from .sdkmanrc
sdk env # Switch to correct SDK versions
```
2. **Verify your setup**:
```bash
./gradlew test
```
#### Development Workflow
1. **Fork the repository** and create a new branch from `master`
2. **Make your changes** following our code standards
3. **Write or update tests** to cover your changes
4. **Run the test suite** to ensure everything passes:
```bash
./gradlew test
```
5. **Commit your changes** using clear, descriptive commit messages
6. **Push to your fork** and submit a pull request
#### Pull Request Guidelines
- Fill in the PR template completely
- Link back to the GitHub issue by replacing `#XXX` with the issue number
- Ensure all tests pass
- Keep commits focused and atomic
- Write clear commit messages in imperative mood (e.g., "fix user login bug")
- Be responsive to feedback during code review
#### Testing Requirements
- All bug fixes must include a test that would have caught the bug
- Tests should follow the existing Cucumber BDD pattern
- Tests should cover both happy path and edge cases
- Run `./gradlew test` to verify all tests pass
### Code Standards
- Follow the existing code style in bash scripts
- Use meaningful variable and function names
- Add comments for complex logic
- Avoid introducing new dependencies without discussion
- Ensure backward compatibility
### Getting Help
- Join our [Discord server](https://discord.gg/y9mVJYVyu4) for questions
- Check existing issues and discussions
- Read the documentation in the repository
## Project Structure
- `src/main/bash/` - Core bash scripts implementing SDKMAN commands
- `src/test/groovy/` - Groovy test code using Spock framework
- `src/test/resources/features/` - Cucumber feature files (BDD tests)
## Recognition
Contributors will be recognized in our release notes and commit history. We appreciate your efforts to make SDKMAN! better!
## Developer Certificate of Origin
By contributing to this project, you agree to the terms of the [Developer Certificate of Origin](dco.txt). This certifies that you have the right to submit your contribution under the project's open source license.
## Questions?
If you have questions about contributing, feel free to ask on [Discord](https://discord.gg/y9mVJYVyu4) or open a discussion on GitHub.
================================================
FILE: Dockerfile
================================================
FROM openjdk:11
RUN apt-get update && \
apt-get -y install zip
# Copy all here
RUN mkdir -p /usr/src/app
ADD . /usr/src/app
WORKDIR /usr/src/app
ENTRYPOINT ["./gradlew"]
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
================================================
FILE: README.md
================================================
# SDKMAN! CLI
### The Software Development Kit Manager Command Line Interface
[](#backers)
[](#sponsors)
[](https://discord.gg/y9mVJYVyu4)
SDKMAN is a tool for managing parallel Versions of multiple Software Development Kits on any Unix-based system. It provides a convenient command-line interface for installing, switching, removing, and listing Candidates.
See documentation on the [SDKMAN! website](https://sdkman.io).
## NOTICE
**We are rewriting all the commands for SDKMAN! in [Rust](https://www.rust-lang.org/) under a [new project](https://github.com/sdkman/sdkman-cli-native) that supplements this one. Only bug fixes to supporting code will be
accepted in this project. As a result, no further enhancements will be accepted on the commands in this project, and the commands here will be phased out in due course. This project will eventually form a lightweight
wrapper/launcher for the replacement Rust commands.**
## Installation
Open your favourite terminal and enter the following:
$ curl -s https://get.sdkman.io | bash
If the environment needs tweaking for SDKMAN to be installed, the installer will prompt you accordingly and ask you to restart.
## Running the Cucumber Features
All SDKMAN's BDD tests describing the CLI behaviour are written in Cucumber and can be found under `src/test/resources/features`. These can be run with Gradle by running the following command:
$ ./gradlew test
To perform development, you will need to have a JDK 11 installed which can be obtained by running the following after installing SDKMAN:
$ sdk env install
## Hosting
We're proud to host our backend services on DigitalOcean as a sponsored partner.
[](https://www.digitalocean.com/?refcode=d99e5747251d&utm_campaign=Referral_Invite&utm_medium=Referral_Program&utm_source=badge)
## Contributors
This project exists thanks to all the people who contribute.
<a href="https://github.com/sdkman/sdkman-cli/graphs/contributors"><img src="https://opencollective.com/sdkman/contributors.svg?width=890&button=false" /></a>
## Backers
Thank you to all our backers! [[Become a backer](https://opencollective.com/sdkman#backer)]
<a href="https://opencollective.com/sdkman#backers" target="_blank"><img src="https://opencollective.com/sdkman/backers.svg?width=890"></a>
## Sponsors
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/sdkman#sponsor)]
<a href="https://opencollective.com/sdkman/sponsor/0/website" target="_blank"><img src="https://opencollective.com/sdkman/sponsor/0/avatar.svg"></a>
<a href="https://opencollective.com/sdkman/sponsor/1/website" target="_blank"><img src="https://opencollective.com/sdkman/sponsor/1/avatar.svg"></a>
<a href="https://opencollective.com/sdkman/sponsor/2/website" target="_blank"><img src="https://opencollective.com/sdkman/sponsor/2/avatar.svg"></a>
<a href="https://opencollective.com/sdkman/sponsor/3/website" target="_blank"><img src="https://opencollective.com/sdkman/sponsor/3/avatar.svg"></a>
<a href="https://opencollective.com/sdkman/sponsor/4/website" target="_blank"><img src="https://opencollective.com/sdkman/sponsor/4/avatar.svg"></a>
<a href="https://opencollective.com/sdkman/sponsor/5/website" target="_blank"><img src="https://opencollective.com/sdkman/sponsor/5/avatar.svg"></a>
<a href="https://opencollective.com/sdkman/sponsor/6/website" target="_blank"><img src="https://opencollective.com/sdkman/sponsor/6/avatar.svg"></a>
<a href="https://opencollective.com/sdkman/sponsor/7/website" target="_blank"><img src="https://opencollective.com/sdkman/sponsor/7/avatar.svg"></a>
<a href="https://opencollective.com/sdkman/sponsor/8/website" target="_blank"><img src="https://opencollective.com/sdkman/sponsor/8/avatar.svg"></a>
<a href="https://opencollective.com/sdkman/sponsor/9/website" target="_blank"><img src="https://opencollective.com/sdkman/sponsor/9/avatar.svg"></a>
================================================
FILE: bin/release-binary.sh
================================================
#!/usr/bin/env bash
MONGO_URL="$1"
MONGO_USERNAME="$2"
MONGO_PASSWORD="$3"
PARAM_1="$4"
PARAM_2="$5"
echo "Mongo URL: $MONGO_URL"
if [[ -z "$MONGO_USERNAME" || -z "$MONGO_PASSWORD" ]]; then
echo "No mongo credentials so doing nothing..."
return 1
fi
if [[ "$PARAM_2" == 'stable' ]]; then
FIELD="stableCliVersion"
VERSION="$PARAM_1"
elif [[ "$PARAM_2" == 'latest' ]]; then
FIELD="betaCliVersion"
VERSION="latest+$PARAM_1"
else
return 1
fi
echo "Release: $FIELD as $VERSION"
docker run mongo:3.2 mongo "${MONGO_URL}" \
--username="${MONGO_USERNAME}" \
--password="${MONGO_PASSWORD}" \
--quiet \
--eval "db.application.updateOne({}, {\$set: { \"$FIELD\": \"$VERSION\"}});"
================================================
FILE: build.gradle
================================================
plugins {
id('groovy')
id('org.jreleaser').version('1.4.0').apply(false)
}
String userHome = System.getProperty('user.home')
ext.installBinDir = "${userHome}/.sdkman/bin"
ext.installSrcDir = "${userHome}/.sdkman/src"
ext.installContribDir = "${userHome}/.sdkman/contrib"
ext.environment = hasProperty('env') ? env : 'local'
ext.hash = hasProperty('hash') ? hash : 'hashme'
ext.release = hasProperty('release') ? release : 'latest'
if ("$environment" == 'stable') {
ext.candidatesApi = 'https://api.sdkman.io/2'
ext.brokerApi = 'https://broker.sdkman.io'
} else if ("$environment" == 'beta') {
ext.candidatesApi = 'https://beta.sdkman.io/2'
ext.brokerApi = 'https://broker.sdkman.io'
} else {
ext.candidatesApi = 'http://localhost:8080/2'
ext.brokerApi = 'https://localhost:8080'
}
ext.sdkmanVersion = ext.release == 'latest' ? "latest+${ext.hash}".toString() : ext.release
println("Environment is set to: $environment")
println("Short git hash: $hash")
println("Release set to: $release")
println("Candidates API: $candidatesApi")
println("Broker API: $brokerApi")
println("Version: $sdkmanVersion")
apply from: 'gradle/archive.gradle'
apply from: 'gradle/release.gradle'
repositories {
mavenCentral()
}
dependencies {
testImplementation('com.github.tomakehurst:wiremock:2.27.2') {
exclude module: 'junit'
}
testImplementation('io.cucumber:cucumber-groovy:4.7.1')
testImplementation('io.cucumber:cucumber-junit:4.7.4')
testImplementation('io.cucumber:gherkin:5.2.0')
testImplementation('junit:junit:4.13.2')
testImplementation('org.codehaus.groovy:groovy:2.4.21')
testImplementation('org.codehaus.groovy:groovy-json:2.4.21')
testImplementation('org.codehaus.groovy:groovy-templates:2.4.21')
testImplementation('org.slf4j:slf4j-api:1.7.32')
testImplementation('org.slf4j:slf4j-simple:1.7.32')
testImplementation('org.spockframework:spock-core:1.3-groovy-2.4') {
exclude module: 'groovy-all'
}
}
================================================
FILE: contrib/completion/bash/sdk
================================================
#!/usr/bin/bash
_sdk() {
local -r previous_word=${COMP_WORDS[COMP_CWORD - 1]}
local -r current_word=${COMP_WORDS[COMP_CWORD]}
if ((COMP_CWORD == 3)); then
local -r before_previous_word=${COMP_WORDS[COMP_CWORD - 2]}
__sdkman_complete_candidate_version "$before_previous_word" "$previous_word" "$current_word"
return
fi
__sdkman_complete_command "$previous_word" "$current_word"
}
__sdkman_complete_command() {
local -r command=$1
local -r current_word=$2
local -a candidates
case $command in
sdk)
candidates=("install" "uninstall" "list" "use" "config" "default" "home" "env" "current" "upgrade" "version" "help" "offline" "selfupdate" "update" "flush")
;;
current|c|default|d|home|h|uninstall|rm|upgrade|ug|use|u)
local -r candidate_paths=("${SDKMAN_CANDIDATES_DIR}"/*)
for candidate_path in "${candidate_paths[@]}"; do
candidates+=("${candidate_path##*/}")
done
;;
install|i|list|ls)
candidates=${SDKMAN_CANDIDATES[@]}
;;
env|e)
candidates=("init" "install" "clear")
;;
offline)
candidates=("enable" "disable")
;;
selfupdate)
candidates=("force")
;;
flush)
candidates=("temp" "version")
;;
esac
COMPREPLY=($(compgen -W "${candidates[*]}" -- "$current_word"))
}
__sdkman_complete_candidate_version() {
local -r command=$1
local -r candidate=$2
local -r candidate_version=$3
local -a candidates
case $command in
default|d|home|h|uninstall|rm|use|u)
local -r version_paths=("${SDKMAN_CANDIDATES_DIR}/${candidate}"/*)
for version_path in "${version_paths[@]}"; do
[[ $version_path = *current ]] && continue
candidates+=("${version_path##*/}")
done
;;
install|i)
while IFS= read -r -d, version || [[ -n "$version" ]]; do
candidates+=("$version")
done <<< "$(curl --silent "${SDKMAN_CANDIDATES_API}/candidates/$candidate/${SDKMAN_PLATFORM}/versions/all")"
;;
esac
COMPREPLY=($(compgen -W "${candidates[*]}" -- "$candidate_version"))
}
complete -o default -F _sdk sdk
# Set 'sdkman_auto_complete' to 'true' in .sdkman/etc/config to enable completion
================================================
FILE: dco.txt
================================================
Developer Certificate of Origin
Version 1.1
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
1 Letterman Drive
Suite D4700
San Francisco, CA, 94129
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
================================================
FILE: gradle/archive.gradle
================================================
import org.apache.tools.ant.filters.ReplaceTokens
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
def baseDir = "$buildDir/stage/sdkman-${ext.sdkmanVersion}"
task prepareBin(type: Copy) {
from('src/main/bash')
into("$baseDir/bin")
include('**/sdkman-init.sh')
filter(
ReplaceTokens,
tokens: [
SDKMAN_CANDIDATES_API: candidatesApi,
SDKMAN_BROKER_API: brokerApi
]
)
}
task prepareScripts(type: Copy) {
from('src/main/bash')
into("$baseDir/src")
include('**/*')
exclude('**/sdkman-init.sh')
}
task prepareContrib(type: Copy) {
from('contrib')
into("$baseDir/contrib")
}
tasks.test.configure {
dependsOn(prepareScripts)
testLogging.exceptionFormat = TestExceptionFormat.FULL
}
task assembleArchive(type: Zip, dependsOn: [prepareBin, prepareScripts, prepareContrib]) {
archiveVersion = sdkmanVersion
from('build/stage') {
include('**/*')
}
}
tasks.assemble.configure {
dependsOn(assembleArchive)
}
tasks.test.configure {
dependsOn(prepareBin, prepareScripts, prepareContrib)
}
================================================
FILE: gradle/release.gradle
================================================
apply plugin: 'org.jreleaser'
jreleaser {
project {
name = 'sdkman-cli'
version = sdkmanVersion
description = 'Sdkman - The Software Development Kit Manager'
website = 'https://sdkman.io'
authors = ['Marco Vermeulen']
license = 'Apache-2.0'
extraProperties.put('inceptionYear', '2012')
}
release {
github {
overwrite = true
tagName = '{{projectVersion}}'
changelog {
formatted = 'ALWAYS'
format = '- {{commitShortHash}} {{commitTitle}}'
contributors {
format = '- {{contributorName}}'
}
contentTemplate = file('src/jreleaser/changelog.tpl')
hide {
contributors = ['GitHub']
}
}
}
}
distributions {
'sdkman-cli' {
distributionType = 'BINARY'
artifact {
path = "build/distributions/{{distributionName}}-${sdkmanVersion}.zip"
extraProperties.put("universal", "true")
}
}
}
announce {
twitter {
active = 'RELEASE'
status = 'Released version {{tagName}} of SDKMAN! {{releaseNotesUrl}}'
}
mastodon {
active = 'RELEASE'
status = 'Released version {{tagName}} of SDKMAN! {{releaseNotesUrl}}'
host = 'https://fosstodon.org'
}
}
}
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
================================================
FILE: gradlew
================================================
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"
================================================
FILE: settings.gradle
================================================
pluginManagement {
repositories {
mavenLocal()
gradlePluginPortal()
mavenCentral()
}
}
rootProject.name = "sdkman-cli"
================================================
FILE: src/jreleaser/changelog.tpl
================================================
## Changelog
{{changelogChanges}}
{{changelogContributors}}
================================================
FILE: src/main/bash/sdkman-availability.sh
================================================
#!/usr/bin/env bash
#
# Copyright 2021 Marco Vermeulen
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
function __sdkman_update_service_availability() {
local healthcheck_status=$(__sdkman_determine_healthcheck_status)
__sdkman_set_availability "$healthcheck_status"
}
function __sdkman_determine_healthcheck_status() {
if [[ "$SDKMAN_OFFLINE_MODE" == "true" || "$COMMAND" == "offline" && "$QUALIFIER" == "enable" ]]; then
echo ""
else
echo $(__sdkman_secure_curl_with_timeouts "${SDKMAN_CANDIDATES_API}/healthcheck")
fi
}
function __sdkman_set_availability() {
local healthcheck_status="$1"
local detect_html="$(echo "$healthcheck_status" | tr '[:upper:]' '[:lower:]' | grep 'html')"
if [[ -z "$healthcheck_status" ]]; then
SDKMAN_AVAILABLE="false"
__sdkman_display_offline_warning "$healthcheck_status"
elif [[ -n "$detect_html" ]]; then
SDKMAN_AVAILABLE="false"
__sdkman_display_proxy_warning
else
SDKMAN_AVAILABLE="true"
fi
}
function __sdkman_display_offline_warning() {
local healthcheck_status="$1"
if [[ -z "$healthcheck_status" && "$COMMAND" != "offline" && "$SDKMAN_OFFLINE_MODE" != "true" ]]; then
__sdkman_echo_red "==== INTERNET NOT REACHABLE! ==================================================="
__sdkman_echo_red ""
__sdkman_echo_red " Some functionality is disabled or only partially available."
__sdkman_echo_red " If this persists, please enable the offline mode:"
__sdkman_echo_red ""
__sdkman_echo_red " $ sdk offline"
__sdkman_echo_red ""
__sdkman_echo_red "================================================================================"
echo ""
fi
}
function __sdkman_display_proxy_warning() {
__sdkman_echo_red "==== PROXY DETECTED! ==========================================================="
__sdkman_echo_red "Please ensure you have open internet access to continue."
__sdkman_echo_red "================================================================================"
echo ""
}
================================================
FILE: src/main/bash/sdkman-cache.sh
================================================
#!/usr/bin/env bash
#
# Copyright 2021 Marco Vermeulen
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
function ___sdkman_check_candidates_cache() {
local candidates_cache="$1"
if [[ -f "$candidates_cache" && -z "$(< "$candidates_cache")" ]]; then
__sdkman_echo_red 'WARNING: Cache is corrupt. SDKMAN cannot be used until updated.'
echo ''
__sdkman_echo_no_colour ' $ sdk update'
echo ''
return 1
else
__sdkman_echo_debug "Using existing cache: $SDKMAN_CANDIDATES_CSV"
return 0
fi
}
================================================
FILE: src/main/bash/sdkman-config.sh
================================================
#!/usr/bin/env bash
#
# Copyright 2021 Marco Vermeulen
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
function __sdk_config() {
local -r editor=(${EDITOR:=vi})
if ! command -v "${editor[@]}" > /dev/null; then
__sdkman_echo_red "No default editor configured."
__sdkman_echo_yellow "Please set the default editor with the EDITOR environment variable."
return 1
fi
"${editor[@]}" "${SDKMAN_DIR}/etc/config"
}
================================================
FILE: src/main/bash/sdkman-current.sh
================================================
#!/usr/bin/env bash
#
# Copyright 2021 Marco Vermeulen
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
function __sdk_current() {
local candidate="$1"
echo ""
if [ -n "$candidate" ]; then
__sdkman_determine_current_version "$candidate"
if [ -n "$CURRENT" ]; then
__sdkman_echo_no_colour "Using ${candidate} version ${CURRENT}"
else
__sdkman_echo_red "Not using any version of ${candidate}"
fi
else
local installed_count=0
for ((i = 0; i <= ${#SDKMAN_CANDIDATES[*]}; i++)); do
# Eliminate empty entries due to incompatibility
if [[ -n ${SDKMAN_CANDIDATES[${i}]} ]]; then
__sdkman_determine_current_version "${SDKMAN_CANDIDATES[${i}]}"
if [ -n "$CURRENT" ]; then
if [ ${installed_count} -eq 0 ]; then
__sdkman_echo_no_colour 'Using:'
echo ""
fi
__sdkman_echo_no_colour "${SDKMAN_CANDIDATES[${i}]}: ${CURRENT}"
((installed_count += 1))
fi
fi
done
if [ ${installed_count} -eq 0 ]; then
__sdkman_echo_no_colour 'No candidates are in use'
fi
fi
}
function __sdkman_determine_current_version() {
local candidate present
candidate="$1"
present=$(__sdkman_path_contains "${SDKMAN_CANDIDATES_DIR}/${candidate}")
if [[ "$present" == 'true' ]]; then
if [[ $PATH =~ ${SDKMAN_CANDIDATES_DIR}/${candidate}/([^/]+)/bin ]]; then
if [[ "$zsh_shell" == "true" ]]; then
CURRENT=${match[1]}
else
CURRENT=${BASH_REMATCH[1]}
fi
fi
if [[ "$CURRENT" == "current" ]]; then
CURRENT=$(readlink "${SDKMAN_CANDIDATES_DIR}/${candidate}/current" | sed "s!${SDKMAN_CANDIDATES_DIR}/${candidate}/!!g")
fi
else
CURRENT=""
fi
}
================================================
FILE: src/main/bash/sdkman-default.sh
================================================
#!/usr/bin/env bash
#
# Copyright 2021 Marco Vermeulen
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
function __sdk_default() {
__sdkman_deprecation_notice "default"
local candidate version
candidate="$1"
version="$2"
__sdkman_check_candidate_present "$candidate" || return 1
__sdkman_determine_version "$candidate" "$version" || return 1
if [ ! -d "${SDKMAN_CANDIDATES_DIR}/${candidate}/${VERSION}" ]; then
echo ""
__sdkman_echo_red "Stop! Candidate version is not installed."
echo ""
__sdkman_echo_yellow "Tip: Run the following to install this version"
echo ""
__sdkman_echo_yellow "$ sdk install ${candidate} ${VERSION}"
return 1
fi
__sdkman_link_candidate_version "$candidate" "$VERSION"
echo ""
__sdkman_echo_green "Default ${candidate} version set to ${VERSION}"
}
================================================
FILE: src/main/bash/sdkman-env-helpers.sh
================================================
#!/usr/bin/env bash
#
# Copyright 2021 Marco Vermeulen
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
function __sdkman_check_candidate_present() {
local candidate="$1"
if [ -z "$candidate" ]; then
echo ""
__sdkman_echo_red "No candidate provided."
__sdk_help
return 1
fi
}
function __sdkman_check_version_present() {
local version="$1"
if [ -z "$version" ]; then
echo ""
__sdkman_echo_red "No candidate version provided."
__sdk_help
return 1
fi
}
function __sdkman_determine_version() {
local candidate version folder
candidate="$1"
version="$2"
folder="$3"
if [[ "$SDKMAN_AVAILABLE" == "false" && -n "$version" && -d "${SDKMAN_CANDIDATES_DIR}/${candidate}/${version}" ]]; then
VERSION="$version"
elif [[ "$SDKMAN_AVAILABLE" == "false" && -z "$version" && -L "${SDKMAN_CANDIDATES_DIR}/${candidate}/current" ]]; then
VERSION=$(readlink "${SDKMAN_CANDIDATES_DIR}/${candidate}/current" | sed "s!${SDKMAN_CANDIDATES_DIR}/${candidate}/!!g")
elif [[ "$SDKMAN_AVAILABLE" == "false" && -n "$version" ]]; then
__sdkman_echo_red "Stop! ${candidate} ${version} is not available while offline."
return 1
elif [[ "$SDKMAN_AVAILABLE" == "false" && -z "$version" ]]; then
__sdkman_echo_red "This command is not available while offline."
return 1
else
if [[ -z "$version" ]]; then
version=$(__sdkman_secure_curl "${SDKMAN_CANDIDATES_API}/candidates/default/${candidate}")
fi
local validation_url="${SDKMAN_CANDIDATES_API}/candidates/validate/${candidate}/${version}/${SDKMAN_PLATFORM}"
VERSION_VALID=$(__sdkman_secure_curl "$validation_url")
__sdkman_echo_debug "Validate $candidate $version for $SDKMAN_PLATFORM: $VERSION_VALID"
__sdkman_echo_debug "Validation URL: $validation_url"
if [[ "$VERSION_VALID" == 'valid' || "$VERSION_VALID" == 'invalid' && -n "$folder" ]]; then
VERSION="$version"
elif [[ "$VERSION_VALID" == 'invalid' && -L "${SDKMAN_CANDIDATES_DIR}/${candidate}/${version}" ]]; then
VERSION="$version"
elif [[ "$VERSION_VALID" == 'invalid' && -d "${SDKMAN_CANDIDATES_DIR}/${candidate}/${version}" ]]; then
VERSION="$version"
else
if [[ -z "$version" ]]; then
version="\b"
fi
echo ""
__sdkman_echo_red "Stop! $candidate $version is not available. Possible causes:"
__sdkman_echo_red " * $version is an invalid version"
__sdkman_echo_red " * $candidate binaries are incompatible with your platform"
__sdkman_echo_red " * $candidate has not been released yet"
echo ""
__sdkman_echo_yellow "Tip: see all available versions for your platform:"
echo ""
__sdkman_echo_yellow " $ sdk list $candidate"
return 1
fi
fi
}
================================================
FILE: src/main/bash/sdkman-env.sh
================================================
#!/usr/bin/env bash
#
# Copyright 2021 Marco Vermeulen
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
function __sdk_env() {
local -r sdkmanrc=".sdkmanrc"
local -r subcommand="$1"
case $subcommand in
"") __sdkman_load_env "$sdkmanrc" ;;
init) __sdkman_create_env_file "$sdkmanrc";;
install) __sdkman_setup_env "$sdkmanrc";;
clear) __sdkman_clear_env "$sdkmanrc";;
esac
}
function __sdkman_setup_env() {
local sdkmanrc="$1"
if [[ ! -f "$sdkmanrc" ]]; then
__sdkman_echo_red "Could not find $sdkmanrc in the current directory."
echo ""
__sdkman_echo_yellow "Run 'sdk env init' to create it."
return 1
fi
sdkman_auto_answer="true" USE="n" __sdkman_env_each_candidate "$sdkmanrc" "__sdk_install"
__sdkman_load_env "$sdkmanrc"
}
function __sdkman_load_env() {
local sdkmanrc="$1"
if [[ ! -f "$sdkmanrc" ]]; then
__sdkman_echo_red "Could not find $sdkmanrc in the current directory."
echo ""
__sdkman_echo_yellow "Run 'sdk env init' to create it."
return 1
fi
__sdkman_env_each_candidate "$sdkmanrc" "__sdkman_check_and_use" &&
SDKMAN_ENV=$PWD
}
function __sdkman_check_and_use() {
local -r candidate=$1
local -r version=$2
if [[ ! -d "${SDKMAN_CANDIDATES_DIR}/${candidate}/${version}" ]]; then
__sdkman_echo_red "Stop! $candidate $version is not installed."
echo ""
__sdkman_echo_yellow "Run 'sdk env install' to install it."
return 1
fi
__sdk_use "$candidate" "$version"
}
function __sdkman_create_env_file() {
local sdkmanrc="$1"
if [[ -f "$sdkmanrc" ]]; then
__sdkman_echo_red "$sdkmanrc already exists!"
return 1
fi
__sdkman_determine_current_version "java"
local version
[[ -n "$CURRENT" ]] && version="$CURRENT" || version="$(__sdkman_secure_curl "${SDKMAN_CANDIDATES_API}/candidates/default/java")"
cat <<-eof >|"$sdkmanrc"
# Enable auto-env through the sdkman_auto_env config
# Add key=value pairs of SDKs to use below
java=$version
eof
__sdkman_echo_green "$sdkmanrc created."
}
function __sdkman_clear_env() {
local sdkmanrc="$1"
if [[ -z $SDKMAN_ENV ]]; then
__sdkman_echo_red "No environment currently set!"
return 1
fi
if [[ ! -f ${SDKMAN_ENV}/${sdkmanrc} ]]; then
__sdkman_echo_red "Could not find ${SDKMAN_ENV}/${sdkmanrc}."
return 1
fi
__sdkman_env_each_candidate "${SDKMAN_ENV}/${sdkmanrc}" "__sdkman_env_restore_default_version"
unset SDKMAN_ENV
}
function __sdkman_env_restore_default_version() {
local -r candidate="$1"
local candidate_dir default_version
candidate_dir="${SDKMAN_CANDIDATES_DIR}/${candidate}/current"
if __sdkman_is_symlink $candidate_dir; then
default_version=$(basename $(readlink ${candidate_dir}))
__sdk_use "$candidate" "$default_version" >/dev/null &&
__sdkman_echo_yellow "Restored $candidate version to $default_version (default)"
else
__sdkman_echo_yellow "No default version of $candidate was found"
fi
}
function __sdkman_env_each_candidate() {
local -r filepath=$1
local -r func=$2
local normalised_line
while IFS= read -r line || [[ -n "$line" ]]; do
normalised_line="$(__sdkman_normalise "$line")"
__sdkman_is_blank_line "$normalised_line" && continue
if ! __sdkman_matches_candidate_format "$normalised_line"; then
__sdkman_echo_red "Invalid candidate format!"
echo ""
__sdkman_echo_yellow "Expected 'candidate=version' but found '$normalised_line'"
return 1
fi
$func "${normalised_line%=*}" "${normalised_line#*=}" || return
done < "$filepath"
}
function __sdkman_is_symlink() {
[[ -h "$1" ]]
}
function __sdkman_is_blank_line() {
[[ -z "$1" ]]
}
function __sdkman_normalise() {
local -r line_without_comments="${1/\#*/}"
echo "${line_without_comments//[[:space:]]/}"
}
function __sdkman_matches_candidate_format() {
[[ "$1" =~ ^[[:lower:]]+\=.+$ ]]
}
================================================
FILE: src/main/bash/sdkman-flush.sh
================================================
#!/usr/bin/env bash
#
# Copyright 2021 Marco Vermeulen
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
function __sdk_flush() {
local qualifier="$1"
case "$qualifier" in
version)
if [[ -f "${SDKMAN_DIR}/var/version" ]]; then
rm -f "${SDKMAN_DIR}/var/version"
__sdkman_echo_green "Version file has been flushed."
fi
;;
temp)
__sdkman_cleanup_folder "tmp"
;;
tmp)
__sdkman_cleanup_folder "tmp"
;;
metadata)
__sdkman_cleanup_folder "var/metadata"
;;
*)
__sdkman_cleanup_folder "tmp"
__sdkman_cleanup_folder "var/metadata"
;;
esac
}
function __sdkman_cleanup_folder() {
local folder="$1"
local sdkman_cleanup_dir
local sdkman_cleanup_disk_usage
local sdkman_cleanup_count
sdkman_cleanup_dir="${SDKMAN_DIR}/${folder}"
sdkman_cleanup_disk_usage=$(du -sh "$sdkman_cleanup_dir")
sdkman_cleanup_count=$(ls -1 "$sdkman_cleanup_dir" | wc -l)
rm -rf "$sdkman_cleanup_dir"
mkdir "$sdkman_cleanup_dir"
__sdkman_echo_green "${sdkman_cleanup_count} archive(s) flushed, freeing ${sdkman_cleanup_disk_usage}."
}
================================================
FILE: src/main/bash/sdkman-help.sh
================================================
#!/usr/bin/env bash
#
# Copyright 2021 Marco Vermeulen
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
function __sdk_help() {
__sdkman_deprecation_notice "help"
__sdkman_echo_no_colour ""
__sdkman_echo_no_colour "Usage: sdk <command> [candidate] [version]"
__sdkman_echo_no_colour " sdk offline <enable|disable>"
__sdkman_echo_no_colour ""
__sdkman_echo_no_colour " commands:"
__sdkman_echo_no_colour " install or i <candidate> [version] [local-path]"
__sdkman_echo_no_colour " uninstall or rm <candidate> <version>"
__sdkman_echo_no_colour " list or ls [candidate]"
__sdkman_echo_no_colour " use or u <candidate> <version>"
__sdkman_echo_no_colour " config"
__sdkman_echo_no_colour " default or d <candidate> [version]"
__sdkman_echo_no_colour " home or h <candidate> <version>"
__sdkman_echo_no_colour " env or e [init|install|clear]"
__sdkman_echo_no_colour " current or c [candidate]"
__sdkman_echo_no_colour " upgrade or ug [candidate]"
__sdkman_echo_no_colour " version or v"
__sdkman_echo_no_colour " help"
__sdkman_echo_no_colour " offline [enable|disable]"
if [[ "$sdkman_selfupdate_feature" == "true" ]]; then
__sdkman_echo_no_colour " selfupdate [force]"
fi
__sdkman_echo_no_colour " update"
__sdkman_echo_no_colour " flush [tmp|metadata|version]"
__sdkman_echo_no_colour ""
__sdkman_echo_no_colour " candidate : the SDK to install: groovy, scala, grails, gradle, kotlin, etc."
__sdkman_echo_no_colour " use list command for comprehensive list of candidates"
__sdkman_echo_no_colour " eg: \$ sdk list"
__sdkman_echo_no_colour " version : where optional, defaults to latest stable if not provided"
__sdkman_echo_no_colour " eg: \$ sdk install groovy"
__sdkman_echo_no_colour " local-path : optional path to an existing local installation"
__sdkman_echo_no_colour " eg: \$ sdk install groovy 2.4.13-local /opt/groovy-2.4.13"
__sdkman_echo_no_colour ""
}
================================================
FILE: src/main/bash/sdkman-home.sh
================================================
#!/usr/bin/env bash
#
# Copyright 2021 Marco Vermeulen
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
function __sdk_home() {
__sdkman_deprecation_notice "home"
local candidate version
candidate="$1"
version="$2"
__sdkman_check_version_present "$version" || return 1
__sdkman_check_candidate_present "$candidate" || return 1
__sdkman_determine_version "$candidate" "$version" || return 1
if [[ ! -d "${SDKMAN_CANDIDATES_DIR}/${candidate}/${version}" ]]; then
echo ""
__sdkman_echo_red "Stop! Candidate version is not installed."
echo ""
__sdkman_echo_yellow "Tip: Run the following to install this version"
echo ""
__sdkman_echo_yellow "$ sdk install ${candidate} ${version}"
return 1
fi
echo -n "${SDKMAN_CANDIDATES_DIR}/${candidate}/${version}"
}
================================================
FILE: src/main/bash/sdkman-init.sh
================================================
#!/usr/bin/env bash
#
# Copyright 2021 Marco Vermeulen
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# set env vars if not set
if [ -z "$SDKMAN_CANDIDATES_API" ]; then
export SDKMAN_CANDIDATES_API="@SDKMAN_CANDIDATES_API@"
fi
if [ -z "$SDKMAN_BROKER_API" ]; then
export SDKMAN_BROKER_API="@SDKMAN_BROKER_API@"
fi
if [ -z "$SDKMAN_DIR" ]; then
export SDKMAN_DIR="$HOME/.sdkman"
fi
# Load the sdkman config if it exists.
if [ -f "${SDKMAN_DIR}/etc/config" ]; then
source "${SDKMAN_DIR}/etc/config"
fi
# Read the platform file
SDKMAN_PLATFORM="$(cat "${SDKMAN_DIR}/var/platform")"
export SDKMAN_PLATFORM
# OS specific support (must be 'true' or 'false').
cygwin=false
darwin=false
solaris=false
freebsd=false
SDKMAN_KERNEL="$(uname -s)"
case "${SDKMAN_KERNEL}" in
CYGWIN*)
cygwin=true
;;
Darwin*)
darwin=true
;;
SunOS*)
solaris=true
;;
FreeBSD*)
freebsd=true
esac
# Determine shell
zsh_shell=false
bash_shell=false
if [[ -n "$ZSH_VERSION" ]]; then
zsh_shell=true
elif [[ -n "$BASH_VERSION" ]]; then
bash_shell=true
fi
# Source sdkman module scripts and extension files.
#
# Extension files are prefixed with 'sdkman-' and found in the ext/ folder.
# Use this if extensions are written with the functional approach and want
# to use functions in the main sdkman script. For more details, refer to
# <https://github.com/sdkman/sdkman-extensions>.
OLD_IFS="$IFS"
IFS=$'\n'
scripts=($(find "${SDKMAN_DIR}/src" "${SDKMAN_DIR}/ext" -type f -name 'sdkman-*.sh'))
for f in "${scripts[@]}"; do
source "$f"
done
IFS="$OLD_IFS"
unset OLD_IFS scripts f
# Create upgrade delay file if it doesn't exist
if [[ ! -f "${SDKMAN_DIR}/var/delay_upgrade" ]]; then
touch "${SDKMAN_DIR}/var/delay_upgrade"
fi
# set curl connect-timeout and max-time
if [[ -z "$sdkman_curl_connect_timeout" ]]; then sdkman_curl_connect_timeout=7; fi
if [[ -z "$sdkman_curl_max_time" ]]; then sdkman_curl_max_time=10; fi
# set curl retry
if [[ -z "${sdkman_curl_retry}" ]]; then sdkman_curl_retry=0; fi
# set curl retry max time in seconds
if [[ -z "${sdkman_curl_retry_max_time}" ]]; then sdkman_curl_retry_max_time=60; fi
# set curl to continue downloading automatically
if [[ -z "${sdkman_curl_continue}" ]]; then sdkman_curl_continue=true; fi
# read list of candidates and set array
SDKMAN_CANDIDATES_CACHE="${SDKMAN_DIR}/var/candidates"
SDKMAN_CANDIDATES_CSV=$(<"$SDKMAN_CANDIDATES_CACHE")
__sdkman_echo_debug "Setting candidates csv: $SDKMAN_CANDIDATES_CSV"
if [[ "$zsh_shell" == 'true' ]]; then
SDKMAN_CANDIDATES=(${(s:,:)SDKMAN_CANDIDATES_CSV})
else
IFS=',' read -a SDKMAN_CANDIDATES <<< "${SDKMAN_CANDIDATES_CSV}"
fi
export SDKMAN_CANDIDATES_DIR="${SDKMAN_DIR}/candidates"
for candidate_name in "${SDKMAN_CANDIDATES[@]}"; do
candidate_dir="${SDKMAN_CANDIDATES_DIR}/${candidate_name}/current"
if [[ -h "$candidate_dir" || -d "${candidate_dir}" ]]; then
__sdkman_export_candidate_home "$candidate_name" "$candidate_dir"
__sdkman_prepend_candidate_to_path "$candidate_dir"
fi
done
unset candidate_name candidate_dir
export PATH
# source completion scripts
if [[ "$sdkman_auto_complete" == 'true' ]]; then
if [[ "$zsh_shell" == 'true' ]]; then
# initialize zsh completions (if not already done)
if ! (( $+functions[compdef] )) ; then
autoload -Uz compinit
if [[ $ZSH_DISABLE_COMPFIX == 'true' ]]; then
compinit -u -C
else
compinit
fi
fi
autoload -U bashcompinit
bashcompinit
source "${SDKMAN_DIR}/contrib/completion/bash/sdk"
__sdkman_echo_debug "ZSH completion script loaded..."
elif [[ "$bash_shell" == 'true' ]]; then
source "${SDKMAN_DIR}/contrib/completion/bash/sdk"
__sdkman_echo_debug "Bash completion script loaded..."
else
__sdkman_echo_debug "No completion scripts found for $SHELL"
fi
fi
if [[ "$sdkman_auto_env" == "true" ]]; then
if [[ "$zsh_shell" == "true" ]]; then
function sdkman_auto_env() {
if [[ -n $SDKMAN_ENV ]] && [[ ! $PWD =~ ^$SDKMAN_ENV ]]; then
sdk env clear
fi
if [[ -f .sdkmanrc ]]; then
sdk env
fi
}
chpwd_functions+=(sdkman_auto_env)
else
function sdkman_auto_env() {
if [[ -n $SDKMAN_ENV ]] && [[ ! $PWD =~ ^$SDKMAN_ENV ]]; then
sdk env clear
fi
if [[ "$SDKMAN_OLD_PWD" != "$PWD" ]] && [[ -f ".sdkmanrc" ]]; then
sdk env
fi
export SDKMAN_OLD_PWD="$PWD"
}
trimmed_prompt_command="${PROMPT_COMMAND%"${PROMPT_COMMAND##*[![:space:]]}"}"
[[ -z "$trimmed_prompt_command" ]] && PROMPT_COMMAND="sdkman_auto_env" || PROMPT_COMMAND="${trimmed_prompt_command%\;};sdkman_auto_env"
fi
sdkman_auto_env
fi
================================================
FILE: src/main/bash/sdkman-install.sh
================================================
#!/usr/bin/env bash
#
# Copyright 2021 Marco Vermeulen
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
function __sdk_install() {
local candidate version folder
candidate="$1"
version="$2"
folder="$3"
__sdkman_check_candidate_present "$candidate" || return 1
__sdkman_determine_version "$candidate" "$version" "$folder" || return 1
if [[ -d "${SDKMAN_CANDIDATES_DIR}/${candidate}/${VERSION}" || -L "${SDKMAN_CANDIDATES_DIR}/${candidate}/${VERSION}" ]]; then
echo ""
__sdkman_echo_yellow "${candidate} ${VERSION} is already installed."
return 0
fi
if [[ ${VERSION_VALID} == 'valid' ]]; then
__sdkman_determine_current_version "$candidate"
__sdkman_install_candidate_version "$candidate" "$VERSION" || return 1
if [[ "$sdkman_auto_answer" != 'true' && "$auto_answer_upgrade" != 'true' && -n "$CURRENT" ]]; then
__sdkman_echo_confirm "Do you want ${candidate} ${VERSION} to be set as default? (Y/n): "
read USE
fi
if [[ -z "$USE" || "$USE" == "y" || "$USE" == "Y" ]]; then
echo ""
__sdkman_echo_green "Setting ${candidate} ${VERSION} as default."
__sdkman_link_candidate_version "$candidate" "$VERSION"
__sdkman_add_to_path "$candidate"
fi
return 0
elif [[ "$VERSION_VALID" == 'invalid' && -n "$folder" ]]; then
__sdkman_install_local_version "$candidate" "$VERSION" "$folder" || return 1
else
echo ""
__sdkman_echo_red "Stop! $1 is not a valid ${candidate} version."
return 1
fi
}
function __sdkman_install_candidate_version() {
local candidate version
candidate="$1"
version="$2"
__sdkman_download "$candidate" "$version" || return 1
__sdkman_echo_green "Installing: ${candidate} ${version}"
mkdir -p "${SDKMAN_CANDIDATES_DIR}/${candidate}"
rm -rf "${SDKMAN_DIR}/tmp/out"
unzip -oq "${SDKMAN_DIR}/tmp/${candidate}-${version}.zip" -d "${SDKMAN_DIR}/tmp/out"
mv -f "$SDKMAN_DIR"/tmp/out/* "${SDKMAN_CANDIDATES_DIR}/${candidate}/${version}"
__sdkman_echo_green "Done installing!"
echo ""
}
function __sdkman_install_local_version() {
local candidate version folder version_length version_length_max
version_length_max=15
candidate="$1"
version="$2"
folder="$3"
# Validate max length of version
version_length=${#version}
__sdkman_echo_debug "Validating that actual version length ($version_length) does not exceed max ($version_length_max)"
if [[ $version_length -gt $version_length_max ]]; then
__sdkman_echo_red "Invalid version! ${version} with length ${version_length} exceeds max of ${version_length_max}!"
return 1
fi
mkdir -p "${SDKMAN_CANDIDATES_DIR}/${candidate}"
# handle relative paths
if [[ "$folder" != /* ]]; then
folder="$(pwd)/$folder"
fi
if [[ -d "$folder" ]]; then
__sdkman_echo_green "Linking ${candidate} ${version} to ${folder}"
ln -s "$folder" "${SDKMAN_CANDIDATES_DIR}/${candidate}/${version}"
__sdkman_echo_green "Done installing!"
else
__sdkman_echo_red "Invalid path! Refusing to link ${candidate} ${version} to ${folder}."
return 1
fi
echo ""
}
function __sdkman_download() {
local candidate version
candidate="$1"
version="$2"
metadata_folder="${SDKMAN_DIR}/var/metadata"
mkdir -p "${metadata_folder}"
local platform_parameter="$SDKMAN_PLATFORM"
local download_url="${SDKMAN_BROKER_API}/download/${candidate}/${version}/${platform_parameter}"
local base_name="${candidate}-${version}"
local tmp_headers_file="${SDKMAN_DIR}/tmp/${base_name}.headers.tmp"
local headers_file="${metadata_folder}/${base_name}.headers"
export local binary_input="${SDKMAN_DIR}/tmp/${base_name}.bin"
export local zip_output="${SDKMAN_DIR}/tmp/${base_name}.zip"
echo ""
__sdkman_echo_no_colour "Downloading: ${candidate} ${version}"
echo ""
__sdkman_echo_no_colour "In progress..."
echo ""
# download binary
__sdkman_secure_curl_download "${download_url}" --output "${binary_input}" --dump-header "${tmp_headers_file}"
grep '^X-Sdkman' "${tmp_headers_file}" > "${headers_file}"
__sdkman_echo_debug "Downloaded binary to: ${binary_input} (HTTP headers written to: ${headers_file})"
# post-installation hook: implements function __sdkman_post_installation_hook
# responsible for taking `binary_input` and producing `zip_output`
local post_installation_hook="${SDKMAN_DIR}/tmp/hook_post_${candidate}_${version}.sh"
__sdkman_echo_debug "Get post-installation hook: ${SDKMAN_CANDIDATES_API}/hooks/post/${candidate}/${version}/${platform_parameter}"
__sdkman_secure_curl "${SDKMAN_CANDIDATES_API}/hooks/post/${candidate}/${version}/${platform_parameter}" >| "$post_installation_hook"
__sdkman_echo_debug "Copy remote post-installation hook: ${post_installation_hook}"
source "$post_installation_hook"
__sdkman_post_installation_hook || return 1
__sdkman_echo_debug "Processed binary as: $zip_output"
__sdkman_echo_debug "Completed post-installation hook..."
__sdkman_validate_zip "${zip_output}" || return 1
__sdkman_checksum_zip "${zip_output}" "${headers_file}" || return 1
echo ""
}
function __sdkman_validate_zip() {
local zip_archive zip_ok
zip_archive="$1"
zip_ok=$(unzip -t "$zip_archive" | grep 'No errors detected in compressed data')
if [ -z "$zip_ok" ]; then
rm -f "$zip_archive"
echo ""
__sdkman_echo_red "Stop! The archive was corrupt and has been removed! Please try installing again."
return 1
fi
}
function __sdkman_checksum_zip() {
local -r zip_archive="$1"
local -r headers_file="$2"
local algorithm checksum cmd
local shasum_avail=false
local md5sum_avail=false
if [ -z "${headers_file}" ]; then
echo ""
__sdkman_echo_debug "Skipping checksum for cached artifact"
return
elif [ ! -f "${headers_file}" ]; then
echo ""
__sdkman_echo_yellow "Metadata file not found at '${headers_file}', skipping checksum..."
return
fi
if [[ "$sdkman_checksum_enable" != "true" ]]; then
echo ""
__sdkman_echo_yellow "Checksums are disabled, skipping verification..."
return
fi
#Check for the appropriate checksum tools
if command -v shasum > /dev/null 2>&1; then
shasum_avail=true
fi
if command -v md5sum > /dev/null 2>&1; then
md5sum_avail=true
fi
while IFS= read -r line; do
algorithm=$(echo $line | sed -n 's/^X-Sdkman-Checksum-\(.*\):.*$/\1/p' | tr '[:lower:]' '[:upper:]')
checksum=$(echo $line | sed -n 's/^X-Sdkman-Checksum-.*:\(.*\)$/\1/p' | tr -cd '[:alnum:]')
if [[ -n ${algorithm} && -n ${checksum} ]]; then
if [[ "$algorithm" =~ 'SHA' && "$shasum_avail" == 'true' ]]; then
cmd="echo \"${checksum} *${zip_archive}\" | shasum --check --quiet"
elif [[ "$algorithm" =~ 'MD5' && "$md5sum_avail" == 'true' ]]; then
cmd="echo \"${checksum} ${zip_archive}\" | md5sum --check --quiet"
fi
if [[ -n $cmd ]]; then
__sdkman_echo_no_colour "Verifying artifact: ${zip_archive} (${algorithm}:${checksum})"
if ! eval "$cmd"; then
rm -f "$zip_archive"
echo ""
__sdkman_echo_red "Stop! An invalid checksum was detected and the archive removed! Please try re-installing."
return 1
fi
else
__sdkman_echo_no_colour "Not able to perform checksum verification at this time."
fi
fi
done < "${headers_file}"
}
================================================
FILE: src/main/bash/sdkman-list.sh
================================================
#!/usr/bin/env bash
#
# Copyright 2021 Marco Vermeulen
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
function __sdk_list() {
local candidate="$1"
if [[ -z "$candidate" ]]; then
__sdkman_list_candidates
else
__sdkman_list_versions "$candidate"
fi
}
function __sdkman_list_candidates() {
if [[ "$SDKMAN_AVAILABLE" == "false" ]]; then
__sdkman_echo_red "This command is not available while offline."
else
__sdkman_echo_paged "$(__sdkman_secure_curl "${SDKMAN_CANDIDATES_API}/candidates/list")"
fi
}
function __sdkman_list_versions() {
local candidate versions_csv
candidate="$1"
versions_csv="$(__sdkman_build_version_csv "$candidate")"
__sdkman_determine_current_version "$candidate"
if [[ "$SDKMAN_AVAILABLE" == "false" ]]; then
__sdkman_offline_list "$candidate" "$versions_csv"
else
__sdkman_echo_paged "$(__sdkman_secure_curl "${SDKMAN_CANDIDATES_API}/candidates/${candidate}/${SDKMAN_PLATFORM}/versions/list?current=${CURRENT}&installed=${versions_csv}")"
fi
}
function __sdkman_build_version_csv() {
local candidate versions_csv
candidate="$1"
versions_csv=""
if [[ -d "${SDKMAN_CANDIDATES_DIR}/${candidate}" ]]; then
for version in $(find "${SDKMAN_CANDIDATES_DIR}/${candidate}" -maxdepth 1 -mindepth 1 \( -type l -o -type d \) -exec basename '{}' \; | sort -r); do
if [[ "$version" != 'current' ]]; then
versions_csv="${version},${versions_csv}"
fi
done
versions_csv=${versions_csv%?}
fi
echo "$versions_csv"
}
function __sdkman_offline_list() {
local candidate versions_csv
candidate="$1"
versions_csv="$2"
__sdkman_echo_no_colour "--------------------------------------------------------------------------------"
__sdkman_echo_yellow "Offline: only showing installed ${candidate} versions"
__sdkman_echo_no_colour "--------------------------------------------------------------------------------"
local versions=($(echo ${versions_csv//,/ }))
for ((i = ${#versions} - 1; i >= 0; i--)); do
if [[ -n "${versions[${i}]}" ]]; then
if [[ "${versions[${i}]}" == "$CURRENT" ]]; then
__sdkman_echo_no_colour " > ${versions[${i}]}"
else
__sdkman_echo_no_colour " * ${versions[${i}]}"
fi
fi
done
if [[ -z "${versions[@]}" ]]; then
__sdkman_echo_yellow " None installed!"
fi
__sdkman_echo_no_colour "--------------------------------------------------------------------------------"
__sdkman_echo_no_colour "* - installed "
__sdkman_echo_no_colour "> - currently in use "
__sdkman_echo_no_colour "--------------------------------------------------------------------------------"
}
================================================
FILE: src/main/bash/sdkman-main.sh
================================================
#!/usr/bin/env bash
#
# Copyright 2021 Marco Vermeulen
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
function ___sdkman_help() {
if [[ -f "$SDKMAN_DIR/libexec/help" ]]; then
"$SDKMAN_DIR/libexec/help"
else
__sdk_help
fi
}
function sdk() {
COMMAND="$1"
QUALIFIER="$2"
case "$COMMAND" in
l)
COMMAND="list"
;;
ls)
COMMAND="list"
;;
v)
COMMAND="version"
;;
u)
COMMAND="use"
;;
i)
COMMAND="install"
;;
rm)
COMMAND="uninstall"
;;
c)
COMMAND="current"
;;
ug)
COMMAND="upgrade"
;;
d)
COMMAND="default"
;;
h)
COMMAND="home"
;;
e)
COMMAND="env"
;;
esac
#
# Various sanity checks and default settings
#
# Check candidates cache
if [[ "$COMMAND" != "update" ]]; then
___sdkman_check_candidates_cache "$SDKMAN_CANDIDATES_CACHE" || return 1
fi
# Always presume internet availability
SDKMAN_AVAILABLE="true"
if [ -z "$SDKMAN_OFFLINE_MODE" ]; then
SDKMAN_OFFLINE_MODE="false"
fi
# ...unless proven otherwise
__sdkman_update_service_availability
# Load the sdkman config if it exists.
if [ -f "${SDKMAN_DIR}/etc/config" ]; then
source "${SDKMAN_DIR}/etc/config"
fi
# no command provided
if [[ -z "$COMMAND" ]]; then
___sdkman_help
return 1
fi
# Check if it is a valid command
CMD_FOUND=""
if [[ "$COMMAND" != "selfupdate" || "$sdkman_selfupdate_feature" == "true" ]]; then
CMD_TARGET="${SDKMAN_DIR}/src/sdkman-${COMMAND}.sh"
if [[ -f "$CMD_TARGET" ]]; then
CMD_FOUND="$CMD_TARGET"
fi
fi
# Check if it is a sourced function
CMD_TARGET="${SDKMAN_DIR}/ext/sdkman-${COMMAND}.sh"
if [[ -f "$CMD_TARGET" ]]; then
CMD_FOUND="$CMD_TARGET"
fi
# couldn't find the command
if [[ -z "$CMD_FOUND" ]]; then
echo ""
__sdkman_echo_red "Invalid command: $COMMAND"
echo ""
___sdkman_help
fi
# Validate offline qualifier
if [[ "$COMMAND" == "offline" && -n "$QUALIFIER" && -z $(echo "enable disable" | grep -w "$QUALIFIER") ]]; then
echo ""
__sdkman_echo_red "Stop! $QUALIFIER is not a valid offline mode."
fi
# Store the return code of the requested command
local final_rc=0
# Native commands found under libexec
local native_command="${SDKMAN_DIR}/libexec/${COMMAND}"
if [[ "$sdkman_native_enable" == 'true' && -f "$native_command" ]]; then
"$native_command" "${@:2}"
elif [ -n "$CMD_FOUND" ]; then
# Check whether the candidate exists
if [[ -n "$QUALIFIER" && "$COMMAND" != "help" && "$COMMAND" != "offline" && "$COMMAND" != "flush" && "$COMMAND" != "selfupdate" && "$COMMAND" != "env" && "$COMMAND" != "completion" && "$COMMAND" != "edit" && "$COMMAND" != "home" && -z $(echo ${SDKMAN_CANDIDATES[@]} | grep -w "$QUALIFIER") ]]; then
echo ""
__sdkman_echo_red "Stop! $QUALIFIER is not a valid candidate."
return 1
fi
# Internal commands use underscores rather than hyphens
local converted_command_name=$(echo "$COMMAND" | tr '-' '_')
# Available as a shell function
__sdk_"$converted_command_name" "${@:2}"
fi
final_rc=$?
return $final_rc
}
================================================
FILE: src/main/bash/sdkman-offline.sh
================================================
#!/usr/bin/env bash
#
# Copyright 2021 Marco Vermeulen
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
function __sdk_offline() {
local mode="$1"
if [[ -z "$mode" || "$mode" == "enable" ]]; then
SDKMAN_OFFLINE_MODE="true"
__sdkman_echo_green "Offline mode enabled."
fi
if [[ "$mode" == "disable" ]]; then
SDKMAN_OFFLINE_MODE="false"
__sdkman_echo_green "Online mode re-enabled!"
fi
}
================================================
FILE: src/main/bash/sdkman-path-helpers.sh
================================================
#!/usr/bin/env bash
#
# Copyright 2021 Marco Vermeulen
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
function __sdkman_path_contains() {
local candidate exists
candidate="$1"
exists="$(echo "$PATH" | grep "$candidate")"
if [[ -n "$exists" ]]; then
echo 'true'
else
echo 'false'
fi
}
function __sdkman_add_to_path() {
local candidate present
candidate="$1"
present=$(__sdkman_path_contains "$candidate")
if [[ "$present" == 'false' ]]; then
PATH="$SDKMAN_CANDIDATES_DIR/$candidate/current/bin:$PATH"
fi
}
function __sdkman_set_candidate_home() {
local candidate version upper_candidate
candidate="$1"
version="$2"
upper_candidate=$(echo "$candidate" | tr '[:lower:]' '[:upper:]')
export "${upper_candidate}_HOME"="${SDKMAN_CANDIDATES_DIR}/${candidate}/${version}"
}
function __sdkman_export_candidate_home() {
local candidate_name candidate_dir ucase_candidate_name
candidate_name="$1"
candidate_dir="$2"
if [ "$zsh_shell" = true ]; then
ucase_candidate_name="${candidate_name:u}"
elif [ "$bash_shell" = true ]; then
ucase_candidate_name="${candidate_name^^}"
else
ucase_candidate_name="$(printf %s "$candidate_name" | tr '[:lower:]' '[:upper:]')"
fi
export "${ucase_candidate_name}_HOME=$candidate_dir"
}
function __sdkman_prepend_candidate_to_path() {
local candidate_dir
candidate_dir="$1"
if [ -d "${candidate_dir}/bin" ]; then
candidate_dir="${candidate_dir}/bin"
fi
[[ ":$PATH:" == *":$candidate_dir:"* ]] || PATH="${candidate_dir}:${PATH}"
}
function __sdkman_link_candidate_version() {
local candidate version
candidate="$1"
version="$2"
# Change the 'current' symlink for the candidate, hence affecting all shells.
if [[ -L "${SDKMAN_CANDIDATES_DIR}/${candidate}/current" || -d "${SDKMAN_CANDIDATES_DIR}/${candidate}/current" ]]; then
rm -rf "${SDKMAN_CANDIDATES_DIR}/${candidate}/current"
fi
ln -s "${version}" "${SDKMAN_CANDIDATES_DIR}/${candidate}/current"
}
================================================
FILE: src/main/bash/sdkman-selfupdate.sh
================================================
#!/usr/bin/env bash
#
# Copyright 2021 Marco Vermeulen
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
function __sdk_selfupdate() {
local force_selfupdate
local sdkman_script_version_api
local sdkman_native_version_api
if [[ "$SDKMAN_AVAILABLE" == "false" ]]; then
echo "This command is not available while offline."
return 1
fi
if [[ "$sdkman_beta_channel" == "true" ]]; then
sdkman_script_version_api="${SDKMAN_CANDIDATES_API}/broker/version/sdkman/script/beta"
sdkman_native_version_api="${SDKMAN_CANDIDATES_API}/broker/version/sdkman/native/beta"
else
sdkman_script_version_api="${SDKMAN_CANDIDATES_API}/broker/version/sdkman/script/stable"
sdkman_native_version_api="${SDKMAN_CANDIDATES_API}/broker/version/sdkman/native/stable"
fi
sdkman_remote_script_version=$(__sdkman_secure_curl "$sdkman_script_version_api")
sdkman_remote_native_version=$(__sdkman_secure_curl "$sdkman_native_version_api")
sdkman_local_script_version=$(< "$SDKMAN_DIR/var/version")
sdkman_local_native_version=$(< "$SDKMAN_DIR/var/version_native")
__sdkman_echo_debug "Script: local version: $sdkman_local_script_version; remote version: $sdkman_remote_script_version"
__sdkman_echo_debug "Native: local version: $sdkman_local_native_version; remote version: $sdkman_remote_native_version"
force_selfupdate="$1"
export sdkman_debug_mode
if [[ "$sdkman_local_script_version" == "$sdkman_remote_script_version" && "$sdkman_local_native_version" == "$sdkman_remote_native_version" && "$force_selfupdate" != "force" ]]; then
echo "No update available at this time."
elif [[ "$sdkman_beta_channel" == "true" ]]; then
__sdkman_secure_curl "${SDKMAN_CANDIDATES_API}/selfupdate/beta/${SDKMAN_PLATFORM}" | bash
else
__sdkman_secure_curl "${SDKMAN_CANDIDATES_API}/selfupdate/stable/${SDKMAN_PLATFORM}" | bash
fi
}
================================================
FILE: src/main/bash/sdkman-uninstall.sh
================================================
#!/usr/bin/env bash
#
# Copyright 2021 Marco Vermeulen
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
function __sdk_uninstall() {
__sdkman_deprecation_notice "uninstall"
local candidate version current
candidate="$1"
version="$2"
__sdkman_check_candidate_present "$candidate" || return 1
__sdkman_check_version_present "$version" || return 1
current=$(readlink "${SDKMAN_CANDIDATES_DIR}/${candidate}/current" | sed "s!${SDKMAN_CANDIDATES_DIR}/${candidate}/!!g")
if [[ -L "${SDKMAN_CANDIDATES_DIR}/${candidate}/current" && "$version" == "$current" ]]; then
echo ""
__sdkman_echo_green "Deselecting ${candidate} ${version}..."
unlink "${SDKMAN_CANDIDATES_DIR}/${candidate}/current"
fi
echo ""
if [ -d "${SDKMAN_CANDIDATES_DIR}/${candidate}/${version}" ]; then
__sdkman_echo_green "Uninstalling ${candidate} ${version}..."
rm -rf "${SDKMAN_CANDIDATES_DIR}/${candidate}/${version}"
else
__sdkman_echo_red "${candidate} ${version} is not installed."
fi
}
================================================
FILE: src/main/bash/sdkman-update.sh
================================================
#!/usr/bin/env bash
#
# Copyright 2021 Marco Vermeulen
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
function __sdk_update() {
local candidates_uri="${SDKMAN_CANDIDATES_API}/candidates/all"
__sdkman_echo_debug "Using candidates endpoint: $candidates_uri"
local fresh_candidates_csv=$(__sdkman_secure_curl_with_timeouts "$candidates_uri")
__sdkman_echo_debug "Local candidates: $SDKMAN_CANDIDATES_CSV"
__sdkman_echo_debug "Fetched candidates: $fresh_candidates_csv"
if [[ -n "${fresh_candidates_csv}" ]] && ! grep -iq 'html' <<< "${fresh_candidates_csv}"; then
__sdkman_echo_debug "Fresh and cached candidate lengths: ${#fresh_candidates_csv} ${#SDKMAN_CANDIDATES_CSV}"
local fresh_candidates combined_candidates diff_candidates
if [[ "${zsh_shell}" == 'true' ]]; then
fresh_candidates=(${(s:,:)fresh_candidates_csv})
else
IFS=',' read -a fresh_candidates <<< "${fresh_candidates_csv}"
fi
combined_candidates=("${fresh_candidates[@]}" "${SDKMAN_CANDIDATES[@]}")
diff_candidates=($(printf $'%s\n' "${combined_candidates[@]}" | sort | uniq -u))
if ((${#diff_candidates[@]})); then
local delta
delta=("${fresh_candidates[@]}" "${diff_candidates[@]}")
delta=($(printf $'%s\n' "${delta[@]}" | sort | uniq -d))
if ((${#delta[@]})); then
__sdkman_echo_green "\nAdding new candidates(s): ${delta[*]}"
fi
delta=("${SDKMAN_CANDIDATES[@]}" "${diff_candidates[@]}")
delta=($(printf $'%s\n' "${delta[@]}" | sort | uniq -d))
if ((${#delta[@]})); then
__sdkman_echo_green "\nRemoving obsolete candidates(s): ${delta[*]}"
fi
echo "${fresh_candidates_csv}" >| "${SDKMAN_CANDIDATES_CACHE}"
__sdkman_echo_yellow $'\nPlease open a new terminal now...'
else
touch "${SDKMAN_CANDIDATES_CACHE}"
__sdkman_echo_green $'\nNo new candidates found at this time.'
fi
fi
}
================================================
FILE: src/main/bash/sdkman-upgrade.sh
================================================
#!/usr/bin/env bash
#
# Copyright 2021 Marco Vermeulen
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
function __sdk_upgrade() {
local all candidates candidate upgradable installed_count upgradable_count upgradable_candidates
if [ -n "$1" ]; then
all=false
candidates=$1
else
all=true
if [[ "$zsh_shell" == 'true' ]]; then
candidates=(${SDKMAN_CANDIDATES[@]})
else
candidates=${SDKMAN_CANDIDATES[@]}
fi
fi
installed_count=0
upgradable_count=0
echo ""
for candidate in ${candidates}; do
upgradable="$(__sdkman_determine_upgradable_version "$candidate")"
case $? in
1)
$all || __sdkman_echo_red "Not using any version of ${candidate}"
;;
2)
echo ""
__sdkman_echo_red "Stop! Could not get remote version of ${candidate}"
return 1
;;
*)
if [ -n "$upgradable" ]; then
[ ${upgradable_count} -eq 0 ] && __sdkman_echo_no_colour "Available defaults:"
__sdkman_echo_no_colour "$upgradable"
((upgradable_count += 1))
upgradable_candidates=(${upgradable_candidates[@]} $candidate)
fi
((installed_count += 1))
;;
esac
done
if $all; then
if [ ${installed_count} -eq 0 ]; then
__sdkman_echo_no_colour 'No candidates are in use'
elif [ ${upgradable_count} -eq 0 ]; then
__sdkman_echo_no_colour "All candidates are up-to-date"
fi
elif [ ${upgradable_count} -eq 0 ]; then
__sdkman_echo_no_colour "${candidate} is up-to-date"
fi
if [ ${upgradable_count} -gt 0 ]; then
echo ""
if [[ "$sdkman_auto_answer" != 'true' ]]; then
__sdkman_echo_confirm "Use prescribed default version(s)? (Y/n): "
read UPGRADE_ALL
fi
export auto_answer_upgrade='true'
if [[ -z "$UPGRADE_ALL" || "$UPGRADE_ALL" == "y" || "$UPGRADE_ALL" == "Y" ]]; then
# Using array for bash & zsh compatibility
for ((i = 0; i <= ${#upgradable_candidates[*]}; i++)); do
upgradable_candidate="${upgradable_candidates[${i}]}"
# Filter empty elements (in bash arrays are zero index based, in zsh they are 1 based)
if [[ -n "$upgradable_candidate" ]]; then
__sdk_install $upgradable_candidate
fi
done
fi
unset auto_answer_upgrade
fi
}
function __sdkman_determine_upgradable_version() {
local candidate local_versions remote_default_version
candidate="$1"
# Resolve local versions
local_versions="$(echo $(find "${SDKMAN_CANDIDATES_DIR}/${candidate}" -maxdepth 1 -mindepth 1 -type d -exec basename '{}' \; 2> /dev/null) | sed -e "s/ /, /g")"
if [ ${#local_versions} -eq 0 ]; then
return 1
fi
# Resolve remote default version
remote_default_version="$(__sdkman_secure_curl "${SDKMAN_CANDIDATES_API}/candidates/default/${candidate}")"
if [ -z "$remote_default_version" ]; then
return 2
fi
# Check upgradable or not
if [ ! -d "${SDKMAN_CANDIDATES_DIR}/${candidate}/${remote_default_version}" ]; then
__sdkman_echo_yellow "${candidate} (local: ${local_versions}; default: ${remote_default_version})"
fi
}
================================================
FILE: src/main/bash/sdkman-use.sh
================================================
#!/usr/bin/env bash
#
# Copyright 2021 Marco Vermeulen
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
function __sdk_use() {
local candidate version install
candidate="$1"
version="$2"
__sdkman_check_version_present "$version" || return 1
__sdkman_check_candidate_present "$candidate" || return 1
if [[ ! -d "${SDKMAN_CANDIDATES_DIR}/${candidate}/${version}" ]]; then
echo ""
__sdkman_echo_red "Stop! Candidate version is not installed."
echo ""
__sdkman_echo_yellow "Tip: Run the following to install this version"
echo ""
__sdkman_echo_yellow "$ sdk install ${candidate} ${version}"
return 1
fi
# Just update the *_HOME and PATH for this shell.
__sdkman_set_candidate_home "$candidate" "$version"
if [[ $PATH =~ ${SDKMAN_CANDIDATES_DIR}/${candidate}/([^/]+) ]]; then
local matched_version
if [[ "$zsh_shell" == "true" ]]; then
matched_version=${match[1]}
else
matched_version=${BASH_REMATCH[1]}
fi
export PATH=${PATH//${SDKMAN_CANDIDATES_DIR}\/${candidate}\/${matched_version}/${SDKMAN_CANDIDATES_DIR}\/${candidate}\/${version}}
fi
if [[ ! (-L "${SDKMAN_CANDIDATES_DIR}/${candidate}/current" || -d "${SDKMAN_CANDIDATES_DIR}/${candidate}/current") ]]; then
__sdkman_echo_green "Setting ${candidate} version ${version} as default."
__sdkman_link_candidate_version "$candidate" "$version"
fi
echo ""
__sdkman_echo_green "Using ${candidate} version ${version} in this shell."
}
================================================
FILE: src/main/bash/sdkman-utils.sh
================================================
#!/usr/bin/env bash
#
# Copyright 2021 Marco Vermeulen
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
function __sdkman_echo_debug() {
if [[ "$sdkman_debug_mode" == 'true' ]]; then
echo "$1"
fi
}
function __sdkman_secure_curl() {
if [[ "${sdkman_insecure_ssl}" == 'true' ]]; then
curl --insecure --silent --location "$1"
else
curl --silent --location "$1"
fi
}
function __sdkman_secure_curl_download() {
local curl_params
curl_params=('--progress-bar' '--location')
if [[ "${sdkman_debug_mode}" == 'true' ]]; then
curl_params+=('--verbose')
fi
if [[ "${sdkman_curl_continue}" == 'true' ]]; then
curl_params+=('-C' '-')
fi
if [[ -n "${sdkman_curl_retry_max_time}" ]]; then
curl_params+=('--retry-max-time' "${sdkman_curl_retry_max_time}")
fi
if [[ -n "${sdkman_curl_retry}" ]]; then
curl_params+=('--retry' "${sdkman_curl_retry}")
fi
if [[ "${sdkman_insecure_ssl}" == 'true' ]]; then
curl_params+=('--insecure')
fi
curl "${curl_params[@]}" "${@}"
}
function __sdkman_secure_curl_with_timeouts() {
if [[ "${sdkman_insecure_ssl}" == 'true' ]]; then
curl --insecure --silent --location --connect-timeout ${sdkman_curl_connect_timeout} --max-time ${sdkman_curl_max_time} "$1"
else
curl --silent --location --connect-timeout ${sdkman_curl_connect_timeout} --max-time ${sdkman_curl_max_time} "$1"
fi
}
function __sdkman_echo_paged() {
if [[ -n "$PAGER" ]]; then
echo "$@" | eval "$PAGER"
elif command -v less >& /dev/null; then
echo "$@" | less
else
echo "$@"
fi
}
function __sdkman_echo() {
if [[ "$sdkman_colour_enable" == 'false' ]]; then
echo -e "$2"
else
echo -e "\033[1;$1$2\033[0m"
fi
}
function __sdkman_echo_red() {
__sdkman_echo "31m" "$1"
}
function __sdkman_echo_no_colour() {
echo "$1"
}
function __sdkman_echo_yellow() {
__sdkman_echo "33m" "$1"
}
function __sdkman_echo_green() {
__sdkman_echo "32m" "$1"
}
function __sdkman_echo_cyan() {
__sdkman_echo "36m" "$1"
}
function __sdkman_echo_confirm() {
if [[ "$sdkman_colour_enable" == 'false' ]]; then
echo -n "$1"
else
echo -e -n "\033[1;33m$1\033[0m"
fi
}
function __sdkman_deprecation_notice() {
local message="
[Deprecation Notice]:
This legacy '$1' command is replaced by a native implementation
and it will be removed in a future release.
Please follow the discussion here:
https://github.com/sdkman/sdkman-cli/discussions/1332"
if [[ "$sdkman_colour_enable" == 'false' ]]; then
__sdkman_echo_no_colour "$message"
else
__sdkman_echo_yellow "$message"
fi
}
================================================
FILE: src/main/bash/sdkman-version.sh
================================================
#!/usr/bin/env bash
#
# Copyright 2021 Marco Vermeulen
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
function __sdk_version() {
__sdkman_deprecation_notice "version"
local version=$(cat $SDKMAN_DIR/var/version)
echo ""
__sdkman_echo_yellow "SDKMAN $version"
}
================================================
FILE: src/test/groovy/sdkman/cucumber/RunCukeTests.groovy
================================================
package sdkman.cucumber
import io.cucumber.junit.Cucumber
import io.cucumber.junit.CucumberOptions
import org.junit.runner.RunWith
@RunWith(Cucumber)
@CucumberOptions(
strict = true,
features = ["src/test/resources/features"],
glue = ["sdkman.steps"],
tags = ["not @manual", "not @review"]
)
class RunCukeTests {}
================================================
FILE: src/test/groovy/sdkman/env/BashEnv.groovy
================================================
package sdkman.env
import groovy.transform.ToString
/**
* <p>As part of the sdkman test suite we need to launch a bash shell and execute
* multiple commands in it. This is tricky to do using Java's support for
* working with external processes as the API can't tell you when a command
* has finished executing.</p>
* <p>This class provides some hacks that allow you to serially execute commands
* in an external bash process in a fairly reliable manner and to retrieve the
* output of those commands.</p>
*/
@ToString(includeNames = true)
class BashEnv {
static final PROMPT = ""
static final EXIT_CODE_CMD = 'echo "Exit code is: $?"'
static final EXIT_CODE_PATTERN = ~/Exit code is: (\d+)\s*${PROMPT}?$/
private final Object outputLock = new Object()
def exitCode
def process
def processOutput = new StringBuilder()
def commandOutput
// Command timeout in milliseconds
def timeout = 5000
def workDir
def env
BashEnv(workDir, Map env) {
this.workDir = workDir as File
def basicPath = "/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/run/current-system/sw/bin"
def localBinDir = "${workDir}/bin"
def modifiedPath = "$localBinDir:$basicPath"
env = env + [PS1: PROMPT, PATH: modifiedPath]
this.env = env.collect { k, v -> k + '=' + v }
}
/**
* Starts the external bash process.
*/
void start() {
process = ["bash", "--noprofile", "--norc", "--posix", "-i", "-o", "noclobber"].execute(env, workDir)
consumeProcessStream(process.inputStream)
consumeProcessStream(process.errorStream)
}
/**
* Stops the external bash process and waits for it to finish.
*/
void stop() {
execute("exit")
process.waitFor()
}
/**
* Sends a command line to the external bash process and returns once the
* command has finished executing. If the command is interactive and requires
* input during it's execution (for example a y/n answer to a question) you
* can provide that input as a list of strings.
*/
void execute(String cmdline, List inputs = []) {
resetOutput()
if (cmdline != "exit") {
exitCode = null
}
process.outputStream << cmdline << "\n"
process.outputStream.flush()
if (cmdline != "exit") {
for (input in inputs) {
process.outputStream << input << "\n"
}
process.outputStream << EXIT_CODE_CMD << "\n"
process.outputStream.flush()
}
def start = System.currentTimeMillis()
while (cmdline != "exit") {
Thread.sleep 100
synchronized (outputLock) {
// Remove all the extraneous text that's not related to the
// command's output. This includes the command string itself,
// the 'echo' command to display the command's exit code, and
// the exit code line.
removeFromOutput(cmdline + "\n")
removeFromOutput(PROMPT + EXIT_CODE_CMD + "\n")
def str = processOutput.toString()
def m = EXIT_CODE_PATTERN.matcher(str)
if (m) {
exitCode = m[0][1]
// Remove this exit code line from the output.
commandOutput = m.replaceAll('')
break
}
// If the command times out, we should break out of the loop and
// display whatever output has already been produced.
if (System.currentTimeMillis() - start > timeout) {
commandOutput = "ALERT! Command timed out. Last output was:\n\n${processOutput}"
break
}
}
}
}
/**
* Returns the exit code of the last command that was executed.
*/
int getStatus() {
if (!exitCode) throw new IllegalStateException("Did you run execute() before getting the status?")
return exitCode.toInteger()
}
/**
* Returns the text output (both stdout and stderr) of the last command
* that was executed.
*/
String getOutput() {
return commandOutput
}
/**
* Clears the saved command output.
*/
void resetOutput() {
synchronized (outputLock) {
processOutput = new StringBuilder()
}
}
private void consumeProcessStream(final InputStream stream) {
char[] buffer = new char[256]
Thread.start {
def reader = new InputStreamReader(stream)
def charsRead = 0
while (charsRead != -1) {
charsRead = reader.read(buffer, 0, 256)
if (charsRead > 0) {
synchronized (outputLock) {
processOutput.append(buffer, 0, charsRead)
}
}
}
}
}
private void removeFromOutput(String line) {
synchronized (outputLock) {
def pos = processOutput.indexOf(line)
if (pos != -1) {
processOutput.delete(pos, pos + line.size() - 1)
}
}
}
}
================================================
FILE: src/test/groovy/sdkman/env/CleanBashEnvBuilder.groovy
================================================
package sdkman.env
class CleanBashEnvBuilder {
private final File baseFolder
String httpProxy
static CleanBashEnvBuilder create(File baseFolder) {
new CleanBashEnvBuilder(baseFolder)
}
private CleanBashEnvBuilder(File baseFolder) {
this.baseFolder = baseFolder
}
CleanBashEnvBuilder withHttpProxy(String httpProxy) {
this.httpProxy = httpProxy
this
}
BashEnv build() {
def env = [HOME: baseFolder.absolutePath]
if (httpProxy) {
env.put("http_proxy", httpProxy)
}
new BashEnv(baseFolder.absolutePath, env)
}
}
================================================
FILE: src/test/groovy/sdkman/env/SdkmanBashEnvBuilder.groovy
================================================
package sdkman.env
import groovy.transform.ToString
import sdkman.stubs.CurlStub
import sdkman.stubs.UnameStub
import sdkman.support.UnixUtils
@ToString(includeNames = true)
class SdkmanBashEnvBuilder {
final BUILD_STAGE_DIR = "build/stage/sdkman-latest+hashme"
final BUILD_BIN_DIR = "$BUILD_STAGE_DIR/bin"
final BUILD_SRC_DIR = "$BUILD_STAGE_DIR/src"
final BUILD_COMPLETION_DIR = "$BUILD_STAGE_DIR/contrib/completion/bash"
//mandatory fields
private final File baseFolder
//optional fields with sensible defaults
private Optional<CurlStub> curlStub = Optional.empty()
private Optional<UnameStub> unameStub = Optional.empty()
private List candidates = ['groovy', 'grails', 'java']
private String platform = UnixUtils.inferPlatform()
private boolean offlineMode = false
private String candidatesApi = "http://localhost:8080/2"
private String brokerApi = "http://localhost:8080/2"
private String jdkHome = "/path/to/my/jdk"
private String httpProxy
private String scriptVersion
private String nativeVersion
private boolean debugMode = true
Map config = [
sdkman_auto_answer : 'false',
sdkman_beta_channel: 'false',
sdkman_native_enable: 'false',
sdkman_selfupdate_feature: 'true'
]
File sdkmanDir, sdkmanBinDir, sdkmanVarDir, sdkmanSrcDir, sdkmanEtcDir, sdkmanExtDir,
sdkmanTmpDir, sdkmanCandidatesDir, sdkmanMetadataDir, sdkmanContribDir
static SdkmanBashEnvBuilder create(File baseFolder) {
new SdkmanBashEnvBuilder(baseFolder)
}
private SdkmanBashEnvBuilder(File baseFolder) {
this.baseFolder = baseFolder
}
SdkmanBashEnvBuilder withCurlStub(CurlStub curlStub) {
this.curlStub = Optional.of(curlStub)
this
}
SdkmanBashEnvBuilder withUnameStub(UnameStub unameStub) {
this.unameStub = Optional.of(unameStub)
this
}
SdkmanBashEnvBuilder withPlatform(String platform) {
this.platform = platform
this
}
SdkmanBashEnvBuilder withCandidates(List candidates) {
this.candidates = candidates
this
}
SdkmanBashEnvBuilder withConfiguration(String key, String value) {
config.put key, value
this
}
SdkmanBashEnvBuilder withOfflineMode(boolean offlineMode) {
this.offlineMode = offlineMode
this
}
SdkmanBashEnvBuilder withCandidatesApi(String service) {
this.candidatesApi = service
this
}
SdkmanBashEnvBuilder withBrokerApi(String service) {
this.brokerApi = service
this
}
SdkmanBashEnvBuilder withJdkHome(String jdkHome) {
this.jdkHome = jdkHome
this
}
SdkmanBashEnvBuilder withHttpProxy(String httpProxy) {
this.httpProxy = httpProxy
this
}
SdkmanBashEnvBuilder withScriptVersion(String version) {
this.scriptVersion = version
this
}
SdkmanBashEnvBuilder withNativeVersion(String version) {
this.nativeVersion = version
this
}
SdkmanBashEnvBuilder withDebugMode(boolean debugMode) {
this.debugMode = debugMode
this
}
BashEnv build() {
sdkmanDir = prepareDirectory(baseFolder, ".sdkman")
sdkmanBinDir = prepareDirectory(sdkmanDir, "bin")
sdkmanVarDir = prepareDirectory(sdkmanDir, "var")
sdkmanSrcDir = prepareDirectory(sdkmanDir, "src")
sdkmanEtcDir = prepareDirectory(sdkmanDir, "etc")
sdkmanExtDir = prepareDirectory(sdkmanDir, "ext")
sdkmanTmpDir = prepareDirectory(sdkmanDir, "tmp")
sdkmanCandidatesDir = prepareDirectory(sdkmanDir, "candidates")
sdkmanMetadataDir = prepareDirectory(sdkmanVarDir, "metadata")
sdkmanContribDir = prepareDirectory(sdkmanDir, "contrib")
curlStub.map { it.build() }
unameStub.map { it.build() }
initializeConfiguration(sdkmanEtcDir, config)
initializeCandidates(sdkmanCandidatesDir, candidates)
initializeCandidatesCache(sdkmanVarDir, candidates)
initializePlatformDescriptor(sdkmanVarDir, platform)
initializeScriptVersionFile(sdkmanVarDir, scriptVersion)
initializeNativeVersionFile(sdkmanVarDir, nativeVersion)
primeInitScript(sdkmanBinDir)
primeModuleScripts(sdkmanSrcDir)
primeBashCompletionScript(sdkmanContribDir)
def env = [
SDKMAN_DIR : sdkmanDir.absolutePath,
SDKMAN_CANDIDATES_DIR: sdkmanCandidatesDir.absolutePath,
SDKMAN_OFFLINE_MODE : "$offlineMode",
SDKMAN_CANDIDATES_API: candidatesApi,
SDKMAN_BROKER_API : brokerApi,
sdkman_debug_mode : Boolean.toString(debugMode),
JAVA_HOME : jdkHome
]
if (httpProxy) {
env.put("http_proxy", httpProxy)
}
new BashEnv(baseFolder.absolutePath, env)
}
private prepareDirectory(File target, String directoryName) {
def directory = new File(target, directoryName)
directory.mkdirs()
directory
}
private initializeScriptVersionFile(File folder, String version) {
if (version) {
new File(folder, "version") << version
}
}
private initializeNativeVersionFile(File folder, String version) {
if (version) {
new File(folder, "version_native") << version
}
}
private initializeCandidates(File folder, List candidates) {
candidates.each { candidate ->
new File(folder, candidate).mkdirs()
}
}
private initializeCandidatesCache(File folder, List candidates) {
def candidatesCache = new File(folder, "candidates")
if (candidates) {
candidatesCache << candidates.join(",")
} else {
candidatesCache << ""
}
}
private initializePlatformDescriptor(File folder, String platform) {
def platformDescriptor = new File(folder, "platform")
platformDescriptor << platform
}
private initializeConfiguration(File targetFolder, Map config) {
def configFile = new File(targetFolder, "config")
config.each { key, value ->
configFile << "$key=$value\n"
}
}
private primeInitScript(File targetFolder) {
def sourceInitScript = new File(BUILD_BIN_DIR, 'sdkman-init.sh')
if (!sourceInitScript.exists())
throw new IllegalStateException("sdkman-init.sh has not been prepared for consumption.")
def destInitScript = new File(targetFolder, "sdkman-init.sh")
destInitScript << sourceInitScript.text
destInitScript
}
private primeBashCompletionScript(File targetFolder) {
def sourceCompletionScript = new File(BUILD_COMPLETION_DIR, 'sdk')
if (!sourceCompletionScript.exists())
throw new IllegalStateException("sdk has not been prepared for consumption.")
new FileTreeBuilder(targetFolder).with {
completion {
bash {
sdk(sourceCompletionScript.text)
}
}
}
}
private primeModuleScripts(File targetFolder) {
for (f in new File(BUILD_SRC_DIR).listFiles()) {
if (!(f.name in ['selfupdate.sh', 'install.sh', 'sdkman-init.sh'])) {
new File(targetFolder, f.name) << f.text
}
}
}
}
================================================
FILE: src/test/groovy/sdkman/specs/CandidatesCacheUpdateFailureSpec.groovy
================================================
package sdkman.specs
import sdkman.support.SdkmanEnvSpecification
class CandidatesCacheUpdateFailureSpec extends SdkmanEnvSpecification {
static final String CANDIDATES_API = "http://localhost:8080/2"
static final String HEALTHCHECK_ENDPOINT = "$CANDIDATES_API/healthcheck"
static final String CANDIDATES_ALL_ENDPOINT = "$CANDIDATES_API/candidates/all"
File candidatesCache
def setup() {
candidatesCache = new File("${sdkmanDotDirectory}/var", "candidates")
curlStub.primeWith(HEALTHCHECK_ENDPOINT, "echo dbfb025be9f97fda2052b5febcca0155")
.primeWith(CANDIDATES_ALL_ENDPOINT, "echo html")
sdkmanBashEnvBuilder.withConfiguration("sdkman_debug_mode", "true")
}
void "should not update candidates if error downloading candidate list"() {
given:
bash = sdkmanBashEnvBuilder
.withCandidates([])
.build()
and:
bash.start()
when:
bash.execute("source $bootstrapScript")
bash.execute("sdk update")
then:
!bash.output.contains('Fresh and cached candidate lengths')
}
}
================================================
FILE: src/test/groovy/sdkman/specs/CandidatesCacheUpdateSpec.groovy
================================================
package sdkman.specs
import sdkman.support.SdkmanEnvSpecification
class CandidatesCacheUpdateSpec extends SdkmanEnvSpecification {
static final String CANDIDATES_API = "http://localhost:8080/2"
static final String HEALTHCHECK_ENDPOINT = "$CANDIDATES_API/healthcheck"
static final String CANDIDATES_ALL_ENDPOINT = "$CANDIDATES_API/candidates/all"
File candidatesCache
def setup() {
candidatesCache = new File("${sdkmanDotDirectory}/var", "candidates")
curlStub.primeWith(HEALTHCHECK_ENDPOINT, "echo dbfb025be9f97fda2052b5febcca0155")
.primeWith(CANDIDATES_ALL_ENDPOINT, "echo groovy,scala")
sdkmanBashEnvBuilder.withConfiguration("sdkman_debug_mode", "true")
}
void "should issue a warning and escape if cache is empty"() {
given:
bash = sdkmanBashEnvBuilder
.withCandidates([])
.build()
and:
bash.start()
when:
bash.execute("source $bootstrapScript")
bash.execute("sdk version")
then:
bash.output.contains('WARNING: Cache is corrupt. SDKMAN cannot be used until updated.')
bash.output.contains('$ sdk update')
and:
!bash.output.contains("SDKMAN 5.0.0")
}
void "should log a success message if cache exists"() {
given:
bash = sdkmanBashEnvBuilder
.withCandidates(['groovy'])
.build()
and:
bash.start()
when:
bash.execute("source $bootstrapScript")
bash.execute("sdk help")
then:
bash.output.contains('Using existing cache')
}
void "should bypass cache check if update command issued"() {
given:
bash = sdkmanBashEnvBuilder
.withCandidates([])
.build()
and:
bash.start()
when:
bash.execute("source $bootstrapScript")
bash.execute("sdk update")
then:
bash.output.contains('Adding new candidates(s): groovy scala')
and:
candidatesCache.text.trim() == "groovy,scala"
}
}
================================================
FILE: src/test/groovy/sdkman/specs/CompletionSpec.groovy
================================================
package sdkman.specs
import sdkman.support.SdkmanEnvSpecification
class CompletionSpec extends SdkmanEnvSpecification {
static final String CANDIDATES_API = "http://localhost:8080/2"
def "should complete the list of commands"() {
given:
bash = sdkmanBashEnvBuilder
.withConfiguration("sdkman_auto_complete", "true")
.build()
bash.start()
bash.execute("source $bootstrapScript")
when:
bash.execute("COMP_CWORD=1; COMP_WORDS=(sdk); _sdk")
bash.execute('echo "\${COMPREPLY[@]}"')
then:
bash.output.contains("install uninstall list use config default home env current upgrade version help offline selfupdate update flush")
}
def "should complete the list of candidates"() {
given:
bash = sdkmanBashEnvBuilder
.withCandidates(["java", "groovy"])
.withConfiguration("sdkman_auto_complete", "true")
.build()
bash.start()
bash.execute("source $bootstrapScript")
when:
bash.execute("COMP_CWORD=2; COMP_WORDS=(sdk install); _sdk")
bash.execute('echo "\${COMPREPLY[@]}"')
then:
bash.output.contains("java groovy")
}
def "should complete the list of Java versions"() {
given:
curlStub.primeWith("$CANDIDATES_API/candidates/java/darwinx64/versions/all", "echo 16.0.1.hs-adpt,17.0.0-tem")
unameStub.forKernel("Darwin").forMachine("x86_64")
bash = sdkmanBashEnvBuilder
.withConfiguration("sdkman_auto_complete", "true")
.withUnameStub(unameStub)
.withPlatform("darwinx64")
.build()
bash.start()
bash.execute("source $bootstrapScript")
when:
bash.execute("COMP_CWORD=3; COMP_WORDS=(sdk install java); _sdk")
bash.execute('echo "\${COMPREPLY[@]}"')
then:
bash.output.contains("16.0.1.hs-adpt 17.0.0-tem")
}
}
================================================
FILE: src/test/groovy/sdkman/specs/ConfigCommandSpec.groovy
================================================
package sdkman.specs
import sdkman.support.SdkmanEnvSpecification
class ConfigCommandSpec extends SdkmanEnvSpecification {
def "it should open the config in the system's default editor"() {
given:
bash = sdkmanBashEnvBuilder
.withOfflineMode(true)
.build()
bash.start()
bash.execute("source $bootstrapScript")
when:
setupEnv(bash)
bash.execute("sdk config")
then:
verifyOutput(bash.output, sdkmanBaseDirectory)
where:
setupEnv << [
{
it.execute('nano() { echo "nano was called with $*"; }')
it.execute("EDITOR=nano")
},
{
it.execute('code() { echo "code was called with $*"; }')
it.execute("EDITOR='code --wait'")
},
{
it.execute('vi() { echo "vi was called with $*"; }')
it.execute("unset EDITOR")
},
{
it.execute("EDITOR=/does/not/exist")
}
]
verifyOutput << [
{ output, baseDirectory -> output.contains("nano was called with ${baseDirectory}/.sdkman/etc/config") },
{ output, baseDirectory -> output.contains("code was called with --wait ${baseDirectory}/.sdkman/etc/config") },
{ output, baseDirectory -> output.contains("vi was called with ${baseDirectory}/.sdkman/etc/config") },
{ output, _ -> output.contains("No default editor configured.") }
]
}
}
================================================
FILE: src/test/groovy/sdkman/specs/CurrentCommandSpec.groovy
================================================
package sdkman.specs
import sdkman.support.SdkmanEnvSpecification
import java.nio.file.Paths
import static java.nio.file.Files.createSymbolicLink
class CurrentCommandSpec extends SdkmanEnvSpecification {
static final CANDIDATES_API = "http://localhost:8080/2"
static final HEALTHCHECK_ENDPOINT = "$CANDIDATES_API/healthcheck"
def setup() {
curlStub.primeWith(HEALTHCHECK_ENDPOINT, "echo dbfb025be9f97fda2052b5febcca0155")
}
void "should display current version of all candidates installed"() {
given:
def installedCandidates = [
"gradle": "2.7",
"groovy": "2.4.4",
"vertx" : "3.0.0"
]
def allCandidates = [
"asciidoctorj",
"crash",
"gaiden",
"glide",
"gradle",
"grails",
"griffon",
"groovy",
"groovyserv",
"jbake",
"jbossforge",
"lazybones",
"springboot",
"vertx"
]
bash = sdkmanBashEnvBuilder
.withOfflineMode(false)
.withCandidates(installedCandidates.keySet().toList())
.build()
prepareFoldersFor(installedCandidates)
bash.start()
bash.execute("source $bootstrapScript")
when:
bash.execute('sdk current')
then:
bash.output.contains("Using:")
bash.output.contains("groovy: 2.4.4")
bash.output.contains("gradle: 2.7")
bash.output.contains("vertx: 3.0.0")
}
private prepareFoldersFor(Map installedCandidates) {
installedCandidates.forEach { candidate, version ->
def candidateVersionDirectory = "$candidatesDirectory/$candidate/$version"
def candidateVersionBinDirectory = "$candidateVersionDirectory/bin"
new File(candidateVersionBinDirectory).mkdirs()
def candidateVersionPath = Paths.get(candidateVersionDirectory)
def symlink = Paths.get("$candidatesDirectory/$candidate/current")
createSymbolicLink(symlink, candidateVersionPath)
}
}
}
================================================
FILE: src/test/groovy/sdkman/specs/EnvCommandSpec.groovy
================================================
package sdkman.specs
import sdkman.support.SdkmanEnvSpecification
import java.nio.file.Paths
import static java.nio.file.Files.createSymbolicLink
class EnvCommandSpec extends SdkmanEnvSpecification {
static final String CANDIDATES_API = "http://localhost:8080/2"
static final String CANDIDATES_DEFAULT_JAVA = "$CANDIDATES_API/candidates/default/java"
def "should generate .sdkmanrc when called with 'init'"() {
given:
curlStub.primeWith(CANDIDATES_DEFAULT_JAVA, "echo 11.0.6.hs-adpt")
setupCandidates(candidatesDirectory)
bash = sdkmanBashEnvBuilder
.withOfflineMode(true)
.build()
bash.start()
bash.execute("source $bootstrapScript")
when:
bash.execute("sdk env init")
then:
new File(bash.workDir, '.sdkmanrc').text.contains(expected)
where:
setupCandidates << [
{ directory ->
new FileTreeBuilder(directory).with {
"java" {
"8.0.252.hs" {
"bin" {}
}
}
}
createSymbolicLink(Paths.get("$directory/java/current"), Paths.get("$directory/java/8.0.252.hs"))
},
{} // NOOP
]
expected << ["java=8.0.252.hs\n", "java=11.0.6.hs-adpt\n"]
}
def "should use the candidates contained in .sdkmanrc"() {
given:
new FileTreeBuilder(candidatesDirectory).with {
"grails" {
"2.1.0" {}
}
"groovy" {
"2.4.1" {}
}
}
bash = sdkmanBashEnvBuilder
.withOfflineMode(true)
.build()
new File(bash.workDir, '.sdkmanrc').text = sdkmanrc
bash.start()
bash.execute("source $bootstrapScript")
when:
bash.execute("sdk env")
then:
verifyAll(bash.output) {
contains("Using groovy version 2.4.1 in this shell.")
contains("Using grails version 2.1.0 in this shell.")
}
where:
sdkmanrc << [
"grails=2.1.0\ngroovy=2.4.1",
"grails=2.1.0\ngroovy=2.4.1\n",
" grails=2.1.0\ngroovy=2.4.1\n",
"grails=2.1.0 \ngroovy=2.4.1\n",
"grails=2.1.0\ngroovy = 2.4.1\n",
]
}
def "should execute 'sdk env' when entering a directory with an .sdkmanrc"() {
given:
new FileTreeBuilder(candidatesDirectory).with {
"groovy" {
"2.4.1" {}
}
}
bash = sdkmanBashEnvBuilder
.withOfflineMode(true)
.withConfiguration("sdkman_auto_env", sdkmanAutoEnv)
.build()
new FileTreeBuilder(bash.workDir).with {
"project" {
".sdkmanrc"("groovy=2.4.1\n")
}
}
bash.start()
bash.execute("source $bootstrapScript")
when:
bash.execute("cd project")
then:
verifyOutput(bash.output)
where:
sdkmanAutoEnv | verifyOutput
'true' | { it.contains("Using groovy version 2.4.1 in this shell") }
'false' | { !it.contains("Using groovy version 2.4.1 in this shell") }
}
def "should not execute 'sdk env' when already being in a directory with an .sdkmanrc"() {
given:
new FileTreeBuilder(candidatesDirectory).with {
"groovy" {
"2.4.1" {}
}
}
bash = sdkmanBashEnvBuilder
.withOfflineMode(true)
.withConfiguration("sdkman_auto_env", "true")
.build()
new FileTreeBuilder(bash.workDir).with {
"project" {
".sdkmanrc"("groovy=2.4.1\n")
}
}
bash.start()
bash.execute("source $bootstrapScript")
when:
bash.execute("cd project")
bash.execute("ls")
then:
!bash.output.contains("Using groovy version 2.4.1 in this shell")
}
def "should execute 'sdk env' when opening a new terminal in a directory with an .sdkmanrc"() {
given:
new FileTreeBuilder(candidatesDirectory).with {
"groovy" {
"2.4.1" {}
}
}
bash = sdkmanBashEnvBuilder
.withOfflineMode(true)
.withConfiguration("sdkman_auto_env", "true")
.build()
new FileTreeBuilder(bash.workDir).with {
".sdkmanrc"("groovy=2.4.1\n")
}
bash.start()
when:
bash.execute("source $bootstrapScript")
then:
bash.output.contains("Using groovy version 2.4.1 in this shell")
}
def "should execute 'sdk env' after executing 'sdk env clear'"() {
given:
new FileTreeBuilder(candidatesDirectory).with {
"groovy" {
"2.4.1" {}
}
}
bash = sdkmanBashEnvBuilder
.withOfflineMode(true)
.withConfiguration("sdkman_auto_env", "true")
.build()
new FileTreeBuilder(bash.workDir).with {
"project" {
".sdkmanrc"("groovy=2.4.1\n")
}
}
bash.start()
bash.execute("source $bootstrapScript")
when:
bash.execute("cd project")
bash.execute("cd ..")
bash.execute("cd project")
then:
bash.output.contains('Using groovy version 2.4.1 in this shell')
}
def "should execute 'sdk env clear' when exiting from a directory with an .sdkmanrc"() {
given:
new FileTreeBuilder(candidatesDirectory).with {
"groovy" {
"2.4.1" {}
"2.4.6" {}
}
}
createSymbolicLink(Paths.get("$candidatesDirectory/groovy/current"), Paths.get("$candidatesDirectory/groovy/2.4.6"))
bash = sdkmanBashEnvBuilder
.withOfflineMode(true)
.withConfiguration("sdkman_auto_env", "true")
.build()
new FileTreeBuilder(bash.workDir).with {
"project" {
".sdkmanrc"("groovy=2.4.1\n")
}
}
bash.start()
bash.execute("source $bootstrapScript")
when:
bash.execute("cd project")
bash.execute("cd ..")
then:
bash.output.contains("Restored groovy version to 2.4.6")
}
def "should execute 'sdk env clear; sdk env' when switching to another directory with an .sdkmanrc"() {
given:
new FileTreeBuilder(candidatesDirectory).with {
"groovy" {
"2.4.1" {}
"2.4.6" {}
"2.5.14" {}
}
"ant" {
"1.9.15" {}
"1.10.8" {}
}
}
createSymbolicLink(Paths.get("$candidatesDirectory/groovy/current"), Paths.get("$candidatesDirectory/groovy/2.5.14"))
createSymbolicLink(Paths.get("$candidatesDirectory/ant/current"), Paths.get("$candidatesDirectory/ant/1.10.8"))
bash = sdkmanBashEnvBuilder
.withOfflineMode(true)
.withConfiguration("sdkman_auto_env", "true")
.build()
new FileTreeBuilder(bash.workDir).with {
"projectA" {
".sdkmanrc"("groovy=2.4.1\nant=1.9.15\n")
}
"projectB" {
".sdkmanrc"("groovy=2.4.6\n")
}
}
bash.start()
bash.execute("source $bootstrapScript")
when:
bash.execute("cd projectA")
then:
bash.output.contains('Using groovy version 2.4.1 in this shell')
bash.output.contains('Using ant version 1.9.15 in this shell')
when:
bash.execute("cd ../projectB")
then:
bash.output.contains("Restored ant version to 1.10.8")
bash.output.contains('Using groovy version 2.4.6 in this shell')
when:
bash.execute("cd ..")
then:
bash.output.contains('Restored groovy version to 2.5.14')
!bash.output.contains('ant')
}
def "should not execute 'sdk env clear' when entering a subdirectory within the current active configuration"() {
given:
new FileTreeBuilder(candidatesDirectory).with {
"groovy" {
"2.4.1" {}
}
}
bash = sdkmanBashEnvBuilder
.withOfflineMode(true)
.withConfiguration("sdkman_auto_env", "true")
.build()
new FileTreeBuilder(bash.workDir).with {
"project" {
".sdkmanrc"("groovy=2.4.1\n")
}
"src" {}
}
bash.start()
bash.execute("source $bootstrapScript")
when:
bash.execute("cd project")
bash.execute("cd src")
then:
!bash.output.contains("Restored groovy version to 2.4.6")
}
def "should issue an error if .sdkmanrc contains a malformed candidate version"() {
given:
bash = sdkmanBashEnvBuilder
.withOfflineMode(true)
.build()
new File(bash.workDir, ".sdkmanrc").text = "groovy 2.4.1"
bash.start()
bash.execute("source $bootstrapScript")
when:
bash.execute("sdk env")
then:
verifyAll(bash) {
status == 1
output.contains("Invalid candidate format!")
}
}
def "should issue an error when .sdkmanrc contains a candidate version which is not installed"() {
given:
bash = sdkmanBashEnvBuilder
.withOfflineMode(true)
.build()
new File(bash.workDir, ".sdkmanrc").text = "groovy=2.4.1"
bash.start()
bash.execute("source $bootstrapScript")
when:
bash.execute("sdk env")
then:
verifyAll(bash) {
status == 1
output.contains("Stop! groovy 2.4.1 is not installed.")
output.contains("Run 'sdk env install' to install it.")
}
}
def "should support blank lines, comments and inline comments"() {
given:
new FileTreeBuilder(candidatesDirectory).with {
"groovy" {
"2.4.1" {}
}
}
bash = sdkmanBashEnvBuilder
.withOfflineMode(true)
.build()
new File(bash.workDir, ".sdkmanrc").text = sdkmanrc
bash.start()
bash.execute("source $bootstrapScript")
when:
bash.execute("sdk env")
then:
bash.output.contains("Using groovy version 2.4.1 in this shell.")
where:
sdkmanrc << [
"\ngroovy=2.4.1\n",
"# this is a comment\ngroovy=2.4.1\n",
"groovy=2.4.1 # this is a comment too\n"
]
}
}
================================================
FILE: src/test/groovy/sdkman/specs/InitialisationSpec.groovy
================================================
package sdkman.specs
import sdkman.support.SdkmanEnvSpecification
import java.nio.file.Files
import java.nio.file.Paths
class InitialisationSpec extends SdkmanEnvSpecification {
static final allCandidates = [
"asciidoctorj", "crash", "gaiden", "glide", "gradle", "grails", "griffon", "groovy",
"groovyserv", "jbake", "jbossforge", "lazybones", "springboot", "vertx"
]
def setup() {
bash = sdkmanBashEnvBuilder
.withCandidates(allCandidates)
.build()
prepareCandidateDirectories(allCandidates)
}
void "should include all candidates in PATH"() {
given:
bash.start()
bash.execute("source $bootstrapScript")
bash.resetOutput()
when:
bash.execute('echo "$PATH"')
def pathParts = bash.output.split(':')
def pathElementMatcher = ~/$candidatesDirectory\/([^\/]+)\/.*/
def includedCandidates = pathParts
.collect { it.replace("\n", "") }
.collect { it =~ pathElementMatcher }
.findAll { it }
.collect { it[0][1] }
.sort()
println("Available: ${allCandidates}")
println("Included : $includedCandidates")
and:
def missingCandidates = allCandidates - includedCandidates
then:
missingCandidates.isEmpty()
}
void "should reinitialize candidates in PATH if necessary"() {
given:
bash.start()
bash.execute("source $bootstrapScript")
bash.resetOutput()
and:
def originalPath = bash.env.grep { it =~ /^PATH=/ }.first() as String
bash.execute(originalPath)
when:
bash.execute("source $bootstrapScript")
bash.execute('echo "$PATH"')
then:
def pathParts = bash.output.split(':')
def pathElementMatcher = ~/$candidatesDirectory\/([^\/]+)\/.*/
def includedCandidates = pathParts
.collect { it.replace("\n", "") }
.collect { it =~ pathElementMatcher }
.findAll { it }
.collect { it[0][1] }
.sort()
println("Available: $allCandidates")
println("Included : $includedCandidates")
def missingCandidates = allCandidates - includedCandidates
missingCandidates.isEmpty()
}
void "should not duplicate PATH entries if re-sourced"() {
given:
bash.start()
bash.execute("source $bootstrapScript")
bash.resetOutput()
when:
bash.execute("source $bootstrapScript")
bash.execute('echo "$PATH"')
def pathParts = bash.output.split(':')
def pathElementMatcher = ~/$candidatesDirectory\/([^\/]+)\/.*/
def includedCandidates = pathParts
.collect { it.replace("\n", "") }
.collect { it =~ pathElementMatcher }
.findAll { it }
.collect { it[0][1] }
.sort()
println("Available: $allCandidates")
println("Included : $includedCandidates")
and:
def duplicateCandidates = includedCandidates - allCandidates
then:
duplicateCandidates.isEmpty()
}
private prepareCandidateDirectories(List candidates) {
candidates.forEach {
def current = Paths.get("$candidatesDirectory/$it/current")
def targetFilename = "$candidatesDirectory/$it/xxx"
new File(targetFilename).createNewFile()
def target = Paths.get(targetFilename)
Files.createSymbolicLink(current, target)
}
}
}
================================================
FILE: src/test/groovy/sdkman/specs/SdkCompatibilitySpec.groovy
================================================
package sdkman.specs
import sdkman.support.SdkmanEnvSpecification
import java.nio.file.Files
import java.nio.file.Paths
class SdkCompatibilitySpec extends SdkmanEnvSpecification {
def allCandidates = ["groovy", "grails", "scala", "sbt"]
def setup() {
bash = sdkmanBashEnvBuilder
.withCandidates(allCandidates)
.build()
}
void "should add candidate bin folder to the path if present"() {
given:
def candidateFolder = prepareCandidateFolder("scala", "2.11.7", true)
and:
bash.start()
bash.execute("source $bootstrapScript")
bash.resetOutput()
when:
bash.execute('echo $PATH')
def pathEntries = bash.output.split(':')
def firstPathEntry = pathEntries.first()
then:
firstPathEntry.contains("$candidateFolder/bin")
}
void "should add candidate base folder to the path if no bin folder present"() {
given:
def candidateFolder = prepareCandidateFolder("sbt", "1.0.3", false)
and:
bash.start()
bash.execute("source $bootstrapScript")
bash.resetOutput()
when:
bash.execute('echo $PATH')
def pathEntries = bash.output.split(':')
def firstPathEntry = pathEntries.first()
then:
firstPathEntry.contains("$candidateFolder")
and:
!firstPathEntry.contains("$candidateFolder/bin")
}
private prepareCandidateFolder(String candidate, String version, boolean hasBinFolder) {
def candidateBaseDir = "${candidatesDirectory.absolutePath}/$candidate"
def candidateCurrentDir = Paths.get("$candidateBaseDir/current")
def candidateLocation = "${candidatesDirectory.absolutePath}/$candidate/$version"
def candidatePath = Paths.get(candidateLocation)
def binLocation = hasBinFolder ? "$candidateLocation/bin/" : "$candidateLocation"
def executableFilename = "run.sh"
def executableFile = "$binLocation/$executableFilename" as File
new File(binLocation).mkdirs()
executableFile.createNewFile()
Files.createSymbolicLink(candidateCurrentDir, candidatePath)
candidateCurrentDir.toString()
}
}
================================================
FILE: src/test/groovy/sdkman/specs/SelfupdateFeatureSpec.groovy
================================================
package sdkman.specs
import sdkman.support.SdkmanEnvSpecification
import java.time.Instant
import static java.time.temporal.ChronoUnit.DAYS
class SelfupdateFeatureSpec extends SdkmanEnvSpecification {
static final String CANDIDATES_API = "http://localhost:8080/2"
static final String HEALTHCHECK_ENDPOINT = "$CANDIDATES_API/healthcheck"
static final String VERSION_ENDPOINT = "$CANDIDATES_API/broker/download/sdkman/version/stable"
def setup() {
curlStub.primeWith(HEALTHCHECK_ENDPOINT, "echo dbfb025be9f97fda2052b5febcca0155")
curlStub.primeWith(VERSION_ENDPOINT, "echo 5.0.0")
}
def "should list selfupdate as a valid command when the selfupdate feature is toggled on"() {
given:
bash = sdkmanBashEnvBuilder
.withConfiguration("sdkman_selfupdate_feature", selfUpdateFeature)
.build()
bash.start()
bash.execute("source $bootstrapScript")
when:
bash.execute("sdk help")
then:
verifyOutput(bash.output)
where:
selfUpdateFeature | verifyOutput
"false" | { !it.contains("selfupdate") }
"true" | { it.contains("selfupdate") }
}
def "should source sdkman-selfupdate.sh when the selfupdate feature is toggled on"() {
given:
bash = sdkmanBashEnvBuilder
.withConfiguration("sdkman_selfupdate_feature", selfupdateFeature)
.build()
bash.start()
bash.execute("source $bootstrapScript")
when:
bash.execute("sdk selfupdate")
then:
verifyOutput(bash.output)
where:
selfupdateFeature | verifyOutput
"false" | { it.contains("Invalid command: selfupdate") }
"true" | { !it.contains("Invalid command: selfupdate") }
}
}
================================================
FILE: src/test/groovy/sdkman/steps/command_line_interop_steps.groovy
================================================
package sdkman.steps
import static cucumber.api.groovy.EN.And
And(~'^I enter \"([^\"]*)\"$') { String command ->
bash.execute(command)
result = bash.output
}
And(~'^I enter "([^"]*)" and answer "([^"]*)"$') { String command, String answer ->
bash.execute(command, [answer])
result = bash.output
}
And(~'^I see \"([^\"]*)\"$') { String output ->
assert result.contains(output)
}
And(~'^I do not see "([^"]*)"$') { String output ->
assert !result.contains(output)
}
And(~'^I see only \"([^\"]*)\"$') { String output ->
assert result?.replaceAll("\\n", "") == output
}
And(~'^I see the current sdkman version$') { ->
assert result.contains("SDKMAN")
}
And(~'^I see a single occurrence of \"([^\"]*)\"$') { String occurrence ->
assert result.count(occurrence) == 1
}
And(~'^I see no occurrences of \"([^\"]*)\"$') { String occurrence ->
assert result.count(occurrence) == 0
}
And(~'the "(.*)" variable contains "(.*)"') { String home, String segment ->
bash.execute("echo \$$home")
assert bash.output.contains(segment)
}
And(~'the "(.*)" variable is not set') { String home ->
bash.execute("echo \$$home")
assert !bash.output.contains(".sdkman/")
}
And(~'^the home path ends with \"([^\"]*)\"$') { String suffix ->
def path = sdkmanBaseDir.absolutePath + "/" + suffix
assert result.trim().endsWith(path)
}
================================================
FILE: src/test/groovy/sdkman/steps/env.groovy
================================================
package sdkman.steps
import com.github.tomakehurst.wiremock.client.WireMock
import sdkman.support.FilesystemUtils
import sdkman.support.WireMockServerProvider
import static cucumber.api.groovy.Hooks.After
import static cucumber.api.groovy.Hooks.Before
HTTP_PROXY = System.getProperty("httpProxy") ?: ""
FAKE_JDK_PATH = "/path/to/my/openjdk"
SERVICE_UP_HOST = "localhost"
SERVICE_UP_PORT = 8080
SERVICE_UP_URL = "http://$SERVICE_UP_HOST:$SERVICE_UP_PORT"
SERVICE_DOWN_URL = "http://localhost:0"
counter = "${(Math.random() * 10000).toInteger()}".padLeft(4, "0")
localGroovyCandidate = "/tmp/groovy-core" as File
sdkmanScriptVersion = "5.0.0"
sdkmanNativeVersion = "0.0.1"
sdkmanBaseEnv = FilesystemUtils.prepareBaseDir().absolutePath
sdkmanBaseDir = sdkmanBaseEnv as File
sdkmanDirEnv = "$sdkmanBaseEnv/.sdkman"
sdkmanDir = sdkmanDirEnv as File
candidatesDir = "${sdkmanDirEnv}/candidates" as File
binDir = "${sdkmanDirEnv}/bin" as File
srcDir = "${sdkmanDirEnv}/src" as File
varDir = "${sdkmanDirEnv}/var" as File
metadataDir = "${varDir}/metadata" as File
etcDir = "${sdkmanDirEnv}/etc" as File
extDir = "${sdkmanDirEnv}/ext" as File
tmpDir = "${sdkmanDir}/tmp" as File
healthcheckFile = new File(varDir, "healthcheck")
candidatesFile = new File(varDir, "candidates")
versionFile = new File(varDir, "version")
initScript = new File(binDir, "sdkman-init.sh")
localCandidates = ['groovy', 'grails', 'java', 'kotlin', 'scala']
bash = null
if (!binding.hasVariable("wireMock")) {
wireMock = WireMockServerProvider.wireMockServer()
}
addShutdownHook {
wireMock.stop()
}
Before() {
WireMock.reset()
cleanUp()
}
private cleanUp() {
sdkmanBaseDir.deleteDir()
localGroovyCandidate.deleteDir()
}
After() { scenario ->
def output = bash?.output
if (output) {
scenario.write("\nOutput: \n${output}")
}
bash?.stop()
}
================================================
FILE: src/test/groovy/sdkman/steps/env_steps.groovy
================================================
package sdkman.steps
import static cucumber.api.groovy.EN.And
And(~/^the file "([^"]+)" exists and contains "([^"]+)"$/) { String filename, String content ->
new File(sdkmanBaseEnv, filename).withWriter {
it.writeLine(content)
}
}
================================================
FILE: src/test/groovy/sdkman/steps/flush_steps.groovy
================================================
package sdkman.steps
import static cucumber.api.groovy.EN.And
And(~'^the candidate "([^"]*)" is known locally$') { String candidate ->
assert candidatesFile.text.contains(candidate)
}
And(~'^no candidates are know locally$') { ->
assert !candidatesFile.exists()
}
And(~'^the file "([^"]*)" in temporary storage$') { String fileName ->
new File(tmpDir, fileName).createNewFile()
}
And(~'^no "([^"]*)" file is present in temporary storage$') { String fileName ->
assert !new File(tmpDir, fileName).exists()
}
And(~'^a prior version "([^"]*)" was detected$') { String version ->
assert versionFile.exists()
assert versionFile.text.contains(version)
}
And(~'^no version file can be found$') { ->
assert !versionFile.exists()
}
And(~'^the Remote Version has been flushed$') { ->
assert versionFile.delete()
}
And(~'^a headers file "([^"]*)" in metadata directory with checksum "([^"]*)" using algorithm "([^"]*)"$') {
String fileName, String checksum, String algorithm ->
new File(metadataDir, fileName).withWriter { out ->
out.println "X-Sdkman-Checksum-${algorithm}: ${checksum}"
}
}
And(~'^no metadata is cached$') { ->
assert !metadataDir.listFiles()
}
================================================
FILE: src/test/groovy/sdkman/steps/initialisation_steps.groovy
================================================
package sdkman.steps
import sdkman.env.SdkmanBashEnvBuilder
import sdkman.stubs.UnameStub
import java.util.zip.ZipException
import java.util.zip.ZipFile
import static cucumber.api.groovy.EN.And
import static sdkman.stubs.WebServiceStub.primeEndpointWithString
import static sdkman.stubs.WebServiceStub.primeSelfupdate
And(~'^the sdkman work folder is created$') { ->
assert sdkmanDir.isDirectory(), "The SDKMAN directory does not exist."
}
And(~'^the "([^"]*)" folder exists in user home$') { String arg1 ->
assert sdkmanDir.isDirectory(), "The SDKMAN directory does not exist."
}
And(~'^the archive for candidate "([^"]*)" version "([^"]*)" is corrupt$') { String candidate, String version ->
try {
new ZipFile(new File("src/test/resources/__files/${candidate}-${version}.zip"))
assert false, "Archive was not corrupt!"
} catch (ZipException ze) {
//expected behaviour
}
}
And(~'^the archive for candidate "([^"]*)" version "([^"]*)" is removed$') { String candidate, String version ->
def archive = new File("${sdkmanDir}/tmp/${candidate}-${version}.zip")
assert !archive.exists()
}
And(~'^the sdkman (.*) version "(.*)" is available for download$') { format, version ->
primeEndpointWithString("/broker/version/sdkman/${format}/stable", version)
}
And(~'^the internet is reachable$') { ->
primeEndpointWithString("/healthcheck", "12345")
primeSelfupdate()
offlineMode = false
serviceUrlEnv = SERVICE_UP_URL
javaHome = FAKE_JDK_PATH
}
And(~'^the internet is not reachable$') { ->
offlineMode = false
serviceUrlEnv = SERVICE_DOWN_URL
javaHome = FAKE_JDK_PATH
}
And(~'^offline mode is disabled with reachable internet$') { ->
primeEndpointWithString("/healthcheck", "12345")
offlineMode = false
serviceUrlEnv = SERVICE_UP_URL
javaHome = FAKE_JDK_PATH
}
And(~'^offline mode is enabled with reachable internet$') { ->
primeEndpointWithString("/healthcheck", "12345")
offlineMode = true
serviceUrlEnv = SERVICE_UP_URL
javaHome = FAKE_JDK_PATH
}
And(~'^offline mode is enabled with unreachable internet$') { ->
offlineMode = true
serviceUrlEnv = SERVICE_DOWN_URL
javaHome = FAKE_JDK_PATH
}
And(~'^an "(.*)" machine with "(.*)" installed$') { String machine, String kernel ->
def binFolder = "$sdkmanBaseDir/bin" as File
UnameStub.prepareIn(binFolder)
.forKernel(kernel)
.forMachine(machine)
.build()
}
And(~'^an initialised environment$') { ->
bash = SdkmanBashEnvBuilder.create(sdkmanBaseDir)
.withOfflineMode(offlineMode)
.withCandidatesApi(serviceUrlEnv)
.withBrokerApi(serviceUrlEnv)
.withJdkHome(javaHome)
.withHttpProxy(HTTP_PROXY)
.withScriptVersion(sdkmanScriptVersion)
.withNativeVersion(sdkmanNativeVersion)
.withCandidates(localCandidates)
.build()
}
And(~'^an initialised environment without debug prints$') { ->
bash = SdkmanBashEnvBuilder.create(sdkmanBaseDir)
.withOfflineMode(offlineMode)
.withCandidatesApi(serviceUrlEnv)
.withBrokerApi(serviceUrlEnv)
.withJdkHome(javaHome)
.withHttpProxy(HTTP_PROXY)
.withScriptVersion(sdkmanScriptVersion)
.withNativeVersion(sdkmanNativeVersion)
.withCandidates(localCandidates)
.withDebugMode(false)
.build()
}
And(~'^the system is bootstrapped$') { ->
bash.start()
bash.execute("source $sdkmanDirEnv/bin/sdkman-init.sh")
}
And(~'^the system is bootstrapped again$') { ->
bash.execute("source $sdkmanDirEnv/bin/sdkman-init.sh")
}
And(~/^the sdkman scripts version is "([^"]*)"$/) { String version ->
sdkmanScriptVersion = version
}
And(~/^the sdkman native version is "([^"]*)"$/) { String version ->
sdkmanNativeVersion = version
}
And(~/^the candidates cache is initialised with "(.*)"$/) { String candidate ->
localCandidates << candidate
}
And(~/^a project configuration is active$/) { ->
bash.execute("SDKMAN_ENV=" + sdkmanBaseEnv)
}
And(~/^a project configuration is active but points to a directory without configuration$/) { ->
def emptyDir = tmpDir.getPath() + "/empty"
bash.execute("mkdir $emptyDir")
bash.execute("SDKMAN_ENV=$emptyDir")
}
================================================
FILE: src/test/groovy/sdkman/steps/installation_steps.groovy
================================================
package sdkman.steps
import java.nio.file.FileSystems
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import static cucumber.api.groovy.EN.And
import static java.nio.file.Files.isSameFile
import static java.nio.file.Files.isSymbolicLink
import static sdkman.support.FilesystemUtils.prepareCandidateBinFolder
import static sdkman.support.FilesystemUtils.prepareCandidateWithVersionFolder
And(~'^the candidate "([^"]*)" version "([^"]*)" is installed$') { String candidate, String version ->
def file = "${candidatesDir}/${candidate}/${version}" as File
if (!file.exists()) println bash.output
assert file.exists()
}
And(~'^the candidate "([^"]*)" version "([^"]*)" is not installed$') { String candidate, String version ->
def directory = FileSystems.default.getPath("$candidatesDir/$candidate/$version")
if (Files.exists(directory)) println bash.output
assert !Files.exists(directory)
}
And(~'^the candidate "([^"]*)" version "([^"]*)" is already installed and default$') { String candidate, String version ->
def candidateVersion = prepareCandidateWithVersionFolder("$candidatesDir", candidate, version)
def currentLink = FileSystems.default.getPath("$candidatesDir/$candidate/current")
Files.createSymbolicLink currentLink, candidateVersion
}
And(~'^the candidate "([^"]*)" version "([^"]*)" is the default$') { String candidate, String version ->
def localVersion = FileSystems.default.getPath("$candidatesDir/$candidate/$version")
def currentLink = FileSystems.default.getPath("$candidatesDir/$candidate/current")
Files.createSymbolicLink currentLink, localVersion
}
And(~'^the candidate "([^"]*)" version "([^"]*)" is already installed but not default$') { String candidate, String version ->
prepareCandidateWithVersionFolder "$candidatesDir", candidate, version
}
And(~'^I do not have a "([^"]*)" candidate installed$') { String candidate ->
def candidateDir = FileSystems.default.getPath("${candidatesDir}/${candidate}")
assert !candidateDir.toFile().listFiles()
}
And(~'^the candidate "([^"]*)" does not exist locally$') { String candidate ->
def candidateDir = "${candidatesDir}/${candidate}" as File
candidateDir.deleteDir()
assert !candidateDir.exists()
}
And(~'^I have a local candidate "([^"]*)" version "([^"]*)" at "([^"]*)"$') { String candidate, String version, String directory ->
prepareCandidateBinFolder directory, candidate, version
}
And(~'^I have a local candidate "([^"]*)" version "([^"]*)" at relative path "([^"]*)"$') { String candidate, String version, String relativePath ->
def fullPath = new File(sdkmanBaseDir.absolutePath, relativePath)
prepareCandidateBinFolder fullPath.absolutePath, candidate, version
}
And(~'^the candidate "([^"]*)" version "([^"]*)" is linked to the relative path "([^"]*)"$') { String candidate, String version, String relativePath ->
def fullPath = new File(sdkmanBaseDir.absolutePath, relativePath)
assertLinkedCandidate(fullPath.absolutePath, candidate, version)
}
And(~'^the candidate "([^"]*)" version "([^"]*)" is linked to "([^"]*)"$') { String candidate, String version, String directory ->
assertLinkedCandidate(directory, candidate, version)
}
def assertLinkedCandidate(String directory, String candidate, String version) {
Path versionFolder = Paths.get("$candidatesDir/$candidate/$version")
assert isSymbolicLink(versionFolder)
assert isSameFile(versionFolder, Paths.get(directory))
}
And(~'^the candidate "([^"]*)" version "([^"]*)" is already linked to "([^"]*)"$') { String candidate, String version, String folder ->
def fileSystem = FileSystems.default
def candidateFolder = "$candidatesDir/$candidate" as File
candidateFolder.mkdirs()
def link = fileSystem.getPath("$candidatesDir/$candidate/$version")
def target = prepareCandidateBinFolder(folder, candidate, version)
Files.createSymbolicLink(link, target)
}
And(~'^I have configured "([^"]*)" to "([^"]*)"$') { String configName, String flag ->
def configFile = new File("$sdkmanDir/etc/config")
configFile.write "${configName}=${flag}"
}
And(~/^the exit code is (\d+)$/) { Integer rc ->
assert bash.getStatus() == rc
}
And(~'^the response headers file is created for candidate "([^"]*)" and version "([^"]*)"$') { String candidate, String version ->
def headersFile = "${metadataDir}/${candidate}-${version}.headers" as File
assert headersFile.exists()
}
And(~'^no response headers are written for candidate "([^"]*)" and version "([^"]*)"$') { String candidate, String version ->
def headersFile = "${metadataDir}/${candidate}-${version}.headers" as File
assert !headersFile.exists()
}
================================================
FILE: src/test/groovy/sdkman/steps/stub_steps.groovy
================================================
package sdkman.steps
import io.cucumber.datatable.DataTable
import sdkman.support.UnixUtils
import static cucumber.api.groovy.EN.And
import static sdkman.stubs.HookResponses.*
import static sdkman.stubs.WebServiceStub.*
import static sdkman.support.FilesystemUtils.readCurrentFromCandidateFolder
import static sdkman.support.FilesystemUtils.readVersionsCsvFromCandidateFolder
And(~'^the default "([^"]*)" version is "([^"]*)"$') { String candidate, String version ->
primeEndpointWithString("/candidates/default/${candidate}", version)
primeDownloadFor(SERVICE_UP_URL, candidate, version, UnixUtils.inferPlatform())
primeEndpointWithString("/hooks/post/${candidate}/${version}/${UnixUtils.inferPlatform()}", postInstallationHookSuccess())
}
And(~'^an available selfupdate endpoint$') { ->
primeEndpointWithString("/selfupdate/stable/${UnixUtils.inferPlatform()}", 'echo "Successfully upgraded SDKMAN."')
primeEndpointWithString("/selfupdate/beta/${UnixUtils.inferPlatform()}", 'echo "Successfully upgraded SDKMAN."')
}
And(~'^the candidate "([^"]*)" version "([^"]*)" is available for download$') { String candidate, String version ->
primeEndpointWithString("/candidates/validate/${candidate}/${version}/${UnixUtils.inferPlatform()}", "valid")
primeDownloadFor(SERVICE_UP_URL, candidate, version, UnixUtils.inferPlatform())
primeEndpointWithString("/hooks/post/${candidate}/${version}/${UnixUtils.inferPlatform()}", postInstallationHookSuccess())
}
And(~'^the candidate "([^"]*)" version "([^"]*)" is available for download with checksum "([^"]*)" using algorithm "([^"]*)"$') {
String candidate, String version, String checksum, String algorithm ->
primeEndpointWithString("/candidates/validate/${candidate}/${version}/${UnixUtils.inferPlatform()}", "valid")
primeDownloadFor(SERVICE_UP_URL, candidate, version, UnixUtils.inferPlatform(), ["X-Sdkman-Checksum-${algorithm}": "${checksum}"])
primeEndpointWithString("/hooks/post/${candidate}/${version}/${UnixUtils.inferPlatform()}", postInstallationHookSuccess())
}
And(~/^the appropriate universal hook is available for "([^"]*)" version "([^"]*)" on "([^"]*)"$/) { String candidate, String version, String os ->
String lcPlatform = UnixUtils.inferPlatform(os)
primeUniversalHookFor(candidate, version, lcPlatform)
}
And(~/^the appropriate multi-platform hook is available for "([^"]*)" version "([^"]*)" on "([^"]*)" with architecture "(.*)"$/) { String candidate, String version, String os, String architecture ->
String lcPlatform = UnixUtils.inferPlatform(os, architecture)
primePlatformSpecificHookFor(candidate, version, lcPlatform)
}
And(~'^the candidate "([^"]*)" version "([^"]*)" is not available for download$') { String candidate, String version ->
primeEndpointWithString("/candidates/validate/${candidate}/${version}/${UnixUtils.inferPlatform()}", "invalid")
}
And(~/^the candidate "(.*)" version "(.*)" is available for download on "(.*)" with architecture "(.*)"$/) { String candidate, String version, String os, String architecture ->
String lcPlatform = UnixUtils.inferPlatform(os, architecture)
primeEndpointWithString("/candidates/validate/${candidate}/${version}/${lcPlatform}", "valid")
primeDownloadFor(SERVICE_UP_URL, candidate, version, lcPlatform)
}
And(~/^a post-installation hook is served for "([^"]*)" "([^"]*)" on "([^"]*)" with architecture "([^"]*)" that returns successfully$/) { String candidate, String version, String os, String architecture ->
String lcPlatform = UnixUtils.inferPlatform(os, architecture)
primeEndpointWithString("/hooks/post/${candidate}/${version}/${lcPlatform}", postInstallationHookSuccess())
}
And(~/^a post-installation hook is served for "([^"]*)" "([^"]*)" on "([^"]*)" with architecture "([^"]*)" that returns a failure$/) { String candidate, String version, String os, String architecture ->
String lcPlatform = UnixUtils.inferPlatform(os, architecture)
primeEndpointWithString("/hooks/post/${candidate}/${version}/${lcPlatform}", postInstallationHookFailure())
}
And(~/^the candidate "(.*?)" version "(.*?)" is not available for download on "(.*?)"$/) { String candidate, String version, String os ->
String lcPlatform = UnixUtils.inferPlatform(os)
primeEndpointWithString("/candidates/validate/${candidate}/${version}/${lcPlatform}", "invalid")
}
And(~'^a "([^"]*)" list view is available for consumption$') { String candidate ->
primeEndpointWithString("/candidates/${candidate}/${UnixUtils.inferPlatform()}/versions/list?current=&installed=", "Available ${candidate.capitalize()} Versions")
}
And(~'^the candidate "([^"]*)" version "([^"]*)" is a valid candidate version$') { String candidate, String version ->
primeEndpointWithString("/candidates/validate/${candidate}/${version}/${UnixUtils.inferPlatform()}", "valid")
}
And(~'^the candidate "([^"]*)" version "([^"]*)" is not a valid candidate version$') { String candidate, String version ->
primeEndpointWithString("/candidates/validate/${candidate}/${version}/${UnixUtils.inferPlatform()}", "invalid")
}
And(~/^the candidate "(.*?)" has a version list available$/) { String candidate ->
def current = readCurrentFromCandidateFolder(candidatesDir, candidate)
def versions = readVersionsCsvFromCandidateFolder(candidatesDir, candidate)
def url = "/candidates/${candidate}/${UnixUtils.inferPlatform()}/versions/list?current=${current}&installed=${versions}"
println("Priming url: $url")
primeEndpointWithString(url, "Candidate: $candidate; Versions: $versions; Current: $current; Platform: ${UnixUtils.inferPlatform()}")
}
And(~/^The candidate list is available$/) { ->
primeEndpointWithString("/candidates/list", "Candidate List")
}
And(~/^the following candidates are currently available from remote API:$/) { DataTable dt ->
primeEndpointWithString("/candidates/all", dt.asList(String).drop(1).join(","))
}
================================================
FILE: src/test/groovy/sdkman/steps/update_steps.groovy
================================================
package sdkman.steps
import io.cucumber.datatable.DataTable
import static cucumber.api.groovy.EN.And
And(~/^the following candidates are available for installation in local cache:$/) { DataTable dt ->
localCandidates = dt.asList(String).drop(1)
}
And(~/^the Candidates cache should contain "(.*)"$/) { String candidates ->
assert candidatesFile.text.trim() == candidates
}
================================================
FILE: src/test/groovy/sdkman/steps/use_steps.groovy
================================================
package sdkman.steps
import java.nio.file.FileSystems
import java.nio.file.Files
import static cucumber.api.groovy.EN.And
And(~'^the candidate "([^"]*)" version "([^"]*)" is in use$') { String candidate, String version ->
def directory = FileSystems.default.getPath("$candidatesDir/$candidate/$version")
def current = FileSystems.default.getPath("$candidatesDir/$candidate/current")
def symlinkFile = current.toFile()
if (!symlinkFile.exists()) {
assert Files.createSymbolicLink(current, directory)
}
}
And(~'^the candidate "([^"]*)" version "([^"]*)" is not in use$') { String candidate, String version ->
def directory = FileSystems.default.getPath("$candidatesDir/$candidate/$version")
def current = FileSystems.default.getPath("$candidatesDir/$candidate/current")
def symlinkFile = current.toFile()
if (symlinkFile.exists()) {
assert !Files.isSameFile(current, directory)
}
}
And(~'^the candidate "([^"]*)" version "([^"]*)" should be in use$') { String candidate, String version ->
bash.execute("$candidate --version")
assert bash.output.contains(version)
}
And(~'^the candidate "([^"]*)" version "([^"]*)" should be the default$') { String candidate, String version ->
def directory = FileSystems.default.getPath("$candidatesDir/$candidate/$version")
def current = FileSystems.default.getPath("$candidatesDir/$candidate/current")
assert Files.isSameFile(current, directory)
}
And(~'^the candidate "([^"]*)" version "([^"]*)" should not be the default$') { String candidate, String version ->
def directory = FileSystems.default.getPath("$candidatesDir/$candidate/$version")
def current = FileSystems.default.getPath("$candidatesDir/$candidate/current")
assert (!Files.isSymbolicLink(current) || (Files.isSymbolicLink(current) && !Files.isSameFile(current, directory)))
}
And(~'^the candidate "([^"]*)" is no longer selected$') { String candidate ->
def symlink = new File("$candidatesDir/$candidate/current")
assert !symlink.exists()
}
================================================
FILE: src/test/groovy/sdkman/stubs/CurlStub.groovy
================================================
package sdkman.stubs
class CurlStub {
private File file
private commands = [:]
static CurlStub prepareIn(File folder) {
folder.mkdirs()
def file = new File(folder, "curl")
file.createNewFile()
file.write "#!/usr/bin/env bash\n"
file.executable = true
new CurlStub(file: file)
}
CurlStub primeWith(String request, String snippet) {
commands.put request, snippet
this
}
void build() {
commands.each { request, snippet ->
//use last arg because we use curl with options
file << 'if [[ "${@: -1}" == "'
file << "$request"
file << '" ]]; then\n'
file << " $snippet\n"
file << 'fi\n'
}
}
}
================================================
FILE: src/test/groovy/sdkman/stubs/HookResponses.groovy
================================================
package sdkman.stubs
class HookResponses {
static postInstallationHookSuccess() {
'''\
#!/usr/bin/env bash
function __sdkman_post_installation_hook {
mv -f $binary_input $zip_output
echo "Post-installation hook success"
return 0
}
'''
}
static postInstallationHookFailure() {
'''\
#!/usr/bin/env bash
function __sdkman_post_installation_hook {
echo "Post-installation hook failure"
return 1
}
'''
}
}
================================================
FILE: src/test/groovy/sdkman/stubs/UnameStub.groovy
================================================
package sdkman.stubs
class UnameStub {
private File file
def kernel = "Linux"
def machine = "x86_64"
static UnameStub prepareIn(File folder) {
folder.mkdirs()
def file = new File(folder, "uname")
file.createNewFile()
file.write "#!/usr/bin/env bash\n"
file.executable = true
new UnameStub(file: file)
}
UnameStub forKernel(String kernel) {
this.kernel = kernel
this
}
UnameStub forMachine(String machine) {
this.machine = machine
this
}
void build() {
file << """
|if [[ "\$1" == '-m' ]]; then
| echo "$machine"
|elif [[ "\$1" == '-s' ]]; then
| echo "$kernel"
|else
| echo "$machine"
|fi
""".stripMargin('|')
}
}
================================================
FILE: src/test/groovy/sdkman/stubs/WebServiceStub.groovy
================================================
package sdkman.stubs
import static com.github.tomakehurst.wiremock.client.WireMock.*
class WebServiceStub {
static primeEndpointWithString(String endpoint, String body) {
stubFor(get(urlEqualTo(endpoint)).willReturn(
aResponse()
.withStatus(200)
.withHeader("Content-Type", "text/plain")
.withBody(body)))
}
static primeUniversalHookFor(String candidate, String version, String platform) {
primeHookFor(candidate, version, platform, true)
}
static primePlatformSpecificHookFor(String candidate, String version, String platform) {
primeHookFor(candidate, version, platform, false)
}
private static primeHookFor(String candidate, String version, String platform, boolean universal = true) {
def hookFile = "hooks/post_hook_${candidate}_${version}_${universal ? 'universal' : platform}.sh"
stubFor(get(urlEqualTo("/hooks/post/$candidate/$version/$platform")).willReturn(
aResponse()
.withStatus(200)
.withHeader("Content-Type", "text/plain")
.withBodyFile(hookFile)))
}
static primeDownloadFor(String host, String candidate, String version, String platform) {
primeDownloadFor(host, candidate, version, platform, [:])
}
static primeDownloadFor(String host, String candidate, String version, String platform, Map<String, String> headers) {
def binary = (candidate == "java") ? "jdk-${version}-${platform}.tar.gz" : "${candidate}-${version}.zip"
def responseBuilder = aResponse()
.withHeader("Location", "${host}/${binary}")
.withStatus(302)
headers.each { responseBuilder.withHeader(it.key, it.value) }
stubFor(get(urlEqualTo("/download/${candidate}/${version}/${platform}"))
.willReturn(responseBuilder))
stubFor(get(urlEqualTo("/$binary")).willReturn(
aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/zip")
.withBodyFile(binary)))
}
static primeSelfupdate() {
stubFor(get(urlEqualTo("/selfupdate")).willReturn(
aResponse()
.withStatus(200)
.withHeader("Content-Type", "text/plain")
.withBodyFile("selfupdate.sh")))
}
}
================================================
FILE: src/test/groovy/sdkman/support/BashEnvSpecification.groovy
================================================
package sdkman.support
import sdkman.env.BashEnv
import spock.lang.Specification
abstract class BashEnvSpecification extends Specification {
BashEnv bash
void cleanup() {
println bash.output
bash.stop()
}
}
================================================
FILE: src/test/groovy/sdkman/support/FilesystemUtils.groovy
================================================
package sdkman.support
import java.nio.file.FileSystems
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
class FilesystemUtils {
static final DEFAULT_BASE_DIR = "/tmp/sdkman-test"
static readVersionsCsvFromCandidateFolder(File baseDir, String candidate) {
def versionFiles = new File(baseDir, candidate).listFiles()
versionFiles?.findAll { it.name != "current" }?.sort()?.collect { it.name }?.join(",") ?: ""
}
static readCurrentFromCandidateFolder(File baseDir, String candidate) {
def versionFiles = new File(baseDir, candidate).listFiles()
def currentSymlinkPath = versionFiles.find { it.name == "current" }?.absolutePath
currentSymlinkPath ? Files.readSymbolicLink(Paths.get(currentSymlinkPath)).fileName : ""
}
static prepareBaseDir() {
def baseDir = "$DEFAULT_BASE_DIR/${UUID.randomUUID()}" as File
baseDir.mkdirs()
baseDir
}
static prepareCandidateWithVersionFolder(String baseDir, String candidate, String version) {
def directory = "$baseDir/$candidate/$version"
prepareCandidateBinFolder directory, candidate, version
}
static prepareCandidateBinFolder(String folder, String candidate, String version) {
def fileSystem = FileSystems.default
def binFolderPath = fileSystem.getPath("$folder/bin")
Files.createDirectories binFolderPath
prepareCandidateExecutable binFolderPath, candidate, version
return fileSystem.getPath("$folder")
}
private static prepareCandidateExecutable(Path binFolder, String candidate, String version) {
def candidateFile = new File("$binFolder/$candidate")
candidateFile.write "echo ${candidate.capitalize()} Version: ${version}"
candidateFile.executable = true
}
}
================================================
FILE: src/test/groovy/sdkman/support/SdkmanEnvSpecification.groovy
================================================
package sdkman.support
import sdkman.env.SdkmanBashEnvBuilder
import sdkman.stubs.CurlStub
import sdkman.stubs.UnameStub
import static sdkman.support.FilesystemUtils.prepareBaseDir
abstract class SdkmanEnvSpecification extends BashEnvSpecification {
SdkmanBashEnvBuilder sdkmanBashEnvBuilder
CurlStub curlStub
UnameStub unameStub
File sdkmanBaseDirectory
File sdkmanDotDirectory
File candidatesDirectory
String bootstrapScript
def setup() {
sdkmanBaseDirectory = prepareBaseDir()
curlStub = CurlStub.prepareIn(new File(sdkmanBaseDirectory, "bin"))
unameStub = UnameStub.prepareIn(new File(sdkmanBaseDirectory, "bin"))
sdkmanBashEnvBuilder = SdkmanBashEnvBuilder
.create(sdkmanBaseDirectory)
.withUnameStub(unameStub)
.withCurlStub(curlStub)
sdkmanDotDirectory = new File(sdkmanBaseDirectory, ".sdkman")
candidatesDirectory = new File(sdkmanDotDirectory, "candidates")
bootstrapScript = "${sdkmanDotDirectory}/bin/sdkman-init.sh"
}
def cleanup() {
assert sdkmanBaseDirectory.deleteDir()
}
}
================================================
FILE: src/test/groovy/sdkman/support/UnixUtils.groovy
================================================
package sdkman.support
class UnixUtils {
private static platforms = [
"Linux" : [
"x86_64": "linuxx64",
"aarch64": "linuxarm64",
],
"Darwin": [
"x86_64": "darwinx64",
"arm64": "darwinarm64",
]
]
static execute(String command) {
command.execute().text.trim()
}
static osName() { execute("uname -s") }
static osArch() { execute("uname -m") }
static inferPlatform(
String osName = osName(),
String architecture = osArch()) {
platforms[osName][architecture] ?: osName.toLowerCase()
}
}
================================================
FILE: src/test/groovy/sdkman/support/WireMockServerProvider.groovy
================================================
package sdkman.support
import com.github.tomakehurst.wiremock.WireMockServer
import com.github.tomakehurst.wiremock.client.WireMock
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig
class WireMockServerProvider {
static SERVICE_UP_HOST = "localhost"
static SERVICE_UP_PORT = 8080
static WireMockServer wireMockServer
static def wireMockServer() {
wireMockServer ?: createWireMockServer()
}
private static def createWireMockServer() {
wireMockServer = new WireMockServer(wireMockConfig().port(SERVICE_UP_PORT))
wireMockServer.start()
WireMock.configureFor(SERVICE_UP_HOST, SERVICE_UP_PORT)
wireMockServer
}
}
================================================
FILE: src/test/jmeter/SDKman.jmx
================================================
<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="2.5" jmeter="2.10 r1533061">
<hashTree>
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="SDKman" enabled="true">
<stringProp name="TestPlan.comments"></stringProp>
<boolProp name="TestPlan.functional_mode">false</boolProp>
<boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
<elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="TestPlan.user_define_classpath"></stringProp>
</TestPlan>
<hashTree>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Thread Group" enabled="true">
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
<boolProp name="LoopController.continue_forever">false</boolProp>
<intProp name="LoopController.loops">-1</intProp>
</elementProp>
<stringProp name="ThreadGroup.num_threads">1</stringProp>
<stringProp name="ThreadGroup.ramp_time">1</stringProp>
<longProp name="ThreadGroup.start_time">1382782760000</longProp>
<longProp name="ThreadGroup.end_time">1382782760000</longProp>
<boolProp name="ThreadGroup.scheduler">false</boolProp>
<stringProp name="ThreadGroup.duration"></stringProp>
<stringProp name="ThreadGroup.delay"></stringProp>
</ThreadGroup>
<hashTree>
<CounterConfig guiclass="CounterConfigGui" testclass="CounterConfig" testname="Counter" enabled="true">
<stringProp name="CounterConfig.start">0</stringProp>
<stringProp name="CounterConfig.end">100000</stringProp>
<stringProp name="CounterConfig.incr">1</stringProp>
<stringProp name="CounterConfig.name">counter</stringProp>
<stringProp name="CounterConfig.format">00000</stringProp>
<boolProp name="CounterConfig.per_user">true</boolProp>
</CounterConfig>
<hashTree/>
<Arguments guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments">
<elementProp name="host" elementType="Argument">
<stringProp name="Argument.name">host</stringProp>
<stringProp name="Argument.value">localhost</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
<elementProp name="port" elementType="Argument">
<stringProp name="Argument.name">port</stringProp>
<stringProp name="Argument.value">8080</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
</collectionProp>
</Arguments>
<hashTree/>
<CSVDataSet guiclass="TestBeanGUI" testclass="CSVDataSet" testname="CSV Data Set Config" enabled="true">
<stringProp name="delimiter">,</stringProp>
<stringProp name="fileEncoding"></stringProp>
<stringProp name="filename">candidates.csv</stringProp>
<boolProp name="quotedData">false</boolProp>
<boolProp name="recycle">true</boolProp>
<stringProp name="shareMode">shareMode.thread</stringProp>
<boolProp name="stopThread">false</boolProp>
<stringProp name="variableNames">candidate,version</stringProp>
</CSVDataSet>
<hashTree/>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="API Alive" enabled="true">
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="HTTPSampler.domain">${host}</stringProp>
<stringProp name="HTTPSampler.port">${po
gitextract_5nup9gj_/
├── .editorconfig
├── .github/
│ ├── CODEOWNERS
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ ├── config.yml
│ │ ├── feature_request.md
│ │ └── support_request.md
│ ├── auto_assign.yml
│ ├── dependabot.yml
│ ├── pull_request_template.md
│ └── workflows/
│ ├── beta.yml
│ ├── chloe-triage.yml
│ ├── notify-on-failure.yml
│ ├── pr.yml
│ └── release.yml
├── .gitignore
├── .sdkmanrc
├── CLAUDE.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Dockerfile
├── LICENSE
├── README.md
├── bin/
│ └── release-binary.sh
├── build.gradle
├── contrib/
│ └── completion/
│ └── bash/
│ └── sdk
├── dco.txt
├── gradle/
│ ├── archive.gradle
│ ├── release.gradle
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── settings.gradle
└── src/
├── jreleaser/
│ └── changelog.tpl
├── main/
│ └── bash/
│ ├── sdkman-availability.sh
│ ├── sdkman-cache.sh
│ ├── sdkman-config.sh
│ ├── sdkman-current.sh
│ ├── sdkman-default.sh
│ ├── sdkman-env-helpers.sh
│ ├── sdkman-env.sh
│ ├── sdkman-flush.sh
│ ├── sdkman-help.sh
│ ├── sdkman-home.sh
│ ├── sdkman-init.sh
│ ├── sdkman-install.sh
│ ├── sdkman-list.sh
│ ├── sdkman-main.sh
│ ├── sdkman-offline.sh
│ ├── sdkman-path-helpers.sh
│ ├── sdkman-selfupdate.sh
│ ├── sdkman-uninstall.sh
│ ├── sdkman-update.sh
│ ├── sdkman-upgrade.sh
│ ├── sdkman-use.sh
│ ├── sdkman-utils.sh
│ └── sdkman-version.sh
└── test/
├── groovy/
│ └── sdkman/
│ ├── cucumber/
│ │ └── RunCukeTests.groovy
│ ├── env/
│ │ ├── BashEnv.groovy
│ │ ├── CleanBashEnvBuilder.groovy
│ │ └── SdkmanBashEnvBuilder.groovy
│ ├── specs/
│ │ ├── CandidatesCacheUpdateFailureSpec.groovy
│ │ ├── CandidatesCacheUpdateSpec.groovy
│ │ ├── CompletionSpec.groovy
│ │ ├── ConfigCommandSpec.groovy
│ │ ├── CurrentCommandSpec.groovy
│ │ ├── EnvCommandSpec.groovy
│ │ ├── InitialisationSpec.groovy
│ │ ├── SdkCompatibilitySpec.groovy
│ │ └── SelfupdateFeatureSpec.groovy
│ ├── steps/
│ │ ├── command_line_interop_steps.groovy
│ │ ├── env.groovy
│ │ ├── env_steps.groovy
│ │ ├── flush_steps.groovy
│ │ ├── initialisation_steps.groovy
│ │ ├── installation_steps.groovy
│ │ ├── stub_steps.groovy
│ │ ├── update_steps.groovy
│ │ └── use_steps.groovy
│ ├── stubs/
│ │ ├── CurlStub.groovy
│ │ ├── HookResponses.groovy
│ │ ├── UnameStub.groovy
│ │ └── WebServiceStub.groovy
│ └── support/
│ ├── BashEnvSpecification.groovy
│ ├── FilesystemUtils.groovy
│ ├── SdkmanEnvSpecification.groovy
│ ├── UnixUtils.groovy
│ └── WireMockServerProvider.groovy
├── jmeter/
│ ├── SDKman.jmx
│ └── candidates.csv
└── resources/
├── __files/
│ └── hooks/
│ ├── post_hook_java_8.0.101_linuxx64.sh
│ ├── post_hook_java_8.0.111_linuxx64.sh
│ └── post_hook_java_8.0.111_universal.sh
└── features/
├── checksum_verification.feature
├── command_line_interop.feature
├── current_candidate.feature
├── default_version.feature
├── flush.feature
├── home.feature
├── hooks.feature
├── install_candidate.feature
├── install_sdkman.feature
├── java_installation.feature
├── list_candidate_versions.feature
├── list_candidates.feature
├── local_developement_versions.feature
├── mnemonics.feature
├── offline_mode.feature
├── path_initialisation.feature
├── per_project_configuration.feature
├── self_update.feature
├── service_unavailable.feature
├── uninstall_candidate.feature
├── update.feature
├── upgrade_candidate.feature
├── use_version.feature
└── version.feature
Condensed preview — 117 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (305K chars).
[
{
"path": ".editorconfig",
"chars": 8792,
"preview": "[*]\ncharset = utf-8\nend_of_line = lf\nindent_size = 4\nindent_style = tab\ninsert_final_newline = false\nmax_line_length = 1"
},
{
"path": ".github/CODEOWNERS",
"chars": 21,
"preview": "* @sdkman/sdkman-cli\n"
},
{
"path": ".github/FUNDING.yml",
"chars": 24,
"preview": "open_collective: sdkman\n"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 941,
"preview": "---\nname: Bug report\nabout: You found a bug in SDKMAN!\ntitle: \"Bug: [A concise title]\"\nlabels: 'bug'\nassignees: ''\n\n---\n"
},
{
"path": ".github/ISSUE_TEMPLATE/config.yml",
"chars": 476,
"preview": "blank_issues_enabled: false\ncontact_links:\n - name: SDKMAN! usage documentation\n url: https://sdkman.io/usage\n ab"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.md",
"chars": 710,
"preview": "---\nname: Feature request\nabout: Suggest an idea for SDKMAN!\ntitle: 'Feature: [A concise title]'\nlabels: 'enhancement'\na"
},
{
"path": ".github/ISSUE_TEMPLATE/support_request.md",
"chars": 1164,
"preview": "---\nname: Support request or question\nabout: You need help using SDKMAN!\ntitle: \"Question: [Short summary]\"\nlabels: 'sup"
},
{
"path": ".github/auto_assign.yml",
"chars": 108,
"preview": "---\naddReviewers: true\naddAssignees: author\n\nreviewers:\n - marc0der\n - helpermethod\n\nnumberOfReviewers: 0\n"
},
{
"path": ".github/dependabot.yml",
"chars": 111,
"preview": "version: 2\nupdates:\n - package-ecosystem: github-actions\n directory: /\n schedule:\n interval: daily\n"
},
{
"path": ".github/pull_request_template.md",
"chars": 248,
"preview": "<!-- To raise a new Pull Request, the following prerequisites need to be met. Please tick before proceeding: -->\n\n- [ ] "
},
{
"path": ".github/workflows/beta.yml",
"chars": 1590,
"preview": "name: Beta Release\non:\n push:\n branches:\n - master\n paths-ignore:\n - .github/workflows/*\n\njobs:\n pre-r"
},
{
"path": ".github/workflows/chloe-triage.yml",
"chars": 249,
"preview": "name: Notify Chloé (Issue Triage)\n\non:\n issues:\n types: [opened]\n issue_comment:\n types: [created]\n pull_reques"
},
{
"path": ".github/workflows/notify-on-failure.yml",
"chars": 235,
"preview": "name: Notify on Failure\n\non:\n workflow_run:\n workflows: [\"*\"]\n branches: [master, main]\n types: [completed]\n\nj"
},
{
"path": ".github/workflows/pr.yml",
"chars": 673,
"preview": "name: Pull Requests\n#run only on PR, not fork. Pull request that way the PR submitter will not have access to the repo c"
},
{
"path": ".github/workflows/release.yml",
"chars": 1840,
"preview": "name: Release\non:\n workflow_dispatch:\n inputs:\n version:\n description: \"Release version\"\n require"
},
{
"path": ".gitignore",
"chars": 208,
"preview": "*~\n.#*\n.classpath\n.gradle/\n.gradletasknamecache\n.idea/\n.pid.lock\n.project\n.settings/\n.vscode/\n\\#*\nbuild/\nclasses\nsdkman-"
},
{
"path": ".sdkmanrc",
"chars": 113,
"preview": "# Enable auto-env through the sdkman_auto_env config\n# Add key=value pairs of SDKs to use below\njava=11.0.17-tem\n"
},
{
"path": "CLAUDE.md",
"chars": 3507,
"preview": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## "
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 5469,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participa"
},
{
"path": "CONTRIBUTING.md",
"chars": 4297,
"preview": "# Contributing to SDKMAN! CLI\n\nThank you for your interest in contributing to SDKMAN! CLI. We greatly value the feedback"
},
{
"path": "Dockerfile",
"chars": 175,
"preview": "FROM openjdk:11\n\nRUN apt-get update && \\\n apt-get -y install zip\n\n# Copy all here\nRUN mkdir -p /usr/src/app\nADD . /usr/"
},
{
"path": "LICENSE",
"chars": 10174,
"preview": "\n Apache License\n Version 2.0, January 2004\n "
},
{
"path": "README.md",
"chars": 4301,
"preview": "# SDKMAN! CLI\n### The Software Development Kit Manager Command Line Interface\n\n[\n\tid('org.jreleaser').version('1.4.0').apply(false)\n}\n\nString userHome = System.getProperty('user"
},
{
"path": "contrib/completion/bash/sdk",
"chars": 2090,
"preview": "#!/usr/bin/bash\n\n_sdk() {\n\tlocal -r previous_word=${COMP_WORDS[COMP_CWORD - 1]}\n\tlocal -r current_word=${COMP_WORDS[COMP"
},
{
"path": "dco.txt",
"chars": 1421,
"preview": "Developer Certificate of Origin\nVersion 1.1\n\nCopyright (C) 2004, 2006 The Linux Foundation and its contributors.\n1 Lette"
},
{
"path": "gradle/archive.gradle",
"chars": 1035,
"preview": "import org.apache.tools.ant.filters.ReplaceTokens\nimport org.gradle.api.tasks.testing.logging.TestExceptionFormat\n\ndef b"
},
{
"path": "gradle/release.gradle",
"chars": 1147,
"preview": "apply plugin: 'org.jreleaser'\n\njreleaser {\n\tproject {\n\t\tname = 'sdkman-cli'\n\t\tversion = sdkmanVersion\n\t\tdescription = 'S"
},
{
"path": "gradle/wrapper/gradle-wrapper.properties",
"chars": 202,
"preview": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributi"
},
{
"path": "gradlew",
"chars": 5766,
"preview": "#!/usr/bin/env sh\n\n#\n# Copyright 2015 the original author or authors.\n#\n# Licensed under the Apache License, Version 2.0"
},
{
"path": "settings.gradle",
"chars": 128,
"preview": "pluginManagement {\n\trepositories {\n\t\tmavenLocal()\n\t\tgradlePluginPortal()\n\t\tmavenCentral()\n\t}\n}\n\nrootProject.name = \"sdkm"
},
{
"path": "src/jreleaser/changelog.tpl",
"chars": 61,
"preview": "## Changelog\n\n{{changelogChanges}}\n{{changelogContributors}}\n"
},
{
"path": "src/main/bash/sdkman-availability.sh",
"chars": 2485,
"preview": "#!/usr/bin/env bash\n\n#\n# Copyright 2021 Marco Vermeulen\n#\n# Licensed under the Apache License, Version 2.0 (the \"Lic"
},
{
"path": "src/main/bash/sdkman-cache.sh",
"chars": 1024,
"preview": "#!/usr/bin/env bash\n\n#\n# Copyright 2021 Marco Vermeulen\n#\n# Licensed under the Apache License, Version 2.0 (the \"Lic"
},
{
"path": "src/main/bash/sdkman-config.sh",
"chars": 941,
"preview": "#!/usr/bin/env bash\n\n#\n# Copyright 2021 Marco Vermeulen\n#\n# Licensed under the Apache License, Version 2.0 (the \"Lic"
},
{
"path": "src/main/bash/sdkman-current.sh",
"chars": 2139,
"preview": "#!/usr/bin/env bash\n\n#\n# Copyright 2021 Marco Vermeulen\n#\n# Licensed under the Apache License, Version 2.0 (the \"Lic"
},
{
"path": "src/main/bash/sdkman-default.sh",
"chars": 1326,
"preview": "#!/usr/bin/env bash\n\n#\n# Copyright 2021 Marco Vermeulen\n#\n# Licensed under the Apache License, Version 2.0 (the \"Lic"
},
{
"path": "src/main/bash/sdkman-env-helpers.sh",
"chars": 3169,
"preview": "#!/usr/bin/env bash\n\n#\n# Copyright 2021 Marco Vermeulen\n#\n# Licensed under the Apache License, Version 2.0 (the \"Lic"
},
{
"path": "src/main/bash/sdkman-env.sh",
"chars": 4297,
"preview": "#!/usr/bin/env bash\n\n#\n# Copyright 2021 Marco Vermeulen\n#\n# Licensed under the Apache License, Version 2.0 (the \"Lic"
},
{
"path": "src/main/bash/sdkman-flush.sh",
"chars": 1575,
"preview": "#!/usr/bin/env bash\n\n#\n# Copyright 2021 Marco Vermeulen\n#\n# Licensed under the Apache License, Version 2.0 (the \"Lic"
},
{
"path": "src/main/bash/sdkman-help.sh",
"chars": 2688,
"preview": "#!/usr/bin/env bash\n\n#\n# Copyright 2021 Marco Vermeulen\n#\n# Licensed under the Apache License, Version 2.0 (the \"Lic"
},
{
"path": "src/main/bash/sdkman-home.sh",
"chars": 1299,
"preview": "#!/usr/bin/env bash\n\n#\n# Copyright 2021 Marco Vermeulen\n#\n# Licensed under the Apache License, Version 2.0 (the \"Lic"
},
{
"path": "src/main/bash/sdkman-init.sh",
"chars": 5071,
"preview": "#!/usr/bin/env bash\n\n#\n# Copyright 2021 Marco Vermeulen\n#\n# Licensed under the Apache License, Version 2.0 (the \"Lic"
},
{
"path": "src/main/bash/sdkman-install.sh",
"chars": 7635,
"preview": "#!/usr/bin/env bash\n\n#\n# Copyright 2021 Marco Vermeulen\n#\n# Licensed under the Apache License, Version 2.0 (the \"Lic"
},
{
"path": "src/main/bash/sdkman-list.sh",
"chars": 3228,
"preview": "#!/usr/bin/env bash\n\n#\n# Copyright 2021 Marco Vermeulen\n#\n# Licensed under the Apache License, Version 2.0 (the \"Lic"
},
{
"path": "src/main/bash/sdkman-main.sh",
"chars": 3518,
"preview": "#!/usr/bin/env bash\n\n#\n# Copyright 2021 Marco Vermeulen\n#\n# Licensed under the Apache License, Version 2.0 (the \"Lic"
},
{
"path": "src/main/bash/sdkman-offline.sh",
"chars": 920,
"preview": "#!/usr/bin/env bash\n\n#\n# Copyright 2021 Marco Vermeulen\n#\n# Licensed under the Apache License, Version 2.0 (the \"Lic"
},
{
"path": "src/main/bash/sdkman-path-helpers.sh",
"chars": 2464,
"preview": "#!/usr/bin/env bash\n\n#\n# Copyright 2021 Marco Vermeulen\n#\n# Licensed under the Apache License, Version 2.0 (the \"Lic"
},
{
"path": "src/main/bash/sdkman-selfupdate.sh",
"chars": 2349,
"preview": "#!/usr/bin/env bash\n\n#\n# Copyright 2021 Marco Vermeulen\n#\n# Licensed under the Apache License, Version 2.0 (the \"Lic"
},
{
"path": "src/main/bash/sdkman-uninstall.sh",
"chars": 1503,
"preview": "#!/usr/bin/env bash\n\n#\n# Copyright 2021 Marco Vermeulen\n#\n# Licensed under the Apache License, Version 2.0 (the \"Lic"
},
{
"path": "src/main/bash/sdkman-update.sh",
"chars": 2357,
"preview": "#!/usr/bin/env bash\n\n#\n# Copyright 2021 Marco Vermeulen\n#\n# Licensed under the Apache License, Version 2.0 (the \"Lic"
},
{
"path": "src/main/bash/sdkman-upgrade.sh",
"chars": 3443,
"preview": "#!/usr/bin/env bash\n\n#\n# Copyright 2021 Marco Vermeulen\n#\n# Licensed under the Apache License, Version 2.0 (the \"Lic"
},
{
"path": "src/main/bash/sdkman-use.sh",
"chars": 1956,
"preview": "#!/usr/bin/env bash\n\n#\n# Copyright 2021 Marco Vermeulen\n#\n# Licensed under the Apache License, Version 2.0 (the \"Lic"
},
{
"path": "src/main/bash/sdkman-utils.sh",
"chars": 3040,
"preview": "#!/usr/bin/env bash\n\n#\n# Copyright 2021 Marco Vermeulen\n#\n# Licensed under the Apache License, Version 2.0 (the \"Lic"
},
{
"path": "src/main/bash/sdkman-version.sh",
"chars": 791,
"preview": "#!/usr/bin/env bash\n\n#\n# Copyright 2021 Marco Vermeulen\n#\n# Licensed under the Apache License, Version 2.0 (the \"Lic"
},
{
"path": "src/test/groovy/sdkman/cucumber/RunCukeTests.groovy",
"chars": 324,
"preview": "package sdkman.cucumber\n\nimport io.cucumber.junit.Cucumber\nimport io.cucumber.junit.CucumberOptions\nimport org.junit.run"
},
{
"path": "src/test/groovy/sdkman/env/BashEnv.groovy",
"chars": 4421,
"preview": "package sdkman.env\n\nimport groovy.transform.ToString\n\n/**\n * <p>As part of the sdkman test suite we need to launch a bas"
},
{
"path": "src/test/groovy/sdkman/env/CleanBashEnvBuilder.groovy",
"chars": 544,
"preview": "package sdkman.env\n\nclass CleanBashEnvBuilder {\n\n\tprivate final File baseFolder\n\tString httpProxy\n\n\tstatic CleanBashEnvB"
},
{
"path": "src/test/groovy/sdkman/env/SdkmanBashEnvBuilder.groovy",
"chars": 6524,
"preview": "package sdkman.env\n\nimport groovy.transform.ToString\nimport sdkman.stubs.CurlStub\nimport sdkman.stubs.UnameStub\nimport s"
},
{
"path": "src/test/groovy/sdkman/specs/CandidatesCacheUpdateFailureSpec.groovy",
"chars": 1014,
"preview": "package sdkman.specs\n\nimport sdkman.support.SdkmanEnvSpecification\n\nclass CandidatesCacheUpdateFailureSpec extends Sdkma"
},
{
"path": "src/test/groovy/sdkman/specs/CandidatesCacheUpdateSpec.groovy",
"chars": 1796,
"preview": "package sdkman.specs\n\nimport sdkman.support.SdkmanEnvSpecification\n\nclass CandidatesCacheUpdateSpec extends SdkmanEnvSpe"
},
{
"path": "src/test/groovy/sdkman/specs/CompletionSpec.groovy",
"chars": 1709,
"preview": "package sdkman.specs\n\nimport sdkman.support.SdkmanEnvSpecification\n\nclass CompletionSpec extends SdkmanEnvSpecification "
},
{
"path": "src/test/groovy/sdkman/specs/ConfigCommandSpec.groovy",
"chars": 1266,
"preview": "package sdkman.specs\n\nimport sdkman.support.SdkmanEnvSpecification\n\nclass ConfigCommandSpec extends SdkmanEnvSpecificati"
},
{
"path": "src/test/groovy/sdkman/specs/CurrentCommandSpec.groovy",
"chars": 1798,
"preview": "package sdkman.specs\n\nimport sdkman.support.SdkmanEnvSpecification\n\nimport java.nio.file.Paths\n\nimport static java.nio.f"
},
{
"path": "src/test/groovy/sdkman/specs/EnvCommandSpec.groovy",
"chars": 8668,
"preview": "package sdkman.specs\n\nimport sdkman.support.SdkmanEnvSpecification\n\nimport java.nio.file.Paths\n\nimport static java.nio.f"
},
{
"path": "src/test/groovy/sdkman/specs/InitialisationSpec.groovy",
"chars": 3041,
"preview": "package sdkman.specs\n\nimport sdkman.support.SdkmanEnvSpecification\n\nimport java.nio.file.Files\nimport java.nio.file.Path"
},
{
"path": "src/test/groovy/sdkman/specs/SdkCompatibilitySpec.groovy",
"chars": 1973,
"preview": "package sdkman.specs\n\nimport sdkman.support.SdkmanEnvSpecification\n\nimport java.nio.file.Files\nimport java.nio.file.Path"
},
{
"path": "src/test/groovy/sdkman/specs/SelfupdateFeatureSpec.groovy",
"chars": 1633,
"preview": "package sdkman.specs\n\nimport sdkman.support.SdkmanEnvSpecification\n\nimport java.time.Instant\n\nimport static java.time.te"
},
{
"path": "src/test/groovy/sdkman/steps/command_line_interop_steps.groovy",
"chars": 1330,
"preview": "package sdkman.steps\n\nimport static cucumber.api.groovy.EN.And\n\nAnd(~'^I enter \\\"([^\\\"]*)\\\"$') { String command ->\n\tbash"
},
{
"path": "src/test/groovy/sdkman/steps/env.groovy",
"chars": 1835,
"preview": "package sdkman.steps\n\nimport com.github.tomakehurst.wiremock.client.WireMock\nimport sdkman.support.FilesystemUtils\nimpor"
},
{
"path": "src/test/groovy/sdkman/steps/env_steps.groovy",
"chars": 237,
"preview": "package sdkman.steps\n\nimport static cucumber.api.groovy.EN.And\n\nAnd(~/^the file \"([^\"]+)\" exists and contains \"([^\"]+)\"$"
},
{
"path": "src/test/groovy/sdkman/steps/flush_steps.groovy",
"chars": 1180,
"preview": "package sdkman.steps\n\nimport static cucumber.api.groovy.EN.And\n\nAnd(~'^the candidate \"([^\"]*)\" is known locally$') { Str"
},
{
"path": "src/test/groovy/sdkman/steps/initialisation_steps.groovy",
"chars": 4038,
"preview": "package sdkman.steps\n\nimport sdkman.env.SdkmanBashEnvBuilder\nimport sdkman.stubs.UnameStub\n\nimport java.util.zip.ZipExce"
},
{
"path": "src/test/groovy/sdkman/steps/installation_steps.groovy",
"chars": 4615,
"preview": "package sdkman.steps\n\nimport java.nio.file.FileSystems\nimport java.nio.file.Files\nimport java.nio.file.Path\nimport java."
},
{
"path": "src/test/groovy/sdkman/steps/stub_steps.groovy",
"chars": 5841,
"preview": "package sdkman.steps\n\nimport io.cucumber.datatable.DataTable\nimport sdkman.support.UnixUtils\n\nimport static cucumber.api"
},
{
"path": "src/test/groovy/sdkman/steps/update_steps.groovy",
"chars": 379,
"preview": "package sdkman.steps\n\nimport io.cucumber.datatable.DataTable\n\nimport static cucumber.api.groovy.EN.And\n\nAnd(~/^the follo"
},
{
"path": "src/test/groovy/sdkman/steps/use_steps.groovy",
"chars": 1974,
"preview": "package sdkman.steps\n\nimport java.nio.file.FileSystems\nimport java.nio.file.Files\n\nimport static cucumber.api.groovy.EN."
},
{
"path": "src/test/groovy/sdkman/stubs/CurlStub.groovy",
"chars": 640,
"preview": "package sdkman.stubs\n\nclass CurlStub {\n\n\tprivate File file\n\tprivate commands = [:]\n\n\tstatic CurlStub prepareIn(File fold"
},
{
"path": "src/test/groovy/sdkman/stubs/HookResponses.groovy",
"chars": 418,
"preview": "package sdkman.stubs\n\nclass HookResponses {\n\t\n\tstatic postInstallationHookSuccess() {\n\t\t'''\\\n#!/usr/bin/env bash\nfunctio"
},
{
"path": "src/test/groovy/sdkman/stubs/UnameStub.groovy",
"chars": 681,
"preview": "package sdkman.stubs\n\nclass UnameStub {\n\n\tprivate File file\n\tdef kernel = \"Linux\"\n\tdef machine = \"x86_64\"\n\n\tstatic Uname"
},
{
"path": "src/test/groovy/sdkman/stubs/WebServiceStub.groovy",
"chars": 2088,
"preview": "package sdkman.stubs\n\nimport static com.github.tomakehurst.wiremock.client.WireMock.*\n\nclass WebServiceStub {\n\n\tstatic p"
},
{
"path": "src/test/groovy/sdkman/support/BashEnvSpecification.groovy",
"chars": 218,
"preview": "package sdkman.support\n\nimport sdkman.env.BashEnv\nimport spock.lang.Specification\n\nabstract class BashEnvSpecification e"
},
{
"path": "src/test/groovy/sdkman/support/FilesystemUtils.groovy",
"chars": 1691,
"preview": "package sdkman.support\n\nimport java.nio.file.FileSystems\nimport java.nio.file.Files\nimport java.nio.file.Path\nimport jav"
},
{
"path": "src/test/groovy/sdkman/support/SdkmanEnvSpecification.groovy",
"chars": 1039,
"preview": "package sdkman.support\n\nimport sdkman.env.SdkmanBashEnvBuilder\nimport sdkman.stubs.CurlStub\nimport sdkman.stubs.UnameStu"
},
{
"path": "src/test/groovy/sdkman/support/UnixUtils.groovy",
"chars": 546,
"preview": "package sdkman.support\n\nclass UnixUtils {\n\n\tprivate static platforms = [\n\t\t\t\"Linux\" : [\n\t\t\t\t\t\"x86_64\": \"linuxx64\",\n\t\t\t"
},
{
"path": "src/test/groovy/sdkman/support/WireMockServerProvider.groovy",
"chars": 669,
"preview": "package sdkman.support\n\nimport com.github.tomakehurst.wiremock.WireMockServer\nimport com.github.tomakehurst.wiremock.cli"
},
{
"path": "src/test/jmeter/SDKman.jmx",
"chars": 27313,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<jmeterTestPlan version=\"1.2\" properties=\"2.5\" jmeter=\"2.10 r1533061\">\n <hashTre"
},
{
"path": "src/test/jmeter/candidates.csv",
"chars": 112,
"preview": "gaiden,0.3\ngradle,1.8\ngrails,2.3.1\ngriffon,1.4.0\ngroovy,2.1.8\ngroovyserv,0.13\nlazybones,0.4\nspringboot,0.5.0.M5\n"
},
{
"path": "src/test/resources/__files/hooks/post_hook_java_8.0.101_linuxx64.sh",
"chars": 254,
"preview": "#!/usr/bin/env bash\n# failed download with non-zero exit code\nfunction __sdkman_post_installation_hook() {\n\techo \"POST: "
},
{
"path": "src/test/resources/__files/hooks/post_hook_java_8.0.111_linuxx64.sh",
"chars": 308,
"preview": "#!/usr/bin/env bash\n# convert tar.gz to zip\nfunction __sdkman_post_installation_hook() {\n\techo \"POST: converting $binary"
},
{
"path": "src/test/resources/__files/hooks/post_hook_java_8.0.111_universal.sh",
"chars": 195,
"preview": "#!/usr/bin/env bash\n# passthrough used for zip binaries\nfunction __sdkman_post_installation_hook() {\n\techo \"POST: passth"
},
{
"path": "src/test/resources/features/checksum_verification.feature",
"chars": 4374,
"preview": "Feature: Verify checksums\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\t\tAnd I have "
},
{
"path": "src/test/resources/features/command_line_interop.feature",
"chars": 696,
"preview": "Feature: Command Line Interop\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\t\tAnd the"
},
{
"path": "src/test/resources/features/current_candidate.feature",
"chars": 1565,
"preview": "Feature: Current Candidate\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\n\tScenario: "
},
{
"path": "src/test/resources/features/default_version.feature",
"chars": 1592,
"preview": "Feature: Default Version\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\n\tScenario: De"
},
{
"path": "src/test/resources/features/flush.feature",
"chars": 1295,
"preview": "Feature: Flush\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\t\tAnd the system is boot"
},
{
"path": "src/test/resources/features/home.feature",
"chars": 1974,
"preview": "Feature: Print home path\n\n* Print home directory\n* Printing the home directory does not require reaching out to the inte"
},
{
"path": "src/test/resources/features/hooks.feature",
"chars": 1164,
"preview": "Feature: Hooks\n\n\tWe can safely remove this feature when `.tar.gz` and `.zip` are supported directly by the backend.\n\t\n\tB"
},
{
"path": "src/test/resources/features/install_candidate.feature",
"chars": 4611,
"preview": "Feature: Install Candidate\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\n\tScenario: "
},
{
"path": "src/test/resources/features/install_sdkman.feature",
"chars": 3328,
"preview": "@manual\nFeature: Install SDKMAN\n\n\tPlatform defaults as follows:\n\t* Ubuntu: .profile, .bashrc\n\t* Fedora: .bash_profile,"
},
{
"path": "src/test/resources/features/java_installation.feature",
"chars": 3504,
"preview": "Feature: Java Multi Platform Binary Distribution\n\n\tThree versions of Java are used to test various installation scenario"
},
{
"path": "src/test/resources/features/list_candidate_versions.feature",
"chars": 2507,
"preview": "Feature: List Candidate Versions\n\n\tA dummy template to be served back that has the following information:\n\t* Candidate: "
},
{
"path": "src/test/resources/features/list_candidates.feature",
"chars": 292,
"preview": "Feature: List Candidates\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\n\tScenario: A "
},
{
"path": "src/test/resources/features/local_developement_versions.feature",
"chars": 5352,
"preview": "Feature: Local Development Versions\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\n\tS"
},
{
"path": "src/test/resources/features/mnemonics.feature",
"chars": 3881,
"preview": "Feature: Mnemonics\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\n\tScenario: Shortcut"
},
{
"path": "src/test/resources/features/offline_mode.feature",
"chars": 4588,
"preview": "Feature: Offline Mode\n\n\t# offline modes\n\n\tScenario: Enter an invalid offline mode\n\t\tGiven offline mode is disabled with "
},
{
"path": "src/test/resources/features/path_initialisation.feature",
"chars": 1507,
"preview": "Feature: Path Initialisation\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\n\tScenario"
},
{
"path": "src/test/resources/features/per_project_configuration.feature",
"chars": 3110,
"preview": "Feature: Per-project configuration\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\n\tSc"
},
{
"path": "src/test/resources/features/self_update.feature",
"chars": 1357,
"preview": "Feature: Self Update\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd the sdkman scripts version is \"5.0.0\"\n\t\tAnd t"
},
{
"path": "src/test/resources/features/service_unavailable.feature",
"chars": 4775,
"preview": "Feature: Service Unavailable\n\n\tBackground:\n\t\tGiven the internet is not reachable\n\t\tAnd an initialised environment\n\n\t# li"
},
{
"path": "src/test/resources/features/uninstall_candidate.feature",
"chars": 1688,
"preview": "Feature: Uninstall Candidate\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\n\tScenario"
},
{
"path": "src/test/resources/features/update.feature",
"chars": 1672,
"preview": "Feature: Update\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd the following candidates are available for install"
},
{
"path": "src/test/resources/features/upgrade_candidate.feature",
"chars": 6430,
"preview": "Feature: Upgrade Candidate\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd the candidates cache is initialised wit"
},
{
"path": "src/test/resources/features/use_version.feature",
"chars": 2165,
"preview": "Feature: Use Version\n\n\tBackground:\n\t\tGiven the internet is reachable\n\t\tAnd an initialised environment\n\n\tScenario: Use wi"
},
{
"path": "src/test/resources/features/version.feature",
"chars": 265,
"preview": "Feature: Version\n\n\tScenario: Show the current version of sdkman\n\t\tGiven the internet is reachable\n\t\tAnd the sdkman scrip"
}
]
// ... and 1 more files (download for full content)
About this extraction
This page contains the full source code of the sdkman/sdkman-cli GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 117 files (268.7 KB), approximately 76.5k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.