Publish from your pipeline
Copy one job, store one token, and let your default branch hand the shared part of your project to your team.
Your project lives in a folder, and your repository already carries it. This page is about the last step: letting the pipeline that builds your default branch hand the shared part of that folder to your company catalogue, so a colleague reads the same requests as you without cloning anything.
You need two things, and the second is the only one you have to keep safe: a job to copy, and a project token to store as a variable.
What the job sends
The job packs .ryno/ into an archive and posts it to your Ryno service. What
goes in is decided before anything leaves the runner, and it is a short list:
your shared requests and the environments your team versions. Values you marked
as local are left where they are.
It runs on every pipeline of your default branch, even when nothing under
.ryno/ has changed. That is on purpose. An archive that arrives unchanged is a
sign of life, and it is what lets the service tell a project that has nothing
new to say from a pipeline that has stopped running. Only the branch matters: a
job on a working branch would publish something your team has not agreed on yet.
Add the job to your pipeline
Copy this into .gitlab-ci.yml, at the root of your repository, and put your own
service in place of https://ryno.example.com. Nothing else in it is
yours to fill in.
publish-ryno-catalog:
stage: deploy
rules:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
resource_group: ryno-publication
script:
- tar --exclude='*.env.local.json' -czf ryno-catalog.tar.gz .ryno
- curl --fail-with-body --silent --show-error --header "Authorization: Bearer $RYNO_PROJECT_TOKEN" --header "Content-Type: application/gzip" --data-binary @ryno-catalog.tar.gz "https://ryno.example.com/api/v1/publications"
Only GitLab is covered here. If you build somewhere else, the same two commands work in any job that can reach your service, and you write the surrounding syntax yourself. There is no snippet for another forge on this page, because a snippet written without being run takes longer to debug than one you wrote.
Store the token as a masked, protected variable
Ryno gives you a project token once, when you attach your project. Put it in a
CI/CD variable of your repository named RYNO_PROJECT_TOKEN, and turn on both
of the flags GitLab offers for it:
Maskedhides the value in job logs. The token is shaped to fit that flag, so GitLab accepts it as it is, with nothing to trim or re-encode.Protectedhands the variable only to jobs running on a protected branch. Since the job publishes from your default branch alone, nothing else has any business reading it.
The token opens with rynopt, which is what lets a secret scanner recognise it
if it ever ends up somewhere it should not. It says which project you publish
to, so the address in the job carries no project name at all.
What your runner needs
Two commands, and no image to pull: tar and curl. Both are on almost every
runner already, which is the point — the first thing you meet should not be an
error about an image you do not control.
One version matters: curl 7.76 or newer, for --fail-with-body. Older builds
do not know the option and stop on it. With it, a refused publication both fails
the job and prints what the service answered, which is the line that tells you
what to fix.
What is published, and what stays behind
| Published | What it holds |
|---|---|
collections/**/*.json |
Your requests, at any depth of the tree you arranged them in. |
environments/*.env.json |
The environments your team versions, so a reader can fill in the variables a request refers to. |
Everything else is left out, and it is left out by construction: a file that is not on that list has nowhere to go, even when somebody has committed it by mistake. Two cases are worth saying out loud.
A file ending in .env.local.json never leaves your machine, whichever folder
it sits in. If one turns up in an archive built by a pipeline, the receipt names
it, because that means a secret has been committed and nothing else is going to
tell you.
Flows stay out for now. They are yours and they stay in your repository; what a colleague reads from the catalogue is requests and shared environments.
When the job is refused
The service answers in plain text, one line of cause and one line of what to do, and the job prints it for you. Your token is never echoed back, not even partly, because a job log gets read, copied and pasted.
A refusal that says the project token is not valid means the value in
RYNO_PROJECT_TOKEN no longer names a project. Regenerate the token from Ryno,
then paste the new value into the variable. Regenerating draws a fresh token, so
the old one stops working the moment you ask for a new one, and every job still
holding it is refused until you update the variable.
Two other refusals read the same way. A file that your requests’ own format rejects fails the whole publication, with the name of the file and the reason: publishing the rest would leave your team with a catalogue that looks complete and is missing the request you just added. An archive that is too large, or that holds too many files, is refused with the limit it went past.
A refused job changes nothing. The version your team reads stays the one from the last publication that went through.
This project was removed
If an administrator removed your project from the company workspace, a job that still holds its token is refused with these two lines:
Refused: this project was removed from its RYNO workspace.
Refused: remove the RYNO publish job from this pipeline, or attach this repository again from RYNO and paste the new project token into the masked RYNO_PROJECT_TOKEN variable.
The token is not wrong, and regenerating it will not help: the project it named
no longer exists. You have two ways out. Delete the publish job from your
pipeline if the project is not meant to come back. Or attach the repository
again from Ryno, which creates a new project with a new token, and paste that
token into RYNO_PROJECT_TOKEN.
Remove a project says what the removal takes away and what it leaves.