├── .git-blame-ignore-revs ├── .github ├── dependabot.yml └── workflows │ ├── release.yml │ ├── rustfmt.yml │ ├── test.yml │ └── updateversiondb.yml ├── .gitignore ├── .rustfmt.toml ├── .vscode └── launch.json ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── build.rs ├── deploy ├── msi │ ├── Bitmaps │ │ ├── New.ico │ │ ├── Up.ico │ │ ├── bannrbmp.bmp │ │ ├── dlgbmp.bmp │ │ ├── exclamic.ico │ │ └── info.ico │ ├── Julia.wxs │ └── WixUI_InstallDir_NoLicense.wxs ├── msix │ ├── Fragments │ │ └── Julia.json │ ├── Images │ │ ├── LargeTile.png │ │ ├── LargeTile.scale-125.png │ │ ├── LargeTile.scale-150.png │ │ ├── LargeTile.scale-200.png │ │ ├── LargeTile.scale-400.png │ │ ├── SmallTile.png │ │ ├── SmallTile.scale-125.png │ │ ├── SmallTile.scale-150.png │ │ ├── SmallTile.scale-200.png │ │ ├── SmallTile.scale-400.png │ │ ├── SplashScreen.png │ │ ├── SplashScreen.scale-125.png │ │ ├── SplashScreen.scale-150.png │ │ ├── SplashScreen.scale-200.png │ │ ├── SplashScreen.scale-400.png │ │ ├── Square150x150Logo.png │ │ ├── Square150x150Logo.scale-125.png │ │ ├── Square150x150Logo.scale-150.png │ │ ├── Square150x150Logo.scale-200.png │ │ ├── Square150x150Logo.scale-400.png │ │ ├── Square44x44Logo.altform-lightunplated_targetsize-16.png │ │ ├── Square44x44Logo.altform-lightunplated_targetsize-24.png │ │ ├── Square44x44Logo.altform-lightunplated_targetsize-256.png │ │ ├── Square44x44Logo.altform-lightunplated_targetsize-32.png │ │ ├── Square44x44Logo.altform-lightunplated_targetsize-48.png │ │ ├── Square44x44Logo.altform-unplated_targetsize-16.png │ │ ├── Square44x44Logo.altform-unplated_targetsize-24.png │ │ ├── Square44x44Logo.altform-unplated_targetsize-256.png │ │ ├── Square44x44Logo.altform-unplated_targetsize-32.png │ │ ├── Square44x44Logo.altform-unplated_targetsize-48.png │ │ ├── Square44x44Logo.png │ │ ├── Square44x44Logo.scale-125.png │ │ ├── Square44x44Logo.scale-150.png │ │ ├── Square44x44Logo.scale-200.png │ │ ├── Square44x44Logo.scale-400.png │ │ ├── Square44x44Logo.targetsize-16.png │ │ ├── Square44x44Logo.targetsize-24.png │ │ ├── Square44x44Logo.targetsize-256.png │ │ ├── Square44x44Logo.targetsize-32.png │ │ ├── Square44x44Logo.targetsize-48.png │ │ ├── StoreLogo.png │ │ ├── StoreLogo.scale-125.png │ │ ├── StoreLogo.scale-150.png │ │ ├── StoreLogo.scale-200.png │ │ ├── StoreLogo.scale-400.png │ │ ├── Wide310x150Logo.png │ │ ├── Wide310x150Logo.scale-125.png │ │ ├── Wide310x150Logo.scale-150.png │ │ ├── Wide310x150Logo.scale-200.png │ │ └── Wide310x150Logo.scale-400.png │ ├── PackagingLayout.xml │ └── appxmanifest.xml ├── shellscript │ └── juliaup-init.sh ├── winappinstaller │ └── Julia.appinstaller ├── winpkgidentityext │ ├── appmanifest │ │ └── app.manifest │ ├── create-msix.ps1 │ ├── juliaup.msix │ └── msix │ │ └── appxmanifest.xml └── winstoresubmission │ ├── SBConfig.json │ ├── images │ └── 1.0 │ │ └── en-us │ │ └── julia-screenshot.png │ └── pdps │ └── en-us │ └── PDP.xml ├── devdocs └── versiondbupdates.pptx ├── release.toml ├── scripts ├── build_msi.ps1 ├── download_bundled_julia.ps1 └── versiondb │ ├── Manifest.toml │ ├── Project.toml │ └── updateversiondb.jl ├── src ├── bin │ ├── juliainstaller.rs │ ├── julialauncher.rs │ └── juliaup.rs ├── cli.rs ├── command_add.rs ├── command_api.rs ├── command_completions.rs ├── command_config_backgroundselfupdate.rs ├── command_config_modifypath.rs ├── command_config_startupselfupdate.rs ├── command_config_symlinks.rs ├── command_config_versionsdbupdate.rs ├── command_default.rs ├── command_gc.rs ├── command_info.rs ├── command_initial_setup_from_launcher.rs ├── command_link.rs ├── command_list.rs ├── command_override.rs ├── command_remove.rs ├── command_selfchannel.rs ├── command_selfuninstall.rs ├── command_selfupdate.rs ├── command_status.rs ├── command_update.rs ├── command_update_version_db.rs ├── config_file.rs ├── global_paths.rs ├── jsonstructs_versionsdb.rs ├── julia.ico ├── lib.rs ├── operations.rs ├── utils.rs └── versions_file.rs ├── tests ├── channel_selection.rs ├── command_add.rs ├── command_default.rs ├── command_gc.rs ├── command_initial_setup_from_launcher_test.rs ├── command_list_test.rs ├── command_override_test.rs ├── command_remove.rs ├── command_status_test.rs └── command_update.rs └── versiondb ├── versiondb-aarch64-apple-darwin.json ├── versiondb-aarch64-unknown-linux-gnu.json ├── versiondb-aarch64-unknown-linux-musl.json ├── versiondb-i686-pc-windows-gnu.json ├── versiondb-i686-pc-windows-msvc.json ├── versiondb-i686-unknown-linux-gnu.json ├── versiondb-i686-unknown-linux-musl.json ├── versiondb-x86_64-apple-darwin.json ├── versiondb-x86_64-pc-windows-gnu.json ├── versiondb-x86_64-pc-windows-msvc.json ├── versiondb-x86_64-unknown-freebsd.json ├── versiondb-x86_64-unknown-linux-gnu.json └── versiondb-x86_64-unknown-linux-musl.json /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # .git-blame-ignore-revs 2 | # formatting: ran cargo fmt on all files 3 | eec639c162233a270805009b87a3529955869447 4 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # Maintain dependencies for cargo 4 | - package-ecosystem: "cargo" 5 | directory: "/" 6 | schedule: 7 | interval: "weekly" 8 | # Maintain dependencies for GitHub Actions 9 | - package-ecosystem: "github-actions" 10 | directory: "/" 11 | schedule: 12 | interval: "weekly" 13 | -------------------------------------------------------------------------------- /.github/workflows/rustfmt.yml: -------------------------------------------------------------------------------- 1 | name: rustfmt 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | workflow_dispatch: 8 | 9 | # https://github.com/reviewdog/action-suggester?tab=readme-ov-file#required-permissions 10 | permissions: 11 | contents: read 12 | checks: write 13 | issues: write 14 | pull-requests: write 15 | 16 | jobs: 17 | rustfmt: 18 | name: Rustfmt Check 19 | runs-on: ubuntu-latest 20 | steps: 21 | - name: Checkout code 22 | uses: actions/checkout@v4 23 | - name: Setup Rust 24 | uses: actions-rust-lang/setup-rust-toolchain@v1 25 | with: 26 | components: rustfmt 27 | - name: Rustfmt check 28 | if: ${{ github.event_name != 'pull_request' }} 29 | run: cargo fmt -- --check -v 30 | - name: Rustfmt PR 31 | if: ${{ github.event_name == 'pull_request' }} 32 | run: cargo fmt -- -v 33 | - name: Suggest format changes 34 | if: ${{ github.event_name == 'pull_request' }} 35 | uses: reviewdog/action-suggester@v1 36 | with: 37 | tool_name: rustfmt 38 | fail_on_error: true 39 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | workflow_dispatch: 9 | 10 | concurrency: 11 | group: ${{ github.head_ref || github.ref_name || github.run_id }} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | 16 | check-juliaup: 17 | runs-on: ${{ matrix.os }}-latest 18 | strategy: 19 | matrix: 20 | label: [ 21 | x86_64-pc-windows-msvc-windowsstore, 22 | x86_64-pc-windows-msvc-windowsappinstaller, 23 | x86_64-pc-windows-gnu-msi, 24 | x86_64-pc-windows-gnu-msi, 25 | x86_64-apple-darwin, 26 | x86_64-unknown-linux-gnu, 27 | x86_64-unknown-linux-musl, 28 | x86_64-unknown-freebsd, 29 | i686-pc-windows-msvc-windowsstore, 30 | i686-pc-windows-msvc-windowsappinstaller, 31 | i686-pc-windows-gnu-msi, 32 | i686-unknown-linux-gnu, 33 | i686-unknown-linux-musl, 34 | aarch64-unknown-linux-gnu, 35 | aarch64-unknown-linux-musl, 36 | aarch64-apple-darwin 37 | ] 38 | include: 39 | - label: x86_64-pc-windows-msvc-windowsstore 40 | target: x86_64-pc-windows-msvc 41 | os: windows 42 | features: windowsstore,binjuliainstaller,binjulialauncher 43 | rustflags: 44 | - label: x86_64-pc-windows-msvc-windowsappinstaller 45 | target: x86_64-pc-windows-msvc 46 | os: windows 47 | features: windowsappinstaller,binjuliainstaller,binjulialauncher 48 | rustflags: 49 | - label: x86_64-pc-windows-gnu-msi 50 | target: x86_64-pc-windows-gnu 51 | os: windows 52 | features: windowsappinstaller,binjuliainstaller,binjulialauncher 53 | rustflags: 54 | - label: i686-pc-windows-gnu-msi 55 | target: i686-pc-windows-gnu 56 | os: windows 57 | features: windowsappinstaller,binjuliainstaller,binjulialauncher 58 | rustflags: 59 | - label: x86_64-apple-darwin 60 | target: x86_64-apple-darwin 61 | os: macos 62 | features: selfupdate,binjuliainstaller,binjulialauncher 63 | rustflags: 64 | - label: x86_64-unknown-linux-gnu 65 | target: x86_64-unknown-linux-gnu 66 | os: ubuntu 67 | features: selfupdate,binjuliainstaller,binjulialauncher 68 | rustflags: 69 | - label: x86_64-unknown-linux-musl 70 | target: x86_64-unknown-linux-musl 71 | os: ubuntu 72 | features: selfupdate,binjuliainstaller,binjulialauncher 73 | rustflags: -C target-feature=+crt-static 74 | - label: x86_64-unknown-freebsd 75 | target: x86_64-unknown-freebsd 76 | os: ubuntu 77 | features: selfupdate,binjuliainstaller,binjulialauncher 78 | rustflags: 79 | - label: i686-pc-windows-msvc-windowsstore 80 | target: i686-pc-windows-msvc 81 | os: windows 82 | features: windowsstore,binjuliainstaller,binjulialauncher 83 | rustflags: 84 | - label: i686-pc-windows-msvc-windowsappinstaller 85 | target: i686-pc-windows-msvc 86 | os: windows 87 | features: windowsappinstaller,binjuliainstaller,binjulialauncher 88 | rustflags: 89 | - label: i686-unknown-linux-gnu 90 | target: i686-unknown-linux-gnu 91 | os: ubuntu 92 | features: selfupdate,binjuliainstaller,binjulialauncher 93 | rustflags: 94 | - label: i686-unknown-linux-musl 95 | target: i686-unknown-linux-musl 96 | os: ubuntu 97 | features: selfupdate,binjuliainstaller,binjulialauncher 98 | rustflags: -C target-feature=+crt-static 99 | - label: aarch64-unknown-linux-gnu 100 | target: aarch64-unknown-linux-gnu 101 | os: ubuntu 102 | features: selfupdate,binjuliainstaller,binjulialauncher 103 | rustflags: 104 | - label: aarch64-unknown-linux-musl 105 | target: aarch64-unknown-linux-musl 106 | os: ubuntu 107 | features: selfupdate,binjuliainstaller,binjulialauncher 108 | rustflags: -C target-feature=+crt-static 109 | - label: aarch64-apple-darwin 110 | target: aarch64-apple-darwin 111 | os: macos 112 | features: selfupdate,binjuliainstaller,binjulialauncher 113 | rustflags: 114 | steps: 115 | - uses: actions/checkout@v4 116 | - if: ${{ contains(matrix.target, '-musl') }} 117 | run: sudo apt-get install musl-tools 118 | - uses: actions-rust-lang/setup-rust-toolchain@v1 119 | with: 120 | toolchain: stable 121 | target: ${{matrix.target}} 122 | - name: Check build 123 | uses: clechasseur/rs-cargo@v2 124 | with: 125 | command: check 126 | use-cross: ${{ matrix.os == 'ubuntu' }} 127 | args: --release --bins --target ${{matrix.target}} --features ${{matrix.features}} 128 | env: 129 | CARGO_TARGET_x86_64-unknown-linux-musl: ${{matrix.rustflags}} 130 | CARGO_TARGET_i686-unknown-linux-musl: ${{matrix.rustflags}} 131 | CARGO_TARGET_aarch64-unknown-linux-musl: ${{matrix.rustflags}} 132 | 133 | test-juliaup: 134 | runs-on: ${{ matrix.os }}-latest 135 | strategy: 136 | matrix: 137 | target: [ 138 | x86_64-pc-windows-msvc, 139 | x86_64-pc-windows-gnu, 140 | x86_64-apple-darwin, 141 | x86_64-unknown-linux-gnu, 142 | x86_64-unknown-linux-musl, 143 | x86_64-unknown-freebsd, 144 | i686-pc-windows-msvc, 145 | i686-pc-windows-gnu, 146 | ] 147 | include: 148 | - target: x86_64-pc-windows-msvc 149 | os: windows 150 | features: dummy,binjulialauncher 151 | rustflags: 152 | toolchain: stable 153 | - target: x86_64-pc-windows-gnu 154 | os: windows 155 | features: dummy,binjulialauncher 156 | rustflags: 157 | toolchain: stable-gnu 158 | - target: x86_64-apple-darwin 159 | os: macos 160 | features: dummy,binjulialauncher 161 | rustflags: 162 | toolchain: stable 163 | - target: x86_64-unknown-linux-gnu 164 | os: ubuntu 165 | features: dummy,binjulialauncher 166 | rustflags: 167 | toolchain: stable 168 | - target: x86_64-unknown-linux-musl 169 | os: ubuntu 170 | features: dummy,binjulialauncher 171 | rustflags: -C target-feature=+crt-static 172 | toolchain: stable 173 | - target: x86_64-unknown-freebsd 174 | os: ubuntu 175 | features: dummy,binjulialauncher 176 | rustflags: 177 | toolchain: stable 178 | - target: i686-pc-windows-msvc 179 | os: windows 180 | features: dummy,binjulialauncher 181 | rustflags: 182 | toolchain: stable 183 | - target: i686-pc-windows-gnu 184 | os: windows 185 | features: dummy,binjulialauncher 186 | rustflags: 187 | toolchain: stable-i686-gnu 188 | steps: 189 | - uses: actions/checkout@v4 190 | - if: ${{ contains(matrix.target, '-musl') }} 191 | run: sudo apt-get install musl-tools 192 | - uses: egor-tensin/setup-mingw@v2 193 | if: ${{ contains(matrix.toolchain, 'stable-i686-gnu') }} 194 | with: 195 | platform: x86 196 | version: 12.2.0 197 | - uses: actions-rust-lang/setup-rust-toolchain@v1 198 | if: ${{ ! contains(matrix.target, 'freebsd') }} 199 | with: 200 | toolchain: ${{matrix.toolchain}} 201 | target: ${{matrix.target}} 202 | - name: Test 203 | if: ${{ ! contains(matrix.target, 'freebsd') }} 204 | run: cargo test --target ${{matrix.target}} --features ${{matrix.features}} 205 | env: 206 | CARGO_TARGET_x86_64-unknown-linux-musl: ${{matrix.rustflags}} 207 | CARGO_TARGET_i686-unknown-linux-musl: ${{matrix.rustflags}} 208 | CARGO_TARGET_aarch64-unknown-linux-musl: ${{matrix.rustflags}} 209 | - name: Test FreeBSD 210 | if: ${{ contains(matrix.target, 'freebsd') }} 211 | uses: vmactions/freebsd-vm@v1 212 | with: 213 | release: "13.4" 214 | usesh: true 215 | mem: 8192 216 | copyback: false 217 | prepare: | 218 | pkg install -y curl 219 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain ${{matrix.toolchain}} 220 | run: | 221 | . "${HOME}/.cargo/env" 222 | export RUST_BACKTRACE=full 223 | cargo test --target ${{matrix.target}} --features ${{matrix.features}} 224 | -------------------------------------------------------------------------------- /.github/workflows/updateversiondb.yml: -------------------------------------------------------------------------------- 1 | name: Update Version DB 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | concurrency: 7 | group: versiondbupdate 8 | cancel-in-progress: true 9 | 10 | jobs: 11 | 12 | update-versiondb: 13 | runs-on: ubuntu-latest 14 | outputs: 15 | versionDbIsUpdated: ${{ steps.runjuliascript.outputs.versionDbIsUpdated }} 16 | steps: 17 | - uses: actions/checkout@v4 18 | - uses: julia-actions/setup-julia@v1 19 | with: 20 | version: 1.8 21 | - id: runjuliascript 22 | name: Update Version DB 23 | run: | 24 | julia scripts/versiondb/updateversiondb.jl scriptoutputfolder >> $GITHUB_OUTPUT 25 | - uses: actions/upload-artifact@v4 26 | with: 27 | name: scriptoutput 28 | path: scriptoutputfolder 29 | 30 | upload_versiondb: 31 | needs: [update-versiondb] 32 | environment: dev-channel 33 | runs-on: ubuntu-latest 34 | if: needs.update-versiondb.outputs.versionDbIsUpdated == 'true' 35 | steps: 36 | - uses: actions/checkout@v4 37 | - name: Download script output 38 | uses: actions/download-artifact@v4 39 | with: 40 | name: scriptoutput 41 | path: scriptoutputfolder 42 | - name: Upload version db 43 | uses: jakejarvis/s3-sync-action@master 44 | with: 45 | args: --acl public-read --cache-control public,max-age=2678400 --metadata-directive REPLACE 46 | env: 47 | AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }} 48 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} 49 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 50 | SOURCE_DIR: 'scriptoutputfolder/newversiondbs' 51 | - name: Upload version file 52 | uses: jakejarvis/s3-sync-action@master 53 | with: 54 | args: --acl public-read --cache-control public,max-age=0 --metadata-directive REPLACE 55 | env: 56 | AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }} 57 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} 58 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 59 | SOURCE_DIR: 'scriptoutputfolder/versionfile' 60 | - run: | 61 | mv scriptoutputfolder/versiondbbuild/* versiondb 62 | rm -rf scriptoutputfolder 63 | - name: Create Pull Request 64 | uses: peter-evans/create-pull-request@v7 65 | with: 66 | token: ${{ secrets.GITHUB_TOKEN }} 67 | commit-message: Update version db 68 | title: '[AUTO] Update version db' 69 | body: >- 70 | The version database has updated, this PR incorporates these changes. 71 | Please note: CI will not automatically run on this PR. 72 | To trigger CI, please close and re-open this PR. 73 | cc: @${{ github.actor }} 74 | labels: no changelog 75 | 76 | upload_versiondb_dev_channel: 77 | needs: [upload_versiondb] 78 | environment: dev-channel 79 | runs-on: ubuntu-latest 80 | steps: 81 | - name: Download script output 82 | uses: actions/download-artifact@v4 83 | with: 84 | name: scriptoutput 85 | path: scriptoutputfolder 86 | - run: | 87 | mv scriptoutputfolder/versionfile/DBVERSION scriptoutputfolder/versionfile/DEVCHANNELDBVERSION 88 | - name: Upload version file 89 | uses: jakejarvis/s3-sync-action@master 90 | with: 91 | args: --acl public-read --cache-control public,max-age=0 --metadata-directive REPLACE 92 | env: 93 | AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }} 94 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} 95 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 96 | SOURCE_DIR: 'scriptoutputfolder/versionfile' 97 | 98 | upload_versiondb_releasepreview_channel: 99 | needs: [upload_versiondb] 100 | environment: release-preview-channel 101 | runs-on: ubuntu-latest 102 | steps: 103 | - name: Download script output 104 | uses: actions/download-artifact@v4 105 | with: 106 | name: scriptoutput 107 | path: scriptoutputfolder 108 | - run: | 109 | mv scriptoutputfolder/versionfile/DBVERSION scriptoutputfolder/versionfile/RELEASEPREVIEWCHANNELDBVERSION 110 | - name: Upload version file 111 | uses: jakejarvis/s3-sync-action@master 112 | with: 113 | args: --acl public-read --cache-control public,max-age=0 --metadata-directive REPLACE 114 | env: 115 | AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }} 116 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} 117 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 118 | SOURCE_DIR: 'scriptoutputfolder/versionfile' 119 | 120 | upload_versiondb_release_channel: 121 | needs: [upload_versiondb] 122 | environment: release-channel 123 | runs-on: ubuntu-latest 124 | steps: 125 | - name: Download script output 126 | uses: actions/download-artifact@v4 127 | with: 128 | name: scriptoutput 129 | path: scriptoutputfolder 130 | - run: | 131 | mv scriptoutputfolder/versionfile/DBVERSION scriptoutputfolder/versionfile/RELEASECHANNELDBVERSION 132 | - name: Upload version file 133 | uses: jakejarvis/s3-sync-action@master 134 | with: 135 | args: --acl public-read --cache-control public,max-age=0 --metadata-directive REPLACE 136 | env: 137 | AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }} 138 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} 139 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 140 | SOURCE_DIR: 'scriptoutputfolder/versionfile' 141 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /optionalpackages 2 | /output 3 | /build 4 | /target 5 | /.wix 6 | .vscode/settings.json 7 | 8 | ## Ignore Visual Studio temporary files, build results, and 9 | ## files generated by popular Visual Studio add-ons. 10 | ## 11 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 12 | 13 | # User-specific files 14 | *.rsuser 15 | *.suo 16 | *.user 17 | *.userosscache 18 | *.sln.docstates 19 | 20 | # User-specific files (MonoDevelop/Xamarin Studio) 21 | *.userprefs 22 | 23 | # Mono auto generated files 24 | mono_crash.* 25 | 26 | # Build results 27 | [Dd]ebug/ 28 | [Dd]ebugPublic/ 29 | [Rr]elease/ 30 | [Rr]eleases/ 31 | x64/ 32 | x86/ 33 | [Aa][Rr][Mm]/ 34 | [Aa][Rr][Mm]64/ 35 | bld/ 36 | [Bb]in/ 37 | [Oo]bj/ 38 | [Ll]og/ 39 | [Ll]ogs/ 40 | 41 | # Visual Studio 2015/2017 cache/options directory 42 | .vs/ 43 | # Uncomment if you have tasks that create the project's static files in wwwroot 44 | #wwwroot/ 45 | 46 | # Visual Studio 2017 auto generated files 47 | Generated\ Files/ 48 | 49 | # MSTest test Results 50 | [Tt]est[Rr]esult*/ 51 | [Bb]uild[Ll]og.* 52 | 53 | # NUnit 54 | *.VisualState.xml 55 | TestResult.xml 56 | nunit-*.xml 57 | 58 | # Build Results of an ATL Project 59 | [Dd]ebugPS/ 60 | [Rr]eleasePS/ 61 | dlldata.c 62 | 63 | # Benchmark Results 64 | BenchmarkDotNet.Artifacts/ 65 | 66 | # .NET Core 67 | project.lock.json 68 | project.fragment.lock.json 69 | artifacts/ 70 | 71 | # StyleCop 72 | StyleCopReport.xml 73 | 74 | # Files built by Visual Studio 75 | *_i.c 76 | *_p.c 77 | *_h.h 78 | *.ilk 79 | *.meta 80 | *.obj 81 | *.iobj 82 | *.pch 83 | *.pdb 84 | *.ipdb 85 | *.pgc 86 | *.pgd 87 | *.rsp 88 | *.sbr 89 | *.tlb 90 | *.tli 91 | *.tlh 92 | *.tmp 93 | *.tmp_proj 94 | *_wpftmp.csproj 95 | *.log 96 | *.vspscc 97 | *.vssscc 98 | .builds 99 | *.pidb 100 | *.svclog 101 | *.scc 102 | 103 | # Chutzpah Test files 104 | _Chutzpah* 105 | 106 | # Visual C++ cache files 107 | ipch/ 108 | *.aps 109 | *.ncb 110 | *.opendb 111 | *.opensdf 112 | *.sdf 113 | *.cachefile 114 | *.VC.db 115 | *.VC.VC.opendb 116 | 117 | # Visual Studio profiler 118 | *.psess 119 | *.vsp 120 | *.vspx 121 | *.sap 122 | 123 | # Visual Studio Trace Files 124 | *.e2e 125 | 126 | # TFS 2012 Local Workspace 127 | $tf/ 128 | 129 | # Guidance Automation Toolkit 130 | *.gpState 131 | 132 | # ReSharper is a .NET coding add-in 133 | _ReSharper*/ 134 | *.[Rr]e[Ss]harper 135 | *.DotSettings.user 136 | 137 | # TeamCity is a build add-in 138 | _TeamCity* 139 | 140 | # DotCover is a Code Coverage Tool 141 | *.dotCover 142 | 143 | # AxoCover is a Code Coverage Tool 144 | .axoCover/* 145 | !.axoCover/settings.json 146 | 147 | # Visual Studio code coverage results 148 | *.coverage 149 | *.coveragexml 150 | 151 | # NCrunch 152 | _NCrunch_* 153 | .*crunch*.local.xml 154 | nCrunchTemp_* 155 | 156 | # MightyMoose 157 | *.mm.* 158 | AutoTest.Net/ 159 | 160 | # Web workbench (sass) 161 | .sass-cache/ 162 | 163 | # Installshield output folder 164 | [Ee]xpress/ 165 | 166 | # DocProject is a documentation generator add-in 167 | DocProject/buildhelp/ 168 | DocProject/Help/*.HxT 169 | DocProject/Help/*.HxC 170 | DocProject/Help/*.hhc 171 | DocProject/Help/*.hhk 172 | DocProject/Help/*.hhp 173 | DocProject/Help/Html2 174 | DocProject/Help/html 175 | 176 | # Click-Once directory 177 | publish/ 178 | 179 | # Publish Web Output 180 | *.[Pp]ublish.xml 181 | *.azurePubxml 182 | # Note: Comment the next line if you want to checkin your web deploy settings, 183 | # but database connection strings (with potential passwords) will be unencrypted 184 | *.pubxml 185 | *.publishproj 186 | 187 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 188 | # checkin your Azure Web App publish settings, but sensitive information contained 189 | # in these scripts will be unencrypted 190 | PublishScripts/ 191 | 192 | # NuGet Packages 193 | *.nupkg 194 | # NuGet Symbol Packages 195 | *.snupkg 196 | # The packages folder can be ignored because of Package Restore 197 | **/[Pp]ackages/* 198 | # except build/, which is used as an MSBuild target. 199 | !**/[Pp]ackages/build/ 200 | # Uncomment if necessary however generally it will be regenerated when needed 201 | #!**/[Pp]ackages/repositories.config 202 | # NuGet v3's project.json files produces more ignorable files 203 | *.nuget.props 204 | *.nuget.targets 205 | 206 | # Microsoft Azure Build Output 207 | csx/ 208 | *.build.csdef 209 | 210 | # Microsoft Azure Emulator 211 | ecf/ 212 | rcf/ 213 | 214 | # Windows Store app package directories and files 215 | AppPackages/ 216 | BundleArtifacts/ 217 | Package.StoreAssociation.xml 218 | _pkginfo.txt 219 | *.appx 220 | *.appxbundle 221 | *.appxupload 222 | 223 | # Visual Studio cache files 224 | # files ending in .cache can be ignored 225 | *.[Cc]ache 226 | # but keep track of directories ending in .cache 227 | !?*.[Cc]ache/ 228 | 229 | # Others 230 | ClientBin/ 231 | ~$* 232 | *~ 233 | *.dbmdl 234 | *.dbproj.schemaview 235 | *.jfm 236 | *.pfx 237 | *.publishsettings 238 | orleans.codegen.cs 239 | 240 | # Including strong name files can present a security risk 241 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 242 | #*.snk 243 | 244 | # Since there are multiple workflows, uncomment next line to ignore bower_components 245 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 246 | #bower_components/ 247 | 248 | # RIA/Silverlight projects 249 | Generated_Code/ 250 | 251 | # Backup & report files from converting an old project file 252 | # to a newer Visual Studio version. Backup files are not needed, 253 | # because we have git ;-) 254 | _UpgradeReport_Files/ 255 | Backup*/ 256 | UpgradeLog*.XML 257 | UpgradeLog*.htm 258 | ServiceFabricBackup/ 259 | *.rptproj.bak 260 | 261 | # SQL Server files 262 | *.mdf 263 | *.ldf 264 | *.ndf 265 | 266 | # Business Intelligence projects 267 | *.rdl.data 268 | *.bim.layout 269 | *.bim_*.settings 270 | *.rptproj.rsuser 271 | *- [Bb]ackup.rdl 272 | *- [Bb]ackup ([0-9]).rdl 273 | *- [Bb]ackup ([0-9][0-9]).rdl 274 | 275 | # Microsoft Fakes 276 | FakesAssemblies/ 277 | 278 | # GhostDoc plugin setting file 279 | *.GhostDoc.xml 280 | 281 | # Node.js Tools for Visual Studio 282 | .ntvs_analysis.dat 283 | node_modules/ 284 | 285 | # Visual Studio 6 build log 286 | *.plg 287 | 288 | # Visual Studio 6 workspace options file 289 | *.opt 290 | 291 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 292 | *.vbw 293 | 294 | # Visual Studio LightSwitch build output 295 | **/*.HTMLClient/GeneratedArtifacts 296 | **/*.DesktopClient/GeneratedArtifacts 297 | **/*.DesktopClient/ModelManifest.xml 298 | **/*.Server/GeneratedArtifacts 299 | **/*.Server/ModelManifest.xml 300 | _Pvt_Extensions 301 | 302 | # Paket dependency manager 303 | .paket/paket.exe 304 | paket-files/ 305 | 306 | # FAKE - F# Make 307 | .fake/ 308 | 309 | # CodeRush personal settings 310 | .cr/personal 311 | 312 | # Python Tools for Visual Studio (PTVS) 313 | __pycache__/ 314 | *.pyc 315 | 316 | # Cake - Uncomment if you are using it 317 | # tools/** 318 | # !tools/packages.config 319 | 320 | # Tabs Studio 321 | *.tss 322 | 323 | # Telerik's JustMock configuration file 324 | *.jmconfig 325 | 326 | # BizTalk build output 327 | *.btp.cs 328 | *.btm.cs 329 | *.odx.cs 330 | *.xsd.cs 331 | 332 | # OpenCover UI analysis results 333 | OpenCover/ 334 | 335 | # Azure Stream Analytics local run output 336 | ASALocalRun/ 337 | 338 | # MSBuild Binary and Structured Log 339 | *.binlog 340 | 341 | # NVidia Nsight GPU debugger configuration file 342 | *.nvuser 343 | 344 | # MFractors (Xamarin productivity tool) working folder 345 | .mfractor/ 346 | 347 | # Local History for Visual Studio 348 | .localhistory/ 349 | 350 | # BeatPulse healthcheck temp database 351 | healthchecksdb 352 | 353 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 354 | MigrationBackup/ 355 | 356 | # Ionide (cross platform F# VS Code tools) working folder 357 | .ionide/ 358 | 359 | !/src/bin 360 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | tab_spaces = 4 2 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "lldb", 9 | "request": "launch", 10 | "name": "Debug unit tests in library 'juliaup'", 11 | "cargo": { 12 | "args": [ 13 | "test", 14 | "--no-run", 15 | "--lib", 16 | "--package=juliaup" 17 | ], 18 | "filter": { 19 | "name": "juliaup", 20 | "kind": "lib" 21 | } 22 | }, 23 | "args": [], 24 | "cwd": "${workspaceFolder}" 25 | }, 26 | { 27 | "type": "lldb", 28 | "request": "launch", 29 | "name": "Debug executable 'juliainstaller'", 30 | "cargo": { 31 | "args": [ 32 | "build", 33 | "--bin=juliainstaller", 34 | "--package=juliaup" 35 | ], 36 | "filter": { 37 | "name": "juliainstaller", 38 | "kind": "bin" 39 | } 40 | }, 41 | "args": [], 42 | "cwd": "${workspaceFolder}" 43 | }, 44 | { 45 | "type": "lldb", 46 | "request": "launch", 47 | "name": "Debug unit tests in executable 'juliainstaller'", 48 | "cargo": { 49 | "args": [ 50 | "test", 51 | "--no-run", 52 | "--bin=juliainstaller", 53 | "--package=juliaup" 54 | ], 55 | "filter": { 56 | "name": "juliainstaller", 57 | "kind": "bin" 58 | } 59 | }, 60 | "args": [], 61 | "cwd": "${workspaceFolder}" 62 | }, 63 | { 64 | "type": "lldb", 65 | "request": "launch", 66 | "name": "Debug executable 'julialauncher'", 67 | "cargo": { 68 | "args": [ 69 | "build", 70 | "--bin=julialauncher", 71 | "--package=juliaup" 72 | ], 73 | "filter": { 74 | "name": "julialauncher", 75 | "kind": "bin" 76 | } 77 | }, 78 | "args": [], 79 | "cwd": "${workspaceFolder}" 80 | }, 81 | { 82 | "type": "lldb", 83 | "request": "launch", 84 | "name": "Debug unit tests in executable 'julialauncher'", 85 | "cargo": { 86 | "args": [ 87 | "test", 88 | "--no-run", 89 | "--bin=julialauncher", 90 | "--package=juliaup" 91 | ], 92 | "filter": { 93 | "name": "julialauncher", 94 | "kind": "bin" 95 | } 96 | }, 97 | "args": [], 98 | "cwd": "${workspaceFolder}" 99 | }, 100 | { 101 | "type": "lldb", 102 | "request": "launch", 103 | "name": "Debug executable 'juliaup'", 104 | "cargo": { 105 | "args": [ 106 | "build", 107 | "--bin=juliaup", 108 | "--package=juliaup" 109 | ], 110 | "filter": { 111 | "name": "juliaup", 112 | "kind": "bin" 113 | } 114 | }, 115 | "args": [], 116 | "cwd": "${workspaceFolder}" 117 | }, 118 | { 119 | "type": "lldb", 120 | "request": "launch", 121 | "name": "Debug unit tests in executable 'juliaup'", 122 | "cargo": { 123 | "args": [ 124 | "test", 125 | "--no-run", 126 | "--bin=juliaup", 127 | "--package=juliaup" 128 | ], 129 | "filter": { 130 | "name": "juliaup", 131 | "kind": "bin" 132 | } 133 | }, 134 | "args": [], 135 | "cwd": "${workspaceFolder}" 136 | }, 137 | { 138 | "type": "lldb", 139 | "request": "launch", 140 | "name": "Debug integration test 'command_add'", 141 | "cargo": { 142 | "args": [ 143 | "test", 144 | "--no-run", 145 | "--test=command_add", 146 | "--package=juliaup" 147 | ], 148 | "filter": { 149 | "name": "command_add", 150 | "kind": "test" 151 | } 152 | }, 153 | "args": [], 154 | "cwd": "${workspaceFolder}" 155 | }, 156 | { 157 | "type": "lldb", 158 | "request": "launch", 159 | "name": "Debug integration test 'command_default'", 160 | "cargo": { 161 | "args": [ 162 | "test", 163 | "--no-run", 164 | "--test=command_default", 165 | "--package=juliaup" 166 | ], 167 | "filter": { 168 | "name": "command_default", 169 | "kind": "test" 170 | } 171 | }, 172 | "args": [], 173 | "cwd": "${workspaceFolder}" 174 | }, 175 | { 176 | "type": "lldb", 177 | "request": "launch", 178 | "name": "Debug integration test 'command_initial_setup_from_launcher_test'", 179 | "cargo": { 180 | "args": [ 181 | "test", 182 | "--no-run", 183 | "--test=command_initial_setup_from_launcher_test", 184 | "--package=juliaup" 185 | ], 186 | "filter": { 187 | "name": "command_initial_setup_from_launcher_test", 188 | "kind": "test" 189 | } 190 | }, 191 | "args": [], 192 | "cwd": "${workspaceFolder}" 193 | }, 194 | { 195 | "type": "lldb", 196 | "request": "launch", 197 | "name": "Debug integration test 'command_list_test'", 198 | "cargo": { 199 | "args": [ 200 | "test", 201 | "--no-run", 202 | "--test=command_list_test", 203 | "--package=juliaup" 204 | ], 205 | "filter": { 206 | "name": "command_list_test", 207 | "kind": "test" 208 | } 209 | }, 210 | "args": [], 211 | "cwd": "${workspaceFolder}" 212 | }, 213 | { 214 | "type": "lldb", 215 | "request": "launch", 216 | "name": "Debug integration test 'command_remove'", 217 | "cargo": { 218 | "args": [ 219 | "test", 220 | "--no-run", 221 | "--test=command_remove", 222 | "--package=juliaup" 223 | ], 224 | "filter": { 225 | "name": "command_remove", 226 | "kind": "test" 227 | } 228 | }, 229 | "args": [], 230 | "cwd": "${workspaceFolder}" 231 | }, 232 | { 233 | "type": "lldb", 234 | "request": "launch", 235 | "name": "Debug integration test 'command_status_test'", 236 | "cargo": { 237 | "args": [ 238 | "test", 239 | "--no-run", 240 | "--test=command_status_test", 241 | "--package=juliaup" 242 | ], 243 | "filter": { 244 | "name": "command_status_test", 245 | "kind": "test" 246 | } 247 | }, 248 | "args": [], 249 | "cwd": "${workspaceFolder}" 250 | }, 251 | { 252 | "type": "lldb", 253 | "request": "launch", 254 | "name": "Debug integration test 'command_update'", 255 | "cargo": { 256 | "args": [ 257 | "test", 258 | "--no-run", 259 | "--test=command_update", 260 | "--package=juliaup" 261 | ], 262 | "filter": { 263 | "name": "command_update", 264 | "kind": "test" 265 | } 266 | }, 267 | "args": [], 268 | "cwd": "${workspaceFolder}" 269 | } 270 | ] 271 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [1.14.5] - 2/2/2024 4 | 5 | ### Fixed 6 | - Fix more StoreBroker bugs ([`4fef76f`](https://github.com/JuliaLang/juliaup/commit/4fef76f)) (davidanthoff) 7 | 8 | ## [1.14.4] - 2/2/2024 9 | 10 | ### Fixed 11 | - Fix how we force old PowerShell version ([`f94ee4b`](https://github.com/JuliaLang/juliaup/commit/f94ee4b)) (davidanthoff) 12 | 13 | ## [1.14.3] - 2/2/2024 14 | 15 | ### Fixed 16 | - Force old PowerShell version on workflow to fix StoreBroker ([`40c954d`](https://github.com/JuliaLang/juliaup/commit/40c954d)) (davidanthoff) 17 | 18 | ## [1.14.2] - 2/2/2024 19 | 20 | ### Fixed 21 | - Fix use of direct download StoreBroker ([`40a0d94`](https://github.com/JuliaLang/juliaup/commit/40a0d94)) (davidanthoff) 22 | 23 | ## [1.14.1] - 2/2/2024 24 | 25 | ### Fixed 26 | - Use StoreBroker directly from GitHub until new version is released ([`00a39e9`](https://github.com/JuliaLang/juliaup/commit/00a39e9)) (davidanthoff) 27 | 28 | ## [1.14.0] - 2/2/2024 29 | 30 | ### Added 31 | - Support nightly channels ([#809](https://github.com/JuliaLang/juliaup/pull/809)) (Roger-luo,maleadt,davidanthoff) 32 | 33 | [1.14.0]: https://github.com/JuliaLang/juliaup/releases/tag/v1.14.0 34 | [1.14.1]: https://github.com/JuliaLang/juliaup/releases/tag/v1.14.1 35 | [1.14.2]: https://github.com/JuliaLang/juliaup/releases/tag/v1.14.1 36 | [1.14.3]: https://github.com/JuliaLang/juliaup/releases/tag/v1.14.1 37 | [1.14.4]: https://github.com/JuliaLang/juliaup/releases/tag/v1.14.1 38 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "juliaup" 3 | version = "1.17.21" 4 | description = "Julia installer and version multiplexer" 5 | repository = "https://github.com/julialang/juliaup" 6 | license = "MIT" 7 | keywords = ["julia"] 8 | categories = ["command-line-utilities"] 9 | edition = "2021" 10 | default-run = "juliaup" 11 | authors = ["David Anthoff "] 12 | exclude = [ 13 | ".github/**", 14 | ".vscode/**", 15 | ".wix/**", 16 | "deploy/**", 17 | "devdocs/**", 18 | "scripts/**", 19 | "download_bundled_julia.ps1", 20 | ] 21 | 22 | [profile.release] 23 | lto = true 24 | codegen-units = 1 25 | 26 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 27 | 28 | [dependencies] 29 | clap = { version = "4.5", features = ["derive"] } 30 | clap_complete = "4.5" 31 | dirs = "6.0.0" 32 | dunce = "1.0" 33 | serde = { version = "1.0", features = ["derive"] } 34 | serde_json = "1.0" 35 | semver = "1.0.18" 36 | anyhow = "1.0.72" 37 | tempfile = "3.12" 38 | flate2 = "1.0" 39 | tar = "0.4.39" 40 | normpath = "1.3" 41 | fs_extra = "1.3.0" 42 | thiserror = "2.0" 43 | indicatif = "0.17" 44 | console = "0.15" 45 | ctrlc = "3.4" 46 | url = "2.4.0" 47 | cli-table = "0.5.0" 48 | itertools = "0.14.0" 49 | cluFlock = "1.2.7" 50 | chrono = { version = "0.4.26", features = ["serde"] } 51 | human-panic = "2.0" 52 | log = "0.4.25" 53 | env_logger = "0.11" 54 | dialoguer = "0.11.0" 55 | shellexpand = "3.1.0" 56 | env_proxy = "0.4.1" 57 | bstr = "1.10" 58 | indoc = "2.0.3" 59 | is-terminal = "0.4" 60 | path-absolutize = "3.1.0" 61 | human-sort = "0.2.2" 62 | regex = "1.10" 63 | 64 | [target.'cfg(windows)'.dependencies] 65 | windows = { version = "0.61.1", features = ["Win32_Foundation", "Win32_UI_Shell", "Win32_Security", "Win32_System_JobObjects", "Win32_System_Console", "Win32_System_Threading", "Services_Store", "Foundation", "Foundation_Collections", "Web_Http", "Web_Http_Headers", "Storage_Streams", "Management_Deployment"] } 66 | 67 | [target.'cfg(target_os = "macos")'.dependencies] 68 | reqwest = { version = "0.12", default-features = false, features = ["blocking", "native-tls", "socks"] } 69 | 70 | [target.'cfg(all(not(target_os = "macos"),not(windows)))'.dependencies] 71 | reqwest = { version = "0.12", default-features = false, features = ["blocking", "rustls-tls-native-roots", "socks"] } 72 | 73 | [target.'cfg(not(windows))'.dependencies] 74 | nix = { version = "0.29.0", features = ["process"] } 75 | 76 | [build-dependencies] 77 | anyhow = "1.0.72" 78 | itertools = "0.14.0" 79 | serde = { version = "1.0.175", features = ["derive"] } 80 | serde_json = "1.0.103" 81 | semver = "1.0.18" 82 | built = "0.7.1" 83 | 84 | [target.'cfg(windows)'.build-dependencies] 85 | winres = "0.1.12" 86 | 87 | [dev-dependencies] 88 | assert_cmd = "2.0" 89 | assert_fs = "1.1" 90 | indoc = "2.0" 91 | predicates = "3.1" 92 | 93 | [features] 94 | selfupdate = [] 95 | windowsstore = [] 96 | windowsappinstaller = [] 97 | dummy = [] 98 | binjuliainstaller = [] 99 | binjulialauncher = [] 100 | winpkgidentityext = [] 101 | 102 | [package.metadata.msix] 103 | winstoremsix = { file = "deploy/msix/PackagingLayout.xml", variables = [ 104 | {name = "FlatBundle", value="false"}, 105 | {name = "PublisherDisplayName", value="JuliaHub, Inc."}, 106 | {name = "IdentityPublisher", value = "CN=7FB784C5-4411-4067-914E-A7B06CC00FFC"} 107 | ] } 108 | winappinstallermsix = { file = "deploy/msix/PackagingLayout.xml", variables = [ 109 | {name = "FlatBundle", value="true"}, 110 | {name = "PublisherDisplayName", value="JuliaHub, Inc."}, 111 | {name = "IdentityPublisher", value = "CN="JuliaHub, Inc.", O="JuliaHub, Inc.", L=CAMBRIDGE, S=Massachusetts, C=US"} 112 | ] } 113 | 114 | [package.metadata.winappinstaller] 115 | winappinstaller = "deploy/winappinstaller/Julia.appinstaller" 116 | 117 | [package.metadata.winres] 118 | 119 | [[bin]] 120 | name = "julia" 121 | path = "src/bin/julialauncher.rs" 122 | 123 | [[bin]] 124 | name = "julialauncher" 125 | path = "src/bin/julialauncher.rs" 126 | required-features = ["binjulialauncher"] 127 | 128 | [[bin]] 129 | name = "juliaup" 130 | path = "src/bin/juliaup.rs" 131 | 132 | [[bin]] 133 | name = "juliainstaller" 134 | path = "src/bin/juliainstaller.rs" 135 | required-features = ["binjuliainstaller"] 136 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020-2023 David Anthoff 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Juliaup - Julia version manager 2 | 3 | This repository contains a cross-platform installer for the Julia programming language. 4 | 5 | The installer also bundles a full Julia version manager called `juliaup`. One can use `juliaup` to install specific Julia versions, it alerts users when new Julia versions are released and provides a convenient Julia release channel abstraction. 6 | 7 | ## Status 8 | 9 | This installer is considered production ready. 10 | 11 | ## Installation 12 | 13 | On all platforms it is recommended that you first uninstall any previous Julia versions and undo any modifications you might have made to put `julia` on the `PATH` before you install Julia with the installer in this repository. 14 | 15 | ### Windows 16 | 17 | On Windows Julia and Juliaup can be installed directly from the Windows store [here](https://www.microsoft.com/store/apps/9NJNWW8PVKMN). One can also install exactly the same version by executing 18 | 19 | ``` 20 | winget install --name Julia --id 9NJNWW8PVKMN -e -s msstore 21 | ``` 22 | 23 | on a command line. 24 | 25 | If the Windows Store is blocked on a system, we have an alternative [MSIX App Installer](https://learn.microsoft.com/en-us/windows/msix/app-installer/app-installer-file-overview) based setup. Note that this is currently experimental, please report back successes and failures [here](https://github.com/JuliaLang/juliaup/issues/343). To use the App Installer version, download [this](https://install.julialang.org/Julia.appinstaller) file and open it by double clicking on it. 26 | 27 | If neither the Windows Store nor the App Installer version work on your Windows system, you can also use a MSI based installer. Note that this installation methods comes with serious limitations and is generally not recommended unless no other method works. For example, there is no automatic update mechanism for Juliaup with this installation method. The 64 bit version of the MSI installer can be downloaded from [here](https://install.julialang.org/Julia-x64.msi) and the 32 bit version from [here](https://install.julialang.org/Julia-x86.msi). By default the install will be a per-user install that does not require elevation. You can also do a system install by running the following command from a shell: 28 | 29 | ``` 30 | msiexec /i ALLUSERS=1 31 | ``` 32 | 33 | ### Mac, Linux, and FreeBSD 34 | 35 | Juliaup can be installed on Unix-like platforms (currently Linux, Mac, or FreeBSD) by executing 36 | 37 | ``` 38 | curl -fsSL https://install.julialang.org | sh 39 | ``` 40 | 41 | in a shell. 42 | 43 | #### Command line arguments 44 | 45 | One can pass various command line arguments to the Julia installer. The syntax for installer arguments is 46 | 47 | ```bash 48 | curl -fsSL https://install.julialang.org | sh -s -- 49 | ``` 50 | 51 | Here `` should be replaced with one or more of the following arguments: 52 | - `--yes` (or `-y`): Run the installer in a non-interactive mode. All configuration values use their default. 53 | - `--default-channel `: Configure the default channel. For example `--default-channel lts` would install the `lts` channel and configure it as the default. 54 | - `--path` (or `-p`): Install `juliaup` in a custom location. 55 | 56 | ### Software Repositories 57 | 58 | **Important note:** As of now, we strongly recommend to install Juliaup via the Windows Store or `curl` command above rather than through OS-specific software repositories (see below) as the Juliaup variants provided by the latter currently have some drawbacks (that we hope to lift in the future). 59 | 60 | ##### [Homebrew](https://brew.sh) 61 | 62 | ``` 63 | brew install juliaup 64 | ``` 65 | 66 | ##### Arch Linux - AUR 67 | 68 | On Arch Linux, Juliaup is available in the Arch User Repository (AUR) in two packages. 69 | 70 | 1. [juliaup](https://aur.archlinux.org/packages/juliaup/) (locally built) 71 | 2. [juliaup-bin](https://aur.archlinux.org/packages/juliaup-bin/) (binary from github releases) 72 | 73 | ##### [openSUSE Tumbleweed](https://get.opensuse.org/tumbleweed/) 74 | 75 | On openSUSE Tumbleweed, Juliaup is available. To install, run with root privileges: 76 | 77 | ```sh 78 | zypper install juliaup 79 | ``` 80 | 81 | ##### [Solus](https://getsol.us) 82 | 83 | On Solus, Juliaup is available. To install, run with root privileges: 84 | 85 | ```sh 86 | eopkg install juliaup 87 | ``` 88 | 89 | ##### [cargo](https://crates.io/crates/juliaup/) 90 | 91 | To install via Rust's cargo, run: 92 | 93 | ```sh 94 | cargo install juliaup 95 | ``` 96 | 97 | ## Continuous Integration (CI) 98 | 99 | If you use GitHub Actions as your CI provider, you can use the [`julia-actions/install-juliaup`](https://github.com/julia-actions/install-juliaup) action to install Juliaup in CI. 100 | 101 | ## Using Juliaup 102 | 103 | Once you have installed Juliaup, `julia` is on the `PATH`, and on Windows there is a start menu shortcut and it will show up as a profile in Windows Terminal. Any of those will start Julia. The VS Code extension will also automatically find this Julia installation. 104 | 105 | Here are some of the things you can do with `juliaup`: 106 | - `juliaup list` lists all the available channels. 107 | - `juliaup update` installs the latest available Julia version for all your channels. 108 | - `juliaup update release` updates the `release` channel to the latest version. 109 | - `juliaup status` shows you which Julia versions you have installed and which one is configured as the default. 110 | - `juliaup add 1.5.1` adds Julia 1.5.1 to your system (it can then be launched via the command `julia +1.5.1`). 111 | - `juliaup default 1.5.3` configures the `julia` command to start Julia 1.5.3. 112 | - `juliaup default 1.6` configures the `julia` command to start the latest 1.6.x version of Julia you have installed on your system (and inform you if there is a newer version in 1.6.x available). 113 | - `juliaup default release` configures the `julia` command to start the latest stable version of Julia (this is also the default value). 114 | - `juliaup remove 1.5.3` deletes Julia 1.5.3 from your system. 115 | - `juliaup add 1.6.1~x86` installs the 32 bit version of Julia 1.6.1 on your system. 116 | - `juliaup default 1.6~x86` configures the `julia` command to start the latest 1.6.x 32 bit version of Julia you have installed on your system. 117 | - `juliaup link dev ~/juliasrc/julia` configures the `dev` channel to use a binary that you provide that is located at `~/juliasrc/julia`. You can then use `dev` as if it was a system provided channel, i.e. make it the default or use it with the `+` version selector. You can use other names than `dev` and link as many versions into `juliaup` as you want. 118 | - `juliaup self update` installs the latest version, which is necessary if new releases reach the beta channel, etc. 119 | - `juliaup self uninstall` uninstalls Juliaup. Note that on some platforms this command is not available, in those situations one should use platform specific methods to uninstall Juliaup. 120 | - `juliaup override status` shows all configured directory overrides. 121 | - `juliaup override set lts` sets a directory override for the current working directory to the `lts` channel. 122 | - `juliaup override unset` removes a directory override for the current working directory. 123 | - `juliaup override set --path foo/bar lts` sets a directory override for the path `foo/bar` to the `lts` channel. 124 | - `juliaup override unset --path foo/bar` removes a directory override for the path `foo/bar`. 125 | - `juliaup override unset --nonexistent` removes all directory overrides for paths that no longer exist. 126 | - `juliaup completions bash > ~/.local/share/bash-completion/completions/juliaup` generates Bash completions for `juliaup` and saves them to a file. To use them, simply source this file in your `~/.bashrc`. Other supported shells are `zsh`, `fish`, `elvish` and `powershell`. 127 | - `juliaup` shows you what other commands are available. 128 | 129 | The available system provided channels are: 130 | - `release`: always points to the latest stable version. 131 | - `lts`: always points to the latest long term supported version. 132 | - `alpha`: always points to the latest alpha version if one exists. If a newer beta or release candidate exists, it will point to that, and if there is no alpha, beta, or rc candidate available it will point to the same version as the `release` channel. 133 | - `beta`: always points to the latest beta version if one exists. If a newer release candidate exists, it will point to that, and if there is neither a beta or rc candidate available it will point to the same version as the `release` channel. 134 | - `rc`: same as `beta`, but only starts with release candidate versions. 135 | - `nightly`: always points to the latest build from the `master` branch in the Julia repository. 136 | - `x.y-nightly`: always points to the latest build from the `release-x.y` branch in the Julia repository, e.g. `1.11-nightly` gives the latest build on the `release-1.11` branch`. 137 | - `pr{number}` (e.g. `pr123`): points to the latest successful build of a PR branch (https://github.com/JuliaLang/julia/pull/{number}). Only available if CI has recently and successfully built Julia on that branch. 138 | - specific versions, e.g. `1.5.4`. 139 | - minor version channels, e.g. `1.5`. 140 | - major version channels, e.g. `1`. 141 | 142 | All of these channels can be combined with the `~x86`, `~x64` or `~aarch64` suffix to download a specific platform version. 143 | 144 | ## Using installed Julia versions 145 | 146 | To launch the default Julia version simply run `julia` in your terminal. 147 | 148 | To launch a specific Julia version, say in channel `release`, run `julia +release`. 149 | 150 | ## Overrides 151 | 152 | The Julia launcher `julia` automatically determines which specific version of Julia to launch. There are several ways to control and override which Juliaup channel should be used: 153 | 154 | 1. A command line Julia version specifier, such as `julia +release`. 155 | 2. The `JULIAUP_CHANNEL` environment variable. 156 | 3. A directory override, set with the `juliaup override set` command. 157 | 3. The default Juliaup channel. 158 | 159 | The channel is used in the order listed above, using the first available option. 160 | 161 | ## Path used by Juliaup 162 | 163 | Juliaup will by default use the Julia depot at `~/.julia` to store Julia versions and configuration files. This can be changed by setting 164 | the `JULIAUP_DEPOT_PATH` environment variable. Caution: Previous versions of Juliaup used the content of the environment variable 165 | `JULIA_DEPOT_PATH` to locate Juliaup files, the current version changed this behavior and no longer depends on `JULIA_DEPOT_PATH`. 166 | 167 | ## Juliaup server 168 | 169 | Juliaup by default downloads julia binary tarballs from the official server "https://julialang-s3.julialang.org". 170 | If requested, the environment variable `JULIAUP_SERVER` can be used to tell Juliaup to use a third-party mirror server. 171 | 172 | ## Development guides 173 | 174 | For juliaup developers, information on how to build juliaup locally, update julia versions, and release updates 175 | can be found in the wiki https://github.com/JuliaLang/juliaup/wiki 176 | 177 | To use unstable preview versions of juliaup (e.g. to get a patch before it makes it into the latest release), use 178 | 179 | ``` 180 | curl -fsSL https://install.julialang.org/releasepreview | sh 181 | ``` 182 | 183 | ## More information 184 | 185 | [This JuliaCon 2021 talk](https://www.youtube.com/watch?v=rFlbjWC6zYA) is a short introduction to Juliaup. Note that the video was recorded before the Linux and Mac versions were finished, but all the information about `juliaup` itself applies equally on Linux and Mac. 186 | 187 | [This JuliaCon 2022 talk](https://www.youtube.com/watch?v=14zfdbzq5BM) provides some background on the design of Juliaup. 188 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | extern crate itertools; 2 | extern crate semver; 3 | extern crate serde; 4 | extern crate serde_json; 5 | #[cfg(windows)] 6 | extern crate winres; 7 | #[path = "src/jsonstructs_versionsdb.rs"] 8 | mod jsonstructs_versionsdb; 9 | 10 | use anyhow::Result; 11 | use serde_json::Value; 12 | use std::env; 13 | use std::fs::File; 14 | use std::path::Path; 15 | use std::path::PathBuf; 16 | 17 | fn main() -> Result<()> { 18 | let target_platform = std::env::var("TARGET").unwrap(); 19 | 20 | let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); 21 | 22 | let db_path = Path::new(&env::var("CARGO_MANIFEST_DIR").unwrap()) 23 | .join("versiondb") 24 | .join(format!("versiondb-{}.json", target_platform)); 25 | 26 | let version_db_path = out_path.join("versionsdb.json"); 27 | std::fs::copy(&db_path, &version_db_path).unwrap(); 28 | 29 | let file = File::open(&db_path)?; 30 | let data: Value = serde_json::from_reader(file)?; 31 | let bundled_version_as_string: String = 32 | data["AvailableChannels"]["release"]["Version"].to_string(); 33 | let bundled_dbversion_as_string: String = data["Version"].to_string(); 34 | let bundled_version_path = Path::new(&out_path).join("bundled_version.rs"); 35 | std::fs::write( 36 | &bundled_version_path, 37 | format!( 38 | "pub const BUNDLED_JULIA_VERSION: &str = {}; pub const BUNDLED_DB_VERSION: &str = {};", 39 | bundled_version_as_string, bundled_dbversion_as_string 40 | ), 41 | ) 42 | .unwrap(); 43 | 44 | #[cfg(windows)] 45 | { 46 | let mut res = winres::WindowsResource::new(); 47 | res.set_icon("src/julia.ico"); 48 | 49 | #[cfg(feature = "winpkgidentityext")] 50 | res.set_manifest_file("deploy/winpkgidentityext/app.manifest"); 51 | 52 | res.compile().unwrap(); 53 | } 54 | 55 | let various_constants_path = Path::new(&out_path).join("various_constants.rs"); 56 | std::fs::write( 57 | &various_constants_path, 58 | format!("pub const JULIAUP_TARGET: &str = \"{}\";", &target_platform), 59 | ) 60 | .unwrap(); 61 | 62 | built::write_built_file().expect("Failed to acquire build-time information"); 63 | 64 | Ok(()) 65 | } 66 | -------------------------------------------------------------------------------- /deploy/msi/Bitmaps/New.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msi/Bitmaps/New.ico -------------------------------------------------------------------------------- /deploy/msi/Bitmaps/Up.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msi/Bitmaps/Up.ico -------------------------------------------------------------------------------- /deploy/msi/Bitmaps/bannrbmp.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msi/Bitmaps/bannrbmp.bmp -------------------------------------------------------------------------------- /deploy/msi/Bitmaps/dlgbmp.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msi/Bitmaps/dlgbmp.bmp -------------------------------------------------------------------------------- /deploy/msi/Bitmaps/exclamic.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msi/Bitmaps/exclamic.ico -------------------------------------------------------------------------------- /deploy/msi/Bitmaps/info.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msi/Bitmaps/info.ico -------------------------------------------------------------------------------- /deploy/msi/Julia.wxs: -------------------------------------------------------------------------------- 1 |  3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /deploy/msi/WixUI_InstallDir_NoLicense.wxs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /deploy/msix/Fragments/Julia.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": [ 3 | { 4 | "name": "Julia", 5 | "commandline": "julia.exe", 6 | "icon": "ms-appx://JuliaComputingInc.Julia/Public/Fragments/julia.ico" 7 | } 8 | ] 9 | } -------------------------------------------------------------------------------- /deploy/msix/Images/LargeTile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/LargeTile.png -------------------------------------------------------------------------------- /deploy/msix/Images/LargeTile.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/LargeTile.scale-125.png -------------------------------------------------------------------------------- /deploy/msix/Images/LargeTile.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/LargeTile.scale-150.png -------------------------------------------------------------------------------- /deploy/msix/Images/LargeTile.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/LargeTile.scale-200.png -------------------------------------------------------------------------------- /deploy/msix/Images/LargeTile.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/LargeTile.scale-400.png -------------------------------------------------------------------------------- /deploy/msix/Images/SmallTile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/SmallTile.png -------------------------------------------------------------------------------- /deploy/msix/Images/SmallTile.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/SmallTile.scale-125.png -------------------------------------------------------------------------------- /deploy/msix/Images/SmallTile.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/SmallTile.scale-150.png -------------------------------------------------------------------------------- /deploy/msix/Images/SmallTile.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/SmallTile.scale-200.png -------------------------------------------------------------------------------- /deploy/msix/Images/SmallTile.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/SmallTile.scale-400.png -------------------------------------------------------------------------------- /deploy/msix/Images/SplashScreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/SplashScreen.png -------------------------------------------------------------------------------- /deploy/msix/Images/SplashScreen.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/SplashScreen.scale-125.png -------------------------------------------------------------------------------- /deploy/msix/Images/SplashScreen.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/SplashScreen.scale-150.png -------------------------------------------------------------------------------- /deploy/msix/Images/SplashScreen.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/SplashScreen.scale-200.png -------------------------------------------------------------------------------- /deploy/msix/Images/SplashScreen.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/SplashScreen.scale-400.png -------------------------------------------------------------------------------- /deploy/msix/Images/Square150x150Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/Square150x150Logo.png -------------------------------------------------------------------------------- /deploy/msix/Images/Square150x150Logo.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/Square150x150Logo.scale-125.png -------------------------------------------------------------------------------- /deploy/msix/Images/Square150x150Logo.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/Square150x150Logo.scale-150.png -------------------------------------------------------------------------------- /deploy/msix/Images/Square150x150Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/Square150x150Logo.scale-200.png -------------------------------------------------------------------------------- /deploy/msix/Images/Square150x150Logo.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/Square150x150Logo.scale-400.png -------------------------------------------------------------------------------- /deploy/msix/Images/Square44x44Logo.altform-lightunplated_targetsize-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/Square44x44Logo.altform-lightunplated_targetsize-16.png -------------------------------------------------------------------------------- /deploy/msix/Images/Square44x44Logo.altform-lightunplated_targetsize-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/Square44x44Logo.altform-lightunplated_targetsize-24.png -------------------------------------------------------------------------------- /deploy/msix/Images/Square44x44Logo.altform-lightunplated_targetsize-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/Square44x44Logo.altform-lightunplated_targetsize-256.png -------------------------------------------------------------------------------- /deploy/msix/Images/Square44x44Logo.altform-lightunplated_targetsize-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/Square44x44Logo.altform-lightunplated_targetsize-32.png -------------------------------------------------------------------------------- /deploy/msix/Images/Square44x44Logo.altform-lightunplated_targetsize-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/Square44x44Logo.altform-lightunplated_targetsize-48.png -------------------------------------------------------------------------------- /deploy/msix/Images/Square44x44Logo.altform-unplated_targetsize-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/Square44x44Logo.altform-unplated_targetsize-16.png -------------------------------------------------------------------------------- /deploy/msix/Images/Square44x44Logo.altform-unplated_targetsize-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/Square44x44Logo.altform-unplated_targetsize-24.png -------------------------------------------------------------------------------- /deploy/msix/Images/Square44x44Logo.altform-unplated_targetsize-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/Square44x44Logo.altform-unplated_targetsize-256.png -------------------------------------------------------------------------------- /deploy/msix/Images/Square44x44Logo.altform-unplated_targetsize-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/Square44x44Logo.altform-unplated_targetsize-32.png -------------------------------------------------------------------------------- /deploy/msix/Images/Square44x44Logo.altform-unplated_targetsize-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/Square44x44Logo.altform-unplated_targetsize-48.png -------------------------------------------------------------------------------- /deploy/msix/Images/Square44x44Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/Square44x44Logo.png -------------------------------------------------------------------------------- /deploy/msix/Images/Square44x44Logo.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/Square44x44Logo.scale-125.png -------------------------------------------------------------------------------- /deploy/msix/Images/Square44x44Logo.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/Square44x44Logo.scale-150.png -------------------------------------------------------------------------------- /deploy/msix/Images/Square44x44Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/Square44x44Logo.scale-200.png -------------------------------------------------------------------------------- /deploy/msix/Images/Square44x44Logo.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/Square44x44Logo.scale-400.png -------------------------------------------------------------------------------- /deploy/msix/Images/Square44x44Logo.targetsize-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/Square44x44Logo.targetsize-16.png -------------------------------------------------------------------------------- /deploy/msix/Images/Square44x44Logo.targetsize-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/Square44x44Logo.targetsize-24.png -------------------------------------------------------------------------------- /deploy/msix/Images/Square44x44Logo.targetsize-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/Square44x44Logo.targetsize-256.png -------------------------------------------------------------------------------- /deploy/msix/Images/Square44x44Logo.targetsize-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/Square44x44Logo.targetsize-32.png -------------------------------------------------------------------------------- /deploy/msix/Images/Square44x44Logo.targetsize-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/Square44x44Logo.targetsize-48.png -------------------------------------------------------------------------------- /deploy/msix/Images/StoreLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/StoreLogo.png -------------------------------------------------------------------------------- /deploy/msix/Images/StoreLogo.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/StoreLogo.scale-125.png -------------------------------------------------------------------------------- /deploy/msix/Images/StoreLogo.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/StoreLogo.scale-150.png -------------------------------------------------------------------------------- /deploy/msix/Images/StoreLogo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/StoreLogo.scale-200.png -------------------------------------------------------------------------------- /deploy/msix/Images/StoreLogo.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/StoreLogo.scale-400.png -------------------------------------------------------------------------------- /deploy/msix/Images/Wide310x150Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/Wide310x150Logo.png -------------------------------------------------------------------------------- /deploy/msix/Images/Wide310x150Logo.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/Wide310x150Logo.scale-125.png -------------------------------------------------------------------------------- /deploy/msix/Images/Wide310x150Logo.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/Wide310x150Logo.scale-150.png -------------------------------------------------------------------------------- /deploy/msix/Images/Wide310x150Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/Wide310x150Logo.scale-200.png -------------------------------------------------------------------------------- /deploy/msix/Images/Wide310x150Logo.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/msix/Images/Wide310x150Logo.scale-400.png -------------------------------------------------------------------------------- /deploy/msix/PackagingLayout.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 34 | 35 | -------------------------------------------------------------------------------- /deploy/msix/appxmanifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 13 | 14 | 15 | 16 | 17 | Julia 18 | {{{PublisherDisplayName}}} 19 | Images\StoreLogo.png 20 | disabled 21 | disabled 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 42 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 75 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /deploy/winappinstaller/Julia.appinstaller: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 14 | 20 | 26 | 32 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /deploy/winpkgidentityext/appmanifest/app.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /deploy/winpkgidentityext/create-msix.ps1: -------------------------------------------------------------------------------- 1 | Remove-Item .\juliaup.msix 2 | & 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.22000.0\x64\makeappx.exe' pack /d .\msix\ /p .\juliaup.msix /nv 3 | 4 | $params = @{ 5 | Endpoint = "https://eus.codesigning.azure.net/" 6 | CodeSigningAccountName = "juliahubwincertsaccount" 7 | CertificateProfileName = "JuliaHubWinCert" 8 | FilesFolder = "." 9 | FilesFolderFilter = "msix" 10 | FileDigest = "SHA256" 11 | TimestampRfc3161 = "http://timestamp.acs.microsoft.com" 12 | TimestampDigest = "SHA256" 13 | ExcludeManagedIdentityCredential = $True 14 | ExcludeEnvironmentCredential = $True 15 | ExcludeWorkloadIdentityCredential = $True 16 | ExcludeSharedTokenCacheCredential = $True 17 | ExcludeVisualStudioCredential = $True 18 | ExcludeVisualStudioCodeCredential = $True 19 | ExcludeAzurePowerShellCredential = $True 20 | ExcludeAzureDeveloperCliCredential = $True 21 | ExcludeInteractiveBrowserCredential = $True 22 | } 23 | 24 | Invoke-TrustedSigning @params 25 | -------------------------------------------------------------------------------- /deploy/winpkgidentityext/juliaup.msix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/winpkgidentityext/juliaup.msix -------------------------------------------------------------------------------- /deploy/winpkgidentityext/msix/appxmanifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | 15 | 16 | 17 | 18 | Julia Dev 19 | JuliaHub, Inc. 20 | Images\StoreLogo.png 21 | disabled 22 | disabled 23 | true 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 44 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 68 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /deploy/winstoresubmission/images/1.0/en-us/julia-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/deploy/winstoresubmission/images/1.0/en-us/julia-screenshot.png -------------------------------------------------------------------------------- /deploy/winstoresubmission/pdps/en-us/PDP.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | julia 14 | 15 | julialang 16 | 17 | 18 | Julia is a high-level, high-performance, dynamic, open-source programming language. 19 | 20 | 21 | Julia is a high-level, high-performance, dynamic, open-source programming language. 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /devdocs/versiondbupdates.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/devdocs/versiondbupdates.pptx -------------------------------------------------------------------------------- /release.toml: -------------------------------------------------------------------------------- 1 | publish = false 2 | -------------------------------------------------------------------------------- /scripts/build_msi.ps1: -------------------------------------------------------------------------------- 1 | if (Test-Path -Path $PSScriptRoot\..\target\x86_64-pc-windows-gnu\release) { 2 | Write-Output "Creating x64 installer..." 3 | md $PSScriptRoot\..\target\msi\x86_64-pc-windows-gnu\release -force | Out-Null 4 | Copy-Item $PSScriptRoot\..\target\x86_64-pc-windows-gnu\release\*.exe $PSScriptRoot\..\target\msi\x86_64-pc-windows-gnu\release 5 | Copy-Item $PSScriptRoot\..\deploy\msi\Bitmaps $PSScriptRoot\..\target\msi\x86_64-pc-windows-gnu\release -Recurse -Force 6 | wix build -ext WixToolset.UI.wixext $PSScriptRoot\..\deploy\msi\Julia.wxs -b $PSScriptRoot\..\target\msi\x86_64-pc-windows-gnu\release -arch x64 -o $PSScriptRoot\..\target\msi\Julia-x64.msi 7 | } 8 | else { 9 | Write-Output "Skipping x64 installer." 10 | } 11 | 12 | if (Test-Path -Path $PSScriptRoot\..\target\i686-pc-windows-gnu\release) { 13 | Write-Output "Creating x86 installer..." 14 | md $PSScriptRoot\..\target\msi\i686-pc-windows-gnu\release -force | Out-Null 15 | Copy-Item $PSScriptRoot\..\target\i686-pc-windows-gnu\release\*.exe $PSScriptRoot\..\target\msi\i686-pc-windows-gnu\release 16 | Copy-Item $PSScriptRoot\..\deploy\msi\Bitmaps $PSScriptRoot\..\target\msi\i686-pc-windows-gnu\release -Recurse -Force 17 | wix build -ext WixToolset.UI.wixext $PSScriptRoot\..\deploy\msi\Julia.wxs -b $PSScriptRoot\..\target\msi\i686-pc-windows-gnu\release -arch x86 -o $PSScriptRoot\..\target\msi\Julia-x86.msi 18 | } 19 | else { 20 | Write-Output "Skipping x86 installer." 21 | } 22 | -------------------------------------------------------------------------------- /scripts/download_bundled_julia.ps1: -------------------------------------------------------------------------------- 1 | $x64Versions = Get-Content $PSScriptRoot\..\versiondb\versiondb-x86_64-pc-windows-msvc.json | ConvertFrom-Json 2 | $x64VersionFromChannel = $x64Versions.AvailableChannels.release.Version 3 | $x64DownloadUrl = $x64Versions.AvailableVersions.$x64VersionFromChannel.UrlPath 4 | $x64Filename = Split-Path $x64DownloadUrl -leaf 5 | 6 | $x86Versions = Get-Content $PSScriptRoot\..\versiondb\versiondb-i686-pc-windows-msvc.json | ConvertFrom-Json 7 | $x86VersionFromChannel = $x86Versions.AvailableChannels.release.Version 8 | $x86DownloadUrl = $x86Versions.AvailableVersions.$x86VersionFromChannel.UrlPath 9 | $x86Filename = Split-Path $x86DownloadUrl -leaf 10 | 11 | mkdir -Force $PSScriptRoot\..\target\bundledjulia\downloads 12 | mkdir -Force $PSScriptRoot\..\target\bundledjulia\extracted 13 | 14 | if (-Not (Test-Path $PSScriptRoot\..\"target\bundledjulia\downloads\$x64Filename")) 15 | { 16 | Invoke-WebRequest "https://julialang-s3.julialang.org/$x64DownloadUrl" -OutFile $PSScriptRoot\..\"target\bundledjulia\downloads\$x64Filename" 17 | mkdir -Force $PSScriptRoot\..\target\bundledjulia\extracted\x64 18 | Remove-Item $PSScriptRoot\..\target\bundledjulia\extracted\x64\* -Force -Recurse 19 | tar -xvzf $PSScriptRoot\..\"target\bundledjulia\downloads\$x64Filename" -C $PSScriptRoot\..\target\bundledjulia\extracted\x64 --strip-components=1 20 | } 21 | 22 | if (-Not (Test-Path $PSScriptRoot\..\"target\bundledjulia\downloads\$x86Filename")) 23 | { 24 | Invoke-WebRequest "https://julialang-s3.julialang.org/$x86DownloadUrl" -OutFile $PSScriptRoot\..\"target\bundledjulia\downloads\$x86Filename" 25 | mkdir -Force $PSScriptRoot\..\target\bundledjulia\extracted\x86 26 | Remove-Item $PSScriptRoot\..\target\bundledjulia\extracted\x86\* -Force -Recurse 27 | tar -xvzf $PSScriptRoot\..\"target\bundledjulia\downloads\$x86Filename" -C $PSScriptRoot\..\target\bundledjulia\extracted\x86 --strip-components=1 28 | } 29 | -------------------------------------------------------------------------------- /scripts/versiondb/Manifest.toml: -------------------------------------------------------------------------------- 1 | # This file is machine-generated - editing it directly is not advised 2 | 3 | julia_version = "1.8.3" 4 | manifest_format = "2.0" 5 | project_hash = "c147aef412e9bc7efe306819287ff5e946292fc4" 6 | 7 | [[deps.Artifacts]] 8 | uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" 9 | 10 | [[deps.Base64]] 11 | uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" 12 | 13 | [[deps.Compat]] 14 | deps = ["Dates", "LinearAlgebra", "UUIDs"] 15 | git-tree-sha1 = "aaabba4ce1b7f8a9b34c015053d3b1edf60fa49c" 16 | uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" 17 | version = "4.4.0" 18 | 19 | [[deps.CompilerSupportLibraries_jll]] 20 | deps = ["Artifacts", "Libdl"] 21 | uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" 22 | version = "0.5.2+0" 23 | 24 | [[deps.DataAPI]] 25 | git-tree-sha1 = "e08915633fcb3ea83bf9d6126292e5bc5c739922" 26 | uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a" 27 | version = "1.13.0" 28 | 29 | [[deps.DataStructures]] 30 | deps = ["Compat", "InteractiveUtils", "OrderedCollections"] 31 | git-tree-sha1 = "d1fff3a548102f48987a52a2e0d114fa97d730f0" 32 | uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" 33 | version = "0.18.13" 34 | 35 | [[deps.DataValueInterfaces]] 36 | git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6" 37 | uuid = "e2d170a0-9d28-54be-80f0-106bbe20a464" 38 | version = "1.0.0" 39 | 40 | [[deps.DataValues]] 41 | deps = ["DataValueInterfaces", "Dates"] 42 | git-tree-sha1 = "d88a19299eba280a6d062e135a43f00323ae70bf" 43 | uuid = "e7dc6d0d-1eca-5fa6-8ad6-5aecde8b7ea5" 44 | version = "0.4.13" 45 | 46 | [[deps.Dates]] 47 | deps = ["Printf"] 48 | uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" 49 | 50 | [[deps.InteractiveUtils]] 51 | deps = ["Markdown"] 52 | uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" 53 | 54 | [[deps.IterableTables]] 55 | deps = ["DataValues", "IteratorInterfaceExtensions", "Requires", "TableTraits", "TableTraitsUtils"] 56 | git-tree-sha1 = "70300b876b2cebde43ebc0df42bc8c94a144e1b4" 57 | uuid = "1c8ee90f-4401-5389-894e-7a04a3dc0f4d" 58 | version = "1.0.0" 59 | 60 | [[deps.IteratorInterfaceExtensions]] 61 | git-tree-sha1 = "a3f24677c21f5bbe9d2a714f95dcd58337fb2856" 62 | uuid = "82899510-4779-5014-852e-03e436cf321d" 63 | version = "1.0.0" 64 | 65 | [[deps.JSON]] 66 | deps = ["Dates", "Mmap", "Parsers", "Unicode"] 67 | git-tree-sha1 = "3c837543ddb02250ef42f4738347454f95079d4e" 68 | uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" 69 | version = "0.21.3" 70 | 71 | [[deps.Libdl]] 72 | uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" 73 | 74 | [[deps.LinearAlgebra]] 75 | deps = ["Libdl", "libblastrampoline_jll"] 76 | uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" 77 | 78 | [[deps.Logging]] 79 | uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" 80 | 81 | [[deps.MacroTools]] 82 | deps = ["Markdown", "Random"] 83 | git-tree-sha1 = "42324d08725e200c23d4dfb549e0d5d89dede2d2" 84 | uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" 85 | version = "0.5.10" 86 | 87 | [[deps.Markdown]] 88 | deps = ["Base64"] 89 | uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" 90 | 91 | [[deps.Missings]] 92 | deps = ["DataAPI"] 93 | git-tree-sha1 = "bf210ce90b6c9eed32d25dbcae1ebc565df2687f" 94 | uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28" 95 | version = "1.0.2" 96 | 97 | [[deps.Mmap]] 98 | uuid = "a63ad114-7e13-5084-954f-fe012c677804" 99 | 100 | [[deps.OpenBLAS_jll]] 101 | deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] 102 | uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" 103 | version = "0.3.20+0" 104 | 105 | [[deps.OrderedCollections]] 106 | git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c" 107 | uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" 108 | version = "1.4.1" 109 | 110 | [[deps.Parsers]] 111 | deps = ["Dates", "SnoopPrecompile"] 112 | git-tree-sha1 = "b64719e8b4504983c7fca6cc9db3ebc8acc2a4d6" 113 | uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" 114 | version = "2.5.1" 115 | 116 | [[deps.Printf]] 117 | deps = ["Unicode"] 118 | uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" 119 | 120 | [[deps.Query]] 121 | deps = ["DataValues", "IterableTables", "MacroTools", "QueryOperators", "Statistics"] 122 | git-tree-sha1 = "a66aa7ca6f5c29f0e303ccef5c8bd55067df9bbe" 123 | uuid = "1a8c2f83-1ff3-5112-b086-8aa67b057ba1" 124 | version = "1.0.0" 125 | 126 | [[deps.QueryOperators]] 127 | deps = ["DataStructures", "DataValues", "IteratorInterfaceExtensions", "TableShowUtils"] 128 | git-tree-sha1 = "911c64c204e7ecabfd1872eb93c49b4e7c701f02" 129 | uuid = "2aef5ad7-51ca-5a8f-8e88-e75cf067b44b" 130 | version = "0.9.3" 131 | 132 | [[deps.Random]] 133 | deps = ["SHA", "Serialization"] 134 | uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" 135 | 136 | [[deps.Requires]] 137 | deps = ["UUIDs"] 138 | git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7" 139 | uuid = "ae029012-a4dd-5104-9daa-d747884805df" 140 | version = "1.3.0" 141 | 142 | [[deps.SHA]] 143 | uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" 144 | version = "0.7.0" 145 | 146 | [[deps.Serialization]] 147 | uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" 148 | 149 | [[deps.SnoopPrecompile]] 150 | git-tree-sha1 = "f604441450a3c0569830946e5b33b78c928e1a85" 151 | uuid = "66db9d55-30c0-4569-8b51-7e840670fc0c" 152 | version = "1.0.1" 153 | 154 | [[deps.SparseArrays]] 155 | deps = ["LinearAlgebra", "Random"] 156 | uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" 157 | 158 | [[deps.Statistics]] 159 | deps = ["LinearAlgebra", "SparseArrays"] 160 | uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" 161 | 162 | [[deps.TableShowUtils]] 163 | deps = ["DataValues", "Dates", "JSON", "Markdown", "Test"] 164 | git-tree-sha1 = "14c54e1e96431fb87f0d2f5983f090f1b9d06457" 165 | uuid = "5e66a065-1f0a-5976-b372-e0b8c017ca10" 166 | version = "0.2.5" 167 | 168 | [[deps.TableTraits]] 169 | deps = ["IteratorInterfaceExtensions"] 170 | git-tree-sha1 = "c06b2f539df1c6efa794486abfb6ed2022561a39" 171 | uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c" 172 | version = "1.0.1" 173 | 174 | [[deps.TableTraitsUtils]] 175 | deps = ["DataValues", "IteratorInterfaceExtensions", "Missings", "TableTraits"] 176 | git-tree-sha1 = "78fecfe140d7abb480b53a44f3f85b6aa373c293" 177 | uuid = "382cd787-c1b6-5bf2-a167-d5b971a19bda" 178 | version = "1.0.2" 179 | 180 | [[deps.Test]] 181 | deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] 182 | uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" 183 | 184 | [[deps.UUIDs]] 185 | deps = ["Random", "SHA"] 186 | uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" 187 | 188 | [[deps.Unicode]] 189 | uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" 190 | 191 | [[deps.libblastrampoline_jll]] 192 | deps = ["Artifacts", "Libdl", "OpenBLAS_jll"] 193 | uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" 194 | version = "5.1.1+0" 195 | -------------------------------------------------------------------------------- /scripts/versiondb/Project.toml: -------------------------------------------------------------------------------- 1 | [deps] 2 | JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" 3 | OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" 4 | Query = "1a8c2f83-1ff3-5112-b086-8aa67b057ba1" 5 | -------------------------------------------------------------------------------- /src/bin/juliaup.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{Context, Result}; 2 | use clap::Parser; 3 | use juliaup::cli::{ConfigSubCmd, Juliaup, OverrideSubCmd, SelfSubCmd}; 4 | use juliaup::command_api::run_command_api; 5 | use juliaup::command_completions::run_command_completions; 6 | #[cfg(not(windows))] 7 | use juliaup::command_config_symlinks::run_command_config_symlinks; 8 | use juliaup::command_config_versionsdbupdate::run_command_config_versionsdbupdate; 9 | use juliaup::command_default::run_command_default; 10 | use juliaup::command_gc::run_command_gc; 11 | use juliaup::command_info::run_command_info; 12 | use juliaup::command_initial_setup_from_launcher::run_command_initial_setup_from_launcher; 13 | use juliaup::command_link::run_command_link; 14 | use juliaup::command_list::run_command_list; 15 | use juliaup::command_override::{run_command_override_status, run_command_override_unset}; 16 | use juliaup::command_remove::run_command_remove; 17 | use juliaup::command_selfupdate::run_command_selfupdate; 18 | use juliaup::command_status::run_command_status; 19 | use juliaup::command_update::run_command_update; 20 | use juliaup::command_update_version_db::run_command_update_version_db; 21 | use juliaup::global_paths::get_paths; 22 | use juliaup::{command_add::run_command_add, command_override::run_command_override_set}; 23 | #[cfg(feature = "selfupdate")] 24 | use juliaup::{ 25 | command_config_backgroundselfupdate::run_command_config_backgroundselfupdate, 26 | command_config_modifypath::run_command_config_modifypath, 27 | command_config_startupselfupdate::run_command_config_startupselfupdate, 28 | command_selfchannel::run_command_selfchannel, 29 | }; 30 | 31 | #[cfg(feature = "selfupdate")] 32 | use juliaup::command_selfuninstall::run_command_selfuninstall; 33 | 34 | #[cfg(not(feature = "selfupdate"))] 35 | use juliaup::command_selfuninstall::run_command_selfuninstall_unavailable; 36 | 37 | use log::info; 38 | 39 | fn main() -> Result<()> { 40 | human_panic::setup_panic!( 41 | human_panic::Metadata::new("Juliaup", env!("CARGO_PKG_VERSION")) 42 | .support("https://github.com/JuliaLang/juliaup") 43 | ); 44 | 45 | let env = env_logger::Env::new() 46 | .filter("JULIAUP_LOG") 47 | .write_style("JULIAUP_LOG_STYLE"); 48 | env_logger::init_from_env(env); 49 | 50 | #[cfg(feature = "winpkgidentityext")] 51 | { 52 | use windows::Management::Deployment::{AddPackageOptions, PackageManager}; 53 | 54 | let package_manager = PackageManager::new().unwrap(); 55 | 56 | let package_manager_options = AddPackageOptions::new().unwrap(); 57 | 58 | let self_location = std::env::current_exe().unwrap(); 59 | let self_location = self_location.parent().unwrap(); 60 | let pkg_loc = self_location.join("juliaup.msix"); 61 | 62 | let external_loc = 63 | windows::Foundation::Uri::CreateUri(&windows::core::HSTRING::from(self_location)) 64 | .unwrap(); 65 | let pkg_loc = 66 | windows::Foundation::Uri::CreateUri(&windows::core::HSTRING::from(pkg_loc.as_os_str())) 67 | .unwrap(); 68 | 69 | package_manager_options 70 | .SetExternalLocationUri(&external_loc) 71 | .unwrap(); 72 | package_manager_options.SetAllowUnsigned(false).unwrap(); 73 | 74 | let depl_result = package_manager 75 | .AddPackageByUriAsync(&pkg_loc, &package_manager_options) 76 | .unwrap() 77 | .get() 78 | .unwrap(); 79 | 80 | if !depl_result.IsRegistered().unwrap() { 81 | eprintln!( 82 | "Failed to register package identity. Error Message ${:?}", 83 | depl_result.ErrorText() 84 | ); 85 | } 86 | } 87 | 88 | info!("Parsing command line arguments."); 89 | let args = Juliaup::parse(); 90 | 91 | let paths = get_paths().with_context(|| "Trying to load all global paths.")?; 92 | 93 | match args { 94 | Juliaup::Default { channel } => run_command_default(&channel, &paths), 95 | Juliaup::Add { channel } => run_command_add(&channel, &paths), 96 | Juliaup::Remove { channel } => run_command_remove(&channel, &paths), 97 | Juliaup::Status {} => run_command_status(&paths), 98 | Juliaup::Update { channel } => run_command_update(channel, &paths), 99 | Juliaup::Gc { prune_linked } => run_command_gc(prune_linked, &paths), 100 | Juliaup::Link { 101 | channel, 102 | file, 103 | args, 104 | } => run_command_link(&channel, &file, &args, &paths), 105 | Juliaup::List {} => run_command_list(&paths), 106 | Juliaup::Config(subcmd) => match subcmd { 107 | #[cfg(not(windows))] 108 | ConfigSubCmd::ChannelSymlinks { value } => { 109 | run_command_config_symlinks(value, false, &paths) 110 | } 111 | #[cfg(feature = "selfupdate")] 112 | ConfigSubCmd::BackgroundSelfupdateInterval { value } => { 113 | run_command_config_backgroundselfupdate(value, false, &paths) 114 | } 115 | #[cfg(feature = "selfupdate")] 116 | ConfigSubCmd::StartupSelfupdateInterval { value } => { 117 | run_command_config_startupselfupdate(value, false, &paths) 118 | } 119 | #[cfg(feature = "selfupdate")] 120 | ConfigSubCmd::ModifyPath { value } => { 121 | run_command_config_modifypath(value, false, &paths) 122 | } 123 | ConfigSubCmd::VersionsDbUpdateInterval { value } => { 124 | run_command_config_versionsdbupdate(value, false, &paths) 125 | } 126 | }, 127 | Juliaup::Api { command } => run_command_api(&command, &paths), 128 | Juliaup::InitialSetupFromLauncher {} => run_command_initial_setup_from_launcher(&paths), 129 | Juliaup::UpdateVersionDb {} => run_command_update_version_db(&paths), 130 | Juliaup::OverrideSubCmd(subcmd) => match subcmd { 131 | OverrideSubCmd::Status {} => run_command_override_status(&paths), 132 | OverrideSubCmd::Set { channel, path } => { 133 | run_command_override_set(&paths, channel, path) 134 | } 135 | OverrideSubCmd::Unset { nonexistent, path } => { 136 | run_command_override_unset(&paths, nonexistent, path) 137 | } 138 | }, 139 | Juliaup::Info {} => run_command_info(&paths), 140 | #[cfg(feature = "selfupdate")] 141 | Juliaup::SecretSelfUpdate {} => run_command_selfupdate(&paths), 142 | Juliaup::SelfSubCmd(subcmd) => match subcmd { 143 | SelfSubCmd::Update {} => run_command_selfupdate(&paths), 144 | #[cfg(feature = "selfupdate")] 145 | SelfSubCmd::Channel { channel } => run_command_selfchannel(channel, &paths), 146 | #[cfg(feature = "selfupdate")] 147 | SelfSubCmd::Uninstall {} => run_command_selfuninstall(&paths), 148 | #[cfg(not(feature = "selfupdate"))] 149 | SelfSubCmd::Uninstall {} => run_command_selfuninstall_unavailable(), 150 | }, 151 | Juliaup::Completions { shell } => run_command_completions(shell), 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/cli.rs: -------------------------------------------------------------------------------- 1 | use clap::{Parser, ValueEnum}; 2 | 3 | #[derive(Parser)] 4 | #[clap(name = "Juliaup", version)] 5 | #[command( 6 | after_help = "To launch a specific Julia version, use `julia +{channel}` e.g. `julia +1.6`. 7 | Entering just `julia` uses the default channel set via `juliaup default`." 8 | )] 9 | /// The Julia Version Manager 10 | pub enum Juliaup { 11 | /// Set the default Julia version 12 | Default { channel: String }, 13 | /// Add a specific Julia version or channel to your system. Access via `julia +{channel}` e.g. `julia +1.6` 14 | Add { channel: String }, 15 | /// Link an existing Julia binary to a custom channel name 16 | Link { 17 | channel: String, 18 | file: String, 19 | args: Vec, 20 | }, 21 | /// List all available channels 22 | #[clap(alias = "ls")] 23 | List {}, 24 | #[clap(subcommand, name = "override")] 25 | OverrideSubCmd(OverrideSubCmd), 26 | #[clap(alias = "up")] 27 | /// Update all or a specific channel to the latest Julia version 28 | Update { channel: Option }, 29 | #[clap(alias = "rm")] 30 | /// Remove a Julia version from your system 31 | Remove { channel: String }, 32 | #[clap(alias = "st")] 33 | /// Show all installed Julia versions 34 | Status {}, 35 | /// Garbage collect uninstalled Julia versions 36 | Gc { 37 | #[clap(long)] 38 | prune_linked: bool, 39 | }, 40 | #[clap(subcommand, name = "config")] 41 | /// Juliaup configuration 42 | Config(ConfigSubCmd), 43 | #[clap(hide = true)] 44 | Api { command: String }, 45 | #[clap(name = "46029ef5-0b73-4a71-bff3-d0d05de42aac", hide = true)] 46 | InitialSetupFromLauncher {}, 47 | #[clap(name = "0cf1528f-0b15-46b1-9ac9-e5bf5ccccbcf", hide = true)] 48 | UpdateVersionDb {}, 49 | #[clap(name = "info", hide = true)] 50 | Info {}, 51 | #[clap(subcommand, name = "self")] 52 | SelfSubCmd(SelfSubCmd), 53 | /// Generate tab-completion scripts for your shell 54 | Completions { shell: clap_complete::Shell }, 55 | // This is used for the cron jobs that we create. By using this UUID for the command 56 | // We can identify the cron jobs that were created by juliaup for uninstall purposes 57 | #[cfg(feature = "selfupdate")] 58 | #[clap(name = "4c79c12db1d34bbbab1f6c6f838f423f", hide = true)] 59 | SecretSelfUpdate {}, 60 | } 61 | 62 | #[derive(Parser)] 63 | /// Manage directory overrides 64 | pub enum OverrideSubCmd { 65 | Status {}, 66 | Set { 67 | channel: String, 68 | #[clap(long, short)] 69 | path: Option, 70 | }, 71 | Unset { 72 | #[clap(long, short)] 73 | nonexistent: bool, 74 | #[clap(long, short)] 75 | path: Option, 76 | }, 77 | } 78 | 79 | #[derive(Debug, ValueEnum, Clone)] 80 | pub enum JuliaupChannel { 81 | #[clap(name = "release")] 82 | Release, 83 | #[clap(name = "releasepreview")] 84 | ReleasePreview, 85 | #[clap(name = "dev")] 86 | Dev, 87 | } 88 | 89 | impl JuliaupChannel { 90 | pub fn to_lowercase(&self) -> &str { 91 | match self { 92 | JuliaupChannel::Release => "release", 93 | JuliaupChannel::ReleasePreview => "releasepreview", 94 | JuliaupChannel::Dev => "dev", 95 | } 96 | } 97 | } 98 | 99 | #[derive(Parser)] 100 | /// Manage this juliaup installation 101 | pub enum SelfSubCmd { 102 | #[cfg(not(feature = "selfupdate"))] 103 | #[clap(alias = "up")] 104 | /// Update the Julia versions database 105 | Update {}, 106 | #[cfg(feature = "selfupdate")] 107 | #[clap(alias = "up")] 108 | /// Update the Julia versions database and juliaup itself 109 | Update {}, 110 | #[cfg(feature = "selfupdate")] 111 | /// Configure the channel to use for juliaup updates. Leave CHANNEL blank to see current channel. 112 | Channel { 113 | #[arg(value_enum)] 114 | channel: Option, 115 | }, 116 | #[cfg(feature = "selfupdate")] 117 | /// Uninstall this version of juliaup from the system 118 | Uninstall {}, 119 | #[cfg(not(feature = "selfupdate"))] 120 | /// Uninstall this version of juliaup from the system (UNAVAILABLE) 121 | Uninstall {}, 122 | } 123 | 124 | #[derive(Parser)] 125 | pub enum ConfigSubCmd { 126 | #[cfg(not(windows))] 127 | #[clap(name = "channelsymlinks")] 128 | /// Create a separate symlink per channel 129 | ChannelSymlinks { 130 | /// New Value 131 | value: Option, 132 | }, 133 | #[cfg(feature = "selfupdate")] 134 | #[clap(name = "backgroundselfupdateinterval")] 135 | /// The time between automatic background updates of Juliaup in minutes, use 0 to disable. 136 | BackgroundSelfupdateInterval { 137 | /// New value 138 | value: Option, 139 | }, 140 | #[cfg(feature = "selfupdate")] 141 | #[clap(name = "startupselfupdateinterval")] 142 | /// The time between automatic updates at Julia startup of Juliaup in minutes, use 0 to disable. 143 | StartupSelfupdateInterval { 144 | /// New value 145 | value: Option, 146 | }, 147 | #[cfg(feature = "selfupdate")] 148 | #[clap(name = "modifypath")] 149 | /// Add the Julia binaries to your PATH by manipulating various shell startup scripts. 150 | ModifyPath { 151 | /// New value 152 | value: Option, 153 | }, 154 | /// The time between automatic updates of the versions database in minutes, use 0 to disable. 155 | #[clap(name = "versionsdbupdateinterval")] 156 | VersionsDbUpdateInterval { 157 | /// New value 158 | value: Option, 159 | }, 160 | } 161 | -------------------------------------------------------------------------------- /src/command_add.rs: -------------------------------------------------------------------------------- 1 | use crate::config_file::{load_mut_config_db, save_config_db, JuliaupConfigChannel}; 2 | use crate::global_paths::GlobalPaths; 3 | #[cfg(not(windows))] 4 | use crate::operations::create_symlink; 5 | use crate::operations::{ 6 | channel_to_name, install_non_db_version, install_version, update_version_db, 7 | }; 8 | use crate::versions_file::load_versions_db; 9 | use anyhow::{anyhow, Context, Result}; 10 | use regex::Regex; 11 | 12 | pub fn run_command_add(channel: &str, paths: &GlobalPaths) -> Result<()> { 13 | // This regex is dynamically compiled, but its runtime is negligible compared to downloading Julia 14 | if Regex::new(r"^(?:pr\d+|nightly|\d+\.\d+-nightly)(?:~|$)") 15 | .unwrap() 16 | .is_match(channel) 17 | { 18 | return add_non_db(channel, paths); 19 | } 20 | 21 | update_version_db(paths).with_context(|| "Failed to update versions db.")?; 22 | let version_db = 23 | load_versions_db(paths).with_context(|| "`add` command failed to load versions db.")?; 24 | 25 | let required_version = &version_db 26 | .available_channels 27 | .get(channel) 28 | .ok_or_else(|| { 29 | anyhow!( 30 | "'{}' is not a valid Julia version or channel name.", 31 | &channel 32 | ) 33 | })? 34 | .version; 35 | 36 | let mut config_file = load_mut_config_db(paths) 37 | .with_context(|| "`add` command failed to load configuration data.")?; 38 | 39 | if config_file.data.installed_channels.contains_key(channel) { 40 | eprintln!("'{}' is already installed.", &channel); 41 | return Ok(()); 42 | } 43 | 44 | install_version(required_version, &mut config_file.data, &version_db, paths)?; 45 | 46 | config_file.data.installed_channels.insert( 47 | channel.to_string(), 48 | JuliaupConfigChannel::SystemChannel { 49 | version: required_version.clone(), 50 | }, 51 | ); 52 | 53 | if config_file.data.default.is_none() { 54 | config_file.data.default = Some(channel.to_string()); 55 | } 56 | 57 | #[cfg(not(windows))] 58 | let create_symlinks = config_file.data.settings.create_channel_symlinks; 59 | 60 | save_config_db(&mut config_file).with_context(|| { 61 | format!( 62 | "Failed to save configuration file from `add` command after '{}' was installed.", 63 | channel 64 | ) 65 | })?; 66 | 67 | #[cfg(not(windows))] 68 | if create_symlinks { 69 | create_symlink( 70 | &JuliaupConfigChannel::SystemChannel { 71 | version: required_version.clone(), 72 | }, 73 | &format!("julia-{}", channel), 74 | paths, 75 | )?; 76 | } 77 | 78 | Ok(()) 79 | } 80 | 81 | fn add_non_db(channel: &str, paths: &GlobalPaths) -> Result<()> { 82 | let mut config_file = load_mut_config_db(paths) 83 | .with_context(|| "`add` command failed to load configuration data.")?; 84 | 85 | if config_file.data.installed_channels.contains_key(channel) { 86 | eprintln!("'{}' is already installed.", &channel); 87 | return Ok(()); 88 | } 89 | 90 | let name = channel_to_name(&channel.to_string())?; 91 | let config_channel = install_non_db_version(channel, &name, paths)?; 92 | 93 | config_file 94 | .data 95 | .installed_channels 96 | .insert(channel.to_string(), config_channel.clone()); 97 | 98 | if config_file.data.default.is_none() { 99 | config_file.data.default = Some(channel.to_string()); 100 | } 101 | 102 | save_config_db(&mut config_file).with_context(|| { 103 | format!( 104 | "Failed to save configuration file from `add` command after '{channel}' was installed.", 105 | ) 106 | })?; 107 | 108 | #[cfg(not(windows))] 109 | if config_file.data.settings.create_channel_symlinks { 110 | create_symlink(&config_channel, &format!("julia-{}", channel), paths)?; 111 | } 112 | Ok(()) 113 | } 114 | -------------------------------------------------------------------------------- /src/command_api.rs: -------------------------------------------------------------------------------- 1 | use crate::config_file::load_config_db; 2 | use crate::config_file::JuliaupConfigChannel; 3 | use crate::global_paths::GlobalPaths; 4 | use crate::utils::parse_versionstring; 5 | use anyhow::{bail, Context, Result}; 6 | use normpath::PathExt; 7 | use semver::Version; 8 | use serde::{Deserialize, Serialize}; 9 | 10 | #[derive(Serialize, Deserialize, Clone)] 11 | pub struct JuliaupChannelInfo { 12 | #[serde(rename = "Name")] 13 | pub name: String, 14 | #[serde(rename = "File")] 15 | pub file: String, 16 | #[serde(rename = "Args")] 17 | pub args: Vec, 18 | #[serde(rename = "Version")] 19 | pub version: String, 20 | #[serde(rename = "Arch")] 21 | pub arch: String, 22 | } 23 | 24 | #[derive(Serialize, Deserialize, Clone)] 25 | pub struct JuliaupApiGetinfoReturn { 26 | #[serde(rename = "DefaultChannel")] 27 | pub default: Option, 28 | #[serde(rename = "OtherChannels")] 29 | pub other_versions: Vec, 30 | } 31 | 32 | pub fn run_command_api(command: &str, paths: &GlobalPaths) -> Result<()> { 33 | if command != "getconfig1" { 34 | bail!("Wrong API command."); 35 | } 36 | 37 | let mut ret_value = JuliaupApiGetinfoReturn { 38 | default: None, 39 | other_versions: Vec::new(), 40 | }; 41 | 42 | let config_file = load_config_db(paths, None).with_context(|| { 43 | "Failed to load configuration file while running the getconfig1 API command." 44 | })?; 45 | 46 | for (key, value) in config_file.data.installed_channels { 47 | let curr = match value { 48 | JuliaupConfigChannel::SystemChannel { 49 | version: fullversion, 50 | } => { 51 | let (platform, mut version) = parse_versionstring(&fullversion) 52 | .with_context(|| "Encountered invalid version string in the configuration file while running the getconfig1 API command.")?; 53 | 54 | version.build = semver::BuildMetadata::EMPTY; 55 | 56 | match config_file.data.installed_versions.get(&fullversion) { 57 | Some(channel) => JuliaupChannelInfo { 58 | name: key.clone(), 59 | file: paths.juliauphome 60 | .join(&channel.path) 61 | .join("bin") 62 | .join(format!("julia{}", std::env::consts::EXE_SUFFIX)) 63 | .normalize() 64 | .with_context(|| "Normalizing the path for an entry from the config file failed while running the getconfig1 API command.")? 65 | .into_path_buf() 66 | .to_string_lossy() 67 | .to_string(), 68 | args: Vec::new(), 69 | version: version.to_string(), 70 | arch: platform 71 | }, 72 | None => bail!("The channel '{}' is configured as a system channel, but no such channel exists in the versions database.", key) 73 | } 74 | } 75 | JuliaupConfigChannel::LinkedChannel { command, args } => { 76 | let mut new_args: Vec = Vec::new(); 77 | 78 | for i in args.as_ref().unwrap() { 79 | new_args.push(i.to_string()); 80 | } 81 | 82 | new_args.push("--version".to_string()); 83 | 84 | let res = std::process::Command::new(&command) 85 | .args(&new_args) 86 | .output(); 87 | 88 | match res { 89 | Ok(output) => { 90 | let expected_version_prefix = "julia version "; 91 | 92 | let trimmed_string = std::str::from_utf8(&output.stdout).unwrap().trim(); 93 | 94 | if !trimmed_string.starts_with(expected_version_prefix) { 95 | continue; 96 | } 97 | 98 | let version = 99 | Version::parse(&trimmed_string[expected_version_prefix.len()..])?; 100 | 101 | JuliaupChannelInfo { 102 | name: key.clone(), 103 | file: command.clone(), 104 | args: args.unwrap_or_default(), 105 | version: version.to_string(), 106 | arch: "".to_string(), 107 | } 108 | } 109 | Err(_) => continue, 110 | } 111 | } 112 | JuliaupConfigChannel::DirectDownloadChannel { path, url: _, local_etag: _, server_etag: _, version } => { 113 | JuliaupChannelInfo { 114 | name: key.clone(), 115 | file: paths.juliauphome 116 | .join(path) 117 | .join("bin") 118 | .join(format!("julia{}", std::env::consts::EXE_SUFFIX)) 119 | .normalize() 120 | .with_context(|| "Normalizing the path for an entry from the config file failed while running the getconfig1 API command.")? 121 | .into_path_buf() 122 | .to_string_lossy() 123 | .to_string(), 124 | args: Vec::new(), 125 | version: version.clone(), 126 | arch: "".to_string(), 127 | } 128 | } 129 | }; 130 | 131 | match config_file.data.default { 132 | Some(ref default_value) => { 133 | if &key == default_value { 134 | ret_value.default = Some(curr.clone()); 135 | } else { 136 | ret_value.other_versions.push(curr); 137 | } 138 | } 139 | None => { 140 | ret_value.other_versions.push(curr); 141 | } 142 | } 143 | } 144 | 145 | // Serialize it to a JSON string. 146 | let j = serde_json::to_string(&ret_value)?; 147 | 148 | // Print, write to a file, or send to an HTTP server. 149 | println!("{}", j); 150 | 151 | Ok(()) 152 | } 153 | -------------------------------------------------------------------------------- /src/command_completions.rs: -------------------------------------------------------------------------------- 1 | use crate::cli; 2 | use anyhow::Result; 3 | use clap::CommandFactory; 4 | use clap_complete::Shell; 5 | use cli::Juliaup; 6 | use std::io; 7 | 8 | pub fn run_command_completions(shell: Shell) -> Result<()> { 9 | clap_complete::generate( 10 | shell, 11 | &mut Juliaup::command(), 12 | "juliaup", 13 | &mut io::stdout().lock(), 14 | ); 15 | Ok(()) 16 | } 17 | -------------------------------------------------------------------------------- /src/command_config_backgroundselfupdate.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "selfupdate")] 2 | use anyhow::Result; 3 | 4 | #[cfg(feature = "selfupdate")] 5 | pub fn run_command_config_backgroundselfupdate( 6 | value: Option, 7 | quiet: bool, 8 | paths: &crate::global_paths::GlobalPaths, 9 | ) -> Result<()> { 10 | use crate::config_file::{load_config_db, load_mut_config_db, save_config_db}; 11 | use crate::operations::{install_background_selfupdate, uninstall_background_selfupdate}; 12 | use anyhow::{bail, Context}; 13 | 14 | match value { 15 | Some(value) => { 16 | if value < 0 { 17 | bail!("Invalid argument."); 18 | } 19 | 20 | let mut config_file = load_mut_config_db(paths) 21 | .with_context(|| "`config` command failed to load configuration data.")?; 22 | 23 | let mut value_changed = false; 24 | 25 | let value = if value == 0 { None } else { Some(value) }; 26 | 27 | if value != config_file.self_data.background_selfupdate_interval { 28 | config_file.self_data.background_selfupdate_interval = value; 29 | 30 | value_changed = true; 31 | 32 | match value { 33 | Some(value) => { 34 | install_background_selfupdate(value).unwrap(); 35 | } 36 | None => { 37 | uninstall_background_selfupdate().unwrap(); 38 | } 39 | } 40 | } 41 | 42 | save_config_db(&mut config_file) 43 | .with_context(|| "Failed to save configuration file from `config` command.")?; 44 | 45 | if !quiet { 46 | if value_changed { 47 | eprintln!( 48 | "Property 'backgroundselfupdateinterval' set to '{}'", 49 | match value { 50 | Some(value) => value, 51 | None => 0, 52 | } 53 | ); 54 | } else { 55 | eprintln!( 56 | "Property 'backgroundselfupdateinterval' is already set to '{}'", 57 | match value { 58 | Some(value) => value, 59 | None => 0, 60 | } 61 | ); 62 | } 63 | } 64 | } 65 | None => { 66 | let config_file = load_config_db(paths, None) 67 | .with_context(|| "`config` command failed to load configuration data.")?; 68 | 69 | if !quiet { 70 | eprintln!( 71 | "Property 'backgroundselfupdateinterval' set to '{}'", 72 | match config_file.self_data.background_selfupdate_interval { 73 | Some(value) => value, 74 | None => 0, 75 | } 76 | ); 77 | } 78 | } 79 | }; 80 | 81 | Ok(()) 82 | } 83 | -------------------------------------------------------------------------------- /src/command_config_modifypath.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "selfupdate")] 2 | pub fn run_command_config_modifypath( 3 | value: Option, 4 | quiet: bool, 5 | paths: &crate::global_paths::GlobalPaths, 6 | ) -> anyhow::Result<()> { 7 | use crate::config_file::{load_config_db, load_mut_config_db, save_config_db}; 8 | use crate::operations::{ 9 | add_binfolder_to_path_in_shell_scripts, remove_binfolder_from_path_in_shell_scripts, 10 | }; 11 | use anyhow::Context; 12 | 13 | match value { 14 | Some(value) => { 15 | let mut config_file = load_mut_config_db(paths) 16 | .with_context(|| "`config` command failed to load configuration data.")?; 17 | 18 | let mut value_changed = false; 19 | 20 | if value != config_file.self_data.modify_path { 21 | config_file.self_data.modify_path = value; 22 | 23 | value_changed = true; 24 | } 25 | 26 | if value { 27 | add_binfolder_to_path_in_shell_scripts(&paths.juliaupselfbin)?; 28 | } else { 29 | remove_binfolder_from_path_in_shell_scripts()?; 30 | } 31 | 32 | save_config_db(&mut config_file) 33 | .with_context(|| "Failed to save configuration file from `config` command.")?; 34 | 35 | if !quiet { 36 | if value_changed { 37 | eprintln!("Property 'modifypath' set to '{}'", value); 38 | } else { 39 | eprintln!("Property 'modifypath' is already set to '{}'", value); 40 | } 41 | } 42 | } 43 | None => { 44 | let config_file = load_config_db(paths, None) 45 | .with_context(|| "`config` command failed to load configuration data.")?; 46 | 47 | if !quiet { 48 | eprintln!( 49 | "Property 'modifypath' set to '{}'", 50 | config_file.self_data.modify_path 51 | ); 52 | } 53 | } 54 | }; 55 | 56 | Ok(()) 57 | } 58 | -------------------------------------------------------------------------------- /src/command_config_startupselfupdate.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "selfupdate")] 2 | use anyhow::Result; 3 | 4 | #[cfg(feature = "selfupdate")] 5 | pub fn run_command_config_startupselfupdate( 6 | value: Option, 7 | quiet: bool, 8 | paths: &crate::global_paths::GlobalPaths, 9 | ) -> Result<()> { 10 | use crate::config_file::{load_config_db, load_mut_config_db, save_config_db}; 11 | use anyhow::{bail, Context}; 12 | 13 | match value { 14 | Some(value) => { 15 | if value < 0 { 16 | bail!("Invalid argument."); 17 | } 18 | 19 | let mut config_file = load_mut_config_db(paths) 20 | .with_context(|| "`config` command failed to load configuration data.")?; 21 | 22 | let mut value_changed = false; 23 | 24 | let value = if value == 0 { None } else { Some(value) }; 25 | 26 | if value != config_file.self_data.startup_selfupdate_interval { 27 | config_file.self_data.startup_selfupdate_interval = value; 28 | 29 | value_changed = true; 30 | } 31 | 32 | save_config_db(&mut config_file) 33 | .with_context(|| "Failed to save configuration file from `config` command.")?; 34 | 35 | if !quiet { 36 | if value_changed { 37 | eprintln!( 38 | "Property 'startupselfupdateinterval' set to '{}'", 39 | match value { 40 | Some(value) => value, 41 | None => 0, 42 | } 43 | ); 44 | } else { 45 | eprintln!( 46 | "Property 'startupselfupdateinterval' is already set to '{}'", 47 | match value { 48 | Some(value) => value, 49 | None => 0, 50 | } 51 | ); 52 | } 53 | } 54 | } 55 | None => { 56 | let config_file = load_config_db(paths, None) 57 | .with_context(|| "`config` command failed to load configuration data.")?; 58 | 59 | if !quiet { 60 | eprintln!( 61 | "Property 'startupselfupdateinterval' set to '{}'", 62 | match config_file.self_data.startup_selfupdate_interval { 63 | Some(value) => value, 64 | None => 0, 65 | } 66 | ); 67 | } 68 | } 69 | }; 70 | 71 | Ok(()) 72 | } 73 | -------------------------------------------------------------------------------- /src/command_config_symlinks.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(windows))] 2 | use anyhow::Result; 3 | 4 | #[cfg(not(windows))] 5 | pub fn run_command_config_symlinks( 6 | value: Option, 7 | quiet: bool, 8 | paths: &crate::global_paths::GlobalPaths, 9 | ) -> Result<()> { 10 | use crate::config_file::{load_config_db, load_mut_config_db, save_config_db}; 11 | use crate::operations::{create_symlink, remove_symlink}; 12 | use anyhow::Context; 13 | 14 | match value { 15 | Some(value) => { 16 | let mut config_file = load_mut_config_db(paths) 17 | .with_context(|| "`config` command failed to load configuration data.")?; 18 | 19 | let mut value_changed = false; 20 | 21 | if value != config_file.data.settings.create_channel_symlinks { 22 | config_file.data.settings.create_channel_symlinks = value; 23 | value_changed = true; 24 | 25 | for (channel_name, channel) in &config_file.data.installed_channels { 26 | if value { 27 | create_symlink(channel, &format!("julia-{}", channel_name), paths)?; 28 | } else { 29 | remove_symlink(&format!("julia-{}", channel_name))?; 30 | } 31 | } 32 | } 33 | 34 | save_config_db(&mut config_file) 35 | .with_context(|| "Failed to save configuration file from `config` command.")?; 36 | 37 | if !quiet { 38 | if value_changed { 39 | eprintln!("Property 'channelsymlinks' set to '{}'", value); 40 | } else { 41 | eprintln!("Property 'channelsymlinks' is already set to '{}'", value); 42 | } 43 | } 44 | } 45 | None => { 46 | let config_file = load_config_db(paths, None) 47 | .with_context(|| "`config` command failed to load configuration data.")?; 48 | 49 | if !quiet { 50 | eprintln!( 51 | "Property 'channelsymlinks' set to '{}'", 52 | config_file.data.settings.create_channel_symlinks 53 | ); 54 | } 55 | } 56 | }; 57 | 58 | Ok(()) 59 | } 60 | -------------------------------------------------------------------------------- /src/command_config_versionsdbupdate.rs: -------------------------------------------------------------------------------- 1 | use crate::config_file::{load_config_db, load_mut_config_db, save_config_db}; 2 | use anyhow::{bail, Context, Result}; 3 | 4 | pub fn run_command_config_versionsdbupdate( 5 | value: Option, 6 | quiet: bool, 7 | paths: &crate::global_paths::GlobalPaths, 8 | ) -> Result<()> { 9 | match value { 10 | Some(value) => { 11 | if value < 0 { 12 | bail!("Invalid argument."); 13 | } 14 | 15 | let mut config_file = load_mut_config_db(paths) 16 | .with_context(|| "`config` command failed to load configuration data.")?; 17 | 18 | let mut value_changed = false; 19 | 20 | if value != config_file.data.settings.versionsdb_update_interval { 21 | config_file.data.settings.versionsdb_update_interval = value; 22 | 23 | value_changed = true; 24 | } 25 | 26 | save_config_db(&mut config_file) 27 | .with_context(|| "Failed to save configuration file from `config` command.")?; 28 | 29 | if !quiet { 30 | if value_changed { 31 | eprintln!("Property 'versionsdbupdateinterval' set to '{}'", value); 32 | } else { 33 | eprintln!( 34 | "Property 'versionsdbupdateinterval' is already set to '{}'", 35 | value 36 | ); 37 | } 38 | } 39 | } 40 | None => { 41 | let config_file = load_config_db(paths, None) 42 | .with_context(|| "`config` command failed to load configuration data.")?; 43 | 44 | if !quiet { 45 | eprintln!( 46 | "Property 'versionsdbupdateinterval' set to '{}'", 47 | config_file.data.settings.versionsdb_update_interval 48 | ); 49 | } 50 | } 51 | }; 52 | 53 | Ok(()) 54 | } 55 | -------------------------------------------------------------------------------- /src/command_default.rs: -------------------------------------------------------------------------------- 1 | use crate::operations::is_valid_channel; 2 | use crate::versions_file::load_versions_db; 3 | use crate::{config_file::*, global_paths::GlobalPaths}; 4 | use anyhow::{bail, Context, Result}; 5 | 6 | pub fn run_command_default(channel: &str, paths: &GlobalPaths) -> Result<()> { 7 | let mut config_file = load_mut_config_db(paths) 8 | .with_context(|| "`default` command failed to load configuration data.")?; 9 | 10 | if !config_file.data.installed_channels.contains_key(channel) { 11 | let version_db = load_versions_db(paths) 12 | .with_context(|| "`default` command failed to load versions db.")?; 13 | if !is_valid_channel(&version_db, &channel.to_string())? { 14 | bail!("'{}' is not a valid Julia version.", channel); 15 | } else { 16 | bail!( 17 | "'{}' is not an installed Julia version, run `juliaup add {}` first.", 18 | channel, 19 | channel 20 | ); 21 | } 22 | } 23 | 24 | config_file.data.default = Some(channel.to_string()); 25 | 26 | save_config_db(&mut config_file) 27 | .with_context(|| "`default` command failed to save configuration db.")?; 28 | 29 | eprintln!("Configured the default Julia version to be '{}'.", channel); 30 | 31 | Ok(()) 32 | } 33 | -------------------------------------------------------------------------------- /src/command_gc.rs: -------------------------------------------------------------------------------- 1 | use crate::config_file::{load_mut_config_db, save_config_db}; 2 | use crate::global_paths::GlobalPaths; 3 | use crate::operations::garbage_collect_versions; 4 | use anyhow::{Context, Result}; 5 | 6 | pub fn run_command_gc(prune_linked: bool, paths: &GlobalPaths) -> Result<()> { 7 | let mut config_file = load_mut_config_db(paths) 8 | .with_context(|| "`gc` command failed to load configuration data.")?; 9 | 10 | garbage_collect_versions(prune_linked, &mut config_file.data, paths)?; 11 | 12 | save_config_db(&mut config_file) 13 | .with_context(|| "`gc` command failed to save configuration db.")?; 14 | 15 | Ok(()) 16 | } 17 | -------------------------------------------------------------------------------- /src/command_info.rs: -------------------------------------------------------------------------------- 1 | use std::io::BufReader; 2 | 3 | use crate::config_file::load_config_db; 4 | use crate::jsonstructs_versionsdb::JuliaupVersionDB; 5 | use crate::operations::download_juliaup_version; 6 | use crate::utils::get_juliaserver_base_url; 7 | use crate::{get_bundled_dbversion, global_paths::GlobalPaths}; 8 | use crate::{get_juliaup_target, get_own_version}; 9 | use anyhow::{bail, Context, Result}; 10 | 11 | pub fn run_command_info(paths: &GlobalPaths) -> Result<()> { 12 | #[cfg(feature = "selfupdate")] 13 | let config_file = load_config_db(paths, None).with_context(|| { 14 | "`run_command_update_version_db` command failed to load configuration db." 15 | })?; 16 | 17 | #[cfg(feature = "selfupdate")] 18 | let juliaup_channel = match &config_file.self_data.juliaup_channel { 19 | Some(juliaup_channel) => juliaup_channel.to_string(), 20 | None => "release".to_string(), 21 | }; 22 | 23 | #[cfg(not(feature = "selfupdate"))] 24 | let _config_file = load_config_db(paths, None).with_context(|| { 25 | "`run_command_update_version_db` command failed to load configuration db." 26 | })?; 27 | 28 | // TODO Figure out how we can learn about the correctn Juliaup channel here 29 | #[cfg(not(feature = "selfupdate"))] 30 | let juliaup_channel = "release".to_string(); 31 | 32 | let juliaupserver_base = 33 | get_juliaserver_base_url().with_context(|| "Failed to get Juliaup server base URL.")?; 34 | 35 | let dbversion_url_path = match juliaup_channel.as_str() { 36 | "release" => "juliaup/RELEASECHANNELDBVERSION", 37 | "releasepreview" => "juliaup/RELEASEPREVIEWCHANNELDBVERSION", 38 | "dev" => "juliaup/DEVCHANNELDBVERSION", 39 | _ => bail!( 40 | "Juliaup is configured to a channel named '{}' that does not exist.", 41 | &juliaup_channel 42 | ), 43 | }; 44 | 45 | let dbversion_url = juliaupserver_base 46 | .join(dbversion_url_path) 47 | .with_context(|| { 48 | format!( 49 | "Failed to construct a valid url from '{}' and '{}'.", 50 | juliaupserver_base, dbversion_url_path 51 | ) 52 | })?; 53 | 54 | let online_dbversion = download_juliaup_version(dbversion_url.as_ref()) 55 | .with_context(|| "Failed to download current version db version.")?; 56 | 57 | let bundled_dbversion = get_bundled_dbversion() 58 | .with_context(|| "Failed to determine the bundled version db version.")?; 59 | 60 | let local_dbversion = match std::fs::OpenOptions::new() 61 | .read(true) 62 | .open(&paths.versiondb) 63 | { 64 | Ok(file) => { 65 | let reader = BufReader::new(&file); 66 | 67 | if let Ok(versiondb) = 68 | serde_json::from_reader::, JuliaupVersionDB>(reader) 69 | { 70 | if let Ok(version) = semver::Version::parse(&versiondb.version) { 71 | Some(version) 72 | } else { 73 | None 74 | } 75 | } else { 76 | None 77 | } 78 | } 79 | Err(_) => None, 80 | }; 81 | 82 | println!("Juliaup version: {}", get_own_version().unwrap()); 83 | println!("Platform triplet: {}", get_juliaup_target()); 84 | println!("Bundled version db: {}", bundled_dbversion); 85 | println!("Online version db: {}", online_dbversion); 86 | println!("Local version db: {:?}", local_dbversion); 87 | 88 | Ok(()) 89 | } 90 | -------------------------------------------------------------------------------- /src/command_initial_setup_from_launcher.rs: -------------------------------------------------------------------------------- 1 | use crate::{command_add::run_command_add, global_paths::GlobalPaths}; 2 | use anyhow::{Context, Result}; 3 | 4 | pub fn run_command_initial_setup_from_launcher(paths: &GlobalPaths) -> Result<()> { 5 | run_command_add("release", paths) 6 | .with_context(|| "Failed to run `run_command_add` from the `run_command_initial_setup_from_launcher` command.")?; 7 | 8 | Ok(()) 9 | } 10 | -------------------------------------------------------------------------------- /src/command_link.rs: -------------------------------------------------------------------------------- 1 | use crate::config_file::JuliaupConfigChannel; 2 | use crate::config_file::{load_mut_config_db, save_config_db}; 3 | use crate::global_paths::GlobalPaths; 4 | #[cfg(not(windows))] 5 | use crate::operations::create_symlink; 6 | use crate::operations::is_valid_channel; 7 | use crate::utils::is_valid_julia_path; 8 | use crate::versions_file::load_versions_db; 9 | use anyhow::{bail, Context, Result}; 10 | use path_absolutize::Absolutize; 11 | use std::path::Path; 12 | 13 | pub fn run_command_link( 14 | channel: &str, 15 | file: &str, 16 | args: &[String], 17 | paths: &GlobalPaths, 18 | ) -> Result<()> { 19 | let mut config_file = load_mut_config_db(paths) 20 | .with_context(|| "`link` command failed to load configuration data.")?; 21 | 22 | let versiondb_data = 23 | load_versions_db(paths).with_context(|| "`link` command failed to load versions db.")?; 24 | 25 | if config_file.data.installed_channels.contains_key(channel) { 26 | bail!("Channel name `{}` is already used.", channel) 27 | } 28 | 29 | if is_valid_channel(&versiondb_data, &channel.to_string())? { 30 | eprintln!("WARNING: The channel name `{}` is also a system channel. By linking your custom binary to this channel you are hiding this system channel.", channel); 31 | } 32 | 33 | let absolute_file_path = Path::new(file) 34 | .absolutize() 35 | .with_context(|| format!("Failed to convert path `{}` to absolute path.", file))?; 36 | 37 | if !is_valid_julia_path(&absolute_file_path.to_path_buf()) { 38 | eprintln!("WARNING: There is no julia binary at {}. If this was a mistake, run `juliaup remove {}` and try again.", absolute_file_path.to_string_lossy(), channel); 39 | } 40 | 41 | config_file.data.installed_channels.insert( 42 | channel.to_string(), 43 | JuliaupConfigChannel::LinkedChannel { 44 | command: absolute_file_path.to_string_lossy().to_string(), 45 | args: Some(args.to_vec()), 46 | }, 47 | ); 48 | 49 | #[cfg(not(windows))] 50 | let create_symlinks = config_file.data.settings.create_channel_symlinks; 51 | 52 | save_config_db(&mut config_file) 53 | .with_context(|| "`link` command failed to save configuration db.")?; 54 | 55 | #[cfg(not(windows))] 56 | if create_symlinks { 57 | create_symlink( 58 | &JuliaupConfigChannel::LinkedChannel { 59 | command: file.to_string(), 60 | args: Some(args.to_vec()), 61 | }, 62 | &format!("julia-{}", channel), 63 | paths, 64 | )?; 65 | } 66 | 67 | Ok(()) 68 | } 69 | -------------------------------------------------------------------------------- /src/command_list.rs: -------------------------------------------------------------------------------- 1 | use crate::operations::{channel_to_name, get_channel_variations}; 2 | use crate::{global_paths::GlobalPaths, versions_file::load_versions_db}; 3 | use anyhow::{Context, Result}; 4 | use cli_table::{ 5 | format::{Border, HorizontalLine, Separator}, 6 | print_stdout, ColorChoice, Table, WithTitle, 7 | }; 8 | use human_sort::compare; 9 | use itertools::Itertools; 10 | 11 | #[derive(Table)] 12 | struct ChannelRow { 13 | #[table(title = "Channel")] 14 | name: String, 15 | #[table(title = "Version")] 16 | version: String, 17 | } 18 | 19 | pub fn run_command_list(paths: &GlobalPaths) -> Result<()> { 20 | let versiondb_data = 21 | load_versions_db(paths).with_context(|| "`list` command failed to load versions db.")?; 22 | 23 | let non_db_channels: Vec = (get_channel_variations("nightly")?) 24 | .into_iter() 25 | .chain(get_channel_variations("x.y-nightly")?) 26 | .chain(get_channel_variations("pr{number}")?) 27 | .collect(); 28 | let non_db_rows: Vec = non_db_channels 29 | .into_iter() 30 | .map(|channel| { 31 | let name = channel_to_name(&channel).expect("Failed to identify version"); 32 | ChannelRow { 33 | name: channel, 34 | version: name, 35 | } 36 | }) 37 | .collect(); 38 | 39 | let rows_in_table: Vec<_> = versiondb_data 40 | .available_channels 41 | .iter() 42 | .map(|i| -> ChannelRow { 43 | ChannelRow { 44 | name: i.0.to_string(), 45 | version: i.1.version.clone(), 46 | } 47 | }) 48 | .sorted_by(|a, b| compare(&a.name, &b.name)) 49 | .chain(non_db_rows) 50 | .collect(); 51 | 52 | print_stdout( 53 | rows_in_table 54 | .with_title() 55 | .color_choice(ColorChoice::Never) 56 | .border(Border::builder().build()) 57 | .separator( 58 | Separator::builder() 59 | .title(Some(HorizontalLine::new('1', '2', '3', '-'))) 60 | .build(), 61 | ), 62 | )?; 63 | 64 | Ok(()) 65 | } 66 | -------------------------------------------------------------------------------- /src/command_override.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | env::current_dir, 3 | path::{Path, PathBuf}, 4 | }; 5 | 6 | use anyhow::{bail, Context, Result}; 7 | use cli_table::{ 8 | format::{Border, HorizontalLine, Separator}, 9 | print_stdout, ColorChoice, Table, WithTitle, 10 | }; 11 | use itertools::Itertools; 12 | 13 | use crate::{ 14 | config_file::{load_config_db, load_mut_config_db, save_config_db, JuliaupOverride}, 15 | global_paths::GlobalPaths, 16 | }; 17 | 18 | #[derive(Table)] 19 | struct OverrideRow { 20 | #[table(title = "Path")] 21 | path: String, 22 | #[table(title = "Channel")] 23 | channel: String, 24 | } 25 | 26 | pub fn run_command_override_status(paths: &GlobalPaths) -> Result<()> { 27 | let config_file = load_config_db(paths, None) 28 | .with_context(|| "`override status` command failed to load configuration file.")?; 29 | 30 | let rows_in_table: Vec<_> = config_file 31 | .data 32 | .overrides 33 | .iter() 34 | .sorted_by_key(|i| i.path.to_string()) 35 | .map(|i| -> OverrideRow { 36 | OverrideRow { 37 | path: dunce::simplified(&PathBuf::from(&i.path)) 38 | .to_string_lossy() 39 | .to_string(), 40 | channel: i.channel.to_string(), 41 | } 42 | }) 43 | .collect(); 44 | 45 | print_stdout( 46 | rows_in_table 47 | .with_title() 48 | .color_choice(ColorChoice::Never) 49 | .border(Border::builder().build()) 50 | .separator( 51 | Separator::builder() 52 | .title(Some(HorizontalLine::new('1', '2', '3', '-'))) 53 | .build(), 54 | ), 55 | )?; 56 | 57 | Ok(()) 58 | } 59 | 60 | pub fn run_command_override_set( 61 | paths: &GlobalPaths, 62 | channel: String, 63 | path: Option, 64 | ) -> Result<()> { 65 | let mut config_file = load_mut_config_db(paths) 66 | .with_context(|| "`override set` command failed to load configuration data.")?; 67 | 68 | if !config_file.data.installed_channels.contains_key(&channel) { 69 | bail!( 70 | "'{}' is not installed. Please run `juliaup add {}` to install channel or version.", 71 | &channel, 72 | &channel 73 | ); 74 | } 75 | 76 | let path = match path { 77 | Some(path) => PathBuf::from(path), 78 | None => current_dir()?, 79 | } 80 | .canonicalize()?; 81 | 82 | if let Some(i) = config_file 83 | .data 84 | .overrides 85 | .iter_mut() 86 | .find(|i| i.path == path.to_string_lossy()) 87 | { 88 | if i.channel == channel { 89 | eprintln!( 90 | "Override already set to '{}' for '{}'.", 91 | channel, 92 | path.to_string_lossy() 93 | ); 94 | return Ok(()); 95 | } else { 96 | eprintln!( 97 | "Override changed from '{}' to '{}' for '{}'.", 98 | i.channel, 99 | channel, 100 | path.to_string_lossy() 101 | ); 102 | i.channel = channel.clone(); 103 | 104 | save_config_db(&mut config_file).with_context(|| { 105 | "Failed to save configuration file from `override set` command." 106 | })?; 107 | return Ok(()); 108 | } 109 | } 110 | 111 | config_file.data.overrides.push(JuliaupOverride { 112 | path: path.to_string_lossy().to_string(), 113 | channel: channel.clone(), 114 | }); 115 | eprintln!( 116 | "Override set to '{}' for '{}'.", 117 | channel, 118 | path.to_string_lossy() 119 | ); 120 | 121 | save_config_db(&mut config_file) 122 | .with_context(|| "Failed to save configuration file from `override set` command.")?; 123 | 124 | Ok(()) 125 | } 126 | 127 | pub fn run_command_override_unset( 128 | paths: &GlobalPaths, 129 | nonexistent: bool, 130 | path: Option, 131 | ) -> Result<()> { 132 | let mut config_file = load_mut_config_db(paths) 133 | .with_context(|| "`override unset` command failed to load configuration data.")?; 134 | 135 | let path = match path { 136 | Some(path) => PathBuf::from(path), 137 | None => current_dir()?, 138 | } 139 | .canonicalize()?; 140 | 141 | if nonexistent { 142 | config_file 143 | .data 144 | .overrides 145 | .retain(|x| Path::new(&x.path).is_dir()); 146 | } else { 147 | // First remove any duplicates 148 | config_file 149 | .data 150 | .overrides 151 | .retain(|x| Path::new(&x.path) != path); 152 | } 153 | 154 | save_config_db(&mut config_file) 155 | .with_context(|| "Failed to save configuration file from `override add` command.")?; 156 | 157 | Ok(()) 158 | } 159 | -------------------------------------------------------------------------------- /src/command_remove.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(windows))] 2 | use crate::operations::remove_symlink; 3 | use crate::{ 4 | config_file::{load_mut_config_db, save_config_db, JuliaupConfigChannel}, 5 | global_paths::GlobalPaths, 6 | operations::garbage_collect_versions, 7 | }; 8 | use anyhow::{bail, Context, Result}; 9 | 10 | pub fn run_command_remove(channel: &str, paths: &GlobalPaths) -> Result<()> { 11 | let mut config_file = load_mut_config_db(paths) 12 | .with_context(|| "`remove` command failed to load configuration data.")?; 13 | 14 | if !config_file.data.installed_channels.contains_key(channel) { 15 | bail!( 16 | "'{}' cannot be removed because it is currently not installed.", 17 | channel 18 | ); 19 | } 20 | 21 | if let Some(ref default_value) = config_file.data.default { 22 | if channel == default_value { 23 | bail!( 24 | "'{}' cannot be removed because it is currently configured as the default channel.", 25 | channel 26 | ); 27 | } 28 | } 29 | 30 | if config_file 31 | .data 32 | .overrides 33 | .iter() 34 | .any(|i| i.channel == channel) 35 | { 36 | bail!( 37 | "'{}' cannot be removed because it is currently used in a directory override.", 38 | channel 39 | ); 40 | } 41 | 42 | let x = config_file.data.installed_channels.get(channel).unwrap(); 43 | 44 | if let JuliaupConfigChannel::DirectDownloadChannel { 45 | path, 46 | url: _, 47 | local_etag: _, 48 | server_etag: _, 49 | version: _, 50 | } = x 51 | { 52 | let path_to_delete = paths.juliauphome.join(&path); 53 | 54 | let display = path_to_delete.display(); 55 | 56 | if std::fs::remove_dir_all(&path_to_delete).is_err() { 57 | eprintln!("WARNING: Failed to delete {}. You can try to delete at a later point by running `juliaup gc`.", display) 58 | } 59 | }; 60 | 61 | config_file.data.installed_channels.remove(channel); 62 | 63 | #[cfg(not(windows))] 64 | remove_symlink(&format!("julia-{}", channel))?; 65 | 66 | garbage_collect_versions(false, &mut config_file.data, paths)?; 67 | 68 | save_config_db(&mut config_file).with_context(|| { 69 | format!( 70 | "Failed to save configuration file from `remove` command after '{}' was installed.", 71 | channel 72 | ) 73 | })?; 74 | 75 | eprintln!("Julia '{}' successfully removed.", channel); 76 | 77 | Ok(()) 78 | } 79 | -------------------------------------------------------------------------------- /src/command_selfchannel.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "selfupdate")] 2 | use anyhow::Result; 3 | 4 | #[cfg(feature = "selfupdate")] 5 | pub fn run_command_selfchannel( 6 | channel: Option, 7 | paths: &crate::global_paths::GlobalPaths, 8 | ) -> Result<()> { 9 | use crate::config_file::{load_mut_config_db, save_config_db}; 10 | use anyhow::Context; 11 | 12 | let mut config_file = load_mut_config_db(paths) 13 | .with_context(|| "`self update` command failed to load configuration data.")?; 14 | 15 | match channel { 16 | Some(chan) => { 17 | config_file.self_data.juliaup_channel = Some(chan.to_lowercase().to_string()); 18 | save_config_db(&mut config_file)?; 19 | } 20 | None => { 21 | let channel_name = config_file 22 | .self_data 23 | .juliaup_channel 24 | .expect("juliaup_channel should not be empty."); 25 | println!("Your juliaup is currently on channel `{}`. Run `juliaup self channel -h` for help on how to set the juliaup channel.", channel_name); 26 | } 27 | } 28 | 29 | Ok(()) 30 | } 31 | -------------------------------------------------------------------------------- /src/command_selfuninstall.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "selfupdate")] 2 | use anyhow::Result; 3 | 4 | #[cfg(feature = "selfupdate")] 5 | pub fn run_command_selfuninstall(paths: &crate::global_paths::GlobalPaths) -> Result<()> { 6 | use dialoguer::Confirm; 7 | 8 | use crate::{ 9 | command_config_backgroundselfupdate::run_command_config_backgroundselfupdate, 10 | command_config_modifypath::run_command_config_modifypath, 11 | command_config_startupselfupdate::run_command_config_startupselfupdate, 12 | command_config_symlinks::run_command_config_symlinks, 13 | }; 14 | 15 | let choice = Confirm::new() 16 | .with_prompt("Do you really want to uninstall Julia?") 17 | .default(false) 18 | .interact()?; 19 | 20 | if !choice { 21 | return Ok(()); 22 | } 23 | 24 | eprint!("Removing background self update task."); 25 | match run_command_config_backgroundselfupdate(Some(0), true, paths) { 26 | Ok(_) => eprintln!(" Success."), 27 | Err(_) => eprintln!(" Failed."), 28 | }; 29 | 30 | eprint!("Removing startup self update configuration."); 31 | match run_command_config_startupselfupdate(Some(0), true, &paths) { 32 | Ok(_) => eprintln!(" Success."), 33 | Err(_) => eprintln!(" Failed."), 34 | }; 35 | 36 | eprint!("Removing PATH modifications in startup scripts."); 37 | match run_command_config_modifypath(Some(false), true, &paths) { 38 | Ok(_) => eprintln!(" Success."), 39 | Err(_) => eprintln!(" Failed."), 40 | }; 41 | 42 | eprint!("Removing symlinks."); 43 | match run_command_config_symlinks(Some(false), true, &paths) { 44 | Ok(_) => eprintln!(" Success."), 45 | Err(_) => eprintln!(" Failed."), 46 | }; 47 | 48 | eprint!("Deleting Juliaup home folder {:?}.", paths.juliauphome); 49 | match std::fs::remove_dir_all(&paths.juliauphome) { 50 | Ok(_) => eprintln!(" Success."), 51 | Err(_) => eprintln!(" Failed."), 52 | }; 53 | 54 | if paths.juliauphome != paths.juliaupselfhome { 55 | let juliaup_binfolder_path = paths.juliaupselfhome.join("bin"); 56 | let julia_symlink_path = juliaup_binfolder_path.join("julia"); 57 | let julialauncher_path = juliaup_binfolder_path.join("julialauncher"); 58 | let juliaup_path = juliaup_binfolder_path.join("juliaup"); 59 | let juliaup_config_path = paths.juliaupselfhome.join("juliaupself.json"); 60 | 61 | eprint!("Deleting julia symlink {:?}.", julia_symlink_path); 62 | match std::fs::remove_file(&julia_symlink_path) { 63 | Ok(_) => eprintln!(" Success."), 64 | Err(_) => eprintln!(" Failed."), 65 | }; 66 | 67 | eprint!("Deleting julialauncher binary {:?}.", julialauncher_path); 68 | match std::fs::remove_file(&julialauncher_path) { 69 | Ok(_) => eprintln!(" Success."), 70 | Err(_) => eprintln!(" Failed."), 71 | }; 72 | 73 | eprint!("Deleting juliaup binary {:?}.", juliaup_path); 74 | match std::fs::remove_file(&juliaup_path) { 75 | Ok(_) => eprintln!(" Success."), 76 | Err(_) => eprintln!(" Failed."), 77 | }; 78 | 79 | if juliaup_binfolder_path.read_dir()?.next().is_none() { 80 | eprint!( 81 | "Deleting the Juliaup bin folder {:?}.", 82 | juliaup_binfolder_path 83 | ); 84 | match std::fs::remove_dir(&juliaup_binfolder_path) { 85 | Ok(_) => { 86 | eprintln!(" Success."); 87 | 88 | eprint!( 89 | "Deleting the Juliaup configuration file {:?}.", 90 | juliaup_config_path 91 | ); 92 | match std::fs::remove_file(&juliaup_config_path) { 93 | Ok(_) => eprintln!(" Success."), 94 | Err(_) => eprintln!(" Failed."), 95 | }; 96 | 97 | if paths.juliaupselfhome.read_dir()?.next().is_none() { 98 | eprint!("Deleting the Juliaup folder {:?}.", paths.juliaupselfhome); 99 | match std::fs::remove_dir(&paths.juliaupselfhome) { 100 | Ok(_) => eprintln!(" Success."), 101 | Err(_) => { 102 | eprintln!(" Failed, skipping removal of the entire Juliaup folder.") 103 | } 104 | }; 105 | } else { 106 | eprintln!("The Juliaup folder {:?} is not empty, skipping removal of the entire Juliaup folder.", paths.juliaupselfhome); 107 | } 108 | } 109 | Err(_) => eprintln!(" Failed, skipping removal of the entire Juliaup folder."), 110 | }; 111 | } else { 112 | eprintln!("The Juliaup bin folder {:?} is not empty, skipping removal of the entire Juliaup folder.", juliaup_binfolder_path); 113 | } 114 | } 115 | 116 | eprintln!("Successfully removed Juliaup."); 117 | 118 | Ok(()) 119 | } 120 | 121 | #[cfg(not(feature = "selfupdate"))] 122 | use anyhow::Result; 123 | 124 | #[cfg(not(feature = "selfupdate"))] 125 | pub fn run_command_selfuninstall_unavailable() -> Result<()> { 126 | eprintln!( 127 | "Self uninstall command is unavailable in this variant of Juliaup. 128 | This software was built with the intention of distributing it 129 | through a package manager other than cargo or upstream." 130 | ); 131 | Ok(()) 132 | } 133 | -------------------------------------------------------------------------------- /src/command_selfupdate.rs: -------------------------------------------------------------------------------- 1 | use crate::global_paths::GlobalPaths; 2 | use crate::operations::update_version_db; 3 | use anyhow::{Context, Result}; 4 | 5 | #[cfg(feature = "selfupdate")] 6 | pub fn run_command_selfupdate(paths: &GlobalPaths) -> Result<()> { 7 | use crate::config_file::{load_mut_config_db, save_config_db}; 8 | use crate::operations::{download_extract_sans_parent, download_juliaup_version}; 9 | use crate::utils::get_juliaserver_base_url; 10 | use crate::{get_juliaup_target, get_own_version}; 11 | use anyhow::{anyhow, bail}; 12 | 13 | update_version_db(paths).with_context(|| "Failed to update versions db.")?; 14 | 15 | let mut config_file = load_mut_config_db(paths) 16 | .with_context(|| "`selfupdate` command failed to load configuration db.")?; 17 | 18 | let juliaup_channel = match &config_file.self_data.juliaup_channel { 19 | Some(juliaup_channel) => juliaup_channel.to_string(), 20 | None => "release".to_string(), 21 | }; 22 | 23 | let juliaupserver_base = 24 | get_juliaserver_base_url().with_context(|| "Failed to get Juliaup server base URL.")?; 25 | 26 | let version_url_path = match juliaup_channel.as_str() { 27 | "release" => "juliaup/RELEASECHANNELVERSION", 28 | "releasepreview" => "juliaup/RELEASEPREVIEWCHANNELVERSION", 29 | "dev" => "juliaup/DEVCHANNELVERSION", 30 | _ => bail!( 31 | "Juliaup is configured to a channel named '{}' that does not exist.", 32 | &juliaup_channel 33 | ), 34 | }; 35 | 36 | eprintln!("Checking for self-updates"); 37 | 38 | let version_url = juliaupserver_base.join(version_url_path).with_context(|| { 39 | format!( 40 | "Failed to construct a valid url from '{}' and '{}'.", 41 | juliaupserver_base, version_url_path 42 | ) 43 | })?; 44 | 45 | let version = download_juliaup_version(&version_url.to_string())?; 46 | 47 | config_file.self_data.last_selfupdate = Some(chrono::Utc::now()); 48 | 49 | save_config_db(&mut config_file).with_context(|| "Failed to save configuration file.")?; 50 | 51 | if version == get_own_version().unwrap() { 52 | eprintln!( 53 | "Juliaup unchanged on channel '{}' - {}", 54 | juliaup_channel, version 55 | ); 56 | } else { 57 | let juliaup_target = get_juliaup_target(); 58 | 59 | let juliaupserver_base = 60 | get_juliaserver_base_url().with_context(|| "Failed to get Juliaup server base URL.")?; 61 | 62 | let download_url_path = 63 | format!("juliaup/bin/juliaup-{}-{}.tar.gz", version, juliaup_target); 64 | 65 | let new_juliaup_url = juliaupserver_base 66 | .join(&download_url_path) 67 | .with_context(|| { 68 | format!( 69 | "Failed to construct a valid url from '{}' and '{}'.", 70 | juliaupserver_base, download_url_path 71 | ) 72 | })?; 73 | 74 | let my_own_path = std::env::current_exe() 75 | .with_context(|| "Could not determine the path of the running exe.")?; 76 | 77 | let my_own_folder = my_own_path 78 | .parent() 79 | .ok_or_else(|| anyhow!("Could not determine parent."))?; 80 | 81 | eprintln!( 82 | "Found new version {} on channel {}.", 83 | version, juliaup_channel 84 | ); 85 | 86 | download_extract_sans_parent(&new_juliaup_url.to_string(), &my_own_folder, 0)?; 87 | eprintln!("Updated Juliaup to version {}.", version); 88 | } 89 | 90 | Ok(()) 91 | } 92 | 93 | #[cfg(feature = "windowsstore")] 94 | pub fn run_command_selfupdate(paths: &GlobalPaths) -> Result<()> { 95 | use windows::{ 96 | core::Interface, 97 | Win32::{System::Console::GetConsoleWindow, UI::Shell::IInitializeWithWindow}, 98 | }; 99 | 100 | update_version_db(paths).with_context(|| "Failed to update versions db.")?; 101 | 102 | let update_manager = windows::Services::Store::StoreContext::GetDefault() 103 | .with_context(|| "Failed to get the store context.")?; 104 | 105 | let interop: IInitializeWithWindow = update_manager 106 | .cast() 107 | .with_context(|| "Failed to cast the store context to IInitializeWithWindow.")?; 108 | 109 | unsafe { 110 | let x = GetConsoleWindow(); 111 | 112 | interop 113 | .Initialize(x) 114 | .with_context(|| "Call to IInitializeWithWindow.Initialize failed.")?; 115 | } 116 | 117 | let updates = update_manager 118 | .GetAppAndOptionalStorePackageUpdatesAsync() 119 | .with_context(|| "Call to GetAppAndOptionalStorePackageUpdatesAsync failed.")? 120 | .get() 121 | .with_context(|| { 122 | "get on the return of GetAppAndOptionalStorePackageUpdatesAsync failed." 123 | })?; 124 | 125 | if updates 126 | .Size() 127 | .with_context(|| "Call to Size on update results failed.")? 128 | > 0 129 | { 130 | eprintln!("An update is available."); 131 | 132 | let download_operation = update_manager 133 | .RequestDownloadAndInstallStorePackageUpdatesAsync(&updates) 134 | .with_context(|| "Call to RequestDownloadAndInstallStorePackageUpdatesAsync failed.")?; 135 | 136 | download_operation.get().with_context(|| { 137 | "get on result from RequestDownloadAndInstallStorePackageUpdatesAsync failed." 138 | })?; 139 | // This code will not be reached if the user opts to install updates 140 | } else { 141 | eprintln!("No updates available."); 142 | } 143 | 144 | Ok(()) 145 | } 146 | 147 | #[cfg(not(any(feature = "windowsstore", feature = "selfupdate")))] 148 | pub fn run_command_selfupdate(paths: &GlobalPaths) -> Result<()> { 149 | update_version_db(paths).with_context(|| "Failed to update versions db.")?; 150 | Ok(()) 151 | } 152 | -------------------------------------------------------------------------------- /src/command_status.rs: -------------------------------------------------------------------------------- 1 | use crate::config_file::load_config_db; 2 | use crate::config_file::JuliaupConfigChannel; 3 | use crate::global_paths::GlobalPaths; 4 | use crate::versions_file::load_versions_db; 5 | use anyhow::{Context, Result}; 6 | use cli_table::format::HorizontalLine; 7 | use cli_table::format::Separator; 8 | use cli_table::ColorChoice; 9 | use cli_table::{ 10 | format::{Border, Justify}, 11 | print_stdout, Table, WithTitle, 12 | }; 13 | use human_sort::compare; 14 | use itertools::Itertools; 15 | 16 | #[derive(Table)] 17 | struct ChannelRow { 18 | #[table(title = "Default", justify = "Justify::Right")] 19 | default: &'static str, 20 | #[table(title = "Channel")] 21 | name: String, 22 | #[table(title = "Version")] 23 | version: String, 24 | #[table(title = "Update")] 25 | update: String, 26 | } 27 | 28 | pub fn run_command_status(paths: &GlobalPaths) -> Result<()> { 29 | let config_file = load_config_db(paths, None) 30 | .with_context(|| "`status` command failed to load configuration file.")?; 31 | 32 | let versiondb_data = 33 | load_versions_db(paths).with_context(|| "`status` command failed to load versions db.")?; 34 | 35 | let rows_in_table: Vec<_> = config_file 36 | .data 37 | .installed_channels 38 | .iter() 39 | .sorted_by(|a, b| compare(&a.0.to_string(), &b.0.to_string())) 40 | .map(|i| -> ChannelRow { 41 | ChannelRow { 42 | default: match config_file.data.default { 43 | Some(ref default_value) => { 44 | if i.0 == default_value { 45 | "*" 46 | } else { 47 | "" 48 | } 49 | } 50 | None => "", 51 | }, 52 | name: i.0.to_string(), 53 | version: match i.1 { 54 | JuliaupConfigChannel::SystemChannel { version } => version.clone(), 55 | JuliaupConfigChannel::DirectDownloadChannel { 56 | path: _, 57 | url: _, 58 | local_etag: _, 59 | server_etag: _, 60 | version, 61 | } => { 62 | format!("Development version {}", version) 63 | } 64 | JuliaupConfigChannel::LinkedChannel { command, args } => { 65 | let mut combined_command = String::new(); 66 | 67 | if command.contains(' ') { 68 | combined_command.push('\"'); 69 | combined_command.push_str(command); 70 | combined_command.push('\"'); 71 | } else { 72 | combined_command.push_str(command); 73 | } 74 | 75 | if let Some(args) = args { 76 | for i in args { 77 | combined_command.push(' '); 78 | if i.contains(' ') { 79 | combined_command.push('\"'); 80 | combined_command.push_str(i); 81 | combined_command.push('\"'); 82 | } else { 83 | combined_command.push_str(i); 84 | } 85 | } 86 | } 87 | format!("Linked to `{}`", combined_command) 88 | } 89 | }, 90 | update: match i.1 { 91 | JuliaupConfigChannel::SystemChannel { version } => { 92 | match versiondb_data.available_channels.get(i.0) { 93 | Some(channel) => { 94 | if &channel.version != version { 95 | format!("Update to {} available", channel.version) 96 | } else { 97 | "".to_string() 98 | } 99 | } 100 | None => "".to_string(), 101 | } 102 | } 103 | JuliaupConfigChannel::LinkedChannel { 104 | command: _, 105 | args: _, 106 | } => "".to_string(), 107 | JuliaupConfigChannel::DirectDownloadChannel { 108 | path: _, 109 | url: _, 110 | local_etag, 111 | server_etag, 112 | version: _, 113 | } => { 114 | if local_etag != server_etag { 115 | "Update available".to_string() 116 | } else { 117 | "".to_string() 118 | } 119 | } 120 | }, 121 | } 122 | }) 123 | .collect(); 124 | 125 | print_stdout( 126 | rows_in_table 127 | .with_title() 128 | .color_choice(ColorChoice::Never) 129 | .border(Border::builder().build()) 130 | .separator( 131 | Separator::builder() 132 | .title(Some(HorizontalLine::new('1', '2', '3', '-'))) 133 | .build(), 134 | ), 135 | )?; 136 | 137 | Ok(()) 138 | } 139 | -------------------------------------------------------------------------------- /src/command_update.rs: -------------------------------------------------------------------------------- 1 | use crate::config_file::JuliaupConfig; 2 | use crate::config_file::{load_mut_config_db, save_config_db, JuliaupConfigChannel}; 3 | use crate::global_paths::GlobalPaths; 4 | use crate::jsonstructs_versionsdb::JuliaupVersionDB; 5 | #[cfg(not(windows))] 6 | use crate::operations::create_symlink; 7 | use crate::operations::{garbage_collect_versions, install_from_url}; 8 | use crate::operations::{install_version, update_version_db}; 9 | use crate::versions_file::load_versions_db; 10 | use anyhow::{anyhow, bail, Context, Result}; 11 | use console::style; 12 | use std::path::PathBuf; 13 | 14 | fn update_channel( 15 | config_db: &mut JuliaupConfig, 16 | channel: &String, 17 | version_db: &JuliaupVersionDB, 18 | ignore_non_updatable_channel: bool, 19 | paths: &GlobalPaths, 20 | ) -> Result<()> { 21 | let current_version = 22 | &config_db.installed_channels.get(channel).ok_or_else(|| anyhow!("Trying to get the installed version for a channel that does not exist in the config database."))?.clone(); 23 | 24 | match current_version { 25 | JuliaupConfigChannel::SystemChannel { version } => { 26 | let should_version = version_db.available_channels.get(channel); 27 | 28 | if let Some(should_version) = should_version { 29 | if &should_version.version != version { 30 | eprintln!("{} channel {}", style("Updating").green().bold(), channel); 31 | 32 | install_version(&should_version.version, config_db, version_db, paths) 33 | .with_context(|| { 34 | format!( 35 | "Failed to install '{}' while updating channel '{}'.", 36 | should_version.version, channel 37 | ) 38 | })?; 39 | 40 | config_db.installed_channels.insert( 41 | channel.clone(), 42 | JuliaupConfigChannel::SystemChannel { 43 | version: should_version.version.clone(), 44 | }, 45 | ); 46 | 47 | #[cfg(not(windows))] 48 | if config_db.settings.create_channel_symlinks { 49 | create_symlink( 50 | &JuliaupConfigChannel::SystemChannel { 51 | version: should_version.version.clone(), 52 | }, 53 | &format!("julia-{}", channel), 54 | paths, 55 | )?; 56 | } 57 | } 58 | } else if ignore_non_updatable_channel { 59 | eprintln!("Skipping update for '{}' channel, it no longer exists in the version database.", channel); 60 | } else { 61 | bail!( 62 | "Failed to update '{}' because it no longer exists in the version database.", 63 | channel 64 | ); 65 | } 66 | } 67 | JuliaupConfigChannel::LinkedChannel { 68 | command: _, 69 | args: _, 70 | } => { 71 | if !ignore_non_updatable_channel { 72 | bail!( 73 | "Failed to update '{}' because it is a linked channel.", 74 | channel 75 | ); 76 | } 77 | } 78 | JuliaupConfigChannel::DirectDownloadChannel { 79 | path, 80 | url, 81 | local_etag, 82 | server_etag, 83 | version, 84 | } => { 85 | if local_etag != server_etag { 86 | // We only do this so that we use `version` on both Windows and Linux to prevent a compiler warning/error 87 | if version.is_empty() { 88 | eprintln!( 89 | "Channel {} version is empty, you may need to manually codesign this channel if you trust the contents of this pull request.", 90 | channel 91 | ); 92 | } 93 | eprintln!("{} channel {}", style("Updating").green().bold(), channel); 94 | 95 | let channel_data = 96 | install_from_url(&url::Url::parse(url)?, &PathBuf::from(path), paths)?; 97 | 98 | config_db 99 | .installed_channels 100 | .insert(channel.clone(), channel_data); 101 | 102 | #[cfg(not(windows))] 103 | if config_db.settings.create_channel_symlinks { 104 | create_symlink( 105 | &JuliaupConfigChannel::DirectDownloadChannel { 106 | path: path.clone(), 107 | url: url.clone(), 108 | local_etag: local_etag.clone(), 109 | server_etag: server_etag.clone(), 110 | version: version.clone(), 111 | }, 112 | &channel, 113 | paths, 114 | )?; 115 | } 116 | } 117 | } 118 | } 119 | 120 | Ok(()) 121 | } 122 | 123 | pub fn run_command_update(channel: Option, paths: &GlobalPaths) -> Result<()> { 124 | update_version_db(paths).with_context(|| "Failed to update versions db.")?; 125 | 126 | let version_db = 127 | load_versions_db(paths).with_context(|| "`update` command failed to load versions db.")?; 128 | 129 | let mut config_file = load_mut_config_db(paths) 130 | .with_context(|| "`update` command failed to load configuration data.")?; 131 | 132 | match channel { 133 | None => { 134 | for (k, _) in config_file.data.installed_channels.clone() { 135 | update_channel(&mut config_file.data, &k, &version_db, true, paths)?; 136 | } 137 | } 138 | Some(channel) => { 139 | if !config_file.data.installed_channels.contains_key(&channel) { 140 | bail!( 141 | "'{}' cannot be updated because it is currently not installed.", 142 | channel 143 | ); 144 | } 145 | 146 | update_channel(&mut config_file.data, &channel, &version_db, false, paths)?; 147 | } 148 | }; 149 | 150 | garbage_collect_versions(false, &mut config_file.data, paths)?; 151 | 152 | save_config_db(&mut config_file) 153 | .with_context(|| "`update` command failed to save configuration db.")?; 154 | 155 | Ok(()) 156 | } 157 | -------------------------------------------------------------------------------- /src/command_update_version_db.rs: -------------------------------------------------------------------------------- 1 | use crate::{global_paths::GlobalPaths, operations::update_version_db}; 2 | use anyhow::{Context, Result}; 3 | 4 | pub fn run_command_update_version_db(paths: &GlobalPaths) -> Result<()> { 5 | update_version_db(paths).with_context(|| "Failed to update version db.")?; 6 | 7 | Ok(()) 8 | } 9 | -------------------------------------------------------------------------------- /src/global_paths.rs: -------------------------------------------------------------------------------- 1 | use crate::get_juliaup_target; 2 | #[cfg(feature = "selfupdate")] 3 | use anyhow::Context; 4 | use anyhow::{anyhow, bail, Result}; 5 | use std::path::PathBuf; 6 | pub struct GlobalPaths { 7 | pub juliauphome: PathBuf, 8 | pub juliaupconfig: PathBuf, 9 | pub lockfile: PathBuf, 10 | pub versiondb: PathBuf, 11 | #[cfg(feature = "selfupdate")] 12 | pub juliaupselfhome: PathBuf, 13 | #[cfg(feature = "selfupdate")] 14 | pub juliaupselfconfig: PathBuf, 15 | #[cfg(feature = "selfupdate")] 16 | pub juliaupselfbin: PathBuf, 17 | } 18 | 19 | fn get_juliaup_home_path() -> Result { 20 | match std::env::var("JULIAUP_DEPOT_PATH") { 21 | Ok(val) => { 22 | let val = val.trim(); 23 | 24 | if val.is_empty() { 25 | return get_default_juliaup_home_path(); 26 | } else { 27 | let path = PathBuf::from(val); 28 | 29 | if !path.is_absolute() { 30 | return Err(anyhow!("The current value of '{}' for the environment variable JULIAUP_DEPOT_PATH is not an absolute path.", val)); 31 | } else { 32 | return Ok(PathBuf::from(val).join("juliaup")); 33 | } 34 | } 35 | } 36 | Err(_) => return get_default_juliaup_home_path(), 37 | } 38 | } 39 | 40 | /// Return ~/.julia/juliaup, if such a directory can be found 41 | fn get_default_juliaup_home_path() -> Result { 42 | let path = dirs::home_dir() 43 | .ok_or_else(|| anyhow!("Could not determine the path of the user home directory."))? 44 | .join(".julia") 45 | .join("juliaup"); 46 | 47 | if !path.is_absolute() { 48 | bail!( 49 | "The system returned an invalid home directory path `{}`.", 50 | path.display() 51 | ); 52 | }; 53 | Ok(path) 54 | } 55 | 56 | pub fn get_paths() -> Result { 57 | let juliauphome = get_juliaup_home_path()?; 58 | 59 | #[cfg(feature = "selfupdate")] 60 | let my_own_path = std::env::current_exe() 61 | .with_context(|| "Could not determine the path of the running exe.")?; 62 | 63 | #[cfg(feature = "selfupdate")] 64 | let juliaupselfbin = my_own_path 65 | .parent() 66 | .ok_or_else(|| anyhow!("Could not determine parent."))? 67 | .to_path_buf(); 68 | 69 | let juliaupconfig = juliauphome.join("juliaup.json"); 70 | 71 | let versiondb = juliauphome.join(format!("versiondb-{}.json", get_juliaup_target())); 72 | 73 | let lockfile = juliauphome.join(".juliaup-lock"); 74 | 75 | #[cfg(feature = "selfupdate")] 76 | let juliaupselfhome = my_own_path 77 | .parent() 78 | .ok_or_else(|| anyhow!("Failed to get path of folder of own executable."))? 79 | .parent() 80 | .ok_or_else(|| anyhow!("Failed to get parent path of folder of own executable."))? 81 | .to_path_buf(); 82 | 83 | #[cfg(feature = "selfupdate")] 84 | let juliaupselfconfig = juliaupselfhome.join("juliaupself.json"); 85 | 86 | Ok(GlobalPaths { 87 | juliauphome, 88 | juliaupconfig, 89 | lockfile, 90 | versiondb, 91 | #[cfg(feature = "selfupdate")] 92 | juliaupselfhome, 93 | #[cfg(feature = "selfupdate")] 94 | juliaupselfconfig, 95 | #[cfg(feature = "selfupdate")] 96 | juliaupselfbin, 97 | }) 98 | } 99 | -------------------------------------------------------------------------------- /src/jsonstructs_versionsdb.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use std::collections::HashMap; 3 | 4 | #[derive(Serialize, Deserialize)] 5 | pub struct JuliaupVersionDBVersion { 6 | #[serde(rename = "UrlPath")] 7 | pub url_path: String, 8 | } 9 | 10 | #[derive(Serialize, Deserialize)] 11 | pub struct JuliaupVersionDBChannel { 12 | #[serde(rename = "Version")] 13 | pub version: String, 14 | } 15 | 16 | #[derive(Serialize, Deserialize)] 17 | pub struct JuliaupVersionDB { 18 | #[serde(rename = "AvailableVersions")] 19 | pub available_versions: HashMap, 20 | #[serde(rename = "AvailableChannels")] 21 | pub available_channels: HashMap, 22 | #[serde(rename = "Version")] 23 | pub version: String, 24 | } 25 | -------------------------------------------------------------------------------- /src/julia.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuliaLang/juliaup/eb5a9ed84b6463128d70629499f2cbcc9d5edbef/src/julia.ico -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Context; 2 | 3 | pub mod cli; 4 | pub mod command_add; 5 | pub mod command_api; 6 | pub mod command_completions; 7 | pub mod command_config_backgroundselfupdate; 8 | pub mod command_config_modifypath; 9 | pub mod command_config_startupselfupdate; 10 | pub mod command_config_symlinks; 11 | pub mod command_config_versionsdbupdate; 12 | pub mod command_default; 13 | pub mod command_gc; 14 | pub mod command_info; 15 | pub mod command_initial_setup_from_launcher; 16 | pub mod command_link; 17 | pub mod command_list; 18 | pub mod command_override; 19 | pub mod command_remove; 20 | pub mod command_selfchannel; 21 | pub mod command_selfuninstall; 22 | pub mod command_selfupdate; 23 | pub mod command_status; 24 | pub mod command_update; 25 | pub mod command_update_version_db; 26 | pub mod config_file; 27 | pub mod global_paths; 28 | pub mod jsonstructs_versionsdb; 29 | pub mod operations; 30 | pub mod utils; 31 | pub mod versions_file; 32 | 33 | include!(concat!(env!("OUT_DIR"), "/bundled_version.rs")); 34 | include!(concat!(env!("OUT_DIR"), "/various_constants.rs")); 35 | include!(concat!(env!("OUT_DIR"), "/built.rs")); 36 | 37 | pub fn get_bundled_julia_version() -> &'static str { 38 | BUNDLED_JULIA_VERSION 39 | } 40 | 41 | pub fn get_bundled_dbversion() -> anyhow::Result { 42 | let dbversion = semver::Version::parse(BUNDLED_DB_VERSION) 43 | .with_context(|| "Failed to parse our own db version.")?; 44 | 45 | Ok(dbversion) 46 | } 47 | 48 | pub fn get_juliaup_target() -> &'static str { 49 | JULIAUP_TARGET 50 | } 51 | 52 | pub fn get_own_version() -> anyhow::Result { 53 | use semver::Version; 54 | 55 | let version = 56 | Version::parse(PKG_VERSION).with_context(|| "Failed to parse our own version.")?; 57 | 58 | Ok(version) 59 | } 60 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{anyhow, bail, Context, Result}; 2 | use semver::{BuildMetadata, Version}; 3 | use std::path::PathBuf; 4 | use url::Url; 5 | 6 | pub fn get_juliaserver_base_url() -> Result { 7 | let base_url = if let Ok(val) = std::env::var("JULIAUP_SERVER") { 8 | if val.ends_with('/') { 9 | val 10 | } else { 11 | format!("{}/", val) 12 | } 13 | } else { 14 | "https://julialang-s3.julialang.org".to_string() 15 | }; 16 | 17 | let parsed_url = Url::parse(&base_url).with_context(|| { 18 | format!( 19 | "Failed to parse the value of JULIAUP_SERVER '{}' as a uri.", 20 | base_url 21 | ) 22 | })?; 23 | 24 | Ok(parsed_url) 25 | } 26 | 27 | pub fn get_julianightlies_base_url() -> Result { 28 | let base_url = if let Ok(val) = std::env::var("JULIAUP_NIGHTLY_SERVER") { 29 | if val.ends_with('/') { 30 | val 31 | } else { 32 | format!("{}/", val) 33 | } 34 | } else { 35 | "https://julialangnightlies-s3.julialang.org".to_string() 36 | }; 37 | 38 | let parsed_url = Url::parse(&base_url).with_context(|| { 39 | format!( 40 | "Failed to parse the value of JULIAUP_NIGHTLY_SERVER '{}' as a uri.", 41 | base_url 42 | ) 43 | })?; 44 | 45 | Ok(parsed_url) 46 | } 47 | 48 | pub fn get_bin_dir() -> Result { 49 | let entry_sep = if std::env::consts::OS == "windows" { 50 | ';' 51 | } else { 52 | ':' 53 | }; 54 | 55 | let path = match std::env::var("JULIAUP_BIN_DIR") { 56 | Ok(val) => { 57 | let path = PathBuf::from(val.split(entry_sep).next().unwrap()); // We can unwrap here because even when we split an empty string we should get a first element. 58 | 59 | if !path.is_absolute() { 60 | bail!("The `JULIAUP_BIN_DIR` environment variable contains a value that resolves to an an invalid path `{}`.", path.display()); 61 | }; 62 | 63 | path 64 | } 65 | Err(_) => { 66 | let mut path = std::env::current_exe() 67 | .with_context(|| "Could not determine the path of the running exe.")? 68 | .parent() 69 | .ok_or_else(|| anyhow!("Could not determine parent."))? 70 | .to_path_buf(); 71 | 72 | if let Some(home_dir) = dirs::home_dir() { 73 | if !path.starts_with(&home_dir) { 74 | path = home_dir.join(".local").join("bin"); 75 | 76 | if !path.is_absolute() { 77 | bail!( 78 | "The system returned an invalid home directory path `{}`.", 79 | path.display() 80 | ); 81 | }; 82 | } 83 | } 84 | 85 | path 86 | } 87 | }; 88 | 89 | Ok(path) 90 | } 91 | 92 | pub fn is_valid_julia_path(julia_path: &PathBuf) -> bool { 93 | return std::process::Command::new(julia_path) 94 | .arg("-v") 95 | .stdout(std::process::Stdio::null()) 96 | .spawn() 97 | .is_ok(); 98 | } 99 | 100 | pub fn get_arch() -> Result { 101 | if std::env::consts::ARCH == "x86" { 102 | return Ok("x86".to_string()); 103 | } else if std::env::consts::ARCH == "x86_64" { 104 | return Ok("x64".to_string()); 105 | } else if std::env::consts::ARCH == "aarch64" { 106 | return Ok("aarch64".to_string()); 107 | } 108 | 109 | bail!("Running on an unknown arch: {}.", std::env::consts::ARCH) 110 | } 111 | 112 | pub fn parse_versionstring(value: &String) -> Result<(String, Version)> { 113 | let version = Version::parse(value).unwrap(); 114 | 115 | let build_parts: Vec<&str> = version.build.split('.').collect(); 116 | 117 | if build_parts.len() != 4 { 118 | bail!( 119 | "`{}` is an invalid version specifier: the build part must have four parts.", 120 | value 121 | ); 122 | } 123 | 124 | let version_without_build = semver::Version { 125 | major: version.major, 126 | minor: version.minor, 127 | patch: version.patch, 128 | pre: version.pre, 129 | build: BuildMetadata::EMPTY, 130 | }; 131 | 132 | let platform = build_parts[1]; 133 | 134 | Ok((platform.to_string(), version_without_build)) 135 | } 136 | 137 | #[cfg(test)] 138 | mod tests { 139 | use super::*; 140 | 141 | #[test] 142 | fn test_parse_versionstring() { 143 | let s = "1.1.1"; 144 | assert!(parse_versionstring(&s.to_owned()).is_err()); 145 | 146 | let s = "1.1.1+0.x86.apple.darwin14"; 147 | let (p, v) = parse_versionstring(&s.to_owned()).unwrap(); 148 | assert_eq!(p, "x86"); 149 | assert_eq!(v, Version::new(1, 1, 1)); 150 | 151 | let s = "1.1.1+0.x64.apple.darwin14"; 152 | let (p, v) = parse_versionstring(&s.to_owned()).unwrap(); 153 | assert_eq!(p, "x64"); 154 | assert_eq!(v, Version::new(1, 1, 1)); 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/versions_file.rs: -------------------------------------------------------------------------------- 1 | use std::{fs::File, io::BufReader}; 2 | 3 | // use std::fs::File; 4 | // use std::io::BufReader; 5 | // use crate::utils::get_juliaup_home_path; 6 | use crate::{ 7 | get_bundled_dbversion, global_paths::GlobalPaths, jsonstructs_versionsdb::JuliaupVersionDB, 8 | }; 9 | use anyhow::{Context, Result}; 10 | use semver::Version; 11 | 12 | fn load_vendored_db() -> Result { 13 | let vendored_db = include_str!(concat!(env!("OUT_DIR"), "/versionsdb.json")); 14 | 15 | let db: JuliaupVersionDB = serde_json::from_str(vendored_db) 16 | .with_context(|| "Failed to parse vendored version db.")?; 17 | 18 | Ok(db) 19 | } 20 | 21 | pub fn load_versions_db(paths: &GlobalPaths) -> Result { 22 | let file = File::open(&paths.versiondb); 23 | 24 | let local_version_db = match file { 25 | Ok(file) => { 26 | let reader = BufReader::new(&file); 27 | 28 | if let Ok(versiondb) = 29 | serde_json::from_reader::, JuliaupVersionDB>(reader) 30 | { 31 | Some(versiondb) 32 | } else { 33 | None 34 | } 35 | } 36 | Err(_) => None, 37 | }; 38 | 39 | let db = match local_version_db { 40 | Some(local_version_db) => { 41 | if let Ok(version) = Version::parse(&local_version_db.version) { 42 | if version >= get_bundled_dbversion().unwrap() { 43 | local_version_db 44 | } else { 45 | load_vendored_db().with_context(|| "Failed to load vendored version db.")? 46 | } 47 | } else { 48 | load_vendored_db().with_context(|| "Failed to load vendored version db.")? 49 | } 50 | } 51 | None => load_vendored_db().with_context(|| "Failed to load vendored version db.")?, 52 | }; 53 | 54 | Ok(db) 55 | } 56 | -------------------------------------------------------------------------------- /tests/channel_selection.rs: -------------------------------------------------------------------------------- 1 | use assert_cmd::Command; 2 | 3 | #[test] 4 | fn channel_selection() { 5 | let depot_dir = assert_fs::TempDir::new().unwrap(); 6 | 7 | Command::cargo_bin("juliaup") 8 | .unwrap() 9 | .arg("add") 10 | .arg("1.6.7") 11 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 12 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 13 | .assert() 14 | .success() 15 | .stdout(""); 16 | 17 | Command::cargo_bin("juliaup") 18 | .unwrap() 19 | .arg("add") 20 | .arg("1.7.3") 21 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 22 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 23 | .assert() 24 | .success() 25 | .stdout(""); 26 | 27 | Command::cargo_bin("juliaup") 28 | .unwrap() 29 | .arg("add") 30 | .arg("1.8.5") 31 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 32 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 33 | .assert() 34 | .success() 35 | .stdout(""); 36 | 37 | Command::cargo_bin("juliaup") 38 | .unwrap() 39 | .arg("default") 40 | .arg("1.6.7") 41 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 42 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 43 | .assert() 44 | .success() 45 | .stdout(""); 46 | 47 | Command::cargo_bin("julia") 48 | .unwrap() 49 | .arg("-e") 50 | .arg("print(VERSION)") 51 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 52 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 53 | .assert() 54 | .success() 55 | .stdout("1.6.7"); 56 | 57 | Command::cargo_bin("julia") 58 | .unwrap() 59 | .arg("+1.8.5") 60 | .arg("-e") 61 | .arg("print(VERSION)") 62 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 63 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 64 | .assert() 65 | .success() 66 | .stdout("1.8.5"); 67 | 68 | Command::cargo_bin("julia") 69 | .unwrap() 70 | .arg("-e") 71 | .arg("print(VERSION)") 72 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 73 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 74 | .env("JULIAUP_CHANNEL", "1.7.3") 75 | .assert() 76 | .success() 77 | .stdout("1.7.3"); 78 | 79 | Command::cargo_bin("julia") 80 | .unwrap() 81 | .arg("+1.8.5") 82 | .arg("-e") 83 | .arg("print(VERSION)") 84 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 85 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 86 | .env("JULIAUP_CHANNEL", "1.7.3") 87 | .assert() 88 | .success() 89 | .stdout("1.8.5"); 90 | 91 | // Now testing incorrect channels 92 | 93 | Command::cargo_bin("julia") 94 | .unwrap() 95 | .arg("+1.8.6") 96 | .arg("-e") 97 | .arg("print(VERSION)") 98 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 99 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 100 | .assert() 101 | .failure() 102 | .stderr("ERROR: Invalid Juliaup channel `1.8.6`. Please run `juliaup list` to get a list of valid channels and versions.\n"); 103 | 104 | Command::cargo_bin("julia") 105 | .unwrap() 106 | .arg("-e") 107 | .arg("print(VERSION)") 108 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 109 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 110 | .env("JULIAUP_CHANNEL", "1.7.4") 111 | .assert() 112 | .failure() 113 | .stderr( 114 | "ERROR: Invalid Juliaup channel `1.7.4` from environment variable JULIAUP_CHANNEL. Please run `juliaup list` to get a list of valid channels and versions.\n", 115 | ); 116 | 117 | Command::cargo_bin("julia") 118 | .unwrap() 119 | .arg("+1.8.6") 120 | .arg("-e") 121 | .arg("print(VERSION)") 122 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 123 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 124 | .env("JULIAUP_CHANNEL", "1.7.4") 125 | .assert() 126 | .failure() 127 | .stderr("ERROR: Invalid Juliaup channel `1.8.6`. Please run `juliaup list` to get a list of valid channels and versions.\n"); 128 | 129 | // https://github.com/JuliaLang/juliaup/issues/766 130 | Command::cargo_bin("julia") 131 | .unwrap() 132 | .arg("+1.8.2") 133 | .arg("-e") 134 | .arg("print(VERSION)") 135 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 136 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 137 | .env("JULIAUP_CHANNEL", "1.7.4") 138 | .assert() 139 | .failure() 140 | .stderr("ERROR: `1.8.2` is not installed. Please run `juliaup add 1.8.2` to install channel or version.\n"); 141 | 142 | // https://github.com/JuliaLang/juliaup/issues/820 143 | Command::cargo_bin("julia") 144 | .unwrap() 145 | .arg("+nightly") 146 | .arg("-e") 147 | .arg("print(VERSION)") 148 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 149 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 150 | .env("JULIAUP_CHANNEL", "1.7.4") 151 | .assert() 152 | .failure() 153 | .stderr("ERROR: `nightly` is not installed. Please run `juliaup add nightly` to install channel or version.\n"); 154 | 155 | // https://github.com/JuliaLang/juliaup/issues/995 156 | Command::cargo_bin("julia") 157 | .unwrap() 158 | .arg("+pr1") 159 | .arg("-e") 160 | .arg("print(VERSION)") 161 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 162 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 163 | .env("JULIAUP_CHANNEL", "1.7.4") 164 | .assert() 165 | .failure() 166 | .stderr("ERROR: `pr1` is not installed. Please run `juliaup add pr1` to install pull request channel if available.\n"); 167 | } 168 | -------------------------------------------------------------------------------- /tests/command_add.rs: -------------------------------------------------------------------------------- 1 | use assert_cmd::Command; 2 | use predicates::prelude::predicate; 3 | 4 | #[test] 5 | fn command_add() { 6 | let depot_dir = assert_fs::TempDir::new().unwrap(); 7 | 8 | Command::cargo_bin("juliaup") 9 | .unwrap() 10 | .arg("add") 11 | .arg("1.6.4") 12 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 13 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 14 | .assert() 15 | .success() 16 | .stdout(""); 17 | 18 | Command::cargo_bin("juliaup") 19 | .unwrap() 20 | .arg("add") 21 | .arg("nightly") 22 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 23 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 24 | .assert() 25 | .success() 26 | .stdout(""); 27 | 28 | Command::cargo_bin("juliaup") 29 | .unwrap() 30 | .arg("add") 31 | .arg("1.11-nightly") 32 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 33 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 34 | .assert() 35 | .success() 36 | .stdout(""); 37 | 38 | Command::cargo_bin("julia") 39 | .unwrap() 40 | .arg("+1.6.4") 41 | .arg("-e") 42 | .arg("print(VERSION)") 43 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 44 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 45 | .assert() 46 | .success() 47 | .stdout("1.6.4"); 48 | 49 | Command::cargo_bin("julia") 50 | .unwrap() 51 | .arg("+nightly") 52 | .arg("-e") 53 | .arg("print(VERSION)") 54 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 55 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 56 | .assert() 57 | .success() 58 | .stdout( 59 | predicate::str::is_match( 60 | "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)-DEV\\.(0|[1-9]\\d*)", 61 | ) 62 | .unwrap(), 63 | ); 64 | } 65 | -------------------------------------------------------------------------------- /tests/command_default.rs: -------------------------------------------------------------------------------- 1 | use assert_cmd::Command; 2 | 3 | #[test] 4 | fn command_default() { 5 | let depot_dir = assert_fs::TempDir::new().unwrap(); 6 | 7 | Command::cargo_bin("juliaup") 8 | .unwrap() 9 | .arg("add") 10 | .arg("1.6.0") 11 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 12 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 13 | .assert() 14 | .success() 15 | .stdout(""); 16 | 17 | Command::cargo_bin("juliaup") 18 | .unwrap() 19 | .arg("default") 20 | .arg("1.6.0") 21 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 22 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 23 | .assert() 24 | .success() 25 | .stdout(""); 26 | 27 | Command::cargo_bin("julia") 28 | .unwrap() 29 | .arg("-e") 30 | .arg("print(VERSION)") 31 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 32 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 33 | .assert() 34 | .success() 35 | .stdout("1.6.0"); 36 | } 37 | -------------------------------------------------------------------------------- /tests/command_gc.rs: -------------------------------------------------------------------------------- 1 | use assert_cmd::Command; 2 | use predicates::prelude::*; 3 | 4 | #[test] 5 | fn command_gc() { 6 | let depot_dir = assert_fs::TempDir::new().unwrap(); 7 | 8 | Command::cargo_bin("juliaup") 9 | .unwrap() 10 | .arg("add") 11 | .arg("1.6.7") 12 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 13 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 14 | .assert() 15 | .success(); 16 | 17 | Command::cargo_bin("juliaup") 18 | .unwrap() 19 | .arg("link") 20 | .arg("julib") 21 | .arg("julib") 22 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 23 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 24 | .assert() 25 | .success(); 26 | 27 | Command::cargo_bin("juliaup") 28 | .unwrap() 29 | .arg("link") 30 | .arg("julic") 31 | .arg("julic") 32 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 33 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 34 | .assert() 35 | .success(); 36 | 37 | Command::cargo_bin("juliaup") 38 | .unwrap() 39 | .arg("status") 40 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 41 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 42 | .assert() 43 | .success() 44 | .stdout( 45 | predicate::str::contains("\n") 46 | .count(5) 47 | .and(predicate::str::contains("julic")) 48 | .and(predicate::str::contains("julib")), 49 | ); 50 | 51 | Command::cargo_bin("juliaup") 52 | .unwrap() 53 | .arg("gc") 54 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 55 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 56 | .assert() 57 | .success(); 58 | 59 | Command::cargo_bin("juliaup") 60 | .unwrap() 61 | .arg("status") 62 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 63 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 64 | .assert() 65 | .success() 66 | .stdout( 67 | predicate::str::contains("\n") 68 | .count(5) 69 | .and(predicate::str::contains("julic")) 70 | .and(predicate::str::contains("julib")), 71 | ); 72 | 73 | Command::cargo_bin("juliaup") 74 | .unwrap() 75 | .arg("gc") 76 | .arg("--prune-linked") 77 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 78 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 79 | .assert() 80 | .success(); 81 | 82 | Command::cargo_bin("juliaup") 83 | .unwrap() 84 | .arg("status") 85 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 86 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 87 | .assert() 88 | .success() 89 | .stdout( 90 | predicate::str::contains("\n") 91 | .count(3) 92 | .and(predicate::str::contains("julic").not()) 93 | .and(predicate::str::contains("julib").not()), 94 | ); 95 | } 96 | -------------------------------------------------------------------------------- /tests/command_initial_setup_from_launcher_test.rs: -------------------------------------------------------------------------------- 1 | use assert_cmd::Command; 2 | use assert_fs::prelude::*; 3 | use predicates::prelude::*; 4 | use std::path::Path; 5 | 6 | #[test] 7 | fn command_initial_setup() { 8 | let depot_dir = assert_fs::TempDir::new().unwrap(); 9 | 10 | depot_dir 11 | .child(Path::new("juliaup")) 12 | .assert(predicate::path::missing()); 13 | 14 | Command::cargo_bin("juliaup") 15 | .unwrap() 16 | .arg("46029ef5-0b73-4a71-bff3-d0d05de42aac") 17 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 18 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 19 | .assert() 20 | .success() 21 | .stdout(predicate::str::is_empty()) 22 | .stderr( 23 | predicate::str::starts_with("Checking for new Julia versions\nInstalling Julia 1.") 24 | .and( 25 | predicate::str::contains("apple.darwin14") 26 | .not() 27 | .or( 28 | predicate::str::contains("Checking standard library notarization") 29 | .and(predicate::str::ends_with("done.\n")), 30 | ), 31 | ), 32 | ); 33 | 34 | depot_dir 35 | .child(Path::new("juliaup").join("juliaup.json")) 36 | .assert(predicate::path::exists()); 37 | } 38 | -------------------------------------------------------------------------------- /tests/command_list_test.rs: -------------------------------------------------------------------------------- 1 | use assert_cmd::Command; 2 | use predicates::prelude::*; 3 | 4 | #[test] 5 | fn command_list() { 6 | let depot_dir = tempfile::Builder::new() 7 | .prefix("juliauptest") 8 | .tempdir() 9 | .unwrap(); 10 | 11 | Command::cargo_bin("juliaup") 12 | .unwrap() 13 | .arg("list") 14 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 15 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 16 | .assert() 17 | .success() 18 | .stdout(predicate::str::starts_with(" Channel").and(predicate::str::contains("release"))) 19 | .stdout(predicate::str::contains("nightly")) 20 | .stdout(predicate::str::contains("x.y-nightly")) 21 | .stdout(predicate::str::contains("pr{number}")); 22 | 23 | Command::cargo_bin("juliaup") 24 | .unwrap() 25 | .arg("ls") 26 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 27 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 28 | .assert() 29 | .success() 30 | .stdout(predicate::str::starts_with(" Channel").and(predicate::str::contains("release"))); 31 | } 32 | -------------------------------------------------------------------------------- /tests/command_remove.rs: -------------------------------------------------------------------------------- 1 | use assert_cmd::Command; 2 | use predicates::boolean::PredicateBooleanExt; 3 | 4 | #[test] 5 | fn command_remove() { 6 | let depot_dir = tempfile::Builder::new() 7 | .prefix("juliauptest") 8 | .tempdir() 9 | .unwrap(); 10 | 11 | Command::cargo_bin("juliaup") 12 | .unwrap() 13 | .arg("status") 14 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 15 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 16 | .assert() 17 | .success() 18 | .stdout(predicates::str::contains("1.6.4").not()); 19 | 20 | Command::cargo_bin("juliaup") 21 | .unwrap() 22 | .arg("add") 23 | .arg("1.6.4") 24 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 25 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 26 | .assert() 27 | .success() 28 | .stdout(""); 29 | 30 | Command::cargo_bin("juliaup") 31 | .unwrap() 32 | .arg("status") 33 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 34 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 35 | .assert() 36 | .success() 37 | .stdout(predicates::str::contains("1.6.4")); 38 | 39 | Command::cargo_bin("juliaup") 40 | .unwrap() 41 | .arg("add") 42 | .arg("release") 43 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 44 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 45 | .assert() 46 | .success() 47 | .stdout(""); 48 | 49 | Command::cargo_bin("juliaup") 50 | .unwrap() 51 | .arg("status") 52 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 53 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 54 | .assert() 55 | .success() 56 | .stdout(predicates::str::contains("1.6.4").and(predicates::str::contains("release"))); 57 | 58 | Command::cargo_bin("juliaup") 59 | .unwrap() 60 | .arg("remove") 61 | .arg("release") 62 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 63 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 64 | .assert() 65 | .success() 66 | .stdout(""); 67 | 68 | Command::cargo_bin("juliaup") 69 | .unwrap() 70 | .arg("status") 71 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 72 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 73 | .assert() 74 | .success() 75 | .stdout(predicates::str::contains("1.6.4").and(predicates::str::contains("release").not())); 76 | 77 | Command::cargo_bin("juliaup") 78 | .unwrap() 79 | .arg("add") 80 | .arg("nightly") 81 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 82 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 83 | .assert() 84 | .success() 85 | .stdout(""); 86 | 87 | Command::cargo_bin("juliaup") 88 | .unwrap() 89 | .arg("status") 90 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 91 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 92 | .assert() 93 | .success() 94 | .stdout(predicates::str::contains("1.6.4").and(predicates::str::contains("-DEV"))); 95 | 96 | Command::cargo_bin("juliaup") 97 | .unwrap() 98 | .arg("remove") 99 | .arg("nightly") 100 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 101 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 102 | .assert() 103 | .success() 104 | .stdout(""); 105 | 106 | Command::cargo_bin("juliaup") 107 | .unwrap() 108 | .arg("status") 109 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 110 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 111 | .assert() 112 | .success() 113 | .stdout(predicates::str::contains("1.6.4").and(predicates::str::contains("-DEV").not())); 114 | } 115 | -------------------------------------------------------------------------------- /tests/command_status_test.rs: -------------------------------------------------------------------------------- 1 | use assert_cmd::Command; 2 | 3 | #[test] 4 | fn command_status() { 5 | let depot_dir = tempfile::Builder::new() 6 | .prefix("juliauptest") 7 | .tempdir() 8 | .unwrap(); 9 | 10 | Command::cargo_bin("juliaup") 11 | .unwrap() 12 | .arg("status") 13 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 14 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 15 | .assert() 16 | .success() 17 | .stdout(" Default Channel Version Update \n-----------------------------------\n"); 18 | 19 | Command::cargo_bin("juliaup") 20 | .unwrap() 21 | .arg("st") 22 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 23 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 24 | .assert() 25 | .success() 26 | .stdout(" Default Channel Version Update \n-----------------------------------\n"); 27 | } 28 | -------------------------------------------------------------------------------- /tests/command_update.rs: -------------------------------------------------------------------------------- 1 | use assert_cmd::Command; 2 | 3 | #[test] 4 | fn command_update() { 5 | let depot_dir = tempfile::Builder::new() 6 | .prefix("juliauptest") 7 | .tempdir() 8 | .unwrap(); 9 | 10 | Command::cargo_bin("juliaup") 11 | .unwrap() 12 | .arg("update") 13 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 14 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 15 | .assert() 16 | .success() 17 | .stdout(""); 18 | 19 | Command::cargo_bin("juliaup") 20 | .unwrap() 21 | .arg("up") 22 | .env("JULIA_DEPOT_PATH", depot_dir.path()) 23 | .env("JULIAUP_DEPOT_PATH", depot_dir.path()) 24 | .assert() 25 | .success() 26 | .stdout(""); 27 | } 28 | --------------------------------------------------------------------------------