
Preview Environments für jeden Pull Request mit der Sliplane API
Jonas ScholzPreview Environments klingen aufwendig, sind aber überraschend einfach umzusetzen. Die Idee: Jeder Pull Request bekommt seine eigene Live-URL, damit Reviewer die App direkt ausprobieren können – ohne den Branch lokal auschecken zu müssen.
In diesem Tutorial baust du einen GitHub Actions Workflow, der die Sliplane API nutzt, um beim Öffnen eines PRs automatisch ein Preview Environment zu erstellen, die URL als Kommentar zu posten und alles wieder aufzuräumen, wenn der PR geschlossen wird.
So funktioniert's
Der Workflow hat zwei Hauptjobs:
- PR geöffnet → Sliplane Service aus dem PR-Branch erstellen, warten bis er läuft, URL als PR-Kommentar posten
- PR geschlossen → Service anhand des Namens finden und löschen
Das läuft alles über die Sliplane API. Jeder öffentliche Service bekommt automatisch eine Managed Domain – du hast also sofort eine Live-URL.
Voraussetzungen
- Ein Sliplane Account mit einem Server und Projekt
- Deine App muss in einem GitHub Repository liegen und via Docker deploybar sein
- Grundkenntnisse in GitHub Actions
Schritt 1: Sliplane API Token erstellen
Geh in die Sliplane Team Settings und erstell einen neuen API Token mit read-write Zugriff. Kopier ihn dir sofort – du siehst ihn nur einmal.
Schritt 2: Project ID und Server ID herausfinden
Du brauchst zwei IDs aus Sliplane:
- Project ID — sichtbar in der URL, wenn du ein Projekt im Dashboard öffnest (z.B.
project_abc123) - Server ID — sichtbar in der URL, wenn du einen Server öffnest (z.B.
server_xyz456)
Du kannst sie auch direkt über die API abrufen:
# Alle Projekte auflisten
curl https://ctrl.sliplane.io/v0/projects \
-H "Authorization: Bearer YOUR_API_TOKEN"
# Server in einem Projekt auflisten
curl https://ctrl.sliplane.io/v0/projects/PROJECT_ID/servers \
-H "Authorization: Bearer YOUR_API_TOKEN"
Schritt 3: Secret in GitHub hinterlegen
Geh in deinem Repository zu Settings → Secrets and variables → Actions und füg folgendes hinzu:
| Secret | Wert |
|---|---|
SLIPLANE_API_TOKEN | Dein Sliplane API Token |
Schritt 4: Workflow erstellen
Leg die Datei .github/workflows/preview.yml in deinem Repository an:
name: Preview Environment
on:
pull_request:
types: [opened, reopened, closed]
env:
SLIPLANE_API: https://ctrl.sliplane.io/v0
PROJECT_ID: your_project_id
SERVER_ID: your_server_id
jobs:
preview:
runs-on: ubuntu-latest
steps:
- name: Create Preview
if: github.event.action == 'opened' || github.event.action == 'reopened'
id: create-preview
run: |
PREVIEW_NAME="preview-pr-${{ github.event.number }}"
BRANCH="${{ github.head_ref }}"
RESPONSE=$(curl -s -X POST "$SLIPLANE_API/projects/$PROJECT_ID/services" \
-H "Authorization: Bearer ${{ secrets.SLIPLANE_API_TOKEN }}" \
-H "Content-Type: application/json" \
-d '{
"name": "'"$PREVIEW_NAME"'",
"serverId": "'"$SERVER_ID"'",
"deployment": {
"url": "https://github.com/${{ github.repository }}",
"branch": "'"$BRANCH"'",
"autoDeploy": true
},
"network": {
"public": true,
"protocol": "http"
}
}')
echo "service_id=$(echo "$RESPONSE" | jq -r '.id')" >> $GITHUB_OUTPUT
echo "domain=$(echo "$RESPONSE" | jq -r '.network.managedDomain')" >> $GITHUB_OUTPUT
- name: Wait for Service to be Ready
if: github.event.action == 'opened' || github.event.action == 'reopened'
run: |
SERVICE_ID="${{ steps.create-preview.outputs.service_id }}"
for i in {1..60}; do
STATUS=$(curl -s "$SLIPLANE_API/projects/$PROJECT_ID/services/$SERVICE_ID" \
-H "Authorization: Bearer ${{ secrets.SLIPLANE_API_TOKEN }}" \
| jq -r '.status')
echo "Attempt $i: Status is $STATUS"
if [ "$STATUS" = "live" ]; then
echo "Service is ready!"
break
elif [ "$STATUS" = "failed" ]; then
echo "Service failed to start"
exit 1
fi
if [ $i -eq 60 ]; then
echo "Timeout waiting for service to be ready"
exit 1
fi
sleep 5
done
- name: Comment Preview URL
if: github.event.action == 'opened' || github.event.action == 'reopened'
uses: actions/github-script@v7
with:
script: |
const url = 'https://${{ steps.create-preview.outputs.domain }}';
const body = `Preview deployed at: ${url}`;
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});
const botComment = comments.find(c => c.body.includes('Preview deployed at:'));
if (botComment) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body,
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body,
});
}
- name: Delete Preview
if: github.event.action == 'closed'
run: |
SERVICE_ID=$(curl -s "$SLIPLANE_API/projects/$PROJECT_ID/services" \
-H "Authorization: Bearer ${{ secrets.SLIPLANE_API_TOKEN }}" \
| jq -r '.[] | select(.name == "preview-pr-${{ github.event.number }}") | .id')
if [ -n "$SERVICE_ID" ]; then
curl -X DELETE "$SLIPLANE_API/projects/$PROJECT_ID/services/$SERVICE_ID" \
-H "Authorization: Bearer ${{ secrets.SLIPLANE_API_TOKEN }}"
fi
Ersetze your_project_id und your_server_id im env-Block mit deinen IDs aus Schritt 2.
Was die einzelnen Schritte machen
Create Preview — ruft POST /v0/projects/{projectId}/services auf und startet einen neuen Service aus deinem PR-Branch. Mit autoDeploy: true deployed Sliplane automatisch neu, wenn neue Commits auf den Branch gepusht werden. Die Response enthält eine managedDomain (z.B. preview-pr-42.sliplane.app), die wir an die nächsten Schritte weitergeben.
Du kannst dem Service auch Environment Variables oder Volumes mitgeben – dafür gibt es die Felder env und volumes im Service-Creation-Request. Schau in der API-Referenz nach dem vollständigen Schema.
Wait for Service — pollt GET /v0/projects/{projectId}/services/{serviceId} alle 5 Sekunden, bis der Status live ist. Bei failed bricht der Schritt sofort mit einem Fehler ab, nach 5 Minuten läuft ein Timeout.
Comment Preview URL — postet (oder aktualisiert) einen Kommentar am PR mit der Live-URL. Aktualisieren statt neu erstellen hält den PR übersichtlich, falls er geschlossen und wieder geöffnet wird.
Delete Preview — listet beim Schließen des PRs alle Services im Projekt auf, findet den passenden anhand des Namens und löscht ihn via DELETE /v0/projects/{projectId}/services/{serviceId}.
Schritt 5: Pull Request öffnen
Push den Workflow in deinen Main-Branch und öffne dann einen neuen PR. Im Actions-Tab siehst du den Workflow starten, und nach ein paar Minuten erscheint ein Bot-Kommentar am PR:
Preview deployed at: https://preview-pr-42.sliplane.app
Wenn du den PR mergst oder schließt, läuft der Cleanup-Job und der Service wird automatisch gelöscht.
Fazit
Das war's – vollautomatische Preview Environments mit etwa 80 Zeilen YAML und der Sliplane API. Jeder Reviewer bekommt eine Live-URL zum Ausprobieren, und du musst dir über Aufräumen keine Gedanken machen.
In der Sliplane API-Referenz findest du noch mehr: Environment Variables, Volumes, TCP/UDP-Services und mehr. Wenn du eigene Domains statt der Managed Domains willst, geht das auch – über POST /v0/projects/{projectId}/services/{serviceId}/domains.
Cheers,
Jonas