mirror of
https://github.com/storytold/craft-launcher.git
synced 2026-11-02 18:07:04 +00:00
packaging: [[ ]] tests, named positional parameters and explicit returns (SonarCloud S7688, S7679, S7682) (#808)
* packaging: [[ ]] tests, named positional parameters and explicit returns (SonarCloud S7688, S7679, S7682) Every packaging script is bash (macOS ships bash 3.2, which has [[ ]]), so the single-bracket tests become [[ ]]: no word splitting or globbing of unquoted operands. Every right-hand side of = is quoted or a plain literal, so no test turns into a pattern match. Functions that read positional parameters copy them into named locals, and the env.sh helpers end with a bare return, which keeps the last command's status. * fix: use POSIX test in generated /bin/sh helper
This commit is contained in:
+10
-5
@@ -20,10 +20,11 @@ workspace_version() {
|
||||
/^\[/ { in_pkg = ($0 == "[workspace.package]") ; next }
|
||||
in_pkg && $1 == "version" { gsub(/[" ]/, "", $3); print $3; exit }
|
||||
' "$ROOT/Cargo.toml"
|
||||
return
|
||||
}
|
||||
|
||||
VERSION="${ARTCRAFT_LAUNCHER_VERSION:-$(workspace_version)}"
|
||||
if [ -z "$VERSION" ]; then
|
||||
if [[ -z "$VERSION" ]]; then
|
||||
echo "error: could not read [workspace.package] version from $ROOT/Cargo.toml" >&2
|
||||
exit 1
|
||||
fi
|
||||
@@ -33,7 +34,7 @@ DIST="${DIST:-$ROOT/dist/release}"
|
||||
mkdir -p "$DIST"
|
||||
export DIST
|
||||
|
||||
if [ -z "${ARTCRAFT_LAUNCHER_BUILD_SHA:-}" ]; then
|
||||
if [[ -z "${ARTCRAFT_LAUNCHER_BUILD_SHA:-}" ]]; then
|
||||
ARTCRAFT_LAUNCHER_BUILD_SHA="$(git -C "$ROOT" rev-parse HEAD 2>/dev/null || true)"
|
||||
fi
|
||||
export ARTCRAFT_LAUNCHER_BUILD_SHA
|
||||
@@ -42,18 +43,22 @@ export CARGO_TARGET_DIR="${CARGO_TARGET_DIR:-$ROOT/target}"
|
||||
|
||||
# Emit a GitHub Actions warning (plain stderr outside Actions).
|
||||
warn() {
|
||||
if [ -n "${GITHUB_ACTIONS:-}" ]; then echo "::warning::$*"; else echo "warning: $*" >&2; fi
|
||||
if [[ -n "${GITHUB_ACTIONS:-}" ]]; then echo "::warning::$*"; else echo "warning: $*" >&2; fi
|
||||
return
|
||||
}
|
||||
|
||||
# Copy licence, notice and readme files that exist into a package directory.
|
||||
copy_docs() {
|
||||
local dest="$1" f
|
||||
for f in README.md LICENSE-MIT LICENSE-APACHE NOTICE ATTRIBUTION.md; do
|
||||
if [ -f "$ROOT/$f" ]; then cp "$ROOT/$f" "$dest/"; fi
|
||||
if [[ -f "$ROOT/$f" ]]; then cp "$ROOT/$f" "$dest/"; fi
|
||||
done
|
||||
return
|
||||
}
|
||||
|
||||
# Portable SHA-256 of a file (prints just the hash).
|
||||
sha256() {
|
||||
if command -v sha256sum >/dev/null; then sha256sum "$1" | cut -d' ' -f1; else shasum -a 256 "$1" | cut -d' ' -f1; fi
|
||||
local file="$1"
|
||||
if command -v sha256sum >/dev/null; then sha256sum "$file" | cut -d' ' -f1; else shasum -a 256 "$file" | cut -d' ' -f1; fi
|
||||
return
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ PKG=artcraft-launcher
|
||||
|
||||
SKIP_BUILD=0
|
||||
DRY_RUN=0
|
||||
while [ $# -gt 0 ]; do
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--skip-build) SKIP_BUILD=1; shift ;;
|
||||
--dry-run) DRY_RUN=1; SKIP_BUILD=1; shift ;;
|
||||
@@ -37,7 +37,7 @@ case "$MACHINE" in
|
||||
arm64 | aarch64) ARCH=aarch64 ;;
|
||||
*) echo "unsupported architecture $MACHINE" >&2; exit 2 ;;
|
||||
esac
|
||||
if [ "$DRY_RUN" = 0 ] && [ "$(uname -s)" != FreeBSD ]; then
|
||||
if [[ "$DRY_RUN" = 0 ]] && [[ "$(uname -s)" != FreeBSD ]]; then
|
||||
echo "error: build on FreeBSD (or pass --dry-run to check the tree layout)" >&2
|
||||
exit 2
|
||||
fi
|
||||
@@ -46,7 +46,7 @@ BASENAME="$PKG-$VERSION-freebsd-$ARCH"
|
||||
echo "==> ArtCraft Launcher $VERSION for FreeBSD $ARCH"
|
||||
|
||||
export CARGO_BUILD_JOBS="${CARGO_BUILD_JOBS:-4}"
|
||||
if [ "$SKIP_BUILD" = 0 ]; then
|
||||
if [[ "$SKIP_BUILD" = 0 ]]; then
|
||||
(cd "$ROOT" && cargo build --release --locked -p "$PKG")
|
||||
fi
|
||||
WORK="$CARGO_TARGET_DIR/freebsd-package"
|
||||
@@ -56,7 +56,7 @@ mkdir -p "$WORK"
|
||||
|
||||
BIN="$CARGO_TARGET_DIR/release"
|
||||
OUT_DIR="$DIST"
|
||||
if [ "$DRY_RUN" = 1 ]; then
|
||||
if [[ "$DRY_RUN" = 1 ]]; then
|
||||
# A stub binary, and the tarball stays in the work dir so dist/ only ever holds real builds.
|
||||
BIN="$WORK/stub-bin"
|
||||
OUT_DIR="$WORK"
|
||||
@@ -81,7 +81,7 @@ copy_docs "$STAGE/share/doc/$PKG"
|
||||
for f in "bin/$PKG" "share/applications/$APP_ID.desktop" \
|
||||
"share/icons/hicolor/256x256/apps/$APP_ID.png" "share/icons/hicolor/scalable/apps/$APP_ID.svg" \
|
||||
"share/doc/$PKG/LICENSE-MIT" "share/doc/$PKG/LICENSE-APACHE"; do
|
||||
if [ ! -e "$STAGE/$f" ]; then echo "error: $f is missing from the package" >&2; exit 1; fi
|
||||
if [[ ! -e "$STAGE/$f" ]]; then echo "error: $f is missing from the package" >&2; exit 1; fi
|
||||
done
|
||||
|
||||
# ---- .tar.gz ------------------------------------------------------------------------------------
|
||||
@@ -89,7 +89,7 @@ mkdir -p "$OUT_DIR"
|
||||
tar -C "$WORK" -czf "$OUT_DIR/$BASENAME.tar.gz" "$BASENAME"
|
||||
echo "wrote $OUT_DIR/$BASENAME.tar.gz"
|
||||
|
||||
if [ "$DRY_RUN" = 1 ]; then
|
||||
if [[ "$DRY_RUN" = 1 ]]; then
|
||||
echo "==> dry run: tarball contents"
|
||||
tar -tzvf "$OUT_DIR/$BASENAME.tar.gz"
|
||||
fi
|
||||
|
||||
+4
-1
@@ -30,7 +30,10 @@ MAC="$TMP/mac.svg"
|
||||
sed "s/$BOX/viewBox=\"-17.5 -26.92 151.34 151.34\"/" "$SVG" >"$MAC"
|
||||
FLAT="$SVG"
|
||||
|
||||
render() { resvg -w "$2" -h "$2" "$1" "$3" </dev/null; }
|
||||
render() {
|
||||
local src="$1" size="$2" out="$3"
|
||||
resvg -w "$size" -h "$size" "$src" "$out" </dev/null
|
||||
}
|
||||
|
||||
render "$MAC" 1024 "$DIR/artcraft-launcher-1024.png"
|
||||
|
||||
|
||||
@@ -52,10 +52,10 @@ fail() {
|
||||
|
||||
# 1. Launch works and passes arguments through.
|
||||
out="$("$APPDIR/AppRun" open file.png)" || fail "AppRun exited non-zero"
|
||||
[ "$out" = "stub: open file.png" ] || fail "arguments not forwarded: $out"
|
||||
[[ "$out" = "stub: open file.png" ]] || fail "arguments not forwarded: $out"
|
||||
|
||||
DESKTOP="$XDG_DATA_HOME/applications/$APP_ID.desktop"
|
||||
[ -f "$DESKTOP" ] || fail "desktop entry not installed"
|
||||
[[ -f "$DESKTOP" ]] || fail "desktop entry not installed"
|
||||
|
||||
# 2. Exec rewritten to the AppImage (quoted, spaces intact), TryExec dropped,
|
||||
# everything else preserved from the packaged entry.
|
||||
@@ -70,22 +70,22 @@ fi
|
||||
|
||||
# 3. Icons landed under the name the .desktop entry references.
|
||||
for f in "16x16/apps/$APP_ID.png" "256x256/apps/$APP_ID.png" "scalable/apps/$APP_ID.svg"; do
|
||||
[ -f "$XDG_DATA_HOME/icons/hicolor/$f" ] || fail "icon missing: $f"
|
||||
[[ -f "$XDG_DATA_HOME/icons/hicolor/$f" ]] || fail "icon missing: $f"
|
||||
done
|
||||
[ -f "$CACHE_CALLS" ] || fail "installed icons did not refresh the icon cache"
|
||||
[ "$(wc -l <"$CACHE_CALLS")" -eq 1 ] || fail "icon cache refreshed more than once"
|
||||
[[ -f "$CACHE_CALLS" ]] || fail "installed icons did not refresh the icon cache"
|
||||
[[ "$(wc -l <"$CACHE_CALLS")" -eq 1 ]] || fail "icon cache refreshed more than once"
|
||||
grep -Fxq -- "-f -t $XDG_DATA_HOME/icons/hicolor" "$CACHE_CALLS" || fail "wrong icon cache arguments"
|
||||
if [ -n "$REAL_CACHE_UPDATER" ]; then
|
||||
[ -f "$XDG_DATA_HOME/icons/hicolor/icon-theme.cache" ] || fail "GTK did not build the icon cache"
|
||||
if [[ -n "$REAL_CACHE_UPDATER" ]]; then
|
||||
[[ -f "$XDG_DATA_HOME/icons/hicolor/icon-theme.cache" ]] || fail "GTK did not build the icon cache"
|
||||
fi
|
||||
|
||||
# 4. Idempotent: a second launch must not rewrite anything (inode unchanged).
|
||||
before="$(stat -c %i "$DESKTOP")"
|
||||
out="$("$APPDIR/AppRun")" || fail "second launch exited non-zero"
|
||||
[ "$out" = "stub: " ] || fail "second launch output: $out"
|
||||
[[ "$out" = "stub: " ]] || fail "second launch output: $out"
|
||||
after="$(stat -c %i "$DESKTOP")"
|
||||
[ "$before" = "$after" ] || fail "desktop entry rewritten although unchanged"
|
||||
[ "$(wc -l <"$CACHE_CALLS")" -eq 1 ] || fail "unchanged icons refreshed the cache"
|
||||
[[ "$before" = "$after" ]] || fail "desktop entry rewritten although unchanged"
|
||||
[[ "$(wc -l <"$CACHE_CALLS")" -eq 1 ]] || fail "unchanged icons refreshed the cache"
|
||||
|
||||
# 5. Moving the AppImage refreshes Exec.
|
||||
mv "$APPIMAGE" "$TMP/moved.AppImage"
|
||||
@@ -97,31 +97,31 @@ cp "$DESKTOP" "$TMP/generated-entry"
|
||||
# 6. Opt-out installs nothing.
|
||||
rm -rf "$XDG_DATA_HOME"
|
||||
ARTCRAFT_LAUNCHER_NO_DESKTOP_INTEGRATION=1 "$APPDIR/AppRun" >/dev/null || fail "opt-out launch exited non-zero"
|
||||
[ ! -e "$XDG_DATA_HOME" ] || fail "opt-out still integrated"
|
||||
[[ ! -e "$XDG_DATA_HOME" ]] || fail "opt-out still integrated"
|
||||
|
||||
# 7. An unwritable data dir never blocks the launch.
|
||||
mkdir -p "$TMP/ro" && chmod a-w "$TMP/ro"
|
||||
out="$(XDG_DATA_HOME="$TMP/ro/data" "$APPDIR/AppRun" out.txt)" || fail "launch blocked by unwritable data dir"
|
||||
[ "$out" = "stub: out.txt" ] || fail "stub not run against unwritable data dir: $out"
|
||||
[[ "$out" = "stub: out.txt" ]] || fail "stub not run against unwritable data dir: $out"
|
||||
chmod u+w "$TMP/ro"
|
||||
|
||||
# 8. No $APPIMAGE (extracted AppDir): launch only, nothing installed.
|
||||
rm -rf "$XDG_DATA_HOME"
|
||||
env -u APPIMAGE "$APPDIR/AppRun" >/dev/null || fail "extracted-AppDir launch exited non-zero"
|
||||
[ ! -e "$XDG_DATA_HOME" ] || fail "integrated without $APPIMAGE"
|
||||
[[ ! -e "$XDG_DATA_HOME" ]] || fail "integrated without $APPIMAGE"
|
||||
|
||||
# 9. A path that would need Exec escaping (here a `$`): launch only, nothing installed.
|
||||
rm -rf "$XDG_DATA_HOME"
|
||||
odd="$TMP/odd\$(id).AppImage"
|
||||
: >"$odd"
|
||||
out="$(APPIMAGE="$odd" "$APPDIR/AppRun" x)" || fail "launch with an odd path exited non-zero"
|
||||
[ "$out" = "stub: x" ] || fail "odd path: $out"
|
||||
[ ! -e "$XDG_DATA_HOME" ] || fail "integrated an AppImage path that needs escaping"
|
||||
[[ "$out" = "stub: x" ]] || fail "odd path: $out"
|
||||
[[ ! -e "$XDG_DATA_HOME" ]] || fail "integrated an AppImage path that needs escaping"
|
||||
|
||||
# 10. Gear Lever's environment flag suppresses integration, including icon writes.
|
||||
out="$(DESKTOPINTEGRATION=1 "$APPDIR/AppRun" managed)" || fail "manager launch failed"
|
||||
[ "$out" = "stub: managed" ] || fail "manager launch lost arguments"
|
||||
[ ! -e "$XDG_DATA_HOME" ] || fail "manager-integrated AppImage integrated again"
|
||||
[[ "$out" = "stub: managed" ]] || fail "manager launch lost arguments"
|
||||
[[ ! -e "$XDG_DATA_HOME" ]] || fail "manager-integrated AppImage integrated again"
|
||||
|
||||
# 11. A manager entry under the native id keeps TryExec and its launch environment.
|
||||
mkdir -p "$(dirname "$DESKTOP")"
|
||||
@@ -172,13 +172,13 @@ printf '#!/bin/sh\nexit 1\n' >"$TMP/tools/gtk-update-icon-cache"
|
||||
icon="16x16/apps/$APP_ID.png"
|
||||
printf 'stale icon' >"$XDG_DATA_HOME/icons/hicolor/$icon"
|
||||
out="$("$APPDIR/AppRun" cache-failure)" || fail "cache failure blocked launch"
|
||||
[ "$out" = "stub: cache-failure" ] || fail "cache failure lost arguments"
|
||||
[[ "$out" = "stub: cache-failure" ]] || fail "cache failure lost arguments"
|
||||
cmp -s "$APPDIR/usr/share/icons/hicolor/$icon" "$XDG_DATA_HOME/icons/hicolor/$icon" || fail "changed icon not refreshed"
|
||||
|
||||
# 14. A symlink to someone else's entry must not be replaced.
|
||||
rm "$DESKTOP"
|
||||
ln -s "$TMP/manager-entry" "$DESKTOP"
|
||||
"$APPDIR/AppRun" >/dev/null || fail "launch with desktop symlink failed"
|
||||
[ -L "$DESKTOP" ] || fail "desktop symlink replaced"
|
||||
[[ -L "$DESKTOP" ]] || fail "desktop symlink replaced"
|
||||
|
||||
echo "AppRun desktop integration ok"
|
||||
|
||||
@@ -22,7 +22,7 @@ FLATHUB=https://dl.flathub.org/repo/flathub.flatpakrepo
|
||||
|
||||
TEST=1
|
||||
TARBALL=""
|
||||
while [ $# -gt 0 ]; do
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--no-test) TEST=0; shift ;;
|
||||
-h | --help) sed -n '2,14p' "$0"; exit 0 ;;
|
||||
@@ -39,7 +39,7 @@ case "$ARCH" in
|
||||
esac
|
||||
BASENAME="$PKG-$VERSION-linux-$ARCH"
|
||||
TARBALL="${TARBALL:-$DIST/$BASENAME.tar.gz}"
|
||||
[ -f "$TARBALL" ] || { echo "error: $TARBALL not found (run packaging/linux/package.sh --formats tar first)" >&2; exit 1; }
|
||||
[[ -f "$TARBALL" ]] || { echo "error: $TARBALL not found (run packaging/linux/package.sh --formats tar first)" >&2; exit 1; }
|
||||
for tool in flatpak flatpak-builder; do
|
||||
command -v "$tool" >/dev/null || { echo "error: $tool not found (apt install flatpak flatpak-builder librsvg2-common)" >&2; exit 1; }
|
||||
done
|
||||
@@ -50,7 +50,7 @@ WORK="$CARGO_TARGET_DIR/flatpak-bundle"
|
||||
rm -rf "$WORK"
|
||||
mkdir -p "$WORK/stage"
|
||||
tar -xzf "$TARBALL" -C "$WORK/stage" --strip-components=1
|
||||
[ -x "$WORK/stage/bin/$PKG" ] || { echo "error: $TARBALL has no bin/$PKG" >&2; exit 1; }
|
||||
[[ -x "$WORK/stage/bin/$PKG" ]] || { echo "error: $TARBALL has no bin/$PKG" >&2; exit 1; }
|
||||
cp "$HERE/flatpak/$APP_ID.bundle.yml" "$WORK/$APP_ID.yml"
|
||||
|
||||
flatpak remote-add --user --if-not-exists flathub "$FLATHUB"
|
||||
@@ -63,7 +63,7 @@ OUT="$DIST/$BASENAME.flatpak"
|
||||
flatpak build-bundle --runtime-repo="$FLATHUB" "$WORK/repo" "$OUT" "$APP_ID" stable
|
||||
echo "wrote $OUT"
|
||||
|
||||
if [ "$TEST" = 1 ]; then
|
||||
if [[ "$TEST" = 1 ]]; then
|
||||
flatpak install --user -y --noninteractive --reinstall "$OUT"
|
||||
flatpak info --user "$APP_ID"
|
||||
flatpak run --command=sh "$APP_ID" -c 'test -x /app/bin/artcraft-launcher && ls /app/share/applications /app/share/metainfo /app/share/icons/hicolor/scalable/apps'
|
||||
|
||||
@@ -26,7 +26,7 @@ PKG=artcraft-launcher
|
||||
|
||||
SKIP_BUILD=0
|
||||
FORMATS="appimage deb rpm tar"
|
||||
while [ $# -gt 0 ]; do
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--skip-build) SKIP_BUILD=1; shift ;;
|
||||
--formats) FORMATS="$2"; shift 2 ;;
|
||||
@@ -55,8 +55,8 @@ BASENAME="$PKG-$VERSION-linux-$ARCH"
|
||||
|
||||
echo "==> ArtCraft Launcher $VERSION for Linux $ARCH ($FORMATS)"
|
||||
|
||||
if [ "$SKIP_BUILD" = 0 ]; then
|
||||
if [ -n "${CROSS_TARGET:-}" ]; then
|
||||
if [[ "$SKIP_BUILD" = 0 ]]; then
|
||||
if [[ -n "${CROSS_TARGET:-}" ]]; then
|
||||
(cd "$ROOT" && cargo build --release --locked -p "$PKG" --target "$CROSS_TARGET")
|
||||
else
|
||||
(cd "$ROOT" && cargo build --release --locked -p "$PKG")
|
||||
@@ -86,7 +86,7 @@ if command -v appstreamcli >/dev/null; then
|
||||
appstreamcli validate --no-net --explain "$STAGE/usr/share/metainfo/$APP_ID.metainfo.xml"
|
||||
fi
|
||||
|
||||
has() { case " $FORMATS " in *" $1 "*) return 0 ;; *) return 1 ;; esac; }
|
||||
has() { local format="$1"; case " $FORMATS " in *" $format "*) return 0 ;; *) return 1 ;; esac; }
|
||||
|
||||
# ---- .tar.gz ------------------------------------------------------------------------------------
|
||||
if has tar; then
|
||||
@@ -123,9 +123,9 @@ if has appimage; then
|
||||
ln -s "$APP_ID.png" "$APPDIR/.DirIcon"
|
||||
|
||||
TOOL="${APPIMAGETOOL:-$(command -v appimagetool || true)}"
|
||||
if [ -z "$TOOL" ]; then
|
||||
if [[ -z "$TOOL" ]]; then
|
||||
TOOL="$CARGO_TARGET_DIR/appimagetool-$ARCH.AppImage"
|
||||
if [ ! -x "$TOOL" ]; then
|
||||
if [[ ! -x "$TOOL" ]]; then
|
||||
curl --proto '=https' -fsSL -o "$TOOL" "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-$ARCH.AppImage"
|
||||
chmod +x "$TOOL"
|
||||
fi
|
||||
@@ -149,16 +149,16 @@ if has appimage; then
|
||||
# package) appimagetool also writes the .zsync, into its working directory, hence the cd.
|
||||
(cd "$DIST" && ARCH="$ARCH" APPIMAGE_EXTRACT_AND_RUN=1 "$TOOL" --no-appstream -u "$UPDATE_INFO" "$APPDIR" "$OUT")
|
||||
echo "wrote $OUT"
|
||||
if [ -s "$OUT.zsync" ]; then
|
||||
if [[ -s "$OUT.zsync" ]]; then
|
||||
echo "wrote $OUT.zsync"
|
||||
else
|
||||
warn "zsyncmake not found, so $OUT.zsync was not written; AppImage delta updates need it"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "${CROSS_TARGET:-}" ]; then
|
||||
if [[ -z "${CROSS_TARGET:-}" ]]; then
|
||||
"$STAGE/usr/bin/$PKG" --version
|
||||
elif [ -n "${EMULATOR:-}" ]; then
|
||||
elif [[ -n "${EMULATOR:-}" ]]; then
|
||||
"$EMULATOR" "$STAGE/usr/bin/$PKG" --version
|
||||
fi
|
||||
echo "==> done"
|
||||
|
||||
@@ -12,7 +12,7 @@ umask 077
|
||||
# shellcheck source=../env.sh
|
||||
. "$(dirname "${BASH_SOURCE[0]}")/../env.sh"
|
||||
|
||||
if [ -z "${APPLE_CERTIFICATE:-}" ]; then
|
||||
if [[ -z "${APPLE_CERTIFICATE:-}" ]]; then
|
||||
warn "APPLE_CERTIFICATE is not set: macOS builds will be ad-hoc signed and not notarized"
|
||||
exit 0
|
||||
fi
|
||||
@@ -43,13 +43,13 @@ security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KC_PASS"
|
||||
security list-keychains -d user -s "$KEYCHAIN" $(security list-keychains -d user | tr -d '"')
|
||||
|
||||
IDENTITY="$(security find-identity -v -p codesigning "$KEYCHAIN" | awk '/Developer ID Application/ { print $2; exit }')"
|
||||
if [ -z "$IDENTITY" ]; then
|
||||
if [[ -z "$IDENTITY" ]]; then
|
||||
security find-identity -v -p codesigning "$KEYCHAIN" >&2
|
||||
echo "error: no 'Developer ID Application' identity in APPLE_CERTIFICATE" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "Imported signing identity $IDENTITY"
|
||||
if [ -n "${GITHUB_ENV:-}" ]; then
|
||||
if [[ -n "${GITHUB_ENV:-}" ]]; then
|
||||
{ echo "MACOS_SIGN_IDENTITY=$IDENTITY"; echo "MACOS_KEYCHAIN=$KEYCHAIN"; } >>"$GITHUB_ENV"
|
||||
else
|
||||
echo "MACOS_SIGN_IDENTITY=$IDENTITY"
|
||||
|
||||
+11
-11
@@ -22,7 +22,7 @@ APP_NAME="ArtCraft Launcher" # display name: bundle, executable, icon, DMG vol
|
||||
|
||||
ARCH=universal
|
||||
SKIP_BUILD=0
|
||||
while [ $# -gt 0 ]; do
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--arch) ARCH="$2"; shift 2 ;;
|
||||
--skip-build) SKIP_BUILD=1; shift ;;
|
||||
@@ -46,9 +46,9 @@ APP="$WORK/$APP_NAME.app"
|
||||
DMG="$DIST/$PKG-$VERSION-macos-$ARCH.dmg"
|
||||
|
||||
NOTARIZE=0
|
||||
if [ "$IDENTITY" = "-" ]; then
|
||||
if [[ "$IDENTITY" = "-" ]]; then
|
||||
warn "macOS: ad-hoc signing (no MACOS_SIGN_IDENTITY); artifacts are not notarized"
|
||||
elif [ -n "${APPLE_ID:-}" ] && [ -n "${APPLE_PASSWORD:-}" ] && [ -n "${APPLE_TEAM_ID:-}" ]; then
|
||||
elif [[ -n "${APPLE_ID:-}" ]] && [[ -n "${APPLE_PASSWORD:-}" ]] && [[ -n "${APPLE_TEAM_ID:-}" ]]; then
|
||||
NOTARIZE=1
|
||||
else
|
||||
warn "macOS: APPLE_ID / APPLE_PASSWORD / APPLE_TEAM_ID incomplete; signed but not notarized"
|
||||
@@ -57,7 +57,7 @@ fi
|
||||
echo "==> $APP_NAME $VERSION for macOS ($ARCH), identity: $IDENTITY, notarize: $NOTARIZE"
|
||||
|
||||
# ---- build -------------------------------------------------------------------------------------
|
||||
if [ "$SKIP_BUILD" = 0 ]; then
|
||||
if [[ "$SKIP_BUILD" = 0 ]]; then
|
||||
args=()
|
||||
for t in "${TARGETS[@]}"; do args+=(--target "$t"); done
|
||||
(cd "$ROOT" && cargo build --release --locked -p "$PKG" "${args[@]}")
|
||||
@@ -74,9 +74,9 @@ lipo -info "$WORK/bin/$PKG"
|
||||
sign() {
|
||||
# Hardened runtime + secure timestamp for a real identity; ad-hoc can't be timestamped.
|
||||
local ts=(--timestamp)
|
||||
[ "$IDENTITY" = "-" ] && ts=(--timestamp=none)
|
||||
[[ "$IDENTITY" = "-" ]] && ts=(--timestamp=none)
|
||||
local kc=()
|
||||
[ -n "${MACOS_KEYCHAIN:-}" ] && kc=(--keychain "$MACOS_KEYCHAIN")
|
||||
[[ -n "${MACOS_KEYCHAIN:-}" ]] && kc=(--keychain "$MACOS_KEYCHAIN")
|
||||
codesign --force --sign "$IDENTITY" ${kc[@]+"${kc[@]}"} "${ts[@]}" "$@"
|
||||
}
|
||||
|
||||
@@ -88,8 +88,8 @@ notarize() {
|
||||
echo "$out"
|
||||
id="$(printf '%s' "$out" | plutil -extract id raw -o - - 2>/dev/null || true)"
|
||||
status="$(printf '%s' "$out" | plutil -extract status raw -o - - 2>/dev/null || true)"
|
||||
if [ "$status" != "Accepted" ]; then
|
||||
if [ -n "$id" ]; then
|
||||
if [[ "$status" != "Accepted" ]]; then
|
||||
if [[ -n "$id" ]]; then
|
||||
xcrun notarytool log "$id" --apple-id "$APPLE_ID" --password "$APPLE_PASSWORD" --team-id "$APPLE_TEAM_ID" || true
|
||||
fi
|
||||
echo "error: notarization of $(basename "$file") failed (status: ${status:-unknown})" >&2
|
||||
@@ -123,7 +123,7 @@ sign --options runtime --entitlements "$HERE/entitlements.plist" "$APP/Contents/
|
||||
sign --options runtime --entitlements "$HERE/entitlements.plist" "$APP"
|
||||
codesign --verify --strict --deep --verbose=2 "$APP"
|
||||
|
||||
if [ "$NOTARIZE" = 1 ]; then
|
||||
if [[ "$NOTARIZE" = 1 ]]; then
|
||||
ditto -c -k --keepParent "$APP" "$WORK/notarize.zip"
|
||||
notarize "$WORK/notarize.zip"
|
||||
xcrun stapler staple "$APP"
|
||||
@@ -156,7 +156,7 @@ hdiutil_retry() {
|
||||
if hdiutil "$@"; then
|
||||
return 0
|
||||
fi
|
||||
[ "$attempt" = 5 ] && { echo "hdiutil $what failed 5 times" >&2; return 1; }
|
||||
[[ "$attempt" = 5 ]] && { echo "hdiutil $what failed 5 times" >&2; return 1; }
|
||||
warn "hdiutil $what failed (attempt $attempt of 5); retrying in $((attempt * 10))s"
|
||||
sleep $((attempt * 10))
|
||||
done
|
||||
@@ -164,7 +164,7 @@ hdiutil_retry() {
|
||||
hdiutil_retry create create -srcfolder "$STAGE" -volname "$APP_NAME" -fs HFS+ -format UDZO -imagekey zlib-level=9 -ov "$DMG"
|
||||
sign "$DMG"
|
||||
codesign --verify --strict --verbose=2 "$DMG"
|
||||
if [ "$NOTARIZE" = 1 ]; then
|
||||
if [[ "$NOTARIZE" = 1 ]]; then
|
||||
notarize "$DMG"
|
||||
xcrun stapler staple "$DMG"
|
||||
xcrun stapler validate "$DMG"
|
||||
|
||||
+13
-13
@@ -20,7 +20,7 @@ PKG=artcraft-launcher
|
||||
APP_NAME="ArtCraft Launcher"
|
||||
|
||||
ARCH=universal
|
||||
while [ $# -gt 0 ]; do
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--arch) ARCH="$2"; shift 2 ;;
|
||||
-h | --help) sed -n '2,14p' "$0"; exit 0 ;;
|
||||
@@ -37,13 +37,13 @@ esac
|
||||
DMG="$DIST/$PKG-$VERSION-macos-$ARCH.dmg"
|
||||
IDENTITY="${MACOS_SIGN_IDENTITY:--}"
|
||||
STRICT=0
|
||||
if [ "$IDENTITY" != "-" ] && [ -n "${APPLE_ID:-}" ] && [ -n "${APPLE_PASSWORD:-}" ] && [ -n "${APPLE_TEAM_ID:-}" ]; then
|
||||
if [[ "$IDENTITY" != "-" ]] && [[ -n "${APPLE_ID:-}" ]] && [[ -n "${APPLE_PASSWORD:-}" ]] && [[ -n "${APPLE_TEAM_ID:-}" ]]; then
|
||||
STRICT=1
|
||||
fi
|
||||
|
||||
FAILED=0
|
||||
fail() {
|
||||
if [ "$STRICT" = 1 ]; then
|
||||
if [[ "$STRICT" = 1 ]]; then
|
||||
echo "error: $*" >&2
|
||||
FAILED=1
|
||||
else
|
||||
@@ -54,19 +54,19 @@ fail() {
|
||||
# spctl's online ticket lookup can lag a few seconds behind notarytool's "Accepted".
|
||||
assess() {
|
||||
local out attempt tries=1
|
||||
[ "$STRICT" = 1 ] && tries=5
|
||||
[[ "$STRICT" = 1 ]] && tries=5
|
||||
for attempt in $(seq "$tries"); do
|
||||
if out="$(spctl "$@" 2>&1)"; then
|
||||
printf '%s\n' "$out"
|
||||
return 0
|
||||
fi
|
||||
[ "$attempt" = "$tries" ] || sleep 10
|
||||
[[ "$attempt" = "$tries" ]] || sleep 10
|
||||
done
|
||||
printf '%s\n' "$out"
|
||||
return 1
|
||||
}
|
||||
|
||||
if [ ! -f "$DMG" ]; then
|
||||
if [[ ! -f "$DMG" ]]; then
|
||||
echo "error: missing $DMG" >&2
|
||||
exit 1
|
||||
fi
|
||||
@@ -82,7 +82,7 @@ trap cleanup EXIT
|
||||
# ---- DMG ---------------------------------------------------------------------------------------
|
||||
echo "==> $(basename "$DMG")"
|
||||
codesign --verify --strict --verbose=2 "$DMG"
|
||||
if [ "$STRICT" = 1 ]; then
|
||||
if [[ "$STRICT" = 1 ]]; then
|
||||
xcrun stapler validate "$DMG" || fail "no notarization ticket stapled to the DMG"
|
||||
assess --assess --type open --context context:primary-signature -vv "$DMG" \
|
||||
|| fail "Gatekeeper rejects the DMG"
|
||||
@@ -93,7 +93,7 @@ mkdir -p "$MNT"
|
||||
hdiutil attach "$DMG" -readonly -nobrowse -noautoopen -mountpoint "$MNT" -quiet
|
||||
APP="$MNT/$APP_NAME.app"
|
||||
EXE="$APP/Contents/MacOS/$APP_NAME"
|
||||
if [ ! -x "$EXE" ]; then
|
||||
if [[ ! -x "$EXE" ]]; then
|
||||
echo "error: $(basename "$DMG") has no $APP_NAME.app/Contents/MacOS/$APP_NAME" >&2
|
||||
exit 1
|
||||
fi
|
||||
@@ -128,11 +128,11 @@ printf '%s\n' "$details" | grep -q '^Authority=Developer ID Application:' \
|
||||
|| fail "$APP_NAME.app is not signed with a Developer ID Application identity"
|
||||
printf '%s\n' "$details" | grep -q '^Timestamp=' \
|
||||
|| fail "$APP_NAME.app signature has no secure timestamp"
|
||||
if [ -n "${APPLE_TEAM_ID:-}" ]; then
|
||||
if [[ -n "${APPLE_TEAM_ID:-}" ]]; then
|
||||
printf '%s\n' "$details" | grep -qx "TeamIdentifier=$APPLE_TEAM_ID" \
|
||||
|| fail "$APP_NAME.app is signed by a different team"
|
||||
fi
|
||||
if [ "$STRICT" = 1 ]; then
|
||||
if [[ "$STRICT" = 1 ]]; then
|
||||
xcrun stapler validate "$APP" || fail "no notarization ticket stapled to $APP_NAME.app"
|
||||
if gk="$(assess --assess --type execute -vv "$APP")"; then
|
||||
printf '%s\n' "$gk"
|
||||
@@ -144,17 +144,17 @@ if [ "$STRICT" = 1 ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$FAILED" != 0 ]; then
|
||||
if [[ "$FAILED" != 0 ]]; then
|
||||
echo "error: macOS artifacts failed verification" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$STRICT" = 1 ]; then
|
||||
if [[ "$STRICT" = 1 ]]; then
|
||||
result="signed (Developer ID, hardened runtime), notarized; DMG and app stapled"
|
||||
else
|
||||
result="signature intact; Developer ID and notarization not required (no signing secrets)"
|
||||
fi
|
||||
echo "==> verified: $result"
|
||||
if [ -n "${GITHUB_STEP_SUMMARY:-}" ]; then
|
||||
if [[ -n "${GITHUB_STEP_SUMMARY:-}" ]]; then
|
||||
echo "- macOS \`$(basename "$DMG")\`: $result" >>"$GITHUB_STEP_SUMMARY"
|
||||
fi
|
||||
|
||||
@@ -11,13 +11,13 @@ set -euo pipefail
|
||||
. "$(dirname "${BASH_SOURCE[0]}")/../env.sh"
|
||||
HERE="$ROOT/packaging/web"
|
||||
|
||||
if [ "${1:-}" != "--skip-build" ]; then
|
||||
if [[ "${1:-}" != "--skip-build" ]]; then
|
||||
command -v trunk >/dev/null || { echo "error: trunk not found (cargo install trunk --locked)" >&2; exit 1; }
|
||||
(cd "$ROOT/apps/artcraft-launcher-web" && trunk build --release --cargo-profile wasm-release)
|
||||
fi
|
||||
|
||||
SITE="$ROOT/dist/web"
|
||||
[ -f "$SITE/index.html" ] || { echo "error: $SITE/index.html missing; run without --skip-build" >&2; exit 1; }
|
||||
[[ -f "$SITE/index.html" ]] || { echo "error: $SITE/index.html missing; run without --skip-build" >&2; exit 1; }
|
||||
# Paths must be relative so the site works under any prefix (public_url = "./" in Trunk.toml).
|
||||
if grep -Eq '(src|href)="/[^/]' "$SITE/index.html"; then
|
||||
echo "error: $SITE/index.html has root-absolute URLs; it would break when served from a sub-path" >&2
|
||||
@@ -30,16 +30,16 @@ fi
|
||||
MAX_WASM_BYTES="${ARTCRAFT_LAUNCHER_WASM_MAX_BYTES:-25165824}" # 24 MiB
|
||||
WASM_COUNT=0
|
||||
for wasm in "$SITE"/*.wasm; do
|
||||
[ -f "$wasm" ] || continue
|
||||
[[ -f "$wasm" ]] || continue
|
||||
WASM_COUNT=$((WASM_COUNT + 1))
|
||||
size=$(wc -c <"$wasm" | tr -d ' ')
|
||||
echo "$(basename "$wasm"): $size bytes ($((size / 1048576)) MiB; limit $MAX_WASM_BYTES)"
|
||||
if [ "$size" -gt "$MAX_WASM_BYTES" ]; then
|
||||
if [[ "$size" -gt "$MAX_WASM_BYTES" ]]; then
|
||||
echo "error: $(basename "$wasm") is $size bytes, over the $MAX_WASM_BYTES-byte limit (Cloudflare's per-file cap is 25 MiB)" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
[ "$WASM_COUNT" -gt 0 ] || { echo "error: no .wasm in $SITE" >&2; exit 1; }
|
||||
[[ "$WASM_COUNT" -gt 0 ]] || { echo "error: no .wasm in $SITE" >&2; exit 1; }
|
||||
|
||||
NAME="artcraft-launcher-web-$VERSION"
|
||||
WORK="$CARGO_TARGET_DIR/web-package"
|
||||
|
||||
Reference in New Issue
Block a user