javi-forge 1.6.0 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/analyze.d.ts +1 -1
- package/dist/commands/analyze.js +15 -15
- package/dist/commands/atlassian-mcp.d.ts +42 -0
- package/dist/commands/atlassian-mcp.js +98 -0
- package/dist/commands/ci.d.ts +3 -3
- package/dist/commands/ci.js +185 -147
- package/dist/commands/crash-recovery.d.ts +34 -0
- package/dist/commands/crash-recovery.js +123 -0
- package/dist/commands/doctor.d.ts +2 -2
- package/dist/commands/doctor.js +113 -61
- package/dist/commands/harness-audit.d.ts +35 -0
- package/dist/commands/harness-audit.js +277 -0
- package/dist/commands/init.d.ts +1 -1
- package/dist/commands/init.js +384 -141
- package/dist/commands/llmstxt.d.ts +1 -1
- package/dist/commands/llmstxt.js +36 -34
- package/dist/commands/parallel-batch.d.ts +42 -0
- package/dist/commands/parallel-batch.js +90 -0
- package/dist/commands/plugin.d.ts +10 -1
- package/dist/commands/plugin.js +92 -47
- package/dist/commands/secret-scanner.d.ts +30 -0
- package/dist/commands/secret-scanner.js +272 -0
- package/dist/commands/security-analysis.d.ts +74 -0
- package/dist/commands/security-analysis.js +487 -0
- package/dist/commands/security.d.ts +11 -5
- package/dist/commands/security.js +216 -76
- package/dist/commands/skill-scanner.d.ts +63 -0
- package/dist/commands/skill-scanner.js +383 -0
- package/dist/commands/skills.d.ts +62 -5
- package/dist/commands/skills.js +439 -54
- package/dist/commands/supply-chain.d.ts +23 -0
- package/dist/commands/supply-chain.js +126 -0
- package/dist/commands/tdd-pipeline.d.ts +17 -0
- package/dist/commands/tdd-pipeline.js +144 -0
- package/dist/commands/tdd.d.ts +1 -1
- package/dist/commands/tdd.js +21 -18
- package/dist/commands/team-presets.d.ts +53 -0
- package/dist/commands/team-presets.js +201 -0
- package/dist/commands/workflow.d.ts +23 -0
- package/dist/commands/workflow.js +114 -0
- package/dist/constants.d.ts +15 -1
- package/dist/constants.js +161 -122
- package/dist/index.js +308 -98
- package/dist/lib/agent-skills.d.ts +36 -1
- package/dist/lib/agent-skills.js +168 -19
- package/dist/lib/auto-skill-install.d.ts +37 -0
- package/dist/lib/auto-skill-install.js +92 -0
- package/dist/lib/auto-wire.d.ts +20 -0
- package/dist/lib/auto-wire.js +240 -0
- package/dist/lib/claudemd.d.ts +13 -1
- package/dist/lib/claudemd.js +174 -24
- package/dist/lib/codex-export.d.ts +1 -1
- package/dist/lib/codex-export.js +29 -31
- package/dist/lib/common.d.ts +1 -1
- package/dist/lib/common.js +52 -44
- package/dist/lib/context.d.ts +17 -2
- package/dist/lib/context.js +142 -13
- package/dist/lib/docker.d.ts +1 -1
- package/dist/lib/docker.js +141 -112
- package/dist/lib/frontmatter.d.ts +1 -1
- package/dist/lib/frontmatter.js +29 -15
- package/dist/lib/plugin.d.ts +9 -3
- package/dist/lib/plugin.js +128 -69
- package/dist/lib/skill-publish.d.ts +40 -0
- package/dist/lib/skill-publish.js +146 -0
- package/dist/lib/stack-detector.d.ts +38 -0
- package/dist/lib/stack-detector.js +207 -0
- package/dist/lib/template.d.ts +16 -1
- package/dist/lib/template.js +46 -17
- package/dist/lib/workflow/discovery.d.ts +19 -0
- package/dist/lib/workflow/discovery.js +68 -0
- package/dist/lib/workflow/index.d.ts +5 -0
- package/dist/lib/workflow/index.js +5 -0
- package/dist/lib/workflow/parser.d.ts +16 -0
- package/dist/lib/workflow/parser.js +198 -0
- package/dist/lib/workflow/renderer.d.ts +9 -0
- package/dist/lib/workflow/renderer.js +152 -0
- package/dist/lib/workflow/validator.d.ts +10 -0
- package/dist/lib/workflow/validator.js +189 -0
- package/dist/tasks/index.d.ts +4 -0
- package/dist/tasks/index.js +4 -0
- package/dist/tasks/scaffold-tasks.d.ts +3 -0
- package/dist/tasks/scaffold-tasks.js +14 -0
- package/dist/tasks/task-id.d.ts +30 -0
- package/dist/tasks/task-id.js +55 -0
- package/dist/tasks/task-tracker.d.ts +15 -0
- package/dist/tasks/task-tracker.js +81 -0
- package/dist/types/index.d.ts +134 -6
- package/dist/types/index.js +11 -1
- package/dist/ui/AnalyzeUI.d.ts +1 -1
- package/dist/ui/AnalyzeUI.js +38 -39
- package/dist/ui/App.d.ts +5 -3
- package/dist/ui/App.js +86 -46
- package/dist/ui/AutoSkills.d.ts +9 -0
- package/dist/ui/AutoSkills.js +124 -0
- package/dist/ui/CI.d.ts +2 -2
- package/dist/ui/CI.js +24 -26
- package/dist/ui/CIContext.d.ts +1 -1
- package/dist/ui/CIContext.js +3 -2
- package/dist/ui/CISelector.d.ts +2 -2
- package/dist/ui/CISelector.js +23 -15
- package/dist/ui/Doctor.d.ts +1 -1
- package/dist/ui/Doctor.js +35 -29
- package/dist/ui/Header.d.ts +1 -1
- package/dist/ui/Header.js +14 -14
- package/dist/ui/HookProfileSelector.d.ts +9 -0
- package/dist/ui/HookProfileSelector.js +54 -0
- package/dist/ui/LlmsTxt.d.ts +1 -1
- package/dist/ui/LlmsTxt.js +31 -22
- package/dist/ui/MemorySelector.d.ts +2 -2
- package/dist/ui/MemorySelector.js +28 -16
- package/dist/ui/NameInput.d.ts +1 -1
- package/dist/ui/NameInput.js +21 -21
- package/dist/ui/OptionSelector.d.ts +6 -2
- package/dist/ui/OptionSelector.js +83 -32
- package/dist/ui/Plugin.d.ts +4 -3
- package/dist/ui/Plugin.js +78 -35
- package/dist/ui/Progress.d.ts +3 -3
- package/dist/ui/Progress.js +23 -22
- package/dist/ui/Skills.d.ts +2 -2
- package/dist/ui/Skills.js +61 -32
- package/dist/ui/StackSelector.d.ts +2 -2
- package/dist/ui/StackSelector.js +26 -16
- package/dist/ui/Summary.d.ts +3 -3
- package/dist/ui/Summary.js +60 -50
- package/dist/ui/Welcome.d.ts +1 -1
- package/dist/ui/Welcome.js +15 -16
- package/dist/ui/theme.d.ts +1 -1
- package/dist/ui/theme.js +6 -6
- package/package.json +9 -6
- package/templates/common/atlassian/mcp-atlassian-snippet.json +16 -0
- package/templates/common/repoforge/mcp-repoforge-snippet.json +11 -0
- package/templates/common/repoforge/repoforge.yaml +34 -0
- package/templates/github/deploy-docker-zero-downtime.yml +140 -0
- package/templates/github/repoforge-graph.yml +45 -0
- package/templates/gitlab/deploy-docker-zero-downtime.yml +57 -0
- package/templates/local-ai/.env.example +17 -0
- package/templates/local-ai/docker-compose.yml +95 -0
- package/templates/security-hooks/claude-settings-security.json +30 -0
- package/templates/security-hooks/commit-msg-signing +29 -0
- package/templates/security-hooks/pre-commit-permissions +74 -0
- package/templates/security-hooks/pre-commit-secrets +74 -0
- package/templates/security-hooks/pre-push-branch-protection +62 -0
- package/templates/security-hooks/pre-push-deps +83 -0
- package/templates/security-hooks/pre-push-signing +67 -0
- package/templates/woodpecker/deploy-docker-zero-downtime.yml +50 -0
- package/templates/workflows/ci-pipeline.dot +15 -0
- package/templates/workflows/feature-flow.dot +21 -0
- package/templates/workflows/release.dot +16 -0
- package/dist/__integration__/helpers.d.ts +0 -20
- package/dist/__integration__/helpers.d.ts.map +0 -1
- package/dist/__integration__/helpers.js +0 -31
- package/dist/__integration__/helpers.js.map +0 -1
- package/dist/commands/analyze.d.ts.map +0 -1
- package/dist/commands/analyze.js.map +0 -1
- package/dist/commands/ci.d.ts.map +0 -1
- package/dist/commands/ci.js.map +0 -1
- package/dist/commands/doctor.d.ts.map +0 -1
- package/dist/commands/doctor.js.map +0 -1
- package/dist/commands/init.d.ts.map +0 -1
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/llmstxt.d.ts.map +0 -1
- package/dist/commands/llmstxt.js.map +0 -1
- package/dist/commands/plugin.d.ts.map +0 -1
- package/dist/commands/plugin.js.map +0 -1
- package/dist/commands/security.d.ts.map +0 -1
- package/dist/commands/security.js.map +0 -1
- package/dist/commands/skills.d.ts.map +0 -1
- package/dist/commands/skills.js.map +0 -1
- package/dist/commands/tdd.d.ts.map +0 -1
- package/dist/commands/tdd.js.map +0 -1
- package/dist/constants.d.ts.map +0 -1
- package/dist/constants.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/lib/agent-skills.d.ts.map +0 -1
- package/dist/lib/agent-skills.js.map +0 -1
- package/dist/lib/claudemd.d.ts.map +0 -1
- package/dist/lib/claudemd.js.map +0 -1
- package/dist/lib/codex-export.d.ts.map +0 -1
- package/dist/lib/codex-export.js.map +0 -1
- package/dist/lib/common.d.ts.map +0 -1
- package/dist/lib/common.js.map +0 -1
- package/dist/lib/context.d.ts.map +0 -1
- package/dist/lib/context.js.map +0 -1
- package/dist/lib/docker.d.ts.map +0 -1
- package/dist/lib/docker.js.map +0 -1
- package/dist/lib/frontmatter.d.ts.map +0 -1
- package/dist/lib/frontmatter.js.map +0 -1
- package/dist/lib/plugin.d.ts.map +0 -1
- package/dist/lib/plugin.js.map +0 -1
- package/dist/lib/template.d.ts.map +0 -1
- package/dist/lib/template.js.map +0 -1
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js.map +0 -1
- package/dist/ui/AnalyzeUI.d.ts.map +0 -1
- package/dist/ui/AnalyzeUI.js.map +0 -1
- package/dist/ui/App.d.ts.map +0 -1
- package/dist/ui/App.js.map +0 -1
- package/dist/ui/CI.d.ts.map +0 -1
- package/dist/ui/CI.js.map +0 -1
- package/dist/ui/CIContext.d.ts.map +0 -1
- package/dist/ui/CIContext.js.map +0 -1
- package/dist/ui/CISelector.d.ts.map +0 -1
- package/dist/ui/CISelector.js.map +0 -1
- package/dist/ui/Doctor.d.ts.map +0 -1
- package/dist/ui/Doctor.js.map +0 -1
- package/dist/ui/Header.d.ts.map +0 -1
- package/dist/ui/Header.js.map +0 -1
- package/dist/ui/LlmsTxt.d.ts.map +0 -1
- package/dist/ui/LlmsTxt.js.map +0 -1
- package/dist/ui/MemorySelector.d.ts.map +0 -1
- package/dist/ui/MemorySelector.js.map +0 -1
- package/dist/ui/NameInput.d.ts.map +0 -1
- package/dist/ui/NameInput.js.map +0 -1
- package/dist/ui/OptionSelector.d.ts.map +0 -1
- package/dist/ui/OptionSelector.js.map +0 -1
- package/dist/ui/Plugin.d.ts.map +0 -1
- package/dist/ui/Plugin.js.map +0 -1
- package/dist/ui/Progress.d.ts.map +0 -1
- package/dist/ui/Progress.js.map +0 -1
- package/dist/ui/Skills.d.ts.map +0 -1
- package/dist/ui/Skills.js.map +0 -1
- package/dist/ui/StackSelector.d.ts.map +0 -1
- package/dist/ui/StackSelector.js.map +0 -1
- package/dist/ui/Summary.d.ts.map +0 -1
- package/dist/ui/Summary.js.map +0 -1
- package/dist/ui/Welcome.d.ts.map +0 -1
- package/dist/ui/Welcome.js.map +0 -1
- package/dist/ui/theme.d.ts.map +0 -1
- package/dist/ui/theme.js.map +0 -1
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# ===========================================
|
|
2
|
+
# Zero-Downtime Docker Deploy (docker rollout)
|
|
3
|
+
# ===========================================
|
|
4
|
+
# Copy to: .github/workflows/deploy.yml
|
|
5
|
+
#
|
|
6
|
+
# Requires:
|
|
7
|
+
# - SSH access to the deploy target (secrets.DEPLOY_HOST, secrets.DEPLOY_USER, secrets.DEPLOY_KEY)
|
|
8
|
+
# - docker-rollout installed on the target: https://github.com/Wowu/docker-rollout
|
|
9
|
+
# - A docker-compose.yml on the target with healthcheck-enabled services
|
|
10
|
+
#
|
|
11
|
+
# Flow:
|
|
12
|
+
# 1. Build & push image to registry
|
|
13
|
+
# 2. SSH into deploy target
|
|
14
|
+
# 3. Pull new image
|
|
15
|
+
# 4. `docker rollout <service>` — spins up new container, waits for healthcheck, drains old
|
|
16
|
+
# ===========================================
|
|
17
|
+
|
|
18
|
+
name: Deploy (Zero-Downtime)
|
|
19
|
+
|
|
20
|
+
on:
|
|
21
|
+
push:
|
|
22
|
+
branches: [main]
|
|
23
|
+
paths-ignore:
|
|
24
|
+
- '**/*.md'
|
|
25
|
+
- '.gitignore'
|
|
26
|
+
workflow_dispatch:
|
|
27
|
+
inputs:
|
|
28
|
+
service:
|
|
29
|
+
description: 'Service to deploy (from docker-compose.yml)'
|
|
30
|
+
required: true
|
|
31
|
+
default: '__SERVICE_NAME__'
|
|
32
|
+
skip_build:
|
|
33
|
+
description: 'Skip image build (deploy existing latest)'
|
|
34
|
+
default: false
|
|
35
|
+
type: boolean
|
|
36
|
+
|
|
37
|
+
permissions:
|
|
38
|
+
contents: read
|
|
39
|
+
packages: write
|
|
40
|
+
|
|
41
|
+
concurrency:
|
|
42
|
+
group: deploy-${{ github.ref }}
|
|
43
|
+
cancel-in-progress: false # Never cancel in-progress deploys
|
|
44
|
+
|
|
45
|
+
env:
|
|
46
|
+
REGISTRY: ghcr.io
|
|
47
|
+
IMAGE_NAME: ${{ github.repository }}
|
|
48
|
+
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
|
|
49
|
+
DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
|
|
50
|
+
|
|
51
|
+
jobs:
|
|
52
|
+
build-and-push:
|
|
53
|
+
if: ${{ github.event.inputs.skip_build != 'true' }}
|
|
54
|
+
runs-on: ubuntu-latest
|
|
55
|
+
outputs:
|
|
56
|
+
image_tag: ${{ steps.meta.outputs.tags }}
|
|
57
|
+
steps:
|
|
58
|
+
- uses: actions/checkout@v4
|
|
59
|
+
|
|
60
|
+
- name: Log in to Container Registry
|
|
61
|
+
uses: docker/login-action@v3
|
|
62
|
+
with:
|
|
63
|
+
registry: ${{ env.REGISTRY }}
|
|
64
|
+
username: ${{ github.actor }}
|
|
65
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
66
|
+
|
|
67
|
+
- name: Extract metadata
|
|
68
|
+
id: meta
|
|
69
|
+
uses: docker/metadata-action@v5
|
|
70
|
+
with:
|
|
71
|
+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
|
72
|
+
tags: |
|
|
73
|
+
type=sha
|
|
74
|
+
type=raw,value=latest
|
|
75
|
+
|
|
76
|
+
- name: Build and push
|
|
77
|
+
uses: docker/build-push-action@v5
|
|
78
|
+
with:
|
|
79
|
+
context: .
|
|
80
|
+
push: true
|
|
81
|
+
tags: ${{ steps.meta.outputs.tags }}
|
|
82
|
+
labels: ${{ steps.meta.outputs.labels }}
|
|
83
|
+
|
|
84
|
+
deploy:
|
|
85
|
+
needs: [build-and-push]
|
|
86
|
+
if: always() && (needs.build-and-push.result == 'success' || github.event.inputs.skip_build == 'true')
|
|
87
|
+
runs-on: ubuntu-latest
|
|
88
|
+
environment: production
|
|
89
|
+
steps:
|
|
90
|
+
- name: Deploy via SSH + docker rollout
|
|
91
|
+
uses: appleboy/ssh-action@v1
|
|
92
|
+
with:
|
|
93
|
+
host: ${{ env.DEPLOY_HOST }}
|
|
94
|
+
username: ${{ env.DEPLOY_USER }}
|
|
95
|
+
key: ${{ secrets.DEPLOY_KEY }}
|
|
96
|
+
script: |
|
|
97
|
+
set -euo pipefail
|
|
98
|
+
|
|
99
|
+
cd ${{ secrets.DEPLOY_DIR || '~/app' }}
|
|
100
|
+
|
|
101
|
+
# Pull latest images
|
|
102
|
+
docker compose pull
|
|
103
|
+
|
|
104
|
+
# Zero-downtime rollout for the target service
|
|
105
|
+
SERVICE="${{ github.event.inputs.service || '__SERVICE_NAME__' }}"
|
|
106
|
+
echo "::group::Rolling out ${SERVICE}"
|
|
107
|
+
docker rollout "${SERVICE}"
|
|
108
|
+
echo "::endgroup::"
|
|
109
|
+
|
|
110
|
+
# Cleanup dangling images
|
|
111
|
+
docker image prune -f
|
|
112
|
+
|
|
113
|
+
- name: Verify deployment
|
|
114
|
+
uses: appleboy/ssh-action@v1
|
|
115
|
+
with:
|
|
116
|
+
host: ${{ env.DEPLOY_HOST }}
|
|
117
|
+
username: ${{ env.DEPLOY_USER }}
|
|
118
|
+
key: ${{ secrets.DEPLOY_KEY }}
|
|
119
|
+
script: |
|
|
120
|
+
set -euo pipefail
|
|
121
|
+
cd ${{ secrets.DEPLOY_DIR || '~/app' }}
|
|
122
|
+
SERVICE="${{ github.event.inputs.service || '__SERVICE_NAME__' }}"
|
|
123
|
+
|
|
124
|
+
# Wait for healthcheck (max 60s)
|
|
125
|
+
TIMEOUT=60
|
|
126
|
+
ELAPSED=0
|
|
127
|
+
while [ $ELAPSED -lt $TIMEOUT ]; do
|
|
128
|
+
HEALTH=$(docker compose ps "${SERVICE}" --format json | jq -r '.[0].Health // "none"' 2>/dev/null || echo "unknown")
|
|
129
|
+
if [ "$HEALTH" = "healthy" ]; then
|
|
130
|
+
echo "✅ ${SERVICE} is healthy"
|
|
131
|
+
exit 0
|
|
132
|
+
fi
|
|
133
|
+
sleep 5
|
|
134
|
+
ELAPSED=$((ELAPSED + 5))
|
|
135
|
+
echo "⏳ Waiting for healthcheck... (${ELAPSED}s/${TIMEOUT}s)"
|
|
136
|
+
done
|
|
137
|
+
|
|
138
|
+
echo "❌ ${SERVICE} did not become healthy within ${TIMEOUT}s"
|
|
139
|
+
docker compose logs --tail=50 "${SERVICE}"
|
|
140
|
+
exit 1
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# ===========================================
|
|
2
|
+
# RepoForge Call Graph — keep graph fresh on every push
|
|
3
|
+
# ===========================================
|
|
4
|
+
# Copy to: .github/workflows/repoforge-graph.yml
|
|
5
|
+
# ===========================================
|
|
6
|
+
|
|
7
|
+
name: RepoForge Graph
|
|
8
|
+
|
|
9
|
+
on:
|
|
10
|
+
push:
|
|
11
|
+
branches: [main]
|
|
12
|
+
paths-ignore:
|
|
13
|
+
- '**/*.md'
|
|
14
|
+
- '.gitignore'
|
|
15
|
+
pull_request:
|
|
16
|
+
branches: [main]
|
|
17
|
+
paths-ignore:
|
|
18
|
+
- '**/*.md'
|
|
19
|
+
- '.gitignore'
|
|
20
|
+
|
|
21
|
+
permissions:
|
|
22
|
+
contents: read
|
|
23
|
+
|
|
24
|
+
concurrency:
|
|
25
|
+
group: repoforge-graph-${{ github.ref }}
|
|
26
|
+
cancel-in-progress: true
|
|
27
|
+
|
|
28
|
+
jobs:
|
|
29
|
+
graph:
|
|
30
|
+
runs-on: ubuntu-latest
|
|
31
|
+
steps:
|
|
32
|
+
- uses: actions/checkout@v4
|
|
33
|
+
|
|
34
|
+
- name: Install repoforge
|
|
35
|
+
run: npm install -g repoforge
|
|
36
|
+
|
|
37
|
+
- name: Generate call graph
|
|
38
|
+
run: repoforge graph --type calls --output .repoforge/call-graph.json
|
|
39
|
+
|
|
40
|
+
- name: Upload graph artifact
|
|
41
|
+
uses: actions/upload-artifact@v4
|
|
42
|
+
with:
|
|
43
|
+
name: call-graph
|
|
44
|
+
path: .repoforge/call-graph.json
|
|
45
|
+
retention-days: 30
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# ===========================================
|
|
2
|
+
# Zero-Downtime Docker Deploy (docker rollout)
|
|
3
|
+
# ===========================================
|
|
4
|
+
# Merge into: .gitlab-ci.yml
|
|
5
|
+
#
|
|
6
|
+
# Requires:
|
|
7
|
+
# - CI/CD variables: DEPLOY_HOST, DEPLOY_USER, DEPLOY_KEY, DEPLOY_DIR
|
|
8
|
+
# - docker-rollout installed on target: https://github.com/Wowu/docker-rollout
|
|
9
|
+
# - A docker-compose.yml on the target with healthcheck-enabled services
|
|
10
|
+
# ===========================================
|
|
11
|
+
|
|
12
|
+
stages:
|
|
13
|
+
- build
|
|
14
|
+
- deploy
|
|
15
|
+
|
|
16
|
+
variables:
|
|
17
|
+
SERVICE_NAME: "__SERVICE_NAME__"
|
|
18
|
+
|
|
19
|
+
build-image:
|
|
20
|
+
stage: build
|
|
21
|
+
image: docker:latest
|
|
22
|
+
services:
|
|
23
|
+
- docker:dind
|
|
24
|
+
before_script:
|
|
25
|
+
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
|
|
26
|
+
script:
|
|
27
|
+
- docker build -t "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA" -t "$CI_REGISTRY_IMAGE:latest" .
|
|
28
|
+
- docker push "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA"
|
|
29
|
+
- docker push "$CI_REGISTRY_IMAGE:latest"
|
|
30
|
+
only:
|
|
31
|
+
- main
|
|
32
|
+
|
|
33
|
+
deploy-zero-downtime:
|
|
34
|
+
stage: deploy
|
|
35
|
+
image: alpine:latest
|
|
36
|
+
needs: ["build-image"]
|
|
37
|
+
before_script:
|
|
38
|
+
- apk add --no-cache openssh-client
|
|
39
|
+
- eval "$(ssh-agent -s)"
|
|
40
|
+
- echo "$DEPLOY_KEY" | ssh-add -
|
|
41
|
+
- mkdir -p ~/.ssh && chmod 700 ~/.ssh
|
|
42
|
+
- ssh-keyscan "$DEPLOY_HOST" >> ~/.ssh/known_hosts
|
|
43
|
+
script:
|
|
44
|
+
- |
|
|
45
|
+
ssh "$DEPLOY_USER@$DEPLOY_HOST" << 'ENDSSH'
|
|
46
|
+
set -euo pipefail
|
|
47
|
+
cd ${DEPLOY_DIR:-~/app}
|
|
48
|
+
docker compose pull
|
|
49
|
+
docker rollout "${SERVICE_NAME}"
|
|
50
|
+
docker image prune -f
|
|
51
|
+
echo "Deploy complete"
|
|
52
|
+
ENDSSH
|
|
53
|
+
environment:
|
|
54
|
+
name: production
|
|
55
|
+
only:
|
|
56
|
+
- main
|
|
57
|
+
when: manual
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Local AI Stack — Environment Variables
|
|
2
|
+
# Copy to .env in your project: cp .env.example .env
|
|
3
|
+
|
|
4
|
+
# ── Ollama ──────────────────────────────────────────────────────────────────
|
|
5
|
+
OLLAMA_PORT=11434
|
|
6
|
+
|
|
7
|
+
# ── Open WebUI ──────────────────────────────────────────────────────────────
|
|
8
|
+
WEBUI_PORT=3000
|
|
9
|
+
WEBUI_AUTH=false
|
|
10
|
+
|
|
11
|
+
# ── n8n ─────────────────────────────────────────────────────────────────────
|
|
12
|
+
N8N_PORT=5678
|
|
13
|
+
|
|
14
|
+
# ── Supabase (Postgres) ────────────────────────────────────────────────────
|
|
15
|
+
SUPABASE_DB_PORT=5432
|
|
16
|
+
SUPABASE_DB_PASSWORD=postgres
|
|
17
|
+
SUPABASE_DB_NAME=app
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Local AI Development Stack
|
|
2
|
+
# Generated by javi-forge — https://github.com/JNZader/javi-forge
|
|
3
|
+
#
|
|
4
|
+
# Usage:
|
|
5
|
+
# docker compose up -d # Start Ollama (core)
|
|
6
|
+
# docker compose --profile ui up -d # Start Ollama + Open WebUI
|
|
7
|
+
# docker compose --profile auto up -d # Start Ollama + n8n + Open WebUI
|
|
8
|
+
# docker compose --profile full up -d # Start everything
|
|
9
|
+
#
|
|
10
|
+
# Pull a model after starting:
|
|
11
|
+
# docker exec ollama ollama pull llama3.2
|
|
12
|
+
# docker exec ollama ollama pull codellama
|
|
13
|
+
|
|
14
|
+
services:
|
|
15
|
+
# ── Core: Ollama (local LLM inference) ────────────────────────────────────
|
|
16
|
+
ollama:
|
|
17
|
+
image: ollama/ollama:latest
|
|
18
|
+
container_name: ollama
|
|
19
|
+
restart: unless-stopped
|
|
20
|
+
ports:
|
|
21
|
+
- "${OLLAMA_PORT:-11434}:11434"
|
|
22
|
+
volumes:
|
|
23
|
+
- ollama_data:/root/.ollama
|
|
24
|
+
environment:
|
|
25
|
+
- OLLAMA_HOST=0.0.0.0
|
|
26
|
+
# Uncomment for NVIDIA GPU support:
|
|
27
|
+
# deploy:
|
|
28
|
+
# resources:
|
|
29
|
+
# reservations:
|
|
30
|
+
# devices:
|
|
31
|
+
# - driver: nvidia
|
|
32
|
+
# count: all
|
|
33
|
+
# capabilities: [gpu]
|
|
34
|
+
healthcheck:
|
|
35
|
+
test: ["CMD-SHELL", "curl -f http://localhost:11434/api/tags || exit 1"]
|
|
36
|
+
interval: 30s
|
|
37
|
+
timeout: 10s
|
|
38
|
+
retries: 3
|
|
39
|
+
start_period: 10s
|
|
40
|
+
|
|
41
|
+
# ── Optional: Open WebUI (chat interface) ─────────────────────────────────
|
|
42
|
+
open-webui:
|
|
43
|
+
image: ghcr.io/open-webui/open-webui:main
|
|
44
|
+
container_name: open-webui
|
|
45
|
+
profiles: ["ui", "auto", "full"]
|
|
46
|
+
restart: unless-stopped
|
|
47
|
+
ports:
|
|
48
|
+
- "${WEBUI_PORT:-3000}:8080"
|
|
49
|
+
volumes:
|
|
50
|
+
- open_webui_data:/app/backend/data
|
|
51
|
+
environment:
|
|
52
|
+
- OLLAMA_BASE_URL=http://ollama:11434
|
|
53
|
+
- WEBUI_AUTH=${WEBUI_AUTH:-false}
|
|
54
|
+
depends_on:
|
|
55
|
+
ollama:
|
|
56
|
+
condition: service_healthy
|
|
57
|
+
|
|
58
|
+
# ── Optional: n8n (workflow automation) ───────────────────────────────────
|
|
59
|
+
n8n:
|
|
60
|
+
image: n8nio/n8n:latest
|
|
61
|
+
container_name: n8n
|
|
62
|
+
profiles: ["auto", "full"]
|
|
63
|
+
restart: unless-stopped
|
|
64
|
+
ports:
|
|
65
|
+
- "${N8N_PORT:-5678}:5678"
|
|
66
|
+
volumes:
|
|
67
|
+
- n8n_data:/home/node/.n8n
|
|
68
|
+
environment:
|
|
69
|
+
- N8N_SECURE_COOKIE=false
|
|
70
|
+
- OLLAMA_HOST=http://ollama:11434
|
|
71
|
+
|
|
72
|
+
# ── Optional: Supabase (Postgres + Auth + Realtime) ───────────────────────
|
|
73
|
+
supabase-db:
|
|
74
|
+
image: supabase/postgres:15.6.1.145
|
|
75
|
+
container_name: supabase-db
|
|
76
|
+
profiles: ["full"]
|
|
77
|
+
restart: unless-stopped
|
|
78
|
+
ports:
|
|
79
|
+
- "${SUPABASE_DB_PORT:-5432}:5432"
|
|
80
|
+
volumes:
|
|
81
|
+
- supabase_db_data:/var/lib/postgresql/data
|
|
82
|
+
environment:
|
|
83
|
+
POSTGRES_PASSWORD: ${SUPABASE_DB_PASSWORD:-postgres}
|
|
84
|
+
POSTGRES_DB: ${SUPABASE_DB_NAME:-app}
|
|
85
|
+
healthcheck:
|
|
86
|
+
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
|
87
|
+
interval: 10s
|
|
88
|
+
timeout: 5s
|
|
89
|
+
retries: 5
|
|
90
|
+
|
|
91
|
+
volumes:
|
|
92
|
+
ollama_data:
|
|
93
|
+
open_webui_data:
|
|
94
|
+
n8n_data:
|
|
95
|
+
supabase_db_data:
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/anthropics/claude-code/main/settings-schema.json",
|
|
3
|
+
"_comment": "Runtime security hooks (kiteguard-style) — scaffolded by javi-forge",
|
|
4
|
+
"hooks": {
|
|
5
|
+
"PreToolUse": [
|
|
6
|
+
{
|
|
7
|
+
"matcher": "Bash",
|
|
8
|
+
"hook": "COMMAND=\"$CLAUDE_TOOL_INPUT\"\nBLOCKED_PATTERNS=(\n 'rm -rf /'\n 'rm -rf ~'\n 'chmod 777'\n 'curl.*|.*sh'\n 'wget.*|.*sh'\n 'eval.*\\$'\n 'base64.*-d.*|.*sh'\n ':(){:|:&};:'\n)\nfor pattern in \"${BLOCKED_PATTERNS[@]}\"; do\n if echo \"$COMMAND\" | grep -qE \"$pattern\"; then\n echo \"BLOCKED: Dangerous command pattern detected: $pattern\"\n exit 2\n fi\ndone\nexit 0",
|
|
9
|
+
"description": "Block dangerous shell commands (rm -rf /, chmod 777, pipe-to-sh, fork bombs)"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"matcher": "Bash",
|
|
13
|
+
"hook": "COMMAND=\"$CLAUDE_TOOL_INPUT\"\nSENSITIVE_PATHS=(\n '.env'\n '.env.local'\n '.env.production'\n 'credentials'\n 'secrets'\n '.ssh'\n '.gnupg'\n '.aws/credentials'\n)\nfor path in \"${SENSITIVE_PATHS[@]}\"; do\n if echo \"$COMMAND\" | grep -qE \"(cat|less|head|tail|bat|read).*$path\"; then\n echo \"BLOCKED: Attempt to read sensitive file: $path\"\n exit 2\n fi\ndone\nexit 0",
|
|
14
|
+
"description": "Prevent reading sensitive files (.env, credentials, SSH keys)"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"matcher": "Write|Edit",
|
|
18
|
+
"hook": "INPUT=\"$CLAUDE_TOOL_INPUT\"\nPROTECTED_FILES=(\n '.env'\n '.env.production'\n 'credentials.json'\n 'serviceAccountKey.json'\n '.ssh/'\n '.gnupg/'\n)\nfor pf in \"${PROTECTED_FILES[@]}\"; do\n if echo \"$INPUT\" | grep -q \"$pf\"; then\n echo \"BLOCKED: Cannot modify protected file: $pf\"\n exit 2\n fi\ndone\nexit 0",
|
|
19
|
+
"description": "Prevent writing to credential and secret files"
|
|
20
|
+
}
|
|
21
|
+
],
|
|
22
|
+
"PostToolUse": [
|
|
23
|
+
{
|
|
24
|
+
"matcher": "Bash",
|
|
25
|
+
"hook": "OUTPUT=\"$CLAUDE_TOOL_OUTPUT\"\nLEAK_PATTERNS=(\n 'AKIA[0-9A-Z]{16}'\n 'ghp_[A-Za-z0-9]{36}'\n 'sk_live_[0-9a-zA-Z]{24,}'\n 'xox[baprs]-[0-9a-zA-Z-]+'\n '-----BEGIN.*PRIVATE KEY-----'\n)\nfor pattern in \"${LEAK_PATTERNS[@]}\"; do\n if echo \"$OUTPUT\" | grep -qE \"$pattern\"; then\n echo \"WARNING: Command output may contain secrets. Review carefully.\"\n exit 0\n fi\ndone\nexit 0",
|
|
26
|
+
"description": "Warn if command output contains potential secrets"
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# =============================================================================
|
|
3
|
+
# SECURITY LAYER 6: Commit Message Signing Reminder (commit-msg)
|
|
4
|
+
# =============================================================================
|
|
5
|
+
# Reminds developers to sign commits if signing is configured but the
|
|
6
|
+
# current commit is not signed. Non-blocking — just a nudge.
|
|
7
|
+
#
|
|
8
|
+
# This complements layer 4 (pre-push signing) with an earlier reminder.
|
|
9
|
+
# =============================================================================
|
|
10
|
+
|
|
11
|
+
set -e
|
|
12
|
+
|
|
13
|
+
YELLOW='\033[1;33m'
|
|
14
|
+
CYAN='\033[0;36m'
|
|
15
|
+
GREEN='\033[0;32m'
|
|
16
|
+
NC='\033[0m'
|
|
17
|
+
|
|
18
|
+
echo "SECURITY [6/6]: Commit signing reminder..."
|
|
19
|
+
|
|
20
|
+
GPG_SIGN=$(git config --get commit.gpgsign 2>/dev/null || echo "false")
|
|
21
|
+
|
|
22
|
+
if [ "$GPG_SIGN" = "true" ]; then
|
|
23
|
+
echo -e "${GREEN} Commit signing is enabled. Good.${NC}"
|
|
24
|
+
else
|
|
25
|
+
echo -e "${YELLOW} Tip: Enable commit signing for supply-chain security.${NC}"
|
|
26
|
+
echo -e "${CYAN} git config commit.gpgsign true${NC}"
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
exit 0
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# =============================================================================
|
|
3
|
+
# SECURITY LAYER 3: Permission Boundaries (pre-commit)
|
|
4
|
+
# =============================================================================
|
|
5
|
+
# Validates that staged files don't introduce overly permissive file modes,
|
|
6
|
+
# executable flags on non-script files, or world-writable permissions.
|
|
7
|
+
#
|
|
8
|
+
# To skip: git commit --no-verify (NOT recommended)
|
|
9
|
+
# =============================================================================
|
|
10
|
+
|
|
11
|
+
set -e
|
|
12
|
+
|
|
13
|
+
RED='\033[0;31m'
|
|
14
|
+
YELLOW='\033[1;33m'
|
|
15
|
+
GREEN='\033[0;32m'
|
|
16
|
+
NC='\033[0m'
|
|
17
|
+
|
|
18
|
+
echo "SECURITY [3/6]: Permission boundary check..."
|
|
19
|
+
|
|
20
|
+
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM)
|
|
21
|
+
|
|
22
|
+
if [ -z "$STAGED_FILES" ]; then
|
|
23
|
+
echo -e "${GREEN} No staged files to check.${NC}"
|
|
24
|
+
exit 0
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
ISSUES=0
|
|
28
|
+
|
|
29
|
+
# Check for world-writable files (o+w)
|
|
30
|
+
for file in $STAGED_FILES; do
|
|
31
|
+
if [ -f "$file" ]; then
|
|
32
|
+
PERMS=$(stat -c '%a' "$file" 2>/dev/null || stat -f '%Lp' "$file" 2>/dev/null || echo "")
|
|
33
|
+
if [ -n "$PERMS" ]; then
|
|
34
|
+
# Check if last digit (other permissions) has write (2, 3, 6, 7)
|
|
35
|
+
OTHERS=${PERMS: -1}
|
|
36
|
+
if [[ "$OTHERS" =~ [2367] ]]; then
|
|
37
|
+
echo -e "${RED} World-writable: $file (mode $PERMS)${NC}"
|
|
38
|
+
ISSUES=$((ISSUES + 1))
|
|
39
|
+
fi
|
|
40
|
+
fi
|
|
41
|
+
fi
|
|
42
|
+
done
|
|
43
|
+
|
|
44
|
+
# Check for executable flag on non-script files
|
|
45
|
+
SCRIPT_EXTENSIONS='\.sh$|\.bash$|\.zsh$|\.py$|\.rb$|\.pl$'
|
|
46
|
+
for file in $STAGED_FILES; do
|
|
47
|
+
if [ -f "$file" ] && [ -x "$file" ]; then
|
|
48
|
+
# Allow scripts and hooks (no extension = likely a hook)
|
|
49
|
+
BASENAME=$(basename "$file")
|
|
50
|
+
if echo "$file" | grep -qE "$SCRIPT_EXTENSIONS"; then
|
|
51
|
+
continue
|
|
52
|
+
fi
|
|
53
|
+
# Allow files in hooks/ directories
|
|
54
|
+
if echo "$file" | grep -q '/hooks/'; then
|
|
55
|
+
continue
|
|
56
|
+
fi
|
|
57
|
+
# Allow if it has a shebang
|
|
58
|
+
if head -1 "$file" 2>/dev/null | grep -q '^#!'; then
|
|
59
|
+
continue
|
|
60
|
+
fi
|
|
61
|
+
echo -e "${YELLOW} Unexpected executable: $file${NC}"
|
|
62
|
+
ISSUES=$((ISSUES + 1))
|
|
63
|
+
fi
|
|
64
|
+
done
|
|
65
|
+
|
|
66
|
+
if [ "$ISSUES" -gt 0 ]; then
|
|
67
|
+
echo -e ""
|
|
68
|
+
echo -e "${RED}COMMIT BLOCKED: $ISSUES permission issue(s) found.${NC}"
|
|
69
|
+
echo -e "${YELLOW} Fix file permissions before committing.${NC}"
|
|
70
|
+
exit 1
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
echo -e "${GREEN} Permissions OK.${NC}"
|
|
74
|
+
exit 0
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# =============================================================================
|
|
3
|
+
# SECURITY LAYER 1: Secret Scanning (pre-commit)
|
|
4
|
+
# =============================================================================
|
|
5
|
+
# Scans staged files for hardcoded secrets, API keys, and credentials.
|
|
6
|
+
# Runs on every commit to prevent accidental secret leaks.
|
|
7
|
+
#
|
|
8
|
+
# Requires: git
|
|
9
|
+
# To skip: git commit --no-verify (NOT recommended)
|
|
10
|
+
# =============================================================================
|
|
11
|
+
|
|
12
|
+
set -e
|
|
13
|
+
|
|
14
|
+
RED='\033[0;31m'
|
|
15
|
+
YELLOW='\033[1;33m'
|
|
16
|
+
GREEN='\033[0;32m'
|
|
17
|
+
NC='\033[0m'
|
|
18
|
+
|
|
19
|
+
echo "SECURITY [1/6]: Secret scanning..."
|
|
20
|
+
|
|
21
|
+
# Patterns that indicate hardcoded secrets
|
|
22
|
+
SECRET_PATTERNS=(
|
|
23
|
+
# AWS
|
|
24
|
+
'AKIA[0-9A-Z]{16}'
|
|
25
|
+
# Generic API keys (long hex/base64 strings assigned to key-like vars)
|
|
26
|
+
'(?i)(api[_-]?key|api[_-]?secret|access[_-]?token|auth[_-]?token)\s*[:=]\s*["\x27][A-Za-z0-9+/=_-]{20,}["\x27]'
|
|
27
|
+
# Private keys
|
|
28
|
+
'-----BEGIN (RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----'
|
|
29
|
+
# GitHub tokens
|
|
30
|
+
'gh[pousr]_[A-Za-z0-9_]{36,}'
|
|
31
|
+
# Generic password assignments
|
|
32
|
+
'(?i)(password|passwd|pwd)\s*[:=]\s*["\x27][^\s"'\'']{8,}["\x27]'
|
|
33
|
+
# Slack tokens
|
|
34
|
+
'xox[baprs]-[0-9a-zA-Z-]+'
|
|
35
|
+
# Stripe keys
|
|
36
|
+
'sk_live_[0-9a-zA-Z]{24,}'
|
|
37
|
+
'rk_live_[0-9a-zA-Z]{24,}'
|
|
38
|
+
# SendGrid
|
|
39
|
+
'SG\.[A-Za-z0-9_-]{22}\.[A-Za-z0-9_-]{43}'
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM)
|
|
43
|
+
|
|
44
|
+
if [ -z "$STAGED_FILES" ]; then
|
|
45
|
+
echo -e "${GREEN} No staged files to scan.${NC}"
|
|
46
|
+
exit 0
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
FOUND=0
|
|
50
|
+
for pattern in "${SECRET_PATTERNS[@]}"; do
|
|
51
|
+
# Use grep -P for perl-compatible regex, fall back to -E
|
|
52
|
+
MATCHES=$(echo "$STAGED_FILES" | xargs git diff --cached -- | grep -nP "$pattern" 2>/dev/null || true)
|
|
53
|
+
if [ -n "$MATCHES" ]; then
|
|
54
|
+
if [ "$FOUND" -eq 0 ]; then
|
|
55
|
+
echo -e "${RED} Potential secrets detected in staged changes:${NC}"
|
|
56
|
+
fi
|
|
57
|
+
echo -e "${YELLOW} Pattern: $pattern${NC}"
|
|
58
|
+
echo "$MATCHES" | head -5 | while read -r line; do
|
|
59
|
+
echo -e "${RED} $line${NC}"
|
|
60
|
+
done
|
|
61
|
+
FOUND=$((FOUND + 1))
|
|
62
|
+
fi
|
|
63
|
+
done
|
|
64
|
+
|
|
65
|
+
if [ "$FOUND" -gt 0 ]; then
|
|
66
|
+
echo -e ""
|
|
67
|
+
echo -e "${RED}COMMIT BLOCKED: $FOUND secret pattern(s) detected.${NC}"
|
|
68
|
+
echo -e "${YELLOW} Remove secrets and use environment variables instead.${NC}"
|
|
69
|
+
echo -e "${YELLOW} If this is a false positive, add to .secret-scan-ignore${NC}"
|
|
70
|
+
exit 1
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
echo -e "${GREEN} No secrets found.${NC}"
|
|
74
|
+
exit 0
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# =============================================================================
|
|
3
|
+
# SECURITY LAYER 5: Branch Protection (pre-push)
|
|
4
|
+
# =============================================================================
|
|
5
|
+
# Prevents direct pushes to protected branches (main, master, release/*).
|
|
6
|
+
# Forces use of pull requests for protected branches.
|
|
7
|
+
#
|
|
8
|
+
# To skip: git push --no-verify (NOT recommended)
|
|
9
|
+
# =============================================================================
|
|
10
|
+
|
|
11
|
+
set -e
|
|
12
|
+
|
|
13
|
+
RED='\033[0;31m'
|
|
14
|
+
YELLOW='\033[1;33m'
|
|
15
|
+
GREEN='\033[0;32m'
|
|
16
|
+
NC='\033[0m'
|
|
17
|
+
|
|
18
|
+
echo "SECURITY [5/6]: Branch protection check..."
|
|
19
|
+
|
|
20
|
+
# Protected branch patterns
|
|
21
|
+
PROTECTED_BRANCHES=(
|
|
22
|
+
"main"
|
|
23
|
+
"master"
|
|
24
|
+
"release/*"
|
|
25
|
+
"production"
|
|
26
|
+
"staging"
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
|
|
30
|
+
|
|
31
|
+
if [ -z "$CURRENT_BRANCH" ]; then
|
|
32
|
+
echo -e "${YELLOW} Could not determine current branch. Skipping.${NC}"
|
|
33
|
+
exit 0
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
for pattern in "${PROTECTED_BRANCHES[@]}"; do
|
|
37
|
+
# Support glob patterns
|
|
38
|
+
if [[ "$CURRENT_BRANCH" == $pattern ]]; then
|
|
39
|
+
# Check if we're on CI (allow CI pushes)
|
|
40
|
+
if [ -n "$CI" ] || [ -n "$GITHUB_ACTIONS" ] || [ -n "$GITLAB_CI" ]; then
|
|
41
|
+
echo -e "${GREEN} CI environment detected. Allowing push to $CURRENT_BRANCH.${NC}"
|
|
42
|
+
exit 0
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
echo -e ""
|
|
46
|
+
echo -e "${RED}PUSH BLOCKED: Direct push to protected branch '$CURRENT_BRANCH'.${NC}"
|
|
47
|
+
echo -e ""
|
|
48
|
+
echo -e "${YELLOW} Protected branches require pull requests:${NC}"
|
|
49
|
+
for b in "${PROTECTED_BRANCHES[@]}"; do
|
|
50
|
+
echo -e "${YELLOW} - $b${NC}"
|
|
51
|
+
done
|
|
52
|
+
echo -e ""
|
|
53
|
+
echo -e "${YELLOW} Create a feature branch and open a PR instead:${NC}"
|
|
54
|
+
echo -e "${GREEN} git checkout -b feat/my-feature${NC}"
|
|
55
|
+
echo -e "${GREEN} git push -u origin feat/my-feature${NC}"
|
|
56
|
+
echo -e "${GREEN} gh pr create${NC}"
|
|
57
|
+
exit 1
|
|
58
|
+
fi
|
|
59
|
+
done
|
|
60
|
+
|
|
61
|
+
echo -e "${GREEN} Branch '$CURRENT_BRANCH' is not protected. OK.${NC}"
|
|
62
|
+
exit 0
|