From 459d92cef94c7ad84e5fbd2e30da8fa4f4a05d1f Mon Sep 17 00:00:00 2001 From: John Livingston Date: Mon, 14 Nov 2022 16:54:08 +0100 Subject: [PATCH] Embedding Prosody using AppImage: Thanks to this commit, there is no more need to manually install Prosody on the server. The plugin now build and embed an AppImage of Prosody. In this commit: * building and using a Prosody AppImage. * Adding a launcher in the AppImage: the first command argument tells if we want to run prosody or prosodyctl * prosodyCtl functions now uses the AppImage. * Prosody AppImage: extract once at the startup, then run the squashfs --- CHANGELOG.md | 11 ++++++ build-prosody.sh | 54 +++++++++++++++++++++++++++ package.json | 3 +- prosody/appimage_x86_64.yml | 59 ++++++++++++++++++++++++++++++ prosody/launcher.lua | 13 +++++++ server/lib/diagnostic/prosody.ts | 2 + server/lib/prosody/config.ts | 25 ++++++++++++- server/lib/prosody/config/paths.ts | 5 +++ server/lib/prosody/ctl.ts | 49 ++++++++++++++++++++++--- server/lib/settings.ts | 22 +++++++---- server/main.ts | 3 +- 11 files changed, 231 insertions(+), 15 deletions(-) create mode 100644 build-prosody.sh create mode 100644 prosody/appimage_x86_64.yml create mode 100644 prosody/launcher.lua diff --git a/CHANGELOG.md b/CHANGELOG.md index d8f5e1a1..c8285600 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,10 +21,21 @@ the server will try to use the new mode after updating the plugin. If you don't want the chat server to be active, just uninstall the plugin (settings won't be lost, you just have to download it again). +### Important Notes + +This version of the plugin comes with a builtin Prosody AppImage. + +If you were using this plugin before this version, and if you had installed Prosody manually, +you can safely uninstall Prosody. + +If you were using the custom Peertube docker image that is embedding Prosody, you can switch back to the official +Peertube image. + ### New Features * Removed deprecated modes, only keeping «Prosody server controlled by Peertube». * BOSH proxy optimization + enabling websocket. +* Builtin Prosody AppImage. No more manual installation required. ### Changes diff --git a/build-prosody.sh b/build-prosody.sh new file mode 100644 index 00000000..dca3f3bf --- /dev/null +++ b/build-prosody.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +set -euo pipefail +set -x + +rootdir="$(pwd)" +prosody_build_dir="$rootdir/build/prosody" +prosody_destination_dir="$rootdir/dist/server/prosody" + +if [[ ! -d "$prosody_build_dir" ]]; then + mkdir -p "$prosody_build_dir" +fi + +cd "$prosody_build_dir" + +if [ -f "$prosody_build_dir/livechat-prosody-x86_64.AppImage" ]; then + echo "Prosody image already built." +else + echo "Prosody image must be build..." + + # Prerequisite: you must have python3-venv installed on your system + if [[ ! -d "venv" ]]; then + echo "Creating the python venv..." + python3 -m venv venv + fi + + echo "Activating the python venv..." + source venv/bin/activate + + echo "Installing appimage-builder..." + pip3 install appimage-builder + + echo "Copying appimage source files..." + cp "$rootdir/prosody/appimage_x86_64.yml" "$prosody_build_dir/appimage_x86_64.yml" + cp "$rootdir/prosody/launcher.lua" "$prosody_build_dir/launcher.lua" + + echo "Building Prosody..." + appimage-builder --recipe "$prosody_build_dir/appimage_x86_64.yml" +fi + +echo "Copying Prosody dist files..." +mkdir -p "$prosody_destination_dir" && cp $prosody_build_dir/livechat-prosody-x86_64.AppImage "$prosody_destination_dir/" + +# For some obscur reason, if we keep AppDir and appimage-build folders, +# and if we try to install the plugin using the Peertube CLI, +# the installation fails because there are some subfolders that are right protected. +# To avoid that, we clean them: +echo "Cleaning build folders..." +rm -rf "$prosody_build_dir/AppDir" +rm -rf "$prosody_build_dir/appimage-build" + +echo "Prosody AppImage OK." + +exit 0 diff --git a/package.json b/package.json index 80284a93..e7754025 100644 --- a/package.json +++ b/package.json @@ -88,6 +88,7 @@ "clean:light": "rm -rf dist/*", "prepare": "npm run clean && npm run build", "build:converse": "bash conversejs/build-conversejs.sh", + "build:prosody": "bash build-prosody.sh", "build:images": "mkdir -p dist/client/images && npx svgo -f public/images/ -o dist/client/images/", "build:avatars": "mkdir -p dist/server/avatars && ./build-avatars.js", "build:webpack": "webpack --mode=production", @@ -95,7 +96,7 @@ "build:serverconverse": "mkdir -p dist/server/conversejs && cp conversejs/index.html dist/server/conversejs/", "build:prosodymodules": "mkdir -p dist/server/prosody-modules && cp -r prosody-modules/* dist/server/prosody-modules/", "build:styles": "sass assets:dist/assets", - "build": "npm-run-all -s clean:light -p build:converse build:images build:avatars build:webpack build:server build:serverconverse build:prosodymodules build:styles", + "build": "npm-run-all -s clean:light -p build:converse build:prosody build:images build:avatars build:webpack build:server build:serverconverse build:prosodymodules build:styles", "lint": "npm-run-all -s lint:script lint:styles", "lint:script": "npx eslint --ext .js --ext .ts .", "lint:styles": "stylelint 'conversejs/**/*.scss' 'assets/**/*.css'", diff --git a/prosody/appimage_x86_64.yml b/prosody/appimage_x86_64.yml new file mode 100644 index 00000000..dde0832f --- /dev/null +++ b/prosody/appimage_x86_64.yml @@ -0,0 +1,59 @@ +# This file is meant to be used with appimage-builder: https://appimage-builder.readthedocs.io +# See it is use in the build-prosody.sh script. + +version: 1 + +script: + # Remove any previous build + - rm -rf AppDir | true + # Make usr dirs + - mkdir -p AppDir/usr/bin + # Copy the launcher code into the AppDir + - cp ./launcher.lua AppDir/usr/bin/ + +AppDir: + path: ./AppDir + + app_info: + id: org.peertube-plugin-livechat.prosody + name: prosody + icon: utilities-terminal + version: 1.0.0 + exec: usr/bin/lua5.2 + exec_args: "$APPDIR/usr/bin/launcher.lua $@" + + apt: + arch: amd64 + sources: + - sourceline: 'deb [arch=amd64] https://deb.debian.org/debian/ bullseye main contrib' + key_url: 'http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x648ACFD622F3D138' + - sourceline: 'deb [arch=amd64] https://deb.debian.org/debian/ bullseye-backports main contrib' + + include: + - lua5.2 + - prosody + + files: + exclude: + - usr/share/man + - usr/share/doc/*/README.* + - usr/share/doc/*/changelog.* + - usr/share/doc/*/NEWS.* + - usr/share/doc/*/TODO.* + - etc/init.d/* + - etc/logrotate.d/* + + runtime: + # Here we use the path_mappings to rewrite, on runtime, all paths. + # Note: this assume that peertube-plugin-livechat is not in a subdir of one of following mappings. + # This seems a reasonable assumption. + path_mappings: + - /etc/:$APPDIR/etc/ + - /lib/:$APPDIR/lib/ + - /lib64/:$APPDIR/lib64/ + - /runtime/:$APPDIR/runtime/ + - /usr/:$APPDIR/usr/ + +AppImage: + arch: x86_64 + file_name: 'livechat-prosody-x86_64.AppImage' diff --git a/prosody/launcher.lua b/prosody/launcher.lua new file mode 100644 index 00000000..67fb0a5c --- /dev/null +++ b/prosody/launcher.lua @@ -0,0 +1,13 @@ +#!/usr/bin/env lua + +-- This file is a launcher, that takes the first argument to choose what to launch. + +local what = table.remove(arg, 1); +if what == 'prosody' then + dofile('/usr/bin/prosody'); +elseif what == 'prosodyctl' then + dofile('/usr/bin/prosodyctl'); +else + print("Unknown command: "..what); + os.exit(1); +end diff --git a/server/lib/diagnostic/prosody.ts b/server/lib/diagnostic/prosody.ts index 2fe69424..13910c5f 100644 --- a/server/lib/diagnostic/prosody.ts +++ b/server/lib/diagnostic/prosody.ts @@ -33,6 +33,8 @@ export async function diagProsody (test: string, options: RegisterServerOptions) result.messages.push(`Prosody will use ${wantedConfig.baseApiUrl} as base uri from api calls`) + result.messages.push(`Prosody path will be '${wantedConfig.paths.exec}'`) + result.messages.push(`Prosody modules path will be '${wantedConfig.paths.modules}'`) result.messages.push(`Prosody rooms will be grouped by '${wantedConfig.roomType}'.`) diff --git a/server/lib/prosody/config.ts b/server/lib/prosody/config.ts index 600e9b85..f8e414d6 100644 --- a/server/lib/prosody/config.ts +++ b/server/lib/prosody/config.ts @@ -54,6 +54,24 @@ async function getProsodyFilePaths (options: RegisterServerOptions): Promise { + const logger = options.peertubeHelpers.logger + const filePaths = await getProsodyFilePaths(options) + const appImageToExtract = filePaths.appImageToExtract + if (!appImageToExtract) { + return + } + + return new Promise((resolve, reject) => { + const spawned = child_process.spawn(appImageToExtract, ['--appimage-extract'], { + cwd: filePaths.dir, + env: { + ...process.env + } + }) + spawned.stdout.on('data', (data) => { + logger.debug(`AppImage extract printed: ${data as string}`) + }) + spawned.stderr.on('data', (data) => { + logger.error(`AppImage extract has errors: ${data as string}`) + }) + spawned.on('error', reject) + spawned.on('close', (_code) => { // 'close' and not 'exit', to be sure it is finished. + resolve() + }) + }) +} + interface ProsodyCtlResult { code: number | null stdout: string @@ -23,11 +55,13 @@ async function prosodyCtl (options: RegisterServerOptions, command: string): Pro let d: string = '' let e: string = '' let m: string = '' - const spawned = child_process.spawn('prosodyctl', [ + const cmdArgs = [ + ...filePaths.execCtlArgs, '--config', filePaths.config, command - ], { + ] + const spawned = child_process.spawn(filePaths.execCtl, cmdArgs, { cwd: filePaths.dir, env: { ...process.env, @@ -44,7 +78,10 @@ async function prosodyCtl (options: RegisterServerOptions, command: string): Pro m += data as string }) spawned.on('error', reject) - spawned.on('exit', (code) => { + + // on 'close' and not 'exit', to be sure everything is done + // (else it can cause trouble by cleaning AppImage extract too soon) + spawned.on('close', (code) => { resolve({ code: code, stdout: d, @@ -163,8 +200,9 @@ async function ensureProsodyRunning (options: RegisterServerOptions): PromiseChat mode' + descriptionHTML: '

