├── test-requirements.txt ├── .gitattributes ├── pytest.ini ├── images ├── enable-addon.png ├── install-button.png ├── open-item-panel.png ├── selector-usage.png ├── menu-search-usage.png └── simple-transform-panel.png ├── .editorconfig ├── .gitignore ├── package.json ├── tests ├── conftest.py ├── test_addon_registered.py └── test_add_icons.py ├── helpers.py ├── LICENSE.md ├── .github └── workflows │ ├── verify.yml │ └── publish.yml ├── README.md └── simple_icons_blender.template /test-requirements.txt: -------------------------------------------------------------------------------- 1 | pytest 2 | pytest-blender 3 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | simple_icons_blender.py -diff 3 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | addopts = -sx 3 | blender-addons-dirs = . 4 | -------------------------------------------------------------------------------- /images/enable-addon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mondeja/simple-icons-blender/HEAD/images/enable-addon.png -------------------------------------------------------------------------------- /images/install-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mondeja/simple-icons-blender/HEAD/images/install-button.png -------------------------------------------------------------------------------- /images/open-item-panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mondeja/simple-icons-blender/HEAD/images/open-item-panel.png -------------------------------------------------------------------------------- /images/selector-usage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mondeja/simple-icons-blender/HEAD/images/selector-usage.png -------------------------------------------------------------------------------- /images/menu-search-usage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mondeja/simple-icons-blender/HEAD/images/menu-search-usage.png -------------------------------------------------------------------------------- /images/simple-transform-panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mondeja/simple-icons-blender/HEAD/images/simple-transform-panel.png -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | charset = utf-8 7 | indent_style = space 8 | trim_trailing_whitespace = true 9 | 10 | [*.md] 11 | trim_trailing_whitespace = false 12 | 13 | [*.template] 14 | indent_size = 4 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | venv/ 2 | node_modules/ 3 | *.backup 4 | *.zip 5 | .pytest_cache/ 6 | htmlcov/ 7 | package-lock.json 8 | *.blend1 9 | *.mp4 10 | *.gif 11 | failed-slugs.txt 12 | scripts/showcase/frames/ 13 | scripts/showcase/inkscape-pngs/ 14 | build-failed-slugs-list.js 15 | __pycache__/ 16 | *.tar.xz 17 | blender-*/ 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simple-icons-blender", 3 | "version": "16.2.0", 4 | "description": "Blender Simple Icons addon", 5 | "scripts": { 6 | "build": "node build.js" 7 | }, 8 | "keywords": [], 9 | "author": "", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "simple-icons": "16.2.0" 13 | }, 14 | "type": "module" 15 | } 16 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | import pytest 5 | 6 | ROOT_DIR = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) 7 | if ROOT_DIR not in sys.path: 8 | sys.path.append(ROOT_DIR) 9 | 10 | from helpers import simple_icons_slugs as _simple_icons_slugs 11 | 12 | @pytest.fixture 13 | def simple_icons_slugs(): 14 | return _simple_icons_slugs() 15 | -------------------------------------------------------------------------------- /tests/test_addon_registered.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | def test_addon_registered(simple_icons_slugs): 4 | import _bpy 5 | import addon_utils 6 | 7 | # check addon registered 8 | installed_addons = [addon.__name__ for addon in addon_utils.modules()] 9 | assert "simple_icons_blender" in installed_addons 10 | 11 | # check addon operators registered 12 | current_operators = [cls.__name__ for cls in _bpy.types.Operator.__subclasses__()] 13 | for expected_si_operator in [f"AddSi_{slug}" for slug in simple_icons_slugs]: 14 | assert expected_si_operator in current_operators 15 | -------------------------------------------------------------------------------- /helpers.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | 4 | def simple_icons_slugs(): 5 | with open("simple_icons_blender.py") as f: 6 | slugs = re.findall( 7 | r'^class AddSi_(.+)\(A,O\):', 8 | f.read(), 9 | re.M, 10 | ) 11 | return slugs 12 | 13 | 14 | def simple_icons_titles(): 15 | with open("simple_icons_blender.py") as f: 16 | titles = re.findall(r'bl_label=(.+);bl_description=', f.read(), re.M) 17 | return [title.strip("'").replace("\\'", "'") for title in titles[:-1]] 18 | 19 | 20 | def simple_icons_svgs(): 21 | with open("simple_icons_blender.py") as f: 22 | svgs = re.findall(r';si_svg=(.+)$', f.read(), re.M) 23 | return [svg.strip("'").replace("\\'", "'") for svg in svgs] 24 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2022, Álvaro Mondéjar Rubio 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /.github/workflows/verify.yml: -------------------------------------------------------------------------------- 1 | name: Verify 2 | 3 | on: 4 | push: 5 | branches: 6 | - develop 7 | pull_request: 8 | workflow_dispatch: 9 | 10 | jobs: 11 | test: 12 | name: Test 13 | runs-on: ${{ matrix.platform }} 14 | timeout-minutes: 30 15 | strategy: 16 | fail-fast: false 17 | matrix: 18 | platform: 19 | - ubuntu-latest 20 | - macos-latest 21 | blender-version: 22 | - "2.93.9" 23 | - "3.1.2" 24 | 25 | steps: 26 | - uses: actions/checkout@v4 27 | - name: Set up Python 28 | uses: actions/setup-python@v5 29 | with: 30 | python-version: 3.x 31 | - name: Cache Blender ${{ matrix.blender-version }} 32 | uses: actions/cache@v4 33 | id: cache-blender 34 | with: 35 | path: | 36 | blender-* 37 | _blender-executable-path.txt 38 | key: ${{ runner.os }}-${{ matrix.blender-version }} 39 | - name: Download Blender ${{ matrix.blender-version }} 40 | if: steps.cache-blender.outputs.cache-hit != 'true' 41 | id: download-blender 42 | run: | 43 | python -m pip install -U pip blender-downloader 44 | echo "$(blender-downloader \ 45 | ${{ matrix.blender-version }} --extract --remove-compressed \ 46 | --print-blender-executable --quiet)" > _blender-executable-path.txt 47 | - name: Install dependencies 48 | id: install-dependencies 49 | run: | 50 | python -m pip install -r test-requirements.txt 51 | blender_executable="$(< _blender-executable-path.txt tr -d '\n')" 52 | python_blender_executable="$(pytest-blender --blender-executable $blender_executable)" 53 | $python_blender_executable -m ensurepip 54 | $python_blender_executable -m pip install pytest 55 | echo "blender-executable=$blender_executable" >> $GITHUB_OUTPUT 56 | - name: Run tests 57 | run: pytest -s --blender-executable "${{ steps.install-dependencies.outputs.blender-executable }}" tests 58 | -------------------------------------------------------------------------------- /tests/test_add_icons.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | 4 | import pytest 5 | 6 | from helpers import simple_icons_slugs, simple_icons_titles 7 | 8 | 9 | @pytest.mark.parametrize( 10 | ('slug', 'title'), 11 | zip(simple_icons_slugs(), simple_icons_titles()) 12 | ) 13 | def test_add_icon_operators(slug, title): 14 | import bpy 15 | 16 | # add icon 17 | op_executor = getattr(bpy.ops.mesh, f"si_{slug}") 18 | assert op_executor() == {"FINISHED"} 19 | 20 | # get added icon (created inside a new collection) 21 | last_collection = bpy.context.scene.collection.children[-1] 22 | assert last_collection.name == title.replace(os.sep, "-") 23 | last_object = last_collection.all_objects[-1] 24 | 25 | # check that icon is properly added 26 | assert bpy.context.active_object == last_object 27 | assert last_object in bpy.context.selected_objects 28 | 29 | assert last_object.type == "CURVE" 30 | assert tuple(last_object.location) == (0.0, 0.0, 0.0) 31 | assert re.match(r'SVGMat\.\d+', last_object.active_material.name) 32 | 33 | if slug == "simpleicons": 34 | # test simpleicons icon geometry 35 | bezier_curves_expected_n_points = [13, 5, 5, 12, 4, 4] 36 | assert len(last_object.data.splines) == len(bezier_curves_expected_n_points) 37 | 38 | for i, spline in enumerate(last_object.data.splines): 39 | assert spline.type == "BEZIER" 40 | 41 | n_points = len(spline.bezier_points) 42 | n_expected_points = bezier_curves_expected_n_points[i] 43 | assert n_points == n_expected_points, ( 44 | f"Expected {n_expected_points} points at index {i}, got {n_points}" 45 | ) 46 | 47 | for obj in last_collection.objects: 48 | bpy.data.objects.remove(obj, do_unlink=True) 49 | bpy.data.collections.remove(last_collection) 50 | 51 | # check that was removed 52 | last_collection = bpy.context.scene.collection.children[-1] 53 | assert last_collection.name != title.replace(os.sep, "-") 54 | last_object = last_collection.all_objects[-1] 55 | 56 | # check that icon is properly added 57 | assert last_object.type != "CURVE" 58 | assert last_object.type == "CAMERA" 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # simple-icons-blender 2 | 3 | [Simple Icons] Blender addon. Add 2D SVG brand icons to Blender easily. 4 | 5 | Requires Blender v2.93.0 or greater. 6 | 7 | [![Verify Workflow][tests-badge]][tests-link] 8 | 9 | ## Download 10 | 11 | Download the addon using [the next link](https://github.com/mondeja/simple-icons-blender/releases/download/16.2.0/simple_icons_blender.py): 12 | 13 | ```txt 14 | https://github.com/mondeja/simple-icons-blender/releases/download/16.2.0/simple_icons_blender.py 15 | ``` 16 | 17 | If you want to install [another version of Simple Icons][si-releases], change the version 18 | number in the link. 19 | 20 | ## Install 21 | 22 | Under `Edit` -> `Preferences` -> `Add-ons`, press on `Install` and select the 23 | downloaded file: 24 | 25 |

