TypeScript CI/CD Workflows
Generated by typescript-ci-library, which is composed by all three TypeScript service archetypes:
typescript-rest-service-archetypetypescript-grpc-service-archetypetypescript-graphql-service-archetype
The protocol has no effect on the pipeline - all three render the same two workflow files.
Build Workflow
.github/workflows/build.yaml, shown as rendered for a project named billing-service:
name: Build
on:
push:
branches: ["**"]
pull_request:
permissions:
contents: write
id-token: write
env:
IMAGE_NAME: billing-service
APPLICATION_NAME: billing-service
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup pnpm
uses: p6m-actions/js-pnpm-setup@v1
- name: Cut Patch Version
if: github.ref_name == 'main'
id: cut-patch
uses: p6m-actions/js-pnpm-cut-tag@v1
with:
version-level: patch
- name: Build
uses: p6m-actions/js-pnpm-build@v1
# ... shared container build, release and dispatch steps
Step Chain
| # | Step | Scope | Runs on |
|---|---|---|---|
| 1 | Checkout | Shared | Always |
| 2 | Setup pnpm | TypeScript | Always |
| 3 | Cut Patch Version | TypeScript | main |
| 4 | Build | TypeScript | Always |
| 5 | Login to Artifactory Container Registry | Shared | main |
| 6 | Set up Docker Buildx | Shared | Always |
| 7 | Build and Publish Docker Image | Shared | Always |
| 8 | Make Artifacts | Shared | main |
| 9 | Create Github release | Shared | main |
| 10 | Update Application Manifest | Shared | main |
TypeScript is the plainest of the six: no extra toolchain step, no build-step overrides, no fetch-depth change.
Language-Specific Steps
Setup pnpm
- name: Setup pnpm
uses: p6m-actions/js-pnpm-setup@v1
p6m-actions/js-pnpm-setup installs Node.js and pnpm and restores the pnpm store cache.
| Input | Default | Passed by the workflow |
|---|---|---|
node-version | 18 | (default) |
install-dependencies | true | (default) - runs pnpm install |
project-path | . | (default) |
| Output | Description |
|---|---|
cache-hit | Whether the pnpm store cache was restored |
pnpm-cache-dir | Path to the pnpm store |
The generated workflow does not pin node-version, so it takes the action's default of 18. If your service targets a newer Node runtime, add node-version explicitly so CI matches the container.
Cut Patch Version
- name: Cut Patch Version
if: github.ref_name == 'main'
id: cut-patch
uses: p6m-actions/js-pnpm-cut-tag@v1
with:
version-level: patch
p6m-actions/js-pnpm-cut-tag bumps version in package.json, commits, and creates an annotated tag.
| Input | Required | Value |
|---|---|---|
version-level | Yes | patch in build.yaml; the dispatch input in cut-tag.yaml |
project-path | No | . (default) |
| Output | Description |
|---|---|
version | The new version, e.g. 1.4.2 |
tag | The created tag |
Both outputs feed the shared steps: tag becomes the Docker image tag, and version and tag name the GitHub release.
Unlike the other languages, this action does not use token-exchange. It commits and tags through p6m-actions/p6m-release-action@v2 with an explicit commit message of [skip ci] Bump version to <version>, which is what stops the push from re-triggering the build.
Build
- name: Build
uses: p6m-actions/js-pnpm-build@v1
p6m-actions/js-pnpm-build runs lint, test, and build in that order. No inputs are overridden, so all defaults apply:
| Input | Default | What runs |
|---|---|---|
run-lint | true | pnpm lint, guarded |
run-test | true | pnpm test, guarded |
run-build | true | pnpm build, guarded |
node-options | --max_old_space_size=4096 | Applied to all three |
project-path | . | Repository root |
archive-coverage | false | Coverage is not uploaded |
Outputs: none. The action's contribution is the compiled output on disk and a non-zero exit if any phase fails.
package.json scripts are the contractEach of the three phases first greps package.json for the script it is about to run and skips with a log message if it is missing. That makes the scripts, not the workflow, the real definition of what CI does:
- The archetypes render
build(tsup),test(vitest run),dev, andstart. Those two phases run. - They do not render a
lintscript, so linting logs "No lint script found in package.json. Skipping linting." Adding alintscript is what turns the gate on.
The flip side is that renaming or deleting test silently turns testing off rather than failing the build. If tests matter to you, keep the script name.
Cut Tag Workflow
.github/workflows/cut-tag.yaml:
name: Cut Tag
on:
workflow_dispatch:
inputs:
version-level:
description: "Version bump level"
required: true
default: "patch"
type: choice
options:
- patch
- minor
- major
permissions:
id-token: write
contents: write
jobs:
cut-tag:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- uses: p6m-actions/js-pnpm-setup@v1
- uses: p6m-actions/js-pnpm-cut-tag@v1
with:
version-level: ${{ inputs.version-level }}
| Input | version-level - patch, minor, or major |
| Outputs | A [skip ci] version bump commit in package.json and an annotated tag, both pushed |
Version File
package.json is the source of truth:
{
"name": "billing-service",
"version": "1.4.2"
}
Related
- CI/CD Workflows - the shared steps and the CD handoff
- JavaScript Actions - the wider pnpm action catalog
- TypeScript Setup Guide - local development environment