OpenAPI-First #3: Versioning Contract qua dev, SIT, production
Làm thế nào để quản lý version API contract qua các môi trường dev, SIT và production? Bài này hướng dẫn chiến lược versioning với npm tags và CI/CD automation.
Nghe bài viết này dưới dạng podcastKhi API contract được đóng gói thành npm package, câu hỏi tiếp theo là: làm thế nào để dev environment luôn có version mới nhất, trong khi SIT và production lại cần version ổn định, được pin cứng?
Câu trả lời nằm ở npm tags — một tính năng ít được biết đến nhưng rất mạnh.
npm tags hoạt động như thế nào?
Mỗi npm package có thể có nhiều tags, mỗi tag trỏ đến một version:
@your-org/api-contracts-client
├── latest → 1.2.0 (production)
├── sit → 1.2.0-sit.1 (SIT)
└── dev → 1.2.0-dev.a3f9c1 (development)
Khác với Docker tag latest, npm tag không tự động cập nhật — bạn phải chỉ định khi publish:
pnpm publish --tag dev # trỏ tag "dev" đến version vừa publish
pnpm publish --tag sit # trỏ tag "sit"
pnpm publish # mặc định trỏ tag "latest"
Chiến lược version theo môi trường
| Môi trường | Format | Ví dụ | Cách install |
|---|---|---|---|
| Dev | x.y.z-dev.{sha} |
1.2.0-dev.a3f9c1 |
pnpm add @org/pkg@dev |
| SIT | x.y.z-sit.{n} |
1.2.0-sit.1 |
pnpm add @org/pkg@1.2.0-sit.1 |
| Production | x.y.z |
1.2.0 |
pnpm add @org/pkg@1.2.0 |
Dev dùng tag — luôn lấy version mới nhất khi pnpm install.
SIT và production pin version cứng — biết chính xác version nào đang chạy.
CI workflow cho dev branch
# .github/workflows/publish-dev.yml
name: Publish Dev
on:
push:
branches: [dev]
paths: ['openapi.yaml']
jobs:
publish:
runs-on: ubuntu-latest
permissions:
packages: write
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 20
registry-url: https://npm.pkg.github.com
- run: pnpm install
- name: Bump dev version
run: |
SHA=$(git rev-parse --short HEAD)
for pkg in packages/nestjs-contracts packages/client-types; do
node -e "
const fs = require('fs');
const pkg = JSON.parse(fs.readFileSync('$pkg/package.json'));
const base = pkg.version.split('-')[0];
pkg.version = base + '-dev.' + '$SHA';
fs.writeFileSync('$pkg/package.json', JSON.stringify(pkg, null, 2));
"
done
- run: pnpm --filter './packages/*' publish --no-git-checks --tag dev
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Workflow cho SIT và production
SIT và production không auto publish — cần bump version tay để có ý thức về những gì đang release:
# .github/workflows/publish-release.yml
name: Publish Release
on:
push:
branches: [sit, main]
paths:
- 'packages/*/package.json' # chỉ trigger khi version được bump tay
jobs:
publish:
runs-on: ubuntu-latest
steps:
- name: Determine tag
id: tag
run: |
if [[ "${{ github.ref_name }}" == "sit" ]]; then
echo "tag=sit" >> $GITHUB_OUTPUT
else
echo "tag=latest" >> $GITHUB_OUTPUT
fi
- run: pnpm --filter './packages/*' publish --no-git-checks --tag ${{ steps.tag.outputs.tag }}
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Quy trình bump version tay
# Chuẩn bị SIT release
cd packages/nestjs-contracts
pnpm version prerelease --preid=sit
# 1.1.0 → 1.1.1-sit.0
cd ../client-types
pnpm version prerelease --preid=sit
git commit -am "chore: bump to sit release"
git push origin sit
# → CI tự publish với tag "sit"
# Production release
cd packages/nestjs-contracts
pnpm version minor # hoặc patch / major
# 1.1.1-sit.0 → 1.2.0
cd ../client-types
pnpm version minor
git commit -am "chore: release v1.2.0"
git push origin main
# → CI publish với tag "latest"
Consumer repo cập nhật version
# Dev — luôn dùng tag, không cần thay đổi package.json
pnpm install # tự kéo dev version mới nhất
# SIT — pin cứng version
pnpm add @your-org/api-contracts-nestjs@1.2.0-sit.1
# Production — pin cứng version
pnpm add @your-org/api-contracts-nestjs@1.2.0
Tại sao SIT và production phải pin version?
Dùng tag @sit hay @latest ở môi trường quan trọng có rủi ro: nếu ai đó vô tình publish version mới lên tag đó, deploy tiếp theo sẽ kéo code khác mà không ai biết.
Pin version cứng đảm bảo:
- Biết chính xác version nào đang chạy ở production
- Rollback dễ dàng — chỉ cần revert
package.json - Deploy reproducible — cùng
package.jsonluôn ra cùng kết quả
Kết luận
- npm tags cho phép cùng một package có nhiều "kênh" phân phối.
- Dev dùng tag để luôn có version mới nhất tự động.
- SIT và production pin version cứng để kiểm soát chính xác.
- Chỉ bump version tay trước khi push lên sit/main — không auto bump.
- CI chỉ publish khi
package.jsonthay đổi — không publish thừa.
Chưa có bình luận
Để lại bình luận
Bình luận sẽ được phê duyệt trước khi hiển thị.