Skip to content
This repository was archived by the owner on Nov 10, 2025. It is now read-only.

Commit e95dab4

Browse files
committed
feat: add automatic Compact compiler version update
Users with outdated Compact versions now get prompted to update automatically instead of hitting cryptic compilation errors. New CompactUpdater utility: - Detects version mismatches before project creation - Prompts user with clear explanation of issue - Updates Compact automatically with progress feedback - Verifies update success and re-checks requirements User Experience: - Clear version mismatch messaging (e.g., "requires 0.23.0+, found 0.15.0") - Optional automatic update (user can decline) - Real-time progress during update - Manual update instructions if declined Implementation: - New: src/utils/compact-updater.ts (automatic update logic) - Modified: src/create-app.ts (integrated auto-update flow) - Modified: src/utils/requirement-checker.ts (version checking) - Modified: src/utils/templates.ts (version requirements) - Modified: README.md (documented auto-update feature) Resolves: Issue reported by Linux user with Compact 0.15.0 when Counter template requires 0.23.0+
1 parent 5e154d6 commit e95dab4

File tree

5 files changed

+325
-21
lines changed

5 files changed

+325
-21
lines changed

README.md

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,16 @@ create-mn-app my-app
3636

3737
## Features
3838

39-
- Interactive project setup with template selection
40-
- Auto-detects package manager (npm/yarn/pnpm/bun)
41-
- Environment health checks
42-
- TypeScript with hot reloading
43-
- Pre-configured Compact contracts
44-
- Secure wallet generation
39+
- **Interactive project setup** with template selection
40+
- **Auto-detects package manager** (npm/yarn/pnpm/bun)
41+
- **Smart dependency management**:
42+
- Checks Node.js, Docker, and Compact compiler versions
43+
- **Automatic Compact compiler updates** when version mismatch detected
44+
- Prompts user before updating with clear explanations
45+
- **TypeScript** with hot reloading
46+
- **Pre-configured Compact contracts**
47+
- **Secure wallet generation**
48+
- **Environment health checks**
4549

4650
## Quick Start
4751

@@ -95,10 +99,13 @@ npm install
9599

96100
## Requirements
97101

