management.am 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. #!/usr/bin/env bash
  2. ##############################################################################################
  3. # THIS MODULE INCLUDES ALL THE ACTIONS INTENDED TO MANAGE THE APPS INSTALLED FROM THE DATABASE
  4. # AND ONE OPTION IS FOR LOCAL APPIMAGES INSTEAD
  5. # OPTIONS: BACKUP/RESTORE, DOWNGRADE, LAUNCHER, LOCK/UNLOCK, NOLIBFUSE, REMOVE
  6. ##############################################################################################
  7. # BACKUP
  8. _backup_name() {
  9. printf "\n ◆ To set date and time as a name, press ENTER (default)\n ◆ To set the version as a name, press \"1\"\n ◆ To set a custom name, write anything else\n\n"
  10. read -r -p " Write your choice here, or leave blank to use \"date/time\": " response
  11. case "$response" in
  12. '')
  13. backupname=$(date +%F-%X | sed 's/://g' | sed 's/-//g')
  14. ;;
  15. '1')
  16. _check_version
  17. backupname=$(grep -w " ◆ $app_name |" "$AMCACHEDIR"/version-args 2>/dev/null | sed 's:.*| ::')
  18. ;;
  19. *)
  20. backupname="$(echo "$response" | sed 's/ /_/g')"
  21. ;;
  22. esac
  23. }
  24. _backup() {
  25. if [ ! -f "$argpath"/remove ]; then
  26. echo " \"$2\" is not a valid argument or is not installed."
  27. else
  28. read -r -p " Do you wish to backup the current version of $2? (y/N) " yn
  29. if ! echo "$yn" | grep -i '^y' >/dev/null 2>&1; then
  30. printf "\n OPERATION ABORTED!\n\n"
  31. else
  32. mkdir -p "$HOME/.am-snapshots/$2"
  33. app_name="$2"
  34. _backup_name
  35. if test -d "$HOME/.am-snapshots/$2/$backupname"; then
  36. echo " 💀 ERROR: \"$2/$backupname\" already exists, ABORTED!"
  37. echo "$DIVIDING_LINE"
  38. return 1
  39. else
  40. cp -r "$argpath" "$HOME/.am-snapshots/$2/$backupname"
  41. fi
  42. echo " SAVED in $HOME/.am-snapshots/$2/$backupname"
  43. fi
  44. fi
  45. echo "$DIVIDING_LINE"
  46. }
  47. # RESTORE
  48. _overwrite() {
  49. if [ ! -d "$HOME/.am-snapshots/$2" ]; then
  50. echo " ERROR: No snapshot for \"$2\" found."
  51. elif [ ! -f "$argpath"/remove ]; then
  52. echo " \"$2\" is not a valid argument or is not installed."
  53. else
  54. read -r -p " Do you wish to overwrite $2 with an older version? (y,N) " yn
  55. if ! echo "$yn" | grep -i '^y' >/dev/null 2>&1; then
  56. printf "\n OPERATION ABORTED! \n\n"
  57. else
  58. printf "\n Please, select a snapshot or press CTRL+C to abort:\n\n"
  59. sleep 1
  60. select d in "$HOME/.am-snapshots/$2"/*; do
  61. test -n "$d" && break
  62. echo ">>> Invalid Selection"
  63. done
  64. cp -r --backup=t "$d"/* "$argpath"/
  65. rm -R -f ./tmp "$argpath"/*~
  66. echo " RESTORE COMPLETED SUCCESSFULLY!"
  67. fi
  68. fi
  69. echo "$DIVIDING_LINE"
  70. }
  71. # DOWNGRADE
  72. _downgrade_entries_filter() {
  73. grep -vi "^#\|version0=" ./AM-updater | grep "api.github.com" \
  74. | tr '=' '\n' | sed 's/^\$(//g' \
  75. | tail -1 | sed '1,${
  76. s/version=$(//g
  77. s/head -1)/head -100/g
  78. s#$REPO#'"$REPO"'#g
  79. s#releases/latest#releases#g
  80. s#releases #releases?per_page=100 #g
  81. s#/$tag/$app##g
  82. }'
  83. }
  84. _downgrade() {
  85. # Safety checks
  86. if [ ! -f "$argpath"/AM-updater ]; then
  87. echo " No AM-updater available for \"$2\", cannot detect any URL!"
  88. return 1
  89. elif [ -f "$argpath"/"$2"-rollback ]; then
  90. cd "$argpath" || return 1
  91. ./"$2"-rollback || return 1
  92. return 0
  93. elif ! grep -q "api.github.com" "$argpath"/AM-updater; then
  94. echo " The option \"$1\" only works for https://github.com"
  95. return 1
  96. fi
  97. cd "$argpath" || return 1
  98. entries_list=$(_downgrade_entries_filter)
  99. urls="$(eval "$entries_list" 2>/dev/null | grep -vi "zsync$")"
  100. if ! echo "$urls" | grep -q "[0-9]"; then
  101. echo " ERROR: No valid links found, sorry!"
  102. exit 1
  103. fi
  104. echo "$DIVIDING_LINE"
  105. echo "You have chosen to roll back \"$2\"."
  106. echo "$DIVIDING_LINE"
  107. echo "Select a URL from this menu (read carefully) or press CTRL+C to abort:"
  108. echo "$DIVIDING_LINE"
  109. sleep 1
  110. select d in $urls; do
  111. test -n "$d" && break
  112. echo ">>> Invalid Selection"
  113. done
  114. cp ./AM-updater ./AM-rollback
  115. sed -i 's/version0/#version0/g' ./AM-rollback
  116. sed -i 's/appimageupdatetool -Or/#appimageupdatetool -Or/g' ./AM-rollback
  117. [ -f ./"$2".zsync ] && mv ./"$2".zsync ./"$2".zsync.old
  118. if wget --version | head -1 | grep -q ' 1.'; then
  119. sed -i '/wget/c\wget -q --show-progress '"$d"'' ./AM-rollback
  120. else
  121. sed -i '/wget/c\wget '"$d"'' ./AM-rollback
  122. fi
  123. ./AM-rollback && rm -f ./AM-rollback || return 1
  124. mv ./"$2".zsync.old ./"$2".zsync 2>/dev/null
  125. echo "$d" > ./version
  126. echo "ROLLBACK SUCCESSFUL!"
  127. }
  128. # ICON THEME CHANGER
  129. _icon_theme() {
  130. if [ "$AMCLI" = am ] && test -f /usr/local/share/applications/"$arg"*AM.desktop; then
  131. $SUDOCMD sed -i "s#Icon=$argpath/icons/#Icon=#g" /usr/local/share/applications/"$arg"*AM.desktop 2>/dev/null
  132. else
  133. sed -i "s#Icon=$argpath/icons/#Icon=#g" "$DATADIR"/applications/"$arg"*AM.desktop 2>/dev/null
  134. fi
  135. _icon_theme_export_to_datadir 2>/dev/null
  136. }
  137. # LAUNCHER
  138. _launcher_appimage_integration() {
  139. "$arg" --appimage-extract *.desktop 1>/dev/null && mv ./squashfs-root/*.desktop ./"$appimage".desktop
  140. "$arg" --appimage-extract .DirIcon 1>/dev/null && mv ./squashfs-root/.DirIcon ./DirIcon
  141. COUNT=0
  142. while [ "$COUNT" -lt 10 ]; do # Tries to get the actual icon/desktop if it is a symlink to another symlink
  143. if [ -L ./"$appimage".desktop ]; then
  144. LINKPATH="$(readlink ./"$appimage".desktop | sed 's|^\./||' 2>/dev/null)"
  145. "$arg" --appimage-extract "$LINKPATH" 1>/dev/null && mv ./squashfs-root/"$LINKPATH" ./"$appimage".desktop
  146. fi
  147. if [ -L ./DirIcon ]; then
  148. LINKPATH="$(readlink ./DirIcon | sed 's|^\./||' 2>/dev/null)"
  149. "$arg" --appimage-extract "$LINKPATH" 1>/dev/null && mv ./squashfs-root/"$LINKPATH" "$DATADIR"/icons/ 1>/dev/null
  150. fi
  151. [ ! -L ./"$appimage".desktop ] && [ ! -L ./DirIcon ] && break
  152. COUNT=$((COUNT + 1))
  153. done
  154. sed -i "s#Exec=[^ ]*#Exec=\"$arg\"#g" ./"$appimage".desktop
  155. mv ./"$appimage".desktop "$DATADIR"/applications/AppImages/"$appimage"-AM.desktop
  156. rm -R -f ./squashfs-root ./DirIcon
  157. }
  158. _launcher_appimage_bin() {
  159. mkdir -p "$BINDIR"
  160. if ! echo "$PATH" | grep "$BINDIR" >/dev/null 2>&1; then
  161. echo "$DIVIDING_LINE"
  162. echo "WARNING: \"$BINDIR\" is not in PATH, apps may not run from command line." | _fit
  163. echo "$DIVIDING_LINE"
  164. fi
  165. read -r -p " Write a custom command to launch the app, or leave blank: " response
  166. if [ -z "$response" ]; then
  167. appimage_cmd=$(echo "$appimage" | tr '[:upper:]' '[:lower:]')
  168. if ! echo "$appimage" | grep -q -i ".appimage"; then
  169. [ -n "$BINDIR" ] && printf '#!/bin/sh\n%s' "$arg" >> "$BINDIR"/"$appimage_cmd".appimage \
  170. && chmod a+x "$BINDIR"/"$appimage_cmd".appimage && echo "New command: \"$appimage_cmd.appimage\" in $BINDIR" | _fit
  171. else
  172. [ -n "$BINDIR" ] && printf '#!/bin/sh\n%s' "$arg" >> "$BINDIR"/"$appimage_cmd" \
  173. && chmod a+x "$BINDIR"/"$appimage_cmd" && echo "New command: \"$appimage_cmd\" in $BINDIR" | _fit
  174. fi
  175. elif command -v "$response" 1>/dev/null; then
  176. if test -f "$BINDIR"/"$response"; then
  177. echo "WARNING, \"$BINDIR/$response\" already exists!" | _fit
  178. read -r -p " Do you Want to override it? (y,N) " yn
  179. if echo "$yn" | grep -i '^y' >/dev/null 2>&1; then
  180. ln -sf "$arg" "$BINDIR"/"$response"
  181. else
  182. echo " You have chosen not to assign a command."
  183. fi
  184. else
  185. echo "ERROR: the \"$response\" command alredy exists, retry!" | _fit
  186. fi
  187. else
  188. [ -n "$BINDIR" ] && ln -sf "$arg" "$BINDIR"/"$response"
  189. fi
  190. }
  191. _launcher(){
  192. _clean_launchers 2>/dev/null 1>/dev/null
  193. if [ ! -f "$arg" ]; then
  194. echo "ERROR: \"$arg\" not found" | _fit
  195. elif ! grep -Eaoq -m 1 'github.com/AppImage/AppImageKit/wiki/FUSE' "$arg" 2>/dev/null; then
  196. echo "ERROR: \"$arg\" is NOT an AppImage" | _fit
  197. else
  198. printf " ◆ File: %s\n" "$arg"
  199. appimage=$(basename -- "$arg")
  200. mkdir -p "$DATADIR"/applications/AppImages
  201. mkdir -p "$DATADIR"/icons
  202. chmod a+x "$arg"
  203. cd "$(dirname "$arg")" || return
  204. _launcher_appimage_integration 2>/dev/null
  205. _launcher_appimage_bin
  206. fi
  207. }
  208. # LOCK/UNLOCK
  209. _lock() {
  210. if [ ! -f "$argpath"/AM-updater ]; then
  211. echo " \"$AMCLIUPPER\" cannot manage updates for $2, \"AM-updater\" file not found!"
  212. return 1
  213. fi
  214. read -r -p " Do you wish to keep $2 at its current version? (y/N) " yn
  215. if echo "$yn" | grep -i '^y' >/dev/null 2>&1; then
  216. mv "$argpath"/AM-updater "$argpath"/AM-LOCK 1>/dev/null
  217. echo " $2 has been locked at current version!"
  218. else
  219. echo " Operation aborted!"
  220. return 1
  221. fi
  222. }
  223. _unlock() {
  224. if ! test -f "$argpath"/AM-LOCK; then
  225. echo " \"$2\" cannot be unlocked, \"AM-LOCK\" file not found!"
  226. return 1
  227. fi
  228. read -r -p " Do you wish to unlock updates for $2? (Y/n) " yn
  229. if echo "$yn" | grep -i '^n' >/dev/null 2>&1; then
  230. echo " $2 is still locked at current version!"
  231. return 1
  232. else
  233. mv "$argpath"/AM-LOCK "$argpath"/AM-updater 1>/dev/null
  234. echo " \"$2\" can now receive updates!"
  235. fi
  236. }
  237. # NO LIBFUSE
  238. _nolibfuse() {
  239. AMCLIPATH_ORIGIN="$AMCLIPATH"
  240. target="$(echo "${2}" | tr '[:lower:]' '[:upper:]')"
  241. # safety checks
  242. if ! cd "$argpath" 2>/dev/null; then
  243. echo " ⚠️ Error: \"$target\" is NOT installed."
  244. return 1
  245. fi
  246. if ! grep -Eaoq -m 1 'github.com/AppImage/AppImageKit/wiki/FUSE' ./"$arg" 2>/dev/null; then
  247. echo " ⚠️ Error: $target is NOT an AppImage."
  248. return 1
  249. elif file "$arg" 2>/dev/null | grep -qi "static"; then
  250. echo " ◆ $target is already a new generation AppImage."
  251. return 1
  252. elif test -f ./*.zsync; then
  253. echo "$DIVIDING_LINE"
  254. echo " Warning! Your AppImage uses \"zsync\" to update."
  255. echo " The .zsync file will be removed and will no longer work"
  256. echo " your \"AM-updater\" will likely still be able to update the AppImage"
  257. echo " by comparing the old vs new version url, but it is not guaranteed"
  258. read -r -p " Do you want to proceede anyway? (N/y) " yn
  259. if ! echo "$yn" | grep -i '^y' >/dev/null 2>&1; then
  260. return 1
  261. fi
  262. fi
  263. # If appimagetool is not installed, download it
  264. if ! command -v appimagetool 1>/dev/null; then
  265. _online_check
  266. printf " ...downloading appimagetool\r"
  267. wget -q "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-$ARCH.AppImage" -O ./appimagetool || return 1
  268. chmod a+x ./appimagetool
  269. fi
  270. # Extract the AppImage
  271. printf " ...extracting the AppImage\r"
  272. ./"$2" --appimage-extract >/dev/null 2>&1 && chmod 0755 ./squashfs-root
  273. # Convert the AppImage to a new generation one
  274. printf " ...trying to convert in new generation AppImage\r"
  275. if [ -f ./appimagetool ]; then
  276. PATH="$PATH:$PWD" ARCH="$(uname -m)" ./appimagetool -n ./squashfs-root >/dev/null 2>&1
  277. elif command -v appimagetool 1>/dev/null; then
  278. ARCH="$(uname -m)" appimagetool -n ./squashfs-root >/dev/null 2>&1
  279. fi
  280. # Test if the AppImage has been created
  281. if ! test -f ./*.AppImage; then
  282. echo " 💀Error when trying to convert $target. Operation Aborted."
  283. rm -Rf ./appimagetool ./squashfs-root
  284. return 1
  285. fi
  286. mv ./"$2" ./"$2".old && mv ./*.AppImage ./"$2" || return 1
  287. echo " ◆ $target has been converted to a new generation AppImage."
  288. rm -Rf ./appimagetool ./squashfs-root ./*.zsync
  289. if [ -f ./AM-updater ] && ! grep -q 'nolibfuse' ./AM-updater; then
  290. sed -i "s/^else/ echo y | $AMCLIPATH_ORIGIN nolibfuse \"\$APP\"\n notify-send \"\$APP has been converted too\!\"\nelse/g" ./AM-updater 2>/dev/null
  291. echo " The next update may replace this AppImage with a Type2 one"
  292. echo " so I added this command to the bottom of the \"AM-updater\" script!"
  293. fi
  294. echo " Contact the upstream developers to make them officially upgrade!"
  295. read -r -p " Do you wish to remove the old libfuse2 AppImage? (Y/n) " yn
  296. if echo "$yn" | grep -i '^n' >/dev/null 2>&1; then
  297. return 1
  298. else
  299. rm -f ./*.old
  300. fi
  301. }
  302. # REMOVE
  303. _detect_path_to_remove() {
  304. if [ -f "$APPMAN_APPSPATH"/"$arg"/remove ]; then
  305. RMPATH="1"
  306. elif [ -f "$APPSPATH"/"$arg"/remove ]; then
  307. [ -w "$APPSPATH"/"$arg" ] && $SUDOCMD echo -e "\r" >/dev/null
  308. RMPATH="1"
  309. else
  310. RMPATH=""
  311. fi
  312. echo "$DIVIDING_LINE"
  313. }
  314. _remove() {
  315. [ "$AMCLI" = am ] && _detect_appman_apps
  316. _detect_path_to_remove
  317. [ -z "$RMPATH" ] && echo -e " \"${RED}$arg\033[0m\" is not a valid \"APPNAME\", see \"$AMCLI -f\" for more." && return 1
  318. read -r -p " ◆ Do you wish to remove \"$arg\"? (Y/n) " yn
  319. if echo "$yn" | grep -i '^n' >/dev/null 2>&1; then
  320. echo -e " \"${LightBlue}$arg\033[0m\" has not been removed!"
  321. else
  322. [ "$AMCLI" = am ] && "$APPMAN_APPSPATH"/"$arg"/remove 2>/dev/null || $SUDOCMD "$APPSPATH"/"$arg"/remove 2>/dev/null || return 1
  323. _clean_amcachedir
  324. sleep 0.1
  325. echo -e " \"${Green}$arg\033[0m\" has been removed!"
  326. fi
  327. [ -d "$DATADIR"/icons/hicolor/scalable/apps ] && find "$DATADIR"/icons/hicolor/scalable/apps -xtype l -exec rm {} \;
  328. }
  329. _hard_remove() {
  330. [ "$AMCLI" = am ] && _detect_appman_apps
  331. _detect_path_to_remove
  332. [ -z "$RMPATH" ] && echo -e " \"${RED}$arg\033[0m\" is not a valid \"APPNAME\", see \"$AMCLI -f\" for more." && return 1
  333. [ "$AMCLI" = am ] && "$APPMAN_APPSPATH"/"$arg"/remove 2>/dev/null || $SUDOCMD "$APPSPATH"/"$arg"/remove 2>/dev/null || return 1
  334. _clean_amcachedir
  335. sleep 0.1
  336. echo -e " \"${Green}$arg\033[0m\" has been removed!"
  337. [ -d "$DATADIR"/icons/hicolor/scalable/apps ] && find "$DATADIR"/icons/hicolor/scalable/apps -xtype l -exec rm {} \;
  338. }
  339. ###########################################################################
  340. # OPTIONS AVAILABLE IN THIS MODULE
  341. ###########################################################################
  342. # Main logic
  343. [ -z "$2" ] && echo " USAGE: $AMCLI $1 [ARGUMENT]" && exit 1
  344. case "$1" in
  345. 'backup'|'-b')
  346. # Do a snapshot of an installed app
  347. _determine_args
  348. entries="$(echo "$@" | cut -f2- -d ' ')"
  349. for arg in $entries; do
  350. argpath=$(echo "$ARGPATHS" | grep "/$arg$")
  351. _backup "${@}"
  352. shift
  353. done
  354. ;;
  355. 'downgrade'|'--rollback')
  356. # Downgrade the installed app to a previous version, from its online source
  357. _online_check
  358. _determine_args
  359. entries="$(echo "$@" | cut -f2- -d ' ')"
  360. for arg in $entries; do
  361. argpath=$(echo "$ARGPATHS" | grep "/$arg$")
  362. _downgrade "${@}"
  363. _clean_amcachedir
  364. _update_list_updatable_apps
  365. shift
  366. done
  367. ;;
  368. 'icons'|'--icons')
  369. # Place local AppImages into the menu by dragging and dropping them into the terminal
  370. _determine_args
  371. if [ "$2" = "--all" ]; then
  372. entries="$ARGS"
  373. read -r -p " ◆ Do you wish to allow custom icon theming for ALL the apps? (y,N) " yn
  374. else
  375. entries="$(echo "$@" | cut -f2- -d ' ')"
  376. read -r -p " ◆ Do you wish to allow custom icon theming for the selected apps? (y,N) " yn
  377. fi
  378. if echo "$yn" | grep -i '^y' >/dev/null 2>&1; then
  379. for arg in $entries; do
  380. argpath=$(echo "$ARGPATHS" | grep "/$arg$")
  381. _icon_theme "${@}"
  382. done
  383. echo " ✔ Success!"
  384. else
  385. echo " ✖ Aborted!"
  386. fi
  387. ;;
  388. 'launcher'|'--launcher')
  389. # Place local AppImages into the menu by dragging and dropping them into the terminal
  390. entries="$(echo "$@" | cut -f2- -d ' ')"
  391. if [ "$2" = "-u" ] || [ "$2" = "-U" ] || [ "$2" = "update" ]; then
  392. entries="$(echo "$@" | cut -f3- -d ' ')"
  393. echo "$DIVIDING_LINE"
  394. _update_launchers
  395. printf "%b\n ◆ Updates check finished! \n" "$DIVIDING_LINE"
  396. fi
  397. for arg in $entries; do
  398. echo "$DIVIDING_LINE"
  399. _launcher "${@}"
  400. done
  401. echo "$DIVIDING_LINE"
  402. ;;
  403. 'lock')
  404. # Lock the version of an installed app
  405. _determine_args
  406. entries="$(echo "$@" | cut -f2- -d ' ')"
  407. for arg in $entries; do
  408. argpath=$(echo "$ARGPATHS" | grep "/$arg$")
  409. _lock "${@}"
  410. shift
  411. done
  412. ;;
  413. 'unlock')
  414. # Unlock the version of an installed app
  415. _determine_args
  416. entries="$(echo "$@" | cut -f2- -d ' ')"
  417. for arg in $entries; do
  418. argpath=$(echo "$ARGPATHS" | grep "/$arg$")
  419. _unlock "${@}"
  420. shift
  421. done
  422. ;;
  423. 'nolibfuse')
  424. # Convert old AppImages to a new standard and get rid of libfuse2 dependency
  425. _determine_args
  426. entries="$(echo "$@" | cut -f2- -d ' ')"
  427. for arg in $entries; do
  428. argpath=$(echo "$ARGPATHS" | grep "/$arg$")
  429. _nolibfuse "${@}"
  430. _remove_info_files
  431. shift
  432. done
  433. ;;
  434. 'overwrite'|'-o')
  435. # Restore an app to a previous version using a snapshot (see "backup" or "-b", above)
  436. _determine_args
  437. entries="$(echo "$@" | cut -f2- -d ' ')"
  438. for arg in $entries; do
  439. argpath=$(echo "$ARGPATHS" | grep "/$arg$")
  440. _overwrite "${@}"
  441. _remove_info_files
  442. shift
  443. done
  444. ;;
  445. 'remove'|'-r')
  446. # Remove apps
  447. entries="$(echo "$@" | cut -f2- -d ' ')"
  448. for arg in $entries; do
  449. _remove "${@}"
  450. _remove_info_files
  451. shift
  452. done
  453. echo "$DIVIDING_LINE"
  454. ;;
  455. '-R')
  456. # Remove apps without confirmation
  457. entries="$(echo "$@" | cut -f2- -d ' ')"
  458. for arg in $entries; do
  459. _hard_remove "${@}"
  460. _remove_info_files
  461. shift
  462. done
  463. echo "$DIVIDING_LINE"
  464. ;;
  465. esac