Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
<!-- ADD CHANGES HERE -->

Unreleased

- Fixed issue where Onfire CLI cannot parse commands on firebase-tools v15
- This is due to the lazy loading feature preventing all commands from being loaded

v1.3.2

- Added feature to list folders/files in current path to make it easier to use
Expand Down
13 changes: 8 additions & 5 deletions src/firebase-cmd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
spawnSync,
} from "child_process";
import spawn from "cross-spawn";
import { writeFileSync } from "fs";

const COMMAND_TIMEOUT = 10000;

Expand Down Expand Up @@ -251,12 +252,15 @@ export class FirebaseCommands {
});

child.on("exit", () => {
if (childBufferString === "ENOENT") {
throw new Error(
`Firebase Tools module not found. Install using 'npm install -g firebase-tools'`
this.loadModuleChildProcess?.kill();
this.loadModuleChildProcess = null;
if (childBufferString.includes("ERROR__")) {
const message = childBufferString.replace(
"ERROR__",
"Error loading module: "
);
throw new Error(message);
}
this.loadModuleChildProcess = null;
try {
res(JSON.parse(childBufferString));
} catch (_) {
Expand All @@ -266,7 +270,6 @@ export class FirebaseCommands {
});

child.send(`${rootPath}/firebase-tools`);
// this.loadModuleChildProcess.kill();
});

// await new Promise((res, rej) => setTimeout(res, 5000));
Expand Down
48 changes: 45 additions & 3 deletions src/module-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,54 @@ function stringify(obj) {
return str;
}

function determineCommandList(map, parentKey = null, depth = 0) {
if (depth > 10) return []
const commandList = []
if (map) {
for (let [key, value] of Object.entries(map)) {
if (key === "load") continue
if (typeof value?.load === "function") {
if (!parentKey) {
commandList.push(key)
} else {
commandList.push([parentKey, key].join(":"))
}
}

if (!parentKey) {
commandList.push(...determineCommandList(value, key, depth + 1))
} else {
commandList.push(...determineCommandList(value, [parentKey, key].join(":"), depth + 1))
}
}
}

return commandList.map((e) => e.toLowerCase())
}

process.on('message', function (path) {
try {
const client = require(path); // Load the Firebase Tools module
const configStr = stringify(client) // Removes circular reference and convert to string
let configStr = stringify(client) // Removes circular reference and convert to string

const clientObj = JSON.parse(configStr)
if (clientObj.cli.commands.length === 0) {
/**
* Due to firebase-tools v15 introducing lazy laoding of commands
* we would need to run `getCommand([command_name])` for each command
* to register each command
*/
const commands = determineCommandList(client)
for (let command of commands) {
require(path).getCommand(command)
}

const clientWithLoadedCommands = require(path);
configStr = stringify(clientWithLoadedCommands)
}

process.send(configStr); // Send to main process
} catch (_) {
process.send('ENOENT'); // Send to main process
} catch (err) {
process.send(`ERROR__${err}`); // Send to main process
}
});
12 changes: 7 additions & 5 deletions src/onfire-cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1034,23 +1034,25 @@ export class OnFireCLI extends CommandLineInterface {
} else {
this.firebaseCommands = cmdConfig;
}
this.attachCustomCommands();
this.savedConfig["firebaseCommands"] = this.firebaseCommands;
} else {
this.firebaseCommands = this.savedConfig["firebaseCommands"];
this.firebaseCli
.getCommandConfig()
.then((firebaseCommands) => {
if (firebaseCommands !== null) {
this.firebaseCommands = firebaseCommands;
this.savedConfig["firebaseCommands"] = this.firebaseCommands;
this.attachCustomCommands();
this.savedConfig["firebaseCommands"] = this.firebaseCommands;
} else {
throw new Error("Could not load firebase-tools commands");
}
})
.catch((_) => {
// No action needed since we can use the cached firebaseCommands
.catch((err) => {
throw err;
});
}
this.attachCustomCommands();
this.savedConfig["firebaseCommands"] = this.firebaseCommands;
}

async init() {
Expand Down
38 changes: 20 additions & 18 deletions tests/onfire-cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class MockOnFireCLI extends OnFireCLI {
}

const appdistribution = {
distributeLen: 10,
distributeLen: 20,
testers: {
addLen: 5,
removeLen: 5,
Expand All @@ -94,22 +94,24 @@ describe("Test loading of Firebase commands", () => {
it("Should load 'appdistribution:distribute' command", async () => {
const firebaseCommands = onfireCLI._getFirebaseCommands();
expect(firebaseCommands["appdistribution:distribute"].description).toEqual(
"upload a release binary"
"upload a release binary and optionally distribute it to testers and run automated tests"
);
});

it("Should load 'appdistribution:testers:add' command", async () => {
const firebaseCommands = onfireCLI._getFirebaseCommands();
expect(firebaseCommands["appdistribution:testers:add"].description).toEqual(
"add testers to project (and possibly group)"
"add testers to project (and App Distribution group, if specified via flag)"
);
});

it("Should load 'appdistribution:testers:remove' command", async () => {
const firebaseCommands = onfireCLI._getFirebaseCommands();
expect(
firebaseCommands["appdistribution:testers:remove"].description
).toEqual("remove testers from a project (or group)");
).toEqual(
"remove testers from a project (or App Distribution group, if specified via flag)"
);
});

it("Should load 'exit' command", async () => {
Expand Down Expand Up @@ -147,6 +149,15 @@ describe("Run simple commands", () => {
"--testers-file",
"--groups",
"--groups-file",
"--test-devices",
"--test-devices-file",
"--test-username",
"--test-password",
"--test-password-file",
"--test-username-resource",
"--test-password-resource",
"--test-case-ids",
"--test-case-ids-file",
"--project",
]);
});
Expand Down Expand Up @@ -471,20 +482,20 @@ describe("Test getting rendering list", () => {
expect(renderMessage.length).toEqual(5);
});

it("Should show that the selected command in index [0] is 'appdistribution:distribute -> upload a release binary'", () => {
it("Should show that the selected command in index [0] is 'appdistribution:distribute -> upload a release binary and optionally distribute it to testers and run automated tests'", () => {
const renderMessage = onfireCLI._getCommandsToRender();
const cmdLabel = `-> upload a release binary`;
const cmdLabel = `-> upload a release binary and optionally distribute it to testers and run automated tests`;
expect(renderMessage[0]).toEqual(
`${cli._textCyan(cli._textBold(">"))} ${cli._textGreen(
cli._textBold("appdistribution:distribute")
)} ${cli._textGreen(cmdLabel)}\x1b[K`
);
});

it("Should show that the unselected command in index [1] is 'appdistribution:testers:add -> add testers to project (and possibly group)'", () => {
it("Should show that the unselected command in index [2] is 'appdistribution:testers:add -> add testers to project (and App Distribution group, if specified via flag)'", () => {
const renderMessage = onfireCLI._getCommandsToRender();
const cmdLabel = `-> add testers to project (and possibly group)`;
expect(renderMessage[1]).toEqual(
const cmdLabel = `-> add testers to project (and App Distribution group, if specified via flag)`;
expect(renderMessage[2]).toEqual(
` ${cli._textBold("appdistribution:testers:add")} ${cmdLabel}\x1b[K`
);
});
Expand Down Expand Up @@ -542,15 +553,6 @@ describe("Test getting rendering list", () => {
)} ${cli._textGreen(cmdLabel)}\x1b[K`
);
});

it("Should show that the unselected option in index [1] is 'experimental:functions:shell -> launch full Node shell with emulated functions. (Alias for `firebase functions:shell.)'", () => {
const renderMessage = onfireCLI._getCommandsToRender();
const cmdLabel =
"-> launch full Node shell with emulated functions. (Alias for `firebase functions:shell.)";
expect(renderMessage[1]).toEqual(
` ${cli._textBold("experimental:functions:shell")} ${cmdLabel}\x1b[K`
);
});
});
});

Expand Down