Getting Started

The State Tool offers a number of methods for simplifying and securing your Project configuration and integrating it with your development environment.

For information on installing the State Tool, see the installation guide.

Creating Your First Project

To work with the State Tool you need to create an ActiveState Platform project.

You can create and configure your project in two ways:

  1. Use the State Tool to create your first Python or Perl project on the command line.
  2. Navigate to the ActiveState Platform website and create your Python, Perl, or Tcl project in the Dashboard.

Use the command line

To create your account using the State Tool:

  1. Sign up for the ActiveState Platform if you don’t already have an account.

    state auth signup
    

    You will be prompted to accept the Terms of Service agreement and then enter the details for your new account.

  2. Sign in with your account.

    state auth
    
  3. Create a new project using the state init command and push it to the ActiveState Platform.

    state init <org>/<project_name> --language=<language>[version]
    
    • <org> is either your username or the name of an organization you belong to.
    • <project_name> is the name of the project to create.
    • <language> is the runtime language for your project (perl, python3, or python2) and you can optionally specify the version using the syntax @version_number.


    Follow the instructions on screen and switch to the directory where the project is initialized and run state push.

    For example:

    state init ActiveCo/python-web-dev --language=python3@3.6.6
    cd ActiveCo/python-web-dev
    state push
    
  4. Optional. Configure the operating systems for your project. By default, only the platform you run the init command on is added.

    For example, if you initialize the project on Linux, but you only want macOS and Windows, you can remove Linux and add the latest platforms for macOS and Windows 10:

    state platforms remove Linux 4.9.125
    state platforms add Darwin 16.6.0
    state platforms add Windows 10.0.17134.1
    

    For more information on managing platforms, see the platforms command.

  5. Configure packages for your project. You can either add packages individually, or all at once using a requirements.txt file (Python) or cpanfile or META.json file (Perl).

    To add an individual package:

    state packages add pandas@1.0.0
    

    To add packages using a requirements.txt file:

    state packages import --file requirements.txt 
    

    TIP: You can search for specific packages on the Platform using the state packages search subcommand. For example: state packages search pandas lists the versions of pandas that are available.

    Run state pull after you add packages to sync you local project with the latest commit on the ActiveState Platform.

    For more information on managing packages, see the packages command.

Use the Dashboard

To create your account and project using the Dashboard:

  1. Navigate to http://platform.activestate.com and create an account if you don’t already have one, otherwise just sign in.
  2. Click the Projects tab and then click Build a Custom Runtime.
  3. Choose the language and operating systems to use.
  4. Add the packages to include in your project. You can search for packages individually, or use a requirements.txt file to add all of the required packages and versions at once.
  5. Click Commit Changes to build your project.

For more information on projects, see the Projects documentation.

Activating Your Project

It’s time to activate your project! Go ahead and switch back to your command prompt. Before we can do anything we need to make sure that we’re authenticated, to do this simply run the state auth command:

state auth

You will be prompted for your username and password, and if all goes well it should show a friendly “You have authenticated” message.

Now we’re ready to activate our project. Run the state activate command:

state activate owner/projectName

The owner would be your username or if you created your project in an organization then instead of your username you can use the organization name.

This will create a new project folder under the current working directory containing an activestate.yaml file with some essentials. The State Tool will “activate” under this new project directory. This is a brand new project though and being in an “activated state” doesn’t mean much yet, so let’s deactivate by executing the exit command (or hit Ctrl+D).

Now when you want to work on your project again you can run the same state activate owner/projectName command again from anywhere and you’ll be entered into an activated state under your project directory. Alternatively you can manually enter into your project directory and run state activate without any other arguments.

For information on the “activated” environment, see About the State Tool activated environment.

Configuring Your Projects “Activated State”

So we have the basics in place, now it’s time to actually put everything to good use. What’s the point of all this if it’s not actually doing anything but changing a directory? Think of it as moving into a brand new house that’s completely empty; now it’s time to decorate.

Constants

You can define constants in your activestate.yaml file and reuse them throughout the file. Constants are defined in a constants section using name/value pairs:

constants:
  - name: LOCATION
    value: World

You can use the constants you define in other constants, scripts, events, and other configuration fields. The following example defines the same LOCATION constant, and references it in the subsequent entry as $constants.LOCATION. Constants are referenced this way wherever you use them in your activestate.yaml file. Constant names are not case-sensitive.

constants:
  - name: LOCATION
    value: World
  - name: HELLO
    value: Hello $constants.LOCATION

Using configuration values as variables is supported for many different fields, not just constants. You can also wrap your variables in brackets in case you’re using them in more advanced scenarios where the variable parser might otherwise get confused, so for example you could use your constant like so:

constants:
  - name: LOCATION
    value: World
  - name: HELLO
    value: Hello ${constants.LOCATION}

