Compare commits
17 Commits
version-1.
...
version-1.
Author | SHA1 | Date | |
---|---|---|---|
e777d67922 | |||
7edeff5ee1 | |||
63b976cb44 | |||
73a62da8da | |||
2c70ceb489 | |||
38ccb54b50 | |||
1df113ca01 | |||
887a9cc697 | |||
143456ae1d | |||
f41ea8c5a3 | |||
7d90a92e24 | |||
2388f800dc | |||
38a3bcba71 | |||
2cf400a74c | |||
54ebb0bf5d | |||
9e84d2a416 | |||
772e6b0b1b |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,10 +1,10 @@
|
|||||||
*.pyc
|
*.pyc
|
||||||
*.swp
|
*.swp
|
||||||
*.blend
|
/*.blend*
|
||||||
*.blend[1-9]
|
|
||||||
blender_cloud/wheels/*.whl
|
blender_cloud/wheels/*.whl
|
||||||
/textures*/
|
/textures*/
|
||||||
/test_*.py
|
/test_*.py
|
||||||
/dist/
|
/dist/
|
||||||
/build/
|
/build/
|
||||||
/addon-bundle/*.zip
|
/addon-bundle/*.zip
|
||||||
|
__pycache__
|
||||||
|
@@ -22,6 +22,9 @@ cd $(dirname $(readlink -f $0))
|
|||||||
BCLOUD=$(ls ../dist/blender_cloud-*.addon.zip | tail -n 1)
|
BCLOUD=$(ls ../dist/blender_cloud-*.addon.zip | tail -n 1)
|
||||||
BID=$(ls ../../../blender-id-addon/dist/blender_id-*.addon.zip | tail -n 1)
|
BID=$(ls ../../../blender-id-addon/dist/blender_id-*.addon.zip | tail -n 1)
|
||||||
|
|
||||||
|
[ -z "$BCLOUD" ] && echo "BCloud addon not found" >&2
|
||||||
|
[ -z "$BID" ] && echo "B'ID addon not found" >&2
|
||||||
|
|
||||||
cp -va $BCLOUD $BID .
|
cp -va $BCLOUD $BID .
|
||||||
|
|
||||||
BUNDLE=$(basename $BCLOUD)
|
BUNDLE=$(basename $BCLOUD)
|
||||||
|
@@ -21,7 +21,7 @@
|
|||||||
bl_info = {
|
bl_info = {
|
||||||
'name': 'Blender Cloud',
|
'name': 'Blender Cloud',
|
||||||
'author': 'Sybren A. Stüvel and Francesco Siddi',
|
'author': 'Sybren A. Stüvel and Francesco Siddi',
|
||||||
'version': (1, 4, 1),
|
'version': (1, 4, 4),
|
||||||
'blender': (2, 77, 0),
|
'blender': (2, 77, 0),
|
||||||
'location': 'Addon Preferences panel, and Ctrl+Shift+Alt+A anywhere for texture browser',
|
'location': 'Addon Preferences panel, and Ctrl+Shift+Alt+A anywhere for texture browser',
|
||||||
'description': 'Texture library browser and Blender Sync. Requires the Blender ID addon '
|
'description': 'Texture library browser and Blender Sync. Requires the Blender ID addon '
|
||||||
@@ -29,7 +29,6 @@ bl_info = {
|
|||||||
'wiki_url': 'http://wiki.blender.org/index.php/Extensions:2.6/Py/'
|
'wiki_url': 'http://wiki.blender.org/index.php/Extensions:2.6/Py/'
|
||||||
'Scripts/System/BlenderCloud',
|
'Scripts/System/BlenderCloud',
|
||||||
'category': 'System',
|
'category': 'System',
|
||||||
'support': 'OFFICIAL'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
@@ -178,12 +178,28 @@ class AsyncModalOperatorMixin:
|
|||||||
log = logging.getLogger('%s.AsyncModalOperatorMixin' % __name__)
|
log = logging.getLogger('%s.AsyncModalOperatorMixin' % __name__)
|
||||||
|
|
||||||
_state = 'INITIALIZING'
|
_state = 'INITIALIZING'
|
||||||
|
stop_upon_exception = False
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
context.window_manager.modal_handler_add(self)
|
context.window_manager.modal_handler_add(self)
|
||||||
self.timer = context.window_manager.event_timer_add(1 / 15, context.window)
|
self.timer = context.window_manager.event_timer_add(1 / 15, context.window)
|
||||||
|
|
||||||
|
self.log.info('Starting')
|
||||||
|
self._new_async_task(self.async_execute(context))
|
||||||
|
|
||||||
return {'RUNNING_MODAL'}
|
return {'RUNNING_MODAL'}
|
||||||
|
|
||||||
|
async def async_execute(self, context):
|
||||||
|
"""Entry point of the asynchronous operator.
|
||||||
|
|
||||||
|
Implement in a subclass.
|
||||||
|
"""
|
||||||
|
return
|
||||||
|
|
||||||
|
def quit(self):
|
||||||
|
"""Signals the state machine to stop this operator from running."""
|
||||||
|
self._state = 'QUIT'
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
return self.invoke(context, None)
|
return self.invoke(context, None)
|
||||||
|
|
||||||
@@ -195,6 +211,11 @@ class AsyncModalOperatorMixin:
|
|||||||
if ex is not None:
|
if ex is not None:
|
||||||
self._state = 'EXCEPTION'
|
self._state = 'EXCEPTION'
|
||||||
self.log.error('Exception while running task: %s', ex)
|
self.log.error('Exception while running task: %s', ex)
|
||||||
|
if self.stop_upon_exception:
|
||||||
|
self.quit()
|
||||||
|
self._finish(context)
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
return {'RUNNING_MODAL'}
|
return {'RUNNING_MODAL'}
|
||||||
|
|
||||||
if self._state == 'QUIT':
|
if self._state == 'QUIT':
|
||||||
|
@@ -31,8 +31,8 @@ import rna_prop_ui
|
|||||||
|
|
||||||
from . import pillar
|
from . import pillar
|
||||||
|
|
||||||
PILLAR_SERVER_URL = 'https://cloudapi.blender.org/'
|
PILLAR_SERVER_URL = 'https://cloud.blender.org/api/'
|
||||||
# PILLAR_SERVER_URL = 'http://localhost:5000/'
|
# PILLAR_SERVER_URL = 'http://pillar:5001/api/'
|
||||||
|
|
||||||
ADDON_NAME = 'blender_cloud'
|
ADDON_NAME = 'blender_cloud'
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
@@ -107,11 +107,7 @@ class PILLAR_OT_image_share(pillar.PillarOperatorMixin,
|
|||||||
self.report({'ERROR'}, 'Datablock is dirty, save it first.')
|
self.report({'ERROR'}, 'Datablock is dirty, save it first.')
|
||||||
return {'CANCELLED'}
|
return {'CANCELLED'}
|
||||||
|
|
||||||
async_loop.AsyncModalOperatorMixin.invoke(self, context, event)
|
return async_loop.AsyncModalOperatorMixin.invoke(self, context, event)
|
||||||
|
|
||||||
self.log.info('Starting sharing')
|
|
||||||
self._new_async_task(self.async_execute(context))
|
|
||||||
return {'RUNNING_MODAL'}
|
|
||||||
|
|
||||||
async def async_execute(self, context):
|
async def async_execute(self, context):
|
||||||
"""Entry point of the asynchronous operator."""
|
"""Entry point of the asynchronous operator."""
|
||||||
@@ -121,15 +117,15 @@ class PILLAR_OT_image_share(pillar.PillarOperatorMixin,
|
|||||||
try:
|
try:
|
||||||
# Refresh credentials
|
# Refresh credentials
|
||||||
try:
|
try:
|
||||||
self.user_id = await self.check_credentials(context,
|
db_user = await self.check_credentials(context, REQUIRES_ROLES_FOR_IMAGE_SHARING)
|
||||||
REQUIRES_ROLES_FOR_IMAGE_SHARING)
|
self.user_id = db_user['_id']
|
||||||
self.log.debug('Found user ID: %s', self.user_id)
|
self.log.debug('Found user ID: %s', self.user_id)
|
||||||
except pillar.NotSubscribedToCloudError:
|
except pillar.NotSubscribedToCloudError:
|
||||||
self.log.exception('User not subscribed to cloud.')
|
self.log.exception('User not subscribed to cloud.')
|
||||||
self.report({'ERROR'}, 'Please subscribe to the Blender Cloud.')
|
self.report({'ERROR'}, 'Please subscribe to the Blender Cloud.')
|
||||||
self._state = 'QUIT'
|
self._state = 'QUIT'
|
||||||
return
|
return
|
||||||
except pillar.CredentialsNotSyncedError:
|
except pillar.UserNotLoggedInError:
|
||||||
self.log.exception('Error checking/refreshing credentials.')
|
self.log.exception('Error checking/refreshing credentials.')
|
||||||
self.report({'ERROR'}, 'Please log in on Blender ID first.')
|
self.report({'ERROR'}, 'Please log in on Blender ID first.')
|
||||||
self._state = 'QUIT'
|
self._state = 'QUIT'
|
||||||
@@ -287,7 +283,7 @@ class PILLAR_OT_image_share(pillar.PillarOperatorMixin,
|
|||||||
async def upload_screenshot(self, context) -> pillarsdk.Node:
|
async def upload_screenshot(self, context) -> pillarsdk.Node:
|
||||||
"""Takes a screenshot, saves it to a temp file, and uploads it."""
|
"""Takes a screenshot, saves it to a temp file, and uploads it."""
|
||||||
|
|
||||||
self.name = datetime.datetime.now().strftime('Screenshot-%Y-%m-%d-%H:%M:%S.png')
|
self.name = datetime.datetime.now().strftime('Screenshot-%Y-%m-%d-%H%M%S.png')
|
||||||
self.report({'INFO'}, "Uploading %s '%s'" % (self.target.lower(), self.name))
|
self.report({'INFO'}, "Uploading %s '%s'" % (self.target.lower(), self.name))
|
||||||
|
|
||||||
with tempfile.TemporaryDirectory() as tmpdir:
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
|
@@ -241,7 +241,7 @@ async def check_pillar_credentials(required_roles: set):
|
|||||||
profile.save_json()
|
profile.save_json()
|
||||||
raise NotSubscribedToCloudError()
|
raise NotSubscribedToCloudError()
|
||||||
|
|
||||||
return pillar_user_id
|
return db_user
|
||||||
|
|
||||||
|
|
||||||
async def refresh_pillar_credentials(required_roles: set):
|
async def refresh_pillar_credentials(required_roles: set):
|
||||||
@@ -256,7 +256,7 @@ async def refresh_pillar_credentials(required_roles: set):
|
|||||||
import blender_id
|
import blender_id
|
||||||
|
|
||||||
from . import blender
|
from . import blender
|
||||||
pillar_endpoint = blender.preferences().pillar_server.rstrip('/')
|
pillar_endpoint = blender.preferences().pillar_server
|
||||||
|
|
||||||
# Create a subclient token and send it to Pillar.
|
# Create a subclient token and send it to Pillar.
|
||||||
# May raise a blender_id.BlenderIdCommError
|
# May raise a blender_id.BlenderIdCommError
|
||||||
@@ -780,15 +780,16 @@ def is_cancelled(future: asyncio.Future) -> bool:
|
|||||||
|
|
||||||
class PillarOperatorMixin:
|
class PillarOperatorMixin:
|
||||||
async def check_credentials(self, context, required_roles) -> bool:
|
async def check_credentials(self, context, required_roles) -> bool:
|
||||||
"""Checks credentials with Pillar, and if ok returns the user ID.
|
"""Checks credentials with Pillar, and if ok returns the user document from Pillar/MongoDB.
|
||||||
|
|
||||||
Returns None if the user cannot be found, or if the user is not a Cloud subscriber.
|
:raises UserNotLoggedInError: if the user is not logged in
|
||||||
|
:raises NotSubscribedToCloudError: if the user does not have any of the required roles
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# self.report({'INFO'}, 'Checking Blender Cloud credentials')
|
# self.report({'INFO'}, 'Checking Blender Cloud credentials')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
user_id = await check_pillar_credentials(required_roles)
|
db_user = await check_pillar_credentials(required_roles)
|
||||||
except NotSubscribedToCloudError:
|
except NotSubscribedToCloudError:
|
||||||
self._log_subscription_needed()
|
self._log_subscription_needed()
|
||||||
raise
|
raise
|
||||||
@@ -796,20 +797,22 @@ class PillarOperatorMixin:
|
|||||||
self.log.info('Credentials not synced, re-syncing automatically.')
|
self.log.info('Credentials not synced, re-syncing automatically.')
|
||||||
else:
|
else:
|
||||||
self.log.info('Credentials okay.')
|
self.log.info('Credentials okay.')
|
||||||
return user_id
|
return db_user
|
||||||
|
|
||||||
try:
|
try:
|
||||||
user_id = await refresh_pillar_credentials(required_roles)
|
db_user = await refresh_pillar_credentials(required_roles)
|
||||||
except NotSubscribedToCloudError:
|
except NotSubscribedToCloudError:
|
||||||
self._log_subscription_needed()
|
self._log_subscription_needed()
|
||||||
raise
|
raise
|
||||||
|
except CredentialsNotSyncedError:
|
||||||
|
self.log.info('Credentials not synced after refreshing, handling as not logged in.')
|
||||||
|
raise UserNotLoggedInError('Not logged in.')
|
||||||
except UserNotLoggedInError:
|
except UserNotLoggedInError:
|
||||||
self.log.error('User not logged in on Blender ID.')
|
self.log.error('User not logged in on Blender ID.')
|
||||||
|
raise
|
||||||
else:
|
else:
|
||||||
self.log.info('Credentials refreshed and ok.')
|
self.log.info('Credentials refreshed and ok.')
|
||||||
return user_id
|
return db_user
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _log_subscription_needed(self):
|
def _log_subscription_needed(self):
|
||||||
self.log.warning(
|
self.log.warning(
|
||||||
|
@@ -234,11 +234,7 @@ class PILLAR_OT_sync(pillar.PillarOperatorMixin,
|
|||||||
self.bss_report({'ERROR'}, 'No Blender version to sync for was given.')
|
self.bss_report({'ERROR'}, 'No Blender version to sync for was given.')
|
||||||
return {'CANCELLED'}
|
return {'CANCELLED'}
|
||||||
|
|
||||||
async_loop.AsyncModalOperatorMixin.invoke(self, context, event)
|
return async_loop.AsyncModalOperatorMixin.invoke(self, context, event)
|
||||||
|
|
||||||
self.log.info('Starting synchronisation')
|
|
||||||
self._new_async_task(self.async_execute(context))
|
|
||||||
return {'RUNNING_MODAL'}
|
|
||||||
|
|
||||||
def action_select(self, context):
|
def action_select(self, context):
|
||||||
"""Allows selection of the Blender version to use.
|
"""Allows selection of the Blender version to use.
|
||||||
@@ -285,14 +281,15 @@ class PILLAR_OT_sync(pillar.PillarOperatorMixin,
|
|||||||
try:
|
try:
|
||||||
# Refresh credentials
|
# Refresh credentials
|
||||||
try:
|
try:
|
||||||
self.user_id = await self.check_credentials(context, REQUIRES_ROLES_FOR_SYNC)
|
db_user = await self.check_credentials(context, REQUIRES_ROLES_FOR_SYNC)
|
||||||
|
self.user_id = db_user['_id']
|
||||||
log.debug('Found user ID: %s', self.user_id)
|
log.debug('Found user ID: %s', self.user_id)
|
||||||
except pillar.NotSubscribedToCloudError:
|
except pillar.NotSubscribedToCloudError:
|
||||||
self.log.exception('User not subscribed to cloud.')
|
self.log.exception('User not subscribed to cloud.')
|
||||||
self.bss_report({'SUBSCRIBE'}, 'Please subscribe to the Blender Cloud.')
|
self.bss_report({'SUBSCRIBE'}, 'Please subscribe to the Blender Cloud.')
|
||||||
self._state = 'QUIT'
|
self._state = 'QUIT'
|
||||||
return
|
return
|
||||||
except pillar.CredentialsNotSyncedError:
|
except pillar.UserNotLoggedInError:
|
||||||
self.log.exception('Error checking/refreshing credentials.')
|
self.log.exception('Error checking/refreshing credentials.')
|
||||||
self.bss_report({'ERROR'}, 'Please log in on Blender ID first.')
|
self.bss_report({'ERROR'}, 'Please log in on Blender ID first.')
|
||||||
self._state = 'QUIT'
|
self._state = 'QUIT'
|
||||||
|
@@ -258,8 +258,9 @@ class BlenderCloudBrowser(pillar.PillarOperatorMixin,
|
|||||||
scroll_offset_space_left = 0
|
scroll_offset_space_left = 0
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
# Refuse to start if the file hasn't been saved.
|
# Refuse to start if the file hasn't been saved. It's okay if
|
||||||
if context.blend_data.is_dirty:
|
# it's dirty, we just need to know where '//' points to.
|
||||||
|
if not os.path.exists(context.blend_data.filepath):
|
||||||
self.report({'ERROR'}, 'Please save your Blend file before using '
|
self.report({'ERROR'}, 'Please save your Blend file before using '
|
||||||
'the Blender Cloud addon.')
|
'the Blender Cloud addon.')
|
||||||
return {'CANCELLED'}
|
return {'CANCELLED'}
|
||||||
@@ -288,10 +289,7 @@ class BlenderCloudBrowser(pillar.PillarOperatorMixin,
|
|||||||
self._scroll_reset()
|
self._scroll_reset()
|
||||||
|
|
||||||
context.window.cursor_modal_set('DEFAULT')
|
context.window.cursor_modal_set('DEFAULT')
|
||||||
async_loop.AsyncModalOperatorMixin.invoke(self, context, event)
|
return async_loop.AsyncModalOperatorMixin.invoke(self, context, event)
|
||||||
self._new_async_task(self.async_execute(context))
|
|
||||||
|
|
||||||
return {'RUNNING_MODAL'}
|
|
||||||
|
|
||||||
def modal(self, context, event):
|
def modal(self, context, event):
|
||||||
result = async_loop.AsyncModalOperatorMixin.modal(self, context, event)
|
result = async_loop.AsyncModalOperatorMixin.modal(self, context, event)
|
||||||
@@ -366,13 +364,13 @@ class BlenderCloudBrowser(pillar.PillarOperatorMixin,
|
|||||||
self.log.debug('Checking credentials')
|
self.log.debug('Checking credentials')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
user_id = await self.check_credentials(context, REQUIRED_ROLES_FOR_TEXTURE_BROWSER)
|
db_user = await self.check_credentials(context, REQUIRED_ROLES_FOR_TEXTURE_BROWSER)
|
||||||
except pillar.NotSubscribedToCloudError:
|
except pillar.NotSubscribedToCloudError:
|
||||||
self.log.info('User not subscribed to Blender Cloud.')
|
self.log.info('User not subscribed to Blender Cloud.')
|
||||||
self._show_subscribe_screen()
|
self._show_subscribe_screen()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if user_id is None:
|
if db_user is None:
|
||||||
raise pillar.UserNotLoggedInError()
|
raise pillar.UserNotLoggedInError()
|
||||||
|
|
||||||
await self.async_download_previews()
|
await self.async_download_previews()
|
||||||
@@ -850,13 +848,6 @@ class PILLAR_OT_switch_hdri(pillar.PillarOperatorMixin,
|
|||||||
file_uuid = bpy.props.StringProperty(name='file_uuid',
|
file_uuid = bpy.props.StringProperty(name='file_uuid',
|
||||||
description='File ID to download')
|
description='File ID to download')
|
||||||
|
|
||||||
def invoke(self, context, event):
|
|
||||||
async_loop.AsyncModalOperatorMixin.invoke(self, context, event)
|
|
||||||
|
|
||||||
self.log.info('Starting')
|
|
||||||
self._new_async_task(self.async_execute(context))
|
|
||||||
return {'RUNNING_MODAL'}
|
|
||||||
|
|
||||||
async def async_execute(self, context):
|
async def async_execute(self, context):
|
||||||
"""Entry point of the asynchronous operator."""
|
"""Entry point of the asynchronous operator."""
|
||||||
|
|
||||||
@@ -864,19 +855,20 @@ class PILLAR_OT_switch_hdri(pillar.PillarOperatorMixin,
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
user_id = await self.check_credentials(context, REQUIRED_ROLES_FOR_TEXTURE_BROWSER)
|
db_user = await self.check_credentials(context, REQUIRED_ROLES_FOR_TEXTURE_BROWSER)
|
||||||
|
user_id = db_user['_id']
|
||||||
except pillar.NotSubscribedToCloudError:
|
except pillar.NotSubscribedToCloudError:
|
||||||
self.log.exception('User not subscribed to cloud.')
|
self.log.exception('User not subscribed to cloud.')
|
||||||
self.report({'ERROR'}, 'Please subscribe to the Blender Cloud.')
|
self.report({'ERROR'}, 'Please subscribe to the Blender Cloud.')
|
||||||
self._state = 'QUIT'
|
self._state = 'QUIT'
|
||||||
return
|
return
|
||||||
except pillar.CredentialsNotSyncedError:
|
except pillar.UserNotLoggedInError:
|
||||||
self.log.exception('Error checking/refreshing credentials.')
|
self.log.exception('Error checking/refreshing credentials.')
|
||||||
self.report({'ERROR'}, 'Please log in on Blender ID first.')
|
self.report({'ERROR'}, 'Please log in on Blender ID first.')
|
||||||
self._state = 'QUIT'
|
self._state = 'QUIT'
|
||||||
return
|
return
|
||||||
|
|
||||||
if user_id is None:
|
if not user_id:
|
||||||
raise pillar.UserNotLoggedInError()
|
raise pillar.UserNotLoggedInError()
|
||||||
|
|
||||||
await self.download_and_replace(context)
|
await self.download_and_replace(context)
|
||||||
@@ -977,6 +969,11 @@ def _hdri_download_panel(self, current_image):
|
|||||||
props.file_uuid = current_image.hdri_variation
|
props.file_uuid = current_image.hdri_variation
|
||||||
|
|
||||||
|
|
||||||
|
# Storage for variation labels, as the strings in EnumProperty items
|
||||||
|
# MUST be kept in Python memory.
|
||||||
|
variation_label_storage = {}
|
||||||
|
|
||||||
|
|
||||||
def hdri_variation_choices(self, context):
|
def hdri_variation_choices(self, context):
|
||||||
if context.area.type == 'IMAGE_EDITOR':
|
if context.area.type == 'IMAGE_EDITOR':
|
||||||
image = context.edit_image
|
image = context.edit_image
|
||||||
@@ -988,8 +985,11 @@ def hdri_variation_choices(self, context):
|
|||||||
if 'bcloud_node' not in image:
|
if 'bcloud_node' not in image:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
choices = [(file_doc['file'], file_doc['resolution'], '')
|
choices = []
|
||||||
for file_doc in image['bcloud_node']['properties']['files']]
|
for file_doc in image['bcloud_node']['properties']['files']:
|
||||||
|
label = file_doc['resolution']
|
||||||
|
variation_label_storage[label] = label
|
||||||
|
choices.append((file_doc['file'], label, ''))
|
||||||
|
|
||||||
return choices
|
return choices
|
||||||
|
|
||||||
|
2
setup.py
2
setup.py
@@ -196,7 +196,7 @@ setup(
|
|||||||
'wheels': BuildWheels},
|
'wheels': BuildWheels},
|
||||||
name='blender_cloud',
|
name='blender_cloud',
|
||||||
description='The Blender Cloud addon allows browsing the Blender Cloud from Blender.',
|
description='The Blender Cloud addon allows browsing the Blender Cloud from Blender.',
|
||||||
version='1.4.1',
|
version='1.4.4',
|
||||||
author='Sybren A. Stüvel',
|
author='Sybren A. Stüvel',
|
||||||
author_email='sybren@stuvel.eu',
|
author_email='sybren@stuvel.eu',
|
||||||
packages=find_packages('.'),
|
packages=find_packages('.'),
|
||||||
|
15
update_version.sh
Executable file
15
update_version.sh
Executable file
@@ -0,0 +1,15 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if [ -z "$1" ]; then
|
||||||
|
echo "Usage: $0 new-version" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
BL_INFO_VER=$(echo "$1" | sed 's/\./, /g')
|
||||||
|
|
||||||
|
sed "s/version='[^']*'/version='$1'/" -i setup.py
|
||||||
|
sed "s/'version': ([^)]*)/'version': ($BL_INFO_VER)/" -i blender_cloud/__init__.py
|
||||||
|
|
||||||
|
git diff
|
||||||
|
echo
|
||||||
|
echo "Don't forget to commit!"
|
Reference in New Issue
Block a user