CI/CD from scratch
Build a full CI/CD pipeline from zero using DojOps plan and apply.
Difficulty: Intermediate Duration: 35 minutes What you’ll build: A complete CI/CD pipeline for a Node.js app — GitHub Actions workflow, multi-stage Dockerfile, and Docker Compose config — generated from a single high-level goal and executed with per-step approval.
What you’ll learn
- How the task planner decomposes a goal into a dependency-aware task graph
- Why topological execution order matters and how it works in practice
- How to review and validate a plan before any files are touched
- How to use the interactive approval workflow to control each generated file
- How to verify the audit trail and confirm chain integrity after execution
Prerequisites
- Node.js >= 20 (nodejs.org )
- Git installed
- DojOps installed:
npm i -g @dojops/cli - An API key from a supported LLM provider
This tutorial starts from a completely empty project — no CI, no Docker, nothing. Starting from zero lets you see the full planning and execution flow without any existing context getting in the way.
Workshop steps
Step 1: Install DojOps and create a project
If you haven’t installed DojOps yet:
npm i -g @dojops/cliVerify the version:
dojops --version@dojops/cli v1.1.6Create a fresh Node.js project. This represents the bare minimum: an app that builds and runs, but has zero DevOps infrastructure:
mkdir my-node-app && cd my-node-app
git init
npm init -yAdd minimal scripts to package.json so the CI workflow has something real to run:
{
"name": "my-node-app",
"version": "1.0.0",
"scripts": {
"build": "echo 'Build complete'",
"test": "echo 'All tests passed'",
"lint": "echo 'No lint errors'",
"start": "node index.js"
}
}Create a minimal entry point:
echo 'console.log("my-node-app running")' > index.jsConfigure your LLM provider:
dojops provider add openai --token sk-...Your project now has exactly two files: package.json and index.js. Everything that follows starts from here.
Step 2: Initialize
Run dojops init so DojOps understands what it’s working with:
dojops initInitializing DojOps...
Languages detected:
JavaScript
CI/CD platforms:
(none detected)
Container files:
(none detected)
DevOps files found:
.gitignore
package.json
Recommended agents:
github-actions-specialist
dockerfile-specialist
Project context saved to .dojops/context.jsonDojOps correctly identifies this as a bare project. No CI, no containers, no infrastructure. That context gets saved to .dojops/context.json — every subsequent command reads it, so the LLM always knows the current state of your project rather than making assumptions.
Step 3: Plan the CI/CD pipeline
This is where the real work starts. Instead of running separate commands for each file, you give DojOps a high-level goal and let the planner decompose it:
dojops plan "Set up CI/CD for a Node.js app with GitHub Actions, Docker, and Docker Compose"Planning: "Set up CI/CD for a Node.js app with GitHub Actions, Docker, and Docker Compose"
Analyzing goal...
Identifying required skills...
Building task graph...
Task Graph (3 tasks):
1. [github-actions] Create GitHub Actions CI workflow
Skill: github-actions
Output: .github/workflows/ci.yml
Dependencies: none
2. [dockerfile] Create multi-stage Dockerfile
Skill: dockerfile
Output: Dockerfile
Dependencies: none
3. [docker-compose] Create Docker Compose for local development
Skill: docker-compose
Output: docker-compose.yml
Dependencies: [2] dockerfile
Execution order (topological sort):
Phase 1 (parallel): github-actions, dockerfile
Phase 2 (serial): docker-compose
Estimated files: 3
Plan saved to .dojops/plans/plan-a1b2c3d4.jsonThe planner figured out that Docker Compose depends on the Dockerfile — you can’t reference a Docker image in Compose before defining how to build it. Tasks 1 and 2 have no dependencies, so they can run in parallel. Task 3 waits for Task 2. That’s topological sort in practice.
The plan is saved locally. Nothing has been written to your project yet.
Step 4: Validate before executing
Review the plan before touching any files:
dojops validateValidating plan plan-a1b2c3d4...
Task 1: github-actions
Skill: github-actions
Output: .github/workflows/ci.yml
Writes: .github/workflows/ (directory will be created)
Status: valid
Task 2: dockerfile
Skill: dockerfile
Output: Dockerfile
Writes: ./Dockerfile
Status: valid
Task 3: docker-compose
Skill: docker-compose
Output: docker-compose.yml
Depends: task 2 (dockerfile)
Writes: ./docker-compose.yml
Status: valid
All 3 tasks valid. No conflicts detected.Validation checks that the skill exists, the output paths are writable, and dependency ordering is consistent. Get a plain-language explanation of what the plan will actually do:
dojops explain lastPlan Explanation: plan-a1b2c3d4
This plan builds CI/CD infrastructure for a JavaScript Node.js application.
Task 1 creates a GitHub Actions workflow at .github/workflows/ci.yml. It will
run on push and pull request events, execute your npm build, test, and lint
scripts, cache node_modules to speed up subsequent runs, and test against
Node.js 20 and 22.
Task 2 creates a multi-stage Dockerfile. The first stage installs dependencies,
the second builds the application, and the third creates a minimal production
image from node:20-slim. The final image runs as a non-root user.
Task 3 creates a docker-compose.yml that references the Dockerfile from Task 2.
It exposes port 3000, mounts a volume for development hot-reloading, and sets
up environment variables for NODE_ENV.
3 tasks, 3 files to create.explain translates the task graph into plain English. This is the moment to catch anything you didn’t intend before execution. If the explanation doesn’t match your goal, refine the prompt and re-plan.
Step 5: Execute with approval workflow
Apply the plan. DojOps runs tasks in topological order, pausing at each step for your approval:
dojops applyExecuting plan plan-a1b2c3d4...
Phase 1 (parallel execution)
──────────────────────────────
[1/3] github-actions: Create GitHub Actions CI workflow
Generating...done
Preview of .github/workflows/ci.yml:
- Triggers: push, pull_request (main branch)
- Matrix: Node.js 20, 22
- Steps: checkout, setup-node, npm ci, build, test, lint
- Caching: node_modules via actions/cache
- Security: npm audit on each run
Apply this file? (y/n/diff): diffType diff to see the full file content before deciding:
Apply this file? (y/n/diff): y
Created: .github/workflows/ci.yml
[2/3] dockerfile: Create multi-stage Dockerfile
Generating...done
Preview of Dockerfile:
- Stage 1 (deps): node:20.11.1-slim, npm ci --only=production
- Stage 2 (builder): full build, runs npm run build
- Stage 3 (runner): node:20.11.1-slim, non-root user, EXPOSE 3000
Apply this file? (y/n/diff): y
Created: Dockerfile
Phase 2 (serial execution)
──────────────────────────────
[3/3] docker-compose: Create Docker Compose config
Generating...done (using Dockerfile from task 2)
Preview of docker-compose.yml:
- Service: app (builds from ./Dockerfile)
- Port mapping: 3000:3000
- Volume: .:/app (development hot reload)
- Environment: NODE_ENV=development
Apply this file? (y/n/diff): y
Created: docker-compose.yml
────────────────────────────────────────
Plan executed successfully.
Files created: 3
Files modified: 0
Duration: 28sThe diff option is your escape hatch. Use it before approving anything you’re unsure about. For automated pipelines where interactive prompts aren’t practical, use --yes to approve everything automatically:
dojops apply --yesStep 6: Inspect the generated files
Before accepting the output, read what was actually generated. Start with the workflow:
cat .github/workflows/ci.ymlname: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20, 22]
steps:
- uses: actions/checkout@v4
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Test
run: npm test
- name: Lint
run: npm run lint
- name: Security audit
run: npm audit --audit-level=highNode version matrix, dependency caching, security audit — all present. Check the Dockerfile:
cat Dockerfile# Stage 1: Install dependencies
FROM node:20.11.1-slim AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
# Stage 2: Build
FROM node:20.11.1-slim AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
# Stage 3: Production runner
FROM node:20.11.1-slim AS runner
WORKDIR /app
RUN addgroup --system nodejs && adduser --system --ingroup nodejs nextjs
COPY --from=builder /app/dist ./dist
COPY --from=deps /app/node_modules ./node_modules
USER nextjs
EXPOSE 3000
CMD ["node", "dist/index.js"]Pinned base image, multi-stage build, non-root user. These aren’t cosmetic — they directly reduce your attack surface and prevent subtle breakage from floating tags.
Step 7: Verify the results
Run the quality check to see where you stand:
dojops checkDevOps Quality Check
Score: 78/100 (Good)
Findings:
info Consider adding a SECURITY.md
info Consider adding Dependabot or Renovate for dependency updates
info Consider adding a Makefile for common tasks
Missing files:
SECURITY.md
CODEOWNERS
renovate.json or dependabot.ymlFrom 0 to 78. The critical CI/CD and container infrastructure is in place. What remains is informational — nothing that will get you paged at 2am.
Check the audit trail:
dojops history list◇ Plans (2) ────────────────────────────────────────────────────────────╮
│ │
│ plan-f7a2b1c3 COMPLETED 3/20/2026 Set up CI/CD pipeline for │
│ Node.js with Docker │
│ plan-c4d5e6f7 COMPLETED 3/20/2026 Add Docker Compose for local │
│ development │
│ │
├────────────────────────────────────────────────────────────────────────╯Verify the chain hasn’t been tampered with:
dojops history verifyAudit chain integrity: verified
Entries: 6
Chain status: valid (all hashes match)Each entry links to the previous via a SHA-256 hash. If any entry were modified after the fact, verification would fail. This is what makes the audit log useful for compliance — not just a log file anyone can edit.
Try it yourself
These challenges go beyond the workshop steps. Try them without guidance:
Challenge 1 — Add Kubernetes. Run dojops plan "Add Kubernetes deployment and service manifests for this Node.js app". Watch how the planner builds a dependency graph when a Namespace must exist before a Deployment can reference it.
Challenge 2 — Push the score above 85. The quality check identified three info items. Use DojOps to generate SECURITY.md and renovate.json. Run dojops check after each addition and watch the score climb.
Challenge 3 — Replay mode. Run dojops apply --replay exec-001. DojOps replays the exact generation from the audit log using temperature: 0. Compare the output to the original — deterministic generation means you get the same file every time.
Troubleshooting
dojops plan exits with “no skills matched”
The planner couldn’t map your goal to any of the 38 built-in skills. Be more specific: include the tool names explicitly (e.g., “GitHub Actions” instead of “CI/CD”). Run dojops skills list to see what’s available.
dojops validate reports a dependency conflict
Two tasks write to the same path, or a dependency was declared but the referenced task ID doesn’t exist. Run dojops explain last to read the task graph — the conflict is usually visible in plain text. Re-plan with a more precise goal.
dojops apply hangs at the approval prompt
You’re running in a non-interactive shell (CI, Docker, piped input). Add --yes to auto-approve, or use dojops apply --yes to skip interactive prompts entirely.
Generated Dockerfile doesn’t match my framework
The planner inferred your setup from package.json. If you’re using Next.js, Remix, or another framework with a custom build output, include that in your prompt: “Create a multi-stage Dockerfile for a Next.js app with standalone output.” Specificity produces better output.
What you learned
You started with two files — package.json and index.js — and ended with a quality-scored CI/CD pipeline. The planner decomposed a single sentence into a dependency-aware task graph, determined execution order automatically, and generated three production-quality files. You reviewed each one before it was written. Every operation was recorded in a hash-chained audit log you can verify independently. The quality check gave you a concrete score — 78/100 — and told you exactly what’s left to do.
Next steps
- Enhance a Next.js Project — Improve an existing project’s DevOps setup without starting over
- Security Audit & Remediation — Scan for vulnerabilities and auto-fix findings
- Task Planner — Deep dive into goal decomposition and task graphs
- Execution Engine — Policies, sandboxing, and audit trails