How to create and share your own Bitrise step

We're going to go through creating a step (properly) and sharing it with the Bitrise community. Tutorial based on a real step created and written by SΓ©bastien Pousse.

Guest blog post by SΓ©bastien Pousse, the original appeared on Developers @ Yellow Pages in French.

SΓ©bastien works for PagesJaunes (French Yellow Pages) as head of innovation and mobile strategy and he likes sports, especially climbing, squash, mountain biking and running.


Bitrise is a solution that allows you to industrialize your mobile compilations (iOS, Android, Xamarin, ReactNative, Ionic etc.) by making dedicated virtual machines available for a defined period of time.

These virtual machines are launched on demand following trigger events (activity on your source code repositories for example) and execute a workflow containing a set of steps.

A step is a cogwheel of your workflow, that will run and perform a specific task.

Bitrise offers a catalogue of steps already written:


The objective of this tutorial

In this tutorial I'll assist you in the creation of a step, explaining all the essential steps until the submission of the step to the Bitrise team to appear in the catalogue above.

Customer in the command line

Bitrise is a SAAS solution but it is still possible to work locally with it. For that, we will first install a Bitrise client in CLI with homebrew .

brew install bitrise


We will do updates with a bitrise setup, which will take care of updating the kernel tools like envman, stepman. It'll also install Bitrise plugins, in particular, the one for steps that is important for us.

bitrise setup

  β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—
  β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—
  β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘β•šβ•β•β•β•β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β•
  β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—
  β•šβ•β•β•β•β•β• β•šβ•β•   β•šβ•β•   β•šβ•β•  β•šβ•β•β•šβ•β•β•šβ•β•β•β•β•β•β•β•šβ•β•β•β•β•β•β•

  version: 1.12.0

Full setup: false
Clean setup: false
Detected OS: darwin

Checking Bitrise Core tools...
[OK] envman (1.1.9): /Users/spousse/.bitrise/tools/envman
[OK] stepman (0.9.37): /Users/spousse/.bitrise/tools/stepman

Doing OS X specific setup
Checking required tools...
[OK] Homebrew 1.5.2: /usr/local/bin/brew
[OK] Homebrew/homebrew-core (git revision f4c7d; last commit 2018-01-30)
[OK] xcodebuild (Xcode 8.1 | Build version 8B62): /usr/bin/xcodebuild
[OK] active Xcode (Command Line Tools) path (xcode-select --print-path): /Applications/

Checking Bitrise Plugins...
[OK] Plugin workflow-editor (1.1.3): /Users/spousse/.bitrise/plugins/workflow-editor
[OK] Plugin analytics (0.9.11): /Users/spousse/.bitrise/plugins/analytics
[OK] Plugin init (1.0.1): /Users/spousse/.bitrise/plugins/init
[OK] Plugin step (0.9.5): /Users/spousse/.bitrise/plugins/step

Checking Bitrise Toolkits...
[OK] go (1.9.2): /Users/spousse/.bitrise/toolkits/go/inst/go/bin/go
[OK] bash (GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin15)): /bin/bash

All the required tools are installed! We're ready to rock!!

To start using bitrise:
* cd into your project's directory (if you're not there already)
* call: bitrise init
* follow the guide

That's all :)


Initialization of the step

Let's get down to business. We will start with an actual case: at the moment I need to be able to upload a build to wetransfer to send it to a third party.

We will design a step which uploads a file on wetransfer.

The small novelty is that since version 1.6 of the Bitrise client in CLI, the creation of a step is simplified. Go to a working directory and run the command bitrise :step create

bitrise :step create
Who are you / who's the author? : kawaiseb
What's the title / name of the Step? : wetransfer
Generated Step ID (from provided Title): wetransfer
Please provide a summary : Upload files to wetransfer and share a short url to download
Please provide a description : This step send files to wetransfer. Wetransfer give you a short url to download files. Limit 2GO.

Answer all the questions above.

