javi-forge 1.22.1 → 1.22.3
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/LICENSE +21 -0
- package/dist/commands/ci.js +2 -2
- package/dist/lib/common.d.ts +0 -2
- package/dist/lib/common.js +0 -6
- package/dist/lib/docker.js +2 -1
- package/modules/engram/install-engram.sh +12 -1
- package/package.json +1 -1
- package/templates/github/ci-go.yml +1 -1
- package/templates/github/ci-java.yml +1 -1
- package/templates/github/ci-node.yml +1 -1
- package/templates/github/ci-python.yml +1 -1
- package/templates/github/ci-rust.yml +1 -1
- package/templates/github/deploy-docker-zero-downtime.yml +16 -8
- package/templates/github/ghagga-review.yml +3 -1
- package/templates/local-ai/.env.example +9 -2
- package/templates/local-ai/docker-compose.yml +14 -6
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 JNZader
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/commands/ci.js
CHANGED
|
@@ -734,8 +734,8 @@ async function runStep(options) {
|
|
|
734
734
|
"the image must be resolved before any step runs");
|
|
735
735
|
}
|
|
736
736
|
const workdir = runner.directory !== "."
|
|
737
|
-
?
|
|
738
|
-
:
|
|
737
|
+
? `${CONTAINER_WORKDIR}/${runner.directory}`
|
|
738
|
+
: CONTAINER_WORKDIR;
|
|
739
739
|
const result = await runInContainer({
|
|
740
740
|
projectDir,
|
|
741
741
|
image,
|
package/dist/lib/common.d.ts
CHANGED
|
@@ -10,8 +10,6 @@ export declare function backupIfExists(filePath: string): Promise<boolean>;
|
|
|
10
10
|
export declare function ensureDirExists(dirPath: string): Promise<void>;
|
|
11
11
|
/** Check if a directory is a git repository */
|
|
12
12
|
export declare function isGitRepo(dir: string): Promise<boolean>;
|
|
13
|
-
/** Resolve the forge assets root (the package root directory) */
|
|
14
|
-
export declare function getForgeRoot(): string;
|
|
15
13
|
/** Stack display names */
|
|
16
14
|
export declare const STACK_LABELS: Record<Stack, string>;
|
|
17
15
|
//# sourceMappingURL=common.d.ts.map
|
package/dist/lib/common.js
CHANGED
|
@@ -100,12 +100,6 @@ export async function ensureDirExists(dirPath) {
|
|
|
100
100
|
export async function isGitRepo(dir) {
|
|
101
101
|
return fs.pathExists(path.join(dir, ".git"));
|
|
102
102
|
}
|
|
103
|
-
/** Resolve the forge assets root (the package root directory) */
|
|
104
|
-
export function getForgeRoot() {
|
|
105
|
-
// When running from dist/, go up one level
|
|
106
|
-
const thisDir = path.dirname(new URL(import.meta.url).pathname);
|
|
107
|
-
return path.resolve(thisDir, "..");
|
|
108
|
-
}
|
|
109
103
|
/** Stack display names */
|
|
110
104
|
export const STACK_LABELS = {
|
|
111
105
|
node: "Node.js / TypeScript",
|
package/dist/lib/docker.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import crypto from "node:crypto";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
4
5
|
import fs from "fs-extra";
|
|
5
6
|
import { execFileAsync } from "./exec.js";
|
|
6
7
|
// =============================================================================
|
|
@@ -139,7 +140,7 @@ export async function ensureImage(options) {
|
|
|
139
140
|
else {
|
|
140
141
|
imageName = getImageName(stack);
|
|
141
142
|
const dockerDir = dockerfilesDir ??
|
|
142
|
-
path.join(path.dirname(
|
|
143
|
+
path.join(path.dirname(fileURLToPath(import.meta.url)), "../../ci-local/docker");
|
|
143
144
|
dockerfilePath = path.join(dockerDir, `${stack}.Dockerfile`);
|
|
144
145
|
contextDir = dockerDir;
|
|
145
146
|
// Write Dockerfile if it doesn't exist yet (first run)
|
|
@@ -153,20 +153,31 @@ fi
|
|
|
153
153
|
CHECKSUM_URL="https://github.com/${REPO}/releases/download/${VERSION}/checksums.txt"
|
|
154
154
|
if curl -fsSL -o "${TMP_DIR}/checksums.txt" "$CHECKSUM_URL" 2>/dev/null; then
|
|
155
155
|
echo -e "${CYAN}Verifying checksum...${NC}"
|
|
156
|
+
CHECKSUM_RAN=false
|
|
156
157
|
pushd "$TMP_DIR" > /dev/null
|
|
157
158
|
if command -v sha256sum &>/dev/null; then
|
|
158
159
|
sha256sum -c checksums.txt --ignore-missing || {
|
|
159
160
|
echo -e "${RED}Checksum verification failed!${NC}"
|
|
160
161
|
exit 1
|
|
161
162
|
}
|
|
163
|
+
CHECKSUM_RAN=true
|
|
162
164
|
elif command -v shasum &>/dev/null; then
|
|
163
165
|
shasum -a 256 -c checksums.txt --ignore-missing || {
|
|
164
166
|
echo -e "${RED}Checksum verification failed!${NC}"
|
|
165
167
|
exit 1
|
|
166
168
|
}
|
|
169
|
+
CHECKSUM_RAN=true
|
|
167
170
|
fi
|
|
168
171
|
popd > /dev/null
|
|
169
|
-
|
|
172
|
+
if [[ "$CHECKSUM_RAN" == "true" ]]; then
|
|
173
|
+
echo -e "${GREEN}Checksum verified${NC}"
|
|
174
|
+
elif [[ "$NO_VERIFY" == "true" ]]; then
|
|
175
|
+
echo -e "${YELLOW}WARNING: neither sha256sum nor shasum found; skipping checksum verification (--no-verify)${NC}"
|
|
176
|
+
else
|
|
177
|
+
echo -e "${RED}Error: neither sha256sum nor shasum is available. Cannot verify download integrity.${NC}"
|
|
178
|
+
echo -e "${YELLOW}Install coreutils (sha256sum) or perl (shasum), or use --no-verify to skip verification (NOT RECOMMENDED).${NC}"
|
|
179
|
+
exit 1
|
|
180
|
+
fi
|
|
170
181
|
else
|
|
171
182
|
if [[ "$NO_VERIFY" == "true" ]]; then
|
|
172
183
|
echo -e "${YELLOW}WARNING: Skipping checksum verification (--no-verify)${NC}"
|
package/package.json
CHANGED
|
@@ -34,7 +34,7 @@ concurrency:
|
|
|
34
34
|
|
|
35
35
|
jobs:
|
|
36
36
|
build:
|
|
37
|
-
uses: JNZader/javi-forge/.github/workflows/reusable-build-go.yml@main
|
|
37
|
+
uses: JNZader/javi-forge/.github/workflows/reusable-build-go.yml@667908ddb5e1aeb8e44ec65e84e4bc0e4f936d6c # main@2026-08-10
|
|
38
38
|
with:
|
|
39
39
|
go-version: '1.23' # Change to: 1.22, 1.23
|
|
40
40
|
run-lint: true
|
|
@@ -37,7 +37,7 @@ concurrency:
|
|
|
37
37
|
|
|
38
38
|
jobs:
|
|
39
39
|
build:
|
|
40
|
-
uses: JNZader/javi-forge/.github/workflows/reusable-build-java.yml@main
|
|
40
|
+
uses: JNZader/javi-forge/.github/workflows/reusable-build-java.yml@667908ddb5e1aeb8e44ec65e84e4bc0e4f936d6c # main@2026-08-10
|
|
41
41
|
with:
|
|
42
42
|
java-version: '21' # Change to your version: 17, 21, 25
|
|
43
43
|
run-spotless: true
|
|
@@ -34,7 +34,7 @@ concurrency:
|
|
|
34
34
|
|
|
35
35
|
jobs:
|
|
36
36
|
build:
|
|
37
|
-
uses: JNZader/javi-forge/.github/workflows/reusable-build-node.yml@main
|
|
37
|
+
uses: JNZader/javi-forge/.github/workflows/reusable-build-node.yml@667908ddb5e1aeb8e44ec65e84e4bc0e4f936d6c # main@2026-08-10
|
|
38
38
|
with:
|
|
39
39
|
node-version: '20' # Change to: 18, 20, 22
|
|
40
40
|
package-manager: 'npm' # Change to: npm, yarn, pnpm
|
|
@@ -34,7 +34,7 @@ concurrency:
|
|
|
34
34
|
|
|
35
35
|
jobs:
|
|
36
36
|
build:
|
|
37
|
-
uses: JNZader/javi-forge/.github/workflows/reusable-build-python.yml@main
|
|
37
|
+
uses: JNZader/javi-forge/.github/workflows/reusable-build-python.yml@667908ddb5e1aeb8e44ec65e84e4bc0e4f936d6c # main@2026-08-10
|
|
38
38
|
with:
|
|
39
39
|
python-version: '3.12' # Change to: 3.10, 3.11, 3.12
|
|
40
40
|
package-manager: 'pip' # Change to: pip, poetry, uv
|
|
@@ -34,7 +34,7 @@ concurrency:
|
|
|
34
34
|
|
|
35
35
|
jobs:
|
|
36
36
|
build:
|
|
37
|
-
uses: JNZader/javi-forge/.github/workflows/reusable-build-rust.yml@main
|
|
37
|
+
uses: JNZader/javi-forge/.github/workflows/reusable-build-rust.yml@667908ddb5e1aeb8e44ec65e84e4bc0e4f936d6c # main@2026-08-10
|
|
38
38
|
with:
|
|
39
39
|
toolchain: 'stable' # Change to: stable, beta, nightly
|
|
40
40
|
run-clippy: true
|
|
@@ -55,10 +55,10 @@ jobs:
|
|
|
55
55
|
outputs:
|
|
56
56
|
image_tag: ${{ steps.meta.outputs.tags }}
|
|
57
57
|
steps:
|
|
58
|
-
- uses: actions/checkout@v4
|
|
58
|
+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
|
|
59
59
|
|
|
60
60
|
- name: Log in to Container Registry
|
|
61
|
-
uses: docker/login-action@v3
|
|
61
|
+
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
|
|
62
62
|
with:
|
|
63
63
|
registry: ${{ env.REGISTRY }}
|
|
64
64
|
username: ${{ github.actor }}
|
|
@@ -66,7 +66,7 @@ jobs:
|
|
|
66
66
|
|
|
67
67
|
- name: Extract metadata
|
|
68
68
|
id: meta
|
|
69
|
-
uses: docker/metadata-action@v5
|
|
69
|
+
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
|
|
70
70
|
with:
|
|
71
71
|
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
|
72
72
|
tags: |
|
|
@@ -74,7 +74,7 @@ jobs:
|
|
|
74
74
|
type=raw,value=latest
|
|
75
75
|
|
|
76
76
|
- name: Build and push
|
|
77
|
-
uses: docker/build-push-action@v5
|
|
77
|
+
uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5.4.0
|
|
78
78
|
with:
|
|
79
79
|
context: .
|
|
80
80
|
push: true
|
|
@@ -88,11 +88,16 @@ jobs:
|
|
|
88
88
|
environment: production
|
|
89
89
|
steps:
|
|
90
90
|
- name: Deploy via SSH + docker rollout
|
|
91
|
-
uses: appleboy/ssh-action@v1
|
|
91
|
+
uses: appleboy/ssh-action@0ff4204d59e8e51228ff73bce53f80d53301dee2 # v1.2.5
|
|
92
|
+
env:
|
|
93
|
+
# Expression-injection hardening: the workflow_dispatch input is passed
|
|
94
|
+
# as an env var (never interpolated into the shell script text).
|
|
95
|
+
SERVICE: ${{ github.event.inputs.service || '__SERVICE_NAME__' }}
|
|
92
96
|
with:
|
|
93
97
|
host: ${{ env.DEPLOY_HOST }}
|
|
94
98
|
username: ${{ env.DEPLOY_USER }}
|
|
95
99
|
key: ${{ secrets.DEPLOY_KEY }}
|
|
100
|
+
envs: SERVICE
|
|
96
101
|
script: |
|
|
97
102
|
set -euo pipefail
|
|
98
103
|
|
|
@@ -102,7 +107,6 @@ jobs:
|
|
|
102
107
|
docker compose pull
|
|
103
108
|
|
|
104
109
|
# Zero-downtime rollout for the target service
|
|
105
|
-
SERVICE="${{ github.event.inputs.service || '__SERVICE_NAME__' }}"
|
|
106
110
|
echo "::group::Rolling out ${SERVICE}"
|
|
107
111
|
docker rollout "${SERVICE}"
|
|
108
112
|
echo "::endgroup::"
|
|
@@ -111,15 +115,19 @@ jobs:
|
|
|
111
115
|
docker image prune -f
|
|
112
116
|
|
|
113
117
|
- name: Verify deployment
|
|
114
|
-
uses: appleboy/ssh-action@v1
|
|
118
|
+
uses: appleboy/ssh-action@0ff4204d59e8e51228ff73bce53f80d53301dee2 # v1.2.5
|
|
119
|
+
env:
|
|
120
|
+
# Expression-injection hardening: the workflow_dispatch input is passed
|
|
121
|
+
# as an env var (never interpolated into the shell script text).
|
|
122
|
+
SERVICE: ${{ github.event.inputs.service || '__SERVICE_NAME__' }}
|
|
115
123
|
with:
|
|
116
124
|
host: ${{ env.DEPLOY_HOST }}
|
|
117
125
|
username: ${{ env.DEPLOY_USER }}
|
|
118
126
|
key: ${{ secrets.DEPLOY_KEY }}
|
|
127
|
+
envs: SERVICE
|
|
119
128
|
script: |
|
|
120
129
|
set -euo pipefail
|
|
121
130
|
cd ${{ secrets.DEPLOY_DIR || '~/app' }}
|
|
122
|
-
SERVICE="${{ github.event.inputs.service || '__SERVICE_NAME__' }}"
|
|
123
131
|
|
|
124
132
|
# Wait for healthcheck (max 60s)
|
|
125
133
|
TIMEOUT=60
|
|
@@ -21,7 +21,9 @@ jobs:
|
|
|
21
21
|
review:
|
|
22
22
|
runs-on: ubuntu-latest
|
|
23
23
|
steps:
|
|
24
|
-
- uses: actions/checkout@v4
|
|
24
|
+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
|
|
25
|
+
# TODO(pin): ghagga has no v1 tag upstream (releases start at v2) — this
|
|
26
|
+
# ref cannot be SHA-pinned as-is; pick a real released tag and pin it.
|
|
25
27
|
- uses: JNZader/ghagga@v1
|
|
26
28
|
with:
|
|
27
29
|
mode: simple
|
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
# Local AI Stack — Environment Variables
|
|
2
2
|
# Copy to .env in your project: cp .env.example .env
|
|
3
|
+
#
|
|
4
|
+
# Secure defaults: all ports bind to 127.0.0.1 (loopback), WebUI auth is ON,
|
|
5
|
+
# and SUPABASE_DB_PASSWORD is REQUIRED (no default) for the "full" profile.
|
|
3
6
|
|
|
4
7
|
# ── Ollama ──────────────────────────────────────────────────────────────────
|
|
5
8
|
OLLAMA_PORT=11434
|
|
6
9
|
|
|
7
10
|
# ── Open WebUI ──────────────────────────────────────────────────────────────
|
|
8
11
|
WEBUI_PORT=3000
|
|
9
|
-
|
|
12
|
+
# Auth is ON by default (create the admin account on first visit).
|
|
13
|
+
# Set to false ONLY if you accept an unauthenticated local UI.
|
|
14
|
+
WEBUI_AUTH=true
|
|
10
15
|
|
|
11
16
|
# ── n8n ─────────────────────────────────────────────────────────────────────
|
|
12
17
|
N8N_PORT=5678
|
|
13
18
|
|
|
14
19
|
# ── Supabase (Postgres) ────────────────────────────────────────────────────
|
|
15
20
|
SUPABASE_DB_PORT=5432
|
|
16
|
-
|
|
21
|
+
# CHANGE ME — required, no default (Postgres refuses to start if unset). Generate one:
|
|
22
|
+
# openssl rand -base64 24
|
|
23
|
+
SUPABASE_DB_PASSWORD=
|
|
17
24
|
SUPABASE_DB_NAME=app
|
|
@@ -18,7 +18,9 @@ services:
|
|
|
18
18
|
container_name: ollama
|
|
19
19
|
restart: unless-stopped
|
|
20
20
|
ports:
|
|
21
|
-
|
|
21
|
+
# Bound to loopback by default — this is a LOCAL dev stack. To expose on
|
|
22
|
+
# your LAN, change 127.0.0.1 to your interface IP (opt-in, not default).
|
|
23
|
+
- "127.0.0.1:${OLLAMA_PORT:-11434}:11434"
|
|
22
24
|
volumes:
|
|
23
25
|
- ollama_data:/root/.ollama
|
|
24
26
|
environment:
|
|
@@ -45,12 +47,14 @@ services:
|
|
|
45
47
|
profiles: ["ui", "auto", "full"]
|
|
46
48
|
restart: unless-stopped
|
|
47
49
|
ports:
|
|
48
|
-
- "
|
|
50
|
+
- "127.0.0.1:${WEBUI_PORT:-3000}:8080"
|
|
49
51
|
volumes:
|
|
50
52
|
- open_webui_data:/app/backend/data
|
|
51
53
|
environment:
|
|
52
54
|
- OLLAMA_BASE_URL=http://ollama:11434
|
|
53
|
-
|
|
55
|
+
# Auth ON by default. Set WEBUI_AUTH=false in .env only if you accept an
|
|
56
|
+
# unauthenticated UI (loopback-only mitigates, but any local process can reach it).
|
|
57
|
+
- WEBUI_AUTH=${WEBUI_AUTH:-true}
|
|
54
58
|
depends_on:
|
|
55
59
|
ollama:
|
|
56
60
|
condition: service_healthy
|
|
@@ -62,7 +66,7 @@ services:
|
|
|
62
66
|
profiles: ["auto", "full"]
|
|
63
67
|
restart: unless-stopped
|
|
64
68
|
ports:
|
|
65
|
-
- "
|
|
69
|
+
- "127.0.0.1:${N8N_PORT:-5678}:5678"
|
|
66
70
|
volumes:
|
|
67
71
|
- n8n_data:/home/node/.n8n
|
|
68
72
|
environment:
|
|
@@ -76,11 +80,15 @@ services:
|
|
|
76
80
|
profiles: ["full"]
|
|
77
81
|
restart: unless-stopped
|
|
78
82
|
ports:
|
|
79
|
-
- "
|
|
83
|
+
- "127.0.0.1:${SUPABASE_DB_PORT:-5432}:5432"
|
|
80
84
|
volumes:
|
|
81
85
|
- supabase_db_data:/var/lib/postgresql/data
|
|
82
86
|
environment:
|
|
83
|
-
|
|
87
|
+
# No insecure default — REQUIRED. If unset/empty, Postgres refuses to
|
|
88
|
+
# initialize ("superuser password is not specified"): set it in .env.
|
|
89
|
+
# Note: a compose-level `:?` guard would also break the core (ollama-only)
|
|
90
|
+
# profile at parse time, so the check is delegated to the postgres image.
|
|
91
|
+
POSTGRES_PASSWORD: ${SUPABASE_DB_PASSWORD}
|
|
84
92
|
POSTGRES_DB: ${SUPABASE_DB_NAME:-app}
|
|
85
93
|
healthcheck:
|
|
86
94
|
test: ["CMD-SHELL", "pg_isready -U postgres"]
|