26 | 27 |

28 | 29 | Search for `Simple Icons` in the search bar and enable the addon marking the 30 | top-left checkbox: 31 | 32 |

33 | 34 |

35 | 36 | ## Usage 37 | 38 | You can load icons from the `3D Viewport` in object mode clicking on `Add` -> 39 | `Curve` -> `Simple Icons`: 40 | 41 |

42 | 43 |

44 | 45 | All icons are operators, so you can directly load one through the `Menu Search` 46 | (`Edit` -> `Menu Search`): 47 | 48 |

49 | 50 |

51 | 52 | After loading the icon, you can use the built-in panel "Simple transform" 53 | to define the size, location and rotation easily. 54 | 55 |

56 | 57 |

58 | 59 | Is placed inside the "Item" tab at the top right of the 3D viewport: 60 | 61 |

62 | 63 |

64 | 65 | This panel will only be shown when you select a *Curve* object as the active one. 66 | 67 | [Simple Icons]: https://simpleicons.org 68 | [si-releases]: https://github.com/simple-icons/simple-icons/releases 69 | [tests-link]: https://github.com/mondeja/simple-icons-blender/actions/workflows/verify.yml 70 | [tests-badge]: https://img.shields.io/github/actions/workflow/status/mondeja/simple-icons-blender/verify.yml?branch=develop&label=tests 71 | -------------------------------------------------------------------------------- /simple_icons_blender.template: -------------------------------------------------------------------------------- 1 | import math 2 | import os 3 | import string 4 | import tempfile 5 | 6 | import bpy 7 | 8 | from io_curve_svg.import_svg import load as import_svg_file 9 | 10 | bl_info = { 11 | "name": "Simple Icons", 12 | "description": "Blender Simple Icons add-on", 13 | "author": "mondeja", 14 | "license": "BSD-3-Clause", 15 | "category": "Add Curve", 16 | "version": (16, 2, 0), 17 | "blender": (2, 93, 0), 18 | "support": "COMMUNITY", 19 | } 20 | 21 | class BlenderVersionError(Exception): 22 | pass 23 | 24 | if bl_info["blender"] > bpy.app.version: 25 | raise BlenderVersionError(f"This addon requires Blender >= {bl_info['blender']}") 26 | 27 | # ----------------------------------------------- 28 | 29 | class AddSi: 30 | def execute(self, context): 31 | simple_icons_blender_tempdir = os.path.join( 32 | tempfile.gettempdir(), "simple-icons-blender", 33 | ) 34 | if not os.path.isdir(simple_icons_blender_tempdir): 35 | os.mkdir(simple_icons_blender_tempdir) 36 | 37 | temp_filepath = os.path.join( 38 | simple_icons_blender_tempdir, self.bl_label.replace(os.sep, "-"), 39 | ) 40 | if not os.path.isfile(temp_filepath): 41 | with open(temp_filepath, "w") as f: 42 | f.write(self.si_svg) 43 | 44 | import_svg_file(self, context, temp_filepath) 45 | 46 | context.view_layer.objects.active = context.scene.objects[-1] 47 | context.scene.objects[-1].select_set(True) 48 | return {"FINISHED"} 49 | 50 | A = AddSi 51 | O = bpy.types.Operator 52 | 53 | %(simple_icons_classes)s 54 | 55 | G = globals() 56 | SI_CLASSES = [G[object_name] for object_name in G if object_name.startswith("AddSi_")] 57 | PAGINATION_SUBMENUS = %(letter_submenus_array)s 58 | 59 | # ------------------------------------------------ 60 | # Menu and operators 61 | 62 | class VIEW3D_MT_simple_icons_add(bpy.types.Menu): 63 | # Define the "Simple Icons" menu 64 | bl_idname = "VIEW3D_MT_simple_icons_add" 65 | bl_label = "Simple Icons" 66 | 67 | def draw(self, context): 68 | for submenu in PAGINATION_SUBMENUS: 69 | self.layout.menu("VIEW3D_MT_simple_icons_add_" + submenu + "_submenu") 70 | 71 | 72 | class VIEW3D_MT_simple_icons_add_letter_submenu(bpy.types.Menu): 73 | def draw(self, context): 74 | for cls in SI_CLASSES: 75 | if cls.bl_label[0].upper() == self.bl_label: 76 | self.layout.operator(cls.bl_idname, text=cls.bl_label) 77 | LS = VIEW3D_MT_simple_icons_add_letter_submenu 78 | 79 | class VIEW3D_MT_simple_icons_add_symbol_submenu(bpy.types.Menu): 80 | bl_idname = "VIEW3D_MT_simple_icons_add_symbol_submenu" 81 | bl_label = "#" 82 | 83 | def draw(self, context): 84 | for cls in SI_CLASSES: 85 | if cls.bl_label[0].upper() not in string.ascii_uppercase: 86 | self.layout.operator(cls.bl_idname, text=cls.bl_label) 87 | 88 | %(letter_submenu_classes)s 89 | def menu_func(self, context): 90 | self.layout.separator() 91 | self.layout.menu("VIEW3D_MT_simple_icons_add", 92 | text="Simple Icons", icon="CURVE_DATA") 93 | 94 | 95 | def register(): 96 | for cls in SI_CLASSES: 97 | bpy.utils.register_class(cls) 98 | bpy.utils.register_class(VIEW3D_MT_simple_icons_add) 99 | for submenu in PAGINATION_SUBMENUS: 100 | bpy.utils.register_class(G["VIEW3D_MT_simple_icons_add_" + submenu + "_submenu"]) 101 | bpy.types.VIEW3D_MT_curve_add.append(menu_func) 102 | 103 | register_panel() 104 | 105 | 106 | def unregister(): 107 | bpy.types.VIEW3D_MT_curve_add.remove(menu_func) 108 | 109 | for submenu in PAGINATION_SUBMENUS: 110 | bpy.utils.unregister_class(G["VIEW3D_MT_simple_icons_add_" + submenu + "_submenu"]) 111 | 112 | for cls in SI_CLASSES: 113 | bpy.utils.unregister_class(cls); 114 | bpy.utils.unregister_class(VIEW3D_MT_simple_icons_add) 115 | 116 | unregister_panel() 117 | 118 | 119 | # ------------------------------------------------ 120 | # Add icon panel 121 | 122 | def transform_simple_icon(self, context): 123 | obj = context.active_object 124 | scale = context.scene.simple_icon_scale * 10 125 | rot_x, rot_y, rot_z = context.scene.simple_icon_rotation 126 | 127 | obj.scale = [scale, scale, 1] 128 | obj.location = context.scene.simple_icon_location 129 | obj.rotation_euler = [ 130 | math.radians(rot_x), 131 | math.radians(rot_y), 132 | math.radians(rot_z), 133 | ] 134 | 135 | class AddSimpleTransformPanel(bpy.types.Panel): 136 | """Creates a Panel in the 3D Viewport N Panel""" 137 | bl_label = "Simple transform" 138 | bl_idname = "OBJECT_PT_add_simple_transform_panel" 139 | bl_space_type = "VIEW_3D" 140 | bl_region_type = "UI" 141 | bl_options = set() 142 | bl_category = "Item" 143 | 144 | def draw(self, context): 145 | layout = self.layout 146 | row = layout.row() 147 | row.prop(context.scene, "simple_icon_scale") 148 | row = layout.row() 149 | row.prop(context.scene, "simple_icon_location") 150 | row = layout.row() 151 | row.prop(context.scene, "simple_icon_rotation") 152 | 153 | @classmethod 154 | def poll(cls, context): 155 | return context.active_object.type == "CURVE" 156 | 157 | def register_panel(): 158 | bpy.types.Scene.simple_icon_scale = bpy.props.IntProperty( 159 | name="Scale", 160 | min=0, 161 | default=1, 162 | update=transform_simple_icon, 163 | ) 164 | bpy.types.Scene.simple_icon_location = bpy.props.FloatVectorProperty( 165 | name="Location", 166 | default=(0, 0, 0), 167 | update=transform_simple_icon, 168 | ) 169 | bpy.types.Scene.simple_icon_rotation = bpy.props.FloatVectorProperty( 170 | name="Rotation", 171 | min=-360, 172 | max=360, 173 | default=(0, 0, 0), 174 | update=transform_simple_icon, 175 | ) 176 | bpy.utils.register_class(AddSimpleTransformPanel) 177 | 178 | 179 | def unregister_panel(): 180 | del bpy.types.Scene.simple_icon_scale 181 | del bpy.types.Scene.simple_icon_location 182 | del bpy.types.Scene.simple_icon_rotation 183 | bpy.utils.unregister_class(AddSimpleTransformPanel) 184 | 185 | 186 | 187 | if __name__ == "__main__": 188 | register() 189 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | schedule: 4 | - cron: 0 0 * * 1,2,3,4,5 5 | workflow_dispatch: 6 | 7 | jobs: 8 | release: 9 | name: Release 10 | runs-on: ubuntu-latest 11 | timeout-minutes: 30 12 | steps: 13 | - uses: actions/checkout@v4 14 | with: 15 | # Ensure we are checked out on the develop branch 16 | ref: develop 17 | # Ensure custom credentials are used when pushing 18 | persist-credentials: false 19 | # Fetch everything so we can checkout master 20 | fetch-depth: 0 21 | - name: Use NodeJS v20 22 | uses: actions/setup-node@v4 23 | with: 24 | node-version: 20.x 25 | - name: Set up Python 26 | uses: actions/setup-python@v5 27 | with: 28 | python-version: 3.x 29 | - name: Compare release versions 30 | id: get-releases 31 | run: | 32 | simple_icons_version="$(curl --retry 5 -s https://api.github.com/repos/simple-icons/simple-icons/releases/latest | jq -r .tag_name)" 33 | echo "si=$simple_icons_version" >> $GITHUB_OUTPUT 34 | echo "lib=$(cat package.json | grep '"version":' | cut -d'"' -f4)" >> $GITHUB_OUTPUT 35 | - name: Bump version 36 | if: steps.get-releases.outputs.lib != steps.get-releases.outputs.si 37 | run: | 38 | # Update versions in package.json 39 | sed -i \ 40 | 's/"version": "${{ steps.get-releases.outputs.lib }}",/"version": "${{ steps.get-releases.outputs.si }}",/' \ 41 | package.json 42 | sed -i \ 43 | 's/"simple-icons": "${{ steps.get-releases.outputs.lib }}"/"simple-icons": "${{ steps.get-releases.outputs.si }}"/' \ 44 | package.json 45 | 46 | # Update version in README 47 | sed -i \ 48 | 's/${{ steps.get-releases.outputs.lib }}/${{ steps.get-releases.outputs.si }}/g' \ 49 | README.md 50 | 51 | # Update version in addon 52 | v_major="$(echo ${{ steps.get-releases.outputs.si }} | cut -d'.' -f1)" 53 | v_minor="$(echo ${{ steps.get-releases.outputs.si }} | cut -d'.' -f2)" 54 | v_patch="$(echo ${{ steps.get-releases.outputs.si }} | cut -d'.' -f3)" 55 | sed -i -E \ 56 | "s/(\"|')version(\"|'): [(][0-9]+, [0-9]+, [0-9]+[)],/\1version\2: ($v_major, $v_minor, $v_patch),/" \ 57 | simple_icons_blender.template 58 | - name: Blender cache setup 59 | if: steps.get-releases.outputs.lib != steps.get-releases.outputs.si 60 | uses: actions/cache@v4 61 | id: cache-blender 62 | with: 63 | path: | 64 | blender-* 65 | _blender-executable-path.txt 66 | key: ${{ runner.os }}-2.93.9 67 | - name: Download Blender 2.93.9 68 | if: (steps.cache-blender.outputs.cache-hit != 'true' && steps.get-releases.outputs.lib != steps.get-releases.outputs.si) 69 | id: download-blender 70 | run: | 71 | python -m pip install --upgrade blender-downloader 72 | echo "$(blender-downloader 2.93.9 --extract --remove-compressed \ 73 | --quiet --print-blender-executable)" > _blender-executable-path.txt 74 | - name: Install dependencies 75 | if: steps.get-releases.outputs.lib != steps.get-releases.outputs.si 76 | id: install-dependencies 77 | run: | 78 | npm install 79 | python -m pip install --upgrade pip 80 | python -m pip install -r test-requirements.txt 81 | blender_executable="$(< _blender-executable-path.txt tr -d '\n')" 82 | python_blender_executable="$(pytest-blender --blender-executable $blender_executable)" 83 | $python_blender_executable -m ensurepip 84 | $python_blender_executable -m pip install pytest 85 | echo "blender-executable=$blender_executable" >> $GITHUB_OUTPUT 86 | - name: Build library 87 | if: steps.get-releases.outputs.lib != steps.get-releases.outputs.si 88 | run: npm run build 89 | - name: Test with pytest 90 | if: steps.get-releases.outputs.lib != steps.get-releases.outputs.si 91 | run: pytest -s --blender-executable "${{ steps.install-dependencies.outputs.blender-executable }}" tests 92 | - name: Commit 93 | if: steps.get-releases.outputs.lib != steps.get-releases.outputs.si 94 | run: | 95 | set -e 96 | # Set up git credential 97 | git config --global user.email "github-actions[bot]@users.noreply.github.com" 98 | git config --global user.name "github-actions[bot]" 99 | # Create a version bump commit 100 | git add package.json simple_icons_blender.py simple_icons_blender.template README.md 101 | git commit -m "Version bump" 102 | # Merge develop into master 103 | git checkout master 104 | git merge develop -m "Release ${{ steps.get-releases.outputs.si }}" 105 | # Set up remote using a Personal Access Token 106 | git remote remove origin 107 | git remote add origin https://${{secrets.RELEASE_TOKEN}}@github.com/mondeja/simple-icons-blender.git 108 | # Push develop first, to prevent conflicts with parallel activity 109 | git push origin develop 110 | # Push master only after develop was safely pushed 111 | git push origin master 112 | - name: Create and push git tag 113 | if: steps.get-releases.outputs.lib != steps.get-releases.outputs.si 114 | run: | 115 | set -e 116 | tag="${{ steps.get-releases.outputs.si }}" 117 | git tag -a "${tag}" -m "" 118 | git push origin "${tag}" 119 | - name: Create GitHub release 120 | if: steps.get-releases.outputs.lib != steps.get-releases.outputs.si 121 | id: create-release 122 | uses: softprops/action-gh-release@v2 123 | env: 124 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 125 | with: 126 | name: Release ${{ steps.get-releases.outputs.si }} 127 | tag_name: ${{ steps.get-releases.outputs.si }} 128 | body: | 129 | See https://github.com/simple-icons/simple-icons/releases/tag/${{ steps.get-releases.outputs.si }} 130 | - uses: shogo82148/actions-upload-release-asset@v1 131 | name: Upload release asset 132 | if: steps.get-releases.outputs.lib != steps.get-releases.outputs.si 133 | with: 134 | upload_url: ${{ steps.create-release.outputs.upload_url }} 135 | asset_path: simple_icons_blender.py 136 | --------------------------------------------------------------------------------