Repository navigation
Expand file tree
/
Copy pathinstall.sh
More file actions
executable file
·293 lines (249 loc) · 10.2 KB
/
Copy pathinstall.sh
File metadata and controls
executable file
·293 lines (249 loc) · 10.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
#!/usr/bin/env bash
# Installs the latest Flyback release for this machine, or updates a copy it installed.
#
# curl -fsSL https://raw.githubusercontent.com/markovcd/Flyback/main/install.sh | bash
# curl -fsSL https://raw.githubusercontent.com/markovcd/Flyback/main/install.sh | bash -s -- --uninstall
#
# FLYBACK_VERSION=0.4.0 installs that release instead. FLYBACK_DIR is where the copy
# goes: a folder on Linux and Windows, the .app on macOS. --no-links writes only the copy,
# with no command links, menu entry, shortcut or PATH change, so a trial leaves a real
# install as it was. --uninstall removes the copy and everything this script put beside it.
set -euo pipefail
repo=markovcd/Flyback
# The public half of the key releases are signed with; src/Flyback.Editor/Updates/release-key.pem.
release_key='-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAErHgbPx4egI1i8sCs5r+mRZBaGFB3
xktcInkfCBtFj5RIKR6nb326r7tyR6kjNSOJ/O8DnblXrd+mtnvG1/toug==
-----END PUBLIC KEY-----'
say() { printf '%s\n' "$*" >&2; }
die() { say "flyback: $*"; exit 1; }
need() { command -v "$1" >/dev/null 2>&1 || die "this needs $1, which is not installed"; }
uninstall=false
links=true
for argument in "$@"; do
case "$argument" in
--uninstall) uninstall=true ;;
--no-links) links=false ;;
-h | --help)
say "usage: install.sh [--uninstall] [--no-links]"
say " FLYBACK_VERSION the release to install, rather than the latest"
say " FLYBACK_DIR where the copy goes (the .app on macOS)"
say " --no-links write only the copy: no command links, menu entry, shortcut or PATH change"
exit 0
;;
*) die "unknown option $argument" ;;
esac
done
case "$(uname -s)" in
Linux) os=linux ;;
Darwin) os=osx ;;
MINGW* | MSYS* | CYGWIN*) os=win ;;
*) die "no release is built for $(uname -s)" ;;
esac
case "$(uname -m)" in
x86_64 | amd64) arch=x64 ;;
arm64 | aarch64) arch=arm64 ;;
*) die "no release is built for $(uname -m)" ;;
esac
# A shell under Rosetta reports x86_64 on Apple silicon.
if [ "$os" = osx ] && [ "$(sysctl -n hw.optional.arm64 2>/dev/null || echo 0)" = 1 ]; then
arch=arm64
fi
rid=$os-$arch
bin=$HOME/.local/bin
applications=${XDG_DATA_HOME:-$HOME/.local/share}/applications
case "$os" in
linux)
plugins=plugins
dest=${FLYBACK_DIR:-${XDG_DATA_HOME:-$HOME/.local/share}/flyback}
programs=$dest
shell=Flyback
;;
osx)
plugins=Contents/MacOS/plugins
if [ -n "${FLYBACK_DIR:-}" ]; then
dest=$FLYBACK_DIR
elif [ -w /Applications ] || [ -d /Applications/Flyback.app ]; then
dest=/Applications/Flyback.app
else
dest=$HOME/Applications/Flyback.app
fi
programs=$dest/Contents/MacOS
shell=Flyback
;;
win)
plugins=plugins
dest=${FLYBACK_DIR:-$(cygpath -u "$LOCALAPPDATA")/Programs/Flyback}
programs=$dest
shell=Flyback.exe
windows=$(cygpath -w "$dest")
;;
esac
commands=(flyback-cli flyback-viewer)
[ "$os" = linux ] && commands+=(flyback)
# What a command in $bin links to.
target() { case "$1" in flyback) echo "$programs/Flyback" ;; *) echo "$programs/$1" ;; esac; }
# Files a running copy has open cannot be replaced or removed on Windows, and elsewhere
# it would go on running the old version beside the new one.
refuse_if_running() {
[ -d "$dest" ] || return 0
if [ "$os" = win ]; then
running=$(powershell.exe -NoProfile -Command \
"(Get-Process | Where-Object { \$_.Path -like '$windows\\*' }).Count" | tr -d '\r')
[ "${running:-0}" = 0 ] || die "Flyback is running from $dest; close it first"
elif [ "$os" = linux ]; then
# By executable, since a command started through its link in $bin names the link.
for exe in /proc/[0-9]*/exe; do
case "$(readlink "$exe" 2>/dev/null)" in
"$programs"/*) die "Flyback is running from $dest; close it first" ;;
esac
done
elif pgrep -f "$programs/" >/dev/null; then
die "Flyback is running from $dest; close it first"
fi
}
if $uninstall; then
refuse_if_running
removed=false
# Only a folder holding Flyback is removed, so a mistyped FLYBACK_DIR takes nothing with it.
if [ -d "$dest" ]; then
[ -f "$programs/$shell" ] || die "$dest does not hold Flyback, so it is left alone"
rm -rf "${dest:?}"
say "Removed $dest"
removed=true
fi
# A link, desktop entry or shortcut is only this script's if it points into this copy.
if [ "$os" = win ]; then
report=$(powershell.exe -NoProfile -Command "
\$ErrorActionPreference = 'Stop'
\$link = [Environment]::GetFolderPath('Programs') + '\\Flyback.lnk'
if ((Test-Path \$link) -and (New-Object -ComObject WScript.Shell).CreateShortcut(\$link).TargetPath -eq '$windows\\Flyback.exe') {
Remove-Item \$link
'Removed ' + \$link
}
\$path = @([Environment]::GetEnvironmentVariable('Path', 'User') -split ';' | Where-Object { \$_ })
if (\$path -contains '$windows') {
[Environment]::SetEnvironmentVariable('Path', (@(\$path | Where-Object { \$_ -ne '$windows' }) -join ';'), 'User')
'Removed $windows from PATH'
}" | tr -d '\r') || die "could not remove the Start menu shortcut or the PATH entry"
if [ -n "$report" ]; then
say "$report"
removed=true
fi
else
for command in "${commands[@]}"; do
link=$bin/$command
if [ -L "$link" ] && [ "$(readlink "$link")" = "$(target "$command")" ]; then
rm -f "$link"
say "Removed $link"
removed=true
fi
done
entry=$applications/flyback.desktop
if [ "$os" = linux ] && [ -f "$entry" ] && grep -qF "Exec=\"$programs/Flyback\"" "$entry"; then
rm -f "$entry"
say "Removed $entry"
removed=true
fi
fi
$removed || say "Flyback is not installed in $dest."
exit 0
fi
need curl
need unzip
need openssl
case "$rid" in
linux-x64 | osx-arm64 | win-x64) ;;
*) die "releases are built for linux-x64, osx-arm64 and win-x64, not $rid; see https://github.com/$repo#build-and-publish to build it" ;;
esac
version=${FLYBACK_VERSION:-}
if [ -z "$version" ]; then
latest=$(curl -fsSLI -o /dev/null -w '%{url_effective}' "https://github.com/$repo/releases/latest") \
|| die "could not reach GitHub"
version=${latest##*/}
fi
version=${version#v}
[[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || die "'$version' is not a release version"
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT
base=https://github.com/$repo/releases/download/v$version
package=flyback-$version-$rid.zip
say "Downloading Flyback $version for $rid"
curl -fL --progress-bar -o "$tmp/$package" "$base/$package" || die "v$version has no $package"
curl -fsSL -o "$tmp/SHA256SUMS" "$base/SHA256SUMS" || die "v$version has no SHA256SUMS"
curl -fsSL -o "$tmp/SHA256SUMS.sig" "$base/SHA256SUMS.sig" || die "v$version has no SHA256SUMS.sig"
printf '%s\n' "$release_key" > "$tmp/release-key.pem"
openssl dgst -sha256 -verify "$tmp/release-key.pem" -signature "$tmp/SHA256SUMS.sig" "$tmp/SHA256SUMS" >/dev/null 2>&1 \
|| die "SHA256SUMS is not signed with Flyback's release key"
expected=$(awk -v f="$package" '$2 == f || $2 == "*" f { print $1 }' "$tmp/SHA256SUMS")
actual=$(openssl dgst -sha256 -r "$tmp/$package" | cut -d' ' -f1)
[ -n "$expected" ] && [ "$expected" = "$actual" ] || die "$package does not match its checksum"
unzip -q "$tmp/$package" -d "$tmp/unpacked"
payload=$tmp/unpacked/$rid
[ "$os" = osx ] && payload=$payload/Flyback.app
[ -d "$payload" ] || die "$package does not hold $rid"
refuse_if_running
# Replaced over what is there, so a plugin somebody added stays; a plugin the release
# ships is replaced whole, so none of its old assemblies load beside the new ones.
mkdir -p "$dest"
for plugin in "$payload/$plugins"/*/; do
[ -d "$plugin" ] && rm -rf "${dest:?}/$plugins/$(basename "$plugin")"
done
cp -R "$payload/." "$dest/"
# Apple silicon starts nothing unsigned, and the bundle is built on Linux, which cannot sign.
if [ "$os" = osx ]; then
xattr -cr "$dest" 2>/dev/null || true
chmod +x "$programs/Flyback" "$programs/flyback-cli" "$programs/flyback-viewer"
# A plugin installed into the bundle breaks its signature, and codesign refuses its
# dotted folder name; the editor keeps installed plugins in its data folder instead.
installed=${XDG_CONFIG_HOME:-$HOME/.config}/Flyback/plugins
while IFS= read -r plugin; do
name=$(basename "$plugin")
[ -d "$payload/$plugins/$name" ] && continue
mkdir -p "$installed"
if [ -d "$installed/$name" ]; then rm -rf "$plugin"; else mv "$plugin" "$installed/$name"; fi
done < <(find "$dest/$plugins" -mindepth 1 -maxdepth 1 -type d 2>/dev/null)
codesign --force --deep --sign - "$dest" >/dev/null 2>&1 || die "could not sign $dest; run: codesign --force --deep --sign - \"$dest\""
fi
on_path() { case ":$PATH:" in *":$1:"*) return 0 ;; esac; return 1; }
# The commands on PATH, the menu entry and the shortcut: what makes it the machine's Flyback.
put_beside() {
case "$os" in
linux | osx)
mkdir -p "$bin"
for command in "${commands[@]}"; do ln -sf "$(target "$command")" "$bin/$command"; done
if [ "$os" = linux ]; then
mkdir -p "$applications"
cat > "$applications/flyback.desktop" <<EOF
[Desktop Entry]
Type=Application
Name=Flyback
Comment=A patchable synthesizer for picture and sound
Exec="$programs/Flyback" %f
Icon=$programs/flyback.png
Terminal=false
Categories=AudioVideo;Audio;Graphics;
EOF
fi
on_path "$bin" || say "Add $bin to PATH for flyback-cli and flyback-viewer."
;;
win)
powershell.exe -NoProfile -Command "
\$ErrorActionPreference = 'Stop'
\$shortcut = (New-Object -ComObject WScript.Shell).CreateShortcut([Environment]::GetFolderPath('Programs') + '\\Flyback.lnk')
\$shortcut.TargetPath = '$windows\\Flyback.exe'
\$shortcut.WorkingDirectory = '$windows'
\$shortcut.Save()
\$path = @([Environment]::GetEnvironmentVariable('Path', 'User') -split ';' | Where-Object { \$_ })
if (\$path -notcontains '$windows') {
[Environment]::SetEnvironmentVariable('Path', ((\$path + '$windows') -join ';'), 'User')
}" >/dev/null || say "Could not add the Start menu shortcut or put $windows on PATH."
;;
esac
}
if $links; then
put_beside
else
say "--no-links: no command link, menu entry, shortcut or PATH entry was written."
fi
say "Flyback $version is installed in $dest."