Sets up CI/CD pipelines for DataRobot application templates using GitLab, GitHub Actions, and Pulumi. Includes simple path with Pulumi Cloud and GitHub Secrets, plus advanced GPG encryption.
How this skill is triggered — by the user, by Claude, or both
Slash command
/datarobot-agent-skills:datarobot-app-framework-cicdThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
This skill provides comprehensive guidance for setting up production-grade CI/CD pipelines for DataRobot application templates, including automated testing, review deployments, and continuous delivery.
QUICK_REFERENCE.mdREADME.mdexamples/github-cd-pulumi-cloud.ymlexamples/github-deploy-pulumi-cloud.ymlexamples/github-destroy-pulumi-cloud.ymlexamples/workflows-README.mdreferences/example-taskfile.yamlreferences/workflow-examples.mdscripts/decrypt-secrets.shscripts/encrypt-secrets.shscripts/github-cd.ymlscripts/github-deploy.ymlscripts/github-destroy.ymlscripts/gitlab-ci.ymlscripts/infra-README.mdscripts/pulumi-setup.shscripts/setup-github-secrets.shscripts/setup-gitlab-variables.shscripts/taskfile-snippets.yamlThis skill provides comprehensive guidance for setting up production-grade CI/CD pipelines for DataRobot application templates, including automated testing, review deployments, and continuous delivery.
Default behavior: When a user asks to "set up CI/CD" without specifying a platform or backend, always use the Simple Path below — three workflow files, two GitHub Secrets, done. Do not create infra/scripts/, do not add CI/CD tasks to infra/Taskfile.yaml, do not involve GPG encryption unless the user explicitly asks for it.
Only deviate from the simple path when the user specifies:
scripts/ and see Implementation Patternscripts/For most data scientists and AI engineers, this is all you need. No GPG encryption, no cloud storage account, no extra scripts.
What to create in the user's repository:
Copy the three workflow files to .github/workflows/:
| Source | Destination | Trigger |
|---|---|---|
examples/github-cd-pulumi-cloud.yml | .github/workflows/cd.yml | Automatic — every merge to main |
examples/github-deploy-pulumi-cloud.yml | .github/workflows/deploy-pr.yml | Manual — user picks PR branch + enters stack name (e.g. pr-42) |
examples/github-destroy-pulumi-cloud.yml | .github/workflows/destroy.yml | Manual — user enters stack name to tear down |
Create .github/workflows/README.md from examples/workflows-README.md. This is the setup guide that tells the user exactly what secrets and variables to add and how.
Tell the user to follow the setup guide in .github/workflows/README.md.
That's it. Do not add anything to infra/Taskfile.yaml or create infra/scripts/ for this path.
Required GitHub Secrets (both required — no defaults):
| Name | Kind |
|---|---|
DATAROBOT_API_TOKEN | Secret |
PULUMI_ACCESS_TOKEN | Secret |
Optional GitHub Variable (defaults to ci if not set):
| Name | Kind | Default |
|---|---|---|
PULUMI_STACK_CI_NAME | Variable | ci |
When to use the advanced approach (GPG + DIY backends) instead:
.env behind a single passphrase — only one GitHub Secret needed)The templates and scripts for all of these are in scripts/ in this skill directory. If the skill has already been propagated to the project's infra/ directory (common in downstream templates), look in infra/scripts/ instead. See the Implementation Pattern section below for full setup guidance.
| Scenario | Key files in scripts/ |
|---|---|
| Azure Blob / S3 / GCS Pulumi backend | pulumi-setup.sh, taskfile-snippets.yaml |
| GitHub Actions + GPG secrets | github-deploy.yml, github-cd.yml, encrypt-secrets.sh, setup-github-secrets.sh |
| GitLab CI/CD | gitlab-ci.yml, setup-gitlab-variables.sh |
The example workflows use uv run pulumi up --yes directly. Before copying them, check infra/Taskfile.yaml — the project may already wrap the deploy command in a task:
cat infra/Taskfile.yaml # look for 'up-yes', 'deploy', or similar tasks
| What you find | What to use in CI |
|---|---|
up-yes task | task up-yes — non-interactive, purpose-built for CI; prefer this over raw Pulumi |
deploy task (alias for up) | Avoid — typically runs pulumi up interactively; only safe in CI if you confirm it passes -y internally |
| No Taskfile or no relevant task | Keep uv run pulumi up --yes as-is |
To use task in a workflow, add an install step and swap the run command:
- name: Install Task
run: pip install go-task-bin
- name: Deploy
working-directory: infra
env:
DATAROBOT_API_TOKEN: ${{ secrets.DATAROBOT_API_TOKEN }}
PULUMI_ACCESS_TOKEN: ${{ secrets.PULUMI_ACCESS_TOKEN }}
run: |
uv sync --all-extras
task up-yes
DATAROBOT_API_TOKEN should come from a DataRobot service account — a DataRobot user created for automation, not tied to anyone's personal login. This prevents CI/CD from breaking when the engineer who originally set it up leaves the team.
To set one up: ask your DataRobot admin to create a dedicated user (e.g. [email protected]). Under that account, go to Developer Tools → API Key and generate a token. Store it as the DATAROBOT_API_TOKEN secret in GitHub.
Note: This is purely a DataRobot concept — it has no relation to Pulumi state management or backend configuration. "Service account" here just means a non-personal DataRobot user.
When implementing CI/CD for an application template, follow this structure:
Project Structure:
application-template-root/
├── infra/
│ ├── README.md # ⚠️ GENERATE THIS — tailored to the chosen CI/CD platform and Pulumi backend
│ ├── Taskfile.yaml # ⚠️ CI/CD tasks go HERE — copy from infra/scripts/taskfile-snippets.yaml
│ └── scripts/ # Copy entire scripts/ directory here
│ ├── README.md # Copy from scripts/infra-README.md
│ ├── setup-github-secrets.sh
│ ├── setup-gitlab-variables.sh
│ ├── encrypt-secrets.sh
│ ├── decrypt-secrets.sh
│ ├── pulumi-setup.sh
│ ├── gitlab-ci.yml
│ ├── github-deploy.yml
│ ├── github-cd.yml
│ ├── github-destroy.yml
│ └── taskfile-snippets.yaml
├── .env # User's secrets (never commit!)
├── .env.gpg # Encrypted secrets (commit for GitHub)
├── .gitlab-ci.yml # Copy from infra/scripts/gitlab-ci.yml
├── .github/
│ └── workflows/
│ ├── deploy.yml # Copy from infra/scripts/github-deploy.yml (PR review deploys)
│ ├── cd.yml # Copy from infra/scripts/github-cd.yml (push-to-main CD)
│ └── destroy.yml # Copy from infra/scripts/github-destroy.yml
└── Taskfile.yml # Root Taskfile — ADD ONLY one `includes` entry (see below). DO NOT add tasks here.
Key Points:
infra/README.md tailored to the chosen platform and backend — see "Generating infra/README.md" belowinfra/scripts/ directoryinfra/Taskfile.yaml — NEVER add CI/CD tasks directly to the root Taskfile.yml.env and .env.gpg stay in project rootinfra/scripts/ reference ../../.env (two levels up)Taskfile.yml gets exactly ONE addition: an includes entry pointing to ./infra/Taskfile.yaml.gitlab-ci.yml, .github/workflows/) are copied to standard locationsRoot Taskfile.yml — the only change needed:
# Add this includes block to the existing root Taskfile.yml:
includes:
infra:
taskfile: ./infra/Taskfile.yaml
dir: infra
# Tasks are then run as: task infra:encrypt-secrets, task infra:setup-github-secrets, etc.
After determining the user's CI/CD platform (GitHub/GitLab) and Pulumi backend, always create infra/README.md with content tailored to their choices. It should cover:
task infra:* commands needed to bootstraptask infra:* commands relevant to their platformdeploy.yml fires on PR open/sync (review stack), cd.yml fires on push to main (CI stack), destroy.yml is manualreview_app is manual on MR, deploy_ci fires on push to default branch, destroy_review_app is manual.env.gpg)Adjust section titles, task names, and stack-naming strategy to match what was actually configured. The README should be accurate enough that a new contributor can set up CI/CD without referring to any other document.
See references/workflow-examples.md for step-by-step examples covering GitLab CI/CD, GitHub Actions with GPG secrets, and continuous delivery setup.
Application templates use Task to simplify local development and CI/CD workflows. Task provides a unified interface for Python and TypeScript/React components.
See references/example-taskfile.yaml for a complete example.
# Install Task
pip install go-task-bin
# Install dependencies
task install
# Run linters (with fixes)
task lint
# Run linters (check only)
task lint-check
# Run tests
task test
The complete pipeline configuration lives in scripts/gitlab-ci.yml. Copy it to your repository root:
cp infra/scripts/gitlab-ci.yml .gitlab-ci.yml
Key pipeline jobs:
lint / test — run on every same-project MRreview_app — manual deploy per MR; stack name driven by the PULUMI_STACK_REVIEW_NAME CI/CD variabledeploy_ci — automatic deploy on merge to default branch; stack name driven by PULUMI_STACK_CI_NAMEdestroy_review_app — manual cleanup of review stacksPULUMI_STACK_REVIEW_NAME and PULUMI_STACK_CI_NAME must be set as plain CI/CD variables in GitLab (Settings → CI/CD → Variables). The pipeline file includes sensible defaults that project-level variables override.
The complete workflow files live in scripts/:
scripts/github-deploy.yml → copy to .github/workflows/deploy.ymlscripts/github-destroy.yml → copy to .github/workflows/destroy.ymlmkdir -p .github/workflows
cp infra/scripts/github-deploy.yml .github/workflows/deploy.yml
cp infra/scripts/github-destroy.yml .github/workflows/destroy.yml
The deploy workflow triggers on pull requests and derives PULUMI_STACK_NAME from the PULUMI_STACK_REVIEW_NAME Actions variable and the PR number. Set PULUMI_STACK_REVIEW_NAME and PULUMI_STACK_CI_NAME as repository variables (Settings → Secrets and variables → Actions → Variables tab), not secrets.
The simplest approach for managing Pulumi state:
# Install Pulumi
curl -fsSL https://get.pulumi.com | sh
# Login to Pulumi Cloud
pulumi login
# Create/select stack
pulumi stack select --create dev
# Deploy
pulumi up
CI/CD Setup: Add PULUMI_ACCESS_TOKEN to your CI/CD secrets. Get token from Pulumi Console.
For organizations that cannot use Pulumi Cloud:
# Login to Azure backend
pulumi login azblob://container-name
# Set Azure credentials
export AZURE_STORAGE_ACCOUNT=myaccount
export AZURE_STORAGE_KEY=mykey
# Login to S3 backend
pulumi login s3://bucket-name
# AWS credentials from environment
export AWS_ACCESS_KEY_ID=...
export AWS_SECRET_ACCESS_KEY=...
# Login to GCS backend
pulumi login gs://bucket-name
# GCP credentials from environment
export GOOGLE_CREDENTIALS=...
When a developer has an existing local stack (a Pulumi.<stackname>.yaml file) that was
created against a different backend than the CI/CD destination, the stack state must be
exported and re-imported before switching.
pulumi-setup.sh handles this automatically: it checks pulumi whoami --verbose for the
Backend URL and compares it with the target URL. If they differ and local stack files
exist, it offers to migrate them.
Manual migration steps (if not using the script):
# 1. Confirm current backend and stacks
pulumi whoami --verbose # note "Backend URL:"
pulumi stack ls -a # list stacks on current backend
# 2. Export each stack that exists locally (Pulumi.<name>.yaml)
pulumi stack export --stack <stackname> --file <stackname>-backup.json
# 3. Login to the new backend (set any required credentials first)
# Examples:
pulumi login # Pulumi Cloud
pulumi login azblob://my-container # Azure Blob
pulumi login s3://my-bucket # AWS S3
# 4. Create the stack in the new backend and import state
pulumi stack select --create <stackname>
pulumi stack import --file <stackname>-backup.json
# 5. Clean up the backup
rm <stackname>-backup.json
Key signals that migration is needed:
pulumi whoami --verbose shows Backend URL: file:// (local) but CI/CD uses cloud storage# List all stacks
pulumi stack ls -a
# Output:
# NAME LAST UPDATE RESOURCE COUNT
# organization/project/prod 1 day ago 15
# organization/project/staging 2 days ago 12
# organization/project/dev 1 hour ago 10
# github-pr-repo-42 3 hours ago 13
# Select and update a stack
pulumi stack select dev
pulumi up
# View stack outputs
pulumi stack output --json
# Delete a stack
pulumi stack rm review-app-123 --yes
All credentials (DataRobot API token, Pulumi access token, LLM keys, cloud storage keys) are stored in .env and committed to the repository encrypted as .env.gpg. The only secret that needs to be configured in the CI/CD system directly is CICD_SECRET_PASSPHRASE (the GPG passphrase). Non-sensitive stack name variables (PULUMI_STACK_CI_NAME, PULUMI_STACK_REVIEW_NAME) are set as plain variables, not secrets.
DATAROBOT_API_TOKEN should come from a DataRobot service account — a DataRobot user created for automation, not tied to anyone's personal login. This prevents CI/CD from breaking when the engineer who originally set it up leaves the team.
To set one up: ask your DataRobot admin to create a dedicated user (e.g. [email protected]). Under that account, go to Developer Tools → API Key and generate a token. Store it as the DATAROBOT_API_TOKEN secret in your CI/CD system.
Note: This is purely a DataRobot concept — it has no relation to Pulumi state management or backend configuration. "Service account" here just means a non-personal DataRobot user.
Run scripts/setup-github-secrets.sh for interactive setup — it sets CICD_SECRET_PASSPHRASE as a repository secret and PULUMI_STACK_CI_NAME / PULUMI_STACK_REVIEW_NAME as repository variables.
To encrypt .env for CI:
task infra:encrypt-secrets
# or: ./infra/scripts/encrypt-secrets.sh
Add the resulting .env.gpg to git. For local decryption:
task infra:decrypt-secrets
# or: ./infra/scripts/decrypt-secrets.sh
Run scripts/setup-gitlab-variables.sh for interactive setup — it sets:
CICD_SECRET_PASSPHRASE — masked, for decrypting .env.gpgGITLAB_API_TOKEN — masked, for posting MR commentsPULUMI_STACK_CI_NAME / PULUMI_STACK_REVIEW_NAME — plain variablesAlternatively configure in the UI: Project Settings → CI/CD → Variables. Mark CICD_SECRET_PASSPHRASE and GITLAB_API_TOKEN as Masked and Protected.
github-pr-{repo}-{number})Pulumi state conflicts:
Secret decryption failures:
Deployment timeouts:
Stack not found:
pulumi stack ls -aResource conflicts:
Reference implementations:
npx claudepluginhub datarobot-oss/datarobot-agent-skills --plugin datarobot-agent-skillsProvides expert guidance on Harness templates for steps, stages, pipelines; runtime inputs, expression language, and patterns like CI/CD, GitOps, Canary, Blue-Green deployments.
Generates a complete CI/CD pipeline with lint, test, build, deploy, and verify stages. Detects project type and recommends GitHub Actions, Vercel, Railway, or Docker-based deployment.
Scaffolds test + deploy CI/CD pipelines for GitHub Actions, GitLab CI, Jenkins, and targets like Vercel, Netlify, Docker after assessing user's git host and deploy setup. Teaches basics to beginners.