diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 9cfe71e038d70f58d7f3e340916d723e2dd3ede7..a21699c563837b0457f68f6c70c4bf9a948bf32f 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -15,6 +15,9 @@ variables:
   # Bump to invalidate GitLab CI cache.
   CACHE_REV: 0
 
+  GIT_SUBMODULE_STRATEGY: recursive
+
+
 ############################################################
 # CI Step
 ############################################################
diff --git a/.gitlab/script/ghcup_cross.sh b/.gitlab/script/ghcup_cross.sh
index 0b779f2d20eda6f97a3f522d2ad2a69293bea5b9..996fa7d818560c47f11102a5dc752e1c2ae5b40e 100755
--- a/.gitlab/script/ghcup_cross.sh
+++ b/.gitlab/script/ghcup_cross.sh
@@ -5,8 +5,6 @@ set -eux
 . "$( cd "$(dirname "$0")" ; pwd -P )/../ghcup_env"
 
 mkdir -p "$CI_PROJECT_DIR"/.local/bin
-mkdir -p data/
-git clone https://github.com/haskell/ghcup-metadata.git data/metadata
 
 CI_PROJECT_DIR=$(pwd)
 
diff --git a/.gitlab/script/ghcup_git.sh b/.gitlab/script/ghcup_git.sh
index 6199a919d088cc3a87f57ad3fc8481e31c8bba62..38c4c282bb70044cd08dabd8949b651f9b6db5a6 100755
--- a/.gitlab/script/ghcup_git.sh
+++ b/.gitlab/script/ghcup_git.sh
@@ -5,8 +5,6 @@ set -eux
 . "$( cd "$(dirname "$0")" ; pwd -P )/../ghcup_env"
 
 mkdir -p "$CI_PROJECT_DIR"/.local/bin
-mkdir -p data/
-git clone https://github.com/haskell/ghcup-metadata.git data/metadata
 
 CI_PROJECT_DIR=$(pwd)
 
diff --git a/.gitlab/script/ghcup_hls.sh b/.gitlab/script/ghcup_hls.sh
index ce7a57172a37215e87f38ca482ff688fb1b60a36..6f323ec3c66a1187462f2ee1514f9fb2276eb067 100755
--- a/.gitlab/script/ghcup_hls.sh
+++ b/.gitlab/script/ghcup_hls.sh
@@ -5,8 +5,6 @@ set -eux
 . "$( cd "$(dirname "$0")" ; pwd -P )/../ghcup_env"
 
 mkdir -p "$CI_PROJECT_DIR"/.local/bin
-mkdir -p data/
-git clone https://github.com/haskell/ghcup-metadata.git data/metadata
 
 CI_PROJECT_DIR=$(pwd)
 
diff --git a/.gitmodules b/.gitmodules
index ea15d10f39f3a8d2819773f718d8681fb83f9e28..e304260a4a1efda8eae63a45656666b328d33506 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,4 +1,4 @@
 [submodule "data/metadata"]
 	path = data/metadata
 	url = https://github.com/haskell/ghcup-metadata.git
-	branch = ghcup-0.1.17.5
+	branch = master
diff --git a/cabal.ghc8107.project b/cabal.ghc8107.project
index 62a515bdd8f71658c845dc5df25738ad3ae57ec1..0d8d35213d90fe6b24325515b796628cf12cf840 100644
--- a/cabal.ghc8107.project
+++ b/cabal.ghc8107.project
@@ -8,6 +8,11 @@ package ghcup
     tests: True
     flags: +tui
 
+source-repository-package
+  type: git
+  location: https://github.com/bgamari/terminal-size.git
+  tag: 34ea816bd63f75f800eedac12c6908c6f3736036
+
 constraints: http-io-streams -brotli,
              any.Cabal ==3.6.2.0,
              any.aeson >= 2.0.1.0
