diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 03acd93ff90319e35369cf6ccbf157c82c32b702..d8235371b320fa68511edade29d1a0baae45e2fa 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -21,38 +21,7 @@ jobs:
     steps:
       - uses: actions/checkout@v2
       - name: Setup Rust toolchain
-        run: |
-          set -ex
-          rustup set profile minimal
-          rustup update --force nightly
-          rustup default nightly
-      - name: Install target
-        run: |
-          set -ex
-          rustup target add ${{ matrix.target }}
-      - name: Query Rust and Cargo versions
-        run: |
-          set -ex
-          rustc -Vv
-          cargo -V
-          rustup -Vv
-          rustup show
-          which rustc
-          which cargo
-          which rustup
-      - name: Generate lockfile
-        run: |
-          set -ex
-          N=5
-          n=0
-          until [ $n -ge $N ]
-          do
-            if cargo generate-lockfile ; then
-              break
-            fi
-            n=$((n+1))
-            sleep 1
-          done
+        run: TARGET=${{ matrix.target }} sh ./ci/install-rust.sh
       - name: Execute run-docker.sh
         run: LIBC_CI=1 sh ./ci/run-docker.sh ${{ matrix.target }}
 
@@ -68,44 +37,15 @@ jobs:
     steps:
       - uses: actions/checkout@v2
       - name: Setup Rust toolchain
-        run: |
-          set -ex
-          rustup set profile minimal
-          rustup update --force nightly
-          rustup default nightly
-      - name: Install target
-        run: |
-          set -ex
-          rustup target add ${{ matrix.target }}
-      - name: Query Rust and Cargo versions
-        run: |
-          set -ex
-          rustc -Vv
-          cargo -V
-          rustup -Vv
-          rustup show
-          which rustc
-          which cargo
-          which rustup
-      - name: Generate lockfile
-        run: |
-          set -ex
-          N=5
-          n=0
-          until [ $n -ge $N ]
-          do
-            if cargo generate-lockfile ; then
-              break
-            fi
-            n=$((n+1))
-            sleep 1
-          done
+        run: TARGET=${{ matrix.target }} sh ./ci/install-rust.sh
       - name: Execute run.sh
         run: LIBC_CI=1 sh ./ci/run.sh ${{ matrix.target }}
 
   windows:
     name: Windows
     runs-on: windows-2019
+    env:
+      OS: windows
     strategy:
       fail-fast: false
       matrix:
@@ -125,60 +65,7 @@ jobs:
     steps:
       - uses: actions/checkout@v2
       - name: Setup Rust toolchain
-        run: |
-          rustup set profile minimal
-          rustup update --force --no-self-update nightly-${{ matrix.target }}
-          rustup default nightly-${{ matrix.target }}
-      - name: Install target
-        run: rustup target add ${{ matrix.target }}
-      - name: Install MinGW32
-        run: |
-          set -ex
-          if [[ ${ARCH_BITS} = "i686" ]]; then
-            choco install mingw --x86 --force
-          fi
-        shell: bash
-      - name: Find GCC libraries
-        run: |
-          set -ex
-          gcc -print-search-dirs
-          /usr/bin/find "C:\ProgramData\Chocolatey" -name "crt2*"
-          /usr/bin/find "C:\ProgramData\Chocolatey" -name "dllcrt2*"
-          /usr/bin/find "C:\ProgramData\Chocolatey" -name "libmsvcrt*"
-        shell: bash
-      - name: Fix MinGW
-        run: |
-          set -ex
-          if [[ -n ${ARCH_BITS} ]]; then
-            for i in crt2.o dllcrt2.o libmingwex.a libmsvcrt.a ; do
-              cp -f "/C/ProgramData/Chocolatey/lib/mingw/tools/install/mingw${ARCH_BITS}/${ARCH}-w64-mingw32/lib/$i" "`rustc --print sysroot`/lib/rustlib/${TARGET}/lib"
-            done
-          fi
-        shell: bash
-      - name: Query Rust and Cargo versions
-        run: |
-          set -ex
-          rustc -Vv
-          cargo -V
-          rustup -Vv
-          rustup show
-          which rustc
-          which cargo
-          which rustup
-        shell: bash
-      - name: Generate lockfile
-        run: |
-          set -ex
-          N=5
-          n=0
-          until [ $n -ge $N ]
-          do
-            if cargo generate-lockfile ; then
-              break
-            fi
-            n=$((n+1))
-            sleep 1
-          done
+        run: TARGET=${{ matrix.target }} sh ./ci/install-rust.sh
         shell: bash
       - name: Execute run.sh
         run: LIBC_CI=1 sh ./ci/run.sh ${{ matrix.target }}
@@ -192,34 +79,7 @@ jobs:
     steps:
       - uses: actions/checkout@v2
       - name: Setup Rust toolchain
