From GitHub Actions
This section summarizes key differences between GitHub Actions and Space Automation and provides sample scripts to help you with the migration process.
Disclaimer: All the information about GitHub Actions is taken from the official GitHub website and is valid only at the moment of posting this document. As any product evolves over time, this information may be already outdated.
Main concepts
GitHub Actions | Space Automation |
---|---|
Workflow is a configurable automated process.
| (Not yet available) Pipeline is a configurable automated process.
|
Job is a defined task made up of steps.
| Job is a defined task made up of steps.
|
Step is a building block for a job.
| Step is a building block for a job.
|
Action is a portable building block that you can run as a step.
| No matching entity. |
Hello World! name: Hello World
on: push
jobs:
hello-world:
name: Hello world job
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Hello from vm
run: echo Hello World! | Hello World! job("Hello world") {
container(displayName = "Say Hello", image = "ubuntu") {
shellScript {
content = "echo Hello World!"
}
}
} |
Configuration as code
Both products provide no UI to configure your builds. The configuration is performed using script files.
GitHub Actions | Space Automation |
---|---|
YAML-based DSL for declarative scripts. If you need scripts that change execution flow, you can create custom actions based on JavaScript. | Kotlin-based DSL. You can implement any complex execution logic in place using pure Kotlin. |
Script location
GitHub Actions | Space Automation |
---|---|
Workflows are located in separate files inside the / project root
├─── .github
│ ├─── action1 // custom action
│ │ ├─── action.yml
│ │ └─── ...
│ ├─── action2 // custom action
│ │ ├─── action.yml
│ │ └─── ...
│ ├─── myworkflow1.yml // workflow file
│ ├─── myworkflow2.yml // workflow file
... | All configuration is done using a single / project root
├─── .space.kts
... |
Execution environment
GitHub Actions | Space Automation |
---|---|
|
|
Execution limits
GitHub Actions | Space Automation |
---|---|
|
|
Running commands
GitHub Actions | Space Automation |
---|---|
name: A workflow for showing project dir
on: push
jobs:
show:
name: Show dir content
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Show dir
run: |
echo Project dir:
ls ./ | job("Show dir content") {
container(image = "ubuntu:latest") {
shellScript {
content = """
echo Project dir:
ls ./
"""
}
}
} |
Running code
GitHub Actions | Space Automation |
---|---|
Running imperative code requires creating a custom action. For example, the following script gets a random joke from icanhazdadjoke.com. Workflow in name: Get a joke
on:
push
jobs:
action:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: ha-ha
uses: ./.github/actions/joke-action Action in name: "Get a joke action"
description: "Use an external API to get a joke"
outputs:
joke-output:
description: The resulting joke from the icanhazdadjokes API
runs:
using: "node12"
main: "main.js"
const getJoke = require("./joke");
const core = require("@actions/core");
async function run() {
const joke = await getJoke();
console.log(joke);
core.setOutput("joke-output", joke);
}
run();
const request = require("request-promise");
const options = {
method: "GET",
uri: "https://icanhazdadjoke.com/",
headers: {
Accept: "application/json"
},
json: true
};
async function getJoke() {
const res = await request(options);
return res.joke;
}
module.exports = getJoke; | Any step allows you to run arbitrary Kotlin code in place. For example, the following script gets a random joke from icanhazdadjoke.com using the OkHttp client.
@file:DependsOn("com.squareup.okhttp:okhttp:2.7.4", "org.json:json:20200518")
import com.squareup.okhttp.*
import org.json.JSONObject
job("Get random joke") {
container(image = "amazoncorretto:17-alpine") {
kotlinScript {
val client = OkHttpClient()
val request = Request.Builder()
.url("http://icanhazdadjoke.com")
.addHeader("Accept", "application/json")
.build()
val response = client.newCall(request).execute()
val jData = response.body().string()
val jObject = JSONObject(jData)
val joke = jObject.get("joke").toString()
println(joke)
}
}
} |
Trigger events
GitHub Actions | Space Automation |
---|---|
To set run triggers for a job, you should use the
All triggers excepting name: My workflow
on:
schedule:
- cron: '0 8 * * *'
push:
branches:
- main
pull_request:
branches:
- mybranch | To set run triggers for a job, you should use the
You cannot explicitly specify a filter by branch: Automation will go through the list of triggers in job("My job") {
startOn {
schedule { cron("0 8 * * *") }
codeReviewOpened {}
}
} |
Sharing files
GitHub Actions | Space Automation |
---|---|
You can share files between jobs using the upload-artifact and download-artifact actions. For example: jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: ...
- uses: actions/upload-artifact@main
with:
name: step-artifacts
path: public/
test:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/download-artifact@main
with:
name: step-artifacts
path: public
- name: ... | You can share files between jobs using the special API or directly through the containers' job("Example shell scripts") {
container(image = "ubuntu") {
shellScript {
content = """
./gradlew build
cp -R ./output /mnt/space/share/output
"""
}
}
container(image = "ubuntu") {
shellScript {
content = """
echo Ouput content
ls /mnt/space/share/output
"""
}
}
} |
Sharing parameters
GitHub Actions | Space Automation |
---|---|
You can specify the output of one action to be the input of the following action. In the example below, the name: JS Actions
on:
push
jobs:
action:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: ha-ha
uses: ./.github/actions/joke-action
id: jokes
- name: create-issue
uses: ./.github/actions/issue-maker
with:
repo-token: ${{secrets.GITHUB_TOKEN}}
joke: ${{steps.jokes.outputs.joke-output}} | All steps in a job share the parameter storage. To access the storage, you should use the job("Reuse params") {
container(displayName = "Set param", image = "amazoncorretto:17-alpine") {
kotlinScript { api ->
api.parameters["joke"] = "funny"
}
}
container(displayName = "Use param", image = "amazoncorretto:17-alpine") {
kotlinScript { api ->
println("Here's the joke: ${api.parameters["joke"]}!")
}
}
} |
Publishing packages
GitHub Actions | Space Automation |
---|---|
GitHub provides its own package repository manager GitHub Packages. Workflows can use it to get and publish packages. To do this, you can use special actions (or write your own one that uses the corresponding external tool). To authenticate in GitHub Packages, you should use the - name: Build container image
uses: docker/build-push-action@v1
with:
username: ${{github.actor}}
password: ${{secrets.GITHUB_TOKEN}}
registry: docker.pkg.github.com
repository: UserName/my_project/my_package
tag_with_sha: true | The package repository manager in Space is called Space Packages. Some automation scenarios do not require authentication. For example, for Docker, Automation provides a special API and authentication in Packages is not required. job("Build and push Docker") {
docker {
build {
args["HTTP_PROXY"] = "http://10.20.30.2:1234"
labels["vendor"] = "mycompany"
}
push("mycompany.registry.jetbrains.space/p/prj/mydocker/myimage") {
tag = "version1.0"
}
}
} Nevertheless, there are cases when providing authentication credentials is required. For example, in Gradle, credentials must be specified in publishing {
repositories {
maven {
credentials {
username = "$System.env.JB_SPACE_CLIENT_ID"
password = "$System.env.JB_SPACE_CLIENT_SECRET"
}
url = "https://maven.pkg.jetbrains.space/mycompany/p/projectkey/my-maven-repo"
}
}
} |
Using other product subsystems
GitHub and JetBrains Space contain multiple subsystems: project repositories, issue tracking, CI/CD, and others. Both GitHub Actions and Space Automation let you access these subsystems from your build scripts.
GitHub Actions | Space Automation |
---|---|
Working with other subsystems is possible in custom actions using JavaScript code. GitHub Actions provides subsystems API in separate npm modules that you should reference in your actions. For example, core functionality is available in the const core = require("@actions/core");
const github = require("@actions/github"); There's also an official client oktokit that lets you work with GitHub subsystems. For example, this is how you can use it to create a new issue with custom data: const core = require("@actions/core");
const github = require("@actions/github");
async function run() {
try {
const issueTitle = core.getInput("issue-title");
const jokeBody = core.getInput("joke");
const token = core.getInput("repo-token");
const octokit = github.getOctokit(token);
const newIssue = await octokit.issues.create({
repo: github.context.repo.repo,
owner: github.context.repo.owner,
title: issueTitle,
body: jokeBody
});
} catch (err) {
core.setFailed(err.message);
}
}
run() | In Space, each subsystem provides an API for accessing it. In job("build and publish") {
container(image = "gradle") {
kotlinScript { api ->
try {
api.gradle("build")
} catch (ex: Exception) {
val recipient = MessageRecipient.Channel(ChatChannel.FromName("CI-channel"))
val content = ChatMessage.Text("Build failed")
api.space().chats.messages.sendMessage(recipient, content)
}
}
}
} |
Using services
GitHub Actions | Space Automation |
---|---|
For example, this is how you run a service container with PostgreSQL: jobs:
container-job:
runs-on: ubuntu-latest
container: node:10.18-jessie
services:
# Label used to access the service container
postgres:
image: postgres
env:
POSTGRES_PASSWORD: 1234 The service runs in a separate container inside the container that runs the job. Network resources are shared between containers. The service container hostname is defined by the label ( | For example, this is how you run a service container with PostgreSQL: job("service") {
container(image = "node:10.18-jessie") {
service("postgres") {
env["POSTGRES_PASSWORD"] = "1234"
}
}
} The service runs in a separate container inside the container that runs the step. Network resources are shared between containers. The service container hostname is defined by the image name ( |
Secrets and parameters
GitHub Actions | Space Automation |
---|---|
You can create secrets on the project or organization level. To make a secret available to an action, you must set the secret as an input or environment variable in the workflow file. name: Secrets
on:
push
jobs:
use_secrets:
runs-on: ubuntu-latest
steps:
- shell: bash
env:
MY_SECRET: ${{ secrets.MySecret }}
run: |
echo My secret is "$MY_SECRET" | You can create secrets and parameters only on the project level. To access secrets and parameters you should use environmental variables and a special API. job("Use secrets") {
container(image = "ubuntu:latest") {
env["MY_SECRET"] = Secrets("my-secret")
shellScript {
content = "echo My secret is ${'$'}MY_SECRET"
}
}
} |
Custom workflows
GitHub Actions | Space Automation |
---|---|
Apart from CI/CD, you can create custom workflows: custom scenarios that are triggered by various events in the system and can change the system state. How does it look: Place a | Not yet available. What is planned: You can create a custom Automation workflow that can be triggered by Space webhooks. As in |
Running build matrix
GitHub Actions | Space Automation |
---|---|
Using strategy:
matrix:
node: [6, 8, 10]
steps:
- uses: actions/setup-node@v1
with:
# The Node.js version to configure
node-version: ${{ matrix.node }} | Not yet available. As a workaround, you can create a number of jobs – one for each version. |
User interface
GitHub Actions | Space Automation |
---|---|
The UI is represented with the Actions tab of a particular project. Here you can view workflow logs, run and cancel workflow execution, view artifacts. Test results are available only in the form of logs. | The UI is represented with the Jobs page of a particular project. Here you can view, run, cancel jobs, view artifacts, and more. Test results are shown with a table that lets you instantly see failed tests. |
Sample scripts. Hello World
GitHub Actions | Space Automation |
---|---|
Using a 'hello-world' container:
name: Hello World
on: push
jobs:
build:
name: Hello world action
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: ./action-a
name: "Hello Actions"
description: "Say hello"
author: "me@example.com"
runs:
using: "docker"
image: "hello-world" | Using a 'hello-world' container job("Hello world") {
container(image = "hello-world")
} |
Using the name: Hello World
on: push
jobs:
build:
name: Hello world action
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Hello from vm
run: echo Hello world! | Using the job("Hello world") {
container(image = "ubuntu") {
shellScript {
content = "echo Hello world!"
}
}
} |
Sample scripts. Build and run tests using Gradle
GitHub Actions | Space Automation |
---|---|
First, you should configure Java in a virtual machine using the name: Java CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Build with Gradle
run: ./gradlew build | Use the special job("Build and run tests") {
gradlew("amazoncorretto:17-alpine", "build")
} |
Sample scripts. Build and publish a Docker image
GitHub Actions | Space Automation |
---|---|
Build-and-Push-Docker-Image:
runs-on: ubuntu-latest
needs: test
name: Docker Build, Tag, Push
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Build container image
uses: docker/build-push-action@v1
with:
username: ${{github.actor}}
password: ${{secrets.GITHUB_TOKEN}}
registry: docker.pkg.github.com
repository: UserName/myproject/mypackage
tag_with_sha: true | Use the special job("Build and push Docker") {
docker {
build {
context = "docker"
file = "./docker/Dockerfile"
args["HTTP_PROXY"] = "http://10.20.30.2:1234"
labels["vendor"] = "mycompany"
}
push("mycompany.registry.jetbrains.space/p/myproject/myrepo/myimage") {
tag = "version1.0"
}
}
} |