# Publish from CI

> Publish from GitHub Actions or Jenkins with the job's own identity token, so the job stores no secret.

Source: https://docs.telesphorepo.com/publish-from-ci/

A CI job publishes with the identity token that its CI system gives it. The job stores no secret.

You need three things: a CI file, a CI pipeline on the server, and a job that runs the file.

[Set up the project](/publish-from-a-project/#set-up-the-project) first. A Docker repository needs no `telesphorepo.toml`: its **Publish to this repository** tab shows the CI file.

## Get the CI file

When the CLI asks for a CI file at setup, choose it.

#### GitHub Actions

Choose **GitHub Actions**. The CLI writes `.github/workflows/publish.yml`.

#### Jenkins with a Jenkinsfile

Choose **Jenkinsfile**. The CLI writes `Jenkinsfile`.

#### Jenkins freestyle job

Choose **Jenkins freestyle job**. The CLI prints a build step. Keep it for [the job](#set-up-the-job).

Commit the file with `telesphorepo.toml`, and push it.

## Ask for a CI pipeline

The CI pipeline says which job may publish to which repository. Send these values to your server admin.

#### GitHub Actions

Put your GitHub repository in place of `your-org/your-app`.

```text title="Send this to your server admin"
Where the job runs   GitHub Actions
GitHub repository    your-org/your-app
Workflow file        publish.yml
Branches             main
Repositories         your-repo
```

#### Jenkins

Put your Jenkins address and the full name of your job in their places.

```text title="Send this to your server admin"
Where the job runs   Jenkins
Jenkins address      https://jenkins.example.com
Jobs                 team/app
Repositories         your-repo
```

The admin adds it as in [Add a CI pipeline](/repositories-and-access/#add-a-ci-pipeline).

## Set up Jenkins

#### GitHub Actions

GitHub Actions gives each job its token. Skip this part.

#### Jenkins: the identity token

Do this once on each Jenkins.

1. Install the plugin **OpenID Connect Provider**, under **Manage Jenkins** › **Plugins** › **Available plugins**.

2. Open **Manage Jenkins** › **Credentials**, and add a credential of the kind **OpenID Connect id token**.

3. Fill in the **ID** and the **Audience**, then save.

   ```text
   ID         telesphorepo-id-token
   Audience   https://your-server
   ```

## Set up the job

#### GitHub Actions: the run

Push to `main`. The workflow file is the job.

Done when the run shows a green check on the **Actions** tab of GitHub.

#### Jenkins with a Jenkinsfile: the job

1. Make a Pipeline job that reads the `Jenkinsfile` from your repository.

2. Click **Build Now**.

Done when the build log ends with `Finished: SUCCESS`.

#### Jenkins freestyle job: the job

1. Open your job, then click **Configure**.

2. Under **Environment**, tick **Use secret text(s) or file(s)**.

3. Under **Bindings**, click **Add**, then **Secret text**. Fill in these values.

   ```text
   Variable      OIDC_TOKEN
   Credentials   telesphorepo-id-token
   ```

4. Under **Build Steps**, click **Add build step**, then **Execute shell**.

5. Paste the build step that the CLI printed.

6. Click **Save**, then **Build Now**.

Done when the build log ends with `Finished: SUCCESS`.

## Download from a private repository in CI

The identity token can only publish. If the build also downloads from a private repository, keep a read token in a CI secret.

The **Use this repository** tab names the variables that each tool reads. Set them from the secret in the build step.

## Write the CI file by hand

The **Publish to this repository** tab shows the whole file for your build tool. These are the lines that publish.

### GitHub Actions

Add `id-token: write` to the `permissions`, and keep the others. Then install the CLI and publish after your build steps.

```yaml title=".github/workflows/publish.yml"
on:
  push:
    branches: ["main"]

permissions:
  id-token: write
  contents: read

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      # the setup step of your build tool goes here
      - run: curl -fsSL https://your-server/install.sh | sh
      - run: telesphorepo publish
```

### Jenkinsfile

Add a stage that gets the token, installs the CLI and publishes.

```groovy title="Jenkinsfile"
stage('Publish to your-repo') {
  steps {
    withCredentials([string(credentialsId: 'telesphorepo-id-token', variable: 'OIDC_TOKEN')]) {
      sh '''
        curl -fsSL https://your-server/install.sh | sh
        export PATH="$HOME/.local/bin:$PATH"
        telesphorepo publish
      '''
    }
  }
}
```

### Jenkins freestyle job

A freestyle job needs no file. Follow [Set up the job](#set-up-the-job).

## If something goes wrong

The job got no identity token

In GitHub Actions, add `id-token: write` under `permissions` in the workflow file.

In Jenkins, check the binding: the variable is `OIDC_TOKEN`, and the credential is `telesphorepo-id-token`.

The CLI refuses `OIDC_TOKEN` and names two addresses

Set the **Audience** of the Jenkins credential to your server’s address, the one in `telesphorepo.toml`.

The server refuses the job’s token

Ask your server admin to set the **Public base URL** on **Settings** › **General**. See [Set the public address](/run-the-server/#set-the-public-address).

The token is too old

The server takes a job’s token for 10 minutes only. A freestyle job gets it when the build starts.

For a longer build, use a Jenkinsfile. It gets the token only in the publish stage.

No CI pipeline matches the job, or the server cannot reach Jenkins

Ask your server admin to check the values of the CI pipeline. See [Jenkins: a freestyle job or a Jenkinsfile](/repositories-and-access/#jenkins-a-freestyle-job-or-a-jenkinsfile).

The Jenkins agent runs Windows

A Jenkinsfile from the CLI runs PowerShell on a Windows agent.

For a freestyle job, choose **Execute Windows PowerShell**, and install the CLI with `irm https://your-server/install.ps1 | iex`.

The project is in a subfolder of the git repository

Put `cd` and the subfolder before `telesphorepo publish`. The CLI prints the line.

`no telesphorepo.toml in this folder or above` in CI

Commit `telesphorepo.toml`, and push it. If git ignores the file, see [Publish from a project](/publish-from-a-project/#if-something-goes-wrong).