diff --git a/cabal.ghc8107.project.freeze b/cabal.ghc8107.project.freeze
index ed1d0040a414d332363e2b710c0ecae13301e8b8..8e2580aafa93587e8a63b3906c76733c754a0191 100644
--- a/cabal.ghc8107.project.freeze
+++ b/cabal.ghc8107.project.freeze
@@ -10,13 +10,12 @@ constraints: any.Cabal ==3.6.2.0,
              any.StateVar ==1.2.2,
              any.abstract-deque ==0.3,
              abstract-deque -usecas,
-             any.aeson ==2.0.1.0,
+             any.aeson ==2.0.2.0,
              aeson -bytestring-builder -cffi +ordered-keymap,
              any.aeson-pretty ==0.8.9,
              aeson-pretty +lib-only,
-             any.alex ==3.2.6,
-             alex +small_base,
-             any.ansi-terminal ==0.11,
+             any.alex ==3.2.7.1,
+             any.ansi-terminal ==0.11.1,
              ansi-terminal -example,
              any.ansi-wl-pprint ==0.6.9,
              ansi-wl-pprint -example,
@@ -53,7 +52,7 @@ constraints: any.Cabal ==3.6.2.0,
              any.chs-cabal ==0.1.1.1,
              any.chs-deps ==0.1.0.0,
              chs-deps -cross,
-             any.clock ==0.8.2,
+             any.clock ==0.8.3,
              clock -llvm,
              any.colour ==2.3.6,
              any.comonad ==5.0.8,
@@ -67,7 +66,7 @@ constraints: any.Cabal ==3.6.2.0,
              contravariant +semigroups +statevar +tagged,
              any.cpphs ==1.20.9.1,
              cpphs -old-locale,
-             any.cryptohash-sha1 ==0.11.100.1,
+             any.cryptohash-sha1 ==0.11.101.0,
              any.cryptohash-sha256 ==0.11.102.1,
              cryptohash-sha256 -exe +use-cbits,
              any.data-clist ==0.1.2.3,
@@ -88,8 +87,8 @@ constraints: any.Cabal ==3.6.2.0,
              any.ghc-byteorder ==4.11.0.0.10,
              any.ghc-prim ==0.6.1,
              any.happy ==1.20.0,
-             any.hashable ==1.3.5.0,
-             hashable +integer-gmp -random-initial-seed,
+             any.hashable ==1.4.0.2,
+             hashable +containers +integer-gmp -random-initial-seed,
              any.haskus-utils-data ==1.4,
              any.haskus-utils-types ==1.5.1,
              any.haskus-utils-variant ==3.2.1,
@@ -113,9 +112,9 @@ constraints: any.Cabal ==3.6.2.0,
              io-streams +network -nointeractivetests +zlib,
              any.language-c ==0.9.0.1,
              language-c -allwarnings +iecfpextension +usebytestrings,
-             any.libarchive ==3.0.3.1,
-             libarchive -cross -low-memory -system-libarchive,
-             any.libyaml-streamly ==0.2.0,
+             any.libarchive ==3.0.3.2,
+             libarchive -cross -low-memory +no-exe -system-libarchive,
+             any.libyaml-streamly ==0.2.1,
              libyaml-streamly -no-unicode -system-libyaml,
              any.lockfree-queue ==0.2.3.1,
              any.lzma-static ==5.2.5.4,
@@ -126,7 +125,7 @@ constraints: any.Cabal ==3.6.2.0,
              any.microlens-th ==0.4.3.10,
              any.monad-control ==1.0.3.1,
              any.mtl ==2.2.2,
-             any.network ==3.1.2.5,
+             any.network ==3.1.2.7,
              network -devel,
              any.network-uri ==2.6.4.1,
              any.openssl-streams ==1.2.3.0,
@@ -154,7 +153,7 @@ constraints: any.Cabal ==3.6.2.0,
              any.random ==1.2.1,
              any.recursion-schemes ==5.2.2.2,
              recursion-schemes +template-haskell,
-             any.regex-base ==0.94.0.1,
+             any.regex-base ==0.94.0.2,
              any.regex-posix ==0.96.0.1,
              regex-posix -_regex-posix-clib,
              any.resourcet ==1.2.4.3,
@@ -167,15 +166,15 @@ constraints: any.Cabal ==3.6.2.0,
              scientific -bytestring-builder -integer-simple,
              any.semialign ==1.2.0.1,
              semialign +semigroupoids,
