From dfc82be3de903afb98cccf0d17d1c60e6a60f1aa Mon Sep 17 00:00:00 2001 From: Arjan Dikhoff Date: Thu, 10 Oct 2024 13:45:35 +0200 Subject: [PATCH] Add script that automatically changes Godot to Redot in all variations - Add Python script that intelligently changes filenames and contents. - Makes exceptions for different variations of capitalizations and hyphenation (like godot-engine, godotengine, Godot, GODOT, etc.) - Distinguishes between names that can be changed immediately versus names that need to change later, like subdomains that don't exist yet. - The goal was to eliminate every mention of Godot. However since we are aiming to stay compatible with Godot, at least some of the unimplemented names will remain so. - Removed some content that could only apply to Godot (like mentions of Godot foundation) - Change ci/cd pipeline scripts (in ./.github) to include the name change in the build - Tested successfully on forked repo - includes weekly offline docs and class sync - Also change contribution and PR hints - Change README.md to reflect the changes - Also add short remark on how to generate the name change locally --- .github/ISSUE_TEMPLATE/bug_report.md | 8 +- .github/ISSUE_TEMPLATE/config.yml | 16 +- .github/ISSUE_TEMPLATE/enhancement_request.md | 2 +- .github/PULL_REQUEST_TEMPLATE.md | 2 +- .github/workflows/build_offline_docs.yml | 18 +- .github/workflows/ci.yml | 6 +- .github/workflows/sync_class_ref.yml | 10 +- .gitignore | 2 + README.md | 52 +-- contributing/how_to_contribute.rst | 20 -- favicon.png | Bin 0 -> 23267 bytes img/README.md | 2 +- img/docs_logo.svg | 36 +- migrate.py | 316 ++++++++++++++++++ 14 files changed, 421 insertions(+), 69 deletions(-) create mode 100644 favicon.png create mode 100644 migrate.py diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index c9e3a2f3d61..9af86869377 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -6,7 +6,7 @@ labels: bug assignees: '' --- -**Your Godot version:** +**Your Redot version:** **Issue description:** @@ -14,6 +14,6 @@ assignees: '' If you know how to fix the issue you are reporting please consider opening a pull request. We provide a tutorial on -using git here: https://docs.godotengine.org/en/stable/community/contributing/pr_workflow.html, -writing documentation at https://docs.godotengine.org/en/stable/community/contributing/docs_writing_guidelines.html -and contributing to the class reference here: https://docs.godotengine.org/en/stable/community/contributing/updating_the_class_reference.html +using git here: https://docs.redotengine.org/en/stable/community/contributing/pr_workflow.html, +writing documentation at https://docs.redotengine.org/en/stable/community/contributing/docs_writing_guidelines.html +and contributing to the class reference here: https://docs.redotengine.org/en/stable/community/contributing/updating_the_class_reference.html diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 9b01966e08a..d8eabd1e1d4 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,14 +1,14 @@ blank_issues_enabled: false contact_links: - - name: Godot community channels - url: https://godotengine.org/community + - name: Redot community channels + url: https://redotengine.org/community about: Please ask for technical support on one of the other community channels, not here. - - name: Godot proposals - url: https://github.com/godotengine/godot-proposals - about: Please submit engine feature proposals on the Godot proposals repository, not here. + - name: Redot proposals + url: https://github.com/redot-engine/redot-proposals + about: Please submit engine feature proposals on the Redot proposals repository, not here. - - name: Main Godot repository - url: https://github.com/godotengine/godot - about: Report engine bugs on the main Godot repository + - name: Main Redot repository + url: https://github.com/redot-engine/redot + about: Report engine bugs on the main Redot repository diff --git a/.github/ISSUE_TEMPLATE/enhancement_request.md b/.github/ISSUE_TEMPLATE/enhancement_request.md index d6b2f3caa93..d7c3aed25a6 100644 --- a/.github/ISSUE_TEMPLATE/enhancement_request.md +++ b/.github/ISSUE_TEMPLATE/enhancement_request.md @@ -6,7 +6,7 @@ labels: enhancement assignees: '' --- -**Your Godot version:** +**Your Redot version:** **Issue description:** diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 62e3e211d3b..b37c87b763e 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -4,5 +4,5 @@ PRs can target other branches (e.g. `3.2`, `3.5`) if the same change was done in PRs must not target `stable`, as that branch is updated manually. The type of content accepted into the documentation is explained here: -https://docs.godotengine.org/en/latest/community/contributing/content_guidelines.html +https://docs.redotengine.org/en/latest/community/contributing/content_guidelines.html --> diff --git a/.github/workflows/build_offline_docs.yml b/.github/workflows/build_offline_docs.yml index 4499de52bb2..3c7b58ea872 100644 --- a/.github/workflows/build_offline_docs.yml +++ b/.github/workflows/build_offline_docs.yml @@ -26,12 +26,16 @@ jobs: sudo apt update sudo apt install parallel libwebp7 + - name: Migrate to Redot + run: | + python migrate.py . _migrated False + - name: Sphinx - Build HTML - run: make SPHINXOPTS='--color' html + run: make SPHINXOPTS='--color' SPHINXSOURCEDIR='./_migrated' html - uses: actions/upload-artifact@v4 with: - name: godot-docs-html-${{ matrix.branch }} + name: redot-docs-html-${{ matrix.branch }} path: _build/html # Keep the current build and the previous build (in case a scheduled build failed). # This makes it more likely to have at least one successful build available at all times. @@ -46,15 +50,15 @@ jobs: parallel --will-cite sed -i "s/\\.webp$/\\.png/g" ::: {about,community,contributing,getting_started,tutorials}/**/*.rst # Remove banners at the top of each page when building `latest`. - sed -i 's/"godot_is_latest": True/"godot_is_latest": False/' conf.py - sed -i 's/"godot_show_article_status": True/"godot_show_article_status": False/' conf.py + sed -i 's/"redot_is_latest": True/"redot_is_latest": False/' conf.py + sed -i 's/"redot_show_article_status": True/"redot_show_article_status": False/' conf.py - make SPHINXOPTS='--color' epub + make SPHINXOPTS='--color' SPHINXSOURCEDIR='./_migrated' epub - uses: actions/upload-artifact@v4 with: - name: godot-docs-epub-${{ matrix.branch }} - path: _build/epub/GodotEngine.epub + name: redot-docs-epub-${{ matrix.branch }} + path: _build/epub/RedotEngine.epub # Keep the current build and the previous build (in case a scheduled build failed). # This makes it more likely to have at least one successful build available at all times. retention-days: 15 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 028da18569c..51f0b5beaa9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,6 +28,10 @@ jobs: bash _tools/format.sh codespell -I _tools/codespell-ignore.txt -x _tools/codespell-ignore-lines.txt -S tutorials/i18n/locales.rst {about,community,contributing,getting_started,tutorials}/**/*.rst + - name: Migrate to Redot + run: | + python migrate.py . _migrated True + # Use dummy builder to improve performance as we don't need the generated HTML in this workflow. - name: Sphinx build - run: make SPHINXOPTS='--color -W' dummy + run: make SPHINXOPTS='--color -W' SPHINXSOURCEDIR='./_migrated' dummy diff --git a/.github/workflows/sync_class_ref.yml b/.github/workflows/sync_class_ref.yml index fdeb0cedb4d..3acd2ee4413 100644 --- a/.github/workflows/sync_class_ref.yml +++ b/.github/workflows/sync_class_ref.yml @@ -27,7 +27,7 @@ jobs: - name: Checkout the engine repository uses: actions/checkout@v4 with: - repository: 'godotengine/godot' + repository: 'redot-engine/redot-engine' # Use the appropriate branch for the documentation version. ref: ${{ env.engine_rev }} path: './.engine-src' @@ -38,7 +38,7 @@ jobs: cd ./.engine-src hash=$(git rev-parse HEAD) hash_short=$(git rev-parse --short HEAD) - echo "Checked out godotengine/godot at $hash" + echo "Checked out redot-engine/redot at $hash" echo "rev_hash=$hash" >> $GITHUB_OUTPUT echo "rev_hash_short=$hash_short" >> $GITHUB_OUTPUT @@ -59,10 +59,10 @@ jobs: delete-branch: true # Configure the commit author. - author: 'Godot Organization ' - committer: 'Godot Organization ' + author: 'Redot Organization ' + committer: 'Redot Organization ' # Configure the pull-request. title: 'classref: Sync with current ${{ env.engine_rev }} branch (${{ steps.engine.outputs.rev_hash_short }})' - body: 'Update Godot API online class reference to match the engine at https://github.com/godotengine/godot/commit/${{ steps.engine.outputs.rev_hash }} (`${{ env.engine_rev }}`).' + body: 'Update Redot API online class reference to match the engine at https://github.com/redot-engine/redot/commit/${{ steps.engine.outputs.rev_hash }} (`${{ env.engine_rev }}`).' labels: 'area:class reference,bug,enhancement' diff --git a/.gitignore b/.gitignore index b9e2fbe6c42..2e2df35cecf 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ .env _build/ +_build-old/ +_migrated/ env/ __pycache__ *.pyc diff --git a/README.md b/README.md index fbd2ba22fa5..8f3f8d181e6 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,38 @@ # Redot Engine documentation -This repository contains the source files of [Godot Engine](https://godotengine.org)'s documentation, in reStructuredText markup language (reST). +This repository contains the source files of [Redot Engine](https://redotengine.org)'s documentation, in reStructuredText markup language (reST). -They are meant to be parsed with the [Sphinx](https://www.sphinx-doc.org/) documentation builder to build the HTML documentation on [Godot's website](https://docs.godotengine.org). +They are meant to be parsed with the [Sphinx](https://www.sphinx-doc.org/) documentation builder to build the HTML documentation on [Redot's website](https://docs.redotengine.org). ## Download for offline use To browse the documentation offline, you can download an HTML copy (updated every Monday): -[stable](https://nightly.link/godotengine/godot-docs/workflows/build_offline_docs/master/godot-docs-html-stable.zip), -[latest](https://nightly.link/godotengine/godot-docs/workflows/build_offline_docs/master/godot-docs-html-master.zip), -[3.6](https://nightly.link/godotengine/godot-docs/workflows/build_offline_docs/master/godot-docs-html-3.6.zip). Extract +[stable](https://nightly.link/redot-engine/redot-docs/workflows/build_offline_docs/master/redot-docs-html-stable.zip), +[latest](https://nightly.link/redot-engine/redot-docs/workflows/build_offline_docs/master/redot-docs-html-master.zip), +[3.6](https://nightly.link/redot-engine/redot-docs/workflows/build_offline_docs/master/redot-docs-html-3.6.zip). Extract the ZIP archive then open the top-level `index.html` in a web browser. For mobile devices or e-readers, you can also download an ePub copy (updated every Monday): -[stable](https://nightly.link/godotengine/godot-docs/workflows/build_offline_docs/master/godot-docs-epub-stable.zip), -[latest](https://nightly.link/godotengine/godot-docs/workflows/build_offline_docs/master/godot-docs-epub-master.zip), -[3.6](https://nightly.link/godotengine/godot-docs/workflows/build_offline_docs/master/godot-docs-epub-3.6.zip). Extract -the ZIP archive then open the `GodotEngine.epub` file in an e-book reader application. +[stable](https://nightly.link/redot-engine/redot-docs/workflows/build_offline_docs/master/redot-docs-epub-stable.zip), +[latest](https://nightly.link/redot-engine/redot-docs/workflows/build_offline_docs/master/redot-docs-epub-master.zip), +[3.6](https://nightly.link/redot-engine/redot-docs/workflows/build_offline_docs/master/redot-docs-epub-3.6.zip). Extract +the ZIP archive then open the `RedotEngine.epub` file in an e-book reader application. + +## Migrating + +We are transitioning from Godot to Redot. In this period, a temporary solution is available. +``` +python migrate.py . _migrated True +``` + +After the docs are converted, you can build with +``` +sphinx-build -b html ./_migrated/ _build/html +``` ## Theming -The Godot documentation uses the default `sphinx_rtd_theme` with many +The Redot documentation uses the default `sphinx_rtd_theme` with many [customizations](_static/) applied on top. It will automatically switch between the light and dark theme depending on your browser/OS' theming preference. @@ -31,22 +43,22 @@ add-on. ## Contributing -All contributors are welcome to help on the Godot documentation. +All contributors are welcome to help on the Redot documentation. -To get started, head to the [Contributing section](https://docs.godotengine.org/en/latest/contributing/ways_to_contribute.html#contributing-to-the-documentation) of the online manual. There, you will find all the information you need to write and submit changes. +To get started, head to the [Contributing section](https://docs.redotengine.org/en/latest/contributing/ways_to_contribute.html#contributing-to-the-documentation) of the online manual. There, you will find all the information you need to write and submit changes. Here are some quick links to the areas you might be interested in: -1. [Contributing to the online manual](https://docs.godotengine.org/en/latest/contributing/documentation/contributing_to_the_documentation.html) -2. [Contributing to the class reference](https://docs.godotengine.org/en/latest/contributing/documentation/updating_the_class_reference.html) -3. [Content guidelines](https://docs.godotengine.org/en/latest/contributing/documentation/content_guidelines.html) -4. [Writing guidelines](https://docs.godotengine.org/en/latest/contributing/documentation/docs_writing_guidelines.html) -5. [Building the manual](https://docs.godotengine.org/en/latest/contributing/documentation/building_the_manual.html) -6. [Translating the documentation](https://docs.godotengine.org/en/latest/contributing/documentation/editor_and_docs_localization.html) +1. [Contributing to the online manual](https://docs.redotengine.org/en/latest/contributing/documentation/contributing_to_the_documentation.html) +2. [Contributing to the class reference](https://docs.redotengine.org/en/latest/contributing/documentation/updating_the_class_reference.html) +3. [Content guidelines](https://docs.redotengine.org/en/latest/contributing/documentation/content_guidelines.html) +4. [Writing guidelines](https://docs.redotengine.org/en/latest/contributing/documentation/docs_writing_guidelines.html) +5. [Building the manual](https://docs.redotengine.org/en/latest/contributing/documentation/building_the_manual.html) +6. [Translating the documentation](https://docs.redotengine.org/en/latest/contributing/documentation/editor_and_docs_localization.html) ## License -With the exception of the `classes/` folder, all the content of this repository is licensed under the Creative Commons Attribution 3.0 Unported license ([CC BY 3.0](https://creativecommons.org/licenses/by/3.0/)) and is to be attributed to "Juan Linietsky, Ariel Manzur and the Godot community". +With the exception of the `classes/` folder, all the content of this repository is licensed under the Creative Commons Attribution 3.0 Unported license ([CC BY 3.0](https://creativecommons.org/licenses/by/3.0/)) and is to be attributed to "Juan Linietsky, Ariel Manzur and the Redot community". See [LICENSE.txt](/LICENSE.txt) for details. -The files in the `classes/` folder are derived from [Godot's main source repository](https://github.com/godotengine/godot) and are distributed under the MIT license, with the same authors as above. +The files in the `classes/` folder are derived from [Redot's main source repository](https://github.com/redot-engine/redot) and are distributed under the MIT license, with the same authors as above. diff --git a/contributing/how_to_contribute.rst b/contributing/how_to_contribute.rst index 47733956731..e9603a72b0e 100644 --- a/contributing/how_to_contribute.rst +++ b/contributing/how_to_contribute.rst @@ -11,26 +11,6 @@ Because contrary to popular opinion, we need more than just programmers on the p Fundraising ----------- -- **Donate** - - We created the non-profit `Godot Foundation `_ to be able to support the Godot Engine in both matters of finance and administration. - In practice, this means the Foundation hires people to work part-time or full-time on the project. - These jobs include engine development as well as related tasks like code reviews, production management, community & marketing efforts, and more. - - With as little as 5 EUR per month, you can help us keep going strong. - Currently, we are intending to hire more core developers, as to cover more ground with full-time specialists that supplement and guide volunteer work. - - `Join the Development Fund `_ - -- **Donation Drives** - Think about your followers on social media, or other communities you are active in. - Use that reach to remind your social environment that even small contributions can make a difference, especially when done by a great number of people at the same time. - - Are you a content creator? Consider adding a link to the `Godot Development Fund `_ to your descriptions. - If you do live streams, perhaps think about organizing a stream with donation incentives. - -.. - **Buy Official Merch** - - **Publish Godot Games.** You heard right, simply publishing a game #MadeWithGodot can positively impact the well-being of this project. Your personal success elevates the engine to a viable alternative for other developers, growing the community further. diff --git a/favicon.png b/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..38cbcdf12dfbfef56c7acae76a7f2ef3b956a9cb GIT binary patch literal 23267 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 + + + + diff --git a/migrate.py b/migrate.py new file mode 100644 index 00000000000..217f7bb9071 --- /dev/null +++ b/migrate.py @@ -0,0 +1,316 @@ +""" +## Migrate files from Godot to Redot + +Usage (order is important): +py migrate.py [inputdir] [outputdir] [include unimplemented] + +example: +py migrate.py . _migrated True + +Will replace specific godot strings with redot. It tries to ignore external projects and other things that can't +change. + +A distinction is made between unimplemented instances of the godot keyword (for instance references to the main +website), and implemented ones (like references to github repositories that are already compatible). + +The idea is that, as the project is being migrated to the new name, the unimplemented mappings will gradually move +to the 'normal' mappings. This way the docs can grow along with the project, keeping broken links to a minimum. When +all is done, migrate.py has served its purpose and can be deleted. + +It will recursively traverse all directories, targeting .rst and .md files. It will convert the text and filename +if necessary, and then save them to the output directory (default _migrated) + +From there, the docs can be built in the normal way. +""" + +import fnmatch +import os +from shutil import copyfile +import shutil +import sys +import codecs +from distutils.dir_util import copy_tree + +encoding = 'utf-8' +defaultInputDirectory = '.' +defaultOutputDirectory = '_migrated' +defaultIncludeUnimplemented = False +filename_masks = ['.rst', '.md'] + +# Mappings that will currently lead to nowhere. Can be treated as a todo list. +mappings_unimplemented = [ + # Non existing urls + ('https://hosted.weblate.org/projects/godot-engine/godot-docs', 'https://hosted.weblate.org/projects/redot-engine/redot-docs'), + ('https://hosted.weblate.org/engage/godot-engine/', 'https://hosted.weblate.org/engage/redot-engine/'), + ('https://store.steampowered.com/app/404790/Godot_Engine/', 'https://store.steampowered.com/app/TODO'), + ('https://flathub.org/apps/details/org.godotengine.Godot', 'https://flathub.org/apps/details/org.redot-engine.Redot'), + ('https://godot.foundation', 'https://redot.foundation'), + ('https://hosted.weblate.org/projects/godot-engine/godot/', 'https://hosted.weblate.org/projects/redot-engine/redot/'), + ('https://hosted.weblate.org/projects/godot-engine/', 'https://hosted.weblate.org/projects/redot-engine/'), + ('https://hosted.weblate.org/browse/godot-engine', 'https://hosted.weblate.org/browse/redot-engine'), + ('https://repo1.maven.org/maven2/org/godotengine/godot/', 'https://repo1.maven.org/maven2/org/redot-engine/redot/'), + # Non existing internal urls + ('https://chat.godotengine.org/', 'https://chat.redotengine.org/'), + ('https://editor.godotengine.org', 'https://editor.redotengine.org'), + ('https://forum.godotengine.org/', 'https://forum.redotengine.org/'), + ('https://fund.godotengine.org', 'https://fund.redotengine.org'), + # The following mappings probably require changes to the core engine + ('GodotEngine.epub', 'RedotEngine.epub'), + ('godotengine.org/license', 'redotengine.org/license'), + ('AsGodotDictionary', 'AsRedotDictionary'), + ('GODOT_', 'REDOT_'), + ('-godot-', '-redot-'), + ('project.godot', 'project.redot'), + ('Godot.Collections', 'Redot.Collections'), + ('"Godot"', '"Redot"'), + ('.godot/', '.redot/'), + ('.godot.', '.redot.'), + ('APPDATA%\\Godot\\', 'APPDATA%\\Redot\\'), + ('AppData%\\Godot\\', 'AppData%\\Redot\\'), + ('Caches/Godot/', 'Caches/Redot/'), + ('cache/godot/', 'cache/redot/'), + ('Support/Godot/', 'Support/Redot/'), + ('config/godot/', 'config/redot/'), + ('share/godot/', 'share/redot/'), + (' godot_', ' redot_'), + ('org.godotengine.Godot', 'org.redotengine.Redot'), + ('godot-ios-plugins', 'redot-ios-plugins'), + ('godot-syntax-themes', 'redot-syntax-themes'), + ('godot_skin', 'redot_skin'), + ('godot_scene_node', 'redot_scene_node'), + ('``godotengine/godot', '``redot-engine/redot'), + ('>/Godot/', '>/Redot/'), + ('``.godot``', '``.redot``'), + ('``godot``', '``redot``'), + ('/godot.', '/redot.'), + ('GodotPhysics', 'RedotPhysics'), + ('AsGodotObject', 'AsRedotObject'), + ('non-Godot', 'non-Redot'), + ('Godot-', 'Redot-'), + ('libgodot', 'libredot'), + ('godot.linuxbsd', 'redot.linuxbsd'), + ('Godot.app', 'Redot.app'), + ('MacOS/Godot', 'MacOS/Redot'), + ('C:\\godot', 'C:\\redot'), + ('GodotSharp', 'RedotSharp'), + ('godot.gdkey', 'redot.gdkey'), + ('godot-nir', 'redot-nir'), + ('godot-angle', 'redot-angle'), + ('godot-binary', 'redot-binary'), + ('``Godot', '``Redot'), + ('godot/modules', 'redot/modules'), + ('godot_binary', 'redot_binary'), + ('godotengine.org', 'redotengine.org'), + ('godot-source', 'redot-source'), + ('godot/bin', 'redot/bin'), + ('gdb godot', 'gdb redot'), + ('GODOT', 'REDOT'), + ('USERNAME/godot', 'USERNAME/redot'), + ('godot-xr', 'redot-xr'), + ('godotisawesome', 'redotisawesome'), + ('godot-cpp', 'redot-cpp'), + ('GodotCPP', 'RedotCPP'), + ('namespace godot', 'namespace redot'), + ('godot_cpp', 'redot_cpp'), + ('GodotObject', 'RedotObject'), + ('GodotBot', 'RedotBot'), + ('GodotPlugin', 'RedotPlugin'), + ('org.godotengine', 'org.redotengine'), + ('/godot>', '/redot>'), + ('repos/godotengine/godot', 'repos/redot-engine/redot'), + ('godot demo', 'redot demo'), + ('godotsharp', 'redotsharp'), +] + +# Mappings that should work on first migration +mappings = [ + # Table breakers + ('| ``"Please include this when reporting the bug on: https://github.com/godotengine/godot/issues"`` |', '| ``"Please include this when reporting the bug on: https://github.com/redot-engine/godot/issues"``|'), + ('https://github.com/godotengine/godot/pull/40364>`_ for more. |', 'https://github.com/redot-engine/redot/pull/40364>`_ for more. |'), + # Almost existing urls + ('https://docs.godotengine.org', 'https://docs.redotengine.org'), + ('https://godotengine.org', 'https://redotengine.org'), + # Existing urls + ('https://nightly.link/godotengine/godot-docs/workflows/build_offline_docs/master/godot', 'https://nightly.link/redot-engine/redot-docs/workflows/build_offline_docs/master/redot'), + ('https://github.com/godotengine/godot-docs/issues', 'https://github.com/redot-engine/redot-docs/issues'), + ('https://github.com/godotengine/godot/blob/master', 'https://github.com/redot-engine/redot/blob/master'), + ('https://raw.githubusercontent.com/godotengine/godot/master', 'https://raw.githubusercontent.com/redot-engine/redot/master'), + ('https://github.com/godotengine/godot-demo-projects', 'https://github.com/redot-engine/redot-demo-projects'), + ('https://discord.gg/bdcfAYM4W9', 'https://discord.gg/redot'), + ('https://github.com/godotengine/godot', 'https://github.com/redot-engine/redot'), + ('https://github.com/godotengine/godot-proposals', 'https://github.com/redot-engine/redot-proposals'), + ('https://raw.githubusercontent.com/godotengine/godot-docs', 'https://raw.githubusercontent.com/redot-engine/redot-docs'), + ('https://github.com/godotengine/', 'https://github.com/redot-engine/'), + # Generic replacements + ('GODOT_COPYRIGHT.txt', 'REDOT_COPYRIGHT.txt'), + ('godot-docs', 'redot-docs'), + ('GODOT ENGINE', 'REDOT ENGINE'), + ('/bin/godot', '/bin/redot'), + ('/Applications/Godot.app', '/Applications/Redot.app'), + ('highlight=Godot', 'highlight=Redot'), + ('/godot_', '/redot_'), + ('/godot-', '/redot-'), + ('_godot_', '_redot_'), + ('``godot``', '``redot``'), + ('Godot ', 'Redot '), + (' Godot', ' Redot'), + (' Godot.', ' Redot.'), + (' Godot?', ' Redot?'), + ('Godot\'', 'Redot\''), + ('Godot,', 'Redot,'), + ('Godot:', 'Redot:'), + (' godot ', ' redot '), + ('\nGodot.', '\nRedot.'), + ('_godot\n', '_redot\n'), + ('godot.gif', 'redot.gif'), + ('godot.jpg', 'redot.jpg'), + ('godot.png', 'redot.png'), + ('"godot_', '"redot_'), + ('"godotengine"', '"redotengine"'), + ('GodotEngine', 'RedotEngine'), + ('godot-giscus', 'redot-giscus'), + ('"godotengine/', '"redotengine/'), + ('godot_is_latest', 'redot_is_latest'), + ('godot-edit-guideline', 'redot-edit-guideline'), + ('_godot_', '_redot_'), + ('to_godot', 'to_redot'), + ('godot.html', 'redot.html'), + ('by-godot', 'by-redot'), +] + +filename_mappings = [ + ('godot', 'redot'), +] + +static_dirs = [ + '**/img', + '**/files', + '_extensions', + '_static', + '_styleguides', + '_templates', + '_tools', +] + +alphanumeric = [ + 'py', + 'md', + 'css', + 'txt', + 'css', + 'js', + 'html', + 'csv', + 'rst', +] + +# force stdout encoding so it won't fail on print statements +if (sys.stdout.encoding != encoding): + sys.stdout = codecs.getwriter(encoding)(sys.stdout.buffer, 'strict') + sys.stdout.encoding = encoding + +def is_target(filename): + return any(filename.lower().endswith(m) for m in filename_masks) + +def generateOutputName(root, fileName, outputDirectory): + on = os.path.join('.', outputDirectory, root, fileName) + on = convertContent(on, filename_mappings) + ensureDirExists(on) + return on + +def convertContent(content, mappings): + for mapping in mappings: + search, replace = mapping + if (search != ''): + content = content.replace(search, replace) + return content + +def ensureDirExists(outputName): + dirname = os.path.dirname(outputName) + try: + os.makedirs(dirname) + except FileExistsError: + pass + +def copyFile(root, filename, outputDirectory): + inputName = os.path.join(root, filename) + outputName = generateOutputName(root, inputName.replace('.\\', '').replace('./', ''), outputDirectory) + + print(f'Copying "{inputName}" to "{outputName}"') + shutil.copyfile(inputName, outputName) + +def convertFile(root, filename, outputDirectory, includeUnimplemented): + inputName = os.path.join(root, filename) + outputName = generateOutputName(root, filename, outputDirectory) + + print(f'Converting "{inputName}" to "{outputName}"') + with open(inputName, mode = 'r', encoding = encoding) as input: + data = input.read() + + if (includeUnimplemented): + data = convertContent(data, mappings_unimplemented) + data = convertContent(data, mappings) + ensureDirExists(outputName) + with open(outputName, mode = 'w', encoding = encoding) as output: + output.write(data) + +def copyGlobalDir(inputDirectory, inputMask, outputDirectory): + for root, dirs, files in os.walk(inputDirectory): + if (inputMask in root and outputDirectory not in root): + for f in files: + inputName = os.path.join(root, f) + outputName = generateOutputName(root, f, outputDirectory) + ensureDirExists(outputName) + print(f"Copying {inputName} to {outputName}") + copyfile(inputName, outputName) + +def convertStaticDir(inputDirectory, outputDirectory): + for root, dirs, files in os.walk(inputDirectory): + if (outputDirectory not in root and '__' not in root): + for f in files: + if (f.split('.')[1] in alphanumeric): + convertFile(root, f, outputDirectory, True) + else: + copyFile(root, f, outputDirectory) + +def migrate(inputDirectory, outputDirectory, includeUnimplemented): + outputsig = os.path.join('.', outputDirectory) + for root, dirs, files in os.walk(inputDirectory): + # ignore output path + if (root.startswith(outputsig)): + continue + + items = filter(is_target, files) + for item in items: + convertFile(root, item, outputDirectory, includeUnimplemented) + +inputDir = defaultInputDirectory +outputDir = defaultOutputDirectory +includeUnimplemented = defaultIncludeUnimplemented +if (len(sys.argv) > 1): + inputDir = sys.argv[1] +if (len(sys.argv) > 2): + outputDir = sys.argv[2] +if (len(sys.argv) > 3): + includeUnimplemented = sys.argv[3] + +print(f"Simple rst migrator. Uses str.replace to map from Godot to Redot.") +print(f"Usage: py migrate.py [inputDir] [outputDir] [includeUnimplemented], example: py migrate.py . _mymigration True") +print(f"Author: @Craptain on X") +print(f"Input directory: {inputDir}, output directory: {outputDir}, include unimplemented: {includeUnimplemented}") + +migrate(inputDir, outputDir, includeUnimplemented) + +print("Copying config files...") +convertFile(inputDir, 'conf.py', outputDir, includeUnimplemented) +convertFile(inputDir, 'robots.txt', outputDir, includeUnimplemented) +print("Copying static directories...") + +for dir in static_dirs: + if ('**' in dir): + print(f"Copying dirs with mask {dir}") + copyGlobalDir(inputDir, dir.split('/')[1], outputDir) + else: + print(f"Converting dir {dir}") + convertStaticDir(dir, outputDir) +print("Done")