Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: separate onSuccess events for the Fuels CLI #2616

Closed
5 changes: 5 additions & 0 deletions .changeset/four-seas-stare.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"fuels": minor
---

feat!: separate `onSuccess` events for the Fuels CLI
30 changes: 25 additions & 5 deletions apps/demo-fuels/fuels.config.full.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable no-console */
import { createConfig } from 'fuels';
import type { CommandEvent, ContractDeployOptions, FuelsConfig } from 'fuels';
import type { ContractDeployOptions, DeployedContract, FuelsConfig } from 'fuels';

const MY_FIRST_DEPLOYED_CONTRACT_NAME = '';

Expand Down Expand Up @@ -84,11 +84,31 @@ export default createConfig({
},
// #endregion deployConfig-fn

// #region onSuccess
onSuccess: (event: CommandEvent, config: FuelsConfig) => {
console.log('fuels:onSuccess', { event, config });
// #region onBuild
onBuild: (data: unknown, config: FuelsConfig) => {
console.log('fuels:onBuild', { data, config });
},
// #endregion onSuccess
// #endregion onBuild

// #region onDeploy
// #import { DeployedContract, FuelsConfig };

onDeploy: (data: DeployedContract[], config: FuelsConfig) => {
console.log('fuels:onDeploy', { data, config });
},
// #endregion onDeploy

// #region onDev
onDev: (data: unknown, config: FuelsConfig) => {
console.log('fuels:onDev', { data, config });
},
// #endregion onDev

// #region onNode
onNode: (data: unknown, config: FuelsConfig) => {
console.log('fuels:onNode', { data, config });
},
// #endregion onNode

// #region onFailure
onFailure: (error: Error, config: FuelsConfig) => {
Expand Down
41 changes: 37 additions & 4 deletions apps/docs/src/guide/fuels-cli/config-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,16 +121,49 @@ Or use a function for crafting dynamic deployment flows:

<<< @../../../demo-fuels/fuels.config.full.ts#deployConfig-fn{ts:line-numbers}

## `onSuccess`
## `onBuild`

Pass a callback function to be called after a successful run.
A callback function that is called after a build event has been successful.

Parameters:

- `event` — The event that triggered this execution
- `data` — The data (always `null` for `onBuild`)
- `config` — The loaded config (`fuels.config.ts`)

<<< @../../../demo-fuels/fuels.config.full.ts#onSuccess{ts:line-numbers}
<<< @../../../demo-fuels/fuels.config.full.ts#onBuild{ts:line-numbers}

## `onDeploy`

A callback function that is called after a deployment event has been successful.

Parameters:

- `data` — The data (an array of deployed contracts)
- `config` — The loaded config (`fuels.config.ts`)

<<< @../../../demo-fuels/fuels.config.full.ts#onDeploy{ts:line-numbers}

## `onDev`

A callback function that is called after the [`fuels dev`](./commands.md#fuels-dev) command has successfully restarted.

Parameters:

- `data` — The data (always `null` for `onDev`)
- `config` — The loaded config (`fuels.config.ts`)

<<< @../../../demo-fuels/fuels.config.full.ts#onDev{ts:line-numbers}

## `onNode`

A callback function that is called after the [`fuels node`](./commands.md#fuels-node) command has successfully refreshed.

Parameters:

- `data` — The data (always `null` for `onNode`)
- `config` — The loaded config (`fuels.config.ts`)

<<< @../../../demo-fuels/fuels.config.full.ts#onNode{ts:line-numbers}

## `onFailure`

Expand Down
48 changes: 48 additions & 0 deletions packages/fuels/src/cli/commands/build/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { fuelsConfig } from '../../../../test/fixtures/fuels.config';
import { mockLogger } from '../../../../test/utils/mockLogger';

import { build } from '.';
import * as buildSwayProgramsMod from './buildSwayPrograms';
import * as generateTypesMod from './generateTypes';

/**
* @group node
*/
describe('build', () => {
const mockAll = () => {
const { log } = mockLogger();

const onBuild = vi.fn();

const buildSwayPrograms = vi
.spyOn(buildSwayProgramsMod, 'buildSwayPrograms')
.mockResolvedValue();
const generateTypes = vi.spyOn(generateTypesMod, 'generateTypes').mockResolvedValue();

return {
onBuild,
log,
buildSwayPrograms,
generateTypes,
};
};

test('should build sway programs and generate types', async () => {
const { log, buildSwayPrograms, generateTypes } = mockAll();

await build(fuelsConfig);

expect(log).toHaveBeenCalledWith('Building..');
expect(buildSwayPrograms).toHaveBeenCalled();
expect(generateTypes).toHaveBeenCalled();
});

test('should call onBuild callback', async () => {
const { onBuild } = mockAll();
const config = { ...fuelsConfig, onBuild };

await build(config);

expect(onBuild).toHaveBeenCalledWith(null, config);
});
});
2 changes: 1 addition & 1 deletion packages/fuels/src/cli/commands/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ export async function build(config: FuelsConfig, program?: Command) {

await buildSwayPrograms(config);
await generateTypes(config);
config.onBuild?.(null, config);

const options = program?.opts();

if (options?.deploy) {
const fuelCore = await autoStartFuelCore(config);
await deploy(config);
Expand Down
40 changes: 40 additions & 0 deletions packages/fuels/src/cli/commands/deploy/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type { Provider } from '@fuel-ts/account';
import { Wallet } from '@fuel-ts/account';
import { FUEL_NETWORK_URL } from '@fuel-ts/account/configs';

import { fuelsConfig } from '../../../../test/fixtures/fuels.config';
import type { DeployedContract } from '../../types';

import { deploy } from '.';
import * as createWalletMod from './createWallet';
import * as saveContractIdsMod from './saveContractIds';

/**
* @group node
*/
describe('deploy', () => {
const mockAll = () => {
const onDeploy = vi.fn();

const provider = { url: FUEL_NETWORK_URL } as Provider;
const wallet = Wallet.fromPrivateKey('0x01', provider);
const createWallet = vi.spyOn(createWalletMod, 'createWallet').mockResolvedValue(wallet);

vi.spyOn(saveContractIdsMod, 'saveContractIds').mockResolvedValue();

return {
onDeploy,
createWallet,
};
};

test('should call onDeploy callback', async () => {
const { onDeploy } = mockAll();
const expectedContracts: DeployedContract[] = [];
const config = { ...fuelsConfig, contracts: [], onDeploy };

await deploy(config);

expect(onDeploy).toHaveBeenCalledWith(expectedContracts, config);
});
});
1 change: 1 addition & 0 deletions packages/fuels/src/cli/commands/deploy/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export async function deploy(config: FuelsConfig) {
}

await saveContractIds(contracts, config.output);
config.onDeploy?.(contracts, config);

return contracts;
}
11 changes: 11 additions & 0 deletions packages/fuels/src/cli/commands/dev/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ describe('dev', () => {
function mockAll() {
const { autoStartFuelCore, fuelCore, killChildProcess } = mockStartFuelCore();

const onDev = vi.fn();
const onFailure = vi.fn();

const withConfigErrorHandler = vi
Expand All @@ -50,6 +51,7 @@ describe('dev', () => {
fuelCore,
killChildProcess,
loadConfig,
onDev,
onFailure,
withConfigErrorHandler,
};
Expand All @@ -66,6 +68,15 @@ describe('dev', () => {
expect(deploy).toHaveBeenCalledTimes(1);
});

test('should call `onDev` callback on success', async () => {
const { onDev } = mockAll();
const config: FuelsConfig = { ...fuelsConfig, onDev };

await dev(config);

expect(onDev).toHaveBeenCalledWith(null, config);
});

it('dev should handle and log error from `buildAndDeploy`', async () => {
const { error } = mockLogger();

Expand Down
7 changes: 5 additions & 2 deletions packages/fuels/src/cli/commands/dev/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { watch } from 'chokidar';
import { globSync } from 'glob';

import { loadConfig } from '../../config/loadConfig';
import type { FuelsConfig } from '../../types';
import { type FuelsConfig } from '../../types';
import { error, log } from '../../utils/logger';
import { build } from '../build';
import { deploy } from '../deploy';
Expand All @@ -18,7 +18,10 @@ export const closeAllFileHandlers = (handlers: FSWatcher[]) => {

export const buildAndDeploy = async (config: FuelsConfig) => {
await build(config);
return deploy(config);
const deployedContracts = await deploy(config);
config.onDev?.(null, config);

return deployedContracts;
};

export const getConfigFilepathsToWatch = (config: FuelsConfig) => {
Expand Down
15 changes: 12 additions & 3 deletions packages/fuels/src/cli/commands/node/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ describe('node', () => {
function mockAll() {
const { autoStartFuelCore, fuelCore, killChildProcess } = mockStartFuelCore();

const onNode = vi.fn();
const onFailure = vi.fn();

const withConfigErrorHandler = vi
Expand All @@ -35,6 +36,7 @@ describe('node', () => {
fuelCore,
killChildProcess,
loadConfig,
onNode,
onFailure,
withConfigErrorHandler,
};
Expand All @@ -53,16 +55,23 @@ describe('node', () => {

test('should restart everything when config file changes', async () => {
const { log } = mockLogger();
const { autoStartFuelCore, fuelCore, killChildProcess, loadConfig, withConfigErrorHandler } =
mockAll();
const {
autoStartFuelCore,
fuelCore,
killChildProcess,
loadConfig,
withConfigErrorHandler,
onNode,
} = mockAll();

const config = structuredClone(fuelsConfig);
const config = { ...fuelsConfig, onNode };
const close = vi.fn();
const watchHandlers = [{ close }, { close }] as unknown as FSWatcher[];

await configFileChanged({ config, fuelCore, watchHandlers })('event', 'some/path');

// configFileChanged() internals
expect(onNode).toHaveBeenCalledTimes(1);
expect(log).toHaveBeenCalledTimes(1);
expect(close).toHaveBeenCalledTimes(2);
expect(killChildProcess).toHaveBeenCalledTimes(1);
Expand Down
1 change: 1 addition & 0 deletions packages/fuels/src/cli/commands/node/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export const configFileChanged = (state: NodeState) => async (_event: string, pa
try {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
await node(await loadConfig(state.config.basePath));
state.config.onNode?.(null, state.config);
} catch (err: unknown) {
await withConfigErrorHandler(<Error>err, state.config);
}
Expand Down
27 changes: 2 additions & 25 deletions packages/fuels/src/cli/commands/withConfig.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,10 @@ describe('withConfig', () => {
});

function mockAll(params?: { shouldErrorOnDeploy?: boolean; shouldErrorOnLoadConfig?: boolean }) {
const onSuccess = vi.fn();
const onFailure = vi.fn();

const copyConfig: FuelsConfig = {
...structuredClone(fuelsConfig),
onSuccess,
onFailure,
};

Expand Down Expand Up @@ -57,30 +55,13 @@ describe('withConfig', () => {
command,
deploy,
loadConfig,
onSuccess,
onFailure,
error,
};
}

test('onSuccess hook in config file', async () => {
const { command, deploy, configPath, loadConfig, onSuccess, onFailure } = mockAll({
shouldErrorOnDeploy: false,
});

await withConfig(command, Commands.deploy, deploy)();

expect(loadConfig).toHaveBeenCalledTimes(1);
expect(loadConfig.mock.calls[0][0]).toEqual(configPath);

expect(onSuccess).toHaveBeenCalledTimes(1);
expect(onSuccess.mock.calls[0][0]).toEqual({ data: [], type: 'deploy' });

expect(onFailure).toHaveBeenCalledTimes(0);
});

test('onFailure hook in config file', async () => {
const { command, deploy, error, loadConfig, configPath, onSuccess, onFailure } = mockAll({
const { command, deploy, error, loadConfig, configPath, onFailure } = mockAll({
shouldErrorOnDeploy: true,
});

Expand All @@ -89,15 +70,13 @@ describe('withConfig', () => {
expect(loadConfig).toHaveBeenCalledTimes(1);
expect(loadConfig.mock.calls[0][0]).toEqual(configPath);

expect(onSuccess).toHaveBeenCalledTimes(0);

expect(error).toHaveBeenCalledTimes(1);
expect(onFailure).toHaveBeenCalledTimes(1);
expect(onFailure.mock.calls[0][0].toString()).toMatch(/something.+happened/i);
});

test('should handle error when loading config file', async () => {
const { command, deploy, error, loadConfig, configPath, onSuccess } = mockAll({
const { command, deploy, error, loadConfig, configPath } = mockAll({
shouldErrorOnLoadConfig: true,
});

Expand All @@ -106,8 +85,6 @@ describe('withConfig', () => {
expect(loadConfig).toHaveBeenCalledTimes(1);
expect(loadConfig.mock.calls[0][0]).toEqual(configPath);

expect(onSuccess).toHaveBeenCalledTimes(0);

expect(error).toHaveBeenCalledTimes(1);
});
});
Loading
Loading