diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..b3d0029 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,38 @@ +name: 'Test OTA Release' + +on: + push: + branches: + - 'main' + pull_request: + branches: + - 'main' + +jobs: + test_ota_release: + name: 'Test OTA Release Action' + runs-on: 'ubuntu-latest' + steps: + - name: 'Checkout' + uses: 'actions/checkout@v4' + + - name: 'Create artifacts for test' + run: | + mkdir -p artifacts + hello_time="$(date +'%Y-%m-%dT%H:%M:%S%z')" + echo "${hello_time}: Hello, World!" > artifacts/hello.txt + sleep 4.2 + goodbye_time="$(date +'%Y-%m-%dT%H:%M:%S%z')" + echo "${goodbye_time}: Goodbye, World!" > artifacts/goodbye.txt + + - name: 'Test OTA Release' + uses: './' # Use an action in the root directory + with: + release_type: 'file' + persist_dir: '/tmp/persist' + artifacts_dir: 'artifacts' + base_install_path_on_device: '/tmp/ota' + project_access_token: ${{ secrets.TEST_PROJECT_ACCESS_TOKEN }} + signing_key_mangement: 'local' + signing_key: ${{ secrets.TEST_SIGNING_KEY }} + signing_key_password: ${{ secrets.TEST_SIGNING_KEY_PASSWORD }} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..60dac5d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,7 @@ +FROM alpine:3.10 + +RUN apk add --no-cache bash curl gzip + +COPY entrypoint.sh /entrypoint.sh + +ENTRYPOINT ["/entrypoint.sh"] \ No newline at end of file diff --git a/action.yml b/action.yml new file mode 100644 index 0000000..6df74ce --- /dev/null +++ b/action.yml @@ -0,0 +1,47 @@ +name: 'Create OTA Release' +description: 'Creates OTA update releases' +author: 'Ning Shang (Thistle Technologies)' +inputs: + release_type: + description: 'Release type ("file", "zip_archive", or "rootfs")' + required: true + default: 'file' + persist_dir: + description: 'Path to the directory where the device can persist data' + required: true + default: '' + artifacts_dir: + description: 'Path to the directory where OTA update artifacts are stored' + required: false + default: '' + rootfs_img_path: + description: 'Path to the rootfs image file. Required only if release_type is "rootfs"' + required: false + default: '' + zip_archive_path: + description: 'Path to the zip archive file. Required only if release_type is "archive"' + required: false + default: '' + base_install_path_on_device: + description: 'Path to base directory on device file system where OTA update artifacts will be installed. Required if release_type is "file" or "archive"' + required: false + default: '' + project_access_token: + description: 'Project access token can be obtained from the project settings page in Thistle Control Center' + required: true + default: '' + signing_key_mangement: + description: 'Incidates how the signing key is managed ("local" or "remote")' + required: true + default: 'local' + signing_key: + description: 'Minisign signing key in Thistle format. Required only if signing_key_mangement is "local"' + required: false + default: '' + signing_key_password: + description: 'Password for the signing key. Required only if signing_key_mangement is "local" and the signing key is password protected' + required: false + default: '' +runs: + using: 'docker' + image: 'Dockerfile' \ No newline at end of file diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100755 index 0000000..f4cd3d1 --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,97 @@ +#!/usr/bin/env bash +# +# Environment variables: +# They are created from an input in a workflow file or a default value by +# GitHub, with the name INPUT_. Cf., +# https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions. +# +# The following environment variables correspond to the input identifiers in +# action.yml. +# +# INPUT_RELEASE_TYPE +# INPUT_PERSIST_DIR +# INPUT_ARTIFACTS_DIR +# INPUT_ROOTFS_IMG_PATH +# INPUT_ZIP_ARCHIVE_PATH +# INPUT_BASE_INSTALL_PATH_ON_DEVICE +# INPUT_PROJECT_ACCESS_TOKEN +# INPUT_SIGNING_KEY_MANAGEMENT +# INPUT_SIGNING_KEY +# INPUT_SIGNING_KEY_PASSWORD +# + +set -euxo pipefail + +SCRIPT_DIR="$(cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +readonly SCRIPT_DIR +TRH_BINARY_PATH="${SCRIPT_DIR}/trh" +readonly TRH_BINARY_PATH +TRH_DOWNLOAD_URL="https://downloads.thistle.tech/embedded-client/1.0.0/trh-1.0.0-x86_64-unknown-linux-musl.gz" +readonly TRH_DOWNLOAD_URL + +# Set environment variables +export THISTLE_KEY="${HOME}/.minisign/minisign.key" +export THISTLE_KEY_PASS="${INPUT_SIGNING_KEY_PASSWORD}" +export THISTLE_TOKEN="${INPUT_PROJECT_ACCESS_TOKEN}" + +download_trh() { + curl -A "curl (ota_release_action)" -L -o /tmp/trh.gz "${TRH_DOWNLOAD_URL}" + gunzip -c /tmp/trh.gz > "${TRH_BINARY_PATH}" + chmod +x "${TRH_BINARY_PATH}" + "${TRH_BINARY_PATH}" --version +} + +file_release() { + # Hack alert: Need to do this to get a manifest template + mkdir -p "$(dirname "${THISTLE_KEY}")" + echo "${INPUT_SIGNING_KEY}" > "${THISTLE_KEY}" + cat "${THISTLE_KEY}" + "${TRH_BINARY_PATH}" init --persist="${INPUT_PERSIST_DIR}" + + local artifacts_dir="${INPUT_ARTIFACTS_DIR:-}" + local base_install_path="${INPUT_BASE_INSTALL_PATH_ON_DEVICE:-}" + [ -z "${artifacts_dir}" ] && { echo "No artifacts directory provided"; exit 1; } + [ -z "${base_install_path}" ] && { echo "No base install path provided"; exit 1; } + + "${TRH_BINARY_PATH}" prepare --target="${artifacts_dir}" --file-base-path="${base_install_path}" + + "${TRH_BINARY_PATH}" release + + echo "done" +} + +rootfs_release() { + echo "Not implemented" + exit 1 +} + +zip_archive_release() { + echo "Not implemented" + exit 1 +} + +do_it() { + + local release_type="${INPUT_RELEASE_TYPE:-}" + [ -z "${release_type}" ] && { echo "No release type provided"; exit 1; } + + download_trh + + case "${INPUT_RELEASE_TYPE}" in + "file") + file_release + ;; + "rootfs") + rootfs_release + ;; + "zip_archive") + zip_archive_release + ;; + *) + echo "Unknown release type: ${INPUT_RELEASE_TYPE}" + exit 1 + ;; + esac +} + +do_it