metadata.json claimed MIT while no LICENSE file existed, so the repository had no effective licence at all. It is now GPL-3.0-or-later: the full FSF text in LICENSE, the identifier in metadata.json, and REUSE-style SPDX-FileCopyrightText and SPDX-License-Identifier headers on all 33 source files, which is the convention KDE itself uses. Worth recording that GPL-3.0 is a preference here rather than an obligation. Nothing in the dependency graph forces it: linkctl is MIT and is invoked as a subprocess rather than linked, and the KDE QML modules the applet imports are LGPL. Removed two things that should not ship in a public repository. tests's QML linter had an absolute sandbox path, /tmp/pyside/PySide6/qmllint, baked into its search list; it now looks for Qt's qmllint and PySide6's pyside6-qmllint on PATH and honours a QMLLINT override, and says how to install one when it finds nothing. The systemd unit carried a Documentation= URL pointing at a host that does not exist, and its ExecStart silently assumed a particular checkout directory; the URL is gone and the path is now an obvious PATH/TO/CHECKOUT placeholder next to the comment telling you to edit it. Added a Credits section naming the work this is built on, since the project is mostly glue: linkctl does the camera control, schlarpc's firmware teardown documents the extension-unit protocol, and vrwallace's controller established that the Link 2 needs both pan and tilt axes in a single VIDIOC_S_EXT_CTRLS call, which is the detail the whole thing depends on. Also made the two Python tests executable, matching the other scripts. Audited for operator-specific detail: no credentials, tokens, private hostnames, IP addresses or absolute home paths in any tracked file or anywhere in the history's diffs. Three merge commits do carry Reviewed-on and Reviewed-by trailers naming a private forge, added by the forge at merge time; those are commit metadata rather than file content and are handled separately. Tested: 169 checks across seven suites plus the QML linter, all passing after the header insertion, which confirms it did not disturb the order-sensitive `.pragma library` line in logic.js or the qmldir module definitions. shellcheck clean.
128 lines
4.7 KiB
Bash
Executable File
128 lines
4.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# SPDX-FileCopyrightText: 2026 branderson
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
#
|
|
# Checks the binary-resolution command the Plasma widget generates, by running
|
|
# it in a real shell against real directories.
|
|
#
|
|
# This matters because Plasma executes applet commands through
|
|
# KProcess::setShellCommand, which is `/bin/sh -c` inheriting plasmashell's
|
|
# environment. That environment never sources a login profile, so anything
|
|
# that puts linkctl on PATH from a shell rc file (mise or asdf shims,
|
|
# ~/.local/bin on some distributions) is invisible to the widget even though
|
|
# it works in a terminal. tests/test_joystick_logic.js checks the string's
|
|
# shape; this checks that the string actually finds a binary.
|
|
#
|
|
# Run: tests/test_resolver.sh
|
|
|
|
set -uo pipefail
|
|
|
|
HERE=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
|
|
readonly HERE
|
|
readonly LOGIC="$HERE/../plasmoid/package/contents/ui/logic.js"
|
|
|
|
command -v node >/dev/null 2>&1 || { echo "SKIP: node not available"; exit 0; }
|
|
|
|
# Ask logic.js for the command, so the test cannot drift from the shipped code.
|
|
CMD=$(node -e '
|
|
const fs = require("fs"), vm = require("vm");
|
|
const src = fs.readFileSync(process.argv[1], "utf8");
|
|
const ctx = { Math, JSON, String };
|
|
vm.createContext(ctx);
|
|
vm.runInContext(src.replace(/^\s*\.pragma\s+library\s*$/m, ""), ctx);
|
|
process.stdout.write(ctx.resolveCommand("linkctl"));
|
|
' "$LOGIC")
|
|
|
|
[[ -n $CMD ]] || { echo "FAIL: could not generate the resolver command"; exit 1; }
|
|
|
|
TMP=$(mktemp -d)
|
|
cleanup() { rm -rf "$TMP"; }
|
|
trap cleanup EXIT
|
|
|
|
checks=0
|
|
failures=0
|
|
|
|
# Run the generated command with a deliberately bare environment, the way
|
|
# plasmashell would, and compare what it prints.
|
|
expect() {
|
|
local label=$1 home=$2 path=$3 want=$4 got
|
|
checks=$((checks + 1))
|
|
got=$(env -i HOME="$home" PATH="$path" sh -c "$CMD" 2>/dev/null)
|
|
if [[ $got == "$want" ]]; then
|
|
printf 'ok %s\n' "$label"
|
|
else
|
|
failures=$((failures + 1))
|
|
printf 'FAIL %s\n got: %s\n want: %s\n' \
|
|
"$label" "${got:-<empty>}" "${want:-<empty>}"
|
|
fi
|
|
}
|
|
|
|
make_exe() { mkdir -p "$(dirname "$1")" && printf '#!/bin/sh\n' > "$1" && chmod +x "$1"; }
|
|
|
|
mkdir -p "$TMP/empty"
|
|
|
|
# On PATH: the ordinary case, and the only one that worked before the fix.
|
|
make_exe "$TMP/onpath/linkctl"
|
|
expect "finds it on PATH" "$TMP/empty" "$TMP/onpath:/usr/bin:/bin" \
|
|
"$TMP/onpath/linkctl"
|
|
|
|
# Not on PATH at all. These are the cases that broke the widget while the
|
|
# terminal kept working.
|
|
make_exe "$TMP/mise/.local/share/mise/shims/linkctl"
|
|
expect "finds a mise shim off PATH" "$TMP/mise" "/usr/bin:/bin" \
|
|
"$TMP/mise/.local/share/mise/shims/linkctl"
|
|
|
|
make_exe "$TMP/localbin/.local/bin/linkctl"
|
|
expect "finds ~/.local/bin off PATH" "$TMP/localbin" "/usr/bin:/bin" \
|
|
"$TMP/localbin/.local/bin/linkctl"
|
|
|
|
make_exe "$TMP/cargo/.cargo/bin/linkctl"
|
|
expect "finds ~/.cargo/bin off PATH" "$TMP/cargo" "/usr/bin:/bin" \
|
|
"$TMP/cargo/.cargo/bin/linkctl"
|
|
|
|
# Absent entirely: must print nothing, so the widget can say so plainly
|
|
# rather than running something unexpected.
|
|
expect "prints nothing when absent" "$TMP/empty" "/usr/bin:/bin" ""
|
|
|
|
# Present but not executable must not count as found.
|
|
mkdir -p "$TMP/noexec/.local/bin"
|
|
printf 'not executable' > "$TMP/noexec/.local/bin/linkctl"
|
|
expect "ignores a non-executable file" "$TMP/noexec" "/usr/bin:/bin" ""
|
|
|
|
# PATH wins over the fallback list, so an explicit install is preferred.
|
|
make_exe "$TMP/both/.local/bin/linkctl"
|
|
make_exe "$TMP/preferred/linkctl"
|
|
expect "prefers PATH over the search list" "$TMP/both" \
|
|
"$TMP/preferred:/usr/bin:/bin" "$TMP/preferred/linkctl"
|
|
|
|
# The uniqueness suffix exists only to change the string Plasma keys its
|
|
# command containers by. It must not change what the shell actually runs.
|
|
ask_logic() {
|
|
node -e '
|
|
const fs = require("fs"), vm = require("vm");
|
|
const src = fs.readFileSync(process.argv[1], "utf8");
|
|
const ctx = { Math, JSON, String };
|
|
vm.createContext(ctx);
|
|
vm.runInContext(src.replace(/^\s*\.pragma\s+library\s*$/m, ""), ctx);
|
|
const tick = ctx.tickCommand(ctx.baseCmd("echo", ""), 1, 1, 6, false, false);
|
|
process.stdout.write(process.argv[2] === "unique"
|
|
? ctx.uniqueify(tick, 42) : tick);
|
|
' "$LOGIC" "$1"
|
|
}
|
|
|
|
checks=$((checks + 1))
|
|
plain_out=$(sh -c "$(ask_logic plain)" 2>&1)
|
|
unique_out=$(sh -c "$(ask_logic unique)" 2>&1)
|
|
if [[ $plain_out == "$unique_out" && -n $plain_out ]]; then
|
|
printf 'ok %s\n' "uniqueness suffix does not change what the shell runs"
|
|
else
|
|
failures=$((failures + 1))
|
|
printf 'FAIL %s\n plain: %s\n unique: %s\n' \
|
|
"uniqueness suffix does not change what the shell runs" \
|
|
"$plain_out" "$unique_out"
|
|
fi
|
|
|
|
printf '\n%d checks, %d failures\n' "$checks" "$failures"
|
|
[[ $failures -eq 0 ]]
|