diff --git a/.travis.yml b/.travis.yml index 770e7cd..5cc1cfc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,6 +26,7 @@ install: - git clone git+ssh://git@github.com/nxt-dev/nxt.git - pip install ./nxt - pip install ./nxt_editor + - pip install twine - sudo apt-get install -y libxkbcommon-x11-0 - sudo apt-get install -y libgl1-mesa-dev @@ -33,10 +34,10 @@ script: - | if [ "$TRAVIS_BRANCH" = "release" ] && [ "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then python -m nxt.cli test - exit 0 + exit $? fi - | if [ "$TRAVIS_BRANCH" = "release" ] && [ "$TRAVIS_EVENT_TYPE" = "push" ]; then python -m nxt.cli exec nxt/build/packaging.nxt -s /make_and_test_upload - exit 0 + exit $? fi \ No newline at end of file diff --git a/README.md b/README.md index 2e2422e..ecd906b 100644 --- a/README.md +++ b/README.md @@ -19,32 +19,45 @@ # Installation ### Requirements -- [Python 2.7]([Python 2.7.0 Release | Python.org](https://www.python.org/download/releases/2.7)) +- Python >= [2.7.*](https://www.python.org/download/releases/2.7) <= [3.7.*](https://www.python.org/download/releases/3.7) -### PIP package -1. Download the pip source distribution(`nxt-#.#.#.tar.gz`) from the [latest release](https://github.com/SunriseProductions/nxt/releases/latest) +### PIP package +- From [PyPi](https://pypi.org/project/nxt-editor/): + - `pip install nxt-editor` + +- From GitHub: + 1. Download the pip source distribution(`nxt-#.#.#.tar.gz`) from the [latest release](https://github.com/nxt-dev/nxt_editor/releases/latest) -2. `pip install path/to/nxt-#.#.#.tar.gz` + 2. `pip install path/to/nxt-#.#.#.tar.gz` ### Maya plugin: -1. Download the maya module(`nxt_maya.zip`) from the [latest release](https://github.com/SunriseProductions/nxt/releases/latest) +1. Download the maya module(`nxt_maya.zip`) from the [latest release](https://github.com/SunriseProductions/nxt_editor/releases/latest) 2. Follow the [nxt_maya](integration/maya/README.md) instructions (also included in the download) # Updating ### PIP package +- From [PyPi](https://pypi.org/project/nxt-editor/): + - `pip install -U nxt-editor` -1. Download the pip source distribution(`nxt-#.#.#.tar.gz`) from the [latest release](https://github.com/SunriseProductions/nxt/releases/latest) -2. `pip install --upgrade path/to/nxt-#.#.#.tar.gz` +- From GitHub: + 1. Download the pip source distribution(`nxt-#.#.#.tar.gz`) from the [latest release](https://github.com/SunriseProductions/nxt_editor/releases/latest) + + 2. `pip install --upgrade path/to/nxt-#.#.#.tar.gz` ### Maya plugin: -1. Download the `nxt_maya` zip from the [latest release](https://github.com/SunriseProductions/nxt/releases/latest) +1. Download the `nxt_maya` zip from the [latest release](https://github.com/SunriseProductions/nxt_editor/releases/latest) 2. Extract the zip and replace the existing nxt_maya files with the newly extracted files. 3. Re-launch Maya + + +## Acknowledgements + +[Sunrise Productions](https://sunriseproductions.tv/) | [School of Visual Art and Design](https://www.southern.edu/visualartanddesign/) diff --git a/build/GitUtils.nxt b/build/GitUtils.nxt deleted file mode 100644 index 0abc010..0000000 --- a/build/GitUtils.nxt +++ /dev/null @@ -1,390 +0,0 @@ -{ - "version": "1.15", - "alias": "GitUtils", - "color": "#806a70", - "mute": false, - "solo": false, - "meta_data": { - "positions": { - "/GitCmd": [ - -371.0, - -257.0 - ], - "/GitCurBranch": [ - 109.45052337903013, - -360.62349384340297 - ], - "/GitPR": [ - -318.0, - -42.0 - ], - "/GitRelease": [ - -328.0, - -103.0 - ], - "/GitStatus": [ - 101.54142822649686, - -102.39260294554322 - ], - "/GitUpload": [ - -1360.0, - 920.0 - ], - "/JsonLoad": [ - -346.0, - 257.0 - ], - "/populate": [ - 18.0, - 195.0 - ], - "/ship": [ - -304.0, - 44.0 - ], - "/unzip_env": [ - 79.51086299864536, - 76.76910910214035 - ] - } - }, - "nodes": { - "/GitCmd": { - "attrs": { - "cmd_args": { - "type": "str", - "value": "\"-help\"" - }, - "input": { - "type": "str", - "value": "''" - } - }, - "child_order": [ - "ParseGitReturn" - ], - "code": [ - "self.input = subprocess.check_output([\"git\", ${cmd_args}]).decode(\"utf8\")" - ] - }, - "/GitCmd/ParseGitReturn": { - "attrs": { - "allow_multi_line": { - "type": "bool", - "value": "False" - }, - "input": { - "type": "str", - "value": "''" - }, - "key": { - "type": "str", - "value": "\"\"" - }, - "lines": { - "type": "list", - "value": "[]" - }, - "result": { - "type": "str", - "value": "''" - } - }, - "code": [ - "self.lines = [l for l in self.input.split(\"\\n\") if l.startswith(${key})]", - "if ${allow_multi_line}:", - " self.result = [l.strip(${key}).strip() for l in self.lines]", - "else:", - " if self.lines:", - " self.result = w(self.lines[0].strip(${key}).strip(), '\"\"\"')", - "# print(self.input.split(\"\\n\"))" - ] - }, - "/GitCurBranch": { - "attrs": { - "ILLEGAL_BRANCHES": { - "type": "tuple", - "value": "('master',)" - } - }, - "start_point": false, - "child_order": [ - "CurBranch", - "ValidateLegalBranch", - "ValidateExactBranch" - ], - "comment": "Get the name of the current branch.", - "code": [ - "print('Validating branch...')" - ] - }, - "/GitCurBranch/CurBranch": { - "attrs": { - "cmd_args": { - "value": "'branch'" - } - }, - "child_order": [ - "ParseGitReturn", - "UpdateWorkingBranch" - ], - "instance": "/GitCmd" - }, - "/GitCurBranch/CurBranch/ParseGitReturn": { - "instance": "/GitCmd/ParseGitReturn", - "code": [ - "self.lines = [l for l in self.input.split(\"\\n\") if l.startswith(${key})]", - "if ${allow_multi_line}:", - " self.result = [l.strip(${key}).strip() for l in self.lines]", - "else:", - " if self.lines:", - " self.result = w(self.lines[0].strip(${key}).strip())", - "# print(self.input.split(\"\\n\"))" - ] - }, - "/GitCurBranch/CurBranch/UpdateWorkingBranch": { - "comment": "Updates the STAGE.branch to the current git branch.\n\n", - "enabled": false, - "code": [ - "STAGE.branch = ${../ParseGitReturn.result}", - "print('Updated STAGE.branch to \"{}\"'.format(STAGE.branch))" - ] - }, - "/GitCurBranch/ValidateExactBranch": { - "attrs": { - "exact_branch": { - "type": "raw" - } - }, - "enabled": false, - "code": [ - "branch = ${../CurBranch/ParseGitReturn.result}", - "if branch != ${exact_branch}:", - " raise Exception(\"Invalid branch '{}' is not ${exact_branch}\".format(branch))", - "print('Your current branch \"{}\" is valid!'.format(branch))" - ] - }, - "/GitCurBranch/ValidateLegalBranch": { - "enabled": false, - "code": [ - "branch = ${../CurBranch/ParseGitReturn.result}", - "if branch in ${ILLEGAL_BRANCHES}:", - " raise Exception('Can not push to \"{}\" with this bot!'.format(branch))", - "print('Your current branch \"{}\" is valid!'.format(branch))" - ] - }, - "/GitPR": { - "attrs": { - "body": { - "type": "raw" - }, - "pr_data": {}, - "secrets_path": { - "type": "raw" - }, - "target": { - "type": "raw" - }, - "title": { - "type": "raw" - }, - "upload_url": { - "type": "raw" - } - }, - "enabled": true, - "code": [ - "data = {", - " \"title\": \"${title}\",", - " \"body\": \"${body}\",", - " \"head\": \"${branch}\",", - " \"base\": \"${target}\"", - "}", - "print('Opening PR \"${title}\" from \"${branch}\" to \"${target}\"')", - "url = 'https://api.github.com/repos/SunriseProductions/nxt/pulls'", - "all_rel_resp = requests.get(url, auth=self.secret)", - "if all_rel_resp.status_code != 200:", - " raise Exception('Cannot access pulls ({})'.format(all_rel_resp.status_code))", - "pr_found = False", - "all_prs = all_rel_resp.json()", - "for pr in all_prs:", - " if pr['title'] == data['title']:", - " if pr['head']['label'] != 'SunriseProductions:${branch}':", - " raise Exception('Incompatable exsisting PR called {}'.format(data['title']))", - " pr_dict = pr", - " pr_found = True", - " break", - " ", - "", - "if not pr_found:", - " print('Making new PR')", - " new_rel_resp = requests.post(url, auth=self.secret, data=json.dumps(data))", - " if new_rel_resp.status_code != 201:", - " raise Exception('Unsuccessful PR ({})'.format(new_rel_resp.text))", - " print('New PR made')", - " pr_dict = new_rel_resp.json()", - "html_url = pr_dict[\"html_url\"]", - "print(html_url)", - "webbrowser.open_new_tab(html_url)", - "self.pr_data = pr_dict" - ] - }, - "/GitRelease": { - "attrs": { - "secrets_path": { - "type": "raw" - }, - "upload_url": { - "type": "raw" - } - }, - "enabled": true, - "code": [ - "import os", - "import sys", - "import json", - "import requests", - "", - "data = {", - " \"tag_name\": \"${version_tag}\",", - " \"target_commitish\": \"master\",", - " \"name\": \"${version_name} v${version_str}\",", - " \"body\": STAGE.changelog_text,", - " \"draft\": True,", - " \"prerelease\": False", - "}", - "url = 'https://api.github.com/repos/SunriseProductions/nxt/releases'", - "all_rel_resp = requests.get(url, auth=self.secret)", - "if all_rel_resp.status_code != 200:", - " print('cannot access releases')", - " print(all_rel_resp.status_code)", - " sys.exit()", - "all_releases = all_rel_resp.json()", - "existing_release = None", - "for release in all_releases:", - " if release['tag_name'] == data['tag_name']:", - " existing_release = release", - " break", - "", - "if not existing_release:", - " print('making new release')", - " new_rel_resp = requests.post(url, auth=self.secret, data=json.dumps(data))", - " if new_rel_resp.status_code != 201:", - " print('unsuccessful release')", - " print(new_rel_resp.status_code)", - " sys.exit()", - " print('new release made')", - " release_dict = new_rel_resp.json()", - "else:", - " print('using existing release')", - " release_dict = existing_release", - "STAGE.release_dict = release_dict" - ] - }, - "/GitStatus": { - "child_order": [ - "Status", - "ValidateStatus" - ], - "comment": "Check that there are no un-commited changes on the branch.", - "code": [ - "print('Getting status commits...')" - ] - }, - "/GitStatus/Status": { - "attrs": { - "cmd_args": { - "type": "str", - "value": "\"status\"" - } - }, - "child_order": [ - "ParseGitReturn" - ], - "instance": "/GitCmd" - }, - "/GitStatus/Status/ParseGitReturn": { - "attrs": { - "key": { - "value": "'\\tmodified:'" - } - } - }, - "/GitStatus/ValidateStatus": { - "code": [ - "if ${../Status/ParseGitReturn.result}:", - " print('Changes not staged for commit:', ${../Status/ParseGitReturn.result})", - " raise Exception('You have un-commited changes!')" - ] - }, - "/GitUpload": { - "attrs": { - "asset_path": { - "type": "raw", - "value": "${path::asset}" - }, - "content_type": { - "type": "raw", - "value": "application/zip" - } - }, - "child_order": [ - "DoIt" - ], - "code": [ - "print('Starting upload...')" - ] - }, - "/GitUpload/DoIt": { - "attrs": { - "upload_url": { - "type": "str", - "value": "''" - } - }, - "code": [ - "asset_name = os.path.basename(r'${asset_path}')", - "self.upload_url = STAGE.release_dict['upload_url']", - "for asset in STAGE.release_dict['assets']:", - " if asset['name'] == asset_name:", - " exist_err = 'Existing asset found with name:\"{}\", not uploading.'.format(asset_name)", - " raise Exception(exist_err)", - " self.upload_url = None", - " break", - " ", - "if self.upload_url: ", - " self.upload_url = self.upload_url.replace('{?name,label}', '?name={}').format(asset_name)", - " headers = {'Content-Type': '${content_type}'}", - " # print(self.upload_url)", - " with open(r'${asset_path}', 'rb') as fp:", - " response = requests.post(self.upload_url, auth=self.secret,", - " data=fp, timeout=60, headers=headers)", - " if response.status_code == 201:", - " print('Successful upload!')", - " else:", - " raise Exception('Upload failed!')" - ] - }, - "/JsonLoad": { - "attrs": { - "file_data": { - "type": "raw" - }, - "file_path": { - "type": "raw" - } - }, - "code": [ - "with open('${file_path}', 'r') as f:", - " self.file_data = json.load(f)", - "if self.file_data is None:", - " raise Exception('Failed to load json from \"${file_path}\"')" - ] - }, - "/make_module_folder/zip_mod": { - "code": [ - "" - ] - } - } -} \ No newline at end of file diff --git a/build/ReleaseBot.nxt b/build/ReleaseBot.nxt deleted file mode 100644 index 324063b..0000000 --- a/build/ReleaseBot.nxt +++ /dev/null @@ -1,722 +0,0 @@ -{ - "version": "1.15", - "alias": "ReleaseBot", - "color": "#b33b5b", - "mute": false, - "solo": false, - "references": [ - "generate_changelog_md.nxt", - "make_maya_plugin.nxt", - "generate_hotkeys_md.nxt", - "make_package.nxt", - "GitUtils.nxt", - "$NXT_BUILTINS/sub_graphs.nxt" - ], - "meta_data": { - "positions": { - "/": [ - 0.0, - 0.0, - 0.0 - ], - "/BeginPR": [ - 1200.0, - 0.0 - ], - "/BeginRelease": [ - -260.0, - 0.0 - ], - "/BeginRelease/GitCurBranch/CurBranch/ParseGitReturn": [ - 0.0, - 0.0, - 0.0 - ], - "/CheckCommits": [ - 80.0, - 100.0 - ], - "/CreateRelease": [ - 2760.0, - 60.0 - ], - "/DoIt": [ - -1640.0, - 900.0 - ], - "/GenerateChangelog": [ - 1500.0, - 0.0 - ], - "/GenerateHotkeysMD": [ - 1800.0, - 0.0 - ], - "/GitCmd": [ - -800.0, - 760.0 - ], - "/GitCmd2": [ - 618.6319850156622, - 104.42705080595923 - ], - "/GitCurBranch": [ - -800.0, - 520.0 - ], - "/GitCurBranch2": [ - -160.0, - 420.0 - ], - "/GitPR": [ - -1200.0, - 680.0 - ], - "/GitRelease": [ - -800.0, - 600.0 - ], - "/GitRelease2": [ - 219.4455692046359, - -150.45574389461672 - ], - "/GitStatus": [ - -1200.0, - 520.0 - ], - "/GitUpload": [ - -1200.0, - 760.0 - ], - "/JsonLoad": [ - -1200.0, - 200.0 - ], - "/JsonLoad2": [ - -720.0, - 420.0 - ], - "/MakeReleaseDir": [ - 540.0, - 0.0 - ], - "/OldVersionCheck": [ - 280.0, - 380.0 - ], - "/PR_Dev": [ - 1240.0, - 180.0 - ], - "/ParseGitReturn": [ - -400.00000000000006, - 218.85416666666669 - ], - "/ParseGitReturn2": [ - 139.97772221620224, - 201.2760907674117 - ], - "/ReleaseLoop": [ - 2740.0, - 0.0 - ], - "/STAGE": [ - -1123.7400818788076, - -75.57563472014294 - ], - "/UpdateHotkeys": [ - 1811.2303797361437, - -305.46632882310877 - ], - "/UpdateMasterPR": [ - 1520.0, - -120.0 - ], - "/UploadHotkeys2": [ - -1640.0, - 900.0 - ], - "/ValidatePushed": [ - 900.0, - 0.0 - ], - "/_sub_graph": [ - -1200.0, - 0.0 - ], - "/make_module_folder": [ - 2420.0, - 0.0 - ], - "/make_package": [ - 2100.0, - 0.0 - ], - "/populate": [ - -800.0, - 680.0 - ], - "/ship": [ - -1200.0, - 600.0 - ], - "/unzip_env": [ - -1200.0, - 1000.0 - ], - "/versions": [ - 140.0, - 0.0 - ], - "name": [ - 0.0, - 0.0, - 0.0 - ] - }, - "collapse": { - "/BeginPR": false, - "/BeginPR/PR_Dev/GetPR_URL": false, - "/BeginPR/PR_Master": false, - "/BeginPR/PR_Master/GetPR_URL": false, - "/BeginPush/Push/ParseGitReturn": false, - "/BeginRelease/CheckCommits": true, - "/BeginRelease/CheckCommits/Status": true, - "/BeginRelease/GitCurBranch/CurBranch": true, - "/CheckCommits/Status": true, - "/CheckCommits/Status/ParseGitReturn": true, - "/CheckCommits/ValidateStatus": false, - "/CreateRelease/UploadChangelog": false, - "/CreateRelease/UploadHotkeys": false, - "/GitCmd": true, - "/GitCurBranch": true, - "/GitCurBranch/CurBranch": false, - "/GitCurBranch/CurBranch/ParseGitReturn": false, - "/GitCurBranch/CurBranch/UpdateWorkingBranch": false, - "/GitCurBranch/ValidateExactBranch": false, - "/GitCurBranch/ValidateLegalBranch": false, - "/GitPR": false, - "/GitStatus": true, - "/GitUpload": true, - "/GitUpload/DoIt": false, - "/JsonLoad": false, - "/PR_Dev": false, - "/ValidatePushed": false, - "/ValidatePushed/Push": true, - "/ValidatePushed/ValidatePush": false, - "/VersionCheck/CheckoutMaster/ParseGitReturn": true, - "/VersionCheck/CheckoutWorking/ParseGitReturn": true, - "/VersionCheck/GitPull/ParseGitReturn": true, - "/VersionCheck/LoadMasterVersion/ReadMasterVersion": true, - "/VersionCheck/ValidateMaster/CurBranch": true, - "/VersionCheck/ValidateMaster/CurBranch/ParseGitReturn": true, - "/VersionCheck/ValidateMaster/CurBranch/UpdateWorkingBranch": true, - "/VersionCheck/ValidateMaster/ValidateExactBranch": true, - "/VersionCheck/ValidateMaster/ValidateLegalBranch": true, - "/VersionCheck/ValidateWorking/CurBranch": true, - "/VersionCheck/ValidateWorking/CurBranch/ParseGitReturn": true, - "/VersionCheck/ValidateWorking/CurBranch/UpdateWorkingBranch": true, - "/VersionCheck/ValidateWorking/ValidateExactBranch": true, - "/VersionCheck/ValidateWorking/ValidateLegalBranch": true, - "/make_package": false, - "/populate": true, - "/populate/docs": false, - "/populate/launch": false, - "/populate/src": false, - "/populate/src/remove_extras": false, - "/ship": true, - "/ship/get_release": false, - "/ship/upload": false, - "/ship/zip": false, - "/ship/zip/remove_ship_dir": false, - "/unzip_env": true, - "/unzip_env/remove_zipped": true, - "/versions": false - } - }, - "nodes": { - "/": { - "attrs": { - "branch": { - "type": "raw", - "value": "dev", - "comment": "Name of the head branch" - }, - "changelog_md": { - "type": "raw", - "value": "${release_dir}/changelog.md", - "comment": "File path to changelog.md" - }, - "executable": { - "type": "raw", - "value": "python.exe", - "comment": "Path the current python executable" - }, - "hotfix_version": { - "type": "int", - "value": "0", - "comment": "Hotfix number updating to" - }, - "hotkeys_md": { - "type": "raw", - "value": "${release_dir}/hotkeys.md", - "comment": "File path to hotkeys.md" - }, - "ignore_uncommited_changes": { - "type": "bool", - "value": "False", - "comment": "Force the graph to ignore uncommited changes on disc" - }, - "master_pr_url": { - "type": "raw", - "value": "https://api.github.com/repos/SunriseProductions/nxt/pulls/123", - "comment": "The api url to the pr who's base is master" - }, - "release_body": { - "type": "raw", - "value": "Auto generated release", - "comment": "Markdown or plain text used for the release body as well as the master PR" - }, - "release_dict": { - "type": "dict", - "value": "{}", - "comment": "Data returned from REST request" - }, - "release_dir": { - "type": "raw", - "value": "${path::TEMP}", - "comment": "File path to the temp dir were the release files get dumped" - }, - "release_title": { - "type": "raw", - "value": "Release v${version}", - "comment": "Release title" - }, - "release_type": { - "type": "NoneType", - "comment": "minor or hotfix" - }, - "secret": { - "type": "tuple", - "value": "()", - "comment": "tuple of (github_username, auth_token). DO NOT enter those details here! Please create a file with them and let the layer node load them for you." - }, - "secret_path": { - "type": "raw", - "value": "${file::secrets}" - }, - "version": { - "type": "raw", - "value": "editor_v1.2.3-api_v4.5.6" - } - }, - "child_order": [ - "CheckCommits", - "ValidatePushed", - "CreateRelease", - "BeginPR", - "ParseGitReturn", - "ParseGitReturn2", - "GitCmd", - "GitCmd2", - "JsonLoad2", - "BeginRelease", - "GitCurBranch2", - "make_package", - "GenerateHotkeysMD", - "make_module_folder", - "UpdateMasterPR", - "GenerateChangelog", - "versions", - "ReleaseLoop", - "PR_Dev", - "MakeReleaseDir" - ], - "comment": "This graph is meant to be referenced into another graph to run, only edit if you are sure you know what you're doing.\nAttrs will auto populate with the correct data as the graph runs. The only thing you need to setup is the 'release_type'.\nExpects you to have a 'secrets' file in the same dir as this graph. See the attr comment on 'secrets'.", - "code": [ - "# Builtin", - "import sys", - "import os", - "import subprocess", - "import json", - "import requests", - "import pickle", - "import webbrowser", - "import shutil", - "# Internal", - "import nxt", - "from nxt.constants import API_VERSION", - "from nxt.ui.constants import EDITOR_VERSION", - "", - "# Load git auth token", - "with open('${secret_path}', 'rb') as fp:", - " self.secret = pickle.load(fp)", - "", - "", - "editor_version = 'editor-v' + EDITOR_VERSION.VERSION_STR", - "api_version = 'api-v' + API_VERSION.VERSION_STR", - "", - "self.version = editor_version + '-' + api_version", - "" - ] - }, - "/BeginPR/DoDev": { - "code": [ - "if ${/versions.hotfix}:", - " execute(start='/PR_Dev')" - ] - }, - "/BeginPR/PR_Master": { - "attrs": { - "body": { - "type": "raw", - "value": "Auto generated PR > ${target}" - }, - "target": { - "type": "raw", - "value": "master" - }, - "title": { - "value": "Release ${version}" - } - }, - "child_order": [ - "GetPR_URL" - ], - "instance": "/GitPR", - "enabled": true - }, - "/BeginPR/PR_Master/GetPR_URL": { - "enabled": true, - "code": [ - "STAGE.master_pr_url = self.pr_data['url']" - ] - }, - "/BeginRelease/CheckCommits": { - "comment": "Check that there are no un-commited changes on the branch.", - "instance": "/GitStatus", - "code": [ - "print('Validating commits...')" - ] - }, - "/BeginRelease/GitCurBranch": { - "child_order": [ - "CurBranch", - "ValidateLegalBranch" - ], - "instance": "/GitCurBranch" - }, - "/BeginRelease/GitCurBranch/CurBranch": { - "child_order": [ - "ParseGitReturn", - "UpdateWorkingBranch" - ] - }, - "/BeginRelease/GitCurBranch/CurBranch/ParseGitReturn": { - "attrs": { - "key": { - "value": "\"*\"" - } - }, - "instance": "/GitCurBranch/CurBranch/ParseGitReturn", - "code": [ - "self.lines = [l for l in self.input.split(\"\\n\") if l.startswith(${key})]", - "if ${allow_multi_line}:", - " self.result = [l.strip(${key}).strip() for l in self.lines]", - "else:", - " if self.lines:", - " self.result = w(self.lines[0].strip(${key}).strip())", - "# print(self.input.split(\"\\n\"))" - ] - }, - "/BeginRelease/GitCurBranch/CurBranch/UpdateWorkingBranch": { - "enabled": true - }, - "/BeginRelease/GitCurBranch/ValidateLegalBranch": { - "enabled": true - }, - "/CreateRelease/DraftRelease": { - "instance": "/GitRelease" - }, - "/CreateRelease/OpenReleaseURL": { - "code": [ - "webbrowser.open(STAGE.release_dict[\"html_url\"], new=2)" - ] - }, - "/CreateRelease/UploadChangelog": { - "attrs": { - "asset_path": { - "value": "${changelog_md}" - } - }, - "instance": "/CreateRelease/UploadHotkeys" - }, - "/CreateRelease/UploadHotkeys": { - "attrs": { - "asset_path": { - "type": "raw", - "value": "${/GenerateHotkeysMD/Setup.output_file}" - }, - "content_type": { - "type": "raw", - "value": "text/markdown" - } - }, - "instance": "/GitUpload" - }, - "/CreateRelease/UploadMayaMod": { - "attrs": { - "asset_path": { - "type": "raw", - "value": "${/make_module_folder/zip_mod.zip_path}" - } - }, - "instance": "/CreateRelease/UploadHotkeys", - "code": [ - "print('Starting nxt_maya upload...')" - ] - }, - "/CreateRelease/UploadPackage": { - "attrs": { - "asset_path": { - "type": "raw", - "value": "${/make_package.result_path}" - } - }, - "instance": "/CreateRelease/UploadHotkeys", - "code": [ - "print('Starting package upload...')" - ] - }, - "/GenerateChangelog/UpdateMasterPR": { - "code": [ - "data = {\"body\": STAGE.changelog_text}", - "new_rel_resp = requests.post(STAGE.master_pr_url, auth=self.secret, data=json.dumps(data))", - "if new_rel_resp.status_code != 200:", - " raise Exception('Unsuccessfully updated PR ({})'.format(new_rel_resp.text))", - "print('Updated master PR')" - ] - }, - "/GenerateHotkeysMD/Setup": {}, - "/PR_Dev/GetPR_URL": { - "enabled": false - }, - "/ValidatePushed/GetRemoteHEAD": { - "attrs": { - "remote_sha": {} - }, - "code": [ - "response = requests.get('https://api.github.com/repos/SunriseProductions/nxt/branches/${branch}', auth=self.secret, timeout=30)", - "remote_branch = json.loads(response.content)", - "self.remote_sha = remote_branch['commit']['sha']" - ] - }, - "/ValidatePushed/Push": { - "attrs": { - "cmd_args": { - "type": "str", - "value": "\"rev-parse\", \"HEAD\"" - } - }, - "instance": "/GitCmd" - }, - "/ValidatePushed/ValidatePush": { - "code": [ - "if ${../Push/ParseGitReturn.result} != '${../GetRemoteHEAD.remote_sha}':", - " raise Exception('Your local HEAD does not match the remote HEAD, please push you commits!')", - "print(\"Remote and local in sync!\")" - ] - }, - "/make_module_folder/zip_mod": { - "attrs": { - "zip_name": { - "type": "raw", - "value": "${release_dir}/nxt_maya" - }, - "zip_path": { - "type": "raw", - "value": "${zip_name}.zip" - } - }, - "code": [ - "", - "self.zip_path = shutil.make_archive('${zip_name}', 'zip', '${mod_folder}')", - "shutil.rmtree('${mod_folder}')" - ] - }, - "/BeginPR": { - "child_order": [ - "PR_Master", - "DoDev" - ], - "comment": "Open a PR to master and dev, then open them in your browser.", - "execute_in": "/ValidatePushed", - "code": [ - "print(\"Beginning pull request...\")" - ] - }, - "/BeginRelease": { - "start_point": true, - "child_order": [ - "GitCurBranch", - "CheckCommits" - ], - "comment": "Begin a release", - "code": [ - "print('Beginning Release!')" - ] - }, - "/CreateRelease": { - "child_order": [ - "DraftRelease", - "UploadHotkeys", - "UploadChangelog", - "UploadPackage", - "UploadMayaMod", - "OpenReleaseURL" - ], - "comment": "Create the draft release and open it in your browser.", - "code": [ - "print(\"Beginning release...\")" - ] - }, - "/GenerateChangelog": { - "child_order": [ - "ParseCommits", - "UpdateMasterPR" - ], - "execute_in": "/BeginPR" - }, - "/GenerateHotkeysMD": { - "start_point": false, - "child_order": [ - "Setup" - ], - "execute_in": "/GenerateChangelog", - "code": [ - "print(\"Generating hotkeys.md...\")" - ] - }, - "/MakeReleaseDir": { - "execute_in": "/versions", - "code": [ - "dir_name = 'nxt_${version}'.replace('.', '-')", - "dir_path = os.path.join('${path::}', dir_name)", - "if os.path.isdir(dir_path):", - " print('Release dir already exsists, removing it...')", - " shutil.rmtree(dir_path)", - "", - "os.mkdir(dir_path)", - "STAGE.release_dir = dir_path.replace(os.sep, '/')" - ] - }, - "/PR_Dev": { - "attrs": { - "target": { - "value": "dev" - }, - "title": { - "type": "raw", - "value": "Release v${version} (${target})" - } - }, - "child_order": [ - "GetPR_URL" - ], - "instance": "/BeginPR/PR_Master", - "enabled": true - }, - "/ReleaseLoop": { - "comment": "Calls /CreateRelease", - "execute_in": "/make_module_folder", - "code": [ - "# Parse return from versions sub-graph", - "for version_info in (${/versions.API}, ${/versions.EDITOR}, ${/versions.GRAPH}):", - " if not version_info:", - " continue", - " version_cat, version_str = version_info", - " version_tag = version_cat.lower() + '_v' + version_str", - " version_name = version_cat.capitalize()", - " parameters = {'/CreateRelease.version_tag': version_tag,", - " '/CreateRelease.version_name': version_name,", - " '/CreateRelease.version_str': version_str}", - " execute(start='/CreateRelease', parameters=parameters)" - ] - }, - "/ValidatePushed": { - "child_order": [ - "Push", - "GetRemoteHEAD", - "ValidatePush" - ], - "comment": "Push your changes to remote.", - "execute_in": "/MakeReleaseDir", - "code": [ - "print('Pushing...')" - ] - }, - "/make_module_folder": { - "attrs": { - "output_dir": { - "value": "${release_dir}" - } - }, - "start_point": false, - "child_order": [ - "copy_source", - "qt_py", - "make_resources", - "populate_mod_template", - "zip_mod" - ], - "execute_in": "/make_package" - }, - "/make_package": { - "attrs": { - "dest_dir": { - "type": "raw", - "value": "${release_dir}" - }, - "expected_result": { - "type": "raw", - "value": "nxt-${version}.tar.gz", - "comment": "Assumes naming based on patterns in setuptools. Setuptools does not appear to allow forcing this name." - }, - "result_path": { - "comment": "Resulting path, computed in code block to use platform correct sperators." - } - }, - "start_point": false, - "execute_in": "/GenerateHotkeysMD", - "code": [ - "self.result_path = os.path.join('${dest_dir}', '${expected_result}').replace(os.sep, '/')" - ] - }, - "/versions": { - "attrs": { - "API": { - "type": "NoneType", - "value": "None" - }, - "EDITOR": { - "type": "NoneType", - "value": "None" - }, - "GRAPH": { - "type": "NoneType", - "value": "None" - }, - "_graph_path": { - "value": "${file::validate_version_numbers.nxt}" - }, - "hotfix": { - "type": "bool", - "value": "False" - } - }, - "comment": "Runs version validation sub-graph", - "instance": "/_sub_graph", - "execute_in": "/BeginRelease" - } - } -} \ No newline at end of file diff --git a/build/generate_changelog_md.nxt b/build/generate_changelog_md.nxt deleted file mode 100644 index c57319c..0000000 --- a/build/generate_changelog_md.nxt +++ /dev/null @@ -1,431 +0,0 @@ -{ - "version": "1.15", - "alias": "generate_change_log", - "color": "#ffaaff", - "mute": false, - "solo": false, - "meta_data": { - "positions": { - "/": [ - 0.0, - 0.0, - 0.0 - ], - "/BeginPR": [ - 1200.0, - 0.0 - ], - "/BeginRelease": [ - 0, - 0 - ], - "/BeginRelease/GitCurBranch/CurBranch/ParseGitReturn": [ - 0.0, - 0.0, - 0.0 - ], - "/CheckCommits": [ - 300.0, - 0.0 - ], - "/CreateRelease": [ - 2743.3073550205086, - -1.0996848810221387 - ], - "/DoIt": [ - -1640.0, - 900.0 - ], - "/GenerateChangelog": [ - 0.0, - -20.0 - ], - "/GenerateHotkeysMD": [ - 1800.0, - 0.0 - ], - "/GitCmd": [ - -800.0, - 760.0 - ], - "/GitCmd2": [ - 618.6319850156622, - 104.42705080595923 - ], - "/GitCurBranch": [ - -800.0, - 520.0 - ], - "/GitCurBranch2": [ - -160.0, - 420.0 - ], - "/GitPR": [ - -1200.0, - 680.0 - ], - "/GitRelease": [ - -800.0, - 600.0 - ], - "/GitRelease2": [ - 219.4455692046359, - -150.45574389461672 - ], - "/GitStatus": [ - -1200.0, - 520.0 - ], - "/GitUpload": [ - -1200.0, - 760.0 - ], - "/JsonLoad": [ - -1200.0, - 200.0 - ], - "/JsonLoad2": [ - -720.0, - 420.0 - ], - "/ParseGitReturn": [ - -400.00000000000006, - 218.85416666666669 - ], - "/ParseGitReturn2": [ - 139.97772221620224, - 201.2760907674117 - ], - "/STAGE": [ - -1123.7400818788076, - -75.57563472014294 - ], - "/UpdateHotkeys": [ - 1811.2303797361437, - -305.46632882310877 - ], - "/UploadHotkeys2": [ - -1640.0, - 900.0 - ], - "/ValidatePushed": [ - 900.0, - 0.0 - ], - "/VersionCheck": [ - 600.0, - 0.0 - ], - "/make_module_folder": [ - 2421.8606515416, - -0.12985212006404367 - ], - "/make_package": [ - 2100.0, - 0.0 - ], - "/node": [ - 1528.7310849793016, - 173.60697703569716 - ], - "/populate": [ - -800.0, - 680.0 - ], - "/ship": [ - -1200.0, - 600.0 - ], - "/unzip_env": [ - -1200.0, - 1000.0 - ], - "name": [ - 0.0, - 0.0, - 0.0 - ] - }, - "collapse": { - "/BeginPR": false, - "/BeginPR/PR_Dev": false, - "/BeginPR/PR_Dev/GetPR_URL": false, - "/BeginPR/PR_Master": false, - "/BeginPR/PR_Master/GetPR_URL": false, - "/BeginPush/Push/ParseGitReturn": false, - "/BeginRelease/GitCurBranch/CurBranch": true, - "/CheckCommits": false, - "/CheckCommits/Status": true, - "/CheckCommits/Status/ParseGitReturn": true, - "/CheckCommits/ValidateStatus": true, - "/CreateRelease/UploadChangelog": false, - "/CreateRelease/UploadHotkeys": false, - "/GitCmd": true, - "/GitCurBranch": true, - "/GitCurBranch/CurBranch": false, - "/GitCurBranch/CurBranch/ParseGitReturn": false, - "/GitCurBranch/CurBranch/UpdateWorkingBranch": false, - "/GitCurBranch/ValidateExactBranch": false, - "/GitCurBranch/ValidateLegalBranch": false, - "/GitPR": false, - "/GitStatus": true, - "/GitUpload": true, - "/GitUpload/DoIt": false, - "/JsonLoad": false, - "/ValidatePushed": false, - "/ValidatePushed/Push": true, - "/ValidatePushed/ValidatePush": false, - "/VersionCheck": false, - "/VersionCheck/CheckoutMaster": true, - "/VersionCheck/CheckoutMaster/ParseGitReturn": true, - "/VersionCheck/CheckoutWorking": true, - "/VersionCheck/CheckoutWorking/ParseGitReturn": true, - "/VersionCheck/GitPull": true, - "/VersionCheck/GitPull/ParseGitReturn": true, - "/VersionCheck/LoadMasterVersion": true, - "/VersionCheck/LoadMasterVersion/ReadMasterVersion": true, - "/VersionCheck/ValidateMaster": true, - "/VersionCheck/ValidateMaster/CurBranch": true, - "/VersionCheck/ValidateMaster/CurBranch/ParseGitReturn": true, - "/VersionCheck/ValidateMaster/CurBranch/UpdateWorkingBranch": true, - "/VersionCheck/ValidateMaster/ValidateExactBranch": true, - "/VersionCheck/ValidateMaster/ValidateLegalBranch": true, - "/VersionCheck/ValidateVersion": false, - "/VersionCheck/ValidateWorking": true, - "/VersionCheck/ValidateWorking/CurBranch": true, - "/VersionCheck/ValidateWorking/CurBranch/ParseGitReturn": true, - "/VersionCheck/ValidateWorking/CurBranch/UpdateWorkingBranch": true, - "/VersionCheck/ValidateWorking/ValidateExactBranch": true, - "/VersionCheck/ValidateWorking/ValidateLegalBranch": true, - "/make_package": false, - "/populate": true, - "/populate/docs": false, - "/populate/launch": false, - "/populate/src": false, - "/populate/src/remove_extras": false, - "/ship": true, - "/ship/get_release": false, - "/ship/upload": false, - "/ship/zip": false, - "/ship/zip/remove_ship_dir": false, - "/unzip_env": true, - "/unzip_env/remove_zipped": true - } - }, - "nodes": { - "/": { - "attrs": { - "branch": { - "type": "raw", - "value": "dev", - "comment": "Name of the head branch" - }, - "changelog_md": { - "type": "raw", - "value": "${release_dir}/changelog.md", - "comment": "File path to changelog.md" - }, - "executable": { - "type": "raw", - "value": "python.exe", - "comment": "Path the current python executable" - }, - "hotfix_version": { - "type": "int", - "value": "0", - "comment": "Hotfix number updating to" - }, - "hotkeys_md": { - "type": "raw", - "value": "${release_dir}/hotkeys.md", - "comment": "File path to hotkeys.md" - }, - "ignore_uncommited_changes": { - "type": "bool", - "value": "False", - "comment": "Force the graph to ignore uncommited changes on disc" - }, - "master_pr_url": { - "type": "raw", - "value": "https://api.github.com/repos/SunriseProductions/nxt/pulls/123", - "comment": "The api url to the pr who's base is master" - }, - "release_body": { - "type": "raw", - "value": "Auto generated release", - "comment": "Markdown or plain text used for the release body as well as the master PR" - }, - "release_dict": { - "type": "dict", - "value": "{}", - "comment": "Data returned from REST request" - }, - "release_dir": { - "type": "raw", - "value": "${path::}", - "comment": "File path to the temp dir were the release files get dumped" - }, - "release_title": { - "type": "raw", - "value": "Release v${version}", - "comment": "Release title" - }, - "release_type": { - "type": "NoneType", - "comment": "minor or hotfix" - }, - "secret": { - "type": "tuple", - "value": "()", - "comment": "tuple of (github_username, auth_token). DO NOT enter those details here! Please create a file with them and let the layer node load them for you." - }, - "secret_path": { - "type": "raw", - "value": "${file::secrets}" - } - }, - "child_order": [ - "ParseGitReturn", - "ParseGitReturn2", - "GitCmd", - "GitCmd2", - "JsonLoad2", - "GitCurBranch2", - "GenerateChangelog", - "node" - ], - "comment": "This graph is meant to be referenced into another graph to run, only edit if you are sure you know what you're doing.\nAttrs will auto populate with the correct data as the graph runs. The only thing you need to setup is the 'release_type'.\nExpects you to have a 'secrets' file in the same dir as this graph. See the attr comment on 'secrets'.", - "code": [ - "# Builtin", - "import sys", - "import os", - "import subprocess", - "import json", - "import requests", - "import pickle", - "import webbrowser", - "import shutil", - "# Internal", - "import nxt", - "", - "# Load git auth token", - "with open('${secret_path}', 'rb') as fp:", - " self.secret = pickle.load(fp)", - "", - "self.executable = sys.executable.replace(os.path.sep, '/')" - ] - }, - "/GenerateChangelog/ParseCommits": { - "attrs": { - "graph_version": { - "value": "unknown" - } - }, - "child_order": [ - "WriteChangelog" - ], - "code": [ - "commits = []", - "next_pg = {'url': '${master_pr_url}/commits'}", - "self.lists = {}", - "for name in self.order:", - " self.lists[name] = []", - "", - "while next_pg:", - " response = requests.get(next_pg['url'], auth=self.secret, timeout=30)", - " commits += json.loads(response.content)", - " next_pg = response.links.get('next')", - "for commit in commits:", - " message = commit['commit']['message']", - " for line in message.split('\\n'):", - " for name in self.order:", - " token = self.tokens[name]", - " if line.startswith(token):", - " self.lists[name].append(line.split(token)[-1])", - "", - "# This attribute needed for token subbing in child", - "from nxt.constants import GRAPH_VERSION", - "self.graph_version = GRAPH_VERSION.VERSION_STR" - ] - }, - "/GenerateChangelog/ParseCommits/WriteChangelog": { - "code": [ - "STAGE.changelog_md = os.path.join('${release_dir}', 'changelog.md')", - "with open(STAGE.changelog_md, 'w+') as changelog:", - " changelog.write('')", - " for name in self.order:", - " lines = self.lists[name]", - " if not lines:", - " continue", - " changelog.write(self.headers[name])", - " for line in lines:", - " token = self.tokens[name].rstrip()", - " changelog.write('`{}` {}\\n'.format(token, line))", - " changelog.write(${footer})", - "with open('${changelog_md}') as f:", - " STAGE.changelog_text = f.read()" - ] - }, - "/GenerateChangelog": { - "attrs": { - "footer": { - "type": "str", - "value": "\"\"\"${contents::${file::release_footer.md}}\"\"\"" - }, - "headers": { - "type": "dict", - "value": "{}" - }, - "lists": { - "type": "dict", - "value": "{}" - }, - "order": { - "type": "list", - "value": "[]" - }, - "tokens": { - "type": "dict", - "value": "{}" - } - }, - "child_order": [ - "ParseCommits" - ], - "comment": "Generate a changelog from the commits on the master PR", - "enabled": true, - "code": [ - "print('Generating changelog...')", - "", - "CRITICAL = 'critical'", - "ADD = 'add'", - "CHANGE = 'change'", - "REMOVE = 'remove'", - "NOTE = 'note'", - "", - "self.tokens = {", - " CRITICAL: '! ',", - " ADD: '+ ',", - " CHANGE: '* ',", - " REMOVE: '- ',", - " NOTE: '... '", - "}", - "self.lists = {", - " CRITICAL: [],", - " ADD: [],", - " CHANGE: [],", - " REMOVE: [],", - " NOTE: []", - "}", - "self.headers = {", - " CRITICAL: '## Critical changes:\\n',", - " ADD: '## Additions:\\n',", - " CHANGE: '## Changes:\\n',", - " REMOVE: '## Removals:\\n',", - " NOTE: '## Notes:\\n'", - "}", - "self.order = [CRITICAL, ADD, CHANGE, REMOVE, NOTE]" - ] - } - } -} \ No newline at end of file diff --git a/build/make_maya_plugin.nxt b/build/make_maya_plugin.nxt index 27cb33e..fe646db 100644 --- a/build/make_maya_plugin.nxt +++ b/build/make_maya_plugin.nxt @@ -1,122 +1,181 @@ { - "version": "1.15", - "alias": "make_maya_plugin", - "color": "#0052aa", - "mute": false, - "solo": false, + "version": "1.17", + "alias": "make_maya_plugin", + "color": "#0052aa", + "mute": false, + "solo": false, + "references": [ + "../../nxt/build/GitUtils.nxt" + ], "meta_data": { "positions": { "/": [ - 0.0, - 0.0, + 0.0, + 0.0, 0.0 - ], + ], + "/clone_fresh_source": [ + 540.0, + 80.0 + ], "/make_module_folder": [ - -135.0, - -149.0 - ], + 560.0, + -220.0 + ], "/zip_maya_plugin": [ - -142.0, + -142.0, -162.0 ] } - }, + }, "nodes": { "/": { "child_order": [ - "make_module_folder" - ], + "make_module_folder", + "clone_fresh_source" + ], + "attrs": { + "mod_folder": { + "type": "raw", + "value": "${output_dir}/nxt_maya" + }, + "nxt_root": { + "type": "raw", + "value": "${path::..}" + }, + "output_dir": { + "type": "raw", + "value": "${nxt_root}/build" + }, + "src": { + "type": "raw", + "value": "remote" + }, + "src_clone": { + "type": "raw", + "value": "${path::nxt-src}" + } + }, + "code": [ + "import os", + "import shutil", + "import sys", + "import subprocess" + ] + }, + "/clone_fresh_source": { + "instance": "/GitClone", + "attrs": { + "cmd_args": { + "type": "str", + "value": "\"clone\", \"git@github.com:nxt-dev/nxt.git\", \"${dir}\"" + }, + "dir": { + "type": "raw", + "value": "${src_clone}" + } + } + }, + "/make_module_folder": { + "start_point": true, + "child_order": [ + "copy_source", + "qt_py", + "make_resources", + "populate_mod_template", + "clean_up" + ], + "code": [ + "if os.path.isdir('${mod_folder}'):", + " shutil.rmtree('${mod_folder}')", + " print('removed old build')", + "template_mod = os.path.join('${nxt_root}', 'integration', 'maya')", + "shutil.copytree(template_mod, '${mod_folder}')" + ] + }, + "/make_module_folder/clean_up": { "code": [ - "import os", - "import shutil", - "import sys" + "if os.path.isdir('D:/Projects/nxt_editor/build/nxt-src'):", + " try:", + " shutil.rmtree('${src_clone}')", + " except:", + " print('!' * 79)", + " print('Failed to delete soruce clone, do so by hand!')", + " print(' '* 3 + '${src_clone}')" ] - }, + }, "/make_module_folder/copy_source": { "attrs": { - "source_folder": { - "type": "raw", + "clone_node": { + "type": "raw", + "value": "/clone_fresh_source" + }, + "core_script_src": { + "type": "raw", "value": "${mod_folder}/scripts/nxt" + }, + "editor_script_src": { + "type": "raw", + "value": "${mod_folder}/scripts/nxt_editor" } - }, + }, "code": [ - "shutil.copytree('${nxt_root}/nxt', '${source_folder}')" + "shutil.copytree('${path::${nxt_root}/nxt_editor}', '${editor_script_src}')", + "", + "# Get core code", + "if '${src}' == 'local':", + " shutil.copytree('${path::${nxt_root}/../nxt/nxt}', '${core_script_src}')", + "else:", + " if os.path.isdir('${src_clone}'):", + " shutil.rmtree('${src_clone}')", + " execute(start='${clone_node}')", + " shutil.copytree('${path::${src_clone}/nxt}', '${core_script_src}')" ] - }, + }, "/make_module_folder/make_resources": { "code": [ - "import subprocess", - "result_path = '${../copy_source.source_folder}/ui/resources.py'", - "result_pyc = result_path + 'c'", - "qrc_path = '${../copy_source.source_folder}/ui/resources/resources.qrc'", - "if os.path.isfile(result_path):", - " os.remove(result_path)", - " print('removed old {}'.format(result_path))", - "if os.path.isfile(result_pyc):", - " os.remove(result_pyc)", - " print('removed old {}'.format(result_pyc))", - "print('generating nxt resources from {} to {}'.format(qrc_path, result_path))", + "import subprocess", + "result_path = '${../copy_source.source_folder}/ui/resources.py'", + "result_pyc = result_path + 'c'", + "qrc_path = '${../copy_source.source_folder}/ui/resources/resources.qrc'", + "if os.path.isfile(result_path):", + " os.remove(result_path)", + " print('removed old {}'.format(result_path))", + "if os.path.isfile(result_pyc):", + " os.remove(result_pyc)", + " print('removed old {}'.format(result_pyc))", + "print('generating nxt resources from {} to {}'.format(qrc_path, result_path))", "subprocess.call(['pyside2-rcc', qrc_path, '-o', result_path])" ] - }, + }, "/make_module_folder/populate_mod_template": { "attrs": { "mod_file": { - "type": "raw", + "type": "raw", "value": "${mod_folder}/nxt.mod" } - }, + }, "code": [ - "# from nxt_maya import MAYA_PLUGIN_VERSION", - "with open('${mod_file}', 'r') as fp:", - " content = fp.read()", - "content = content.replace('', '0.1.0')", - "with open('${mod_file}', 'w') as fp:", + "# from nxt_maya import MAYA_PLUGIN_VERSION", + "with open('${mod_file}', 'r') as fp:", + " content = fp.read()", + "content = content.replace('', '0.1.0')", + "with open('${mod_file}', 'w') as fp:", " fp.write(content)" ] - }, + }, "/make_module_folder/qt_py": { "code": [ - "import requests", - "", - "result = requests.get(\"https://raw.githubusercontent.com/mottosso/Qt.py/master/Qt.py\")", - "", - "# Making qt.py into a differently shaped module so it doesn't display in plugin list.", - "os.makedirs('${mod_folder}/scripts/Qt')", - "", - "with open('${mod_folder}/scripts/Qt/__init__.py', 'w+') as fp:", + "import requests", + "", + "result = requests.get(\"https://raw.githubusercontent.com/mottosso/Qt.py/master/Qt.py\")", + "", + "# Making qt.py into a differently shaped module so it doesn't display in plugin list.", + "os.makedirs('${mod_folder}/scripts/Qt')", + "", + "with open('${mod_folder}/scripts/Qt/__init__.py', 'w+') as fp:", " fp.write(result.content)" ] - }, - "/make_module_folder": { - "attrs": { - "mod_folder": { - "type": "raw", - "value": "${output_dir}/nxt_maya" - }, - "nxt_root": { - "type": "raw", - "value": "${path::..}" - }, - "output_dir": { - "type": "raw", - "value": "${nxt_root}/build" - } - }, - "start_point": true, - "child_order": [ - "copy_source", - "qt_py", - "make_resources", - "populate_mod_template" - ], - "code": [ - "if os.path.isdir('${mod_folder}'):", - " shutil.rmtree('${mod_folder}')", - " print('removed old build')", - "template_mod = os.path.join('${nxt_root}', 'integration', 'maya')", - "shutil.copytree(template_mod, '${mod_folder}')" - ] } } } \ No newline at end of file diff --git a/build/release.nxt b/build/release.nxt new file mode 100644 index 0000000..b879517 --- /dev/null +++ b/build/release.nxt @@ -0,0 +1,193 @@ +{ + "version": "1.17", + "alias": "release", + "color": "#008bbe", + "mute": false, + "solo": false, + "references": [ + "make_maya_plugin.nxt", + "../../nxt/build/release.nxt" + ], + "comp_overrides": { + "../../nxt/build/GitUtils.nxt": { + "solo": false + } + }, + "meta_data": { + "aliases": { + "../../nxt/build/release.nxt": "Release_Core" + }, + "colors": { + "../../nxt/build/release.nxt": "#aaaaff" + }, + "positions": { + "/CreateRelease": [ + 2180.0, + 60.0 + ], + "/CreateRelease_pasted": [ + 2220.0, + 240.0 + ], + "/GitClone": [ + -1000.0, + 640.0 + ], + "/GitCmd": [ + -1000.0, + 540.0 + ], + "/GitCurBranch": [ + -1000.0, + 300.0 + ], + "/GitPR": [ + -1000.0, + 360.0 + ], + "/GitRelease": [ + -1000.0, + 480.0 + ], + "/GitStatus": [ + -1020.0, + 120.0 + ], + "/GitUpload": [ + -1000.0, + 740.0 + ], + "/JsonLoad": [ + -1000.0, + 420.0 + ], + "/ReleaseLoop": [ + 2140.0, + 0.0 + ], + "/build_maya_plugin": [ + 1800.0, + 0.0 + ], + "/clone_fresh_source": [ + 1840.0, + 300.0 + ], + "/make_module_folder": [ + 1820.0, + 0.0 + ], + "/versions": [ + 140.0, + 0.0 + ] + }, + "collapse": { + "/CreateRelease/UploadMayaPlugin": false, + "/ValidatePushed/Push": false, + "/clone_fresh_source": false + } + }, + "nodes": { + "/": { + "child_order": [ + "CreateRelease", + "ReleaseLoop", + "make_module_folder", + "versions" + ], + "attrs": { + "repo": { + "value": "nxt_editor" + } + }, + "code": [ + "# Builtin", + "import sys", + "import os", + "import subprocess", + "import json", + "import requests", + "import pickle", + "import webbrowser", + "import shutil", + "# Internal", + "import nxt", + "from nxt.constants import API_VERSION", + "from nxt_editor.constants import EDITOR_VERSION" + ] + }, + "/BeginRelease/set_version_string": { + "code": [ + "STAGE.version = 'editor-v' + EDITOR_VERSION.VERSION_STR", + "" + ] + }, + "/CreateRelease": { + "child_order": [ + "DraftRelease", + "UploadMayaPlugin", + "OpenReleaseURL" + ] + }, + "/CreateRelease/UploadMayaPlugin": { + "instance": "/GitUpload", + "attrs": { + "asset_path": { + "type": "raw", + "value": "${/make_module_folder/zip_maya_plugin.zip_path}" + }, + "content_type": { + "type": "raw", + "value": "application/zip" + } + } + }, + "/ReleaseLoop": { + "execute_in": "/make_module_folder" + }, + "/ValidatePushed/GetRemoteHEAD": { + "attrs": { + "repo": { + "value": "nxt_editor" + } + } + }, + "/make_module_folder": { + "start_point": false, + "execute_in": "/GenerateChangelog", + "child_order": [ + "copy_source", + "qt_py", + "make_resources", + "populate_mod_template", + "clean_up", + "zip_maya_plugin" + ] + }, + "/make_module_folder/zip_maya_plugin": { + "attrs": { + "zip_name": { + "type": "raw", + "value": "${release_dir}/nxt_maya" + }, + "zip_path": { + "type": "raw", + "value": "${zip_name}.zip" + } + }, + "code": [ + "self.zip_path = shutil.make_archive('${zip_name}', 'zip', '${mod_folder}')", + "shutil.rmtree('${mod_folder}')" + ] + }, + "/versions": { + "attrs": { + "EDITOR": { + "type": "NoneType", + "value": "None" + } + } + } + } +} \ No newline at end of file diff --git a/build/validate_version_numbers.nxt b/build/validate_version_numbers.nxt deleted file mode 100644 index e84fbbf..0000000 --- a/build/validate_version_numbers.nxt +++ /dev/null @@ -1,441 +0,0 @@ -{ - "version": "1.16", - "alias": "validate_version_numbers", - "color": "#be6c00", - "mute": false, - "solo": false, - "references": [ - "GitUtils.nxt" - ], - "meta_data": { - "positions": { - "/CheckoutMaster": [ - 140.0, - 0.0 - ], - "/CheckoutWorking": [ - 840.0, - 0.0 - ], - "/GitCmd": [ - -1600.0, - -200.0 - ], - "/GitCurBranch": [ - -1200.0, - -20.0 - ], - "/GitPR": [ - -1600.0, - -60.0 - ], - "/GitRelease": [ - -1600.0, - -280.0 - ], - "/GitStatus": [ - -1200.0, - -280.0 - ], - "/GitUpload": [ - -1600.0, - 240.0 - ], - "/JsonLoad": [ - -1600.0, - 60.0 - ], - "/ParseVersionJSON": [ - 480.0, - 0.0 - ], - "/ParseVersions": [ - 460.0, - 0.0 - ], - "/PreCheck": [ - -160.0, - 0.0 - ], - "/ValidateVersion": [ - 1140.0, - 0.0 - ], - "/ValidateWorking": [ - -20.0, - 120.0 - ], - "/init": [ - -240.0, - 0.0 - ], - "/make_module_folder": [ - -1600.0, - 160.0 - ] - }, - "collapse": { - "/CheckoutMaster/CheckoutMaster": false, - "/CheckoutMaster/GitPull": false, - "/CheckoutMaster/ValidateMaster": false, - "/CheckoutWorking/CheckoutWorking": true, - "/CheckoutWorking/ValidateWorking": true, - "/GitCurBranch": true, - "/GitStatus": true, - "/GitUpload": true, - "/ParseVersions": false, - "/make_module_folder": true - } - }, - "nodes": { - "/": { - "child_order": [ - "CheckCommits", - "ValidatePushed", - "CreateRelease", - "BeginPR", - "ParseGitReturn", - "ParseGitReturn2", - "GitCmd", - "GitCmd2", - "CheckoutMaster", - "JsonLoad2", - "BeginRelease", - "GitCurBranch2", - "CheckoutWorking", - "make_package", - "GenerateHotkeysMD", - "make_module_folder", - "UpdateMasterPR", - "GenerateChangelog", - "ParseVersionJSON", - "ValidateWorking", - "ParseVersions", - "ValidateVersion", - "init" - ], - "attrs": { - "API": { - "type": "NoneType", - "value": "None" - }, - "EDITOR": { - "type": "NoneType", - "value": "None" - }, - "GRAPH": { - "comment": "minor or hotfix", - "type": "NoneType", - "value": "None" - }, - "branch": { - "comment": "Name of the head branch", - "type": "NoneType" - }, - "hotfix": { - "comment": "Will be set during the init, leave as False", - "type": "bool", - "value": "False" - }, - "master_api_version": { - "comment": "Expected API version number", - "type": "list", - "value": "[0, 0, 0]" - }, - "master_editor_version": { - "comment": "Expected EDITOR version number", - "type": "list", - "value": "[0, 0, 0]" - }, - "master_graph_version": { - "comment": "Expected GRAPH version number", - "type": "list", - "value": "[0, 0]" - }, - "release_types": { - "type": "dict", - "value": "{'major':0, 'minor':1, 'hotfix':2}" - } - }, - "code": [ - "# Builtin", - "import subprocess", - "import json", - "# Internal", - "from nxt.constants import API_VERSION, GRAPH_VERSION", - "from nxt.ui.constants import EDITOR_VERSION", - "attrs = ('API', 'EDITOR', 'GRAPH')", - "for attr in attrs:", - " val = getattr(self, attr)", - " if val == 'None' or val is None:", - " continue", - " if not val.startswith((\"'\", \"\\\"\")):", - " setattr(STAGE, attr, w(val))", - "" - ] - }, - "/CheckoutMaster": { - "start_point": false, - "execute_in": "/init", - "child_order": [ - "CheckoutMaster", - "ValidateMaster", - "GitPull", - "ValidateUpToDate" - ], - "enabled": true, - "comment": "Checks out master and makes sure your version number is incremented correctly against master.", - "code": [ - "print('Getting master...')", - "" - ] - }, - "/CheckoutMaster/CheckoutMaster": { - "instance": "/GitCmd", - "child_order": [ - "ParseGitReturn" - ], - "attrs": { - "cmd_args": { - "value": "\"checkout\", \"master\"" - } - } - }, - "/CheckoutMaster/GitPull": { - "instance": "/GitCmd", - "child_order": [ - "ParseGitReturn" - ], - "attrs": { - "cmd_args": { - "value": "\"pull\"" - } - } - }, - "/CheckoutMaster/GitPull/ParseGitReturn": { - "attrs": { - "allow_multi_line": { - "value": "True" - } - } - }, - "/CheckoutMaster/ValidateMaster": { - "instance": "/GitCurBranch", - "child_order": [ - "ValidateExactBranch" - ], - "code": [ - "" - ] - }, - "/CheckoutMaster/ValidateMaster/CurBranch/ParseGitReturn": { - "attrs": { - "key": { - "value": "\"*\"" - } - } - }, - "/CheckoutMaster/ValidateMaster/ValidateExactBranch": { - "enabled": true, - "attrs": { - "exact_branch": { - "value": "'master'" - } - }, - "code": [ - "branch = ${../CurBranch/ParseGitReturn.result}", - "if branch != ${exact_branch}:", - " raise Exception(\"Invalid branch '{}' is not ${exact_branch}\".format(branch))", - "print('Your current branch \"{}\" is valid!'.format(branch))" - ] - }, - "/CheckoutMaster/ValidateUpToDate": { - "code": [ - "git_return = '\\n'.join(${../GitPull/ParseGitReturn.result})", - "if 'Updating' in git_return and 'Fast-forward' in git_return:", - " print('Updating master')", - " ", - "elif 'Already up to date.' in git_return or 'Already up-to-date':", - " print('Master already up to date')", - "else:", - " raise Exception('Failed to pull master!')", - "" - ] - }, - "/CheckoutWorking": { - "start_point": false, - "execute_in": "/ParseVersions", - "child_order": [ - "CheckoutWorking", - "ValidateWorking" - ], - "enabled": true - }, - "/CheckoutWorking/CheckoutWorking": { - "instance": "/GitCmd", - "attrs": { - "cmd_args": { - "value": "\"checkout\", \"${branch}\"" - } - } - }, - "/CheckoutWorking/ValidateWorking": { - "instance": "/GitCurBranch", - "child_order": [ - "ValidateExactBranch" - ], - "code": [ - "" - ] - }, - "/CheckoutWorking/ValidateWorking/CurBranch/ParseGitReturn": { - "attrs": { - "key": { - "value": "\"*\"" - } - } - }, - "/CheckoutWorking/ValidateWorking/ValidateExactBranch": { - "enabled": true, - "attrs": { - "exact_branch": { - "value": "'${branch}'" - } - }, - "code": [ - "branch = ${../CurBranch/ParseGitReturn.result}", - "if branch != ${exact_branch}:", - " raise Exception(\"Invalid branch '{}' is not ${exact_branch}\".format(branch))", - "print('Your current branch \"{}\" is valid!'.format(branch))" - ] - }, - "/ParseVersions": { - "execute_in": "/CheckoutMaster", - "child_order": [ - "Parse_API_Version", - "Parse_EDITOR_Version" - ] - }, - "/ParseVersions/Parse_API_Version": { - "instance": "/JsonLoad", - "execute_in": "/CheckoutMaster", - "child_order": [ - "ReadMasterVersions" - ], - "attrs": { - "file_path": { - "type": "raw", - "value": "${file::../nxt/version.json}" - } - } - }, - "/ParseVersions/Parse_API_Version/ReadMasterVersions": { - "code": [ - "for version_name, version_numbers in self.file_data.items():", - " if not isinstance(version_numbers, dict):", - " continue", - " version_number = []", - " for version_typ in ('MAJOR', 'MINOR', 'PATCH'):", - " version_num = version_numbers.get(version_typ)", - " if not isinstance(version_num, int):", - " continue", - " version_number.append(version_num)", - " attr_name = 'master_{}_version'.format(version_name.lower())", - " setattr(STAGE, attr_name, version_number)" - ] - }, - "/ParseVersions/Parse_EDITOR_Version": { - "instance": "../Parse_API_Version", - "attrs": { - "file_path": { - "type": "raw", - "value": "${file::../nxt/ui/version.json}" - } - } - }, - "/ValidateVersion": { - "execute_in": "/CheckoutWorking", - "code": [ - "# The index of the int that should incriment in the version num", - "version_data = {", - "'API': {'rel_type': ${/.API},", - " 'version': ${/.master_api_version},", - " 'actual': API_VERSION.VERSION_TUPLE},", - "'GRAPH': {'rel_type': ${/.GRAPH},", - " 'version': ${/.master_graph_version},", - " 'actual': GRAPH_VERSION.VERSION_TUPLE},", - "'EDITOR': {'rel_type': ${/.EDITOR},", - " 'version': ${/.master_editor_version},", - " 'actual': EDITOR_VERSION.VERSION_TUPLE}", - " }", - "passed = True", - "for rel_cat, rel_dict in version_data.items():", - " actual = rel_dict['actual']", - " master = rel_dict['version']", - " print master", - " rel_type = rel_dict['rel_type']", - " if rel_type is None:", - " continue", - " incriment_idx = self.release_types.get(rel_type)", - " expected = master[:]", - " expected[incriment_idx] += 1", - " for i in range(incriment_idx+1, 3):", - " print('resetting' + str(i))", - " try:", - " expected[i] = 0", - " except IndexError:", - " continue", - " expected = tuple(expected)", - " if expected != actual:", - " passed = False", - " print('Invlaid {} version number! Expected:{} Actual:{}'.format(rel_cat, expected, actual))", - " continue", - " version_str = '.'.join(str(i) for i in actual)", - " version_info = [rel_cat, version_str]", - " print('Pushing to STAGE: ', rel_cat, version_info)", - " setattr(STAGE, rel_cat, version_info)", - " print('{} version number {} is valid {}'.format(rel_cat, actual, rel_type))", - "if not passed:", - " raise Exception('Invlaid version numbers detected! See log')", - "", - "" - ] - }, - "/init": { - "start_point": true, - "code": [ - "rel_cats = (${/.API}, ${/.EDITOR}, ${/.GRAPH})", - "if not any(rel_cats):", - " print(\"\"\"API: ${/.API}, EDITOR:${/.EDITOR}, GRAPH:${/.GRAPH}\"\"\")", - " raise Exception('Unable to validate version numbers, no parameters provided!')", - "", - "for mode in rel_cats:", - " if mode is None:", - " continue", - " if mode.lower() not in ('major', 'minor', 'hotfix'):", - " raise Exception('Unknown release type \"{}\"'.format(mode))", - "if ${/.GRAPH} is not None and ${/.GRAPH}.lower() == 'hotfix':", - " raise Exception('Cannot hotfix GRAPH versions!')", - "if not '${/.branch}':", - " raise Exception('No working branch provided!')", - "STAGE.hotfix = True", - "hotfix_str = 'hotfix'", - "if ${API} and ${API} != hotfix_str:", - " STAGE.hotfix = False", - "", - "if ${EDITOR} and ${EDITOR} != hotfix_str:", - " STAGE.hotfix = False", - " ", - "if ${GRAPH} and ${GRAPH} != hotfix_str:", - " STAGE.hotfix = False", - "", - "# Debug tool", - "# for rel_cat, actual in (('API', (0, 1, 1)), ('EDITOR', (2, 0, 0)), ('GRAPH', (1, 15, 0))):", - "# version_str = '.'.join(str(i) for i in actual)", - "# version_info = [rel_cat, version_str]", - "# print('Setting', rel_cat, version_str)", - "# setattr(STAGE, rel_cat, version_info)" - ] - } - } -} \ No newline at end of file diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..3a15f69 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,5 @@ +# Example Content + +The files in this folder cover most of the concepts in nxt. +Open the graphs in your nxt editor and brows around. The nodes have comments and code designed to explain nxt's features. + diff --git a/examples/example_file.txt b/examples/example_file.txt new file mode 100644 index 0000000..7b9a58f --- /dev/null +++ b/examples/example_file.txt @@ -0,0 +1 @@ +Hello, I am an example file. Located at ${file::example_file.txt} \ No newline at end of file diff --git a/examples/file_list.nxt b/examples/file_list.nxt new file mode 100644 index 0000000..7b2e7e0 --- /dev/null +++ b/examples/file_list.nxt @@ -0,0 +1,60 @@ +{ + "version": "1.17", + "alias": "file_list", + "color": "#aa50a3", + "mute": false, + "solo": false, + "meta_data": { + "positions": { + "/list_files": [ + 0, + 0 + ] + } + }, + "nodes": { + "/": { + "attrs": { + "files": { + "comment": "A list that will be populated with file paths", + "type": "list", + "value": "[]" + }, + "root_dir": { + "comment": "Folder to list files in", + "type": "raw", + "value": "/path/to/dir" + } + }, + "code": [ + "import os" + ] + }, + "/list_files": { + "start_point": true, + "child_order": [ + "save_to_STAGE" + ], + "comment": "This graph simply lists files in its directory. It is used by sub_graphs.nxt.", + "code": [ + "\"\"\"", + "${_comment}", + "\"\"\"", + "if os.path.isdir('${root_dir}'):", + " self.files = os.listdir('${root_dir}')", + "else:", + " raise IOError('\"${root_dir}\" is not a valid directory path!')" + ] + }, + "/list_files/save_to_STAGE": { + "code": [ + "\"\"\"", + "In this node we push the files we found back up to the world node via the STAGE keyword.", + "Any attributes on the STAGE (aka layer or world node) will be sent back to super graphs.", + "\"\"\"", + "STAGE.files = ${files}", + "STAGE.a_message_for_you = 'A message from the sub graph.' # We'll send back an extra message here just as an example" + ] + } + } +} \ No newline at end of file diff --git a/examples/layering.nxt b/examples/layering.nxt new file mode 100644 index 0000000..d90b4a9 --- /dev/null +++ b/examples/layering.nxt @@ -0,0 +1,69 @@ +{ + "version": "1.17", + "alias": "layering", + "color": "#df9d02", + "mute": false, + "solo": false, + "references": [ + "the_basics.nxt" + ], + "meta_data": { + "positions": { + "/instance2": [ + 380.0, + 260.0 + ] + } + }, + "nodes": { + "/": { + "child_order": [ + "counting", + "instance2", + "README" + ], + "attrs": { + "another_num": { + "type": "int", + "value": "8" + }, + "example_attr": { + "comment": "Right click my source to see my historical value", + "value": "I've been overloaded" + } + } + }, + "/README": { + "comment": "I've replaced the comment here, notice the 2 colored dots indicating there is an opinion about this node's coment on 2 layers.", + "code": [ + "\"\"\"", + "This graph covers how layers work. Notice this node exesits on 2 layers.", + "Layering is one of the most powerful tools nxt has to offer. With layers we can non-distructivly change and extend graphs.", + "", + "You'll notice the different layer colors are displayed almost everywhere to give you a quick viusal hint as to where data is coming from.", + "However if an internal attribute (any attr not in the user attr table) has been overloaded you'll see colored dots next to it.", + "", + "At the bottom right of the code editor there are 2 colored dots inidcating that both layers have an opinion about the code on this node.", + "", + "Attributes also have what's called `Historical Values`, try right clicking on the source cell of the `.example_attr`.", + "\"\"\"" + ] + }, + "/counting": { + "attrs": { + "count": { + "value": "${another_num}" + } + } + }, + "/instance2": { + "instance": "/instance", + "comment": "You can of course instance from other layers both above and below you.", + "attrs": { + "thing_i_like": { + "value": "Grapes" + } + } + } + } +} \ No newline at end of file diff --git a/examples/quick_start_graphs/README.md b/examples/quick_start_graphs/README.md new file mode 100644 index 0000000..cb4bb5d --- /dev/null +++ b/examples/quick_start_graphs/README.md @@ -0,0 +1,23 @@ +# NXT Quick Start +Graphs in this folder contain the scaffolding to support common use cases of nxt. Just copy them out to your project, no need to update the reference paths. + +_The assumption is you're generally familiar with the basics of nxt and need a little boost into using some of the deeper features._ + +### Quick Start Files +- `my_sub_graph.nxt` Is a simple example of how to call a sub graph. +- `my_remote_maya_graph.nxt` Is a simple example of how to call a graph in a remote headless Maya session. +- `my_contexts.py` Context plugin example see [here](#example-remote-context). +- `maya_context.nxt` Graph used by the context plugin, again see [here](#example-remote-context). + +--- + +# Example remote context +To quickly get started with remote contexts we've provided a Maya example. +- First open the nxt editor, navigate to the help menu, and select `Open Plugins Dir`. +- Next copy `my_contexts.py` and `maya_context.nxt` to the plugins folder that just opened. + - _Assuming your API major version is 0, the path to place this file would be: `~/nxt/config/0/plugins/my_contexts.py`_ + +If you're working on Windows and have Maya 2019 installed in its default location then you're all set. +If that isn't you, go ahead and read the comments in `my_contexts.py` to configure your context correctly. + +Checkout `my_remote_maya_graph.nxt` for an example on how to use your new remote context! diff --git a/examples/quick_start_graphs/maya_context.nxt b/examples/quick_start_graphs/maya_context.nxt new file mode 100644 index 0000000..ee4e1d8 --- /dev/null +++ b/examples/quick_start_graphs/maya_context.nxt @@ -0,0 +1,55 @@ +{ + "version": "1.17", + "alias": "my_remote_maya_graph", + "color": "#991c24", + "mute": false, + "solo": false, + "references": [ + "$NXT_BUILTINS/remote_contexts.nxt" + ], + "meta_data": { + "positions": { + "/_remote_sub_graph": [ + -600.0, + 60.0 + ], + "/_sub_graph": [ + -600.0, + 0.0 + ], + "/start": [ + 0, + 0 + ] + } + }, + "nodes": { + "/": { + "attrs": { + "maya_graph_file": { + "type": "raw", + "value": "${path::/do_something_in_maya.nxt}" + } + } + }, + "/start": { + "start_point": true, + "child_order": [ + "exec_graph_in_Maya" + ], + "comment": "Put any setup here that should run before the maya graph starts up." + }, + "/start/exec_graph_in_Maya": { + "instance": "/_remote_sub_graph", + "comment": "Any attrs from the sub graph that you want to use after exec should be added to this node.\nChildren of this node will then inhert the values.", + "attrs": { + "_context": { + "value": "Maya" + }, + "_graph_path": { + "value": "${maya_graph_file}" + } + } + } + } +} \ No newline at end of file diff --git a/examples/quick_start_graphs/my_contexts.py b/examples/quick_start_graphs/my_contexts.py new file mode 100644 index 0000000..31b89a1 --- /dev/null +++ b/examples/quick_start_graphs/my_contexts.py @@ -0,0 +1,31 @@ +# Builtin +import os + +# External +from nxt.remote.contexts import RemoteContext, register_context + +""" +This file should be placed in your nxt config folder. +First navigate to the help menu, and select `Open Plugins Dir`. + +Next copy this file and `maya_context.nxt` to the plugins folder that just +opened. + +Example location: + ~/nxt/config/0/plugins +""" + +# Maya 2019 + +# This is the name you'll use in your graphs to call this context +maya2019_name = 'Maya' +# This is the path to the mayapy executable +maya2019_exe = 'C:/Program Files/Autodesk/Maya2019/bin/mayapy.exe' +# This is the path to your custom context graph +maya2019_graph = os.path.abspath(os.path.join(os.path.dirname(__file__), + 'maya_context.nxt')) +# Create a RemoteContext object +maya_2019_context = RemoteContext(maya2019_name, maya2019_exe, maya2019_graph) +# Register your context with nxt +register_context(maya_2019_context) + diff --git a/examples/quick_start_graphs/my_sub_graph.nxt b/examples/quick_start_graphs/my_sub_graph.nxt new file mode 100644 index 0000000..b18283b --- /dev/null +++ b/examples/quick_start_graphs/my_sub_graph.nxt @@ -0,0 +1,59 @@ +{ + "version": "1.17", + "alias": "my_sub_graph", + "color": "#991c24", + "mute": false, + "solo": false, + "references": [ + "$NXT_BUILTINS/sub_graphs.nxt" + ], + "meta_data": { + "positions": { + "/_sub_graph": [ + -600.0, + 0.0 + ], + "/execute_sub_graph": [ + 80.0, + 100.0 + ], + "/start": [ + 0, + 0 + ] + } + }, + "nodes": { + "/": { + "child_order": [ + "start", + "execute_sub_graph" + ], + "attrs": { + "sub_graph_file": { + "type": "raw", + "value": "${path::/another_graph.nxt}" + } + } + }, + "/start": { + "start_point": true, + "child_order": [ + "execute_sub_graph" + ], + "comment": "Put any setup here that should run before the sub graph exec.", + "code": [ + "# Setup before you run your graph" + ] + }, + "/start/execute_sub_graph": { + "instance": "/_sub_graph", + "comment": "Any attrs from the sub graph that you want to use after exec should be added to this node.\nChildren of this node will then inhert the values.", + "attrs": { + "_graph_path": { + "value": "${sub_graph_file}" + } + } + } + } +} \ No newline at end of file diff --git a/examples/the_basics.nxt b/examples/the_basics.nxt new file mode 100644 index 0000000..793aadc --- /dev/null +++ b/examples/the_basics.nxt @@ -0,0 +1,298 @@ +{ + "version": "1.17", + "alias": "the_basics", + "color": "#00a9cb", + "mute": false, + "solo": false, + "meta_data": { + "positions": { + "/README": [ + -200.0, + -340.0 + ], + "/counting": [ + 140.0, + -120.0 + ], + "/hello_user": [ + -580.0, + -60.0 + ], + "/hello_world": [ + -580.0, + -120.0 + ], + "/instance": [ + 60.0, + 260.0 + ], + "/instance_src": [ + -300.0, + 260.0 + ], + "/live_tokens": [ + -300.0, + 140.0 + ], + "/results": [ + 60.0, + 140.0 + ], + "/tokens": [ + -300.0, + 20.0 + ] + } + }, + "nodes": { + "/": { + "child_order": [ + "hello_world", + "counting", + "tokens", + "hello_user", + "live_tokens", + "results", + "README", + "instance_src", + "instance" + ], + "comment": "If you haven't already, navigate to the `Window` menu and open the `Output Log`.\nFor simplicity lets just look at the `Raw Ouput` tab. Our examples will be printing a lot.", + "attrs": { + "example_attr": { + "comment": "Just an attr", + "type": "raw", + "value": "This is just an example" + }, + "my_num": { + "comment": "We'll use this int in the /counting node", + "type": "int", + "value": "0" + } + }, + "code": [ + "\"\"\"", + "This is what we call the `World` or `Layer` node.", + "Its path is `/` and what makes it sepcial is its where you can import external modules and define globals.", + "Every node in the graph inherits from this node. Notice the attribute called `my_num` in the Property Editor.", + "(If you don't see the `Property Editor` navigate to `Window` > `Property Editor`)", + "Click around to different nodes and you'll see they all have access to that attirbute.", + "\"\"\"", + "import getpass # This import is avaible to every node in the graph.", + "", + "my_global = 'A global string!' # This variable name is avaible everywhere in this graph without the use of the global keyword.", + "" + ] + }, + "/README": { + "comment": "This is a comment, you'll see them used on other nodes.", + "code": [ + "\"\"\"", + "This graph covers some of the basics of nxt.", + "Each node has a little comment explaining what it does and how to use it.", + "One node you won't see on the `Stage View` is the `World` node. You can access it by double clicking the layer name in the `Layer Manager`.", + "If you don't see a widget called `Layer Manager` you can open it by navigating to `Window` > `Layer Manager`.", + "\"\"\"" + ] + }, + "/counting": { + "child_order": [ + "child" + ], + "comment": "Execute this hierarcy to see how attributes are inherited by children nodes.", + "attrs": { + "count": { + "comment": "Number to count to", + "type": "int", + "value": "5" + } + }, + "code": [ + "\"\"\"", + "${/._comment}", + "", + "", + "${_comment}", + "", + "Right click this node in the `Stage View` and select `Execute Hierarchy` ", + "\"\"\"", + "", + "print('-'*79)", + "print('Hi I\\'m ${_name} and my child will inherit whatever number I count to.')", + "for _ in range(${count}):", + " self.my_num += 1", + "" + ] + }, + "/counting/child": { + "code": [ + "print('My parent counted to ${my_num}')", + "print('-'*79)" + ] + }, + "/hello_user": { + "comment": "Here is a a slighly more complex example showing how code can be set and used in an attr value.\nTokens directly string sub. Since we want Python to evaluate the value of `.noun` we use format.", + "attrs": { + "noun": { + "value": "getpass.getuser()" + } + }, + "code": [ + "\"\"\"", + "${/._comment}", + "", + "${_comment}", + "", + "", + "To execute just this node, right click this node in the `Stage View` and select `Execute ${_nxtpath}` ", + "\"\"\"", + "print('Hello {}'.format(${noun}))" + ] + }, + "/hello_world": { + "comment": "Execute this node to print \"Hello World\" except using a token for the noun.\nTry changing the value of the attr `noun` and see what happens.", + "attrs": { + "noun": { + "type": "raw", + "value": "World" + } + }, + "code": [ + "\"\"\"", + "${/._comment}", + "", + "", + "${_comment}", + "", + "To execute just this node, right click this node in the `Stage View` and select `Execute ${_nxtpath}` ", + "\"\"\"", + "print('Hello ${noun}')" + ] + }, + "/instance": { + "instance": "/instance_src", + "attrs": { + "thing_i_like": { + "value": "Toast" + } + } + }, + "/instance_src": { + "comment": "Any node in the graph can be instanced. Instancing is basically creating a linked copy of a node. The code and attributes are not duplicated.\n- You can instance as many times as you want.\n- Each instance can overload any attibute of its instance source node.\n- Instances inheirt from their parent and their instance.\n\nTake careful note of this node and its instance, see how attrs are overloaded and inherited.", + "attrs": { + "inst_attr": { + "type": "list", + "value": "[1, 2, 3]" + }, + "thing_i_like": { + "type": "raw", + "value": "Cereal" + } + }, + "code": [ + "\"\"\"", + "${_comment}", + "\"\"\"", + "# My node path is ${_nxtpath}", + "# I like ${thing_i_like}" + ] + }, + "/live_tokens": { + "comment": "This little execution chain will demonstrait how tokens remain live during execution.", + "attrs": { + "live": { + "type": "raw", + "value": "Hello World." + } + }, + "code": [ + "\"\"\"", + "${/._comment}", + "", + "${_comment}", + "", + "Right click this node in the `Stage View` and select `Execute From Selected` ", + "\"\"\"", + "print('-'*79)", + "print('${_name} says: ')", + "print('${live}')", + "# Now we're going to set a new value", + "self.live = 'Hey there!'", + "" + ] + }, + "/results": { + "execute_in": "/live_tokens", + "comment": "Notice how this node prints the token with the value that was set during execution.", + "code": [ + "\"\"\"", + "${_comment}", + "\"\"\"", + "print('${_name} got the new token value:')", + "# Note that the value shown in the ediotr is the pre-exec value.", + "# To see how execution changed things navigate to `View` > `Cached View` after executing from /live_tokens.", + "print('${/live_tokens.live}')", + "" + ] + }, + "/tokens": { + "child_order": [ + "non_attr_tokens" + ], + "comment": "Any attr of any node can be accessed via tokens.\nTokens are recursivly resolved, so if a token resolves to a token we contrinue resolving it until no more tokens are returned.\n\nThere are fiew things you should note about tokens.\n\n- They are direct string subs, they do not work with non-stringable objects.\n- Be careful using quote marks in attribute values as they may cause syntax errors.\n- Tokens are live, which is to say if a node changes the attrs value, a node executed later will get the new value in its tokens.", + "attrs": { + "my_custom_attr": { + "type": "str", + "value": "\"${/.example_attr}\"" + }, + "quotes": { + "type": "str", + "value": "'World'" + } + }, + "code": [ + "\"\"\"", + "${_comment}", + "\"\"\"", + "# Example of improper useage of quotes in an attr value", + "msg = 'Hello ${quotes}'", + "# If you do need quotes try something like this", + "msg = 'Hello' + ${quotes}", + "", + "# Other examples", + "", + "'${/hello_world.noun}' # Getting an attr from another node", + "'${/counting.count}' # Get an attr from another node", + "'${/.my_num}' # Getting an attr directly from the world node", + "${my_custom_attr} # Tokens resolve though multiple references, have a look at the actuall value of `.my_custom_attr`", + "", + "'${_nxtcolor}' # Getting the node's color (Very useful for workflow nodes)", + "", + "'${_nxtpath}' # Get the full node path of the node the token is in", + "", + "'${_name}' # Get the node's name." + ] + }, + "/tokens/non_attr_tokens": { + "code": [ + "\"\"\"", + "Below are some examples of other tokens we provide", + "", + "As you'll see below, another cool feature of tokens is they can be nested.", + "\"\"\"", + "# File token, resolves to real and valid file/folder path.", + "'${file::}' # The folder this graph is saved in.", + "'${file::../}' # The parent folder of this graphs folder.", + "'${file::the_basics.nxt}' # This graph.", + "", + "# Path token, like the file token the path token creates a valid path, but the file/folder doesn't have to exsist.", + "'${path::output_dir}'", + "'${path::/home/fakefile.txt}'", + "", + "# Contents token, allows you to resolve the contents of a file anywhere. ", + "# If you open that file in a text editor you'll notice it contains a token.", + "'${contents::${file::example_file.txt}}'" + ] + } + } +} \ No newline at end of file diff --git a/examples/using_sub_graphs.nxt b/examples/using_sub_graphs.nxt new file mode 100644 index 0000000..3a87255 --- /dev/null +++ b/examples/using_sub_graphs.nxt @@ -0,0 +1,103 @@ +{ + "version": "1.17", + "alias": "builtin_graphs", + "color": "#f38b00", + "mute": false, + "solo": false, + "references": [ + "$NXT_BUILTINS/sub_graphs.nxt" + ], + "meta_data": { + "positions": { + "/README": [ + 0.0, + -200.0 + ], + "/_sub_graph": [ + -600.0, + 0.0 + ], + "/execute_sub_graph": [ + 0.0, + 0.0 + ], + "/node": [ + 0.0, + 140.0 + ], + "/process_return": [ + 40.0, + 20.0 + ] + } + }, + "nodes": { + "/": { + "attrs": { + "another_graph": { + "comment": "The graph we want to execute.", + "type": "raw", + "value": "${file::file_list.nxt}" + } + } + }, + "/README": { + "code": [ + "\"\"\"", + "This graph contains a simple example of a sub-graph.", + "If you inspect this graph in a text editor you'll see its referencing our builtin graph \"${file::$NXT_BUILTINS/sub_graphs.nxt}\".", + "When using a builtin graph its generaly best practice to use the environment variable $NXT_BUILTINS as the graph filepath's root.", + "Doing so maximizes portibility of your graph.", + "", + "In this graph we're going to execute \"${another_graph}\".", + "Any attributes avaible on the calling node (In this case node `/execute_sub_graph`), will be passed to the sub_graphs world node.", + "So in our case we're going to be most interested in passing down the attr `.root_dir` as it needed by the sub graph.", + "Check out `/execute_sub_graph/process_return` to see what is returned from sub graphs.", + "\"\"\"" + ] + }, + "/execute_sub_graph": { + "start_point": true, + "instance": "/_sub_graph", + "child_order": [ + "process_return" + ], + "attrs": { + "_graph_path": { + "type": "raw", + "value": "${another_graph}" + }, + "a_message_for_you": { + "comment": "Expected return from sub graph", + "type": "NoneType" + }, + "files": { + "comment": "Expected return from sub graph", + "type": "list", + "value": "[]" + }, + "root_dir": { + "type": "raw", + "value": "${file::}" + } + } + }, + "/execute_sub_graph/process_return": { + "comment": "All of the attrs from the sub graphs world node are passed back to the calling node.\nNote: Attrs created at runtime are not inherited by child nodes. We intent to enable this in the future.\nIf you want to use a returned attr from a sub graph, add an attr on the calling node with the same name as the attr in the sub graph.\n\nIf you execute this graph and check cached mode you'll see we got back a list of files as well as a message from the sub graph.\n", + "code": [ + "\"\"\"", + "${_comment}", + "\"\"\"", + "# Notice these tokens resolve to nothing before we execute. ", + "# Thats because the attrs referenced here will be created and returned by the sub graph at runtime.", + "print('-'*79)", + "print('The sub graph ran. These are the files contained in \"${root_dir}\"')", + "", + "for filepath in ${files}:", + " print(' '*3 + '- ' + filepath)", + "", + "print('The sub graph also sent back a message: \"${a_message_for_you}\"')" + ] + } + } +} \ No newline at end of file diff --git a/integration/maya/plug-ins/nxt_maya.py b/integration/maya/plug-ins/nxt_maya.py index b548925..fbe8eb5 100644 --- a/integration/maya/plug-ins/nxt_maya.py +++ b/integration/maya/plug-ins/nxt_maya.py @@ -16,7 +16,7 @@ from Qt import QtCore # Internal -import nxt.ui.main_window +import nxt_editor.main_window import nxt.remote.nxt_socket from nxt import nxt_log @@ -87,18 +87,11 @@ def doIt(self, args): string_args = [] for arg in range(len(args)): string_args += [args.asString(arg)] - if 'reload' in string_args: - try: - from showtools.shared.imports.reload import autoreload - autoreload(nxt.ui.main_window) - except: - logger.exception("Unable to reload!") - return if 'close' in string_args: if __NXT_INSTANCE__: __NXT_INSTANCE__.close() return - nxt_win = nxt.ui.main_window.MainWindow() + nxt_win = nxt_editor.main_window.MainWindow() if 'win32' in sys.platform: # gives nxt it's own entry on taskbar nxt_win.setWindowFlags(QtCore.Qt.Window) diff --git a/nxt_editor/actions.py b/nxt_editor/actions.py index c9f5d3e..15c7eb6 100644 --- a/nxt_editor/actions.py +++ b/nxt_editor/actions.py @@ -394,7 +394,7 @@ def __init__(self, main_window): def save_layer(): layer = self.save_layer_action.data() - self.save_layer_action.setData(None) + clear_action_data(self.actions()) self.main_window.save_layer(layer) self.save_layer_action.triggered.connect(save_layer) # Save as @@ -407,7 +407,7 @@ def save_layer(): def save_layer_as(): layer = self.save_layer_as_action.data() - self.save_layer_as_action.setData(None) + clear_action_data(self.actions()) self.main_window.save_layer_as(layer) self.save_layer_as_action.triggered.connect(save_layer_as) # Save all layers @@ -427,7 +427,7 @@ def save_layer_as(): def open_source(): layer = self.open_source_action.data() - self.open_source_action.setData(None) + clear_action_data(self.actions()) self.main_window.open_source(layer) self.open_source_action.triggered.connect(open_source) # Change color @@ -469,6 +469,7 @@ def change_color(): def remove_layer(): layer = self.remove_layer_action.data() + clear_action_data(self.actions()) if not layer: layer = self.main_window.model.display_layer if not layer: @@ -488,7 +489,7 @@ def remove_layer(): def mute_layer(): layer = self.mute_layer_action.data() - self.mute_layer_action.setData(None) + clear_action_data(self.actions()) if not layer: layer = self.main_window.model.target_layer if not layer: @@ -505,7 +506,7 @@ def mute_layer(): def solo_layer(): layer = self.solo_layer_action.data() - self.solo_layer_action.setData(None) + clear_action_data(self.actions()) if not layer: layer = self.main_window.model.target_layer if not layer: @@ -520,6 +521,7 @@ def solo_layer(): def new_above(): layer = self.new_layer_above_action.data() + clear_action_data(self.actions()) if not layer: layer = self.main_window.model.target_layer if not layer: @@ -535,6 +537,7 @@ def new_above(): def new_below(): layer = self.new_layer_below_action.data() + clear_action_data(self.actions()) if not layer: layer = self.main_window.model.target_layer if not layer: @@ -551,6 +554,7 @@ def new_below(): def ref_above(): layer = self.ref_layer_above_action.data() + clear_action_data(self.actions()) if not layer: layer = self.main_window.model.target_layer if not layer: @@ -567,6 +571,7 @@ def ref_above(): def ref_below(): layer = self.ref_layer_below_action.data() + clear_action_data(self.actions()) if not layer: layer = self.main_window.model.target_layer if not layer: @@ -1758,3 +1763,9 @@ def __init__(self, main_window): self.unindent_line, self.run_line_global_action, self.run_line_local_action] + + +def clear_action_data(action_list): + """Resets the data for each action in the given list to None""" + for action in action_list: + action.setData(None) diff --git a/nxt_editor/dockwidgets/layer_manager.py b/nxt_editor/dockwidgets/layer_manager.py index a4b6746..9f1d8de 100644 --- a/nxt_editor/dockwidgets/layer_manager.py +++ b/nxt_editor/dockwidgets/layer_manager.py @@ -12,6 +12,7 @@ from nxt_editor.dockwidgets.dock_widget_base import DockWidgetBase from nxt import nxt_path + logger = logging.getLogger(nxt_editor.LOGGER_NAME) @@ -192,7 +193,6 @@ def set_disp_layer(): menu.addAction(self.actions.ref_layer_above_action) self.actions.ref_layer_below_action.setData(layer) menu.addAction(self.actions.ref_layer_below_action) - menu.popup(QtGui.QCursor.pos()) diff --git a/nxt_editor/main_window.py b/nxt_editor/main_window.py index 419c0c7..126da05 100644 --- a/nxt_editor/main_window.py +++ b/nxt_editor/main_window.py @@ -7,7 +7,7 @@ from collections import OrderedDict import webbrowser from functools import partial -import threading +import time # External from Qt import QtWidgets @@ -31,7 +31,7 @@ from nxt_editor.dialogs import (NxtFileDialog, NxtWarningDialog, UnsavedLayersDialogue, UnsavedChangesMessage) from nxt_editor import actions, LoggingSignaler -from nxt.constants import API_VERSION, GRAPH_VERSION +from nxt.constants import API_VERSION, GRAPH_VERSION, USER_PLUGIN_DIR from nxt.remote.client import NxtClient from nxt_editor import resources @@ -245,7 +245,6 @@ def __init__(self, filepath=None, parent=None): self.in_startup = False app = QtWidgets.QApplication.instance() app.aboutToQuit.connect(self.shutdown_rpc_server) - self.close_signal.connect(self.shutdown_rpc_server) # RPC def startup_rpc_server(self, join=True): @@ -282,6 +281,14 @@ def shutdown_rpc_server(self): self.nxt.shutdown_rpc_server() if self.model: self.model.processing.emit(False) + if not self.rpc_log_tail: + return + wait_started = time.time() + while not self.rpc_log_tail.isFinished(): + QtWidgets.QApplication.processEvents() + if time.time() - wait_started > 5: + logger.error('Failed to stop rpc log tail!') + return self.rpc_log_tail = None def safe_stop_rpc_tailing(self): @@ -736,6 +743,7 @@ def closeEvent(self, event): event.ignore() return event.accept() + self.shutdown_rpc_server() # Window state state_key = user_dir.EDITOR_CACHE.WINODW_STATE geo_key = user_dir.EDITOR_CACHE.MAIN_WIN_GEO @@ -1026,6 +1034,8 @@ def __init__(self, parent=None): self.help_menu.setTearOffEnabled(True) prefs_dir_action = self.help_menu.addAction('Open Prefs Dir') prefs_dir_action.triggered.connect(self.open_prefs_dir) + config_dir_action = self.help_menu.addAction('Open Plugins Dir') + config_dir_action.triggered.connect(self.open_plugins_dir) self.help_menu.addSeparator() self.help_menu.addAction(self.main_window.app_actions.docs_action) github_action = self.help_menu.addAction('GitHub') @@ -1118,6 +1128,19 @@ def open_prefs_dir(): except: logger.exception('Failed to open user dir') + @staticmethod + def open_plugins_dir(): + d = USER_PLUGIN_DIR + if 'darwin' in sys.platform: + os.system('open {}'.format(d)) + elif 'win' in sys.platform: + os.startfile(d) + else: + try: + os.system('xdg-open {}'.format(d)) + except: + logger.exception('Failed to open user config dir') + def about_message(self): text = ('nxt {} \n' 'graph v{}\n' @@ -1390,6 +1413,8 @@ def run(self): except OSError: logger.warning('Failed to start/connect to rpc server. Please try ' 'starting the rpc server via the UI') + if self.main_window.model: + self.main_window.model.processing.emit(False) return remote_rpc_log_file_path = None if not self.main_window.nxt.rpc_server: diff --git a/nxt_editor/stage_model.py b/nxt_editor/stage_model.py index 6dcb256..f6fe679 100644 --- a/nxt_editor/stage_model.py +++ b/nxt_editor/stage_model.py @@ -2709,13 +2709,12 @@ def _execute_node(self, node_path): # can know we're in a thread safe environment. t._run() self.process_events() - return else: self.processing.emit(True) t.start() - while not t.isFinished(): - self.process_events() - self.processing.emit(False) + while not t.isFinished(): + self.process_events() + self.processing.emit(False) if t.raised_exception: raise t.raised_exception diff --git a/nxt_editor/version.json b/nxt_editor/version.json index 2bbf50b..5af8f69 100644 --- a/nxt_editor/version.json +++ b/nxt_editor/version.json @@ -2,6 +2,6 @@ "EDITOR": { "MAJOR": 3, "MINOR": 3, - "PATCH": 3 + "PATCH": 6 } }