Forgejo Latest

Scale applications based on pending jobs on Forgejo repository.

Availability: v2.18+ Maintainer: Community

Suggest a change

Trigger Specification

This specification describes the forgejo-runner trigger for Forgejo Actions. It scales based on the amount of jobs pending in given runner labels.

triggers:
  - type: forgejo-runner
    metadata:
      # Required: the name of the registered runner 
      name: "forgejo-runner-ubuntu"
      # Required: the url of the forgejo instance 
      address: "http://localhost:3000"
      # Optional: Scope of the jobs to check, global is the default one and will get all the jobs in the instance with the defined labels
      global: "true" 
      # Optional: User to set as a scope, global should be false, if no repo is set will get all the pending jobs for the user
      owner: "cobak"
      # Optional: Repository level scope
      repo: "my-repo"
      # Required: Will get the jobs with match with this labels
      labels: "ubuntu-latest"

Parameter list:

  • name - Name of the registered runner.
  • address - Url of the forgejo instance.
  • global - Scope of the jobs to check. (Values: true, false, Default: true, Optional, Mutually exclusice with owner and repo)
  • owner - User to set as a scope. (Optional, Mutually exclusice with global and repo)
  • repo - User to set as a scope. (Optional, Mutually exclusice with global and owner)
  • labels - Labels to match the job with.

Authentication Parameters

  • token - Required token to connect to forgejo instance.

Options on how to set the scope (global, user, repo)

  1. If you set owner and repo KEDA forgejo scaler will retrieve jobs from that repository:
    owner: "username"
    repo: "my_repo"
    
  2. If you set the organization will retrieve the organization jobs:
    org: "my_org"
    
  3. If you set global: true will retrieve all the jobs from the instance
    global: "true"
    
  4. And if you don’t set any of this optional fields will retrieve the jobs from the user who creates the token

Configuration

Registering runners and binding config with autoscalers

To match job endpoints with job execution, we need to pre-register a runner, save the generated JSON file as a ConfigMap, and share it with the autoscaler. review the different ways to register a runner here, select the desired scope (user, repository or global) and create the corresponding JSON file.

Autoscaler definition and runner registration should match to work as expected.

Create a .runner file and a registration file with this commands, and set the output in a configmap.

docker run -v /var/run/docker.sock:/var/run/docker.sock  -v $PWD:/data --rm code.forgejo.org/forgejo/runner:6.3.1 forgejo-runner register --no-interactive --token xxxx --name runner --instance http://localhost:3000/ --labels ubuntu-20:docker://node:20-bookworm,docker:docker://ghcr.io/catthehacker/ubuntu:act-latest
docker run -v /var/run/docker.sock:/var/run/docker.sock  -v $PWD:/data --rm code.forgejo.org/forgejo/runner:6.3.1 forgejo-runner generate-config > registration.yaml 
apiVersion: v1
kind: ConfigMap
metadata:
  name: runner-config
  namespace: runners
data:
  .runner: |
    {
        "WARNING": "This file is automatically generated by act-runner. Do not edit it manually unless you know what you are doing. Removing this file will cause act runner to re-register as a new runner.",
        "id": 3,
        "uuid": "be6b5260-e73f-4e21-af84-8e205dbaaacd",
        "name": "runner",
        "token": "<global-runner-token>",
        "address": "http://localhost:3000/",
        "labels": [
            "ubuntu-20:docker://node:20-bookworm",
            "docker:docker://ghcr.io/catthehacker/ubuntu:act-latest"
        ]
      }    

---

apiVersion: v1
kind: ConfigMap
metadata:
  name: runner-registration
  namespace: runners