-        run: |
-          set -ex
-          rustup set profile minimal
-          rustup update --force nightly
-          rustup default nightly
-      - name: Query Rust and Cargo versions
-        run: |
-          set -ex
-          rustc -Vv
-          cargo -V
-          rustup -Vv
-          rustup show
-          which rustc
-          which cargo
-          which rustup
-      - name: Generate lockfile
-        run: |
-          set -ex
-          N=5
-          n=0
-          until [ $n -ge $N ]
-          do
-            if cargo generate-lockfile ; then
-              break
-            fi
-            n=$((n+1))
-            sleep 1
-          done
+        run: sh ./ci/install-rust.sh
       - name: Check style
         run: sh ci/style.sh
       - name: Generate documentation
@@ -268,38 +128,7 @@ jobs:
     steps:
       - uses: actions/checkout@v2
       - name: Setup Rust toolchain
-        run: |
-          set -ex
-          rustup set profile minimal
-          rustup update --force nightly
-          rustup default nightly
-      - name: Install target
-        run: |
-          set -ex
-          rustup target add ${{ matrix.target }}
-      - name: Query Rust and Cargo versions
-        run: |
-          set -ex
-          rustc -Vv
-          cargo -V
-          rustup -Vv
-          rustup show
-          which rustc
-          which cargo
-          which rustup
-      - name: Generate lockfile
-        run: |
-          set -ex
-          N=5
-          n=0
-          until [ $n -ge $N ]
-          do
-            if cargo generate-lockfile ; then
-              break
-            fi
-            n=$((n+1))
-            sleep 1
-          done
+        run: TARGET=${{ matrix.target }} sh ./ci/install-rust.sh
       - name: Execute run-docker.sh
         run: LIBC_CI=1 sh ./ci/run-docker.sh ${{ matrix.target }}
 
@@ -313,34 +142,7 @@ jobs:
     steps:
       - uses: actions/checkout@v2
       - name: Setup Rust toolchain
-        run: |
-          set -ex
-          rustup set profile minimal
-          rustup update --force nightly
-          rustup default nightly
-      - name: Query Rust and Cargo versions
-        run: |
-          set -ex
-          rustc -Vv
-          cargo -V
-          rustup -Vv
-          rustup show
-          which rustc
-          which cargo
-          which rustup
-      - name: Generate lockfile
-        run: |
-          set -ex
-          N=5
-          n=0
-          until [ $n -ge $N ]
-          do
-            if cargo generate-lockfile ; then
-              break
-            fi
-            n=$((n+1))
-            sleep 1
-          done
+        run: sh ./ci/install-rust.sh
       - name: Execute run-docker.sh
         run: LIBC_CI=1 sh ./ci/run-docker.sh switch
 
@@ -367,34 +169,7 @@ jobs:
     steps:
       - uses: actions/checkout@v2
       - name: Setup Rust toolchain
-        run: |
-          set -ex
-          rustup set profile minimal
-          rustup update --force ${{ matrix.toolchain }}
-          rustup default ${{ matrix.toolchain }}
-      - name: Query Rust and Cargo versions
-        run: |
-          set -ex
-          rustc -Vv
-          cargo -V
-          rustup -Vv
-          rustup show
-          which rustc
-          which cargo
-          which rustup
-      - name: Generate lockfile
-        run: |
-          set -ex
-          N=5
-          n=0
-          until [ $n -ge $N ]
-          do
-            if cargo generate-lockfile ; then
-              break
-            fi
-            n=$((n+1))
-            sleep 1
-          done
+        run: TOOLCHAIN=${{ matrix.toolchain }} sh ./ci/install-rust.sh
       - name: Execute build.sh
         run: LIBC_CI=1 TOOLCHAIN=${{ matrix.toolchain }} sh ./ci/build.sh
 
@@ -421,34 +196,7 @@ jobs:
     steps:
       - uses: actions/checkout@v2
       - name: Setup Rust toolchain
-        run: |
-          set -ex
-          rustup set profile minimal
-          rustup update --force ${{ matrix.toolchain }}
-          rustup default ${{ matrix.toolchain }}
-      - name: Query Rust and Cargo versions
-        run: |
-          set -ex
-          rustc -Vv
-          cargo -V
-          rustup -Vv
-          rustup show
-          which rustc
-          which cargo
-          which rustup
-      - name: Generate lockfile
-        run: |
-          set -ex
-          N=5
-          n=0
-          until [ $n -ge $N ]
-          do
-            if cargo generate-lockfile ; then
-              break
-            fi
-            n=$((n+1))
-            sleep 1
-          done
+        run: TOOLCHAIN=${{ matrix.toolchain }} sh ./ci/install-rust.sh
       - name: Execute build.sh
         run: LIBC_CI=1 TOOLCHAIN=${{ matrix.toolchain }} sh ./ci/build.sh
 
@@ -461,34 +209,8 @@ jobs:
     steps:
       - uses: actions/checkout@v2
       - name: Setup Rust toolchain
