Jobs
Jobs get stuff done. This page explains to create and configure jobs.
Job lifecycle
Jobs run arbitrary shell commands inside a dedicated environment called an agent. Agents can take many forms, including ephemeral Docker containers, Kubernetes pods, or x86/ARM Virtual Machines.
When a job is scheduled, the following happens:
- Allocate agent: pick a suitable agent from the pool of warm agents
- Initialize: execute setup steps such as importing environment variables, loading SSH keys, mounting secrets, and installing the Semaphore toolbox
- Run commands: execute your commands
- End job and save logs: the job activity log is saved for future inspection
- Destroy agent: the used agent is discarded along with all its contents
You can get non-ephemeral agents with self-hosted agents.
Jobs, blocks and pipelines
Semaphore uses jobs, blocks and pipelines to structure the workflow.
- Job: the minimum unit of work, a sequence of commands. Every job exists inside a block
- Block: contains one of more jobs. Jobs in the same block run concurrently and share properties
- Pipeline: a group of blocks connected by dependencies. A workflow may span multiple pipelines
How to create a job
You can create a job with the visual editor or by creating a YAML file.
- Editor
- YAML
Open your project on Semaphore and press Edit Workflow.
- Select the first block
- Type your shell commands
- Press Run the workflow, then press Looks good, Start →
- Create a file called
.semaphore/semaphore.yml
at the repository's root - Add the pipeline
name
- Define an agent
- Create a
blocks
key and type the block'sname
- Add a
task.jobs
key. The value is a list of jobs - Type the job's
name
- Add the job's
commands
. The value is a list of shell commands (one line per list item) - Save the file, commit and push it to your remote repository
You can use the following code as a starting point:
version: v1.0
name: Initial Pipeline
agent:
machine:
type: e1-standard-2
os_image: ubuntu2004
blocks:
- name: 'Block #1'
dependencies: []
task:
jobs:
- name: 'Job #1'
commands:
- echo "hello, world!"'
- echo "add as many commands as you like"
Semaphore automatically starts the job when the file is saved. Click the running job to follow the progress and view its log.
Do not use exit
in the job commands. Doing so terminates the terminal session and marks the job as failed. If you want force a non-exit status code use return <int>
instead.
Run jobs in parallel
Jobs in the same block always run in parallel.
- Editor
- YAML
To run two jobs in parallel:
- Select the block
- Press + Add job
- Type the job name and commands
Here you can also:
- Delete a job by pressing the X sign next to it.
- Delete the whole block along with the jobs by scrolling down and clicking on Delete block...
- Add a new
name
item underjobs
- Add your shell commands (one per line) under
commands
- Save the file, commit and push it to your repository
version: v1.0
name: Initial Pipeline
agent:
machine:
type: e1-standard-2
os_image: ubuntu2004
blocks:
- name: 'Block #1'
dependencies: []
task:
jobs:
- name: 'Job #1'
commands:
- echo "this is job 1"
- name: 'Job #2'
commands:
- echo "this is job 2"
You can't share files between jobs living in the same block.
Run jobs in sequence
If you want to run jobs in sequence, i.e. not in parallel, you must define them in separate blocks.
- Editor
- YAML
- Click on +Add Block
- Type the name of the block
- Adjust dependencies to define execution order
- Type the name and commands for the job
- Add a new job entry under
blocks
- Add a
dependencies
. List the names of the dependent blocks.
version: v1.0
name: Initial Pipeline
agent:
machine:
type: e1-standard-2
os_image: ubuntu2004
blocks:
- name: 'Block #1'
dependencies: []
task:
jobs:
- name: 'Job #1'
commands:
- echo "this is job 1 of block 1"
- name: 'Block #2'
dependencies:
- 'Block #1'
task:
jobs:
- name: 'Job #1'
commands:
- echo "this is job 1 of block 2"
Using dependencies
You can use block dependencies to control the execution flow of the workflow. See block dependencies to learn more.
Semaphore toolbox
The Semaphore toolbox is a set of built-in command line tools to carry essential tasks in your jobs such as cloning the repository or moving data between jobs.
The most-used tools in the Semaphore toolbox are:
- checkout clones the remote Git repository
- cache speeds up jobs by caching downloaded files
- artifact saves and moves files between jobs
checkout
The checkout command clones the remote Git repository and cd
s into the repository directory so you're ready to work.
The following example shows the first commands for working with a Node.js project. We run checkout
to get a local copy of the code. Next, we can run npm install
because we can assume that package.json
and package-lock.json
exist in the current directory.
checkout
npm install
Here is how the same code looks in a Semaphore job.
- Editor
- YAML
version: v1.0
name: Initial Pipeline
agent:
machine:
type: e1-standard-2
os_image: ubuntu2004
blocks:
- name: Install
dependencies: []
task:
jobs:
- name: npm install
commands:
- checkout
- npm install
How does checkout work?
Semaphore defines four environment variables to control how checkout works:
SEMAPHORE_GIT_URL
: the URL of the repository (e.g. git@github.com:mycompany/myproject.git).SEMAPHORE_GIT_DIR
: the path where the repository is to be cloned (e.g./home/semaphore/myproject
)SEMAPHORE_GIT_SHA
: the SHA key for the HEAD used forgit reset -q --hard
SEMAPHORE_GIT_DEPTH
: checkout does by default a shallow clone. This is the depth level for the shallow clone. Defaults to 50
cache
Using cache
in self-hosted agents might require additional setup steps.
The main function of the cache is to speed up job execution by caching downloaded files.
The cache store
and cache restore
commands can detect well-known dependency managers and persist files automatically. Let's say we want to speed up npm install
, here is how to do it:
checkout
cache restore
npm install
cache store
The highlighted lines show how to use the cache:
- cache store: saves
node_modules
to non-ephemeral storage. It knows it's a Node project because it foundpackage.json
in the working folderx. - cache restore: retrieves the cached copy of
node_modules
to the working directoryx.
Cache is not limited to Node.js. It works with several languages and frameworks. Alternatively, you can use cache with any kind of file or folder but in that case, you need to supply additional arguments
artifact
Using artifact
in self-hosted agents might require additional setup steps.
The artifact command can be used:
- as a way to move files between jobs and runs
- as persistent storage for artifacts like compiled binaries or bundles
The following example shows how to persist files between jobs. In the first job we have:
checkout
npm run build
artifact push workflow dist
In the following jobs, we can access the content of the dist folder with:
artifact pull workflow dist
Let's do another example: this time we want to save the compiled binary hello.exe
:
checkout
go build
artifact push project hello.exe
Artifact namespaces
Semaphore uses three separate namespaces of artifacts: job, workflow, and project. The syntax is:
artifact <pull|push> <job|workflow|project> </path/to/file/or/folder>
The namespace used controls at what level the artifact is accessible:
- job artifacts are only accessible to the job that created it. Useful for collecting debugging data
- workflow artifacts are accessible to all jobs in all running pipelines. The main use case is to pass data between jobs.
- project artifacts are always accessible. They are ideal for storing final deliverables.
For more information, see the Semaphore toolbox documentation.
Debugging jobs
This section shows tips to detect and debug failing jobs.
Why my job has failed?
Semaphore ends the job as soon as a command ends with non-zero exit status. Once a job has failed, no new jobs will be started and the workflow is marked as failed.
Open the job log to see why it failed. The problematic command is shown in red. You can click on the commands to expand their output.
If you want to ignore the exit status of a command append || true
at the end. For example:
echo "the next command might fail, that's OK, I don't care"
command_that_might_fail || true
echo "continuing job..."
Interactive debug with SSH
You can debug a job interactively by SSHing into the agent. This is a very powerful feature for troubleshooting.
If this is the first time using an interactive session you need to install and connect the Semaphore command line tool.
To open an interactive session, open the job log and:
- Click on SSH Debug
- Copy the command shown
- Run the command in a terminal
You'll be presented with a welcome message like this:
* Creating debug session for job 'd5972748-12d9-216f-a010-242683a04b27'
* Setting duration to 60 minutes
* Waiting for the debug session to boot up ...
* Waiting for ssh daemon to become ready.
Semaphore CI Debug Session.
- Checkout your code with `checkout`
- Run your CI commands with `source ~/commands.sh`
- Leave the session with `exit`
Documentation: https://docs.semaphoreci.com/essentials/debugging-with-ssh-access/.
semaphore@semaphore-vm:~$
To run the actual job commands in the SSH session:
source ~/commands.sh
You can actually run anything in the agent, including commands that were not actually part of the job. Exit the session to end the job.
By default, the duration of the SSH session is limited to one hour. To run longer debug sessions, pass the duration flag to the previous command as shown below:
sem debug job <job-id> --duration 3h
- Setting Bash option
set -e
disables shell autocompletion and causes the SSH session to exit with error.
Inspecting running jobs
You attach a terminal console to a running job. The steps are the same as debugging a job. The only difference is that Semaphore presents the following command (only while the job is running):
sem attach <job-id>
You can explore running processes, inspect the environment variables, and take a peek at the log files to help identify problems with your jobs.
Port forwarding
When SSH is not enough to troubleshoot an issue, you can use port forwarding to connect to services listening to ports in the agent.
A typical use case for this feature is troubleshooting end-to-end tests. Let's say a test is failing and you can't find any obvious cause from the logs alone. Port forwarding the HTTP port in the agent to your local machine can reveal how the application "looks".
To start a port-forwarding session:
sem port-forward <job-id> <local-port> <remote-port>
For example, to forward an application listening on port 3000 in the agent to your machine on port 6000:
sem port-forward <job-id> 6000 3000
You can now connect to http://localhost:6000
to view the application running remotely in the agent.
Port-forwarding only works for Virtual Machine-based agents. It's not available in Docker environments.