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: add deploy flag to fuels cli build command #1419

Merged
merged 15 commits into from
Nov 9, 2023
Merged
12 changes: 11 additions & 1 deletion .changeset/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,15 @@
"access": "public",
"baseBranch": "master",
"updateInternalDependencies": "patch",
"ignore": ["fuel-gauge"]
"ignore": [
"fuel-gauge",
"docs",
"demo-fuels",
"demo-react-cra",
"demo-react-vite",
"demo-nextjs",
"demo-node-esm",
"demo-typegen",
"@fuel-ts/docs-snippets"
]
}
5 changes: 5 additions & 0 deletions .changeset/light-cameras-tease.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"fuels": patch
---

Adding new flag to Fuels CLI build command
24 changes: 24 additions & 0 deletions apps/docs/src/guide/cli/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,37 @@ In a nutshell:

## `fuels build`

```console
npx fuels help build
```

```
Options:
-p, --path <path> Path to project root (default: "/Users/anderson/Code/fuel/fuels-ts/apps/docs")
-d, --deploy Deploy contracts after build (auto-starts a `fuel-core` node if needed)
-h, --help Display help
```

Examples:

```console
npx fuels build
```

1. Build all Sway programs under your `workspace` using `forc` <sup>[1](#commands-for-wrapped-utiltities)</sup>
1. Generate types for them using `fuels-typegen` <sup>[2](#typegen)</sup>

```console
npx fuels build --deploy
```

Using the `--deploy` flag will aditionally:

1. Auto-start a short-lived `fuel-core` node if _needed_ ([docs](./config-file.md#autostartfuelcore))
2. Run `deploy` on that node

> _This is useful when working with contracts because a contract's ID is generated only on deployment._

## `fuels deploy`

```console
Expand Down
4 changes: 4 additions & 0 deletions packages/fuels/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ export const configureCli = () => {
(command = program.command(Commands.build))
.description('Build Sway programs and generate Typescript for them')
.addOption(pathOption)
.option(
'-d, --deploy',
'Deploy contracts after build (auto-starts a `fuel-core` node if needed)'
)
.action(withConfig(command, Commands.build, build));

(command = program.command(Commands.deploy))
Expand Down
15 changes: 14 additions & 1 deletion packages/fuels/src/cli/commands/build/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
import { type Command } from 'commander';

import type { FuelsConfig } from '../../types';
import { log } from '../../utils/logger';
import { deploy } from '../deploy';
import { autoStartFuelCore } from '../dev/startFuelCore';

import { buildSwayPrograms } from './buildSwayPrograms';
import { generateTypes } from './generateTypes';

export async function build(config: FuelsConfig) {
export async function build(config: FuelsConfig, program?: Command) {
log('Building..');

await buildSwayPrograms(config);
await generateTypes(config);

const options = program?.opts();

if (options?.deploy) {
const fuelCore = await autoStartFuelCore(config);
await deploy(config);
fuelCore?.killChildProcess();
}
}
13 changes: 2 additions & 11 deletions packages/fuels/src/cli/commands/dev/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@ import { build } from '../build';
import { deploy } from '../deploy';
import { withConfigErrorHandler } from '../withConfig';

import { defaultConsensusKey } from './defaultChainConfig';
import type { FuelCoreNode } from './startFuelCore';
import { startFuelCore } from './startFuelCore';
import { autoStartFuelCore } from './startFuelCore';

export const closeAllFileHandlers = (handlers: FSWatcher[]) => {
handlers.forEach((h) => h.close());
Expand Down Expand Up @@ -56,15 +55,7 @@ export const configFileChanged = (state: DevState) => async (_event: string, pat
};

export const dev = async (config: FuelsConfig) => {
let fuelCore: FuelCoreNode | undefined;

if (config.autoStartFuelCore) {
fuelCore = await startFuelCore(config);
// eslint-disable-next-line no-param-reassign
config.providerUrl = fuelCore.providerUrl;
// eslint-disable-next-line no-param-reassign
config.privateKey = defaultConsensusKey;
}
const fuelCore = await autoStartFuelCore(config);

const configFilePaths = getConfigFilepathsToWatch(config);

Expand Down
14 changes: 14 additions & 0 deletions packages/fuels/src/cli/commands/dev/startFuelCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,17 @@ export const startFuelCore = async (config: FuelsConfig): Promise<FuelCoreNode>
core.on('error', reject);
});
};

export const autoStartFuelCore = async (config: FuelsConfig) => {
let fuelCore: FuelCoreNode | undefined;

if (config.autoStartFuelCore) {
fuelCore = await startFuelCore(config);
// eslint-disable-next-line no-param-reassign
config.providerUrl = fuelCore.providerUrl;
// eslint-disable-next-line no-param-reassign
config.privateKey = defaultConsensusKey;
}

return fuelCore;
};
7 changes: 5 additions & 2 deletions packages/fuels/src/cli/commands/withConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ export const withConfigErrorHandler = async (err: Error, config?: FuelsConfig) =
export function withConfig<CType extends Commands>(
program: Command,
command: CType,
fn: (config: FuelsConfig) => Promise<Extract<CommandEvent, { type: CType }>['data']>
fn: (
config: FuelsConfig,
options?: Command
) => Promise<Extract<CommandEvent, { type: CType }>['data']>
) {
return async () => {
const options = program.opts();
Expand All @@ -30,7 +33,7 @@ export function withConfig<CType extends Commands>(
}

try {
const eventData = await fn(config);
const eventData = await fn(config, program);
config.onSuccess?.(
{
type: command,
Expand Down
33 changes: 33 additions & 0 deletions packages/fuels/test/features/build.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { existsSync } from 'fs';
import { join } from 'path';

import * as deployMod from '../../src/cli/commands/deploy/index';
import { mockStartFuelCore } from '../utils/mockStartFuelCore';
import { resetDiskAndMocks } from '../utils/resetDiskAndMocks';
import {
buildFlagsDeploy,
contractsFooDir,
generatedDir,
initFlagsUseBuiltinBinaries,
Expand All @@ -14,7 +17,16 @@ describe('build', () => {
beforeEach(resetDiskAndMocks);
afterEach(resetDiskAndMocks);

function mockAll() {
const { startFuelCore, killChildProcess } = mockStartFuelCore();
const deploy = jest.spyOn(deployMod, 'deploy').mockImplementation();

return { startFuelCore, killChildProcess, deploy };
}

it('should run `build` command', async () => {
const { startFuelCore, killChildProcess, deploy } = mockAll();

await runInit();
await runBuild();

Expand All @@ -34,9 +46,15 @@ describe('build', () => {
].map((f) => join(__dirname, '..', 'fixtures', 'generated', f));

files.forEach((file) => expect(existsSync(file)).toBeTruthy());

expect(startFuelCore).toHaveBeenCalledTimes(0);
expect(deploy).toHaveBeenCalledTimes(0);
expect(killChildProcess).toHaveBeenCalledTimes(0);
});

it('should run `build` command with contracts-only', async () => {
const { startFuelCore, killChildProcess, deploy } = mockAll();

await runInit([initFlagsUseBuiltinBinaries, '-c', contractsFooDir, '-o', generatedDir].flat());
await runBuild();

Expand All @@ -49,5 +67,20 @@ describe('build', () => {
].map((f) => join(__dirname, '..', 'fixtures', 'generated', f));

files.forEach((file) => expect(existsSync(file)).toBeTruthy());

expect(startFuelCore).toHaveBeenCalledTimes(0);
expect(deploy).toHaveBeenCalledTimes(0);
expect(killChildProcess).toHaveBeenCalledTimes(0);
});

it('should run `build` command with `--deploy` flag', async () => {
const { startFuelCore, killChildProcess, deploy } = mockAll();

await runInit();
await runBuild([buildFlagsDeploy]);

expect(startFuelCore).toHaveBeenCalledTimes(1);
expect(deploy).toHaveBeenCalledTimes(1);
expect(killChildProcess).toHaveBeenCalledTimes(1);
});
});
21 changes: 5 additions & 16 deletions packages/fuels/test/features/dev.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import * as chokidar from 'chokidar';

import type { FuelsConfig } from '../../src';
import * as buildMod from '../../src/cli/commands/build/index';
import * as deployMod from '../../src/cli/commands/deploy/index';
import * as startCoreMod from '../../src/cli/commands/dev/startFuelCore';
import { mockLogger } from '../utils/mockLogger';
import { mockStartFuelCore } from '../utils/mockStartFuelCore';
import { resetDiskAndMocks } from '../utils/resetDiskAndMocks';
import { runInit, runDev } from '../utils/runCommands';

Expand All @@ -20,18 +19,7 @@ describe('dev', () => {
function mockAll() {
mockLogger();

const startFuelCore = jest
.spyOn(startCoreMod, 'startFuelCore')
.mockImplementation((_config: FuelsConfig) =>
Promise.resolve({
bindIp: '0.0.0.0',
accessIp: '127.0.0.1',
port: 4000,
providerUrl: `http://127.0.0.1:4000/graphql`,
killChildProcess: jest.fn(),
chainConfig: '/some/path/chainConfig.json',
})
);
const { startFuelCore, killChildProcess } = mockStartFuelCore();

const build = jest.spyOn(buildMod, 'build').mockImplementation();
const deploy = jest.spyOn(deployMod, 'deploy').mockImplementation();
Expand All @@ -41,16 +29,17 @@ describe('dev', () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const watch = jest.spyOn(chokidar, 'watch').mockReturnValue({ on } as any);

return { startFuelCore, build, deploy, on, watch };
return { startFuelCore, killChildProcess, build, deploy, on, watch };
}

it('should run `dev` command', async () => {
const { startFuelCore, build, deploy, on, watch } = mockAll();
const { startFuelCore, killChildProcess, build, deploy, on, watch } = mockAll();

await runInit();
await runDev();

expect(startFuelCore).toHaveBeenCalledTimes(1);
expect(killChildProcess).toHaveBeenCalledTimes(0);
expect(build).toHaveBeenCalledTimes(1);
expect(deploy).toHaveBeenCalledTimes(1);

Expand Down
21 changes: 21 additions & 0 deletions packages/fuels/test/utils/mockStartFuelCore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { FuelsConfig } from '../../src';
import * as startFuelCoreMod from '../../src/cli/commands/dev/startFuelCore';

export const mockStartFuelCore = () => {
const killChildProcess = jest.fn();

const startFuelCore = jest
.spyOn(startFuelCoreMod, 'startFuelCore')
.mockImplementation((_config: FuelsConfig) =>
Promise.resolve({
bindIp: '0.0.0.0',
accessIp: '127.0.0.1',
port: 4000,
providerUrl: `http://127.0.0.1:4000/graphql`,
killChildProcess,
chainConfig: '/some/path/chainConfig.json',
})
);

return { startFuelCore, killChildProcess };
};
5 changes: 3 additions & 2 deletions packages/fuels/test/utils/runCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export const initFlagsDefault = [
initFlagsUseBuiltinBinaries,
initFlagsAutoStartFuelCore,
];
export const buildFlagsDeploy = '--deploy';

/**
* Command callers
Expand All @@ -55,8 +56,8 @@ export async function runInit(flags: string[] = initFlagsDefault.flat()) {
return runCommand(Commands.init, flags);
}

export async function runBuild() {
return runCommand(Commands.build);
export async function runBuild(flags: string[] = []) {
return runCommand(Commands.build, flags);
}

export async function runDeploy() {
Expand Down