-             any.semigroupoids ==5.3.6,
+             any.semigroupoids ==5.3.7,
              semigroupoids +comonad +containers +contravariant +distributive +tagged +unordered-containers,
              any.setenv ==0.1.1.3,
              any.split ==0.2.3.4,
              any.splitmix ==0.1.0.4,
              splitmix -optimised-mixer,
              any.stm ==2.5.0.1,
-             any.streamly ==0.8.0,
-             streamly -debug -dev -fusion-plugin -has-llvm -inspection -no-fusion +opt -streamk -use-c-malloc,
+             any.streamly ==0.8.1.1,
+             streamly -debug -dev -fusion-plugin -has-llvm -inspection -limit-build-mem -no-fusion +opt -streamk -use-c-malloc,
              any.strict ==0.4.0.1,
              strict +assoc,
              any.strict-base ==0.4.0.0,
@@ -187,12 +186,14 @@ constraints: any.Cabal ==3.6.2.0,
              any.terminal-size ==0.3.2.1,
              any.terminfo ==0.4.1.4,
              any.text ==1.2.4.1,
+             any.text-short ==0.1.5,
+             text-short -asserts,
              any.text-zipper ==0.11,
              any.tf-random ==0.5,
              any.th-abstraction ==0.4.3.0,
              any.th-compat ==0.1.3,
              any.th-lift ==0.8.2,
-             any.th-lift-instances ==0.1.18,
+             any.th-lift-instances ==0.1.19,
              any.these ==1.1.1.1,
              these +assoc,
              any.time ==1.9.3,
@@ -203,12 +204,14 @@ constraints: any.Cabal ==3.6.2.0,
              transformers-base +orphaninstances,
              any.transformers-compat ==0.7.1,
              transformers-compat -five +five-three -four +generic-deriving +mtl -three -two,
+             any.unicode-data ==0.3.0,
+             unicode-data -ucd2haskell,
              any.unix ==2.7.2.2,
              any.unix-bytestring ==0.3.7.6,
-             any.unix-compat ==0.5.3,
+             any.unix-compat ==0.5.4,
              unix-compat -old-time,
              any.unliftio-core ==0.2.0.1,
-             any.unordered-containers ==0.2.15.0,
+             any.unordered-containers ==0.2.16.0,
              unordered-containers -debug,
              any.uri-bytestring ==0.3.3.1,
              uri-bytestring -lib-werror,
@@ -216,15 +219,15 @@ constraints: any.Cabal ==3.6.2.0,
              any.uuid-types ==1.0.5,
              any.vector ==0.12.3.1,
              vector +boundschecks -internalchecks -unsafechecks -wall,
-             any.versions ==5.0.0,
+             any.versions ==5.0.2,
              any.vty ==5.33,
              any.witherable ==0.4.2,
              any.word-wrap ==0.5,
              any.word8 ==0.1.3,
              any.xor ==0.0.1.0,
-             any.yaml-streamly ==0.12.0,
+             any.yaml-streamly ==0.12.1,
              yaml-streamly +no-examples +no-exe,
              any.zlib ==0.6.2.3,
              zlib -bundled-c-zlib -non-blocking-ffi -pkg-config,
              any.zlib-bindings ==0.1.1.5
-index-state: hackage.haskell.org 2021-11-12T11:11:19Z
+index-state: hackage.haskell.org 2022-02-15T12:16:42Z
diff --git a/cabal.ghc901.project b/cabal.ghc902.project
similarity index 73%
rename from cabal.ghc901.project
rename to cabal.ghc902.project
index 386eb267c0a90e9f3e71b586542927ca65eeeb9a..e9c38398dbf2cadf819a65bbb7c86b939f53729a 100644
--- a/cabal.ghc901.project
+++ b/cabal.ghc902.project
@@ -8,6 +8,11 @@ package ghcup
     tests: True
     flags: +tui
 
