What this converter does
Paste a Jenkinsfile and you get a GitHub Actions workflow plus a list of everything that did not map cleanly. It handles declarative pipelines (pipeline { }) and the common shape of scripted pipelines (node { stage { } }). Conversion runs entirely in your browser; your pipeline never leaves the page, which matters because Jenkinsfiles tend to contain internal hostnames and credential IDs.
The output is a starting point, not a finished migration. Every construct without a clean equivalent becomes a TODO step that fails loudly, so nothing silently disappears. The notes panel tells you which repository secrets to create.
How Jenkins concepts map to GitHub Actions
| Jenkins | GitHub Actions | Notes |
|---|---|---|
agent any / agent { label } | runs-on | Labels mentioning linux, windows or mac map to hosted runners; anything else becomes [self-hosted, label]. |
agent { docker { image } } | container: | Linux runners only. The image needs a shell. |
stage | job (or a group of steps) | Jenkins stages share one workspace; jobs do not. Use artifacts or the single-job layout. |
parallel { } | Sibling jobs with the same needs | Next stage fans in with needs: [a, b, c]. |
matrix { axes } | strategy.matrix | excludes become matrix.exclude. |
when { branch 'main' } | if: github.ref == 'refs/heads/main' | Globs become startsWith/contains. |
environment { } | env: | credentials('id') becomes ${{ secrets.ID }}. |
parameters { } | workflow_dispatch.inputs | params.X becomes ${{ inputs.X }}. |
triggers { cron } | on.schedule | Jenkins H has no equivalent; GitHub schedules run in UTC. |
options { timeout } | timeout-minutes | |
disableConcurrentBuilds() | concurrency: | abortPrevious becomes cancel-in-progress. |
sh / bat / powershell | run: | With shell: cmd or pwsh where needed. |
withCredentials | env: from secrets.* | Username/password pairs become two secrets. |
archiveArtifacts, stash/unstash | actions/upload-artifact / download-artifact | |
junit | mikepenz/action-junit-report | Needs permissions: checks: write. |
retry(n) | nick-fields/retry | |
timeout(...) { } | step timeout-minutes | |
dir('x') { } | working-directory | Actions (uses:) ignore it; pass a path input instead. |
post { always / success / failure } | steps with if: always() etc. | Pipeline-level post becomes a final job that needs everything. |
input | Protected environment: | Required reviewers replace manual approval steps. |
${env.BUILD_NUMBER}, ${BRANCH_NAME} … | github.run_number, github.ref_name … | Twenty-odd built-in variables are mapped. |
script { }, shared libraries, plugins | TODO step | Simple if/try/def x = sh(returnStdout) are converted; the rest is left for you, verbatim, in a comment. |
Why one job per stage is the default
It keeps the shape of your pipeline visible in the Actions graph and lets parallel stages and per-stage agents map directly. The cost is that each job starts from a clean runner, so a Build job cannot see what Checkout produced. If your stages pass files to each other, either switch to the single-job layout (stages become named steps in one job) or add upload-artifact/download-artifact between the jobs. Both layouts are offered above; pick the one that needs fewer edits.
Things that never convert automatically
- Shared library calls (
@Library, custom steps). You will see them as TODO steps with the original call quoted. - Groovy logic beyond simple branches: loops, maps, string manipulation. Move it into a script in your repository and call that from a
runstep. - Plugin steps such as
emailext,slackSend,withSonarQubeEnv. The notes name the Marketplace action that usually replaces each one. - UNSTABLE builds. Actions has success, failure, cancelled and skipped; there is no yellow.
FAQ
Is my Jenkinsfile uploaded anywhere?
No. The page is static and the converter is a single JavaScript file that runs in your tab. There is no backend to send anything to. You can check the network tab, or read convert.js on GitHub.
How does this compare with GitHub's Actions Importer?
GitHub Actions Importer is a CLI that connects to your Jenkins server and converts pipelines in bulk, including plugin configuration it can read from the server. It is the right tool for a large estate. This page is for the moment you have one Jenkinsfile in front of you and want a workflow to start from without installing anything or connecting a server.
The output has errors in it.
Every generated workflow is checked against actionlint in this tool's test suite, but your Jenkinsfile may hit a shape the parser does not understand. Email the Jenkinsfile (redact what you need to) and it will become a test case.
Migrating a whole organisation?
The same converters ship as a command-line audit. Point it at a GitHub organisation, a GitLab group, an Azure DevOps organisation, a Bitbucket workspace or a Jenkins server and it inventories every CI configuration it finds, converts what it can, estimates the effort per repository and lists the secrets to create. With --open-prs it opens a pull request in each GitHub repository with the workflow and the notes. Nothing is sent anywhere except the platform you point it at.
GITHUB_TOKEN=… npx jenkins-to-actions audit --github-org acme # report + workflows GITHUB_TOKEN=… npx jenkins-to-actions audit --github-org acme --open-prs # one PR per repository GITLAB_TOKEN=… npx jenkins-to-actions audit --gitlab-group acme/platform AZURE_DEVOPS_TOKEN=… npx jenkins-to-actions audit --azure-org acme npx jenkins-to-actions audit --bitbucket-workspace acme --user me --token APP_PASSWORD npx jenkins-to-actions audit --url https://jenkins.example.com --user me --token APITOKEN
See a real result: this pull request was opened by one command on a repository holding one config per system. The README has the details, and the migration page describes the fixed-fee audit if you would rather have it done for you. Once you are on Actions, the cost calculator and the free CI Cost Report action show what the minutes cost.