data:
  registration.yaml: |
    log:
      # The level of logging, can be trace, debug, info, warn, error, fatal
      level: info
      # The level of logging for jobs, can be trace, debug, info, earn, error, fatal
      job_level: info

    runner:
      # Where to store the registration result.
      file: /data/.runner
      # Execute how many tasks concurrently at the same time.
      capacity: 1
      # Extra environment variables to run jobs.
      envs:
        DOCKER_HOST: tcp://localhost:2376
        DOCKER_TLS_VERIFY: 1
        DOCKER_CERT_PATH: /certs/client
      # Extra environment variables to run jobs from a file.
      # It will be ignored if it's empty or the file doesn't exist.
      env_file: .env
      # The timeout for a job to be finished.
      # Please note that the Forgejo instance also has a timeout (3h by default) for the job.
      # So the job could be stopped by the Forgejo instance if it's timeout is shorter than this.
      timeout: 3h
      # The timeout for the runner to wait for running jobs to finish when
      # shutting down because a TERM or INT signal has been received.  Any
      # running jobs that haven't finished after this timeout will be
      # cancelled.
      # If unset or zero the jobs will be cancelled immediately.
      shutdown_timeout: 3h
      # Whether skip verifying the TLS certificate of the instance.
      insecure: false
      # The timeout for fetching the job from the Forgejo instance.
      fetch_timeout: 5s
      # The interval for fetching the job from the Forgejo instance.
      fetch_interval: 2s
      # The interval for reporting the job status and logs to the Forgejo instance.
      report_interval: 1s
      # The labels of a runner are used to determine which jobs the runner can run, and how to run them.
      # Like: ["macos-arm64:host", "ubuntu-latest:docker://node:20-bookworm", "ubuntu-22.04:docker://node:20-bookworm"]
      # If it's empty when registering, it will ask for inputting labels.
      # If it's empty when executing the `daemon`, it will use labels in the `.runner` file.
      labels:
        - ubuntu-20:docker://node:20-bookworm
        - docker:docker://ghcr.io/catthehacker/ubuntu:act-latest

    cache:
      # Enable cache server to use actions/cache.
      enabled: true
      # The directory to store the cache data.
      # If it's empty, the cache data will be stored in $HOME/.cache/actcache.
      dir: ""
      # The host of the cache server.
      # It's not for the address to listen, but the address to connect from job containers.
      # So 0.0.0.0 is a bad choice, leave it empty to detect automatically.
      host: ""
      # The port of the cache server.
      # 0 means to use a random available port.
      port: 0
      # The port of the cache proxy.
      # 0 means to use a random available port.
      proxy_port: 0
      # The external cache server URL. Valid only when enable is true.
      # If it's specified, it will be used to set the ACTIONS_CACHE_URL environment variable. The URL should generally end with "/".
      # Otherwise it will be set to the the URL of the internal cache server.
      external_server: ""
      # The shared cache secret. When communicating with a cache server, the runner uses this secret to verify the authenticity of the cache requests.
      # When using an external cache server it is required to set the same secret for the runner and the cache server.
      secret: ""
      # Overrides the ACTIONS_CACHE_URL passed to workflow containers. This should only be used if the runner host is not reachable from the
      # workflow containers, and requires further setup.
      actions_cache_url_override: ""

    container:
      # Specifies the network to which the container will connect.
      # Could be host, bridge or the name of a custom network.
      # If it's empty, create a network automatically.
      network: host
      # Whether to create networks with IPv6 enabled. Requires the Docker daemon to be set up accordingly.
      # Only takes effect if "network" is set to "".
      enable_ipv6: false
      # Whether to use privileged mode or not when launching task containers (privileged mode is required for Docker-in-Docker).
      privileged: false
      # And other options to be used when the container is started (eg, --add-host=my.forgejo.url:host-gateway).
      options: -v /certs/client:/certs/client
      # The parent directory of a job's working directory.
      # If it's empty, /workspace will be used.
      workdir_parent:
      # Volumes (including bind mounts) can be mounted to containers. Glob syntax is supported, see https://github.com/gobwas/glob
      # You can specify multiple volumes. If the sequence is empty, no volumes can be mounted.
      # For example, if you only allow containers to mount the `data` volume and all the json files in `/src`, you should change the config to:
      # valid_volumes:
      #   - data
      #   - /src/*.json
      # If you want to allow any volume, please use the following configuration:
      # valid_volumes:
      #   - '**'
      valid_volumes:
        - /certs/client
      # overrides the docker client host with the specified one.
      # If "-" or "", an available docker host will automatically be found.
      # If "automount", an available docker host will automatically be found and mounted in the job container (e.g. /var/run/docker.sock).
      # Otherwise the specified docker host will be used and an error will be returned if it doesn't work.
      docker_host: "-"
      # Pull docker image(s) even if already present
      force_pull: false
      # Rebuild local docker image(s) even if already present
      force_rebuild: false

    host:
      # The parent directory of a job's working directory.
      # If it's empty, $HOME/.cache/act/ will be used.
      workdir_parent:    