Chat Server

' }) registerSetting({ name: 'chat-help-builtin-prosody', @@ -39,14 +39,22 @@ function initSettings (options: RegisterServerOptions): void { label: 'Prosody server', descriptionHTML: `This plugin uses the Prosody XMPP server to handle chat rooms.
The Peertube server will control this Prosody server.
-Important Note: you have to install Prosody on your server. -Please read the documentation.`, +By default, this plugin comes with a Prosody AppImage.`, private: true }) + registerSetting({ + name: 'use-system-prosody', + type: 'input-checkbox', + label: 'Use system Prosody', + descriptionHTML: `Warning: don't check this settings if you are not sure of what you are doing.
+By checking this settings, your Peertube will use the Prosody server that comes with your system, +and not the embeded AppImage.
+Only use this if you encounter problems with the embedded Prosody.`, + private: true, + default: false + }) + // TODO: fix the settings order. Since there is no more multiple chat-mode, the order is not good. registerSetting({ name: 'prosody-list-rooms', label: 'List existing rooms', diff --git a/server/main.ts b/server/main.ts index 3cca0260..07140457 100644 --- a/server/main.ts +++ b/server/main.ts @@ -3,7 +3,7 @@ import { migrateSettings } from './lib/migration/settings' import { initSettings } from './lib/settings' import { initCustomFields } from './lib/custom-fields' import { initRouters } from './lib/routers/index' -import { ensureProsodyRunning, ensureProsodyNotRunning } from './lib/prosody/ctl' +import { prepareProsody, ensureProsodyRunning, ensureProsodyNotRunning } from './lib/prosody/ctl' import decache from 'decache' // FIXME: Peertube unregister don't have any parameter. @@ -25,6 +25,7 @@ async function register (options: RegisterServerOptions): Promise { await initRouters(options) try { + await prepareProsody(options) await ensureProsodyRunning(options) } catch (error) { options.peertubeHelpers.logger.error('Error when launching Prosody: ' + (error as string))