Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10d8647b50 |
17
.commitlintrc.yml
Normal file
17
.commitlintrc.yml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
rules:
|
||||||
|
# Types autorisés (à ajuster au besoin)
|
||||||
|
type-enum:
|
||||||
|
- 2
|
||||||
|
- always
|
||||||
|
- [feat, fix, chore, docs, style, refactor, perf, test, build, ci, revert]
|
||||||
|
subject-empty:
|
||||||
|
- 2
|
||||||
|
- never
|
||||||
|
header-max-length:
|
||||||
|
- 2
|
||||||
|
- always
|
||||||
|
- 100
|
||||||
|
type-case:
|
||||||
|
- 2
|
||||||
|
- always
|
||||||
|
- lower-case
|
||||||
173
.gitea/workflows/ci.yml
Normal file
173
.gitea/workflows/ci.yml
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
env:
|
||||||
|
REGISTRY: ${{ secrets.DOCKER_REGISTRY || 'registry.local' }}
|
||||||
|
IMAGE_NAME: ${{ secrets.DOCKER_IMAGE || 'mon-projet' }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
commitlint:
|
||||||
|
runs-on: docker
|
||||||
|
env:
|
||||||
|
FROM_REF: ${{ github.event.pull_request.base.sha || github.event.before || '' }}
|
||||||
|
TO_REF: ${{ github.sha }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Run commitlint (conventional commits)
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
from="${FROM_REF}"
|
||||||
|
if [ -z "$from" ]; then
|
||||||
|
from="HEAD~1"
|
||||||
|
fi
|
||||||
|
docker run --rm -v "$PWD:/workspace" -w /workspace ghcr.io/conventional-changelog/commitlint:latest --from "$from" --to "$TO_REF"
|
||||||
|
|
||||||
|
lint:
|
||||||
|
runs-on: docker
|
||||||
|
needs: commitlint
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup tools
|
||||||
|
run: |
|
||||||
|
echo "TODO: installer vos dépendances de lint (npm ci, pip install -r requirements.txt, etc.)"
|
||||||
|
|
||||||
|
- name: Lint
|
||||||
|
run: |
|
||||||
|
echo "TODO: remplacer par la commande réelle de lint, ex: npm run lint"
|
||||||
|
|
||||||
|
test:
|
||||||
|
runs-on: docker
|
||||||
|
needs: lint
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
echo "TODO: installer les dépendances de test"
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: |
|
||||||
|
echo "TODO: remplacer par la commande réelle de tests, ex: npm test"
|
||||||
|
|
||||||
|
build:
|
||||||
|
runs-on: docker
|
||||||
|
needs: test
|
||||||
|
env:
|
||||||
|
DOCKER_USER: ${{ secrets.DOCKER_USERNAME }}
|
||||||
|
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
DOCKER_REGISTRY: ${{ env.REGISTRY }}
|
||||||
|
DOCKER_IMAGE: ${{ env.IMAGE_NAME }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Fetch tags
|
||||||
|
run: git fetch --tags --force
|
||||||
|
|
||||||
|
- name: Compute next version (semver)
|
||||||
|
id: version
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
last_tag=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
|
||||||
|
if [ -z "$last_tag" ]; then
|
||||||
|
base="v0.0.0"
|
||||||
|
commits=$(git log --format=%s%n%b HEAD)
|
||||||
|
else
|
||||||
|
base="$last_tag"
|
||||||
|
commits=$(git log --format=%s%n%b "${last_tag}..HEAD")
|
||||||
|
fi
|
||||||
|
|
||||||
|
bump="patch"
|
||||||
|
if echo "$commits" | grep -qiE "(^| )feat!"; then
|
||||||
|
bump="major"
|
||||||
|
elif echo "$commits" | grep -qiE "BREAKING CHANGE"; then
|
||||||
|
bump="major"
|
||||||
|
elif echo "$commits" | grep -qiE "^feat:"; then
|
||||||
|
bump="minor"
|
||||||
|
elif echo "$commits" | grep -qiE "^fix:"; then
|
||||||
|
bump="patch"
|
||||||
|
fi
|
||||||
|
|
||||||
|
semver="${base#v}"
|
||||||
|
major=${semver%%.*}
|
||||||
|
minor=${semver#*.}; minor=${minor%%.*}
|
||||||
|
patch=${semver##*.}
|
||||||
|
|
||||||
|
case "$bump" in
|
||||||
|
major) major=$((major+1)); minor=0; patch=0 ;;
|
||||||
|
minor) minor=$((minor+1)); patch=0 ;;
|
||||||
|
patch) patch=$((patch+1)) ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
next="v${major}.${minor}.${patch}"
|
||||||
|
echo "next=$next" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Docker login
|
||||||
|
if: env.DOCKER_USER != '' && env.DOCKER_PASSWORD != ''
|
||||||
|
run: |
|
||||||
|
echo "${DOCKER_PASSWORD}" | docker login "${DOCKER_REGISTRY}" -u "${DOCKER_USER}" --password-stdin
|
||||||
|
|
||||||
|
- name: Build Docker image (latest)
|
||||||
|
run: docker build -t "${DOCKER_REGISTRY}/${DOCKER_IMAGE}:latest" -f Dockerfile .
|
||||||
|
|
||||||
|
- name: Build Docker image (versioned)
|
||||||
|
run: docker build -t "${DOCKER_REGISTRY}/${DOCKER_IMAGE}:${{ steps.version.outputs.next }}" -f Dockerfile .
|
||||||
|
|
||||||
|
- name: Push Docker images
|
||||||
|
if: github.event_name == 'push'
|
||||||
|
run: |
|
||||||
|
docker push "${DOCKER_REGISTRY}/${DOCKER_IMAGE}:latest"
|
||||||
|
docker push "${DOCKER_REGISTRY}/${DOCKER_IMAGE}:${{ steps.version.outputs.next }}"
|
||||||
|
|
||||||
|
- name: Generate changelog
|
||||||
|
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||||
|
id: changelog
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
last_tag=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
|
||||||
|
if [ -z "$last_tag" ]; then
|
||||||
|
range="HEAD"
|
||||||
|
last_tag="initial"
|
||||||
|
else
|
||||||
|
range="${last_tag}..HEAD"
|
||||||
|
fi
|
||||||
|
{
|
||||||
|
echo "Changelog since $last_tag"
|
||||||
|
git log --pretty=format:"- %s" $range
|
||||||
|
} > changelog.md
|
||||||
|
|
||||||
|
- name: Create and push tag
|
||||||
|
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||||
|
env:
|
||||||
|
TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||||
|
SERVER_URL: ${{ github.server_url }}
|
||||||
|
REPOSITORY: ${{ github.repository }}
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
tag="${{ steps.version.outputs.next }}"
|
||||||
|
git tag -a "$tag" -m "Release $tag"
|
||||||
|
origin="${SERVER_URL#https://}"
|
||||||
|
origin="${origin#http://}"
|
||||||
|
git push "https://oauth2:${TOKEN}@${origin}/${REPOSITORY}" "$tag"
|
||||||
|
|
||||||
|
- name: Upload changelog
|
||||||
|
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: changelog
|
||||||
|
path: changelog.md
|
||||||
140
.gitea/workflows/deploy.yml
Normal file
140
.gitea/workflows/deploy.yml
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
name: Deploy
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
env:
|
||||||
|
description: "Environnement cible"
|
||||||
|
required: true
|
||||||
|
type: choice
|
||||||
|
options:
|
||||||
|
- staging
|
||||||
|
- prod
|
||||||
|
default: staging
|
||||||
|
version:
|
||||||
|
description: "Tag explicite vX.Y.Z (utilisé pour prod, sinon dernier tag existant)"
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
|
||||||
|
env:
|
||||||
|
REGISTRY: ${{ secrets.DOCKER_REGISTRY || 'registry.local' }}
|
||||||
|
IMAGE_NAME: ${{ secrets.DOCKER_IMAGE || 'mon-projet' }}
|
||||||
|
PROJECT_ROOT: /opt/mon-projet
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
prepare:
|
||||||
|
name: Prepare version and sources
|
||||||
|
runs-on: docker
|
||||||
|
outputs:
|
||||||
|
version: ${{ steps.resolve_version.outputs.version }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Fetch tags
|
||||||
|
run: git fetch --tags --force
|
||||||
|
|
||||||
|
- name: Resolve target version
|
||||||
|
id: resolve_version
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
explicit="${{ inputs.version }}"
|
||||||
|
if [ -n "$explicit" ]; then
|
||||||
|
echo "version=$explicit" >> "$GITHUB_OUTPUT"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
tag=$(git describe --tags --abbrev=0 2>/dev/null || true)
|
||||||
|
if [ -z "$tag" ]; then
|
||||||
|
echo "No tag found and no version provided." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "version=$tag" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Sync repo to /opt/mon-projet
|
||||||
|
run: |
|
||||||
|
mkdir -p "${PROJECT_ROOT}"
|
||||||
|
cp -a . "${PROJECT_ROOT}/"
|
||||||
|
|
||||||
|
build:
|
||||||
|
name: Build images (latest + versioned)
|
||||||
|
runs-on: docker
|
||||||
|
needs: prepare
|
||||||
|
env:
|
||||||
|
DOCKER_USER: ${{ secrets.DOCKER_USERNAME }}
|
||||||
|
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
VERSION_TAG: ${{ needs.prepare.outputs.version }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Docker login
|
||||||
|
if: env.DOCKER_USER != '' && env.DOCKER_PASSWORD != ''
|
||||||
|
run: |
|
||||||
|
echo "${DOCKER_PASSWORD}" | docker login "${REGISTRY}" -u "${DOCKER_USER}" --password-stdin
|
||||||
|
|
||||||
|
- name: Build latest
|
||||||
|
run: docker build -t "${REGISTRY}/${IMAGE_NAME}:latest" -f Dockerfile .
|
||||||
|
|
||||||
|
- name: Build versioned
|
||||||
|
run: docker build -t "${REGISTRY}/${IMAGE_NAME}:${VERSION_TAG}" -f Dockerfile .
|
||||||
|
|
||||||
|
- name: Push images
|
||||||
|
run: |
|
||||||
|
docker push "${REGISTRY}/${IMAGE_NAME}:latest"
|
||||||
|
docker push "${REGISTRY}/${IMAGE_NAME}:${VERSION_TAG}"
|
||||||
|
|
||||||
|
deploy-staging:
|
||||||
|
name: Deploy to staging
|
||||||
|
runs-on: docker
|
||||||
|
needs: build
|
||||||
|
if: inputs.env == 'staging'
|
||||||
|
env:
|
||||||
|
ENV_DIR: ${{ env.PROJECT_ROOT }}/env/staging
|
||||||
|
DOTENV: ${{ env.PROJECT_ROOT }}/env/staging/.env
|
||||||
|
steps:
|
||||||
|
- name: List synced sources
|
||||||
|
run: ls -la "${PROJECT_ROOT}"
|
||||||
|
|
||||||
|
- name: Generate staging .env from secret
|
||||||
|
env:
|
||||||
|
STAGING_ENV: ${{ secrets.STAGING_ENV_VARS }}
|
||||||
|
run: |
|
||||||
|
mkdir -p "${ENV_DIR}"
|
||||||
|
echo "${STAGING_ENV}" > "${DOTENV}"
|
||||||
|
|
||||||
|
- name: Deploy with docker compose
|
||||||
|
working-directory: ${{ env.PROJECT_ROOT }}
|
||||||
|
run: |
|
||||||
|
docker compose -f docker-compose.staging.yml --env-file "${DOTENV}" pull
|
||||||
|
docker compose -f docker-compose.staging.yml --env-file "${DOTENV}" up -d
|
||||||
|
|
||||||
|
deploy-prod:
|
||||||
|
name: Deploy to prod
|
||||||
|
runs-on: docker
|
||||||
|
needs: build
|
||||||
|
if: inputs.env == 'prod'
|
||||||
|
env:
|
||||||
|
ENV_DIR: ${{ env.PROJECT_ROOT }}/env/prod
|
||||||
|
DOTENV: ${{ env.PROJECT_ROOT }}/env/prod/.env
|
||||||
|
VERSION_TAG: ${{ needs.prepare.outputs.version }}
|
||||||
|
steps:
|
||||||
|
- name: Check target version
|
||||||
|
run: echo "Deploying prod with ${VERSION_TAG}"
|
||||||
|
|
||||||
|
- name: Generate prod .env from secret
|
||||||
|
env:
|
||||||
|
PROD_ENV: ${{ secrets.PROD_ENV_VARS }}
|
||||||
|
run: |
|
||||||
|
mkdir -p "${ENV_DIR}"
|
||||||
|
echo "${PROD_ENV}" > "${DOTENV}"
|
||||||
|
|
||||||
|
- name: Deploy with docker compose
|
||||||
|
working-directory: ${{ env.PROJECT_ROOT }}
|
||||||
|
env:
|
||||||
|
IMAGE_TAG: ${{ env.VERSION_TAG }}
|
||||||
|
run: |
|
||||||
|
docker compose -f docker-compose.prod.yml --env-file "${DOTENV}" pull
|
||||||
|
docker compose -f docker-compose.prod.yml --env-file "${DOTENV}" up -d
|
||||||
7
Dockerfile
Normal file
7
Dockerfile
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
FROM alpine:3.19
|
||||||
|
|
||||||
|
# TODO: install your runtime dependencies (node, python, etc.)
|
||||||
|
# COPY . /app
|
||||||
|
# WORKDIR /app
|
||||||
|
|
||||||
|
CMD ["sh", "-c", "echo Build your application image here"]
|
||||||
67
README.md
Normal file
67
README.md
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
# CI/CD Gitea starter
|
||||||
|
|
||||||
|
Empty on purpose: only CI/CD, Docker, compose, and instructions so you can drop your app in.
|
||||||
|
|
||||||
|
## Structure
|
||||||
|
- `.gitea/workflows/ci.yml`: lint -> test -> build with auto-versioning, git tags, Docker images `latest` + `vX.Y.Z`.
|
||||||
|
- `.gitea/workflows/deploy.yml`: manual Deploy (`workflow_dispatch`) to staging or prod.
|
||||||
|
- `docker-compose.staging.yml` / `docker-compose.prod.yml`: per-environment compose.
|
||||||
|
- `Dockerfile`: skeleton image to replace with your app build.
|
||||||
|
- `/opt/mon-projet/env/{staging,prod}/.env`: generated on the VPS from Gitea secrets.
|
||||||
|
- `.commitlintrc.yml`: commit linting rules (conventional-style) enforced in CI.
|
||||||
|
- `scripts/commit-msg.sh`: local commit-msg hook helper (commitlint via npx, no Docker).
|
||||||
|
|
||||||
|
## Required Gitea secrets
|
||||||
|
- `DOCKER_REGISTRY`: registry (e.g. registry.example.com).
|
||||||
|
- `DOCKER_IMAGE`: image name (e.g. mon-projet).
|
||||||
|
- `DOCKER_USERNAME` / `DOCKER_PASSWORD`: registry credentials.
|
||||||
|
- `GITEA_TOKEN`: token with push rights on the repo (for git tag push).
|
||||||
|
- `STAGING_ENV_VARS`: full `.env` content for staging (multi-line allowed).
|
||||||
|
- `PROD_ENV_VARS`: full `.env` content for prod.
|
||||||
|
|
||||||
|
## CI workflow (ci.yml)
|
||||||
|
- Triggers: `push` and `pull_request`.
|
||||||
|
- Strict order: `commitlint` -> `lint` -> `test` -> `build`. Any failure stops later jobs.
|
||||||
|
- Auto-versioning rules (conventional commits):
|
||||||
|
- `feat:` => MINOR, `fix:` => PATCH, `feat!` or `BREAKING CHANGE` => MAJOR.
|
||||||
|
- Tag created (`vX.Y.Z`) + changelog artifact on pushes to `main`.
|
||||||
|
- Builds and pushes Docker images `latest` and `vX.Y.Z` to the configured registry.
|
||||||
|
|
||||||
|
## Deploy workflow (deploy.yml)
|
||||||
|
- Trigger: manual only (`workflow_dispatch`) with input `env` (`staging` | `prod`) and optional `version` (to force a prod tag).
|
||||||
|
- Jobs: `prepare` -> `build` -> `deploy-*` (staging or prod). Deployments always depend on the build.
|
||||||
|
- Build: rebuilds and pushes both tags (`latest` + `vX.Y.Z`) before any deploy.
|
||||||
|
- `.env` generation: secrets `STAGING_ENV_VARS` / `PROD_ENV_VARS` are written on the VPS to `/opt/mon-projet/env/<env>/.env`.
|
||||||
|
- Docker deploy: `docker compose pull` then `docker compose up -d` with the dedicated compose files.
|
||||||
|
- Prod uses the explicit version tag; staging consumes `latest`.
|
||||||
|
|
||||||
|
## Local commitlint (optional)
|
||||||
|
- Requires Node.js + npm locally (no Docker).
|
||||||
|
- One-time setup: `npm install --save-dev @commitlint/cli`
|
||||||
|
- Install the hook: `cp scripts/commit-msg.sh .git/hooks/commit-msg && chmod +x .git/hooks/commit-msg`
|
||||||
|
- The hook will block commits whose messages are not conventional-style (`feat`, `fix`, `chore`, etc.).
|
||||||
|
- Windows (Git Bash) : chmod est optionnel ; copie simplement le fichier dans `.git/hooks/commit-msg`. Vérifie que `npx` fonctionne (`npx --version`).
|
||||||
|
|
||||||
|
## Pour un débutant : comment ça marche ?
|
||||||
|
- Commits propres : écris tes messages comme `feat: ...` (nouvelle fonctionnalité) ou `fix: ...` (correction). Ça pilote l’auto-versioning.
|
||||||
|
- Pousser le code : un `git push` ou une PR lance la CI (vérifie les commits, le lint, les tests, construit l’image Docker).
|
||||||
|
- Versions automatiques : sur la branche `main`, la CI calcule la prochaine version `vX.Y.Z`, tague le repo et pousse les images Docker (`latest` et `vX.Y.Z`).
|
||||||
|
- Déploiements : rien d’automatique. Tu lances manuellement le workflow **Deploy** dans l’onglet Actions de Gitea :
|
||||||
|
- Choisis `staging` pour déployer l’image `latest`.
|
||||||
|
- Choisis `prod` pour déployer l’image `vX.Y.Z` (dernier tag ou celui que tu fournis dans l’input `version`).
|
||||||
|
- Secrets : tes mots de passe/variables sensibles restent dans l’UI Gitea (pas dans le code). Ils servent à créer les fichiers `.env` sur le serveur et à pousser les images.
|
||||||
|
- Serveur : le runner doit avoir Docker/compose et pouvoir écrire dans `/opt/mon-projet/`. Les `.env` sont générés à chaque déploiement et ne sont jamais commités.
|
||||||
|
|
||||||
|
## How to use
|
||||||
|
1) Update `Dockerfile`, lint/test commands in `ci.yml`, and services/ports/volumes in compose files to match your app.
|
||||||
|
2) Add the secrets above in the Gitea repo settings.
|
||||||
|
3) Push code with commit messages `feat:`, `fix:`, or `feat!`/`BREAKING CHANGE` to drive semver bumps.
|
||||||
|
4) Make sure CI is green. No automatic deployment will run if CI fails (and deploy stays manual).
|
||||||
|
5) Run the **Deploy** workflow in Gitea Actions:
|
||||||
|
- `env=staging`: deploys image `latest`.
|
||||||
|
- `env=prod`: deploys image tagged `vX.Y.Z` (latest tag by default or the one supplied via `version`).
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
- Runner must access `/opt/mon-projet/` and Docker/compose.
|
||||||
|
- `.env` files are never versioned; they are regenerated on each deploy from secrets.
|
||||||
|
- If no tag exists yet, provide `version` manually for a prod deploy.
|
||||||
9
docker-compose.prod.yml
Normal file
9
docker-compose.prod.yml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
image: ${REGISTRY:-registry.local}/${IMAGE_NAME:-mon-projet}:${IMAGE_TAG:?vX.Y.Z_required}
|
||||||
|
env_file:
|
||||||
|
- /opt/mon-projet/env/prod/.env
|
||||||
|
restart: unless-stopped
|
||||||
|
command: ["sh", "-c", "echo Replace this command with your application start"]
|
||||||
10
docker-compose.staging.yml
Normal file
10
docker-compose.staging.yml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
image: ${REGISTRY:-registry.local}/${IMAGE_NAME:-mon-projet}:latest
|
||||||
|
env_file:
|
||||||
|
- /opt/mon-projet/env/staging/.env
|
||||||
|
restart: unless-stopped
|
||||||
|
# TODO: expose ports/volumes command to fit your app
|
||||||
|
command: ["sh", "-c", "echo Replace this command with your application start"]
|
||||||
1217
package-lock.json
generated
Normal file
1217
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
5
package.json
Normal file
5
package.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"devDependencies": {
|
||||||
|
"@commitlint/cli": "^20.1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
20
scripts/commit-msg.sh
Normal file
20
scripts/commit-msg.sh
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Local commit-msg hook using commitlint via npx (no Docker/GHCR auth required).
|
||||||
|
# Usage: scripts/commit-msg.sh <commit-msg-file>
|
||||||
|
|
||||||
|
MSG_FILE="${1:?path to commit message file required}"
|
||||||
|
|
||||||
|
if ! command -v npx >/dev/null 2>&1; then
|
||||||
|
echo "npx not found. Install Node.js and run: npm install --save-dev @commitlint/cli" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ensure commitlint is available (installed as devDependency)
|
||||||
|
if ! npx --yes @commitlint/cli@latest --help >/dev/null 2>&1; then
|
||||||
|
echo "Install commitlint locally: npm install --save-dev @commitlint/cli" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
npx --yes @commitlint/cli@latest --config .commitlintrc.yml --edit "${MSG_FILE}"
|
||||||
Reference in New Issue
Block a user