Skip to content

Commit 6197f3d

Browse files
committed
fix uninstall on linux for firefox
1 parent 4548245 commit 6197f3d

4 files changed

Lines changed: 124 additions & 100 deletions

File tree

src/extensionIds.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* Centralized Extension IDs for SmartLink
3+
*
4+
* This file contains all extension IDs used across different browsers.
5+
* Update these IDs if the extensions are republished or if IDs change.
6+
*/
7+
8+
export const EXTENSION_IDS = {
9+
// Chromium-based browsers (Chrome, Edge, Brave, Opera, Vivaldi)
10+
// All use the same Chrome Web Store extension ID
11+
chromium: "hfkipjpbjnpdpaofpilegpmbbfhmoceb",
12+
13+
// Firefox Add-ons
14+
// Format: {GUID}
15+
firefox: "{85f2d9b5-ecd7-4710-8ad9-fc4cf09621d8}",
16+
} as const;
17+
18+
/**
19+
* Get the extension ID for a specific browser
20+
*/
21+
export function getExtensionId(browser: string): string {
22+
const normalizedBrowser = browser.toLowerCase();
23+
24+
switch (normalizedBrowser) {
25+
case "chrome":
26+
case "edge":
27+
case "brave":
28+
case "opera":
29+
case "vivaldi":
30+
return EXTENSION_IDS.chromium;
31+
32+
case "firefox":
33+
return EXTENSION_IDS.firefox;
34+
35+
default:
36+
throw new Error(`Unsupported browser: ${browser}`);
37+
}
38+
}

src/installExtension.ts

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { FirefoxExtensionInstaller } from "./utils/firefox";
77
import { InstallerOptions } from "./type";
88
import { createLogger } from "./utils/logger";
99
import { Vivaldi } from "./utils/vivaldi";
10+
import { getExtensionId, EXTENSION_IDS } from "./extensionIds";
1011

