CtrlK
BlogDocsLog inGet started
Tessl Logo

build-ci-migration-assistant

Assists migrating a build or CI pipeline from one system to another — Jenkins to GitHub Actions, Travis to GitLab CI, Makefile to Bazel — preserving semantics and surfacing untranslatable constructs. Use when switching CI providers, when modernizing a legacy build, or when the user pastes a Jenkinsfile and asks for the GitHub Actions equivalent.

Install with Tessl CLI

npx tessl i github:santosomar/general-secure-coding-agent-skills --skill build-ci-migration-assistant
What are skills?

100

Quality

100%

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SKILL.md
Review
Evals

Build/CI Migration Assistant

Migrating CI is a translation problem with losses. Every CI system has the same core (stages, steps, artifacts, conditions) and each has idioms that don't translate cleanly. The job is to translate what maps, and flag what doesn't.

Concept map — the common core

All CI systems implement these. Translate via the concept, not the syntax:

ConceptJenkinsGitHub ActionsGitLab CICircleCI
Pipeline fileJenkinsfile.github/workflows/*.yml.gitlab-ci.yml.circleci/config.yml
Unit of parallelismstage / paralleljobjob (in stage)job
Sequential groupingstageneeds:stage:requires:
Reusable fragmentShared library, load()Composite action / reusable workflowextends: / include:Orb / command
Run onagent { label }runs-on:tags:executor:
Conditionalwhen { ... }if:rules:when: (filter)
Env varenvironment { }env:variables:environment:
Secretcredentials()${{ secrets.X }}CI/CD variables (masked)Context
Artifact hand-offstash/unstash, archiveArtifactsupload-artifact / download-artifactartifacts:persist_to_workspace
CachePlugin-dependentactions/cachecache:save_cache/restore_cache
Triggertriggers { } / webhookson:rules: / workflow:triggers: (filters)

Step 1 — Extract the dependency graph

Before writing any target syntax, draw the DAG: what runs, what depends on what, what runs in parallel. This is the invariant. Syntax changes; the graph doesn't.

┌─ lint ──┐
checkout ──▶├─ test ──┤──▶ build ──▶ deploy-staging ──▶ deploy-prod
            └─ audit ─┘

Step 2 — Translate node-by-node through the concept map

For each node in the graph, map the source construct → concept → target construct. Most steps are just shell commands inside a wrapper — those translate 1:1.

Step 3 — Flag the untranslatables

Things that do not map cleanly — inventory and escalate:

Source featureWhy it doesn't translateWhat to do
Jenkins input step (human prompt mid-pipeline)GHA/GitLab don't pause for inputSplit into two workflows, use environment approvals
Jenkins Groovy shared librariesArbitrary code, not declarativeRewrite as composite actions / scripts; may need refactoring
Self-hosted agent with local stateCloud runners are ephemeralMove state to a cache or artifact
Implicit workspace sharing between stagesGHA jobs get fresh filesystemsExplicit upload-artifact/download-artifact
Polling triggers (pollSCM)Push-based systems don't pollUse webhook triggers (on: push) — usually what you wanted anyway
Jenkins post { always/failure/success }GHA has if: always() per step, not per stage blockDuplicate the post-step on each job with if: guards

Worked example

Source (Jenkinsfile):

pipeline {
  agent any
  stages {
    stage('Test') {
      steps { sh 'npm ci && npm test' }
    }
    stage('Build') {
      when { branch 'main' }
      steps {
        sh 'npm run build'
        archiveArtifacts artifacts: 'dist/**'
      }
    }
  }
  post {
    failure { slackSend channel: '#ci', message: "Build failed: ${env.BUILD_URL}" }
  }
}

DAG: Test → Build (main only), plus a failure notification.

Target (GitHub Actions):

name: CI
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci && npm test

  build:
    needs: test
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci && npm run build
      - uses: actions/upload-artifact@v4
        with:
          name: dist
          path: dist/

  notify-failure:
    needs: [test, build]
    if: failure()
    runs-on: ubuntu-latest
    steps:
      - run: |
          curl -X POST ${{ secrets.SLACK_WEBHOOK }} \
            -d '{"text":"Build failed: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"}'

Flagged: The post { failure } became a separate job — Jenkins runs post in the same agent with the workspace intact; here it's a fresh runner. If the Slack message needed build artifacts, they'd have to be uploaded first. Also: npm ci runs twice now (once per job — fresh filesystems). Could consolidate test+build into one job, or cache node_modules.

Do not

  • Do not translate line-by-line. Translate through the concept map. A stage is not always a job.
  • Do not silently drop untranslatable features. Every Groovy shared library call you skip is a behavior the new pipeline is missing.
  • Do not assume the old pipeline was correct. Migration is a good time to delete steps nobody remembers the purpose of — but ask first.
  • Do not migrate and switch over in one step. Run both pipelines in parallel for a few builds; diff the results.

Output format

## Dependency graph (extracted)
<ascii dag>

## Translated pipeline
<target file path>
<code block>

## Untranslated / degraded
| Source construct | Issue | Proposed workaround |
| ... | ... | ... |

## Verification plan
- Run both pipelines on the same commit; diff: <what to compare>
Repository
santosomar/general-secure-coding-agent-skills
Last updated
Created

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.