Create a token on forgejo

The autoscaler will continuously fetch new waiting jobs from Forgejo, so it requires a properly configured token with the appropriate permissions. Navigate to Forgejo > User settings > Applications > Access Tokens and generate a new token. This token will be used by the autoscaler

Configuring KEDA Autoscaler

Create a new custom resource with the required parameters and custom fields based on the scope chosen during runner registration. For example, set global="true" for global runners or specify repo=xxx and owner=xxx for repository level runners.

Choose the runner image you want to use (>6.1 of forgejo runner releases) or a custom one with --one-job command.

Apply the new custom resource to your cluster.

apiVersion: v1
kind: Secret
metadata:
  name: forgejo-runner-secret
  namespace: runners
data:
  token: "BEARER_TOKEN"
---
apiVersion: keda.sh/v1alpha1
kind: TriggerAuthentication
metadata:
  name: forgejo-runner-creds
  namespace: runners
spec:
  secretTargetRef:
    - parameter: token
      name: forgejo-runner-secret
      key: token
---
apiVersion: keda.sh/v1alpha1
kind: ScaledJob
metadata:
  labels:
    app: forgejo-runner
  name: upload-platform-forgejo-runner
  namespace: runners
spec:
  jobTargetRef:
    template:
      metadata:
        labels:
          app: forgejo-runner
      spec:
        volumes:
          - name: runner-data
            emptyDir: {}
          - name: runner-config
            configMap:
              name: upload-platform-runner-config
          - name: runner-registration
            configMap:
              name: upload-platform-runner-registration
          - name: docker-certs
            emptyDir: {}
        restartPolicy: Never
        shareProcessNamespace: true
        containers:
          - name: runner
            image: code.forgejo.org/forgejo/runner:6.3.1
            command:
              - sh
              - -c
              - |
                while ! nc -z localhost 2376 </dev/null; do 
                  echo 'waiting for docker daemon...'
                  sleep 5
                done

                cp /config/.runner /data/.runner

                exec forgejo-runner -c /registration/registration.yaml one-job                
            securityContext:
              privileged: true
              runAsUser: 0
            env:
              - name: DOCKER_HOST
                value: tcp://localhost:2376
              - name: DOCKER_CERT_PATH
                value: /certs/client
              - name: DOCKER_TLS_VERIFY
                value: "1"
            volumeMounts:
              - name: docker-certs
                mountPath: /certs
              - name: runner-data
                mountPath: /data
              - name: runner-registration
                mountPath: /registration
                readOnly: false
              - name: runner-config
                mountPath: /config
                readOnly: false
          - name: daemon
            image: docker:27.4.1-dind
            command:
              - sh
              - -c
              - |
                dockerd-entrypoint.sh 2>&1 &

                while kill -0 7 2>/dev/null
                  do sleep 10 
                  echo "watching main job execution"
                done;

                sleep 10

                echo "main continaer exited, stopping dind"
                exit 0                
            env:
              - name: DOCKER_TLS_CERTDIR
                value: /certs
              - name: DOCKER_HOST
                value: tcp://0.0.0.0:2376
            securityContext:
              privileged: true
              runAsUser: 0
            volumeMounts:
              - name: docker-certs
                mountPath: /certs
  minReplicaCount: 0
  maxReplicaCount: 20
  pollingInterval: 30
  triggers:
    - type: forgejo-runner
      metadata:
        name: "runner"
        address: "http://localhost:3000/"
        global: "true"
        labels: "ubuntu-20,docker"
      authenticationRef:
        name: forgejo-runner-creds