├── 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 |