1112
export async function installExtensionCommand(
1213
argv: ArgumentsCamelCase<{
@@ -88,53 +89,51 @@ export async function installExtensionCommand(
8889
}
8990

9091
async function installForBrowser(browser: string, installerOptions: InstallerOptions, logger: any): Promise<void> {
92+
logger.info(`Installation pour ${browser.charAt(0).toUpperCase() + browser.slice(1)} - Initialisation`);
93+
9194
if (browser === "chrome") {
92-
logger.info("Installation pour Chrome - Initialisation");
93-
const chromeExtensionId = "hfkipjpbjnpdpaofpilegpmbbfhmoceb";
94-
logger.info(`Installation de l'extension pour Chrome avec ID ${chromeExtensionId}...`);
95+
const extensionId = getExtensionId("chrome");
96+
logger.info(`Installation de l'extension pour Chrome avec ID ${extensionId}...`);
9597
const chromeInstaller = new Chrome(installerOptions);
9698
logger.debug("Instance Chrome créée, début de l'installation");
97-
await chromeInstaller.installExtension(chromeExtensionId);
99+
await chromeInstaller.installExtension(extensionId);
98100
logger.info("Installation de l'extension Chrome terminée avec succès");
99101
} else if (browser === "edge") {
100-
logger.info("Installation pour Edge - Initialisation");
101-
const edgeExtensionId = "hfkipjpbjnpdpaofpilegpmbbfhmoceb";
102-
logger.info(`Installation de l'extension pour Edge avec ID ${edgeExtensionId}...`);
102+
const extensionId = getExtensionId("edge");
103+
logger.info(`Installation de l'extension pour Edge avec ID ${extensionId}...`);
103104
const edgeInstaller = new Edge(installerOptions);
104105
logger.debug("Instance Edge créée, début de l'installation");
105-
await edgeInstaller.installExtension(edgeExtensionId);
106+
await edgeInstaller.installExtension(extensionId);
106107
logger.info("Installation de l'extension Edge terminée avec succès");
107108
} else if (browser === "brave") {
108-
logger.info("Installation pour Brave - Initialisation");
109-
const braveExtensionId = "hfkipjpbjnpdpaofpilegpmbbfhmoceb";
110-
logger.info(`Installation de l'extension pour Brave avec ID ${braveExtensionId}...`);
109+
const extensionId = getExtensionId("brave");
110+
logger.info(`Installation de l'extension pour Brave avec ID ${extensionId}...`);
111111
const braveInstaller = new Brave(installerOptions);
112112
logger.debug("Instance Brave créée, début de l'installation");
113-
await braveInstaller.installExtension(braveExtensionId);
113+
await braveInstaller.installExtension(extensionId);
114114
logger.info("Installation de l'extension Brave terminée avec succès");
115115
} else if (browser === "opera") {
116-
logger.info("Installation pour Opera - Initialisation");
117-
const operaExtensionId = "hfkipjpbjnpdpaofpilegpmbbfhmoceb";
118-
logger.info(`Installation de l'extension pour Opera avec ID ${operaExtensionId}...`);
116+
const extensionId = getExtensionId("opera");
117+
logger.info(`Installation de l'extension pour Opera avec ID ${extensionId}...`);
119118
const operaInstaller = new Opera(installerOptions);
120119
logger.debug("Instance Opera créée, début de l'installation");
121-
await operaInstaller.installExtension(operaExtensionId);
120+
await operaInstaller.installExtension(extensionId);
122121
logger.info("Installation de l'extension Opera terminée avec succès");
123122
} else if (browser === "firefox") {
124-
logger.info("Installation pour Firefox - Initialisation");
123+
// Pour Firefox, on utilise le slug du Mozilla Add-ons Store
125124
const firefoxExtensionName = "smartlink-extension";
126125
logger.info(`Installation de l'extension pour Firefox avec nom ${firefoxExtensionName}...`);
126+
logger.debug(`ID Firefox de l'extension: ${EXTENSION_IDS.firefox}`);
127127
logger.debug("Création de l'instance Firefox");
128128
const Firefox = new FirefoxExtensionInstaller(installerOptions);
129129
await Firefox.installExtensions([firefoxExtensionName]);
130130
logger.info("Installation de l'extension Firefox terminée avec succès");
131131
} else if (browser === "vivaldi") {
132-
logger.info("Installation pour Vivaldi - Initialisation");
133-
const vivaldiExtensionId = "hfkipjpbjnpdpaofpilegpmbbfhmoceb";
134-
logger.info(`Installation de l'extension pour Vivaldi avec ID ${vivaldiExtensionId}...`);
132+
const extensionId = getExtensionId("vivaldi");
133+
logger.info(`Installation de l'extension pour Vivaldi avec ID ${extensionId}...`);
135134
const vivaldiInstaller = new Vivaldi(installerOptions);
136135
logger.debug("Instance Vivaldi créée, début de l'installation");
137-
await vivaldiInstaller.installExtension(vivaldiExtensionId);
136+
await vivaldiInstaller.installExtension(extensionId);
138137
logger.info("Installation de l'extension Vivaldi terminée avec succès");
139138
} else {
140139
logger.error(`Navigateur non supporté: ${browser}`);

src/uninstallExtension.ts

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { FirefoxExtensionInstaller } from "./utils/firefox";
77
import { InstallerOptions } from "./type";
88
import { createLogger } from "./utils/logger";
99
import { Vivaldi } from "./utils/vivaldi";
10+
import { getExtensionId } from "./extensionIds";
1011

1112
export async function uninstallExtensionCommand(
1213
argv: ArgumentsCamelCase<{
@@ -76,53 +77,39 @@ export async function uninstallExtensionCommand(
7677
}
7778

7879
async function uninstallForBrowser(browser: string, installerOptions: InstallerOptions, logger: any): Promise<void> {
80+
const extensionId = getExtensionId(browser);
81+
logger.info(`Désinstallation pour ${browser.charAt(0).toUpperCase() + browser.slice(1)} - Initialisation`);
82+
logger.info(`Désinstallation de l'extension avec ID ${extensionId}...`);
83+
7984
if (browser === "chrome") {
80-
logger.info("Désinstallation pour Chrome - Initialisation");
81-
const chromeExtensionId = "hfkipjpbjnpdpaofpilegpmbbfhmoceb";
82-
logger.info(`Désinstallation de l'extension pour Chrome avec ID ${chromeExtensionId}...`);
8385
const chromeInstaller = new Chrome(installerOptions);
8486
logger.debug("Instance Chrome créée, début de la désinstallation");
85-
await chromeInstaller.uninstallExtension(chromeExtensionId);
87+
await chromeInstaller.uninstallExtension(extensionId);
8688
logger.info("Désinstallation de l'extension Chrome terminée avec succès");
8789
} else if (browser === "edge") {
88-
logger.info("Désinstallation pour Edge - Initialisation");
89-
const edgeExtensionId = "hfkipjpbjnpdpaofpilegpmbbfhmoceb";
90-
logger.info(`Désinstallation de l'extension pour Edge avec ID ${edgeExtensionId}...`);
9190
const edgeInstaller = new Edge(installerOptions);
9291
logger.debug("Instance Edge créée, début de la désinstallation");
93-
await edgeInstaller.uninstallExtension(edgeExtensionId);
92+
await edgeInstaller.uninstallExtension(extensionId);
9493
logger.info("Désinstallation de l'extension Edge terminée avec succès");
9594
} else if (browser === "brave") {
96-
logger.info("Désinstallation pour Brave - Initialisation");
97-
const braveExtensionId = "hfkipjpbjnpdpaofpilegpmbbfhmoceb";
98-
logger.info(`Désinstallation de l'extension pour Brave avec ID ${braveExtensionId}...`);
9995
const braveInstaller = new Brave(installerOptions);
10096
logger.debug("Instance Brave créée, début de la désinstallation");
101-
await braveInstaller.uninstallExtension(braveExtensionId);
97+
await braveInstaller.uninstallExtension(extensionId);
10298
logger.info("Désinstallation de l'extension Brave terminée avec succès");
10399
} else if (browser === "opera") {
104-
logger.info("Désinstallation pour Opera - Initialisation");
105-
const operaExtensionId = "hfkipjpbjnpdpaofpilegpmbbfhmoceb";
106-
logger.info(`Désinstallation de l'extension pour Opera avec ID ${operaExtensionId}...`);
107100
const operaInstaller = new Opera(installerOptions);
108101
logger.debug("Instance Opera créée, début de la désinstallation");
109-
await operaInstaller.uninstallExtension(operaExtensionId);
102+
await operaInstaller.uninstallExtension(extensionId);
110103
logger.info("Désinstallation de l'extension Opera terminée avec succès");
111104
} else if (browser === "firefox") {
112-
logger.info("Désinstallation pour Firefox - Initialisation");
113-
const firefoxExtensionId = "{c7303b67-e0fd-40f6-a14c-8ae3c2c9b4e7}";
114-
logger.info(`Désinstallation de l'extension pour Firefox avec ID ${firefoxExtensionId}...`);
115105
logger.debug("Création de l'instance Firefox");
116106
const Firefox = new FirefoxExtensionInstaller(installerOptions);
117-
await Firefox.uninstallExtension(firefoxExtensionId);
107+
await Firefox.uninstallExtension(extensionId);
118108
logger.info("Désinstallation de l'extension Firefox terminée avec succès");
119109
} else if (browser === "vivaldi") {
120-
logger.info("Désinstallation pour Vivaldi - Initialisation");
121-
const vivaldiExtensionId = "hfkipjpbjnpdpaofpilegpmbbfhmoceb";
122-
logger.info(`Désinstallation de l'extension pour Vivaldi avec ID ${vivaldiExtensionId}...`);
123110
const vivaldiInstaller = new Vivaldi(installerOptions);
124111
logger.debug("Instance Vivaldi créée, début de la désinstallation");
125-
await vivaldiInstaller.uninstallExtension(vivaldiExtensionId);
112+
await vivaldiInstaller.uninstallExtension(extensionId);
126113
logger.info("Désinstallation de l'extension Vivaldi terminée avec succès");
127114
} else {
128115
logger.error(`Navigateur non supporté: ${browser}`);

src/utils/firefox.ts

Lines changed: 55 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -129,11 +129,8 @@ export class FirefoxExtensionInstaller {
129129
*/
130130
getFirefoxProfiles() {
131131
try {
132-
const baseDir = this.getDefaultFirefoxProfilesDir();
133132
// Path to the profiles.ini file
134-
const profilesIniPath = join(baseDir, "profiles.ini");
135-
136-
this.log("debug", `Looking for profiles.ini at: ${profilesIniPath}`);
133+
const profilesIniPath = join(this.getDefaultFirefoxProfilesDir(), "profiles.ini");
137134

138135
if (!existsSync(profilesIniPath)) {
139136
this.log("error", `Firefox profiles.ini not found at ${profilesIniPath}`);
@@ -154,26 +151,6 @@ export class FirefoxExtensionInstaller {
154151
}
155152
}
156153

157-
// Try alternative locations on Linux
158-
if (process.platform === "linux") {
159-
const homeDir = homedir();
160-
const altLocations = [
161-
join(homeDir, ".mozilla", "firefox", "profiles.ini"),
162-
join(homeDir, "snap", "firefox", "common", ".mozilla", "firefox", "profiles.ini"),
163-
join(homeDir, ".var", "app", "org.mozilla.firefox", "data", "mozilla", "firefox", "profiles.ini"),
164-
];
165-
166-
for (const altPath of altLocations) {
167-
this.log("debug", `Trying alternative location: ${altPath}`);
168-
if (existsSync(altPath)) {
169-
this.log("info", `Found profiles.ini at alternative location: ${altPath}`);
170-
const profilesIni = readFileSync(altPath, "utf8");
171-
return this.parseProfilesIni(profilesIni, dirname(altPath));
172-
}
173-
}
174-
}
175-
176-
this.log("error", "No profiles.ini found in any location");
177154
return [];
178155
}
179156

@@ -278,21 +255,31 @@ export class FirefoxExtensionInstaller {
278255
let command: string;
279256

280257
if (process.platform === "win32") {
281-
command = "tasklist | findstr firefox";
258+
// Windows - check for firefox.exe
259+
command = 'tasklist /FI "IMAGENAME eq firefox.exe" /NH | findstr firefox';
282260
} else if (process.platform === "darwin") {
283-
command = 'pgrep -x "firefox" || pgrep -x "Firefox"';
261+
// macOS - check for Firefox.app
262+
command = 'pgrep -lf "Firefox.app/Contents/MacOS/firefox" | head -1';
284263
} else {
285-
// Linux - check multiple possible process names
286-
command = 'pgrep -f "firefox|firefox-bin" || pgrep -x "firefox" || pgrep -x "firefox-esr"';
264+
// Linux - check for main firefox process, exclude helper processes
265+
// Use pgrep with full command line to be more accurate
266+
command = 'pgrep -f "^/.*firefox$|^firefox$" | head -1';
287267
}
288268

269+
this.log("debug", `Vérification si Firefox est en cours d'exécution: ${command}`);
289270
const { stdout } = await execAsync(command);
290-
const isRunning = stdout.trim().length > 0;
291-
this.log("debug", `Firefox running check: ${isRunning ? "running" : "not running"}`);
292-
return isRunning;
271+
272+
const result = stdout.trim();
273+
if (result.length > 0) {
274+
this.log("debug", `Firefox semble être en cours d'exécution: ${result}`);
275+
return true;
276+
}
277+
278+
this.log("debug", "Firefox n'est pas en cours d'exécution");
279+
return false;
293280
} catch (error) {
294-
// Process not found - this is expected when Firefox is not running
295-
this.log("debug", "Firefox not running (process not found)");
281+
// Command failed (exit code != 0) usually means no process found
282+
this.log("debug", "Aucun processus Firefox détecté");
296283
return false;
297284
}
298285
}
@@ -668,49 +655,60 @@ export class FirefoxExtensionInstaller {
668655
async uninstallExtension(extensionId: string) {
669656
try {
670657
this.log("info", `Début de la désinstallation de l'extension ${extensionId}`);
671-
672-
// Fermer Firefox avant la désinstallation
658+
659+
// Vérifier si Firefox est en cours d'exécution
673660
const isRunning = await this.isFirefoxRunning();
661+
674662
if (isRunning) {
675-
this.log("warn", "Firefox est en cours d'exécution. Tentative de fermeture...");
663+
this.log("warn", "Firefox semble être en cours d'exécution. Tentative de fermeture automatique...");
664+
676665
try {
666+
// Tenter de fermer Firefox
677667
await BrowserController.close("firefox");
668+
678669
// Attendre que Firefox se ferme complètement
670+
this.log("debug", "Attente de 3 secondes pour que Firefox se ferme complètement...");
679671
await new Promise((resolve) => setTimeout(resolve, 3000));
680-
681-
// Vérifier à nouveau
672+
673+
// Vérifier à nouveau si Firefox est fermé
682674
const stillRunning = await this.isFirefoxRunning();
675+
683676
if (stillRunning) {
684-
this.log("error", "Firefox est toujours en cours d'exécution. Veuillez le fermer manuellement.");
685-
throw new Error("Firefox doit être fermé pour désinstaller l'extension");
677+
this.log("warn", "Firefox est toujours en cours d'exécution après la tentative de fermeture.");
678+
this.log("info", "Tentative de désinstallation malgré tout...");
679+
} else {
680+
this.log("info", "Firefox a été fermé avec succès");
686681
}
687682
} catch (error) {
688-
this.log("error", `Impossible de fermer Firefox automatiquement: ${error}`);
689-
throw new Error("Veuillez fermer Firefox manuellement avant de désinstaller l'extension");
683+
this.log("warn", `Impossible de fermer Firefox automatiquement: ${error}`);
684+
this.log("info", "Tentative de désinstallation malgré tout...");
690685
}
686+
} else {
687+
this.log("debug", "Firefox n'est pas en cours d'exécution, poursuite de la désinstallation");
691688
}
692689

693690
const profiles = this.getFirefoxProfiles();
694-
691+
695692
if (profiles.length === 0) {
696693
throw new Error("Aucun profil Firefox trouvé");
697694
}
698-
699-
this.log("info", `Désinstallation de l'extension ${extensionId} pour ${profiles.length} profils Firefox...`);
695+
696+
this.log("info", `Désinstallation de l'extension ${extensionId} pour ${profiles.length} profil(s) Firefox...`);
700697

701698
let foundAny = false;
702-
699+
let successCount = 0;
700+
703701
for (const profile of profiles) {
704702
this.log("debug", `Traitement du profil: ${profile.name} (${profile.path})`);
705-
703+
706704
const profilePath = profile.path;
707-
705+
708706
// Vérifier que le profil existe
709707
if (!existsSync(profilePath)) {
710708
this.log("warn", `Le profil ${profile.name} n'existe pas à ${profilePath}`);
711709
continue;
712710
}
713-
711+
714712
const extensionsDir = join(profilePath, "extensions");
715713
const extensionPath = join(extensionsDir, `${extensionId}.xpi`);
716714

@@ -720,10 +718,11 @@ export class FirefoxExtensionInstaller {
720718
try {
721719
rmSync(extensionPath, { force: true });
722720
foundAny = true;
723-
this.log("info", `Extension supprimée avec succès de ${profile.name}`);
721+
successCount++;
722+
this.log("info", `✓ Extension supprimée avec succès du profil ${profile.name}`);
724723
} catch (error) {
725-
this.log("error", `Impossible de supprimer ${extensionPath}: ${error}`);
726-
throw new Error(`Erreur de permissions: impossible de supprimer ${extensionPath}. Vérifiez les permissions ou exécutez avec sudo.`);
724+
this.log("error", `Impossible de supprimer ${extensionPath}: ${error}`);
725+
this.log("warn", "Cela peut être dû à des permissions insuffisantes ou à Firefox verrouillant le fichier.");
727726
}
728727
} else {
729728
this.log("debug", `Extension non trouvée dans ${profile.name}: ${extensionPath}`);
@@ -743,7 +742,7 @@ export class FirefoxExtensionInstaller {
743742
if (extensionsData.addons.length < initialLength) {
744743
writeFileSync(extensionsJsonPath, JSON.stringify(extensionsData, null, 2));
745744
foundAny = true;
746-
this.log("info", `Extension supprimée de extensions.json pour le profil: ${profile.name}`);
745+
this.log("info", `Extension supprimée de extensions.json pour le profil: ${profile.name}`);
747746
} else {
748747
this.log("debug", `Extension ${extensionId} non trouvée dans extensions.json`);
749748
}
@@ -758,9 +757,10 @@ export class FirefoxExtensionInstaller {
758757

759758
if (!foundAny) {
760759
this.log("warn", `L'extension ${extensionId} n'a été trouvée dans aucun profil Firefox`);
761-
this.log("info", "Cela peut être normal si l'extension n'était pas installée ou a déjà été désinstallée.");
760+
this.log("info", "Cela peut être normal si l'extension n'était pas installée.");
762761
} else {
763-
this.log("info", "Désinstallation terminée avec succès. Veuillez redémarrer Firefox pour appliquer les changements.");
762+
this.log("info", `✓ Désinstallation terminée avec succès (${successCount} profil(s) traité(s))`);
763+
this.log("info", "Vous pouvez maintenant rouvrir Firefox.");
764764
}
765765
} catch (error) {
766766
this.log("error", `Erreur dans Firefox.uninstallExtension: ${error}`);

0 commit comments

Comments
 (0)