-        run: |
-          set -ex
-          rustup set profile minimal
-          rustup update --force nightly
-          rustup default nightly
-      - name: Query Rust and Cargo versions
-        run: |
-          set -ex
-          rustc -Vv
-          cargo -V
-          rustup -Vv
-          rustup show
-          which rustc
-          which cargo
-          which rustup
-      - name: Generate lockfile
-        run: |
-          set -ex
-          N=5
-          n=0
-          until [ $n -ge $N ]
-          do
-            if cargo generate-lockfile ; then
-              break
-            fi
-            n=$((n+1))
-            sleep 1
-          done
+        # FIXME: Pin nightly version to make semverver compilable.
+        run: TOOLCHAIN=nightly-2020-06-18 sh ./ci/install-rust.sh
       - name: Check breaking changes
         run: sh ci/semver.sh linux
 
@@ -501,33 +223,7 @@ jobs:
     steps:
       - uses: actions/checkout@v2
       - name: Setup Rust toolchain
-        run: |
-          set -ex
-          rustup set profile minimal
-          rustup update --force nightly
-          rustup default nightly
-      - name: Query Rust and Cargo versions
-        run: |
-          set -ex
-          rustc -Vv
-          cargo -V
-          rustup -Vv
-          rustup show
-          which rustc
-          which cargo
-          which rustup
-      - name: Generate lockfile
-        run: |
-          set -ex
-          N=5
-          n=0
-          until [ $n -ge $N ]
-          do
-            if cargo generate-lockfile ; then
-              break
-            fi
-            n=$((n+1))
-            sleep 1
-          done
+        # FIXME: Pin nightly version to make semverver compilable.
+        run: TOOLCHAIN=nightly-2020-06-18 sh ./ci/install-rust.sh
       - name: Check breaking changes
         run: sh ci/semver.sh macos
diff --git a/ci/install-rust.sh b/ci/install-rust.sh
new file mode 100644
index 0000000000000000000000000000000000000000..598dec282d003abe4ddf64d6a247c7b9cadcaa69
--- /dev/null
+++ b/ci/install-rust.sh
@@ -0,0 +1,68 @@
+#!/usr/bin/env sh
+# This is intended to be used in CI only.
+
+set -ex
+
+echo "Setup toolchain"
+toolchain=
+if [ -n "$TOOLCHAIN" ]; then
+  toolchain=$TOOLCHAIN
+else
+  toolchain=nightly
+fi
+if [ "$OS" = "windows" ]; then
+  : "${TARGET?The TARGET environment variable must be set.}"
+  rustup set profile minimal
+  rustup update --force $toolchain-"$TARGET"
+  rustup default $toolchain-"$TARGET"
+else
+  rustup set profile minimal
+  rustup update --force $toolchain
+  rustup default $toolchain
+fi
+
+if [ -n "$TARGET" ]; then
+  echo "Install target"
+  rustup target add "$TARGET"
+fi
+
+if [ "$OS" = "windows" ]; then
+  if [ "$ARCH_BITS" = "i686" ]; then
+    echo "Install MinGW32"
+    choco install mingw --x86 --force
+  fi
+
+  echo "Find GCC libraries"
+  gcc -print-search-dirs
+  /usr/bin/find "C:\ProgramData\Chocolatey" -name "crt2*"
+  /usr/bin/find "C:\ProgramData\Chocolatey" -name "dllcrt2*"
+  /usr/bin/find "C:\ProgramData\Chocolatey" -name "libmsvcrt*"
+
+  if [ -n "$ARCH_BITS" ]; then
+    echo "Fix MinGW"
+    for i in crt2.o dllcrt2.o libmingwex.a libmsvcrt.a ; do
+      cp -f "/C/ProgramData/Chocolatey/lib/mingw/tools/install/mingw$ARCH_BITS/$ARCH-w64-mingw32/lib/$i" "$(rustc --print sysroot)/lib/rustlib/$TARGET/lib"
+    done
+  fi
+fi
+
+echo "Query rust and cargo versions"
+rustc -Vv
+cargo -V
+rustup -Vv
+rustup show
+which rustc
+which cargo
+which rustup
+
+echo "Generate lockfile"
+N=5
+n=0
+until [ $n -ge $N ]
+do
+  if cargo generate-lockfile; then
+    break
+  fi
+  n=$((n+1))
+  sleep 1
+done
diff --git a/ci/semver.sh b/ci/semver.sh
index a8b0b3c5caa46cd8d03d95ecb12304048a5bb8d5..8f8ce40c82bbf05a449750219065c2873ea2318e 100644
--- a/ci/semver.sh
+++ b/ci/semver.sh
@@ -13,15 +13,10 @@ if ! rustc --version | grep -E "nightly" ; then
     exit 1
 fi
 
-# FIXME: Pin nightly version to make semverver compile.
-NIGHTLY_DATE=nightly-2020-06-18
-
-rustup override set ${NIGHTLY_DATE}
-
 rustup component add rustc-dev llvm-tools-preview
 
 # FIXME: Use upstream once it gets rustup.
-cargo +${NIGHTLY_DATE} install semververfork
+cargo install semververfork
 
 TARGETS=
 case "${OS}" in