diff --git a/.github/workflows/gha.py b/.github/workflows/gha.py new file mode 100755 index 00000000..d67ec548 --- /dev/null +++ b/.github/workflows/gha.py @@ -0,0 +1,103 @@ +# GitHub Actions Utility Functions +# https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions +import os +import sys + +errors_were_printed = False + +def fail_if_errors(): + if errors_were_printed: + print("Exiting due to previous errors.") + sys.exit(1) + +def print_error(message): + global errors_were_printed + errors_were_printed = True + print(f"::error::{message}") + +def print_warning(message): + print(f"::warning::{message}") + +def print_notice(message): + print(f"::notice::{message}") + +def print_debug(message): + print(f"::debug::{message}") + +def github_file_command(command, message): + command = f"GITHUB_{command}" + command_file = os.getenv(command) + + if command_file is None: + print_error(f"Missing required GitHub environment variable '{command}'") + sys.exit(1) + + if not os.path.exists(command_file): + print_error(f"'{command}' points to non-existent file '{command_file}')") + sys.exit(1) + + with open(command_file, 'a') as command_file_handle: + command_file_handle.write(message) + command_file_handle.write('\n') + +def set_output(name, value): + if isinstance(value, bool): + value = "true" if value else "false" + github_file_command("OUTPUT", f"{name}< 0: + print_error("Bad command line, too many arguments specified.") + sys.exit(1) + + pop_arg() # Skip script name + command = pop_arg() + if command == "print_error": + message = pop_arg() + done_parsing() + print_error(message) + elif command == "print_warning": + message = pop_arg() + done_parsing() + print_warning(message) + elif command == "print_notice": + message = pop_arg() + done_parsing() + print_notice(message) + elif command == "set_output": + name = pop_arg() + value = pop_arg() + done_parsing() + set_output(name, value) + elif command == "set_environment_variable": + name = pop_arg() + value = pop_arg() + done_parsing() + set_environment_variable(name, value) + elif command == "add_path": + path = pop_arg() + done_parsing() + add_path(path) + else: + print_error(f"Unknown command '{command}'") + sys.exit(1) + + fail_if_errors() diff --git a/.github/workflows/update-bonsai-version-info.py b/.github/workflows/update-bonsai-version-info.py new file mode 100755 index 00000000..f9edab38 --- /dev/null +++ b/.github/workflows/update-bonsai-version-info.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +import os +import re + +import gha + +TEMPLATE_PATH = ".github/workflows/version-info-template.md" +INSTALLATION_ARTICLE_PATH = "articles/installation.md" + +project_url_base = "https://github.com/bonsai-rx/bonsai/" + +#================================================================================================== +# Get inputs +#================================================================================================== +def get_environment_variable(name) -> str: + ret = os.getenv(name) + + if ret is None or ret == '': + gha.print_error(f"Missing required parameter '{name}'") + return '' + + return ret + +workflow_dispatch_version = get_environment_variable('workflow_dispatch_version') + +# Handle forks for testing purposes +if os.getenv('is_canonical_docs_repo') != 'true': + project_fork_url = get_environment_variable('project_fork_url') + project_url_base = project_fork_url.removesuffix('.git') + if project_url_base[-1] != '/': + project_url_base += '/' + +gha.fail_if_errors() + +#================================================================================================== +# Populate template +#================================================================================================== + +version_info = "" +with open(TEMPLATE_PATH, 'r', encoding='utf-8') as f: + version_info = f.read() + +version_info = version_info.replace("$VERSION$", workflow_dispatch_version) +version_info = version_info.replace("$PROJECT_URL_BASE$", project_url_base) + +#================================================================================================== +# Update article +#================================================================================================== + +article = "" +with open(INSTALLATION_ARTICLE_PATH, 'r', encoding='utf-8') as f: + article = f.read() + +def replace_function(match): + return f"{match.group(1)}{version_info}{match.group(3)}" + +(article, replacement_count) = re.subn( + r'(\r?\n)(.+)()', + replace_function, + article, + 1, + re.DOTALL +) + +if replacement_count != 1: + gha.print_error(f"Failed to find the RELEASE_INFO block within '{INSTALLATION_ARTICLE_PATH}'.") + gha.fail_if_errors() + +with open(INSTALLATION_ARTICLE_PATH, 'w',encoding='utf-8') as f: + f.write(article) + +print(f"Bonsai release info in '{INSTALLATION_ARTICLE_PATH}' updated to {workflow_dispatch_version}.") diff --git a/.github/workflows/version-bump.yml b/.github/workflows/version-bump.yml new file mode 100644 index 00000000..17237e54 --- /dev/null +++ b/.github/workflows/version-bump.yml @@ -0,0 +1,93 @@ +# This workflow bumps the submodule version used for a particular project +# In the case of Bonsai being updated, this also updates the installation page with the latest version info +name: Update documented project version +run-name: Update `${{github.event.inputs.project}}` to `${{github.event.inputs.version}}` +on: + workflow_dispatch: + inputs: + project: + description: "The name of the project to be updated (IE: a folder name within the src directory)" + required: true + version: + description: "The target version to update to (IE: a Git tag in the project)" + required: true + project-fork-url: + description: "Git URL of the project for testing in forks" + default: "" +concurrency: + group: version-bump +permissions: + # Required to trigger GitHub Pages deployment + actions: write + # Required to push changes + contents: write +jobs: + update: + name: Update ${{github.event.inputs.project}} to ${{github.event.inputs.version}} + runs-on: ubuntu-latest + env: + PROJECT: ${{github.event.inputs.project}} + VERSION: ${{github.event.inputs.version}} + steps: + # ----------------------------------------------------------------------- Checkout + - name: Checkout + uses: actions/checkout@v4 + + # ----------------------------------------------------------------------- Override the submodule URL + # This is to support testing release automation in forks, it is disabled for the canonical docs repo + - name: Override ${{github.event.inputs.project}}'s submodule URL + if: vars.IS_CANONICAL_DOCS_REPO != 'true' && github.event.inputs.project-fork-url != '' + run: git config --local --add "submodule.src/$PROJECT.url" "$FORK_URL" + env: + FORK_URL: ${{github.event.inputs.project-fork-url}} + + # ----------------------------------------------------------------------- Update the submodule + - name: Clone ${{github.event.inputs.project}} submodule + run: git submodule update --init "src/$PROJECT/" + - name: Update ${{github.event.inputs.project}} submodule + working-directory: src/${{github.event.inputs.project}}/ + run: git checkout "refs/tags/$VERSION" + - name: Stage ${{github.event.inputs.project}} submodule + run: git add "src/$PROJECT/" + + # ----------------------------------------------------------------------- Update the installation page + - name: Setup Python 3.10 + if: github.event.inputs.project == 'bonsai' + uses: actions/setup-python@v5 + with: + python-version: '3.10' + - name: Update Bonsai version info + if: github.event.inputs.project == 'bonsai' + run: | + python .github/workflows/update-bonsai-version-info.py + git add articles/installation.md + env: + workflow_dispatch_version: ${{github.event.inputs.version}} + is_canonical_docs_repo: ${{vars.IS_CANONICAL_DOCS_REPO}} + project_fork_url: ${{github.event.inputs.project-fork-url}} + + # ----------------------------------------------------------------------- Commit changes + # Skip the rest of the job if there aren't any changes to commit + # (IE: the submodule was already the relevant version) + - name: Check if update was necessary + id: pre-commit-check + run: | + (git diff-index --cached --exit-code HEAD \ + && python .github/workflows/gha.py print_notice "Version bump was no-op, no changes to commit.") \ + || python .github/workflows/gha.py set_output continue true + - name: Commit changes + if: steps.pre-commit-check.outputs.continue == 'true' + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git commit -m "Update \`$PROJECT\` to \`$VERSION\`" + - name: Push changes + if: steps.pre-commit-check.outputs.continue == 'true' + run: git push + # The above push will not actually trigger a deployment as actions performed using temporary GitHub Actions tokens + # do not trigger events in order to avoid unintentional recursion. As such we manually trigger deployment. + - name: Trigger GitHub Pages deployment + if: steps.pre-commit-check.outputs.continue == 'true' + run: gh workflow run build.yml + env: + GH_TOKEN: ${{github.token}} diff --git a/.github/workflows/version-info-template.md b/.github/workflows/version-info-template.md new file mode 100644 index 00000000..c7cb3b30 --- /dev/null +++ b/.github/workflows/version-info-template.md @@ -0,0 +1,4 @@ +The latest stable release is: **$VERSION$** + +[ Download Installer (.exe)]($PROJECT_URL_BASE$releases/download/$VERSION$/Bonsai-$VERSION$.exe){class="btn btn-success"} +[ Download Portable (.zip)]($PROJECT_URL_BASE$releases/download/$VERSION$/Bonsai.zip){class="btn btn-warning"} diff --git a/articles/installation.md b/articles/installation.md index 9ea91ad3..0e35e2de 100644 --- a/articles/installation.md +++ b/articles/installation.md @@ -5,11 +5,13 @@ title: "Installing Bonsai" # Installing Bonsai -The Bonsai editor is currently designed to work with the .NET framework on Windows desktop operating systems, version 7 or later. The easiest way to get started with Bonsai is by downloading the latest installer. +The Bonsai editor is currently designed to work with the .NET Framework on Windows desktop operating systems, version 7 or later. The easiest way to get started with Bonsai is by downloading the latest installer. -The latest stable release is: **2.8.3 (2.8.3.4546)** + +The latest stable release is: **2.8.3** [ Download Installer (.exe)](https://github.com/bonsai-rx/bonsai/releases/download/2.8.3/Bonsai-2.8.3.exe){class="btn btn-success"} [ Download Portable (.zip)](https://github.com/bonsai-rx/bonsai/releases/download/2.8.3/Bonsai.zip){class="btn btn-warning"} + The installer will make sure that .NET and any other required system dependencies for running Bonsai are correctly setup in your computer. \ No newline at end of file