+source-repository-package
+  type: git
+  location: https://github.com/bgamari/terminal-size.git
+  tag: 34ea816bd63f75f800eedac12c6908c6f3736036
+
 constraints: http-io-streams -brotli,
              any.Cabal ==3.6.2.0,
              any.aeson >= 2.0.1.0
@@ -26,4 +31,4 @@ package aeson
 
 allow-newer: base, ghc-prim, template-haskell, language-c
 
-with-compiler: ghc-9.0.1
+with-compiler: ghc-9.0.2
diff --git a/cabal.ghc901.project.freeze b/cabal.ghc902.project.freeze
similarity index 85%
rename from cabal.ghc901.project.freeze
rename to cabal.ghc902.project.freeze
index 64af36845a8786cc57983a3f9e5d3404ca395d06..209064c79a2af24089e983f589b29cf38608e5d7 100644
--- a/cabal.ghc901.project.freeze
+++ b/cabal.ghc902.project.freeze
@@ -10,13 +10,12 @@ constraints: any.Cabal ==3.6.2.0,
              any.StateVar ==1.2.2,
              any.abstract-deque ==0.3,
              abstract-deque -usecas,
-             any.aeson ==2.0.1.0,
+             any.aeson ==2.0.2.0,
              aeson -bytestring-builder -cffi +ordered-keymap,
              any.aeson-pretty ==0.8.9,
              aeson-pretty +lib-only,
-             any.alex ==3.2.6,
-             alex +small_base,
-             any.ansi-terminal ==0.11,
+             any.alex ==3.2.7.1,
+             any.ansi-terminal ==0.11.1,
              ansi-terminal -example,
              any.ansi-wl-pprint ==0.6.9,
              ansi-wl-pprint -example,
@@ -28,7 +27,7 @@ constraints: any.Cabal ==3.6.2.0,
              atomic-primops -debug,
              any.attoparsec ==0.13.2.5,
              attoparsec -developer,
-             any.base ==4.15.0.0,
+             any.base ==4.15.1.0,
              any.base-compat ==0.12.1,
              any.base-compat-batteries ==0.12.1,
              any.base-orphans ==0.8.6,
@@ -53,7 +52,7 @@ constraints: any.Cabal ==3.6.2.0,
              any.chs-cabal ==0.1.1.1,
              any.chs-deps ==0.1.0.0,
              chs-deps -cross,
-             any.clock ==0.8.2,
+             any.clock ==0.8.3,
              clock -llvm,
              any.colour ==2.3.6,
              any.comonad ==5.0.8,
@@ -67,13 +66,13 @@ constraints: any.Cabal ==3.6.2.0,
              contravariant +semigroups +statevar +tagged,
              any.cpphs ==1.20.9.1,
              cpphs -old-locale,
-             any.cryptohash-sha1 ==0.11.100.1,
+             any.cryptohash-sha1 ==0.11.101.0,
              any.cryptohash-sha256 ==0.11.102.1,
              cryptohash-sha256 -exe +use-cbits,
              any.data-clist ==0.1.2.3,
              any.data-fix ==0.3.2,
              any.deepseq ==1.4.5.0,
-             any.directory ==1.3.6.1,
+             any.directory ==1.3.6.2,
              any.disk-free-space ==0.1.0.1,
              any.distributive ==0.6.2.1,
              distributive +semigroups +tagged,
@@ -84,13 +83,13 @@ constraints: any.Cabal ==3.6.2.0,
              any.free ==5.1.7,
              any.fusion-plugin-types ==0.1.0,
              any.generic-arbitrary ==0.1.0,
-             any.ghc-bignum ==1.0,
-             any.ghc-boot-th ==9.0.1,
+             any.ghc-bignum ==1.1,
+             any.ghc-boot-th ==9.0.2,
              any.ghc-byteorder ==4.11.0.0.10,
              any.ghc-prim ==0.7.0,
              any.happy ==1.20.0,
-             any.hashable ==1.3.5.0,
-             hashable +integer-gmp -random-initial-seed,
+             any.hashable ==1.4.0.2,
+             hashable +containers +integer-gmp -random-initial-seed,
              any.haskus-utils-data ==1.4,
              any.haskus-utils-types ==1.5.1,
              any.haskus-utils-variant ==3.2.1,
