From daa63c8e20d268300bc4339582398b61550b9f2a Mon Sep 17 00:00:00 2001 From: yalh76 Date: Wed, 7 Jul 2021 16:42:05 +0200 Subject: [PATCH 01/15] From Scratch --- .../ISSUE_TEMPLATE.md | 4 +- .github/PULL_REQUEST_TEMPLATE.md | 16 ++ README.md | 4 - check_process | 13 +- conf/app.src | 7 - conf/docker-compose.yml | 56 ---- conf/iframely.config.local.js | 74 ++++-- conf/iframely.service | 45 ++++ conf/iframely.src | 7 + conf/imagemagick.src | 7 + conf/lemmy-translations.src | 7 + conf/lemmy-ui.service | 49 ++++ conf/lemmy-ui.src | 7 + conf/lemmy.hjson | 103 ++++++-- conf/lemmy.service | 46 ++++ conf/lemmy.src | 7 + conf/pict-rs.service | 45 ++++ conf/pict-rs.src | 7 + doc/.gitkeep | 0 doc/DISCLAIMER.md | 3 + doc/screenshots/.gitkeep | 0 doc/screenshots/screenshot1.webp | Bin 0 -> 99358 bytes manifest.json | 18 +- pull_request_template.md | 16 -- scripts/_common.sh | 4 +- scripts/backup | 34 ++- scripts/install | 236 +++++++++++++---- scripts/remove | 101 +++++++- scripts/restore | 128 +++++++-- scripts/upgrade | 244 +++++++++++++++--- 30 files changed, 1009 insertions(+), 279 deletions(-) rename issue_template.md => .github/ISSUE_TEMPLATE.md (94%) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md delete mode 100644 conf/app.src delete mode 100644 conf/docker-compose.yml create mode 100644 conf/iframely.service create mode 100644 conf/iframely.src create mode 100644 conf/imagemagick.src create mode 100644 conf/lemmy-translations.src create mode 100644 conf/lemmy-ui.service create mode 100644 conf/lemmy-ui.src create mode 100644 conf/lemmy.service create mode 100644 conf/lemmy.src create mode 100644 conf/pict-rs.service create mode 100644 conf/pict-rs.src create mode 100644 doc/.gitkeep create mode 100644 doc/DISCLAIMER.md create mode 100644 doc/screenshots/.gitkeep create mode 100644 doc/screenshots/screenshot1.webp delete mode 100644 pull_request_template.md diff --git a/issue_template.md b/.github/ISSUE_TEMPLATE.md similarity index 94% rename from issue_template.md rename to .github/ISSUE_TEMPLATE.md index 4a290c4..2729a6b 100644 --- a/issue_template.md +++ b/.github/ISSUE_TEMPLATE.md @@ -8,7 +8,7 @@ about: When creating a bug report, please use the following template to provide 1. *Read this whole template first.* 2. *Determine if you are on the right place:* - *If you were performing an action on the app from the webadmin or the CLI (install, update, backup, restore, change_url...), you are on the right place!* - - *Otherwise, the issue may be due to REPLACEBYYOURAPP itself. Refer to its documentation or repository for help.* + - *Otherwise, the issue may be due to the app itself. Refer to its documentation or repository for help.* - *When in doubt, post here and we will figure it out together.* 3. *Delete the italic comments as you write over them below, and remove this guide.* --- @@ -31,7 +31,7 @@ about: When creating a bug report, please use the following template to provide - *If you performed a command from the CLI, the command itself is enough. For example:* ```sh - sudo yunohost app install REPLACEBYYOURAPP + sudo yunohost app install the_app ``` - *If you used the webadmin, please perform the equivalent command from the CLI first.* - *If the error occurs in your browser, explain what you did:* diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..ef70e18 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,16 @@ +## Problem + +- *Description of why you made this PR* + +## Solution + +- *And how do you fix that problem* + +## PR Status + +- [ ] Code finished and ready to be reviewed/tested +- [ ] The fix/enhancement were manually tested (if applicable) + +## Automatic tests + +Automatic tests can be triggered on https://ci-apps-dev.yunohost.org/ *after creating the PR*, by commenting "!testme", "!gogogadgetoci" or "By the power of systemd, I invoke The Great App CI to test this Pull Request!". (N.B. : for this to work you need to be a member of the Yunohost-Apps organization) diff --git a/README.md b/README.md index 2743631..f6892e1 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,6 @@ # Lemmy app for YunoHost -**Warning:** This app uses Docker. YunoHost do not encourages to use black box container technologies like Docker and Ansible. - -Then why this package uses Docker? -It's because the developers of the core app do not support simple installation. And packaging without documentaion is time consuming. [![Integration level](https://dash.yunohost.org/integration/lemmy.svg)](https://dash.yunohost.org/appci/app/lemmy) ![](https://ci-apps.yunohost.org/ci/badges/lemmy.status.svg) ![](https://ci-apps.yunohost.org/ci/badges/lemmy.maintain.svg) diff --git a/check_process b/check_process index aea319c..254ac36 100644 --- a/check_process +++ b/check_process @@ -1,14 +1,9 @@ -# See here for more information -# https://github.com/YunoHost/package_check#syntax-check_process-file - -# Move this file from check_process.default to check_process when you have filled it. - ;; Test complet ; Manifest - domain="domain.tld" (DOMAIN) - path="/path" (PATH) - admin="john" (USER) - sitename="lemmy website) (SITENAME) + domain="domain.tld" + path="/" + admin="john" + sitename="lemmy website" ; Checks pkg_linter=1 setup_sub_dir=0 diff --git a/conf/app.src b/conf/app.src deleted file mode 100644 index 17489bf..0000000 --- a/conf/app.src +++ /dev/null @@ -1,7 +0,0 @@ -SOURCE_URL=url of app's source -SOURCE_SUM=sha256 checksum -SOURCE_SUM_PRG=sha256sum -SOURCE_FORMAT=tar.gz -SOURCE_IN_SUBDIR=true -SOURCE_FILENAME= -SOURCE_EXTRACT=true diff --git a/conf/docker-compose.yml b/conf/docker-compose.yml deleted file mode 100644 index 75339d7..0000000 --- a/conf/docker-compose.yml +++ /dev/null @@ -1,56 +0,0 @@ -version: "2.2" - -services: - postgres: - image: postgres:12-alpine - environment: - - POSTGRES_USER=lemmy - - POSTGRES_PASSWORD=password - - POSTGRES_DB=lemmy - volumes: - - ./volumes/postgres:/var/lib/postgresql/data - restart: always - - lemmy: - image: dessalines/lemmy:0.9.9 - ports: - - "127.0.0.1:__PORT__LEMMY__:8536" - restart: always - environment: - - RUST_LOG=error - volumes: - - ./lemmy.hjson:/config/config.hjson - depends_on: - - postgres - - pictrs - - iframely - - lemmy-ui: - image: dessalines/lemmy-ui:0.9.9 - ports: - - "127.0.0.1:__PORT_UI__:1234" - restart: always - environment: - - LEMMY_INTERNAL_HOST=lemmy:8536 - - LEMMY_EXTERNAL_HOST=localhost:__PORT__LEMMY__ - - LEMMY_HTTPS=false - depends_on: - - lemmy - - pictrs: - image: asonix/pictrs:v0.2.5-r0 - ports: - - "127.0.0.1:__PORT_PICTRS__:8080" - user: 991:991 - volumes: - - ./volumes/pictrs:/mnt - restart: always - - iframely: - image: dogbin/iframely:latest - ports: - - "127.0.0.1:__PORT_IFRAMELY__:80" - volumes: - - ./iframely.config.local.js:/iframely/config.local.js:ro - restart: always - mem_limit: 100m diff --git a/conf/iframely.config.local.js b/conf/iframely.config.local.js index b521e9f..668043f 100644 --- a/conf/iframely.config.local.js +++ b/conf/iframely.config.local.js @@ -37,7 +37,7 @@ }, */ - port: 80, //can be overridden by PORT env var + port: __PORT_IFRAMELY__, //can be overridden by PORT env var host: '0.0.0.0', // Dockers beware. See https://github.com/itteco/iframely/issues/132#issuecomment-242991246 //can be overridden by HOST env var @@ -58,7 +58,7 @@ - memcached - https://github.com/3rd-Eden/node-memcached */ CACHE_ENGINE: 'node-cache', - CACHE_TTL: 0, // In seconds. + CACHE_TTL: 0, // In seconds. // 0 = 'never expire' for memcached & node-cache to let cache engine decide itself when to evict the record // 0 = 'no cache' for redis. Use high enough (e.g. 365*24*60*60*1000) ttl for similar 'never expire' approach instead @@ -104,16 +104,31 @@ // DISABLE_HTTP2: true, // Customize API calls to oembed endpoints. + // Must have: please add your `access_token` for Facebook and Instagram API calls ADD_OEMBED_PARAMS: [{ - // Endpoint url regexp array. - re: [/^http:\/\/api\.instagram\.com\/oembed/], - // Custom get params object. - params: { + + re: [ // Endpoint's URL regexp array. + /^https:\/\/graph\.facebook\.com\/v\d+\.\d+\/instagram_oembed/i + ], + params: { // Custom query-string params object. + + // TODO: get your access Insagtam token as described + // on https://developers.facebook.com/docs/instagram/oembed/ + access_token: '', // The simplest way is + // to use `{app-id}|{app secret}` as access token + + // Add any other optional params hidecaption: true } }, { - re: [/^https:\/\/www\.facebook\.com\/plugins\/page\/oembed\.json/i], + re: [/^https:\/\/graph\.facebook\.com\/v\d+\.\d+\/oembed_page/i], params: { + // TODO: get your access token as described + // on https://developers.facebook.com/docs/plugins/oembed + access_token: '', // The simplest way is + // to use `{app-id}|{app secret}` as access token + + // Add any other optional params show_posts: 0, show_facepile: 0, maxwidth: 600 @@ -126,20 +141,24 @@ limit: 1, maxwidth: 600 } - /* }, { - // Facebook https://developers.facebook.com/docs/plugins/oembed-endpoints - re: [/^https:\/\/www\.facebook\.com\/plugins\/\w+\/oembed\.json/i], + // Facebook https://developers.facebook.com/docs/plugins/oembed/ + re: [/^https:\/\/graph\.facebook\.com\/v\d+\.\d+\/oembed_/i], params: { - // Skip script tag and fb-root div. - omitscript: true + // TODO: get your access token as described + // on https://developers.facebook.com/docs/plugins/oembed + access_token: '', // The simplest way is + // to use `{app-id}|{app secret}` as access token + + // Add any other optional params, like skip script tag and fb-root div + // omitscript: true } - */ }], + /* Configure use of HTTP proxies as needed. + You don't have to specify all options per regex - just what you need to override + */ /* - // Configure use of HTTP proxies as needed. - // You don't have to specify all options per regex - just what you need to override PROXY: [{ re: [/^https?:\/\/www\.domain\.com/], proxy_server: 'http://1.2.3.4:8080', @@ -152,14 +171,16 @@ // Refer to: https://github.com/request/request // Overrides previous params if overlapped. }, + cache_ttl: 3600, // in seconds, cache response for 1 hour. disable_http2: true }], */ // Customize API calls to 3rd parties. At the very least - configure required keys. + // For available provider options - please see the code of its domain plugin. providerOptions: { - locale: "en_US", // ISO 639-1 two-letter language code, e.g. en_CA or fr_CH. - // Will be added as highest priotity in accept-language header with each request. + locale: "en_US", // ISO 639-1 two-letter language code, e.g. en_CA or fr_CH. + // Will be added as highest priotity in accept-language header with each request. // Plus is used in FB, YouTube and perhaps other plugins "twitter": { "max-width": 550, @@ -203,21 +224,18 @@ // It is probably the same API key you use for Google Maps. youtube: { // api_key: "INSERT YOUR VALUE", + // parts: [ "snippet", "player" ], // list of fields you want to use in the request, in most cases you only need those two get_params: "?rel=0&showinfo=1" // https://developers.google.com/youtube/player_parameters }, vimeo: { get_params: "?byline=0&badge=0" // https://developer.vimeo.com/player/embedding }, - - /* soundcloud: { old_player: true // enables classic player }, giphy: { media_only: true // disables branded player for gifs and returns just the image - } - */ - /* + }, bandcamp: { get_params: '/size=large/bgcol=333333/linkcol=ffffff/artwork=small/transparent=true/', media: { @@ -230,7 +248,11 @@ 'max-width': 700 } } - } + }, + // Docs: https://dev.twitch.tv/docs/embed/video-and-clips + twitch: { + parent: 'jsbin.com, null.jsbin.com, localhost' + }, */ }, @@ -276,7 +298,11 @@ // And this is AWS metadata service // https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html /^https?:\/\/169\.254\.169\.254/ - ] + ], + + // Endpoint for prerender service, if you need it. Used to parse React apps. Very slow. + // Tested with https://github.com/prerender/prerender + // PRERENDER_URL: "https://domain/render?url=" }; module.exports = config; diff --git a/conf/iframely.service b/conf/iframely.service new file mode 100644 index 0000000..4203218 --- /dev/null +++ b/conf/iframely.service @@ -0,0 +1,45 @@ +[Unit] +Description=__APP__ Iframely Daemon +After=network.target + +[Service] +Type=simple +User=__APP__ +Group=__APP__ +WorkingDirectory=__FINALPATH__/iframely/ +ExecStart=__YNH_NODE__ -- server +StandardOutput=append:/var/log/__APP__/__APP__-iframely.log +StandardError=inherit + +# Sandboxing options to harden security +# Depending on specificities of your service/app, you may need to tweak these +# .. but this should be a good baseline +# Details for these options: https://www.freedesktop.org/software/systemd/man/systemd.exec.html +NoNewPrivileges=yes +PrivateTmp=yes +PrivateDevices=yes +RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 +RestrictNamespaces=yes +RestrictRealtime=yes +DevicePolicy=closed +ProtectSystem=full +ProtectControlGroups=yes +ProtectKernelModules=yes +ProtectKernelTunables=yes +LockPersonality=yes +SystemCallFilter=~@clock @debug @module @mount @obsolete @reboot @setuid @swap + +# Denying access to capabilities that should not be relevant for webapps +# Doc: https://man7.org/linux/man-pages/man7/capabilities.7.html +CapabilityBoundingSet=~CAP_RAWIO CAP_MKNOD +CapabilityBoundingSet=~CAP_AUDIT_CONTROL CAP_AUDIT_READ CAP_AUDIT_WRITE +CapabilityBoundingSet=~CAP_SYS_BOOT CAP_SYS_TIME CAP_SYS_MODULE CAP_SYS_PACCT +CapabilityBoundingSet=~CAP_LEASE CAP_LINUX_IMMUTABLE CAP_IPC_LOCK +CapabilityBoundingSet=~CAP_BLOCK_SUSPEND CAP_WAKE_ALARM +CapabilityBoundingSet=~CAP_SYS_TTY_CONFIG +CapabilityBoundingSet=~CAP_MAC_ADMIN CAP_MAC_OVERRIDE +CapabilityBoundingSet=~CAP_NET_ADMIN CAP_NET_BROADCAST CAP_NET_RAW +CapabilityBoundingSet=~CAP_SYS_ADMIN CAP_SYS_PTRACE CAP_SYSLOG + +[Install] +WantedBy=multi-user.target diff --git a/conf/iframely.src b/conf/iframely.src new file mode 100644 index 0000000..15e217e --- /dev/null +++ b/conf/iframely.src @@ -0,0 +1,7 @@ +SOURCE_URL=https://github.com/itteco/iframely/archive/refs/tags/v1.6.0.tar.gz +SOURCE_SUM=8130267e17e4484a2cdd028cdffb619f430b9cd19b3614b5d1de2d05304d03f8 +SOURCE_SUM_PRG=sha256sum +SOURCE_FORMAT=tar.gz +SOURCE_IN_SUBDIR=true +SOURCE_FILENAME= +SOURCE_EXTRACT=true diff --git a/conf/imagemagick.src b/conf/imagemagick.src new file mode 100644 index 0000000..885e716 --- /dev/null +++ b/conf/imagemagick.src @@ -0,0 +1,7 @@ +SOURCE_URL=https://github.com/ImageMagick/ImageMagick/archive/refs/tags/7.0.11-14.tar.gz +SOURCE_SUM=dfa5aa3f7f289f12c2f9ee6c7c19b02ae857b4eec02f40298f60f5c11048a016 +SOURCE_SUM_PRG=sha256sum +SOURCE_FORMAT=tar.gz +SOURCE_IN_SUBDIR=true +SOURCE_FILENAME= +SOURCE_EXTRACT=true diff --git a/conf/lemmy-translations.src b/conf/lemmy-translations.src new file mode 100644 index 0000000..cb2e5dd --- /dev/null +++ b/conf/lemmy-translations.src @@ -0,0 +1,7 @@ +SOURCE_URL=https://codeload.github.com/LemmyNet/lemmy-translations/tar.gz/9e3dfebe14693553f1002ef06f02201ca1d52863 +SOURCE_SUM=1693789ac4d6a3905530b2cf76ad50151082f5e96d1639dc06e30219649a6c87 +SOURCE_SUM_PRG=sha256sum +SOURCE_FORMAT=tar.gz +SOURCE_IN_SUBDIR=true +SOURCE_FILENAME= +SOURCE_EXTRACT=true diff --git a/conf/lemmy-ui.service b/conf/lemmy-ui.service new file mode 100644 index 0000000..bce73bd --- /dev/null +++ b/conf/lemmy-ui.service @@ -0,0 +1,49 @@ +[Unit] +Description=__APP__ Lemmy UI Daemon +After=network.target + +[Service] +Type=simple +User=__APP__ +Group=__APP__ +Environment="LEMMY_INTERNAL_HOST=127.0.0.1:__PORT_LEMMY__" +Environment=" LEMMY_EXTERNAL_HOST=__DOMAIN__" +Environment="LEMMY_HTTPS=true" +Environment="LEMMY_UI_HOST=0.0.0.0:__PORT_UI__" +WorkingDirectory=__FINALPATH__/lemmy-ui/ +ExecStart=__YNH_NODE__ dist/js/server.js +StandardOutput=append:/var/log/__APP__/__APP__-ui.log +StandardError=inherit + +# Sandboxing options to harden security +# Depending on specificities of your service/app, you may need to tweak these +# .. but this should be a good baseline +# Details for these options: https://www.freedesktop.org/software/systemd/man/systemd.exec.html +NoNewPrivileges=yes +PrivateTmp=yes +PrivateDevices=yes +RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 +RestrictNamespaces=yes +RestrictRealtime=yes +DevicePolicy=closed +ProtectSystem=full +ProtectControlGroups=yes +ProtectKernelModules=yes +ProtectKernelTunables=yes +LockPersonality=yes +SystemCallFilter=~@clock @debug @module @mount @obsolete @reboot @setuid @swap + +# Denying access to capabilities that should not be relevant for webapps +# Doc: https://man7.org/linux/man-pages/man7/capabilities.7.html +CapabilityBoundingSet=~CAP_RAWIO CAP_MKNOD +CapabilityBoundingSet=~CAP_AUDIT_CONTROL CAP_AUDIT_READ CAP_AUDIT_WRITE +CapabilityBoundingSet=~CAP_SYS_BOOT CAP_SYS_TIME CAP_SYS_MODULE CAP_SYS_PACCT +CapabilityBoundingSet=~CAP_LEASE CAP_LINUX_IMMUTABLE CAP_IPC_LOCK +CapabilityBoundingSet=~CAP_BLOCK_SUSPEND CAP_WAKE_ALARM +CapabilityBoundingSet=~CAP_SYS_TTY_CONFIG +CapabilityBoundingSet=~CAP_MAC_ADMIN CAP_MAC_OVERRIDE +CapabilityBoundingSet=~CAP_NET_ADMIN CAP_NET_BROADCAST CAP_NET_RAW +CapabilityBoundingSet=~CAP_SYS_ADMIN CAP_SYS_PTRACE CAP_SYSLOG + +[Install] +WantedBy=multi-user.target diff --git a/conf/lemmy-ui.src b/conf/lemmy-ui.src new file mode 100644 index 0000000..5d59d2a --- /dev/null +++ b/conf/lemmy-ui.src @@ -0,0 +1,7 @@ +SOURCE_URL=https://github.com/LemmyNet/lemmy-ui/archive/refs/tags/0.11.2.tar.gz +SOURCE_SUM=645954b3726803b0ba6b756a2b4f4099e2de29e59cadd828bebd592b29149611 +SOURCE_SUM_PRG=sha256sum +SOURCE_FORMAT=tar.gz +SOURCE_IN_SUBDIR=true +SOURCE_FILENAME= +SOURCE_EXTRACT=true diff --git a/conf/lemmy.hjson b/conf/lemmy.hjson index c66f93f..38d0c5e 100644 --- a/conf/lemmy.hjson +++ b/conf/lemmy.hjson @@ -6,41 +6,90 @@ # username for the admin user admin_username: "__ADMIN__" # password for the admin user - admin_password: "__ADMIN_PASS__" + admin_password: "__PASSWORD__" + # optional: email for the admin user (can be omitted and set later through the website) + admin_email: "__ADMIN_EMAIL__" # name of the site (can be changed later) - site_name: "__NAME__" + site_name: "__SITENAME__" + } + # settings related to the postgresql database + database: { + # username to connect to postgres + user: "__DB_USER__" + # password to connect to postgres + password: "__DB_PWD__" + # host where postgres is running + host: "localhost" + # port where postgres can be accessed + port: 5432 + # name of the postgres database for lemmy + database: "__DB_NAME__" + # maximum number of active sql connections + pool_size: 5 } - # the domain name of your instance (eg "lemmy.ml") hostname: "__DOMAIN__" # address where lemmy should listen for incoming requests bind: "0.0.0.0" # port where lemmy should listen for incoming requests - port: 8536 + port: __PORT_LEMMY__ + # whether tls is required for activitypub. only disable this for debugging, never for producion. + tls_enabled: true # json web token for authorization between server and client jwt_secret: "__RANDOM__" - # settings related to the postgresql database - database: { - # name of the postgres database for lemmy - database: "lemmy" - # username to connect to postgres - user: "lemmy" - # password to connect to postgres - password: "password" - # host where postgres is running - host: "postgres" + # address where pictrs is available + pictrs_url: "http://127.0.0.1:__PORT_PICTRS__" + # address where iframely is available + iframely_url: "http://127.0.0.1:__PORT_IFRAMELY__" + # rate limits for various user actions, by user ip + rate_limit: { + # maximum number of messages created in interval + message: 180 + # interval length for message limit + message_per_second: 60 + # maximum number of posts created in interval + post: 6 + # interval length for post limit + post_per_second: 600 + # maximum number of registrations in interval + register: 3 + # interval length for registration limit + register_per_second: 3600 + # maximum number of image uploads in interval + image: 6 + # interval length for image uploads + image_per_second: 3600 + } + # settings related to activitypub federation + federation: { + # whether to enable activitypub federation. + enabled: true + # Allows and blocks are described here: + # https://join-lemmy.org/docs/en/federation/administration.html#instance-allowlist-and-blocklist + # + # list of instances with which federation is allowed + # allowed_instances: ["instance1.tld","instance2.tld"] + # instances which we never federate anything with (but previously federated objects are unaffected) + # blocked_instances: [] + # If true, only federate with instances on the allowlist and block everything else. If false, + # use allowlist only for remote communities, and posts/comments in local communities. + # strict_allowlist: true + } + captcha: { + enabled: true + difficulty: medium # Can be easy, medium, or hard + } + # email sending configuration + email: { + # hostname and port of the smtp server + smtp_server: "127.0.0.1:25" + # login name for smtp server + smtp_login: "" + # password to login to the smtp server + smtp_password: "" + # address to send emails from, eg "noreply@your-instance.com" + smtp_from_address: "lemmy@__DOMAIN__" + # whether or not smtp connections should use tls + use_tls: true } -# # optional: email sending configuration -# email: { -# # hostname and port of the smtp server - smtp_server: "127.0.0.1:25" -# # login name for smtp server - smtp_login: "" -# # password to login to the smtp server - smtp_password: "" -# # address to send emails from, eg "noreply@your-instance.com" - smtp_from_address: "lemmy@__DOMAIN__" -# # whether or not smtp connections should use tls - use_tls: true -# } } diff --git a/conf/lemmy.service b/conf/lemmy.service new file mode 100644 index 0000000..b56b41b --- /dev/null +++ b/conf/lemmy.service @@ -0,0 +1,46 @@ +[Unit] +Description=__APP__ Lemmy Daemon +After=network.target + +[Service] +Type=simple +User=__APP__ +Group=__APP__ +Environment="LEMMY_CONFIG_LOCATION=__FINALPATH__/config/config.hjson" +WorkingDirectory=__FINALPATH__/lemmy_server/ +ExecStart=__FINALPATH__/lemmy_server/lemmy_server +StandardOutput=append:/var/log/__APP__/__APP__.log +StandardError=inherit + +# Sandboxing options to harden security +# Depending on specificities of your service/app, you may need to tweak these +# .. but this should be a good baseline +# Details for these options: https://www.freedesktop.org/software/systemd/man/systemd.exec.html +NoNewPrivileges=yes +PrivateTmp=yes +PrivateDevices=yes +RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 +RestrictNamespaces=yes +RestrictRealtime=yes +DevicePolicy=closed +ProtectSystem=full +ProtectControlGroups=yes +ProtectKernelModules=yes +ProtectKernelTunables=yes +LockPersonality=yes +SystemCallFilter=~@clock @debug @module @mount @obsolete @reboot @setuid @swap + +# Denying access to capabilities that should not be relevant for webapps +# Doc: https://man7.org/linux/man-pages/man7/capabilities.7.html +CapabilityBoundingSet=~CAP_RAWIO CAP_MKNOD +CapabilityBoundingSet=~CAP_AUDIT_CONTROL CAP_AUDIT_READ CAP_AUDIT_WRITE +CapabilityBoundingSet=~CAP_SYS_BOOT CAP_SYS_TIME CAP_SYS_MODULE CAP_SYS_PACCT +CapabilityBoundingSet=~CAP_LEASE CAP_LINUX_IMMUTABLE CAP_IPC_LOCK +CapabilityBoundingSet=~CAP_BLOCK_SUSPEND CAP_WAKE_ALARM +CapabilityBoundingSet=~CAP_SYS_TTY_CONFIG +CapabilityBoundingSet=~CAP_MAC_ADMIN CAP_MAC_OVERRIDE +CapabilityBoundingSet=~CAP_NET_ADMIN CAP_NET_BROADCAST CAP_NET_RAW +CapabilityBoundingSet=~CAP_SYS_ADMIN CAP_SYS_PTRACE CAP_SYSLOG + +[Install] +WantedBy=multi-user.target diff --git a/conf/lemmy.src b/conf/lemmy.src new file mode 100644 index 0000000..8866357 --- /dev/null +++ b/conf/lemmy.src @@ -0,0 +1,7 @@ +SOURCE_URL=https://github.com/LemmyNet/lemmy/archive/refs/tags/0.11.0.tar.gz +SOURCE_SUM=8c93268d5cb7b30c9c25e2fdeef83153d95b7b79ad0b0a6f354d89e72a0ec641 +SOURCE_SUM_PRG=sha256sum +SOURCE_FORMAT=tar.gz +SOURCE_IN_SUBDIR=true +SOURCE_FILENAME= +SOURCE_EXTRACT=true diff --git a/conf/pict-rs.service b/conf/pict-rs.service new file mode 100644 index 0000000..186ba5e --- /dev/null +++ b/conf/pict-rs.service @@ -0,0 +1,45 @@ +[Unit] +Description=__APP__ pict-rs Daemon +After=network.target + +[Service] +Type=simple +User=__APP__ +Group=__APP__ +WorkingDirectory=__FINALPATH__/pict-rs/ +ExecStart=__FINALPATH__/pict-rs/pict-rs -a 127.0.0.1:__PORT_PICTRS__ -p __DATADIR__/pictrs-data +StandardOutput=append:/var/log/__APP__/__APP__-pict-rs.log +StandardError=inherit + +# Sandboxing options to harden security +# Depending on specificities of your service/app, you may need to tweak these +# .. but this should be a good baseline +# Details for these options: https://www.freedesktop.org/software/systemd/man/systemd.exec.html +NoNewPrivileges=yes +PrivateTmp=yes +PrivateDevices=yes +RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 +RestrictNamespaces=yes +RestrictRealtime=yes +DevicePolicy=closed +ProtectSystem=full +ProtectControlGroups=yes +ProtectKernelModules=yes +ProtectKernelTunables=yes +LockPersonality=yes +SystemCallFilter=~@clock @debug @module @mount @obsolete @reboot @setuid @swap + +# Denying access to capabilities that should not be relevant for webapps +# Doc: https://man7.org/linux/man-pages/man7/capabilities.7.html +CapabilityBoundingSet=~CAP_RAWIO CAP_MKNOD +CapabilityBoundingSet=~CAP_AUDIT_CONTROL CAP_AUDIT_READ CAP_AUDIT_WRITE +CapabilityBoundingSet=~CAP_SYS_BOOT CAP_SYS_TIME CAP_SYS_MODULE CAP_SYS_PACCT +CapabilityBoundingSet=~CAP_LEASE CAP_LINUX_IMMUTABLE CAP_IPC_LOCK +CapabilityBoundingSet=~CAP_BLOCK_SUSPEND CAP_WAKE_ALARM +CapabilityBoundingSet=~CAP_SYS_TTY_CONFIG +CapabilityBoundingSet=~CAP_MAC_ADMIN CAP_MAC_OVERRIDE +CapabilityBoundingSet=~CAP_NET_ADMIN CAP_NET_BROADCAST CAP_NET_RAW +CapabilityBoundingSet=~CAP_SYS_ADMIN CAP_SYS_PTRACE CAP_SYSLOG + +[Install] +WantedBy=multi-user.target diff --git a/conf/pict-rs.src b/conf/pict-rs.src new file mode 100644 index 0000000..50edba3 --- /dev/null +++ b/conf/pict-rs.src @@ -0,0 +1,7 @@ +SOURCE_URL=https://git.asonix.dog/asonix/pict-rs/archive/v0.2.6-r2.tar.gz +SOURCE_SUM=c8542ff79fc2f0699b33994d6718a9f8f4bfc94e6c7c7e1e5dc13911afd40d10 +SOURCE_SUM_PRG=sha256sum +SOURCE_FORMAT=tar.gz +SOURCE_IN_SUBDIR=true +SOURCE_FILENAME= +SOURCE_EXTRACT=true diff --git a/doc/.gitkeep b/doc/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/doc/DISCLAIMER.md b/doc/DISCLAIMER.md new file mode 100644 index 0000000..bbabfe8 --- /dev/null +++ b/doc/DISCLAIMER.md @@ -0,0 +1,3 @@ +* Any known limitations, constrains or stuff not working, such as (but not limited to): + * Lemmy require full domain path to be installed. Eg. lemmy.domain.tld + * The admin username and password will be sent to the admin of the YunoHost through mail. \ No newline at end of file diff --git a/doc/screenshots/.gitkeep b/doc/screenshots/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/doc/screenshots/screenshot1.webp b/doc/screenshots/screenshot1.webp new file mode 100644 index 0000000000000000000000000000000000000000..2601ab1fce083276b60985302b2a070c9c809793 GIT binary patch literal 99358 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%0DVqGnWE!dBP& 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 db.sql #================================================= # END OF SCRIPT diff --git a/scripts/install b/scripts/install index b02ed40..ecc63b5 100755 --- a/scripts/install +++ b/scripts/install @@ -14,8 +14,7 @@ source /usr/share/yunohost/helpers #================================================= ynh_clean_setup () { - ### Remove this function if there's nothing to clean before calling the remove script. - true + ynh_clean_check_starting } # Exit if an error occurs during the execution of the script ynh_abort_if_errors @@ -26,19 +25,18 @@ ynh_abort_if_errors domain=$YNH_APP_ARG_DOMAIN path_url="/" -version=$(curl -s https://api.github.com/repos/docker/compose/releases/latest | grep 'tag_name' | cut -d\" -f4) admin=$YNH_APP_ARG_ADMIN password=$(ynh_string_random --length=12) sitename=$YNH_APP_ARG_SITENAME random=$(ynh_string_random --length=10) +admin_email=$(ynh_user_get_info --username=$admin --key="mail") app=$YNH_APP_INSTANCE_NAME #================================================= # CHECK IF THE APP CAN BE INSTALLED WITH THESE ARGS #================================================= - -ynh_script_progression --message="Validating installation parameters..." --weight=1 +ynh_script_progression --message="Validating installation parameters..." final_path=/var/www/$app test ! -e "$final_path" || ynh_die --message="This path already contains a folder" @@ -49,7 +47,7 @@ ynh_webpath_register --app=$app --domain=$domain --path_url=$path_url #================================================= # STORE SETTINGS FROM MANIFEST #================================================= -ynh_script_progression --message="Storing installation settings..." --weight=1 +ynh_script_progression --message="Storing installation settings..." ynh_app_setting_set --app=$app --key=domain --value=$domain ynh_app_setting_set --app=$app --key=path --value=$path_url @@ -63,97 +61,229 @@ ynh_app_setting_set --app=$app --key=random --value=$random #================================================= # FIND AND OPEN A PORT #================================================= -ynh_script_progression --message="Finding an available port..." --weight=1 +ynh_script_progression --message="Finding an available port..." # Find an available port port_lemmy=$(ynh_find_port --port=8536) -port_ui=$(ynh_find_port --port=1235) -port_pictrs=$(ynh_find_port --port=8537) -port_iframely=$(ynh_find_port --port=8061) ynh_app_setting_set --app=$app --key=port_lemmy --value=$port_lemmy +port_ui=$(ynh_find_port --port=1235) ynh_app_setting_set --app=$app --key=port_ui --value=$port_ui +port_pictrs=$(ynh_find_port --port=8537) ynh_app_setting_set --app=$app --key=port_pictrs --value=$port_pictrs +port_iframely=$(ynh_find_port --port=8061) ynh_app_setting_set --app=$app --key=port_iframely --value=$port_iframely #================================================= # INSTALL DEPENDENCIES #================================================= -ynh_script_progression --message="Installing dependencies..." --weight=1 +ynh_script_progression --message="Installing dependencies..." -#ynh_install_app_dependencies $pkg_dependencies +ynh_install_app_dependencies $pkg_dependencies +ynh_install_nodejs --nodejs_version=$NODEJS_VERSION +ynh_use_nodejs +ynh_install_extra_app_dependencies --repo="deb https://dl.yarnpkg.com/debian/ stable main" --package="yarn" --key="https://dl.yarnpkg.com/debian/pubkey.gpg" -# Install Docker and compose -curl -sSL https://get.docker.com | sh -systemctl enable docker --quiet -curl -L https://github.com/docker/compose/releases/download/${version}/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose +#================================================= +# CREATE DEDICATED USER +#================================================= +ynh_script_progression --message="Configuring system user..." + +# Create a system user +ynh_system_user_create --username=$app --home_dir=$final_path + +#================================================= +# CREATE A POSTGRESQL DATABASE +#================================================= +ynh_script_progression --message="Creating a PostgreSQL database..." + +ynh_psql_test_if_first_run +db_name=$(ynh_sanitize_dbid --db_name=$app) +db_user=$db_name +db_pwd=$(ynh_string_random --length=30) +ynh_app_setting_set --app=$app --key=db_name --value=$db_name +ynh_app_setting_set --app=$app --key=db_pwd --value=$db_pwd +ynh_psql_setup_db --db_user=$db_user --db_name=$db_name --db_pwd=$db_pwd #================================================= # DOWNLOAD, CHECK AND UNPACK SOURCE #================================================= -ynh_script_progression --message="Setting up source files..." --weight=1 +ynh_script_progression --message="Setting up source files..." ynh_app_setting_set --app=$app --key=final_path --value=$final_path +# Download, check integrity, uncompress and patch the source from app.src +ynh_setup_source --dest_dir="$final_path/build-lemmy/" --source_id="lemmy" +ynh_setup_source --dest_dir="$final_path/lemmy-ui/" --source_id="lemmy-ui" +ynh_setup_source --dest_dir="$final_path/lemmy-ui/lemmy-translations/" --source_id="lemmy-translations" +ynh_setup_source --dest_dir="$final_path/iframely/" --source_id="iframely" +ynh_setup_source --dest_dir="$final_path/build-pict-rs/" --source_id="pict-rs" +ynh_setup_source --dest_dir="$final_path/build-imagemagick/" --source_id="imagemagick" + +chmod 750 "$final_path" +chmod -R o-rwx "$final_path" +chown -R $app:$app "$final_path" -mkdir "$final_path" -cp -f ../conf/docker-compose.yml "$final_path/docker-compose.yml" -cp -f ../conf/lemmy.hjson "$final_path/lemmy.hjson" -cp -f ../conf/iframely.config.local.js "$final_path/iframely.config.local.js" -pushd "$final_path" - mkdir -p volumes/pictrs - chown -R 991:991 volumes/pictrs -popd #================================================= # NGINX CONFIGURATION #================================================= -ynh_script_progression --message="Configuring NGINX web server..." --weight=1 +ynh_script_progression --message="Configuring NGINX web server..." # Create a dedicated NGINX config -ynh_add_nginx_config 'port_lemmy port_ui port_pictrs port_iframely' +ynh_add_nginx_config #================================================= -# MODIFY A CONFIG FILE +# SPECIFIC SETUP #================================================= +# CREATE DATA DIRECTORY +#================================================= +ynh_script_progression --message="Creating a data directory..." +datadir=/home/yunohost.app/$app +ynh_app_setting_set --app=$app --key=datadir --value=$datadir +mkdir -p $datadir/pictrs-data -ynh_replace_string --match_string="__ADMIN__" --replace_string="$admin" --target_file="$final_path/lemmy.hjson" -ynh_replace_string --match_string="__ADMIN_PASS__" --replace_string="$password" --target_file="$final_path/lemmy.hjson" -ynh_replace_string --match_string="__NAME__" --replace_string="$sitename" --target_file="$final_path/lemmy.hjson" -ynh_replace_string --match_string="__DOMAIN__" --replace_string="$domain" --target_file="$final_path/lemmy.hjson" -ynh_replace_string --match_string="__RANDOM__" --replace_string="$random" --target_file="$final_path/lemmy.hjson" -ynh_replace_string --match_string="__PORT__LEMMY__" --replace_string="$port_lemmy" --target_file="$final_path/docker-compose.yml" -ynh_replace_string --match_string="__PORT_UI__" --replace_string="$port_ui" --target_file="$final_path/docker-compose.yml" -ynh_replace_string --match_string="__PORT_PICTRS__" --replace_string="$port_pictrs" --target_file="$final_path/docker-compose.yml" -ynh_replace_string --match_string="__PORT_IFRAMELY__" --replace_string="$port_iframely" --target_file="$final_path/docker-compose.yml" -ynh_replace_string --match_string="__DOMAIN__" --replace_string="$domain" --target_file="$final_path/iframely.config.local.js" +chmod 750 "$datadir" +chmod -R o-rwx "$datadir" +chown -R $app:$app "$datadir" +#================================================= +# MAKE INSTALL +#================================================= +ynh_script_progression --message="Making install..." + +# Install ImageMagick +pushd "$final_path/build-imagemagick/" + ./configure + make + make install + ldconfig /usr/local/lib +popd + +# Install rustup with the toolchain needed by lemmy +pushd "$final_path" + sudo -u "$app" RUSTUP_HOME="$final_path"/.rustup CARGO_HOME="$final_path"/.cargo bash -c 'curl -sSf -L https://static.rust-lang.org/rustup.sh | sh -s -- -y --default-toolchain nightly' +popd + +export PATH="$PATH:$final_path/.cargo/bin:$final_path/.local/bin:/usr/local/sbin" + +# Compile lemmy +pushd $final_path/build-lemmy + ynh_exec_warn_less sudo -u "$app" env PATH="$PATH" cargo build --release +popd + +# Compile pict-rs +pushd $final_path/build-pict-rs + ynh_exec_warn_less sudo -u "$app" env PATH="$PATH" cargo build --release +popd + +# Install lemmy +mkdir -p "$final_path/lemmy_server/" +cp -af "$final_path/build-lemmy/target/release/lemmy_server" "$final_path/lemmy_server/lemmy_server" + +# Install pict-rs +mkdir -p "$final_path/pict-rs/" +cp -af "$final_path/build-pict-rs/target/release/pict-rs" "$final_path/pict-rs/pict-rs" + +# Remove build files and rustup +ynh_secure_remove --file="$final_path/build-lemmy" +ynh_secure_remove --file="$final_path/build-pict-rs" +ynh_secure_remove --file="$final_path/build-imagemagick" +ynh_secure_remove --file="$final_path/.cargo" +ynh_secure_remove --file="$final_path/.rustup" + +# Compile lemmy-ui +pushd $final_path/lemmy-ui + ynh_exec_warn_less sudo -u "$app" env PATH="$PATH" yarn install --pure-lockfile + ynh_exec_warn_less sudo -u "$app" env PATH="$PATH" yarn build:prod +popd + +# Compile iframely +pushd $final_path/iframely + ynh_exec_warn_less sudo -u "$app" env PATH="$PATH" $ynh_npm install +popd + +chmod 750 "$final_path" +chmod -R o-rwx "$final_path" +chown -R $app:$app "$final_path" + +#================================================= +# ADD A CONFIGURATION +#================================================= +ynh_script_progression --message="Adding a configuration file..." + +mkdir -p "$final_path/config/" +ynh_add_config --template="../conf/lemmy.hjson" --destination="$final_path/config/config.hjson" + +chmod 400 "$final_path/config/config.hjson" +chown $app:$app "$final_path/config/config.hjson" + +ynh_add_config --template="../conf/iframely.config.local.js" --destination="$final_path/iframely/iframely.config.local.js" + +chmod 400 "$final_path/iframely/iframely.config.local.js" +chown $app:$app "$final_path/iframely/iframely.config.local.js" + +#================================================= +# SETUP SYSTEMD +#================================================= +ynh_script_progression --message="Configuring a systemd service..." + +# Create a dedicated systemd config +ynh_add_systemd_config --service="$app" --template="lemmy.service" +ynh_add_systemd_config --service="$app-ui" --template="lemmy-ui.service" +ynh_add_systemd_config --service="$app-iframely" --template="iframely.service" +ynh_add_systemd_config --service="$app-pict-rs" --template="pict-rs.service" + +#================================================= +# GENERIC FINALIZATION +#================================================= +# SETUP LOGROTATE +#================================================= +ynh_script_progression --message="Configuring log rotation..." + +mkdir -p "/var/log/$app" +chmod 750 "/var/log/$app" +chmod -R o-rwx "/var/log/$app" +chown -R $app:$app "/var/log/$app" + +# Use logrotate to manage application logfile(s) +ynh_use_logrotate + +#================================================= +# INTEGRATE SERVICE IN YUNOHOST +#================================================= +ynh_script_progression --message="Integrating service in YunoHost..." + +yunohost service add $app --description="A short description of the app" --log="/var/log/$app/$app.log" +yunohost service add $app-ui --description="A short description of the app" --log="/var/log/$app/$app-ui.log" +yunohost service add $app-iframely --description="A short description of the app" --log="/var/log/$app/$app-iframely.log" +yunohost service add $app-pict-rs --description="A short description of the app" --log="/var/log/$app/$app-pict-rs.log" + +#================================================= +# START SYSTEMD SERVICE +#================================================= +ynh_script_progression --message="Starting a systemd service..." + +# Start a systemd service +ynh_systemd_action --service_name=$app --action="start" --line_match="Started" --log_path=systemd +ynh_systemd_action --service_name=$app-ui --action="start" --line_match="Started" --log_path=systemd +ynh_systemd_action --service_name=$app-iframely --action="start" --line_match="Started" --log_path=systemd +ynh_systemd_action --service_name=$app-pict-rs --action="start" --line_match="Started" --log_path=systemd #================================================= # SETUP SSOWAT #================================================= -ynh_script_progression --message="Configuring permissions..." --weight=1 - +ynh_script_progression --message="Configuring permissions..." +# Make app public ynh_permission_update --permission="main" --add="visitors" - #================================================= # RELOAD NGINX #================================================= -ynh_script_progression --message="Reloading NGINX web server..." --weight=1 +ynh_script_progression --message="Reloading NGINX web server..." ynh_systemd_action --service_name=nginx --action=reload - -#================================================= -# Run Lemmy through Docker -#================================================= -# chown -R $(whoami) /usr/local/bin - -chmod +x /usr/local/bin/docker-compose -cd "$final_path" && docker-compose up -d - - #================================================= # SEND A README FOR THE ADMIN #================================================= @@ -175,4 +305,4 @@ ynh_send_readme_to_admin "$message" # END OF SCRIPT #================================================= -ynh_script_progression --message="Installation of $app completed" --last +ynh_script_progression --message="Installation of $app completed" diff --git a/scripts/remove b/scripts/remove index d8471bb..4ed0f1d 100755 --- a/scripts/remove +++ b/scripts/remove @@ -12,30 +12,91 @@ source /usr/share/yunohost/helpers #================================================= # LOAD SETTINGS #================================================= -ynh_script_progression --message="Loading installation settings..." --weight=1 +ynh_script_progression --message="Loading installation settings..." app=$YNH_APP_INSTANCE_NAME domain=$(ynh_app_setting_get --app=$app --key=domain) +db_name=$(ynh_app_setting_get --app=$app --key=db_name) +db_user=$db_name final_path=$(ynh_app_setting_get --app=$app --key=final_path) #================================================= # STANDARD REMOVE #================================================= -cd "$final_path" && docker-compose down --rmi all +# REMOVE SERVICE INTEGRATION IN YUNOHOST +#================================================= + +# Remove the service from the list of services known by YunoHost (added from `yunohost service add`) +if ynh_exec_warn_less yunohost service status $app >/dev/null +then + ynh_script_progression --message="Removing $app service integration..." + yunohost service remove $app +fi +if ynh_exec_warn_less yunohost service status $app >/dev/null +then + ynh_script_progression --message="Removing $app-ui service integration..." + yunohost service remove $app-ui +fi +if ynh_exec_warn_less yunohost service status $app >/dev/null +then + ynh_script_progression --message="Removing $app-iframely service integration..." + yunohost service remove $app-iframely +fi +if ynh_exec_warn_less yunohost service status $app >/dev/null +then + ynh_script_progression --message="Removing $app-pict-rs service integration..." + yunohost service remove $app-pict-rs +fi + +#================================================= +# STOP AND REMOVE SERVICE +#================================================= +ynh_script_progression --message="Stopping and removing the systemd service..." + +# Remove the dedicated systemd config +ynh_remove_systemd_config +ynh_remove_systemd_config --service=$app-ui +ynh_remove_systemd_config --service=$app-iframely +ynh_remove_systemd_config --service=$app-pict-rs + +#================================================= +# REMOVE THE POSTGRESQL DATABASE +#================================================= +ynh_script_progression --message="Removing the PostgreSQL database..." + +# Remove a database if it exists, along with the associated user +ynh_psql_remove_db --db_user=$db_user --db_name=$db_name + +#================================================= +# REMOVE IMAGEMAGICK +#================================================= +ynh_script_progression --message="Removing ImageMagick..." + +ynh_setup_source --dest_dir="$final_path/build-imagemagick/" --source_id="imagemagick" + +# Install ImageMagick +pushd "$final_path/build-imagemagick/" + ./configure + make uninstall + ldconfig /usr/local/lib +popd + +ynh_secure_remove --file="$final_path/build-imagemagick" #================================================= # REMOVE DEPENDENCIES #================================================= -ynh_script_progression --message="Removing dependencies..." --weight=1 +ynh_script_progression --message="Removing dependencies..." # Remove metapackage and its dependencies +ynh_remove_nodejs ynh_remove_app_dependencies #================================================= # REMOVE APP MAIN DIR #================================================= -ynh_script_progression --message="Removing app main directory..." --weight=1 +ynh_script_progression --message="Removing app main directory..." # Remove the app directory securely ynh_secure_remove --file="$final_path" @@ -43,13 +104,41 @@ ynh_secure_remove --file="$final_path" #================================================= # REMOVE NGINX CONFIGURATION #================================================= -ynh_script_progression --message="Removing NGINX web server configuration..." --weight=1 +ynh_script_progression --message="Removing NGINX web server configuration..." # Remove the dedicated NGINX config ynh_remove_nginx_config +#================================================= +# REMOVE LOGROTATE CONFIGURATION +#================================================= +ynh_script_progression --message="Removing logrotate configuration..." + +# Remove the app-specific logrotate config +ynh_remove_logrotate + +#================================================= +# SPECIFIC REMOVE +#================================================= +# REMOVE VARIOUS FILES +#================================================= +ynh_script_progression --message="Removing various files..." + +# Remove the log files +ynh_secure_remove --file="/var/log/$app" + +#================================================= +# GENERIC FINALIZATION +#================================================= +# REMOVE DEDICATED USER +#================================================= +ynh_script_progression --message="Removing the dedicated system user..." + +# Delete a system user +ynh_system_user_delete --username=$app + #================================================= # END OF SCRIPT #================================================= -ynh_script_progression --message="Removal of $app completed" --last +ynh_script_progression --message="Removal of $app completed" diff --git a/scripts/restore b/scripts/restore index a5fa025..587ed4d 100755 --- a/scripts/restore +++ b/scripts/restore @@ -15,8 +15,7 @@ source /usr/share/yunohost/helpers #================================================= ynh_clean_setup () { - #### Remove this function if there's nothing to clean before calling the remove script. - true + ynh_clean_check_starting } # Exit if an error occurs during the execution of the script ynh_abort_if_errors @@ -24,19 +23,22 @@ ynh_abort_if_errors #================================================= # LOAD SETTINGS #================================================= -ynh_script_progression --message="Loading installation settings..." --weight=1 +ynh_script_progression --message="Loading installation settings..." app=$YNH_APP_INSTANCE_NAME domain=$(ynh_app_setting_get --app=$app --key=domain) path_url=$(ynh_app_setting_get --app=$app --key=path) final_path=$(ynh_app_setting_get --app=$app --key=final_path) -version=$(curl -s https://api.github.com/repos/docker/compose/releases/latest | grep 'tag_name' | cut -d\" -f4) +db_name=$(ynh_app_setting_get --app=$app --key=db_name) +db_user=$db_name +db_pwd=$(ynh_app_setting_get --app=$app --key=db_pwd) +datadir=$(ynh_app_setting_get --app=$app --key=datadir) #================================================= # CHECK IF THE APP CAN BE RESTORED #================================================= -ynh_script_progression --message="Validating restoration parameters..." --weight=1 +ynh_script_progression --message="Validating restoration parameters..." ynh_webpath_available --domain=$domain --path_url=$path_url \ || ynh_die --message="Path not available: ${domain}${path_url}" @@ -48,45 +50,133 @@ test ! -d $final_path \ #================================================= # RESTORE THE NGINX CONFIGURATION #================================================= +ynh_script_progression --message="Restoring the NGINX web server configuration..." ynh_restore_file --origin_path="/etc/nginx/conf.d/$domain.d/$app.conf" +#================================================= +# RECREATE THE DEDICATED USER +#================================================= +ynh_script_progression --message="Recreating the dedicated system user..." + +# Create the dedicated user (if not existing) +ynh_system_user_create --username=$app --home_dir=$final_path + #================================================= # RESTORE THE APP MAIN DIR #================================================= -ynh_script_progression --message="Restoring the app main directory..." --weight=1 +ynh_script_progression --message="Restoring the app main directory..." ynh_restore_file --origin_path="$final_path" +chmod 750 "$final_path" +chmod -R o-rwx "$final_path" +chown -R $app:$app "$final_path" #================================================= -# INSTALL DEPENDENCIES +# RESTORE THE DATA DIRECTORY #================================================= -ynh_script_progression --message="Installing dependencies..." --weight=1 +ynh_script_progression --message="Restoring the data directory..." -#ynh_install_app_dependencies $pkg_dependencies +ynh_restore_file --origin_path="$datadir" --not_mandatory -# Install Docker and compose -curl -sSL https://get.docker.com | sh -systemctl enable docker --quiet -curl -L https://github.com/docker/compose/releases/download/${version}/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose +mkdir -p $datadir + +chmod 750 "$datadir" +chmod -R o-rwx "$datadir" +chown -R $app:$app "$datadir" #================================================= # SPECIFIC RESTORATION #================================================= -# Run Lemmy through Docker +# REINSTALL DEPENDENCIES #================================================= -# chown -R $(whoami) /usr/local/bin +ynh_script_progression --message="Reinstalling dependencies..." -chmod +x /usr/local/bin/docker-compose -cd "$final_path" && docker-compose up -d +# Define and install dependencies +ynh_install_app_dependencies $pkg_dependencies +ynh_install_nodejs --nodejs_version=$NODEJS_VERSION +ynh_use_nodejs +ynh_install_extra_app_dependencies --repo="deb https://dl.yarnpkg.com/debian/ stable main" --package="yarn" --key="https://dl.yarnpkg.com/debian/pubkey.gpg" + +#================================================= +# BUILDING IMAGEMAGICK +#================================================= +ynh_script_progression --message="Building ImageMagick..." + +ynh_setup_source --dest_dir="$final_path/build-imagemagick/" --source_id="imagemagick" + +# Install ImageMagick +pushd "$final_path/build-imagemagick/" + ./configure + make + make install + ldconfig /usr/local/lib +popd + +ynh_secure_remove --file="$final_path/build-imagemagick" + +#================================================= +# RESTORE THE POSTGRESQL DATABASE +#================================================= +ynh_script_progression --message="Restoring the PostgreSQL database..." + +ynh_psql_test_if_first_run +ynh_psql_setup_db --db_user=$db_user --db_name=$db_name --db_pwd=$db_pwd +ynh_psql_execute_file_as_root --file="./db.sql" --database="$db_name" + +#================================================= +# RESTORE SYSTEMD +#================================================= +ynh_script_progression --message="Restoring the systemd configuration..." + +ynh_restore_file --origin_path="/etc/systemd/system/$app.service" +systemctl enable $app.service --quiet +ynh_restore_file --origin_path="/etc/systemd/system/$app-ui.service" +systemctl enable $app-ui.service --quiet +ynh_restore_file --origin_path="/etc/systemd/system/$app-iframely.service" +systemctl enable $app-iframely.service --quiet +ynh_restore_file --origin_path="/etc/systemd/system/$app-pict-rs.service" +systemctl enable $app-pict-rs.service --quiet + +#================================================= +# INTEGRATE SERVICE IN YUNOHOST +#================================================= +ynh_script_progression --message="Integrating service in YunoHost..." + +yunohost service add $app --description="A short description of the app" --log="/var/log/$app/$app.log" +yunohost service add $app-ui --description="A short description of the app" --log="/var/log/$app/$app-ui.log" +yunohost service add $app-iframely --description="A short description of the app" --log="/var/log/$app/$app-iframely.log" +yunohost service add $app-pict-rs --description="A short description of the app" --log="/var/log/$app/$app-pict-rs.log" + +#================================================= +# START SYSTEMD SERVICE +#================================================= +ynh_script_progression --message="Starting a systemd service..." + +ynh_systemd_action --service_name=$app --action="start" --line_match="Started" --log_path=systemd +ynh_systemd_action --service_name=$app-ui --action="start" --line_match="Started" --log_path=systemd +ynh_systemd_action --service_name=$app-iframely --action="start" --line_match="Started" --log_path=systemd +ynh_systemd_action --service_name=$app-pict-rs --action="start" --line_match="Started" --log_path=systemd + +#================================================= +# RESTORE THE LOGROTATE CONFIGURATION +#================================================= +ynh_script_progression --message="Restoring the logrotate configuration..." + +mkdir -p "/var/log/$app" +chmod 750 "/var/log/$app" +chmod -R o-rwx "/var/log/$app" +chown -R $app:$app "/var/log/$app" + +ynh_restore_file --origin_path="/etc/logrotate.d/$app" #================================================= # GENERIC FINALIZATION #================================================= # RELOAD NGINX AND PHP-FPM #================================================= -ynh_script_progression --message="Reloading NGINX web server..." --weight=1 +ynh_script_progression --message="Reloading NGINX web server..." ynh_systemd_action --service_name=nginx --action=reload @@ -94,4 +184,4 @@ ynh_systemd_action --service_name=nginx --action=reload # END OF SCRIPT #================================================= -ynh_script_progression --message="Restoration completed for $app" --last +ynh_script_progression --message="Restoration completed for $app" diff --git a/scripts/upgrade b/scripts/upgrade index b97d808..596a5a0 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -12,7 +12,7 @@ source /usr/share/yunohost/helpers #================================================= # LOAD SETTINGS #================================================= -ynh_script_progression --message="Loading installation settings..." --weight=1 +ynh_script_progression --message="Loading installation settings..." app=$YNH_APP_INSTANCE_NAME @@ -20,6 +20,10 @@ domain=$(ynh_app_setting_get --app=$app --key=domain) path_url=$(ynh_app_setting_get --app=$app --key=path) admin=$(ynh_app_setting_get --app=$app --key=admin) final_path=$(ynh_app_setting_get --app=$app --key=final_path) +db_name=$(ynh_app_setting_get --app=$app --key=db_name) +db_user=$db_name +db_pwd=$(ynh_app_setting_get --app=$app --key=db_pwd) +datadir=$(ynh_app_setting_get --app=$app --key=datadir) password=$(ynh_app_setting_get --app=$app --key=password) sitename=$(ynh_app_setting_get --app=$app --key=sitename) random=$(ynh_app_setting_get --app=$app --key=random) @@ -27,17 +31,24 @@ port_lemmy=$(ynh_app_setting_get --app=$app --key=port_lemmy) port_ui=$(ynh_app_setting_get --app=$app --key=port_ui) port_pictrs=$(ynh_app_setting_get --app=$app --key=port_pictrs) port_iframely=$(ynh_app_setting_get --app=$app --key=port_iframely) -version=$(curl -s https://api.github.com/repos/docker/compose/releases/latest | grep 'tag_name' | cut -d\" -f4) +admin_email=$(ynh_user_get_info --username=$admin --key="mail") +#================================================= +# CHECK VERSION +#================================================= +ynh_script_progression --message="Checking version..." + +upgrade_type=$(ynh_check_app_version_changed) #================================================= # BACKUP BEFORE UPGRADE THEN ACTIVE TRAP #================================================= -ynh_script_progression --message="Backing up the app before upgrading (may take a while)..." --weight=1 +ynh_script_progression --message="Backing up the app before upgrading (may take a while)..." # Backup the current version of the app ynh_backup_before_upgrade ynh_clean_setup () { + ynh_clean_check_starting # Restore it if the upgrade fails ynh_restore_upgradebackup } @@ -47,7 +58,44 @@ ynh_abort_if_errors #================================================= # STANDARD UPGRADE STEPS #================================================= +# STOP SYSTEMD SERVICE +#================================================= +ynh_script_progression --message="Stopping a systemd service..." +ynh_systemd_action --service_name=$app --action="stop" --line_match="Stopped" --log_path=systemd +ynh_systemd_action --service_name=$app-ui --action="stop" --line_match="Stopped" --log_path=systemd +ynh_systemd_action --service_name=$app-iframely --action="stop" --line_match="Stopped" --log_path=systemd +ynh_systemd_action --service_name=$app-pict-rs --action="stop" --line_match="Stopped" --log_path=systemd + +#================================================= +# ENSURE DOWNWARD COMPATIBILITY +#================================================= +ynh_script_progression --message="Ensuring downward compatibility..." + +# Cleaning legacy permissions +if ynh_legacy_permissions_exists; then + ynh_legacy_permissions_delete_all + + ynh_app_setting_delete --app=$app --key=is_public +fi + +if ! ynh_permission_exists --permission="admin"; then + # Create the required permissions + ynh_permission_create --permission="admin" --url="/admin" --allowed=$admin +fi + +# Create a permission if needed +if ! ynh_permission_exists --permission="api"; then + ynh_permission_create --permission="api" --url="/api" --allowed="visitors" --show_tile="false" --protected="true" +fi + +#================================================= +# CREATE DEDICATED USER +#================================================= +ynh_script_progression --message="Making sure dedicated system user exists..." + +# Create a dedicated user (if not existing) +ynh_system_user_create --username=$app --home_dir=$final_path #================================================= # DOWNLOAD, CHECK AND UNPACK SOURCE @@ -55,78 +103,190 @@ ynh_abort_if_errors if [ "$upgrade_type" == "UPGRADE_APP" ] then - ynh_script_progression --message="Upgrading source files..." --weight=1 + ynh_script_progression --message="Upgrading source files..." - # Download, check integrity, uncompress and patch the source from app.src - cp -f ../conf/docker-compose.yml "$final_path/docker-compose.yml" - cp -f ../conf/lemmy.hjson "$final_path/lemmy.hjson" - cp -f ../conf/iframely.config.local.js "$final_path/iframely.config.local.js" - pushd "$final_path" - chown -R 991:991 volumes/pictrs - popd + # Download, check integrity, uncompress the source of lemmy from app.src to his build directory + ynh_setup_source --dest_dir="$final_path/build-lemmy/" --source_id="lemmy" + ynh_setup_source --dest_dir="$final_path/lemmy-ui/" --source_id="lemmy-ui" + ynh_setup_source --dest_dir="$final_path/lemmy-ui/lemmy-translations/" --source_id="lemmy-translations" + + ynh_setup_source --dest_dir="$final_path/iframely/" --source_id="iframely" + ynh_setup_source --dest_dir="$final_path/build-pict-rs/" --source_id="pict-rs" + ynh_setup_source --dest_dir="$final_path/build-imagemagick/" --source_id="imagemagick" fi +chmod 750 "$final_path" +chmod -R o-rwx "$final_path" +chown -R $app:$app "$final_path" + #================================================= # NGINX CONFIGURATION #================================================= -ynh_script_progression --message="Upgrading NGINX web server configuration..." --weight=1 +ynh_script_progression --message="Upgrading NGINX web server configuration..." # Create a dedicated NGINX config -ynh_add_nginx_config 'port_lemmy port_ui port_pictrs port_iframely' +ynh_add_nginx_config #================================================= # UPGRADE DEPENDENCIES #================================================= -ynh_script_progression --message="Upgrading dependencies..." --weight=1 - -#ynh_install_app_dependencies $pkg_dependencies - -# Install Docker and compose -curl -sSL https://get.docker.com | sh -systemctl enable docker --quiet -curl -L https://github.com/docker/compose/releases/download/${version}/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose +ynh_script_progression --message="Upgrading dependencies..." +ynh_install_app_dependencies $pkg_dependencies +ynh_install_nodejs --nodejs_version=$NODEJS_VERSION +ynh_use_nodejs +ynh_install_extra_app_dependencies --repo="deb https://dl.yarnpkg.com/debian/ stable main" --package="yarn" --key="https://dl.yarnpkg.com/debian/pubkey.gpg" #================================================= # SPECIFIC UPGRADE #================================================= +# MAKE UPGRADE +#================================================= +ynh_script_progression --message="Making upgrade..." +if [ "$upgrade_type" == "UPGRADE_APP" ] +then + # Install ImageMagick + pushd "$final_path/build-imagemagick/" + ./configure + make + make install + ldconfig /usr/local/lib + popd + + # Install rustup with the toolchain needed by lemmy + pushd "$final_path" + sudo -u "$app" RUSTUP_HOME="$final_path"/.rustup CARGO_HOME="$final_path"/.cargo bash -c 'curl -sSf -L https://static.rust-lang.org/rustup.sh | sh -s -- -y --default-toolchain nightly' + popd + + export PATH="$PATH:$final_path/.cargo/bin:$final_path/.local/bin:/usr/local/sbin" + + # Compile lemmy + pushd "$final_path"/build + ynh_exec_warn_less sudo -u "$app" env PATH="$PATH" cargo build --release + popd + + # Compile pict-rs + pushd $final_path/build-pict-rs + ynh_exec_warn_less sudo -u "$app" env PATH="$PATH" cargo build --release + popd + + # Remove old generated files before copying the new ones + ynh_secure_remove --file="$final_path/lemmy/.fingerprint" + ynh_secure_remove --file="$final_path/lemmy/build" + ynh_secure_remove --file="$final_path/lemmy/deps" + ynh_secure_remove --file="$final_path/lemmy/examples" + ynh_secure_remove --file="$final_path/lemmy/incremental" + ynh_secure_remove --file="$final_path/lemmy/.cargo-lock" + ynh_secure_remove --file="$final_path/lemmy/lemmy.d" + + # Install lemmy + mkdir -p "$final_path/lemmy_server/" + cp -af "$final_path/build-lemmy/target/release/lemmy_server" "$final_path/lemmy_server/lemmy_server" + + # Install pict-rs + mkdir -p "$final_path/pict-rs/" + cp -af "$final_path/build-pict-rs/target/release/pict-rs" "$final_path/pict-rs/pict-rs" + + # Remove build files and rustup + ynh_secure_remove --file="$final_path/build-lemmy" + ynh_secure_remove --file="$final_path/build-pict-rs" + ynh_secure_remove --file="$final_path/build-imagemagick" + ynh_secure_remove --file="$final_path/.cargo" + ynh_secure_remove --file="$final_path/.rustup" + + # Compile lemmy-ui + pushd $final_path/lemmy-ui + ynh_exec_warn_less sudo -u "$app" env PATH="$PATH" yarn install --pure-lockfile + ynh_exec_warn_less sudo -u "$app" env PATH="$PATH" yarn build:prod + popd + + # Compile iframely + pushd $final_path/iframely + ynh_exec_warn_less sudo -u "$app" env PATH="$PATH" $ynh_npm install + popd +fi + +chmod 750 "$final_path" +chmod -R o-rwx "$final_path" +chown -R $app:$app "$final_path" + +mkdir -p "/var/log/$app" +chmod 750 "/var/log/$app" +chmod -R o-rwx "/var/log/$app" +chown -R $app:$app "/var/log/$app" #================================================= -# MODIFY A CONFIG FILE +# UPDATE A CONFIG FILE #================================================= +ynh_script_progression --message="Updating a configuration file..." +mkdir -p "$final_path/lemmy_server/" +ynh_add_config --template="../conf/lemmy.hjson" --destination="$final_path/config/config.hjson" -ynh_replace_string --match_string="match_string" --replace_string="replace_string" --target_file="$final_path/CONFIG_FILE" +chmod 400 "$final_path/config/config.hjson" +chown $app:$app "$final_path/config/config.hjson" +ynh_add_config --template="../conf/iframely.config.local.js" --destination="$final_path/iframely/iframely.config.local.js" -ynh_replace_string --match_string="__ADMIN__" --replace_string="$admin" --target_file="$final_path/lemmy.hjson" -ynh_replace_string --match_string="__ADMIN_PASS__" --replace_string="$password" --target_file="$final_path/lemmy.hjson" -ynh_replace_string --match_string="__NAME__" --replace_string="$sitename" --target_file="$final_path/lemmy.hjson" -ynh_replace_string --match_string="__DOMAIN__" --replace_string="$domain" --target_file="$final_path/lemmy.hjson" -ynh_replace_string --match_string="__RANDOM__" --replace_string="$random" --target_file="$final_path/lemmy.hjson" -ynh_replace_string --match_string="__PORT__LEMMY__" --replace_string="$port_lemmy" --target_file="$final_path/docker-compose.yml" -ynh_replace_string --match_string="__PORT_UI__" --replace_string="$port_ui" --target_file="$final_path/docker-compose.yml" -ynh_replace_string --match_string="__PORT_PICTRS__" --replace_string="$port_pictrs" --target_file="$final_path/docker-compose.yml" -ynh_replace_string --match_string="__PORT_IFRAMELY__" --replace_string="$port_iframely" --target_file="$final_path/docker-compose.yml" -ynh_replace_string --match_string="__DOMAIN__" --replace_string="$domain" --target_file="$final_path/iframely.config.local.js" +chmod 400 "$final_path/iframely/iframely.config.local.js" +chown $app:$app "$final_path/iframely/iframely.config.local.js" + +#================================================= +# SETUP SYSTEMD +#================================================= +ynh_script_progression --message="Upgrading systemd configuration..." + +# Create a dedicated systemd config +ynh_add_systemd_config --service="$app" --template="lemmy.service" +ynh_add_systemd_config --service="$app-ui" --template="lemmy-ui.service" +ynh_add_systemd_config --service="$app-iframely" --template="iframely.service" +ynh_add_systemd_config --service="$app-pict-rs" --template="pict-rs.service" + +#================================================= +# GENERIC FINALIZATION +#================================================= +# SETUP LOGROTATE +#================================================= +ynh_script_progression --message="Upgrading logrotate configuration..." + +mkdir -p "/var/log/$app" +chmod 750 "/var/log/$app" +chmod -R o-rwx "/var/log/$app" +chown -R $app:$app "/var/log/$app" + +# Use logrotate to manage app-specific logfile(s) +ynh_use_logrotate --non-append + +#================================================= +# INTEGRATE SERVICE IN YUNOHOST +#================================================= +ynh_script_progression --message="Integrating service in YunoHost..." + +yunohost service add $app --description="A short description of the app" --log="/var/log/$app/$app.log" +yunohost service add $app-ui --description="A short description of the app" --log="/var/log/$app/$app-ui.log" +yunohost service add $app-iframely --description="A short description of the app" --log="/var/log/$app/$app-iframely.log" +yunohost service add $app-pict-rs --description="A short description of the app" --log="/var/log/$app/$app-pict-rs.log" + +#================================================= +# START SYSTEMD SERVICE +#================================================= +ynh_script_progression --message="Starting a systemd service..." + +ynh_systemd_action --service_name=$app --action="start" --line_match="Started" --log_path=systemd +ynh_systemd_action --service_name=$app-ui --action="start" --line_match="Started" --log_path=systemd +ynh_systemd_action --service_name=$app-iframely --action="start" --line_match="Started" --log_path=systemd +ynh_systemd_action --service_name=$app-pict-rs --action="start" --line_match="Started" --log_path=systemd #================================================= # RELOAD NGINX #================================================= -ynh_script_progression --message="Reloading NGINX web server..." --weight=1 +ynh_script_progression --message="Reloading NGINX web server..." ynh_systemd_action --service_name=nginx --action=reload -#================================================= -# Run Lemmy through Docker -#================================================= -#chown -R $(whoami) /usr/local/bin -chmod +x /usr/local/bin/docker-compose -cd "$final_path" && docker-compose up -d - #================================================= # END OF SCRIPT #================================================= -ynh_script_progression --message="Upgrade of $app completed" --last +ynh_script_progression --message="Upgrade of $app completed" From ebac8ae545cdcb8aae8355cdc0b4df527474b988 Mon Sep 17 00:00:00 2001 From: Yunohost-Bot <> Date: Wed, 7 Jul 2021 14:42:12 +0000 Subject: [PATCH 02/15] Auto-update README --- README.md | 72 ++++++++++++++++++++-------------------------------- README_fr.md | 48 +++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 45 deletions(-) create mode 100644 README_fr.md diff --git a/README.md b/README.md index f6892e1..8dfea55 100644 --- a/README.md +++ b/README.md @@ -1,70 +1,52 @@ + +# lemmy for YunoHost -# Lemmy app for YunoHost +[![Integration level](https://dash.yunohost.org/integration/lemmy.svg)](https://dash.yunohost.org/appci/app/lemmy) ![](https://ci-apps.yunohost.org/ci/badges/lemmy.status.svg) ![](https://ci-apps.yunohost.org/ci/badges/lemmy.maintain.svg) +[![Install lemmy with YunoHost](https://install-app.yunohost.org/install-with-yunohost.svg)](https://install-app.yunohost.org/?app=lemmy) -[![Integration level](https://dash.yunohost.org/integration/lemmy.svg)](https://dash.yunohost.org/appci/app/lemmy) ![](https://ci-apps.yunohost.org/ci/badges/lemmy.status.svg) ![](https://ci-apps.yunohost.org/ci/badges/lemmy.maintain.svg) +*[Lire ce readme en français.](./README_fr.md)* -[![Install Lemmy with YunoHost](https://install-app.yunohost.org/install-with-yunohost.svg)](https://install-app.yunohost.org/?app=lemmy) - - -> *This package allows you to install Lemmy quickly and simply on a YunoHost server. +> *This package allows you to install lemmy quickly and simply on a YunoHost server. If you don't have YunoHost, please consult [the guide](https://yunohost.org/#/install) to learn how to install it.* ## Overview -Lemmy is similar to sites like Reddit, Lobste.rs, Raddle, or Hacker News: you subscribe to forums you're interested in, post links and discussions, then vote, and comment on them. Behind the scenes, it is very different; anyone can easily run a server, and all these servers are federated (think email), and connected to the same universe, called the Fediverse. -**Shipped version:** 0.9.9 +A link aggregator / Reddit clone for the fediverse. + +**Shipped version:** 0.9.9~ynh1 + +**Demo:** https://join.lemmy.ml/join/ ## Screenshots -![](https://raw.githubusercontent.com/LemmyNet/joinlemmy-site/main/static/images/main_img.webp) +![](./doc/screenshots/screenshot1.webp) -## Demo +## Disclaimers / important information -* [Official demo](https://join.lemmy.ml/join/) +* Any known limitations, constrains or stuff not working, such as (but not limited to): + * Lemmy require full domain path to be installed. Eg. lemmy.domain.tld + * The admin username and password will be sent to the admin of the YunoHost through mail. +## Documentation and resources -## Configuration - -Lemmy require full domain path to be instlled. Eg. lemmy.domain.tld - -The admin username and password will be sent to the admin of the YunoHost through mail. - -## Documentation - - * Official documentation: https://join.lemmy.ml/docs/en/index.html - - -## YunoHost specific features - -#### Multi-user support - -Are LDAP and HTTP auth supported? No -Can the app be used by multiple users? Yes - -#### Supported architectures - -* x86-64 - [![Build Status](https://ci-apps.yunohost.org/ci/logs/lemmy%20%28Apps%29.svg)](https://ci-apps.yunohost.org/ci/apps/lemmy/) -* ARMv8-A - [![Build Status](https://ci-apps-arm.yunohost.org/ci/logs/lemmy%20%28Apps%29.svg)](https://ci-apps-arm.yunohost.org/ci/apps/lemmy/) - -## Links - - * Report a bug: https://github.com/YunoHost-Apps/lemmy_ynh/issues - * App website: https://join.lemmy.ml - * Upstream app repository: https://github.com/LemmyNet/lemmy - * YunoHost website: https://yunohost.org/ - ---- +* Official app website: https://join-lemmy.org/ +* Official admin documentation: https://join-lemmy.org/docs/en/ +* Upstream app code repository: https://github.com/LemmyNet/lemmy +* YunoHost documentation for this app: https://yunohost.org/app_lemmy +* Report a bug: https://github.com/YunoHost-Apps/lemmy_ynh/issues ## Developer info -**Only if you want to use a testing branch for coding, instead of merging directly into master.** Please send your pull request to the [testing branch](https://github.com/YunoHost-Apps/lemmy_ynh/tree/testing). To try the testing branch, please proceed like that. ``` sudo yunohost app install https://github.com/YunoHost-Apps/lemmy_ynh/tree/testing --debug - or - sudo yunohost app upgrade lemmy -u https://github.com/YunoHost-Apps/lemmy_ynh/tree/testing --debug ``` + +**More info regarding app packaging:** https://yunohost.org/packaging_apps \ No newline at end of file diff --git a/README_fr.md b/README_fr.md new file mode 100644 index 0000000..e3ab297 --- /dev/null +++ b/README_fr.md @@ -0,0 +1,48 @@ +# lemmy pour YunoHost + +[![Niveau d'intégration](https://dash.yunohost.org/integration/lemmy.svg)](https://dash.yunohost.org/appci/app/lemmy) ![](https://ci-apps.yunohost.org/ci/badges/lemmy.status.svg) ![](https://ci-apps.yunohost.org/ci/badges/lemmy.maintain.svg) +[![Installer lemmy avec YunoHost](https://install-app.yunohost.org/install-with-yunohost.svg)](https://install-app.yunohost.org/?app=lemmy) + +*[Read this readme in english.](./README.md)* +*[Lire ce readme en français.](./README_fr.md)* + +> *Ce package vous permet d'installer lemmy rapidement et simplement sur un serveur YunoHost. +Si vous n'avez pas YunoHost, regardez [ici](https://yunohost.org/#/install) pour savoir comment l'installer et en profiter.* + +## Vue d'ensemble + + + +**Version incluse :** 0.9.9~ynh1 + +**Démo :** https://join.lemmy.ml/join/ + +## Captures d'écran + +![](./doc/screenshots/screenshot1.webp) + +## Avertissements / informations importantes + +* Any known limitations, constrains or stuff not working, such as (but not limited to): + * Lemmy require full domain path to be installed. Eg. lemmy.domain.tld + * The admin username and password will be sent to the admin of the YunoHost through mail. +## Documentations et ressources + +* Site officiel de l'app : https://join-lemmy.org/ +* Documentation officielle de l'admin : https://join-lemmy.org/docs/en/ +* Dépôt de code officiel de l'app : https://github.com/LemmyNet/lemmy +* Documentation YunoHost pour cette app : https://yunohost.org/app_lemmy +* Signaler un bug : https://github.com/YunoHost-Apps/lemmy_ynh/issues + +## Informations pour les développeurs + +Merci de faire vos pull request sur la [branche testing](https://github.com/YunoHost-Apps/lemmy_ynh/tree/testing). + +Pour essayer la branche testing, procédez comme suit. +``` +sudo yunohost app install https://github.com/YunoHost-Apps/lemmy_ynh/tree/testing --debug +ou +sudo yunohost app upgrade lemmy -u https://github.com/YunoHost-Apps/lemmy_ynh/tree/testing --debug +``` + +**Plus d'infos sur le packaging d'applications :** https://yunohost.org/packaging_apps \ No newline at end of file From 2764e30174bc3010b454cca534735cac7c14d8f9 Mon Sep 17 00:00:00 2001 From: yalh76 Date: Fri, 16 Jul 2021 14:58:54 +0200 Subject: [PATCH 03/15] Fix upgrade --- scripts/upgrade | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/upgrade b/scripts/upgrade index 596a5a0..bd34178 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -159,10 +159,10 @@ then sudo -u "$app" RUSTUP_HOME="$final_path"/.rustup CARGO_HOME="$final_path"/.cargo bash -c 'curl -sSf -L https://static.rust-lang.org/rustup.sh | sh -s -- -y --default-toolchain nightly' popd - export PATH="$PATH:$final_path/.cargo/bin:$final_path/.local/bin:/usr/local/sbin" + export PATH="$PATH:$final_path/.cargo/bin:$final_path/.local/bin:/usr/local/sbin" # Compile lemmy - pushd "$final_path"/build + pushd $final_path/build-lemmy ynh_exec_warn_less sudo -u "$app" env PATH="$PATH" cargo build --release popd From c441200623fc2db7b6c396e6086326ca6c75b833 Mon Sep 17 00:00:00 2001 From: yalh76 Date: Tue, 20 Jul 2021 22:07:39 +0200 Subject: [PATCH 04/15] from docker instead of building --- conf/docker-image-extract.src | 7 +++ conf/iframely.src | 7 --- conf/imagemagick.src | 4 +- conf/lemmy-ui.src | 7 --- conf/lemmy.service | 4 +- conf/lemmy.src | 7 --- scripts/_common.sh | 15 ++++++- scripts/install | 71 +++++++++++++++--------------- scripts/remove | 1 + scripts/restore | 4 +- scripts/upgrade | 82 +++++++++++++++-------------------- 11 files changed, 97 insertions(+), 112 deletions(-) create mode 100644 conf/docker-image-extract.src delete mode 100644 conf/iframely.src delete mode 100644 conf/lemmy-ui.src delete mode 100644 conf/lemmy.src diff --git a/conf/docker-image-extract.src b/conf/docker-image-extract.src new file mode 100644 index 0000000..64fe1a1 --- /dev/null +++ b/conf/docker-image-extract.src @@ -0,0 +1,7 @@ +SOURCE_URL=https://codeload.github.com/jjlin/docker-image-extract/tar.gz/a9e455e44bbbfba897bf3342d9661b182cee67a9 +SOURCE_SUM=9eb0c734e83a3fd7102fc7209af4977024ec467fbc819782491af47295675f67 +SOURCE_SUM_PRG=sha256sum +SOURCE_FORMAT=tar.gz +SOURCE_IN_SUBDIR=true +SOURCE_FILENAME= +SOURCE_EXTRACT=true diff --git a/conf/iframely.src b/conf/iframely.src deleted file mode 100644 index 15e217e..0000000 --- a/conf/iframely.src +++ /dev/null @@ -1,7 +0,0 @@ -SOURCE_URL=https://github.com/itteco/iframely/archive/refs/tags/v1.6.0.tar.gz -SOURCE_SUM=8130267e17e4484a2cdd028cdffb619f430b9cd19b3614b5d1de2d05304d03f8 -SOURCE_SUM_PRG=sha256sum -SOURCE_FORMAT=tar.gz -SOURCE_IN_SUBDIR=true -SOURCE_FILENAME= -SOURCE_EXTRACT=true diff --git a/conf/imagemagick.src b/conf/imagemagick.src index 885e716..82b963e 100644 --- a/conf/imagemagick.src +++ b/conf/imagemagick.src @@ -1,5 +1,5 @@ -SOURCE_URL=https://github.com/ImageMagick/ImageMagick/archive/refs/tags/7.0.11-14.tar.gz -SOURCE_SUM=dfa5aa3f7f289f12c2f9ee6c7c19b02ae857b4eec02f40298f60f5c11048a016 +SOURCE_URL=https://github.com/ImageMagick/ImageMagick/archive/refs/tags/7.0.10-62.tar.gz +SOURCE_SUM=84442158aea070095efa832cfe868fd99d6befdf609444f0c9e9f1b4f25480cd SOURCE_SUM_PRG=sha256sum SOURCE_FORMAT=tar.gz SOURCE_IN_SUBDIR=true diff --git a/conf/lemmy-ui.src b/conf/lemmy-ui.src deleted file mode 100644 index 5d59d2a..0000000 --- a/conf/lemmy-ui.src +++ /dev/null @@ -1,7 +0,0 @@ -SOURCE_URL=https://github.com/LemmyNet/lemmy-ui/archive/refs/tags/0.11.2.tar.gz -SOURCE_SUM=645954b3726803b0ba6b756a2b4f4099e2de29e59cadd828bebd592b29149611 -SOURCE_SUM_PRG=sha256sum -SOURCE_FORMAT=tar.gz -SOURCE_IN_SUBDIR=true -SOURCE_FILENAME= -SOURCE_EXTRACT=true diff --git a/conf/lemmy.service b/conf/lemmy.service index b56b41b..02e1fd0 100644 --- a/conf/lemmy.service +++ b/conf/lemmy.service @@ -7,8 +7,8 @@ Type=simple User=__APP__ Group=__APP__ Environment="LEMMY_CONFIG_LOCATION=__FINALPATH__/config/config.hjson" -WorkingDirectory=__FINALPATH__/lemmy_server/ -ExecStart=__FINALPATH__/lemmy_server/lemmy_server +WorkingDirectory=__FINALPATH__/lemmy/ +ExecStart=__FINALPATH__/lemmy/lemmy StandardOutput=append:/var/log/__APP__/__APP__.log StandardError=inherit diff --git a/conf/lemmy.src b/conf/lemmy.src deleted file mode 100644 index 8866357..0000000 --- a/conf/lemmy.src +++ /dev/null @@ -1,7 +0,0 @@ -SOURCE_URL=https://github.com/LemmyNet/lemmy/archive/refs/tags/0.11.0.tar.gz -SOURCE_SUM=8c93268d5cb7b30c9c25e2fdeef83153d95b7b79ad0b0a6f354d89e72a0ec641 -SOURCE_SUM_PRG=sha256sum -SOURCE_FORMAT=tar.gz -SOURCE_IN_SUBDIR=true -SOURCE_FILENAME= -SOURCE_EXTRACT=true diff --git a/scripts/_common.sh b/scripts/_common.sh index e927f87..b1b4f5b 100644 --- a/scripts/_common.sh +++ b/scripts/_common.sh @@ -5,10 +5,23 @@ #================================================= # dependencies used by the app -pkg_dependencies="postgresql postgresql-contrib espeak bash-completion ffmpeg clang pkg-config libgexiv2-dev libgexiv2-2 libssl-dev libc6-dev libpq-dev libavutil-dev libavformat-dev libavfilter-dev libavdevice-dev libavresample-dev libjpeg-turbo-progs libpng-dev" +lemmy_dependencies="postgresql postgresql-contrib" +lemmyui_dependencies="espeak" +iframely_dependencies="musl-dev" +pictrs_dependencies="bash-completion ffmpeg clang pkg-config libgexiv2-dev libgexiv2-2" +imagemagick_dependencies="" + +pkg_dependencies="$lemmy_dependencies $lemmyui_dependencies $iframely_dependencies $pictrs_dependencies $imagemagick_dependencies" +# libssl-dev libc6-dev libpq-dev libavutil-dev libavformat-dev libavfilter-dev libavdevice-dev libavresample-dev libjpeg-turbo-progs libpng-dev NODEJS_VERSION=12 +LEMMY_VERSION=0.11.0 + +LEMMYUI_VERSION=0.11.0 + +PICTRS_VERSION=v0.2.6-r1 + #================================================= # PERSONAL HELPERS #================================================= diff --git a/scripts/install b/scripts/install index ecc63b5..950ed35 100755 --- a/scripts/install +++ b/scripts/install @@ -81,7 +81,7 @@ ynh_script_progression --message="Installing dependencies..." ynh_install_app_dependencies $pkg_dependencies ynh_install_nodejs --nodejs_version=$NODEJS_VERSION ynh_use_nodejs -ynh_install_extra_app_dependencies --repo="deb https://dl.yarnpkg.com/debian/ stable main" --package="yarn" --key="https://dl.yarnpkg.com/debian/pubkey.gpg" +ln -s /usr/lib/x86_64-linux-musl/libc.so /lib/libc.musl-x86_64.so.1 #================================================= # CREATE DEDICATED USER @@ -111,11 +111,11 @@ ynh_script_progression --message="Setting up source files..." ynh_app_setting_set --app=$app --key=final_path --value=$final_path # Download, check integrity, uncompress and patch the source from app.src -ynh_setup_source --dest_dir="$final_path/build-lemmy/" --source_id="lemmy" -ynh_setup_source --dest_dir="$final_path/lemmy-ui/" --source_id="lemmy-ui" -ynh_setup_source --dest_dir="$final_path/lemmy-ui/lemmy-translations/" --source_id="lemmy-translations" -ynh_setup_source --dest_dir="$final_path/iframely/" --source_id="iframely" +ynh_setup_source --dest_dir="$final_path/build-lemmy/" --source_id="docker-image-extract" ynh_setup_source --dest_dir="$final_path/build-pict-rs/" --source_id="pict-rs" +ynh_setup_source --dest_dir="$final_path/build-lemmy-ui/" --source_id="docker-image-extract" +ynh_setup_source --dest_dir="$final_path/lemmy-ui/lemmy-translations/" --source_id="lemmy-translations" +ynh_setup_source --dest_dir="$final_path/build-iframely/" --source_id="docker-image-extract" ynh_setup_source --dest_dir="$final_path/build-imagemagick/" --source_id="imagemagick" chmod 750 "$final_path" @@ -151,56 +151,55 @@ chown -R $app:$app "$datadir" #================================================= ynh_script_progression --message="Making install..." -# Install ImageMagick -pushd "$final_path/build-imagemagick/" - ./configure - make - make install - ldconfig /usr/local/lib +# Install lemmy +pushd $final_path/build-lemmy + ./docker-image-extract dessalines/lemmy:$LEMMY_VERSION popd +mkdir -p "$final_path/lemmy/" +mv -f "$final_path/build-lemmy/output/app/lemmy" "$final_path/lemmy/lemmy" +ynh_secure_remove --file="$final_path/build-lemmy" -# Install rustup with the toolchain needed by lemmy +# Install rustup with the toolchain needed by pict-rs pushd "$final_path" sudo -u "$app" RUSTUP_HOME="$final_path"/.rustup CARGO_HOME="$final_path"/.cargo bash -c 'curl -sSf -L https://static.rust-lang.org/rustup.sh | sh -s -- -y --default-toolchain nightly' popd export PATH="$PATH:$final_path/.cargo/bin:$final_path/.local/bin:/usr/local/sbin" -# Compile lemmy -pushd $final_path/build-lemmy - ynh_exec_warn_less sudo -u "$app" env PATH="$PATH" cargo build --release -popd - -# Compile pict-rs +# Install pict-rs pushd $final_path/build-pict-rs ynh_exec_warn_less sudo -u "$app" env PATH="$PATH" cargo build --release popd - -# Install lemmy -mkdir -p "$final_path/lemmy_server/" -cp -af "$final_path/build-lemmy/target/release/lemmy_server" "$final_path/lemmy_server/lemmy_server" - -# Install pict-rs mkdir -p "$final_path/pict-rs/" -cp -af "$final_path/build-pict-rs/target/release/pict-rs" "$final_path/pict-rs/pict-rs" - -# Remove build files and rustup -ynh_secure_remove --file="$final_path/build-lemmy" +mv -f "$final_path/build-pict-rs/target/release/pict-rs" "$final_path/pict-rs/pict-rs" ynh_secure_remove --file="$final_path/build-pict-rs" -ynh_secure_remove --file="$final_path/build-imagemagick" ynh_secure_remove --file="$final_path/.cargo" ynh_secure_remove --file="$final_path/.rustup" -# Compile lemmy-ui -pushd $final_path/lemmy-ui - ynh_exec_warn_less sudo -u "$app" env PATH="$PATH" yarn install --pure-lockfile - ynh_exec_warn_less sudo -u "$app" env PATH="$PATH" yarn build:prod +# Install lemmy-ui +pushd $final_path/build-lemmy-ui + ./docker-image-extract dessalines/lemmy-ui:$LEMMYUI_VERSION popd +mkdir -p "$final_path/lemmy-ui/" +rsync -a "$final_path/build-lemmy-ui/output/app/" "$final_path/lemmy-ui/" +ynh_secure_remove --file="$final_path/build-lemmy-ui" -# Compile iframely -pushd $final_path/iframely - ynh_exec_warn_less sudo -u "$app" env PATH="$PATH" $ynh_npm install +# Install iframely +pushd $final_path/build-iframely + ./docker-image-extract dogbin/iframely:latest popd +mkdir -p "$final_path/iframely/" +rsync -a "$final_path/build-iframely/output/iframely/" "$final_path/iframely/" +ynh_secure_remove --file="$final_path/build-iframely" + +# Install ImageMagick +pushd "$final_path/build-imagemagick/" + ./configure --with-modules + make + make install + ldconfig /usr/local/lib +popd +ynh_secure_remove --file="$final_path/build-imagemagick" chmod 750 "$final_path" chmod -R o-rwx "$final_path" diff --git a/scripts/remove b/scripts/remove index 4ed0f1d..09a217d 100755 --- a/scripts/remove +++ b/scripts/remove @@ -92,6 +92,7 @@ ynh_script_progression --message="Removing dependencies..." # Remove metapackage and its dependencies ynh_remove_nodejs ynh_remove_app_dependencies +rm /lib/libc.musl-x86_64.so.1 #================================================= # REMOVE APP MAIN DIR diff --git a/scripts/restore b/scripts/restore index 587ed4d..d0ba091 100755 --- a/scripts/restore +++ b/scripts/restore @@ -97,7 +97,6 @@ ynh_script_progression --message="Reinstalling dependencies..." ynh_install_app_dependencies $pkg_dependencies ynh_install_nodejs --nodejs_version=$NODEJS_VERSION ynh_use_nodejs -ynh_install_extra_app_dependencies --repo="deb https://dl.yarnpkg.com/debian/ stable main" --package="yarn" --key="https://dl.yarnpkg.com/debian/pubkey.gpg" #================================================= # BUILDING IMAGEMAGICK @@ -108,12 +107,11 @@ ynh_setup_source --dest_dir="$final_path/build-imagemagick/" --source_id="imagem # Install ImageMagick pushd "$final_path/build-imagemagick/" - ./configure + ./configure --with-modules make make install ldconfig /usr/local/lib popd - ynh_secure_remove --file="$final_path/build-imagemagick" #================================================= diff --git a/scripts/upgrade b/scripts/upgrade index bd34178..f3a846e 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -106,12 +106,11 @@ then ynh_script_progression --message="Upgrading source files..." # Download, check integrity, uncompress the source of lemmy from app.src to his build directory - ynh_setup_source --dest_dir="$final_path/build-lemmy/" --source_id="lemmy" - ynh_setup_source --dest_dir="$final_path/lemmy-ui/" --source_id="lemmy-ui" - ynh_setup_source --dest_dir="$final_path/lemmy-ui/lemmy-translations/" --source_id="lemmy-translations" - - ynh_setup_source --dest_dir="$final_path/iframely/" --source_id="iframely" + ynh_setup_source --dest_dir="$final_path/build-lemmy/" --source_id="docker-image-extract" ynh_setup_source --dest_dir="$final_path/build-pict-rs/" --source_id="pict-rs" + ynh_setup_source --dest_dir="$final_path/build-lemmy-ui/" --source_id="docker-image-extract" + ynh_setup_source --dest_dir="$final_path/lemmy-ui/lemmy-translations/" --source_id="lemmy-translations" + ynh_setup_source --dest_dir="$final_path/build-iframely/" --source_id="docker-image-extract" ynh_setup_source --dest_dir="$final_path/build-imagemagick/" --source_id="imagemagick" fi @@ -135,7 +134,6 @@ ynh_script_progression --message="Upgrading dependencies..." ynh_install_app_dependencies $pkg_dependencies ynh_install_nodejs --nodejs_version=$NODEJS_VERSION ynh_use_nodejs -ynh_install_extra_app_dependencies --repo="deb https://dl.yarnpkg.com/debian/ stable main" --package="yarn" --key="https://dl.yarnpkg.com/debian/pubkey.gpg" #================================================= # SPECIFIC UPGRADE @@ -146,65 +144,55 @@ ynh_script_progression --message="Making upgrade..." if [ "$upgrade_type" == "UPGRADE_APP" ] then - # Install ImageMagick - pushd "$final_path/build-imagemagick/" - ./configure - make - make install - ldconfig /usr/local/lib + # Install lemmy + pushd $final_path/build-lemmy + ./docker-image-extract dessalines/lemmy:$LEMMY_VERSION popd + mkdir -p "$final_path/lemmy/" + mv -f "$final_path/build-lemmy/output/app/lemmy" "$final_path/lemmy/lemmy" + ynh_secure_remove --file="$final_path/build-lemmy" - # Install rustup with the toolchain needed by lemmy + # Install rustup with the toolchain needed by pict-rs pushd "$final_path" sudo -u "$app" RUSTUP_HOME="$final_path"/.rustup CARGO_HOME="$final_path"/.cargo bash -c 'curl -sSf -L https://static.rust-lang.org/rustup.sh | sh -s -- -y --default-toolchain nightly' popd export PATH="$PATH:$final_path/.cargo/bin:$final_path/.local/bin:/usr/local/sbin" - # Compile lemmy - pushd $final_path/build-lemmy - ynh_exec_warn_less sudo -u "$app" env PATH="$PATH" cargo build --release - popd - - # Compile pict-rs + # Install pict-rs pushd $final_path/build-pict-rs ynh_exec_warn_less sudo -u "$app" env PATH="$PATH" cargo build --release popd - - # Remove old generated files before copying the new ones - ynh_secure_remove --file="$final_path/lemmy/.fingerprint" - ynh_secure_remove --file="$final_path/lemmy/build" - ynh_secure_remove --file="$final_path/lemmy/deps" - ynh_secure_remove --file="$final_path/lemmy/examples" - ynh_secure_remove --file="$final_path/lemmy/incremental" - ynh_secure_remove --file="$final_path/lemmy/.cargo-lock" - ynh_secure_remove --file="$final_path/lemmy/lemmy.d" - - # Install lemmy - mkdir -p "$final_path/lemmy_server/" - cp -af "$final_path/build-lemmy/target/release/lemmy_server" "$final_path/lemmy_server/lemmy_server" - - # Install pict-rs mkdir -p "$final_path/pict-rs/" - cp -af "$final_path/build-pict-rs/target/release/pict-rs" "$final_path/pict-rs/pict-rs" - - # Remove build files and rustup - ynh_secure_remove --file="$final_path/build-lemmy" + mv -f "$final_path/build-pict-rs/target/release/pict-rs" "$final_path/pict-rs/pict-rs" ynh_secure_remove --file="$final_path/build-pict-rs" - ynh_secure_remove --file="$final_path/build-imagemagick" ynh_secure_remove --file="$final_path/.cargo" ynh_secure_remove --file="$final_path/.rustup" - # Compile lemmy-ui - pushd $final_path/lemmy-ui - ynh_exec_warn_less sudo -u "$app" env PATH="$PATH" yarn install --pure-lockfile - ynh_exec_warn_less sudo -u "$app" env PATH="$PATH" yarn build:prod + # Install lemmy-ui + pushd $final_path/build-lemmy-ui + ./docker-image-extract dessalines/lemmy-ui:$LEMMYUI_VERSION popd + mkdir -p "$final_path/lemmy-ui/" + rsync -a "$final_path/build-lemmy-ui/output/app/" "$final_path/lemmy-ui/" + ynh_secure_remove --file="$final_path/build-lemmy-ui" - # Compile iframely - pushd $final_path/iframely - ynh_exec_warn_less sudo -u "$app" env PATH="$PATH" $ynh_npm install + # Install iframely + pushd $final_path/build-iframely + ./docker-image-extract dogbin/iframely:latest popd + mkdir -p "$final_path/iframely/" + rsync -a "$final_path/build-iframely/output/iframely/" "$final_path/iframely/" + ynh_secure_remove --file="$final_path/build-iframely" + + # Install ImageMagick + pushd "$final_path/build-imagemagick/" + ./configure --with-modules + make + make install + ldconfig /usr/local/lib + popd + ynh_secure_remove --file="$final_path/build-imagemagick" fi chmod 750 "$final_path" @@ -221,7 +209,7 @@ chown -R $app:$app "/var/log/$app" #================================================= ynh_script_progression --message="Updating a configuration file..." -mkdir -p "$final_path/lemmy_server/" +mkdir -p "$final_path/lemmy/" ynh_add_config --template="../conf/lemmy.hjson" --destination="$final_path/config/config.hjson" chmod 400 "$final_path/config/config.hjson" From 6e7e9e3a20a15ba4018a745574e124309b3d8fa0 Mon Sep 17 00:00:00 2001 From: yalh76 Date: Fri, 30 Jul 2021 16:26:53 +0200 Subject: [PATCH 05/15] Upgrade to 0.11.2~ynh1 --- README.md | 2 +- README_fr.md | 2 +- conf/iframely.config.local.js | 72 +++++++++++------------------------ manifest.json | 2 +- scripts/_common.sh | 18 +++++---- scripts/install | 47 ++++++++++++----------- scripts/upgrade | 44 ++++++++++----------- 7 files changed, 83 insertions(+), 104 deletions(-) diff --git a/README.md b/README.md index 8dfea55..a9303cb 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ If you don't have YunoHost, please consult [the guide](https://yunohost.org/#/in A link aggregator / Reddit clone for the fediverse. -**Shipped version:** 0.9.9~ynh1 +**Shipped version:** 0.11.2~ynh1 **Demo:** https://join.lemmy.ml/join/ diff --git a/README_fr.md b/README_fr.md index e3ab297..e960d11 100644 --- a/README_fr.md +++ b/README_fr.md @@ -13,7 +13,7 @@ Si vous n'avez pas YunoHost, regardez [ici](https://yunohost.org/#/install) pour -**Version incluse :** 0.9.9~ynh1 +**Version incluse :** 0.11.2~ynh1 **Démo :** https://join.lemmy.ml/join/ diff --git a/conf/iframely.config.local.js b/conf/iframely.config.local.js index 668043f..1381fb0 100644 --- a/conf/iframely.config.local.js +++ b/conf/iframely.config.local.js @@ -58,7 +58,7 @@ - memcached - https://github.com/3rd-Eden/node-memcached */ CACHE_ENGINE: 'node-cache', - CACHE_TTL: 0, // In seconds. + CACHE_TTL: 0, // In seconds. // 0 = 'never expire' for memcached & node-cache to let cache engine decide itself when to evict the record // 0 = 'no cache' for redis. Use high enough (e.g. 365*24*60*60*1000) ttl for similar 'never expire' approach instead @@ -104,31 +104,16 @@ // DISABLE_HTTP2: true, // Customize API calls to oembed endpoints. - // Must have: please add your `access_token` for Facebook and Instagram API calls ADD_OEMBED_PARAMS: [{ - - re: [ // Endpoint's URL regexp array. - /^https:\/\/graph\.facebook\.com\/v\d+\.\d+\/instagram_oembed/i - ], - params: { // Custom query-string params object. - - // TODO: get your access Insagtam token as described - // on https://developers.facebook.com/docs/instagram/oembed/ - access_token: '', // The simplest way is - // to use `{app-id}|{app secret}` as access token - - // Add any other optional params + // Endpoint url regexp array. + re: [/^http:\/\/api\.instagram\.com\/oembed/], + // Custom get params object. + params: { hidecaption: true } }, { - re: [/^https:\/\/graph\.facebook\.com\/v\d+\.\d+\/oembed_page/i], + re: [/^https:\/\/www\.facebook\.com\/plugins\/page\/oembed\.json/i], params: { - // TODO: get your access token as described - // on https://developers.facebook.com/docs/plugins/oembed - access_token: '', // The simplest way is - // to use `{app-id}|{app secret}` as access token - - // Add any other optional params show_posts: 0, show_facepile: 0, maxwidth: 600 @@ -141,24 +126,20 @@ limit: 1, maxwidth: 600 } + /* }, { - // Facebook https://developers.facebook.com/docs/plugins/oembed/ - re: [/^https:\/\/graph\.facebook\.com\/v\d+\.\d+\/oembed_/i], + // Facebook https://developers.facebook.com/docs/plugins/oembed-endpoints + re: [/^https:\/\/www\.facebook\.com\/plugins\/\w+\/oembed\.json/i], params: { - // TODO: get your access token as described - // on https://developers.facebook.com/docs/plugins/oembed - access_token: '', // The simplest way is - // to use `{app-id}|{app secret}` as access token - - // Add any other optional params, like skip script tag and fb-root div - // omitscript: true + // Skip script tag and fb-root div. + omitscript: true } + */ }], - /* Configure use of HTTP proxies as needed. - You don't have to specify all options per regex - just what you need to override - */ /* + // Configure use of HTTP proxies as needed. + // You don't have to specify all options per regex - just what you need to override PROXY: [{ re: [/^https?:\/\/www\.domain\.com/], proxy_server: 'http://1.2.3.4:8080', @@ -171,16 +152,14 @@ // Refer to: https://github.com/request/request // Overrides previous params if overlapped. }, - cache_ttl: 3600, // in seconds, cache response for 1 hour. disable_http2: true }], */ // Customize API calls to 3rd parties. At the very least - configure required keys. - // For available provider options - please see the code of its domain plugin. providerOptions: { - locale: "en_US", // ISO 639-1 two-letter language code, e.g. en_CA or fr_CH. - // Will be added as highest priotity in accept-language header with each request. + locale: "en_US", // ISO 639-1 two-letter language code, e.g. en_CA or fr_CH. + // Will be added as highest priotity in accept-language header with each request. // Plus is used in FB, YouTube and perhaps other plugins "twitter": { "max-width": 550, @@ -224,18 +203,21 @@ // It is probably the same API key you use for Google Maps. youtube: { // api_key: "INSERT YOUR VALUE", - // parts: [ "snippet", "player" ], // list of fields you want to use in the request, in most cases you only need those two get_params: "?rel=0&showinfo=1" // https://developers.google.com/youtube/player_parameters }, vimeo: { get_params: "?byline=0&badge=0" // https://developer.vimeo.com/player/embedding }, + + /* soundcloud: { old_player: true // enables classic player }, giphy: { media_only: true // disables branded player for gifs and returns just the image - }, + } + */ + /* bandcamp: { get_params: '/size=large/bgcol=333333/linkcol=ffffff/artwork=small/transparent=true/', media: { @@ -248,11 +230,7 @@ 'max-width': 700 } } - }, - // Docs: https://dev.twitch.tv/docs/embed/video-and-clips - twitch: { - parent: 'jsbin.com, null.jsbin.com, localhost' - }, + } */ }, @@ -298,11 +276,7 @@ // And this is AWS metadata service // https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html /^https?:\/\/169\.254\.169\.254/ - ], - - // Endpoint for prerender service, if you need it. Used to parse React apps. Very slow. - // Tested with https://github.com/prerender/prerender - // PRERENDER_URL: "https://domain/render?url=" + ] }; module.exports = config; diff --git a/manifest.json b/manifest.json index cb58aa6..4a3cfbc 100644 --- a/manifest.json +++ b/manifest.json @@ -5,7 +5,7 @@ "description": { "en": "A link aggregator / Reddit clone for the fediverse." }, - "version": "0.9.9~ynh1", + "version": "0.11.2~ynh1", "url": "https://join.lemmy.ml/", "upstream": { "license": "GPL-3.0", diff --git a/scripts/_common.sh b/scripts/_common.sh index b1b4f5b..35c4880 100644 --- a/scripts/_common.sh +++ b/scripts/_common.sh @@ -7,20 +7,24 @@ # dependencies used by the app lemmy_dependencies="postgresql postgresql-contrib" lemmyui_dependencies="espeak" -iframely_dependencies="musl-dev" -pictrs_dependencies="bash-completion ffmpeg clang pkg-config libgexiv2-dev libgexiv2-2" +imagemagick_build_dependencies="libltdl-dev libjpeg-dev libpng-dev libwebp-dev liblzma-dev libxml2-dev" imagemagick_dependencies="" +pictrs_build_dependencies="pkg-config build-essential libgexiv2-dev libxml2 libltdl7 libavcodec-dev libavfilter-dev libavdevice-dev libavformat-dev libavresample-dev libavutil-dev libswscale-dev libswresample-dev llvm-dev libclang-dev clang" +pictrs_dependencies="libgexiv2-2 libpng16-16 libjpeg62-turbo libwebp6 libwebpdemux2 libwebpmux3 libltdl7 libgomp1 libxml2 libavcodec58 libavfilter7 libavdevice58 libavformat58 libavresample4 libavutil56 libswscale5 libswresample3 tini" +iframely_dependencies="musl-dev" -pkg_dependencies="$lemmy_dependencies $lemmyui_dependencies $iframely_dependencies $pictrs_dependencies $imagemagick_dependencies" -# libssl-dev libc6-dev libpq-dev libavutil-dev libavformat-dev libavfilter-dev libavdevice-dev libavresample-dev libjpeg-turbo-progs libpng-dev +pkg_dependencies="$lemmy_dependencies $lemmyui_dependencies $imagemagick_dependencies $pictrs_dependencies $iframely_dependencies" +pkg_build_dependencies="$imagemagick_build_dependencies $pictrs_build_dependencies" NODEJS_VERSION=12 -LEMMY_VERSION=0.11.0 +LEMMY_VERSION=0.11.2 -LEMMYUI_VERSION=0.11.0 +LEMMYUI_VERSION=0.11.2 -PICTRS_VERSION=v0.2.6-r1 +PICTRS_VERSION=v0.2.6-r2 + +IFRAMELY_VERSION=latest #================================================= # PERSONAL HELPERS diff --git a/scripts/install b/scripts/install index 950ed35..2a3eb6a 100755 --- a/scripts/install +++ b/scripts/install @@ -78,7 +78,7 @@ ynh_app_setting_set --app=$app --key=port_iframely --value=$port_iframely #================================================= ynh_script_progression --message="Installing dependencies..." -ynh_install_app_dependencies $pkg_dependencies +ynh_install_app_dependencies $pkg_dependencies $pkg_build_dependencies ynh_install_nodejs --nodejs_version=$NODEJS_VERSION ynh_use_nodejs ln -s /usr/lib/x86_64-linux-musl/libc.so /lib/libc.musl-x86_64.so.1 @@ -112,11 +112,11 @@ ynh_script_progression --message="Setting up source files..." ynh_app_setting_set --app=$app --key=final_path --value=$final_path # Download, check integrity, uncompress and patch the source from app.src ynh_setup_source --dest_dir="$final_path/build-lemmy/" --source_id="docker-image-extract" -ynh_setup_source --dest_dir="$final_path/build-pict-rs/" --source_id="pict-rs" ynh_setup_source --dest_dir="$final_path/build-lemmy-ui/" --source_id="docker-image-extract" ynh_setup_source --dest_dir="$final_path/lemmy-ui/lemmy-translations/" --source_id="lemmy-translations" -ynh_setup_source --dest_dir="$final_path/build-iframely/" --source_id="docker-image-extract" ynh_setup_source --dest_dir="$final_path/build-imagemagick/" --source_id="imagemagick" +ynh_setup_source --dest_dir="$final_path/build-pict-rs/" --source_id="pict-rs" +ynh_setup_source --dest_dir="$final_path/build-iframely/" --source_id="docker-image-extract" chmod 750 "$final_path" chmod -R o-rwx "$final_path" @@ -159,6 +159,23 @@ mkdir -p "$final_path/lemmy/" mv -f "$final_path/build-lemmy/output/app/lemmy" "$final_path/lemmy/lemmy" ynh_secure_remove --file="$final_path/build-lemmy" +# Install lemmy-ui +pushd $final_path/build-lemmy-ui + ./docker-image-extract dessalines/lemmy-ui:$LEMMYUI_VERSION +popd +mkdir -p "$final_path/lemmy-ui/" +rsync -a "$final_path/build-lemmy-ui/output/app/" "$final_path/lemmy-ui/" +ynh_secure_remove --file="$final_path/build-lemmy-ui" + +# Install ImageMagick +pushd "$final_path/build-imagemagick/" + ./configure --with-modules + make + make install + ldconfig /usr/local/lib +popd +ynh_secure_remove --file="$final_path/build-imagemagick" + # Install rustup with the toolchain needed by pict-rs pushd "$final_path" sudo -u "$app" RUSTUP_HOME="$final_path"/.rustup CARGO_HOME="$final_path"/.cargo bash -c 'curl -sSf -L https://static.rust-lang.org/rustup.sh | sh -s -- -y --default-toolchain nightly' @@ -176,31 +193,15 @@ ynh_secure_remove --file="$final_path/build-pict-rs" ynh_secure_remove --file="$final_path/.cargo" ynh_secure_remove --file="$final_path/.rustup" -# Install lemmy-ui -pushd $final_path/build-lemmy-ui - ./docker-image-extract dessalines/lemmy-ui:$LEMMYUI_VERSION -popd -mkdir -p "$final_path/lemmy-ui/" -rsync -a "$final_path/build-lemmy-ui/output/app/" "$final_path/lemmy-ui/" -ynh_secure_remove --file="$final_path/build-lemmy-ui" - # Install iframely pushd $final_path/build-iframely - ./docker-image-extract dogbin/iframely:latest + ./docker-image-extract dogbin/iframely:$IFRAMELY_VERSION popd mkdir -p "$final_path/iframely/" rsync -a "$final_path/build-iframely/output/iframely/" "$final_path/iframely/" ynh_secure_remove --file="$final_path/build-iframely" -# Install ImageMagick -pushd "$final_path/build-imagemagick/" - ./configure --with-modules - make - make install - ldconfig /usr/local/lib -popd -ynh_secure_remove --file="$final_path/build-imagemagick" - +ynh_install_app_dependencies $pkg_dependencies chmod 750 "$final_path" chmod -R o-rwx "$final_path" chown -R $app:$app "$final_path" @@ -254,8 +255,8 @@ ynh_script_progression --message="Integrating service in YunoHost..." yunohost service add $app --description="A short description of the app" --log="/var/log/$app/$app.log" yunohost service add $app-ui --description="A short description of the app" --log="/var/log/$app/$app-ui.log" -yunohost service add $app-iframely --description="A short description of the app" --log="/var/log/$app/$app-iframely.log" yunohost service add $app-pict-rs --description="A short description of the app" --log="/var/log/$app/$app-pict-rs.log" +yunohost service add $app-iframely --description="A short description of the app" --log="/var/log/$app/$app-iframely.log" #================================================= # START SYSTEMD SERVICE @@ -265,8 +266,8 @@ ynh_script_progression --message="Starting a systemd service..." # Start a systemd service ynh_systemd_action --service_name=$app --action="start" --line_match="Started" --log_path=systemd ynh_systemd_action --service_name=$app-ui --action="start" --line_match="Started" --log_path=systemd -ynh_systemd_action --service_name=$app-iframely --action="start" --line_match="Started" --log_path=systemd ynh_systemd_action --service_name=$app-pict-rs --action="start" --line_match="Started" --log_path=systemd +ynh_systemd_action --service_name=$app-iframely --action="start" --line_match="Started" --log_path=systemd #================================================= # SETUP SSOWAT diff --git a/scripts/upgrade b/scripts/upgrade index f3a846e..3017d1e 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -64,8 +64,8 @@ ynh_script_progression --message="Stopping a systemd service..." ynh_systemd_action --service_name=$app --action="stop" --line_match="Stopped" --log_path=systemd ynh_systemd_action --service_name=$app-ui --action="stop" --line_match="Stopped" --log_path=systemd -ynh_systemd_action --service_name=$app-iframely --action="stop" --line_match="Stopped" --log_path=systemd ynh_systemd_action --service_name=$app-pict-rs --action="stop" --line_match="Stopped" --log_path=systemd +ynh_systemd_action --service_name=$app-iframely --action="stop" --line_match="Stopped" --log_path=systemd #================================================= # ENSURE DOWNWARD COMPATIBILITY @@ -107,11 +107,11 @@ then # Download, check integrity, uncompress the source of lemmy from app.src to his build directory ynh_setup_source --dest_dir="$final_path/build-lemmy/" --source_id="docker-image-extract" - ynh_setup_source --dest_dir="$final_path/build-pict-rs/" --source_id="pict-rs" ynh_setup_source --dest_dir="$final_path/build-lemmy-ui/" --source_id="docker-image-extract" ynh_setup_source --dest_dir="$final_path/lemmy-ui/lemmy-translations/" --source_id="lemmy-translations" - ynh_setup_source --dest_dir="$final_path/build-iframely/" --source_id="docker-image-extract" ynh_setup_source --dest_dir="$final_path/build-imagemagick/" --source_id="imagemagick" + ynh_setup_source --dest_dir="$final_path/build-pict-rs/" --source_id="pict-rs" + ynh_setup_source --dest_dir="$final_path/build-iframely/" --source_id="docker-image-extract" fi chmod 750 "$final_path" @@ -152,6 +152,23 @@ then mv -f "$final_path/build-lemmy/output/app/lemmy" "$final_path/lemmy/lemmy" ynh_secure_remove --file="$final_path/build-lemmy" + # Install lemmy-ui + pushd $final_path/build-lemmy-ui + ./docker-image-extract dessalines/lemmy-ui:$LEMMYUI_VERSION + popd + mkdir -p "$final_path/lemmy-ui/" + rsync -a "$final_path/build-lemmy-ui/output/app/" "$final_path/lemmy-ui/" + ynh_secure_remove --file="$final_path/build-lemmy-ui" + + # Install ImageMagick + pushd "$final_path/build-imagemagick/" + ./configure --with-modules + make + make install + ldconfig /usr/local/lib + popd + ynh_secure_remove --file="$final_path/build-imagemagick" + # Install rustup with the toolchain needed by pict-rs pushd "$final_path" sudo -u "$app" RUSTUP_HOME="$final_path"/.rustup CARGO_HOME="$final_path"/.cargo bash -c 'curl -sSf -L https://static.rust-lang.org/rustup.sh | sh -s -- -y --default-toolchain nightly' @@ -169,14 +186,6 @@ then ynh_secure_remove --file="$final_path/.cargo" ynh_secure_remove --file="$final_path/.rustup" - # Install lemmy-ui - pushd $final_path/build-lemmy-ui - ./docker-image-extract dessalines/lemmy-ui:$LEMMYUI_VERSION - popd - mkdir -p "$final_path/lemmy-ui/" - rsync -a "$final_path/build-lemmy-ui/output/app/" "$final_path/lemmy-ui/" - ynh_secure_remove --file="$final_path/build-lemmy-ui" - # Install iframely pushd $final_path/build-iframely ./docker-image-extract dogbin/iframely:latest @@ -184,15 +193,6 @@ then mkdir -p "$final_path/iframely/" rsync -a "$final_path/build-iframely/output/iframely/" "$final_path/iframely/" ynh_secure_remove --file="$final_path/build-iframely" - - # Install ImageMagick - pushd "$final_path/build-imagemagick/" - ./configure --with-modules - make - make install - ldconfig /usr/local/lib - popd - ynh_secure_remove --file="$final_path/build-imagemagick" fi chmod 750 "$final_path" @@ -253,8 +253,8 @@ ynh_script_progression --message="Integrating service in YunoHost..." yunohost service add $app --description="A short description of the app" --log="/var/log/$app/$app.log" yunohost service add $app-ui --description="A short description of the app" --log="/var/log/$app/$app-ui.log" -yunohost service add $app-iframely --description="A short description of the app" --log="/var/log/$app/$app-iframely.log" yunohost service add $app-pict-rs --description="A short description of the app" --log="/var/log/$app/$app-pict-rs.log" +yunohost service add $app-iframely --description="A short description of the app" --log="/var/log/$app/$app-iframely.log" #================================================= # START SYSTEMD SERVICE @@ -263,8 +263,8 @@ ynh_script_progression --message="Starting a systemd service..." ynh_systemd_action --service_name=$app --action="start" --line_match="Started" --log_path=systemd ynh_systemd_action --service_name=$app-ui --action="start" --line_match="Started" --log_path=systemd -ynh_systemd_action --service_name=$app-iframely --action="start" --line_match="Started" --log_path=systemd ynh_systemd_action --service_name=$app-pict-rs --action="start" --line_match="Started" --log_path=systemd +ynh_systemd_action --service_name=$app-iframely --action="start" --line_match="Started" --log_path=systemd #================================================= # RELOAD NGINX From e50af4b463a3abc2a1b3f2c061c705911e2311e6 Mon Sep 17 00:00:00 2001 From: yalh76 Date: Fri, 30 Jul 2021 20:15:04 +0200 Subject: [PATCH 06/15] less warnings --- scripts/install | 14 +++++++------- scripts/restore | 7 ++++++- scripts/upgrade | 15 ++++++++++----- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/scripts/install b/scripts/install index 2a3eb6a..71b5141 100755 --- a/scripts/install +++ b/scripts/install @@ -78,10 +78,10 @@ ynh_app_setting_set --app=$app --key=port_iframely --value=$port_iframely #================================================= ynh_script_progression --message="Installing dependencies..." -ynh_install_app_dependencies $pkg_dependencies $pkg_build_dependencies +ynh_exec_warn_less ynh_install_app_dependencies $pkg_dependencies $pkg_build_dependencies ynh_install_nodejs --nodejs_version=$NODEJS_VERSION ynh_use_nodejs -ln -s /usr/lib/x86_64-linux-musl/libc.so /lib/libc.musl-x86_64.so.1 +ln -fs /usr/lib/x86_64-linux-musl/libc.so /lib/libc.musl-x86_64.so.1 #================================================= # CREATE DEDICATED USER @@ -169,10 +169,10 @@ ynh_secure_remove --file="$final_path/build-lemmy-ui" # Install ImageMagick pushd "$final_path/build-imagemagick/" - ./configure --with-modules - make - make install - ldconfig /usr/local/lib + ynh_exec_warn_less ./configure --with-modules + ynh_exec_warn_less make + ynh_exec_warn_less make install + ynh_exec_warn_less ldconfig /usr/local/lib popd ynh_secure_remove --file="$final_path/build-imagemagick" @@ -201,7 +201,7 @@ mkdir -p "$final_path/iframely/" rsync -a "$final_path/build-iframely/output/iframely/" "$final_path/iframely/" ynh_secure_remove --file="$final_path/build-iframely" -ynh_install_app_dependencies $pkg_dependencies +ynh_exec_warn_less ynh_install_app_dependencies $pkg_dependencies chmod 750 "$final_path" chmod -R o-rwx "$final_path" chown -R $app:$app "$final_path" diff --git a/scripts/restore b/scripts/restore index d0ba091..9bb22a0 100755 --- a/scripts/restore +++ b/scripts/restore @@ -94,9 +94,10 @@ chown -R $app:$app "$datadir" ynh_script_progression --message="Reinstalling dependencies..." # Define and install dependencies -ynh_install_app_dependencies $pkg_dependencies +ynh_exec_warn_less ynh_install_app_dependencies $pkg_dependencies ynh_install_nodejs --nodejs_version=$NODEJS_VERSION ynh_use_nodejs +ln -fs /usr/lib/x86_64-linux-musl/libc.so /lib/libc.musl-x86_64.so.1 #================================================= # BUILDING IMAGEMAGICK @@ -105,6 +106,8 @@ ynh_script_progression --message="Building ImageMagick..." ynh_setup_source --dest_dir="$final_path/build-imagemagick/" --source_id="imagemagick" +ynh_exec_warn_less ynh_install_app_dependencies $pkg_dependencies $imagemagick_build_dependencies + # Install ImageMagick pushd "$final_path/build-imagemagick/" ./configure --with-modules @@ -114,6 +117,8 @@ pushd "$final_path/build-imagemagick/" popd ynh_secure_remove --file="$final_path/build-imagemagick" +ynh_exec_warn_less ynh_install_app_dependencies $pkg_dependencies + #================================================= # RESTORE THE POSTGRESQL DATABASE #================================================= diff --git a/scripts/upgrade b/scripts/upgrade index 3017d1e..a6aae14 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -131,9 +131,10 @@ ynh_add_nginx_config #================================================= ynh_script_progression --message="Upgrading dependencies..." -ynh_install_app_dependencies $pkg_dependencies +ynh_exec_warn_less ynh_install_app_dependencies $pkg_dependencies ynh_install_nodejs --nodejs_version=$NODEJS_VERSION ynh_use_nodejs +ln -fs /usr/lib/x86_64-linux-musl/libc.so /lib/libc.musl-x86_64.so.1 #================================================= # SPECIFIC UPGRADE @@ -144,6 +145,8 @@ ynh_script_progression --message="Making upgrade..." if [ "$upgrade_type" == "UPGRADE_APP" ] then + ynh_exec_warn_less ynh_install_app_dependencies $pkg_dependencies $pkg_build_dependencies + # Install lemmy pushd $final_path/build-lemmy ./docker-image-extract dessalines/lemmy:$LEMMY_VERSION @@ -162,10 +165,10 @@ then # Install ImageMagick pushd "$final_path/build-imagemagick/" - ./configure --with-modules - make - make install - ldconfig /usr/local/lib + ynh_exec_warn_less ./configure --with-modules + ynh_exec_warn_less make + ynh_exec_warn_less make install + ynh_exec_warn_less ldconfig /usr/local/lib popd ynh_secure_remove --file="$final_path/build-imagemagick" @@ -193,6 +196,8 @@ then mkdir -p "$final_path/iframely/" rsync -a "$final_path/build-iframely/output/iframely/" "$final_path/iframely/" ynh_secure_remove --file="$final_path/build-iframely" + + ynh_exec_warn_less ynh_install_app_dependencies $pkg_dependencies fi chmod 750 "$final_path" From 5263ae0848a7e7f3540543e300dbf8320a47ce6e Mon Sep 17 00:00:00 2001 From: yalh76 Date: Sun, 22 Aug 2021 14:13:53 +0200 Subject: [PATCH 07/15] Less warnings --- scripts/install | 4 +++- scripts/remove | 2 +- scripts/restore | 8 ++++---- scripts/upgrade | 14 +++----------- 4 files changed, 11 insertions(+), 17 deletions(-) diff --git a/scripts/install b/scripts/install index 71b5141..656da08 100755 --- a/scripts/install +++ b/scripts/install @@ -178,7 +178,7 @@ ynh_secure_remove --file="$final_path/build-imagemagick" # Install rustup with the toolchain needed by pict-rs pushd "$final_path" - sudo -u "$app" RUSTUP_HOME="$final_path"/.rustup CARGO_HOME="$final_path"/.cargo bash -c 'curl -sSf -L https://static.rust-lang.org/rustup.sh | sh -s -- -y --default-toolchain nightly' + ynh_exec_warn_less sudo -u "$app" RUSTUP_HOME="$final_path/.rustup" CARGO_HOME="$final_path/.cargo" bash -c 'curl -sSf -L https://static.rust-lang.org/rustup.sh | sh -s -- -y --default-toolchain nightly' popd export PATH="$PATH:$final_path/.cargo/bin:$final_path/.local/bin:/usr/local/sbin" @@ -190,6 +190,8 @@ popd mkdir -p "$final_path/pict-rs/" mv -f "$final_path/build-pict-rs/target/release/pict-rs" "$final_path/pict-rs/pict-rs" ynh_secure_remove --file="$final_path/build-pict-rs" + +# Remove rustup ynh_secure_remove --file="$final_path/.cargo" ynh_secure_remove --file="$final_path/.rustup" diff --git a/scripts/remove b/scripts/remove index 09a217d..4086ae0 100755 --- a/scripts/remove +++ b/scripts/remove @@ -92,7 +92,7 @@ ynh_script_progression --message="Removing dependencies..." # Remove metapackage and its dependencies ynh_remove_nodejs ynh_remove_app_dependencies -rm /lib/libc.musl-x86_64.so.1 +ynh_secure_remove --file="/lib/libc.musl-x86_64.so.1" #================================================= # REMOVE APP MAIN DIR diff --git a/scripts/restore b/scripts/restore index 9bb22a0..f86cd16 100755 --- a/scripts/restore +++ b/scripts/restore @@ -110,10 +110,10 @@ ynh_exec_warn_less ynh_install_app_dependencies $pkg_dependencies $imagemagick_b # Install ImageMagick pushd "$final_path/build-imagemagick/" - ./configure --with-modules - make - make install - ldconfig /usr/local/lib + ynh_exec_warn_less ./configure --with-modules + ynh_exec_warn_less make + ynh_exec_warn_less make install + ynh_exec_warn_less ldconfig /usr/local/lib popd ynh_secure_remove --file="$final_path/build-imagemagick" diff --git a/scripts/upgrade b/scripts/upgrade index a6aae14..fad10dd 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -79,16 +79,6 @@ if ynh_legacy_permissions_exists; then ynh_app_setting_delete --app=$app --key=is_public fi -if ! ynh_permission_exists --permission="admin"; then - # Create the required permissions - ynh_permission_create --permission="admin" --url="/admin" --allowed=$admin -fi - -# Create a permission if needed -if ! ynh_permission_exists --permission="api"; then - ynh_permission_create --permission="api" --url="/api" --allowed="visitors" --show_tile="false" --protected="true" -fi - #================================================= # CREATE DEDICATED USER #================================================= @@ -174,7 +164,7 @@ then # Install rustup with the toolchain needed by pict-rs pushd "$final_path" - sudo -u "$app" RUSTUP_HOME="$final_path"/.rustup CARGO_HOME="$final_path"/.cargo bash -c 'curl -sSf -L https://static.rust-lang.org/rustup.sh | sh -s -- -y --default-toolchain nightly' + ynh_exec_warn_less sudo -u "$app" RUSTUP_HOME="$final_path/.rustup" CARGO_HOME="$final_path/.cargo" bash -c 'curl -sSf -L https://static.rust-lang.org/rustup.sh | sh -s -- -y --default-toolchain nightly' popd export PATH="$PATH:$final_path/.cargo/bin:$final_path/.local/bin:/usr/local/sbin" @@ -186,6 +176,8 @@ then mkdir -p "$final_path/pict-rs/" mv -f "$final_path/build-pict-rs/target/release/pict-rs" "$final_path/pict-rs/pict-rs" ynh_secure_remove --file="$final_path/build-pict-rs" + + # Remove rustup ynh_secure_remove --file="$final_path/.cargo" ynh_secure_remove --file="$final_path/.rustup" From a5afb198257e5ea90cb7deadc23ee1dfb3d95c13 Mon Sep 17 00:00:00 2001 From: yalh76 Date: Sun, 22 Aug 2021 14:24:34 +0200 Subject: [PATCH 08/15] Upgrade to 0.11.3 --- README.md | 2 +- README_fr.md | 2 +- manifest.json | 2 +- scripts/_common.sh | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a9303cb..2c89e71 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ If you don't have YunoHost, please consult [the guide](https://yunohost.org/#/in A link aggregator / Reddit clone for the fediverse. -**Shipped version:** 0.11.2~ynh1 +**Shipped version:** 0.11.3~ynh1 **Demo:** https://join.lemmy.ml/join/ diff --git a/README_fr.md b/README_fr.md index e960d11..ed1bb49 100644 --- a/README_fr.md +++ b/README_fr.md @@ -13,7 +13,7 @@ Si vous n'avez pas YunoHost, regardez [ici](https://yunohost.org/#/install) pour -**Version incluse :** 0.11.2~ynh1 +**Version incluse :** 0.11.3~ynh1 **Démo :** https://join.lemmy.ml/join/ diff --git a/manifest.json b/manifest.json index 4a3cfbc..aba28ed 100644 --- a/manifest.json +++ b/manifest.json @@ -5,7 +5,7 @@ "description": { "en": "A link aggregator / Reddit clone for the fediverse." }, - "version": "0.11.2~ynh1", + "version": "0.11.3~ynh1", "url": "https://join.lemmy.ml/", "upstream": { "license": "GPL-3.0", diff --git a/scripts/_common.sh b/scripts/_common.sh index 35c4880..7d9e542 100644 --- a/scripts/_common.sh +++ b/scripts/_common.sh @@ -18,9 +18,9 @@ pkg_build_dependencies="$imagemagick_build_dependencies $pictrs_build_dependenci NODEJS_VERSION=12 -LEMMY_VERSION=0.11.2 +LEMMY_VERSION=0.11.3 -LEMMYUI_VERSION=0.11.2 +LEMMYUI_VERSION=0.11.3 PICTRS_VERSION=v0.2.6-r2 From 638bdd19ba468f3d58f08b8047bceb7b94c72aef Mon Sep 17 00:00:00 2001 From: yalh76 Date: Sun, 22 Aug 2021 14:26:49 +0200 Subject: [PATCH 09/15] Fix imagemagick removal --- scripts/remove | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/scripts/remove b/scripts/remove index 4086ae0..ab3109a 100755 --- a/scripts/remove +++ b/scripts/remove @@ -77,9 +77,8 @@ ynh_setup_source --dest_dir="$final_path/build-imagemagick/" --source_id="imagem # Install ImageMagick pushd "$final_path/build-imagemagick/" - ./configure - make uninstall - ldconfig /usr/local/lib + ynh_exec_warn_less ./configure + ynh_exec_warn_less make uninstall popd ynh_secure_remove --file="$final_path/build-imagemagick" From fff2fd41b7850a80bcfa549375e4fe9f15d66cd1 Mon Sep 17 00:00:00 2001 From: yalh76 Date: Sun, 22 Aug 2021 14:29:38 +0200 Subject: [PATCH 10/15] Update remove --- scripts/remove | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts/remove b/scripts/remove index ab3109a..fb4a03e 100755 --- a/scripts/remove +++ b/scripts/remove @@ -60,6 +60,14 @@ ynh_remove_systemd_config --service=$app-ui ynh_remove_systemd_config --service=$app-iframely ynh_remove_systemd_config --service=$app-pict-rs +#================================================= +# REMOVE LOGROTATE CONFIGURATION +#================================================= +ynh_script_progression --message="Removing logrotate configuration..." + +# Remove the app-specific logrotate config +ynh_remove_logrotate + #================================================= # REMOVE THE POSTGRESQL DATABASE #================================================= @@ -109,14 +117,6 @@ ynh_script_progression --message="Removing NGINX web server configuration..." # Remove the dedicated NGINX config ynh_remove_nginx_config -#================================================= -# REMOVE LOGROTATE CONFIGURATION -#================================================= -ynh_script_progression --message="Removing logrotate configuration..." - -# Remove the app-specific logrotate config -ynh_remove_logrotate - #================================================= # SPECIFIC REMOVE #================================================= From f7691177ed4debc45866651ab37cd78bd30ac7fd Mon Sep 17 00:00:00 2001 From: yalh76 Date: Sun, 22 Aug 2021 14:30:50 +0200 Subject: [PATCH 11/15] Update restore --- scripts/restore | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/scripts/restore b/scripts/restore index f86cd16..ea42491 100755 --- a/scripts/restore +++ b/scripts/restore @@ -142,6 +142,18 @@ systemctl enable $app-iframely.service --quiet ynh_restore_file --origin_path="/etc/systemd/system/$app-pict-rs.service" systemctl enable $app-pict-rs.service --quiet +#================================================= +# RESTORE THE LOGROTATE CONFIGURATION +#================================================= +ynh_script_progression --message="Restoring the logrotate configuration..." + +mkdir -p "/var/log/$app" +chmod 750 "/var/log/$app" +chmod -R o-rwx "/var/log/$app" +chown -R $app:$app "/var/log/$app" + +ynh_restore_file --origin_path="/etc/logrotate.d/$app" + #================================================= # INTEGRATE SERVICE IN YUNOHOST #================================================= @@ -162,18 +174,6 @@ ynh_systemd_action --service_name=$app-ui --action="start" --line_match="Started ynh_systemd_action --service_name=$app-iframely --action="start" --line_match="Started" --log_path=systemd ynh_systemd_action --service_name=$app-pict-rs --action="start" --line_match="Started" --log_path=systemd -#================================================= -# RESTORE THE LOGROTATE CONFIGURATION -#================================================= -ynh_script_progression --message="Restoring the logrotate configuration..." - -mkdir -p "/var/log/$app" -chmod 750 "/var/log/$app" -chmod -R o-rwx "/var/log/$app" -chown -R $app:$app "/var/log/$app" - -ynh_restore_file --origin_path="/etc/logrotate.d/$app" - #================================================= # GENERIC FINALIZATION #================================================= From 158b82234366bf65a8f3425f58d6e21e407d78eb Mon Sep 17 00:00:00 2001 From: yalh76 Date: Sun, 22 Aug 2021 14:33:50 +0200 Subject: [PATCH 12/15] Update upgrade --- scripts/upgrade | 5 ----- 1 file changed, 5 deletions(-) diff --git a/scripts/upgrade b/scripts/upgrade index fad10dd..c768394 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -196,11 +196,6 @@ chmod 750 "$final_path" chmod -R o-rwx "$final_path" chown -R $app:$app "$final_path" -mkdir -p "/var/log/$app" -chmod 750 "/var/log/$app" -chmod -R o-rwx "/var/log/$app" -chown -R $app:$app "/var/log/$app" - #================================================= # UPDATE A CONFIG FILE #================================================= From 5ef8fef39b1af81be744bf4bf94644c30cbfd9f1 Mon Sep 17 00:00:00 2001 From: yalh76 Date: Sun, 22 Aug 2021 15:44:08 +0200 Subject: [PATCH 13/15] Update lemmy-ui.service --- conf/lemmy-ui.service | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/lemmy-ui.service b/conf/lemmy-ui.service index bce73bd..5e97a05 100644 --- a/conf/lemmy-ui.service +++ b/conf/lemmy-ui.service @@ -7,7 +7,7 @@ Type=simple User=__APP__ Group=__APP__ Environment="LEMMY_INTERNAL_HOST=127.0.0.1:__PORT_LEMMY__" -Environment=" LEMMY_EXTERNAL_HOST=__DOMAIN__" +Environment="LEMMY_EXTERNAL_HOST=__DOMAIN__" Environment="LEMMY_HTTPS=true" Environment="LEMMY_UI_HOST=0.0.0.0:__PORT_UI__" WorkingDirectory=__FINALPATH__/lemmy-ui/ From c864d496ca4a6e0c62adeb4b436e8ad755b6e007 Mon Sep 17 00:00:00 2001 From: yalh76 Date: Fri, 27 Aug 2021 02:06:02 +0200 Subject: [PATCH 14/15] Update restore --- scripts/restore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/restore b/scripts/restore index ea42491..268c6bb 100755 --- a/scripts/restore +++ b/scripts/restore @@ -177,7 +177,7 @@ ynh_systemd_action --service_name=$app-pict-rs --action="start" --line_match="St #================================================= # GENERIC FINALIZATION #================================================= -# RELOAD NGINX AND PHP-FPM +# RELOAD NGINX #================================================= ynh_script_progression --message="Reloading NGINX web server..." From 87292610c1f095b737f429d1078ff34565cca164 Mon Sep 17 00:00:00 2001 From: yalh76 Date: Sun, 29 Aug 2021 12:52:06 +0200 Subject: [PATCH 15/15] Remove ynh_exec_warn_less causing issue with rustup --- scripts/install | 2 +- scripts/upgrade | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/install b/scripts/install index 656da08..090d481 100755 --- a/scripts/install +++ b/scripts/install @@ -178,7 +178,7 @@ ynh_secure_remove --file="$final_path/build-imagemagick" # Install rustup with the toolchain needed by pict-rs pushd "$final_path" - ynh_exec_warn_less sudo -u "$app" RUSTUP_HOME="$final_path/.rustup" CARGO_HOME="$final_path/.cargo" bash -c 'curl -sSf -L https://static.rust-lang.org/rustup.sh | sh -s -- -y --default-toolchain nightly' + sudo -u "$app" RUSTUP_HOME="$final_path/.rustup" CARGO_HOME="$final_path/.cargo" bash -c 'curl -sSf -L https://static.rust-lang.org/rustup.sh | sh -s -- -y --default-toolchain nightly' popd export PATH="$PATH:$final_path/.cargo/bin:$final_path/.local/bin:/usr/local/sbin" diff --git a/scripts/upgrade b/scripts/upgrade index c768394..99187ed 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -164,7 +164,7 @@ then # Install rustup with the toolchain needed by pict-rs pushd "$final_path" - ynh_exec_warn_less sudo -u "$app" RUSTUP_HOME="$final_path/.rustup" CARGO_HOME="$final_path/.cargo" bash -c 'curl -sSf -L https://static.rust-lang.org/rustup.sh | sh -s -- -y --default-toolchain nightly' + sudo -u "$app" RUSTUP_HOME="$final_path/.rustup" CARGO_HOME="$final_path/.cargo" bash -c 'curl -sSf -L https://static.rust-lang.org/rustup.sh | sh -s -- -y --default-toolchain nightly' popd export PATH="$PATH:$final_path/.cargo/bin:$final_path/.local/bin:/usr/local/sbin"