ci
pfy ci update writes a CI pipeline and a Dockerfile into your repository from templates embedded in the CLI. It reads project.yaml for the language, registry, and image name, so after pfy init it usually needs no flags. The flags and the jobs each generated pipeline contains are below.
Synopsis
pfy ci <subcommand> [flags]Description
The templates are embedded in the CLI binary. Which jobs you get depends on ci.type (github or gitlab) and ci.language (go or node); the two languages produce different pipelines, see Supported Platforms.
The command reads its defaults from project.yaml (created with pfy init), so you can run it without flags. Re-running it overwrites the generated files.
Subcommands
update- Generate CI/CD pipeline templates
ci update
Generate CI/CD pipeline templates for your project.
Usage
pfy ci update [--type <platform>] [--language <lang>] [--output <directory>] [flags]Configuration Priority
Values are resolved in this order:
- CLI flags (highest priority)
project.yamlfile- Auto-detection (e.g., from
go.mod) - Defaults (lowest priority)
With project.yaml:
# Run `pfy init` once
pfy init --name my-app
# Then just run
pfy ci updateWithout project.yaml:
pfy ci update --type github --language go --project-name my-app --output .githubFlags
| Flag | Default | Description |
|---|---|---|
--type | (varies) | CI/CD platform type (github or gitlab) - resolved as flag > project.yaml > github |
--language | (detect) | Project language (go or node) - auto-detected |
--project-name | (detect) | Project name - auto-detected from directory |
--registry | (varies) | Docker registry URL - resolved as flag > project.yaml > ghcr.io |
--image-name | (detect) | Docker image name - defaults to project name |
--output | (varies) | Output directory path for the CI templates |
--with-dependabot | false | Generate Dependabot/Renovate configuration |
Default output paths:
- GitHub:
.github - GitLab:
.(current directory)
Examples
Using project.yaml (recommended):
# Initialize project once
pfy init --name my-service --language go
# Generate templates (reads from project.yaml)
pfy ci updateGenerate GitHub Actions workflows (auto-detect language):
pfy ci update
# or explicitly
pfy ci update --type githubCreates:
.github/workflows/ci.yml- Main CI pipeline.github/workflows/release.yml- Release workflowDockerfile- Optimized Dockerfile for your language
Generate with dependency management:
pfy ci update --with-dependabotAdditionally creates:
.github/dependabot.yml- Automated dependency updates
Generate for specific language:
pfy ci update --language node --project-name my-apiGenerate GitLab CI configuration:
pfy ci update --type gitlab --with-dependabotCreates:
.gitlab-ci.yml- GitLab CI pipelinerenovate.json- Renovate Bot configuration (if --with-dependabot)Dockerfile- Optimized Dockerfile for your language
Custom registry and image name:
pfy ci update --registry docker.io --image-name myorg/myappOutput
Created CI workflow: .github/workflows/ci.yml
Created release workflow: .github/workflows/release.yml
Created Dockerfile: Dockerfile
CI/CD templates generated successfully!
Language: go
Project: my-project
Registry: ghcr.io
Image: ghcr.io/my-projectSupported Platforms
GitHub Actions
Generates workflow files in .github/workflows/ and optional configuration:
ci.yml - Main CI Pipeline (push to main and pull requests):
| Job | Go | Node.js | What it does |
|---|---|---|---|
gitleaks | yes | yes | Secret scanning over the full git history (fetch-depth: 0) |
lint-and-test | yes | yes | golangci-lint + go test -race with coverage, or npm run lint + npm test -- --coverage |
deployment-drift | yes | yes | Runs pfy deploy diff --env $PFY_ENV --exit-code --ci. Skipped until you set the PFY_ENV repository variable and the PFY_MANAGER_URL / PFY_MANAGER_TOKEN secrets |
build-and-scan | no | yes | Docker build tagged with the project.yaml version, scanned by Trivy (fails on CRITICAL/HIGH) |
version-and-push | no | yes | On main only: pfy version bump --part patch, commit project.yaml with [skip ci], push a linux/amd64,linux/arm64 image tagged <version> and latest |
Node.js version-and-push does not install pfy
The template's "Install pfy CLI" step sits inside a Go-only block, so the Node.js job calls pfy on a runner that does not have it. Add an install step yourself; see Auto-Versioning.
release.yml - Release Workflow (on v* tags):
- Go: GoReleaser (
goreleaser release --clean) builds and publishes the release, using the repository's.goreleaser.ymlif there is one. - Node.js: builds and pushes a multi-arch image tagged with the git tag and
latest, runsnpm run build, and creates a GitHub release withdist/**attached.
GitLab CI/CD
Generates .gitlab-ci.yml with these stages:
| Stage | Go | Node.js | What it does |
|---|---|---|---|
security | yes | yes | Gitleaks (gitleaks detect --no-git, which scans the checked-out tree, not the history) |
test | yes | yes | lint and test jobs with coverage, plus the deployment-drift job (runs only when PFY_ENV is set) |
build | no | yes | docker build tagged with the project.yaml version, saved as an artifact |
scan | no | yes | Trivy on the built image (fails on HIGH/CRITICAL) |
deploy | no | yes | On main: bump patch version, commit with [skip ci], re-tag and push <version> and latest. On tags: push the tag. The job does not install pfy (only a comment marks where to) |
Optional: renovate.json for automated dependency updates (with --with-dependabot)
Template Features
Multi-Language Support
Templates automatically adapt based on detected or specified language:
Go Projects:
- golangci-lint for code quality
go testwith race detection and coverage- GoReleaser for multi-platform binary releases
- A single-stage Alpine Dockerfile that copies a pre-built binary from
${TARGETOS}/${TARGETARCH}/app(the layout GoReleaser produces) and runs it as a non-root user. It does not compile the code itself.
Node.js Projects:
- npm-based linting and testing
- Code coverage with standard npm test
- npm build for production releases
- Multi-stage Node.js Alpine Dockerfile:
npm ci,npm run build, runsdist/index.jsas a non-root user on port 3000 with an HTTP health check on/health
Security Features
Gitleaks Secret Scanning:
- Runs on every push and pull request
- GitHub: scans the whole git history (
fetch-depth: 0); GitLab: scans the checked-out files only (--no-git)
Trivy Vulnerability Scanning (Node.js pipelines):
- Scans the built Docker image
- Fails the job on CRITICAL or HIGH severity findings
Build Features (Node.js pipelines)
- Multi-platform image push:
linux/amd64andlinux/arm64 - GitHub Actions build cache (
type=gha) - Automatic patch bump and image push on
main
Dependency Management (Optional)
Dependabot (GitHub):
- Weekly automated dependency updates
- Separate tracking for language packages, Docker, and GitHub Actions
- Configurable pull request limits
- Custom commit message prefixes
Renovate (GitLab):
- Automated dependency updates with automerge rules
- Minor/patch updates auto-merged
- Major updates require manual review
- Security updates prioritized and auto-merged
Workflow Examples
Initial CI Setup for GitHub
# Navigate to your project
cd my-project
# Generate CI/CD templates
pfy ci update
# Or with dependency management
pfy ci update --with-dependabot
# Review the generated files
ls -la .github/workflows/
cat .github/workflows/ci.yml
# Commit to repository
git add .github/ Dockerfile
git commit -m "Add CI/CD workflows"
git pushInitial CI Setup for GitLab
# Navigate to your project
cd my-project
# Generate GitLab CI configuration
pfy ci update --type gitlab --with-dependabot
# Review the generated files
cat .gitlab-ci.yml
cat renovate.json
# Commit to repository
git add .gitlab-ci.yml renovate.json Dockerfile
git commit -m "Add GitLab CI/CD pipeline"
git pushMulti-Registry Setup
# For Docker Hub
pfy ci update --registry docker.io --image-name myorg/myapp
# For AWS ECR
pfy ci update --registry 123456789.dkr.ecr.us-east-1.amazonaws.com --image-name myapp
# For custom registry
pfy ci update --registry registry.example.com --image-name company/projectTemplate Customization
The generated files are plain YAML; edit them as you like. Remember that pfy ci update overwrites them.
Common Customizations
There is no flag for the Go or Node.js version. The templates always write Go 1.25.5 and Node 20. Edit the generated file:
# .github/workflows/ci.yml
go-version: "1.25.5" # Go
node-version: "20" # Node.jsAdd more Docker platforms:
# In .github/workflows/ci.yml, modify platforms:
platforms: linux/amd64,linux/arm64,linux/arm/v7Use a different registry:
# Regenerate with custom registry
pfy ci update --registry registry.example.com --image-name myappCustomize the build context:
# Modify context path if Dockerfile is in subdirectory:
context: ./services/apiBest Practices
Security
- Use the generated Gitleaks configuration to prevent secret leaks
- Let Trivy fail builds on critical vulnerabilities
- Store registry credentials as CI/CD secrets, never in code
- Enable Dependabot/Renovate for automated security updates
- Review dependency update PRs before merging
Docker Images
- Keep base images minimal (Alpine-based images are generated)
- Tag images with semantic versions
- Use registry-specific credentials and access controls
- Enable Docker layer caching for faster builds
CI/CD Workflows
- Run tests before building Docker images
- Use separate workflows for CI and releases
- Only push images from main branch or tags
- Enable branch protection requiring CI to pass
- Review generated templates before committing
Dependency Management
- Use
--with-dependabotfor automated updates - Configure automerge for minor/patch updates
- Manually review major version updates
- Keep CI/CD workflows and Docker base images updated
- Monitor security advisories
Updating Existing CI
Re-running pfy ci update regenerates the template files, overwriting local changes. Node.js pipelines also bake the current project.yaml version into the build job, so regenerate after a manual pfy version set:
# Update GitLab CI configuration
pfy ci update --type gitlab
# Review changes
git diff .gitlab-ci.yml
# Commit if satisfactory
git add .gitlab-ci.yml
git commit -m "Update CI/CD pipeline template"
git pushSee Also
- Configuration - CLI configuration and authentication
- Installation - Installing the CLI
- Quick Start - Getting started guide