diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000000000000000000000000000000000000..d25f07ad89963162921aef4ed51a42c3352fefe7
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,57 @@
+name: build and release
+
+on:
+  workflow_dispatch:
+  release:
+    types: [ created ]
+
+permissions:
+  contents: write
+
+jobs:
+  build:
+    name: ${{ matrix.platform.os_name }} with rust ${{ matrix.toolchain }}
+    runs-on: ${{ matrix.platform.os }}
+    strategy:
+      fail-fast: false
+      matrix:
+        platform:
+          - os_name: Linux-i686-musl
+            os: ubuntu-20.04
+            target: i686-unknown-linux-musl
+            bin: winapps-linux-i686-musl
+          - os_name: Linux-i686
+            os: ubuntu-20.04
+            target: i686-unknown-linux-gnu
+            bin: winapps-linux-i686
+          - os_name: Linux-x86_64-musl
+            os: ubuntu-20.04
+            target: x84_64-unknown-linux-musl
+            bin: winapps-linux-amd64-musl
+          - os_name: Linux-x86_64
+            os: ubuntu-20.04
+            target: x86_64-unknown-linux-gnu
+            bin: winapps-linux-amd64
+        toolchain:
+          - nightly
+
+    steps:
+      - uses: actions/checkout@v4
+      - name: Build binary
+        uses: houseabsolute/actions-rust-cross@v0
+        with:
+          command: "build"
+          target: ${{ matrix.platform.target }}
+          toolchain: ${{ matrix.toolchain }}
+          args: "--release"
+          strip: true
+      - name: Rename binary (linux and macos)
+        run: mv target/${{ matrix.platform.target }}/release/winapps target/${{ matrix.platform.target }}/release/${{ matrix.platform.bin }}
+      - name: Generate SHA-256
+        run: shasum -a 256 target/${{ matrix.platform.target }}/release/${{ matrix.platform.bin }} | cut -d ' ' -f 1 > target/${{ matrix.platform.target }}/release/${{ matrix.platform.bin }}.sha256
+      - name: Release binary and SHA-256 checksum to GitHub
+        uses: softprops/action-gh-release@v1
+        with:
+          files: |
+            target/${{ matrix.platform.target }}/release/${{ matrix.platform.bin }}
+            target/${{ matrix.platform.target }}/release/${{ matrix.platform.bin }}.sha256
diff --git a/scripts/install.sh b/scripts/install.sh
new file mode 100644
index 0000000000000000000000000000000000000000..3954e5efec07f9d54774e7f235c0ce2f5cbdfa13
--- /dev/null
+++ b/scripts/install.sh
@@ -0,0 +1,146 @@
+#!/bin/sh
+
+# This install script is intended to download and install the latest available
+# release of the winapps rewrite..
+#
+# It attempts to identify the current platform and an error will be thrown if
+# the platform is not supported.
+# It is based on the install script from dep (https://raw.githubusercontent.com/golang/dep/master/install.sh)
+#
+# Environment variables:
+# - INSTALL_DIRECTORY (optional): defaults to ~/.local/bin
+# - WINAPPS_RELEASE_TAG (optional): defaults to fetching the latest release
+# - WINAPPS_USE_MUSL (optional): use musl instead of glibc
+# - WINAPPS_ARCH (optional): use a specific value for ARCH (mostly for testing)
+#
+# You can install using this script:
+# $ curl https://raw.githubusercontent.com/winapps-org-winapps/rewrite/scripts/install.sh | sh
+
+set -e
+
+RELEASES_URL="https://github.com/winapps-org/winapps/releases"
+
+downloadJSON() {
+    url="$2"
+
+    echo "Fetching $url.."
+    if test -x "$(command -v curl)"; then
+        response=$(curl -s -L -w 'HTTPSTATUS:%{http_code}' -H 'Accept: application/json' "$url")
+        body=$(echo "$response" | sed -e 's/HTTPSTATUS\:.*//g')
+        code=$(echo "$response" | tr -d '\n' | sed -e 's/.*HTTPSTATUS://')
+    elif test -x "$(command -v wget)"; then
+        temp=$(mktemp)
+        body=$(wget -q --header='Accept: application/json' -O - --server-response "$url" 2> "$temp")
+        code=$(awk '/^  HTTP/{print $2}' < "$temp" | tail -1)
+        rm "$temp"
+    else
+        echo "Neither curl nor wget was available to perform http requests."
+        exit 1
+    fi
+    if [ "$code" != 200 ]; then
+        echo "Request failed with code $code"
+        exit 1
+    fi
+
+    eval "$1='$body'"
+}
+
+downloadFile() {
+    url="$1"
+    destination="$2"
+
+    echo "Fetching $url.."
+    if test -x "$(command -v curl)"; then
+        code=$(curl -s -w '%{http_code}' -L "$url" -o "$destination")
+    elif test -x "$(command -v wget)"; then
+        code=$(wget -q -O "$destination" --server-response "$url" 2>&1 | awk '/^  HTTP/{print $2}' | tail -1)
+    else
+        echo "Neither curl nor wget was available to perform http requests."
+        exit 1
+    fi
+
+    if [ "$code" != 200 ]; then
+        echo "Request failed with code $code"
+        exit 1
+    fi
+}
+
+initArch() {
+    ARCH=$(uname -m)
+    if [ -n "$WINAPPS_ARCH" ]; then
+        echo "Using WINAPPS_ARCH"
+        ARCH="$WINAPPS_ARCH"
+    fi
+    case $ARCH in
+        amd64) ARCH="amd64";;
+        x86_64) ARCH="amd64";;
+        i386) ARCH="i686";;
+        i686) ARCH="i686";;
+        *) echo "Architecture ${ARCH} is not supported by winapps"; exit 1;;
+    esac
+    echo "ARCH = $ARCH"
+}
+
+initOS() {
+    OS=$(uname | tr '[:upper:]' '[:lower:]')
+    case "$OS" in
+        linux) OS='linux';;
+        *) echo "OS ${OS} is not supported by winapps"; exit 1;;
+    esac
+    echo "OS = $OS"
+}
+
+# identify platform based on uname output
+initArch
+initOS
+
+# determine install directory if required
+if [ -z "$INSTALL_DIRECTORY" ]; then
+    if [ -d "$HOME/.local/bin" ]; then
+        INSTALL_DIRECTORY="$HOME/.local/bin"
+    else
+        echo "Installation directory not specified and ~/.local/bin does not exist"
+        exit 1
+    fi
+fi
+echo "Will install into $INSTALL_DIRECTORY"
+echo "Make sure it is on your PATH"
+
+if [ -n "$WINAPPS_USE_MUSL" ]; then
+    BINARY="winapps-${OS}-${ARCH}-musl"
+else
+    BINARY="winapps-${OS}-${ARCH}"
+fi
+
+# if WINAPPS_RELEASE_TAG was not provided, assume latest
+if [ -z "$WINAPPS_RELEASE_TAG" ]; then
+    downloadJSON LATEST_RELEASE "$RELEASES_URL/latest"
+    WINAPPS_RELEASE_TAG=$(echo "${LATEST_RELEASE}" | tr -s '\n' ' ' | sed 's/.*"tag_name":"//' | sed 's/".*//' )
+fi
+echo "Release Tag = $WINAPPS_RELEASE_TAG"
+
+# fetch the real release data to make sure it exists before we attempt a download
+downloadJSON RELEASE_DATA "$RELEASES_URL/tag/$WINAPPS_RELEASE_TAG"
+
+BINARY_URL="$RELEASES_URL/download/$WINAPPS_RELEASE_TAG/$BINARY"
+DOWNLOAD_FILE=$(mktemp)
+
+downloadFile "$BINARY_URL" "$DOWNLOAD_FILE"
+
+echo "Setting executable permissions."
+chmod +x "$DOWNLOAD_FILE"
+
+INSTALL_NAME="winapps"
+
+echo "Moving executable to $INSTALL_DIRECTORY/$INSTALL_NAME"
+mv "$DOWNLOAD_FILE" "$INSTALL_DIRECTORY/$INSTALL_NAME"
+
+if test -x "$(command -v python3)"; then
+    curl https://raw.githubusercontent.com/winapps-org/winapps/rewrite/scripts/install_quickemu.py | python3
+else
+    echo "python3 is not installed. Please install it in order to install quickemu."
+    echo "Once you have installed python3, run the following command to install quickemu:"
+    echo "curl https://raw.githubusercontent.com/winapps-org/winapps/rewrite/scripts/install_quickemu.py | python3"
+    echo "You may ignore this if quickemu is already installed."
+    exit 1
+fi
diff --git a/scripts/install_quickemu b/scripts/install_quickemu.py
similarity index 100%
rename from scripts/install_quickemu
rename to scripts/install_quickemu.py