@ipul-labs/forge 1.16.4 → 1.16.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ipul-labs/forge",
3
- "version": "1.16.4",
3
+ "version": "1.16.5",
4
4
  "description": "AI-powered code change & deploy control panel — connects Claude, GitLab, and Kubernetes",
5
5
  "readmeFilename": "README.md",
6
6
  "type": "module",
@@ -16,6 +16,7 @@
16
16
  "files": [
17
17
  "README.md",
18
18
  "bin",
19
+ "scripts",
19
20
  "server/forge",
20
21
  "dist"
21
22
  ],
@@ -26,7 +27,7 @@
26
27
  "typecheck": "tsc --noEmit",
27
28
  "test": "vitest run",
28
29
  "test:watch": "vitest",
29
- "prepack": "node -e \"const p=require('./package.json'),fs=require('fs');p.readme=fs.readFileSync('README.md','utf8');fs.writeFileSync('package.json',JSON.stringify(p,null,2)+'\\n')\""
30
+ "prepack": "node scripts/inject-readme.cjs"
30
31
  },
31
32
  "dependencies": {
32
33
  "@kubernetes/client-node": "^1.0.0",
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ const fs = require("fs");
3
+ const path = require("path");
4
+
5
+ const root = path.resolve(__dirname, "..");
6
+ const pkgPath = path.join(root, "package.json");
7
+ const readmePath = path.join(root, "README.md");
8
+
9
+ if (!fs.existsSync(readmePath)) process.exit(0);
10
+
11
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
12
+ pkg.readme = fs.readFileSync(readmePath, "utf8");
13
+ fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
@@ -0,0 +1,134 @@
1
+ #!/usr/bin/env bash
2
+ # ──────────────────────────────────────────────────────────────────────────────
3
+ # Local dry-run of the same manifests + commands the *-rollout CI jobs execute.
4
+ # Catches manifest bugs, missing env vars, image-pull problems, and rollout
5
+ # logic errors BEFORE burning another CI cycle. Uses your local kubeconfig
6
+ # (admin creds against k3s), so it does NOT test the agent SA's RBAC — for
7
+ # that, the actual CI run is still the source of truth.
8
+ #
9
+ # ./scripts/local-rollout.sh [test|prep|prod] [image-tag]
10
+ #
11
+ # Defaults: env=test, image-tag=<current deployment's tag OR "latest">
12
+ #
13
+ # Exits non-zero on the first failure with a clear message.
14
+ # ──────────────────────────────────────────────────────────────────────────────
15
+
16
+ set -euo pipefail
17
+
18
+ ENV=${1:-test}
19
+ IMAGE_TAG=${2:-}
20
+
21
+ case "$ENV" in
22
+ test) NS=test; DEPLOY=forge-test; MANIFEST_DIR=manifests/test/forge-test ;;
23
+ prep) NS=prep; DEPLOY=forge-prep; MANIFEST_DIR=manifests/prep/forge-prep ;;
24
+ prod) NS=prod; DEPLOY=forge; MANIFEST_DIR=manifests/prod/forge ;;
25
+ *) echo "Usage: $0 [test|prep|prod] [image-tag]"; exit 2 ;;
26
+ esac
27
+
28
+ # All envs pull from the same consolidated image repo. Per-env differences
29
+ # live in the manifests, not in the image.
30
+ IMAGE_REPO=iplayulisten/forge
31
+
32
+ # ─── Helpers ──────────────────────────────────────────────────────────────────
33
+
34
+ c_step() { printf '\n\033[1;34m── %s ──\033[0m\n' "$*"; }
35
+ c_ok() { printf '\033[1;32m✓\033[0m %s\n' "$*"; }
36
+ c_warn() { printf '\033[1;33m⚠\033[0m %s\n' "$*"; }
37
+ c_fail() { printf '\033[1;31m✗\033[0m %s\n' "$*"; exit 1; }
38
+
39
+ require() { command -v "$1" >/dev/null 2>&1 || c_fail "missing required binary: $1"; }
40
+
41
+ # ─── Preflight ────────────────────────────────────────────────────────────────
42
+
43
+ c_step "Preflight"
44
+ require kubectl
45
+ require openssl
46
+
47
+ CTX=$(kubectl config current-context 2>/dev/null || true)
48
+ [ -n "$CTX" ] || c_fail "no current kubectl context — set one with 'kubectl config use-context <name>'"
49
+ c_ok "kubectl context: $CTX"
50
+
51
+ if ! kubectl get ns "$NS" >/dev/null 2>&1; then
52
+ c_warn "namespace '$NS' doesn't exist — creating it"
53
+ kubectl create namespace "$NS"
54
+ c_ok "namespace '$NS' created"
55
+ else
56
+ c_ok "namespace '$NS' exists"
57
+ fi
58
+
59
+ [ -d "$MANIFEST_DIR" ] || c_fail "manifest dir not found: $MANIFEST_DIR"
60
+ c_ok "manifest dir: $MANIFEST_DIR"
61
+
62
+ # ─── Secret creation (mirrors CI) ─────────────────────────────────────────────
63
+
64
+ c_step "Secret: forge-secret"
65
+ : "${FORGE_TOKEN:=}"
66
+ : "${ANTHROPIC_API_KEY:=}"
67
+ : "${GITLAB_TOKEN:=}"
68
+
69
+ if [ -z "$FORGE_TOKEN" ]; then
70
+ c_warn "FORGE_TOKEN unset — generating a throwaway for this run"
71
+ FORGE_TOKEN=$(openssl rand -hex 32)
72
+ fi
73
+ [ -n "$GITLAB_TOKEN" ] || c_warn "GITLAB_TOKEN unset (forge backend needs this at runtime — ok for manifest test)"
74
+ [ -n "$ANTHROPIC_API_KEY" ] || c_warn "ANTHROPIC_API_KEY unset (optional, only needed for quota probe)"
75
+
76
+ kubectl create secret generic forge-secret \
77
+ --from-literal=FORGE_TOKEN="$FORGE_TOKEN" \
78
+ --from-literal=ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY" \
79
+ --from-literal=GITLAB_TOKEN="$GITLAB_TOKEN" \
80
+ -n "$NS" --dry-run=client -o yaml | kubectl apply -f -
81
+ c_ok "forge-secret applied"
82
+
83
+ # ─── Apply manifests in CI order ──────────────────────────────────────────────
84
+
85
+ for f in config-map.yaml service.yaml deployment.yaml; do
86
+ path="$MANIFEST_DIR/$f"
87
+ [ -f "$path" ] || c_fail "missing manifest: $path"
88
+ c_step "Applying $f"
89
+ kubectl apply -f "$path"
90
+ c_ok "$f applied"
91
+ done
92
+
93
+ # ─── Image tag ────────────────────────────────────────────────────────────────
94
+
95
+ if [ -z "$IMAGE_TAG" ]; then
96
+ CURRENT_IMAGE=$(kubectl get deploy/"$DEPLOY" -n "$NS" -o jsonpath='{.spec.template.spec.containers[0].image}' 2>/dev/null || true)
97
+ if [ -n "$CURRENT_IMAGE" ] && [ "$CURRENT_IMAGE" != "*__VERSION__*" ] && [[ "$CURRENT_IMAGE" != *__VERSION__* ]]; then
98
+ IMAGE_TAG=${CURRENT_IMAGE##*:}
99
+ c_ok "reusing current deployment image tag: $IMAGE_TAG"
100
+ else
101
+ IMAGE_TAG=latest
102
+ c_warn "no usable tag in current deployment — falling back to '$IMAGE_TAG' (may fail to pull)"
103
+ fi
104
+ fi
105
+
106
+ c_step "kubectl set image → ${IMAGE_REPO}:${IMAGE_TAG}"
107
+ kubectl set image deployment/"$DEPLOY" -n "$NS" "${DEPLOY}=${IMAGE_REPO}:${IMAGE_TAG}"
108
+ c_ok "image set"
109
+
110
+ # ─── Rollout ──────────────────────────────────────────────────────────────────
111
+
112
+ c_step "Rollout status (120s timeout)"
113
+ if kubectl rollout status deployment/"$DEPLOY" -n "$NS" --timeout=120s; then
114
+ c_ok "rollout succeeded"
115
+ else
116
+ c_warn "rollout did not finish in 120s — dumping pod state to help diagnose"
117
+ kubectl get pods -n "$NS" -l app="$DEPLOY" -o wide || true
118
+ kubectl describe deployment/"$DEPLOY" -n "$NS" | sed -n '/Events:/,$p' || true
119
+ kubectl get events -n "$NS" --sort-by=.lastTimestamp | tail -20 || true
120
+ exit 1
121
+ fi
122
+
123
+ # ─── Summary ──────────────────────────────────────────────────────────────────
124
+
125
+ c_step "Summary"
126
+ kubectl get pods,svc,deploy -n "$NS" -l app="$DEPLOY"
127
+ c_ok "local rollout for '$ENV' passed"
128
+ echo
129
+ echo "Next: push CI. If CI fails where this didn't, it's agent SA RBAC."
130
+ echo "To tear down what this script created:"
131
+ echo " kubectl delete -f $MANIFEST_DIR/deployment.yaml -n $NS"
132
+ echo " kubectl delete -f $MANIFEST_DIR/service.yaml -n $NS"
133
+ echo " kubectl delete -f $MANIFEST_DIR/config-map.yaml -n $NS"
134
+ echo " kubectl delete secret forge-secret -n $NS"