aboutsummaryrefslogtreecommitdiff
path: root/docker
diff options
context:
space:
mode:
Diffstat (limited to 'docker')
-rwxr-xr-xdocker/scripts/ciq-env.sh49
-rwxr-xr-xdocker/scripts/gen-key.sh39
-rwxr-xr-xdocker/scripts/sdk-setup.sh88
3 files changed, 176 insertions, 0 deletions
diff --git a/docker/scripts/ciq-env.sh b/docker/scripts/ciq-env.sh
new file mode 100755
index 0000000..945d59a
--- /dev/null
+++ b/docker/scripts/ciq-env.sh
@@ -0,0 +1,49 @@
+#!/usr/bin/env bash
+# Shared helpers for the container scripts. Source this file; don't run it.
+#
+# Paths, as seen inside the container:
+# /work the project folder (bind mount)
+# /home/dev the project's .cache/home folder (bind mount); the Garmin
+# tools keep the SDK and device files under ~/.Garmin
+# /work/.cache/keys the developer key (never baked into an image)
+
+CIQ_HOME="${HOME}/.Garmin/ConnectIQ"
+KEY_DIR="/work/.cache/keys"
+DEV_KEY="${KEY_DIR}/developer_key.der"
+BUILD_DIR="/work/build"
+
+# Print an error and stop. Used for every user-facing failure, so the message
+# says what to do next instead of leaving a bare stack trace.
+die() {
+ echo "error: $*" >&2
+ exit 1
+}
+
+# Progress line on stderr, so stdout stays clean for machine-readable output.
+log() {
+ echo "==> $*" >&2
+}
+
+# Find the SDK selected by `make sdk` and put its bin/ folder on PATH.
+# Sets CIQ_SDK to the SDK root folder.
+ciq_require_sdk() {
+ local cfg="${CIQ_HOME}/current-sdk.cfg"
+ [[ -f "${cfg}" ]] || die "Connect IQ SDK not installed. Run 'make sdk' first (you accept Garmin's licence and log in there)."
+ CIQ_SDK="$(tr -d '\r\n' < "${cfg}")"
+ CIQ_SDK="${CIQ_SDK%/}"
+ [[ -x "${CIQ_SDK}/bin/monkeyc" ]] || die "SDK folder '${CIQ_SDK}' has no bin/monkeyc. Run 'make sdk' again."
+ export CIQ_SDK
+ export PATH="${CIQ_SDK}/bin:${PATH}"
+}
+
+# The developer key signs every build, including test builds.
+ciq_require_key() {
+ [[ -f "${DEV_KEY}" ]] || die "No developer key at .cache/keys/developer_key.der. Run 'make key' first."
+}
+
+# Fail early, with a pointer to `make sdk`, when a device's files are missing.
+ciq_require_device() {
+ local device="$1"
+ [[ -f "${CIQ_HOME}/Devices/${device}/compiler.json" ]] \
+ || die "No device files for '${device}'. Is it in manifest.xml? Then run 'make sdk' to download them."
+}
diff --git a/docker/scripts/gen-key.sh b/docker/scripts/gen-key.sh
new file mode 100755
index 0000000..7b99863
--- /dev/null
+++ b/docker/scripts/gen-key.sh
@@ -0,0 +1,39 @@
+#!/usr/bin/env bash
+# Generate the developer key that signs every build (README section 15.6).
+#
+# Output: .cache/keys/developer_key.der (PKCS#8 DER, what monkeyc wants) and
+# developer_key.pem, both mode 600 and git-ignored. An existing key is never
+# overwritten: the Connect IQ Store rejects updates signed with a different
+# key, so replacing it by accident would strand the published app.
+set -euo pipefail
+# shellcheck source=ciq-env.sh
+source "$(dirname "${BASH_SOURCE[0]}")/ciq-env.sh"
+
+umask 077
+mkdir -p "${KEY_DIR}"
+
+if [[ -f "${DEV_KEY}" ]]; then
+ log "Developer key already exists at .cache/keys/developer_key.der; leaving it untouched."
+ exit 0
+fi
+
+log "Generating a 4096-bit RSA developer key (this takes a few seconds)..."
+openssl genrsa -out "${KEY_DIR}/developer_key.pem" 4096 2>/dev/null
+openssl pkcs8 -topk8 -inform PEM -outform DER -nocrypt \
+ -in "${KEY_DIR}/developer_key.pem" -out "${DEV_KEY}"
+chmod 600 "${KEY_DIR}/developer_key.pem" "${DEV_KEY}"
+
+# A short fingerprint of the public key, so a backup can be recognised later.
+fingerprint="$(openssl pkey -in "${KEY_DIR}/developer_key.pem" -pubout -outform DER \
+ | sha256sum | cut -c1-16)"
+
+cat >&2 <<EOF
+
+Developer key created: .cache/keys/developer_key.der
+Public key fingerprint (SHA-256, first 16 hex digits): ${fingerprint}
+
+BACK IT UP NOW. Copy the folder .cache/keys/ to an encrypted place (a password
+manager attachment or an encrypted USB drive). Garmin rejects app updates that
+are signed with a different key; without this key you would have to publish a
+new app and re-add every entry on the watch. 'make nuke' deletes this folder.
+EOF
diff --git a/docker/scripts/sdk-setup.sh b/docker/scripts/sdk-setup.sh
new file mode 100755
index 0000000..278b060
--- /dev/null
+++ b/docker/scripts/sdk-setup.sh
@@ -0,0 +1,88 @@
+#!/usr/bin/env bash
+# Interactive first-time setup behind `make sdk` (README section 15.3).
+#
+# 1. Garmin's agreements: you read them in your browser and accept them here.
+# 2. Login with your Garmin account. Email and password are typed here and
+# passed only to the SDK manager CLI, which sends them to Garmin's sign-in
+# page. Neither is written to disk; the CLI stores an access token.
+# 3. Download Connect IQ SDK ${SDK_VERSION} and make it the current SDK.
+# 4. Download the device files and simulator fonts for every product listed
+# in manifest.xml.
+# 5. Log out: delete the access token (set KEEP_LOGIN=1 to keep it).
+#
+# Why email and password instead of the CLI's browser login: that flow waits
+# for a redirect to a random localhost port inside the container, which your
+# browser can't reach without host networking (not allowed by the sandbox
+# rules). Password login doesn't support Garmin two-step verification; README
+# section 15.3 describes the fallback for such accounts.
+set -euo pipefail
+# shellcheck source=ciq-env.sh
+source "$(dirname "${BASH_SOURCE[0]}")/ciq-env.sh"
+
+readonly SDKMGR=connect-iq-sdk-manager
+readonly MANIFEST=/work/manifest.xml
+readonly TOKEN="${CIQ_HOME}/token.json"
+
+[[ -t 0 ]] || die "'make sdk' is interactive; run it from a terminal."
+[[ -f "${MANIFEST}" ]] || die "manifest.xml not found in the project folder."
+: "${SDK_VERSION:?SDK_VERSION is not set (the Makefile passes it)}"
+
+echo
+echo "Step 1/5: Garmin's agreements"
+view="$(${SDKMGR} agreement view 2>&1)" || die "Could not fetch the agreement from Garmin: ${view}"
+url="$(sed -n 's/^Agreement: //p' <<<"${view}")"
+hash="$(sed -n 's/^Current Hash: //p' <<<"${view}")"
+[[ -n "${url}" && -n "${hash}" ]] || die "Unexpected output from 'agreement view': ${view}"
+cat <<EOF
+ Please read the Garmin CONNECT IQ SDK License Agreement and the
+ CONNECT IQ Application Developer Agreement in your browser:
+ ${url}
+ Agreement version (hash): ${hash}
+EOF
+read -r -p " Type 'accept' to accept them, or anything else to stop: " answer
+[[ "${answer}" == accept ]] || die "Agreements not accepted; nothing was downloaded."
+# Passing the hash makes the CLI refuse if Garmin changed the text since you read it.
+${SDKMGR} agreement accept --agreement-hash "${hash}" >/dev/null
+echo " Accepted (version ${hash})."
+
+echo
+echo "Step 2/5: Garmin login"
+if [[ -f "${TOKEN}" ]]; then
+ echo " Reusing the login token kept from an earlier run (KEEP_LOGIN=1)."
+else
+ echo " Email and password go only to Garmin's sign-in page and are not stored."
+ echo " Accounts with two-step verification can't log in this way (see README 15.3)."
+ read -r -p " Garmin account email: " GARMIN_USERNAME
+ read -r -s -p " Password (not shown): " GARMIN_PASSWORD
+ echo
+ export GARMIN_USERNAME GARMIN_PASSWORD
+ if ! ${SDKMGR} login; then
+ unset GARMIN_USERNAME GARMIN_PASSWORD
+ die "Login failed. Check email and password. Two-step verification isn't supported here; see README 15.3."
+ fi
+ unset GARMIN_USERNAME GARMIN_PASSWORD
+ echo " Logged in."
+fi
+
+echo
+echo "Step 3/5: Connect IQ SDK ${SDK_VERSION}"
+${SDKMGR} sdk set "${SDK_VERSION}"
+echo " Current SDK: $(${SDKMGR} sdk current-path)"
+
+echo
+echo "Step 4/5: device files and fonts for the products in manifest.xml"
+${SDKMGR} device download --manifest "${MANIFEST}" --include-fonts
+
+echo
+echo "Step 5/5: log out"
+if [[ "${KEEP_LOGIN:-0}" == 1 ]]; then
+ echo " KEEP_LOGIN=1: keeping the token at .cache/home/.Garmin/ConnectIQ/token.json"
+else
+ rm -f "${TOKEN}"
+ echo " Login token deleted. Run 'make sdk' again whenever new device files are needed."
+fi
+
+device_count="$(find "${CIQ_HOME}/Devices" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l)"
+echo
+echo "Done: SDK ${SDK_VERSION}, ${device_count} device folders, $(du -sh "${CIQ_HOME}" | cut -f1) in .cache/home."
+echo "Next: 'make key' (if you have no developer key yet), then 'make test'."