Secrets

Constants are great if you just want to reuse certain values throughout your activestate.yaml file, but what if you want to store values that are sensitive, and which shouldn’t be stored in version control or in any plain text format? This is what “secrets” are meant for; the State Tool has a very useful secret management solution built in. You should store any database passwords, API keys, and other sensitive credentials your project needs access to as secrets.

A “secret” is similar to a constant, except that its value is securely stored on the ActiveState Platform. It is encrypted using RSA encryption in a manner that no one but you will ever be able to decrypt (unless you start sharing your password - which… don’t do that!). Not even ActiveState knows the true value of your secrets; we only store the encrypted value. Secrets also have the concept of sharing, meaning you can share a secret with the people on your project.

Secret Definitions versus Secret Values

It’s important to differentiate between a secret definition and a secret value. You can define a secret without it having a value assigned yet. When a secret is defined but it has no value then the user will be prompted for a value when the secret is being used.

Secret Scopes

When talking about secrets we need to also talk about secret scopes. A scope describes who the secret value “belongs to”. Currently we support two scopes, “user” and “project”.

The “user” scope denotes a secret value as belonging to a user. This means that when you define a secret that is “user scoped” that every user will have to set their own value for this secret, to which only they have access.

The “project” scope denotes a secret value as belonging to everyone in the project. This means that when someone defines a secret value for such a secret that everyone with permissions for the project will get access to this value.

Setting secrets

To define a secret you use the command line tool, not the activestate.yaml. This is because secrets live on the ActiveState Platform (in client-side encrypted format – we do not have access to the real values) and not in your local configuration file. We’ll want to use the state secrets command to define a new secret:

state secrets set project.secret-name secret-value

This will create a secret named secret-name with the value secret-value that will be shared with everyone who has permissions for the project.

If, instead, you want to define a secret that only you have access to, you need to define a user secret by specifying user.secret-name:

state secrets set user.secret-name secret-value

This will still define the secret for everyone with permissions for project, but only you will have access to the value you’ve set. Anyone else that uses this secret will be prompted for their own value.

Retrieving secrets

Now that we have a secret defined we can start using it. To view secrets that exist for your current project you can run the state secrets command. This will produce a concise list of secrets, their “scope” (user or project) as well as a usage example (what you would use to set or retrieve their value).

To retrieve the value of a secret run:

state secrets get project.secret-name

This will retrieve the value for a secret called secret-name whose value is shared with everyone in the project.

Using secrets

So we can set and retrieve secrets, what about using them in our activestate.yaml configuration file? This is actually very simple, and similar to how you use constants. Let’s use our “HELLO” constant from before but this time instead of referencing a constant called “LOCATION” we’ll reference a secret with that name instead. This syntax would look as follows:

constants:
 - name: HELLO
   value: Hello $secrets.user.LOCATION

What’s happening here is the $secrets. prefix indicates that we want to “expand” our identifier as a secret, and the user.LOCATION bits identify it as a secret named LOCATION stored under the user. This syntax is compatible with the output of the “Usage” column when running state secrets to list your secrets. You can copy and paste that value right after the $secrets. prefix in your activestate.yaml file.

It’s important to note that you do not need to first define the user.LOCATION secret. If a secret does not yet exist you will instead be prompted for its value when you try to access it.

Scripts

Scripts in the State Tool can be compared to scripts in NPM, or build targets in a Makefile. You define a script and can then run it whenever you need to. Let’s start with something simple:

scripts:
 - name: hello
   language: bash
   value: echo Hello World

This will register a script with the alias “hello”. You can run this outside your “activated state” via the state run command by running state run hello, but once you state activate it becomes much simpler, you simply invoke it by running hello.

NOTE: When creating new scripts or updating existing scripts, you should specify the language your script uses. Currently, if you do not specify a language you will get a deprecation warning, and the default shell for your operating system will be used. In the future, scripts will fail if no language is specified.

If you are using shell scripts on multiple platforms, you can use constraints to specify the shell to use on each operating system. For example, you could specify batch to run cmd.exe on Windows and bash to run bash on Linux and macOS.

If you want to use one single script for all platforms, you can configure your project on the ActiveState Platform to include a Python or Perl runtime, after which can define a script to use that language, for example:

scripts:
  - name: hello
    language: python3
    value: print("Hello World")

The following language settings are supported:

LanguageDescription
bashFor scripts that use bash (Bourne again shell).
shFor script that use sh (Bourne shell).
batchFor scripts that use cmd.exe on Windows.
perlFor scripts that use your Perl runtime environment.
python2For scripts that use your Python 2 runtime environment.
python3For scripts that use your Python 3 runtime environment.

Note: Support for specifying Tcl as the script language is not yet available.

See the section on Constraining Scripts for information on limiting language to the appropriate operating system. For example, only running batch scripts on Microsoft Windows.

