# Admin

## Installation guide

The installation process on the occasion of a fresh system is several steps which are listed below, in the case of steps that were previously performed on a particular computer, you can skip individual steps.

### Install dependencies

The first step would be to install a package manager if you still don't have one, for Windows this would be choco, and for macOS homebrew.

To do this you need to paste the following command into Powershell or Terminal respectively.

<table><thead><tr><th align="center">Windows</th><th align="center">macOS</th></tr></thead><tbody><tr><td align="center"><pre class="language-powershell"><code class="lang-powershell">Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
</code></pre></td><td align="center"><pre class="language-bash"><code class="lang-bash">/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
</code></pre></td></tr></tbody></table>

If you have encountered any problems, it is recommended to check the program's documentation, which for Windows can be found [here ](https://chocolatey.org/install)and for macOS [here](https://brew.sh/).

If you use Windows you will also need to install git, which you can do by running.

```powershell
choco install git -Y
```

The next step will be to install Chrome and Chromedriver.

<table><thead><tr><th align="center">Windows</th><th align="center">macOS</th></tr></thead><tbody><tr><td align="center"><pre class="language-powershell"><code class="lang-powershell">choco install googlechrome -Y
</code></pre></td><td align="center"><pre class="language-bash"><code class="lang-bash">brew install google-chrome
</code></pre></td></tr><tr><td align="center"><pre class="language-powershell"><code class="lang-powershell">choco install chromedriver -Y
</code></pre></td><td align="center"><pre class="language-bash"><code class="lang-bash">brew install chromedriver
</code></pre></td></tr></tbody></table>

In the next step, we will install python.

<table><thead><tr><th align="center">Windows</th><th align="center">macOS</th></tr></thead><tbody><tr><td align="center"><pre class="language-powershell"><code class="lang-powershell">choco install python -Y
</code></pre></td><td align="center"><pre class="language-bash"><code class="lang-bash">brew install pyenv
</code></pre></td></tr><tr><td align="center"><pre class="language-python"><code class="lang-python">python -m pip install -U pip
</code></pre></td><td align="center"><pre class="language-bash"><code class="lang-bash">pyenv install $(pyenv install -l | grep -v - | tail -1)
</code></pre></td></tr><tr><td align="center">If you encounter a problem with pip installation, you can use instead of command above:</td><td align="center"><pre class="language-bash"><code class="lang-bash"><strong>pyenv global $(pyenv install -l | grep -v - | tail -1)
</strong></code></pre></td></tr><tr><td align="center"><pre class="language-python"><code class="lang-python">python -m ensurepip
</code></pre></td><td align="center"><pre class="language-bash"><code class="lang-bash">eval "$(pyenv init -)"
</code></pre></td></tr><tr><td align="center"></td><td align="center"><pre class="language-bash"><code class="lang-bash">pip install --upgrade setuptools pip
</code></pre></td></tr></tbody></table>

In this step, we will take care of the installation of Ruby.

<table><thead><tr><th align="center">Windows</th><th align="center">macOS</th></tr></thead><tbody><tr><td align="center"><pre class="language-powershell"><code class="lang-powershell">choco install ruby --version=3.0.0.1
</code></pre></td><td align="center"><pre class="language-bash" data-line-numbers><code class="lang-bash">brew install rbenv ruby-build
rbenv init
</code></pre></td></tr><tr><td align="center"><pre class="language-powershell"><code class="lang-powershell">gem install bundler
</code></pre></td><td align="center"><pre class="language-bash" data-line-numbers><code class="lang-bash">rbenv install 3.0.0
rbenv global 3.0.0
</code></pre></td></tr></tbody></table>

The final step will be to install imagemagic.

<table><thead><tr><th width="374" align="center">Windows</th><th align="center">macOS</th></tr></thead><tbody><tr><td align="center"><pre class="language-powershell"><code class="lang-powershell">choco install imagemagick
</code></pre></td><td align="center"><pre><code>brew install imagemagick
</code></pre></td></tr><tr><td align="center"></td><td align="center"><pre><code>brew install mono-libgdiplus
</code></pre></td></tr></tbody></table>

In addition, on **Windows**, you need to download msys2 and you can do this with the following command.

```powershell
choco install msys2
```

{% hint style="info" %}
After completing these steps, you may need to **reboot** the system
{% endhint %}

