Forgejo Actions user guide
Forgejo Actions
provides Continuous Integration driven from the files in the .forgejo/workflows
directory of a repository, with a web interface to show the results. The syntax and semantics of the workflow
files will be familiar to people used to GitHub Actions but they are not and will never be identical.
The following guide explains key concepts to help understand how workflows
are interpreted, with a set of examples that can be copy/pasted and modified to fit particular use cases.
Quick start
- Verify that
Enable Repository Actions
is checked in theRepository
tab of the/{owner}/{repository}/settings
page. If the checkbox does not show it means the administrator of the Forgejo instance did not activate the feature. - Add the following to the
.forgejo/workflows/demo.yaml
file in the repository. - Go to the
Actions
tab of the/{owner}/{repository}/actions
page of the repository to see the result of the run. - Click on the workflow link to see the details and the job execution logs.
Concepts
Forgejo runner
Forgejo
itself does not run the jobs
, it relies on the Forgejo runner to do so. See the Forgejo Actions administrator guide for more information.
Actions
An Action
is a repository that contains the equivalent of a function in any programming language. It comes in two flavors, depending on the file found at the root of the repository:
- action.yml: describes the inputs and outputs of the action and the implementation. See this example.
- Dockerfile: if no
action.yml
file is found, it is used to create an image withdocker build
and run a container from it to carry out the action. See this example and the workflow that uses it. Note that files written outside of the workspace will be lost when the step using such an action terminates.
One of the most commonly used action is checkout which clones the repository that triggered a workflow
. Another one is setup-go that will install Go.
Just as any other program of function, an Action
has pre-requisites to successfully be installed and run. When looking at re-using an existing Action
, this is an important consideration. For instance setup-go depends on NodeJS during installation.
Expressions
In a workflow
file strings that look like ${{ ... }}
are evaluated by the Forgejo runner
and are called expressions. As a shortcut, if: ${{ ... }}
is equivalent to if: ...
, i.e the ${{ }}
surrounding the expression is implicit and can be stripped. Checkout the example that illustrates expressions.
Literals
- boolean: true or false
- null: null
- number: any number format supported by JSON
- string: enclosed in single quotes
Logical operators
Operator | Description |
---|---|
( ) | Logical grouping |
[ ] | Index |
. | Property de-reference |
! | Not |
< | Less than |
<= | Less than or equal |
> | Greater than |
>= | Greater than or equal |
== | Equal |
!= | Not equal |
&& | And |
|| | Or |
NOTE: String comparisons are case insensitive.
Functions
contains( search, item )
. Returnstrue
ifsearch
containsitem
. Ifsearch
is an array, this function returnstrue
if theitem
is an element in the array. Ifsearch
is a string, this function returnstrue
if theitem
is a substring ofsearch
. This function is not case sensitive. Casts values to a string.startsWith( searchString, searchValue )
. Returnstrue
whensearchString
starts withsearchValue
. This function is not case sensitive. Casts values to a string.endsWith( searchString, searchValue )
. Returnstrue
ifsearchString
ends withsearchValue
. This function is not case sensitive. Casts values to a string.format( string, replaceValue0, replaceValue1, ..., replaceValueN)
. Replaces values in thestring
, with the variablereplaceValueN
. Variables in thestring
are specified using the{N}
syntax, whereN
is an integer. You must specify at least onereplaceValue
andstring
. Escape curly braces using double braces.join( array, optionalSeparator )
. The value forarray
can be an array or a string. All values inarray
are concatenated into a string. If you provideoptionalSeparator
, it is inserted between the concatenated values. Otherwise, the default separator,
is used. Casts values to a string.toJSON(value)
. Returns a pretty-print JSON representation ofvalue
.fromJSON(value)
. Returns a JSON object or JSON data type forvalue
. You can use this function to provide a JSON object as an evaluated expression or to convert environment variables from a string.
Caching commonly used files
When a job
starts, it can communicate with the Forgejo runner
to
fetch commonly used files that were saved by previous runs. For
instance the https://code.forgejo.org/actions/setup-go action will do
that by default to save downloading and compiling packages found in
go.mod
.
It is also possible to explicitly control what is cached and when using the https://code.forgejo.org/actions/cache action.
Services
PostgreSQL, redis and other services can be run from container images with something similar to the following. See also the set of examples.
A container with the specified image:
is run before the job
starts and is terminated when it completes. The job can address the service using its name, in this case pgsql
.
The IP address of pgsql
is on the same docker network as the container running the steps and there is no need for port binding (see the docker run —publish option for more information). The postgres:15
image exposes the PostgreSQL port 5432 and a client will be able to connect as shown in this example
image
The location of the container image to run.
env
Key/value pairs injected in the environment when running the container, equivalent to —env.
cmd
A list of command and arguments, equivalent to [COMMAND] [ARG…].
options
A string of additional options, as documented docker run. For instance: “—workdir /myworkdir —ulimit nofile=1024:1024”.
NOTE: the
--volume
option is restricted to a whitelist of volumes configured in the runner executing the task. See the Forgejo Actions administrator guide for more information.
username
The username to authenticate with the registry where the image is located.
password
The password to authenticate with the registry where the image is located.
The list of runners and their tasks
A Forgejo runner
listens on a Forgejo
instance, waiting for jobs. To figure out if a runner is available for a given repository, go to /{owner}/{repository}/settings/actions/runners
. If there are none, you can run one for yourself on your laptop.
Some runners are Global and are available for every repository, others are only available for the repositories within a given user or organization. And there can even be runners dedicated to a single repository. The Forgejo
administrator is the only one able to launch a Global runner. But the user who owns an organization can launch a runner without requiring any special permission. All they need to do is to get a runner registration token and install the runner on their own laptop or on a server of their choosing (see the Forgejo Actions administrator guide for more information).
Clicking on the pencil icon next to a runner shows the list of tasks it executed, with the status and a link to display the details of the execution.
The list of tasks in a repository
From the Actions
tab in a repository, the list of ongoing and past tasks triggered by this repository is displayed with their status.
Following the link on a task displays the logs and the Re-run all jobs
button. It is also possible to re-run a specific job by hovering on it and clicking on the arrows.
Pull request actions are moderated
The first time a user proposes a pull request, the task is blocked to reduce the security risks.
It can be approved by a maintainer of the project and there will be no need to unblock future pull requests.
Secrets
A repository, a user or an organization can hold secrets, a set of key/value pairs that are stored encrypted in the Forgejo
database and revealed to the workflows
as ${{ secrets.KEY }}
. They can be defined from the web interface:
- in
/org/{org}/settings/actions/secrets
to be available in all the repositories that belong to the organization - in
/user/settings/actions/secrets
to be available in all the repositories that belong to the logged in user - in
/{owner}/{repo}/settings/actions/secrets
to be available to theworkflows
of a single repository
Once the secret is added, its value cannot be changed or displayed.
Workflow reference guide
The syntax and semantics of the YAML file describing a workflow
are partially explained here. When an entry is missing the GitHub Actions documentation may be helpful because there are similarities. But there also are significant differences that require testing.
The name of each chapter is a pseudo YAML path where user defined
values are in <>
. For instance jobs.<job_id>.runs-on
documents the
following YAML equivalent where job-id
is myjob
:
on
Workflows will be triggered on
certain events with the following:
e.g. to run a workflow when branch main
is pushed
trigger event | activity types |
---|---|
create | not applicable |
delete | not applicable |
fork | not applicable |
gollum | not applicable |
push | not applicable |
issues | opened , edited , closed , reopened , assigned , unassigned , milestoned , demilestoned , labeled , unlabeled |
issue_comment | created , edited , deleted |
pull_request | opened , edited , closed , reopened , assigned , unassigned , synchronize , labeled , unlabeled |
pull_request_review | submitted , edited |
pull_request_review_comment | created , edited |
release | published , edited |
registry_package | published |
Not everything from https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows is implemented yet. Please refer to the forgejo/actions package source code and the list of webhook event names to find out about supported triggers.
env
Set environment variables that are available in the workflow in the env
context
and as regular environment variables.
- The expression
${{ env.KEY1 }}
will be evaluated tovalue1
- The environment variable
KEY1
will be set tovalue1
jobs.<job_id>
Each job
in a workflow
must specify the kind of machine it needs to run its steps
with runs-on
. For instance docker
in the following workflow
:
means that the Forgejo runner
that claims to provide a kind of machine labelled docker
will be selected by Forgejo
and sent the job to be run.
The actual machine provided by the runner entirely depends on how the Forgejo runner
was registered (see the Forgejo Actions administrator guide for more information).
The list of available labels
for a given repository can be seen in the /{owner}/{repo}/settings/actions/runners
page.
jobs.<job_id>.runs-on
By default the docker
label will create a container from a Node.js 16 Debian GNU/Linux bullseye image and will run each step
as root. Since an application container is used, the jobs will inherit the limitations imposed by the engine (Docker for instance). In particular they will not be able to run or install software that depends on systemd
.
The runs-on: self-hosted
label will run the jobs in a LXC container where software that rely on systemd
can be installed. Nested containers can also be created recursively (see the setup-forgejo
integration tests for an example).
Services
are not supported for jobs that run on LXC.
jobs.<job_id>.container
If the default image is unsuitable, a job can specify an alternate container image with container:
, as shown in this example. For instance the following will ensure the job is run using Alpine 3.18.
jobs.<job_id>.container.options
A string of additional options, as documented in docker run. For instance: “—workdir /myworkdir —ulimit nofile=1024:1024”.
NOTE: the
--volume
option is restricted to a whitelist of volumes configured in the runner executing the task. See the Forgejo Actions administrator guide for more information.
jobs.<job_id>.steps
An array of steps executed sequentially on the host specified by runs-on
.
jobs.<job_id>.steps.if
The step is run if the expression evaluates to true. The following additional boolean functions are supported:
success()
. returns true when none of the previous steps have failed or been canceled.always()
. causes the step to always execute, and returns true, even when canceled. If you want to run a job or step regardless of its success or failure, use the recommended alternative: !cancelled().failure()
. returns true when any previous step of a job fails.
Checkout the workflows in example-if and example-if-fail.
jobs.<job_id>.steps.uses
Specifies the repository from which the Action
will be cloned or a directory where it can be found.
-
Remote actions A relative
Action
such asuses: actions/checkout@v3
will clone the repository at the URL composed by prepending the default actions URL which is https://code.forgejo.org/. It is the equivalent of providing the fully qualified URLuses: https://code.forgejo.org/actions/checkout@v3
. In other words the following:is the same as:
When possible it is strongly recommended to choose fully qualified URLs to avoid ambiguities. During installation, the `Forgejo’ instance may use another default URL and a workflow could fail because it gets an outdated version from https://tooold.org/actions/checkout instead. Or even a repository that does not contain the intended action.
-
Local actions
An action that begins with a
./
will be loaded from a directory instead of being cloned from a repository. The structure of the directory is otherwise the same as if it was located in a remote repository.NOTE: the most common mistake when using an action included in the repository under test is to forget to checkout the repository with
uses: actions/checkout@v3
.
jobs.<job_id>.steps.with
A dictionary mapping the inputs of the action to concrete values. The action.yml
defines and documents the inputs.
For remote actions that are implemented with a Dockerfile
instead of action.yml
, the args
key is used as command line arguments when the container is run.
Debugging workflows with forgejo-runner exec
To get a quicker debug loop when working on a workflow, it may be more
convenient to run them on your laptop using forgejo-runner exec
. For
instance:
Examples
Each example is part of the setup-forgejo action test suite. They can be run locally with something similar to:
- Echo - a single step that prints one sentence
- Expression - a collection of various forms of expression
- Local actions - using an action found in a directory instead of a remote repository
- PostgreSQL service - a PostgreSQL service and a connection to display the (empty) list of tables of the default database
- Using services - illustrates how to configure and use services
- Choosing the image with
container
- replacing theruns-on: docker
image with thealpine:3.18
image usingcontainer:
- Docker action - using a action implemented as a
Dockerfile
Glossary
- action: a repository that can be used in a way similar to a function in any programming language to run a single step.
- expression: a string enclosed in
${{ ... }}
and evaluated at runtime - job: a sequential set of steps.
- label the kind of machine that is matched against the value of
runs-on
in a workflow. - runner: the Forgejo runner daemon tasked to execute the workflows.
- step: a command the runner is required to carry out.
- workflow or task: a file in the
.forgejo/workflows
directory that contains jobs. - workspace is the directory where the files of the job are stored and shared between all steps