Skip to main content

TypeScript CI/CD Workflows

Generated by typescript-ci-library, which is composed by all three TypeScript service archetypes:

  • typescript-rest-service-archetype
  • typescript-grpc-service-archetype
  • typescript-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

#StepScopeRuns on
1CheckoutSharedAlways
2Setup pnpmTypeScriptAlways
3Cut Patch VersionTypeScriptmain
4BuildTypeScriptAlways
5Login to Artifactory Container RegistrySharedmain
6Set up Docker BuildxSharedAlways
7Build and Publish Docker ImageSharedAlways
8Make ArtifactsSharedmain
9Create Github releaseSharedmain
10Update Application ManifestSharedmain

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.

InputDefaultPassed by the workflow
node-version18(default)
install-dependenciestrue(default) - runs pnpm install
project-path.(default)
OutputDescription
cache-hitWhether the pnpm store cache was restored
pnpm-cache-dirPath to the pnpm store
Node 18 is the action default

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.

InputRequiredValue
version-levelYespatch in build.yaml; the dispatch input in cut-tag.yaml
project-pathNo. (default)
OutputDescription
versionThe new version, e.g. 1.4.2
tagThe 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:

InputDefaultWhat runs
run-linttruepnpm lint, guarded
run-testtruepnpm test, guarded
run-buildtruepnpm build, guarded
node-options--max_old_space_size=4096Applied to all three
project-path.Repository root
archive-coveragefalseCoverage is not uploaded

Outputs: none. The action's contribution is the compiled output on disk and a non-zero exit if any phase fails.

Your package.json scripts are the contract

Each 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, and start. Those two phases run.
  • They do not render a lint script, so linting logs "No lint script found in package.json. Skipping linting." Adding a lint script 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 }}
Inputversion-level - patch, minor, or major
OutputsA [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"
}