@@ -113,9 +112,9 @@ constraints: any.Cabal ==3.6.2.0,
              io-streams +network -nointeractivetests +zlib,
              any.language-c ==0.9.0.1,
              language-c -allwarnings +iecfpextension +usebytestrings,
-             any.libarchive ==3.0.3.1,
-             libarchive -cross -low-memory -system-libarchive,
-             any.libyaml-streamly ==0.2.0,
+             any.libarchive ==3.0.3.2,
+             libarchive -cross -low-memory +no-exe -system-libarchive,
+             any.libyaml-streamly ==0.2.1,
              libyaml-streamly -no-unicode -system-libyaml,
              any.lockfree-queue ==0.2.3.1,
              any.lzma-static ==5.2.5.4,
@@ -126,7 +125,7 @@ constraints: any.Cabal ==3.6.2.0,
              any.microlens-th ==0.4.3.10,
              any.monad-control ==1.0.3.1,
              any.mtl ==2.2.2,
-             any.network ==3.1.2.5,
+             any.network ==3.1.2.7,
              network -devel,
              any.network-uri ==2.6.4.1,
              any.openssl-streams ==1.2.3.0,
@@ -147,35 +146,35 @@ constraints: any.Cabal ==3.6.2.0,
              any.pretty ==1.1.3.6,
              any.pretty-terminal ==0.1.0.0,
              any.primitive ==0.7.3.0,
-             any.process ==1.6.11.0,
+             any.process ==1.6.13.2,
              any.profunctors ==5.6.2,
              any.quickcheck-arbitrary-adt ==0.3.1.0,
              any.quickcheck-io ==0.2.0,
              any.random ==1.2.1,
              any.recursion-schemes ==5.2.2.2,
              recursion-schemes +template-haskell,
-             any.regex-base ==0.94.0.1,
+             any.regex-base ==0.94.0.2,
              any.regex-posix ==0.96.0.1,
              regex-posix -_regex-posix-clib,
              any.resourcet ==1.2.4.3,
              any.retry ==0.8.1.2,
              retry -lib-werror,
-             any.rts ==1.0,
+             any.rts ==1.0.2,
              any.safe ==0.3.19,
              any.safe-exceptions ==0.1.7.2,
              any.scientific ==0.3.7.0,
              scientific -bytestring-builder -integer-simple,
              any.semialign ==1.2.0.1,
              semialign +semigroupoids,
-             any.semigroupoids ==5.3.6,
+             any.semigroupoids ==5.3.7,
              semigroupoids +comonad +containers +contravariant +distributive +tagged +unordered-containers,
              any.setenv ==0.1.1.3,
              any.split ==0.2.3.4,
              any.splitmix ==0.1.0.4,
              splitmix -optimised-mixer,
              any.stm ==2.5.0.0,
-             any.streamly ==0.8.0,
-             streamly -debug -dev -fusion-plugin -has-llvm -inspection -no-fusion +opt -streamk -use-c-malloc,
+             any.streamly ==0.8.1.1,
+             streamly -debug -dev -fusion-plugin -has-llvm -inspection -limit-build-mem -no-fusion +opt -streamk -use-c-malloc,
              any.strict ==0.4.0.1,
              strict +assoc,
              any.strict-base ==0.4.0.0,
@@ -185,14 +184,16 @@ constraints: any.Cabal ==3.6.2.0,
              any.temporary ==1.3,
              any.terminal-progress-bar ==0.4.1,
              any.terminal-size ==0.3.2.1,
-             any.terminfo ==0.4.1.4,
-             any.text ==1.2.4.1,
+             any.terminfo ==0.4.1.5,
+             any.text ==1.2.5.0,
+             any.text-short ==0.1.5,
+             text-short -asserts,
              any.text-zipper ==0.11,
              any.tf-random ==0.5,
              any.th-abstraction ==0.4.3.0,
              any.th-compat ==0.1.3,
              any.th-lift ==0.8.2,
