@usequark/quark-create-app 1.24.0 → 1.24.2
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 +2 -2
- package/src/index.js +12 -0
- package/src/scaffold-gitignore.js +54 -0
- package/templates/base-project/.github/dependabot.yml +20 -0
- package/templates/base-project/.github/workflows/ci.yml +9 -9
- package/templates/base-project/.github/workflows/dependabot-auto-merge.yml +1 -1
- package/templates/base-project/.github/workflows/install-windows.yml +3 -3
- package/templates/base-project/.github/workflows/release.yml +2 -2
- package/templates/base-project/biome.json +1 -2
- package/templates/worker/src/index.js +28 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usequark/quark-create-app",
|
|
3
|
-
"version": "1.24.
|
|
3
|
+
"version": "1.24.2",
|
|
4
4
|
"description": "CLI to scaffold new Quark projects with Next.js, Prisma, BullMQ, and Railway deployment",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"quark",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"sync-templates:check": "node scripts/sync-templates.js --check",
|
|
57
57
|
"regen-migration": "node scripts/regenerate-template-migration.js",
|
|
58
58
|
"smoke:published": "node scripts/smoke-published.js",
|
|
59
|
-
"test": "node test-cli.js && node --test src/utils.test.js && node --test src/prepare-hooks.test.js && node --test src/scaffold-output.test.js && node --test src/template-scripts.test.js && node --test src/scaffold-guards.test.js && node --test src/worker-dockerfile.test.js && node --test src/dependabot-config.test.js && node --test 'src/deploy/**/*.test.js' && node --test --test-concurrency=1 'src/deploy/**/*.integration.test.js'",
|
|
59
|
+
"test": "node test-cli.js && node --test src/utils.test.js && node --test src/prepare-hooks.test.js && node --test src/scaffold-output.test.js && node --test src/template-scripts.test.js && node --test src/scaffold-guards.test.js && node --test src/template-config.test.js && node --test src/worker-dockerfile.test.js && node --test src/dependabot-config.test.js && node --test 'src/deploy/**/*.test.js' && node --test --test-concurrency=1 'src/deploy/**/*.integration.test.js'",
|
|
60
60
|
"test:build": "node test-build.js",
|
|
61
61
|
"test:e2e": "node test-e2e.js",
|
|
62
62
|
"test:e2e:full": "node test-e2e-full.js",
|
package/src/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import { Command } from "commander";
|
|
|
9
9
|
import { execa } from "execa";
|
|
10
10
|
import fs from "fs-extra";
|
|
11
11
|
import prompts from "prompts";
|
|
12
|
+
import { SCAFFOLD_GITIGNORE } from "./scaffold-gitignore.js";
|
|
12
13
|
import { findEnclosingGitRepo, formatProjectDisplayName } from "./utils.js";
|
|
13
14
|
|
|
14
15
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -117,6 +118,17 @@ async function copyTemplate(templateName, targetDir, variables = {}) {
|
|
|
117
118
|
// Copy the template
|
|
118
119
|
await fs.copy(templatePath, targetDir);
|
|
119
120
|
|
|
121
|
+
// npm strips every .gitignore from published tarballs, so a CLI installed
|
|
122
|
+
// from npm never has this file in its templates - only a git checkout does.
|
|
123
|
+
// Written from the shared module so both paths produce the same result, and
|
|
124
|
+
// skipped when the copy already provided one so a real template file always
|
|
125
|
+
// wins. Without this, a scaffolded project gets `git init` and no ignore
|
|
126
|
+
// rules, so the first `git add .` stages node_modules, .next and .env.
|
|
127
|
+
const gitignorePath = path.join(targetDir, ".gitignore");
|
|
128
|
+
if (!(await fs.pathExists(gitignorePath))) {
|
|
129
|
+
await fs.writeFile(gitignorePath, SCAFFOLD_GITIGNORE);
|
|
130
|
+
}
|
|
131
|
+
|
|
120
132
|
// Replace variables in package.json files
|
|
121
133
|
if (Object.keys(variables).length > 0) {
|
|
122
134
|
const packageJsonPath = path.join(targetDir, "package.json");
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* .gitignore content for scaffolded projects.
|
|
3
|
+
*
|
|
4
|
+
* This lives in a module rather than only as `base-project/.gitignore` because
|
|
5
|
+
* **npm strips every `.gitignore` out of published tarballs**, unconditionally,
|
|
6
|
+
* with no opt-out. The template copy of the file therefore never reaches anyone
|
|
7
|
+
* installing `@usequark/quark-create-app` from npm - only someone running the CLI
|
|
8
|
+
* from a git checkout sees it. Scaffolded projects were consequently shipping
|
|
9
|
+
* with no `.gitignore` at all, while `.dockerignore`, `.nvmrc` and `.env.example`
|
|
10
|
+
* all arrived normally, because npm strips only `.gitignore` and `.npmrc`.
|
|
11
|
+
*
|
|
12
|
+
* That matters: the scaffolder runs `git init`, so the first `git add .` in a
|
|
13
|
+
* generated project would stage `node_modules/`, `.next/`, and `.env` - which
|
|
14
|
+
* `scripts/prepare.js` populates with real credentials.
|
|
15
|
+
*
|
|
16
|
+
* `generate-templates.js` writes the in-repo copy from this same export, so the
|
|
17
|
+
* file a contributor sees and the file a user gets cannot drift apart.
|
|
18
|
+
*/
|
|
19
|
+
export const SCAFFOLD_GITIGNORE_ENTRIES = [
|
|
20
|
+
"# dependencies",
|
|
21
|
+
"node_modules/",
|
|
22
|
+
"",
|
|
23
|
+
"# environment",
|
|
24
|
+
".env",
|
|
25
|
+
".env.local",
|
|
26
|
+
".env.*.local",
|
|
27
|
+
"",
|
|
28
|
+
"# next.js",
|
|
29
|
+
".next/",
|
|
30
|
+
"out/",
|
|
31
|
+
"",
|
|
32
|
+
"# build artifacts",
|
|
33
|
+
"dist/",
|
|
34
|
+
"build/",
|
|
35
|
+
"",
|
|
36
|
+
"# testing",
|
|
37
|
+
"coverage/",
|
|
38
|
+
"",
|
|
39
|
+
"# misc",
|
|
40
|
+
".DS_Store",
|
|
41
|
+
"*.log",
|
|
42
|
+
".quark-auto-clean.json",
|
|
43
|
+
"",
|
|
44
|
+
"# turbo",
|
|
45
|
+
".turbo/",
|
|
46
|
+
"",
|
|
47
|
+
"# prisma",
|
|
48
|
+
"packages/db/src/generated/",
|
|
49
|
+
"",
|
|
50
|
+
"# uploads",
|
|
51
|
+
"**/uploads/",
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
export const SCAFFOLD_GITIGNORE = `${SCAFFOLD_GITIGNORE_ENTRIES.join("\n")}\n`;
|
|
@@ -29,3 +29,23 @@ updates:
|
|
|
29
29
|
update-types: ["patch"]
|
|
30
30
|
|
|
31
31
|
open-pull-requests-limit: 10
|
|
32
|
+
|
|
33
|
+
# Keep workflow actions current. Without this block nothing tracks the `uses:`
|
|
34
|
+
# pins, and an action that drops to a deprecated runtime only ever shows up as
|
|
35
|
+
# a log annotation — never as a failing check — so scaffolded projects
|
|
36
|
+
# silently accumulate stale pins. Majors get their own PR because action
|
|
37
|
+
# majors can change runtime or drop inputs.
|
|
38
|
+
- package-ecosystem: github-actions
|
|
39
|
+
directory: /
|
|
40
|
+
schedule:
|
|
41
|
+
interval: weekly
|
|
42
|
+
groups:
|
|
43
|
+
actions-majors:
|
|
44
|
+
update-types: ["major"]
|
|
45
|
+
actions-minor:
|
|
46
|
+
update-types: ["minor"]
|
|
47
|
+
actions-patch:
|
|
48
|
+
update-types: ["patch"]
|
|
49
|
+
commit-message:
|
|
50
|
+
prefix: "deps(actions)"
|
|
51
|
+
open-pull-requests-limit: 4
|
|
@@ -25,11 +25,11 @@ jobs:
|
|
|
25
25
|
name: Lint
|
|
26
26
|
runs-on: ubuntu-latest
|
|
27
27
|
steps:
|
|
28
|
-
- uses: actions/checkout@
|
|
28
|
+
- uses: actions/checkout@v6
|
|
29
29
|
|
|
30
|
-
- uses: pnpm/action-setup@
|
|
30
|
+
- uses: pnpm/action-setup@v5
|
|
31
31
|
|
|
32
|
-
- uses: actions/setup-node@
|
|
32
|
+
- uses: actions/setup-node@v6
|
|
33
33
|
with:
|
|
34
34
|
node-version: 24
|
|
35
35
|
cache: pnpm
|
|
@@ -77,11 +77,11 @@ jobs:
|
|
|
77
77
|
NEXTAUTH_SECRET: ci-test-secret-must-be-at-least-32-characters-long
|
|
78
78
|
NODE_ENV: test
|
|
79
79
|
steps:
|
|
80
|
-
- uses: actions/checkout@
|
|
80
|
+
- uses: actions/checkout@v6
|
|
81
81
|
|
|
82
|
-
- uses: pnpm/action-setup@
|
|
82
|
+
- uses: pnpm/action-setup@v5
|
|
83
83
|
|
|
84
|
-
- uses: actions/setup-node@
|
|
84
|
+
- uses: actions/setup-node@v6
|
|
85
85
|
with:
|
|
86
86
|
node-version: 24
|
|
87
87
|
cache: pnpm
|
|
@@ -98,11 +98,11 @@ jobs:
|
|
|
98
98
|
runs-on: ubuntu-latest
|
|
99
99
|
|
|
100
100
|
steps:
|
|
101
|
-
- uses: actions/checkout@
|
|
101
|
+
- uses: actions/checkout@v6
|
|
102
102
|
|
|
103
|
-
- uses: pnpm/action-setup@
|
|
103
|
+
- uses: pnpm/action-setup@v5
|
|
104
104
|
|
|
105
|
-
- uses: actions/setup-node@
|
|
105
|
+
- uses: actions/setup-node@v6
|
|
106
106
|
with:
|
|
107
107
|
node-version: 24
|
|
108
108
|
cache: pnpm
|
|
@@ -15,11 +15,11 @@ jobs:
|
|
|
15
15
|
runs-on: windows-latest
|
|
16
16
|
timeout-minutes: 10
|
|
17
17
|
steps:
|
|
18
|
-
- uses: actions/checkout@
|
|
18
|
+
- uses: actions/checkout@v6
|
|
19
19
|
|
|
20
|
-
- uses: pnpm/action-setup@
|
|
20
|
+
- uses: pnpm/action-setup@v5
|
|
21
21
|
|
|
22
|
-
- uses: actions/setup-node@
|
|
22
|
+
- uses: actions/setup-node@v6
|
|
23
23
|
with:
|
|
24
24
|
node-version: 24
|
|
25
25
|
cache: pnpm
|
|
@@ -19,7 +19,7 @@ jobs:
|
|
|
19
19
|
name: Tag & Release
|
|
20
20
|
runs-on: ubuntu-latest
|
|
21
21
|
steps:
|
|
22
|
-
- uses: actions/checkout@
|
|
22
|
+
- uses: actions/checkout@v6
|
|
23
23
|
with:
|
|
24
24
|
ref: main
|
|
25
25
|
fetch-depth: 0
|
|
@@ -60,7 +60,7 @@ jobs:
|
|
|
60
60
|
git push origin "${{ steps.tag.outputs.tag }}"
|
|
61
61
|
|
|
62
62
|
- name: Create GitHub Release
|
|
63
|
-
uses: softprops/action-gh-release@
|
|
63
|
+
uses: softprops/action-gh-release@v3
|
|
64
64
|
with:
|
|
65
65
|
tag_name: ${{ steps.tag.outputs.tag }}
|
|
66
66
|
generate_release_notes: true
|
|
@@ -131,6 +131,16 @@ export async function waitForRedis(
|
|
|
131
131
|
intervalMs = parseInt(process.env.WORKER_HEALTH_INTERVAL_MS || "1000", 10),
|
|
132
132
|
} = config;
|
|
133
133
|
|
|
134
|
+
// See waitForDatabase: reject a config object in the healthCheck slot
|
|
135
|
+
// rather than failing with an opaque TypeError.
|
|
136
|
+
if (typeof healthCheck !== "function") {
|
|
137
|
+
throw new AppError(
|
|
138
|
+
"waitForRedis expects a health check function as its first argument.",
|
|
139
|
+
500,
|
|
140
|
+
"REDIS_HEALTH_CHECK_INVALID",
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
134
144
|
const reportThrottledError = throttledError(logger, 3000);
|
|
135
145
|
|
|
136
146
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
@@ -190,6 +200,18 @@ export async function waitForDatabase(
|
|
|
190
200
|
intervalMs = parseInt(process.env.WORKER_DB_INTERVAL_MS || "2000", 10),
|
|
191
201
|
} = config;
|
|
192
202
|
|
|
203
|
+
// Guard against the config object being passed in the healthCheck slot:
|
|
204
|
+
// that surfaces as an opaque "healthCheck is not a function" TypeError and,
|
|
205
|
+
// because a TypeError is neither a schema nor a connection error, it
|
|
206
|
+
// rethrows on the first attempt so the retry config never applies.
|
|
207
|
+
if (typeof healthCheck !== "function") {
|
|
208
|
+
throw new AppError(
|
|
209
|
+
"waitForDatabase expects a health check function as its first argument.",
|
|
210
|
+
500,
|
|
211
|
+
"DATABASE_HEALTH_CHECK_INVALID",
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
193
215
|
const reportThrottledError = throttledError(logger, 5000);
|
|
194
216
|
|
|
195
217
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
@@ -379,7 +401,12 @@ async function startWorker() {
|
|
|
379
401
|
|
|
380
402
|
// Wait for database schema to be ready (migrations may still be running)
|
|
381
403
|
logger.info("Checking database schema readiness...");
|
|
382
|
-
|
|
404
|
+
// healthCheck takes the first argument; the config object must not be
|
|
405
|
+
// passed in its slot or the call throws "healthCheck is not a function".
|
|
406
|
+
await waitForDatabase(
|
|
407
|
+
undefined,
|
|
408
|
+
isDevMode ? { maxRetries: 5, intervalMs: 1000 } : {},
|
|
409
|
+
);
|
|
383
410
|
logger.info("Database schema ready");
|
|
384
411
|
|
|
385
412
|
// Register a worker for each queue
|