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

fix: fuels dev hangs after compilation errors #3504

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 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
6 changes: 6 additions & 0 deletions .changeset/silly-pianos-fry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@fuel-ts/contract": patch
"fuels": patch
---

fix: `fuels dev` hangs after compilation errors
2 changes: 1 addition & 1 deletion .github/workflows/pr-release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
name: "Release PR to npm"
runs-on: ubuntu-latest
# comment out if:false to enable release PR to npm
if: false
# if: false
permissions: write-all
steps:
- name: Checkout
Expand Down
1 change: 1 addition & 0 deletions packages/contract/src/contract-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ export default class ContractFactory<TContract extends Contract = Contract> {
const bytecode = deployOptions?.bytecode || this.bytecode;
const stateRoot = options.stateRoot || getContractStorageRoot(options.storageSlots);
const contractId = getContractId(bytecode, options.salt, stateRoot);

const transactionRequest = new CreateTransactionRequest({
bytecodeWitnessIndex: 0,
witnesses: [bytecode],
Expand Down
11 changes: 9 additions & 2 deletions packages/fuels/src/cli/commands/build/buildSwayProgram.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
import { spawn } from 'child_process';

import type { FuelsConfig } from '../../types';
import { debug, loggingConfig } from '../../utils/logger';
import { loggingConfig, log } from '../../utils/logger';

import { onForcExit, onForcError } from './forcHandlers';

export const buildSwayProgram = async (config: FuelsConfig, path: string) => {
debug('Building Sway program', path);
log('Building Sway program', path);

return new Promise<void>((resolve, reject) => {
const args = ['build', '-p', path].concat(config.forcBuildFlags);
const forc = spawn(config.forcPath, args, { stdio: 'pipe' });
if (loggingConfig.isLoggingEnabled) {
// eslint-disable-next-line no-console
forc.stderr?.on('data', (chunk) => console.log(chunk.toString()));

forc.stderr?.on('data', (chunk) => {
if (chunk.toString().includes('Aborting')) {
// because forc logs out 'Aborting due to 2 compilation errors' etc.
onForcError(reject);
}
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried compiling an invalid project with forc build and it exited with an error code 1, so it seems to me that we shouldn't be parsing forc logs but rather just throwing if forc build exits with a non-zero code. Line 37: forc.on('error', onError) should've caught this but it didn't 🤔 Did you investigate this avenue?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

forc.on('error, onError) doesn't do anything because forc sends error messages to stdErr alongside other normal logs

}

if (loggingConfig.isDebugEnabled) {
Expand Down
2 changes: 1 addition & 1 deletion packages/fuels/src/cli/commands/build/generateTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export async function generateTypes(config: FuelsConfig) {
.map(({ programs, type }) => generateTypesForProgramType(config, programs, type))
);

const indexFile = await renderIndexTemplate(pluralizedDirNames);
const indexFile = renderIndexTemplate(pluralizedDirNames);

writeFileSync(join(config.output, 'index.ts'), indexFile);
}
4 changes: 1 addition & 3 deletions packages/fuels/src/cli/commands/deploy/deployContracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
type ForcToml,
} from '../../config/forcUtils';
import type { FuelsConfig, DeployedContract } from '../../types';
import { debug, log } from '../../utils/logger';
import { debug } from '../../utils/logger';

import { createWallet } from './createWallet';
import { getDeployConfig } from './getDeployConfig';
Expand Down Expand Up @@ -138,8 +138,6 @@ export async function deployContracts(config: FuelsConfig) {

const wallet = await createWallet(config.providerUrl, config.privateKey);

log(`Deploying contracts to: ${wallet.provider.url}`);

const contractsLen = config.contracts.length;

for (let i = 0; i < contractsLen; i++) {
Expand Down
15 changes: 12 additions & 3 deletions packages/fuels/src/cli/commands/dev/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,10 @@ describe('dev', () => {
const { log } = mockLogger();
const { build, deploy } = mockAll();

await workspaceFileChanged({ config: fuelsConfig, watchHandlers: [] })('event', 'some/path');
await workspaceFileChanged({ config: fuelsConfig, watchHandlers: [], filesBeingProcsesed: [] })(
'event',
'some/path'
);

expect(log).toHaveBeenCalledTimes(1);
expect(build).toHaveBeenCalledTimes(1);
Expand Down Expand Up @@ -127,7 +130,10 @@ describe('dev', () => {
const close = vi.fn();
const watchHandlers = [{ close }, { close }] as unknown as FSWatcher[];

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

// configFileChanged() internals
expect(log).toHaveBeenCalledTimes(1);
Expand Down Expand Up @@ -163,7 +169,10 @@ describe('dev', () => {
const close = vi.fn();
const watchHandlers = [{ close }, { close }] as unknown as FSWatcher[];

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

// configFileChanged() internals
expect(log).toHaveBeenCalledTimes(1);
Expand Down
22 changes: 17 additions & 5 deletions packages/fuels/src/cli/commands/dev/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { globSync } from 'glob';

import { loadConfig } from '../../config/loadConfig';
import { type FuelsConfig } from '../../types';
import { error, log } from '../../utils/logger';
import { debug, error, log } from '../../utils/logger';
import { build } from '../build';
import { deploy } from '../deploy';
import { withConfigErrorHandler } from '../withConfig';
Expand Down Expand Up @@ -36,15 +36,27 @@ export type DevState = {
config: FuelsConfig;
watchHandlers: FSWatcher[];
fuelCore?: FuelCoreNode;
filesBeingProcsesed: string[];
Dhaiwat10 marked this conversation as resolved.
Show resolved Hide resolved
};

export const workspaceFileChanged = (state: DevState) => async (_event: string, path: string) => {
log(`\nFile changed: ${path}`);
await buildAndDeploy(state.config);
if (!state.filesBeingProcsesed.includes(path)) {
log(`\nFile changed: ${path}`);
try {
state.filesBeingProcsesed.push(path);
await buildAndDeploy(state.config);
} catch (err: unknown) {
debug('Error in buildAndDeploy', err);
} finally {
const newState = { ...state };
newState.filesBeingProcsesed = [...state.filesBeingProcsesed].filter((p) => p !== path);
Object.assign(state, newState);
}
}
};

export const configFileChanged = (state: DevState) => async (_event: string, path: string) => {
log(`\nFile changed: ${path}`);
log(`\nFile changed config: ${path}`);

closeAllFileHandlers(state.watchHandlers);
state.fuelCore?.killChildProcess();
Expand Down Expand Up @@ -79,7 +91,7 @@ export const dev = async (config: FuelsConfig) => {

const watchHandlers: FSWatcher[] = [];
const options = { persistent: true, ignoreInitial: true, ignored: '**/out/**' };
const state = { config, watchHandlers, fuelCore };
const state: DevState = { config, watchHandlers, fuelCore, filesBeingProcsesed: [] };

// watch: fuels.config.ts and snapshotDir
watchHandlers.push(watch(configFilePaths, options).on('all', configFileChanged(state)));
Expand Down
Loading