# Advanced usage ## Advanced YAML syntax YAML has some advanced syntax features that can be used like variables to reduce duplication in your pipeline config: ### Anchors & aliases You can use [YAML anchors & aliases](https://yaml.org/spec/1.2.2/#3222-anchors-and-aliases) as variables in your pipeline config. To convert this: ```yaml steps: - name: test image: golang:1.18 commands: go test ./... - name: build image: golang:1.18 commands: build ``` Just add a new section called **variables** like this: ```diff +variables: + - &golang_image 'golang:1.18' steps: - name: test - image: golang:1.18 + image: *golang_image commands: go test ./... - name: build - image: golang:1.18 + image: *golang_image commands: build ``` ### Map merges and overwrites ```yaml variables: - &base-plugin-settings target: dist recursive: false try: true - &special-setting special: true - &some-plugin codeberg.org/6543/docker-images/print_env steps: - name: develop image: *some-plugin settings: <<: [*base-plugin-settings, *special-setting] # merge two maps into an empty map when: branch: develop - name: main image: *some-plugin settings: <<: *base-plugin-settings # merge one map and ... try: false # ... overwrite original value ongoing: false # ... adding a new value when: branch: main ``` ### Sequence merges ```yaml variables: pre_cmds: &pre_cmds - echo start - whoami post_cmds: &post_cmds - echo stop hello_cmd: &hello_cmd - echo hello steps: - name: step1 image: debian commands: - <<: *pre_cmds # prepend a sequence - echo exec step now do dedicated things - <<: *post_cmds # append a sequence - name: step2 image: debian commands: - <<: [*pre_cmds, *hello_cmd] # prepend two sequences - echo echo from second step - <<: *post_cmds ``` ### References - [Official YAML specification](https://yaml.org/spec/1.2.2/#3222-anchors-and-aliases) - [YAML cheat sheet](https://learnxinyminutes.com/docs/yaml) ## Persisting environment data between steps One can create a file containing environment variables, and then source it in each step that needs them. ```yaml steps: - name: init image: bash commands: - echo "FOO=hello" >> envvars - echo "BAR=world" >> envvars - name: debug image: bash commands: - source envvars - echo $FOO ``` ## Declaring global variables As described in [Global environment variables](./50-environment.md#global-environment-variables), you can define global variables: ```ini WOODPECKER_ENVIRONMENT=first_var:value1,second_var:value2 ``` Note that this tightly couples the server and app configurations (where the app is a completely separate application). But this is a good option for truly global variables which should apply to all steps in all pipelines for all apps. ## Docker in docker (dind) setup :::warning This set up will only work on trusted repositories and for security reasons should only be used in private environments. See [project settings](./75-project-settings.md#trusted) to enable "trusted" mode. ::: The snippet below shows how a step can communicate with the docker daemon running in a `docker:dind` service. :::note If your goal is to build/publish OCI images, consider using the [Docker Buildx Plugin](https://woodpecker-ci.org/plugins/Docker%20Buildx) instead. ::: First we need to define a service running a docker with the `dind` tag. This service must run in `privileged` mode: ```yaml services: - name: docker image: docker:dind # use 'docker:-dind' or similar in production privileged: true ports: - 2376 ``` Next, we need to set up TLS communication between the `dind` service and the step that wants to communicate with the docker daemon (unauthenticated TCP connections have been deprecated [as of docker v27](https://github.com/docker/cli/blob/v27.4.0/docs/deprecated.md#unauthenticated-tcp-connections) and will result in an error in v28). This can be achieved by letting the daemon generate TLS certificates and share them with the client through an agent volume mount (`/opt/woodpeckerci/dind-certs` in the example below). ```diff services: - name: docker image: docker:dind # use 'docker:-dind' or similar in production privileged: true + environment: + DOCKER_TLS_CERTDIR: /dind-certs + volumes: + - /opt/woodpeckerci/dind-certs:/dind-certs ports: - 2376 ``` In the docker client step: 1. Set the `DOCKER_*` environment variables shown below to configure the connection with the daemon. These generic docker environment variables that are framework-agnostic (e.g. frameworks like [TestContainers](https://testcontainers.com/), [Spring Boot Docker Compose](https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-docker-compose) do all respect them). 2. Mount the volume to the location where the daemon has created the certificates (`/opt/woodpeckerci/dind-certs`) Test the connection with the docker client: ```diff steps: - name: test image: docker:cli # in production use something like 'docker:-cli' + environment: + DOCKER_HOST: "tcp://docker:2376" + DOCKER_CERT_PATH: "/dind-certs/client" + DOCKER_TLS_VERIFY: "1" + volumes: + - /opt/woodpeckerci/dind-certs:/dind-certs commands: - docker version ``` This step should output the server and client version information if everything has been set up correctly. Full example: ```yaml steps: - name: test image: docker:cli # use 'docker:-cli' or similar in production environment: DOCKER_HOST: 'tcp://docker:2376' DOCKER_CERT_PATH: '/dind-certs/client' DOCKER_TLS_VERIFY: '1' volumes: - /opt/woodpeckerci/dind-certs:/dind-certs commands: - docker version services: - name: docker image: docker:dind # use 'docker:-dind' or similar in production privileged: true environment: DOCKER_TLS_CERTDIR: /dind-certs volumes: - /opt/woodpeckerci/dind-certs:/dind-certs ports: - 2376 ```