PLAYBOOK HOÀN CHỈNH: GIT FLOW + CI/CD + DEVSECOPS
PHẦN 1 — GIT FLOW
1.1 Sơ đồ chuẩn
feature/* ─┐
├──→ develop ──→ release/* ──→ main ──→ [tag v1.x.x]
bugfix/* ─┘ ↑
hotfix/* ──→ developRule cứng:
- Không push thẳng lên
mainhoặcdevelop - Mọi thứ phải qua Pull Request
mainluôn deployable
1.2 Khởi tạo repo
git init
git commit --allow-empty -m "chore: initial commit"
git branch -M main
git remote add origin https://github.com/user/repo.git
git push -u origin main
git checkout -b develop
git push -u origin developDùng --allow-empty để tránh commit “rác” khi chưa có file nào thực chất.
1.3 Feature workflow
git checkout develop
git pull --rebase origin develop # dùng rebase, không tạo merge commit rác
git checkout -b feature/ten-tinh-nang
# ... code ...
git add -p # add từng hunk, không add cả file blind
git commit -m "feat(module): mô tả ngắn"
git push origin feature/ten-tinh-nangSau khi PR được merge:
git checkout develop && git pull --rebase origin develop
git branch -d feature/ten-tinh-nang
git push origin --delete feature/ten-tinh-nang1.4 Release workflow
git checkout develop && git pull --rebase origin develop
git checkout -b release/v1.2.0
# Chỉnh CHANGELOG, bump version trong pyproject.toml / package.json
git commit -m "chore: bump version to 1.2.0"
git push origin release/v1.2.0
# Sau review:
git checkout main && git merge --no-ff release/v1.2.0
git tag -a v1.2.0 -m "Release v1.2.0"
git push origin main --tags
git checkout develop && git merge --no-ff release/v1.2.0
git push origin develop
git branch -d release/v1.2.0
git push origin --delete release/v1.2.01.5 Hotfix workflow
git checkout main && git pull origin main
git checkout -b hotfix/fix-crash-login
# ... sửa lỗi ...
git commit -m "fix: null pointer khi user chưa xác thực"
git push origin hotfix/fix-crash-login
git checkout main && git merge --no-ff hotfix/fix-crash-login
git tag -a v1.2.1 -m "Hotfix v1.2.1"
git push origin main --tags
git checkout develop && git merge --no-ff hotfix/fix-crash-login
git push origin develop
git branch -d hotfix/fix-crash-login
git push origin --delete hotfix/fix-crash-login1.6 Merge vs Rebase — khi nào dùng gì
| Tình huống | Dùng |
|---|---|
| Feature branch cá nhân, trước khi tạo PR | rebase develop → lịch sử sạch |
| Merge PR vào develop/main | --no-ff merge → giữ merge commit để trace |
| Đồng bộ develop về feature đang làm | rebase |
| Nhánh đã public, nhiều người dùng | Tuyệt đối không rebase |
# Quy trình rebase chuẩn trước khi tạo PR
git checkout develop && git pull --rebase origin develop
git checkout feature/ten-tinh-nang
git rebase develop
# Xử lý conflict từng commit một
git push --force-with-lease origin feature/ten-tinh-nang # KHÔNG dùng --force1.7 Những lệnh hay bị bỏ quên
# Lưu tạm khi cần nhảy nhánh giữa chừng
git stash push -m "wip: đang làm auth module"
git stash list
git stash pop # hoặc git stash apply stash@{0}
# Lấy 1 commit cụ thể từ nhánh khác
git cherry-pick <commit-hash>
git cherry-pick A..B # range từ A+1 đến B
# Dọn dẹp commit trước khi PR
git rebase -i HEAD~4
# squash / fixup / reword / drop từng commit
# Xem lịch sử dạng graph
git log --oneline --graph --all
# So sánh nhánh
git diff develop...feature/ten-tinh-nang
# Ký commit với GPG (nhiều org bắt buộc)
git config --global commit.gpgsign true
git commit -S -m "feat: signed commit"1.8 Cấu hình .github/ chuẩn
.github/
├── CODEOWNERS # auto-assign reviewer đúng người
├── pull_request_template.md # template PR
├── ISSUE_TEMPLATE/
│ ├── bug_report.md
│ └── feature_request.md
└── workflows/
├── ci.yml
└── cd.ymlCODEOWNERS:
# Security-related files → chỉ team security mới approve được
src/crypto/ @org/security-team
*.yml @org/devops-team
docs/ @org/tech-leadpull_request_template.md:
## Mô tả thay đổi
<!-- Tóm tắt ngắn -->
## Loại thay đổi
- [ ] feat - [ ] fix - [ ] refactor - [ ] docs - [ ] chore
## Checklist
- [ ] Đã tự review code của mình
- [ ] Đã viết/cập nhật test
- [ ] Không có secret/credential trong code
- [ ] Đã cập nhật docs nếu cầnPHẦN 2 — CONVENTIONAL COMMITS & PRE-COMMIT
2.1 Conventional Commits
Format: <type>(<scope>): <mô tả>
| Type | Dùng khi |
|---|---|
feat | Tính năng mới |
fix | Sửa bug |
refactor | Tái cấu trúc, không thêm feature/fix bug |
perf | Cải thiện performance |
test | Thêm/sửa test |
docs | Tài liệu |
chore | Build system, dependencies |
ci | CI/CD config |
security | Vá lỗ hổng bảo mật |
revert | Revert commit |
Breaking change: thêm ! sau type hoặc footer BREAKING CHANGE:
feat(api)!: đổi response format của /users endpoint2.2 Pre-commit hooks
Cài đặt:
pip install pre-commit.pre-commit-config.yaml:
repos:
# --- Format & Lint ---
- repo: https://github.com/psf/black
rev: 23.12.0
hooks:
- id: black
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.1.9
hooks:
- id: ruff
args: [--fix]
# --- Security: chặn secret bị commit ---
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.1
hooks:
- id: gitleaks
# --- C++ (nếu dùng) ---
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v17.0.6
hooks:
- id: clang-format
types_or: [c, c++]
# --- Chung ---
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-merge-conflict
- id: detect-private-key # chặn private key
- id: check-added-large-files # chặn file > 500KB
args: ['--maxkb=500']pre-commit install # cài hook vào .git/hooks
pre-commit run --all-files # chạy thủ công toàn bộPHẦN 3 — CI/CD
3.1 Test CI local với act
Đây là giải pháp cho vấn đề push 10 lần mới xanh:
# Cài (cần Docker)
brew install act # macOS
# hoặc: curl https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash
# Dùng
act # chạy tất cả push triggers
act pull_request # simulate PR event
act -j lint # chỉ chạy job "lint"
act -W .github/workflows/ci.yml # chỉ chạy file cụ thể
act --dryrun # xem sẽ chạy gì, không thực thi
act --verbose # log chi tiếtFile .actrc (đặt ở root repo):
-P ubuntu-latest=catthehacker/ubuntu:act-latest
--secret-file .secretsFile .secrets (KHÔNG commit):
GITHUB_TOKEN=ghp_xxx
CODECOV_TOKEN=xxx
SONAR_TOKEN=xxx3.2 CI Pipeline hoàn chỉnh
.github/workflows/ci.yml:
name: CI
on:
push:
branches: [develop, main]
pull_request:
branches: [develop, main]
jobs:
# ── Job 1: Nhanh, fail early ──────────────────────────────
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: pip
- run: pip install black ruff mypy bandit
- run: black --check src/
- run: ruff check src/
- run: mypy src/
- run: bandit -r src/ -ll -x tests/ # SAST cơ bản
# ── Job 2: Test (matrix) ─────────────────────────────────
test:
needs: lint
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.10', '3.11', '3.12']
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: pip
- run: pip install -r requirements.txt -r requirements-dev.txt
- run: pytest tests/ -v --cov=src --cov-report=xml --cov-fail-under=75
- uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
# ── Job 3: Security scan ──────────────────────────────────
security:
needs: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # cần full history cho secret scan
- name: Audit dependencies
run: |
pip install pip-audit
pip-audit -r requirements.txt --fail-on-vuln
- name: Scan secrets (full history)
uses: trufflesecurity/trufflehog@main
with:
path: ./
base: ${{ github.event.repository.default_branch }}
- name: Container scan (nếu có Dockerfile)
uses: aquasecurity/trivy-action@master
with:
scan-type: fs
severity: CRITICAL,HIGH
exit-code: 1
# ── Job 4: SAST nâng cao (SonarCloud) ────────────────────
sonar:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: SonarSource/sonarcloud-github-action@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}3.3 CD Pipeline
.github/workflows/cd.yml:
name: CD
on:
push:
tags: ['v*.*.*']
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write # cần để tạo GitHub Release
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
# Build artifact
- name: Build
run: |
pip install build
python -m build
# Tự động generate CHANGELOG từ conventional commits
- name: Generate changelog
id: changelog
uses: orhun/git-cliff-action@v3
with:
config: cliff.toml
args: --latest
# Tạo GitHub Release với changelog
- name: Create Release
uses: softprops/action-gh-release@v1
with:
body: ${{ steps.changelog.outputs.content }}
files: dist/*
draft: false
prerelease: ${{ contains(github.ref, 'rc') || contains(github.ref, 'beta') }}3.4 Semantic Release tự động
Thay vì tạo tag tay, dùng semantic-release hoặc release-please:
release-please (Google, đơn giản hơn):
# .github/workflows/release-please.yml
name: Release Please
on:
push:
branches: [main]
jobs:
release-please:
runs-on: ubuntu-latest
steps:
- uses: google-github-actions/release-please-action@v4
with:
release-type: python # hoặc node, go, ...
package-name: my-packageNó tự đọc conventional commits → tạo PR “Release v1.x.x” → merge PR đó thì tag và release tự động.
PHẦN 4 — SECRETS MANAGEMENT
4.1 Hierarchy quản lý secret
Môi trường DEV → .env (local only, gitignored)
Môi trường CI → GitHub Secrets / GitLab CI Variables
Môi trường PROD → HashiCorp Vault / AWS Secrets Manager / GCP Secret ManagerTuyệt đối không:
- Hardcode secret trong code
- Commit
.envlên repo - Để secret trong Docker image layer
- Log secret ra stdout
4.2 Rotation tự động (GitHub Secrets)
# Workflow kiểm tra secret sắp hết hạn
- name: Check token expiry
run: |
expiry=$(curl -H "Authorization: token ${{ secrets.MY_TOKEN }}" \
https://api.github.com/rate_limit | jq '.rate.reset')
echo "Token reset: $expiry"Với production: dùng Vault với lease_duration và dynamic secrets — Vault tự xoay key theo chu kỳ.
PHẦN 5 — TESTING STRATEGY
5.1 Testing Pyramid đúng nghĩa
/\
/E2E\ ← ít nhất, chạy chậm, dễ vỡ
/──────\
/Integration\ ← test boundary: DB, API, filesystem
/──────────────\
/ Unit Tests \ ← nhiều nhất, nhanh, isolated
/──────────────────\Coverage không phải là mục tiêu. 80% coverage nhưng toàn test getter/setter là vô nghĩa. Tập trung vào:
- Business logic cốt lõi
- Security-critical path (auth, crypto, input validation)
- Edge cases của parser/analyzer (đặc biệt với NT137)
5.2 Pytest chuẩn
# conftest.py
import pytest
@pytest.fixture(scope='session')
def db():
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(engine)
yield engine
Base.metadata.drop_all(engine)
@pytest.fixture(autouse=True)
def reset_db(db):
# Clean state mỗi test, không leak giữa các test
with db.connect() as conn:
for table in reversed(Base.metadata.sorted_tables):
conn.execute(table.delete())
conn.commit()# Chạy test với output hữu ích
pytest -v --tb=short --cov=src --cov-report=term-missing
# Chỉ chạy test liên quan đến file đang sửa
pytest --lf # last failed
pytest -x # dừng ngay khi fail đầu tiên
pytest -k "test_auth" # filter theo tên5.3 Fuzzing (quan trọng với security tools)
Với project phân tích PE/APK file (NT137), fuzzing là bắt buộc vì input là binary file từ nguồn không tin tưởng:
# Dùng Atheris (Python fuzzer của Google)
pip install atheris
import atheris
import sys
@atheris.instrument_func
def TestOneInput(data):
try:
import pefile
pefile.PE(data=data)
except pefile.PEFormatError:
pass # Expected
except Exception as e:
# Unexpected exception = potential bug
raise
atheris.Setup(sys.argv, TestOneInput)
atheris.Fuzz()python fuzz_pe_parser.py -max_len=65536 corpus/Với C extension: dùng libFuzzer hoặc AFL++.
PHẦN 6 — OBSERVABILITY
6.1 Structured Logging
Không dùng print() hoặc logging.info("user logged in") thuần text.
import structlog
log = structlog.get_logger()
# Gắn context theo request
log = log.bind(request_id="abc-123", user_id=42)
log.info("user_login", method="password", ip="1.2.3.4")
log.warning("rate_limit_exceeded", endpoint="/api/scan", count=100)
log.error("parse_failed", filename="malware.exe", error=str(e))Output (JSON, dễ ingest vào ELK/Datadog):
{"event": "user_login", "request_id": "abc-123", "user_id": 42, "method": "password", "level": "info", "timestamp": "2024-01-15T10:30:00Z"}6.2 Distributed Tracing
Với multi-service hoặc async tasks:
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
tracer = trace.get_tracer(__name__)
def analyze_file(filepath):
with tracer.start_as_current_span("analyze_file") as span:
span.set_attribute("file.path", filepath)
span.set_attribute("file.size", os.path.getsize(filepath))
with tracer.start_as_current_span("extract_features"):
features = extract_features(filepath)
with tracer.start_as_current_span("ml_predict"):
result = model.predict(features)
span.set_attribute("prediction", result)
return resultPHẦN 7 — BRANCH PROTECTION & GOVERNANCE
Cấu hình trên GitHub (Settings → Branches):
Branch: main
✅ Require pull request before merging
✅ Required approvals: 1 (hoặc 2 cho prod)
✅ Dismiss stale reviews when new commits pushed
✅ Require review from CODEOWNERS
✅ Require status checks to pass
✅ lint
✅ test
✅ security
✅ Require branches to be up to date
✅ Require signed commits ← GPG
✅ Do not allow bypassing ← kể cả adminPHẦN 8 — NHỮNG THỨ CÒN LẠI (theo priority)
Với project NT137 (security tool):
| Priority | Topic | Lý do |
|---|---|---|
| 🔴 Cao | Fuzzing (Atheris/AFL++) | Input là binary untrusted, crash = bug nghiêm trọng |
| 🔴 Cao | Input validation tầng API | Path traversal, zip bomb với APK |
| 🟠 Trung | Load testing (Locust) | Biết analyzer xử lý được bao nhiêu file đồng thời |
| 🟠 Trung | Feature flags | Demo từng tính năng riêng lẻ, tắt nhanh khi bug |
| 🟡 Thấp | SBOM generation (CycloneDX) | Audit dependencies có CVE không |
| 🟡 Thấp | Database migration (Alembic) | Schema thay đổi không mất data |
Với CRAB-He paper / Lightning Network:
| Priority | Topic | Lý do |
|---|---|---|
| 🔴 Cao | Property-based testing (Hypothesis) | Verify invariants của protocol với random inputs |
| 🔴 Cao | Fuzzing Go code (go-fuzz/OSS-Fuzz) | Payment channel state machine phải không crash |
| 🟠 Trung | Formal verification mindset | Impossibility proof cần mapping rõ ràng sang code |
General (bất kỳ project nào):
| Topic | Một dòng tóm tắt |
|---|---|
| Trunk-based Development | Thay thế Git Flow khi team nhỏ, deploy nhanh |
| Blue/Green Deployment | Zero-downtime, rollback ngay trong 30s |
| Canary Release | Rollout 5% → 20% → 100%, tự động rollback khi error rate tăng |
| ADR (Architecture Decision Records) | Ghi lại tại sao chọn thư viện/kiến trúc này |
| Dependency pinning | pip-compile, package-lock.json — reproducible builds |
| Supply chain security | Verify checksum/signature của dependencies (Sigstore) |
| API versioning | /v1/, /v2/ hoặc header Accept-Version — không break client |
PHẦN 9 — CHECKLIST NHANH KHI BẮT ĐẦU PROJECT MỚI
□ Khởi tạo repo với main + develop
□ Cấu hình branch protection (main + develop)
□ Thêm CODEOWNERS
□ Thêm PR template
□ Cài pre-commit (gitleaks + linter)
□ Viết .gitignore đúng (secrets, venv, build artifacts)
□ CI pipeline cơ bản (lint → test → security)
□ act test CI chạy local được
□ Secrets đặt trong GitHub Secrets, không trong code
□ Conventional commits từ commit đầu tiên
□ README: cách setup, cách chạy test, cách contributeTóm lại những gì tài liệu cũ của bạn còn thiếu so với bản này:
git add -pthay vìgit add .blind--no-ffkhi merge release/hotfix vào main (giữ merge commit để audit)--force-with-leasevs--force— giải thích rõ tại saoCODEOWNERS+ PR template cụ thể- Pre-commit cho C++ (clang-format)
release-pleasetự động hóa versioning- Fuzzing — quan trọng nhất với security tooling
- Structured logging với
structlog - Testing strategy đúng nghĩa (không chạy theo % coverage)
- Load testing + feature flags
- Checklist khởi tạo project