diff --git a/.dockerignore b/.dockerignore index d125aba..67d79d7 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,13 +1,5 @@ -.env -.env.example -LICENSE -README.md -compose.yml -production-compose.yml.example -staging-compose.yml.example -compose.yml.example -composer.lock -.gitignore -.git/ -vendor/ -systemd/ +# Deny all files by default +* + +# Allow everything in the wiki directory +!wiki/ diff --git a/.env.example b/.env.example index 6e645f5..edcc13d 100644 --- a/.env.example +++ b/.env.example @@ -1,31 +1,88 @@ # Build Arguments + +# This major version number of MediaWiki you want to use, e.g. "1.44" MEDIAWIKI_MAJOR_VERSION="" +# The full version number of MediaWiki you want to use, e.g. "1.44.2" MEDIAWIKI_VERSION="" +# The branch of MediaWiki you want to use for extensions, e.g. "REL1_44" MEDIAWIKI_BRANCH="" +# The version of the Citizen skin you want to use, e.g. "3.11.0" CITIZEN_VERSION="" + # Core Secrets + +# The key to access the upgrade page, this is disabled on the default image +# This needs to be a 16 character alphanumeric string UPGRADE_KEY="" +# The key is used for various security related functions within MediaWiki +# This needs to be a 64 character alphanumeric string SECRET_KEY="" -# DB Secrets + +# DB Configurations + +# The IP address or hostname of your database server DB_SERVER="" +# The name of the table in the database used by MediaWiki DB_NAME="" +# The username used by MediaWiki to connect to the database DB_USER="" +# The password used by MediaWiki to connect to the database DB_PASSWORD="" -# Extension Secrets -TURNSTILE_SITE_KEY="" + +# SMTP Configurations and Secrets + +# The SMTP server host address +SMTP_HOST="" +# The SMTP domain you wish to use for sending emails +SMTP_DOMAIN="" +# The SMTP server port, usually 587 for TLS or 465 for SSL +SMTP_PORT="" +# The email address to send SMTP emails from for service notifications +SMTP_USERNAME="" +# The password for the SMTP username SMTP_PASSWORD="" +# The email address to send emergency notifications to +EMERGENCY_EMAIL="" + + +# Cloudflare Turnstile Secrets + +# The site key for Cloudflare Turnstile +TURNSTILE_SITE_KEY="" +# The secret key for Cloudflare Turnstile TURNSTILE_SECRET_KEY="" -DISCORD_WEBHOOK_URL="" + + +# OpenID Okta Secrets + +# The client ID for OpenID authentication from Okta OPENID_CLIENT_ID="" +# The client secret for OpenID authentication from Okta OPENID_CLIENT_SECRET="" -ACCESS_KEY_ID="" -SECRET_ACCESS_KEY="" -# Wiki Configurations + +# S3 Configurations and Secrets + +# The S3 endpoint URL +S3_ENDPOINT="" +# The S3 access key ID +S3_ACCESS_KEY_ID="" +# The S3 secret access key +S3_SECRET_ACCESS_KEY="" +# The S3 bucket name +S3_BUCKET_NAME="" +# The S3 bucket domain +S3_BUCKET_DOMAIN="" + + +# Miscellaneous Configurations + +# The domain the wiki will be accessible from, without the protocol (e.g. example.com) SITENAME="" +# The full URL the wiki will be accessible from, including the protocol (e.g. https://example.com) WG_SERVER="" -AWS_BUCKET_NAME="" -AWS_BUCKET_DOMAIN="" +# Discord Webhook URL +DISCORD_WEBHOOK_URL="" diff --git a/.env.local.example b/.env.local.example new file mode 100644 index 0000000..50b470d --- /dev/null +++ b/.env.local.example @@ -0,0 +1,96 @@ +# Build Arguments + +# This major version number of MediaWiki you want to use, e.g. "1.44" +MEDIAWIKI_MAJOR_VERSION="" +# The full version number of MediaWiki you want to use, e.g. "1.44.2" +MEDIAWIKI_VERSION="" +# The branch of MediaWiki you want to use for extensions, e.g. "REL1_44" +MEDIAWIKI_BRANCH="" +# The version of the Citizen skin you want to use, e.g. "3.11.0" +CITIZEN_VERSION="" + + +# Core Secrets + +# The key to access the upgrade page, this is disabled on the default image +# This needs to be a 16 character alphanumeric string +UPGRADE_KEY="" +# The key is used for various security related functions within MediaWiki +# This needs to be a 64 character alphanumeric string +SECRET_KEY="" + + +# DB Configurations + +# The IP address or hostname of your database server +DB_SERVER="local-wiki-mariadb" +# The name of the table in the database used by MediaWiki +DB_NAME="local-maria-db" # This needs to match MARIADB_NAME +# The username used by MediaWiki to connect to the database +DB_USER="local-maria-user" # This needs to match MARIADB_USER +# The password used by MediaWiki to connect to the database +DB_PASSWORD="local-maria-password" # This needs to match MARIADB_PASSWORD + + +# SMTP Configurations and Secrets + +# The SMTP server host address +SMTP_HOST="" +# The SMTP domain you wish to use for sending emails +SMTP_DOMAIN="" +# The SMTP server port, usually 587 for TLS or 465 for SSL +SMTP_PORT="" +# The email address to send SMTP emails from for service notifications +SMTP_USERNAME="" +# The password for the SMTP username +SMTP_PASSWORD="" +# The email address to send emergency notifications to +EMERGENCY_EMAIL="" + + +# Cloudflare Turnstile Secrets + +# The site key for Cloudflare Turnstile +TURNSTILE_SITE_KEY="" +# The secret key for Cloudflare Turnstile +TURNSTILE_SECRET_KEY="" + + +# OpenID Okta Secrets - DISABLED LOCALLY + +# The client ID for OpenID authentication from Okta +# OPENID_CLIENT_ID="" +# The client secret for OpenID authentication from Okta +# OPENID_CLIENT_SECRET="" + + +# S3 Configurations and Secrets + +# The S3 endpoint URL +S3_ENDPOINT="http://minio:9000" +# The S3 access key ID +S3_ACCESS_KEY_ID="local-minio-user" # This needs to match MINIO_USER +# The S3 secret access key +S3_SECRET_ACCESS_KEY="local-minio-password" # This needs to match MINIO_PASSWORD +# The S3 bucket name +S3_BUCKET_NAME="" +# The S3 bucket domain +S3_BUCKET_DOMAIN="minio:9000" + + +# Miscellaneous Configurations + +# The domain the wiki will be accessible from, without the protocol (e.g. localhost:3000) +SITENAME="localhost:3000" +# The full URL the wiki will be accessible from, including the protocol (e.g. https://localhost:3000) +WG_SERVER="http://localhost:3000" +# Discord Webhook URL +DISCORD_WEBHOOK_URL="" + + +# Local Configurations +MARIADB_PASSWORD="local-maria-password" # This needs to match DB_PASSWORD +MARIADB_NAME="local-maria-db" # This needs to match DB_NAME +MARIADB_USER="local-maria-user" # This needs to match DB_USER +MINIO_USER="local-minio-user" # This needs to match S3_ACCESS_KEY_ID +MINIO_PASSWORD="local-minio-password" # This needs to match S3_SECRET_ACCESS_KEY diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..1096a0a --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @allthingslinux/wiki-admin diff --git a/.github/workflows/lint-dockerfile.yml b/.github/workflows/lint-dockerfile.yml new file mode 100644 index 0000000..d59a520 --- /dev/null +++ b/.github/workflows/lint-dockerfile.yml @@ -0,0 +1,22 @@ +name: Lint Dockerfile + +on: + pull_request: + branches: + - main + paths: + - '**/Dockerfile*' + +jobs: + hadolint: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Lint Dockerfile with hadolint + uses: hadolint/hadolint-action@v3.1.0 + with: + dockerfile: Dockerfile + failure-threshold: error diff --git a/.github/workflows/lint-php.yml b/.github/workflows/lint-php.yml new file mode 100644 index 0000000..20e84f6 --- /dev/null +++ b/.github/workflows/lint-php.yml @@ -0,0 +1,60 @@ +name: Lint PHP + +on: + pull_request: + branches: [main] + paths: + - '**/*.php' + +permissions: + contents: write + pull-requests: write + +jobs: + phpcs: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v5 + with: + ref: ${{ github.head_ref }} + + - name: Set up PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.3' + tools: phpcs, phpcbf + + - name: Lint with PHPCS + id: phpcs + shell: bash + run: | + set +e + phpcs . + echo "exit_code=$?" >> "$GITHUB_OUTPUT" + set -e + + - name: Auto fix with PHPCBF + if: steps.phpcs.outputs.exit_code != '0' + run: phpcbf . + + - name: Commit fixes + if: steps.phpcs.outputs.exit_code != '0' && github.event.pull_request.head.repo.fork == false + shell: bash + run: | + if git diff --quiet; then + echo "PHPCBF ran but no changes were made." + exit 0 + fi + + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add -A + git commit -m "Apply PHPCBF fixes" + git push + + - name: Lint with PHPCS again + if: steps.phpcs.outputs.exit_code != '0' + run: phpcs . + diff --git a/.github/workflows/lint-python.yml b/.github/workflows/lint-python.yml new file mode 100644 index 0000000..017a09b --- /dev/null +++ b/.github/workflows/lint-python.yml @@ -0,0 +1,26 @@ +name: Lint Python + +on: + pull_request: + branches: + - main + paths: + - '**.py' + +jobs: + pylint: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v5 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.x' + + - name: Install dependencies + run: pip install pylint + + - name: Run Pylint + run: pylint $(git ls-files '*.py') diff --git a/.github/workflows/sync-staging.yml b/.github/workflows/sync-staging.yml deleted file mode 100644 index 9045967..0000000 --- a/.github/workflows/sync-staging.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: Sync Staging - -on: - push: - branches: [main] - -jobs: - sync: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v5 - with: - fetch-depth: 0 - - - run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - git checkout staging - git merge main --no-edit - git push origin staging diff --git a/.github/workflows/test-build.yml b/.github/workflows/test-build.yml new file mode 100644 index 0000000..cdad48c --- /dev/null +++ b/.github/workflows/test-build.yml @@ -0,0 +1,33 @@ +name: Docker Build Test + +on: + pull_request: + branches: + - main + paths: + - 'Dockerfile' + - 'wiki/**' + +jobs: + docker-build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build test image + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile + push: false + tags: test:latest + cache-from: type=gha + cache-to: type=gha,mode=max + build-args: | + MEDIAWIKI_MAJOR_VERSION=1.44 + MEDIAWIKI_VERSION=1.44.3 + MEDIAWIKI_BRANCH=REL1_44 + CITIZEN_VERSION=3.11.0 diff --git a/.gitignore b/.gitignore index 93db8fe..bdb4b19 100755 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,7 @@ -vendor/ .env -compose.yaml composer.lock +compose.yaml +backups/ +database/ +images/ +vendor/ diff --git a/.hadolint.yaml b/.hadolint.yaml new file mode 100644 index 0000000..904cd85 --- /dev/null +++ b/.hadolint.yaml @@ -0,0 +1,3 @@ +--- +ignored: + - DL3018 # Pin versions in apk add diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..a81b3d5 --- /dev/null +++ b/.pylintrc @@ -0,0 +1,415 @@ +# This Pylint rcfile contains a best-effort configuration to uphold the +# best-practices and style described in the Google Python style guide: +# https://google.github.io/styleguide/pyguide.html +# +# Its canonical open-source location is: +# https://google.github.io/styleguide/pylintrc + +# Copyright 2018 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +[MAIN] + +# Files or directories to be skipped. They should be base names, not paths. +ignore=third_party + +# Files or directories matching the regex patterns are skipped. The regex +# matches against base names, not paths. +ignore-patterns= + +# Pickle collected data for later comparisons. +persistent=no + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins= + +# Use multiple processes to speed up Pylint. +jobs=4 + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + + +[MESSAGES CONTROL] + +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED +confidence= + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). See also the "--disable" option for examples. +#enable= + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once).You can also use "--disable=all" to +# disable everything first and then reenable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use"--disable=all --enable=classes +# --disable=W" +disable=R, + abstract-method, + apply-builtin, + arguments-differ, + attribute-defined-outside-init, + backtick, + bad-option-value, + basestring-builtin, + buffer-builtin, + c-extension-no-member, + consider-using-enumerate, + cmp-builtin, + cmp-method, + coerce-builtin, + coerce-method, + delslice-method, + div-method, + eq-without-hash, + execfile-builtin, + file-builtin, + filter-builtin-not-iterating, + fixme, + getslice-method, + global-statement, + hex-method, + idiv-method, + implicit-str-concat, + import-error, + import-self, + import-star-module-level, + input-builtin, + intern-builtin, + invalid-str-codec, + locally-disabled, + long-builtin, + long-suffix, + map-builtin-not-iterating, + misplaced-comparison-constant, + missing-function-docstring, + metaclass-assignment, + next-method-called, + next-method-defined, + no-absolute-import, + no-init, # added + no-member, + no-name-in-module, + no-self-use, + nonzero-method, + oct-method, + old-division, + old-ne-operator, + old-octal-literal, + old-raise-syntax, + parameter-unpacking, + print-statement, + raising-string, + range-builtin-not-iterating, + raw_input-builtin, + rdiv-method, + reduce-builtin, + relative-import, + reload-builtin, + round-builtin, + setslice-method, + signature-differs, + standarderror-builtin, + suppressed-message, + sys-max-int, + trailing-newlines, + unichr-builtin, + unicode-builtin, + unnecessary-pass, + unpacking-in-except, + useless-else-on-loop, + useless-suppression, + using-cmp-argument, + wrong-import-order, + xrange-builtin, + zip-builtin-not-iterating, + + +[REPORTS] + +# Set the output format. Available formats are text, parseable, colorized, msvs +# (visual studio) and html. You can also give a reporter class, eg +# mypackage.mymodule.MyReporterClass. +output-format=text + +# Tells whether to display a full report or only the messages +reports=no + +# Activate the evaluation score. +score=no + +# Python expression which should return a note less than 10 (10 is the highest +# note). You have access to the variables errors warning, statement which +# respectively contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (RP0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details +#msg-template= + + +[BASIC] + +# Good variable names which should always be accepted, separated by a comma +good-names=main,_ + +# Bad variable names which should always be refused, separated by a comma +bad-names= + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Include a hint for the correct naming format with invalid-name +include-naming-hint=no + +# List of decorators that produce properties, such as abc.abstractproperty. Add +# to this list to register other decorators that produce valid properties. +property-classes=abc.abstractproperty,cached_property.cached_property,cached_property.threaded_cached_property,cached_property.cached_property_with_ttl,cached_property.threaded_cached_property_with_ttl + +# Regular expression matching correct function names +function-rgx=^(?:(?PsetUp|tearDown|setUpModule|tearDownModule)|(?P_?[A-Z][a-zA-Z0-9]*)|(?P_?[a-z][a-z0-9_]*))$ + +# Regular expression matching correct variable names +variable-rgx=^[a-z][a-z0-9_]*$ + +# Regular expression matching correct constant names +const-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$ + +# Regular expression matching correct attribute names +attr-rgx=^_{0,2}[a-z][a-z0-9_]*$ + +# Regular expression matching correct argument names +argument-rgx=^[a-z][a-z0-9_]*$ + +# Regular expression matching correct class attribute names +class-attribute-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$ + +# Regular expression matching correct inline iteration names +inlinevar-rgx=^[a-z][a-z0-9_]*$ + +# Regular expression matching correct class names +class-rgx=^_?[A-Z][a-zA-Z0-9]*$ + +# Regular expression matching correct module names +module-rgx=^(_?[a-z][a-z0-9_]*|__init__)$ + +# Regular expression matching correct method names +method-rgx=(?x)^(?:(?P_[a-z0-9_]+__|runTest|setUp|tearDown|setUpTestCase|tearDownTestCase|setupSelf|tearDownClass|setUpClass|(test|assert)_*[A-Z0-9][a-zA-Z0-9_]*|next)|(?P_{0,2}[A-Z][a-zA-Z0-9_]*)|(?P_{0,2}[a-z][a-z0-9_]*))$ + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=(__.*__|main|test.*|.*test|.*Test)$ + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=12 + + +[TYPECHECK] + +# List of decorators that produce context managers, such as +# contextlib.contextmanager. Add to this list to register other decorators that +# produce valid context managers. +contextmanager-decorators=contextlib.contextmanager,contextlib2.contextmanager + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis. It +# supports qualified module names, as well as Unix pattern matching. +ignored-modules= + +# List of class names for which member attributes should not be checked (useful +# for classes with dynamically set attributes). This supports the use of +# qualified names. +ignored-classes=optparse.Values,thread._local,_thread._local + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members= + + +[FORMAT] + +# Maximum number of characters on a single line. +max-line-length=100 + +# TODO(https://github.com/pylint-dev/pylint/issues/3352): Direct pylint to exempt +# lines made too long by directives to pytype. + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=(?x)( + ^\s*(\#\ )??$| + ^\s*(from\s+\S+\s+)?import\s+.+$) + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=yes + +# Maximum number of lines in a module +max-module-lines=99999 + +# String used as indentation unit. The internal Google style guide mandates 2 +# spaces. Google's externaly-published style guide says 4, consistent with +# PEP 8. +indent-string=' ' + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format= + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=TODO + + +[STRING] + +# This flag controls whether inconsistent-quotes generates a warning when the +# character used as a quote delimiter is used inconsistently within a module. +check-quote-consistency=yes + + +[VARIABLES] + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# A regular expression matching the name of dummy variables (i.e. expectedly +# not used). +dummy-variables-rgx=^\*{0,2}(_$|unused_|dummy_) + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-builtins= + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_,_cb + +# List of qualified module names which can have objects that can redefine +# builtins. +redefining-builtins-modules=six,six.moves,past.builtins,future.builtins,functools + + +[LOGGING] + +# Logging modules to check that the string format arguments are in logging +# function parameter format +logging-modules=logging,absl.logging,tensorflow.io.logging + + +[SIMILARITIES] + +# Minimum lines number of a similarity. +min-similarity-lines=4 + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + +# Ignore imports when computing similarities. +ignore-imports=no + + +[SPELLING] + +# Spelling dictionary name. Available dictionaries: none. To make it working +# install python-enchant package. +spelling-dict= + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to indicated private dictionary in +# --spelling-private-dict-file option instead of raising a message. +spelling-store-unknown-words=no + + +[IMPORTS] + +# Deprecated modules which should not be used, separated by a comma +deprecated-modules=regsub, + TERMIOS, + Bastion, + rexec, + sets + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled) +import-graph= + +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled) +ext-import-graph= + +# Create a graph of internal dependencies in the given file (report RP0402 must +# not be disabled) +int-import-graph= + +# Force import order to recognize a module as part of the standard +# compatibility libraries. +known-standard-library= + +# Force import order to recognize a module as part of a third party library. +known-third-party=enchant, absl + +# Analyse import fallback blocks. This can be used to support both Python 2 and +# 3 compatible code, which means that the block might have code that exists +# only in one or another interpreter, leading to false positives when analysed. +analyse-fallback-blocks=no + + +[CLASSES] + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__, + __new__, + setUp + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict, + _fields, + _replace, + _source, + _make + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls, + class_ + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=mcs diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..6ebb7bc --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "files.associations": { + "*.yaml.*": "yaml", + ".env*": "env", + "Justfile*": "just" + } +} diff --git a/Dockerfile b/Dockerfile index 0819c36..c018680 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,51 +1,35 @@ -# Copyright 2025 atmois -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# http://www.apache.org/licenses/LICENSE-2.0 +# Copyright 2025 All Things Linux and Contributors + +# Primary maintainer: Atmois + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 # Builder Stage FROM php:8.3-fpm-alpine AS builder SHELL ["/bin/ash", "-eo", "pipefail", "-c"] -RUN --mount=type=cache,target=/var/cache/apk,sharing=locked \ - --mount=type=cache,target=/tmp/pear,sharing=locked \ +# Install PHP Extensions +COPY --from=mlocati/php-extension-installer:latest /usr/bin/install-php-extensions /usr/local/bin/ + +RUN --mount=type=cache,target=/tmp/phpexts-cache \ set -eux && \ - # Install Build Dependencies - apk add --no-cache --virtual .build-deps \ - libxml2-dev=2.13.8-r0 \ - oniguruma-dev=6.9.10-r0 \ - libzip-dev=1.11.4-r0 \ - icu-dev=76.1-r1 \ - libpng-dev=1.6.47-r0 \ - libjpeg-turbo-dev=3.1.0-r0 \ - freetype-dev=2.13.3-r0 \ - autoconf=2.72-r1 \ - pcre-dev=8.45-r4 \ - make=4.4.1-r3 \ - gcc=14.2.0-r6 \ - g++=14.2.0-r6 \ - git=2.49.1-r0 \ - lua5.1-dev=5.1.5-r13; \ - # Install PHP Extensions - docker-php-ext-configure gd --with-freetype --with-jpeg && \ - docker-php-ext-install -j"$(nproc)" \ - xml \ + install-php-extensions \ + apcu \ + calendar \ + exif \ + gd \ + intl \ + luasandbox \ mbstring \ mysqli \ pdo_mysql \ - intl \ - zip \ - calendar \ - gd; \ - # Install PECL extensions - pecl install apcu-5.1.22 redis luasandbox && \ - docker-php-ext-enable apcu redis luasandbox && \ - # Cleanup in same layer - docker-php-source delete && \ - rm -rf ~/.pearrc && \ - apk del .build-deps + redis \ + wikidiff2 \ + xml \ + zip # Mediawiki Setup Stage FROM php:8.3-fpm-alpine AS mediawiki @@ -60,28 +44,33 @@ ARG MEDIAWIKI_BRANCH RUN --mount=type=cache,target=/var/cache/apk,sharing=locked \ set -eux && \ apk add --no-cache \ - python3=3.12.11-r0 \ - git=2.49.1-r0 \ - ca-certificates=20250619-r0 \ - gnupg=2.4.7-r0 \ - icu-libs=76.1-r1 \ - libzip=1.11.4-r0 \ - libpng=1.6.47-r0 \ - lua5.1-libs=5.1.5-r13 + ca-certificates \ + freetype \ + git \ + gnupg \ + icu-libs \ + libjpeg-turbo \ + libpng \ + libthai \ + libxml2 \ + libzip \ + lua5.1-libs \ + oniguruma \ + python3 COPY --from=builder /usr/local/lib/php/extensions/ /usr/local/lib/php/extensions/ COPY --from=builder /usr/local/etc/php/conf.d/ /usr/local/etc/php/conf.d/ COPY --from=composer:latest /usr/bin/composer /usr/bin/composer -RUN mkdir -p /var/www/atlwiki/mediawiki -WORKDIR /var/www/atlwiki +RUN mkdir -p /var/www/wiki/mediawiki +WORKDIR /var/www/wiki -COPY composer.json /var/www/atlwiki/composer.json +COPY wiki/composer.json /var/www/wiki/composer.json RUN --mount=type=cache,target=/root/.composer \ composer install --no-dev --optimize-autoloader --no-scripts -WORKDIR /var/www/atlwiki/mediawiki +WORKDIR /var/www/wiki/mediawiki RUN --mount=type=cache,target=/tmp/mediawiki-cache \ set -eux && \ @@ -97,33 +86,33 @@ RUN --mount=type=cache,target=/tmp/mediawiki-cache \ # Install Additional Dependencies -COPY extensions.json install_extensions.py /tmp/ +COPY wiki/extensions.json wiki/install_extensions.py /tmp/ RUN --mount=type=cache,target=/root/.composer \ set -eux && \ python3 /tmp/install_extensions.py && \ # Install Citizen skin git clone --branch v${CITIZEN_VERSION} --single-branch --depth 1 \ - https://github.com/StarCitizenTools/mediawiki-skins-Citizen.git /var/www/atlwiki/mediawiki/skins/Citizen + https://github.com/StarCitizenTools/mediawiki-skins-Citizen.git /var/www/wiki/mediawiki/skins/Citizen -COPY composer.local.json ./composer.local.json +COPY wiki/composer.local.json ./composer.local.json RUN --mount=type=cache,target=/root/.composer \ composer update --no-dev --optimize-autoloader --no-scripts # Cleanup -RUN rm -rf /var/www/atlwiki/mediawiki/tests/ \ - /var/www/atlwiki/mediawiki/docs/ \ - /var/www/atlwiki/mediawiki/mw-config/ \ - /var/www/atlwiki/mediawiki/maintenance/dev/ \ - /var/www/atlwiki/mediawiki/maintenance/benchmarks/ \ - /var/www/atlwiki/mediawiki/vendor/*/tests/ \ - /var/www/atlwiki/mediawiki/vendor/*/test/ \ - /var/www/atlwiki/mediawiki/vendor/*/.git* \ - /var/www/atlwiki/mediawiki/skins/Citizen/.git* \ - /var/www/atlwiki/mediawiki/skins/*/tests/ \ - /var/www/atlwiki/mediawiki/extensions/*/tests/ && \ - find /var/www/atlwiki/mediawiki -name "*.md" -delete && \ - find /var/www/atlwiki/mediawiki -name "*.txt" -not -path "*/i18n/*" -delete && \ - rm -f /var/www/atlwiki/mediawiki/composer.local.json /var/www/atlwiki/mediawiki/composer.lock +RUN rm -rf /var/www/wiki/mediawiki/tests/ \ + /var/www/wiki/mediawiki/docs/ \ + /var/www/wiki/mediawiki/mw-config/ \ + /var/www/wiki/mediawiki/maintenance/dev/ \ + /var/www/wiki/mediawiki/maintenance/benchmarks/ \ + /var/www/wiki/mediawiki/vendor/*/tests/ \ + /var/www/wiki/mediawiki/vendor/*/test/ \ + /var/www/wiki/mediawiki/vendor/*/.git* \ + /var/www/wiki/mediawiki/skins/Citizen/.git* \ + /var/www/wiki/mediawiki/skins/*/tests/ \ + /var/www/wiki/mediawiki/extensions/*/tests/ && \ + find /var/www/wiki/mediawiki -name "*.md" -delete && \ + find /var/www/wiki/mediawiki -name "*.txt" -not -path "*/i18n/*" -delete && \ + rm -f /var/www/wiki/mediawiki/composer.local.json /var/www/wiki/mediawiki/composer.lock # Final Stage FROM php:8.3-fpm-alpine AS final @@ -137,18 +126,22 @@ LABEL maintainer="atmois@allthingslinux.org" \ RUN --mount=type=cache,target=/var/cache/apk,sharing=locked \ set -eux && \ apk add --no-cache \ - imagemagick=7.1.2.3-r0 \ - librsvg=2.60.0-r0 \ - rsvg-convert=2.60.0-r0 \ - python3=3.12.11-r0 \ - icu-libs=76.1-r1 \ - oniguruma=6.9.10-r0 \ - libzip=1.11.4-r0 \ - libpng=1.6.47-r0 \ - libjpeg-turbo=3.1.0-r0 \ - freetype=2.13.3-r0 \ - unzip=6.0-r15 \ - lua5.1-libs=5.1.5-r13 + freetype \ + icu-libs \ + imagemagick \ + libavif \ + libjpeg-turbo \ + libpng \ + librsvg \ + libthai \ + libxml2 \ + libzip \ + lua5.1-libs \ + lz4-libs \ + oniguruma \ + python3 \ + rsvg-convert \ + unzip COPY --from=builder /usr/local/lib/php/extensions/ /usr/local/lib/php/extensions/ COPY --from=builder /usr/local/etc/php/conf.d/ /usr/local/etc/php/conf.d/ @@ -157,33 +150,35 @@ COPY --from=builder /usr/local/etc/php/conf.d/ /usr/local/etc/php/conf.d/ RUN addgroup -g 1000 -S mediawiki && \ adduser -u 1000 -S mediawiki -G mediawiki -RUN mkdir -p /var/www/atlwiki/mediawiki && \ - mkdir -p /var/www/atlwiki/cache && \ - mkdir -p /var/www/atlwiki/sitemap && \ - touch /var/www/atlwiki/sitemap/sitemap-index-atl.wiki.xml && \ - ln -s /var/www/atlwiki/sitemap/sitemap-index-atl.wiki.xml /var/www/atlwiki/sitemap.xml && \ - chown -R mediawiki:mediawiki /var/www/atlwiki && \ - chmod -R 775 /var/www/atlwiki/sitemap && \ - chmod -R 770 /var/www/atlwiki/cache +RUN mkdir -p /var/www/wiki/mediawiki && \ + mkdir -p /var/www/wiki/cache && \ + mkdir -p /var/www/wiki/sitemap && \ + touch /var/www/wiki/sitemap/sitemap-index-atl.wiki.xml && \ + ln -s /var/www/wiki/sitemap/sitemap-index-atl.wiki.xml /var/www/wiki/sitemap.xml && \ + chown -R mediawiki:mediawiki /var/www/wiki && \ + chmod -R 775 /var/www/wiki/sitemap && \ + chmod -R 770 /var/www/wiki/cache USER mediawiki -WORKDIR /var/www/atlwiki +WORKDIR /var/www/wiki -COPY --chown=mediawiki:mediawiki --from=mediawiki /var/www/atlwiki . +COPY --chown=mediawiki:mediawiki --from=mediawiki /var/www/wiki . -COPY --chown=mediawiki:mediawiki robots.txt ./robots.txt -COPY --chown=mediawiki:mediawiki .well-known ./.well-known -COPY --chown=mediawiki:mediawiki LocalSettings.php ./mediawiki/LocalSettings.php -COPY --chown=mediawiki:mediawiki configs/ ./configs/ +COPY --chown=mediawiki:mediawiki wiki/robots.txt ./robots.txt +COPY --chown=mediawiki:mediawiki wiki/.well-known ./.well-known +COPY --chown=mediawiki:mediawiki wiki/LocalSettings.php ./mediawiki/LocalSettings.php +COPY --chown=mediawiki:mediawiki wiki/configs/ ./configs/ RUN ln -s ./.well-known/security.txt ./security.txt -# Fix MWCallbackStream.php return type declaration -RUN sed -i "s/public function write( \$string ) {/public function write( \$string ): int {/" /var/www/atlwiki/mediawiki/includes/http/MWCallbackStream.php - USER root -COPY php.ini /usr/local/etc/php/conf.d/custom.ini +COPY wiki/php.ini /usr/local/etc/php/conf.d/custom.ini USER mediawiki + +# Fix MWCallbackStream.php return type declaration (TEMPORARY until Upstream Fixes it) +RUN sed -i "s/public function write( \$string ) {/public function write( \$string ): int {/" /var/www/wiki/mediawiki/includes/http/MWCallbackStream.php + +# Expose Port for FastCGI EXPOSE 9000 # Healthcheck diff --git a/Justfile b/Justfile index 1a72aba..a775488 100644 --- a/Justfile +++ b/Justfile @@ -1,94 +1,10 @@ +import 'just/docker.just' +import 'just/base.just' +import 'just/extra.just' +import 'just/init.just' +import 'just/help.just' +import 'just/opensearch.just' + # Show available recipes default: - @just --list - -# === Docker Management === - -# Start the application -start: - docker compose up -d --build - -# Stop the application -stop: - docker compose down - -# Restart the production wiki after an update (stops, pulls, rebuilds, starts) -update-prod: - docker compose down -v - sudo git pull - just copy-file production-compose.yaml.example compose.yaml - docker compose up -d --build - -# Restart the staging wiki after an update (stops, pulls, rebuilds, starts) -update-staging: - docker compose down -v - sudo git pull - just copy-file staging-compose.yaml.example compose.yaml - docker compose up -d --build - -# === Configuration Setup === - -# Setup production environment (copies prod compose and env files, installs prod systemd) -setup-prod: (copy-file "production-compose.yaml.example" "compose.yaml") env sitemap-prod - -# Setup staging environment (copies staging compose and env files, installs staging systemd) -setup-staging: (copy-file "staging-compose.yaml.example" "compose.yaml") env sitemap-staging - -# Copy environment example to .env -env: (copy-file ".example.env" ".env") - -# === System Services === - -# Setup production sitemap systemd timer -sitemap-prod: - #!/usr/bin/env bash - set -euo pipefail - - echo "Installing production systemd service files..." - sudo cp systemd/wiki-sitemap.service /etc/systemd/system/ - sudo cp systemd/wiki-sitemap.timer /etc/systemd/system/ - - echo "Reloading systemd daemon..." - sudo systemctl daemon-reload - - echo "Enabling and starting production sitemap timer..." - sudo systemctl enable wiki-sitemap.timer - sudo systemctl start wiki-sitemap.timer - - echo "Production sitemap timer setup complete!" - -# Setup staging sitemap systemd timer -sitemap-staging: - #!/usr/bin/env bash - set -euo pipefail - - echo "Installing staging systemd service files..." - sudo cp systemd/staging-wiki-sitemap.service /etc/systemd/system/ - sudo cp systemd/staging-wiki-sitemap.timer /etc/systemd/system/ - - echo "Reloading systemd daemon..." - sudo systemctl daemon-reload - - echo "Enabling and starting staging sitemap timer..." - sudo systemctl enable staging-wiki-sitemap.timer - sudo systemctl start staging-wiki-sitemap.timer - - echo "Staging sitemap timer setup complete!" - -# === Utility Functions === - -# Copy a file (DO NOT RUN MANUALLY) -copy-file src dst: - #!/usr/bin/env bash - set -euo pipefail - - SRC="{{src}}" - DST="{{dst}}" - - if [ ! -f "$SRC" ]; then - echo "Error: source '$SRC' not found." >&2 - exit 1 - fi - - cp -v "$SRC" "$DST" - echo "Successfully copied $SRC to $DST" + just help diff --git a/README.md b/README.md index 0cbb1b9..4d1e632 100755 --- a/README.md +++ b/README.md @@ -1,12 +1,227 @@ # atl.wiki Mediawiki Configs -tbc +> [!WARNING] +> Currently this is hardcoded for atl.wiki, in our next release we aim to create a base Mediawiki image that can be easily customized for other deployments seperate from our own images. +> +> **We do not recommend using this for your own deployments at this time but we welcome any feedback or contributions.** + +This repository contains Mediawiki configurations used for the deployment of [atl.wiki](https://atl.wiki). Including Docker configurations, environment settings, and other necessary files to run Mediawiki in a containerized environment with scripts to aid in the deployment and running of the application. + +# Deployment Instructions +*These are currently just a basic overview, more detailed instructions will be added with the refactor in the next release.* + +> [!NOTE] +> **This only applies if you are running a staging or production instance, local instances have these pre-configured as docker containers.** To deploy this service you will need: +> - A running and accessible [MariaDB](https://mariadb.org) server which is version 10.3 or higher, we recommend having this either as a managed service or having a dedicated server running MariaDB. +> - A running and accessible S3 server or configured provider, we recommend [Cloudflare's R2 service](https://www.cloudflare.com/en-gb/developer-platform/products/r2/). + +1. Install the dependencies on your server or local machine: + - [Docker](https://docs.docker.com/get-docker/) + - [git](https://git-scm.com/install/linux) + - [Docker Compose](https://docs.docker.com/compose/install/) + - [just](https://github.com/casey/just) + +2. Clone this repository, we recommend cloning the repository to `/opt/mediawiki` +```bash + git clone https://github.com/allthingslinux/atl-wiki mediawiki +``` + +3. Navigate to the cloned directory +```bash + cd /opt/mediawiki +``` + +4. Run the file setup scripts, switch {ENV} with local, staging or production. +```bash + just {ENV}-files +``` + +5. Fill out the `.env` file as required with the values explained below. + +6. Start the Mediawiki instance +```bash + just start +``` + +7. Initialize the Mediawiki instance +```bash + just mediawiki-init +``` + +8. Access the Mediawiki instance in your web browser at `https://{localhost:3000 | domain.tld}` + +# Environment Variables + +Variables marked with a `*` are required. The function they provide may be optional but the wiki may not operate correctly without the variables provided. For local setups **we recommend not changing the pre-defined values to ensure the containers can communicate properly**: + +| Variable | Description | Example | +|----------|------------------|---------| +| `MEDIAWIKI_MAJOR_VERSION` * | The major version number of MediaWiki you want to use | `1.44` | +| `MEDIAWIKI_VERSION` * | The full version number of MediaWiki you want to use | `1.44.2` | +| `MEDIAWIKI_BRANCH` * | The branch of MediaWiki you want to use for extensions | `REL1_44` | +| `CITIZEN_VERSION` * | The version of the Citizen skin you want to use | `3.11.0` | +| `UPGRADE_KEY` * | The key to access the upgrade page. Must be a 16-character alphanumeric string | *Generate using `openssl rand -hex 8`* | +| `SECRET_KEY` * | The key used for various security-related functions within MediaWiki. Must be a 64-character alphanumeric string | *Generate using `openssl rand -hex 32`* | +| `DB_SERVER` * | The IP address or hostname of your database server | `db.example.com` or `192.168.0.0` | +| `DB_NAME` * | The name of the table in the database used by MediaWiki | `mediawiki` | +| `DB_USER` * | The username used by MediaWiki to connect to the database | `mediawiki` | +| `DB_PASSWORD` * | The password used by MediaWiki to connect to the database | `0123456789abcdef` | +| `SMTP_HOST` | The SMTP server host address | `smtp.gmail.com` | +| `SMTP_DOMAIN` | The SMTP domain you wish to use for sending emails | `example.com` | +| `SMTP_PORT` | The SMTP server port (usually 587 for TLS or 465 for SSL) | `587` | +| `SMTP_USERNAME` | The email address to send SMTP emails from for service notifications | `noreply@example.com` | +| `SMTP_PASSWORD` | The password for the SMTP username | `0123456789abcdef` | +| `EMERGENCY_EMAIL` | The email address to send emergency notifications to | `admin@example.com` | +| `TURNSTILE_SITE_KEY` * | The site key for Cloudflare Turnstile | `0123456789abcdef` | +| `TURNSTILE_SECRET_KEY` * | The secret key for Cloudflare Turnstile | `0123456789abcdef` | +| `OPENID_CLIENT_ID` | The client ID for OpenID authentication from Okta | `0123456789abcdef` | +| `OPENID_CLIENT_SECRET` | The client secret for OpenID authentication from Okta | `0123456789abcdef` | +| `S3_ENDPOINT` * | The S3 endpoint URL | `https://s3.domain.tld` | +| `S3_ACCESS_KEY_ID` * | The S3 access key ID | `0123456789abcdef` | +| `S3_SECRET_ACCESS_KEY` * | The S3 secret access key | `0123456789abcdef` | +| `S3_BUCKET_NAME` * | The S3 bucket name | `wiki-images` | +| `S3_BUCKET_DOMAIN` * | The S3 bucket domain | `wiki-images.domain.tld` | +| `SITENAME` * | The domain the wiki will be accessible from, without the protocol | `wiki.example.com` | +| `WG_SERVER` * | The full URL the wiki will be accessible from, including the protocol | `https://wiki.example.com` | +| `DISCORD_WEBHOOK_URL` * | Discord Webhook URL | `https://discord.com/api/webhooks/0123456789/abcdef` | + +Below are the extra options for **LOCAL** setups. + +| Variable* | Description | Example | +|-----------|------------------|---------| +| `MARIADB_PASSWORD` * | The MariaDB root password. This needs to match `DB_PASSWORD` | `local-maria-password` | +| `MARIADB_NAME` * | The MariaDB database name. This needs to match `DB_NAME` | `local-maria-db` | +| `MARIADB_USER` * | The MariaDB username. This needs to match `DB_USER` | `local-maria-user` | +| `MINIO_USER` * | The MinIO username. This needs to match `S3_ACCESS_KEY_ID` | `local-minio-user` | +| `MINIO_PASSWORD` * | The MinIO password. This needs to match `S3_SECRET_ACCESS_KEY` | `local-minio-password` | + +# Notes for System Administrators + +- Please DO NOT modify your generated `compose.yaml`, it will be overridden on any update and may cause unexpected behavior. The **only two supported exceptions** is changing the NGINX external port from `3000` or if you use an external database, cache or S3 provider where you can comment out the relevant service lines, but please be aware you will need to manage those services yourself AND it will still be overridden on updates. +- If possible avoid running `docker compose` commands directly, instead use the provided `just` commands to ensure proper operation. These are designed to handle checks and ensure smooth operation of the wiki deployment. +- Any changes to files in the `wiki/` directory will require a clean restart to take effect. Use `just clean-restart` to perform this action safely. +- To fully utilise the image you will require a host OS which utilises Systemd as its init system. You can still run without systemd but certain features will not be available. + +## Project Overview + +### Directory Diagram + +```mermaid +graph LR + ComposerJson["📦 composer.json"] + ComposerLocal["📦 composer.local.json"] + DefaultConf["⚙️ default.conf"] + ExtensionsJson["🔌 extensions.json"] + InstallPy["🐍 install_extensions.py"] + LocalSettings["⚙️ LocalSettings.php"] + MediawikiConf["⚙️ mediawiki.conf"] + PhpIni["⚙️ php.ini"] + Robots["🤖 robots.txt"] + Configs["📁 configs/"] + Wiki["📁 wiki/"] + + ComposerJson --- Wiki + ComposerLocal --- Wiki + DefaultConf --- Wiki + ExtensionsJson --- Wiki + InstallPy --- Wiki + LocalSettings --- Wiki + MediawikiConf --- Wiki + PhpIni --- Wiki + Robots --- Wiki + Configs --- Wiki + + Root["📖 atl-wiki"] + + Deployment --- Root + Wiki --- Root + + Root --- Dockerfile["🐳 Dockerfile"] + Root --- Justfile["⚙️ Justfile"] + Root --- ComposeExample["📄 compose.yaml.example"] + Root --- EnvExample["📄 .env.example"] + Root --- Just["📁 just/"] + Root --- Systemd["📁 systemd/"] + + style Root fill:#e1f5ff + style Deployment fill:#fff4e1 + style Just fill:#e8f5e9 + style Systemd fill:#f3e5f5 + style Wiki fill:#fff9c4 + style Configs fill:#ffebee +``` + +### Directory Map + +| Path | Description | +|------|-------------| +| `wiki/` | MediaWiki and NGINX application configurations | +| `wiki/composer.json` | PHP dependencies for MediaWiki extensions | +| `wiki/composer.local.json` | Local PHP dependency overrides for environment setup | +| `wiki/default.conf` | Not important, just exists because of NGINX quirk | +| `wiki/extensions.json` | MediaWiki extension manifest | +| `wiki/install_extensions.py` | Python script for extension installation | +| `wiki/LocalSettings.php` | MediaWiki configuration file which loads config modules in `wiki/configs/` | +| `wiki/mediawiki.conf` | MediaWiki NGINX configurations | +| `wiki/php.ini` | PHP runtime configuration | +| `wiki/robots.txt` | Crawler instructions | +| `wiki/.well-known/security.txt` | Security reporting instructions | +| `wiki/configs/` | Numbered configuration modules (00-99) ensuring correct load order | +| `compose.yaml.example` | Docker Compose configuration template (environment specific versions available) | +| `env.example` | Environment variables template (environment specific versions available) | +| `Dockerfile` | MediaWiki container build configuration | +| `Justfile` | Main task runner entry point for Just scripts in `just/` | +| `just/` | Modular task automation recipes for operations and setup | +| `systemd/` | Automated maintenance tasks service and timer files | + +# Just Commands +Before running any `just` commands, ensure you have read the `just help` command for important information and the dangers of certain commands. + +| Command | Description | +|---------|-------------| +| `clean-restart` | Restart all wiki containers and removes cache volumes | +| `clean-stop` | Stop all wiki containers and removes cache volumes | +| `db-status` | Verify database connectivity for the wiki | +| `default` | Show available recipes | +| `health` | Check health status of all wiki containers | +| `help` | Display comprehensive help for all available commands | +| `local-files` | Setup local development environment files | +| `mediawiki-init` | Initialize MediaWiki database and create admin user | +| `mediawiki-schema-update` | Update MediaWiki database schema to latest version | +| `production-files` | Setup production environment files and configure sitemap service | +| `restart` | Restart all wiki containers | +| `sitemap-production` | Setup production sitemap generation as a systemd timer service | +| `sitemap-staging` | Setup staging sitemap generation as a systemd timer service | +| `staging-files` | Setup staging environment files and configure sitemap service | +| `start` | Start all wiki containers | +| `status` | Display comprehensive status of wiki deployment including containers, health, and database | +| `stop` | Stop all running wiki containers | +| `update` | Pull latest code, updates compose.yaml from template, and restarts wiki containers | # License -Copyright 2025 Atmois +Copyright 2025 All Things Linux and Contributors + +Primary maintainer: Atmois Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. +you may not use this project except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + +# Upstream Dependencies + +We gratefully acknowledge the use of the following projects which are incorporated in or used by this project: + +| Project | Link | License | +|---------|------|---------| +| Docker | [https://www.docker.com](https://www.docker.com) | Apache-2.0 | +| Docker Compose | [https://github.com/docker/compose](https://github.com/docker/compose) | Apache-2.0 | +| Hadolint | [https://github.com/hadolint/hadolint](https://github.com/hadolint/hadolint) | GPL-3.0 | +| just | [https://github.com/casey/just](https://github.com/casey/just) | CC0-1.0 | +| MediaWiki | [https://www.mediawiki.org](https://www.mediawiki.org) | GPL-2.0-or-later | +| PHP | [https://www.php.net](https://www.php.net) | PHP-3.01 | +| PHP_CodeSniffer | [https://github.com/PHPCSStandards/PHP_CodeSniffer/](https://github.com/PHPCSStandards/PHP_CodeSniffer/) | BSD3-Clause | +| phpdotenv | [https://github.com/vlucas/phpdotenv](https://github.com/vlucas/phpdotenv) | BSD-3-Clause | +| Python | [https://www.python.org](https://www.python.org) | PSF-2.0 | +| Renovate | [https://www.mend.io/renovate](https://www.mend.io/renovate) | AGPL-3.0 | diff --git a/compose.local.yaml.example b/compose.local.yaml.example new file mode 100644 index 0000000..10cbad1 --- /dev/null +++ b/compose.local.yaml.example @@ -0,0 +1,161 @@ +name: local-wiki # DO NOT CHANGE THIS, THIS WILL BREAK JUSTSCRIPTS AND OTHER AUTOMATIONS + +services: + mediawiki: + build: + context: . + dockerfile: Dockerfile + args: + MEDIAWIKI_MAJOR_VERSION: ${MEDIAWIKI_MAJOR_VERSION} + MEDIAWIKI_VERSION: ${MEDIAWIKI_VERSION} + MEDIAWIKI_BRANCH: ${MEDIAWIKI_BRANCH} + CITIZEN_VERSION: ${CITIZEN_VERSION} + container_name: local-wiki-mediawiki + depends_on: + valkey: + condition: service_started + mariadb: + condition: service_healthy + volumes: + - wiki-webroot:/var/www/wiki + - .env:/var/www/wiki/.env:ro + networks: + - wiki-network + restart: unless-stopped + healthcheck: + test: [ "CMD", "php-fpm", "-t" ] + interval: 30s + timeout: 10s + start_period: 60s + retries: 3 + user: "1000:1000" + expose: + - '9000' + + nginx: + image: nginx:stable-alpine + container_name: local-wiki-nginx + depends_on: + mediawiki: + condition: service_healthy + environment: + NGINX_SERVER_NAME: ${SITENAME} + NGINX_ENVSUBST_OUTPUT_DIR: /etc/nginx/conf.d + volumes: + - wiki-webroot:/var/www/wiki:ro + - ./wiki/mediawiki.conf:/etc/nginx/templates/mediawiki.conf.template:ro + - ./wiki/default.conf:/etc/nginx/conf.d/default.conf:ro + ports: + - '3000:80' + networks: + - wiki-network + restart: unless-stopped + healthcheck: + test: [ "CMD", "curl", "-f", "http://localhost/" ] + interval: 30s + timeout: 10s + start_period: 30s + retries: 3 + + valkey: + image: valkey/valkey:alpine + container_name: local-wiki-valkey + volumes: + - valkey-data:/data + networks: + - wiki-network + command: valkey-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru + restart: unless-stopped + healthcheck: + test: [ "CMD", "valkey-cli", "ping" ] + interval: 30s + timeout: 10s + start_period: 30s + retries: 3 + expose: + - '6379' + + mariadb: + image: mariadb:latest + container_name: local-wiki-mariadb + environment: + MARIADB_ROOT_PASSWORD: ${MARIADB_PASSWORD} + MARIADB_DATABASE: ${MARIADB_NAME} + MARIADB_USER: ${MARIADB_USER} + MARIADB_PASSWORD: ${MARIADB_PASSWORD} + volumes: + - ./database:/var/lib/mysql + networks: + - wiki-network + restart: unless-stopped + healthcheck: + test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] + interval: 30s + timeout: 10s + start_period: 60s + retries: 3 + expose: + - '3306' + + minio: + image: minio/minio:latest + container_name: local-wiki-minio + environment: + MINIO_ROOT_USER: ${MINIO_USER} + MINIO_ROOT_PASSWORD: ${MINIO_PASSWORD} + volumes: + - ./images:/data + networks: + - wiki-network + command: server /data + restart: unless-stopped + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"] + interval: 30s + timeout: 10s + start_period: 30s + retries: 3 + expose: + - '9000' + + opensearch: + image: opensearchproject/opensearch:1.3.20 + container_name: local-wiki-opensearch + environment: + - discovery.type=single-node + - cluster.name=mediwiki-cluster + - node.name=mediwiki-node + - bootstrap.memory_lock=true + - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m" + - plugins.security.disabled=true + ulimits: + memlock: + soft: -1 + hard: -1 + nofile: + soft: 65536 + hard: 65536 + volumes: + - opensearch-data:/usr/share/opensearch/data + networks: + - wiki-network + healthcheck: + test: ["CMD-SHELL", "curl -f http://localhost:9200/_cluster/health || exit 1"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s + expose: + - '9200' + +networks: + wiki-network: + driver: bridge + +volumes: + wiki-webroot: + driver: local + valkey-data: + driver: local + opensearch-data: + driver: local diff --git a/compose.production.yaml.example b/compose.production.yaml.example new file mode 100644 index 0000000..41a2941 --- /dev/null +++ b/compose.production.yaml.example @@ -0,0 +1,115 @@ +name: production-wiki # DO NOT CHANGE THIS, THIS WILL BREAK JUSTSCRIPTS AND OTHER AUTOMATIONS + +services: + mediawiki: + build: + context: . + dockerfile: Dockerfile + args: + MEDIAWIKI_MAJOR_VERSION: ${MEDIAWIKI_MAJOR_VERSION} + MEDIAWIKI_VERSION: ${MEDIAWIKI_VERSION} + MEDIAWIKI_BRANCH: ${MEDIAWIKI_BRANCH} + CITIZEN_VERSION: ${CITIZEN_VERSION} + container_name: wiki-mediawiki + depends_on: + valkey: + condition: service_started + volumes: + - wiki-webroot:/var/www/wiki + - .env:/var/www/wiki/.env:ro + networks: + - wiki-network + restart: unless-stopped + healthcheck: + test: [ "CMD", "php-fpm", "-t" ] + interval: 30s + timeout: 10s + start_period: 60s + retries: 3 + user: "1000:1000" + expose: + - '9000' + + nginx: + image: nginx:stable-alpine + container_name: wiki-nginx + depends_on: + mediawiki: + condition: service_healthy + environment: + NGINX_SERVER_NAME: ${SITENAME} + volumes: + - wiki-webroot:/var/www/wiki:ro + - ./wiki/mediawiki.conf:/etc/nginx/templates/mediawiki.conf.template:ro + - ./wiki/default.conf:/etc/nginx/conf.d/default.conf:ro + ports: + - '3000:80' + networks: + - wiki-network + restart: unless-stopped + healthcheck: + test: [ "CMD", "curl", "-f", "http://localhost/" ] + interval: 30s + timeout: 10s + start_period: 30s + retries: 3 + + valkey: + image: valkey/valkey:alpine + container_name: wiki-valkey + volumes: + - valkey-data:/data + networks: + - wiki-network + command: valkey-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru + restart: unless-stopped + healthcheck: + test: [ "CMD", "valkey-cli", "ping" ] + interval: 30s + timeout: 10s + start_period: 30s + retries: 3 + expose: + - '6379' + + opensearch: + image: opensearchproject/opensearch:1.3.20 + container_name: wiki-opensearch + environment: + - discovery.type=single-node + - cluster.name=mediwiki-cluster + - node.name=mediwiki-node + - bootstrap.memory_lock=true + - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m" + - plugins.security.disabled=true + ulimits: + memlock: + soft: -1 + hard: -1 + nofile: + soft: 65536 + hard: 65536 + volumes: + - opensearch-data:/usr/share/opensearch/data + networks: + - wiki-network + healthcheck: + test: ["CMD-SHELL", "curl -f http://localhost:9200/_cluster/health || exit 1"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s + expose: + - '9200' + +networks: + wiki-network: + driver: bridge + +volumes: + wiki-webroot: + driver: local + valkey-data: + driver: local + opensearch-data: + driver: local diff --git a/compose.staging.yaml.example b/compose.staging.yaml.example new file mode 100644 index 0000000..f0c065b --- /dev/null +++ b/compose.staging.yaml.example @@ -0,0 +1,115 @@ +name: staging-wiki # DO NOT CHANGE THIS, THIS WILL BREAK JUSTSCRIPTS AND OTHER AUTOMATIONS + +services: + mediawiki: + build: + context: . + dockerfile: Dockerfile + args: + MEDIAWIKI_MAJOR_VERSION: ${MEDIAWIKI_MAJOR_VERSION} + MEDIAWIKI_VERSION: ${MEDIAWIKI_VERSION} + MEDIAWIKI_BRANCH: ${MEDIAWIKI_BRANCH} + CITIZEN_VERSION: ${CITIZEN_VERSION} + container_name: staging-wiki-mediawiki + depends_on: + valkey: + condition: service_started + volumes: + - wiki-webroot:/var/www/wiki + - .env:/var/www/wiki/.env:ro + networks: + - wiki-network + restart: unless-stopped + healthcheck: + test: [ "CMD", "php-fpm", "-t" ] + interval: 30s + timeout: 10s + start_period: 60s + retries: 3 + user: "1000:1000" + expose: + - '9000' + + nginx: + image: nginx:stable-alpine + container_name: staging-wiki-nginx + depends_on: + mediawiki: + condition: service_healthy + environment: + NGINX_SERVER_NAME: ${SITENAME} + volumes: + - wiki-webroot:/var/www/wiki:ro + - ./wiki/mediawiki.conf:/etc/nginx/templates/mediawiki.conf.template:ro + - ./wiki/default.conf:/etc/nginx/conf.d/default.conf:ro + ports: + - '3001:80' + networks: + - wiki-network + restart: unless-stopped + healthcheck: + test: [ "CMD", "curl", "-f", "http://localhost/" ] + interval: 30s + timeout: 10s + start_period: 30s + retries: 3 + + valkey: + image: valkey/valkey:alpine + container_name: staging-wiki-valkey + volumes: + - valkey-data:/data + networks: + - wiki-network + command: valkey-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru + restart: unless-stopped + healthcheck: + test: [ "CMD", "valkey-cli", "ping" ] + interval: 30s + timeout: 10s + start_period: 30s + retries: 3 + expose: + - '6379' + + opensearch: + image: opensearchproject/opensearch:1.3.20 + container_name: staging-wiki-opensearch + environment: + - discovery.type=single-node + - cluster.name=mediwiki-cluster + - node.name=mediwiki-node + - bootstrap.memory_lock=true + - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m" + - plugins.security.disabled=true + ulimits: + memlock: + soft: -1 + hard: -1 + nofile: + soft: 65536 + hard: 65536 + volumes: + - opensearch-data:/usr/share/opensearch/data + networks: + - wiki-network + healthcheck: + test: ["CMD-SHELL", "curl -f http://localhost:9200/_cluster/health || exit 1"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s + expose: + - '9200' + +networks: + wiki-network: + driver: bridge + +volumes: + wiki-webroot: + driver: local + valkey-data: + driver: local + opensearch-data: + driver: local diff --git a/configs/51-Extensions_Config.php b/configs/51-Extensions_Config.php deleted file mode 100755 index 70a71a2..0000000 --- a/configs/51-Extensions_Config.php +++ /dev/null @@ -1,126 +0,0 @@ - - * @license https://www.apache.org/licenses/LICENSE-2.0 Apache-2.0 - * @link https://atl.wiki - */ - -//#################################################################// TextExtracts -// https://www.mediawiki.org/wiki/Extension:TextExtracts - -$wgExtractsRemoveClasses = [ - 'ul.gallery', - 'gallery', - 'code', - '.metadata' -]; - -//######################################################// VisualEditor -// https://www.mediawiki.org/wiki/Extension:VisualEditor - -$wgVisualEditorAvailableNamespaces = [ - 'Guides' => true, - 'Project' => true -]; -$wgVisualEditorEnableDiffPageBetaFeature = true; -$wgVisualEditorUseSingleEditTab = true; -$wgDefaultUserOptions['visualeditor-enable'] = 1; -$wgDefaultUserOptions['visualeditor-editor'] = "visualeditor"; - -//######################################################// Interwiki -// https://www.mediawiki.org/wiki/Extension:Interwiki - -// https://www.mediawiki.org/wiki/Manual:$wgUserrightsInterwikiDelimiter -$wgUserrightsInterwikiDelimiter = '#'; - -//######################################################// ConfirmEdit -// https://www.mediawiki.org/wiki/Extension:ConfirmEdit - -$wgCaptchaClass = MediaWiki\Extension\ConfirmEdit\Turnstile\Turnstile::class; -$wgTurnstileSiteKey= $_ENV['TURNSTILE_SITE_KEY']; -$wgTurnstileSecretKey= $_ENV['TURNSTILE_SECRET_KEY']; - -//######################################################// AWS -// https://www.mediawiki.org/wiki/Extension:AWS - -$wgAWSRegion = 'auto'; -$wgAWSBucketName = $_ENV['AWS_BUCKET_NAME']; -$wgAWSBucketDomain = $_ENV['AWS_BUCKET_DOMAIN']; -$wgAWSCredentials = [ - 'key' => $_ENV['ACCESS_KEY_ID'], - 'secret' => $_ENV['SECRET_ACCESS_KEY'], -]; -$accountID = '53d9d9e6ebc5a0dddeeb59477445ea0c'; -$wgFileBackends['s3'] = [ - 'class' => 'AmazonS3FileBackend', - 'bucket' => $wgAWSBucketName, - 'region' => $wgAWSRegion, - 'endpoint' => 'https://'.$accountID.'.r2.cloudflarestorage.com', - 'use_path_style_endpoint' => true, -]; - -//######################################################// Discord -// https://www.mediawiki.org/wiki/Extension:Discord - -$wgDiscordWebhookURL = [ $_ENV['DISCORD_WEBHOOK_URL'] ]; -$wgDiscordUseEmojis = true; -$wgDiscordDisabledHooks = [ - 'BlockIpComplete', - 'UnblockUserComplete', - 'FileDeleteComplete', - 'FileUndeleteComplete', - 'ArticleRevisionVisibilitySet', -]; - -//######################################################// CheckUser -// https://www.mediawiki.org/wiki/Extension:CheckUser - -$wgCheckUserLogSuccessfulBotLogins = false; -$wgCheckUserLogLogins = true; - -//######################################################// PluggableAuth -// https://www.mediawiki.org/wiki/Extension:PluggableAuth - -$wgPluggableAuth_Config["Staff Login via All Things Linux (SSO)"] = [ - "plugin" => "OpenIDConnect", - "data" => [ - "providerURL" => "https://sso.allthingslinux.org", - "clientID" => $_ENV['OPENID_CLIENT_ID'], - "clientsecret" => $_ENV['OPENID_CLIENT_SECRET'], - ] -]; -$wgPluggableAuth_EnableLocalLogin = true; -$wgPluggableAuth_EnableLocalProperties = true; - -//######################################################// OpenID_Connect -// https://www.mediawiki.org/wiki/Extension:OpenID_Connect - -$wgOpenIDConnect_MigrateUsersByEmail = true; -$wgOpenIDConnect_UseRealNameAsUserName = true; - -//######################################################// Description2 -// https://www.mediawiki.org/wiki/Extension:Description2 - -$wgEnableMetaDescriptionFunctions = true; - -//######################################################// CodeMirror -// https://www.mediawiki.org/wiki/Extension:CodeMirror - -$wgDefaultUserOptions['usecodemirror'] = true; - -//######################################################// Drafts -// https://www.mediawiki.org/wiki/Extension:Drafts - -$egDraftsAutoSaveInputBased = true; -$egDraftsAutoSaveWait = 15; - -//######################################################// Scribunto -// https://www.mediawiki.org/wiki/Extension:Scribunto - -$wgScribuntoDefaultEngine = 'luasandbox'; diff --git a/just/base.just b/just/base.just new file mode 100644 index 0000000..c7202d1 --- /dev/null +++ b/just/base.just @@ -0,0 +1,112 @@ +# Verify that a file exists, exit with error if not found +_verify-file file: + #!/usr/bin/env bash + set -euo pipefail + JUSTFILE_DIR="{{justfile_directory()}}" + FILE_PATH="$JUSTFILE_DIR/{{file}}" + + echo "Verifying file: {{file}}" + if [ ! -f "$FILE_PATH" ]; then + echo -e "\n\033[1;37m\033[41m ERROR \033[0m \033[1;31mFile '{{file}}' not found\033[0m\n" + exit 1 + fi + +# Detect the environment (local/staging/production) from compose.yaml name field +_detect-env: + #!/usr/bin/env bash + set -euo pipefail + JUSTFILE_DIR="{{justfile_directory()}}" + just _verify-file compose.yaml >/dev/null 2>&1 + + # Extract name field from compose.yaml + name=$(grep -m 1 "^name:" "$JUSTFILE_DIR/compose.yaml" | cut -d':' -f2 | tr -d ' ') + + # Match name pattern to determine environment + if [[ "$name" == *"local-wiki"* ]]; then + echo "local" + elif [[ "$name" == *"staging-wiki"* ]]; then + echo "staging" + elif [[ "$name" == *"production-wiki"* ]]; then + echo "production" + else + echo -e "\n\033[1;37m\033[41m ERROR \033[0m \033[1;31mCould not detect environment from compose.yaml\033[0m\n" + exit 1 + fi + +# Check if containers for the detected environment are running +_check-containers: + #!/usr/bin/env bash + set -euo pipefail + ENV=$(just _detect-env) + echo "Environment detected: $ENV" + + # Count running containers matching the environment prefix + echo "Checking for running containers..." + CONTAINER_COUNT=$(docker ps --filter "name=${ENV}-wiki-" --format "{{{{.Names}}}}" | wc -l) + if [ "$CONTAINER_COUNT" -eq 0 ]; then + echo -e "\n\033[1;37m\033[41m ERROR \033[0m \033[1;31mNo running containers found for '$ENV'. Please start the services first.\033[0m\n" + exit 1 + fi + echo "Found $CONTAINER_COUNT running container(s)" + +# Copy a file from source to destination with verification +_copy-file src dst: + #!/usr/bin/env bash + set -euo pipefail + + SRC="{{src}}" + DST="{{dst}}" + just _destructive-warning "You are about to overwrite {{dst}} which may overwrite existing data in that file." + + echo "Copying file: $SRC → $DST" + if [ ! -f "$SRC" ]; then + echo -e "\n\033[1;37m\033[41m ERROR \033[0m \033[1;31mSource '$SRC' not found\033[0m\n" >&2 + exit 1 + fi + + cp -v "$SRC" "$DST" + echo "Successfully copied $SRC to $DST" + +# Check if the script is running with sudo/root privileges +_check-sudo: + #!/usr/bin/env bash + set -euo pipefail + + if [ "$EUID" -ne 0 ]; then + echo -e "\n\033[1;37m\033[41m ERROR \033[0m \033[1;31mThis script must be run with 'sudo'.\033[0m\n" + exit 1 + fi + +# Check if current directory is owned by root and require sudo if so +_check-sudo-dir: + #!/usr/bin/env bash + set -euo pipefail + + # Get directory owner + OWNER=$(stat -c '%U' .) + + if [ "$OWNER" = "root" ] && [ "$EUID" -ne 0 ]; then + echo -e "\n\033[1;37m\033[41m ERROR \033[0m \033[1;31mCurrent directory is owned by root. Please run with 'sudo'.\033[0m\n" + exit 1 + fi + +# Display a warning for destructive actions and prompt for confirmation +_destructive-warning reason="": + #!/usr/bin/env bash + set -euo pipefail + + echo -e "\n\033[1;37m\033[41m WARNING \033[0m \033[1;33mThis action is destructive. Proceed with caution.\033[0m" + + if [ -n "{{reason}}" ]; then + echo -e " \033[3;38;5;208m{{reason}}\033[0m" + fi + + echo -e "\033[1;33mPlease check the help command or documentation for this script before proceeding to understand the consequences.\033[0m\n" + read -p "Do you want to continue? (y/n): " -r REPLY + + # Normalize to lowercase for comparison + REPLY_LOWER=$(echo "$REPLY" | tr '[:upper:]' '[:lower:]') + if [[ ! "$REPLY_LOWER" =~ ^(y|yes)$ ]]; then + echo "Aborting." + exit 1 + fi diff --git a/just/docker.just b/just/docker.just new file mode 100644 index 0000000..fc587c3 --- /dev/null +++ b/just/docker.just @@ -0,0 +1,73 @@ +# Start all wiki containers +start: + #!/usr/bin/env bash + set -euo pipefail + JUSTFILE_DIR="{{justfile_directory()}}" + just _verify-file compose.yaml + + cd "$JUSTFILE_DIR" && docker compose up -d --build + +# Stop all running wiki containers +stop: + #!/usr/bin/env bash + set -euo pipefail + JUSTFILE_DIR="{{justfile_directory()}}" + just _verify-file compose.yaml + just _check-containers + + cd "$JUSTFILE_DIR" && docker compose down + +# Restart all wiki containers +restart: + #!/usr/bin/env bash + set -euo pipefail + just _verify-file compose.yaml + just _check-containers + + echo "Stopping wiki containers..." + just stop + echo "Starting wiki containers..." + just start + +# Restart all wiki containers and removes cache volumes +clean-restart: + #!/usr/bin/env bash + set -euo pipefail + just _verify-file compose.yaml + just _check-containers + + echo "Stopping wiki containers and removing cache volumes..." + just clean-stop + echo "Starting wiki containers..." + just start + +# Stop all wiki containers and removes cache volumes +clean-stop: + #!/usr/bin/env bash + set -euo pipefail + JUSTFILE_DIR="{{justfile_directory()}}" + just _verify-file compose.yaml + just _check-containers + + cd "$JUSTFILE_DIR" && docker compose down -v + +# Pull latest code, updates compose.yaml from template, and restarts wiki containers +update: + #!/usr/bin/env bash + set -euo pipefail + just _destructive-warning "This will pull the latest code and replace your compose.yaml, check the changelog before proceeding." + just _check-sudo-dir + just _verify-file compose.yaml + just _check-containers + ENV=$(just _detect-env) + + # Stop wiki containers and pull updates + echo "Stopping wiki containers..." + just clean-stop + echo "Pulling latest code from git..." + git pull + # Copy environment-specific compose template + echo "Updating compose.yaml for environment: $ENV" + just _copy-file compose.${ENV}.yaml.example compose.yaml + echo "Restarting wiki containers..." + just start diff --git a/just/extra.just b/just/extra.just new file mode 100644 index 0000000..e935699 --- /dev/null +++ b/just/extra.just @@ -0,0 +1,142 @@ +# === Status === + +# Display comprehensive status of wiki deployment including containers, health, and database +status: + #!/usr/bin/env bash + set -euo pipefail + + echo "Verifying compose.yaml..." + just _verify-file compose.yaml + echo "compose.yaml exists." + + echo "Detecting environment..." + ENV=$(just _detect-env) + echo "Environment: $ENV" + + echo "Verifying containers online..." + just _check-containers + echo "Containers are running" + + echo "Container Status:" + docker compose ps + + echo "Verifying container health..." + just health + + echo "Verifying database connectivity..." + just db-status + +# Check health status of all wiki containers +health: + #!/usr/bin/env bash + set -euo pipefail + just _verify-file compose.yaml + just _check-containers + + echo "Checking container health..." + + # Find containers with unhealthy status + UNHEALTHY_CONTAINERS=$(docker compose ps --format "{{{{.Name}}}}\t{{{{.Status}}}}" | grep -i "unhealthy") + + if [ -n "$UNHEALTHY_CONTAINERS" ]; then + echo -e "\n\033[1;37m\033[41m ERROR \033[0m \033[1;31mUnhealthy containers detected:\033[0m\n" + echo "$UNHEALTHY_CONTAINERS" + exit 1 + fi + + # Display healthy containers + CONTAINERS_WITH_HEALTH=$(docker compose ps --format "{{{{.Name}}}}\t{{{{.Status}}}}" | grep "health") + + if [ -n "$CONTAINERS_WITH_HEALTH" ]; then + echo "All containers with health checks are healthy:" + echo "$CONTAINERS_WITH_HEALTH" + fi + +# Verify database connectivity for the wiki +db-status: + #!/usr/bin/env bash + set -euo pipefail + just _verify-file compose.yaml + just _check-containers + ENV=$(just _detect-env) + + echo "Checking database connectivity for environment: $ENV" + + # Local environment uses containerized MariaDB + if [ "$ENV" = "local" ]; then + if ! docker ps --format '{{{{.Names}}}}' | grep -q "^local-wiki-mariadb$"; then + echo -e "\n\033[1;37m\033[41m ERROR \033[0m \033[1;31mDatabase container 'local-wiki-mariadb' does not exist or is not running.\033[0m\n" + exit 1 + fi + + if docker compose exec mediawiki nc -zv local-wiki-mariadb 3306 2>&1; then + echo "Database container is reachable from mediawiki" + else + echo -e "\n\033[1;37m\033[41m ERROR \033[0m \033[1;31mFailed to reach database container from mediawiki.\033[0m\n" + exit 1 + fi + else + # Staging/production use external database from .env + DB_SERVER=$(grep "^DB_SERVER=" .env | cut -d'=' -f2 | tr -d '"') + + if nc -zv "$DB_SERVER" 3306 2>&1 | grep -q "succeeded"; then + echo "Database server is reachable at $DB_SERVER:3306" + else + echo -e "\n\033[1;37m\033[41m ERROR \033[0m \033[1;31mDatabase server is unreachable at $DB_SERVER:3306.\033[0m\n" + exit 1 + fi + fi + +# === System Services === + +# Setup production sitemap generation as a systemd timer service +sitemap-production: + #!/usr/bin/env bash + set -euo pipefail + just _check-sudo + + echo "Installing production systemd service files..." + sudo cp systemd/wiki-sitemap.service /etc/systemd/system/ + sudo cp systemd/wiki-sitemap.timer /etc/systemd/system/ + + echo "Reloading systemd daemon..." + sudo systemctl daemon-reload + + echo "Enabling and starting production sitemap timer..." + sudo systemctl enable wiki-sitemap.timer + sudo systemctl start wiki-sitemap.timer + + echo "Production sitemap timer setup complete!" + +# Setup staging sitemap generation as a systemd timer service +sitemap-staging: + #!/usr/bin/env bash + set -euo pipefail + just _check-sudo + + echo "Installing staging systemd service files..." + sudo cp systemd/staging-wiki-sitemap.service /etc/systemd/system/ + sudo cp systemd/staging-wiki-sitemap.timer /etc/systemd/system/ + + echo "Reloading systemd daemon..." + sudo systemctl daemon-reload + + echo "Enabling and starting staging sitemap timer..." + sudo systemctl enable staging-wiki-sitemap.timer + sudo systemctl start staging-wiki-sitemap.timer + + echo "Staging sitemap timer setup complete!" + +# Update MediaWiki database schema to latest version +mediawiki-schema-update: + #!/usr/bin/env bash + set -euo pipefail + just _destructive-warning "This will update the MediaWiki database schema to the latest version. Ensure you have a backup before proceeding." + just _verify-file .env + just _verify-file compose.yaml + just _check-containers + ENV=$(just _detect-env) + + # Run database schema updates + docker compose exec mediawiki php /var/www/wiki/mediawiki/maintenance/update.php --quick + echo "MediaWiki database schema update complete!" diff --git a/just/help.just b/just/help.just new file mode 100644 index 0000000..af139f6 --- /dev/null +++ b/just/help.just @@ -0,0 +1,49 @@ +# === Help & Documentation === + +# Display comprehensive help for all available commands +help: + @echo "=== Setup & Initialization ===" + @echo " production-files Setup production environment files (.env, compose.yaml)" + @echo -e " \033[41mDESTRUCTIVE\033[0m: Overwrites existing compose.yaml and .env files" + @echo "" + @echo " staging-files Setup staging environment files (.env, compose.yaml)" + @echo -e " \033[41mDESTRUCTIVE\033[0m: Overwrites existing compose.yaml and .env files" + @echo "" + @echo " local-files Setup local development environment files" + @echo -e " \033[41mDESTRUCTIVE\033[0m: Overwrites existing compose.yaml and .env files" + @echo "" + @echo " mediawiki-init Initialize MediaWiki database and create admin user" + @echo -e " \033[41mDESTRUCTIVE\033[0m: Resets MediaWiki and database, only use on fresh installs" + @echo "" + @echo "=== Container Management ===" + @echo " start Start all wiki containers with docker compose" + @echo "" + @echo " stop Stop all running wiki containers gracefully" + @echo "" + @echo " restart Stop and restart all wiki containers" + @echo "" + @echo " clean-restart Restart containers and remove cache volumes" + @echo "" + @echo " clean-stop Stop containers and remove all cache volumes" + @echo "" + @echo " update Pull latest code, update compose.yaml, and restart containers" + @echo -e " \033[41mDESTRUCTIVE\033[0m: Overwrites compose.yaml and updates containers, check changelog before running" + @echo "" + @echo "=== Status & Monitoring ===" + @echo " status Display comprehensive deployment status (containers, health, database)" + @echo "" + @echo " health Check health status of all wiki containers" + @echo "" + @echo " db-status Verify database connectivity for the wiki" + @echo "" + @echo "=== System Services ===" + @echo " sitemap-production Setup production sitemap generation as systemd timer" + @echo -e " \033[43m\033[30mPrivileged\033[0m: Installs systemd service files" + @echo "" + @echo " sitemap-staging Setup staging sitemap generation as systemd timer" + @echo -e " \033[43m\033[30mPrivileged\033[0m: Installs systemd service files" + @echo "" + @echo " mediawiki-schema-update Update MediaWiki database schema to latest version" + @echo -e " \033[41mDESTRUCTIVE\033[0m: Modifies database schema, backup recommended" + @echo "" + @echo "For detailed information about a specific command, refer to documentation." diff --git a/just/init.just b/just/init.just new file mode 100644 index 0000000..f793c0f --- /dev/null +++ b/just/init.just @@ -0,0 +1,74 @@ +# === Setup Environment Scripts === + +# Setup production environment files and configure sitemap service +production-files: + #!/usr/bin/env bash + set -euo pipefail + just _destructive-warning "This will override your compose.yaml and .env files if they exist. Please back them up if needed." + + echo "Setting up production environment files..." + just _copy-file compose.production.yaml.example compose.yaml + just _copy-file .env.example .env + just sitemap-production + echo "Created .env from .env.example and compose.yaml from compose.production.yaml.example. Please review and modify as needed." + +# Setup staging environment files and configure sitemap service +staging-files: + #!/usr/bin/env bash + set -euo pipefail + just _destructive-warning "This will override your compose.yaml and .env files if they exist. Please back them up if needed." + + echo "Setting up staging environment files..." + just _copy-file compose.staging.yaml.example compose.yaml + just _copy-file .env.example .env + just sitemap-staging + echo "Created .env from .env.example and compose.yaml from compose.staging.yaml.example. Please review and modify as needed." + +# Setup local development environment files +local-files: + #!/usr/bin/env bash + set -euo pipefail + just _destructive-warning "This will override your compose.yaml and .env files if they exist. Please back them up if needed." + + echo "Setting up local development environment files..." + just _copy-file compose.local.yaml.example compose.yaml + just _copy-file .env.local.example .env + echo "Created .env from .env.local.example and compose.yaml from compose.local.yaml.example. Please review and modify as needed." + +# Initialize MediaWiki database and create admin user +mediawiki-init: + #!/usr/bin/env bash + set -euo pipefail + just _destructive-warning "This will reset Mediawiki and your database if already initialized. Only run this on new installs of Mediawiki." + just _verify-file .env + just _verify-file compose.yaml + just _check-containers + ENV=$(just _detect-env) + + # Extract database credentials from .env + DB_USER=$(grep "^DB_USER=" .env | cut -d'=' -f2 | tr -d '"') + DB_PASSWORD=$(grep "^DB_PASSWORD=" .env | cut -d'=' -f2 | tr -d '"') + if [ -z "$DB_USER" ] || [ -z "$DB_PASSWORD" ]; then + echo -e "\n\033[1;37m\033[41m ERROR \033[0m \033[1;31mRequired variables not found in .env (DB_USER, DB_PASSWORD)\033[0m\n" + exit 1 + fi + + # Generate secure random admin password + ADMIN_PASSWORD=$(openssl rand -base64 32 | tr -d "=+/" | cut -c1-20) + echo "" + echo "==========================================" + echo "IMPORTANT: Save this admin password now! It won't be shown again." + echo "Admin password: $ADMIN_PASSWORD" + echo "This is to login to MediaWiki as the initial admin." + echo "==========================================" + echo "" + + # Run MediaWiki pre-configured installation + docker compose exec mediawiki php /var/www/wiki/mediawiki/maintenance/run.php installPreConfigured \ + --dbuser="${DB_USER}" --dbpass="${DB_PASSWORD}" + + just clean-restart + + # Run database schema updates + docker compose exec mediawiki php /var/www/wiki/mediawiki/maintenance/update.php --quick + diff --git a/just/opensearch.just b/just/opensearch.just new file mode 100644 index 0000000..8a5391a --- /dev/null +++ b/just/opensearch.just @@ -0,0 +1,36 @@ +# Update OpenSearch index configuration in MediaWiki +opensearch-update-config: + #!/usr/bin/env bash + set -euo pipefail + just _verify-file compose.yaml + just _check-containers + + docker compose exec mediawiki php extensions/CirrusSearch/maintenance/UpdateSearchIndexConfig.php + +# Reindex OpenSearch in MediaWiki +opensearch-reindex: + #!/usr/bin/env bash + set -euo pipefail + just _verify-file compose.yaml + just _check-containers + + docker compose exec mediawiki php extensions/CirrusSearch/maintenance/ForceSearchIndex.php --skipLinks --indexOnSkip + docker compose exec mediawiki php extensions/CirrusSearch/maintenance/ForceSearchIndex.php --skipParse + +# Run pending jobs in MediaWiki (including OpenSearch indexing jobs) +opensearch-run-jobs: + #!/usr/bin/env bash + set -euo pipefail + just _verify-file compose.yaml + just _check-containers + + docker compose exec mediawiki php maintenance/runJobs.php + +# Update OpenSearch suggester index in MediaWiki +opensearch-suggester: + #!/usr/bin/env bash + set -euo pipefail + just _verify-file compose.yaml + just _check-containers + + docker compose exec mediawiki php extensions/CirrusSearch/maintenance/UpdateSuggesterIndex.php diff --git a/phpcs.xml b/phpcs.xml new file mode 100644 index 0000000..bba1240 --- /dev/null +++ b/phpcs.xml @@ -0,0 +1,26 @@ + + + + + PSR-12 Coding Standard + + + + + + + + + + + wiki + + + */vendor/* + */cache/* + + + + + + diff --git a/production-compose.yaml.example b/production-compose.yaml.example deleted file mode 100644 index a8ea8e0..0000000 --- a/production-compose.yaml.example +++ /dev/null @@ -1,80 +0,0 @@ -name: prod-wiki - -services: - mediawiki: - build: - context: . - dockerfile: Dockerfile - args: - MEDIAWIKI_MAJOR_VERSION: ${MEDIAWIKI_MAJOR_VERSION} - MEDIAWIKI_VERSION: ${MEDIAWIKI_VERSION} - MEDIAWIKI_BRANCH: ${MEDIAWIKI_BRANCH} - CITIZEN_VERSION: ${CITIZEN_VERSION} - container_name: atlwiki-mediawiki - depends_on: - redis: - condition: service_started - volumes: - - wiki-webroot:/var/www/atlwiki - - .env:/var/www/atlwiki/.env:ro - networks: - - wiki-network - restart: unless-stopped - healthcheck: - test: [ "CMD", "php-fpm", "-t" ] - interval: 30s - timeout: 10s - start_period: 60s - retries: 3 - user: "1000:1000" - expose: - - '9000' - - nginx: - image: nginx:stable-alpine - container_name: atlwiki-nginx - depends_on: - mediawiki: - condition: service_healthy - volumes: - - wiki-webroot:/var/www/atlwiki:ro - - ./mediawiki.conf:/etc/nginx/conf.d/mediawiki.conf:ro - ports: - - '3000:80' - networks: - - wiki-network - restart: unless-stopped - healthcheck: - test: [ "CMD", "curl", "-f", "http://localhost/" ] - interval: 30s - timeout: 10s - start_period: 30s - retries: 3 - - redis: - image: redis:alpine - container_name: atlwiki-redis - volumes: - - redis-data:/data - networks: - - wiki-network - command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru - restart: unless-stopped - healthcheck: - test: [ "CMD", "redis-cli", "ping" ] - interval: 30s - timeout: 10s - start_period: 30s - retries: 3 - expose: - - '6379' - -networks: - wiki-network: - driver: bridge - -volumes: - wiki-webroot: - driver: local - redis-data: - driver: local diff --git a/staging-compose.yaml.example b/staging-compose.yaml.example deleted file mode 100644 index 954d02a..0000000 --- a/staging-compose.yaml.example +++ /dev/null @@ -1,80 +0,0 @@ -name: staging-wiki - -services: - mediawiki: - build: - context: . - dockerfile: Dockerfile - args: - MEDIAWIKI_MAJOR_VERSION: ${MEDIAWIKI_MAJOR_VERSION} - MEDIAWIKI_VERSION: ${MEDIAWIKI_VERSION} - MEDIAWIKI_BRANCH: ${MEDIAWIKI_BRANCH} - CITIZEN_VERSION: ${CITIZEN_VERSION} - container_name: staging-atlwiki-mediawiki - depends_on: - redis: - condition: service_started - volumes: - - wiki-webroot:/var/www/atlwiki - - .env:/var/www/atlwiki/.env:ro - networks: - - wiki-network - restart: unless-stopped - healthcheck: - test: [ "CMD", "php-fpm", "-t" ] - interval: 30s - timeout: 10s - start_period: 60s - retries: 3 - user: "1000:1000" - expose: - - '9000' - - nginx: - image: nginx:stable-alpine - container_name: staging-atlwiki-nginx - depends_on: - mediawiki: - condition: service_healthy - volumes: - - wiki-webroot:/var/www/atlwiki:ro - - ./mediawiki.conf:/etc/nginx/conf.d/mediawiki.conf:ro - ports: - - '3001:80' - networks: - - wiki-network - restart: unless-stopped - healthcheck: - test: [ "CMD", "curl", "-f", "http://localhost/" ] - interval: 30s - timeout: 10s - start_period: 30s - retries: 3 - - redis: - image: redis:alpine - container_name: staging-atlwiki-redis - volumes: - - redis-data:/data - networks: - - wiki-network - command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru - restart: unless-stopped - healthcheck: - test: [ "CMD", "redis-cli", "ping" ] - interval: 30s - timeout: 10s - start_period: 30s - retries: 3 - expose: - - '6379' - -networks: - wiki-network: - driver: bridge - -volumes: - wiki-webroot: - driver: local - redis-data: - driver: local diff --git a/systemd/staging-wiki-sitemap.service b/systemd/staging-wiki-sitemap.service index cf40cdb..d9d4c63 100644 --- a/systemd/staging-wiki-sitemap.service +++ b/systemd/staging-wiki-sitemap.service @@ -6,4 +6,4 @@ Requires=docker.service [Service] Type=oneshot User=root -ExecStart=/usr/bin/docker exec staging-atlwiki-mediawiki php /var/www/atlwiki/mediawiki/maintenance/run.php generateSitemap --memory-limit=50M --fspath=/var/www/atlwiki/sitemap/ --identifier=atl.wiki --urlpath=/sitemap/ --server=https://staging.atl.wiki --compress=yes --skip-redirects +ExecStart=/usr/bin/docker exec staging-wiki-mediawiki php /var/www/wiki/mediawiki/maintenance/run.php generateSitemap --memory-limit=50M --fspath=/var/www/wiki/sitemap/ --identifier=atl.wiki --urlpath=/sitemap/ --server=https://staging.atl.wiki --compress=yes --skip-redirects diff --git a/systemd/wiki-sitemap.service b/systemd/wiki-sitemap.service index 94fd902..40499c4 100644 --- a/systemd/wiki-sitemap.service +++ b/systemd/wiki-sitemap.service @@ -6,4 +6,4 @@ Requires=docker.service [Service] Type=oneshot User=root -ExecStart=/usr/bin/docker exec atlwiki-mediawiki php /var/www/atlwiki/mediawiki/maintenance/run.php generateSitemap --memory-limit=50M --fspath=/var/www/atlwiki/sitemap/ --identifier=atl.wiki --urlpath=/sitemap/ --server=https://atl.wiki --compress=yes --skip-redirects +ExecStart=/usr/bin/docker exec wiki-mediawiki php /var/www/wiki/mediawiki/maintenance/run.php generateSitemap --memory-limit=50M --fspath=/var/www/wiki/sitemap/ --identifier=atl.wiki --urlpath=/sitemap/ --server=https://atl.wiki --compress=yes --skip-redirects diff --git a/.well-known/security.txt b/wiki/.well-known/security.txt similarity index 100% rename from .well-known/security.txt rename to wiki/.well-known/security.txt diff --git a/LocalSettings.php b/wiki/LocalSettings.php similarity index 83% rename from LocalSettings.php rename to wiki/LocalSettings.php index 8ea45f0..afd8ec1 100644 --- a/LocalSettings.php +++ b/wiki/LocalSettings.php @@ -1,4 +1,5 @@ safeLoad(); } @@ -82,17 +83,19 @@ // https://www.mediawiki.org/wiki/Manual:$wgCdnServers $wgCdnServers = [ - // Reverse Proxy - '10.0.0.2', - // nginx Container - 'nginx' + // nginx container (local reverse proxy) + 'nginx', + // Wiki server on Tailscale + '100.64.3.0', + // NPM server on Tailscale + '100.64.1.0', ]; // https://www.mediawiki.org/wiki/Manual:$wgUsePrivateIPs $wgUsePrivateIPs = true; // Trust the IP forwarded by the proxy (NPM and Cloudflare) -if (isset($_SERVER['HTTP_X_FORWARDED_FOR']) ) { +if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { $forwardedIps = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); // The first IP in the list is the original client IP $_SERVER['REMOTE_ADDR'] = trim($forwardedIps[0]); @@ -102,7 +105,8 @@ $wgCookieSameSite = 'Lax'; // https://www.mediawiki.org/wiki/Manual:$wgCookieSecure -$wgCookieSecure = true; +// Only use secure cookies if HTTPS is enabled +$wgCookieSecure = (strpos($_ENV['WG_SERVER'], 'https://') === 0); // https://www.mediawiki.org/wiki/Manual:$wgExternalLinkTarget $wgExternalLinkTarget = '_blank'; @@ -119,30 +123,9 @@ // https://www.mediawiki.org/wiki/Manual:$wgArticlePath $wgArticlePath = "/$1"; -// https://www.mediawiki.org/wiki/Manual:$wgActionPaths -$actions = [ - 'view', - 'edit', - 'watch', - 'unwatch', - 'delete', - 'revert', - 'rollback', - 'protect', - 'unprotect', - 'markpatrolled', - 'render', - 'submit', - 'history', - 'purge', - 'info', -]; -foreach ( $actions as $action ) { - $wgActionPaths[$action] = "/$action/$1"; -} - // https://www.mediawiki.org/wiki/Manual:$wgForceHTTPS -$wgForceHTTPS = true; +// Only force HTTPS if the server URL uses HTTPS +$wgForceHTTPS = (strpos($_ENV['WG_SERVER'], 'https://') === 0); //######################################################// DB Config @@ -166,28 +149,30 @@ $wgDBuser = $_ENV['DB_USER']; $wgDBpassword = $_ENV['DB_PASSWORD']; +//######################################################// SMTP + $wgSMTP = [ - "host" => "smtp.gmail.com", - "IDHost" => "allthingslinux.org", - "localhost" => "allthingslinux.org", - "port" => 587, + "host" => $_ENV['SMTP_HOST'], + "IDHost" => $_ENV['SMTP_DOMAIN'], + "localhost" => $_ENV['SMTP_DOMAIN'], + "port" => $_ENV['SMTP_PORT'], "auth" => true, - "username" => "services@allthingslinux.org", + "username" => $_ENV['SMTP_USERNAME'], "password" => $_ENV['SMTP_PASSWORD'] ?? '', ]; //######################################################// Caching // https://www.mediawiki.org/wiki/Manual:$wgCacheDirectory -$wgCacheDirectory = "/var/www/atlwiki/cache"; +$wgCacheDirectory = "/var/www/wiki/cache"; // https://www.mediawiki.org/wiki/Manual:$wgGitInfoCacheDirectory -$wgGitInfoCacheDirectory = "/var/www/atlwiki/cache/gitinfo"; +$wgGitInfoCacheDirectory = "/var/www/wiki/cache/gitinfo"; // https://www.mediawiki.org/wiki/Manual:$wgObjectCaches $wgObjectCaches['redis'] = [ 'class' => 'RedisBagOStuff', - 'servers' => [ 'redis:6379' ], + 'servers' => [ 'valkey:6379' ], 'persistent' => false, 'automaticFailOver' => false, ]; @@ -227,8 +212,8 @@ // https://www.mediawiki.org/wiki/Manual:$wgLocaltimezone $wgLocaltimezone = "UTC"; -// https://www.mediawiki.org/wiki/Manual:$wgDiff3 -$wgDiff3 = "/usr/bin/diff3"; +// https://www.mediawiki.org/wiki/Manual:$wgDiffEngine +$wgDiffEngine = 'wikidiff2'; // https://www.mediawiki.org/wiki/Manual:$wgUseRCPatrol $wgUseRCPatrol = false; diff --git a/configs/10-Branding.php b/wiki/configs/10-Branding.php similarity index 95% rename from configs/10-Branding.php rename to wiki/configs/10-Branding.php index 58371c7..764a43f 100755 --- a/configs/10-Branding.php +++ b/wiki/configs/10-Branding.php @@ -1,4 +1,5 @@ + * @license https://www.apache.org/licenses/LICENSE-2.0 Apache-2.0 + * @link https://atl.wiki + */ + +/* + * Helper reads to avoid undefined index warnings when env vars are empty. + */ +$env = static function (string $key, string $default = ''): string { + return isset($_ENV[$key]) && $_ENV[$key] !== '' ? (string)$_ENV[$key] : $default; +}; + +//#################################################################// TextExtracts +// https://www.mediawiki.org/wiki/Extension:TextExtracts + +$wgExtractsRemoveClasses = [ + 'ul.gallery', + 'gallery', + 'code', + '.metadata' +]; + +//######################################################// VisualEditor +// https://www.mediawiki.org/wiki/Extension:VisualEditor + +$wgVisualEditorAvailableNamespaces = [ + 'Guides' => true, + 'Project' => true +]; +$wgVisualEditorEnableDiffPageBetaFeature = true; +$wgVisualEditorUseSingleEditTab = true; +$wgDefaultUserOptions['visualeditor-enable'] = 1; +$wgDefaultUserOptions['visualeditor-editor'] = 'visualeditor'; + +//######################################################// Interwiki +// https://www.mediawiki.org/wiki/Extension:Interwiki + +// https://www.mediawiki.org/wiki/Manual:$wgUserrightsInterwikiDelimiter +$wgUserrightsInterwikiDelimiter = '#'; + +//######################################################// ConfirmEdit +// https://www.mediawiki.org/wiki/Extension:ConfirmEdit + +$turnstileSiteKey = $env('TURNSTILE_SITE_KEY'); +$turnstileSecretKey = $env('TURNSTILE_SECRET_KEY'); + +if ($turnstileSiteKey !== '' && $turnstileSecretKey !== '') { + $wgCaptchaClass = MediaWiki\Extension\ConfirmEdit\Turnstile\Turnstile::class; + $wgTurnstileSiteKey = $turnstileSiteKey; + $wgTurnstileSecretKey = $turnstileSecretKey; +} + +//######################################################// AWS +// https://www.mediawiki.org/wiki/Extension:AWS + +$s3BucketName = $env('S3_BUCKET_NAME'); +$s3AccessKey = $env('S3_ACCESS_KEY_ID'); +$s3SecretKey = $env('S3_SECRET_ACCESS_KEY'); +$s3Endpoint = $env('S3_ENDPOINT'); +$s3BucketDomain = $env('S3_BUCKET_DOMAIN'); + +if ( + $s3BucketName !== '' && $s3AccessKey !== '' && $s3SecretKey !== '' + && $s3Endpoint !== '' && $s3BucketDomain !== '' +) { + $wgAWSRegion = 'auto'; + $wgAWSBucketName = $s3BucketName; + $wgAWSBucketDomain = $s3BucketDomain; + $wgAWSCredentials = [ + 'key' => $s3AccessKey, + 'secret' => $s3SecretKey, + ]; + + if (!isset($wgFileBackends) || !is_array($wgFileBackends)) { + $wgFileBackends = []; + } + + $wgFileBackends['s3'] = [ + 'class' => 'AmazonS3FileBackend', + 'bucket' => $wgAWSBucketName, + 'region' => 'auto', + 'endpoint' => $s3Endpoint, + 'use_path_style_endpoint' => true, + ]; +} + +//######################################################// Discord +// https://www.mediawiki.org/wiki/Extension:Discord + +$discordWebhook = $env('DISCORD_WEBHOOK_URL'); +if ($discordWebhook !== '') { + $wgDiscordWebhookURL = [ $discordWebhook ]; + $wgDiscordUseEmojis = true; + $wgDiscordDisabledHooks = [ + 'BlockIpComplete', + 'UnblockUserComplete', + 'FileDeleteComplete', + 'FileUndeleteComplete', + 'ArticleRevisionVisibilitySet', + ]; +} + +//######################################################// CheckUser +// https://www.mediawiki.org/wiki/Extension:CheckUser + +$wgCheckUserLogSuccessfulBotLogins = false; +$wgCheckUserLogLogins = true; + +//######################################################// PluggableAuth +// https://www.mediawiki.org/wiki/Extension:PluggableAuth + +$openidClientId = $env('OPENID_CLIENT_ID'); +$openidClientSecret = $env('OPENID_CLIENT_SECRET'); + +if ($openidClientId !== '' && $openidClientSecret !== '') { + $wgPluggableAuth_Config['Staff Login via All Things Linux (SSO)'] = [ + 'plugin' => 'OpenIDConnect', + 'data' => [ + 'providerURL' => 'https://sso.allthingslinux.org', + 'clientID' => $openidClientId, + 'clientsecret' => $openidClientSecret, + ] + ]; +} + +$wgPluggableAuth_EnableLocalLogin = true; +$wgPluggableAuth_EnableLocalProperties = true; + +//######################################################// OpenID_Connect +// https://www.mediawiki.org/wiki/Extension:OpenID_Connect + +$wgOpenIDConnect_MigrateUsersByEmail = true; +$wgOpenIDConnect_UseRealNameAsUserName = true; + +//######################################################// Description2 +// https://www.mediawiki.org/wiki/Extension:Description2 + +$wgEnableMetaDescriptionFunctions = true; + +//######################################################// CodeMirror +// https://www.mediawiki.org/wiki/Extension:CodeMirror + +$wgDefaultUserOptions['usecodemirror'] = true; + +//######################################################// Drafts +// https://www.mediawiki.org/wiki/Extension:Drafts + +$egDraftsAutoSaveInputBased = true; +$egDraftsAutoSaveWait = 15; + +//######################################################// Scribunto +// https://www.mediawiki.org/wiki/Extension:Scribunto + +$wgScribuntoDefaultEngine = 'luasandbox'; + +//######################################################// SimpleBatchUpload +// https://www.mediawiki.org/wiki/Extension:SimpleBatchUpload + +$wgSimpleBatchUploadMaxFilesPerBatch = [ + '*' => 10, +]; + +//######################################################// CirrusSearch +// https://www.mediawiki.org/wiki/Extension:CirrusSearch + +$wgSearchType = 'CirrusSearch'; +$wgCirrusSearchServers = [ + [ 'host' => 'opensearch', 'port' => 9200 ], +]; + +// Keep search updates enabled so the job queue can index new edits. +$wgDisableSearchUpdate = false; diff --git a/configs/60-Skins_Load.php b/wiki/configs/60-Skins_Load.php similarity index 99% rename from configs/60-Skins_Load.php rename to wiki/configs/60-Skins_Load.php index 4e95509..034ff85 100755 --- a/configs/60-Skins_Load.php +++ b/wiki/configs/60-Skins_Load.php @@ -1,4 +1,5 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# http://www.apache.org/licenses/LICENSE-2.0 +# Copyright 2025 All Things Linux and Contributors + +# Primary maintainer: Atmois + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 + +"""Installs MediaWiki extensions as specified in the extensions.json file""" import json import os @@ -14,7 +18,7 @@ MEDIAWIKI_BRANCH = os.environ.get('MEDIAWIKI_BRANCH') EXTENSIONS_JSON = '/tmp/extensions.json' -EXTENSIONS_DIR = '/var/www/atlwiki/mediawiki/extensions' +EXTENSIONS_DIR = '/var/www/wiki/mediawiki/extensions' def run(command): """ @@ -55,6 +59,8 @@ def main(): run(f"mkdir -p {extension_name}") run(f"tar -xzf {tarball_name} -C {extension_name} --strip-components=1") run(f"rm {tarball_name}") + elif install_type == 'composer': + continue else: print(f"Unknown install_type for {extension_name}: {install_type}", file=sys.stderr) sys.exit(1) diff --git a/mediawiki.conf b/wiki/mediawiki.conf similarity index 96% rename from mediawiki.conf rename to wiki/mediawiki.conf index 8a92f78..4cb1211 100644 --- a/mediawiki.conf +++ b/wiki/mediawiki.conf @@ -1,7 +1,7 @@ server { listen 80; - server_name atl.wiki; - root /var/www/atlwiki; + server_name ${NGINX_SERVER_NAME}; + root /var/www/wiki; index index.php; location / { diff --git a/php.ini b/wiki/php.ini similarity index 78% rename from php.ini rename to wiki/php.ini index 28221c9..e8e0190 100644 --- a/php.ini +++ b/wiki/php.ini @@ -11,9 +11,11 @@ opcache.revalidate_freq = 60 memory_limit = 512M max_execution_time = 60 post_max_size = 25M +upload_max_filesize = 25M +max_input_vars = 3000 -; Redis Configurations +; Valkey Configurations session.save_handler = redis -session.save_path = "tcp://redis:6379" +session.save_path = "tcp://valkey:6379" redis.timeout = 2.5 redis.read_timeout = 2.5 diff --git a/robots.txt b/wiki/robots.txt similarity index 88% rename from robots.txt rename to wiki/robots.txt index 10dfd7b..b504b13 100755 --- a/robots.txt +++ b/wiki/robots.txt @@ -94,22 +94,6 @@ Disallow: /*?title=Property%3A Disallow: /*?*&title=Property: Disallow: /*?*&title=Property%3A -# Block action paths -Disallow: /edit/ -Disallow: /watch/ -Disallow: /unwatch/ -Disallow: /delete/ -Disallow: /revert/ -Disallow: /rollback/ -Disallow: /protect/ -Disallow: /unprotect/ -Disallow: /markpatrolled/ -Disallow: /render/ -Disallow: /submit/ -Disallow: /history/ -Disallow: /purge/ -Disallow: /info/ - ################# ## Sitemap URL ## #################