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 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.
@@ -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
- ? `/home/runner/work/${runner.directory}`
738
- : "/home/runner/work";
737
+ ? `${CONTAINER_WORKDIR}/${runner.directory}`
738
+ : CONTAINER_WORKDIR;
739
739
  const result = await runInContainer({
740
740
  projectDir,
741
741
  image,
@@ -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
@@ -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",
@@ -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(new URL(import.meta.url).pathname), "../../ci-local/docker");
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
- echo -e "${GREEN}Checksum verified${NC}"
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "javi-forge",
3
- "version": "1.22.1",
3
+ "version": "1.22.3",
4
4
  "description": "Project scaffolding and AI-ready CI bootstrap",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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
- WEBUI_AUTH=false
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
- SUPABASE_DB_PASSWORD=postgres
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
- - "${OLLAMA_PORT:-11434}:11434"
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
- - "${WEBUI_PORT:-3000}:8080"
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
- - WEBUI_AUTH=${WEBUI_AUTH:-false}
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
- - "${N8N_PORT:-5678}:5678"
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
- - "${SUPABASE_DB_PORT:-5432}:5432"
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
- POSTGRES_PASSWORD: ${SUPABASE_DB_PASSWORD:-postgres}
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"]