diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 37e8cd60..7301356c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,6 +14,7 @@ on: - "spec_files/**" - "post_install_files/**" - "press_kit/**" + - "docs/**" - ".github/workflows/build_iso.yml" push: branches: diff --git a/.github/workflows/deploy_docs.yml b/.github/workflows/deploy_docs.yml new file mode 100644 index 00000000..0b869d8e --- /dev/null +++ b/.github/workflows/deploy_docs.yml @@ -0,0 +1,184 @@ +name: Deploy documentation + +# Check for reference: +# https://github.com/s1rius/ezlog/blob/20dce11e6d324bb18f57dc7c7c6d4a8bf40064de/.github/workflows/publish_pages.yml + +on: + schedule: + - cron: "40 03 */2 * *" # Runs at 03:40, every 2 days + push: + branches: + - main + paths: + - docs/**/*.md + - docs/**/*.png + - docs/**/*.jpg + - docs/**/*.jpeg + - docs/**/*.po + - .github/workflows/deploy_docs.yml + - .github/workflows/install-mdbook/action.yml + - README*.md + - docs/book.toml + pull_request: + branches: + - main + paths: + - docs/**/*.md + - docs/**/*.png + - docs/**/*.jpg + - docs/**/*.jpeg + - docs/**/*.po + - .github/workflows/deploy_docs.yml + - .github/workflows/install-mdbook/action.yml + - README*.md + - docs/book.toml + workflow_dispatch: + +concurrency: + group: pages + cancel-in-progress: true + +env: + runner: ubuntu-latest + cache-mdbook-name: cache-mdbook-bins + EXTRA_LANGUAGES: + SITE_URL: /bazzite/ + MDBOOK_output__html__git_repository_url: "${{ github.server_url }}/${{ github.repository }}" + MDBOOK_output__html__edit_url_template: "${{ github.server_url }}/${{ github.repository }}/edit/${{ github.ref_name }}/docs/{path}" + +jobs: + deploy: + permissions: + contents: read # To push a branch + pages: write # To push to a GitHub Pages site + id-token: write # To update the deployment status + + runs-on: ubuntu-latest + # environment: + # name: github-pages + # url: ${{ steps.deployment.outputs.page_url }} + defaults: + run: + working-directory: ./docs + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install python dependencies + run: | + pip install --user requests + + - name: Cache mdbook + id: cache-mdbook + uses: actions/cache@v4 + with: + path: | + ~/.cargo/bin + ~/.local/share/headless-chrome + key: ${{ runner.os }}-build-${{ env.cache-mdbook-name }}-${{ hashFiles('.github/workflows/install-mdbook/action.yml') }} + + - name: Add mdbook to PATH + if: steps.cache-mdbook.outputs.cache-hit == 'true' + run: | + echo ~/.cargo/bin:$PATH >> $GITHUB_PATH + + - name: Install mdbook + if: steps.cache-mdbook.outputs.cache-hit != 'true' + uses: ./.github/workflows/install-mdbook + + # Necessary in order to have fetch_discourse_md.py available for mdbook-cmd + - name: Add docs/utils to PATH + run: | + echo $PWD/utils:$PATH >> $GITHUB_PATH + + - name: Test fetch_discourse_md.py + run: | + echo "::group::Try fetching a post from discourse" + fetch_discourse_md.py -d "https://universal-blue.discourse.group/docs?topic=31" 2>&1 >/dev/null + echo "::endgroup::" + + - name: Build book in English + env: + MDBOOK_output__html__site_url: ${{ env.SITE_URL }} + DEBUG: "1" + run: | + # This assumes your book is in the root of your repository. + # Just add a `cd` here if you need to change to another directory. + mdbook build -d $GITHUB_WORKSPACE/book + ( + cd $GITHUB_WORKSPACE/book + shopt -s dotglob + mv {html,pdf}/* ./ + ) + + - name: Build all translations + env: + DEBUG: "1" + run: | + for po_lang in ${{ env.EXTRA_LANGUAGES }}; do + echo "::group::Building $po_lang translation" + MDBOOK_BOOK__LANGUAGE=$po_lang \ + MDBOOK_OUTPUT__HTML__SITE_URL=${{ env.SITE_URL }}$po_lang/ \ + mdbook build -d $GITHUB_WORKSPACE/book/$po_lang + ( + cd $GITHUB_WORKSPACE/book/$po_lang + shopt -s dotglob + mv {html,pdf}/* ./ + ) + echo "::endgroup::" + done + + - name: Generate translation progress report + if: env.EXTRA_LANGUAGES != '' + continue-on-error: true + run: | + i18n-report report $GITHUB_WORKSPACE/book/i18n_report.html po/*.po + + - name: Setup Pages + uses: actions/configure-pages@v4 + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: ${{ github.workspace }}/book + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 + with: + token: ${{ github.token }} + + clean_cache: + needs: deploy + runs-on: ubuntu-latest + permissions: + contents: read + actions: write + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Cache Cleanup + env: + GH_TOKEN: ${{ github.token }} + run: | + set -x + old_caches=($(gh cache list \ + --repo ${{ github.repository }} \ + --key "${{ runner.os }}-build-${{ env.cache-mdbook-name }}-" \ + --sort created_at -O asc --json id | jq '.[:-1][].id')) + for id in "${old_caches[@]}"; do + gh cache delete $id + done + - name: Remove Page Artifacts + uses: remagpie/gha-remove-artifact@v1 + with: + only-name: github-pages + max-age: 7776000 # 90 days + max-count: 50 diff --git a/.github/workflows/generate_changelog.yml b/.github/workflows/generate_changelog.yml index a61d676b..18410987 100644 --- a/.github/workflows/generate_changelog.yml +++ b/.github/workflows/generate_changelog.yml @@ -1,5 +1,7 @@ on: push: + paths-ignore: + - docs/** branches: - main diff --git a/.github/workflows/install-mdbook/action.yml b/.github/workflows/install-mdbook/action.yml new file mode 100644 index 00000000..efb4327d --- /dev/null +++ b/.github/workflows/install-mdbook/action.yml @@ -0,0 +1,32 @@ +name: Install mdbook and dependencies + +description: Install mdbook with the dependencies we need. + +runs: + using: composite + steps: + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + # The --locked flag is important for reproducible builds. It also + # avoids breakage due to skews between mdbook and mdbook-svgbob. + - name: Install mdbook + run: cargo install --git https://github.com/HollowMan6/mdBook --rev b5ca7bc39ac2e8073dc2fb9d984c0e46c498c167 mdbook --locked + shell: bash + + - name: Install mdbook-i18n-helpers + run: cargo install mdbook-i18n-helpers --locked --version 0.3.5 + shell: bash + + - name: Install i18n-report + run: cargo install i18n-report --locked --version 0.2.0 + shell: bash + + - name: Install mdbook-pdf + run: cargo install mdbook-pdf --locked --version 0.1.10 --features fetch + shell: bash + + - name: Install mdbook-cmdrun + run: cargo install mdbook-cmdrun --locked --version 0.6.0 + shell: bash diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 00000000..a05ebd68 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,178 @@ +book +debug.txt +# Created by https://www.toptal.com/developers/gitignore/api/python +# Edit at https://www.toptal.com/developers/gitignore?templates=python + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +### Python Patch ### +# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration +poetry.toml + +# ruff +.ruff_cache/ + +# LSP config files +pyrightconfig.json + +# End of https://www.toptal.com/developers/gitignore/api/python diff --git a/docs/Justfile b/docs/Justfile new file mode 100644 index 00000000..b02c4581 --- /dev/null +++ b/docs/Justfile @@ -0,0 +1,74 @@ +export PATH := justfile_directory() + "/utils:" + env("PATH") + +_default: + just --list + +# Install dependencies required for documentation stuff +install_dependencies: + bash ./utils/install-deps.sh + +_build_messages_pot: + #!/usr/bin/bash + DEBUG=1 ./utils/pre-build.py -s src -o src.tmp + MDBOOK_BOOK__SRC="src.tmp" \ + MDBOOK_OUTPUT='{"xgettext": {}}' \ + mdbook build -d po + rm -r src.tmp + +# Check that a translation file exists, otherwise exit with 1 +_is_translation LANG: + [[ -f po/{{LANG}}.po ]] || { \ + echo "ERROR: 'po/{{LANG}}.po' does not exist."; \ + echo "Use 'just add_translation {{LANG}}' to create a new translation file"; \ + exit 1; } + +# Add a language to translate +add_translation LANG: _build_messages_pot + msginit -i po/messages.pot -l {{LANG}} -o po/{{LANG}}.po + +# Flatten a directory containing multiple mdbook outputs +_flatten_outputs OUTPUTS_DIR="./book": + #!/usr/bin/bash + cd {{ OUTPUTS_DIR }} + to_flatten=( \ + # Add here directories you want to flatten + $(ls -d "html" "pdf") \ + ) || true + for dir in "${to_flatten[@]}"; do + ( + shopt -s dotglob + mv $dir/* ./ + ) + done + +# Update a language with a fresh messages.pot +update_translation LANG: (_is_translation LANG) _build_messages_pot + msgmerge --update po/{{LANG}}.po po/messages.pot + +# Equivalent to 'mdbook build' +mdbook_build LANG="": + #!/usr/bin/bash + mdbook clean + mdbook build -d ./book && just _flatten_outputs + if [[ -n "{{LANG}}" ]]; then + MDBOOK_BOOK__LANGUAGE={{LANG}} mdbook build -d book/{{LANG}} && \ + just _flatten_outputs book/{{LANG}} + fi + +_serve_http DIR="./book": + python -m http.server -d {{DIR}} -b 127.0.0.1 3000 + +# Start a lightweight web server with a preview of the mdbook +mdbook_serve LANG="": + #!/usr/bin/bash + set -meo pipefail + just mdbook_build {{LANG}} + just _serve_http & + sleep 1 + printf '\n\n\n\n' + echo "Page ready at 'http://127.0.0.1:3000/{{LANG}}'" + fg + +# Same as 'mdbook_serve' but for a specific language +preview_translation LANG: (_is_translation LANG) + just mdbook_serve {{LANG}} \ No newline at end of file diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..5c34e717 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,237 @@ +# Contributing to Bazzite mdBook documentation + +## Introduction + +This is a guide that will show you: + +- How to write new documentation pages in mdBooks. +- How to transcribe documentation, from Discourse forums (https://universal-blue.discourse.group/) to mdBook pages. + +## Brief explanation in how to work with mdBook + +> _mdBook is a command line tool to create books with Markdown. It is ideal for creating product or API documentation, tutorials, course materials or anything that requires a clean, easily navigable and customizable presentation_ +> +> Source ~ https://rust-lang.github.io/mdBook/ + +**TL;DR**: Its a fancy way tool that allows us to create a documentation website with basic [Markdown](https://commonmark.org/help/). + +--- + +The essential part that cant be missing in a mdBook is the `SUMMARY.md` file. + +```md + + +# General + +- [📜 Bazzite's README](Bazzite_README.md) +- [❓️ FAQ](General/FAQ.md) +- [📖 Installation Guide](General/Installation_Guide/index.md) +- [📝 Desktop Environment Tweaks](General/Desktop_Environment_Tweaks.md) +- [🤝 Contributing to Bazzite](General/Contributing_to_Bazzite.md) +- [🎲 Gaming](Gaming/index.md) + - [Game Launchers](Gaming/Game_Launchers.md) + +# Steam Gaming Mode / Handheld & HTPC Hardware + +- [📺️ Steam Gaming Mode Overview](Handheld_and_HTPC_edition/Steam_Gaming_Mode/index.md) + - [Change Physical Keyboard Layout for Steam Gaming Mode](Handheld_and_HTPC_edition/Change_Physical_Keyboard_Layout_for_Steam_Gaming_Mode.md) +``` + +`SUMMARY.md` acts not only as a nice looking table of contents, but as indexer as well. + +**If a page is not listed in `SUMMARY.md`, it wont be included in the mdBook**\* + +\* Just so you are aware + +--- + +## Transcribe Discourse docs to mdBooks + +Requirements: + +- Markdown compatible code editor (ex.: Visual Studio Code) +- mdBook (can be installed with Homebrew\*) +- Git + +\* If you are using Bazzite or [similar](https://universal-blue.org/), chances are that you already have it installed. + +--- + +Best way to learn is with a real life example. We will transcribe https://universal-blue.discourse.group/docs?topic=2743 + +### 1. Basic preparation + +We will start with getting our utilities ready: + +1. A web browser with the Discourse doc page we want to transcribe. We will use for this example. +2. Our code editor. +3. A terminal open in the `docs` directory + + ```sh + $ cd docs + ``` + + Get sure we have `fetch_discourse_md.py` in there, we will need it + + ```sh + $ ls ./utils/fetch_discourse_md.py + ./utils/fetch_discourse_md.py + ``` + +### 2. Copy the post + +`fetch_discourse_md.py` is your friend for this task. + +1. Copy the URL of the document +2. In the terminal, pass the URL to `fetch_discourse_md.py` + + ```sh + $ ./utils/fetch_discourse_md.py "https://universal-blue.discourse.group/docs?topic=2743" | wl-copy + ``` + + Normally, `fetch_discourse_md.py` would dump the resulting markdown doc in the terminal output, with `wl-copy` we store it in our clipboard for now. + +3. Create the markdown file where we will store our document. The title of the post is "_Dual Boot Preliminary Setup and Post-Setup Guide_", so somewhere under "Advanced" should be fitting. + + > ⚠️ WARNING + > + > Just remember, ⚠️**DO NOT USE SPACES IN THE FILE NAME**⚠️. Is really important, spaces in filenames is going to bit us later in a future. + > Instead, use underscores `_` + + ![](./src/img/doc_guide_filename.jpg) + +### 4. Paste the document in the file + +![](./src/img/doc_guide_paste.jpg) + +### 5. Rewrite URLs + +We are almost done. The problem is `fetch_discourse_md.py` only will give us a dumped version of the Discourse document. + +There is posibly URLs that are pointing to other documentation posts in Discourse that we might have already in our mdBook. + +![](./src/img/doc_guide_discourse_url.jpg) + +The url in the image above is pointing to the _Steam Gaming Mode Overview (Handheld/HTPC)_ post. +At the time of writting this, we have that post avaliable in our mdBook, so we can simply replace that URL with ours + +![](./src/img/doc_guide_rewrite_url.jpg) + +In our case, the post is located in `../Handheld_and_HTPC_edition/Steam_Gaming_Mode/index.md` + +### 6. Link back in `SUMMARY.md` + +We can check how our post looks in mdBook, run in the terminal + +```sh +mdbook serve --open +``` + +Now, more likely you wont find our new added post. + +![](./src/img/doc_guide_where_did_go.jpg) + +If you take a look at [the brief explanation](#brief-explanation-in-how-to-work-with-mdbook), you will read about `SUMMARY.md`. Files not listed in there wont be processed by mdBook. + +Lets add our file there. + +![](./src/img/doc_guide_add_summary.jpg) + +And now our post is ready. + +![](./src/img/doc_guide_there_you_are.jpg) + +## Translate documentation + +> ⚠️ WARNING +> +> It is better to start translation once [transcription](#transcribe-discourse-docs-to-mdbooks) is settled to keep up. + +Translation isnt so straightforward as copying a markdown file and start working. + +We rely in [mdbook-i18n-helpers](https://github.com/google/mdbook-i18n-helpers) for translation, which uses [GNU Gettext](https://www.gnu.org/software/gettext/manual/html_node/index.html). + +We need some more dependencies in order to do translations, which can be installed with this script: + +```sh +bash docs/utils/install-deps.sh +``` + +
+ +Dependencies list
+Ignore if using install-deps.sh +
+ +- `.po` file editor (like [Poedit](https://flathub.org/apps/net.poedit.Poedit)) +- Rust's `cargo` (you can install rust by running + `brew install rustup; rustup-init`) +- `mdbook-i18n-helpers` (after installing rust, + `cargo install mdbook-i18n-helpers`) + +
+ +### 1. Basic preparation + +Move to `docs`, then build the `.pot` file + +```sh +cd docs +just build_messages_pot +``` + +This will create `po/messages.pot`, which acts as an index of text fragments +from all our markdown files. + +### 2. (Optional) Add a new language + +All translations files are stored in `docs/po/` in the form of `xx.po` files, `xx` referencing the language code following [ISO 639][ISO]. Per example, `es.po` would be an Spanish translation +file. + +To add a new language to the documentation, follows these steps: + +1. Get sure you did the [basic preparation](#1-basic-preparation-1) +2. Then run this, replacing `XX` with the [language code][ISO]: + + ```sh + just add_translation XX + ``` + + In my case, I'm going to create an Spanish translation file: + + ```sh + just add_translation es + ls po/ + # es.po messages.pot + ``` + +### 3. Working with a translation file + +![Poedit](./src/img/poedit.jpg) + +We will now open that `.po` file with our po editor (in my case is Poedit). + +We make some changes, hit Ctrl+S to save. + +Lets see the changes we had done with a preview. Run this: + +```sh +just preview_translation XX +``` + +In my case is `es` + +```sh +just preview_translation es +``` + +And there it is! + +![](./src/img/translation_example.jpg) + +## Write new documentation + +WIP + +[ISO]: https://en.wikipedia.org/wiki/List_of_ISO_639_language_codes diff --git a/docs/book.toml b/docs/book.toml new file mode 100644 index 00000000..15ff352d --- /dev/null +++ b/docs/book.toml @@ -0,0 +1,42 @@ +[book] +authors = ["nicknamenamenick", "Zeglius"] +language = "en" +multilingual = false +src = "src" +title = "Bazzite Documentation" + +[build] +use-default-preprocessors = false +create-missing = false + +[preprocessor.links] + +[output.html] +git-repository-url = "https://github.com/ublue-os/bazzite" +edit-url-template = "https://github.com/ublue-os/bazzite/edit/main/docs/{path}" +default-theme = "navy" +preferred-dark-theme = "navy" +additional-css = ["custom.css"] + +[output.html.fold] +enable = true + +[preprocessor.youtube-embed] +command = "python ./preprocessors/youtube-embed.py" + +[preprocessor.replace-urls] +command = "python ./preprocessors/replace-urls.py" +after = ["youtube-embed", "links"] +ignore = ["Introduction*"] + +[preprocessor.gettext] +after = ["links", "cmdrun"] + +[preprocessor.cmdrun] +before = ["replace-urls"] + +[output.pdf] + +[preprocessor.replace-urls.mappings] +# Here we add urls to be overriden +"https://universal-blue.discourse.group/docs?topic=561" = "/Introduction" diff --git a/docs/custom.css b/docs/custom.css new file mode 100644 index 00000000..f7f91971 --- /dev/null +++ b/docs/custom.css @@ -0,0 +1,8 @@ +h1.menu-title::before { + content: ""; + background-image: url("./favicon.svg"); + padding: 1em; + background-position: center center; + background-size: 1.5em; + background-repeat: no-repeat; +} \ No newline at end of file diff --git a/docs/preprocessors/format-author.py b/docs/preprocessors/format-author.py new file mode 100644 index 00000000..96a5f41f --- /dev/null +++ b/docs/preprocessors/format-author.py @@ -0,0 +1,88 @@ +__doc__ = """Example of a mdbook preprocessor""" + +import datetime +import json +import os +import re +import sys +from typing import Any + + +_DEBUG = os.getenv("DEBUG", "") + + +def debug(*obj) -> Any: + return obj + + +if _DEBUG in ["1", "yes"]: + _DEBUG_OUTPUT = "./debug.txt" + if os.path.exists(_DEBUG_OUTPUT): + os.truncate(_DEBUG_OUTPUT, 0) + + def debug(*obj) -> Any: + with open(_DEBUG_OUTPUT, "+a") as stdout: + print(f"DEBUG[{datetime.date.today()}]:", *obj, file=stdout) + return obj + + +def modify_content(content: str) -> str | None: + ############## MODIFY 'content' HERE ############## + """Alter the contents of each chapter + + Args: + content (str): The contents of a chapter received. Is in markdown format. + + Returns: + str | None: The chapter contents modified. + If `None`, the original content will be used instead + """ + + author_template = "
" + r"""Publisher: \g""" + "
" + author_pattern = r"\A(?P\w+)\s\|\s(?P(?P\d{4})-(?P\d{2})-(?P\d{2}))\s(?P