├── docker-compose.yml ├── .gitignore ├── .markdownlint.json ├── .hadolint.yaml ├── CHANGELOG.md ├── .env.example ├── .pre-commit-config.yaml ├── start-fiduswriter.sh ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── workflows │ ├── ci.yml │ ├── update-version.yml │ └── docker-build.yml └── pull_request_template.md ├── .secrets.baseline ├── Dockerfile ├── GETTING_STARTED.md ├── UPGRADE_GUIDE.md ├── volumes └── config │ └── configuration.py ├── Makefile ├── scripts ├── setup.sh └── check-version.py ├── CONTRIBUTING.md ├── docs └── QUICK_REFERENCE.md ├── README.md └── LICENSE.txt /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | fiduswriter: 3 | build: 4 | context: . 5 | args: 6 | FIDUSWRITER_VERSION: ${FIDUSWRITER_VERSION:-4.0.17} 7 | volumes: 8 | - ./volumes/data:/data 9 | ports: 10 | - 8000:8000 11 | container_name: fiduswriter 12 | restart: unless-stopped 13 | user: 999:999 # Ensure we're running as the correct user 14 | environment: 15 | - FIDUSWRITER_VERSION=${FIDUSWRITER_VERSION:-4.0.17} 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Development and instance data 2 | /develop-instance-data/ 3 | /volumes/data/ 4 | 5 | # Environment files 6 | .env 7 | .env.local 8 | 9 | # Python 10 | __pycache__/ 11 | *.py[cod] 12 | *$py.class 13 | *.so 14 | .Python 15 | venv/ 16 | ENV/ 17 | env/ 18 | 19 | # IDEs 20 | .vscode/ 21 | .idea/ 22 | *.swp 23 | *.swo 24 | *~ 25 | .DS_Store 26 | 27 | # Docker 28 | *.tar.gz 29 | backup-*.tar.gz 30 | 31 | # Pre-commit 32 | .pre-commit-config.yaml.bak 33 | 34 | # Logs 35 | *.log 36 | logs/ 37 | 38 | # Testing 39 | .pytest_cache/ 40 | .coverage 41 | htmlcov/ 42 | 43 | # Temporary files 44 | tmp/ 45 | temp/ 46 | *.tmp 47 | -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "default": true, 3 | "MD003": { 4 | "style": "atx" 5 | }, 6 | "MD004": { 7 | "style": "dash" 8 | }, 9 | "MD007": { 10 | "indent": 2 11 | }, 12 | "MD013": { 13 | "line_length": 120, 14 | "heading_line_length": 120, 15 | "code_block_line_length": 120, 16 | "code_blocks": true, 17 | "tables": false, 18 | "headings": true, 19 | "strict": false, 20 | "stern": false 21 | }, 22 | "MD024": { 23 | "siblings_only": true 24 | }, 25 | "MD025": { 26 | "front_matter_title": "" 27 | }, 28 | "MD033": { 29 | "allowed_elements": ["br", "img", "table", "thead", "tbody", "tr", "th", "td", "details", "summary"] 30 | }, 31 | "MD036": false, 32 | "MD040": false, 33 | "MD041": false, 34 | "no-hard-tabs": true, 35 | "whitespace": true 36 | } 37 | -------------------------------------------------------------------------------- /.hadolint.yaml: -------------------------------------------------------------------------------- 1 | failure-threshold: error 2 | 3 | ignored: 4 | # DL3008: Pin versions in apt-get install 5 | # We intentionally don't pin apt package versions as they can vary by Ubuntu release 6 | - DL3008 7 | 8 | # DL3009: Delete the apt-get lists after installing 9 | # We handle this with rm -rf /var/lib/apt/lists/* 10 | - DL3009 11 | 12 | # DL3015: Avoid additional packages by specifying --no-install-recommends 13 | # Some packages need their recommended dependencies 14 | - DL3015 15 | 16 | # DL3016: Pin versions in npm 17 | # Node.js is installed from NodeSource and doesn't need explicit pinning 18 | - DL3016 19 | 20 | # DL3049: Label info messages 21 | # Labels are not critical for this use case 22 | - DL3049 23 | 24 | # DL3059: Multiple consecutive RUN instructions 25 | # We prefer readability over layer optimization in some cases 26 | - DL3059 27 | 28 | # DL4006: Set SHELL option -o pipefail 29 | # Using default shell behavior 30 | - DL4006 31 | 32 | trustedRegistries: 33 | - docker.io 34 | - ghcr.io 35 | 36 | no-fail: false 37 | no-color: false 38 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | No unreleased changes. 11 | 12 | ## [4.0.17] - 2025-12-18 13 | 14 | ### Added 15 | 16 | - Pre-commit configuration for code quality checks 17 | - GitHub Actions CI/CD pipeline for automated testing and building 18 | - Automated version checking workflow to detect new Fiduswriter releases 19 | - Makefile with common development commands 20 | - Helper scripts (check-version.py, setup.sh) 21 | - Comprehensive documentation 22 | 23 | ### Changed 24 | 25 | - Updated to Python 3.14.2 from deadsnakes PPA 26 | - Improved Dockerfile with build arguments support 27 | - Enhanced docker-compose.yml with version configuration 28 | 29 | ### Removed 30 | 31 | - IDE-specific files (.idea, .ropeproject) 32 | 33 | ## [4.0.16] - Previous Release 34 | 35 | Initial Docker setup for Fiduswriter with Ubuntu 24.04 base image. 36 | 37 | [Unreleased]: https://github.com/fiduswriter/fiduswriter-docker/compare/v4.0.17...HEAD 38 | [4.0.17]: https://github.com/fiduswriter/fiduswriter-docker/releases/tag/v4.0.17 39 | [4.0.16]: https://github.com/fiduswriter/fiduswriter-docker/releases/tag/v4.0.16 40 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | # Fiduswriter Docker Environment Configuration 2 | # Copy this file to .env and adjust values as needed 3 | 4 | # Fiduswriter version to use 5 | # Set to a specific version (e.g., 4.0.17) or leave default to use latest in 4.0.x series 6 | FIDUSWRITER_VERSION=4.0.17 7 | 8 | # Container user/group IDs 9 | # These should match the ownership of your volumes directory 10 | EXECUTING_USER_ID=999 11 | EXECUTING_GROUP_ID=999 12 | 13 | # Port mapping 14 | # Change if you need to run on a different port 15 | HOST_PORT=8000 16 | CONTAINER_PORT=8000 17 | 18 | # Data directory 19 | # Path on host machine for persistent data 20 | DATA_DIR=./volumes/data 21 | 22 | # Container restart policy 23 | # Options: no, always, on-failure, unless-stopped 24 | RESTART_POLICY=unless-stopped 25 | 26 | # Timezone (optional) 27 | TZ=UTC 28 | 29 | # Database configuration (if using external database) 30 | # Leave commented out to use default SQLite 31 | # DB_ENGINE=postgresql 32 | # DB_NAME=fiduswriter 33 | # DB_USER=fiduswriter 34 | # DB_PASSWORD=changeme 35 | # DB_HOST=db 36 | # DB_PORT=5432 37 | 38 | # Email configuration (optional) 39 | # EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend 40 | # EMAIL_HOST=smtp.example.com 41 | # EMAIL_PORT=587 42 | # EMAIL_USE_TLS=true 43 | # EMAIL_HOST_USER=your-email@example.com 44 | # EMAIL_HOST_PASSWORD=your-password 45 | # DEFAULT_FROM_EMAIL=noreply@example.com 46 | 47 | # Debug mode (set to false in production!) 48 | DEBUG=false 49 | 50 | # Allowed hosts (comma-separated) 51 | # ALLOWED_HOSTS=localhost,127.0.0.1,your-domain.com 52 | 53 | # Secret key (generate a new one for production!) 54 | # SECRET_KEY=change-this-to-a-random-string-in-production 55 | 56 | # CSRF trusted origins (for HTTPS) 57 | # CSRF_TRUSTED_ORIGINS=https://your-domain.com 58 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # Pre-commit hooks for fiduswriter-docker 2 | # See https://pre-commit.com for more information 3 | # See https://pre-commit.com/hooks.html for more hooks 4 | 5 | repos: 6 | - repo: https://github.com/pre-commit/pre-commit-hooks 7 | rev: v4.5.0 8 | hooks: 9 | - id: trailing-whitespace 10 | - id: end-of-file-fixer 11 | - id: check-yaml 12 | args: [--unsafe] # Allow custom tags in docker-compose 13 | - id: check-added-large-files 14 | args: [--maxkb=1000] 15 | - id: check-case-conflict 16 | - id: check-merge-conflict 17 | - id: check-executables-have-shebangs 18 | - id: check-shebang-scripts-are-executable 19 | - id: detect-private-key 20 | - id: mixed-line-ending 21 | args: [--fix=lf] 22 | 23 | # Dockerfile linting 24 | - repo: https://github.com/hadolint/hadolint 25 | rev: v2.12.0 26 | hooks: 27 | - id: hadolint-docker 28 | args: [--ignore, DL3008, --ignore, DL3009, --ignore, DL3015] 29 | # DL3008: Pin versions in apt-get install (too restrictive for this use case) 30 | # DL3009: Delete apt cache (we do this with rm -rf) 31 | # DL3015: Avoid additional package-specific commands 32 | 33 | # Shell script linting 34 | - repo: https://github.com/shellcheck-py/shellcheck-py 35 | rev: v0.9.0.6 36 | hooks: 37 | - id: shellcheck 38 | args: [--severity=warning] 39 | 40 | # YAML formatting 41 | - repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks 42 | rev: v2.12.0 43 | hooks: 44 | - id: pretty-format-yaml 45 | args: [--autofix, --indent, '2'] 46 | exclude: .github/workflows/.*\.ya?ml$ 47 | 48 | # Markdown linting 49 | - repo: https://github.com/igorshubovych/markdownlint-cli 50 | rev: v0.39.0 51 | hooks: 52 | - id: markdownlint 53 | args: [--fix] 54 | 55 | # Check for secrets 56 | - repo: https://github.com/Yelp/detect-secrets 57 | rev: v1.4.0 58 | hooks: 59 | - id: detect-secrets 60 | args: [--baseline, .secrets.baseline] 61 | exclude: package.lock.json 62 | -------------------------------------------------------------------------------- /start-fiduswriter.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # 3 | # Script to run fiduswriter and/or perform the initial setup 4 | 5 | echo "*** Running as user $(whoami) ..." 6 | 7 | # Activate the virtual environment properly 8 | export PATH="/fiduswriter/venv/bin:$PATH" 9 | 10 | if [ -f /data/configuration.py ]; then 11 | echo '*** Fiduswriter will use the settings found in /data/configuration.py ...' 12 | else 13 | echo '*** /data/configuration.py was not found. A new file with default settings will now be created ...' 14 | 15 | fiduswriter startproject 16 | fi 17 | 18 | # If setup-performed.info does not yet exist, we assume that this is a "fresh" container and perform the setup. 19 | if ! [ -f /fiduswriter/setup-performed.info ]; then 20 | echo '*** Executing fiduswriter setup as this is the first time that this container is launched ...' 21 | fiduswriter setup 22 | touch /fiduswriter/setup-performed.info 23 | fi 24 | 25 | # Handle media directory 26 | if [ -d /fiduswriter/media ] ; then 27 | if ! [ -L /fiduswriter/media ] ; then 28 | if [ -d /data/media ] ; then 29 | rm -rf /fiduswriter/media 30 | else 31 | mv /fiduswriter/media /data/media 32 | fi 33 | fi 34 | fi 35 | if ! [ -L /fiduswriter/media ] ; then 36 | ln -sf /data/media /fiduswriter 37 | fi 38 | 39 | # Handle configuration file 40 | if [ -f /fiduswriter/configuration.py ] ; then 41 | if ! [ -L /fiduswriter/configuration.py ] ; then 42 | if [ -f /data/configuration.py ] ; then 43 | rm /fiduswriter/configuration.py 44 | else 45 | mv /fiduswriter/configuration.py /data/configuration.py 46 | fi 47 | fi 48 | fi 49 | if ! [ -L /fiduswriter/configuration.py ] ; then 50 | ln -sf /data/configuration.py /fiduswriter/configuration.py 51 | fi 52 | 53 | # Handle database file 54 | if [ -f /fiduswriter/fiduswriter.sql ] ; then 55 | if ! [ -L /fiduswriter/fiduswriter.sql ] ; then 56 | if [ -f /data/fiduswriter.sql ] ; then 57 | rm /fiduswriter/fiduswriter.sql 58 | else 59 | mv /fiduswriter/fiduswriter.sql /data/fiduswriter.sql 60 | fi 61 | fi 62 | fi 63 | if ! [ -L /fiduswriter/fiduswriter.sql ] ; then 64 | ln -sf /data/fiduswriter.sql /fiduswriter/fiduswriter.sql 65 | fi 66 | 67 | # Ensure database file is writable 68 | if [ -f /data/fiduswriter.sql ]; then 69 | chmod 664 /data/fiduswriter.sql 70 | fi 71 | 72 | # Run the fiduswriter-server 73 | echo '*** Executing fiduswriter ...' 74 | fiduswriter runserver 0.0.0.0:8000 75 | 76 | exit 0 77 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: Suggest a new feature or enhancement for the Fiduswriter Docker setup 4 | title: '[FEATURE] ' 5 | labels: enhancement 6 | assignees: '' 7 | --- 8 | 9 | ## Feature Description 10 | 11 | A clear and concise description of the feature you'd like to see. 12 | 13 | ## Problem Statement 14 | 15 | Describe the problem this feature would solve. For example: 16 | 17 | - What is the current limitation? 18 | - What workflow is difficult or impossible right now? 19 | - What use case is not currently supported? 20 | 21 | ## Proposed Solution 22 | 23 | A clear and concise description of what you want to happen. 24 | 25 | ### Example Usage 26 | 27 | If applicable, provide an example of how this feature would be used: 28 | 29 | ```bash 30 | # Example commands or configuration 31 | make new-feature 32 | docker compose up --with-new-option 33 | ``` 34 | 35 | or 36 | 37 | ```yaml 38 | # Example docker-compose.yml change 39 | services: 40 | fiduswriter: 41 | environment: 42 | - NEW_FEATURE_ENABLED=true 43 | ``` 44 | 45 | ## Alternatives Considered 46 | 47 | Describe any alternative solutions or features you've considered. 48 | 49 | ## Benefits 50 | 51 | Who would benefit from this feature and how? 52 | 53 | - [ ] Developers working on Fiduswriter Docker 54 | - [ ] Users deploying Fiduswriter in production 55 | - [ ] Users running Fiduswriter locally 56 | - [ ] CI/CD pipelines 57 | - [ ] Other: [specify] 58 | 59 | ## Implementation Notes 60 | 61 | If you have thoughts on how this could be implemented, share them here: 62 | 63 | - Technical approach 64 | - Files that would need to be modified 65 | - Dependencies that might be needed 66 | - Potential challenges 67 | 68 | ## Additional Context 69 | 70 | Add any other context, screenshots, or examples about the feature request here. 71 | 72 | ### Related Issues 73 | 74 | - Related to #[issue number] 75 | - Depends on #[issue number] 76 | - Blocks #[issue number] 77 | 78 | ## Checklist 79 | 80 | - [ ] I have checked existing issues and PRs to ensure this hasn't been requested before 81 | - [ ] I have provided a clear description of the problem and solution 82 | - [ ] I have considered how this fits with the project's goals 83 | - [ ] I am willing to help implement this feature (if applicable) 84 | 85 | ## Additional Information 86 | 87 | **Priority**: [Low / Medium / High / Critical] 88 | 89 | **Complexity**: [Low / Medium / High] (your estimate) 90 | 91 | **Would you be willing to contribute a PR for this feature?**: [Yes / No / Maybe] 92 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Report a bug or issue with the Fiduswriter Docker setup 4 | title: '[BUG] ' 5 | labels: bug 6 | assignees: '' 7 | --- 8 | 9 | ## Bug Description 10 | 11 | A clear and concise description of what the bug is. 12 | 13 | ## Environment 14 | 15 | - **OS**: [e.g., Ubuntu 22.04, macOS 13.0, Windows 11] 16 | - **Docker Version**: [e.g., 24.0.0] 17 | - **Docker Compose Version**: [e.g., 2.20.0] 18 | - **Fiduswriter Version**: [e.g., 4.0.17] 19 | - **Image Tag**: [e.g., latest, 4.0.17, 4.0, 4] 20 | 21 | ## Steps to Reproduce 22 | 23 | Steps to reproduce the behavior: 24 | 25 | 1. Run command '...' 26 | 2. Navigate to '...' 27 | 3. Click on '...' 28 | 4. See error 29 | 30 | ## Expected Behavior 31 | 32 | A clear and concise description of what you expected to happen. 33 | 34 | ## Actual Behavior 35 | 36 | A clear and concise description of what actually happened. 37 | 38 | ## Logs 39 | 40 | Please provide relevant logs: 41 | 42 | ```bash 43 | # Docker logs 44 | docker compose logs 45 | 46 | # Or for specific container 47 | docker logs fiduswriter 48 | ``` 49 | 50 |
51 | Paste logs here 52 | 53 | ``` 54 | [Paste your logs here] 55 | ``` 56 | 57 |
58 | 59 | ## Configuration 60 | 61 | If relevant, share your configuration (remove sensitive information): 62 | 63 |
64 | docker-compose.yml 65 | 66 | ```yaml 67 | [Paste your docker-compose.yml here] 68 | ``` 69 | 70 |
71 | 72 |
73 | .env file (if used) 74 | 75 | ``` 76 | [Paste relevant .env variables here, REMOVE SECRETS] 77 | ``` 78 | 79 |
80 | 81 |
82 | configuration.py (if modified) 83 | 84 | ```python 85 | [Paste relevant parts of configuration.py, REMOVE SECRETS] 86 | ``` 87 | 88 |
89 | 90 | ## Screenshots 91 | 92 | If applicable, add screenshots to help explain your problem. 93 | 94 | ## Additional Context 95 | 96 | Add any other context about the problem here: 97 | 98 | - Did this work in a previous version? 99 | - Are you using any custom configuration? 100 | - Are you behind a proxy or using special networking setup? 101 | - Any relevant host system configuration? 102 | 103 | ## Possible Solution 104 | 105 | If you have an idea of what might be causing the issue or how to fix it, please share. 106 | 107 | ## Checklist 108 | 109 | - [ ] I have checked existing issues to ensure this is not a duplicate 110 | - [ ] I have tested with the latest version 111 | - [ ] I have included all relevant information above 112 | - [ ] I have removed any sensitive information from logs and configuration 113 | -------------------------------------------------------------------------------- /.secrets.baseline: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.4.0", 3 | "plugins_used": [ 4 | { 5 | "name": "ArtifactoryDetector" 6 | }, 7 | { 8 | "name": "AWSKeyDetector" 9 | }, 10 | { 11 | "name": "AzureStorageKeyDetector" 12 | }, 13 | { 14 | "name": "Base64HighEntropyString", 15 | "limit": 4.5 16 | }, 17 | { 18 | "name": "BasicAuthDetector" 19 | }, 20 | { 21 | "name": "CloudantDetector" 22 | }, 23 | { 24 | "name": "DiscordBotTokenDetector" 25 | }, 26 | { 27 | "name": "GitHubTokenDetector" 28 | }, 29 | { 30 | "name": "HexHighEntropyString", 31 | "limit": 3.0 32 | }, 33 | { 34 | "name": "IbmCloudIamDetector" 35 | }, 36 | { 37 | "name": "IbmCosHmacDetector" 38 | }, 39 | { 40 | "name": "JwtTokenDetector" 41 | }, 42 | { 43 | "name": "KeywordDetector", 44 | "keyword_exclude": "" 45 | }, 46 | { 47 | "name": "MailchimpDetector" 48 | }, 49 | { 50 | "name": "NpmDetector" 51 | }, 52 | { 53 | "name": "PrivateKeyDetector" 54 | }, 55 | { 56 | "name": "SendGridDetector" 57 | }, 58 | { 59 | "name": "SlackDetector" 60 | }, 61 | { 62 | "name": "SoftlayerDetector" 63 | }, 64 | { 65 | "name": "SquareOAuthDetector" 66 | }, 67 | { 68 | "name": "StripeDetector" 69 | }, 70 | { 71 | "name": "TwilioKeyDetector" 72 | } 73 | ], 74 | "filters_used": [ 75 | { 76 | "path": "detect_secrets.filters.allowlist.is_line_allowlisted" 77 | }, 78 | { 79 | "path": "detect_secrets.filters.common.is_ignored_due_to_verification_policies", 80 | "min_level": 2 81 | }, 82 | { 83 | "path": "detect_secrets.filters.heuristic.is_indirect_reference" 84 | }, 85 | { 86 | "path": "detect_secrets.filters.heuristic.is_likely_id_string" 87 | }, 88 | { 89 | "path": "detect_secrets.filters.heuristic.is_lock_file" 90 | }, 91 | { 92 | "path": "detect_secrets.filters.heuristic.is_not_alphanumeric_string" 93 | }, 94 | { 95 | "path": "detect_secrets.filters.heuristic.is_potential_uuid" 96 | }, 97 | { 98 | "path": "detect_secrets.filters.heuristic.is_prefixed_with_dollar_sign" 99 | }, 100 | { 101 | "path": "detect_secrets.filters.heuristic.is_sequential_string" 102 | }, 103 | { 104 | "path": "detect_secrets.filters.heuristic.is_swagger_file" 105 | }, 106 | { 107 | "path": "detect_secrets.filters.heuristic.is_templated_secret" 108 | } 109 | ], 110 | "results": {}, 111 | "generated_at": "2025-12-18T00:00:00Z" 112 | } 113 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # vim: set ts=4 sw=4 sts=0 sta et : 2 | FROM ubuntu:24.04 3 | EXPOSE 8000 4 | 5 | # Use build arg to allow overriding version 6 | ARG FIDUSWRITER_VERSION=4.0.17 7 | ENV VERSION=${FIDUSWRITER_VERSION} 8 | 9 | # Executing group, with fixed group id 10 | ENV EXECUTING_GROUP=fiduswriter 11 | ENV EXECUTING_GROUP_ID=999 12 | 13 | # Executing user, with fixed user id 14 | ENV EXECUTING_USER=fiduswriter 15 | ENV EXECUTING_USER_ID=999 16 | 17 | # Data volume, should be owned by 999:999 to ensure the application can 18 | # function correctly. Run `chown 999:999 ` on the host OS to 19 | # get this right. 20 | VOLUME ["/data"] 21 | 22 | # Create user and group with fixed ID, instead of allowing the OS to pick one. 23 | RUN groupadd \ 24 | --system \ 25 | --gid ${EXECUTING_GROUP_ID} \ 26 | ${EXECUTING_GROUP} \ 27 | && useradd \ 28 | --system \ 29 | --create-home \ 30 | --no-log-init \ 31 | --uid ${EXECUTING_USER_ID} \ 32 | --gid ${EXECUTING_GROUP} \ 33 | ${EXECUTING_USER} 34 | 35 | # Chain apt-get update, apt-get install and the removal of the lists. 36 | RUN apt-get update \ 37 | && DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC apt-get install -y \ 38 | build-essential \ 39 | gettext \ 40 | git \ 41 | libjpeg-dev \ 42 | npm \ 43 | python3-venv \ 44 | python3-dev \ 45 | python3-pip \ 46 | unzip \ 47 | wget \ 48 | zlib1g-dev \ 49 | curl \ 50 | rsync \ 51 | libmagic-dev \ 52 | software-properties-common \ 53 | && rm -rf /var/lib/apt/lists/* 54 | 55 | # Install Python 3.14.2 from deadsnakes PPA 56 | RUN add-apt-repository ppa:deadsnakes/ppa -y \ 57 | && apt-get update \ 58 | && DEBIAN_FRONTEND=noninteractive apt-get install -y \ 59 | python3.14 \ 60 | python3.14-venv \ 61 | python3.14-dev \ 62 | && rm -rf /var/lib/apt/lists/* 63 | 64 | # Set Python 3.14 as the default python3 65 | RUN update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.14 1 \ 66 | && update-alternatives --set python3 /usr/bin/python3.14 67 | 68 | # Install Node.js 22.x 69 | RUN curl -sL https://deb.nodesource.com/setup_22.x | bash \ 70 | && apt-get update \ 71 | && apt-get install -y nodejs \ 72 | && rm -rf /var/lib/apt/lists/* 73 | 74 | # Working directories should be absolute. 75 | WORKDIR /fiduswriter 76 | 77 | # Data folder must exist. It could be mapped if you like to make it persistent. 78 | RUN mkdir -p /data/media 79 | RUN chown -R ${EXECUTING_USER}:${EXECUTING_GROUP} /data 80 | RUN chmod -R 755 /data 81 | 82 | # Create virtual environment with the right permissions 83 | RUN python3 -m venv venv 84 | RUN chown -R ${EXECUTING_USER}:${EXECUTING_GROUP} /fiduswriter 85 | 86 | # Install packages 87 | RUN venv/bin/pip install --upgrade setuptools pip wheel 88 | RUN venv/bin/pip install fiduswriter[books,citation-api-import,languagetool,ojs,pandoc,gitrepo-export,phplist,payment-paddle,website]==${VERSION} 89 | 90 | COPY start-fiduswriter.sh /etc/start-fiduswriter.sh 91 | RUN chmod +x /etc/start-fiduswriter.sh 92 | 93 | # Make sure the data directory is writable by the Fidus Writer user 94 | USER ${EXECUTING_USER} 95 | 96 | CMD ["/bin/sh", "/etc/start-fiduswriter.sh"] 97 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - develop 8 | pull_request: 9 | branches: 10 | - main 11 | - develop 12 | workflow_dispatch: 13 | 14 | jobs: 15 | lint: 16 | name: Lint and Validate 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Checkout code 20 | uses: actions/checkout@v4 21 | 22 | - name: Set up Python 23 | uses: actions/setup-python@v5 24 | with: 25 | python-version: '3.14' 26 | 27 | - name: Install pre-commit 28 | run: | 29 | pip install pre-commit 30 | pre-commit install 31 | 32 | - name: Run pre-commit hooks 33 | run: pre-commit run --all-files 34 | 35 | - name: Lint Dockerfile with hadolint 36 | uses: hadolint/hadolint-action@v3.1.0 37 | with: 38 | dockerfile: Dockerfile 39 | ignore: DL3008,DL3009,DL3015 40 | 41 | - name: Validate docker-compose.yml 42 | run: | 43 | docker compose config 44 | 45 | build: 46 | name: Build Docker Image 47 | runs-on: ubuntu-latest 48 | needs: lint 49 | strategy: 50 | matrix: 51 | version: ['4.0.17'] 52 | steps: 53 | - name: Checkout code 54 | uses: actions/checkout@v4 55 | 56 | - name: Set up Docker Buildx 57 | uses: docker/setup-buildx-action@v3 58 | 59 | - name: Build Docker image 60 | uses: docker/build-push-action@v5 61 | with: 62 | context: . 63 | file: ./Dockerfile 64 | push: false 65 | tags: fiduswriter/fiduswriter:${{ matrix.version }} 66 | build-args: | 67 | FIDUSWRITER_VERSION=${{ matrix.version }} 68 | cache-from: type=gha 69 | cache-to: type=gha,mode=max 70 | 71 | - name: Test Docker image 72 | run: | 73 | docker build --build-arg FIDUSWRITER_VERSION=${{ matrix.version }} -t fiduswriter-test:${{ matrix.version }} . 74 | docker run -d --name fiduswriter-test -p 8000:8000 fiduswriter-test:${{ matrix.version }} 75 | 76 | # Wait for container to start 77 | sleep 10 78 | 79 | # Check if container is running 80 | docker ps | grep fiduswriter-test 81 | 82 | # Check logs for errors 83 | docker logs fiduswriter-test 84 | 85 | # Cleanup 86 | docker stop fiduswriter-test 87 | docker rm fiduswriter-test 88 | 89 | shellcheck: 90 | name: ShellCheck 91 | runs-on: ubuntu-latest 92 | steps: 93 | - name: Checkout code 94 | uses: actions/checkout@v4 95 | 96 | - name: Run ShellCheck 97 | uses: ludeeus/action-shellcheck@master 98 | with: 99 | severity: warning 100 | scandir: '.' 101 | format: gcc 102 | 103 | markdown-lint: 104 | name: Markdown Lint 105 | runs-on: ubuntu-latest 106 | steps: 107 | - name: Checkout code 108 | uses: actions/checkout@v4 109 | 110 | - name: Lint Markdown files 111 | uses: avto-dev/markdown-lint@v1 112 | with: 113 | args: '**/*.md' 114 | config: '.markdownlint.json' 115 | ignore: 'node_modules' 116 | -------------------------------------------------------------------------------- /GETTING_STARTED.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Fiduswriter Docker 2 | 3 | Welcome! This guide will help you get Fiduswriter up and running quickly. 4 | 5 | ## Prerequisites 6 | 7 | - **Docker**: Version 20.10 or higher 8 | - **Docker Compose**: Version 2.0 or higher 9 | - **Git**: To clone the repository 10 | 11 | ## Quick Start 12 | 13 | ### Step 1: Clone and Setup 14 | 15 | ```bash 16 | git clone https://github.com/fiduswriter/fiduswriter-docker.git 17 | cd fiduswriter-docker 18 | 19 | # Run the setup script (interactive) 20 | ./scripts/setup.sh 21 | ``` 22 | 23 | The script will guide you through the entire setup process. 24 | 25 | ### Step 2: Access Fiduswriter 26 | 27 | Open your browser to: **** 28 | 29 | ## Manual Setup 30 | 31 | If you prefer step-by-step control: 32 | 33 | ```bash 34 | # 1. Clone repository 35 | git clone https://github.com/fiduswriter/fiduswriter-docker.git 36 | cd fiduswriter-docker 37 | 38 | # 2. Set up data directory with correct permissions 39 | make setup-data 40 | 41 | # 3. Build Docker image 42 | make build 43 | 44 | # 4. Start containers 45 | make up 46 | 47 | # 5. Create admin account 48 | make superuser 49 | 50 | # 6. Access at http://localhost:8000 51 | ``` 52 | 53 | ## Basic Configuration 54 | 55 | ### Change the Port 56 | 57 | If port 8000 is already in use: 58 | 59 | ```bash 60 | echo "HOST_PORT=8080" > .env 61 | docker compose restart 62 | ``` 63 | 64 | ### Configure Your Domain 65 | 66 | Edit `volumes/data/configuration.py`: 67 | 68 | ```python 69 | ALLOWED_HOSTS = ['your-domain.com', 'localhost'] 70 | DEBUG = False # Important for production! 71 | CONTACT_EMAIL = 'admin@your-domain.com' 72 | ``` 73 | 74 | Restart after changes: `docker compose restart` 75 | 76 | ## Common Commands 77 | 78 | ```bash 79 | # View logs 80 | make logs 81 | 82 | # Stop containers 83 | make down 84 | 85 | # Restart containers 86 | make restart 87 | 88 | # Create backup 89 | make backup 90 | 91 | # Check status 92 | make status 93 | 94 | # See all commands 95 | make help 96 | ``` 97 | 98 | ## Next Steps 99 | 100 | 1. **Configure site**: Go to 101 | 2. **Change domain**: Update from example.com to your domain 102 | 3. **Set up email**: Edit `configuration.py` (optional) 103 | 4. **Create backups**: Run `make backup` regularly 104 | 105 | ## Troubleshooting 106 | 107 | ### Container Won't Start 108 | 109 | ```bash 110 | # Check logs 111 | docker compose logs 112 | 113 | # Fix permissions 114 | sudo chown -R 999:999 volumes/data 115 | docker compose restart 116 | ``` 117 | 118 | ### Port Already in Use 119 | 120 | ```bash 121 | # Change port 122 | echo "HOST_PORT=8080" >> .env 123 | docker compose down 124 | docker compose up -d 125 | ``` 126 | 127 | ### Forgot Admin Password 128 | 129 | ```bash 130 | # Create new superuser 131 | make superuser 132 | 133 | # Or change password 134 | docker compose exec fiduswriter venv/bin/fiduswriter changepassword username 135 | ``` 136 | 137 | ## Production Deployment 138 | 139 | For production use: 140 | 141 | 1. **Set DEBUG=False** in `configuration.py` 142 | 2. **Use PostgreSQL** instead of SQLite 143 | 3. **Set up SSL/TLS** with reverse proxy (nginx/apache) 144 | 4. **Configure email** server 145 | 5. **Set up backups** (automated) 146 | 147 | See [README.md](README.md) for detailed production setup instructions. 148 | 149 | ## Getting Help 150 | 151 | - **Documentation**: [README.md](README.md) 152 | - **Quick Reference**: [docs/QUICK_REFERENCE.md](docs/QUICK_REFERENCE.md) 153 | - **Upgrade Guide**: [UPGRADE_GUIDE.md](UPGRADE_GUIDE.md) 154 | - **Issues**: [GitHub Issues](https://github.com/fiduswriter/fiduswriter-docker/issues) 155 | - **Fiduswriter Help**: [Official Documentation](https://www.fiduswriter.org/help/) 156 | 157 | --- 158 | 159 | Happy writing! 📝 160 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | 4 | 5 | ## Type of Change 6 | 7 | 8 | 9 | - [ ] Bug fix (non-breaking change that fixes an issue) 10 | - [ ] New feature (non-breaking change that adds functionality) 11 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 12 | - [ ] Documentation update 13 | - [ ] Code refactoring 14 | - [ ] CI/CD improvements 15 | - [ ] Dependency update 16 | - [ ] Other (please describe): 17 | 18 | ## Motivation and Context 19 | 20 | 21 | 22 | 23 | Fixes #(issue) 24 | Related to #(issue) 25 | 26 | ## Changes Made 27 | 28 | 29 | 30 | - 31 | - 32 | - 33 | 34 | ## How Has This Been Tested? 35 | 36 | 37 | 38 | - [ ] Tested locally with `make test` 39 | - [ ] All linters pass with `make lint` 40 | - [ ] Built Docker image successfully 41 | - [ ] Container starts without errors 42 | - [ ] Verified application functionality 43 | - [ ] Tested with Fiduswriter version: [specify version] 44 | - [ ] Tested on OS: [specify OS] 45 | 46 | ### Test Configuration 47 | 48 | - **Docker Version**: 49 | - **Docker Compose Version**: 50 | - **OS**: 51 | 52 | ## Screenshots (if applicable) 53 | 54 | 55 | 56 | ## Checklist 57 | 58 | 59 | 60 | - [ ] My code follows the project's style guidelines 61 | - [ ] I have performed a self-review of my own code 62 | - [ ] I have commented my code, particularly in hard-to-understand areas 63 | - [ ] I have made corresponding changes to the documentation 64 | - [ ] My changes generate no new warnings or errors 65 | - [ ] I have added tests that prove my fix is effective or that my feature works 66 | - [ ] New and existing unit tests pass locally with my changes 67 | - [ ] Any dependent changes have been merged and published 68 | - [ ] I have updated the CHANGELOG.md file 69 | - [ ] I have checked my code and corrected any misspellings 70 | 71 | ## Pre-commit Checks 72 | 73 | - [ ] Pre-commit hooks pass: `make pre-commit-all` 74 | - [ ] Dockerfile linting passes: `make lint-docker` 75 | - [ ] Shell script linting passes: `make lint-shell` 76 | - [ ] Markdown linting passes: `make lint-markdown` 77 | 78 | ## Breaking Changes 79 | 80 | 81 | 82 | - [ ] This PR introduces breaking changes 83 | 84 |
85 | Breaking changes details and migration guide 86 | 87 | 88 | 89 |
90 | 91 | ## Dependencies 92 | 93 | 94 | 95 | - [ ] This PR adds new dependencies 96 | 97 |
98 | Dependencies list 99 | 100 | 101 | 102 |
103 | 104 | ## Deployment Notes 105 | 106 | 107 | 108 | - [ ] Requires configuration changes 109 | - [ ] Requires data migration 110 | - [ ] Requires manual intervention 111 | - [ ] Other deployment considerations 112 | 113 |
114 | Deployment instructions 115 | 116 | 117 | 118 |
119 | 120 | ## Additional Notes 121 | 122 | 123 | 124 | ## Reviewer Notes 125 | 126 | 127 | 128 | --- 129 | 130 | **By submitting this pull request, I confirm that:** 131 | 132 | - [ ] My contribution is made under the project's license 133 | - [ ] I have read and agree to the [contributing guidelines](CONTRIBUTING.md) 134 | -------------------------------------------------------------------------------- /UPGRADE_GUIDE.md: -------------------------------------------------------------------------------- 1 | # Upgrade Guide 2 | 3 | This guide helps you upgrade your Fiduswriter Docker installation. 4 | 5 | ## Quick Upgrade 6 | 7 | ```bash 8 | # 1. Backup your data (CRITICAL!) 9 | make backup 10 | 11 | # 2. Stop containers 12 | make down 13 | 14 | # 3. Update repository 15 | git pull 16 | 17 | # 4. Rebuild image 18 | make build 19 | 20 | # 5. Start containers 21 | make up 22 | 23 | # 6. Run migrations (if needed) 24 | make migrate 25 | 26 | # 7. Verify 27 | make logs 28 | ``` 29 | 30 | ## Upgrading from 4.0.16 to Current 31 | 32 | ### What's Changed 33 | 34 | - **Python**: Upgraded to 3.14.2 from deadsnakes PPA (handled in container) 35 | - **Build Process**: Now supports build arguments for version specification 36 | - **Tooling**: Added Makefile, pre-commit hooks, and helper scripts 37 | 38 | ### Prerequisites 39 | 40 | 1. **Backup your data**: 41 | 42 | ```bash 43 | make backup 44 | # Or: tar -czf backup-$(date +%Y%m%d).tar.gz volumes/data 45 | ``` 46 | 47 | 2. **Check disk space**: 48 | 49 | ```bash 50 | df -h volumes/data 51 | ``` 52 | 53 | 3. **Save configuration**: 54 | 55 | ```bash 56 | cp volumes/data/configuration.py volumes/data/configuration.py.backup 57 | ``` 58 | 59 | ### Upgrade Steps 60 | 61 | ```bash 62 | # Stop services 63 | docker compose down 64 | 65 | # Update code 66 | git pull origin main 67 | 68 | # Rebuild with no cache (recommended for major upgrades) 69 | docker compose build --no-cache 70 | 71 | # Start services 72 | docker compose up -d 73 | 74 | # Run migrations if needed 75 | docker compose exec fiduswriter venv/bin/fiduswriter migrate 76 | 77 | # Check logs 78 | docker compose logs -f 79 | ``` 80 | 81 | ### Post-Upgrade Verification 82 | 83 | 1. **Check container status**: `docker compose ps` 84 | 2. **Verify Python version**: `docker compose exec fiduswriter python3 --version` 85 | 3. **Test application**: Open and verify functionality 86 | 4. **Check logs**: `docker compose logs` for any errors 87 | 88 | ### Configuration 89 | 90 | Your existing `configuration.py` should work without changes. No action required. 91 | 92 | ## Rollback Procedure 93 | 94 | If you need to rollback: 95 | 96 | ```bash 97 | # Stop current version 98 | docker compose down 99 | 100 | # Checkout previous version 101 | git checkout v4.0.16 102 | 103 | # Restore configuration if needed 104 | cp volumes/data/configuration.py.backup volumes/data/configuration.py 105 | 106 | # Rebuild and start 107 | docker compose build --no-cache 108 | docker compose up -d 109 | ``` 110 | 111 | ## Troubleshooting 112 | 113 | ### Container Won't Start 114 | 115 | ```bash 116 | # Check logs 117 | docker compose logs 118 | 119 | # Fix permissions 120 | sudo chown -R 999:999 volumes/data 121 | sudo chmod -R 755 volumes/data 122 | docker compose restart 123 | ``` 124 | 125 | ### Port Conflicts 126 | 127 | ```bash 128 | # Change port in .env 129 | echo "HOST_PORT=8080" >> .env 130 | docker compose down 131 | docker compose up -d 132 | ``` 133 | 134 | ### Migration Errors 135 | 136 | ```bash 137 | # Show current migrations 138 | docker compose exec fiduswriter venv/bin/fiduswriter showmigrations 139 | 140 | # Run migrations 141 | docker compose exec fiduswriter venv/bin/fiduswriter migrate 142 | ``` 143 | 144 | ### Database Issues 145 | 146 | ```bash 147 | # Run Django checks 148 | docker compose exec fiduswriter venv/bin/fiduswriter check 149 | 150 | # Collect static files 151 | docker compose exec fiduswriter venv/bin/fiduswriter collectstatic --noinput 152 | ``` 153 | 154 | ## Best Practices 155 | 156 | 1. **Always backup before upgrading** 157 | 2. **Test in staging first** if possible 158 | 3. **Read the changelog** before upgrading 159 | 4. **Monitor logs** during and after upgrade 160 | 5. **Verify all functionality** after upgrade 161 | 6. **Use version pinning** in production (not `latest`) 162 | 163 | ## Getting Help 164 | 165 | If you encounter issues: 166 | 167 | 1. Check the [README.md](README.md) 168 | 2. Search [GitHub Issues](https://github.com/fiduswriter/fiduswriter-docker/issues) 169 | 3. Check [Fiduswriter Documentation](https://www.fiduswriter.org/help/) 170 | 4. Create a new issue with full error logs and environment details 171 | 172 | --- 173 | 174 | **Remember**: Always backup before upgrading! 175 | -------------------------------------------------------------------------------- /volumes/config/configuration.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | ############################################# 4 | # Django settings for Fidus Writer project. # 5 | ############################################# 6 | 7 | # After copying this file to configuration.py, adjust the below settings to 8 | # work with your setup. 9 | 10 | # If you don't want to show debug messages, set DEBUG to False. 11 | 12 | DEBUG = True 13 | # SOURCE_MAPS - allows any value used by webpack devtool 14 | # https://webpack.js.org/configuration/devtool/ 15 | # For example 16 | # SOURCE_MAPS = 'cheap-module-source-map' # fast - line numbers only 17 | # SOURCE_MAPS = 'source-map' # slow - line and column number 18 | SOURCE_MAPS = False 19 | 20 | PROJECT_PATH = os.environ.get("PROJECT_PATH", "/fiduswriter") 21 | # SRC_PATH is the root path of the FW sources. 22 | SRC_PATH = os.environ.get("SRC_PATH", "/fiduswriter") 23 | 24 | DATABASES = { 25 | "default": { 26 | "ENGINE": "django.db.backends.sqlite3", 27 | "NAME": os.path.join("/data", "fiduswriter.sql"), 28 | "CONN_MAX_AGE": 15, 29 | } 30 | } 31 | 32 | # Interval between document saves 33 | # DOC_SAVE_INTERVAL = 1 34 | 35 | # Migrate, transpile JavaScript and install required fixtures automatically 36 | # when starting runserver. You might want to turn this off on a production 37 | # server. The default is the opposite of DEBUG 38 | 39 | # AUTO_SETUP = False 40 | 41 | # This determines whether the server is used for testing and will let the 42 | # users know upon signup know that their documents may disappear. 43 | TEST_SERVER = True 44 | # This is the contact email that will be shown in various places all over 45 | # the site. 46 | CONTACT_EMAIL = "mail@email.com" 47 | # If websockets is running on a non-standard port, add it here: 48 | WS_PORT = False 49 | 50 | ADMINS = (("Your Name", "your_email@example.com"),) 51 | 52 | # Whether anyone surfing to the site can open an account with a login/password. 53 | REGISTRATION_OPEN = True 54 | 55 | # Whether user's can login using passwords (if not, they will only be able to 56 | # sign in using social accounts). 57 | PASSWORD_LOGIN = True 58 | 59 | # Whether anyone surfing to the site can open an account or login with a 60 | # socialaccount. 61 | SOCIALACCOUNT_OPEN = True 62 | 63 | ACCOUNT_EMAIL_VERIFICATION = 'optional' 64 | 65 | # This determines whether there is a star labeled "Free" on the login page 66 | IS_FREE = True 67 | 68 | MANAGERS = ADMINS 69 | 70 | INSTALLED_APPS = [ 71 | "book", 72 | "citation_api_import", 73 | "pandoc", 74 | # "pandoc_on_server", 75 | # "gitrepo_export", 76 | # "languagetool", 77 | # "ojs", 78 | # "phplist", 79 | # "payment", 80 | # "website" 81 | # If you want to enable one or several of the social network login options 82 | # below, make sure you add the authorization keys at: 83 | # http://SERVER.COM/admin/socialaccount/socialapp/ 84 | # 'allauth.socialaccount.providers.facebook', 85 | # 'allauth.socialaccount.providers.google', 86 | # 'allauth.socialaccount.providers.twitter', 87 | # 'allauth.socialaccount.providers.github', 88 | # 'allauth.socialaccount.providers.linkedin', 89 | # 'allauth.socialaccount.providers.openid', 90 | # 'allauth.socialaccount.providers.persona', 91 | # 'allauth.socialaccount.providers.soundcloud', 92 | # 'allauth.socialaccount.providers.stackexchange', 93 | ] 94 | 95 | # A list of allowed hostnames of this Fidus Writer installation 96 | ALLOWED_HOSTS = [ 97 | "localhost", 98 | "127.0.0.1", 99 | ] 100 | 101 | PORTS = [8000] 102 | 103 | # Allow the server to listen to all network interfaces (0.0.0.0) instead of just localhost 104 | # SECURITY WARNING: Setting this to True in production environments could expose your server 105 | LISTEN_TO_ALL_INTERFACES = False 106 | 107 | # Enable/disable the service worker (default is True) 108 | USE_SERVICE_WORKER = True 109 | 110 | # The maximum size of user uploaded images in bytes. 111 | MEDIA_MAX_SIZE = 10 * 1024 * 1024 # 10 MB 112 | 113 | # Don't share the SECRET_KEY with anyone. 114 | SECRET_KEY = '5vx9556(@55hv4z(4o)-urkb8a1cq+cg86a$49g%vsm2pt+n+z' # pragma: allowlist secret 115 | 116 | # Media files handling 117 | MEDIA_ROOT = os.path.join('/data', 'media') 118 | MEDIA_URL = '/media/' 119 | 120 | # Static files handling 121 | STATIC_ROOT = os.path.join(PROJECT_PATH, 'static/') 122 | STATIC_URL = '/static/' 123 | -------------------------------------------------------------------------------- /.github/workflows/update-version.yml: -------------------------------------------------------------------------------- 1 | name: Check for Fiduswriter Updates 2 | 3 | on: 4 | schedule: 5 | # Check daily at 3 AM UTC 6 | - cron: '0 3 * * *' 7 | workflow_dispatch: 8 | 9 | jobs: 10 | check-update: 11 | name: Check for New Fiduswriter Version 12 | runs-on: ubuntu-latest 13 | permissions: 14 | contents: write 15 | pull-requests: write 16 | steps: 17 | - name: Checkout code 18 | uses: actions/checkout@v4 19 | with: 20 | fetch-depth: 0 21 | 22 | - name: Set up Python 23 | uses: actions/setup-python@v5 24 | with: 25 | python-version: '3.12' 26 | 27 | - name: Get latest 4.0.x version from PyPI 28 | id: get-version 29 | run: | 30 | # Get latest version in 4.0.x series from PyPI 31 | LATEST=$(python3 << 'EOF' 32 | import json 33 | import urllib.request 34 | import re 35 | 36 | url = "https://pypi.org/pypi/fiduswriter/json" 37 | with urllib.request.urlopen(url) as response: 38 | data = json.loads(response.read()) 39 | versions = list(data['releases'].keys()) 40 | # Filter for 4.0.x versions 41 | pattern = re.compile(r'^4\.0\.\d+$') 42 | v40_versions = [v for v in versions if pattern.match(v)] 43 | # Sort by version number 44 | v40_versions.sort(key=lambda s: [int(u) for u in s.split('.')]) 45 | if v40_versions: 46 | print(v40_versions[-1]) 47 | else: 48 | print("4.0.17") 49 | EOF 50 | ) 51 | echo "latest=$LATEST" >> $GITHUB_OUTPUT 52 | echo "Latest 4.0.x version: $LATEST" 53 | 54 | - name: Get current version from Dockerfile 55 | id: current-version 56 | run: | 57 | CURRENT=$(grep -oP 'ARG FIDUSWRITER_VERSION=\K[0-9.]+' Dockerfile || echo "4.0.17") 58 | echo "current=$CURRENT" >> $GITHUB_OUTPUT 59 | echo "Current version: $CURRENT" 60 | 61 | - name: Compare versions 62 | id: compare 63 | run: | 64 | CURRENT="${{ steps.current-version.outputs.current }}" 65 | LATEST="${{ steps.get-version.outputs.latest }}" 66 | 67 | if [ "$CURRENT" != "$LATEST" ]; then 68 | echo "needs_update=true" >> $GITHUB_OUTPUT 69 | echo "New version available: $LATEST (current: $CURRENT)" 70 | else 71 | echo "needs_update=false" >> $GITHUB_OUTPUT 72 | echo "Already at latest version: $CURRENT" 73 | fi 74 | 75 | - name: Update Dockerfile 76 | if: steps.compare.outputs.needs_update == 'true' 77 | run: | 78 | LATEST="${{ steps.get-version.outputs.latest }}" 79 | sed -i "s/ARG FIDUSWRITER_VERSION=.*/ARG FIDUSWRITER_VERSION=$LATEST/" Dockerfile 80 | echo "Updated Dockerfile to version $LATEST" 81 | 82 | - name: Create Pull Request 83 | if: steps.compare.outputs.needs_update == 'true' 84 | uses: peter-evans/create-pull-request@v6 85 | with: 86 | token: ${{ secrets.GITHUB_TOKEN }} 87 | commit-message: | 88 | chore: Update Fiduswriter to version ${{ steps.get-version.outputs.latest }} 89 | 90 | Automatically detected new Fiduswriter version on PyPI. 91 | branch: update/fiduswriter-${{ steps.get-version.outputs.latest }} 92 | delete-branch: true 93 | title: 'chore: Update Fiduswriter to version ${{ steps.get-version.outputs.latest }}' 94 | body: | 95 | ## Automated Fiduswriter Version Update 96 | 97 | This PR updates Fiduswriter from version ${{ steps.current-version.outputs.current }} to ${{ steps.get-version.outputs.latest }}. 98 | 99 | ### Changes 100 | - Updated `Dockerfile` to use Fiduswriter version ${{ steps.get-version.outputs.latest }} 101 | 102 | ### Release Notes 103 | See the [Fiduswriter releases](https://github.com/fiduswriter/fiduswriter/releases) for details about what changed in this version. 104 | 105 | ### Checklist 106 | - [ ] Review the Fiduswriter changelog 107 | - [ ] Test the Docker image builds successfully 108 | - [ ] Test the application starts correctly 109 | - [ ] Verify no breaking changes affect the Docker setup 110 | 111 | --- 112 | 113 | 🤖 This PR was created automatically by the version check workflow. 114 | labels: | 115 | automated 116 | dependencies 117 | update 118 | assignees: ${{ github.repository_owner }} 119 | 120 | - name: Notify if update found 121 | if: steps.compare.outputs.needs_update == 'true' 122 | run: | 123 | echo "::notice::New Fiduswriter version ${{ steps.get-version.outputs.latest }} detected. PR created." 124 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: help build up down restart logs shell test lint format clean prune 2 | 3 | # Default target 4 | help: 5 | @echo "Fiduswriter Docker - Available Commands" 6 | @echo "========================================" 7 | @echo "" 8 | @echo "Development:" 9 | @echo " make build - Build the Docker image" 10 | @echo " make up - Start the containers" 11 | @echo " make down - Stop the containers" 12 | @echo " make restart - Restart the containers" 13 | @echo " make logs - View container logs" 14 | @echo " make shell - Open a shell in the container" 15 | @echo "" 16 | @echo "Maintenance:" 17 | @echo " make clean - Remove stopped containers and volumes" 18 | @echo " make prune - Deep clean (removes images too)" 19 | @echo " make setup-data - Set up data directory with correct permissions" 20 | @echo " make backup - Backup data directory" 21 | @echo "" 22 | @echo "Quality Assurance:" 23 | @echo " make lint - Run all linters" 24 | @echo " make lint-docker - Lint Dockerfile with hadolint" 25 | @echo " make lint-shell - Lint shell scripts" 26 | @echo " make lint-markdown - Lint markdown files" 27 | @echo " make test - Run tests" 28 | @echo " make pre-commit - Install pre-commit hooks" 29 | @echo "" 30 | @echo "User Management:" 31 | @echo " make superuser - Create a superuser account" 32 | @echo "" 33 | @echo "Version Management:" 34 | @echo " make version - Show current Fiduswriter version" 35 | @echo " make check-update - Check for new Fiduswriter versions" 36 | @echo "" 37 | 38 | # Build the Docker image 39 | build: 40 | @echo "Building Docker image..." 41 | docker compose build 42 | 43 | # Build with specific version 44 | build-version: 45 | @read -p "Enter Fiduswriter version (e.g., 4.0.17): " version; \ 46 | docker compose build --build-arg FIDUSWRITER_VERSION=$$version 47 | 48 | # Start containers 49 | up: 50 | @echo "Starting containers..." 51 | docker compose up -d 52 | 53 | # Stop containers 54 | down: 55 | @echo "Stopping containers..." 56 | docker compose down 57 | 58 | # Restart containers 59 | restart: down up 60 | 61 | # View logs 62 | logs: 63 | docker compose logs -f 64 | 65 | # Open shell in container 66 | shell: 67 | docker compose exec fiduswriter /bin/bash 68 | 69 | # Create superuser 70 | superuser: 71 | @echo "Creating superuser account..." 72 | docker compose exec fiduswriter venv/bin/fiduswriter createsuperuser 73 | 74 | # Run migrations 75 | migrate: 76 | @echo "Running database migrations..." 77 | docker compose exec fiduswriter venv/bin/fiduswriter migrate 78 | 79 | # Clean up 80 | clean: 81 | @echo "Cleaning up stopped containers and volumes..." 82 | docker compose down -v 83 | 84 | # Deep clean 85 | prune: clean 86 | @echo "Removing Docker images..." 87 | docker rmi fiduswriter-docker-fiduswriter 2>/dev/null || true 88 | @echo "Pruning Docker system..." 89 | docker system prune -f 90 | 91 | # Set up data directory with correct permissions 92 | setup-data: 93 | @echo "Setting up data directory..." 94 | mkdir -p volumes/data volumes/data/media 95 | sudo chown -R 999:999 volumes/ 96 | sudo chmod -R 755 volumes/ 97 | @echo "Data directory ready!" 98 | 99 | # Backup data directory 100 | backup: 101 | @echo "Creating backup..." 102 | tar -czf backup-$$(date +%Y%m%d-%H%M%S).tar.gz volumes/data 103 | @echo "Backup created!" 104 | 105 | # Install pre-commit hooks 106 | pre-commit: 107 | @echo "Installing pre-commit hooks..." 108 | pip install pre-commit 109 | pre-commit install 110 | @echo "Pre-commit hooks installed!" 111 | 112 | # Run pre-commit on all files 113 | pre-commit-all: 114 | @echo "Running pre-commit on all files..." 115 | pre-commit run --all-files 116 | 117 | # Lint everything 118 | lint: lint-docker lint-shell lint-markdown 119 | 120 | # Lint Dockerfile 121 | lint-docker: 122 | @echo "Linting Dockerfile..." 123 | @command -v hadolint >/dev/null 2>&1 || { \ 124 | echo "hadolint not found. Install with: brew install hadolint"; \ 125 | exit 1; \ 126 | } 127 | hadolint Dockerfile --config .hadolint.yaml 128 | 129 | # Lint shell scripts 130 | lint-shell: 131 | @echo "Linting shell scripts..." 132 | @command -v shellcheck >/dev/null 2>&1 || { \ 133 | echo "shellcheck not found. Install with: brew install shellcheck"; \ 134 | exit 1; \ 135 | } 136 | shellcheck start-fiduswriter.sh 137 | 138 | # Lint markdown files 139 | lint-markdown: 140 | @echo "Linting markdown files..." 141 | @command -v markdownlint >/dev/null 2>&1 || { \ 142 | echo "markdownlint not found. Install with: npm install -g markdownlint-cli"; \ 143 | exit 1; \ 144 | } 145 | markdownlint '**/*.md' --config .markdownlint.json --ignore node_modules 146 | 147 | # Run tests 148 | test: 149 | @echo "Running tests..." 150 | docker compose build 151 | docker compose up -d 152 | @echo "Waiting for container to start..." 153 | @sleep 10 154 | @echo "Checking container status..." 155 | docker compose ps 156 | @echo "Checking logs..." 157 | docker compose logs 158 | docker compose down 159 | 160 | # Show current version 161 | version: 162 | @echo "Current Fiduswriter version in Dockerfile:" 163 | @grep -oP 'ARG FIDUSWRITER_VERSION=\K[0-9.]+' Dockerfile || echo "Version not found" 164 | 165 | # Check for updates 166 | check-update: 167 | @echo "Checking for new Fiduswriter versions..." 168 | @python3 -c "import json, urllib.request, re; \ 169 | url = 'https://pypi.org/pypi/fiduswriter/json'; \ 170 | response = urllib.request.urlopen(url); \ 171 | data = json.loads(response.read()); \ 172 | versions = list(data['releases'].keys()); \ 173 | pattern = re.compile(r'^4\.0\.\d+$$'); \ 174 | v40_versions = [v for v in versions if pattern.match(v)]; \ 175 | v40_versions.sort(key=lambda s: [int(u) for u in s.split('.')]); \ 176 | print('Latest 4.0.x version:', v40_versions[-1] if v40_versions else 'Unknown')" 177 | @echo "" 178 | @echo "Current version:" 179 | @make version 180 | 181 | # Show container status 182 | status: 183 | @echo "Container status:" 184 | docker compose ps 185 | @echo "" 186 | @echo "Resource usage:" 187 | docker stats --no-stream fiduswriter 2>/dev/null || echo "Container not running" 188 | 189 | # Development setup (complete) 190 | dev-setup: setup-data build up superuser 191 | @echo "" 192 | @echo "Development environment ready!" 193 | @echo "Access Fiduswriter at: http://localhost:8000" 194 | @echo "" 195 | 196 | # Production setup guide 197 | prod-setup: 198 | @echo "Production Setup Guide" 199 | @echo "=====================" 200 | @echo "" 201 | @echo "1. Set up data directory:" 202 | @echo " make setup-data" 203 | @echo "" 204 | @echo "2. Edit configuration:" 205 | @echo " cp volumes/data/configuration.py.example volumes/data/configuration.py" 206 | @echo " nano volumes/data/configuration.py" 207 | @echo "" 208 | @echo "3. Update ALLOWED_HOSTS, set DEBUG=False, configure email, etc." 209 | @echo "" 210 | @echo "4. Build and start:" 211 | @echo " make build" 212 | @echo " make up" 213 | @echo "" 214 | @echo "5. Create superuser:" 215 | @echo " make superuser" 216 | @echo "" 217 | @echo "6. Configure site in admin panel:" 218 | @echo " http://your-domain/admin/sites/site/" 219 | @echo "" 220 | -------------------------------------------------------------------------------- /scripts/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # 3 | # Quick setup script for Fiduswriter Docker 4 | # This script helps set up a new Fiduswriter Docker installation 5 | 6 | set -e 7 | 8 | # Colors for output 9 | RED='\033[0;31m' 10 | GREEN='\033[0;32m' 11 | YELLOW='\033[1;33m' 12 | BLUE='\033[0;34m' 13 | NC='\033[0m' # No Color 14 | 15 | # Function to print colored output 16 | print_info() { 17 | printf "${BLUE}ℹ ${NC}%s\n" "$1" 18 | } 19 | 20 | print_success() { 21 | printf "${GREEN}✓${NC} %s\n" "$1" 22 | } 23 | 24 | print_warning() { 25 | printf "${YELLOW}⚠${NC} %s\n" "$1" 26 | } 27 | 28 | print_error() { 29 | printf "${RED}✗${NC} %s\n" "$1" 30 | } 31 | 32 | print_header() { 33 | echo "" 34 | echo "======================================" 35 | echo "$1" 36 | echo "======================================" 37 | echo "" 38 | } 39 | 40 | # Check if required commands exist 41 | check_requirements() { 42 | print_header "Checking Requirements" 43 | 44 | missing_requirements=0 45 | 46 | if ! command -v docker >/dev/null 2>&1; then 47 | print_error "Docker is not installed" 48 | missing_requirements=1 49 | else 50 | print_success "Docker is installed: $(docker --version)" 51 | fi 52 | 53 | if ! command -v docker >/dev/null 2>&1 || ! docker compose version >/dev/null 2>&1; then 54 | print_error "Docker Compose is not installed or not available" 55 | missing_requirements=1 56 | else 57 | print_success "Docker Compose is available: $(docker compose version)" 58 | fi 59 | 60 | if [ $missing_requirements -eq 1 ]; then 61 | print_error "Please install missing requirements before continuing" 62 | exit 1 63 | fi 64 | } 65 | 66 | # Set up data directory with correct permissions 67 | setup_data_directory() { 68 | print_header "Setting Up Data Directory" 69 | 70 | if [ ! -d "volumes/data" ]; then 71 | print_info "Creating data directory..." 72 | mkdir -p volumes/data volumes/data/media 73 | print_success "Data directory created" 74 | else 75 | print_info "Data directory already exists" 76 | fi 77 | 78 | print_info "Setting correct permissions (user:group 999:999)..." 79 | 80 | # Check if we need sudo 81 | if [ -w "volumes" ]; then 82 | chown -R 999:999 volumes/ 2>/dev/null || { 83 | print_warning "Cannot change ownership without sudo" 84 | sudo chown -R 999:999 volumes/ 85 | } 86 | chmod -R 755 volumes/ 87 | else 88 | sudo chown -R 999:999 volumes/ 89 | sudo chmod -R 755 volumes/ 90 | fi 91 | 92 | print_success "Permissions set correctly" 93 | } 94 | 95 | # Create environment file 96 | setup_environment() { 97 | print_header "Setting Up Environment" 98 | 99 | if [ -f ".env" ]; then 100 | print_warning ".env file already exists, skipping creation" 101 | return 102 | fi 103 | 104 | if [ -f ".env.example" ]; then 105 | print_info "Creating .env file from .env.example..." 106 | cp .env.example .env 107 | print_success ".env file created" 108 | print_info "You can customize settings in .env file" 109 | else 110 | print_warning ".env.example not found, skipping .env creation" 111 | fi 112 | } 113 | 114 | # Build Docker image 115 | build_image() { 116 | print_header "Building Docker Image" 117 | 118 | print_info "This may take several minutes on first run..." 119 | 120 | if docker compose build; then 121 | print_success "Docker image built successfully" 122 | else 123 | print_error "Failed to build Docker image" 124 | exit 1 125 | fi 126 | } 127 | 128 | # Start containers 129 | start_containers() { 130 | print_header "Starting Containers" 131 | 132 | if docker compose up -d; then 133 | print_success "Containers started successfully" 134 | else 135 | print_error "Failed to start containers" 136 | exit 1 137 | fi 138 | 139 | print_info "Waiting for Fiduswriter to initialize..." 140 | sleep 5 141 | } 142 | 143 | # Check container status 144 | check_status() { 145 | print_header "Checking Container Status" 146 | 147 | if docker compose ps | grep -q "fiduswriter.*Up"; then 148 | print_success "Fiduswriter container is running" 149 | else 150 | print_error "Fiduswriter container is not running properly" 151 | print_info "Checking logs..." 152 | docker compose logs --tail=50 153 | exit 1 154 | fi 155 | } 156 | 157 | # Prompt for superuser creation 158 | create_superuser() { 159 | print_header "Create Superuser Account" 160 | 161 | print_info "You need to create a superuser account to access the admin panel" 162 | printf "Would you like to create a superuser now? (y/n): " 163 | read -r response 164 | 165 | if [ "$response" = "y" ] || [ "$response" = "Y" ]; then 166 | print_info "Creating superuser..." 167 | docker compose exec fiduswriter venv/bin/fiduswriter createsuperuser 168 | print_success "Superuser created" 169 | else 170 | print_info "You can create a superuser later with:" 171 | print_info " docker compose exec fiduswriter venv/bin/fiduswriter createsuperuser" 172 | print_info "Or:" 173 | print_info " make superuser" 174 | fi 175 | } 176 | 177 | # Display completion message 178 | show_completion_message() { 179 | print_header "Setup Complete!" 180 | 181 | print_success "Fiduswriter is now running!" 182 | echo "" 183 | print_info "Access Fiduswriter at: http://localhost:8000" 184 | echo "" 185 | print_info "Important next steps:" 186 | echo " 1. Change the site domain in admin panel: http://localhost:8000/admin/sites/site/" 187 | echo " 2. Configure ALLOWED_HOSTS in volumes/data/configuration.py" 188 | echo " 3. Set up email server (optional) in volumes/data/configuration.py" 189 | echo "" 190 | print_info "Useful commands:" 191 | echo " View logs: docker compose logs -f" 192 | echo " Stop containers: docker compose down" 193 | echo " Restart containers: docker compose restart" 194 | echo " Create superuser: docker compose exec fiduswriter venv/bin/fiduswriter createsuperuser" 195 | echo "" 196 | echo " Or use make commands:" 197 | echo " make logs, make down, make restart, make superuser" 198 | echo " Run 'make help' for all available commands" 199 | echo "" 200 | print_info "For production deployment:" 201 | echo " - Set DEBUG=False in volumes/data/configuration.py" 202 | echo " - Configure a proper database (PostgreSQL recommended)" 203 | echo " - Set up SSL/TLS with a reverse proxy (nginx/apache)" 204 | echo " - Configure email server for notifications" 205 | echo " - Set up regular backups (make backup)" 206 | echo "" 207 | } 208 | 209 | # Main setup flow 210 | main() { 211 | clear 212 | 213 | cat << "EOF" 214 | _____ _ _ _ _ _ _ 215 | | ___(_) __| |_ _ _| | | |_ __(_) |_ ___ _ __ 216 | | |_ | |/ _` | | | / __| | __/ _ \ | __/ _ \ '__| 217 | | _| | | (_| | |_| \__ \ | || __/ | || __/ | 218 | |_| |_|\__,_|\__,_|___/ \__\___|_|\__\___|_| 219 | 220 | Docker Quick Setup 221 | EOF 222 | 223 | echo "" 224 | print_info "This script will help you set up Fiduswriter Docker" 225 | echo "" 226 | 227 | # Check if already running 228 | if docker compose ps 2>/dev/null | grep -q "fiduswriter.*Up"; then 229 | print_warning "Fiduswriter is already running!" 230 | printf "Do you want to continue anyway? (y/n): " 231 | read -r response 232 | if [ "$response" != "y" ] && [ "$response" != "Y" ]; then 233 | print_info "Setup cancelled" 234 | exit 0 235 | fi 236 | fi 237 | 238 | check_requirements 239 | setup_data_directory 240 | setup_environment 241 | build_image 242 | start_containers 243 | check_status 244 | 245 | echo "" 246 | create_superuser 247 | echo "" 248 | show_completion_message 249 | } 250 | 251 | # Run main function 252 | main "$@" 253 | -------------------------------------------------------------------------------- /scripts/check-version.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Check for the latest Fiduswriter version in the 4.0.x series from PyPI. 4 | 5 | This script queries the PyPI JSON API to find the latest version in the 4.0.x 6 | series and compares it with the current version in the Dockerfile. 7 | """ 8 | 9 | import json 10 | import re 11 | import sys 12 | import urllib.request 13 | from typing import List, Optional, Tuple 14 | 15 | 16 | def get_latest_version_from_pypi(series: str = "4.0") -> Optional[str]: 17 | """ 18 | Get the latest Fiduswriter version from PyPI for a specific series. 19 | 20 | Args: 21 | series: Version series to check (e.g., "4.0") 22 | 23 | Returns: 24 | Latest version string or None if not found 25 | """ 26 | try: 27 | url = "https://pypi.org/pypi/fiduswriter/json" 28 | with urllib.request.urlopen(url, timeout=10) as response: 29 | data = json.loads(response.read()) 30 | versions = list(data['releases'].keys()) 31 | 32 | # Filter for versions in the specified series 33 | pattern = re.compile(rf'^{re.escape(series)}\.\d+$') 34 | series_versions = [v for v in versions if pattern.match(v)] 35 | 36 | # Sort versions by converting to tuples of integers 37 | series_versions.sort(key=lambda s: [int(u) for u in s.split('.')]) 38 | 39 | if series_versions: 40 | return series_versions[-1] 41 | return None 42 | except Exception as e: 43 | print(f"Error fetching version from PyPI: {e}", file=sys.stderr) 44 | return None 45 | 46 | 47 | def get_current_version_from_dockerfile(dockerfile_path: str = "Dockerfile") -> Optional[str]: 48 | """ 49 | Extract the current Fiduswriter version from the Dockerfile. 50 | 51 | Args: 52 | dockerfile_path: Path to the Dockerfile 53 | 54 | Returns: 55 | Current version string or None if not found 56 | """ 57 | try: 58 | with open(dockerfile_path, 'r') as f: 59 | content = f.read() 60 | match = re.search(r'ARG FIDUSWRITER_VERSION=([0-9.]+)', content) 61 | if match: 62 | return match.group(1) 63 | return None 64 | except FileNotFoundError: 65 | print(f"Error: Dockerfile not found at {dockerfile_path}", file=sys.stderr) 66 | return None 67 | except Exception as e: 68 | print(f"Error reading Dockerfile: {e}", file=sys.stderr) 69 | return None 70 | 71 | 72 | def parse_version(version: str) -> Tuple[int, ...]: 73 | """ 74 | Parse a version string into a tuple of integers. 75 | 76 | Args: 77 | version: Version string (e.g., "4.0.17") 78 | 79 | Returns: 80 | Tuple of integers (e.g., (4, 0, 17)) 81 | """ 82 | return tuple(int(x) for x in version.split('.')) 83 | 84 | 85 | def compare_versions(v1: str, v2: str) -> int: 86 | """ 87 | Compare two version strings. 88 | 89 | Args: 90 | v1: First version string 91 | v2: Second version string 92 | 93 | Returns: 94 | -1 if v1 < v2, 0 if v1 == v2, 1 if v1 > v2 95 | """ 96 | parts1 = parse_version(v1) 97 | parts2 = parse_version(v2) 98 | 99 | if parts1 < parts2: 100 | return -1 101 | elif parts1 > parts2: 102 | return 1 103 | else: 104 | return 0 105 | 106 | 107 | def get_all_versions_in_series(series: str = "4.0") -> List[str]: 108 | """ 109 | Get all available versions in a series from PyPI. 110 | 111 | Args: 112 | series: Version series to check (e.g., "4.0") 113 | 114 | Returns: 115 | List of version strings 116 | """ 117 | try: 118 | url = "https://pypi.org/pypi/fiduswriter/json" 119 | with urllib.request.urlopen(url, timeout=10) as response: 120 | data = json.loads(response.read()) 121 | versions = list(data['releases'].keys()) 122 | 123 | # Filter for versions in the specified series 124 | pattern = re.compile(rf'^{re.escape(series)}\.\d+$') 125 | series_versions = [v for v in versions if pattern.match(v)] 126 | 127 | # Sort versions 128 | series_versions.sort(key=lambda s: [int(u) for u in s.split('.')]) 129 | 130 | return series_versions 131 | except Exception as e: 132 | print(f"Error fetching versions from PyPI: {e}", file=sys.stderr) 133 | return [] 134 | 135 | 136 | def main(): 137 | """Main function to check and report version information.""" 138 | import argparse 139 | 140 | parser = argparse.ArgumentParser( 141 | description="Check for the latest Fiduswriter version from PyPI" 142 | ) 143 | parser.add_argument( 144 | "--series", 145 | default="4.0", 146 | help="Version series to check (default: 4.0)" 147 | ) 148 | parser.add_argument( 149 | "--dockerfile", 150 | default="Dockerfile", 151 | help="Path to Dockerfile (default: Dockerfile)" 152 | ) 153 | parser.add_argument( 154 | "--list", 155 | action="store_true", 156 | help="List all available versions in the series" 157 | ) 158 | parser.add_argument( 159 | "--json", 160 | action="store_true", 161 | help="Output results in JSON format" 162 | ) 163 | parser.add_argument( 164 | "--quiet", 165 | action="store_true", 166 | help="Only output version numbers" 167 | ) 168 | 169 | args = parser.parse_args() 170 | 171 | if args.list: 172 | versions = get_all_versions_in_series(args.series) 173 | if args.json: 174 | print(json.dumps({"series": args.series, "versions": versions}, indent=2)) 175 | else: 176 | if not args.quiet: 177 | print(f"Available versions in {args.series}.x series:") 178 | for version in versions: 179 | print(version) 180 | return 0 181 | 182 | # Get current version from Dockerfile 183 | current_version = get_current_version_from_dockerfile(args.dockerfile) 184 | 185 | # Get latest version from PyPI 186 | latest_version = get_latest_version_from_pypi(args.series) 187 | 188 | if args.json: 189 | result = { 190 | "current_version": current_version, 191 | "latest_version": latest_version, 192 | "update_available": False, 193 | "series": args.series 194 | } 195 | 196 | if current_version and latest_version: 197 | comparison = compare_versions(current_version, latest_version) 198 | result["update_available"] = comparison < 0 199 | result["is_current"] = comparison == 0 200 | result["is_ahead"] = comparison > 0 201 | 202 | print(json.dumps(result, indent=2)) 203 | return 0 204 | 205 | if args.quiet: 206 | if latest_version: 207 | print(latest_version) 208 | return 0 209 | 210 | # Display results 211 | print("=" * 60) 212 | print("Fiduswriter Version Check") 213 | print("=" * 60) 214 | 215 | if current_version: 216 | print(f"Current version (Dockerfile): {current_version}") 217 | else: 218 | print("Current version: Not found in Dockerfile") 219 | 220 | if latest_version: 221 | print(f"Latest version (PyPI): {latest_version}") 222 | else: 223 | print(f"Latest version: Not found on PyPI (series {args.series}.x)") 224 | return 1 225 | 226 | print("=" * 60) 227 | 228 | # Compare versions 229 | if current_version and latest_version: 230 | comparison = compare_versions(current_version, latest_version) 231 | 232 | if comparison < 0: 233 | print(f"⚠️ UPDATE AVAILABLE: {current_version} → {latest_version}") 234 | print("\nTo update, run:") 235 | print(f" sed -i 's/ARG FIDUSWRITER_VERSION=.*/ARG FIDUSWRITER_VERSION={latest_version}/' Dockerfile") 236 | print(" make build") 237 | return 1 238 | elif comparison == 0: 239 | print(f"✅ You are using the latest version ({current_version})") 240 | return 0 241 | else: 242 | print(f"ℹ️ You are ahead of the latest PyPI version") 243 | print(f" (Current: {current_version}, PyPI: {latest_version})") 244 | return 0 245 | 246 | return 1 247 | 248 | 249 | if __name__ == "__main__": 250 | sys.exit(main()) 251 | -------------------------------------------------------------------------------- /.github/workflows/docker-build.yml: -------------------------------------------------------------------------------- 1 | name: Build and Publish Docker Images 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | tags: 8 | - 'v*.*.*' 9 | pull_request: 10 | branches: 11 | - main 12 | workflow_dispatch: 13 | inputs: 14 | fiduswriter_version: 15 | description: 'Fiduswriter version to build (e.g., 4.0.17)' 16 | required: false 17 | default: '4.0.17' 18 | schedule: 19 | # Check for new 4.0.x releases weekly (Sunday at 2 AM UTC) 20 | - cron: '0 2 * * 0' 21 | 22 | jobs: 23 | check-version: 24 | name: Check Latest Fiduswriter Version 25 | runs-on: ubuntu-latest 26 | outputs: 27 | latest_version: ${{ steps.get-version.outputs.version }} 28 | should_build: ${{ steps.compare.outputs.should_build }} 29 | steps: 30 | - name: Checkout code 31 | uses: actions/checkout@v4 32 | 33 | - name: Set up Python 34 | uses: actions/setup-python@v5 35 | with: 36 | python-version: '3.12' 37 | 38 | - name: Get latest 4.0.x version from PyPI 39 | id: get-version 40 | run: | 41 | # Get latest version in 4.0.x series from PyPI 42 | LATEST=$(python3 << 'EOF' 43 | import json 44 | import urllib.request 45 | import re 46 | 47 | url = "https://pypi.org/pypi/fiduswriter/json" 48 | with urllib.request.urlopen(url) as response: 49 | data = json.loads(response.read()) 50 | versions = list(data['releases'].keys()) 51 | # Filter for 4.0.x versions 52 | pattern = re.compile(r'^4\.0\.\d+$') 53 | v40_versions = [v for v in versions if pattern.match(v)] 54 | # Sort by version number 55 | v40_versions.sort(key=lambda s: [int(u) for u in s.split('.')]) 56 | if v40_versions: 57 | print(v40_versions[-1]) 58 | else: 59 | print("4.0.17") 60 | EOF 61 | ) 62 | echo "version=$LATEST" >> $GITHUB_OUTPUT 63 | echo "Latest 4.0.x version: $LATEST" 64 | 65 | - name: Compare with current version 66 | id: compare 67 | run: | 68 | CURRENT_VERSION=$(grep -oP 'ARG FIDUSWRITER_VERSION=\K[0-9.]+' Dockerfile || echo "4.0.16") 69 | LATEST_VERSION="${{ steps.get-version.outputs.version }}" 70 | echo "Current version in Dockerfile: $CURRENT_VERSION" 71 | echo "Latest version on PyPI: $LATEST_VERSION" 72 | 73 | if [ "$CURRENT_VERSION" != "$LATEST_VERSION" ] || [ "${{ github.event_name }}" = "workflow_dispatch" ] || [ "${{ github.event_name }}" = "push" ]; then 74 | echo "should_build=true" >> $GITHUB_OUTPUT 75 | echo "Build should proceed" 76 | else 77 | echo "should_build=false" >> $GITHUB_OUTPUT 78 | echo "No new version detected, skipping build" 79 | fi 80 | 81 | build-and-push: 82 | name: Build and Push Docker Images 83 | runs-on: ubuntu-latest 84 | needs: check-version 85 | if: needs.check-version.outputs.should_build == 'true' 86 | permissions: 87 | contents: write 88 | packages: write 89 | steps: 90 | - name: Checkout code 91 | uses: actions/checkout@v4 92 | 93 | - name: Set up QEMU 94 | uses: docker/setup-qemu-action@v3 95 | 96 | - name: Set up Docker Buildx 97 | uses: docker/setup-buildx-action@v3 98 | 99 | - name: Log in to Docker Hub 100 | if: github.event_name != 'pull_request' 101 | uses: docker/login-action@v3 102 | with: 103 | username: ${{ secrets.DOCKER_USERNAME }} 104 | password: ${{ secrets.DOCKER_PASSWORD }} 105 | 106 | - name: Log in to GitHub Container Registry 107 | if: github.event_name != 'pull_request' 108 | uses: docker/login-action@v3 109 | with: 110 | registry: ghcr.io 111 | username: ${{ github.actor }} 112 | password: ${{ secrets.GITHUB_TOKEN }} 113 | 114 | - name: Determine version 115 | id: version 116 | run: | 117 | if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ -n "${{ github.event.inputs.fiduswriter_version }}" ]; then 118 | VERSION="${{ github.event.inputs.fiduswriter_version }}" 119 | else 120 | VERSION="${{ needs.check-version.outputs.latest_version }}" 121 | fi 122 | echo "version=$VERSION" >> $GITHUB_OUTPUT 123 | echo "Building version: $VERSION" 124 | 125 | # Extract version components 126 | IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION" 127 | echo "major=$MAJOR" >> $GITHUB_OUTPUT 128 | echo "minor=$MINOR" >> $GITHUB_OUTPUT 129 | echo "patch=$PATCH" >> $GITHUB_OUTPUT 130 | 131 | - name: Update Dockerfile with latest version 132 | if: github.event_name != 'pull_request' 133 | run: | 134 | VERSION="${{ steps.version.outputs.version }}" 135 | sed -i "s/ARG FIDUSWRITER_VERSION=.*/ARG FIDUSWRITER_VERSION=$VERSION/" Dockerfile 136 | 137 | if git diff --quiet Dockerfile; then 138 | echo "No changes to Dockerfile" 139 | else 140 | git config --local user.email "github-actions[bot]@users.noreply.github.com" 141 | git config --local user.name "github-actions[bot]" 142 | git add Dockerfile 143 | git commit -m "chore: Update Fiduswriter to version $VERSION" 144 | git push || echo "Push failed, may be behind main" 145 | fi 146 | 147 | - name: Extract metadata for Docker 148 | id: meta 149 | uses: docker/metadata-action@v5 150 | with: 151 | images: | 152 | fiduswriter/fiduswriter 153 | ghcr.io/${{ github.repository }} 154 | tags: | 155 | type=raw,value=latest,enable={{is_default_branch}} 156 | type=raw,value=${{ steps.version.outputs.version }} 157 | type=raw,value=${{ steps.version.outputs.major }}.${{ steps.version.outputs.minor }} 158 | type=raw,value=${{ steps.version.outputs.major }} 159 | type=ref,event=pr 160 | type=sha,prefix={{branch}}- 161 | 162 | - name: Build and push Docker image 163 | uses: docker/build-push-action@v5 164 | with: 165 | context: . 166 | file: ./Dockerfile 167 | platforms: linux/amd64,linux/arm64 168 | push: ${{ github.event_name != 'pull_request' }} 169 | tags: ${{ steps.meta.outputs.tags }} 170 | labels: ${{ steps.meta.outputs.labels }} 171 | build-args: | 172 | FIDUSWRITER_VERSION=${{ steps.version.outputs.version }} 173 | cache-from: type=gha 174 | cache-to: type=gha,mode=max 175 | 176 | - name: Update Docker Hub description 177 | if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main' 178 | uses: peter-evans/dockerhub-description@v4 179 | with: 180 | username: ${{ secrets.DOCKER_USERNAME }} 181 | password: ${{ secrets.DOCKER_PASSWORD }} 182 | repository: fiduswriter/fiduswriter 183 | short-description: "Official Docker image for Fiduswriter - A collaborative online writing tool" 184 | readme-filepath: ./README.md 185 | 186 | - name: Create GitHub Release 187 | if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main' 188 | uses: softprops/action-gh-release@v1 189 | with: 190 | tag_name: v${{ steps.version.outputs.version }} 191 | name: Fiduswriter ${{ steps.version.outputs.version }} 192 | body: | 193 | ## Fiduswriter Docker Image v${{ steps.version.outputs.version }} 194 | 195 | This release contains the Docker image for Fiduswriter version ${{ steps.version.outputs.version }}. 196 | 197 | ### Docker Images 198 | 199 | ```bash 200 | docker pull fiduswriter/fiduswriter:${{ steps.version.outputs.version }} 201 | docker pull fiduswriter/fiduswriter:${{ steps.version.outputs.major }}.${{ steps.version.outputs.minor }} 202 | docker pull fiduswriter/fiduswriter:${{ steps.version.outputs.major }} 203 | docker pull fiduswriter/fiduswriter:latest 204 | ``` 205 | 206 | ### What's Changed 207 | - Updated Fiduswriter to version ${{ steps.version.outputs.version }} 208 | - Built with Python 3.14.2 209 | - Multi-architecture support (amd64, arm64) 210 | 211 | See [Fiduswriter Changelog](https://github.com/fiduswriter/fiduswriter/releases) for application changes. 212 | draft: false 213 | prerelease: false 214 | env: 215 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 216 | continue-on-error: true 217 | 218 | test-image: 219 | name: Test Docker Image 220 | runs-on: ubuntu-latest 221 | needs: [check-version, build-and-push] 222 | if: needs.check-version.outputs.should_build == 'true' 223 | steps: 224 | - name: Checkout code 225 | uses: actions/checkout@v4 226 | 227 | - name: Build test image 228 | run: | 229 | docker build --build-arg FIDUSWRITER_VERSION=${{ needs.check-version.outputs.latest_version }} -t fiduswriter-test . 230 | 231 | - name: Run smoke tests 232 | run: | 233 | # Start container 234 | docker run -d --name fiduswriter-test -p 8000:8000 fiduswriter-test 235 | 236 | # Wait for startup (increased timeout) 237 | echo "Waiting for Fiduswriter to start..." 238 | for i in {1..30}; do 239 | if docker logs fiduswriter-test 2>&1 | grep -q "Starting development server"; then 240 | echo "Fiduswriter started successfully" 241 | break 242 | fi 243 | echo "Waiting... ($i/30)" 244 | sleep 2 245 | done 246 | 247 | # Show logs 248 | echo "Container logs:" 249 | docker logs fiduswriter-test 250 | 251 | # Check if container is still running 252 | if ! docker ps | grep -q fiduswriter-test; then 253 | echo "ERROR: Container stopped unexpectedly" 254 | docker logs fiduswriter-test 255 | exit 1 256 | fi 257 | 258 | # Verify Python version 259 | echo "Checking Python version..." 260 | docker exec fiduswriter-test python3 --version 261 | 262 | # Verify Fiduswriter version 263 | echo "Checking Fiduswriter version..." 264 | docker exec fiduswriter-test /fiduswriter/venv/bin/pip show fiduswriter 265 | 266 | # Cleanup 267 | docker stop fiduswriter-test 268 | docker rm fiduswriter-test 269 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Fiduswriter Docker 2 | 3 | Thank you for your interest in contributing to the Fiduswriter Docker project! This document 4 | provides guidelines and instructions for contributing. 5 | 6 | ## Table of Contents 7 | 8 | - [Code of Conduct](#code-of-conduct) 9 | - [Getting Started](#getting-started) 10 | - [Development Setup](#development-setup) 11 | - [Making Changes](#making-changes) 12 | - [Testing](#testing) 13 | - [Submitting Changes](#submitting-changes) 14 | - [Style Guidelines](#style-guidelines) 15 | - [Reporting Issues](#reporting-issues) 16 | 17 | ## Code of Conduct 18 | 19 | This project follows the [Fiduswriter Code of Conduct](https://github.com/fiduswriter/fiduswriter/blob/main/CODE_OF_CONDUCT.md). 20 | By participating, you are expected to uphold this code. 21 | 22 | ## Getting Started 23 | 24 | 1. **Fork the repository** on GitHub 25 | 2. **Clone your fork** locally: 26 | 27 | ```bash 28 | git clone https://github.com/YOUR-USERNAME/fiduswriter-docker.git 29 | cd fiduswriter-docker 30 | ``` 31 | 32 | 3. **Add upstream remote**: 33 | 34 | ```bash 35 | git remote add upstream https://github.com/fiduswriter/fiduswriter-docker.git 36 | ``` 37 | 38 | ## Development Setup 39 | 40 | ### Prerequisites 41 | 42 | - Docker and Docker Compose 43 | - Python 3.8+ (for pre-commit hooks) 44 | - Git 45 | - Make (optional, but recommended) 46 | 47 | ### Install Development Tools 48 | 49 | ```bash 50 | # Install pre-commit hooks 51 | make pre-commit 52 | 53 | # Or manually 54 | pip install pre-commit 55 | pre-commit install 56 | ``` 57 | 58 | ### Install Linters (Optional) 59 | 60 | For local linting without pre-commit: 61 | 62 | ```bash 63 | # macOS (using Homebrew) 64 | brew install hadolint shellcheck 65 | npm install -g markdownlint-cli 66 | 67 | # Ubuntu/Debian 68 | sudo apt-get install shellcheck 69 | wget -O hadolint https://github.com/hadolint/hadolint/releases/download/v2.12.0/hadolint-Linux-x86_64 70 | chmod +x hadolint 71 | sudo mv hadolint /usr/local/bin/ 72 | 73 | npm install -g markdownlint-cli 74 | ``` 75 | 76 | ### Build and Test Locally 77 | 78 | ```bash 79 | # Complete development setup 80 | make dev-setup 81 | 82 | # Or step by step 83 | make setup-data 84 | make build 85 | make up 86 | make superuser 87 | 88 | # View logs 89 | make logs 90 | ``` 91 | 92 | ## Making Changes 93 | 94 | ### Branch Naming 95 | 96 | Use descriptive branch names: 97 | 98 | - `feature/description` - New features 99 | - `fix/description` - Bug fixes 100 | - `docs/description` - Documentation changes 101 | - `refactor/description` - Code refactoring 102 | - `test/description` - Test additions or fixes 103 | - `chore/description` - Maintenance tasks 104 | 105 | Example: 106 | 107 | ```bash 108 | git checkout -b feature/add-postgresql-support 109 | ``` 110 | 111 | ### Commit Messages 112 | 113 | Follow the [Conventional Commits](https://www.conventionalcommits.org/) specification: 114 | 115 | ``` 116 | (): 117 | 118 | 119 | 120 |