Calling Scripts From Other Scripts

Calling one script from another is fairly straight forward, you can access scripts as “variables” the same as many other activestate.yaml structures. Let’s let the code do the talking:

scripts:
  - name: hello
    language: python3
    value: print("Hello World")
  - name: greeting
    language: python3
    value: |
      $scripts.hello
      print("How are you doing?")

When you execute state run greeting it will inject the value of the “hello” script into the “greeting” script. The resulting code that ends up being ran would look like this”

print("Hello World")
print("How are you doing?")

Calling Scripts by Their Path

The above example can be problematic when you are running scripts in various languages. For example the following code would certainly fail:

scripts:
  - name: hello
    language: python3
    value: print("Hello World")
  - name: greeting
    value: |
      $scripts.hello
      echo "How are you doing?"

Note that the “greeting” script does not have a language defined, this means it runs as either Bash or Batch depending on your platform. But the “hello” script is still Python code, which is being embedded in bash code, which will lead to some sort of syntax error.

You work around such an issue by calling the script by its file, rather than just embedding the code. To do this you can use the path() method that lives on all scripts. So the above code would become:

scripts:
  - name: hello
    language: python3
    value: print("Hello World")
  - name: greeting
    value: |
      $scripts.hello.path()
      echo "How are you doing?"

Now the resulting script will look something like this:

/tmp/7979234.script.py
echo "How are you doing?"

The file in question will have a shebang with our interpreter defined in it, so you don’t need to worry about providing the interpreter unless you are on Windows, which doesn’t support shebang. For cross-platform compatibility you could instead use:

  - name: greeting
    value: |
      python3 $scripts.hello.path()
      echo "How are you doing?"

That way we’re explicitly saying Python is the interpreter for this file.

Using Constants & Secrets

Scripts can use constants too, so for example you could instead use the following value:

value: echo $constants.HELLO

This also works for secrets, so instead of the above you could use echo $secrets.user.HELLO. It gets more interesting though, because in the activestate.yaml EVERYTHING is a “variable”, so you could create another command that references our first command:

scripts:
 - name: log-hello
   value: $scripts.hello > /tmp/hello.txt

You can see how used wisely this can quickly become very powerful.

The main use-case for scripts is to kick off builds, run tests, etc. But the sky’s the limit.

Script Arguments

Scripts support arguments too! Using the hello world sample again, you could define your script like so:

scripts:
 - name: hello
   value: echo hello $1

Now you can run hello world or hello planet and it should print out the argument you passed.

Constraining Scripts

You can constrain scripts to only run on certain platforms. If a language is not specified for the script, they will be run as either bash or batch scripts, depending on the shell that you run them from. This can be problematic because bash and batch are two very different languages, and you might have cross-platform requirements. You can add constraints to your script entries to ensure that the correct command runs based on the operating system identified at run time. For example:

scripts:
 - name: env
   language: bash
   value: printenv
   constraints: 
       os: linux,macos
 - name: env
   language: batch
   value: set
   constraints: 
       os: windows

Now when you run state run env on Windows it will execute the windows constrained script, whereas when you run it on Linux or macOS it will run the script constrained to those platforms.

Constraints can also be negative, so if you would want to run a script on any platform except windows you could use os: -windows.

The possible values for the OS constraint are:

  • windows
  • macos
  • linux

These values can be given in a comma separate fashion, and include a minus character to exclude them (e.g. -windows).

Events

OK so we have constants, secrets and scripts, but your project has some special needs. It might require certain services to be running, or for certain non-language-specific dependencies to be installed. For this you can hook into events, the most important event of which is the ACTIVATE event, as the name implies this event is triggered when you state activate.

NOTE: Currently, ACTIVATE is the only available event. Additional events will be added in future releases.

Events act mostly the same as scripts do, except that they aren’t manually invoked and instead run when their event triggers. For example we could have an ACTIVATE event that looks like this:

events
 - name: ACTIVATE
   value: systemctl start my-service

This would start a service whenever we enter an “activated state”. It’s worth noting that the ACTIVATE event has a special use-case: it is invoked as part of your bashrc (or zshrc, fishrc, etc.) meaning it can export environment variables, register bash aliases, etc.

Sharing with your Team or among multiple computers

Now that you have your activestate.yaml configuration file set up you can share it with your team. You can do this however you prefer, but if your team is using version control we recommend checking in your activestate.yaml file. As we add new capabilities you can update your configuration in the activestate.yaml file and share these capabilities with your team.

This also applies if you are using a project on more than one computer. For example, at home and at work, or on Windows and macOS. The activestate.yaml is not stored in the Platform, so you need to store it in version control, or use another method to keep the copies of your activestate.yaml file in sync on all computers you want to share the scripts, variables, and other settings for your project configuration.