From 23736d858de62b631b8be36ba5e6f21768f255ce Mon Sep 17 00:00:00 2001 From: Paolo Pustorino Date: Sun, 18 Feb 2024 17:51:09 +0100 Subject: [PATCH] refs #147: Code docblocks exporter (#156) * refs #147: WIP on a custom docker image for GDQuest's gdscript-docs-maker. * refs #147: wip on the dockerfile for reference generator. * refs #147: A first version of the Code Reference generator is now working. Docs README explains how to use it. * refs #147: GDScript codeblocks are now highlighted in documentation site. Added support for ancillary languages that may come in handy (md, bash, yaml, json, dockerfile, makefile). * refs #147: Fixed typo in docs REAME.md file. --- .gitignore | 10 +- Dockerfile.DocsMaker | 46 +++ Dockerfile => Dockerfile.MkDocs | 0 Makefile | 23 +- docker-compose.yml | 2 +- docs/README.md | 26 +- docs/content/_assets/css/custom.css | 4 + docs/content/_assets/js/custom.js | 3 + docs/content/_assets/js/gdscript.min.js | 1 + .../scripting-reference/index.md | 1 - docs/mkdocs.yml | 9 + generate_reference | 271 ++++++++++++++++++ 12 files changed, 388 insertions(+), 8 deletions(-) create mode 100644 Dockerfile.DocsMaker rename Dockerfile => Dockerfile.MkDocs (100%) create mode 100644 docs/content/_assets/js/gdscript.min.js create mode 100755 generate_reference diff --git a/.gitignore b/.gitignore index 0c322278e..38af7d697 100644 --- a/.gitignore +++ b/.gitignore @@ -21,4 +21,12 @@ data_*/ popochiu popochiu/** game -game/** \ No newline at end of file +game/** + +# Documentation ignores +docs/**/*.import +docs/**/scripting-reference/*.md +!docs/**/scripting-reference/index.md +Collector*.gd +ReferenceCollectorCLI*.gd +reference.json \ No newline at end of file diff --git a/Dockerfile.DocsMaker b/Dockerfile.DocsMaker new file mode 100644 index 000000000..9949dcf1f --- /dev/null +++ b/Dockerfile.DocsMaker @@ -0,0 +1,46 @@ +FROM python:slim + +ARG GODOT_VERSION="4.1.3" +ARG GODOT_RELEASE_NAME="stable" +ARG GODOT_SUBDIR="" +ARG GODOT_PLATFORM="linux.x86_64" + +ARG GDM_COMMIT_SHA="07cb0d8f0af6da745260d7bfabfc475504c413f1" + +# Override user name at build. If build-arg is not passed, will create user named `host_user` +ARG DOCKER_USER=host_user +ARG DOCKER_USER_UID=1000 +ARG DOCKER_USER_GID=1000 + + +RUN addgroup --system --gid ${DOCKER_USER_GID} ${DOCKER_USER} \ + && adduser --system --uid ${DOCKER_USER_UID} --gid ${DOCKER_USER_GID} --home /home/${DOCKER_USER} ${DOCKER_USER} \ + # Ensure proper permissions on user's home directory + && chown -R ${DOCKER_USER}:${DOCKER_USER} /home/${DOCKER_USER} \ + # Install system-wide dependencies + && apt-get update && apt-get install -y --no-install-recommends unzip wget zip libfontconfig1 \ + && rm -rf /var/lib/apt/lists/* \ + # Install Godot from CDN + && wget -q https://downloads.tuxfamily.org/godotengine/${GODOT_VERSION}${GODOT_SUBDIR}/Godot_v${GODOT_VERSION}-${GODOT_RELEASE_NAME}_${GODOT_PLATFORM}.zip \ + && mkdir /home/${DOCKER_USER}/.cache \ + && mkdir -p /home/${DOCKER_USER}/.config/godot \ + && mkdir -p /home/${DOCKER_USER}/.local/share/godot/export_templates/${GODOT_VERSION}.${GODOT_RELEASE_NAME} \ + && unzip Godot_v${GODOT_VERSION}-${GODOT_RELEASE_NAME}_${GODOT_PLATFORM}.zip \ + && mv Godot_v${GODOT_VERSION}-${GODOT_RELEASE_NAME}_${GODOT_PLATFORM} /usr/local/bin/godot \ + && chmod a+x /usr/local/bin/godot \ + && rm -f Godot_v${GODOT_VERSION}-${GODOT_RELEASE_NAME}_${GODOT_PLATFORM}.zip \ + # Download gdscript-docs-maker source code + && wget https://github.com/GDQuest/gdscript-docs-maker/archive/${GDM_COMMIT_SHA}.zip -O gdscript-docs-maker.zip \ + && unzip gdscript-docs-maker.zip \ + && mv gdscript-docs-maker-${GDM_COMMIT_SHA} /app \ + && rm -f gdscript-docs-maker.zip + +WORKDIR /app + +COPY --chmod=777 generate_reference generate_reference + +# Install application dependencies +RUN python3 setup.py install + +# Run dockmaker script +ENTRYPOINT ["./generate_reference"] diff --git a/Dockerfile b/Dockerfile.MkDocs similarity index 100% rename from Dockerfile rename to Dockerfile.MkDocs diff --git a/Makefile b/Makefile index ecb08e688..4cba5639d 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,31 @@ +CURRENT_UID := $(shell id -u):$(shell id -g) + all: up -up: +docs-up: docker compose pull docker compose up -d -down: +docs-down: docker compose down +gdm-build: + docker build \ + --no-cache \ + --build-arg="DOCKER_USER_UID=$(shell id -u)" \ + --build-arg="DOCKER_USER_GID=$(shell id -g)" \ + -t popochiu-docs-maker:latest \ + -f Dockerfile.DocsMaker . + +gdm-generate: + docker run --rm \ + -v .:/project \ + -v ./docs/content/the-engine-handbook/scripting-reference:/output \ + -u $(CURRENT_UID) \ + popochiu-docs-maker:latest /project \ + -o /output \ + -d addons/popochiu/engine/ + cli: docker compose run --rm documentation bash diff --git a/docker-compose.yml b/docker-compose.yml index 7192e4134..6a8103c61 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,5 +10,5 @@ services: - com.dnsdock.alias=docs.popochiu.local build: context: . - dockerfile: Dockerfile + dockerfile: Dockerfile.MkDocs command: serve -a 0.0.0.0:80 \ No newline at end of file diff --git a/docs/README.md b/docs/README.md index 95a651e39..a76b26349 100644 --- a/docs/README.md +++ b/docs/README.md @@ -34,7 +34,7 @@ Please remember to install Docker as well as the Compose plugin. Make is provided by the `build-essential` metapackage on Ubuntu and derivatives, while on Arch and derivatives you can install `base-devel`: -* **Ubuntu**: `sudo apt install build-essentials` +* **Ubuntu**: `sudo apt install build-essential` * **Arch**: `sudo pacman -Sy base-devel` That's it. You can go to the "Run the docs" section to learn how to run your dock. @@ -78,18 +78,38 @@ About Make, there are different ways to install it: To run the documentation, just enter the project's directory and issue this command: -> `make local-docs-up' +> `make docs-up' This will start the Docker container, and will bind port 286 of the host to the running instance of MkDocs in the container. To view the docs live in your browser, just visit [http://localhost:286](http://localhost:286). To stop the container service, just issue -> `make local-docs-down` +> `make docs-down` The documentation supports live reloading, so your browser will automatically update when you save a file you're working on, create a new file or folder. Please, read the contribution rules before pushing changes to Popochiu Documentation. +## How to export scripting reference to the local development environment + +Scripting reference is automatically exported by GitHub Actions when the doc is published to production. +Should you want to export the refs locally to preview your work on the docs, a make command is available for that. + +**IMPORTANT:** a preliminary step to export the scripting reference is to build the necessary docker image. For that, issue: + +> `make gdm-build` + +and wait for the build to end successfully. Once it's done you can issue: + +> `make gdm-generate` + +to export all of the engine API docs to markdown format. +The exported refereces will be available in `The Engine Handbook > Scripting Reference` section of the documentation. + +**NOTE**: There is no live-reload of the source code. If you change the docblocks in the engine's source files, you will have to manually export local refs again. + +**NOTE**: Locally generated exports are ignored by Git. + ## How to publish the documentation to production MkDocs is automatically triggered by GitHub automation so that new versions of the documentation are published whit every new release. diff --git a/docs/content/_assets/css/custom.css b/docs/content/_assets/css/custom.css index e820032a2..00b8acd66 100644 --- a/docs/content/_assets/css/custom.css +++ b/docs/content/_assets/css/custom.css @@ -805,6 +805,10 @@ code, border: none; border-radius: 4px; } +.rst-content pre code { + color: inherit; + background-color: var(--code-background-color); +} .rst-content tt.literal, .rst-content code.literal { diff --git a/docs/content/_assets/js/custom.js b/docs/content/_assets/js/custom.js index f287084b1..cb62c0371 100644 --- a/docs/content/_assets/js/custom.js +++ b/docs/content/_assets/js/custom.js @@ -302,6 +302,9 @@ $(document).ready(() => { } } + // Activate Highlight.js syntax-highlighter + hljs.highlightAll(); + // Change indentation from spaces to tabs for codeblocks. const codeBlocks = document.querySelectorAll('.rst-content div[class^="highlight"] pre'); for (const codeBlock of codeBlocks) { diff --git a/docs/content/_assets/js/gdscript.min.js b/docs/content/_assets/js/gdscript.min.js new file mode 100644 index 000000000..3204130fa --- /dev/null +++ b/docs/content/_assets/js/gdscript.min.js @@ -0,0 +1 @@ +hljs.registerLanguage("gdscript",function(){"use strict";var e=e||{};function r(e){return{aliases:["godot","gdscript"],keywords:{keyword:"and in not or self void as assert breakpoint class class_name extends is func setget signal tool yield const enum export onready static var break continue if elif else for pass return match while remote sync master puppet remotesync mastersync puppetsync",built_in:"Color8 ColorN abs acos asin atan atan2 bytes2var cartesian2polar ceil char clamp convert cos cosh db2linear decimals dectime deg2rad dict2inst ease exp floor fmod fposmod funcref get_stack hash inst2dict instance_from_id inverse_lerp is_equal_approx is_inf is_instance_valid is_nan is_zero_approx len lerp lerp_angle linear2db load log max min move_toward nearest_po2 ord parse_json polar2cartesian posmod pow preload print_stack push_error push_warning rad2deg rand_range rand_seed randf randi randomize range_lerp round seed sign sin sinh smoothstep sqrt step_decimals stepify str str2var tan tanh to_json type_exists typeof validate_json var2bytes var2str weakref wrapf wrapi bool int float String NodePath Vector2 Rect2 Transform2D Vector3 Rect3 Plane Quat Basis Transform Color RID Object NodePath Dictionary Array PoolByteArray PoolIntArray PoolRealArray PoolStringArray PoolVector2Array PoolVector3Array PoolColorArray",literal:"true false null"},contains:[e.NUMBER_MODE,e.HASH_COMMENT_MODE,{className:"comment",begin:/"""/,end:/"""/},e.QUOTE_STRING_MODE,{variants:[{className:"function",beginKeywords:"func"},{className:"class",beginKeywords:"class"}],end:/:/,contains:[e.UNDERSCORE_TITLE_MODE]}]}}return e.exports=function(e){e.registerLanguage("gdscript",r)},e.exports.definer=r,e.exports.definer||e.exports}()); \ No newline at end of file diff --git a/docs/content/the-engine-handbook/scripting-reference/index.md b/docs/content/the-engine-handbook/scripting-reference/index.md index 3a0de6fc4..286a307ea 100644 --- a/docs/content/the-engine-handbook/scripting-reference/index.md +++ b/docs/content/the-engine-handbook/scripting-reference/index.md @@ -2,4 +2,3 @@ weight: 3020 empty: true --- - diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 49da5095c..7a0d9af1c 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -1,7 +1,16 @@ site_name: Popochiu extra_css: - _assets/css/custom.css + - https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/tomorrow-night-bright.min.css extra_javascript: + - https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js + - https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/markdown.min.js + - https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/bash.min.js + - https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/yaml.min.js + - https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/json.min.js + - https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/dockerfile.min.js + - https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/makefile.min.js + - _assets/js/gdscript.min.js - _assets/js/custom.js markdown_extensions: - admonition diff --git a/generate_reference b/generate_reference new file mode 100755 index 000000000..b3b191afb --- /dev/null +++ b/generate_reference @@ -0,0 +1,271 @@ +#!/usr/bin/env sh + +project_directory="$1" +output_directory="export" +directories_override="" +format="markdown" +author="developer" + +echo_help() { + cat <<'EOT' + Generate a code reference from GDScript + Usage: + generate_reference $project_directory [options] + + Required arguments: + + $project_directory -- path to your Godot project directory. + This directory or one of its subdirectories should contain a + project.godot file. + + Options: + + -h/--help -- Display this help message. + -o/--output-directory -- directory path to output the documentation into. + -d/--directory -- Name of a directory to find files and generate the code reference in the Godot project. + You can use the option multiple times to generate a reference for multiple directories. + -f/--format -- Either `markdown` or `hugo`. If `hugo`, the output document includes a TOML front-matter + at the top. Default: `markdown`. + -a/--author -- If --format is `hugo`, controls the author property in the TOML front-matter. + + + Usage example: + + generate_reference ~/Repositories/other/nakama-godot/project/ -o export-nakama -d addons + + This command walks files in the res://addons directory of the Godot Nakama project, and converts it + to markdown files output in ./export-nakama. +EOT + exit 0 +} + +get_godot_cmd() { + if command -v godot > /dev/null + then + echo godot + else + godotcmd="" + + if [ "$(echo $OSTYPE | head -c 6)" = "darwin" ] + then + godotcmd=$(find $(echo $PATH | tr ":" " ") -name "Godot*.app" -maxdepth 1 2>/dev/null | head -n 1 | tr -d '\n') + if [ "$(echo $godotcmd | tr -d '\n' | tail -c 4)" = ".app" ] + then + godotcmd="$godotcmd/Contents/MacOS/Godot" + fi + fi + + if [ "$godotcmd" = "" ] + then + if command -v zsh > /dev/null + then + godotcmd=$(zsh -c "whence -ap -m 'Godot*' | head -n 1") + elif command -v bash > /dev/null + then + godotcmd=$(bash -c "compgen -c Godot | head -n 1") + fi + fi + + if [ "$godotcmd" = "" ] + then + echo godot + else + echo $godotcmd + fi + fi +} + +is_gnu_sed(){ + sed --version >/dev/null 2>&1 +} + + +# Interpret arguments + +if [ $(echo $1 | head -c 1) != "-" ] +then + shift 1 +fi + +while getopts ':hodfa-:' OPTION; do + # Support of long options with this syntax: --format=hugo + if [ "$OPTION" = "-" ]; then + OPTION="${OPTARG%%=*}" + OPTARG="${OPTARG#$OPTION}" + OPTARG="${OPTARG#=}" + fi + + # Support of long options with this syntax: --format hugo + if [ "$OPTARG" = "" ] + then + OPTARG="$(eval echo \${$OPTIND})"; OPTIND=$(( $OPTIND + 1 )) + fi + if [ "$(echo $OPTARG | cut -c1)" = "-" ] + then + OPTARG="" + OPTIND=$(( $OPTIND - 1 )) + fi + + # Option processing + if [ "$OPTION" = "h" -o "$OPTION" = "help" ] + then + echo_help + elif [ "$OPTARG" = "" ] + then + echo "Missing value for option $OPTION. Try 'generate_reference --help' for more information" + exit 1 + else + case "$OPTION" in + o | output-directory) + output_directory="$OPTARG" + ;; + d | directory) + directories_override="$directories_override $OPTARG" + ;; + f | format) + format="$OPTARG" + ;; + a | author) + author="$OPTARG" + ;; + ?) + echo "Missing arguments. Try 'generate_reference --help' for more information" + exit 1 + ;; + esac + fi +done + +echo "Checking parameters" + +if test -z "$project_directory"; then + echo "Missing first parameter: project_directory." + exit 1 +fi + +if ! test -d "$project_directory"; then + echo "Directory $project_directory does not exist, exiting." + exit 1 +fi + +godot_project_file=$(find "$project_directory" -iname project.godot -print -quit) +if ! test -f "$godot_project_file"; then + echo "Could not find a project.godot file in $project_directory. This program needs a Godot project to work." + exit 1 +fi + + +ERROR_LOG=$(mktemp) +LOG=$(mktemp) +godot_project_dir=$(dirname "$godot_project_file") +godot=$(get_godot_cmd) + +$godot --version 2>"$ERROR_LOG" >/dev/null +test $? -eq 0 -o $? -eq 255 +godot_exec_ok=$? + +if [ $godot_exec_ok -eq 0 ] +then + version=$($godot --version | tail -n 1 | cut -c1-1) + + if [ "$version" = "3" ] + then + ref_collector="ReferenceCollectorCLI.gd" + path_collector="godot-scripts/Collector.gd" + path_ref_collector="godot-scripts/ReferenceCollectorCLI.gd" + else + ref_collector="ReferenceCollectorCLIGd4.gd" + path_collector="godot-scripts/CollectorGd4.gd" + path_ref_collector="godot-scripts/ReferenceCollectorCLIGd4.gd" + fi + + # Override the content of the directories variable in ReferenceCollectorCLI.gd if we got --directory arguments + file_ref_collector=$(mktemp) + cat $path_ref_collector > "$file_ref_collector" + if test "$directories_override" != ""; then + echo "Setting directories" + args=$(echo "$directories_override" | sed -r 's#([-/._a-zA-Z0-9]+)#"res://\1",#g' | sed -r 's/,$//') + + if is_gnu_sed + then + sed -ri "s#^var directories.+#var directories := [$args]#" "$file_ref_collector" + else + sed -i "" -r "s#^var directories.+#var directories := [$args]#" "$file_ref_collector" + fi + fi + + echo "Copying collectors to project directory" + + cp "$file_ref_collector" "$godot_project_dir/$(basename $path_ref_collector)" >/dev/null + cp $path_collector "$godot_project_dir" >/dev/null + + echo "Generating reference json data..." + + if [ "$version" = "3" ] + then + $godot --editor --quit --no-window --script $ref_collector \ + --path "$godot_project_dir" 2>"$ERROR_LOG" >"$LOG" + else + $godot --editor --quit --headless --script $ref_collector \ + --path "$godot_project_dir" 2>"$ERROR_LOG" >"$LOG" + fi + + godot_exec_ok=1 + if grep -q -F "Saved data to res://reference.json" "$LOG" >/dev/null 2>/dev/null + then + godot_exec_ok=0 + fi +fi + +if [ $godot_exec_ok -ne 0 ] +then + ERRORS=$(cat "$ERROR_LOG") + cat </dev/null +fi + +if ! python3 -m gdscript_docs_maker "$godot_project_dir/reference.json" --path "$output_directory" --format "$format" \ + --author "$author" 2>"$ERROR_LOG" +then + echo "Error running gdscript_docs_maker. This is the log:" + cat "$ERROR_LOG" + exit 1 +fi + +echo "Cleaning up..." +rm "$ERROR_LOG" >/dev/null +rm "$LOG" >/dev/null +rm "$godot_project_dir/$(basename $path_ref_collector)" >/dev/null +rm "$godot_project_dir/$(basename $path_collector)" >/dev/null +rm "$godot_project_dir/reference.json" >/dev/null + +exit 0 \ No newline at end of file