What's the primary category of this Step?
Please select from the list:
[1] : access-control
[2] : artifact-info
[3] : installer
[4] : deploy
[5] : utility
[6] : dependency
[7] : code-sign
[8] : build
[9] : test
[10] : notification
(type in the option's number, then hit Enter) : 5

Choose a category. I chose utility.

Toolkit: the entry/base language of the Step.
Our recommendation is to use Bash for very simple Steps
 and for more complex ones use another language, one which we have toolkit support for.
If you're just getting started with Step development our suggestion is to select Bash,
 as that's the easiest option. It's possible to convert the step later, if needed.
Note: Of course even if you select e.g. Bash as the entry language, you can run other scripts from there,
 so it's possible to write the majority of the step's code in e.g. Ruby,
 and have an entry Bash script which does nothing else except running the Ruby script.
Which toolkit (language) would you like to use?
Please select from the list:
[1] : bash
[2] : go
(type in the option's number, then hit Enter) : 1

In my case, I chose to do the step in nodejs and the script will be launched in bash so 1) it is.

Website & source code URL:
Will you host the source code on GitHub? [YES/no]: YES
What's your GitHub username (user/org where you'll register the step's repository)? : kawaiseb
We'll use as the website/repo URL for this step.
Please when you create the repository on GitHub for the step
 create it under the user/org: kawaiseb
 and the name of the repository should be: bitrise-step-wetransfer

Creating Step directory at: /Users/spousse/bitrise-step-wetransfer
 * [OK] created: /Users/spousse/bitrise-step-wetransfer/
 * [OK] created: /Users/spousse/bitrise-step-wetransfer/LICENSE
 * [OK] created: /Users/spousse/bitrise-step-wetransfer/.gitignore
 * [OK] created: /Users/spousse/bitrise-step-wetransfer/step.yml
 * [OK] created: /Users/spousse/bitrise-step-wetransfer/bitrise.yml
 * [OK] created: /Users/spousse/bitrise-step-wetransfer/.bitrise.secrets.yml
 * [OK] created: /Users/spousse/bitrise-step-wetransfer/

Initializing git repository in step directory ...
 $ git "init"
 $ git "remote" "add" "origin" ""

Step is ready!

You can find it at: /Users/spousse/bitrise-step-wetransfer

TIP: cd into /Users/spousse/bitrise-step-wetransfer and run bitrise run test for a quick test drive!

I've decided to store the step on GitHub. The repository of my step is created, as well as all the necessary files.

Let's check that the test workflow starts all right...

bitrise run test


There are some basic rules to know before you start:

  1. Every input, output and parameter in any step is an Environment Variable
  2. The value of an Environment Variable can only be a String
  3. If you create an environment variable in a child process, it will not be visible from the parent process

I repeat these here because these caused me some trouble πŸ™‚ (Read more about these concepts here.)



This file is actually the main configuration file. It will allow you to create one or more workflow(s) and define one or more step(s) to execute. This is your scheduler, which you will find in the cloud. This file is in YAML format.

Let's look at some elements of the file.

Environment variables

We have a series of variables defined globally to our application. These variables are reusable from anywhere thereafter.


  # If you want to share this step into a StepLib
  - BITRISE_STEP_ID: wetransfer

For the variable MY_STEPLIB_REPO_FORK_GIT_URL, the bitrise-steplib project will have to be forked. Indeed it is in this project that our step will be placed in the end.

The test workflow

The test workflow contains a step with a bash script that will display the environment variable declared above. Nothing really exciting at the moment.

    - script:
        - content: |
            echo "Just an example 'secrets' print."
            echo "The value of 'A_SECRET_PARAM' is: $A_SECRET_PARAM"

The test workflow will later allow you to test your step in the command line and also on Bitrise.


Back to the step. I will declare all the parameters I need for uploading to wetransfer. Here's what I need:

  • The sender's email address
  • The email address of the recipient(s)
  • A short message to accompany the link
  • The files to send to wetransfer

It's as simple as that. Once the problem is posed, here is how to solve it.


The description of the step (step.yml)

You can find the source here. In this file, we will describe the operation of the step and set all the parameters necessary for its proper functioning.

I will not go into all the details, I'll only deal with the input variables which are the most interesting in our case:

  # All parameter we need to upload files to wetransfer
  - wtu_mailsender:
      title: Email of the sender
      is_expand: true
      is_required: true

  - wtu_mailreceiver:
      title: One or more email separated by comma for receiver
      is_required: true

This will materialize as follows:

cran-of-Capture-2018-02-19-a - 08.09.34

The is_expand statement is used to directly expand the description field
The is_required statement specifies that the variable is required.

And so on for all the expected parameters. There is also a possibility of making lists.

For example, for the debug mode I just want to make an enum {yes, no}.

# Debug mode deactivated by default
  - wtu_debug_mode: "no"
      title: "Debug mode ?"
      description: |
        Step prints additional debug information if this option
        is enabled
      is_expand: true
      is_required: false
        - "yes"
        - "no"

Fill in the form ...

cran-of-Capture-2018-02-19-a - 08.12.30

Execution of the step (

You can find the source here. This file will be executed each time the step is launched.

Following feedback from Bitrise, I had to make some changes. The most important thing is not to change the working directory. This is advocated by Bitrise.

In the end, the will launch my nodejs script with the set of parameters that have been entered in the configuration interface of the step.

set -ex

THIS_SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

npm install --prefix $THIS_SCRIPT_DIR wetransfert --save

$THIS_SCRIPT_DIR/upload.js "${wtu_debug_mode}" "${wtu_mailsender}" "${wtu_mailreceiver}" "${wtu_filepath}" "${wtu_message}" "${wtu_language}"

The main script (upload.js)

(Source file.) I will not go into the details of the script, it is not really the object of the tutorial, just understand that as a default, you can develop steps in bash and go languages. If you'd like to use any other language, you can, but you have to check if the interpreter belonging to the given language is pre-installed on the Bitrise virtual machines or not. If they are not, then choosing bash, you have to install the chosen interpreter from the bash script and also run your choice of script from there.

The transfer.js script takes arguments on the standard input that match the expected parameters and are provided by the step.

#!/usr/bin/env node

const fs = require('fs');
const { upload } = require('wetransfert');

// USAGE : node wetransfertupload.js debug sender receiver filepath message lang

let debug = process.argv[2]; //debug true/false
let sender = process.argv[3]; //email of the sender
let receivers = process.argv[4]; //email of the receiver
let filepath = process.argv[5]; //filepath where files to send are
let message = process.argv[6]; //body of the mail
let lang = process.argv[7]; //langage of the mail
let tabReceivers;

// testing parameters
if(debug == null || sender == null || receivers == null || filepath == null || lang == null) {
  console.log('ERROR : One or more parameters are invalid');
  return 1;


What is important in particular is to be able to notify the user quickly enough when the step does not have all the required parameters. I did not go very far with this in this first version, especially since we specify values as is_required in the step.yml.

But for example, I could verify that the given directory exists and contains many files, and if it is not the case I could directly send an error message. This saves time and interrupts the step directly.

The audit of the step

Once our step finished running, we will check that everything is ok by launching the audit of our step via the command:

bitrise run audit-this-step

A priori it is a format validation of the step.yml which is tested here. If all went well we'll see this:

+ stepman audit --step-yml ./step.yml
INFO[13:47:49]  * [OK] Success audit (./step.yml)


Forking the bitrise-steplib project

The final goal is to put this step in the project bitrise-steplib and make a PR so that Bitrise can validate the step (or not).

Fork the project:

Then fill in the url of the fork in the file bitrise.yml


Once this is done, you can share his step.

Share the step

Start with the command:

bitrise-step-wetransfer [master] bitrise run share-this-step

This command will prepare the PR for the stepLib. Then what only remains is to submit it to Bitrise.

cran-of-Capture-2018-02-18-a - 22.56.41

Well... I will not hide that I had to go back to it 3 times. πŸ™‚

cran-of-Capture-2018-02-19-a - 10.44.11

Before the step was accepted, here are the main reasons for the refusal by the Bitrise team:

  • Cleanness of the step: I left a lot of unnecessary things in the code
  • Bad compliance with the guidelines on variable names. The input variables must be in lower case, the output variables in upper case, for example
  • Then in my step, I made a change in a directory which could actually have an impact for a person who executed my step in his workflow


Well, this is it, now you can create and share a step. The nice thing is that you can use the technology of your choice, we could have done a script in Ruby or php, in short: I did not see any limits. The only limitation is to preinstall the necessary dependencies.

Feel free to write me comments, ask questions or tell me if some points are not clear enough :)


You can find the source files of the step in question here:

The bitrise-steplib Bitrise project that will need to be forked:

The lib nodejs to use the wetransfer API:

The discussion forum (they are very responsive):

English version by Anna BΓ‘tki.

Get Started for free

Start building now, choose a plan later.

Sign Up

Get started for free

Start building now, choose a plan later.