{% hint style="warning" %}
If you are using **Windows** before proceeding further you must first download .NET Runtime 5.X from [here](https://dotnet.microsoft.com/en-us/download/dotnet/5.0).
{% endhint %}

### Adding licenses to Runner

*to be released...*

### macOS System Access

On macOS, when you launch the application, the first screen you see will inform you of the current status of your granted permissions.

<figure><img src="https://364514667-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FM42sfXyuLMAgfUi8PDvW%2Fuploads%2FU8NBBwr6QEEBib0Agoez%2Fimage.png?alt=media&#x26;token=31d1b9cc-4f78-4721-8f7e-7434237a9ef3" alt=""><figcaption></figcaption></figure>

You need to click the buttons one by one which will open the appropriate tab in the settings and grant all the necessary access to the application.

After doing this, the window will close and you can proceed to the next step.

## Automatic updates

Automatic updates work in a very simple way in which you won't even notice them. The important thing is that you don't have to worry about the update interrupting your work as this process doesn't start while the bot is running. After the update is finished the bot will turn itself on and connect to the portal so that it can continue working.

The only thing you need to check is whether you need to create an exception in the firewall to allow the bot to update itself. A list of hosts that the runner can reach can be found at [#firewall-exceptions](#firewall-exceptions "mention").

## Runner behavior

The following is a description of the path of how a runner that is ready to work behaves.

1. We check the server to see if there is a new job for us (every 5 seconds).
2. There is an empty answer - there is nothing to do, and we return to point 1.
3. When we have something to do we execute a job.
4. The result of the job is returned to the server.
5. We return to point 1.

## Running file based jobs

First, Runner creates a folder for himself with Job, at the following address: `~/.anyrobot/{job_id}`

There you will find two folders and such files in such a structure:

* input - input files directory.
  * `script` - the content of the downloaded task.
  * `payload.json` - that is the entire response from the server, example below.
* output - output files directory
  * `console.txt` - what the console showed - with timestamps, logs etc.
  * `result.txt` - What the script showed on the console itself.

Example `payload.json` response:

```json
{
  "secret": "d7dd76a0-b7a5-44d7-b98f-8712aa0e5707",
  "job_id": "bc55c654-94a6-11eb-a8b3-0242ac130003",
  "workflow_id": "c6d0fd24-94a6-11eb-a8b3-0242ac130003",
  "workflow_name": "Test workflow",
  "trigger_name": "schedule",
  "trigger_id": "2d11707c-96ce-11eb-a8b3-0242ac130003",
  "task": {
    "method": "download",
    "name": "task_dir/task_name",
    "download_url": "https://wwww.serwer/test.rb"
  }
  "assist_requests_url": "https://serwer.xxx.yyy.com/api/v1/jobs/123e4567-e89b-12d3-a456-426614174000/assist_requests",
  "results_url": "https://serwer.xxx.yyy.com/api/v1/jobs/123e4567-e89b-12d3-a456-426614174000/results",
  "parameters": {
    "hello": "world",
    "lorem": "ipsum"
  }
}
```

Script used as an example:

{% code title="test.rb" lineNumbers="true" %}

```ruby
#!/usr/bin/env ruby

require 'json'

# Load payload

file = File.read("input/payload.json")
payload = JSON.parse(file)

# Print ARGV + ENV

puts "Payload: #{payload.inspect}"
puts "Arguments: #{ARGV.inspect}"
puts "Environment: #{ENV.inspect}"
```

{% endcode %}

{% hint style="warning" %}
Note that the working directory is set to the job directory, not the input directory. This may affect the paths of loaded files like `"payload.json"` → `"input/payload.json"`.
{% endhint %}

We already have everything downloaded, in the right folder, so the application executes this file (in case you need to give, for example, `chmod +x` on it, using the built-in console. The application is agnostic to the programming language used, so it is very important that the first line of the file is a shebang:

```
#!/usr/bin/env ruby
#!/usr/bin/env python
#!/usr/bin/env node
#!/usr/bin/env dart
```

Thanks to this Runner knows how to run such a file. The command contained in the shebang (ruby, python, node, etc...) can be changed at will and is passed as a script call command. For known types, additional environment configurations are put in place for the Activity Monitor to work seamlessly.

## Running project based jobs

For project-based Jobs, the principle is very similar to file-based. The biggest difference is the appearance of the `anyrobot.yml` file which is presented in more detail [here](https://docs.anyrobot.com/developing-robots#configuring-project-settings).

1. As with a regular job, its folder is created.&#x20;
2. Then the Runner, based on the `DownloadUrl` from the Task from Job, checks if it has such a repository cloned on disk, and if it has `anyrobot.yml`.
   * If there is not then:
     1. Clones and changes the branch to a value with `BranchName` from Task from Job.
     2. Checks if the project `anyrobot.yml` file exists.
     3. Parses the `anyrobot.yml.`
     4. Executes `install_command` from `anyrobot.yml`.
     5. Executes `after_install` from `anyrobot.yml`.
     6. If something goes wrong it aborts the operation.
3. It checks if there is a new version using `git branch` and determines possibly if it is detached, then `git rev-parse HEAD` and `git ls-remote -q origin refs/heads?{branch}`.
   * If there is a new version:
     1. Executes `before_update` from `anyrobot.yml`.
     2. Saves information about the last commit.
     3. Updates to a newer version: `git reset --hard`, then `git switch {branchName}` and `git pull`.
     4. Checks for `anyrobot.yml` and parses it.
     5. Executes `after_update` from `anyrobot.yml`.
     6. If the update procedure did not go as planned and something went wrong it performs a `git reset --hard` and checkout commit, which he saved for himself in point 2.
4. Once everything is ok the following are launched in sequence:
   1. Executes `before_runner` from `anyrobot.yml`.
   2. Executes `runner_command` from `anyrobot.yml` with passed parameters.\
      For more detailed information about this step, see [File-based jobs](#running-file-based-jobs) above.
   3. Executes `after_runner` from `anyrobot.yml`.
5. If nothing threw an exception or no bad execution code appeared in the terminal Job returns success. In any other case, if something goes wrong during any step a fail is returned (and a report to sentry).

## Job result

There are two possibilities, the executed Job can be successful or failed, and accordingly, then different responses are sent.

### Success - simple output

Let's assume that the executed script will display something like this:

```
Hello, I'm AnyRobot! 😀
Chromedriver is present ✅
```

Here there is no special tag, so the return to the server is quite simple afterwards:

```json
# Request

POST /api/v1/jobs/94916326-9344-43f5-9187-1aea2931eac7/results
Authorization: Bearer 123e4567-e89b-12d3-a456-426614174000

{
  "genre": "success",
  "code": 0,
  "output": "Hello, I'm AnyRobot! 😀
Chromedriver is present ✅"
}

# Response

201 Created
404 job not found
422 bad format
```

### Failure - simple output

Let's assume that the executed script will display something like this:

```
zsh: command not found: dart
```

Thus, to the server goes the information about the failure:

```json
# Request

POST /api/v1/jobs/94916326-9344-43f5-9187-1aea2931eac7/results
Authorization: Bearer 123e4567-e89b-12d3-a456-426614174000

{
  "genre": "failure",
  "code": 127,
  "output": "zsh: command not found: dart"
}

# Response

201 Created
404 job not found
422 bad format
```

### Success - with additional results

The script is responsible for preparing the files `result.json` and `attachments.json` in the `output` directory. If either file is created its contents will be attached to the corresponding key in the uploaded result: `"result"` for `result.json` and `"attachments"` for `attachments.json`. If no file is created, the key will not be added.

```json
{
  "genre": "success",
  "code": 0,
  "output" : "My first output line... hello world!",
  "result": {
    "lorem": "ipsum"
  },
  "attachments": [
    {
      "genre": "html",
      "attachment_id": "123e4567-e89b-12d3-a456-426614174000",
      "name": "Lorem Ipsum",
      "code": "electrical_parameters_voltage",
      "body": "<!doctype html> <html lang=\"en\"> <head> <meta charset=\"utf-8\"> <title>The HTML5 Herald</title> <meta name=\"description\" content=\"The HTML5 Herald\"> <meta name=\"author\" content=\"SitePoint\"> <link rel=\"stylesheet\" href=\"css/styles.css?v=1.0\"> </head> <body> <script src=\"js/scripts.js\"></script> </body> </html>"
    },
    {
      "genre": "file",
      "attachment_id": "123e4567-e89b-12d3-a456-426614174000",
      "name": "test.png",
      "code": "electrical_parameters_voltage",
      "body": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="
   }
  ]
}
```

{% hint style="info" %}
Output is always included because it is independent of the result and attachments files created by the script.
{% endhint %}

More is explained in the chapter [Attachments](https://docs.anyrobot.com/portal/portal-api#attachments) and [Job Results](https://docs.anyrobot.com/portal/portal-api#job-results).

## Firewall exceptions

Here is the list of hosts used by the applications:

* [install.anyrobot.com](https://www.install.anyrobot.com/)
* your portal address
* sentry address - check out sentry docs [here](https://docs.sentry.io/product/security/ip-ranges/)
