create-convex-monorepo 0.2.0 → 0.3.0
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/CONTRIBUTING.md +19 -1
- package/README.md +53 -2
- package/assets/setup/convex-setup.d.mts +16 -0
- package/dist/cli/workspace.d.ts +2 -0
- package/dist/cli/workspace.js +19 -0
- package/dist/cli/workspace.js.map +1 -0
- package/dist/commands/workspace.d.ts +20 -0
- package/dist/commands/workspace.js +272 -0
- package/dist/commands/workspace.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/workspace/add.d.ts +9 -0
- package/dist/workspace/add.js +326 -0
- package/dist/workspace/add.js.map +1 -0
- package/dist/workspace/changes.d.ts +23 -0
- package/dist/workspace/changes.js +157 -0
- package/dist/workspace/changes.js.map +1 -0
- package/dist/workspace/doctor.d.ts +16 -0
- package/dist/workspace/doctor.js +483 -0
- package/dist/workspace/doctor.js.map +1 -0
- package/dist/workspace/env.d.ts +12 -0
- package/dist/workspace/env.js +50 -0
- package/dist/workspace/env.js.map +1 -0
- package/dist/workspace/project.d.ts +28 -0
- package/dist/workspace/project.js +147 -0
- package/dist/workspace/project.js.map +1 -0
- package/dist/workspace/upgrade.d.ts +17 -0
- package/dist/workspace/upgrade.js +86 -0
- package/dist/workspace/upgrade.js.map +1 -0
- package/docs/adding-a-framework.md +6 -0
- package/docs/adding-an-auth-provider.md +8 -0
- package/docs/architecture.md +13 -1
- package/docs/releases.md +18 -24
- package/docs/verification.md +29 -1
- package/package.json +6 -3
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { lstat, readFile } from 'node:fs/promises';
|
|
2
|
+
import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
3
|
+
import { normalizeOptions } from '../generator/options.js';
|
|
4
|
+
export function isRecord(value) {
|
|
5
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
6
|
+
}
|
|
7
|
+
export function isMissing(error) {
|
|
8
|
+
return error instanceof Error && 'code' in error && error.code === 'ENOENT';
|
|
9
|
+
}
|
|
10
|
+
/** Check every existing path component, including the workspace root. */
|
|
11
|
+
export async function safePath(root, path) {
|
|
12
|
+
if (!path ||
|
|
13
|
+
isAbsolute(path) ||
|
|
14
|
+
path.includes('\\') ||
|
|
15
|
+
path.split('/').some((part) => !part || part === '..' || part === '.'))
|
|
16
|
+
throw new Error(`Unsafe workspace path: ${path}`);
|
|
17
|
+
root = resolve(root);
|
|
18
|
+
const target = resolve(root, path);
|
|
19
|
+
const rel = relative(root, target);
|
|
20
|
+
if (!rel || rel === '..' || rel.startsWith(`..${sep}`) || isAbsolute(rel))
|
|
21
|
+
throw new Error(`Unsafe workspace path: ${path}`);
|
|
22
|
+
let current = root;
|
|
23
|
+
for (const part of ['', ...rel.split(sep)]) {
|
|
24
|
+
if (part)
|
|
25
|
+
current = join(current, part);
|
|
26
|
+
try {
|
|
27
|
+
const stat = await lstat(current);
|
|
28
|
+
if (stat.isSymbolicLink())
|
|
29
|
+
throw new Error(`Refusing symlink in workspace path: ${path}`);
|
|
30
|
+
if (current !== target && !stat.isDirectory())
|
|
31
|
+
throw new Error(`Expected a directory in workspace path: ${path}`);
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
if (!isMissing(error))
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return target;
|
|
39
|
+
}
|
|
40
|
+
export async function readText(root, path) {
|
|
41
|
+
const target = await safePath(root, path);
|
|
42
|
+
try {
|
|
43
|
+
const stat = await lstat(target);
|
|
44
|
+
if (!stat.isFile())
|
|
45
|
+
throw new Error(`Expected a regular file: ${path}`);
|
|
46
|
+
if (stat.nlink > 1)
|
|
47
|
+
throw new Error(`Refusing a hard-linked workspace file: ${path}`);
|
|
48
|
+
if (stat.size > 5 * 1024 * 1024)
|
|
49
|
+
throw new Error(`Workspace file is too large: ${path}`);
|
|
50
|
+
return await readFile(target, 'utf8');
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
if (isMissing(error))
|
|
54
|
+
return null;
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export async function readJson(root, path) {
|
|
59
|
+
const text = await readText(root, path);
|
|
60
|
+
if (text === null)
|
|
61
|
+
throw new Error(`Missing workspace file: ${path}`);
|
|
62
|
+
let value;
|
|
63
|
+
try {
|
|
64
|
+
value = JSON.parse(text);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
throw new Error(`Invalid JSON in ${path}.`);
|
|
68
|
+
}
|
|
69
|
+
if (!isRecord(value))
|
|
70
|
+
throw new Error(`Expected a JSON object in ${path}.`);
|
|
71
|
+
return value;
|
|
72
|
+
}
|
|
73
|
+
export function parseWorkspaceConfig(value) {
|
|
74
|
+
if (!isRecord(value) || value.version !== 1)
|
|
75
|
+
throw new Error('Unsupported convex-monorepo.json version. This CLI supports version 1.');
|
|
76
|
+
if (typeof value.name !== 'string' ||
|
|
77
|
+
typeof value.generator !== 'string' ||
|
|
78
|
+
value.generator.length > 256 ||
|
|
79
|
+
!/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-(?:0|[1-9]\d*|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*))*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/.test(value.generator) ||
|
|
80
|
+
value.packageManager !== 'pnpm' ||
|
|
81
|
+
value.monorepo !== 'turbo' ||
|
|
82
|
+
!Array.isArray(value.apps) ||
|
|
83
|
+
!['none', 'clerk'].includes(String(value.auth)))
|
|
84
|
+
throw new Error('Invalid convex-monorepo.json. Expected a named pnpm/Turborepo workspace with applications and a supported auth provider.');
|
|
85
|
+
const apps = value.apps.map((app) => {
|
|
86
|
+
if (!isRecord(app) ||
|
|
87
|
+
typeof app.name !== 'string' ||
|
|
88
|
+
typeof app.framework !== 'string')
|
|
89
|
+
throw new Error('Invalid application in convex-monorepo.json.');
|
|
90
|
+
if (app.example !== undefined &&
|
|
91
|
+
app.example !== 'none' &&
|
|
92
|
+
app.example !== 'messages')
|
|
93
|
+
throw new Error(`Invalid starter content for ${app.name}.`);
|
|
94
|
+
const normalized = normalizeOptions({
|
|
95
|
+
name: value.name,
|
|
96
|
+
apps: [
|
|
97
|
+
{ name: app.name, framework: app.framework },
|
|
98
|
+
],
|
|
99
|
+
}).apps[0];
|
|
100
|
+
if (!normalized)
|
|
101
|
+
throw new Error('Invalid application in convex-monorepo.json.');
|
|
102
|
+
return {
|
|
103
|
+
...normalized,
|
|
104
|
+
...(app.example === undefined ? {} : { example: app.example }),
|
|
105
|
+
};
|
|
106
|
+
});
|
|
107
|
+
const options = normalizeOptions({
|
|
108
|
+
name: value.name,
|
|
109
|
+
apps,
|
|
110
|
+
auth: String(value.auth),
|
|
111
|
+
...(value.example === undefined ? {} : { example: String(value.example) }),
|
|
112
|
+
});
|
|
113
|
+
return {
|
|
114
|
+
version: 1,
|
|
115
|
+
generator: value.generator,
|
|
116
|
+
name: options.name,
|
|
117
|
+
packageManager: 'pnpm',
|
|
118
|
+
monorepo: 'turbo',
|
|
119
|
+
apps,
|
|
120
|
+
auth: options.auth,
|
|
121
|
+
example: options.example,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
export async function loadWorkspace(cwd = process.cwd()) {
|
|
125
|
+
let root = resolve(cwd);
|
|
126
|
+
while (true) {
|
|
127
|
+
const configText = await readText(root, 'convex-monorepo.json');
|
|
128
|
+
if (configText !== null) {
|
|
129
|
+
let value;
|
|
130
|
+
try {
|
|
131
|
+
value = JSON.parse(configText);
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
throw new Error('Invalid JSON in convex-monorepo.json.');
|
|
135
|
+
}
|
|
136
|
+
const config = parseWorkspaceConfig(value);
|
|
137
|
+
if (!isRecord(value))
|
|
138
|
+
throw new Error('Invalid workspace metadata.');
|
|
139
|
+
return { root, config, rawConfig: value, configText };
|
|
140
|
+
}
|
|
141
|
+
const parent = dirname(root);
|
|
142
|
+
if (parent === root)
|
|
143
|
+
throw new Error('No convex-monorepo.json found. Run this command inside a generated workspace.');
|
|
144
|
+
root = parent;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=project.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project.js","sourceRoot":"","sources":["../../src/workspace/project.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { versions } from '../templates/versions.js';
|
|
2
|
+
import type { Workspace } from './project.js';
|
|
3
|
+
/** Read-only comparison against the official generator's latest stable release. */
|
|
4
|
+
export interface UpgradeCheck {
|
|
5
|
+
cliVersion: string;
|
|
6
|
+
projectGenerator: string;
|
|
7
|
+
latestStable: string;
|
|
8
|
+
updateAvailable: boolean;
|
|
9
|
+
projectBehind: boolean;
|
|
10
|
+
/** Versions tested together by the running CLI, not by the remote release. */
|
|
11
|
+
baseline: typeof versions;
|
|
12
|
+
registry: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function checkUpgrade(workspace: Workspace, options?: {
|
|
15
|
+
signal?: AbortSignal;
|
|
16
|
+
fetch?: typeof globalThis.fetch;
|
|
17
|
+
}): Promise<UpgradeCheck>;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { versions } from '../templates/versions.js';
|
|
2
|
+
import { getPackageVersion } from '../version.js';
|
|
3
|
+
function stableVersion(value) {
|
|
4
|
+
if (typeof value !== 'string' ||
|
|
5
|
+
!/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/.test(value)) {
|
|
6
|
+
throw new Error('Expected a stable semantic version in generator release metadata.');
|
|
7
|
+
}
|
|
8
|
+
return value
|
|
9
|
+
.split('+')[0]
|
|
10
|
+
.split('.')
|
|
11
|
+
.map((part) => BigInt(part));
|
|
12
|
+
}
|
|
13
|
+
function isNewer(latest, current) {
|
|
14
|
+
const target = stableVersion(latest);
|
|
15
|
+
// A stable version supersedes a prerelease with the same core version.
|
|
16
|
+
const match = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/.exec(current);
|
|
17
|
+
if (!match)
|
|
18
|
+
throw new Error('The project or CLI generator version is not a semantic version.');
|
|
19
|
+
if (match[4]
|
|
20
|
+
?.slice(1)
|
|
21
|
+
.split('.')
|
|
22
|
+
.some((part) => /^\d+$/.test(part) && part.length > 1 && part.startsWith('0'))) {
|
|
23
|
+
throw new Error('The project or CLI generator version is not a semantic version.');
|
|
24
|
+
}
|
|
25
|
+
for (let index = 0; index < 3; index++) {
|
|
26
|
+
const installed = BigInt(match[index + 1]);
|
|
27
|
+
if (target[index] !== installed)
|
|
28
|
+
return target[index] > installed;
|
|
29
|
+
}
|
|
30
|
+
return Boolean(match[4]);
|
|
31
|
+
}
|
|
32
|
+
export async function checkUpgrade(workspace, options = {}) {
|
|
33
|
+
options.signal?.throwIfAborted();
|
|
34
|
+
const cliVersion = await getPackageVersion();
|
|
35
|
+
options.signal?.throwIfAborted();
|
|
36
|
+
const registry = 'https://registry.npmjs.org/create-convex-monorepo/latest';
|
|
37
|
+
const controller = new AbortController();
|
|
38
|
+
const signal = options.signal
|
|
39
|
+
? AbortSignal.any([options.signal, controller.signal])
|
|
40
|
+
: controller.signal;
|
|
41
|
+
let timer;
|
|
42
|
+
let abortListener;
|
|
43
|
+
try {
|
|
44
|
+
const cancelled = new Promise((_, reject) => {
|
|
45
|
+
abortListener = () => reject(new Error(options.signal?.aborted
|
|
46
|
+
? 'Upgrade check cancelled.'
|
|
47
|
+
: 'The npm registry check timed out after 10 seconds.'));
|
|
48
|
+
signal.addEventListener('abort', abortListener, { once: true });
|
|
49
|
+
timer = setTimeout(() => controller.abort(), 10_000);
|
|
50
|
+
});
|
|
51
|
+
const request = (async () => {
|
|
52
|
+
const response = await (options.fetch ?? globalThis.fetch)(registry, {
|
|
53
|
+
signal,
|
|
54
|
+
headers: { accept: 'application/json' },
|
|
55
|
+
redirect: 'error',
|
|
56
|
+
});
|
|
57
|
+
if (!response.ok)
|
|
58
|
+
throw new Error(`The npm registry returned HTTP ${response.status}.`);
|
|
59
|
+
const data = await response.json();
|
|
60
|
+
if (typeof data !== 'object' || data === null || !('version' in data)) {
|
|
61
|
+
throw new Error('The npm registry returned invalid generator release metadata.');
|
|
62
|
+
}
|
|
63
|
+
stableVersion(data.version);
|
|
64
|
+
if (typeof data.version !== 'string')
|
|
65
|
+
throw new Error('Invalid registry version.');
|
|
66
|
+
return data.version;
|
|
67
|
+
})();
|
|
68
|
+
const latestStable = await Promise.race([request, cancelled]);
|
|
69
|
+
return {
|
|
70
|
+
cliVersion,
|
|
71
|
+
projectGenerator: workspace.config.generator,
|
|
72
|
+
latestStable,
|
|
73
|
+
updateAvailable: isNewer(latestStable, cliVersion),
|
|
74
|
+
projectBehind: isNewer(latestStable, workspace.config.generator),
|
|
75
|
+
baseline: { ...versions },
|
|
76
|
+
registry,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
clearTimeout(timer);
|
|
81
|
+
if (abortListener)
|
|
82
|
+
signal.removeEventListener('abort', abortListener);
|
|
83
|
+
controller.abort();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=upgrade.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upgrade.js","sourceRoot":"","sources":["../../src/workspace/upgrade.ts"],"names":[],"mappings":"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"}
|
|
@@ -25,3 +25,9 @@ Do not add a declaration bundler, a generic API cast, or copied backend code. Ch
|
|
|
25
25
|
Generate the framework alone, with another frontend, and with every advertised auth provider. Cover both `--example messages` and `--example none`; use the shared entry-content helper so blank apps do not import demo files. Install from a clean directory, typecheck, lint, and run a production build. For a native framework, run its actual JavaScript bundler for supported platforms. Exercise the query and mutation against a development backend when credentials or local deployment tooling are available; report separately when that check was skipped.
|
|
26
26
|
|
|
27
27
|
Add focused output assertions, option-selection coverage, and representative generated-project CI coverage. Tests must catch missing generated declarations and widened argument/return types. Update README support notes and this guide if the framework introduces a new integration contract.
|
|
28
|
+
|
|
29
|
+
## Existing-workspace commands
|
|
30
|
+
|
|
31
|
+
Add the framework's dependency expectations and diagnostic checks to `src/workspace/doctor.ts`. Verify that `add app` renders the framework at its real workspace index so development ports remain distinct. Add command tests for a new app next to an existing customized app, both starter choices, inherited auth, automatic URL linking, and a dry run. Extend the command E2E matrix to install and build the added framework.
|
|
32
|
+
|
|
33
|
+
The add planner uses the same template as project creation. It copies only the new application's files and updates workspace metadata and root scripts. Avoid reading external project state inside a template; the planner needs to render it independently in a temporary directory.
|
|
@@ -29,3 +29,11 @@ Test unauthenticated rejection and cross-user isolation on the backend. Generate
|
|
|
29
29
|
Use test credentials only for integration checks that need a provider account, and report those checks separately from static tests. Add provider-specific setup and troubleshooting to the generated README, repository README, and research notes. An unsupported framework combination must fail validation before generation rather than silently omitting auth.
|
|
30
30
|
|
|
31
31
|
Cover both starter-content choices in adapter tests. Blank projects retain authentication configuration and providers but omit example-specific access helpers and functions.
|
|
32
|
+
|
|
33
|
+
## Installing auth after generation
|
|
34
|
+
|
|
35
|
+
Existing-workspace auth installation renders the no-auth baseline and the selected provider, then plans their differences. The current public command supports `add auth clerk`; adding another provider also requires extending CLI selection, metadata validation, doctor dependency checks, and `planAddAuth`.
|
|
36
|
+
|
|
37
|
+
Define the backend files the provider may add or change. Do not broaden the planner to overwrite every backend difference: schemas, user functions, and Convex generated internals must remain under project ownership. For changed app source files, require an unchanged baseline or return an actionable conflict. Merge manifest entries without replacing unrelated dependencies or scripts. Generate a separate setup guide instead of replacing the project's README.
|
|
38
|
+
|
|
39
|
+
Test installation on existing apps for every framework and starter choice, customized-source conflicts, dependency conflicts, repeat installation, stored-data implications, and cancellation. A provider migration between identities needs a separate design; it is not implied by adding an auth adapter.
|
package/docs/architecture.md
CHANGED
|
@@ -40,10 +40,22 @@ The generated `scripts/convex-setup.mjs` is a standalone Node script copied from
|
|
|
40
40
|
|
|
41
41
|
Registries and TypeScript unions make supported choices explicit. Dependencies are pinned in `src/templates/versions.ts` and in framework bindings where necessary. New adapters should add concrete capabilities, with install, type, and bundler evidence for each supported combination. Reject an unsupported combination clearly rather than emitting code known to fail.
|
|
42
42
|
|
|
43
|
-
`convex-monorepo.json` records a versioned description of the output
|
|
43
|
+
`convex-monorepo.json` records a versioned description of the output. Workspace commands validate it before inspecting or planning edits. The exported API supports programmatic generation; custom runtime registry injection is not implemented.
|
|
44
44
|
|
|
45
45
|
## Starter content
|
|
46
46
|
|
|
47
47
|
`example` is independent of framework and auth selection. `messages` keeps the existing example; `none` selects blank apps and the empty backend assets. The backend template chooses the matching official generated declarations. App entry content comes from a shared helper, while framework setup and auth adapters keep their existing responsibilities. Auth adapters omit the demo's access helper in blank mode but still configure the selected provider.
|
|
48
48
|
|
|
49
49
|
Blank apps do not include tests that assume an empty API forever. The generated-project test runner injects temporary compile-time assertions for an exactly empty API and table list, rejects unknown modules, and checks for widened types. Those files belong only to the disposable test fixture.
|
|
50
|
+
|
|
51
|
+
## Workspace commands
|
|
52
|
+
|
|
53
|
+
Both binaries ship in `create-convex-monorepo`: `create-convex-monorepo` creates a project, and `convex-monorepo` manages it afterward. `src/commands/workspace.ts` parses command-specific options and prompts. Application logic lives in `src/workspace` and is exported for programmatic use.
|
|
54
|
+
|
|
55
|
+
`project.ts` walks upward to metadata, validates version 1, retains extension fields, and rejects unsafe file paths. `add.ts` renders temporary scaffolds with the existing app/auth adapters, then selects only the new app or authentication changes. Auth package updates merge only the fields changed by the adapter; source files must match the expected baseline before replacement. Per-app example overrides survive later auth additions.
|
|
56
|
+
|
|
57
|
+
`env.ts` shares the standalone setup script's URL validation and dotenv linking helpers. A declaration file types that plain JavaScript module for the compiled CLI; the JavaScript implementation is separately checked by TypeScript. Generated projects still receive the standalone script and need no CLI dependency for initial Convex setup.
|
|
58
|
+
|
|
59
|
+
`changes.ts` applies file plans after checking snapshots, absent destinations, and path safety. Dry runs perform the same preflight without acquiring a lock or writing. Mutations hold a workspace lock, use exclusive creation or temporary-file replacement, and roll back only files that still match their written contents. The lock coordinates CLI instances, not editors; snapshot checks cannot make arbitrary external writers transactional. File plans contain before/after text and may include local environment values, so the CLI displays paths rather than serializing mutation plans.
|
|
60
|
+
|
|
61
|
+
`doctor.ts` checks local configuration and uses the app's installed TypeScript compiler for an in-memory API probe. It never emits probe files. `upgrade.ts` queries the fixed official npm package endpoint with cancellation and a timeout. Its dependency baseline belongs to the running CLI, and is not a compatibility claim about every newer upstream package.
|
package/docs/releases.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Releases
|
|
2
2
|
|
|
3
|
-
This repository uses Conventional Commits and release-please for versions, changelogs, release PRs, and GitHub releases. GitHub Actions tests and publishes the release tarball to npm. The first publication
|
|
3
|
+
This repository uses Conventional Commits and release-please for versions, changelogs, release PRs, and GitHub releases. GitHub Actions tests and publishes the release tarball to npm. The first publication used a bootstrap token. The workflow now uses trusted publishing without a token fallback. This follows the publishing model in [convex-cloudflare-email](https://github.com/adamtrip-solutions/convex-cloudflare-email/blob/main/.github/workflows/release.yml).
|
|
4
4
|
|
|
5
5
|
## Version policy
|
|
6
6
|
|
|
@@ -15,45 +15,39 @@ This repository uses Conventional Commits and release-please for versions, chang
|
|
|
15
15
|
|
|
16
16
|
A `BREAKING CHANGE:` commit footer also requests a major bump. Explain migration steps when using it. The two pre-major downgrade options are explicitly disabled, so breaking changes still request a major before 1.0. For several commits, the largest requested bump wins. This follows the [release-please versioning strategy](https://github.com/googleapis/release-please/blob/main/src/versioning-strategies/default.ts).
|
|
17
17
|
|
|
18
|
-
The manifest
|
|
18
|
+
The initial manifest recorded 0.1.0 as the existing baseline. `bootstrap-sha` points to the last committed v0.1 work, `0373d22b000dc9cba036a1d2f1cff689b217fd8b`, so initial history is not replayed as new features. This does not publish 0.1.0 or create a historical release. After the first automated release, release-please uses release history. See its [manifest documentation](https://github.com/googleapis/release-please/blob/main/docs/manifest-releaser.md).
|
|
19
19
|
|
|
20
20
|
## GitHub configuration
|
|
21
21
|
|
|
22
22
|
The public repository is `adamtrip-solutions/create-convex-monorepo`. The package's repository URL must match it for npm provenance.
|
|
23
23
|
|
|
24
24
|
- Use `main` as the release branch. Enable Actions and allow GitHub Actions to create pull requests.
|
|
25
|
-
- Enable squash merging with the PR title as the commit title. Require the Conventional PR title check and `CI passed` before merging.
|
|
25
|
+
- Enable squash merging with the PR title as the commit title and keep auto-merge disabled. Require the Conventional PR title check and `CI passed` from GitHub Actions, an approving code-owner review, and resolved conversations before merging. Restrict `main` updates to `adamtrip`; see the administrator exception in [Contributing](../CONTRIBUTING.md#contribution-and-review-process).
|
|
26
|
+
- Require workflow approval for all outside contributors. Approving a workflow run does not approve the PR or publish a release.
|
|
26
27
|
- Create a GitHub environment named `npm`. Restrict deployments to tags matching `v*`. Protect release tags against updates and deletion.
|
|
27
|
-
- Configure
|
|
28
|
+
- Configure npm trusted publishing as described below. No npm secret or publishing-enable variable is required.
|
|
28
29
|
|
|
29
30
|
The release workflow uses `GITHUB_TOKEN` by default. GitHub may require approval before running CI on a bot-created release PR. An optional `RELEASE_PLEASE_TOKEN` with repository contents, issues and pull-request write permissions can avoid that interruption. The npm dispatch step always uses `GITHUB_TOKEN` with `actions: write`; it does not require Actions permission on the optional token. [GitHub token event rules](https://docs.github.com/en/actions/concepts/security/github_token).
|
|
30
31
|
|
|
31
32
|
## First npm publication
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
2. Create a short-lived granular npm token with Read and write package permissions that allow creation of `create-convex-monorepo`. Enable Bypass two-factor authentication for this first unattended publish. A token limited to the existing `convex-cloudflare-email` package cannot create this new package.
|
|
35
|
-
3. Save it as `NPM_BOOTSTRAP_TOKEN` in this repository's [npm environment](https://github.com/adamtrip-solutions/create-convex-monorepo/settings/environments). Use GitHub's secret UI, or run the following command in your own terminal and paste the token at its hidden prompt:
|
|
34
|
+
Version 0.2.0 created the package through GitHub Actions using `NPM_BOOTSTRAP_TOKEN`. The workflow no longer reads that secret. Subsequent releases authenticate through npm trusted publishing.
|
|
36
35
|
|
|
37
|
-
|
|
38
|
-
gh secret set NPM_BOOTSTRAP_TOKEN --env npm --repo adamtrip-solutions/create-convex-monorepo
|
|
39
|
-
```
|
|
36
|
+
## Trusted publisher configuration
|
|
40
37
|
|
|
41
|
-
|
|
38
|
+
In the npm package settings, add a GitHub Actions trusted publisher with these exact values:
|
|
42
39
|
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
| Field | Value |
|
|
41
|
+
| ----------------- | --------------------------- |
|
|
42
|
+
| Organization/user | `adamtrip-solutions` |
|
|
43
|
+
| Repository | `create-convex-monorepo` |
|
|
44
|
+
| Workflow filename | `publish.yml` |
|
|
45
|
+
| Environment | `npm` |
|
|
46
|
+
| Allowed action | Enable direct `npm publish` |
|
|
45
47
|
|
|
46
|
-
|
|
47
|
-
| ----------------- | ------------------------------------------------------- |
|
|
48
|
-
| Organization/user | `adamtrip-solutions` |
|
|
49
|
-
| Repository | `create-convex-monorepo` |
|
|
50
|
-
| Workflow filename | `publish.yml` |
|
|
51
|
-
| Environment | `npm` |
|
|
52
|
-
| Allowed action | Enable direct `npm publish`, not only staged publishing |
|
|
48
|
+
The workflow requests a short-lived OIDC credential from GitHub. npm verifies the repository, workflow, and environment against this configuration. There is no stored npm token in the publishing step. [npm trusted publishing](https://docs.npmjs.com/trusted-publishers/).
|
|
53
49
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
The bootstrap token is available only to the final publish step, after all validation jobs pass. Current npm documentation requires the package to exist before configuring its trusted publisher; the bootstrap token lets CI create it. [Token setup](https://docs.npmjs.com/creating-and-viewing-access-tokens/), [trusted publisher configuration](https://docs.npmjs.com/trusted-publishers/).
|
|
50
|
+
Verify the migration with a new release through the workflow. Retrying an already published version skips publication and does not test authentication. After an OIDC publication succeeds, delete the old GitHub bootstrap secret and revoke the corresponding npm token. If that token is shared with another package, migrate that package or replace its credential before revoking it.
|
|
57
51
|
|
|
58
52
|
## Automated release flow
|
|
59
53
|
|
|
@@ -75,4 +69,4 @@ gh workflow run publish.yml --repo adamtrip-solutions/create-convex-monorepo --r
|
|
|
75
69
|
|
|
76
70
|
Select the tag in the Actions UI when triggering manually. Running from a branch is rejected. If npm already contains that exact tarball, the publish step skips it. A version with different contents fails explicitly; release a new version instead of moving tags or trying to overwrite npm history. If the workflow itself needs a code fix, include it in a new release because retries execute the workflow stored at the original tag.
|
|
77
71
|
|
|
78
|
-
Normal changes do not need manual version edits. If the release PR stalls, inspect the workflow logs, token permissions and `autorelease` labels. Keep release-please's generated title and body intact.
|
|
72
|
+
Normal changes do not need manual version edits. If the release PR stalls, inspect the workflow logs, token permissions and `autorelease` labels. Keep release-please's generated title and body intact.
|
package/docs/verification.md
CHANGED
|
@@ -75,4 +75,32 @@ Fresh review checked the release dispatch, provenance commit identity, bootstrap
|
|
|
75
75
|
|
|
76
76
|
Compared both the local checkout and GitHub workflow of `adamtrip-solutions/convex-cloudflare-email`, whose first npm release workflow succeeded. Its publish step supports `NPM_BOOTSTRAP_TOKEN` for initial package creation. This repository now supports the same token-to-OIDC transition and no longer gates publishing behind `NPM_PUBLISHING_ENABLED`. The token is passed only to the final npm publish step. No secret values were read or transferred from the reference repository, and this package has not been published.
|
|
77
77
|
|
|
78
|
-
A later Windows run exposed an intermittent cancellation failure: setup rejected on the child's abort error before the process closed, leaving the backend directory locked during cleanup. The regression test reproduced the early return locally. Setup now waits for the child close event before settling, preserving the original error. The corrected test and all 98 repository tests passed, together with typecheck, lint, formatting, build, and package smoke checks. Hosted
|
|
78
|
+
A later Windows run exposed an intermittent cancellation failure: setup rejected on the child's abort error before the process closed, leaving the backend directory locked during cleanup. The regression test reproduced the early return locally. Setup now waits for the child close event before settling, preserving the original error. The corrected test and all 98 repository tests passed, together with typecheck, lint, formatting, build, and package smoke checks. Hosted [CI run 34416758903](https://github.com/adamtrip-solutions/create-convex-monorepo/actions/runs/34416758903) confirmed the fix on Windows and passed every generated-project build.
|
|
79
|
+
|
|
80
|
+
## First npm publication
|
|
81
|
+
|
|
82
|
+
GitHub Actions [run 34417030866](https://github.com/adamtrip-solutions/create-convex-monorepo/actions/runs/34417030866) published `create-convex-monorepo@0.2.0` from release commit `406b6dec137ba521a23ebc6d78965f0373e1ba98`. The release ran all 98 tests on Windows and Linux, package smoke checks, and the complete generated-project build matrix before publishing the tested tarball. The registry reports `latest` as `0.2.0` and includes a provenance attestation.
|
|
83
|
+
|
|
84
|
+
Registry checks ran outside the source checkout:
|
|
85
|
+
|
|
86
|
+
- `pnpm create convex-monorepo@0.2.0 smoke --apps next,expo --auth clerk --example none --no-install --no-git` generated the blank workspace successfully.
|
|
87
|
+
- `npx --yes create-convex-monorepo@0.2.0 --version` returned `0.2.0`, using both the default npm cache and a fresh cache.
|
|
88
|
+
- `npx --yes create-convex-monorepo@0.2.0 npx-smoke --apps next,vite,tanstack-start,expo --auth none --no-install --no-git` generated all four apps successfully.
|
|
89
|
+
|
|
90
|
+
An initial npx invocation from inside this package's own checkout failed to find the executable. The same registry command succeeded from the temporary directory. These post-publication smoke checks skipped generated dependency installation; the release workflow had already tested the generated builds. The bootstrap token authenticated the first publish. Token-free OIDC publishing still needs npm trusted-publisher configuration and verification.
|
|
91
|
+
|
|
92
|
+
## Trusted publishing verification
|
|
93
|
+
|
|
94
|
+
GitHub Actions [run 34417938658](https://github.com/adamtrip-solutions/create-convex-monorepo/actions/runs/34417938658) published `create-convex-monorepo@0.2.1` from release commit `72527d37eca8f7bd6ced87440e005253bbaa87bc`. This release removed the bootstrap-token reference from the workflow before publishing, so there was no npm token fallback. All Windows, Linux, and generated-project checks passed, and the registry lists 0.2.1 as latest with provenance.
|
|
95
|
+
|
|
96
|
+
The repository's `NPM_BOOTSTRAP_TOKEN` secret was deleted after publication succeeded. Repository and npm-environment secret listings then contained no npm secret. Revoking the original token in the npm account remains an account-owner action; check whether another repository still uses it first.
|
|
97
|
+
|
|
98
|
+
## Workspace management commands
|
|
99
|
+
|
|
100
|
+
The workspace-command implementation passed frozen installation, formatting, lint, typecheck, all 237 tests, build, pack, installed-package smoke checks, and actionlint. Tests cover all five command routes, interactive choices, strict flags, typed API diagnostics, mocked registry responses, conflicts, cancellation, rollback, CRLF checkouts, port allocation, secret exclusion, and dry runs.
|
|
101
|
+
|
|
102
|
+
`pnpm test:workspace:e2e` and `CCM_EXAMPLE=none pnpm test:workspace:e2e` both passed. The messages case starts with Next.js, adds Vite, TanStack Start, and Expo, then installs Clerk across all existing apps. The blank case installs Clerk first and then adds the remaining apps. Both sync URLs, install dependencies, run doctor, typecheck, lint, and build all four apps. Builds include Next, Vite, Start client/server, and Expo iOS/Android Metro exports. API contract assertions remain enabled for the messages case, and the blank case receives test-only empty API assertions.
|
|
103
|
+
|
|
104
|
+
The packed package exposes both binaries. Its smoke test exercises `convex-monorepo` through the installed executable and pnpm dlx, adds an app and Clerk, and syncs one frontend URL. The live npm upgrade check returned the current stable release from the official registry. Doctor also passed from a nested application directory in an installed four-app workspace. A PTY run exercised interactive app, framework, and starter selection with `--dry-run`; the proposed directory was not created.
|
|
105
|
+
|
|
106
|
+
Independent technical review covered mutation plans and CLI integration. Follow-up checks confirmed fixes for workspace-membership diagnostics and semantic-version build metadata. Integration checks also caught development-port collisions, which now have regression tests. Filesystem conflict detection remains optimistic for unrelated external writers; the lock coordinates CLI invocations. No real Clerk login, stored-data migration, or native Xcode/Gradle build was performed. Hosted CI includes the two command-based build scenarios and Windows unit/package checks.
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-convex-monorepo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Generate typed Convex monorepos with composable frontend and authentication adapters.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
8
|
-
"create-convex-monorepo": "dist/cli/index.js"
|
|
8
|
+
"create-convex-monorepo": "dist/cli/index.js",
|
|
9
|
+
"convex-monorepo": "dist/cli/workspace.js"
|
|
9
10
|
},
|
|
10
11
|
"exports": {
|
|
11
12
|
".": {
|
|
@@ -72,6 +73,8 @@
|
|
|
72
73
|
"test": "vitest run && pnpm test:backend",
|
|
73
74
|
"test:e2e": "node scripts/e2e.mjs",
|
|
74
75
|
"test:backend": "tsx scripts/backend-fixtures.ts && vitest run --config vitest.backend.config.ts",
|
|
75
|
-
"test:package": "node scripts/pack-smoke.mjs"
|
|
76
|
+
"test:package": "node scripts/pack-smoke.mjs",
|
|
77
|
+
"dev:workspace": "tsx src/cli/workspace.ts",
|
|
78
|
+
"test:workspace:e2e": "node scripts/workspace-e2e.mjs"
|
|
76
79
|
}
|
|
77
80
|
}
|