98-
- Node.js 22+
99-
- Docker (for proof server)
100-
- npm/yarn/pnpm/bun
101-
- Compact Compiler (for Counter template - auto-guided installation)
102+
- **Node.js 22+** - Required for all templates
103+
- **Docker** - Required for running the proof server
104+
- **npm/yarn/pnpm/bun** - Package manager (auto-detected)
105+
- **Compact Compiler 0.23.0+** - Required for Counter template
106+
- **Auto-update available**: If you have an older version, the CLI will offer to update it automatically
107+
- Version compatibility checked before project creation
108+
- Manual installation: See [Compact releases](https://github.com/midnightntwrk/compact/releases/latest)
102109

103110
## CLI Options
104111

src/create-app.ts

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import {
3939
getTemplate,
4040
isValidTemplate,
4141
} from "./utils/templates.js";
42+
import { CompactUpdater } from "./utils/compact-updater.js";
4243

4344
export interface CreateAppOptions {
4445
template?: string;
@@ -362,16 +363,66 @@ async function createRemoteTemplate(
362363
checks.push(RequirementChecker.checkDocker());
363364

364365
if (template.requiresCompactCompiler) {
365-
checks.push(RequirementChecker.checkCompactCompiler());
366+
checks.push(
367+
RequirementChecker.checkCompactCompiler(template.compactVersion)
368+
);
366369
}
367370

368371
const allPassed = RequirementChecker.displayResults(checks);
369372

370373
if (!allPassed) {
371-
console.log(
372-
chalk.yellow("\n⚠ Please install missing requirements and try again.\n")
373-
);
374-
process.exit(1);
374+
// Check if the issue is Compact version mismatch
375+
if (template.requiresCompactCompiler && template.compactVersion) {
376+
const currentVersion = CompactUpdater.getCurrentVersion();
377+
if (
378+
currentVersion &&
379+
CompactUpdater.needsUpdate(currentVersion, template.compactVersion)
380+
) {
381+
// Offer to update Compact automatically
382+
const updateSuccess = await CompactUpdater.handleVersionMismatch(
383+
currentVersion,
384+
template.compactVersion
385+
);
386+
387+
if (updateSuccess) {
388+
// Re-check requirements after update
389+
console.log(chalk.cyan("\n[✓] Re-checking requirements...\n"));
390+
const recheckPassed = RequirementChecker.displayResults([
391+
RequirementChecker.checkCompactCompiler(template.compactVersion),
392+
]);
393+
394+
if (!recheckPassed) {
395+
console.log(
396+
chalk.red(
397+
"\n❌ Requirements still not met after update. Please check manually.\n"
398+
)
399+
);
400+
process.exit(1);
401+
}
402+
} else {
403+
console.log(
404+
chalk.yellow(
405+
"\n⚠ Please update Compact manually and try again.\n"
406+
)
407+
);
408+
process.exit(1);
409+
}
410+
} else {
411+
console.log(
412+
chalk.yellow(
413+
"\n⚠ Please install missing requirements and try again.\n"
414+
)
415+
);
416+
process.exit(1);
417+
}
418+
} else {
419+
console.log(
420+
chalk.yellow(
421+
"\n⚠ Please install missing requirements and try again.\n"
422+
)
423+
);
424+
process.exit(1);
425+
}
375426
}
376427
}
377428

src/utils/compact-updater.ts

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
// This file is part of create-mn-app.
2+
// Copyright (C) 2025 Midnight Foundation
3+
// SPDX-License-Identifier: Apache-2.0
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// You may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// https://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
import { execSync, spawn } from "child_process";
17+
import chalk from "chalk";
18+
import ora from "ora";
19+
import prompts from "prompts";
20+
21+
export class CompactUpdater {
22+
/**
23+
* Check if Compact needs updating
24+
*/
25+
static needsUpdate(currentVersion: string, requiredVersion: string): boolean {
26+
const current = currentVersion.split(".").map(Number);
27+
const required = requiredVersion.split(".").map(Number);
28+
29+
for (let i = 0; i < Math.max(current.length, required.length); i++) {
30+
const curr = current[i] || 0;
31+
const req = required[i] || 0;
32+
33+
if (curr < req) return true;
34+
if (curr > req) return false;
35+
}
36+
37+
return false;
38+
}
39+
40+
/**
41+
* Get the current installed Compact version
42+
*/
43+
static getCurrentVersion(): string | null {
44+
try {
45+
const versionOutput = execSync("compact compile --version", {
46+
encoding: "utf-8",
47+
}).trim();
48+
49+
const versionMatch = versionOutput.match(/(\d+\.\d+\.\d+)/);
50+
return versionMatch ? versionMatch[1] : null;
51+
} catch {
52+
return null;
53+
}
54+
}
55+
56+
/**
57+
* Prompt user to update Compact compiler
58+
*/
59+
static async promptUpdate(
60+
currentVersion: string,
61+
requiredVersion: string
62+
): Promise<boolean> {
63+
console.log(
64+
chalk.yellow(
65+
`\n⚠️ Your Compact compiler version ${chalk.bold(
66+
currentVersion
67+
)} is outdated.`
68+
)
69+
);
70+
console.log(
71+
chalk.yellow(
72+
` This template requires version ${chalk.bold(
73+
requiredVersion
74+
)}+ to work correctly.\n`
75+
)
76+
);
77+
78+
const response = await prompts({
79+
type: "confirm",
80+
name: "shouldUpdate",
81+
message: `Would you like to update Compact compiler now?`,
82+
initial: true,
83+
});
84+
85+
return response.shouldUpdate;
86+
}
87+
88+
/**
89+
* Update Compact compiler to the latest version
90+
*/
91+
static async updateCompact(): Promise<boolean> {
92+
const spinner = ora({
93+
text: "Updating Compact compiler to latest version...",
94+
color: "cyan",
95+
}).start();
96+
97+
return new Promise((resolve) => {
98+
const installScript =
99+
"curl --proto '=https' --tlsv1.2 -LsSf https://github.com/midnightntwrk/compact/releases/latest/download/compact-installer.sh | sh";
100+
101+
const updateProcess = spawn("sh", ["-c", installScript], {
102+
stdio: ["inherit", "pipe", "pipe"],
103+
});
104+
105+
let hasError = false;
106+
107+
updateProcess.stdout?.on("data", (data) => {
108+
const output = data.toString().trim();
109+
if (output) {
110+
spinner.text = `Updating Compact compiler... ${chalk.gray(
111+
output.slice(0, 60)
112+
)}`;
113+
}
114+
});
115+
116+
updateProcess.stderr?.on("data", (data) => {
117+
const error = data.toString().trim();
118+
if (error && !error.includes("Downloading")) {
119+
hasError = true;
120+
spinner.fail(
121+
`Failed to update Compact compiler: ${chalk.red(error)}`
122+
);
123+
}
124+
});
125+
126+
updateProcess.on("close", (code) => {
127+
if (code === 0 && !hasError) {
128+
// Verify the new version
129+
const newVersion = this.getCurrentVersion();
130+
if (newVersion) {
131+
spinner.succeed(
132+
`Compact compiler updated successfully to version ${chalk.green(
133+
newVersion
134+
)}`
135+
);
136+
resolve(true);
137+
} else {
138+
spinner.fail("Update completed but could not verify version");
139+
resolve(false);
140+
}
141+
} else {
142+
if (!hasError) {
143+
spinner.fail(
144+
`Failed to update Compact compiler (exit code: ${code})`
145+
);
146+
}
147+
resolve(false);
148+
}
149+
});
150+
151+
updateProcess.on("error", (error) => {
152+
spinner.fail(`Failed to update Compact compiler: ${error.message}`);
153+
resolve(false);
154+
});
155+
});
156+
}
157+
158+
/**
159+
* Handle Compact version mismatch with automatic update option
160+
*/
161+
static async handleVersionMismatch(
162+
currentVersion: string,
163+
requiredVersion: string
164+
): Promise<boolean> {
165+
const shouldUpdate = await this.promptUpdate(
166+
currentVersion,
167+
requiredVersion
168+
);
169+
170+
if (!shouldUpdate) {
171+
console.log(
172+
chalk.yellow(
173+
`\n⚠️ Skipping update. You can update manually later with:`
174+
)
175+
);
176+
console.log(
177+
chalk.gray(
178+
` curl --proto '=https' --tlsv1.2 -LsSf https://github.com/midnightntwrk/compact/releases/latest/download/compact-installer.sh | sh\n`
179+
)
180+
);
181+
return false;
182+
}
183+
184+
const updateSuccess = await this.updateCompact();
185+
186+
if (!updateSuccess) {
187+
console.log(
188+
chalk.red(
189+
`\n❌ Update failed. Please try updating manually or check your internet connection.\n`
190+
)
191+
);
192+
return false;
193+
}
194+
195+
// Verify the update meets requirements
196+
const newVersion = this.getCurrentVersion();
197+
if (newVersion && !this.needsUpdate(newVersion, requiredVersion)) {
198+
console.log(chalk.green(`\n✓ Compact compiler is now up to date!\n`));
199+
return true;
200+
} else {
201+
console.log(
202+
chalk.yellow(
203+
`\n⚠️ Update completed, but version may still need manual verification.\n`
204+
)
205+
);
206+
return false;
207+
}
208+
}
209+
}

src/utils/requirement-checker.ts

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,18 +68,34 @@ export class RequirementChecker {
6868
}
6969

7070
/**
71-
* Check Compact compiler availability
71+
* Check Compact compiler availability and version
7272
*/
73-
static checkCompactCompiler(): RequirementCheck {
73+
static checkCompactCompiler(minVersion?: string): RequirementCheck {
7474
try {
75-
const version = execSync("compact compile --version", {
75+
const versionOutput = execSync("compact compile --version", {
7676
encoding: "utf-8",
7777
}).trim();
78+
79+
// Extract version number (e.g., "Compactc version: 0.23.0" -> "0.23.0")
80+
const versionMatch = versionOutput.match(/(\d+\.\d+\.\d+)/);
81+
const currentVersion = versionMatch ? versionMatch[1] : versionOutput;
82+
83+
// Check version compatibility
84+
let isCompatible = true;
85+
let versionWarning = "";
86+
87+
if (minVersion && currentVersion) {
88+
isCompatible = this.compareVersions(currentVersion, minVersion) >= 0;
89+
if (!isCompatible) {
90+
versionWarning = ` (requires ${minVersion}+, found ${currentVersion})`;
91+
}
92+
}
93+
7894
return {
79-
name: "Compact Compiler",
95+
name: `Compact Compiler${versionWarning}`,
8096
required: true,
81-
found: true,
82-
version: version,
97+
found: isCompatible,
98+
version: currentVersion,
8399
installCommand:
84100
"curl --proto '=https' --tlsv1.2 -LsSf https://github.com/midnightntwrk/compact/releases/latest/download/compact-installer.sh | sh",
85101
};
@@ -94,6 +110,25 @@ export class RequirementChecker {
94110
}
95111
}
96112

113+
/**
114+
* Compare semantic versions (e.g., "0.23.0" vs "0.15.0")
115+
* Returns: 1 if v1 > v2, -1 if v1 < v2, 0 if equal
116+
*/
117+
private static compareVersions(v1: string, v2: string): number {
118+
const parts1 = v1.split(".").map(Number);
119+
const parts2 = v2.split(".").map(Number);
120+
121+
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
122+
const part1 = parts1[i] || 0;
123+
const part2 = parts2[i] || 0;
124+
125+
if (part1 > part2) return 1;
126+
if (part1 < part2) return -1;
127+
}
128+
129+
return 0;
130+
}
131+
97132
/**
98133
* Display requirement check results
99134
*/

0 commit comments

Comments
 (0)