1
mirror of https://github.com/jakejarvis/mastodon-utils.git synced 2025-10-26 03:45:47 -04:00

TIL sourcing bash files more than one layer deep isn't fun

This commit is contained in:
2022-12-20 19:39:39 -05:00
parent 066169ff06
commit 973a9d5507
5 changed files with 277 additions and 273 deletions

View File

@@ -56,7 +56,6 @@ git clone https://github.com/jakejarvis/mastodon-utils.git /home/mastodon/utils
- [`install.sh`](scripts/install.sh): Assumes an absolutely clean install of Ubuntu 20.04 and installs Mastodon ***with all of the quirks from this repo.*** Configure `MASTODON_USER` and other paths in [`init.sh`](init.sh) first if necessary. [Get the far less dangerous version of `install.sh` here instead.](https://github.com/jakejarvis/mastodon-installer/blob/main/install.sh) - [`install.sh`](scripts/install.sh): Assumes an absolutely clean install of Ubuntu 20.04 and installs Mastodon ***with all of the quirks from this repo.*** Configure `MASTODON_USER` and other paths in [`init.sh`](init.sh) first if necessary. [Get the far less dangerous version of `install.sh` here instead.](https://github.com/jakejarvis/mastodon-installer/blob/main/install.sh)
- [`upgrade.sh`](scripts/upgrade.sh): Upgrades Mastodon server (latest version if vanilla Mastodon, latest commit if `glitch-soc`) and ***re-applies every patch*** listed below. [Get the far less dangerous version of `upgrade.sh` here instead.](https://github.com/jakejarvis/mastodon-installer/blob/main/upgrade.sh) - [`upgrade.sh`](scripts/upgrade.sh): Upgrades Mastodon server (latest version if vanilla Mastodon, latest commit if `glitch-soc`) and ***re-applies every patch*** listed below. [Get the far less dangerous version of `upgrade.sh` here instead.](https://github.com/jakejarvis/mastodon-installer/blob/main/upgrade.sh)
- [`apply_patches.sh`](scripts/apply_patches.sh): Apply every patch below on top of the currently installed version of Mastodon.
## Patches ## Patches
@@ -64,8 +63,8 @@ git clone https://github.com/jakejarvis/mastodon-utils.git /home/mastodon/utils
- [`robots.patch`](patches/robots.patch): Disallow search engines for all of Mastodon - [`robots.patch`](patches/robots.patch): Disallow search engines for all of Mastodon
- [`increase-sidekiq-timeout.patch`](patches/increase-sidekiq-timeout.patch): Small bump in Sidekiq's timeout before it decides a remote instance isn't available. **Use this one very carefully!** - [`increase-sidekiq-timeout.patch`](patches/increase-sidekiq-timeout.patch): Small bump in Sidekiq's timeout before it decides a remote instance isn't available. **Use this one very carefully!**
- [`favicons.patch`](patches/favicons.patch): Use custom icon images instead of Mastodon logo
- [`admin-move-federation-to-sidebar.patch`](patches/admin-move-federation-to-sidebar.patch): Why is the most frequently used admin page listed under a link that takes you to another page to open a submenu in the sidebar to finally be able to click on it to go to the page?!? - [`admin-move-federation-to-sidebar.patch`](patches/admin-move-federation-to-sidebar.patch): Why is the most frequently used admin page listed under a link that takes you to another page to open a submenu in the sidebar to finally be able to click on it to go to the page?!?
- [`favicons.patch`](patches/favicons.patch): Use custom icon images instead of Mastodon logo
- [`system-font.patch`](patches/system-font.patch): Use the system's default sans-serif font stack instead of Roboto - [`system-font.patch`](patches/system-font.patch): Use the system's default sans-serif font stack instead of Roboto
- [`glitch/system-font.patch`](patches/glitch/system-font.patch) - [`glitch/system-font.patch`](patches/glitch/system-font.patch)
- [`hide-contact-email.patch`](patches/hide-contact-email.patch): Hides the `mailto:` link on the About page - [`hide-contact-email.patch`](patches/hide-contact-email.patch): Hides the `mailto:` link on the About page

View File

@@ -27,7 +27,7 @@
"type": "grafana", "type": "grafana",
"id": "grafana", "id": "grafana",
"name": "Grafana", "name": "Grafana",
"version": "9.3.1" "version": "9.3.2"
}, },
{ {
"type": "datasource", "type": "datasource",
@@ -138,7 +138,7 @@
"content": "<div style=\"text-align:center;\">\n Say hi:\n <a href=\"https://fediverse.jarv.is/@jake\" target=\"_blank\"><strong>@jake@jarv.is</strong></a> on <a href=\"https://fediverse.jarv.is/about\" target=\"blank\">fediverse.jarv.is</a>\n</div>", "content": "<div style=\"text-align:center;\">\n Say hi:\n <a href=\"https://fediverse.jarv.is/@jake\" target=\"_blank\"><strong>@jake@jarv.is</strong></a> on <a href=\"https://fediverse.jarv.is/about\" target=\"blank\">fediverse.jarv.is</a>\n</div>",
"mode": "html" "mode": "html"
}, },
"pluginVersion": "9.3.1", "pluginVersion": "9.3.2",
"title": "🦣 Welcome to the public metrics for my single-user Mastodon server! 📈", "title": "🦣 Welcome to the public metrics for my single-user Mastodon server! 📈",
"transparent": true, "transparent": true,
"type": "text" "type": "text"
@@ -222,7 +222,7 @@
"showThresholdLabels": false, "showThresholdLabels": false,
"showThresholdMarkers": true "showThresholdMarkers": true
}, },
"pluginVersion": "9.3.1", "pluginVersion": "9.3.2",
"targets": [ "targets": [
{ {
"datasource": { "datasource": {
@@ -301,7 +301,7 @@
"showThresholdLabels": false, "showThresholdLabels": false,
"showThresholdMarkers": true "showThresholdMarkers": true
}, },
"pluginVersion": "9.3.1", "pluginVersion": "9.3.2",
"targets": [ "targets": [
{ {
"datasource": { "datasource": {
@@ -389,7 +389,7 @@
"showThresholdLabels": false, "showThresholdLabels": false,
"showThresholdMarkers": true "showThresholdMarkers": true
}, },
"pluginVersion": "9.3.1", "pluginVersion": "9.3.2",
"targets": [ "targets": [
{ {
"datasource": { "datasource": {
@@ -474,7 +474,7 @@
"showThresholdLabels": false, "showThresholdLabels": false,
"showThresholdMarkers": true "showThresholdMarkers": true
}, },
"pluginVersion": "9.3.1", "pluginVersion": "9.3.2",
"targets": [ "targets": [
{ {
"datasource": { "datasource": {
@@ -550,7 +550,7 @@
"showThresholdLabels": false, "showThresholdLabels": false,
"showThresholdMarkers": true "showThresholdMarkers": true
}, },
"pluginVersion": "9.3.1", "pluginVersion": "9.3.2",
"targets": [ "targets": [
{ {
"datasource": { "datasource": {
@@ -665,7 +665,7 @@
}, },
"textMode": "value" "textMode": "value"
}, },
"pluginVersion": "9.3.1", "pluginVersion": "9.3.2",
"targets": [ "targets": [
{ {
"datasource": { "datasource": {
@@ -1011,7 +1011,6 @@
}, },
"links": [], "links": [],
"mappings": [], "mappings": [],
"max": 8000000000,
"min": 0, "min": 0,
"thresholds": { "thresholds": {
"mode": "percentage", "mode": "percentage",
@@ -1350,13 +1349,7 @@
"mode": "thresholds" "mode": "thresholds"
}, },
"decimals": 0, "decimals": 0,
"links": [ "links": [],
{
"targetBlank": true,
"title": "Sidekiq",
"url": "https://fediverse.jarv.is/sidekiq"
}
],
"mappings": [], "mappings": [],
"thresholds": { "thresholds": {
"mode": "percentage", "mode": "percentage",
@@ -1398,6 +1391,16 @@
} }
] ]
} }
},
{
"id": "links",
"value": [
{
"targetBlank": true,
"title": "",
"url": "https://fediverse.jarv.is/sidekiq/busy"
}
]
} }
] ]
}, },
@@ -1413,6 +1416,16 @@
"fixedColor": "blue", "fixedColor": "blue",
"mode": "fixed" "mode": "fixed"
} }
},
{
"id": "links",
"value": [
{
"targetBlank": true,
"title": "",
"url": "https://fediverse.jarv.is/sidekiq/scheduled"
}
]
} }
] ]
}, },
@@ -1428,6 +1441,16 @@
"fixedColor": "orange", "fixedColor": "orange",
"mode": "fixed" "mode": "fixed"
} }
},
{
"id": "links",
"value": [
{
"targetBlank": true,
"title": "",
"url": "https://fediverse.jarv.is/sidekiq/retries"
}
]
} }
] ]
}, },
@@ -1442,21 +1465,16 @@
"value": { "value": {
"mode": "fixed" "mode": "fixed"
} }
} },
]
},
{
"matcher": {
"id": "byName",
"options": "Failed"
},
"properties": [
{ {
"id": "color", "id": "links",
"value": { "value": [
"fixedColor": "red", {
"mode": "fixed" "targetBlank": true,
} "title": "",
"url": "https://fediverse.jarv.is/sidekiq/morgue"
}
]
} }
] ]
} }
@@ -1491,7 +1509,7 @@
"text": {}, "text": {},
"textMode": "auto" "textMode": "auto"
}, },
"pluginVersion": "9.3.1", "pluginVersion": "9.3.2",
"targets": [ "targets": [
{ {
"datasource": { "datasource": {
@@ -1707,7 +1725,7 @@
"text": {}, "text": {},
"textMode": "value" "textMode": "value"
}, },
"pluginVersion": "9.3.1", "pluginVersion": "9.3.2",
"targets": [ "targets": [
{ {
"datasource": { "datasource": {
@@ -1782,8 +1800,7 @@
"mode": "absolute", "mode": "absolute",
"steps": [ "steps": [
{ {
"color": "green", "color": "green"
"value": null
} }
] ]
} }
@@ -1904,8 +1921,7 @@
"mode": "absolute", "mode": "absolute",
"steps": [ "steps": [
{ {
"color": "green", "color": "green"
"value": null
} }
] ]
}, },
@@ -1974,8 +1990,7 @@
"mode": "percentage", "mode": "percentage",
"steps": [ "steps": [
{ {
"color": "green", "color": "green"
"value": null
} }
] ]
}, },
@@ -2004,7 +2019,7 @@
}, },
"showUnfilled": true "showUnfilled": true
}, },
"pluginVersion": "9.3.1", "pluginVersion": "9.3.2",
"targets": [ "targets": [
{ {
"datasource": { "datasource": {
@@ -2091,7 +2106,7 @@
"text": {}, "text": {},
"textMode": "value" "textMode": "value"
}, },
"pluginVersion": "9.3.1", "pluginVersion": "9.3.2",
"targets": [ "targets": [
{ {
"datasource": { "datasource": {
@@ -2260,7 +2275,7 @@
"text": {}, "text": {},
"textMode": "value" "textMode": "value"
}, },
"pluginVersion": "9.3.1", "pluginVersion": "9.3.2",
"targets": [ "targets": [
{ {
"datasource": { "datasource": {
@@ -2496,204 +2511,6 @@
"x": 0, "x": 0,
"y": 38 "y": 38
}, },
"id": 97,
"panels": [],
"title": "Media Storage",
"type": "row"
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"description": "Total size of all files in the media storage bucket.",
"fieldConfig": {
"defaults": {
"color": {
"fixedColor": "orange",
"mode": "fixed"
},
"custom": {
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 40,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": true,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green"
}
]
},
"unit": "bytes"
},
"overrides": []
},
"gridPos": {
"h": 7,
"w": 12,
"x": 0,
"y": 39
},
"id": 95,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": false
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"editorMode": "code",
"expr": "json_linode_size{bucket=\"jarvis-mastodon\", zone=\"us-east-1\"}",
"legendFormat": "{{hostname}}",
"range": true,
"refId": "A"
}
],
"title": "Linode Bucket Size",
"transformations": [],
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"description": "Number of files in the media storage bucket.",
"fieldConfig": {
"defaults": {
"color": {
"fixedColor": "green",
"mode": "fixed"
},
"custom": {
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 40,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": true,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green"
}
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 7,
"w": 12,
"x": 12,
"y": 39
},
"id": 93,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": false
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"editorMode": "code",
"exemplar": false,
"expr": "json_linode_objects{bucket=\"jarvis-mastodon\", zone=\"us-east-1\"}",
"instant": false,
"interval": "",
"legendFormat": "{{hostname}}",
"range": true,
"refId": "A"
}
],
"title": "Linode Bucket Objects",
"transformations": [],
"type": "timeseries"
},
{
"collapsed": false,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 46
},
"id": 7, "id": 7,
"panels": [], "panels": [],
"title": "Database", "title": "Database",
@@ -2757,7 +2574,7 @@
"h": 7, "h": 7,
"w": 12, "w": 12,
"x": 0, "x": 0,
"y": 47 "y": 39
}, },
"id": 10, "id": 10,
"options": { "options": {
@@ -2865,7 +2682,7 @@
"h": 7, "h": 7,
"w": 12, "w": 12,
"x": 12, "x": 12,
"y": 47 "y": 39
}, },
"id": 11, "id": 11,
"options": { "options": {
@@ -2963,7 +2780,7 @@
"h": 7, "h": 7,
"w": 12, "w": 12,
"x": 0, "x": 0,
"y": 54 "y": 46
}, },
"id": 4, "id": 4,
"options": { "options": {
@@ -3081,7 +2898,7 @@
"h": 7, "h": 7,
"w": 12, "w": 12,
"x": 12, "x": 12,
"y": 54 "y": 46
}, },
"id": 5, "id": 5,
"options": { "options": {
@@ -3127,6 +2944,204 @@
], ],
"type": "timeseries" "type": "timeseries"
}, },
{
"collapsed": false,
"gridPos": {
"h": 1,
"w": 24,
"x": 0,
"y": 53
},
"id": 97,
"panels": [],
"title": "Media Storage",
"type": "row"
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"description": "Total size of all files in the media storage bucket.",
"fieldConfig": {
"defaults": {
"color": {
"fixedColor": "orange",
"mode": "fixed"
},
"custom": {
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 40,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": true,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green"
}
]
},
"unit": "bytes"
},
"overrides": []
},
"gridPos": {
"h": 7,
"w": 12,
"x": 0,
"y": 54
},
"id": 95,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": false
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"editorMode": "code",
"expr": "json_linode_size{bucket=\"jarvis-mastodon\", zone=\"us-east-1\"}",
"legendFormat": "{{hostname}}",
"range": true,
"refId": "A"
}
],
"title": "Linode Bucket Size",
"transformations": [],
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"description": "Number of files in the media storage bucket.",
"fieldConfig": {
"defaults": {
"color": {
"fixedColor": "green",
"mode": "fixed"
},
"custom": {
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 40,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": true,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green"
}
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 7,
"w": 12,
"x": 12,
"y": 54
},
"id": 93,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": false
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS}"
},
"editorMode": "code",
"exemplar": false,
"expr": "json_linode_objects{bucket=\"jarvis-mastodon\", zone=\"us-east-1\"}",
"instant": false,
"interval": "",
"legendFormat": "{{hostname}}",
"range": true,
"refId": "A"
}
],
"title": "Linode Bucket Objects",
"transformations": [],
"type": "timeseries"
},
{ {
"collapsed": false, "collapsed": false,
"gridPos": { "gridPos": {
@@ -3578,6 +3593,6 @@
"timezone": "America/New_York", "timezone": "America/New_York",
"title": "Mastodon public", "title": "Mastodon public",
"uid": "IXDZT_dV9", "uid": "IXDZT_dV9",
"version": 299, "version": 306,
"weekStart": "sunday" "weekStart": "sunday"
} }

View File

@@ -1,23 +0,0 @@
#!/bin/bash
# exit when any step fails
set -euo pipefail
# initialize path
. "$(dirname "$(realpath "$0")")"/../init.sh
# apply custom patches
cd "$APP_ROOT"
as_mastodon git apply --reject --allow-binary-replacement "$UTILS_ROOT"/patches/*.patch
if [ -d "$APP_ROOT/app/javascript/flavours/glitch" ]; then
# apply additional glitch-only patches:
as_mastodon git apply --reject --allow-binary-replacement "$UTILS_ROOT"/patches/glitch/*.patch
fi
# compile new assets
echo "Compiling new assets..."
as_mastodon RAILS_ENV=production bundle exec rails assets:precompile
# optional: create blank custom.css (this overrides any CSS set in the admin panel, but if that's not being used, then
# this is an easy way to save a request to the backend)
as_mastodon touch "$APP_ROOT/public/custom.css"

View File

@@ -122,6 +122,11 @@ as_mastodon git checkout "$(as_mastodon git describe --abbrev=0 --tags --match "
# clone glitch-soc & checkout latest commit: # clone glitch-soc & checkout latest commit:
# as_mastodon git clone https://github.com/glitch-soc/mastodon.git "$APP_ROOT" && cd "$APP_ROOT" # as_mastodon git clone https://github.com/glitch-soc/mastodon.git "$APP_ROOT" && cd "$APP_ROOT"
# apply custom patches:
as_mastodon git apply --reject --allow-binary-replacement "$UTILS_ROOT"/patches/*.patch
# apply additional glitch-only patches:
# as_mastodon git apply --reject --allow-binary-replacement "$UTILS_ROOT"/patches/glitch/*.patch
# install ruby # install ruby
as_mastodon RUBY_CONFIGURE_OPTS=--with-jemalloc rbenv install --skip-existing as_mastodon RUBY_CONFIGURE_OPTS=--with-jemalloc rbenv install --skip-existing
as_mastodon rbenv global "$(as_mastodon cat "$APP_ROOT"/.ruby-version)" as_mastodon rbenv global "$(as_mastodon cat "$APP_ROOT"/.ruby-version)"
@@ -203,8 +208,8 @@ STREAMING_CLUSTER_NUM=1
# manually setup db # manually setup db
as_mastodon RAILS_ENV=production bundle exec rails db:setup as_mastodon RAILS_ENV=production bundle exec rails db:setup
# apply custom patches & precompile assets # precompile assets
. "$UTILS_ROOT/scripts/apply_patches.sh" as_mastodon RAILS_ENV=production bundle exec rails assets:precompile
# install latest certbot # install latest certbot
# https://certbot.eff.org/instructions?ws=nginx&os=pip # https://certbot.eff.org/instructions?ws=nginx&os=pip

View File

@@ -30,6 +30,13 @@ else
as_mastodon git checkout "$(as_mastodon git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)" as_mastodon git checkout "$(as_mastodon git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)"
fi fi
# apply custom patches
as_mastodon git apply --reject --allow-binary-replacement "$UTILS_ROOT"/patches/*.patch
if [ -d "$APP_ROOT/app/javascript/flavours/glitch" ]; then
# apply additional glitch-only patches:
as_mastodon git apply --reject --allow-binary-replacement "$UTILS_ROOT"/patches/glitch/*.patch
fi
# set new ruby version # set new ruby version
as_mastodon RUBY_CONFIGURE_OPTS=--with-jemalloc rbenv install --skip-existing as_mastodon RUBY_CONFIGURE_OPTS=--with-jemalloc rbenv install --skip-existing
as_mastodon rbenv global "$(as_mastodon cat "$APP_ROOT"/.ruby-version)" as_mastodon rbenv global "$(as_mastodon cat "$APP_ROOT"/.ruby-version)"
@@ -41,8 +48,9 @@ as_mastodon bash -c "\. \"$NVM_DIR/nvm.sh\"; nvm install; nvm use; npm install -
as_mastodon bundle install --jobs "$(getconf _NPROCESSORS_ONLN)" as_mastodon bundle install --jobs "$(getconf _NPROCESSORS_ONLN)"
as_mastodon yarn install --pure-lockfile --network-timeout 100000 as_mastodon yarn install --pure-lockfile --network-timeout 100000
# pull & apply latest patches # compile new assets
. "$UTILS_ROOT/scripts/apply_patches.sh" echo "Compiling new assets..."
as_mastodon RAILS_ENV=production bundle exec rails assets:precompile
# run migrations: # run migrations:
# https://docs.joinmastodon.org/admin/upgrading/ # https://docs.joinmastodon.org/admin/upgrading/