Skip to content

Commit 1262777

Browse files
committed
feat: implement versioning
1 parent 65739de commit 1262777

4 files changed

Lines changed: 92 additions & 21 deletions

File tree

electron-app/.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,5 @@ coverage
8181

8282
# Config and config backups
8383
config.toml
84-
config.toml.backup-*
84+
config.toml.backup-*
85+
version.toml

electron-app/src/config/configInstance.js

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,13 @@
44
* Provides async wrappers for ConfigManager operations with lazy initialization.
55
*/
66

7+
import { app } from "electron";
78
import { logger } from "../utils/logger.js";
8-
import { getTemplatePath, getUserConfigPath } from "../utils/paths.js";
9+
import {
10+
getTemplatePath,
11+
getUserConfigPath,
12+
getVersionFilePath,
13+
} from "../utils/paths.js";
914

1015
// Store the singleton ConfigManager instance
1116
let configManager = null;
@@ -26,11 +31,19 @@ async function getConfigManager() {
2631
// Get paths for user config and template
2732
const userConfigPath = getUserConfigPath();
2833
const templatePath = getTemplatePath();
34+
const versionFilePath = getVersionFilePath();
2935

3036
// Create new ConfigManager instance
31-
configManager = new ConfigManager(userConfigPath, templatePath);
37+
configManager = new ConfigManager(
38+
userConfigPath,
39+
templatePath,
40+
versionFilePath,
41+
app.getVersion(),
42+
);
43+
3244
logger.config.info("ConfigManager initialized");
3345
logger.config.path("User config", userConfigPath);
46+
logger.config.path("User version config", versionFilePath);
3447
logger.config.path("Template path", templatePath);
3548
}
3649

electron-app/src/config/configManager.js

Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
* Handles reading, writing, and updating configuration files while maintaining formatting and comments.
55
*/
66

7+
import TOML from "@iarna/toml";
78
import fs from "fs";
89
import path from "path";
9-
import TOML from "@iarna/toml";
1010
import { logger } from "../utils/logger.js";
1111