-             any.th-lift-instances ==0.1.18,
+             any.th-lift-instances ==0.1.19,
              any.these ==1.1.1.1,
              these +assoc,
              any.time ==1.9.3,
@@ -203,12 +204,14 @@ constraints: any.Cabal ==3.6.2.0,
              transformers-base +orphaninstances,
              any.transformers-compat ==0.7.1,
              transformers-compat -five +five-three -four +generic-deriving +mtl -three -two,
+             any.unicode-data ==0.3.0,
+             unicode-data -ucd2haskell,
              any.unix ==2.7.2.2,
              any.unix-bytestring ==0.3.7.6,
-             any.unix-compat ==0.5.3,
+             any.unix-compat ==0.5.4,
              unix-compat -old-time,
              any.unliftio-core ==0.2.0.1,
-             any.unordered-containers ==0.2.15.0,
+             any.unordered-containers ==0.2.16.0,
              unordered-containers -debug,
              any.uri-bytestring ==0.3.3.1,
              uri-bytestring -lib-werror,
@@ -216,15 +219,15 @@ constraints: any.Cabal ==3.6.2.0,
              any.uuid-types ==1.0.5,
              any.vector ==0.12.3.1,
              vector +boundschecks -internalchecks -unsafechecks -wall,
-             any.versions ==5.0.0,
+             any.versions ==5.0.2,
              any.vty ==5.33,
              any.witherable ==0.4.2,
              any.word-wrap ==0.5,
              any.word8 ==0.1.3,
              any.xor ==0.0.1.0,
-             any.yaml-streamly ==0.12.0,
+             any.yaml-streamly ==0.12.1,
              yaml-streamly +no-examples +no-exe,
              any.zlib ==0.6.2.3,
              zlib -bundled-c-zlib -non-blocking-ffi -pkg-config,
              any.zlib-bindings ==0.1.1.5
