/toriicli

CLI utility for Torii.

Primary LanguagePythonMIT LicenseMIT

toriicli

PyPI version fury.io MIT license CI build CD build

CLI utility for Torii, mainly used for automation of building/releasing projects.

Torii is a framework for Unity that fills some gaps in its functionality that I've found over the years.

Installation

$ pip install toriicli

Prerequisites

  • Python 3.7+
  • Unity, installed via Unity Hub
  • NuGet and MSBuild (if you want to use the NuGet subcommand)

Main functionality

toriicli has commands for the following:

  • build: Build a Torii project
  • find: Find an installation of Unity
  • new: Create a new Torii project
  • release: Release a Torii project
  • nuget: Manage NuGet packages for project

Common options

  • -p, --project-path: The path to the toriiproject.yml file of the project. If you don't specify this it defaults to the current working directory.

Projects

A toriicli project is a directory with a file called toriiproject.yml. The toriiproject.yml file is a file containing the configuration for that project.

Please see ./toriicli/example_config.yml for information on how to configure toriicli.

Running toriicli new in a directory will create a new blank project using the example config file.

Steps

Steps are single transformative operations that take place on a directory of files. Examples of steps include 'import', 'export', 'compress', and 'chmod'.

The directory of files that the step performs operations on is called its 'workspace'. When run, each step has its own isolated workspace directory as temporary folders created in the OS recommended area for temporary files. Files can be copied from step to step using the keep field of a step. It defaults to **, so will keep every file from the previous step if not specified.

A step looks a lot like this:

step: export
keep: "game-{{ build_number }}.zip"
filter:
  options: [a_tag]
  targets: [StandaloneWindows]
using:
  backend: local
  container: C:/builds
  path_prefix: "{{ build_def.target }}"

Each step has to have the following fields:

  • step: The type of step to use.
  • keep (optional): A glob pattern of which files to keep from the previous step.
  • filter (optional): Lets you filter on certain factors.
  • using: What config values to provide to the step. Allows for customisable behaviour.

Some values given to a step are templated. This is useful as sometimes you want build artifacts to be named based on the target or version number of the build. In the example above, the build number is used in the keep field by templating it like this: game-{{ build_number }}.zip. When rendered, this template would simply insert the build number there, like: game-0.1.1234.56789.zip. Please note that templated strings need to be wrapped in quotes due to the YAML format.

Additionally, environment variables are respected in fields that are able to be templated. So you could insert the environment variable $SOME_VARIABLE by simply specifying it like that as the value in the step config.

Filters can be applied to only run steps under certain conditions. The following filters are available:

  • targets: Only run this step for this set of build targets.
  • options: Only run this step if this option is specified via the CLI (as -o, --option).

Import

Imports files from an external directory to the step workspace. Supports local store and cloud storage (at the moment just S3).

Example

step: import
using:
  backend: s3
  region: fra1
  endpoint: https://fra1.digitaloceanspaces.com/
  container: $BUCKET_NAME
  key: "{{ build_def.target }}/game-{{ build_number }}.zip"

Export

Like import, but instead copies files from the step workspace to an external directory.

Example

step: export
keep: "game-{{ build_number }}.zip"
filter:
  options: [upload]
using:
  backend: s3
  region: fra1
  endpoint: https://fra1.digitaloceanspaces.com/
  container: $BUCKET_NAME
  path_prefix: "{{ build_def.target }}"

Compress

Compress the workspace into an archive file.

Example

step: compress
using:
  format: zip
  archive_name: "game-{{ build_number }}"

This would produce an archive called game-0.1.123.56789.zip. This file could then be used in the next step by adding this to the step:

keep: "game-{{ build_number }}.zip"

The workspace for that step would then only contain the compressed archive from the prior step.

Chmod

Change permission bits of a file in the workspace.

Example

step: chmod
using:
  file_name: {{ build_def.executable_name }}
  permissions: [S_IEXEC]

This step would ensure the executable from the build had the executable flag set in the filesystem. Valid values for permissions are listed here.

Butler

Run itch.io Butler push on a workspace. Requires Butler to be installed. This is mainly used for released as opposed to builds.

Example

step: butler
filter:
  targets: [StandaloneWindows]
using:
  directory: "coolgame-{{ build_number }}.zip"
  user: my-itchio-user
  game: coolgame
  channel: windows
  user_version: "{{ build_number }}"

This example would push StandaloneWindows target build archives to the windows channel of the itch.io project.

Builds, and post-build steps

$ toriicli build

Builds are defined as build_defs in the project config file. A build definition has 2 things:

When toriicli build is run in the project directory, the build defs are used to generate a JSON file that's placed in the Unity project directory, and Unity is executed with the unity_build_execute_method defined in the project config. This is a static C# method that runs when Unity is launched. It loads the generated build defs file, and builds the game for each platform in a folder (configured by build_output_folder).