1212
/**
@@ -69,7 +69,7 @@ function updateTomlValue(tomlContent, section, key, newValue) {
6969

7070
// Parse the line: key = value # comment
7171
const match = line.match(
72-
/^(\s*)([a-zA-Z_][a-zA-Z0-9_-]*)(\s*=\s*)([^#]+?)((?:\s*#.*)?)$/
72+
/^(\s*)([a-zA-Z_][a-zA-Z0-9_-]*)(\s*=\s*)([^#]+?)((?:\s*#.*)?)$/,
7373
);
7474

7575
// Check if this line matches the key we're looking for
@@ -89,7 +89,7 @@ function updateTomlValue(tomlContent, section, key, newValue) {
8989
} else if (Array.isArray(newValue)) {
9090
// Simple array formatting
9191
const items = newValue.map((v) =>
92-
typeof v === "string" ? `"${v}"` : v
92+
typeof v === "string" ? `"${v}"` : v,
9393
);
9494
formattedValue = `[${items.join(", ")}]`;
9595
} else if (newValue === null || newValue === undefined) {
@@ -111,7 +111,7 @@ function updateTomlValue(tomlContent, section, key, newValue) {
111111
// Warn if key was not found
112112
if (!updated) {
113113
console.warn(
114-
`Warning: Key "${key}" in section "${section || "root"}" not found`
114+
`Warning: Key "${key}" in section "${section || "root"}" not found`,
115115
);
116116
}
117117

@@ -165,13 +165,17 @@ class ConfigManager {
165165
* Creates a new ConfigManager instance.
166166
* @param {string} userConfigPath - Path to the user configuration file.
167167
* @param {string} templatePath - Path to the template configuration file.
168+
* @param {string} versionFilePath - Path to the version.toml (app version)
169+
* @param {string} appVersion - Current electron bundle version from package.json
168170
* @example
169-
* const manager = new ConfigManager("/path/to/config.toml", "/path/to/template.toml");
171+
* const manager = new ConfigManager("/path/to/config.toml", "/path/to/template.toml", "/path/to/version.toml" app.getVersion());
170172
*/
171-
constructor(userConfigPath, templatePath) {
173+
constructor(userConfigPath, templatePath, versionFilePath, appVersion) {
172174
// Store paths
173175
this.userConfigPath = userConfigPath;
174176
this.templatePath = templatePath;
177+
this.versionFilePath = versionFilePath;
178+
this.appVersion = appVersion;
175179

176180
// Ensure user config exists (copy from template on first run)
177181
this.ensureConfigExists();
@@ -192,16 +196,44 @@ class ConfigManager {
192196

193197
// Copy template if user config doesn't exist
194198
if (!fs.existsSync(this.userConfigPath)) {
195-
if (fs.existsSync(this.templatePath)) {
196-
// Copy template to user config location
197-
fs.copyFileSync(this.templatePath, this.userConfigPath);
198-
logger.config.info(
199-
`Created config from template: ${this.userConfigPath}`
200-
);
201-
} else {
202-
// Throw error if template is missing
199+
if (!fs.existsSync(this.templatePath)) {
203200
throw new Error(`Template not found: ${this.templatePath}`);
204201
}
202+
203+
// Copy template to user config location
204+
fs.copyFileSync(this.templatePath, this.userConfigPath);
205+
logger.config.info(
206+
`Created config from template: ${this.userConfigPath}`,
207+
);
208+
209+
fs.writeFileSync(
210+
this.versionFilePath,
211+
`version = "${this.appVersion}"`,
212+
"utf-8",
213+
);
214+
logger.config.info(`Created app version file: ${this.versionFilePath}`);
215+
return;
216+
}
217+
218+
// If config does exist, get app's version
219+
// In case version.toml doesn't exists it returns null
220+
const storedVersion = fs.existsSync(this.versionFilePath)
221+
? (fs
222+
.readFileSync(this.versionFilePath, "utf-8")
223+
.trim()
224+
.match(/^version\s*=\s*"(.+)"$/)?.[1] ?? null)
225+
: null;
226+
227+
if (storedVersion !== this.appVersion) {
228+
fs.copyFileSync(this.templatePath, this.userConfigPath);
229+
fs.writeFileSync(
230+
this.versionFilePath,
231+
`version = "${this.appVersion}"`,
232+
"utf-8",
233+
);
234+
logger.config.info(
235+
`Config updated from template (from version ${storedVersion ?? "unknown"} to ${this.appVersion})`,
236+
);
205237
}
206238
}
207239

@@ -360,4 +392,4 @@ class ConfigManager {
360392
}
361393
}
362394

363-
export { ConfigManager, updateTomlValue, updateTomlFromObject };
395+
export { ConfigManager, updateTomlFromObject, updateTomlValue };

electron-app/src/utils/paths.js

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,14 @@ function getBinaryPath(name) {
5959
return path.join(
6060
getAppPath(),
6161
"binaries",
62-
`${name}-${goos}-${goarch}${ext}`
62+
`${name}-${goos}-${goarch}${ext}`,
6363
);
6464
}
6565

6666
return path.join(
6767
process.resourcesPath,
6868
"binaries",
69-
`${name}-${goos}-${goarch}${ext}`
69+
`${name}-${goos}-${goarch}${ext}`,
7070
);
7171
}
7272

@@ -90,6 +90,25 @@ function getUserConfigPath() {
9090
return path.join(configsDir, "config.toml");
9191
}
9292

93+
/**
94+
* Gets the path to the user app version file.
95+
* @returns {string} The absolute path to the user's version.toml file.
96+
* @example
97+
* const configVersionPath = getVersionFilePath();
98+
* // Development: returns "electron-app/version.toml"
99+
* // Production: returns "userData/configs/version.toml"
100+
*/
101+
function getVersionFilePath() {
102+
if (!app.isPackaged) {
103+
// Development: use local config.toml in project root
104+
return path.join(getAppPath(), "version.toml");
105+
}
106+
107+
// Production: user config in userData directory
108+
const userConfigDir = app.getPath("userData");
109+
return path.join(userConfigDir, "version.toml");
110+
}
111+
93112
/**
94113
* Gets the path to the configuration template file.
95114
* @returns {string} The absolute path to the configuration template file.
@@ -108,4 +127,10 @@ function getTemplatePath() {
108127
return path.join(process.resourcesPath, "config.toml");
109128
}
110129

111-
export { getAppPath, getBinaryPath, getTemplatePath, getUserConfigPath };
130+
export {
131+
getAppPath,
132+
getBinaryPath,
133+
getTemplatePath,
134+
getUserConfigPath,
135+
getVersionFilePath,
136+
};

0 commit comments

Comments
 (0)