Showing preview only (1,247K chars total). Download the full file or copy to clipboard to get everything.
Repository: Jabba-Team/jabba
Branch: main
Commit: b045bf217b9e
Files: 48
Total size: 1.2 MB
Directory structure:
gitextract_ws_eknom/
├── .editorconfig
├── .gitattributes
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ └── bug_report.md
│ └── workflows/
│ └── build.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── Makefile
├── README.md
├── cfg/
│ └── cfg.go
├── command/
│ ├── alias.go
│ ├── check.go
│ ├── check_test.go
│ ├── current.go
│ ├── current_test.go
│ ├── deactivate.go
│ ├── deactivate_test.go
│ ├── fileiter/
│ │ ├── iterator.go
│ │ └── iterator_test.go
│ ├── init.go
│ ├── init_test.go
│ ├── install.go
│ ├── install_test.go
│ ├── link.go
│ ├── ls-alias.go
│ ├── ls-remote.go
│ ├── ls.go
│ ├── uninstall.go
│ ├── use.go
│ ├── use_test.go
│ └── which.go
├── go.mod
├── go.sum
├── index.json
├── install.ps1
├── install.sh
├── jabba.go
├── jabbaw
├── jabbaw.md
├── jabbaw.ps1
├── semver/
│ ├── range.go
│ ├── range_test.go
│ ├── version.go
│ └── version_test.go
└── w32/
├── w32.go
├── w32_darwin.go
├── w32_linux.go
└── w32_windows.go
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
root = true
[*]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.go]
indent_style = tab
indent_size = 4
[Makefile]
indent_style = tab
indent_size = 4
[*.md]
trim_trailing_whitespace = false
================================================
FILE: .gitattributes
================================================
* text=auto
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. MacOS, Linux, Windows]
- CPU Architecture: [e.g. x86_64, arm64]
- Terminal [e.g. iTerm2, kitty, Windows Terminal]
- Jabba Version [e.g. 0.14.0]
- Install Method [e.g. Script, brew, scoop]
**Additional context**
Add any other context about the problem here.
================================================
FILE: .github/workflows/build.yml
================================================
name: Build and Test
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
test:
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.26'
- name: Run tests
run: make test
- name: Build
run: make build
- name: Verify binary
shell: bash
run: |
if [[ "${{ matrix.os }}" == "windows-latest" ]]; then
./jabba.exe --version
else
./jabba --version
fi
build-release:
runs-on: ubuntu-latest
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.26'
- name: Build release binaries
run: make build-release
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: release-binaries
path: release/*
================================================
FILE: .gitignore
================================================
jabba
coverage.html
release
vendor
.tmp
================================================
FILE: CHANGELOG.md
================================================
# Changelog
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## [UNRELEASED] - 2021-11-20
### Changed
- Old default Java version in README.md
- Update dependencies in go.mod and go.sum
### Added
- Homebrew package is broken note in README.md
## [0.11.2](https://github.com/shyiko/jabba/compare/0.11.1...0.11.2) - 2019-01-06
- Oracle JDK installation on macOS ([#350](https://github.com/shyiko/jabba/issues/350)).
## [0.11.1](https://github.com/shyiko/jabba/compare/0.11.0...0.11.1) - 2018-12-18
### Fixed
- Oracle JDK installation on macOS ([#350](https://github.com/shyiko/jabba/issues/350)).
- Broken symlinks (`tar.*`) ([#356](https://github.com/shyiko/jabba/issues/356)).
## [0.11.0](https://github.com/shyiko/jabba/compare/0.10.1...0.11.0) - 2018-10-11
### Added
- Flags to filter remote versions by `--os` and `--arch` (e.g. `jabba ls-remote --os=windows`) ([#240](https://github.com/shyiko/jabba/issues/240))
### Fixed
- File tree normalization
(Adopt OpenJDK 11 distributions contain meta file at the root level causing untgz --strip to fail) ([#311](https://github.com/shyiko/jabba/issues/311)).
## [0.10.1](https://github.com/shyiko/jabba/compare/0.10.0...0.10.1) - 2018-05-07
### Fixed
- `jabba install <semver>` not checking whether JDK is already installed.
## [0.10.0](https://github.com/shyiko/jabba/compare/0.9.6...0.10.0) - 2018-05-06
- [OpenJDK with Shenandoah GC](https://wiki.openjdk.java.net/display/shenandoah/Main) support ([#191](https://github.com/shyiko/jabba/issues/191))
(e.g. `jabba install openjdk-shenandoah@1.9`).
- Ability to install JDK from `tar.xz` archives
(e.g. `jabba install openjdk-shenandoah@1.9.0-220=tgx+file://$PWD/local-copy-of-openjdk-shenandoah-jdk9-b220-x86-release.tar.xz`).
## [0.9.6](https://github.com/shyiko/jabba/compare/0.9.5...0.9.6) - 2018-05-05
### Fixed
- Sporadic "open: permission denied" when installing from tgz/zip's ([#190](https://github.com/shyiko/jabba/issues/190)).
Fix applied in 0.9.5 proved to be incomplete.
## [0.9.5](https://github.com/shyiko/jabba/compare/0.9.4...0.9.5) - 2018-05-04
### Fixed
- Sporadic "open: permission denied" when installing from tgz/zip's ([#190](https://github.com/shyiko/jabba/issues/190)).
## [0.9.4](https://github.com/shyiko/jabba/compare/0.9.3...0.9.4) - 2018-05-01
### Fixed
- Installation from sources containing `Contents/Home` (macOS) (regression introduced in 0.9.3).
## [0.9.3](https://github.com/shyiko/jabba/compare/0.9.2...0.9.3) - 2018-04-30
### Fixed
- `Contents/Home` handling (macOS) ([#187](https://github.com/shyiko/jabba/issues/187)).
## [0.9.2](https://github.com/shyiko/jabba/compare/0.9.1...0.9.2) - 2017-11-18
### Fixed
- `zip` & `tgz` stripping on Windows ([#116](https://github.com/shyiko/jabba/issues/116)).
## [0.9.1](https://github.com/shyiko/jabba/compare/0.9.0...0.9.1) - 2017-10-12
### Fixed
- `tgz is not supported` when trying to install JDK from `tar.gz` on macOS & Windows.
## [0.9.0](https://github.com/shyiko/jabba/compare/0.8.0...0.9.0) - 2017-09-19
### Added
- Latest JDK / `default` alias (automatic) linking ([#6](https://github.com/shyiko/jabba/issues/6))
```sh
$ ll ~/.jabba/jdk/
lrwxrwxrwx 1 shyiko shyiko 30 Sep 19 2017 1.8 -> /home/shyiko/.jabba/jdk/1.8.144/
drwxr-xr-x 8 shyiko shyiko 4096 Sep 19 2017 1.8.144/
drwxr-xr-x 8 shyiko shyiko 4096 Sep 19 2017 1.8.141/
lrwxrwxrwx 8 shyiko shyiko 30 Sep 19 2017 zulu@1.6 -> /home/shyiko/.jabba/jdk/zulu@1.6.97/
drwxr-xr-x 8 shyiko shyiko 4096 Sep 19 2017 zulu@1.6.97/
lrwxrwxrwx 1 shyiko shyiko 30 Sep 19 2017 default -> /home/shyiko/.jabba/jdk/1.8.144/
```
## [0.8.0](https://github.com/shyiko/jabba/compare/0.7.0...0.8.0) - 2017-09-19
### Added
- [Adopt OpenJDK](https://adoptopenjdk.net/) support.
- `jabba ls <semver_range>` & `jabba ls-remote <semver_range>`.
- `jabba ls --latest=<major|minor|patch>` & `jabba ls-remote --latest=<major|minor|patch>`.
```sh
$ jabba ls-remote "zulu@<1.9" --latest=minor
zulu@1.8.144
zulu@1.7.154
zulu@1.6.97
```
- Ability to install JDK in a custom location (`jabba install -o /jdk/destination`)
NOTE: any JDK installed in this way is considered to be unmanaged, i.e. not available to `jabba ls`, `jabba use`, etc. (unless `jabba link`ed).
### Changed
- semver library to [masterminds/semver](https://github.com/Masterminds/semver)
(previously used library proved unreliable when given certain input (e.g. `>=1.6`)).
## [0.7.0](https://github.com/shyiko/jabba/compare/0.6.1...0.7.0) - 2017-05-12
### Added
* Ability to change the location of `~/.jabba` with `JABBA_HOME` env variable (e.g.
`curl -sL https://github.com/shyiko/jabba/raw/master/install.sh | JABBA_HOME=/opt/jabba bash && . /opt/jabba/jabba.sh`)
* `--home` flag for `jabba which` (`jabba which --home <jdk_version>` returns `$JABBA_DIR/jdk/<jdk_version>/Contents/Home` on macOS and
`$JABBA_DIR/jdk/<jdk_version>` everywhere else)
### Changed
* `~` directory referencing inside shell integration scripts (path to home directory is now determined by `$PATH`).
### Fixed
* `jabba deactivate` escaping.
* `JAVA_HOME` propagation in Fish shell on CentOS 7.
## [0.6.1](https://github.com/shyiko/jabba/compare/0.6.0...0.6.1) - 2017-02-27
### Fixed
* `x509: certificate signed by unknown authority` while executing `jabba ls-remote` (macOS) ([#56](https://github.com/shyiko/jabba/issues/56)).
## [0.6.0](https://github.com/shyiko/jabba/compare/0.5.1...0.6.0) - 2016-12-10
### Added
* IBM SDK, Java Technology Edition support (e.g. `jabba install ibm@<version>`).
## [0.5.1](https://github.com/shyiko/jabba/compare/0.5.0...0.5.1) - 2016-11-28
### Fixed
* `link` command (it was missing `mkdir -p ...` call).
## [0.5.0](https://github.com/shyiko/jabba/compare/0.4.0...0.5.0) - 2016-11-11
### Added
* `.jabbarc` support.
## [0.4.0](https://github.com/shyiko/jabba/compare/0.3.3...0.4.0) - 2016-07-22
### Added
* Windows 10 support.
* `link`/`unlink` commands.
### Fixed
* `bin+file://` handling (original file is now copied instead of being moved).
## [0.3.3](https://github.com/shyiko/jabba/compare/0.3.2...0.3.3) - 2016-03-30
### Fixed
* `dmg` handling when GNU tar is installed.
## [0.3.2](https://github.com/shyiko/jabba/compare/0.3.1...0.3.2) - 2016-03-26
### Fixed
* Zulu OpenJDK installation.
## [0.3.1](https://github.com/shyiko/jabba/compare/0.3.0...0.3.1) - 2016-03-26
### Fixed
* `current` (previously output was written to stderr instead of stdout).
## [0.3.0](https://github.com/shyiko/jabba/compare/0.2.0...0.3.0) - 2016-03-26
### Added
* Zulu OpenJDK support (e.g. `jabba install zulu@<version>`).
* Ability to install JDK from `zip` archives (in addition to already implemented `dmg`/`tar.gz`/`bin`).
* Support for custom registries (e.g. `JABBA_INDEX=https://github.com/shyiko/jabba/raw/master/index.json jabba install ...`).
### Fixed
* `which <alias>`.
## [0.2.0](https://github.com/shyiko/jabba/compare/0.1.0...0.2.0) - 2016-03-24
### Added
* `alias default`/`unalias default`, `which`, `deactivate` commands.
## 0.1.0 - 2016-03-23
================================================
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
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.
================================================
FILE: Makefile
================================================
SHELL := /bin/bash -o pipefail
VERSION := $(shell git describe --tags --abbrev=0)
fetch:
go install github.com/modocache/gover@latest
go install github.com/github-release/github-release@latest
clean:
rm -f ./jabba
rm -rf ./build
fmt:
gofmt -l -s -w `find . -type f -name '*.go' -not -path "./vendor/*"`
test:
go vet `go list ./... | grep -v /vendor/`
SRC=`find . -type f -name '*.go' -not -path "./vendor/*"` && gofmt -l -s $$SRC | read && gofmt -l -s -d $$SRC && exit 1 || true
go test `go list ./... | grep -v /vendor/`
test-coverage:
go list ./... | grep -v /vendor/ | xargs -L1 -I{} sh -c 'go test -coverprofile `basename {}`.coverprofile {}' && \
gover && \
go tool cover -html=gover.coverprofile -o coverage.html && \
rm *.coverprofile
build:
CGO_ENABLED=0 go build -ldflags "-s -w -X main.version=${VERSION}"
build-release:
mkdir -p release
@echo "Building for multiple platforms..."
GOOS=windows GOARCH=386 CGO_ENABLED=0 go build -ldflags "-s -w -X main.version=${VERSION}" -o release/jabba-${VERSION}-windows-386.exe
GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -ldflags "-s -w -X main.version=${VERSION}" -o release/jabba-${VERSION}-windows-amd64.exe
GOOS=linux GOARCH=386 CGO_ENABLED=0 go build -ldflags "-s -w -X main.version=${VERSION}" -o release/jabba-${VERSION}-linux-386
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags "-s -w -X main.version=${VERSION}" -o release/jabba-${VERSION}-linux-amd64
GOOS=darwin GOARCH=amd64 CGO_ENABLED=0 go build -ldflags "-s -w -X main.version=${VERSION}" -o release/jabba-${VERSION}-darwin-amd64
GOOS=darwin GOARCH=arm64 CGO_ENABLED=0 go build -ldflags "-s -w -X main.version=${VERSION}" -o release/jabba-${VERSION}-darwin-arm64
GOOS=linux GOARCH=arm CGO_ENABLED=0 go build -ldflags "-s -w -X main.version=${VERSION}" -o release/jabba-${VERSION}-linux-arm
GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -ldflags "-s -w -X main.version=${VERSION}" -o release/jabba-${VERSION}-linux-arm64
@echo "Build complete!"
@ls -lh release/
install: build
JABBA_MAKE_INSTALL=true JABBA_VERSION=${VERSION} bash install.sh
publish: clean build-release
test -n "$(GITHUB_TOKEN)" # $$GITHUB_TOKEN must be set
github-release release --user Jabba-Team --repo jabba --tag ${VERSION} \
--name "${VERSION}" --description "${VERSION}" && \
for file in release/*; do \
filename=$$(basename $$file); \
github-release upload --user Jabba-Team --repo jabba --tag ${VERSION} \
--name "$$filename" --file "$$file"; \
done
================================================
FILE: README.md
================================================
# jabba  [](https://github.com/Jabba-Team/jabba/actions)
Java Version Manager inspired by [nvm](https://github.com/creationix/nvm) (Node.js). Written in Go.
The goal is to provide unified pain-free experience of **installing** (and **switching** between different versions of) JDK regardless of
the OS (macOS, Linux x86/x86_64/ARMv7+, Windows x86_64).
This is a community fork of the [original project](https://github.com/shyiko/jabba) and is currently a work in progress. There may be some out of date instructions in the README, please bear with us while we get things going.
`jabba install`
The JDK index is powered by [DiscoAPI](https://github.com/foojayio/discoapi) as such some of the JDK's may be named slightly differently from the old index
The index will give you the list of jdks for your OS and architecture
- [Oracle JDK](http://www.oracle.com/technetwork/java/javase/archive-139210.html) (latest-version only)
- [Oracle Server JRE](http://www.oracle.com/technetwork/java/javase/downloads/server-jre8-downloads-2133154.html) (latest-version only),
- [Adopt OpenJDK](https://adoptopenjdk.net/) <sup>(jabba >=0.8.0 is required)</sup>
- Hotspot
- [Eclipse OpenJ9](https://www.eclipse.org/openj9/oj9_faq.html)
- [Zulu OpenJDK](http://zulu.org/) <sup>(jabba >=0.3.0 is required)</sup>
- [IBM SDK, Java Technology Edition](https://developer.ibm.com/javasdk/) <sup>(jabba >=0.6.0 is required)</sup>
- [GraalVM CE](https://www.graalvm.org/)
- [OpenJDK](http://openjdk.java.net/)
- [OpenJDK Reference Implementation](http://openjdk.java.net/)
- [OpenJDK with Shenandoah GC](https://wiki.openjdk.java.net/display/shenandoah/Main) <sup>(jabba >=0.10.0 is required)
- [Liberica JDK](https://bell-sw.com/)
- [Amazon Corretto](https://aws.amazon.com/corretto/)
</sup>
... and from custom URLs.
## Install-less
See [jabba-wrapper](jabbaw.md)
## Installation
#### macOS homebrew
```sh
brew install jabba
```
Add the following to your .zshrc
```sh
[ -s "$HOMEBREW_PREFIX/opt/jabba/jabba.sh" ] && . "$HOMEBREW_PREFIX/opt/jabba/jabba.sh"
```
If `HOMEBREW_PREFIX` isn't already defined for you run:
```sh
brew --prefix
```
to get the value
#### macOS / Linux
> (in bash/zsh/...)
```sh
export JABBA_VERSION=...
curl -sL https://github.com/Jabba-Team/jabba/raw/main/install.sh | bash && . ~/.jabba/jabba.sh
```
> Use the same command to upgrade, you can also upgrade from shyiko's 0.11.2 by running this command
The script modifies common shell rc files by default. To skip these provide the `--skip-rc` flag to `install.sh` like so:
```sh
export JABBA_VERSION=...
curl -sL https://github.com/Jabba-Team/jabba/raw/main/install.sh | bash -s -- --skip-rc && . ~/.jabba/jabba.sh
```
Make sure to source `jabba.sh` in your environment if you skip it:
```sh
export JABBA_VERSION=...
[ -s "$JABBA_HOME/jabba.sh" ] && source "$JABBA_HOME/jabba.sh"
```
> In [fish](https://fishshell.com/) command looks a little bit different -
> export JABBA_VERSION=...
`curl -sL https://github.com/Jabba-Team/jabba/raw/main/install.sh | bash; and . ~/.jabba/jabba.fish`
> If you don't have `curl` installed - replace `curl -sL` with `wget -qO-`.
> If you are behind a proxy see -
[curl](https://curl.haxx.se/docs/manpage.html#ENVIRONMENT) /
[wget](https://www.gnu.org/software/wget/manual/wget.html#Proxies) manpage.
Usually simple `http_proxy=http://proxy-server:port https_proxy=http://proxy-server:port curl -sL ...` is enough.
#### Docker
While you can use the same snippet as above, chances are you don't want jabba binary & shell
integration script(s) to be included in the final Docker image, all you want is a JDK. Here is the `Dockerfile` showing how this can be done:
```dockerfile
FROM buildpack-deps:jessie-curl
RUN curl -sL https://github.com/Jabba-Team/jabba/raw/main/install.sh | \
JABBA_COMMAND="install 1.15.0 -o /jdk" bash
ENV JAVA_HOME /jdk
ENV PATH $JAVA_HOME/bin:$PATH
```
> (when `JABBA_COMMAND` env variable is set `install.sh` downloads `jabba` binary, executes specified command and then deletes the binary)
```sh
$ docker build -t <image_name>:<image_tag> .
$ docker run -it --rm <image_name>:<image_tag> java -version
java version "1.15.0....
```
#### Windows 10
> (in powershell)
```powershell
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Invoke-Expression (
Invoke-WebRequest https://github.com/Jabba-Team/jabba/raw/main/install.ps1 -UseBasicParsing
).Content
```
> Use the same command to upgrade, you can also upgrade from shyiko's 0.11.2 by running this command
> Scoop
Whilst jabba is listed in the Scoop package manager, scoop only install the binary, it doesn't add the shell integration, it is recommended you install from the script above or try adding the shell integration manually while we investigate the proper fix, see the (issue here)[https://github.com/Jabba-Team/jabba/issues/48] for details.
## Usage
```sh
# list available JDK's
jabba ls-remote
# you can use any valid semver range to narrow down the list
jabba ls-remote zulu@~1.8.60
jabba ls-remote "*@>=1.6.45 <1.9" --latest=minor
# install Oracle JDK
jabba install 1.15.0
# install Oracle Server JRE
jabba install sjre@1.8
# install Eclipse Temurin / Adoptium (Hotspot)
jabba install temurin@1.8-0
# install Adopt OpenJDK (Hotspot)
jabba install aoj@1.8-0
# install Adopt OpenJDK (Eclipse OpenJ9)
jabba install aoj_openj9@1.9-0
# install Zulu OpenJDK
jabba install zulu@1.8
jabba install zulu@~1.8.144 # same as "zulu@>=1.8.144 <1.9"
# install IBM SDK, Java Technology Edition
jabba install semeru@1.8
# install GraalVM CE
jabba install graalvm@1.0-0
# install Oracle OpenJDK
jabba install oracle_open_jdk@1.10-0
# install Microsoft OpenJDK
jabba install microsoft@11.0.16
# install OpenJDK with Shenandoah GC
jabba install openjdk-shenandoah@1.10-0
# install from custom URL
# (supported qualifiers: zip (since 0.3.0), tgz, tgx (since 0.10.0), dmg, bin, exe)
jabba install 1.8.0-custom=tgz+http://example.com/distribution.tar.gz
jabba install 1.8.0-custom=tgx+http://example.com/distribution.tar.xz
jabba install 1.8.0-custom=zip+file:///opt/distribution.zip
# uninstall JDK
jabba uninstall zulu@1.6.77
# link system JDK
jabba link system@1.8.72 /Library/Java/JavaVirtualMachines/jdk1.8.0_72.jdk
# list all installed JDK's
jabba ls
# switch to a different version of JDK (it must be already `install`ed)
jabba use aoj@1.8
jabba use zulu@~1.6.97
echo "1.8" > .jabbarc
# switch to the JDK specified in .jabbarc (since 0.5.0)
jabba use
# set default java version on shell (since 0.2.0)
# this version will automatically be "jabba use"d every time you open up a new terminal
jabba alias default 1.8
```
> `.jabbarc` has to be a valid YAML file. JDK version can be specified as `jdk: 1.8` or simply as `1.8`
(same as `~1.8`, `1.8.x` `">=1.8.0 <1.9.0"` (mind the quotes)).
> jsyk: **jabba** keeps everything under `~/.jabba` (on Linux/Mac OS X) / `%USERPROFILE%/.jabba` (on Windows). If at any point of time you decide to uninstall **jabba** - just remove this directory.
For more information see `jabba --help`.
## Development
> PREREQUISITE: [go1.8](https://github.com/moovweb/gvm)
```sh
git clone https://github.com/Jabba-Team/jabba $GOPATH/src/github.com/Jabba-Team/jabba
cd $GOPATH/src/github.com/Jabba-Team/jabba
make fetch
go run jabba.go
# to test a change
make test # or "test-coverage" if you want to get a coverage breakdown
# to make a build
make build # or "build-release" (latter is cross-compiling jabba to different OSs/ARCHs)
```
## FAQ
**Q**: What if I already have `java` installed?
A: It's fine. You can switch between system JDK and `jabba`-provided one whenever you feel like it (`jabba use ...` / `jabba deactivate`).
They are not gonna conflict with each other.
**Q**: How do I switch `java` globally?
A: **jabba** doesn't have this functionality built-in because the exact way varies greatly between the operation systems and usually
involves elevated permissions. But. Here are the snippets that <u>should</u> work:
* Windows
> (in powershell as administrator)
```
# select jdk
jabba use ...
# modify global PATH & JAVA_HOME
$envRegKey = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey('SYSTEM\CurrentControlSet\Control\Session Manager\Environment', $true)
$envPath=$envRegKey.GetValue('Path', $null, "DoNotExpandEnvironmentNames").replace('%JAVA_HOME%\bin;', '')
[Environment]::SetEnvironmentVariable('JAVA_HOME', "$(jabba which $(jabba current))", 'Machine')
[Environment]::SetEnvironmentVariable('PATH', "%JAVA_HOME%\bin;$envPath", 'Machine')
```
* Linux
> (tested on Debian/Ubuntu)
```
# select jdk
jabba use ...
sudo update-alternatives --install /usr/bin/java java ${JAVA_HOME%*/}/bin/java 20000
sudo update-alternatives --install /usr/bin/javac javac ${JAVA_HOME%*/}/bin/javac 20000
```
> To switch between multiple GLOBAL alternatives use `sudo update-alternatives --config java`.
## License
[Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0)
By using this software you agree to
- [Oracle Binary Code License Agreement for the Java SE Platform Products and JavaFX](http://www.oracle.com/technetwork/java/javase/terms/license/index.html)
- [Oracle Technology Network Early Adopter Development License Agreement](http://www.oracle.com/technetwork/licenses/ea-license-noexhibits-1938914.html) in case of EA releases
- Apple's Software License Agreement in case of "Java for OS X"
- [International License Agreement for Non-Warranted Programs](http://www14.software.ibm.com/cgi-bin/weblap/lap.pl?la_formnum=&li_formnum=L-PMAA-A3Z8P2&l=en) in case of IBM SDK, Java Technology Edition.
This software is for educational purposes only.
Use it at your own risk.
================================================
FILE: cfg/cfg.go
================================================
package cfg
import (
"os"
"path/filepath"
log "github.com/sirupsen/logrus"
"github.com/mitchellh/go-homedir"
)
func Dir() string {
home := os.Getenv("JABBA_HOME")
if home != "" {
return filepath.Clean(home)
}
dir, err := homedir.Dir()
if err != nil {
log.Fatal(err)
}
return filepath.Join(dir, ".jabba")
}
func Index() string {
registry := os.Getenv("JABBA_INDEX")
if registry == "" {
registry = "https://github.com/Jabba-Team/index/raw/main/index.json"
}
return registry
}
================================================
FILE: command/alias.go
================================================
package command
import (
"io/ioutil"
"os"
"path/filepath"
"github.com/Jabba-Team/jabba/cfg"
)
func SetAlias(name string, ver string) (err error) {
if ver == "" {
err = os.Remove(filepath.Join(cfg.Dir(), name+".alias"))
} else {
err = ioutil.WriteFile(filepath.Join(cfg.Dir(), name+".alias"), []byte(ver), 0666)
}
return
}
func GetAlias(name string) string {
b, err := ioutil.ReadFile(filepath.Join(cfg.Dir(), name+".alias"))
if err != nil {
return ""
}
return string(b)
}
================================================
FILE: command/check.go
================================================
package command
import (
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
"github.com/Jabba-Team/jabba/cfg"
)
type CheckResult struct {
JabbaHome string
JavaHome string
ShellIntegrationOK bool
IntegrationFile string
RCFile string
RCFileHasSource bool
CurrentShell string
IsFunction bool
}
func Check() (*CheckResult, error) {
result := &CheckResult{
JabbaHome: cfg.Dir(),
JavaHome: os.Getenv("JAVA_HOME"),
CurrentShell: detectShell(),
}
// Determine integration file based on shell
result.IntegrationFile = getIntegrationFile(result.CurrentShell)
result.RCFile = getRCFile(result.CurrentShell)
// Check if integration file exists
if _, err := os.Stat(result.IntegrationFile); err == nil {
result.ShellIntegrationOK = true
}
// Check if RC file has source line
if result.RCFile != "" {
result.RCFileHasSource = checkRCFileHasSource(result.RCFile, result.IntegrationFile)
}
// Check if jabba is loaded as function (via JABBA_SHELL_INTEGRATION env var)
result.IsFunction = os.Getenv("JABBA_SHELL_INTEGRATION") == "ON"
return result, nil
}
func detectShell() string {
shell := os.Getenv("SHELL")
if shell == "" {
if runtime.GOOS == "windows" {
// Check if running in PowerShell
if os.Getenv("PSModulePath") != "" {
return "powershell"
}
// Check if running in Git Bash/MSYS/Cygwin
osType := os.Getenv("OSTYPE")
if strings.Contains(osType, "msys") || strings.Contains(osType, "cygwin") {
return "bash"
}
return "powershell"
}
return "bash"
}
// Extract shell name from path
shellName := filepath.Base(shell)
if strings.Contains(shellName, "zsh") {
return "zsh"
} else if strings.Contains(shellName, "fish") {
return "fish"
} else if strings.Contains(shellName, "bash") {
return "bash"
} else if strings.Contains(shellName, "nu") {
return "nushell"
}
return "bash" // default
}
func getIntegrationFile(shell string) string {
jabbaHome := cfg.Dir()
switch shell {
case "powershell":
return filepath.Join(jabbaHome, "jabba.ps1")
case "fish":
return filepath.Join(jabbaHome, "jabba.fish")
case "nushell":
return filepath.Join(jabbaHome, "jabba.nu")
default:
return filepath.Join(jabbaHome, "jabba.sh")
}
}
func getRCFile(shell string) string {
home, err := os.UserHomeDir()
if err != nil {
return ""
}
switch shell {
case "powershell":
if runtime.GOOS == "windows" {
return filepath.Join(os.Getenv("USERPROFILE"), "Documents", "WindowsPowerShell", "Microsoft.PowerShell_profile.ps1")
}
return ""
case "zsh":
return filepath.Join(home, ".zshrc")
case "fish":
return filepath.Join(home, ".config", "fish", "config.fish")
case "nushell":
if runtime.GOOS == "windows" {
return filepath.Join(os.Getenv("APPDATA"), "nushell", "config.nu")
}
return filepath.Join(home, ".config", "nushell", "config.nu")
case "bash":
return filepath.Join(home, ".bashrc")
default:
return filepath.Join(home, ".bashrc")
}
}
func checkRCFileHasSource(rcFile, integrationFile string) bool {
content, err := os.ReadFile(rcFile)
if err != nil {
return false
}
// Look for reference to jabba integration file
return strings.Contains(string(content), "jabba.sh") ||
strings.Contains(string(content), "jabba.ps1") ||
strings.Contains(string(content), "jabba.fish") ||
strings.Contains(string(content), "jabba.nu")
}
func PrintCheckResult(result *CheckResult) {
fmt.Println("Jabba Installation Check")
fmt.Println("========================")
fmt.Printf("JABBA_HOME: %s\n", result.JabbaHome)
fmt.Printf("JAVA_HOME: %s\n", result.JavaHome)
fmt.Printf("Current Shell: %s\n", result.CurrentShell)
fmt.Printf("Integration File: %s\n", result.IntegrationFile)
if result.ShellIntegrationOK {
fmt.Printf("Integration Exists: ✓ Yes\n")
} else {
fmt.Printf("Integration Exists: ✗ No (run 'jabba init' to create)\n")
}
fmt.Printf("RC File: %s\n", result.RCFile)
if result.RCFileHasSource {
fmt.Printf("RC File Configured: ✓ Yes\n")
} else {
fmt.Printf("RC File Configured: ✗ No (run 'jabba init' to configure)\n")
}
if result.IsFunction {
fmt.Printf("Shell Integration: ✓ Active (jabba is a function)\n")
} else {
fmt.Printf("Shell Integration: ✗ Not active (jabba is binary only)\n")
fmt.Println("\nTo activate shell integration, restart your shell or run:")
if result.CurrentShell == "powershell" {
fmt.Printf(" . \"%s\"\n", result.IntegrationFile)
} else {
fmt.Printf(" source \"%s\"\n", result.IntegrationFile)
}
}
}
================================================
FILE: command/check_test.go
================================================
package command
import (
"os"
"path/filepath"
"runtime"
"testing"
)
func TestDetectShell(t *testing.T) {
tests := []struct {
name string
shell string
osType string
psModule string
expected string
}{
{"bash", "/bin/bash", "", "", "bash"},
{"zsh", "/usr/bin/zsh", "", "", "zsh"},
{"fish", "/usr/local/bin/fish", "", "", "fish"},
{"nushell", "/usr/bin/nu", "", "", "nushell"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
os.Setenv("SHELL", tt.shell)
if tt.osType != "" {
os.Setenv("OSTYPE", tt.osType)
}
if tt.psModule != "" {
os.Setenv("PSModulePath", tt.psModule)
}
result := detectShell()
if result != tt.expected {
t.Errorf("detectShell() = %v, want %v", result, tt.expected)
}
os.Unsetenv("SHELL")
os.Unsetenv("OSTYPE")
os.Unsetenv("PSModulePath")
})
}
}
func TestGetIntegrationFile(t *testing.T) {
tests := []struct {
shell string
expected string
}{
{"bash", "jabba.sh"},
{"zsh", "jabba.sh"},
{"fish", "jabba.fish"},
{"powershell", "jabba.ps1"},
{"nushell", "jabba.nu"},
}
for _, tt := range tests {
t.Run(tt.shell, func(t *testing.T) {
result := getIntegrationFile(tt.shell)
if filepath.Base(result) != tt.expected {
t.Errorf("getIntegrationFile(%s) = %v, want filename %v", tt.shell, result, tt.expected)
}
})
}
}
func TestGetRCFile(t *testing.T) {
tests := []struct {
shell string
expected string
}{
{"bash", ".bashrc"},
{"zsh", ".zshrc"},
{"fish", "config.fish"},
}
for _, tt := range tests {
t.Run(tt.shell, func(t *testing.T) {
result := getRCFile(tt.shell)
if !filepath.IsAbs(result) {
t.Errorf("getRCFile(%s) should return absolute path", tt.shell)
}
if filepath.Base(result) != tt.expected {
t.Errorf("getRCFile(%s) = %v, want filename %v", tt.shell, result, tt.expected)
}
})
}
}
func TestCheckRCFileHasSource(t *testing.T) {
tmpDir := t.TempDir()
rcFile := filepath.Join(tmpDir, ".bashrc")
integrationFile := filepath.Join(tmpDir, "jabba.sh")
// Test with no file
if checkRCFileHasSource(rcFile, integrationFile) {
t.Error("checkRCFileHasSource should return false for non-existent file")
}
// Test with file without source line
os.WriteFile(rcFile, []byte("export PATH=/usr/bin:$PATH\n"), 0644)
if checkRCFileHasSource(rcFile, integrationFile) {
t.Error("checkRCFileHasSource should return false when source line is missing")
}
// Test with file with source line
os.WriteFile(rcFile, []byte("export PATH=/usr/bin:$PATH\nsource ~/.jabba/jabba.sh\n"), 0644)
if !checkRCFileHasSource(rcFile, integrationFile) {
t.Error("checkRCFileHasSource should return true when source line is present")
}
}
func TestCheck(t *testing.T) {
// Set up test environment
originalHome := os.Getenv("JABBA_HOME")
tmpDir := t.TempDir()
os.Setenv("JABBA_HOME", tmpDir)
defer os.Setenv("JABBA_HOME", originalHome)
result, err := Check()
if err != nil {
t.Fatalf("Check() error = %v", err)
}
if result.JabbaHome == "" {
t.Error("Check() should return JABBA_HOME")
}
if result.CurrentShell == "" {
t.Error("Check() should detect current shell")
}
if result.IntegrationFile == "" {
t.Error("Check() should return integration file path")
}
}
func TestCheckWithIntegrationFile(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("Skipping Unix-specific test on Windows")
}
tmpDir := t.TempDir()
originalHome := os.Getenv("JABBA_HOME")
os.Setenv("JABBA_HOME", tmpDir)
os.Setenv("SHELL", "/bin/bash")
defer func() {
os.Setenv("JABBA_HOME", originalHome)
os.Unsetenv("SHELL")
}()
// Create integration file
integrationFile := filepath.Join(tmpDir, "jabba.sh")
os.WriteFile(integrationFile, []byte("# test"), 0644)
result, err := Check()
if err != nil {
t.Fatalf("Check() error = %v", err)
}
if !result.ShellIntegrationOK {
t.Error("Check() should detect existing integration file")
}
}
================================================
FILE: command/current.go
================================================
package command
import (
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/Jabba-Team/jabba/cfg"
)
var lookPath = exec.LookPath
func Current() string {
javaPath, err := lookPath("java")
if err == nil {
prefix := filepath.Join(cfg.Dir(), "jdk") + string(os.PathSeparator)
if strings.HasPrefix(javaPath, prefix) {
index := strings.Index(javaPath[len(prefix):], string(os.PathSeparator))
if index != -1 {
return javaPath[len(prefix) : len(prefix)+index]
}
}
}
return ""
}
================================================
FILE: command/current_test.go
================================================
package command
import (
"path/filepath"
"testing"
"github.com/Jabba-Team/jabba/cfg"
)
func TestCurrent(t *testing.T) {
var prevLookPath = lookPath
defer func() { lookPath = prevLookPath }()
lookPath = func(file string) (string, error) {
return filepath.Join(cfg.Dir(), "jdk", "1.8.0", "Contents", "Home", "bin", "java"), nil
}
actual := Current()
expected := "1.8.0"
if actual != expected {
t.Fatalf("actual: %v != expected: %v", actual, expected)
}
}
================================================
FILE: command/deactivate.go
================================================
package command
import (
"os"
"path/filepath"
"regexp"
"github.com/Jabba-Team/jabba/cfg"
)
func Deactivate() ([]string, error) {
pth, _ := os.LookupEnv("PATH")
rgxp := regexp.MustCompile(regexp.QuoteMeta(filepath.Join(cfg.Dir(), "jdk")) + "[^:]+[:]")
// strip references to ~/.jabba/jdk/*, otherwise leave unchanged
pth = rgxp.ReplaceAllString(pth, "")
javaHome, overrideWasSet := os.LookupEnv("JAVA_HOME_BEFORE_JABBA")
if !overrideWasSet {
javaHome, _ = os.LookupEnv("JAVA_HOME")
}
return []string{
"export PATH=\"" + pth + "\"",
"export JAVA_HOME=\"" + javaHome + "\"",
"unset JAVA_HOME_BEFORE_JABBA",
}, nil
}
================================================
FILE: command/deactivate_test.go
================================================
package command
import (
"os"
"reflect"
"testing"
"github.com/Jabba-Team/jabba/cfg"
)
func TestDeactivate(t *testing.T) {
prevPath := os.Getenv("PATH")
defer func() { os.Setenv("PATH", prevPath) }()
os.Setenv("PATH", "/usr/local/bin:"+cfg.Dir()+"/jdk/zulu@1.8.72/bin:/system-jdk/bin:/usr/bin")
os.Setenv("JAVA_HOME", cfg.Dir()+"/jdk/zulu@1.8.72")
os.Setenv("JAVA_HOME_BEFORE_JABBA", "/system-jdk")
actual, err := Deactivate()
if err != nil {
t.Fatalf("err: %v", err)
}
expected := []string{
"export PATH=\"/usr/local/bin:/system-jdk/bin:/usr/bin\"",
"export JAVA_HOME=\"/system-jdk\"",
"unset JAVA_HOME_BEFORE_JABBA",
}
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("actual: %v != expected: %v", actual, expected)
}
}
func TestDeactivateInUnusedEnv(t *testing.T) {
prevPath := os.Getenv("PATH")
defer func() { os.Setenv("PATH", prevPath) }()
os.Setenv("PATH", "/usr/local/bin:/system-jdk/bin:/usr/bin")
os.Setenv("JAVA_HOME", "/system-jdk")
os.Unsetenv("JAVA_HOME_BEFORE_JABBA")
actual, err := Deactivate()
if err != nil {
t.Fatalf("err: %v", err)
}
expected := []string{
"export PATH=\"/usr/local/bin:/system-jdk/bin:/usr/bin\"",
"export JAVA_HOME=\"/system-jdk\"",
"unset JAVA_HOME_BEFORE_JABBA",
}
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("actual: %v != expected: %v", actual, expected)
}
}
================================================
FILE: command/fileiter/iterator.go
================================================
package fileiter
import (
"io/ioutil"
"os"
"path/filepath"
)
type Iterator struct {
head *node
tail *node
breadthFirst bool
}
type node struct {
dir string
stat os.FileInfo
err error
skip bool
next *node
}
type IterationOption = func(*Iterator)
func BreadthFirst() IterationOption {
return func(w *Iterator) {
w.breadthFirst = true
}
}
func New(dir string, opts ...IterationOption) *Iterator {
stat, err := os.Lstat(dir)
n := &node{dir: filepath.Dir(dir), stat: stat, err: err}
w := &Iterator{head: n, tail: n}
for _, opt := range opts {
opt(w)
}
return w
}
func (w *Iterator) Next() bool {
if w.head == nil {
return false
}
if !w.head.skip && w.head.err == nil && w.head.stat.IsDir() {
path := filepath.Join(w.head.dir, w.head.stat.Name())
if ff, err := ioutil.ReadDir(path); len(ff) != 0 { // sorts ASC
if err == nil {
if w.breadthFirst {
// add one-by-one to the tail
for _, f := range ff {
w.tail.next = &node{path, f, nil, false, nil}
w.tail = w.tail.next
}
return w.next(w.head.next)
} else {
w.head = w.head.next // drop current
// add one-by-one to the head (in reverse order)
for i := len(ff) - 1; i > -1; i-- {
w.head = &node{path, ff[i], nil, false, w.head}
}
return w.next(w.head)
}
} else {
if w.breadthFirst {
w.tail.next = &node{w.head.dir, w.head.stat, err, false, nil}
w.tail = w.tail.next
w.head = w.head.next // drop current
return true
} else {
w.head.err = err
return true
}
}
}
}
return w.next(w.head.next)
}
func (w *Iterator) next(v *node) bool {
w.head = v
if v == nil {
w.tail = nil
return false
}
return true
}
func (w *Iterator) Dir() string {
return w.head.dir
}
func (w *Iterator) Name() string {
return w.head.stat.Name()
}
func (w *Iterator) IsDir() bool {
return w.head.stat.IsDir()
}
func (w *Iterator) Err() error {
return w.head.err
}
func (w *Iterator) SkipDir() {
w.head.skip = true
}
================================================
FILE: command/fileiter/iterator_test.go
================================================
package fileiter
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
)
func TestDFS(t *testing.T) {
ok := func(err error) {
if err != nil {
t.Fatal(err)
}
}
dir, err := ioutil.TempDir("", "fileiter_test")
ok(err)
ok(touch(dir, "a", "b", "c"))
ok(touch(dir, "b", "c"))
ok(touch(dir, "c"))
ok(mkdir(dir, "d"))
expectedSeq := []string{
"a",
filepath.Join("a", "b"),
filepath.Join("a", "b", "c"),
"b",
filepath.Join("b", "c"),
"c",
"d",
}
test(t, dir, expectedSeq)
test(t, filepath.Join(dir, "d"), nil)
test(t, filepath.Join(dir, "non-existent"), nil)
}
func TestBFS(t *testing.T) {
ok := func(err error) {
if err != nil {
t.Fatal(err)
}
}
dir, err := ioutil.TempDir("", "fileiter_test")
ok(err)
ok(touch(dir, "a", "b", "c"))
ok(touch(dir, "b", "c"))
ok(touch(dir, "c"))
ok(mkdir(dir, "d"))
expectedSeq := []string{
"a",
"b",
"c",
"d",
filepath.Join("a", "b"),
filepath.Join("b", "c"),
filepath.Join("a", "b", "c"),
}
test(t, dir, expectedSeq, BreadthFirst())
test(t, filepath.Join(dir, "d"), nil, BreadthFirst())
test(t, filepath.Join(dir, "non-existent"), nil, BreadthFirst())
}
func TestNew(t *testing.T) {
ok := func(err error) {
if err != nil {
t.Fatal(err)
}
}
dir, err := ioutil.TempDir("", "fileiter_test")
ok(err)
ok(touch(dir, "a", "b"))
if it := New(filepath.Join(dir, "a", "b")); it.Err() != nil || it.IsDir() {
t.Fatal(it.Err())
}
if it := New(filepath.Join(dir, "a")); it.Err() != nil || !it.IsDir() {
t.Fatal(it.Err())
}
if it := New(filepath.Join(dir, "b")); it.Err() == nil {
t.Fatal()
}
}
func test(t *testing.T, dir string, expectedSeq []string, opts ...IterationOption) {
i := 0
for w := New(dir, opts...); w.Next(); {
if w.Err() != nil {
t.Fatal(w.Err())
}
expected := filepath.Join(dir, expectedSeq[i])
actual := filepath.Join(w.Dir(), w.Name())
if actual != expected {
t.Fatalf("actual: %v != expected: %v", actual, expected)
}
i++
}
}
func touch(path ...string) error {
filename := filepath.Join(path...)
if err := os.MkdirAll(filepath.Dir(filename), 0755); err != nil {
return err
}
if err := ioutil.WriteFile(filename, nil, 0755); err != nil {
return err
}
return nil
}
func mkdir(path ...string) error {
if err := os.MkdirAll(filepath.Join(path...), 0755); err != nil {
return err
}
return nil
}
================================================
FILE: command/init.go
================================================
package command
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/Jabba-Team/jabba/cfg"
)
func Init() error {
shell := detectShell()
integrationFile := getIntegrationFile(shell)
rcFile := getRCFile(shell)
// Create integration file if it doesn't exist
if _, err := os.Stat(integrationFile); os.IsNotExist(err) {
if err := createIntegrationFile(shell, integrationFile); err != nil {
return fmt.Errorf("failed to create integration file: %w", err)
}
fmt.Printf("✓ Created %s\n", integrationFile)
} else {
fmt.Printf("✓ Integration file already exists: %s\n", integrationFile)
}
// Update RC file if needed
if rcFile != "" {
if !checkRCFileHasSource(rcFile, integrationFile) {
if err := updateRCFile(shell, rcFile, integrationFile); err != nil {
return fmt.Errorf("failed to update RC file: %w", err)
}
fmt.Printf("✓ Updated %s\n", rcFile)
} else {
fmt.Printf("✓ RC file already configured: %s\n", rcFile)
}
}
fmt.Println("\nShell integration configured successfully!")
fmt.Println("Restart your shell or run:")
if shell == "powershell" {
fmt.Printf(" . \"%s\"\n", integrationFile)
} else {
fmt.Printf(" source \"%s\"\n", integrationFile)
}
return nil
}
func createIntegrationFile(shell, integrationFile string) error {
// Ensure directory exists
dir := filepath.Dir(integrationFile)
if err := os.MkdirAll(dir, 0755); err != nil {
return err
}
var content string
jabbaHome := cfg.Dir()
switch shell {
case "powershell":
content = generatePowerShellIntegration(jabbaHome)
case "fish":
content = generateFishIntegration(jabbaHome)
case "nushell":
content = generateNushellIntegration(jabbaHome)
default:
content = generateBashIntegration(jabbaHome)
}
return os.WriteFile(integrationFile, []byte(content), 0644)
}
func generateBashIntegration(jabbaHome string) string {
return fmt.Sprintf(`# https://github.com/Jabba-Team/jabba
# This file is intended to be "sourced" (i.e. ". ~/.jabba/jabba.sh")
export JABBA_HOME="%s"
jabba() {
local fd3=$(mktemp /tmp/jabba-fd3.XXXXXX)
# Detect if we're on Windows (Git Bash/MSYS/Cygwin) or Unix/Linux/macOS
case "$OSTYPE" in
msys*|cygwin*|win32)
# Windows: use .exe extension and --fd3 flag (fd redirection doesn't work)
JABBA_SHELL_INTEGRATION=ON "$JABBA_HOME/bin/jabba.exe" "$@" --fd3 "$fd3"
local exit_code=$?
if [ -s "$fd3" ]; then
# Convert Windows paths to Unix-style for Git Bash
# 1. Replace backslashes with forward slashes
# 2. Convert C:/ to /c/ (drive letters)
# 3. Replace semicolons with colons (PATH separator)
eval $(cat "$fd3" | sed 's#\\#/#g' | sed 's#\([A-Za-z]\):/#/\L\1/#g' | sed 's#;#:#g')
fi
;;
*)
# Unix/Linux/macOS: use fd redirection (standard approach)
(JABBA_SHELL_INTEGRATION=ON $JABBA_HOME/bin/jabba "$@" 3>| ${fd3})
local exit_code=$?
eval $(cat ${fd3})
;;
esac
rm -f ${fd3}
return ${exit_code}
}
if [ ! -z "$(jabba alias default)" ]; then
jabba use default
fi
`, jabbaHome)
}
func generatePowerShellIntegration(jabbaHome string) string {
return fmt.Sprintf(`# https://github.com/Jabba-Team/jabba
# This file is intended to be "sourced" (i.e. ". ~/.jabba/jabba.ps1")
$env:JABBA_HOME = "%s"
function jabba {
$fd3 = [IO.Path]::GetTempFileName()
& $env:JABBA_HOME\bin\jabba.exe $args --fd3 $fd3 | Out-Null
$exitCode = $LASTEXITCODE
if (Test-Path $fd3) {
$cmd = Get-Content $fd3 | Out-String
if ($cmd) {
Invoke-Expression $cmd
}
Remove-Item $fd3
}
return $exitCode
}
if (jabba alias default) {
jabba use default
}
`, jabbaHome)
}
func generateFishIntegration(jabbaHome string) string {
return fmt.Sprintf(`# https://github.com/Jabba-Team/jabba
# This file is intended to be "sourced" (i.e. ". ~/.jabba/jabba.fish")
set -xg JABBA_HOME "%s"
function jabba
set fd3 (mktemp /tmp/jabba-fd3.XXXXXX)
env JABBA_SHELL_INTEGRATION=ON $JABBA_HOME/bin/jabba $argv 3> $fd3
set exit_code $status
eval (cat $fd3 | sed "s/^export/set -xg/g" | sed "s/=/ /g" | sed "s/:/\" \"/g" | sed "s/\"/\\\\\"/g" | sed "s/'\\\\''/'/g")
rm -f $fd3
return $exit_code
end
if test ! -z (jabba alias default)
jabba use default
end
`, jabbaHome)
}
func generateNushellIntegration(jabbaHome string) string {
return fmt.Sprintf(`# https://github.com/Jabba-Team/jabba
# This file is intended to be "sourced" (i.e. "source ~/.jabba/jabba.nu")
def --env jabba [...params:string] {
$env.JABBA_HOME = '%s'
let fd3 = mktemp -t jabba-fd3.XXXXXX.env
nu -c $"$env.JABBA_SHELL_INTEGRATION = 'ON'
($env.JABBA_HOME)/bin/jabba ...($params) --fd3 ($fd3)"
let exit_code = $env.LAST_EXIT_CODE
if ( ls $fd3 | where size > 0B | is-not-empty ) {
(
open $fd3
| str trim
| lines
| parse 'export {name}="{value}"'
| transpose --header-row --as-record)| load-env
}
if $exit_code != 0 {
return $exit_code
}
}
`, jabbaHome)
}
func updateRCFile(shell, rcFile, integrationFile string) error {
// Ensure directory exists
dir := filepath.Dir(rcFile)
if err := os.MkdirAll(dir, 0755); err != nil {
return err
}
// Create file if it doesn't exist
if _, err := os.Stat(rcFile); os.IsNotExist(err) {
if err := os.WriteFile(rcFile, []byte(""), 0644); err != nil {
return err
}
}
// Read existing content
content, err := os.ReadFile(rcFile)
if err != nil {
return err
}
var sourceLine string
switch shell {
case "powershell":
sourceLine = fmt.Sprintf("\nif (Test-Path \"%s\") { . \"%s\" }\n", integrationFile, integrationFile)
case "fish":
sourceLine = fmt.Sprintf("\n[ -s \"%s\" ]; and source \"%s\"\n", integrationFile, integrationFile)
case "nushell":
sourceLine = fmt.Sprintf("\nsource '%s'\n", integrationFile)
default:
sourceLine = fmt.Sprintf("\n[ -s \"%s\" ] && source \"%s\"\n", integrationFile, integrationFile)
}
// Append source line
newContent := string(content)
if !strings.HasSuffix(newContent, "\n") && len(newContent) > 0 {
newContent += "\n"
}
newContent += sourceLine
return os.WriteFile(rcFile, []byte(newContent), 0644)
}
================================================
FILE: command/init_test.go
================================================
package command
import (
"os"
"path/filepath"
"runtime"
"strings"
"testing"
)
func TestGenerateBashIntegration(t *testing.T) {
content := generateBashIntegration("/home/user/.jabba")
if !strings.Contains(content, "JABBA_HOME") {
t.Error("Bash integration should contain JABBA_HOME")
}
if !strings.Contains(content, "jabba()") {
t.Error("Bash integration should contain jabba function")
}
if !strings.Contains(content, "msys*|cygwin*|win32") {
t.Error("Bash integration should contain Windows detection")
}
}
func TestGeneratePowerShellIntegration(t *testing.T) {
content := generatePowerShellIntegration("C:\\Users\\test\\.jabba")
if !strings.Contains(content, "$env:JABBA_HOME") {
t.Error("PowerShell integration should contain JABBA_HOME")
}
if !strings.Contains(content, "function jabba") {
t.Error("PowerShell integration should contain jabba function")
}
}
func TestGenerateFishIntegration(t *testing.T) {
content := generateFishIntegration("/home/user/.jabba")
if !strings.Contains(content, "JABBA_HOME") {
t.Error("Fish integration should contain JABBA_HOME")
}
if !strings.Contains(content, "function jabba") {
t.Error("Fish integration should contain jabba function")
}
}
func TestGenerateNushellIntegration(t *testing.T) {
content := generateNushellIntegration("/home/user/.jabba")
if !strings.Contains(content, "JABBA_HOME") {
t.Error("Nushell integration should contain JABBA_HOME")
}
if !strings.Contains(content, "def --env jabba") {
t.Error("Nushell integration should contain jabba function")
}
}
func TestCreateIntegrationFile(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("Skipping Unix-specific test on Windows")
}
tmpDir := t.TempDir()
originalHome := os.Getenv("JABBA_HOME")
os.Setenv("JABBA_HOME", tmpDir)
defer os.Setenv("JABBA_HOME", originalHome)
integrationFile := filepath.Join(tmpDir, "jabba.sh")
err := createIntegrationFile("bash", integrationFile)
if err != nil {
t.Fatalf("createIntegrationFile() error = %v", err)
}
// Check file exists
if _, err := os.Stat(integrationFile); os.IsNotExist(err) {
t.Error("Integration file should be created")
}
// Check content
content, err := os.ReadFile(integrationFile)
if err != nil {
t.Fatalf("Failed to read integration file: %v", err)
}
if !strings.Contains(string(content), "jabba()") {
t.Error("Integration file should contain jabba function")
}
}
func TestUpdateRCFile(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("Skipping Unix-specific test on Windows")
}
tmpDir := t.TempDir()
rcFile := filepath.Join(tmpDir, ".bashrc")
integrationFile := filepath.Join(tmpDir, "jabba.sh")
// Test creating new RC file
err := updateRCFile("bash", rcFile, integrationFile)
if err != nil {
t.Fatalf("updateRCFile() error = %v", err)
}
// Check file exists
if _, err := os.Stat(rcFile); os.IsNotExist(err) {
t.Error("RC file should be created")
}
// Check content
content, err := os.ReadFile(rcFile)
if err != nil {
t.Fatalf("Failed to read RC file: %v", err)
}
if !strings.Contains(string(content), "jabba.sh") {
t.Error("RC file should contain source line for jabba.sh")
}
if !strings.Contains(string(content), "source") {
t.Error("RC file should contain source command")
}
}
func TestUpdateRCFileExisting(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("Skipping Unix-specific test on Windows")
}
tmpDir := t.TempDir()
rcFile := filepath.Join(tmpDir, ".bashrc")
integrationFile := filepath.Join(tmpDir, "jabba.sh")
// Create existing RC file with content
existingContent := "export PATH=/usr/bin:$PATH\n"
os.WriteFile(rcFile, []byte(existingContent), 0644)
err := updateRCFile("bash", rcFile, integrationFile)
if err != nil {
t.Fatalf("updateRCFile() error = %v", err)
}
// Check content preserved and source line added
content, err := os.ReadFile(rcFile)
if err != nil {
t.Fatalf("Failed to read RC file: %v", err)
}
contentStr := string(content)
if !strings.Contains(contentStr, existingContent) {
t.Error("RC file should preserve existing content")
}
if !strings.Contains(contentStr, "jabba.sh") {
t.Error("RC file should contain source line for jabba.sh")
}
}
func TestInit(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("Skipping Unix-specific test on Windows")
}
tmpDir := t.TempDir()
originalHome := os.Getenv("JABBA_HOME")
originalShell := os.Getenv("SHELL")
homeDir := os.Getenv("HOME")
os.Setenv("JABBA_HOME", tmpDir)
os.Setenv("SHELL", "/bin/bash")
os.Setenv("HOME", tmpDir)
defer func() {
os.Setenv("JABBA_HOME", originalHome)
os.Setenv("SHELL", originalShell)
os.Setenv("HOME", homeDir)
}()
err := Init()
if err != nil {
t.Fatalf("Init() error = %v", err)
}
// Check integration file created
integrationFile := filepath.Join(tmpDir, "jabba.sh")
if _, err := os.Stat(integrationFile); os.IsNotExist(err) {
t.Error("Init() should create integration file")
}
// Check RC file updated
rcFile := filepath.Join(tmpDir, ".bashrc")
if _, err := os.Stat(rcFile); os.IsNotExist(err) {
t.Error("Init() should create/update RC file")
}
content, _ := os.ReadFile(rcFile)
if !strings.Contains(string(content), "jabba.sh") {
t.Error("Init() should add source line to RC file")
}
}
func TestInitIdempotent(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("Skipping Unix-specific test on Windows")
}
tmpDir := t.TempDir()
originalHome := os.Getenv("JABBA_HOME")
originalShell := os.Getenv("SHELL")
homeDir := os.Getenv("HOME")
os.Setenv("JABBA_HOME", tmpDir)
os.Setenv("SHELL", "/bin/bash")
os.Setenv("HOME", tmpDir)
defer func() {
os.Setenv("JABBA_HOME", originalHome)
os.Setenv("SHELL", originalShell)
os.Setenv("HOME", homeDir)
}()
// Run init twice
err := Init()
if err != nil {
t.Fatalf("Init() first run error = %v", err)
}
rcFile := filepath.Join(tmpDir, ".bashrc")
contentAfterFirst, _ := os.ReadFile(rcFile)
err = Init()
if err != nil {
t.Fatalf("Init() second run error = %v", err)
}
// Check RC file not duplicated
contentAfterSecond, _ := os.ReadFile(rcFile)
firstCount := strings.Count(string(contentAfterFirst), "jabba.sh")
secondCount := strings.Count(string(contentAfterSecond), "jabba.sh")
if firstCount != secondCount {
t.Error("Init() should be idempotent - should not add duplicate source lines")
}
}
================================================
FILE: command/install.go
================================================
package command
import (
"archive/tar"
"archive/zip"
"compress/gzip"
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"os/exec"
"path/filepath"
"regexp"
"runtime"
"sort"
"strings"
"github.com/Jabba-Team/jabba/cfg"
"github.com/Jabba-Team/jabba/command/fileiter"
"github.com/Jabba-Team/jabba/semver"
"github.com/Jabba-Team/jabba/w32"
log "github.com/sirupsen/logrus"
"github.com/mitchellh/ioprogress"
"github.com/xi2/xz"
)
func Install(selector string, dst string) (string, error) {
var releaseMap map[*semver.Version]string
var ver *semver.Version
var err error
// selector can be in form of <version>=<url>
if strings.Contains(selector, "=") && strings.Contains(selector, "://") {
split := strings.SplitN(selector, "=", 2)
selector = split[0]
// <version> has to be valid per semver
ver, err = semver.ParseVersion(selector)
if err != nil {
return "", err
}
releaseMap = map[*semver.Version]string{ver: split[1]}
} else {
// ... or a version (range will be tried over remote targets)
ver, _ = semver.ParseVersion(selector)
}
// ... apparently it's not
if releaseMap == nil {
ver = nil
rng, err := semver.ParseRange(selector)
if err != nil {
return "", err
}
releaseMap, err = LsRemote(runtime.GOOS, runtime.GOARCH)
if err != nil {
return "", err
}
var vs = make([]*semver.Version, len(releaseMap))
var i = 0
for k := range releaseMap {
vs[i] = k
i++
}
sort.Sort(sort.Reverse(semver.VersionSlice(vs)))
for _, v := range vs {
if rng.Contains(v) {
ver = v
break
}
}
if ver == nil {
tt := make([]string, len(vs))
for i, v := range vs {
tt[i] = v.String()
}
return "", errors.New("No compatible version found for " + selector +
"\nValid install targets: " + strings.Join(tt, ", "))
}
}
// check whether requested version is already installed
if ver != nil && dst == "" {
local, err := Ls()
if err != nil {
return "", err
}
for _, v := range local {
if ver.Equals(v) {
return ver.String(), nil
}
}
}
url := releaseMap[ver]
if matched, _ := regexp.MatchString("^\\w+[+]\\w+://", url); !matched {
return "", errors.New("URL must contain qualifier, e.g. tgz+http://...")
}
if dst == "" {
dst = filepath.Join(cfg.Dir(), "jdk", ver.String())
} else {
if _, err := os.Stat(dst); !os.IsNotExist(err) {
if err == nil { // dst exists
if empty, _ := isEmptyDir(dst); !empty {
err = fmt.Errorf("\"%s\" is not empty", dst)
}
} // or is inaccessible
if err != nil {
return "", err
}
}
}
var fileType = url[0:strings.Index(url, "+")]
url = url[strings.Index(url, "+")+1:]
var file string
var deleteFileWhenFinnished bool
if strings.HasPrefix(url, "file://") {
file = strings.TrimPrefix(url, "file://")
if runtime.GOOS == "windows" {
// file:///C:/path/...
file = strings.Replace(strings.TrimPrefix(file, "/"), "/", "\\", -1)
}
} else {
log.Info("Downloading ", ver, " (", url, ")")
file, err = download(url, fileType)
if err != nil {
return "", err
}
deleteFileWhenFinnished = true
}
switch runtime.GOOS {
case "darwin":
err = installOnDarwin(file, fileType, dst)
case "linux":
err = installOnLinux(file, fileType, dst)
case "windows":
err = installOnWindows(file, fileType, dst)
default:
err = errors.New(runtime.GOOS + " OS is not supported")
}
if err == nil && deleteFileWhenFinnished {
os.Remove(file)
}
return ver.String(), err
}
func isEmptyDir(name string) (bool, error) {
entries, err := ioutil.ReadDir(name)
if err != nil {
return false, err
}
return len(entries) == 0, nil
}
type RedirectTracer struct {
Transport http.RoundTripper
}
func (self RedirectTracer) RoundTrip(req *http.Request) (resp *http.Response, err error) {
transport := self.Transport
if transport == nil {
transport = http.DefaultTransport
}
resp, err = transport.RoundTrip(req)
if err != nil {
return
}
switch resp.StatusCode {
case http.StatusMovedPermanently, http.StatusFound, http.StatusSeeOther, http.StatusTemporaryRedirect:
log.Debug("Following ", resp.StatusCode, " redirect to ", resp.Header.Get("Location"))
}
return
}
func download(url string, fileType string) (file string, err error) {
tmp, err := ioutil.TempFile("", "jabba-d-")
if err != nil {
return
}
if fileType == "exe" {
err = tmp.Close()
if err != nil {
return
}
err = os.Rename(tmp.Name(), tmp.Name()+".exe")
if err != nil {
return
}
tmp, err = os.OpenFile(tmp.Name()+".exe", os.O_RDWR, 0600)
if err != nil {
return
}
}
defer tmp.Close()
file = tmp.Name()
log.Debug("Saving ", url, " to ", file)
// todo: timeout
client := http.Client{Transport: RedirectTracer{}}
client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
if len(via) >= 10 {
return fmt.Errorf("too many redirects")
}
if len(via) != 0 {
// https://github.com/golang/go/issues/4800
for attr, val := range via[0].Header {
if _, ok := req.Header[attr]; !ok {
req.Header[attr] = val
}
}
}
return nil
}
req, err := http.NewRequest("GET", url, nil)
if strings.Contains(url, "zulu") {
req.Header.Set("Referer", "http://www.azul.com/downloads/zulu/")
}
req.Header.Set("Cookie", "oraclelicense=accept-securebackup-cookie")
res, err := client.Do(req)
if err != nil {
return
}
defer res.Body.Close()
progressTracker := &ioprogress.Reader{
Reader: res.Body,
Size: res.ContentLength,
}
_, err = io.Copy(tmp, progressTracker)
if err != nil {
return
}
return
}
func installOnDarwin(file string, fileType string, dst string) (err error) {
switch fileType {
case "dmg":
err = installFromDmg(file, dst)
case "tgz":
err = installFromTgz(file, dst)
case "tgx":
err = installFromTgx(file, dst)
case "zip":
err = installFromZip(file, dst)
default:
return errors.New(fileType + " is not supported")
}
if err == nil {
err = normalizePathToBinJava(dst, runtime.GOOS)
}
if err != nil {
os.RemoveAll(dst)
}
return
}
// **/{Contents/Home,Home,}bin/java -> <dir>/Contents/Home/bin/java
func normalizePathToBinJava(dir string, goos string) error {
dir = filepath.Clean(dir)
if _, err := os.Stat(expectedJavaPath(dir, goos)); os.IsNotExist(err) {
java := "java"
if goos == "windows" {
java = "java.exe"
}
var javaPath string
for it := fileiter.New(dir, fileiter.BreadthFirst()); it.Next(); {
if err := it.Err(); err != nil {
return err
}
if !it.IsDir() && filepath.Base(it.Dir()) == "bin" && it.Name() == java {
javaPath = filepath.Join(it.Dir(), it.Name())
break
}
}
if javaPath != "" {
log.Debugf("Found %s", javaPath)
tmp := dir + "~"
javaPath = strings.Replace(javaPath, dir, tmp, 1)
log.Debugf("Moving %s to %s", dir, tmp)
if err := os.Rename(dir, tmp); err != nil {
return err
}
defer func() {
log.Debugf("Removing %s", tmp)
os.RemoveAll(tmp)
}()
homeDir := filepath.Dir(filepath.Dir(javaPath))
var src, dst string
if goos == "darwin" {
if filepath.Base(homeDir) == "Home" {
src = filepath.Dir(homeDir)
dst = filepath.Join(dir, "Contents")
} else {
src = homeDir
dst = filepath.Join(dir, "Contents", "Home")
}
} else {
src = homeDir
dst = dir
}
log.Debugf("Moving %s to %s", src, dst)
if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil {
return err
}
if err = os.Rename(src, dst); err != nil {
return err
}
}
return assertJavaDistribution(dir, goos)
}
return nil
}
func expectedJavaPath(dir string, goos string) string {
var osSpecificSubDir = ""
if goos == "darwin" {
osSpecificSubDir = filepath.Join("Contents", "Home")
}
java := "java"
if goos == "windows" {
java = "java.exe"
}
return filepath.Join(dir, osSpecificSubDir, "bin", java)
}
func assertJavaDistribution(dir string, goos string) error {
var path = expectedJavaPath(dir, goos)
var err error
if _, err = os.Stat(path); os.IsNotExist(err) {
err = errors.New(path + " wasn't found. " +
"If you believe this is an error - please create a ticket at https://github.com/Jabba-Team/jabba/issues " +
"(specify OS and command that was used)")
}
return err
}
func installFromDmg(src string, dst string) error {
tmp, err := ioutil.TempDir("", "jabba-i-")
if err != nil {
return err
}
defer os.RemoveAll(tmp)
srcName := filepath.Base(src)
pkgdir := tmp + "/" + srcName + "-pkg"
mountpoint := tmp + "/" + srcName
log.Info("Mounting " + src)
err = sh(fmt.Sprintf(`hdiutil mount -mountpoint "%s" "%s"`, mountpoint, src))
if err != nil {
return err
}
defer func() {
log.Info("Unmounting " + mountpoint)
sh(fmt.Sprintf(`hdiutil unmount "%s"`, mountpoint))
}()
log.Info("Extracting " + mountpoint + "/*.pkg")
err = sh(fmt.Sprintf(`pkgutil --expand "%s"/*.pkg "%s"`, mountpoint, pkgdir))
if err == nil {
pathToPayload := ""
// fixme: find a better way
var payloadSize int64
for it := fileiter.New(pkgdir, fileiter.BreadthFirst()); it.Next(); {
if !it.IsDir() && it.Name() == "Payload" {
path := filepath.Join(it.Dir(), it.Name())
stat, err := os.Stat(path)
if err != nil {
return err
}
if payloadSize < stat.Size() { // pick largest (e.g. among javaappletplugin.pkg & jdk180191.pkg)
pathToPayload = path
payloadSize = stat.Size()
}
}
}
if pathToPayload != "" {
log.Info("Extracting " + pathToPayload)
err = sh(fmt.Sprintf(`mkdir -p "%s" && cd "%s" && gzip -dc "%s" | cpio -i`, dst, dst, pathToPayload))
}
}
return err
}
func installOnLinux(file string, fileType string, dst string) (err error) {
switch fileType {
case "bin":
err = installFromBin(file, dst)
case "ia":
err = installFromIa(file, dst)
case "tgz":
err = installFromTgz(file, dst)
case "tgx":
err = installFromTgx(file, dst)
case "zip":
err = installFromZip(file, dst)
default:
return errors.New(fileType + " is not supported")
}
if err == nil {
err = normalizePathToBinJava(dst, runtime.GOOS)
}
if err != nil {
os.RemoveAll(dst)
}
return
}
func installOnWindows(file string, fileType string, dst string) (err error) {
switch fileType {
case "exe":
err = installFromExe(file, dst)
case "tgz":
err = installFromTgz(file, dst)
case "tgx":
err = installFromTgx(file, dst)
case "zip":
err = installFromZip(file, dst)
default:
return errors.New(fileType + " is not supported")
}
if err == nil {
err = normalizePathToBinJava(dst, runtime.GOOS)
}
if err != nil {
os.RemoveAll(dst)
}
return
}
func installFromBin(src string, dst string) (err error) {
tmp, err := ioutil.TempDir("", "jabba-i-")
if err != nil {
return
}
defer os.RemoveAll(tmp)
err = sh("cp " + src + " " + tmp)
if err == nil {
log.Info("Extracting " + filepath.Join(tmp, filepath.Base(src)) + " to " + dst)
err = sh("cd " + tmp + " && echo | sh " + filepath.Base(src) + " && mv jdk*/ " + dst)
}
return
}
func installFromIa(src string, dst string) error {
tmp, err := ioutil.TempDir("", "jabba-i-")
if err != nil {
return err
}
defer os.RemoveAll(tmp)
err = sh("printf 'LICENSE_ACCEPTED=TRUE\\nUSER_INSTALL_DIR=" + dst + "' > " +
filepath.Join(tmp, "installer.properties"))
if err == nil {
log.Info("Extracting " + src + " to " + dst)
err = sh("echo | sh " + src + " -i silent -f " + filepath.Join(tmp, "installer.properties"))
}
return err
}
func installFromExe(src string, dst string) error {
log.Info("Unpacking " + src + " to " + dst)
// using ShellExecute instead of exec.Command so user could decide whether to trust the installer when UAC is active
return w32.ShellExecuteAndWait(w32.HWND(0), "open", src, "/s INSTALLDIR=\""+dst+
"\" STATIC=1 AUTO_UPDATE=0 WEB_JAVA=0 WEB_ANALYTICS=0 REBOOT=0", "", 3)
}
func installFromTgz(src string, dst string) error {
log.Info("Extracting " + src + " to " + dst)
return untgz(src, dst, true)
}
func untgz(src string, dst string, strip bool) error {
gzFile, err := os.Open(src)
if err != nil {
return err
}
defer gzFile.Close()
var prefixToStrip string
if strip {
gzr, err := gzip.NewReader(gzFile)
if err != nil {
return err
}
defer gzr.Close()
r := tar.NewReader(gzr)
var prefix []string
for {
header, err := r.Next()
if err == io.EOF {
break
}
if err != nil {
return err
}
var dir string
if header.Typeflag != tar.TypeDir {
dir = filepath.Dir(header.Name)
} else {
continue
}
if prefix != nil {
dirSplit := strings.Split(dir, string(filepath.Separator))
i, e, dse := 0, len(prefix), len(dirSplit)
if dse < e {
e = dse
}
for i < e {
if prefix[i] != dirSplit[i] {
prefix = prefix[0:i]
break
}
i++
}
} else {
prefix = strings.Split(dir, string(filepath.Separator))
}
}
prefixToStrip = strings.Join(prefix, string(filepath.Separator))
}
gzFile.Seek(0, 0)
gzr, err := gzip.NewReader(gzFile)
if err != nil {
return err
}
defer gzr.Close()
r := tar.NewReader(gzr)
dirCache := make(map[string]bool) // todo: radix tree would perform better here
if err := os.MkdirAll(dst, 0755); err != nil {
return err
}
for {
header, err := r.Next()
if err == io.EOF {
break
}
if err != nil {
return err
}
var dir string
if header.Typeflag != tar.TypeDir {
dir = filepath.Dir(header.Name)
} else {
dir = filepath.Clean(header.Name)
if !strings.HasPrefix(dir, prefixToStrip) {
continue
}
}
dir = strings.TrimPrefix(dir, prefixToStrip)
if dir != "" && dir != "." {
cached := dirCache[dir]
if !cached {
if err := os.MkdirAll(filepath.Join(dst, dir), 0755); err != nil {
return err
}
dirCache[dir] = true
}
}
target := filepath.Join(dst, dir, filepath.Base(header.Name))
switch header.Typeflag {
case tar.TypeReg:
d, err := os.OpenFile(target,
os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.FileMode(header.Mode|0600)&0777)
if err != nil {
return err
}
_, err = io.Copy(d, r)
d.Close()
if err != nil {
return err
}
case tar.TypeSymlink:
if err = os.Symlink(header.Linkname, target); err != nil {
return err
}
}
}
return nil
}
func installFromTgx(src string, dst string) error {
log.Info("Extracting " + src + " to " + dst)
return untgx(src, dst, true)
}
func untgx(src string, dst string, strip bool) error {
xzFile, err := os.Open(src)
if err != nil {
return err
}
defer xzFile.Close()
var prefixToStrip string
if strip {
xzr, err := xz.NewReader(xzFile, 0)
if err != nil {
return err
}
r := tar.NewReader(xzr)
var prefix []string
for {
header, err := r.Next()
if err == io.EOF {
break
}
if err != nil {
return err
}
var dir string
if header.Typeflag != tar.TypeDir {
dir = filepath.Dir(header.Name)
} else {
continue
}
if prefix != nil {
dirSplit := strings.Split(dir, string(filepath.Separator))
i, e, dse := 0, len(prefix), len(dirSplit)
if dse < e {
e = dse
}
for i < e {
if prefix[i] != dirSplit[i] {
prefix = prefix[0:i]
break
}
i++
}
} else {
prefix = strings.Split(dir, string(filepath.Separator))
}
}
prefixToStrip = strings.Join(prefix, string(filepath.Separator))
}
xzFile.Seek(0, 0)
xzr, err := xz.NewReader(xzFile, 0)
if err != nil {
return err
}
r := tar.NewReader(xzr)
dirCache := make(map[string]bool) // todo: radix tree would perform better here
if err := os.MkdirAll(dst, 0755); err != nil {
return err
}
for {
header, err := r.Next()
if err == io.EOF {
break
}
if err != nil {
return err
}
var dir string
if header.Typeflag != tar.TypeDir {
dir = filepath.Dir(header.Name)
} else {
dir = filepath.Clean(header.Name)
if !strings.HasPrefix(dir, prefixToStrip) {
continue
}
}
dir = strings.TrimPrefix(dir, prefixToStrip)
if dir != "" && dir != "." {
cached := dirCache[dir]
if !cached {
if err := os.MkdirAll(filepath.Join(dst, dir), 0755); err != nil {
return err
}
dirCache[dir] = true
}
}
target := filepath.Join(dst, dir, filepath.Base(header.Name))
switch header.Typeflag {
case tar.TypeReg:
d, err := os.OpenFile(target,
os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.FileMode(header.Mode|0600)&0777)
if err != nil {
return err
}
_, err = io.Copy(d, r)
d.Close()
if err != nil {
return err
}
case tar.TypeSymlink:
if err = os.Symlink(header.Linkname, target); err != nil {
return err
}
}
}
return nil
}
func installFromZip(src string, dst string) error {
log.Info("Extracting " + src + " to " + dst)
return unzip(src, dst, true)
}
func unzip(src string, dst string, strip bool) error {
r, err := zip.OpenReader(src)
if err != nil {
return err
}
defer r.Close()
var prefixToStrip string
if strip {
var prefix []string
for _, f := range r.File {
var dir string
if !f.Mode().IsDir() {
dir = filepath.Dir(f.Name)
} else {
continue
}
if prefix != nil {
dirSplit := strings.Split(dir, string(filepath.Separator))
i, e, dse := 0, len(prefix), len(dirSplit)
if dse < e {
e = dse
}
for i < e {
if prefix[i] != dirSplit[i] {
prefix = prefix[0:i]
break
}
i++
}
} else {
prefix = strings.Split(dir, string(filepath.Separator))
}
}
prefixToStrip = strings.Join(prefix, string(filepath.Separator))
}
dirCache := make(map[string]bool) // todo: radix tree would perform better here
if err := os.MkdirAll(dst, 0755); err != nil {
return err
}
for _, f := range r.File {
var dir string
if !f.Mode().IsDir() {
dir = filepath.Dir(f.Name)
} else {
dir = filepath.Clean(f.Name)
if !strings.HasPrefix(dir, prefixToStrip) {
continue
}
}
dir = strings.TrimPrefix(dir, prefixToStrip)
if dir != "" && dir != "." {
cached := dirCache[dir]
if !cached {
if err := os.MkdirAll(filepath.Join(dst, dir), 0755); err != nil {
return err
}
dirCache[dir] = true
}
}
if !f.Mode().IsDir() {
name := filepath.Base(f.Name)
fr, err := f.Open()
if err != nil {
return err
}
d, err := os.OpenFile(filepath.Join(dst, dir, name),
os.O_WRONLY|os.O_CREATE|os.O_TRUNC, (f.Mode()|0600)&0777)
if err != nil {
return err
}
_, err = io.Copy(d, fr)
d.Close()
if err != nil {
return err
}
}
}
return nil
}
func sh(cmd string) error {
var execArg []string
if runtime.GOOS == "windows" {
execArg = []string{"cmd", "/C"}
} else {
execArg = []string{"sh", "-c"}
}
out, err := exec.Command(execArg[0], execArg[1], cmd).CombinedOutput()
if err != nil {
log.Error(string(out))
return errors.New("'" + cmd + "' failed: " + err.Error())
}
return nil
}
================================================
FILE: command/install_test.go
================================================
package command
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
)
func TestBinJavaRelocation(t *testing.T) {
ok := func(err error) {
if err != nil {
t.Fatal(err)
}
}
nok := func(err error) {
if err == nil {
t.Fatal(err)
}
}
dir, err := ioutil.TempDir("", "install_test")
ok(err)
for _, scenario := range []struct {
os string
bin string
prefix string
paths []string
}{
{
os: "linux",
bin: "java",
prefix: "",
paths: []string{""},
},
{
os: "darwin",
bin: "java",
prefix: filepath.Join("Contents", "Home"),
paths: []string{
"",
filepath.Join("Home"),
filepath.Join("Contents", "Home"),
},
},
{
os: "windows",
bin: "java.exe",
prefix: "",
paths: []string{""},
},
} {
for _, p := range scenario.paths {
test1 := filepath.Join(dir, "test1")
ok(touch(test1, p, "bin", scenario.bin))
ok(normalizePathToBinJava(test1, scenario.os))
ok(file(test1, scenario.prefix, "bin", scenario.bin))
test2 := filepath.Join(dir, "test2")
ok(touch(test2, "subdir", p, "bin", scenario.bin))
ok(normalizePathToBinJava(test2, scenario.os))
ok(file(test2, scenario.prefix, "bin", scenario.bin))
test3 := filepath.Join(dir, "test3")
ok(touch(test3, "subdir", "subdir", p, "bin", scenario.bin))
ok(normalizePathToBinJava(test3, scenario.os))
ok(file(test3, scenario.prefix, "bin", scenario.bin))
test4 := filepath.Join(dir, "test4")
ok(touch(test4, "file"))
ok(touch(test4, "subdir", "subdir", p, "bin", scenario.bin))
ok(normalizePathToBinJava(test4, scenario.os))
ok(file(test4, scenario.prefix, "bin", scenario.bin))
test5 := filepath.Join(dir, "test5")
ok(touch(test5, "bin", "file"))
nok(normalizePathToBinJava(test5, scenario.os))
ok(file(test5, "bin", "file"))
}
}
}
func touch(path ...string) error {
filename := filepath.Join(path...)
if err := os.MkdirAll(filepath.Dir(filename), 0755); err != nil {
return err
}
if err := ioutil.WriteFile(filename, nil, 0755); err != nil {
return err
}
return nil
}
func file(path ...string) error {
if _, err := os.Stat(filepath.Join(path...)); os.IsNotExist(err) {
return err
}
return nil
}
================================================
FILE: command/link.go
================================================
package command
import (
"errors"
"os"
"path/filepath"
"runtime"
"strings"
"github.com/Jabba-Team/jabba/cfg"
"github.com/Jabba-Team/jabba/semver"
log "github.com/sirupsen/logrus"
)
func Link(selector string, dir string) error {
if !strings.HasPrefix(selector, "system@") {
return errors.New("Name must begin with 'system@' (e.g. 'system@1.8.73')")
}
// <version> has to be valid per semver
if _, err := semver.ParseVersion(selector); err != nil {
return err
}
if dir == "" {
ver, err := LsBestMatch(selector)
if err != nil {
return err
}
return os.Remove(filepath.Join(cfg.Dir(), "jdk", ver))
} else {
if err := assertJavaDistribution(dir, runtime.GOOS); err != nil {
return err
}
if err := os.MkdirAll(filepath.Join(cfg.Dir(), "jdk"), 0755); err != nil {
return err
}
return os.Symlink(dir, filepath.Join(cfg.Dir(), "jdk", selector))
}
}
func LinkLatest() error {
files, _ := readDir(filepath.Join(cfg.Dir(), "jdk"))
var vs, err = Ls()
if err != nil {
return err
}
cache := make(map[string]string)
for _, f := range files {
if f.IsDir() || f.Mode()&os.ModeSymlink == os.ModeSymlink {
sourceVersion := f.Name()
if strings.Count(sourceVersion, ".") == 1 && !strings.HasPrefix(sourceVersion, "system@") {
target := GetLink(sourceVersion)
_, err := LsBestMatchWithVersionSlice(vs, sourceVersion)
if err != nil {
err := os.Remove(filepath.Join(cfg.Dir(), "jdk", sourceVersion))
if err == nil {
log.Info(sourceVersion + " -/> " + target)
}
if !os.IsNotExist(err) {
return err
}
} else {
cache[sourceVersion] = target
}
}
}
}
for _, v := range semver.VersionSlice(vs).TrimTo(semver.VPMinor) {
sourceVersion := v.TrimTo(semver.VPMinor)
target := filepath.Join(cfg.Dir(), "jdk", v.String())
if v.Prerelease() == "" && cache[sourceVersion] != target && !strings.HasPrefix(sourceVersion, "system@") {
source := filepath.Join(cfg.Dir(), "jdk", sourceVersion)
log.Info(sourceVersion + " -> " + target)
os.Remove(source)
if err := os.Symlink(target, source); err != nil {
return err
}
}
}
return linkAlias("default", vs)
}
func LinkAlias(name string) error {
var vs, err = Ls()
if err != nil {
return err
}
return linkAlias(name, vs)
}
func linkAlias(name string, vs []*semver.Version) error {
defaultAlias := GetAlias(name)
if defaultAlias != "" {
defaultAlias, _ = LsBestMatchWithVersionSlice(vs, defaultAlias)
}
sourceRef := /*"alias@" + */ name
source := filepath.Join(cfg.Dir(), "jdk", sourceRef)
sourceTarget := GetLink(sourceRef)
if defaultAlias != "" {
target := filepath.Join(cfg.Dir(), "jdk", defaultAlias)
if sourceTarget != target {
log.Info(sourceRef + " -> " + target)
os.Remove(source)
if err := os.Symlink(target, source); err != nil {
return err
}
}
} else {
err := os.Remove(source)
if err == nil {
log.Info(sourceRef + " -/> " + sourceTarget)
}
if !os.IsNotExist(err) {
return err
}
}
return nil
}
func GetLink(name string) string {
res, err := filepath.EvalSymlinks(filepath.Join(cfg.Dir(), "jdk", name))
if err != nil {
return ""
}
return res
}
================================================
FILE: command/ls-alias.go
================================================
package command
import (
"io/ioutil"
"path/filepath"
"github.com/Jabba-Team/jabba/cfg"
"strings"
)
func LsAlias() (map[string]string, error) {
dir := cfg.Dir() // Assuming cfg.Dir() provides the directory containing alias files
files, err := ioutil.ReadDir(dir)
if err != nil {
return nil, err
}
aliases := make(map[string]string)
for _, file := range files {
if strings.HasSuffix(file.Name(), ".alias") {
filePath := filepath.Join(dir, file.Name())
content, err := ioutil.ReadFile(filePath)
if err != nil {
return nil, err
}
name := strings.TrimSuffix(file.Name(), ".alias")
aliases[name] = string(content)
}
}
return aliases, nil
}
================================================
FILE: command/ls-remote.go
================================================
package command
import (
"encoding/json"
"errors"
"io/ioutil"
"net/http"
"strconv"
"strings"
"github.com/Jabba-Team/jabba/cfg"
"github.com/Jabba-Team/jabba/semver"
)
type byOS map[string]byArch
type byArch map[string]byDistribution
type byDistribution map[string]map[string]string
func LsRemote(os, arch string) (map[*semver.Version]string, error) {
cnt, err := fetch(cfg.Index())
if err != nil {
return nil, err
}
var index byOS
err = json.Unmarshal(cnt, &index)
if err != nil {
return nil, err
}
releaseMap := make(map[*semver.Version]string)
for key, value := range index[os][arch] {
var prefix string
if key != "jdk" {
if !strings.Contains(key, "@") {
continue
}
prefix = key[strings.Index(key, "@")+1:] + "@"
}
for ver, url := range value {
v, err := semver.ParseVersion(prefix + ver)
if err != nil {
return nil, err
}
releaseMap[v] = url
}
}
return releaseMap, nil
}
func fetch(url string) (content []byte, err error) {
client := http.Client{Transport: RedirectTracer{}}
res, err := client.Get(url)
if err != nil {
return
}
defer res.Body.Close()
if res.StatusCode >= 400 {
return nil, errors.New("GET " + url + " returned " + strconv.Itoa(res.StatusCode))
}
content, err = ioutil.ReadAll(res.Body)
if err != nil {
return
}
return
}
================================================
FILE: command/ls.go
================================================
package command
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sort"
"strings"
"github.com/Jabba-Team/jabba/cfg"
"github.com/Jabba-Team/jabba/semver"
)
var readDir = ioutil.ReadDir
func Ls() ([]*semver.Version, error) {
files, _ := readDir(filepath.Join(cfg.Dir(), "jdk"))
var r []*semver.Version
for _, f := range files {
if f.IsDir() || (f.Mode()&os.ModeSymlink == os.ModeSymlink && strings.HasPrefix(f.Name(), "system@")) {
v, err := semver.ParseVersion(f.Name())
if err != nil {
return nil, err
}
r = append(r, v)
}
}
sort.Sort(sort.Reverse(semver.VersionSlice(r)))
return r, nil
}
func LsBestMatch(selector string) (ver string, err error) {
vs, err := Ls()
if err != nil {
return
}
return LsBestMatchWithVersionSlice(vs, selector)
}
func LsBestMatchWithVersionSlice(vs []*semver.Version, selector string) (ver string, err error) {
rng, err := semver.ParseRange(selector)
if err != nil {
return
}
for _, v := range vs {
if rng.Contains(v) {
ver = v.String()
break
}
}
if ver == "" {
err = fmt.Errorf("%s isn't installed", rng)
}
return
}
================================================
FILE: command/uninstall.go
================================================
package command
import (
"os"
"path/filepath"
"github.com/Jabba-Team/jabba/cfg"
)
func Uninstall(selector string) error {
ver, err := LsBestMatch(selector)
if err != nil {
return err
}
return os.RemoveAll(filepath.Join(cfg.Dir(), "jdk", ver))
}
================================================
FILE: command/use.go
================================================
package command
import (
"os"
"path/filepath"
"regexp"
"runtime"
"github.com/Jabba-Team/jabba/cfg"
)
func Use(selector string) ([]string, error) {
aliasValue := GetAlias(selector)
if aliasValue != "" {
selector = aliasValue
}
ver, err := LsBestMatch(selector)
if err != nil {
return nil, err
}
return usePath(filepath.Join(cfg.Dir(), "jdk", ver))
}
func usePath(path string) ([]string, error) {
path, err := filepath.Abs(path)
if err != nil {
return nil, err
}
pth, _ := os.LookupEnv("PATH")
rgxp := regexp.MustCompile(regexp.QuoteMeta(filepath.Join(cfg.Dir(), "jdk")) + "[^:]+[:]")
// strip references to ~/.jabba/jdk/*, otherwise leave unchanged
pth = rgxp.ReplaceAllString(pth, "")
if runtime.GOOS == "darwin" {
path = filepath.Join(path, "Contents", "Home")
}
systemJavaHome, overrideWasSet := os.LookupEnv("JAVA_HOME_BEFORE_JABBA")
if !overrideWasSet {
systemJavaHome, _ = os.LookupEnv("JAVA_HOME")
}
return []string{
"export PATH=\"" + filepath.Join(path, "bin") + string(os.PathListSeparator) + pth + "\"",
"export JAVA_HOME=\"" + path + "\"",
"export JAVA_HOME_BEFORE_JABBA=\"" + systemJavaHome + "\"",
}, nil
}
================================================
FILE: command/use_test.go
================================================
package command
import (
"os"
"reflect"
"runtime"
"testing"
"time"
"github.com/Jabba-Team/jabba/cfg"
)
type FileInfoMock string
func (f FileInfoMock) Name() string { return string(f) }
func (f FileInfoMock) Size() int64 { return 0 }
func (f FileInfoMock) Mode() os.FileMode { return os.FileMode(0) }
func (f FileInfoMock) ModTime() time.Time { return time.Time{} }
func (f FileInfoMock) IsDir() bool { return true }
func (f FileInfoMock) Sys() interface{} { return nil }
func TestUse(t *testing.T) {
prevPath := os.Getenv("PATH")
defer func() { os.Setenv("PATH", prevPath) }()
var prevReadDir = readDir
defer func() { readDir = prevReadDir }()
readDir = func(dirname string) ([]os.FileInfo, error) {
return []os.FileInfo{
FileInfoMock("1.6.0"), FileInfoMock("1.7.0"), FileInfoMock("1.7.2"), FileInfoMock("1.8.0"),
}, nil
}
os.Setenv("PATH", "/usr/local/bin:"+cfg.Dir()+"/jdk/1.6.0/bin:/usr/bin")
os.Setenv("JAVA_HOME", "/system-jdk")
actual, err := Use("1.7")
if err != nil {
t.Fatalf("err: %v", err)
}
var suffix string
if runtime.GOOS == "darwin" {
suffix = "/Contents/Home"
}
expected := []string{
"export PATH=\"" + cfg.Dir() + "/jdk/1.7.2" + suffix + "/bin:/usr/local/bin:/usr/bin\"",
"export JAVA_HOME=\"" + cfg.Dir() + "/jdk/1.7.2" + suffix + "\"",
"export JAVA_HOME_BEFORE_JABBA=\"/system-jdk\"",
}
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("actual: %v != expected: %v", actual, expected)
}
}
================================================
FILE: command/which.go
================================================
package command
import (
"path/filepath"
"runtime"
"github.com/Jabba-Team/jabba/cfg"
)
func Which(selector string, home bool) (string, error) {
aliasValue := GetAlias(selector)
if aliasValue != "" {
selector = aliasValue
}
ver, err := LsBestMatch(selector)
if err != nil {
return "", err
}
path := filepath.Join(cfg.Dir(), "jdk", ver)
if home && runtime.GOOS == "darwin" {
path = filepath.Join(path, "Contents", "Home")
}
return path, nil
}
================================================
FILE: go.mod
================================================
module github.com/Jabba-Team/jabba
go 1.26
require (
github.com/Masterminds/semver v1.5.0
github.com/hashicorp/go-rootcerts v1.0.2
github.com/mitchellh/go-homedir v1.1.0
github.com/mitchellh/ioprogress v0.0.0-20180201004757-6a23b12fa88e
github.com/sirupsen/logrus v1.9.4
github.com/spf13/cobra v1.10.2
github.com/spf13/pflag v1.0.10
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8
gopkg.in/yaml.v2 v2.4.0
)
require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/stretchr/testify v1.11.1 // indirect
golang.org/x/sys v0.41.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
)
================================================
FILE: go.sum
================================================
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/ioprogress v0.0.0-20180201004757-6a23b12fa88e h1:Qa6dnn8DlasdXRnacluu8HzPts0S1I9zvvUPDbBnXFI=
github.com/mitchellh/ioprogress v0.0.0-20180201004757-6a23b12fa88e/go.mod h1:waEya8ee1Ro/lgxpVhkJI4BVASzkm3UZqkx/cFJiYHM=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
================================================
FILE: index.json
================================================
{
"windows": {
"386": {
"jdk@zulu": {
"10": "zip+https://api.foojay.io/disco/v3.0/ids/23ecc0ec4e860d0127373a80328145f8/redirect",
"12": "zip+https://api.foojay.io/disco/v3.0/ids/8aa2955c453a56ead2ae31c4165d1480/redirect",
"13": "zip+https://api.foojay.io/disco/v3.0/ids/e5b2847946570e5f8511e7e76bac23f5/redirect",
"14": "zip+https://api.foojay.io/disco/v3.0/ids/5f2a3b5fb17a80a7210cc097573226da/redirect",
"15": "zip+https://api.foojay.io/disco/v3.0/ids/90d1ed7aae0f01ac68f60b334559daa5/redirect",
"16": "zip+https://api.foojay.io/disco/v3.0/ids/9fb9ce98c2c6937b1d986bd653222d86/redirect",
"17": "zip+https://api.foojay.io/disco/v3.0/ids/065a19bf3d8444ec1cb5ec8cf3b2e2f3/redirect",
"18": "zip+https://api.foojay.io/disco/v3.0/ids/2fdfffdd45552719fb5397a2c796620f/redirect",
"19": "zip+https://api.foojay.io/disco/v3.0/ids/bca42e53b3612bfc2ceee8efed984a1b/redirect",
"18.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/364db374000c36d8837c5d30b64e1ec8/redirect",
"18.0.2-1": "zip+https://api.foojay.io/disco/v3.0/ids/4f262c0846e3c86d5235efd462750ef3/redirect",
"18.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/45bf4a7cf5b44c7122c23788c1609944/redirect",
"17.0.6": "zip+https://api.foojay.io/disco/v3.0/ids/7250757230621915f86459dad6b9df58/redirect",
"17.0.5": "zip+https://api.foojay.io/disco/v3.0/ids/3846ae3630ba79939a44c3b1bf37d985/redirect",
"17.0.4": "zip+https://api.foojay.io/disco/v3.0/ids/b92ff35d171cf1e2e482801bb4e5ba12/redirect",
"17.0.4-1": "zip+https://api.foojay.io/disco/v3.0/ids/c27fcb6bff079d0e624d819cb5e0e0ff/redirect",
"17.0.3": "zip+https://api.foojay.io/disco/v3.0/ids/45ee0b986d9fa121ecdcd35212cc4af3/redirect",
"17.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/e614315bffb51c258435c92e7de35e80/redirect",
"17.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/6f1c77b7fbc36302574d1d0b16291309/redirect",
"16.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/16a2f73505ad38e3243365cc29675401/redirect",
"16.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/1c4614241dd6f4dbb2c7aeff0163a310/redirect",
"15.0.10": "zip+https://api.foojay.io/disco/v3.0/ids/1f2778551d97c1ed7998fe488975eb4c/redirect",
"15.0.9": "zip+https://api.foojay.io/disco/v3.0/ids/695d760d8a700c207ede8903de141db4/redirect",
"15.0.8": "zip+https://api.foojay.io/disco/v3.0/ids/c57e465bd132b57a90f6ae3b93eb492a/redirect",
"15.0.7": "zip+https://api.foojay.io/disco/v3.0/ids/d84a058abce452ca958cd90f86d84e8c/redirect",
"15.0.6": "zip+https://api.foojay.io/disco/v3.0/ids/aa3fd90ade199f885e80994c7f965505/redirect",
"15.0.5": "zip+https://api.foojay.io/disco/v3.0/ids/0598daa8760b9564e42957a096de80ec/redirect",
"15.0.4": "zip+https://api.foojay.io/disco/v3.0/ids/5a1e545299f9a159ae2ed1d2ce848543/redirect",
"15.0.3": "zip+https://api.foojay.io/disco/v3.0/ids/df32f900b3a8c4b5a2924e557a34cf5f/redirect",
"15.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/f0b43d46f96f3cda3d839f2c7e68baba/redirect",
"15.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/2d46a7278821e06ae750ed60543c0d6d/redirect",
"14.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/b2b65ce6716615c9ee53db021aa473ff/redirect",
"14.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/58ebaec1b0b34eac1a2d05e308ed1640/redirect",
"13.0.14": "zip+https://api.foojay.io/disco/v3.0/ids/40002d0ff7f257b0356d000253ab1a88/redirect",
"13.0.13": "zip+https://api.foojay.io/disco/v3.0/ids/b365fe658222b71a58311a9f33e08e66/redirect",
"13.0.12": "zip+https://api.foojay.io/disco/v3.0/ids/742a5d47f33f5dc3c3079a5e246c6db1/redirect",
"13.0.11": "zip+https://api.foojay.io/disco/v3.0/ids/33665c855a3cea4ea3b739bbf6031f33/redirect",
"13.0.10": "zip+https://api.foojay.io/disco/v3.0/ids/706ff7f71ff3494e9bb5a1199bd747c7/redirect",
"13.0.9": "zip+https://api.foojay.io/disco/v3.0/ids/5d0b2993ad5d866920cb46101d810eb5/redirect",
"13.0.8": "zip+https://api.foojay.io/disco/v3.0/ids/ecd52e25e7cbd4374b839e8ecbaaf485/redirect",
"13.0.7": "zip+https://api.foojay.io/disco/v3.0/ids/34296f3f7314f2a5c78d0f0ec1f1720b/redirect",
"13.0.6": "zip+https://api.foojay.io/disco/v3.0/ids/0f0af3170330bfe9661e6fb357ab42ad/redirect",
"13.0.5": "zip+https://api.foojay.io/disco/v3.0/ids/d27211505e4c8177d8aa01b94102e93b/redirect",
"13.0.5-1": "zip+https://api.foojay.io/disco/v3.0/ids/9e13ff20c393d466271aa2fda38ac6d5/redirect",
"13.0.4": "zip+https://api.foojay.io/disco/v3.0/ids/b68a4045d8a33ca7fd800db288feaa4f/redirect",
"13.0.3": "zip+https://api.foojay.io/disco/v3.0/ids/836c07ff0a691ba2b71cf65746a1493a/redirect",
"13.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/42947df3a1df6ffce17f469e28811e0d/redirect",
"13.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/3f2172c7eb5179efe420ff8a7fb9fb29/redirect",
"12.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/bca74ccf09db4e49c5b6549f469b8e76/redirect",
"12.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/7e816c07ae23acd64b1d8a6fb6df39f5/redirect",
"11.0.18": "zip+https://api.foojay.io/disco/v3.0/ids/2ed74558b1821268a25859d363e21802/redirect",
"11.0.17": "zip+https://api.foojay.io/disco/v3.0/ids/f8861606f0c14ac810b9967b7273f4be/redirect",
"11.0.16": "zip+https://api.foojay.io/disco/v3.0/ids/38c66e38b76436bea673185e317a3467/redirect",
"11.0.16-1": "zip+https://api.foojay.io/disco/v3.0/ids/fa053afdcc1f3122b4786d7211720b48/redirect",
"11.0.15": "zip+https://api.foojay.io/disco/v3.0/ids/99acf9f9f0c792639b1d54134dd7bec8/redirect",
"11.0.14": "zip+https://api.foojay.io/disco/v3.0/ids/4e58194731fc2320b42a827758c686bd/redirect",
"11.0.14-1": "zip+https://api.foojay.io/disco/v3.0/ids/eb3d6458d4a0ed8bd77ce8f676153a3b/redirect",
"11.0.13": "zip+https://api.foojay.io/disco/v3.0/ids/f4756800129af95d52b47745888f4872/redirect",
"11.0.12": "zip+https://api.foojay.io/disco/v3.0/ids/062cacc592d9fd3a5138de01d6d4804c/redirect",
"11.0.11": "zip+https://api.foojay.io/disco/v3.0/ids/4a6cc75d0c62bda20efd0b4c64d21a39/redirect",
"11.0.10": "zip+https://api.foojay.io/disco/v3.0/ids/0d5e8819d4e586c6a2526e701235e60b/redirect",
"11.0.9": "zip+https://api.foojay.io/disco/v3.0/ids/054d45edbabdb49425a0badd500819e5/redirect",
"11.0.9-1": "zip+https://api.foojay.io/disco/v3.0/ids/35ff0568d96ace02278159ad13ed0d93/redirect",
"11.0.8": "zip+https://api.foojay.io/disco/v3.0/ids/1fe6b8b712cb452ebf3d2e7c29d7b70b/redirect",
"11.0.7": "zip+https://api.foojay.io/disco/v3.0/ids/a86248a31a005f08dcacfb9a4d3985bc/redirect",
"11.0.6": "zip+https://api.foojay.io/disco/v3.0/ids/f6b8f08994af7317e5901e229998a7de/redirect",
"10.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/38c9f9721df93c4e714bd04a2f7409e3/redirect",
"10.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/74ad24d23d5749f010f5db474a90857f/redirect",
"9.0.7": "zip+https://api.foojay.io/disco/v3.0/ids/318066f07f4f04dac226869a8a569a41/redirect",
"9.0.4": "zip+https://api.foojay.io/disco/v3.0/ids/b0e84068ed479f066871c1f076e8d64c/redirect",
"9.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/d90dfd9f8b0909d860c6e6918db00550/redirect",
"8.0.362": "zip+https://api.foojay.io/disco/v3.0/ids/1354f1fb5425bdf0f2299eabf04a663f/redirect",
"8.0.352": "zip+https://api.foojay.io/disco/v3.0/ids/8b3043e5fcde2bff68cf3e1758287626/redirect",
"8.0.345": "zip+https://api.foojay.io/disco/v3.0/ids/7c85db80b564a37ab13ce61dd327d9e0/redirect",
"8.0.342": "zip+https://api.foojay.io/disco/v3.0/ids/5dd79da37d94c381f69587de580e040d/redirect",
"8.0.332": "zip+https://api.foojay.io/disco/v3.0/ids/322bba16637f79eada5c73163aae4fde/redirect",
"8.0.322": "zip+https://api.foojay.io/disco/v3.0/ids/e7836d42995e0d32899d32a1aa2e946c/redirect",
"8.0.312": "zip+https://api.foojay.io/disco/v3.0/ids/20cf8f86c0729e31d1c05967ce38afc9/redirect",
"8.0.302": "zip+https://api.foojay.io/disco/v3.0/ids/c1ff6479be1861df654b31a951f64a8d/redirect",
"8.0.292": "zip+https://api.foojay.io/disco/v3.0/ids/1ee7d98367fe791c94b7bb4237a6e202/redirect",
"8.0.282": "zip+https://api.foojay.io/disco/v3.0/ids/7571bf550030e6aca1a117f4af4f348f/redirect",
"8.0.275": "zip+https://api.foojay.io/disco/v3.0/ids/45968bc1c0ee3f3ec2bd9e2797fc5f6d/redirect",
"8.0.272": "zip+https://api.foojay.io/disco/v3.0/ids/f69cc2fb753c3167532475d984ff3112/redirect",
"8.0.265": "zip+https://api.foojay.io/disco/v3.0/ids/926c7579238468c82ccdd4f323354373/redirect",
"8.0.262": "zip+https://api.foojay.io/disco/v3.0/ids/19c9b40f2f5e9df0d434d7c30882151d/redirect",
"8.0.252": "zip+https://api.foojay.io/disco/v3.0/ids/7965752125cda9d28eda044324c8050e/redirect",
"8.0.242": "zip+https://api.foojay.io/disco/v3.0/ids/8b68880672a197664bd188f4eed9e3c4/redirect",
"8.0.232": "zip+https://api.foojay.io/disco/v3.0/ids/4407a3ccecf150ba49d5152b98633a02/redirect",
"8.0.222": "zip+https://api.foojay.io/disco/v3.0/ids/c2e91422dc5378f67fbd5525bcce9496/redirect",
"8.0.212": "zip+https://api.foojay.io/disco/v3.0/ids/7dda4cbb5f00f492fa0d291652344e51/redirect",
"8.0.202": "zip+https://api.foojay.io/disco/v3.0/ids/817e076c2be22017b2bd3afdf3a4d574/redirect",
"8.0.201": "zip+https://api.foojay.io/disco/v3.0/ids/595ce3f4b4973f5ebe0bf0b47a723b08/redirect",
"8.0.192": "zip+https://api.foojay.io/disco/v3.0/ids/b135a3afb5f1df250dd3a5bd89495c8a/redirect",
"8.0.181": "zip+https://api.foojay.io/disco/v3.0/ids/ff60f8c749f6d56500bc467a064de1cd/redirect",
"8.0.172": "zip+https://api.foojay.io/disco/v3.0/ids/95ccd28dcaa6b53b3c0f415e01afcfa0/redirect",
"8.0.163": "zip+https://api.foojay.io/disco/v3.0/ids/096108e44bfd7732b84981681ecca994/redirect",
"8.0.162": "zip+https://api.foojay.io/disco/v3.0/ids/4d1b97945525e502fa397b8116defbee/redirect",
"8.0.153": "zip+https://api.foojay.io/disco/v3.0/ids/d4b6b044a226981b557b618b08fd74c4/redirect",
"7.6.0-2": "zip+https://api.foojay.io/disco/v3.0/ids/d95e16ed49c0c6facb87bb83c20878c5/redirect",
"7.0.352": "zip+https://api.foojay.io/disco/v3.0/ids/6e1c71310c5ed90d842f90d88452ff18/redirect",
"7.0.342": "zip+https://api.foojay.io/disco/v3.0/ids/32eaca5224548708e7c188bba8517e71/redirect",
"7.0.332": "zip+https://api.foojay.io/disco/v3.0/ids/82f0004094b8f8c20ff6ccf0f14fda3a/redirect",
"7.0.322": "zip+https://api.foojay.io/disco/v3.0/ids/b4782cb97460f15dd5b4eb96e408c6d4/redirect",
"7.0.312": "zip+https://api.foojay.io/disco/v3.0/ids/e53d7981816140406210488b6d1e72a0/redirect",
"7.0.302": "zip+https://api.foojay.io/disco/v3.0/ids/e5e451dd5987077ef05be43cae4f9f62/redirect",
"7.0.292": "zip+https://api.foojay.io/disco/v3.0/ids/8a0642613c0562f6e2c896dfcc4d0cd8/redirect",
"7.0.285": "zip+https://api.foojay.io/disco/v3.0/ids/83926d18415a8b54c5a45d1022fa4102/redirect",
"7.0.282": "zip+https://api.foojay.io/disco/v3.0/ids/7d6f5c4353c05c430904c527ce5e6d80/redirect",
"7.0.272": "zip+https://api.foojay.io/disco/v3.0/ids/e421d0e8a9b799ac1f6907d4d635cba5/redirect",
"7.0.262": "zip+https://api.foojay.io/disco/v3.0/ids/e32b8737078ca32812e1de788efff801/redirect",
"7.0.252": "zip+https://api.foojay.io/disco/v3.0/ids/5de2683afac169b20587233d3fa8fa24/redirect",
"1.19.0-0": "zip+https://api.foojay.io/disco/v3.0/ids/bca42e53b3612bfc2ceee8efed984a1b/redirect",
"1.18.0-2": "zip+https://api.foojay.io/disco/v3.0/ids/364db374000c36d8837c5d30b64e1ec8/redirect",
"1.18.0-1": "zip+https://api.foojay.io/disco/v3.0/ids/45bf4a7cf5b44c7122c23788c1609944/redirect",
"1.18.0-0": "zip+https://api.foojay.io/disco/v3.0/ids/2fdfffdd45552719fb5397a2c796620f/redirect",
"1.17.0-6": "zip+https://api.foojay.io/disco/v3.0/ids/7250757230621915f86459dad6b9df58/redirect",
"1.17.0-5": "zip+https://api.foojay.io/disco/v3.0/ids/3846ae3630ba79939a44c3b1bf37d985/redirect",
"1.17.0-4": "zip+https://api.foojay.io/disco/v3.0/ids/b92ff35d171cf1e2e482801bb4e5ba12/redirect",
"1.17.0-3": "zip+https://api.foojay.io/disco/v3.0/ids/45ee0b986d9fa121ecdcd35212cc4af3/redirect",
"1.17.0-2": "zip+https://api.foojay.io/disco/v3.0/ids/e614315bffb51c258435c92e7de35e80/redirect",
"1.17.0-1": "zip+https://api.foojay.io/disco/v3.0/ids/6f1c77b7fbc36302574d1d0b16291309/redirect",
"1.17.0-0": "zip+https://api.foojay.io/disco/v3.0/ids/065a19bf3d8444ec1cb5ec8cf3b2e2f3/redirect",
"1.16.0-2": "zip+https://api.foojay.io/disco/v3.0/ids/16a2f73505ad38e3243365cc29675401/redirect",
"1.16.0-1": "zip+https://api.foojay.io/disco/v3.0/ids/1c4614241dd6f4dbb2c7aeff0163a310/redirect",
"1.16.0-0": "zip+https://api.foojay.io/disco/v3.0/ids/9fb9ce98c2c6937b1d986bd653222d86/redirect",
"1.15.0-10": "zip+https://api.foojay.io/disco/v3.0/ids/1f2778551d97c1ed7998fe488975eb4c/redirect",
"1.15.0-9": "zip+https://api.foojay.io/disco/v3.0/ids/695d760d8a700c207ede8903de141db4/redirect",
"1.15.0-8": "zip+https://api.foojay.io/disco/v3.0/ids/c57e465bd132b57a90f6ae3b93eb492a/redirect",
"1.15.0-7": "zip+https://api.foojay.io/disco/v3.0/ids/d84a058abce452ca958cd90f86d84e8c/redirect",
"1.15.0-6": "zip+https://api.foojay.io/disco/v3.0/ids/aa3fd90ade199f885e80994c7f965505/redirect",
"1.15.0-5": "zip+https://api.foojay.io/disco/v3.0/ids/0598daa8760b9564e42957a096de80ec/redirect",
"1.15.0-4": "zip+https://api.foojay.io/disco/v3.0/ids/5a1e545299f9a159ae2ed1d2ce848543/redirect",
"1.15.0-3": "zip+https://api.foojay.io/disco/v3.0/ids/df32f900b3a8c4b5a2924e557a34cf5f/redirect",
"1.15.0-2": "zip+https://api.foojay.io/disco/v3.0/ids/f0b43d46f96f3cda3d839f2c7e68baba/redirect",
"1.15.0-1": "zip+https://api.foojay.io/disco/v3.0/ids/2d46a7278821e06ae750ed60543c0d6d/redirect",
"1.15.0-0": "zip+https://api.foojay.io/disco/v3.0/ids/90d1ed7aae0f01ac68f60b334559daa5/redirect",
"1.14.0": "zip+https://api.foojay.io/disco/v3.0/ids/5f2a3b5fb17a80a7210cc097573226da/redirect",
"1.14.0-2": "zip+https://api.foojay.io/disco/v3.0/ids/b2b65ce6716615c9ee53db021aa473ff/redirect",
"1.14.0-1": "zip+https://api.foojay.io/disco/v3.0/ids/58ebaec1b0b34eac1a2d05e308ed1640/redirect",
"1.14.0-0": "zip+https://api.foojay.io/disco/v3.0/ids/16005727e2e62e2124522d86fb2901c5/redirect",
"1.13.0": "zip+https://api.foojay.io/disco/v3.0/ids/e5b2847946570e5f8511e7e76bac23f5/redirect",
"1.13.0-14": "zip+https://api.foojay.io/disco/v3.0/ids/40002d0ff7f257b0356d000253ab1a88/redirect",
"1.13.0-13": "zip+https://api.foojay.io/disco/v3.0/ids/b365fe658222b71a58311a9f33e08e66/redirect",
"1.13.0-12": "zip+https://api.foojay.io/disco/v3.0/ids/742a5d47f33f5dc3c3079a5e246c6db1/redirect",
"1.13.0-11": "zip+https://api.foojay.io/disco/v3.0/ids/33665c855a3cea4ea3b739bbf6031f33/redirect",
"1.13.0-10": "zip+https://api.foojay.io/disco/v3.0/ids/706ff7f71ff3494e9bb5a1199bd747c7/redirect",
"1.13.0-9": "zip+https://api.foojay.io/disco/v3.0/ids/5d0b2993ad5d866920cb46101d810eb5/redirect",
"1.13.0-8": "zip+https://api.foojay.io/disco/v3.0/ids/ecd52e25e7cbd4374b839e8ecbaaf485/redirect",
"1.13.0-7": "zip+https://api.foojay.io/disco/v3.0/ids/34296f3f7314f2a5c78d0f0ec1f1720b/redirect",
"1.13.0-6": "zip+https://api.foojay.io/disco/v3.0/ids/0f0af3170330bfe9661e6fb357ab42ad/redirect",
"1.13.0-5": "zip+https://api.foojay.io/disco/v3.0/ids/d27211505e4c8177d8aa01b94102e93b/redirect",
"1.13.0-4": "zip+https://api.foojay.io/disco/v3.0/ids/b68a4045d8a33ca7fd800db288feaa4f/redirect",
"1.13.0-3": "zip+https://api.foojay.io/disco/v3.0/ids/836c07ff0a691ba2b71cf65746a1493a/redirect",
"1.13.0-2": "zip+https://api.foojay.io/disco/v3.0/ids/42947df3a1df6ffce17f469e28811e0d/redirect",
"1.13.0-1": "zip+https://api.foojay.io/disco/v3.0/ids/3f2172c7eb5179efe420ff8a7fb9fb29/redirect",
"1.13.0-0": "zip+https://api.foojay.io/disco/v3.0/ids/566f70a9aef925855a3c818c0ff93a8a/redirect",
"1.12.0": "zip+https://api.foojay.io/disco/v3.0/ids/8aa2955c453a56ead2ae31c4165d1480/redirect",
"1.12.0-2": "zip+https://api.foojay.io/disco/v3.0/ids/bca74ccf09db4e49c5b6549f469b8e76/redirect",
"1.12.0-1": "zip+https://api.foojay.io/disco/v3.0/ids/7e816c07ae23acd64b1d8a6fb6df39f5/redirect",
"1.12.0-0": "zip+https://api.foojay.io/disco/v3.0/ids/edcfdc2e0224904b96aae6df7ff3b294/redirect",
"1.11.0-18": "zip+https://api.foojay.io/disco/v3.0/ids/2ed74558b1821268a25859d363e21802/redirect",
"1.11.0-17": "zip+https://api.foojay.io/disco/v3.0/ids/f8861606f0c14ac810b9967b7273f4be/redirect",
"1.11.0-16": "zip+https://api.foojay.io/disco/v3.0/ids/38c66e38b76436bea673185e317a3467/redirect",
"1.11.0-15": "zip+https://api.foojay.io/disco/v3.0/ids/99acf9f9f0c792639b1d54134dd7bec8/redirect",
"1.11.0-14": "zip+https://api.foojay.io/disco/v3.0/ids/4e58194731fc2320b42a827758c686bd/redirect",
"1.11.0-13": "zip+https://api.foojay.io/disco/v3.0/ids/f4756800129af95d52b47745888f4872/redirect",
"1.11.0-12": "zip+https://api.foojay.io/disco/v3.0/ids/062cacc592d9fd3a5138de01d6d4804c/redirect",
"1.11.0-11": "zip+https://api.foojay.io/disco/v3.0/ids/4a6cc75d0c62bda20efd0b4c64d21a39/redirect",
"1.11.0-10": "zip+https://api.foojay.io/disco/v3.0/ids/0d5e8819d4e586c6a2526e701235e60b/redirect",
"1.11.0-9": "zip+https://api.foojay.io/disco/v3.0/ids/054d45edbabdb49425a0badd500819e5/redirect",
"1.11.0-8": "zip+https://api.foojay.io/disco/v3.0/ids/1fe6b8b712cb452ebf3d2e7c29d7b70b/redirect",
"1.11.0-7": "zip+https://api.foojay.io/disco/v3.0/ids/a86248a31a005f08dcacfb9a4d3985bc/redirect",
"1.11.0-6": "zip+https://api.foojay.io/disco/v3.0/ids/f6b8f08994af7317e5901e229998a7de/redirect",
"1.10.0": "zip+https://api.foojay.io/disco/v3.0/ids/23ecc0ec4e860d0127373a80328145f8/redirect",
"1.10.0-2": "zip+https://api.foojay.io/disco/v3.0/ids/38c9f9721df93c4e714bd04a2f7409e3/redirect",
"1.10.0-1": "zip+https://api.foojay.io/disco/v3.0/ids/74ad24d23d5749f010f5db474a90857f/redirect",
"1.10.0-0": "zip+https://api.foojay.io/disco/v3.0/ids/92948e4055c68e03a727642a4f81093d/redirect",
"1.9.0-7": "zip+https://api.foojay.io/disco/v3.0/ids/318066f07f4f04dac226869a8a569a41/redirect",
"1.9.0-4": "zip+https://api.foojay.io/disco/v3.0/ids/b0e84068ed479f066871c1f076e8d64c/redirect",
"1.9.0-1": "zip+https://api.foojay.io/disco/v3.0/ids/d90dfd9f8b0909d860c6e6918db00550/redirect",
"1.8.362": "zip+https://api.foojay.io/disco/v3.0/ids/1354f1fb5425bdf0f2299eabf04a663f/redirect",
"1.8.352": "zip+https://api.foojay.io/disco/v3.0/ids/8b3043e5fcde2bff68cf3e1758287626/redirect",
"1.8.345": "zip+https://api.foojay.io/disco/v3.0/ids/7c85db80b564a37ab13ce61dd327d9e0/redirect",
"1.8.342": "zip+https://api.foojay.io/disco/v3.0/ids/5dd79da37d94c381f69587de580e040d/redirect",
"1.8.332": "zip+https://api.foojay.io/disco/v3.0/ids/322bba16637f79eada5c73163aae4fde/redirect",
"1.8.322": "zip+https://api.foojay.io/disco/v3.0/ids/e7836d42995e0d32899d32a1aa2e946c/redirect",
"1.8.312": "zip+https://api.foojay.io/disco/v3.0/ids/20cf8f86c0729e31d1c05967ce38afc9/redirect",
"1.8.302": "zip+https://api.foojay.io/disco/v3.0/ids/c1ff6479be1861df654b31a951f64a8d/redirect",
"1.8.292": "zip+https://api.foojay.io/disco/v3.0/ids/1ee7d98367fe791c94b7bb4237a6e202/redirect",
"1.8.282": "zip+https://api.foojay.io/disco/v3.0/ids/7571bf550030e6aca1a117f4af4f348f/redirect",
"1.8.275": "zip+https://api.foojay.io/disco/v3.0/ids/45968bc1c0ee3f3ec2bd9e2797fc5f6d/redirect",
"1.8.272": "zip+https://api.foojay.io/disco/v3.0/ids/f69cc2fb753c3167532475d984ff3112/redirect",
"1.8.265": "zip+https://api.foojay.io/disco/v3.0/ids/926c7579238468c82ccdd4f323354373/redirect",
"1.8.262": "zip+https://api.foojay.io/disco/v3.0/ids/19c9b40f2f5e9df0d434d7c30882151d/redirect",
"1.8.252": "zip+https://api.foojay.io/disco/v3.0/ids/7965752125cda9d28eda044324c8050e/redirect",
"1.8.242": "zip+https://api.foojay.io/disco/v3.0/ids/8b68880672a197664bd188f4eed9e3c4/redirect",
"1.8.232": "zip+https://api.foojay.io/disco/v3.0/ids/4407a3ccecf150ba49d5152b98633a02/redirect",
"1.8.222": "zip+https://api.foojay.io/disco/v3.0/ids/c2e91422dc5378f67fbd5525bcce9496/redirect",
"1.8.212": "zip+https://api.foojay.io/disco/v3.0/ids/7dda4cbb5f00f492fa0d291652344e51/redirect",
"1.8.202": "zip+https://api.foojay.io/disco/v3.0/ids/817e076c2be22017b2bd3afdf3a4d574/redirect",
"1.8.201": "zip+https://api.foojay.io/disco/v3.0/ids/595ce3f4b4973f5ebe0bf0b47a723b08/redirect",
"1.8.192": "zip+https://api.foojay.io/disco/v3.0/ids/b135a3afb5f1df250dd3a5bd89495c8a/redirect",
"1.8.181": "zip+https://api.foojay.io/disco/v3.0/ids/ff60f8c749f6d56500bc467a064de1cd/redirect",
"1.8.172": "zip+https://api.foojay.io/disco/v3.0/ids/95ccd28dcaa6b53b3c0f415e01afcfa0/redirect",
"1.8.163": "zip+https://api.foojay.io/disco/v3.0/ids/096108e44bfd7732b84981681ecca994/redirect",
"1.8.162": "zip+https://api.foojay.io/disco/v3.0/ids/4d1b97945525e502fa397b8116defbee/redirect",
"1.8.153": "zip+https://api.foojay.io/disco/v3.0/ids/d4b6b044a226981b557b618b08fd74c4/redirect",
"1.7.6-0": "zip+https://api.foojay.io/disco/v3.0/ids/d95e16ed49c0c6facb87bb83c20878c5/redirect",
"1.7.0-352": "zip+https://api.foojay.io/disco/v3.0/ids/6e1c71310c5ed90d842f90d88452ff18/redirect",
"1.7.0-342": "zip+https://api.foojay.io/disco/v3.0/ids/32eaca5224548708e7c188bba8517e71/redirect",
"1.7.0-332": "zip+https://api.foojay.io/disco/v3.0/ids/82f0004094b8f8c20ff6ccf0f14fda3a/redirect",
"1.7.0-322": "zip+https://api.foojay.io/disco/v3.0/ids/b4782cb97460f15dd5b4eb96e408c6d4/redirect",
"1.7.0-312": "zip+https://api.foojay.io/disco/v3.0/ids/e53d7981816140406210488b6d1e72a0/redirect",
"1.7.0-302": "zip+https://api.foojay.io/disco/v3.0/ids/e5e451dd5987077ef05be43cae4f9f62/redirect",
"1.7.0-292": "zip+https://api.foojay.io/disco/v3.0/ids/8a0642613c0562f6e2c896dfcc4d0cd8/redirect",
"1.7.0-285": "zip+https://api.foojay.io/disco/v3.0/ids/83926d18415a8b54c5a45d1022fa4102/redirect",
"1.7.0-282": "zip+https://api.foojay.io/disco/v3.0/ids/7d6f5c4353c05c430904c527ce5e6d80/redirect",
"1.7.0-272": "zip+https://api.foojay.io/disco/v3.0/ids/e421d0e8a9b799ac1f6907d4d635cba5/redirect",
"1.7.0-262": "zip+https://api.foojay.io/disco/v3.0/ids/e32b8737078ca32812e1de788efff801/redirect",
"1.7.0-252": "zip+https://api.foojay.io/disco/v3.0/ids/5de2683afac169b20587233d3fa8fa24/redirect"
},
"jdk@temurin": {
"17": "zip+https://api.foojay.io/disco/v3.0/ids/a661fade6248009584332a3d128586c4/redirect",
"18": "zip+https://api.foojay.io/disco/v3.0/ids/414019c455928f9ca02d569fc969279c/redirect",
"19": "zip+https://api.foojay.io/disco/v3.0/ids/39bc43025c2569bea9f234260e0428da/redirect",
"19.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/953c46a4d0125af225aafa7b0348ee1b/redirect",
"19.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/86c8433246f3de199626a5baca29c7bb/redirect",
"18.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/42cdc2341133b795c28c279abeb3fe4e/redirect",
"18.0.2-1": "zip+https://api.foojay.io/disco/v3.0/ids/c82f04c849f45bda9c6b7928a81a23d3/redirect",
"18.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/ed73aaa972210c735ea2bc32aa56a391/redirect",
"17.0.6": "zip+https://api.foojay.io/disco/v3.0/ids/a28e4068e73ac4d72bf7128605d9d349/redirect",
"17.0.5": "zip+https://api.foojay.io/disco/v3.0/ids/9abef4bda168e1c3d08a4c11b0f95d5d/redirect",
"17.0.4": "zip+https://api.foojay.io/disco/v3.0/ids/f531f155b8937948ec9b181cd4c0d294/redirect",
"17.0.4-1": "zip+https://api.foojay.io/disco/v3.0/ids/75d46d08d1ff3a93d2dca021611cf1f9/redirect",
"17.0.3": "zip+https://api.foojay.io/disco/v3.0/ids/9b1d5cef4049f75908833a2c78846bce/redirect",
"17.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/5dbe1edc00d3edac31790d71f12184ca/redirect",
"17.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/dc4885f2240769b829c36bb9f7f2539a/redirect",
"16.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/a223a3e627973f084318bce7e78e915b/redirect",
"11.0.18": "zip+https://api.foojay.io/disco/v3.0/ids/b6f374ccc97853e3b08402a9461ef8af/redirect",
"11.0.17": "zip+https://api.foojay.io/disco/v3.0/ids/5688e6b416d2a3242e25ce4e79bcec4f/redirect",
"11.0.16": "zip+https://api.foojay.io/disco/v3.0/ids/a33251145d8941282e4774d4f4dfe96b/redirect",
"11.0.16-1": "zip+https://api.foojay.io/disco/v3.0/ids/4dafdbd79b220c553593fc155672a4f4/redirect",
"11.0.15": "zip+https://api.foojay.io/disco/v3.0/ids/f5ff8000a8dd5894a4a4419091800dd1/redirect",
"11.0.14": "zip+https://api.foojay.io/disco/v3.0/ids/99ffb3d558ae57ea8ddf0b997ac18d2f/redirect",
"11.0.14-1": "zip+https://api.foojay.io/disco/v3.0/ids/3733c2ae95ac6b6b884a5d35cc1b3b5e/redirect",
"11.0.13": "zip+https://api.foojay.io/disco/v3.0/ids/4eea3e50f7814862682deb409bbef08e/redirect",
"11.0.12": "zip+https://api.foojay.io/disco/v3.0/ids/eb6c306a232ffb430efb933eda5ff91e/redirect",
"8.0.362": "zip+https://api.foojay.io/disco/v3.0/ids/bbe796f9519fa22d9127596cf12677ff/redirect",
"8.0.352": "zip+https://api.foojay.io/disco/v3.0/ids/8c807d6bebfa8528ed70b438603d6617/redirect",
"8.0.345": "zip+https://api.foojay.io/disco/v3.0/ids/5a28e3bc6aadfcd38295b00472f3b5de/redirect",
"8.0.342": "zip+https://api.foojay.io/disco/v3.0/ids/4c818dea8d0a90291cc9da218f7d253b/redirect",
"8.0.332": "zip+https://api.foojay.io/disco/v3.0/ids/04c16456ad9e33b2dce99e35f4707584/redirect",
"8.0.322": "zip+https://api.foojay.io/disco/v3.0/ids/cb8839253ef45a07125b7741fa1a21bf/redirect",
"8.0.312": "zip+https://api.foojay.io/disco/v3.0/ids/3d8089717f7c3646021ed1fd2806b1e0/redirect",
"8.0.302": "zip+https://api.foojay.io/disco/v3.0/ids/9857d9d3dd1719941c814730401a7c55/redirect",
"1.19.0-2": "zip+https://api.foojay.io/disco/v3.0/ids/953c46a4d0125af225aafa7b0348ee1b/redirect",
"1.19.0-1": "zip+https://api.foojay.io/disco/v3.0/ids/86c8433246f3de199626a5baca29c7bb/redirect",
"1.19.0-0": "zip+https://api.foojay.io/disco/v3.0/ids/39bc43025c2569bea9f234260e0428da/redirect",
"1.18.0-2": "zip+https://api.foojay.io/disco/v3.0/ids/42cdc2341133b795c28c279abeb3fe4e/redirect",
"1.18.0-1": "zip+https://api.foojay.io/disco/v3.0/ids/ed73aaa972210c735ea2bc32aa56a391/redirect",
"1.18.0-0": "zip+https://api.foojay.io/disco/v3.0/ids/414019c455928f9ca02d569fc969279c/redirect",
"1.17.0-6": "zip+https://api.foojay.io/disco/v3.0/ids/a28e4068e73ac4d72bf7128605d9d349/redirect",
"1.17.0-5": "zip+https://api.foojay.io/disco/v3.0/ids/9abef4bda168e1c3d08a4c11b0f95d5d/redirect",
"1.17.0-4": "zip+https://api.foojay.io/disco/v3.0/ids/f531f155b8937948ec9b181cd4c0d294/redirect",
"1.17.0-3": "zip+https://api.foojay.io/disco/v3.0/ids/9b1d5cef4049f75908833a2c78846bce/redirect",
"1.17.0-2": "zip+https://api.foojay.io/disco/v3.0/ids/5dbe1edc00d3edac31790d71f12184ca/redirect",
"1.17.0-1": "zip+https://api.foojay.io/disco/v3.0/ids/dc4885f2240769b829c36bb9f7f2539a/redirect",
"1.17.0-0": "zip+https://api.foojay.io/disco/v3.0/ids/a661fade6248009584332a3d128586c4/redirect",
"1.16.0-2": "zip+https://api.foojay.io/disco/v3.0/ids/a223a3e627973f084318bce7e78e915b/redirect",
"1.11.0-18": "zip+https://api.foojay.io/disco/v3.0/ids/b6f374ccc97853e3b08402a9461ef8af/redirect",
"1.11.0-17": "zip+https://api.foojay.io/disco/v3.0/ids/5688e6b416d2a3242e25ce4e79bcec4f/redirect",
"1.11.0-16": "zip+https://api.foojay.io/disco/v3.0/ids/a33251145d8941282e4774d4f4dfe96b/redirect",
"1.11.0-15": "zip+https://api.foojay.io/disco/v3.0/ids/f5ff8000a8dd5894a4a4419091800dd1/redirect",
"1.11.0-14": "zip+https://api.foojay.io/disco/v3.0/ids/99ffb3d558ae57ea8ddf0b997ac18d2f/redirect",
"1.11.0-13": "zip+https://api.foojay.io/disco/v3.0/ids/4eea3e50f7814862682deb409bbef08e/redirect",
"1.11.0-12": "zip+https://api.foojay.io/disco/v3.0/ids/eb6c306a232ffb430efb933eda5ff91e/redirect",
"1.8.0-362": "zip+https://api.foojay.io/disco/v3.0/ids/bbe796f9519fa22d9127596cf12677ff/redirect",
"1.8.0-352": "zip+https://api.foojay.io/disco/v3.0/ids/8c807d6bebfa8528ed70b438603d6617/redirect",
"1.8.0-345": "zip+https://api.foojay.io/disco/v3.0/ids/5a28e3bc6aadfcd38295b00472f3b5de/redirect",
"1.8.0-342": "zip+https://api.foojay.io/disco/v3.0/ids/4c818dea8d0a90291cc9da218f7d253b/redirect",
"1.8.0-332": "zip+https://api.foojay.io/disco/v3.0/ids/04c16456ad9e33b2dce99e35f4707584/redirect",
"1.8.0-322": "zip+https://api.foojay.io/disco/v3.0/ids/cb8839253ef45a07125b7741fa1a21bf/redirect",
"1.8.0-312": "zip+https://api.foojay.io/disco/v3.0/ids/3d8089717f7c3646021ed1fd2806b1e0/redirect",
"1.8.0-302": "zip+https://api.foojay.io/disco/v3.0/ids/9857d9d3dd1719941c814730401a7c55/redirect"
},
"jdk@semeru": {
"8.0.362": "zip+https://api.foojay.io/disco/v3.0/ids/371f033f06150228315a11d988755e28/redirect",
"8.0.352": "zip+https://api.foojay.io/disco/v3.0/ids/7175e22db2db7fb73cb9ffb25f16126a/redirect",
"8.0.345": "zip+https://api.foojay.io/disco/v3.0/ids/f718fbe43978318c341833ea0827c623/redirect",
"8.0.332": "zip+https://api.foojay.io/disco/v3.0/ids/c4277d987d8832d9f5947eca623c59a5/redirect",
"8.0.322": "zip+https://api.foojay.io/disco/v3.0/ids/761ffc5401da3a76d7968bf7186d13d1/redirect",
"8.0.312": "zip+https://api.foojay.io/disco/v3.0/ids/a1a35e3dc9ca126f223a32749ee78745/redirect",
"8.0.302": "zip+https://api.foojay.io/disco/v3.0/ids/89cec191952a26948626fd74b1be138a/redirect",
"1.8.0-362": "zip+https://api.foojay.io/disco/v3.0/ids/371f033f06150228315a11d988755e28/redirect",
"1.8.0-352": "zip+https://api.foojay.io/disco/v3.0/ids/7175e22db2db7fb73cb9ffb25f16126a/redirect",
"1.8.0-345": "zip+https://api.foojay.io/disco/v3.0/ids/f718fbe43978318c341833ea0827c623/redirect",
"1.8.0-332": "zip+https://api.foojay.io/disco/v3.0/ids/c4277d987d8832d9f5947eca623c59a5/redirect",
"1.8.0-322": "zip+https://api.foojay.io/disco/v3.0/ids/761ffc5401da3a76d7968bf7186d13d1/redirect",
"1.8.0-312": "zip+https://api.foojay.io/disco/v3.0/ids/a1a35e3dc9ca126f223a32749ee78745/redirect",
"1.8.0-302": "zip+https://api.foojay.io/disco/v3.0/ids/89cec191952a26948626fd74b1be138a/redirect"
},
"jdk@openlogic": {
"8.0.362": "zip+https://api.foojay.io/disco/v3.0/ids/24a04029a405ae7304255443829f9637/redirect",
"8.0.352": "zip+https://api.foojay.io/disco/v3.0/ids/ae8287fd10bf46eec66eef95945efede/redirect",
"8.0.342": "zip+https://api.foojay.io/disco/v3.0/ids/4f8af0b651561db53521600a0ab98a01/redirect",
"8.0.332": "zip+https://api.foojay.io/disco/v3.0/ids/7d24935ca37aa31868384da374405807/redirect",
"8.0.292": "zip+https://api.foojay.io/disco/v3.0/ids/9636a6199e0cebf8fe99d104b4bfeaa2/redirect",
"8.0.282": "zip+https://api.foojay.io/disco/v3.0/ids/af6c7f07240c9432dd90282e53ca76c6/redirect",
"8.0.272": "zip+https://api.foojay.io/disco/v3.0/ids/53b84c48fe3082e819403a66b529d947/redirect",
"8.0.262": "zip+https://api.foojay.io/disco/v3.0/ids/1323dfd78824dc6776aaea382c2662fc/redirect",
"1.8.0-362": "zip+https://api.foojay.io/disco/v3.0/ids/24a04029a405ae7304255443829f9637/redirect",
"1.8.0-352": "zip+https://api.foojay.io/disco/v3.0/ids/ae8287fd10bf46eec66eef95945efede/redirect",
"1.8.0-342": "zip+https://api.foojay.io/disco/v3.0/ids/4f8af0b651561db53521600a0ab98a01/redirect",
"1.8.0-332": "zip+https://api.foojay.io/disco/v3.0/ids/7d24935ca37aa31868384da374405807/redirect",
"1.8.0-292": "zip+https://api.foojay.io/disco/v3.0/ids/9636a6199e0cebf8fe99d104b4bfeaa2/redirect",
"1.8.0-282": "zip+https://api.foojay.io/disco/v3.0/ids/af6c7f07240c9432dd90282e53ca76c6/redirect",
"1.8.0-272": "zip+https://api.foojay.io/disco/v3.0/ids/53b84c48fe3082e819403a66b529d947/redirect",
"1.8.0-262": "zip+https://api.foojay.io/disco/v3.0/ids/1323dfd78824dc6776aaea382c2662fc/redirect"
},
"jdk@ojdk_build": {
"10.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/4cd3bba786330ec2feb357f1d6332307/redirect",
"10.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/c5435993942948ab29b059e8297e3a0a/redirect",
"9.0.4": "zip+https://api.foojay.io/disco/v3.0/ids/27514fa39b5e9ca57bc083b141d3a551/redirect",
"9.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/66d6e76e222c65597db34a229c20ba6b/redirect",
"8.0.332": "zip+https://api.foojay.io/disco/v3.0/ids/e3960e0a165e8a334c6d13a1a6eaaef6/redirect",
"8.0.322": "zip+https://api.foojay.io/disco/v3.0/ids/6309b0f061f26ca6a134ba95564e4d6c/redirect",
"8.0.312": "zip+https://api.foojay.io/disco/v3.0/ids/c260cfda49f166a78d7b3bbc2249fe0a/redirect",
"8.0.302": "zip+https://api.foojay.io/disco/v3.0/ids/12c50515a2ec6bcf919e975bf1975b03/redirect",
"8.0.292": "zip+https://api.foojay.io/disco/v3.0/ids/3a8dd82fc1fb9a67f1a2fc1ff1a6eb63/redirect",
"8.0.282": "zip+https://api.foojay.io/disco/v3.0/ids/c17b03cd64f5df8b9e7656eee04bd105/redirect",
"8.0.275": "zip+https://api.foojay.io/disco/v3.0/ids/15910f2b5278140cb79dc398219ce7bc/redirect",
"8.0.272": "zip+https://api.foojay.io/disco/v3.0/ids/858c5daa748a4895c80a646f0132f87a/redirect",
"8.0.265": "zip+https://api.foojay.io/disco/v3.0/ids/f6ebd0d19064a50c6d656f1f353e8165/redirect",
"8.0.262": "zip+https://api.foojay.io/disco/v3.0/ids/5d152e2a511c119d8d1dcce587e7358a/redirect",
"8.0.252": "zip+https://api.foojay.io/disco/v3.0/ids/7f7d5e204d99b1663f9e7b3ffc835847/redirect",
"8.0.242": "zip+https://api.foojay.io/disco/v3.0/ids/f6824ac9511409d724c042915b9a1137/redirect",
"8.0.232": "zip+https://api.foojay.io/disco/v3.0/ids/3a7a6e6411fd02898863d8c5cef5fc1f/redirect",
"8.0.222": "zip+https://api.foojay.io/disco/v3.0/ids/fcba2e316cad3484b19909c9facdb45c/redirect",
"8.0.212": "zip+https://api.foojay.io/disco/v3.0/ids/586622517fc467b98da450348ab7723e/redirect",
"8.0.201": "zip+https://api.foojay.io/disco/v3.0/ids/c6838aef6b4a3b0531f2ebe0909ff531/redirect",
"8.0.191": "zip+https://api.foojay.io/disco/v3.0/ids/289fe133fcdef2c4d9fc1e5884280d93/redirect",
"8.0.181": "zip+https://api.foojay.io/disco/v3.0/ids/8439fb8d3d6067b5ccfe760380e274c9/redirect",
"8.0.171": "zip+https://api.foojay.io/disco/v3.0/ids/17eba64ec65b3fc34c33015dca3f47c6/redirect",
"8.0.161": "zip+https://api.foojay.io/disco/v3.0/ids/a5caedd70447dbaa4b957668b0a73734/redirect",
"8.0.151": "zip+https://api.foojay.io/disco/v3.0/ids/ab0f040f61ec36c3b434a0adfcfb979c/redirect",
"8.0.141": "zip+https://api.foojay.io/disco/v3.0/ids/75bc00d1d855ae6eb4267ef4bd5627d5/redirect",
"8.0.131": "zip+https://api.foojay.io/disco/v3.0/ids/bb4246bb62c0362f20fbc60ee4f47812/redirect",
"1.10.0-2": "zip+https://api.foojay.io/disco/v3.0/ids/4cd3bba786330ec2feb357f1d6332307/redirect",
"1.10.0-1": "zip+https://api.foojay.io/disco/v3.0/ids/c5435993942948ab29b059e8297e3a0a/redirect",
"1.9.0-4": "zip+https://api.foojay.io/disco/v3.0/ids/27514fa39b5e9ca57bc083b141d3a551/redirect",
"1.9.0-1": "zip+https://api.foojay.io/disco/v3.0/ids/66d6e76e222c65597db34a229c20ba6b/redirect",
"1.8.0-332": "zip+https://api.foojay.io/disco/v3.0/ids/e3960e0a165e8a334c6d13a1a6eaaef6/redirect",
"1.8.0-322": "zip+https://api.foojay.io/disco/v3.0/ids/6309b0f061f26ca6a134ba95564e4d6c/redirect",
"1.8.0-312": "zip+https://api.foojay.io/disco/v3.0/ids/c260cfda49f166a78d7b3bbc2249fe0a/redirect",
"1.8.0-302": "zip+https://api.foojay.io/disco/v3.0/ids/12c50515a2ec6bcf919e975bf1975b03/redirect",
"1.8.0-292": "zip+https://api.foojay.io/disco/v3.0/ids/3a8dd82fc1fb9a67f1a2fc1ff1a6eb63/redirect",
"1.8.0-282": "zip+https://api.foojay.io/disco/v3.0/ids/c17b03cd64f5df8b9e7656eee04bd105/redirect",
"1.8.0-275": "zip+https://api.foojay.io/disco/v3.0/ids/15910f2b5278140cb79dc398219ce7bc/redirect",
"1.8.0-272": "zip+https://api.foojay.io/disco/v3.0/ids/858c5daa748a4895c80a646f0132f87a/redirect",
"1.8.0-265": "zip+https://api.foojay.io/disco/v3.0/ids/f6ebd0d19064a50c6d656f1f353e8165/redirect",
"1.8.0-262": "zip+https://api.foojay.io/disco/v3.0/ids/5d152e2a511c119d8d1dcce587e7358a/redirect",
"1.8.0-252": "zip+https://api.foojay.io/disco/v3.0/ids/7f7d5e204d99b1663f9e7b3ffc835847/redirect",
"1.8.0-242": "zip+https://api.foojay.io/disco/v3.0/ids/f6824ac9511409d724c042915b9a1137/redirect",
"1.8.0-232": "zip+https://api.foojay.io/disco/v3.0/ids/3a7a6e6411fd02898863d8c5cef5fc1f/redirect",
"1.8.0-222": "zip+https://api.foojay.io/disco/v3.0/ids/fcba2e316cad3484b19909c9facdb45c/redirect",
"1.8.0-212": "zip+https://api.foojay.io/disco/v3.0/ids/586622517fc467b98da450348ab7723e/redirect",
"1.8.0-201": "zip+https://api.foojay.io/disco/v3.0/ids/c6838aef6b4a3b0531f2ebe0909ff531/redirect",
"1.8.0-191": "zip+https://api.foojay.io/disco/v3.0/ids/289fe133fcdef2c4d9fc1e5884280d93/redirect",
"1.8.0-181": "zip+https://api.foojay.io/disco/v3.0/ids/8439fb8d3d6067b5ccfe760380e274c9/redirect",
"1.8.0-171": "zip+https://api.foojay.io/disco/v3.0/ids/17eba64ec65b3fc34c33015dca3f47c6/redirect",
"1.8.0-161": "zip+https://api.foojay.io/disco/v3.0/ids/a5caedd70447dbaa4b957668b0a73734/redirect",
"1.8.0-151": "zip+https://api.foojay.io/disco/v3.0/ids/ab0f040f61ec36c3b434a0adfcfb979c/redirect",
"1.8.0-141": "zip+https://api.foojay.io/disco/v3.0/ids/75bc00d1d855ae6eb4267ef4bd5627d5/redirect",
"1.8.0-131": "zip+https://api.foojay.io/disco/v3.0/ids/bb4246bb62c0362f20fbc60ee4f47812/redirect"
},
"jdk@liberica": {
"12": "zip+https://api.foojay.io/disco/v3.0/ids/93f6a9522852a47e8b72baa4ffb11560/redirect",
"13": "zip+https://api.foojay.io/disco/v3.0/ids/a6b19a0ceac039dc72d63ebb7c1c7f0c/redirect",
"14": "zip+https://api.foojay.io/disco/v3.0/ids/6bba1616bf0f7c295f0fd35003043114/redirect",
"15": "zip+https://api.foojay.io/disco/v3.0/ids/023199eeca0532ed6e1d014d355cb845/redirect",
"16": "zip+https://api.foojay.io/disco/v3.0/ids/faad15dbdec6d602f9d4319b8ed8fe20/redirect",
"17": "zip+https://api.foojay.io/disco/v3.0/ids/bfb117fd91348e9a4c6627fa2b33c479/redirect",
"18": "zip+https://api.foojay.io/disco/v3.0/ids/7e0b923ecab0c62b271d1b6dcfed3b8e/redirect",
"19": "zip+https://api.foojay.io/disco/v3.0/ids/67704107446123ac574f69373329f2d0/redirect",
"19.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/57dd9a6e18ebdf00f7f48c4c1c5b394e/redirect",
"19.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/08ac363d07e7dbbcd96e42b39cd163d0/redirect",
"18.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/7095efcaef916dfd569a9bc0a2e3d2c4/redirect",
"18.0.2-1": "zip+https://api.foojay.io/disco/v3.0/ids/5007defd104487acebaa55745a0b621a/redirect",
"18.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/0e4c42036a25a701aee3c05e5e6e54c1/redirect",
"18.0.1-1": "zip+https://api.foojay.io/disco/v3.0/ids/3e873ca36f3cf39b47c63c4648b829f2/redirect",
"17.0.6": "zip+https://api.foojay.io/disco/v3.0/ids/e243d4db153072fd3da8e8f6d372ba25/redirect",
"17.0.5": "zip+https://api.foojay.io/disco/v3.0/ids/8cc5e5cf8a132c6f77253d96707be4e0/redirect",
"17.0.4": "zip+https://api.foojay.io/disco/v3.0/ids/9dd42002db5470d73ebc0d0c617dddf3/redirect",
"17.0.4-1": "zip+https://api.foojay.io/disco/v3.0/ids/7d85c84c9cbc2b3216bf869f32e7f1ec/redirect",
"17.0.3": "zip+https://api.foojay.io/disco/v3.0/ids/4275c3246b0f49093d56fbcf21093d4c/redirect",
"17.0.3-1": "zip+https://api.foojay.io/disco/v3.0/ids/0911b8bfd3c5c29423e0e9dffe204745/redirect",
"17.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/590c1b634a7901c77399167ec93b0535/redirect",
"17.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/afe38c4055cf88feaee829ea28c49c0e/redirect",
"16.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/4f7a714c20cb631b024e4d20c3edf662/redirect",
"16.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/0f176f40f9c8cfda7fdf75ee42d73002/redirect",
"15.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/bd230f3cdfd24ea0fc8d5d3408d05d4d/redirect",
"15.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/bf463c3c677bc3be4922c0bc85854b95/redirect",
"14.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/b0db0981f7b3e2ca3bdf5bbc19927479/redirect",
"14.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/9affb53fd6185afb30700a486117caf3/redirect",
"13.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/93f6d37e5279c76f77160fa643dc1b44/redirect",
"13.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/e80f526a7aec09969b16daa65dfddd13/redirect",
"12.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/2064459d782c20eb34944f9b59c60e2d/redirect",
"12.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/2001378a9456c8187909abf313294245/redirect",
"11.0.18": "zip+https://api.foojay.io/disco/v3.0/ids/0e8c226ff320d1310c02ece90a8c1ebb/redirect",
"11.0.17": "zip+https://api.foojay.io/disco/v3.0/ids/44295ffa3c5c8098572c957932102a27/redirect",
"11.0.16": "zip+https://api.foojay.io/disco/v3.0/ids/24c40145ee21b8b05e383960aa56f1e1/redirect",
"11.0.16-1": "zip+https://api.foojay.io/disco/v3.0/ids/b15b601e129335671dc504d831b8687d/redirect",
"11.0.15": "zip+https://api.foojay.io/disco/v3.0/ids/59e03f3a1a41c08ab6f8ac9f6ba725eb/redirect",
"11.0.15-1": "zip+https://api.foojay.io/disco/v3.0/ids/f3dff656ce575e1b67e36d725b533165/redirect",
"11.0.14": "zip+https://api.foojay.io/disco/v3.0/ids/0a403f5a0df061b5c7fe1d278b961b33/redirect",
"11.0.14-1": "zip+https://api.foojay.io/disco/v3.0/ids/41d4df914dd9340ed1b273010af80479/redirect",
"11.0.13": "zip+https://api.foojay.io/disco/v3.0/ids/b7d5c5e172f73b263a1de83a3bbf24b0/redirect",
"11.0.12": "zip+https://api.foojay.io/disco/v3.0/ids/8fa3fb44f5f5888804524ac63cc01dd0/redirect",
"11.0.11": "zip+https://api.foojay.io/disco/v3.0/ids/a47f733b5b93f30e1157c7e0d485832e/redirect",
"11.0.10": "zip+https://api.foojay.io/disco/v3.0/ids/5784bddf0af795f3476bd24cde870f01/redirect",
"11.0.9": "zip+https://api.foojay.io/disco/v3.0/ids/8bef32622cebc92825f1bd659c0456e8/redirect",
"11.0.9-1": "zip+https://api.foojay.io/disco/v3.0/ids/8f780d3edffb77da0e5be9d12c22d8f1/redirect",
"11.0.8": "zip+https://api.foojay.io/disco/v3.0/ids/4335087e61ceb2e40a75386dcb5b724a/redirect",
"11.0.7": "zip+https://api.foojay.io/disco/v3.0/ids/3ee0357654f15199995c1e68a063bb22/redirect",
"11.0.6": "zip+https://api.foojay.io/disco/v3.0/ids/06230b580ed0cc76b3f545c8849d945f/redirect",
"11.0.5": "zip+https://api.foojay.io/disco/v3.0/ids/7a0a3142bbba81937b5c747fc2e4c8c0/redirect",
"11.0.4": "zip+https://api.foojay.io/disco/v3.0/ids/f76226f1905a86672c43c8d65dcfcaf5/redirect",
"11.0.3": "zip+https://api.foojay.io/disco/v3.0/ids/8fa5d586c27c9761f5872de9ee5a9644/redirect",
"11.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/d27e0804731e0f81a79518365979dd54/redirect",
"8.0.362": "zip+https://api.foojay.io/disco/v3.0/ids/808b850c7a215e148d37fe991263ac71/redirect",
"8.0.352": "zip+https://api.foojay.io/disco/v3.0/ids/e7c9b69b584b797d1960e2f0c6e88411/redirect",
"8.0.345": "zip+https://api.foojay.io/disco/v3.0/ids/010ed9a5eab62857771c68595976858e/redirect",
"8.0.342": "zip+https://api.foojay.io/disco/v3.0/ids/2321792122870ab3e1ea671d359770dc/redirect",
"8.0.333": "zip+https://api.foojay.io/disco/v3.0/ids/0b39ef3e136c02e995a6640e31dfe210/redirect",
"8.0.332": "zip+https://api.foojay.io/disco/v3.0/ids/2ad83efa7eb8c1c32fffc68143a6b192/redirect",
"8.0.322": "zip+https://api.foojay.io/disco/v3.0/ids/16e53bb149365b91868e1caf0fe81fd2/redirect",
"8.0.312": "zip+https://api.foojay.io/disco/v3.0/ids/88ce879c0a967faab6bc01cc379cc13e/redirect",
"8.0.302": "zip+https://api.foojay.io/disco/v3.0/ids/eacf0ba79d8956602cfdbd03a48b6509/redirect",
"8.0.292": "zip+https://api.foojay.io/disco/v3.0/ids/95796d9bed8dab853f1b73bda03a46d3/redirect",
"8.0.282": "zip+https://api.foojay.io/disco/v3.0/ids/5a07db4de5c125f61204dd7beb2be624/redirect",
"8.0.275": "zip+https://api.foojay.io/disco/v3.0/ids/260c9fb653f1652918b45ce58efab455/redirect",
"8.0.272": "zip+https://api.foojay.io/disco/v3.0/ids/9c6b793d6eeac66ca2c254df5f442255/redirect",
"8.0.265": "zip+https://api.foojay.io/disco/v3.0/ids/7786c195b16a9d3599ef544f658543f6/redirect",
"8.0.262": "zip+https://api.foojay.io/disco/v3.0/ids/e1b390bddd2339f8e32ccfe74693a088/redirect",
"8.0.252": "zip+https://api.foojay.io/disco/v3.0/ids/1357c34a741ea14b574301e258e4139d/redirect",
"8.0.242": "zip+https://api.foojay.io/disco/v3.0/ids/39daac4ea85000f53d6bba149b8bdfc6/redirect",
"1.19.0-2": "zip+https://api.foojay.io/disco/v3.0/ids/57dd9a6e18ebdf00f7f48c4c1c5b394e/redirect",
"1.19.0-1": "zip+https://api.foojay.io/disco/v3.0/ids/08ac363d07e7dbbcd96e42b39cd163d0/redirect",
"1.19.0-0": "zip+https://api.foojay.io/disco/v3.0/ids/67704107446123ac574f69373329f2d0/redirect",
"1.18.0-2": "zip+https://api.foojay.io/disco/v3.0/ids/7095efcaef916dfd569a9bc0a2e3d2c4/redirect",
"1.18.0-1": "zip+https://api.foojay.io/disco/v3.0/ids/0e4c42036a25a701aee3c05e5e6e54c1/redirect",
"1.18.0-0": "zip+https://api.foojay.io/disco/v3.0/ids/7e0b923ecab0c62b271d1b6dcfed3b8e/redirect",
"1.17.0-6": "zip+https://api.foojay.io/disco/v3.0/ids/e243d4db153072fd3da8e8f6d372ba25/redirect",
"1.17.0-5": "zip+https://api.foojay.io/disco/v3.0/ids/8cc5e5cf8a132c6f77253d96707be4e0/redirect",
"1.17.0-4": "zip+https://api.foojay.io/disco/v3.0/ids/9dd42002db5470d73ebc0d0c617dddf3/redirect",
"1.17.0-3": "zip+https://api.foojay.io/disco/v3.0/ids/4275c3246b0f49093d56fbcf21093d4c/redirect",
"1.17.0-2": "zip+https://api.foojay.io/disco/v3.0/ids/590c1b634a7901c77399167ec93b0535/redirect",
"1.17.0-1": "zip+https://api.foojay.io/disco/v3.0/ids/afe38c4055cf88feaee829ea28c49c0e/redirect",
"1.17.0-0": "zip+https://api.foojay.io/disco/v3.0/ids/bfb117fd91348e9a4c6627fa2b33c479/redirect",
"1.16.0-2": "zip+https://api.foojay.io/disco/v3.0/ids/4f7a714c20cb631b024e4d20c3edf662/redirect",
"1.16.0-1": "zip+https://api.foojay.io/disco/v3.0/ids/0f176f40f9c8cfda7fdf75ee42d73002/redirect",
"1.16.0-0": "zip+https://api.foojay.io/disco/v3.0/ids/faad15dbdec6d602f9d4319b8ed8fe20/redirect",
"1.15.0-2": "zip+https://api.foojay.io/disco/v3.0/ids/bd230f3cdfd24ea0fc8d5d3408d05d4d/redirect",
"1.15.0-1": "zip+https://api.foojay.io/disco/v3.0/ids/bf463c3c677bc3be4922c0bc85854b95/redirect",
"1.15.0-0": "zip+https://api.foojay.io/disco/v3.0/ids/023199eeca0532ed6e1d014d355cb845/redirect",
"1.14.0-2": "zip+https://api.foojay.io/disco/v3.0/ids/b0db0981f7b3e2ca3bdf5bbc19927479/redirect",
"1.14.0-1": "zip+https://api.foojay.io/disco/v3.0/ids/9affb53fd6185afb30700a486117caf3/redirect",
"1.14.0-0": "zip+https://api.foojay.io/disco/v3.0/ids/6bba1616bf0f7c295f0fd35003043114/redirect",
"1.13.0-2": "zip+https://api.foojay.io/disco/v3.0/ids/93f6d37e5279c76f77160fa643dc1b44/redirect",
"1.13.0-1": "zip+https://api.foojay.io/disco/v3.0/ids/e80f526a7aec09969b16daa65dfddd13/redirect",
"1.13.0-0": "zip+https://api.foojay.io/disco/v3.0/ids/a6b19a0ceac039dc72d63ebb7c1c7f0c/redirect",
"1.12.0-2": "zip+https://api.foojay.io/disco/v3.0/ids/2064459d782c20eb34944f9b59c60e2d/redirect",
"1.12.0-1": "zip+https://api.foojay.io/disco/v3.0/ids/2001378a9456c8187909abf313294245/redirect",
"1.12.0-0": "zip+https://api.foojay.io/disco/v3.0/ids/93f6a9522852a47e8b72baa4ffb11560/redirect",
"1.11.0-18": "zip+https://api.foojay.io/disco/v3.0/ids/0e8c226ff320d1310c02ece90a8c1ebb/redirect",
"1.11.0-17": "zip+https://api.foojay.io/disco/v3.0/ids/44295ffa3c5c8098572c957932102a27/redirect",
"1.11.0-16": "zip+https://api.foojay.io/disco/v3.0/ids/24c40145ee21b8b05e383960aa56f1e1/redirect",
"1.11.0-15": "zip+https://api.foojay.io/disco/v3.0/ids/59e03f3a1a41c08ab6f8ac9f6ba725eb/redirect",
"1.11.0-14": "zip+https://api.foojay.io/disco/v3.0/ids/0a403f5a0df061b5c7fe1d278b961b33/redirect",
"1.11.0-13": "zip+https://api.foojay.io/disco/v3.0/ids/b7d5c5e172f73b263a1de83a3bbf24b0/redirect",
"1.11.0-12": "zip+https://api.foojay.io/disco/v3.0/ids/8fa3fb44f5f5888804524ac63cc01dd0/redirect",
"1.11.0-11": "zip+https://api.foojay.io/disco/v3.0/ids/a47f733b5b93f30e1157c7e0d485832e/redirect",
"1.11.0-10": "zip+https://api.foojay.io/disco/v3.0/ids/5784bddf0af795f3476bd24cde870f01/redirect",
"1.11.0-9": "zip+https://api.foojay.io/disco/v3.0/ids/8bef32622cebc92825f1bd659c0456e8/redirect",
"1.11.0-8": "zip+https://api.foojay.io/disco/v3.0/ids/4335087e61ceb2e40a75386dcb5b724a/redirect",
"1.11.0-7": "zip+https://api.foojay.io/disco/v3.0/ids/3ee0357654f15199995c1e68a063bb22/redirect",
"1.11.0-6": "zip+https://api.foojay.io/disco/v3.0/ids/06230b580ed0cc76b3f545c8849d945f/redirect",
"1.11.0-5": "zip+https://api.foojay.io/disco/v3.0/ids/7a0a3142bbba81937b5c747fc2e4c8c0/redirect",
"1.11.0-4": "zip+https://api.foojay.io/disco/v3.0/ids/f76226f1905a86672c43c8d65dcfcaf5/redirect",
"1.11.0-3": "zip+https://api.foojay.io/disco/v3.0/ids/8fa5d586c27c9761f5872de9ee5a9644/redirect",
"1.11.0-2": "zip+https://api.foojay.io/disco/v3.0/ids/d27e0804731e0f81a79518365979dd54/redirect",
"1.8.362-9": "zip+https://api.foojay.io/disco/v3.0/ids/808b850c7a215e148d37fe991263ac71/redirect",
"1.8.352-8": "zip+https://api.foojay.io/disco/v3.0/ids/e7c9b69b584b797d1960e2f0c6e88411/redirect",
"1.8.345-1": "zip+https://api.foojay.io/disco/v3.0/ids/010ed9a5eab62857771c68595976858e/redirect",
"1.8.342-7": "zip+https://api.foojay.io/disco/v3.0/ids/2321792122870ab3e1ea671d359770dc/redirect",
"1.8.333-2": "zip+https://api.foojay.io/disco/v3.0/ids/0b39ef3e136c02e995a6640e31dfe210/redirect",
"1.8.332-9": "zip+https://api.foojay.io/disco/v3.0/ids/2ad83efa7eb8c1c32fffc68143a6b192/redirect",
"1.8.322-6": "zip+https://api.foojay.io/disco/v3.0/ids/16e53bb149365b91868e1caf0fe81fd2/redirect",
"1.8.312-7": "zip+https://api.foojay.io/disco/v3.0/ids/88ce879c0a967faab6bc01cc379cc13e/redirect",
"1.8.302-8": "zip+https://api.foojay.io/disco/v3.0/ids/eacf0ba79d8956602cfdbd03a48b6509/redirect",
"1.8.292-10": "zip+https://api.foojay.io/disco/v3.0/ids/95796d9bed8dab853f1b73bda03a46d3/redirect",
"1.8.282-8": "zip+https://api.foojay.io/disco/v3.0/ids/5a07db4de5c125f61204dd7beb2be624/redirect",
"1.8.275-1": "zip+https://api.foojay.io/disco/v3.0/ids/260c9fb653f1652918b45ce58efab455/redirect",
"1.8.272-10": "zip+https://api.foojay.io/disco/v3.0/ids/9c6b793d6eeac66ca2c254df5f442255/redirect",
"1.8.265-1": "zip+https://api.foojay.io/disco/v3.0/ids/7786c195b16a9d3599ef544f658543f6/redirect",
"1.8.262-10": "zip+https://api.foojay.io/disco/v3.0/ids/e1b390bddd2339f8e32ccfe74693a088/redirect",
"1.8.252-9": "zip+https://api.foojay.io/disco/v3.0/ids/1357c34a741ea14b574301e258e4139d/redirect",
"1.8.242-7": "zip+https://api.foojay.io/disco/v3.0/ids/39daac4ea85000f53d6bba149b8bdfc6/redirect"
},
"jdk@jetbrains": {
"11.0.14": "tgz+https://api.foojay.io/disco/v3.0/ids/1986d0b7427fec6c13670dfa11ce49ff/redirect",
"11.0.14-1": "tgz+https://api.foojay.io/disco/v3.0/ids/a8708056300568260859ba7181d1eef1/redirect",
"11.0.13": "tgz+https://api.foojay.io/disco/v3.0/ids/5257dfcfbfb755818acad16586056e1e/redirect",
"11.0.12": "tgz+https://api.foojay.io/disco/v3.0/ids/4d0bf5c49ba293670e85245dcbd0b26f/redirect",
"11.0.11": "tgz+https://api.foojay.io/disco/v3.0/ids/f0902c8251ae6c84e7dd7c6515b0640a/redirect",
"11.0.10": "tgz+https://api.foojay.io/disco/v3.0/ids/503a34f27914d2806506553239c1fec8/redirect",
"11.0.9-1": "tgz+https://api.foojay.io/disco/v3.0/ids/37b3177727d8bb495239169cd6658903/redirect",
"11.0.8": "tgz+https://api.foojay.io/disco/v3.0/ids/3a1f0c4f0ebd77301df5d947ac52a987/redirect",
"11.0.7": "tgz+https://api.foojay.io/disco/v3.0/ids/6e10327b952cdb65417e33164a5e9a2f/redirect",
"11.0.6": "tgz+https://api.foojay.io/disco/v3.0/ids/2c2a7e8935635a3663c39a12c861575e/redirect",
"1.11.0-14": "tgz+https://api.foojay.io/disco/v3.0/ids/1986d0b7427fec6c13670dfa11ce49ff/redirect",
"1.11.0-13": "tgz+https://api.foojay.io/disco/v3.0/ids/5257dfcfbfb755818acad16586056e1e/redirect",
"1.11.0-12": "tgz+https://api.foojay.io/disco/v3.0/ids/4d0bf5c49ba293670e85245dcbd0b26f/redirect",
"1.11.0-11": "tgz+https://api.foojay.io/disco/v3.0/ids/f0902c8251ae6c84e7dd7c6515b0640a/redirect",
"1.11.0-10": "tgz+https://api.foojay.io/disco/v3.0/ids/503a34f27914d2806506553239c1fec8/redirect",
"1.11.0-9": "tgz+https://api.foojay.io/disco/v3.0/ids/37b3177727d8bb495239169cd6658903/redirect",
"1.11.0-8": "tgz+https://api.foojay.io/disco/v3.0/ids/3a1f0c4f0ebd77301df5d947ac52a987/redirect",
"1.11.0-7": "tgz+https://api.foojay.io/disco/v3.0/ids/6e10327b952cdb65417e33164a5e9a2f/redirect",
"1.11.0-6": "tgz+https://api.foojay.io/disco/v3.0/ids/2c2a7e8935635a3663c39a12c861575e/redirect"
},
"jdk@dragonwell": {
"17.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/3cd69ed48cb22cf0a443a35e4d368d06/redirect",
"1.17.0-1": "zip+https://api.foojay.io/disco/v3.0/ids/3cd69ed48cb22cf0a443a35e4d368d06/redirect"
},
"jdk@corretto": {
"8": "zip+https://api.foojay.io/disco/v3.0/ids/eea2d1f9e6c4cfa95616af904ae5531a/redirect",
"11": "zip+https://api.foojay.io/disco/v3.0/ids/383ed332c7c1b045d8c293568adcb60f/redirect",
"11.0.18": "zip+https://api.foojay.io/disco/v3.0/ids/cc705bf3d236bd004092d1cb679d175b/redirect",
"11.0.17": "zip+https://api.foojay.io/disco/v3.0/ids/dc98b4b6be391d8039211f0013395d4f/redirect",
"11.0.16": "zip+https://api.foojay.io/disco/v3.0/ids/6f10af237c0bab2fbacf7c7c837cf72c/redirect",
"11.0.15": "zip+https://api.foojay.io/disco/v3.0/ids/0232141cb165c16586df2c93baedb81b/redirect",
"11.0.14": "zip+https://api.foojay.io/disco/v3.0/ids/c3bcb2e10ab75724ba644e33d9a2607f/redirect",
"11.0.13": "zip+https://api.foojay.io/disco/v3.0/ids/b6d8aba1ad6c62d257a7883421b907ea/redirect",
"11.0.12": "zip+https://api.foojay.io/disco/v3.0/ids/5beb7530bd24d934e712edbc7ba7c015/redirect",
"11.0.11": "zip+https://api.foojay.io/disco/v3.0/ids/ae1559a6fafdec855c7703bba413b8ab/redirect",
"11.0.10": "zip+https://api.foojay.io/disco/v3.0/ids/5a08f5a85c4b6048c3710e45acd51856/redirect",
"1.11.0-18.10.1": "zip+https://api.foojay.io/disco/v3.0/ids/cc705bf3d236bd004092d1cb679d175b/redirect",
"1.11.0-17.8.1": "zip+https://api.foojay.io/disco/v3.0/ids/dc98b4b6be391d8039211f0013395d4f/redirect",
"1.11.0-16.9.1": "zip+https://api.foojay.io/disco/v3.0/ids/7315dc4a1556a9c45e8f99963fe70a17/redirect",
"1.11.0-16.8.1": "zip+https://api.foojay.io/disco/v3.0/ids/6f10af237c0bab2fbacf7c7c837cf72c/redirect",
"1.11.0-15.9.1": "zip+https://api.foojay.io/disco/v3.0/ids/0232141cb165c16586df2c93baedb81b/redirect",
"1.11.0-14.10.1": "zip+https://api.foojay.io/disco/v3.0/ids/0e68be8c6f842346cd46cf45e010789f/redirect",
"1.11.0-14.9.1": "zip+https://api.foojay.io/disco/v3.0/ids/c3bcb2e10ab75724ba644e33d9a2607f/redirect",
"1.11.0-13.8.1": "zip+https://api.foojay.io/disco/v3.0/ids/b6d8aba1ad6c62d257a7883421b907ea/redirect",
"1.11.0-12.7.1": "zip+https://api.foojay.io/disco/v3.0/ids/5beb7530bd24d934e712edbc7ba7c015/redirect",
"1.11.0-11.9.1": "zip+https://api.foojay.io/disco/v3.0/ids/ae1559a6fafdec855c7703bba413b8ab/redirect",
"1.11.0-10.9.1": "zip+https://api.foojay.io/disco/v3.0/ids/5a08f5a85c4b6048c3710e45acd51856/redirect",
"1.11.0-0": "zip+https://api.foojay.io/disco/v3.0/ids/383ed332c7c1b045d8c293568adcb60f/redirect",
"1.8.0-0": "zip+https://api.foojay.io/disco/v3.0/ids/eea2d1f9e6c4cfa95616af904ae5531a/redirect"
},
"jdk@amazon-corretto": {
"8": "zip+https://api.foojay.io/disco/v3.0/ids/eea2d1f9e6c4cfa95616af904ae5531a/redirect",
"11": "zip+https://api.foojay.io/disco/v3.0/ids/383ed332c7c1b045d8c293568adcb60f/redirect",
"11.0.18": "zip+https://api.foojay.io/disco/v3.0/ids/cc705bf3d236bd004092d1cb679d175b/redirect",
"11.0.17": "zip+https://api.foojay.io/disco/v3.0/ids/dc98b4b6be391d8039211f0013395d4f/redirect",
"11.0.16": "zip+https://api.foojay.io/disco/v3.0/ids/6f10af237c0bab2fbacf7c7c837cf72c/redirect",
"11.0.15": "zip+https://api.foojay.io/disco/v3.0/ids/0232141cb165c16586df2c93baedb81b/redirect",
"11.0.14": "zip+https://api.foojay.io/disco/v3.0/ids/c3bcb2e10ab75724ba644e33d9a2607f/redirect",
"11.0.13": "zip+https://api.foojay.io/disco/v3.0/ids/b6d8aba1ad6c62d257a7883421b907ea/redirect",
"11.0.12": "zip+https://api.foojay.io/disco/v3.0/ids/5beb7530bd24d934e712edbc7ba7c015/redirect",
"11.0.11": "zip+https://api.foojay.io/disco/v3.0/ids/ae1559a6fafdec855c7703bba413b8ab/redirect",
"11.0.10": "zip+https://api.foojay.io/disco/v3.0/ids/5a08f5a85c4b6048c3710e45acd51856/redirect",
"1.11.0-18.10.1": "zip+https://api.foojay.io/disco/v3.0/ids/cc705bf3d236bd004092d1cb679d175b/redirect",
"1.11.0-17.8.1": "zip+https://api.foojay.io/disco/v3.0/ids/dc98b4b6be391d8039211f0013395d4f/redirect",
"1.11.0-16.9.1": "zip+https://api.foojay.io/disco/v3.0/ids/7315dc4a1556a9c45e8f99963fe70a17/redirect",
"1.11.0-16.8.1": "zip+https://api.foojay.io/disco/v3.0/ids/6f10af237c0bab2fbacf7c7c837cf72c/redirect",
"1.11.0-15.9.1": "zip+https://api.foojay.io/disco/v3.0/ids/0232141cb165c16586df2c93baedb81b/redirect",
"1.11.0-14.10.1": "zip+https://api.foojay.io/disco/v3.0/ids/0e68be8c6f842346cd46cf45e010789f/redirect",
"1.11.0-14.9.1": "zip+https://api.foojay.io/disco/v3.0/ids/c3bcb2e10ab75724ba644e33d9a2607f/redirect",
"1.11.0-13.8.1": "zip+https://api.foojay.io/disco/v3.0/ids/b6d8aba1ad6c62d257a7883421b907ea/redirect",
"1.11.0-12.7.1": "zip+https://api.foojay.io/disco/v3.0/ids/5beb7530bd24d934e712edbc7ba7c015/redirect",
"1.11.0-11.9.1": "zip+https://api.foojay.io/disco/v3.0/ids/ae1559a6fafdec855c7703bba413b8ab/redirect",
"1.11.0-10.9.1": "zip+https://api.foojay.io/disco/v3.0/ids/5a08f5a85c4b6048c3710e45acd51856/redirect",
"1.11.0-0": "zip+https://api.foojay.io/disco/v3.0/ids/383ed332c7c1b045d8c293568adcb60f/redirect",
"1.8.0-0": "zip+https://api.foojay.io/disco/v3.0/ids/eea2d1f9e6c4cfa95616af904ae5531a/redirect"
},
"jdk@aoj_openj9": {
"8.0.292": "zip+https://api.foojay.io/disco/v3.0/ids/a4551f6b41b817cd38e5a91ca4084995/redirect",
"8.0.282": "zip+https://api.foojay.io/disco/v3.0/ids/918fe976050eda55a63de250d20aa867/redirect",
"8.0.275": "zip+https://api.foojay.io/disco/v3.0/ids/4795b63c3bf48589a5b7ea81a824d750/redirect",
"8.0.272": "zip+https://api.foojay.io/disco/v3.0/ids/e922de1aaddf110219157eda3468e5f2/redirect",
"8.0.265": "zip+https://api.foojay.io/disco/v3.0/ids/3beefaaf0187656e9fbbd1af97fcc80d/redirect",
"8.0.262": "zip+https://api.foojay.io/disco/v3.0/ids/c2df17a700a92e2bc831086eeae7de30/redirect",
"8.0.252": "zip+https://api.foojay.io/disco/v3.0/ids/0ba27c7f59a8fcdc2a453075088f55db/redirect",
"8.0.242": "zip+https://api.foojay.io/disco/v3.0/ids/bdd831d22a2635820c5c2b1fb7e346e7/redirect",
"8.0.232": "zip+https://api.foojay.io/disco/v3.0/ids/bb4826a2c04d1dd4c9ae977554b18822/redirect",
"8.0.222": "zip+https://api.foojay.io/disco/v3.0/ids/af3b69eda0681ab3922aaa331cc51ad5/redirect",
"8.0.212": "zip+https://api.foojay.io/disco/v3.0/ids/33da6ab83c44cedbfd9017acce044bb7/redirect",
"8.0.202": "zip+https://api.foojay.io/disco/v3.0/ids/96c044912e30eb73cd2f052889315d80/redirect",
"1.8.0-292": "zip+https://api.foojay.io/disco/v3.0/ids/a4551f6b41b817cd38e5a91ca4084995/redirect",
"1.8.0-282": "zip+https://api.foojay.io/disco/v3.0/ids/918fe976050eda55a63de250d20aa867/redirect",
"1.8.0-275": "zip+https://api.foojay.io/disco/v3.0/ids/4795b63c3bf48589a5b7ea81a824d750/redirect",
"1.8.0-272": "zip+https://api.foojay.io/disco/v3.0/ids/e922de1aaddf110219157eda3468e5f2/redirect",
"1.8.0-265": "zip+https://api.foojay.io/disco/v3.0/ids/3beefaaf0187656e9fbbd1af97fcc80d/redirect",
"1.8.0-262": "zip+https://api.foojay.io/disco/v3.0/ids/c2df17a700a92e2bc831086eeae7de30/redirect",
"1.8.0-252": "zip+https://api.foojay.io/disco/v3.0/ids/0ba27c7f59a8fcdc2a453075088f55db/redirect",
"1.8.0-242": "zip+https://api.foojay.io/disco/v3.0/ids/bdd831d22a2635820c5c2b1fb7e346e7/redirect",
"1.8.0-232": "zip+https://api.foojay.io/disco/v3.0/ids/bb4826a2c04d1dd4c9ae977554b18822/redirect",
"1.8.0-222": "zip+https://api.foojay.io/disco/v3.0/ids/af3b69eda0681ab3922aaa331cc51ad5/redirect",
"1.8.0-212": "zip+https://api.foojay.io/disco/v3.0/ids/33da6ab83c44cedbfd9017acce044bb7/redirect",
"1.8.0-202": "zip+https://api.foojay.io/disco/v3.0/ids/96c044912e30eb73cd2f052889315d80/redirect"
},
"jdk@adopt-openj9": {
"8.0.292": "zip+https://api.foojay.io/disco/v3.0/ids/a4551f6b41b817cd38e5a91ca4084995/redirect",
"8.0.282": "zip+https://api.foojay.io/disco/v3.0/ids/918fe976050eda55a63de250d20aa867/redirect",
"8.0.275": "zip+https://api.foojay.io/disco/v3.0/ids/4795b63c3bf48589a5b7ea81a824d750/redirect",
"8.0.272": "zip+https://api.foojay.io/disco/v3.0/ids/e922de1aaddf110219157eda3468e5f2/redirect",
"8.0.265": "zip+https://api.foojay.io/disco/v3.0/ids/3beefaaf0187656e9fbbd1af97fcc80d/redirect",
"8.0.262": "zip+https://api.foojay.io/disco/v3.0/ids/c2df17a700a92e2bc831086eeae7de30/redirect",
"8.0.252": "zip+https://api.foojay.io/disco/v3.0/ids/0ba27c7f59a8fcdc2a453075088f55db/redirect",
"8.0.242": "zip+https://api.foojay.io/disco/v3.0/ids/bdd831d22a2635820c5c2b1fb7e346e7/redirect",
"8.0.232": "zip+https://api.foojay.io/disco/v3.0/ids/bb4826a2c04d1dd4c9ae977554b18822/redirect",
"8.0.222": "zip+https://api.foojay.io/disco/v3.0/ids/af3b69eda0681ab3922aaa331cc51ad5/redirect",
"8.0.212": "zip+https://api.foojay.io/disco/v3.0/ids/33da6ab83c44cedbfd9017acce044bb7/redirect",
"8.0.202": "zip+https://api.foojay.io/disco/v3.0/ids/96c044912e30eb73cd2f052889315d80/redirect",
"1.8.0-292": "zip+https://api.foojay.io/disco/v3.0/ids/a4551f6b41b817cd38e5a91ca4084995/redirect",
"1.8.0-282": "zip+https://api.foojay.io/disco/v3.0/ids/918fe976050eda55a63de250d20aa867/redirect",
"1.8.0-275": "zip+https://api.foojay.io/disco/v3.0/ids/4795b63c3bf48589a5b7ea81a824d750/redirect",
"1.8.0-272": "zip+https://api.foojay.io/disco/v3.0/ids/e922de1aaddf110219157eda3468e5f2/redirect",
"1.8.0-265": "zip+https://api.foojay.io/disco/v3.0/ids/3beefaaf0187656e9fbbd1af97fcc80d/redirect",
"1.8.0-262": "zip+https://api.foojay.io/disco/v3.0/ids/c2df17a700a92e2bc831086eeae7de30/redirect",
"1.8.0-252": "zip+https://api.foojay.io/disco/v3.0/ids/0ba27c7f59a8fcdc2a453075088f55db/redirect",
"1.8.0-242": "zip+https://api.foojay.io/disco/v3.0/ids/bdd831d22a2635820c5c2b1fb7e346e7/redirect",
"1.8.0-232": "zip+https://api.foojay.io/disco/v3.0/ids/bb4826a2c04d1dd4c9ae977554b18822/redirect",
"1.8.0-222": "zip+https://api.foojay.io/disco/v3.0/ids/af3b69eda0681ab3922aaa331cc51ad5/redirect",
"1.8.0-212": "zip+https://api.foojay.io/disco/v3.0/ids/33da6ab83c44cedbfd9017acce044bb7/redirect",
"1.8.0-202": "zip+https://api.foojay.io/disco/v3.0/ids/96c044912e30eb73cd2f052889315d80/redirect"
},
"jdk@aoj": {
"12": "zip+https://api.foojay.io/disco/v3.0/ids/e3174fcaebaf3f17e9ef78efb509c832/redirect",
"13": "zip+https://api.foojay.io/disco/v3.0/ids/f3bb2f3a7204898bec3fffb11dd474e3/redirect",
"14": "zip+https://api.foojay.io/disco/v3.0/ids/f59566e8946b11b02729242e212d4c92/redirect",
"15": "zip+https://api.foojay.io/disco/v3.0/ids/7ae34d8a250e717e6197fe052ba289df/redirect",
"16": "zip+https://api.foojay.io/disco/v3.0/ids/e795ecc11de711fa45c04a932a06d98c/redirect",
"16.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/51bfa2ca2ae456194b4be12500e2a1fe/redirect",
"15.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/06c4a82a20d7bd7a04443253ca092488/redirect",
"15.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/06c9126ccf77ac9c37ff2eb77adb54da/redirect",
"14.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/760ee753d724c50973bb0e5e45cbfed9/redirect",
"14.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/3eedb0e3bcbc148328125319f57d2e4d/redirect",
"13.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/7557aedde3b115f52e0a3671176180d8/redirect",
"13.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/1ca9d52ad86896044afcfa7390ba6020/redirect",
"12.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/dff6b32d8020d4839540f203797ae078/redirect",
"12.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/cc5dd58e9d4bfc79fc23ce0c6e4aae41/redirect",
"11.0.11": "zip+https://api.foojay.io/disco/v3.0/ids/46ffc6fbdf840a4a9a09005b9967515d/redirect",
"11.0.10": "zip+https://api.foojay.io/disco/v3.0/ids/9d045cd80fc17f443596bf89553fed83/redirect",
"11.0.9": "zip+https://api.foojay.io/disco/v3.0/ids/feff42f4f639588a9ca9fa27f28e8233/redirect",
"11.0.9-1": "zip+https://api.foojay.io/disco/v3.0/ids/9c749d980678e2dddd3eb58d54f08f4e/redirect",
"11.0.8": "zip+https://api.foojay.io/disco/v3.0/ids/e25ad0db932ceefa0a164cc5fde809e7/redirect",
"11.0.7": "zip+https://api.foojay.io/disco/v3.0/ids/5a556032c4390f159127c5d919ad04a4/redirect",
"11.0.6": "zip+https://api.foojay.io/disco/v3.0/ids/80a05f4cd9b2686df5ca4aca0b4ca108/redirect",
"11.0.5": "zip+https://api.foojay.io/disco/v3.0/ids/eef303ca99dac2d76ac05a38934cc22a/redirect",
"11.0.4": "zip+https://api.foojay.io/disco/v3.0/ids/db9803b781511e176cc2a72fdfe7d1a3/redirect",
"11.0.3": "zip+https://api.foojay.io/disco/v3.0/ids/28300d3908cc3787094d4b2cf50ff4f5/redirect",
"11.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/247c07abeb7e44e34e04c153fb44316e/redirect",
"11.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/d95814c94b3bf6d7a5525113cfbdaf8a/redirect",
"8.0.292": "zip+https://api.foojay.io/disco/v3.0/ids/707642aec2c73771d574e263e485451d/redirect",
"8.0.282": "zip+https://api.foojay.io/disco/v3.0/ids/cc3b8cce78aa4e4dec59fe80c0c82b5d/redirect",
"8.0.275": "zip+https://api.foojay.io/disco/v3.0/ids/448e20abc1f44f60a12014cee401c257/redirect",
"8.0.272": "zip+https://api.foojay.io/disco/v3.0/ids/a5eae3571533770d7e8375b87b36aff5/redirect",
"8.0.265": "zip+https://api.foojay.io/disco/v3.0/ids/3081a6672a2b10e518cbc21aeab8502b/redirect",
"8.0.262": "zip+https://api.foojay.io/disco/v3.0/ids/8b3102c6c08d75fda0cb2c7a95f16fea/redirect",
"8.0.252": "zip+https://api.foojay.io/disco/v3.0/ids/81c3363bc6d79f6f31137b283cf79070/redirect",
"8.0.242": "zip+https://api.foojay.io/disco/v3.0/ids/2bcb9469f59122ebc02d59bfa27e112e/redirect",
"8.0.232": "zip+https://api.foojay.io/disco/v3.0/ids/017fda01ad461c48123bf6484aa42be6/redirect",
"8.0.222": "zip+https://api.foojay.io/disco/v3.0/ids/5bf8cffebe144b166e13e63db36ca942/redirect",
"8.0.212": "zip+https://api.foojay.io/disco/v3.0/ids/b11ffaafc5374b323ae6df629aab2eb6/redirect",
"8.0.202": "zip+https://api.foojay.io/disco/v3.0/ids/8690df9a62f5c2992090b3c21297ba9a/redirect",
"8.0.192": "zip+https://api.foojay.io/disco/v3.0/ids/647abfa75103e644b3956124574efde7/redirect",
"8.0.181": "zip+https://api.foojay.io/disco/v3.0/ids/51e3d94d4ae85dd8329291fc3f527b5c/redirect",
"1.16.0-1": "zip+https://api.foojay.io/disco/v3.0/ids/51bfa2ca2ae456194b4be12500e2a1fe/redirect",
"1.16.0-0": "zip+https://api.foojay.io/disco/v3.0/ids/e795ecc11de711fa45c04a932a06d98c/redirect",
"1.15.0-2": "zip+https://api.foojay.io/disco/v3.0/ids/06c4a82a20d7bd7a04443253ca092488/redirect",
"1.15.0-1": "zip+https://api.foojay.io/disco/v3.0/ids/06c9126ccf77ac9c37ff2eb77adb54da/redirect",
"1.15.0-0": "zip+https://api.foojay.io/disco/v3.0/ids/7ae34d8a250e717e6197fe052ba289df/redirect",
"1.14.0-2": "zip+https://api.foojay.io/disco/v3.0/ids/760ee753d724c50973bb0e5e45cbfed9/redirect",
"1.14.0-1": "zip+https://api.foojay.io/disco/v3.0/ids/3eedb0e3bcbc148328125319f57d2e4d/redirect",
"1.14.0-0": "zip+https://api.foojay.io/disco/v3.0/ids/f59566e8946b11b02729242e212d4c92/redirect",
"1.13.0-2": "zip+https://api.foojay.io/disco/v3.0/ids/7557aedde3b115f52e0a3671176180d8/redirect",
"1.13.0-1": "zip+https://api.foojay.io/disco/v3.0/ids/1ca9d52ad86896044afcfa7390ba6020/redirect",
"1.13.0-0": "zip+https://api.foojay.io/disco/v3.0/ids/f3bb2f3a7204898bec3fffb11dd474e3/redirect",
"1.12.0-2": "zip+https://api.foojay.io/disco/v3.0/ids/dff6b32d8020d4839540f203797ae078/redirect",
"1.12.0-1": "zip+https://api.foojay.io/disco/v3.0/ids/cc5dd58e9d4bfc79fc23ce0c6e4aae41/redirect",
"1.12.0-0": "zip+https://api.foojay.io/disco/v3.0/ids/e3174fcaebaf3f17e9ef78efb509c832/redirect",
"1.11.0-11": "zip+https://api.foojay.io/disco/v3.0/ids/46ffc6fbdf840a4a9a09005b9967515d/redirect",
"1.11.0-10": "zip+https://api.foojay.io/disco/v3.0/ids/9d045cd80fc17f443596bf89553fed83/redirect",
"1.11.0-9": "zip+https://api.foojay.io/disco/v3.0/ids/feff42f4f639588a9ca9fa27f28e8233/redirect",
"1.11.0-8": "zip+https://api.foojay.io/disco/v3.0/ids/e25ad0db932ceefa0a164cc5fde809e7/redirect",
"1.11.0-7": "zip+https://api.foojay.io/disco/v3.0/ids/5a556032c4390f159127c5d919ad04a4/redirect",
"1.11.0-6": "zip+https://api.foojay.io/disco/v3.0/ids/80a05f4cd9b2686df5ca4aca0b4ca108/redirect",
"1.11.0-5": "zip+https://api.foojay.io/disco/v3.0/ids/eef303ca99dac2d76ac05a38934cc22a/redirect",
"1.11.0-4": "zip+https://api.foojay.io/disco/v3.0/ids/db9803b781511e176cc2a72fdfe7d1a3/redirect",
"1.11.0-3": "zip+https://api.foojay.io/disco/v3.0/ids/28300d3908cc3787094d4b2cf50ff4f5/redirect",
"1.11.0-2": "zip+https://api.foojay.io/disco/v3.0/ids/247c07abeb7e44e34e04c153fb44316e/redirect",
"1.11.0-1": "zip+https://api.foojay.io/disco/v3.0/ids/d95814c94b3bf6d7a5525113cfbdaf8a/redirect",
"1.8.0-292": "zip+https://api.foojay.io/disco/v3.0/ids/707642aec2c73771d574e263e485451d/redirect",
"1.8.0-282": "zip+https://api.foojay.io/disco/v3.0/ids/cc3b8cce78aa4e4dec59fe80c0c82b5d/redirect",
"1.8.0-275": "zip+https://api.foojay.io/disco/v3.0/ids/448e20abc1f44f60a12014cee401c257/redirect",
"1.8.0-272": "zip+https://api.foojay.io/disco/v3.0/ids/a5eae3571533770d7e8375b87b36aff5/redirect",
"1.8.0-265": "zip+https://api.foojay.io/disco/v3.0/ids/3081a6672a2b10e518cbc21aeab8502b/redirect",
"1.8.0-262": "zip+https://api.foojay.io/disco/v3.0/ids/8b3102c6c08d75fda0cb2c7a95f16fea/redirect",
"1.8.0-252": "zip+https://api.foojay.io/disco/v3.0/ids/81c3363bc6d79f6f31137b283cf79070/redirect",
"1.8.0-242": "zip+https://api.foojay.io/disco/v3.0/ids/2bcb9469f59122ebc02d59bfa27e112e/redirect",
"1.8.0-232": "zip+https://api.foojay.io/disco/v3.0/ids/017fda01ad461c48123bf6484aa42be6/redirect",
"1.8.0-222": "zip+https://api.foojay.io/disco/v3.0/ids/5bf8cffebe144b166e13e63db36ca942/redirect",
"1.8.0-212": "zip+https://api.foojay.io/disco/v3.0/ids/b11ffaafc5374b323ae6df629aab2eb6/redirect",
"1.8.0-202": "zip+https://api.foojay.io/disco/v3.0/ids/8690df9a62f5c2992090b3c21297ba9a/redirect",
"1.8.0-192": "zip+https://api.foojay.io/disco/v3.0/ids/647abfa75103e644b3956124574efde7/redirect",
"1.8.0-181": "zip+https://api.foojay.io/disco/v3.0/ids/51e3d94d4ae85dd8329291fc3f527b5c/redirect"
},
"jdk@adopt": {
"12": "zip+https://api.foojay.io/disco/v3.0/ids/e3174fcaebaf3f17e9ef78efb509c832/redirect",
"13": "zip+https://api.foojay.io/disco/v3.0/ids/f3bb2f3a7204898bec3fffb11dd474e3/redirect",
"14": "zip+https://api.foojay.io/disco/v3.0/ids/f59566e8946b11b02729242e212d4c92/redirect",
"15": "zip+https://api.foojay.io/disco/v3.0/ids/7ae34d8a250e717e6197fe052ba289df/redirect",
"16": "zip+https://api.foojay.io/disco/v3.0/ids/e795ecc11de711fa45c04a932a06d98c/redirect",
"16.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/51bfa2ca2ae456194b4be12500e2a1fe/redirect",
"15.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/06c4a82a20d7bd7a04443253ca092488/redirect",
"15.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/06c9126ccf77ac9c37ff2eb77adb54da/redirect",
"14.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/760ee753d724c50973bb0e5e45cbfed9/redirect",
"14.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/3eedb0e3bcbc148328125319f57d2e4d/redirect",
"13.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/7557aedde3b115f52e0a3671176180d8/redirect",
"13.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/1ca9d52ad86896044afcfa7390ba6020/redirect",
"12.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/dff6b32d8020d4839540f203797ae078/redirect",
"12.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/cc5dd58e9d4bfc79fc23ce0c6e4aae41/redirect",
"11.0.11": "zip+https://api.foojay.io/disco/v3.0/ids/46ffc6fbdf840a4a9a09005b9967515d/redirect",
"11.0.10": "zip+https://api.foojay.io/disco/v3.0/ids/9d045cd80fc17f443596bf89553fed83/redirect",
"11.0.9": "zip+https://api.foojay.io/disco/v3.0/ids/feff42f4f639588a9ca9fa27f28e8233/redirect",
"11.0.9-1": "zip+https://api.foojay.io/disco/v3.0/ids/9c749d980678e2dddd3eb58d54f08f4e/redirect",
"11.0.8": "zip+https://api.foojay.io/disco/v3.0/ids/e25ad0db932ceefa0a164cc5fde809e7/redirect",
"11.0.7": "zip+https://api.foojay.io/disco/v3.0/ids/5a556032c4390f159127c5d919ad04a4/redirect",
"11.0.6": "zip+https://api.foojay.io/disco/v3.0/ids/80a05f4cd9b2686df5ca4aca0b4ca108/redirect",
"11.0.5": "zip+https://api.foojay.io/disco/v3.0/ids/eef303ca99dac2d76ac05a38934cc22a/redirect",
"11.0.4": "zip+https://api.foojay.io/disco/v3.0/ids/db9803b781511e176cc2a72fdfe7d1a3/redirect",
"11.0.3": "zip+https://api.foojay.io/disco/v3.0/ids/28300d3908cc3787094d4b2cf50ff4f5/redirect",
"11.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/247c07abeb7e44e34e04c153fb44316e/redirect",
"11.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/d95814c94b3bf6d7a5525113cfbdaf8a/redirect",
"8.0.292": "zip+https://api.foojay.io/disco/v3.0/ids/707642aec2c73771d574e263e485451d/redirect",
"8.0.282": "zip+https://api.foojay.io/disco/v3.0/ids/cc3b8cce78aa4e4dec59fe80c0c82b5d/redirect",
"8.0.275": "zip+https://api.foojay.io/disco/v3.0/ids/448e20abc1f44f60a12014cee401c257/redirect",
"8.0.272": "zip+https://api.foojay.io/disco/v3.0/ids/a5eae3571533770d7e8375b87b36aff5/redirect",
"8.0.265": "zip+https://api.foojay.io/disco/v3.0/ids/3081a6672a2b10e518cbc21aeab8502b/redirect",
"8.0.262": "zip+https://api.foojay.io/disco/v3.0/ids/8b3102c6c08d75fda0cb2c7a95f16fea/redirect",
"8.0.252": "zip+https://api.foojay.io/disco/v3.0/ids/81c3363bc6d79f6f31137b283cf79070/redirect",
"8.0.242": "zip+https://api.foojay.io/disco/v3.0/ids/2bcb9469f59122ebc02d59bfa27e112e/redirect",
"8.0.232": "zip+https://api.foojay.io/disco/v3.0/ids/017fda01ad461c48123bf6484aa42be6/redirect",
"8.0.222": "zip+https://api.foojay.io/disco/v3.0/ids/5bf8cffebe144b166e13e63db36ca942/redirect",
"8.0.212": "zip+https://api.foojay.io/disco/v3.0/ids/b11ffaafc5374b323ae6df629aab2eb6/redirect",
"8.0.202": "zip+https://api.foojay.io/disco/v3.0/ids/8690df9a62f5c2992090b3c21297ba9a/redirect",
"8.0.192": "zip+https://api.foojay.io/disco/v3.0/ids/647abfa75103e644b3956124574efde7/redirect",
"8.0.181": "zip+https://api.foojay.io/disco/v3.0/ids/51e3d94d4ae85dd8329291fc3f527b5c/redirect",
"1.16.0-1": "zip+https://api.foojay.io/disco/v3.0/ids/51bfa2ca2ae456194b4be12500e2a1fe/redirect",
"1.16.0-0": "zip+https://api.foojay.io/disco/v3.0/ids/e795ecc11de711fa45c04a932a06d98c/redirect",
"1.15.0-2": "zip+https://api.foojay.io/disco/v3.0/ids/06c4a82a20d7bd7a04443253ca092488/redirect",
"1.15.0-1": "zip+https://api.foojay.io/disco/v3.0/ids/06c9126ccf77ac9c37ff2eb77adb54da/redirect",
"1.15.0-0": "zip+https://api.foojay.io/disco/v3.0/ids/7ae34d8a250e717e6197fe052ba289df/redirect",
"1.14.0-2": "zip+https://api.foojay.io/disco/v3.0/ids/760ee753d724c50973bb0e5e45cbfed9/redirect",
"1.14.0-1": "zip+https://api.foojay.io/disco/v3.0/ids/3eedb0e3bcbc148328125319f57d2e4d/redirect",
"1.14.0-0": "zip+https://api.foojay.io/disco/v3.0/ids/f59566e8946b11b02729242e212d4c92/redirect",
"1.13.0-2": "zip+https://api.foojay.io/disco/v3.0/ids/7557aedde3b115f52e0a3671176180d8/redirect",
"1.13.0-1": "zip+https://api.foojay.io/disco/v3.0/ids/1ca9d52ad86896044afcfa7390ba6020/redirect",
"1.13.0-0": "zip+https://api.foojay.io/disco/v3.0/ids/f3bb2f3a7204898bec3fffb11dd474e3/redirect",
"1.12.0-2": "zip+https://api.foojay.io/disco/v3.0/ids/dff6b32d8020d4839540f203797ae078/redirect",
"1.12.0-1": "zip+https://api.foojay.io/disco/v3.0/ids/cc5dd58e9d4bfc79fc23ce0c6e4aae41/redirect",
"1.12.0-0": "zip+https://api.foojay.io/disco/v3.0/ids/e3174fcaebaf3f17e9ef78efb509c832/redirect",
"1.11.0-11": "zip+https://api.foojay.io/disco/v3.0/ids/46ffc6fbdf840a4a9a09005b9967515d/redirect",
"1.11.0-10": "zip+https://api.foojay.io/disco/v3.0/ids/9d045cd80fc17f443596bf89553fed83/redirect",
"1.11.0-9": "zip+https://api.foojay.io/disco/v3.0/ids/feff42f4f639588a9ca9fa27f28e8233/redirect",
"1.11.0-8": "zip+https://api.foojay.io/disco/v3.0/ids/e25ad0db932ceefa0a164cc5fde809e7/redirect",
"1.11.0-7": "zip+https://api.foojay.io/disco/v3.0/ids/5a556032c4390f159127c5d919ad04a4/redirect",
"1.11.0-6": "zip+https://api.foojay.io/disco/v3.0/ids/80a05f4cd9b2686df5ca4aca0b4ca108/redirect",
"1.11.0-5": "zip+https://api.foojay.io/disco/v3.0/ids/eef303ca99dac2d76ac05a38934cc22a/redirect",
"1.11.0-4": "zip+https://api.foojay.io/disco/v3.0/ids/db9803b781511e176cc2a72fdfe7d1a3/redirect",
"1.11.0-3": "zip+https://api.foojay.io/disco/v3.0/ids/28300d3908cc3787094d4b2cf50ff4f5/redirect",
"1.11.0-2": "zip+https://api.foojay.io/disco/v3.0/ids/247c07abeb7e44e34e04c153fb44316e/redirect",
"1.11.0-1": "zip+https://api.foojay.io/disco/v3.0/ids/d95814c94b3bf6d7a5525113cfbdaf8a/redirect",
"1.8.0-292": "zip+https://api.foojay.io/disco/v3.0/ids/707642aec2c73771d574e263e485451d/redirect",
"1.8.0-282": "zip+https://api.foojay.io/disco/v3.0/ids/cc3b8cce78aa4e4dec59fe80c0c82b5d/redirect",
"1.8.0-275": "zip+https://api.foojay.io/disco/v3.0/ids/448e20abc1f44f60a12014cee401c257/redirect",
"1.8.0-272": "zip+https://api.foojay.io/disco/v3.0/ids/a5eae3571533770d7e8375b87b36aff5/redirect",
"1.8.0-265": "zip+https://api.foojay.io/disco/v3.0/ids/3081a6672a2b10e518cbc21aeab8502b/redirect",
"1.8.0-262": "zip+https://api.foojay.io/disco/v3.0/ids/8b3102c6c08d75fda0cb2c7a95f16fea/redirect",
"1.8.0-252": "zip+https://api.foojay.io/disco/v3.0/ids/81c3363bc6d79f6f31137b283cf79070/redirect",
"1.8.0-242": "zip+https://api.foojay.io/disco/v3.0/ids/2bcb9469f59122ebc02d59bfa27e112e/redirect",
"1.8.0-232": "zip+https://api.foojay.io/disco/v3.0/ids/017fda01ad461c48123bf6484aa42be6/redirect",
"1.8.0-222": "zip+https://api.foojay.io/disco/v3.0/ids/5bf8cffebe144b166e13e63db36ca942/redirect",
"1.8.0-212": "zip+https://api.foojay.io/disco/v3.0/ids/b11ffaafc5374b323ae6df629aab2eb6/redirect",
"1.8.0-202": "zip+https://api.foojay.io/disco/v3.0/ids/8690df9a62f5c2992090b3c21297ba9a/redirect",
"1.8.0-192": "zip+https://api.foojay.io/disco/v3.0/ids/647abfa75103e644b3956124574efde7/redirect",
"1.8.0-181": "zip+https://api.foojay.io/disco/v3.0/ids/51e3d94d4ae85dd8329291fc3f527b5c/redirect"
}
},
"amd64": {
"jdk@zulu": {
"8": "zip+https://api.foojay.io/disco/v3.0/ids/88339a558fccd13a9f02c46b250905e0/redirect",
"9": "zip+https://api.foojay.io/disco/v3.0/ids/cbbc5f4e3c5b8c6ec798297e31249593/redirect",
"10": "zip+https://api.foojay.io/disco/v3.0/ids/f9984c38ddafccbe56171d0dc1840a6c/redirect",
"12": "zip+https://api.foojay.io/disco/v3.0/ids/592e43c4339de9a54c73513239f3fdb9/redirect",
"13": "zip+https://api.foojay.io/disco/v3.0/ids/f2d8e3d0a1312e0bfe5437a7703e5b06/redirect",
"14": "zip+https://api.foojay.io/disco/v3.0/ids/f953d5ae4c4df05155c5d975b6dd3a73/redirect",
"15": "zip+https://api.foojay.io/disco/v3.0/ids/43ff356abe54f488cdf20b02be914ea9/redirect",
"16": "zip+https://api.foojay.io/disco/v3.0/ids/0d95d4fe521697ccc2b0697a77c6c235/redirect",
"17": "zip+https://api.foojay.io/disco/v3.0/ids/048a3c5ab08923d28685bcad718f4a48/redirect",
"18": "zip+https://api.foojay.io/disco/v3.0/ids/8d5ed6b8d2a763ca1cdbc8896365238b/redirect",
"19": "zip+https://api.foojay.io/disco/v3.0/ids/6c5a66f462a77fc8048d59d054da6c61/redirect",
"19.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/90e571909ff46154eecfe78552100b49/redirect",
"19.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/d0bcd46e5c3e972c1f2ea10606306e6a/redirect",
"18.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/03bf8bfe431b5471145c74446185abd2/redirect",
"18.0.2-1": "zip+https://api.foojay.io/disco/v3.0/ids/aac9235ea74b4377701a53ec91d85f1d/redirect",
"18.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/355142052e18fd01cd284cfc1a4ceb08/redirect",
"17.0.6": "zip+https://api.foojay.io/disco/v3.0/ids/c7a6968b55cd1a5d2383cfe0b84ba08a/redirect",
"17.0.5": "zip+https://api.foojay.io/disco/v3.0/ids/29060e083a9314873df323a49643eade/redirect",
"17.0.4": "zip+https://api.foojay.io/disco/v3.0/ids/01a97c23a461707aac24285fe7f067c6/redirect",
"17.0.4-1": "zip+https://api.foojay.io/disco/v3.0/ids/44317db7201dbabd6699ed04a8c218a0/redirect",
"17.0.3": "zip+https://api.foojay.io/disco/v3.0/ids/390dd572d83d0c5970d655ddfc56a1a1/redirect",
"17.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/196da9cb6aa6ad32f94e60265bfb572e/redirect",
"17.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/a2f4feab30868425b45f1ebdfc20e8ce/redirect",
"16.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/912652a54c878b1cf215dcf590744712/redirect",
"16.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/15e62b09f967c41cad4c064a4d27bdc5/redirect",
"15.0.10": "zip+https://api.foojay.io/disco/v3.0/ids/dd0303854b50756976ae8ce82f149e2a/redirect",
"15.0.9": "zip+https://api.foojay.io/disco/v3.0/ids/2dd4f41015eff26d66c6302b58febfe5/redirect",
"15.0.8": "zip+https://api.foojay.io/disco/v3.0/ids/2968260ff40d39fa622dbb8177fa1859/redirect",
"15.0.7": "zip+https://api.foojay.io/disco/v3.0/ids/de82159a10c6286c03bea8190ae30fbf/redirect",
"15.0.6": "zip+https://api.foojay.io/disco/v3.0/ids/f72066f32d8ec4c79204cc1cb70426bd/redirect",
"15.0.5": "zip+https://api.foojay.io/disco/v3.0/ids/352defa06d01c53b69fa3975d6d401f1/redirect",
"15.0.4": "zip+https://api.foojay.io/disco/v3.0/ids/941c8ab7580b512ca70d355b8c4bbcca/redirect",
"15.0.3": "zip+https://api.foojay.io/disco/v3.0/ids/6e4522f2911d3228afda49da539159c3/redirect",
"15.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/6d8658ad05993013f10bff839afb56ee/redirect",
"15.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/67c77a4b44bb49c5b51a0806d9523a5a/redirect",
"14.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/2a3344c7ec95a0fe9fdd73e7f8e74d82/redirect",
"14.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/da730accd163b7a60792b0275a05fd5b/redirect",
"13.0.14": "zip+https://api.foojay.io/disco/v3.0/ids/c368f42d0c2360cc2e84a77579fa9015/redirect",
"13.0.13": "zip+https://api.foojay.io/disco/v3.0/ids/6675688a9775a0886b415490f3f5f1ae/redirect",
"13.0.12": "zip+https://api.foojay.io/disco/v3.0/ids/a00bc2f87e0ff90a2491ee62766b3be4/redirect",
"13.0.11": "zip+https://api.foojay.io/disco/v3.0/ids/54c0898f14e0cc3710f25d7630f6cf3f/redirect",
"13.0.10": "zip+https://api.foojay.io/disco/v3.0/ids/e2ffecb8677c43e033863dcda43b6db5/redirect",
"13.0.9": "zip+https://api.foojay.io/disco/v3.0/ids/5b38cc251bb5035ae82845ddbcc2ba3a/redirect",
"13.0.8": "zip+https://api.foojay.io/disco/v3.0/ids/4c0b4f5145c20b55b9c88e6c4f46fb16/redirect",
"13.0.7": "zip+https://api.foojay.io/disco/v3.0/ids/0da1301d11774d865c0e5fe271583be2/redirect",
"13.0.6": "zip+https://api.foojay.io/disco/v3.0/ids/7c1a87f5602a63857eb3e0065e25e865/redirect",
"13.0.5": "zip+https://api.foojay.io/disco/v3.0/ids/8ea35a2a95c603ced84c5b446d04c1d4/redirect",
"13.0.5-1": "zip+https://api.foojay.io/disco/v3.0/ids/43bd2b348d2dd893c7eb300c869e6288/redirect",
"13.0.4": "zip+https://api.foojay.io/disco/v3.0/ids/52a1e154f4a1c36f57b30cf39c6314f8/redirect",
"13.0.3": "zip+https://api.foojay.io/disco/v3.0/ids/4850172fb031fcc423bddbf3964885ea/redirect",
"13.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/3c0b519c0fb2abcccfad8be507f6d4a1/redirect",
"13.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/d14de4caf6a8eff488898da1f5c4b61e/redirect",
"12.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/8a7155b39cf0bc8b18665e8343281fd5/redirect",
"12.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/922b2c850d6c50df4fd64e42af8f2c9a/redirect",
"11.0.18": "zip+https://api.foojay.io/disco/v3.0/ids/7674d340d8cd952fd06d687a31cde844/redirect",
"11.0.17": "zip+https://api.foojay.io/disco/v3.0/ids/090a5c89d0d4d59c7653a9bfb350d9c5/redirect",
"11.0.16": "zip+https://api.foojay.io/disco/v3.0/ids/34ee719a3de9b0e11f94bea9602cd4a7/redirect",
"11.0.16-1": "zip+https://api.foojay.io/disco/v3.0/ids/c6e63c31e67fe422f7aa328268c19a55/redirect",
"11.0.15": "zip+https://api.foojay.io/disco/v3.0/ids/dcf4876d4b62a53ce5397e4df4b981f8/redirect",
"11.0.14": "zip+https://api.foojay.io/disco/v3.0/ids/466095ea236d1dd13c9cdc977abe4283/redirect",
"11.0.14-1": "zip+https://api.foojay.io/disco/v3.0/ids/95c8b7c5902df7ba55c5efa679ef0c5c/redirect",
"11.0.13": "zip+https://api.foojay.io/disco/v3.0/ids/9cc4ec4af7181f97796205233d14c0a6/redirect",
"11.0.12": "zip+https://api.foojay.io/disco/v3.0/ids/9e03ab65973aa7ddd2192de54e7c151b/redirect",
"11.0.11": "zip+https://api.foojay.io/disco/v3.0/ids/522044c8191fe4e0426edfbd4839cee9/redirect",
"11.0.10": "zip+https://api.foojay.io/disco/v3.0/ids/d50b3ff06da136b885ca8e363121d516/redirect",
"11.0.9": "zip+https://api.foojay.io/disco/v3.0/ids/4cf307f6437e7ae7b64607846c137df3/redirect",
"11.0.9-1": "zip+https://api.foojay.io/disco/v3.0/ids/aee7c919c1099f5285ee9c55ac41aaba/redirect",
"11.0.8": "zip+https://api.foojay.io/disco/v3.0/ids/3c873ae524605a5013b9a29107ee5524/redirect",
"11.0.7": "zip+https://api.foojay.io/disco/v3.0/ids/72b404b01a3c85a283484291c0fc1623/redirect",
"11.0.6": "zip+https://api.foojay.io/disco/v3.0/ids/d2f6ebf9359a70eb61e449a4ec28d2d0/redirect",
"11.0.5": "zip+https://api.foojay.io/disco/v3.0/ids/d2e84770fe3e400016d7f51fe86535f8/redirect",
"11.0.4": "zip+https://api.foojay.io/disco/v3.0/ids/2ba569b7f658e14b01039451bc91d3ab/redirect",
"11.0.3": "zip+https://api.foojay.io/disco/v3.0/ids/3ba4e1f3a99d4f66ae79c9276da9bcac/redirect",
"11.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/fbe1ac4d9fecbd6cd45017161028c926/redirect",
"11.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/16705ec1ab815e317dafed5235210756/redirect",
"10.0.2": "zip+https://api.foojay.io/disco/v3.0/ids/7b0be866902cedc9e31a475ec9023bbc/redirect",
"10.0.1": "zip+https://api.foojay.io/disco/v3.0/ids/b712d2623b6f25d3e9f9ebbb37b505cb/redirect",
"9.0.7": "zip+https://api.foojay.io/disco/v3.0/ids/26773c11c2f52daf5956640cb3a6a06c/redirect",
"9.0
gitextract_ws_eknom/
├── .editorconfig
├── .gitattributes
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ └── bug_report.md
│ └── workflows/
│ └── build.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── Makefile
├── README.md
├── cfg/
│ └── cfg.go
├── command/
│ ├── alias.go
│ ├── check.go
│ ├── check_test.go
│ ├── current.go
│ ├── current_test.go
│ ├── deactivate.go
│ ├── deactivate_test.go
│ ├── fileiter/
│ │ ├── iterator.go
│ │ └── iterator_test.go
│ ├── init.go
│ ├── init_test.go
│ ├── install.go
│ ├── install_test.go
│ ├── link.go
│ ├── ls-alias.go
│ ├── ls-remote.go
│ ├── ls.go
│ ├── uninstall.go
│ ├── use.go
│ ├── use_test.go
│ └── which.go
├── go.mod
├── go.sum
├── index.json
├── install.ps1
├── install.sh
├── jabba.go
├── jabbaw
├── jabbaw.md
├── jabbaw.ps1
├── semver/
│ ├── range.go
│ ├── range_test.go
│ ├── version.go
│ └── version_test.go
└── w32/
├── w32.go
├── w32_darwin.go
├── w32_linux.go
└── w32_windows.go
SYMBOL INDEX (172 symbols across 31 files)
FILE: cfg/cfg.go
function Dir (line 11) | func Dir() string {
function Index (line 23) | func Index() string {
FILE: command/alias.go
function SetAlias (line 11) | func SetAlias(name string, ver string) (err error) {
function GetAlias (line 20) | func GetAlias(name string) string {
FILE: command/check.go
type CheckResult (line 13) | type CheckResult struct
function Check (line 24) | func Check() (*CheckResult, error) {
function detectShell (line 51) | func detectShell() string {
function getIntegrationFile (line 84) | func getIntegrationFile(shell string) string {
function getRCFile (line 98) | func getRCFile(shell string) string {
function checkRCFileHasSource (line 126) | func checkRCFileHasSource(rcFile, integrationFile string) bool {
function PrintCheckResult (line 139) | func PrintCheckResult(result *CheckResult) {
FILE: command/check_test.go
function TestDetectShell (line 10) | func TestDetectShell(t *testing.T) {
function TestGetIntegrationFile (line 46) | func TestGetIntegrationFile(t *testing.T) {
function TestGetRCFile (line 68) | func TestGetRCFile(t *testing.T) {
function TestCheckRCFileHasSource (line 91) | func TestCheckRCFileHasSource(t *testing.T) {
function TestCheck (line 114) | func TestCheck(t *testing.T) {
function TestCheckWithIntegrationFile (line 139) | func TestCheckWithIntegrationFile(t *testing.T) {
FILE: command/current.go
function Current (line 14) | func Current() string {
FILE: command/current_test.go
function TestCurrent (line 10) | func TestCurrent(t *testing.T) {
FILE: command/deactivate.go
function Deactivate (line 11) | func Deactivate() ([]string, error) {
FILE: command/deactivate_test.go
function TestDeactivate (line 11) | func TestDeactivate(t *testing.T) {
function TestDeactivateInUnusedEnv (line 31) | func TestDeactivateInUnusedEnv(t *testing.T) {
FILE: command/fileiter/iterator.go
type Iterator (line 9) | type Iterator struct
method Next (line 41) | func (w *Iterator) Next() bool {
method next (line 80) | func (w *Iterator) next(v *node) bool {
method Dir (line 89) | func (w *Iterator) Dir() string {
method Name (line 93) | func (w *Iterator) Name() string {
method IsDir (line 97) | func (w *Iterator) IsDir() bool {
method Err (line 101) | func (w *Iterator) Err() error {
method SkipDir (line 105) | func (w *Iterator) SkipDir() {
type node (line 15) | type node struct
function BreadthFirst (line 25) | func BreadthFirst() IterationOption {
function New (line 31) | func New(dir string, opts ...IterationOption) *Iterator {
FILE: command/fileiter/iterator_test.go
function TestDFS (line 10) | func TestDFS(t *testing.T) {
function TestBFS (line 36) | func TestBFS(t *testing.T) {
function TestNew (line 62) | func TestNew(t *testing.T) {
function test (line 82) | func test(t *testing.T, dir string, expectedSeq []string, opts ...Iterat...
function touch (line 97) | func touch(path ...string) error {
function mkdir (line 108) | func mkdir(path ...string) error {
FILE: command/init.go
function Init (line 12) | func Init() error {
function createIntegrationFile (line 50) | func createIntegrationFile(shell, integrationFile string) error {
function generateBashIntegration (line 74) | func generateBashIntegration(jabbaHome string) string {
function generatePowerShellIntegration (line 115) | func generatePowerShellIntegration(jabbaHome string) string {
function generateFishIntegration (line 141) | func generateFishIntegration(jabbaHome string) string {
function generateNushellIntegration (line 162) | func generateNushellIntegration(jabbaHome string) string {
function updateRCFile (line 187) | func updateRCFile(shell, rcFile, integrationFile string) error {
FILE: command/init_test.go
function TestGenerateBashIntegration (line 11) | func TestGenerateBashIntegration(t *testing.T) {
function TestGeneratePowerShellIntegration (line 25) | func TestGeneratePowerShellIntegration(t *testing.T) {
function TestGenerateFishIntegration (line 36) | func TestGenerateFishIntegration(t *testing.T) {
function TestGenerateNushellIntegration (line 47) | func TestGenerateNushellIntegration(t *testing.T) {
function TestCreateIntegrationFile (line 58) | func TestCreateIntegrationFile(t *testing.T) {
function TestUpdateRCFile (line 91) | func TestUpdateRCFile(t *testing.T) {
function TestUpdateRCFileExisting (line 125) | func TestUpdateRCFileExisting(t *testing.T) {
function TestInit (line 158) | func TestInit(t *testing.T) {
function TestInitIdempotent (line 201) | func TestInitIdempotent(t *testing.T) {
FILE: command/install.go
function Install (line 29) | func Install(selector string, dst string) (string, error) {
function isEmptyDir (line 144) | func isEmptyDir(name string) (bool, error) {
type RedirectTracer (line 152) | type RedirectTracer struct
method RoundTrip (line 156) | func (self RedirectTracer) RoundTrip(req *http.Request) (resp *http.Re...
function download (line 172) | func download(url string, fileType string) (file string, err error) {
function installOnDarwin (line 231) | func installOnDarwin(file string, fileType string, dst string) (err erro...
function normalizePathToBinJava (line 254) | func normalizePathToBinJava(dir string, goos string) error {
function expectedJavaPath (line 310) | func expectedJavaPath(dir string, goos string) string {
function assertJavaDistribution (line 322) | func assertJavaDistribution(dir string, goos string) error {
function installFromDmg (line 333) | func installFromDmg(src string, dst string) error {
function installOnLinux (line 378) | func installOnLinux(file string, fileType string, dst string) (err error) {
function installOnWindows (line 402) | func installOnWindows(file string, fileType string, dst string) (err err...
function installFromBin (line 424) | func installFromBin(src string, dst string) (err error) {
function installFromIa (line 438) | func installFromIa(src string, dst string) error {
function installFromExe (line 453) | func installFromExe(src string, dst string) error {
function installFromTgz (line 460) | func installFromTgz(src string, dst string) error {
function untgz (line 465) | func untgz(src string, dst string, strip bool) error {
function installFromTgx (line 573) | func installFromTgx(src string, dst string) error {
function untgx (line 578) | func untgx(src string, dst string, strip bool) error {
function installFromZip (line 684) | func installFromZip(src string, dst string) error {
function unzip (line 689) | func unzip(src string, dst string, strip bool) error {
function sh (line 769) | func sh(cmd string) error {
FILE: command/install_test.go
function TestBinJavaRelocation (line 10) | func TestBinJavaRelocation(t *testing.T) {
function touch (line 82) | func touch(path ...string) error {
function file (line 93) | func file(path ...string) error {
FILE: command/link.go
function Link (line 15) | func Link(selector string, dir string) error {
function LinkLatest (line 40) | func LinkLatest() error {
function LinkAlias (line 82) | func LinkAlias(name string) error {
function linkAlias (line 90) | func linkAlias(name string, vs []*semver.Version) error {
function GetLink (line 119) | func GetLink(name string) string {
FILE: command/ls-alias.go
function LsAlias (line 11) | func LsAlias() (map[string]string, error) {
FILE: command/ls-remote.go
type byOS (line 15) | type byOS
type byArch (line 16) | type byArch
type byDistribution (line 17) | type byDistribution
function LsRemote (line 19) | func LsRemote(os, arch string) (map[*semver.Version]string, error) {
function fetch (line 49) | func fetch(url string) (content []byte, err error) {
FILE: command/ls.go
function Ls (line 17) | func Ls() ([]*semver.Version, error) {
function LsBestMatch (line 33) | func LsBestMatch(selector string) (ver string, err error) {
function LsBestMatchWithVersionSlice (line 41) | func LsBestMatchWithVersionSlice(vs []*semver.Version, selector string) ...
FILE: command/uninstall.go
function Uninstall (line 10) | func Uninstall(selector string) error {
FILE: command/use.go
function Use (line 12) | func Use(selector string) ([]string, error) {
function usePath (line 24) | func usePath(path string) ([]string, error) {
FILE: command/use_test.go
type FileInfoMock (line 13) | type FileInfoMock
method Name (line 15) | func (f FileInfoMock) Name() string { return string(f) }
method Size (line 16) | func (f FileInfoMock) Size() int64 { return 0 }
method Mode (line 17) | func (f FileInfoMock) Mode() os.FileMode { return os.FileMode(0) }
method ModTime (line 18) | func (f FileInfoMock) ModTime() time.Time { return time.Time{} }
method IsDir (line 19) | func (f FileInfoMock) IsDir() bool { return true }
method Sys (line 20) | func (f FileInfoMock) Sys() interface{} { return nil }
function TestUse (line 22) | func TestUse(t *testing.T) {
FILE: command/which.go
function Which (line 10) | func Which(selector string, home bool) (string, error) {
FILE: jabba.go
function init (line 27) | func init() {
type simpleFormatter (line 44) | type simpleFormatter struct
method Format (line 46) | func (f *simpleFormatter) Format(entry *log.Entry) ([]byte, error) {
function main (line 56) | func main() {
function parseTrimTo (line 394) | func parseTrimTo(value string) semver.VersionPart {
type jabbarc (line 408) | type jabbarc struct
function rc (line 412) | func rc() (rc jabbarc) {
function use (line 429) | func use(ver string) error {
function printForShellToEval (line 438) | func printForShellToEval(out []string) {
FILE: semver/range.go
function pre070Compat (line 18) | func pre070Compat(version string) string {
type Range (line 38) | type Range struct
method Contains (line 44) | func (l *Range) Contains(r *Version) bool {
method String (line 48) | func (t *Range) String() string {
function ParseRange (line 52) | func ParseRange(raw string) (*Range, error) {
FILE: semver/range_test.go
function TestContains (line 7) | func TestContains(t *testing.T) {
function TestPre070Compat (line 24) | func TestPre070Compat(t *testing.T) {
function assertWithinRange (line 32) | func assertWithinRange(t *testing.T, rng string, ver string, value bool) {
FILE: semver/version.go
type Version (line 9) | type Version struct
method LessThan (line 15) | func (l *Version) LessThan(r *Version) bool {
method Equals (line 22) | func (l *Version) Equals(r *Version) bool {
method String (line 26) | func (t *Version) String() string {
method TrimTo (line 30) | func (t *Version) TrimTo(part VersionPart) string {
method Major (line 46) | func (t *Version) Major() int64 {
method Minor (line 50) | func (t *Version) Minor() int64 {
method Patch (line 54) | func (t *Version) Patch() int64 {
method Prerelease (line 58) | func (t *Version) Prerelease() string {
function ParseVersion (line 62) | func ParseVersion(raw string) (*Version, error) {
type VersionSlice (line 78) | type VersionSlice
method Len (line 82) | func (c VersionSlice) Len() int {
method Swap (line 85) | func (c VersionSlice) Swap(i, j int) {
method Less (line 88) | func (c VersionSlice) Less(i, j int) bool {
method TrimTo (line 100) | func (c VersionSlice) TrimTo(part VersionPart) VersionSlice {
type VersionPart (line 92) | type VersionPart
constant VPMajor (line 95) | VPMajor VersionPart = iota
constant VPMinor (line 96) | VPMinor
constant VPPatch (line 97) | VPPatch
FILE: semver/version_test.go
function TestSort (line 9) | func TestSort(t *testing.T) {
function asVersionSlice (line 20) | func asVersionSlice(t *testing.T, slice ...string) (r []*Version) {
FILE: w32/w32.go
type DWORD (line 4) | type DWORD
type HANDLE (line 5) | type HANDLE
type HINSTANCE (line 6) | type HINSTANCE
type HKEY (line 7) | type HKEY
type HWND (line 8) | type HWND
type ULONG (line 9) | type ULONG
type LPCTSTR (line 10) | type LPCTSTR
type LPVOID (line 11) | type LPVOID
constant ERROR_BAD_FORMAT (line 16) | ERROR_BAD_FORMAT = 11
constant SE_ERR_FNF (line 21) | SE_ERR_FNF = 2
constant SE_ERR_PNF (line 22) | SE_ERR_PNF = 3
constant SE_ERR_ACCESSDENIED (line 23) | SE_ERR_ACCESSDENIED = 5
constant SE_ERR_OOM (line 24) | SE_ERR_OOM = 8
constant SE_ERR_DLLNOTFOUND (line 25) | SE_ERR_DLLNOTFOUND = 32
constant SE_ERR_SHARE (line 26) | SE_ERR_SHARE = 26
constant SE_ERR_ASSOCINCOMPLETE (line 27) | SE_ERR_ASSOCINCOMPLETE = 27
constant SE_ERR_DDETIMEOUT (line 28) | SE_ERR_DDETIMEOUT = 28
constant SE_ERR_DDEFAIL (line 29) | SE_ERR_DDEFAIL = 29
constant SE_ERR_DDEBUSY (line 30) | SE_ERR_DDEBUSY = 30
constant SE_ERR_NOASSOC (line 31) | SE_ERR_NOASSOC = 31
constant SEE_MASK_NOCLOSEPROCESS (line 36) | SEE_MASK_NOCLOSEPROCESS = 0x00000040
type SHELLEXECUTEINFO (line 39) | type SHELLEXECUTEINFO struct
FILE: w32/w32_darwin.go
function ShellExecuteAndWait (line 3) | func ShellExecuteAndWait(hwnd HWND, lpOperation, lpFile, lpParameters, l...
function ShellExecuteEx (line 7) | func ShellExecuteEx(pExecInfo *SHELLEXECUTEINFO) error {
FILE: w32/w32_linux.go
function ShellExecuteAndWait (line 3) | func ShellExecuteAndWait(hwnd HWND, lpOperation, lpFile, lpParameters, l...
function ShellExecuteEx (line 7) | func ShellExecuteEx(pExecInfo *SHELLEXECUTEINFO) error {
FILE: w32/w32_windows.go
function ShellExecuteAndWait (line 20) | func ShellExecuteAndWait(hwnd HWND, lpOperation, lpFile, lpParameters, l...
function ShellExecuteEx (line 44) | func ShellExecuteEx(pExecInfo *SHELLEXECUTEINFO) error {
Condensed preview — 48 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,304K chars).
[
{
"path": ".editorconfig",
"chars": 241,
"preview": "root = true\n\n[*]\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = true\ninsert_final_newline = true\n\n[*.go]\nin"
},
{
"path": ".gitattributes",
"chars": 12,
"preview": "* text=auto\n"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 799,
"preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the b"
},
{
"path": ".github/workflows/build.yml",
"chars": 1278,
"preview": "name: Build and Test\n\non:\n push:\n branches: [ \"main\" ]\n pull_request:\n branches: [ \"main\" ]\n\njobs:\n test:\n s"
},
{
"path": ".gitignore",
"chars": 40,
"preview": "jabba\ncoverage.html\nrelease\nvendor\n.tmp\n"
},
{
"path": "CHANGELOG.md",
"chars": 7195,
"preview": "# Changelog\nAll notable changes to this project will be documented in this file. \nThis project adheres to [Semantic Ver"
},
{
"path": "LICENSE",
"chars": 11357,
"preview": " Apache License\n Version 2.0, January 2004\n "
},
{
"path": "Makefile",
"chars": 2499,
"preview": "SHELL := /bin/bash -o pipefail\nVERSION := $(shell git describe --tags --abbrev=0)\n\nfetch:\n\tgo install github.com/modocac"
},
{
"path": "README.md",
"chars": 9896,
"preview": "# jabba  [\n\nfu"
},
{
"path": "command/alias.go",
"chars": 494,
"preview": "package command\n\nimport (\n\t\"io/ioutil\"\n\t\"os\"\n\t\"path/filepath\"\n\n\t\"github.com/Jabba-Team/jabba/cfg\"\n)\n\nfunc SetAlias(name "
},
{
"path": "command/check.go",
"chars": 4618,
"preview": "package command\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n\n\t\"github.com/Jabba-Team/jabba/cfg\"\n)\n\ntyp"
},
{
"path": "command/check_test.go",
"chars": 3949,
"preview": "package command\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"testing\"\n)\n\nfunc TestDetectShell(t *testing.T) {\n\ttests :="
},
{
"path": "command/current.go",
"chars": 504,
"preview": "package command\n\nimport (\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/Jabba-Team/jabba/cfg\"\n)\n\nvar lookPa"
},
{
"path": "command/current_test.go",
"chars": 471,
"preview": "package command\n\nimport (\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/Jabba-Team/jabba/cfg\"\n)\n\nfunc TestCurrent(t *testing"
},
{
"path": "command/deactivate.go",
"chars": 637,
"preview": "package command\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"regexp\"\n\n\t\"github.com/Jabba-Team/jabba/cfg\"\n)\n\nfunc Deactivate() ([]s"
},
{
"path": "command/deactivate_test.go",
"chars": 1361,
"preview": "package command\n\nimport (\n\t\"os\"\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/Jabba-Team/jabba/cfg\"\n)\n\nfunc TestDeactivate(t *test"
},
{
"path": "command/fileiter/iterator.go",
"chars": 2029,
"preview": "package fileiter\n\nimport (\n\t\"io/ioutil\"\n\t\"os\"\n\t\"path/filepath\"\n)\n\ntype Iterator struct {\n\thead *node\n\ttail "
},
{
"path": "command/fileiter/iterator_test.go",
"chars": 2360,
"preview": "package fileiter\n\nimport (\n\t\"io/ioutil\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n)\n\nfunc TestDFS(t *testing.T) {\n\tok := func(er"
},
{
"path": "command/init.go",
"chars": 6378,
"preview": "package command\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/Jabba-Team/jabba/cfg\"\n)\n\nfunc Init() er"
},
{
"path": "command/init_test.go",
"chars": 6389,
"preview": "package command\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestGenerateBashIntegration(t "
},
{
"path": "command/install.go",
"chars": 18779,
"preview": "package command\n\nimport (\n\t\"archive/tar\"\n\t\"archive/zip\"\n\t\"compress/gzip\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"net/http\""
},
{
"path": "command/install_test.go",
"chars": 2231,
"preview": "package command\n\nimport (\n\t\"io/ioutil\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n)\n\nfunc TestBinJavaRelocation(t *testing.T) {\n\t"
},
{
"path": "command/link.go",
"chars": 3183,
"preview": "package command\n\nimport (\n\t\"errors\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n\n\t\"github.com/Jabba-Team/jabba/cfg\"\n\t\"g"
},
{
"path": "command/ls-alias.go",
"chars": 679,
"preview": "package command\n\nimport (\n\t\"io/ioutil\"\n\t\"path/filepath\"\n\n\t\"github.com/Jabba-Team/jabba/cfg\"\n\t\"strings\"\n)\n\nfunc LsAlias()"
},
{
"path": "command/ls-remote.go",
"chars": 1323,
"preview": "package command\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/Jabba-"
},
{
"path": "command/ls.go",
"chars": 1112,
"preview": "package command\n\nimport (\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"github.com/Jabba-Team/jabba/c"
},
{
"path": "command/uninstall.go",
"chars": 257,
"preview": "package command\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\n\t\"github.com/Jabba-Team/jabba/cfg\"\n)\n\nfunc Uninstall(selector string) "
},
{
"path": "command/use.go",
"chars": 1166,
"preview": "package command\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"runtime\"\n\n\t\"github.com/Jabba-Team/jabba/cfg\"\n)\n\nfunc Use(se"
},
{
"path": "command/use_test.go",
"chars": 1481,
"preview": "package command\n\nimport (\n\t\"os\"\n\t\"reflect\"\n\t\"runtime\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/Jabba-Team/jabba/cfg\"\n)\n\ntype Fil"
},
{
"path": "command/which.go",
"chars": 463,
"preview": "package command\n\nimport (\n\t\"path/filepath\"\n\t\"runtime\"\n\n\t\"github.com/Jabba-Team/jabba/cfg\"\n)\n\nfunc Which(selector string,"
},
{
"path": "go.mod",
"chars": 687,
"preview": "module github.com/Jabba-Team/jabba\n\ngo 1.26\n\nrequire (\n\tgithub.com/Masterminds/semver v1.5.0\n\tgithub.com/hashicorp/go-ro"
},
{
"path": "go.sum",
"chars": 4267,
"preview": "github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=\ngithub.com/Masterminds/semver v1.5."
},
{
"path": "index.json",
"chars": 1102676,
"preview": "{\n \"windows\": {\n \"386\": {\n \"jdk@zulu\": {\n \"10\": \"zip+https://api.foojay.io/disco/v3."
},
{
"path": "install.ps1",
"chars": 3962,
"preview": "$ErrorActionPreference = \"Stop\"\n\n$jabbaHome = if ($env:JABBA_HOME) { $env:JABBA_HOME } else { if ($env:JABBA_DIR) { $env"
},
{
"path": "install.sh",
"chars": 9207,
"preview": "#!/bin/bash -e\n{ # this ensures the entire script is downloaded\n\nJABBA_HOME=${JABBA_HOME:-$JABBA_DIR} # JABBA_DIR is her"
},
{
"path": "jabba.go",
"chars": 10908,
"preview": "package main\n\nimport (\n\t\"bytes\"\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"os\"\n\t\"runtime\"\n\t\"sort\"\n\t\"strings\"\n\n\tyaml"
},
{
"path": "jabbaw",
"chars": 1130,
"preview": "#!/bin/bash -e\n\nJABBA_HOME=${JABBA_HOME:-$JABBA_DIR} # JABBA_DIR is here for backward-compatibility\nJABBA_HOME=${JABBA_H"
},
{
"path": "jabbaw.md",
"chars": 1243,
"preview": "# jabba-wrapper\n\nSimple scripts to use jabba (https://github.com/Jabba-Team/jabba) without installation.\n\nInspired by an"
},
{
"path": "jabbaw.ps1",
"chars": 1114,
"preview": "$ErrorActionPreference = \"Stop\"\n\n$jabbaHome = if ($env:JABBA_HOME) { $env:JABBA_HOME } else { if ($env:JABBA_DIR) { $env"
},
{
"path": "semver/range.go",
"chars": 1578,
"preview": "package semver\n\nimport (\n\t\"fmt\"\n\t\"github.com/Masterminds/semver\"\n\t\"regexp\"\n\t\"strings\"\n)\n\nvar pre070CompatRegexp = regexp"
},
{
"path": "semver/range_test.go",
"chars": 1365,
"preview": "package semver\n\nimport (\n\t\"testing\"\n)\n\nfunc TestContains(t *testing.T) {\n\tassertWithinRange(t, \"1.8\", \"1.8.0\", true)\n\tas"
},
{
"path": "semver/version.go",
"chars": 2481,
"preview": "package semver\n\nimport (\n\t\"fmt\"\n\t\"github.com/Masterminds/semver\"\n\t\"strings\"\n)\n\ntype Version struct {\n\tqualifier string\n\t"
},
{
"path": "semver/version_test.go",
"chars": 724,
"preview": "package semver\n\nimport (\n\t\"reflect\"\n\t\"sort\"\n\t\"testing\"\n)\n\nfunc TestSort(t *testing.T) {\n\tactual := asVersionSlice(t,\n\t\t\""
},
{
"path": "w32/w32.go",
"chars": 1204,
"preview": "package w32\n\ntype (\n\tDWORD uint32\n\tHANDLE uintptr\n\tHINSTANCE HANDLE\n\tHKEY HANDLE\n\tHWND HANDLE\n\tULONG "
},
{
"path": "w32/w32_darwin.go",
"chars": 238,
"preview": "package w32\n\nfunc ShellExecuteAndWait(hwnd HWND, lpOperation, lpFile, lpParameters, lpDirectory string, nShowCmd int) er"
},
{
"path": "w32/w32_linux.go",
"chars": 238,
"preview": "package w32\n\nfunc ShellExecuteAndWait(hwnd HWND, lpOperation, lpFile, lpParameters, lpDirectory string, nShowCmd int) er"
},
{
"path": "w32/w32_windows.go",
"chars": 3155,
"preview": "package w32\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"syscall\"\n\t\"unsafe\"\n)\n\nvar (\n\tmodshell32 = syscall.NewLazyDLL(\"shell32.dll"
}
]
About this extraction
This page contains the full source code of the Jabba-Team/jabba GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 48 files (1.2 MB), approximately 526.0k tokens, and a symbol index with 172 extracted functions, classes, methods, constants, and types. 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.