When Unity exits successfully, toriicli attempts to collect information from the builds based on the build defs it was given. It gets the path to each build as well as a version number from its assembly.

The build info for each build is then submitted to the build post-steps, where files from the build are transformed in a number of steps before reaching their intended destination (be it a folder, or some cloud storage somewhere).

Once the post steps have run, the builds are removed from the build output folder, and the generated build defs JSON file is also deleted. This behaviour can be toggled by providing the --no-clean option to build.

Build post-steps are specified in the build_post_steps area of the project config file. They are run for every build def specified after the Unity build completes.

Build post-steps have an implicit (unspecified) import step as the first step. This step imports from the build output directory into a step workspace. It's the exact same as specifying this as the first step (don't do this!):

step: import
using:
  backend: local
  container: "{{ path }}"

Example: compressing, storing, and optionally uploading to cloud storage

build_post_steps:
  - step: compress
    using:
      format: zip
      archive_name: "game-{{ build_number }}"
  - step: export
    keep: "game-{{ build_number }}.zip"
    using:
      backend: local
      container: C:/game-builds
      path_prefix: "{{ build_def.target }}"
  - step: export
    keep: "game-{{ build_number }}.zip"
    filter:
      options: [upload]
    using:
      backend: s3
      region: fra1
      endpoint: https://fra1.digitaloceanspaces.com/
      container: $BUCKET_NAME
      path_prefix: "{{ build_def.target }}"

This example will compress the build files into a zip, copy the zip to C:/game-builds/{target-name}/game-{build_number}.zip, and upload to S3 cloud storage if the build command is run with the upload option (like this: toriicli build --option upload).

Releasing, and release steps

$ toriicli release 0.1.123.4567

So you've built the game and uploaded it somewhere - what happens when you want to release it? Well you can use the release command for that.

You run it with a build number and it will run steps specified in the release_steps section of the project config.

The release command supports -o, --option for step filtering in the same way as build post-steps.

Example: releasing from cloud storage with itch.io butler

release_steps:
  - step: import
    using:
      backend: s3
      region: fra1
      endpoint: https://fra1.digitaloceanspaces.com/
      container: $BUCKET_NAME
      key: "{{ build_def.target }}/game-{{ build_number }}.zip"
  - step: butler
    filter:
      targets: [StandaloneWindows]
    using:
      directory: "game-{{ build_number }}.zip"
      user: my-itchio-user
      game: gamename
      channel: windows
      user_version: "{{ build_number }}"

Say you had just built and uploaded to cloud storage version 0.1.123.4567 of your game (zipped). If you ran toriicli release 0.1.123.4567 with the steps above, it would download that version of the archive from cloud storage (see the key field of the import step), and then run butler using the zip to release it. Pretty nifty.

NuGet

Toriicli has the nuget subcommand for working with project NuGet packages. To use this subcommand, you need the NuGet CLI and a version of MSBuild. MSBuild comes with Visual Studio, but you can use JetBrains MSBuild or Mono MSBuild if you don't want to install Visual Studio.

Note: in order to function correctly, NuGet must be configured to install packages into the nuget-packages directory in your Unity project folder. If you created your Torii project with toriicli new then you don't need to worry about this. If you are adapting an existing project to use toriicli, then make a file called nuget.config in your Unity project folder, and put this text in it:

<configuration>
  <config>
    <add key="repositoryPath" value="nuget-packages" />
  </config>
</configuration>

This will ensure all NuGet packages get installed to the nuget-packages folder.

The reason for this is that Unity installs its own packages to the packages folder, which NuGet is configured by default to use. They cannot both share the same space, so we need to install NuGet packages to a separate place, as Unity is a bad roommate.

You'll also need a blank packages.config file, like this:

<?xml version="1.0" encoding="utf-8"?>
<packages>
</packages>

Also note: it's best to run these NuGet commands when Unity is not running, as sometimes it will access the installed packages causing permissions errors.

Installing a package

To install a package, simply run:

$ toriicli nuget install <package>

You can optionally specify a version too:

$ toriicli nuget install <package> --version 0.1.2

This will use NuGet to install the package to the nuget-packages folder, it will add the package to your packages.config file, and it will copy the installed package into your Unity project. By default it will copy it into the Assets/NuGetPackages folder, but this can be configured by setting nuget_package_install_path in the config.

Additionally, by default it will install the package for the latest version of the .NET Framework it can, with a maximum version of .NET Framework 4.6.2. This is because as far as I could see, this is the latest possible version Unity supports. If you'd like to customise this, you can configure this by setting unity_dotnet_framework_version in the config.

Uninstalling a package

To uninstall a package, run:

$ toriicli nuget uninstall <package>

This will remove it from your Unity project, and your packages.config.

Restoring packages

To sync your installed packages with your packages.config file, run:

$ toriicli nuget restore

This will reinstall all packages listed in packages.config. This is useful when cloning a repo from fresh, as packages won't normally be committed to the remote.