-index-state: hackage.haskell.org 2021-11-12T11:11:19Z
+index-state: hackage.haskell.org 2022-02-15T12:16:42Z
diff --git a/data/metadata b/data/metadata
index 80b61ee4b7319bd8591f2510f44404cd821eff4c..b1d09952210376cabc38c55bcf52cb251f749749 160000
--- a/data/metadata
+++ b/data/metadata
@@ -1 +1 @@
-Subproject commit 80b61ee4b7319bd8591f2510f44404cd821eff4c
+Subproject commit b1d09952210376cabc38c55bcf52cb251f749749
diff --git a/docs/dev.md b/docs/dev.md
index a63d6ff000d40bbe38f91ac097de7a3f0f32c41f..7fa00fba75e6f4bef437c919611ee565e301656e 100644
--- a/docs/dev.md
+++ b/docs/dev.md
@@ -94,21 +94,25 @@ Every subcommand now lives in its own module under [GHCup.OptParse.MyCommand](ht
 
 3. Add ChangeLog entry
 
-4. Commit and git push with tag. Wait for tests to succeed and release artifacts to build.
+4. If a new ghcup yaml version is needed, create one at [ghcup-metadata repo](https://github.com/haskell/ghcup-metadata) and push to a temporary release branch, then update the `data/metadata` submodule in ghcup-hs repo to that branch, so CI can pass
 
-5. Download release artifacts and upload them `downloads.haskell.org/~ghcup` along with checksum files (`sha256sum --tag * > SHA256SUMS && gpg --detach-sign -u <your-email> SHA256SUMS`)
+5. Commit and git push with tag. Wait for tests to succeed and release artifacts to build.
 
-6. Add ghcup release artifacts to ALL yaml files, see [ghcup-metadata repo](https://github.com/haskell/ghcup-metadata)
+6. Download release artifacts and upload them `downloads.haskell.org/~ghcup` along with checksum files (also check `scripts/releasing/pull_release_artifacts.sh` and `scripts/releasing/sftp-upload-artifacts.sh`)
 
-7. Upload the final `ghcup-<ver>.yaml` (and a detached GPG sig of it) to `webhost.haskell.org/ghcup/data/` (for yaml versions <= 0.0.6) as well as [https://github.com/haskell/ghcup-metadata](https://github.com/haskell/ghcup-metadata) (for all versions).
+7. Add ghcup release artifacts to ALL yaml files, see [ghcup-metadata repo](https://github.com/haskell/ghcup-metadata)
 
-8. Update version in `scripts/bootstrap/bootstrap-haskell` (`ghver` variable at the top of the script)
+8. Upload the final `ghcup-<ver>.yaml` (and a detached GPG sig of it) to `webhost.haskell.org/ghcup/data/` (for yaml versions <= 0.0.6) as well as [https://github.com/haskell/ghcup-metadata](https://github.com/haskell/ghcup-metadata) (for all versions).
 
-9. Upload `scripts/bootstrap/bootstrap-haskell` and `scripts/bootstrap/bootstrap-haskell.ps1` to `webhost.haskell.org/ghcup/sh/`
+9. Update version in `scripts/bootstrap/bootstrap-haskell` (`ghver` variable at the top of the script)
 
-10. Update the top-level ghcup symlinks at `downloads.haskell.org/~ghcup` (see `scripts/update-sftp.sh`)
+10. Upload `scripts/bootstrap/bootstrap-haskell` and `scripts/bootstrap/bootstrap-haskell.ps1` to `webhost.haskell.org/ghcup/sh/`
 
-11. Post on reddit/discourse/etc. and collect rewards
+11. Update the top-level ghcup symlinks at `downloads.haskell.org/~ghcup` (see `scripts/releasing/sftp-symlink-artifacts.sh`)
+
+12. Update the `data/metadata` submodule in ghcup-hs repo to master
+
+13. Post on reddit/discourse/etc. and collect rewards
 
 # Documentation
 
diff --git a/scripts/bootstrap/bootstrap-haskell b/scripts/bootstrap/bootstrap-haskell
index aae90d14c6b0c769dc60062800812b97a2497340..2b32175b1a192cbcc023c6cb7367a8cac149d0af 100755
--- a/scripts/bootstrap/bootstrap-haskell
+++ b/scripts/bootstrap/bootstrap-haskell
@@ -25,7 +25,7 @@
 
 plat="$(uname -s)"
 arch=$(uname -m)
-ghver="0.1.17.4"
+ghver="0.1.17.5"
 base_url="https://downloads.haskell.org/~ghcup"
 
 export GHCUP_SKIP_UPDATE_CHECK=yes
diff --git a/scripts/releasing/pull_release_artifacts.sh b/scripts/releasing/pull_release_artifacts.sh
new file mode 100755
index 0000000000000000000000000000000000000000..2e288128c2c0b8d2d0b5dc250d0eb13ed4686756
--- /dev/null
+++ b/scripts/releasing/pull_release_artifacts.sh
@@ -0,0 +1,49 @@
+
+set -eu
+
+tag=v$1
+ver=$1
+
+dest=$2
+gpg_user=$3
+
+mkdir -p "${dest}"
+
+cd "${dest}"
+
+base_url="https://gitlab.haskell.org/api/v4/projects/618/jobs/artifacts/${tag}/raw"
+
+curl -f -o "x86_64-apple-darwin-ghcup-${ver}" \
+	"${base_url}/out/x86_64-apple-darwin-ghcup-${ver}?job=release:darwin"
+
+curl -f -o "aarch64-apple-darwin-ghcup-${ver}" \
+	"${base_url}/out/aarch64-apple-darwin-ghcup-${ver}?job=release:darwin:aarch64"
+
+curl -f -o "x86_64-freebsd12-ghcup-${ver}" \
+	"${base_url}/out/x86_64-portbld-freebsd-ghcup-${ver}?job=release:freebsd12"
+
+curl -f -o "x86_64-freebsd13-ghcup-${ver}" \
+	"${base_url}/out/x86_64-portbld-freebsd-ghcup-${ver}?job=release:freebsd13"
+
+curl -f -o "i386-linux-ghcup-${ver}" \
+	"${base_url}/out/i386-linux-ghcup-${ver}?job=release:linux:32bit"
+
+curl -f -o "x86_64-linux-ghcup-${ver}" \
+	"${base_url}/out/x86_64-linux-ghcup-${ver}?job=release:linux:64bit"
+
+curl -f -o "aarch64-linux-ghcup-${ver}" \
+	"${base_url}/out/aarch64-linux-ghcup-${ver}?job=release:linux:aarch64"
+
+curl -f -o "armv7-linux-ghcup-${ver}" \
+	"${base_url}/out/armv7-linux-ghcup-${ver}?job=release:linux:armv7"
+
+curl -f -o "x86_64-mingw64-ghcup-${ver}.exe" \
+	"${base_url}/out/x86_64-mingw64-ghcup-${ver}.exe?job=release:windows"
+
+rm -f *.sig
+sha256sum *-ghcup-* > SHA256SUMS
+gpg --detach-sign -u ${gpg_user} SHA256SUMS
+for f in *-ghcup-* ; do gpg --detach-sign -u ${gpg_user} $f ; done
+
+
+
diff --git a/scripts/update-sftp.sh b/scripts/releasing/sftp-symlink-artifacts.sh
similarity index 95%
rename from scripts/update-sftp.sh
rename to scripts/releasing/sftp-symlink-artifacts.sh
index 7c145f3f723e1151e4ab63ac6caea7da3bc0ba92..fa06b054be40116a609dc21446fd7f0472195f23 100755
--- a/scripts/update-sftp.sh
+++ b/scripts/releasing/sftp-symlink-artifacts.sh
@@ -36,3 +36,4 @@ symlink ${ver}/x86_64-mingw64-ghcup-${ver}.exe x86_64-mingw64-ghcup.exe
 EOF
 
 curl -X PURGE https://downloads.haskell.org/~ghcup/
+curl -X PURGE https://downloads.haskell.org/ghcup/
diff --git a/scripts/releasing/sftp-upload-artifacts.sh b/scripts/releasing/sftp-upload-artifacts.sh
new file mode 100755
index 0000000000000000000000000000000000000000..effbc9dce8f9b7358c76512dacf5d306ad4a1f10
--- /dev/null
+++ b/scripts/releasing/sftp-upload-artifacts.sh
@@ -0,0 +1,47 @@
+#!/bin/bash
+
+url=$1
+ver=$2
+artifacts_dir=$3
+
+die() {
+    (>&2 printf "%s\\n" "$1")
+    exit 2
+}
+
+[ -z $url ] && die "no url set"
+[ -z $ver ] && die "no version set"
+[ -z "${artifacts_dir}" ] && die "artifacts_dir not set"
+[ -e "${artifacts_dir}" ] || die "artifacts_dir \"${artifacts_dir}\" does not exist"
+
+cd "${artifacts_dir}"
+
+sftp $url <<EOF
+cd ghcup
+
+mkdir ${ver}
+cd ${ver}
+put SHA256SUMS
+put SHA256SUMS.sig
+put aarch64-apple-darwin-ghcup-${ver}
+put aarch64-apple-darwin-ghcup-${ver}.sig
+put aarch64-linux-ghcup-${ver}
+put aarch64-linux-ghcup-${ver}.sig
+put armv7-linux-ghcup-${ver}
+put armv7-linux-ghcup-${ver}.sig
+put i386-linux-ghcup-${ver}
+put i386-linux-ghcup-${ver}.sig
+put x86_64-apple-darwin-ghcup-${ver}
+put x86_64-apple-darwin-ghcup-${ver}.sig
+put x86_64-freebsd12-ghcup-${ver}
+put x86_64-freebsd12-ghcup-${ver}.sig
+put x86_64-freebsd13-ghcup-${ver}
+put x86_64-freebsd13-ghcup-${ver}.sig
+put x86_64-linux-ghcup-${ver}
+put x86_64-linux-ghcup-${ver}.sig
+put x86_64-mingw64-ghcup-${ver}.exe
+put x86_64-mingw64-ghcup-${ver}.exe.sig
+EOF
+
+curl -X PURGE https://downloads.haskell.org/~ghcup/${ver}/
+curl -X PURGE https://downloads.haskell.org/ghcup/${ver}/