GitHub Actions¶
TRM runs from the command line, so it fits in any CI/CD tool. This page shows how to run it in GitHub Actions to publish a release or install one, without anyone typing answers at a prompt. The same approach works in GitLab CI, Azure Pipelines, Jenkins and similar tools.
What the pipeline needs¶
| Need | How |
|---|---|
| A runner | A GitHub-hosted runner such as ubuntu-latest, when you connect through SAP BTP. Otherwise a self-hosted runner inside your network. |
| The TRM client | npm install -g trm-client on a runner with Node.js. |
| A connection to SAP | Recommended: the BTP connection of trm-plugin-btp-dest, through an SAP BTP destination and the Cloud Connector. On a self-hosted runner, the REST connection is the simplest: it only needs trm-rest on the SAP system. RFC also works, but the runner then needs the SAP NW RFC SDK and node-rfc, see Client setup. |
| An SAP user | A technical user granted authorization object ZTRM_AUTH with activity ACTVT = 16 (Execute; see Server setup), with the authorizations to create, release and import transports. With the BTP connection, it's the user set in the destination. |
| Registry access | Recommended: GitHub Actions trusted publishing, with no token to store. Alternatively, a personal access token with the scopes the job needs. Installing public packages needs no authentication. |
Store passwords, certificates and tokens as GitHub secrets, never in the workflow file.
Reaching the SAP system¶
GitHub-hosted runners run in the cloud, outside your company network, so they can't reach an on-premise SAP system directly. There are two ways around this.
Through SAP BTP (recommended). If the SAP system is connected to SAP BTP with the SAP Cloud
Connector, install the trm-plugin-btp-dest plugin in the job and
connect with the BTP connection type. TRM reaches the system through a BTP destination, so you
can use GitHub-hosted runners and keep the SAP system closed to the internet. The BTP side needs a
one-time setup: a destination, the small trm-ssh
app and a BTP user for the pipeline.
With a self-hosted runner. Install a self-hosted runner on a machine inside your network that can reach the SAP system, and connect with REST (or RFC) as from any other computer. You then maintain the runner machine yourself.
Connecting without prompts¶
Three pieces of configuration replace the interactive questions.
The SAP connection¶
Set the connection type and its data as environment variables:
TRM_CONNECTION_TYPE:BTP,RESTorRFC.TRM_CONNECTION_ARGS: a JSON object with the connection data.
For the BTP connection (plugin installed with npm install -g trm-plugin-btp-dest):
{
"btpEmail": "trm-ci@example.com",
"btpPassword": "secret",
"cfRegion": "eu10",
"btpDestination": "SAP_DEV",
"forwardRfcDest": "NONE"
}
The BTP user can also log in with an SAP Passport instead of email and password: put the .pfx
file, base64 encoded, in the TRM_SAP_PASSPORT environment variable and its passphrase in
TRM_SAP_PASSPORT_PASSPHRASE. All arguments are described in
Connect through SAP BTP.
For a REST connection (self-hosted runner):
{
"endpoint": "https://sap-dev.example.com",
"client": "100",
"user": "TRM_CI",
"passwd": "secret",
"lang": "EN"
}
The same values can be passed as command options (--connection-type and --connection-args,
which also accepts the path of a JSON file).
The registry¶
Nothing to pass for the public registry: on a GitHub-hosted runner it's the only registry TRM
knows, so every command uses it. When the job has permissions: id-token: write, TRM
authenticates with GitHub Actions trusted publishing, with no
secret.
To use a personal access token instead, pass -A '{"token":"<your token>"}' (trm-client 13.3.0
or later; older versions also need -E public). For a private registry, also pass its endpoint
URL with -E. On a self-hosted runner where other registries are
configured, pass -R <alias> or -E <endpoint>, or TRM asks which one to use.
The prompts¶
Add --no-prompts to every command. TRM then never waits for an answer: it uses the options you
passed, applies safe defaults, and stops with an error when a decision is missing. This includes
the connection: missing connection data (for example the SAP user, or the BTP destination) stops the
command with an error naming the argument to add. The decisions
TRM can't take on its own are listed in
Publishing without prompts,
Installing without prompts and
Uninstalling without prompts.
Authenticating to the registry¶
The recommended way to authenticate from GitHub Actions is trusted publishing with OpenID Connect (OIDC): instead of a long-lived token saved as a secret, each run gets a short-lived identity token from GitHub, and the registry accepts it as the GitHub user who triggered the run.
- Link your GitHub account once: on the registry, open Settings and press Link GitHub in the GitHub account card. Everyone who starts the workflow needs a linked account with the permission to publish the package.
-
Allow the workflow to request the token, in the workflow or in the job:
That's all: when a command runs in GitHub Actions, the job can request the token and no -A
option is passed, TRM uses it automatically (trm-client 13.3.0 or later; older versions need
-A '{"oidc":"github"}', which still works).
The TRM client asks GitHub for a token issued for the registry (audience
https://trmregistry.com), sends it to the registry, and asks for a fresh one when a long run
needs it. The token works for everything a personal access token with package:all allows:
publish, install (including private packages), unpublish, deprecate and tags.
If the GitHub account isn't linked, commands that don't need authentication, such as installing public packages, still work as anonymous; the others stop with "GitHub account not linked to a TRM profile".
Use a personal access token instead when the workflow must act as a fixed account whoever starts it, for example for scheduled runs, or when the people starting it don't have a linked GitHub account.
Example: publish when a GitHub release is created¶
This workflow publishes a new version of the package every time a release is published in the
repository, on a GitHub-hosted runner. The tag of the GitHub release (for example v1.4.0)
becomes the TRM version, and the repository's README.md and CHANGELOG.md are attached to the
release. It connects to SAP through a BTP destination and authenticates to the registry as the
person who published the GitHub release.
Repository secrets used: BTP_EMAIL and BTP_PASSWORD, the BTP user of the pipeline.
name: Publish TRM package
on:
release:
types: [published]
permissions:
contents: read
id-token: write # lets TRM authenticate to the registry with GitHub OIDC
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- name: Install TRM client and BTP plugin
run: npm install -g trm-client trm-plugin-btp-dest
- name: Publish
env:
TRM_CONNECTION_TYPE: BTP
TRM_CONNECTION_ARGS: >-
{"cfRegion": "eu10", "btpDestination": "SAP_DEV", "forwardRfcDest": "NONE",
"btpEmail": ${{ toJSON(secrets.BTP_EMAIL) }},
"btpPassword": ${{ toJSON(secrets.BTP_PASSWORD) }}}
run: |
VERSION="${GITHUB_REF_NAME#v}"
trm publish @acme/sales-tools "$VERSION" \
--no-prompts \
--sap-package ZACME_SALES \
--public \
-T QAS \
--readme README.md \
--changelog CHANGELOG.md
toJSON(...)puts each secret in the JSON as a correctly quoted string, even if it contains quotes or backslashes.--sap-packageand--public(or--private) are needed only for the first publish. Later releases reuse them.-Tis the transport target of the publish transports; it can be left out when the system has only one.- Everything else (description, license, dependencies, customizing...) is taken from the previous
release, see What TRM reuses from the previous release.
To stop carrying over customizing transports of the previous release, pass
--drop-cust <transports>(comma separated) or--drop-cust all.
With a self-hosted runner and REST, change runs-on: ubuntu-latest to runs-on: self-hosted,
install only trm-client, and set the connection as in The SAP connection:
env:
TRM_CONNECTION_TYPE: REST
TRM_CONNECTION_ARGS: ${{ secrets.SAP_DEV_CONNECTION }} # the REST JSON shown above
With a personal access token, drop id-token: write, store the token as the TRM_TOKEN
secret, add TRM_TOKEN: ${{ secrets.TRM_TOKEN }} to env, and add
-A "{\"token\":\"$TRM_TOKEN\"}".
Example: install the locked release on a test system¶
This workflow installs the package on a test system, on demand. It uses a lockfile committed in the repository, so the test system gets exactly the release and the dependency versions recorded in it, every time.
name: Install on test system
on:
workflow_dispatch:
permissions:
contents: read
id-token: write # only needed for private packages
jobs:
install:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- name: Install TRM client and BTP plugin
run: npm install -g trm-client trm-plugin-btp-dest
- name: Install package
env:
TRM_CONNECTION_TYPE: BTP
TRM_CONNECTION_ARGS: >-
{"cfRegion": "eu10", "btpDestination": "SAP_TEST", "forwardRfcDest": "NONE",
"btpEmail": ${{ toJSON(secrets.BTP_EMAIL) }},
"btpPassword": ${{ toJSON(secrets.BTP_PASSWORD) }}}
run: |
trm clean-install @acme/sales-tools \
--no-prompts \
--lock-file trm-lock.json \
--package-replacements '[{"originalDevclass":"ZACME_SALES","installDevclass":"ZTEST_SALES"}]'
- Without a version,
trm clean-installinstalls the release recorded in the lockfile. If you pass a version, it must match the lockfile, or the command stops. - To move the test system to a new release, regenerate the lockfile with
trm lockon a system where that release is installed, and commit it. - For a public package, the
id-tokenpermission can go: anyone can download it. - Use
trm installwithout a lockfile instead to get the newest compatible versions.
Keep approvals where they belong¶
A pipeline should automate the repeatable mechanics, not the decision to go live:
- Publish a versioned release.
- Install it on a non-production system, and run your tests.
- Approve it with your organization's usual process.
- Move the landscape transport through quality and production along the normal transport route.
Give the pipeline's SAP user, BTP user and registry credentials only the permissions their job needs, and use separate credentials for publishing and installing. GitHub environments can require a manual approval before a job that touches a sensitive system.
Troubleshooting¶
- The job hangs: a value is missing and TRM is waiting for an answer. Check that
--no-promptsis on every command: without it, TRM asks for anything it doesn't know. - "... is required ... when interactive prompts are disabled": pass the option or connection
argument named in the error. The first publish of a package needs
--sap-packageand--public/--private; connection data goes inTRM_CONNECTION_ARGS. - "Transport target was not declared": the system has more than one transport target; pass
-T(publish) or--landscape-tr-target(install, uninstall). - "Unauthorized" when publishing: the job can't request the GitHub identity token, so TRM ran
without authentication. Add
permissions: id-token: writeto the workflow or job. (With an explicit-A '{"oidc":"github"}'the error is "GitHub Actions OIDC token is not available".) - "GitHub account not linked to a TRM profile": the user who started the run hasn't linked a GitHub account.
- "Invalid connection type "BTP"": install
trm-plugin-btp-destin the samenpm install -gstep astrm-client. - BTP login or tunnel errors: see the one-time setup
(Space Developer role, SSH enabled for
trm-ssh, user without two-factor authentication). - Detailed logs: add
--debug, or use--logger CLI_LOG --logger-out-dir <folder>to also write the log to a file you can upload as a workflow artifact.