Full Code of Jabba-Team/jabba for AI

main b045bf217b9e cached
48 files
1.2 MB
526.0k tokens
172 symbols
1 requests
Download .txt
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 ![Latest Version](https://img.shields.io/badge/latest-0.14.0-blue.svg) [![Actions Status](https://github.com/Jabba-Team/jabba/workflows/make%20Jabba/badge.svg)](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
Download .txt
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
Download .txt
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 ![Latest Version](https://img.shields.io/badge/latest-0.14.0-blue.svg) [![Actions Status](https://github.com/Jab"
  },
  {
    "path": "cfg/cfg.go",
    "chars": 499,
    "preview": "package cfg\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\n\tlog \"github.com/sirupsen/logrus\"\n\t\"github.com/mitchellh/go-homedir\"\n)\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.

Copied to clipboard!