Skip to content

Commit

Permalink
improvement(build), stop the build as soon as a tag-blocker component…
Browse files Browse the repository at this point in the history
…-issue is found (#9031)

Allow to ignore the issue by `--ignore-issues` flag. (similar to `bit
tag` and `bit snap`).
  • Loading branch information
davidfirst authored Jul 15, 2024
1 parent c76b90d commit 1f2351e
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 36 deletions.
2 changes: 1 addition & 1 deletion e2e/functionalities/peer-dependencies.e2e.3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ describe('peer-dependencies functionality', function () {
helper.workspaceJsonc.addPolicyToDependencyResolver({
peerDependencies: { [`@${helper.scopes.remote}/comp2`]: '*' },
});
helper.command.build();
helper.command.build(undefined, '--ignore-issues="DuplicateComponentAndPackage"');
workspaceCapsulesRootDir = helper.command.capsuleListParsed().workspaceCapsulesRootDir;
});
it('should save the peer dependency in the model', () => {
Expand Down
2 changes: 1 addition & 1 deletion e2e/harmony/preview.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ describe('preview feature (during build)', function () {
);
helper.fs.outputFile('index.js', `export { Button } from './button';`);
helper.command.addComponent('button');
helper.command.install();
helper.command.install('--add-missing-deps');
helper.command.compile();
});
it('bit build should run successfully without preview errors', () => {
Expand Down
26 changes: 1 addition & 25 deletions scopes/component/snapping/snapping.main.runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ import Version, { DepEdge, DepEdgeType, Log } from '@teambit/legacy/dist/scope/m
import { SnapCmd } from './snap-cmd';
import { SnappingAspect } from './snapping.aspect';
import { TagCmd } from './tag-cmd';
import { ComponentsHaveIssues } from './components-have-issues';
import ResetCmd from './reset-cmd';
import { tagModelComponent, updateComponentsVersions, BasicTagParams, BasicTagSnapParams } from './tag-model-component';
import { TagDataPerCompRaw, TagFromScopeCmd } from './tag-from-scope.cmd';
Expand Down Expand Up @@ -767,7 +766,7 @@ in case you're unsure about the pattern syntax, use "bit pattern [--help]"`);
const componentsToCheck = components.filter((c) => !c.isDeleted());
const consumerComponents = componentsToCheck.map((c) => c.state._consumer) as ConsumerComponent[];
await this.throwForLegacyDependenciesInsideHarmony(consumerComponents);
await this.throwForComponentIssues(componentsToCheck, ignoreIssues);
await this.builder.throwForComponentIssues(componentsToCheck, ignoreIssues);
this.throwForPendingImport(consumerComponents);
}

Expand Down Expand Up @@ -1028,29 +1027,6 @@ another option, in case this dependency is not in main yet is to remove all refe
return this.workspace.getMany(ids.map((id) => id.changeVersion(undefined)));
}

private async throwForComponentIssues(components: Component[], ignoreIssues?: string) {
if (ignoreIssues === '*') {
// ignore all issues
return;
}
const issuesToIgnoreFromFlag = ignoreIssues?.split(',').map((issue) => issue.trim()) || [];
const issuesToIgnoreFromConfig = this.issues.getIssuesToIgnoreGlobally();
const issuesToIgnore = [...issuesToIgnoreFromFlag, ...issuesToIgnoreFromConfig];
await this.issues.triggerAddComponentIssues(components, issuesToIgnore);
this.issues.removeIgnoredIssuesFromComponents(components, issuesToIgnore);
const legacyComponents = components.map((c) => c.state._consumer) as ConsumerComponent[];
const componentsWithBlockingIssues = legacyComponents.filter((component) => component.issues?.shouldBlockTagging());
if (componentsWithBlockingIssues.length) {
throw new ComponentsHaveIssues(componentsWithBlockingIssues);
}

const workspaceIssues = this.workspace.getWorkspaceIssues();
if (workspaceIssues.length) {
const issuesStr = workspaceIssues.map((issueErr) => issueErr.message).join('\n');
throw new BitError(`the workspace has the following issues:\n${issuesStr}`);
}
}

private throwForPendingImport(components: ConsumerComponent[]) {
const componentsMissingFromScope = components
.filter((c) => !c.componentFromModel && this.scope.isExported(c.id))
Expand Down
3 changes: 1 addition & 2 deletions scopes/component/snapping/snapping.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import { Ref } from '@teambit/legacy/dist/scope/objects';
import { mockComponents } from '@teambit/component.testing.mock-components';
import { SnappingMain } from './snapping.main.runtime';
import { SnappingAspect } from './snapping.aspect';
import { ComponentsHaveIssues } from './components-have-issues';
import { SnapDataPerCompRaw } from './snap-from-scope.cmd';

describe('Snapping aspect', function () {
Expand All @@ -44,7 +43,7 @@ describe('Snapping aspect', function () {
try {
await snapping.tag({ ids: ['comp1'] });
} catch (err: any) {
expect(err.constructor.name).to.equal(ComponentsHaveIssues.name);
expect(err.constructor.name).to.equal('ComponentsHaveIssues');
}
});
// @todo: this test fails during "bit build" for some reason. It passes on "bit test";
Expand Down
11 changes: 11 additions & 0 deletions scopes/pipelines/builder/build.cmd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { OutsideWorkspaceError, Workspace } from '@teambit/workspace';
import { COMPONENT_PATTERN_HELP } from '@teambit/legacy/dist/constants';
import chalk from 'chalk';
import { BuilderMain } from './builder.main.runtime';
import { IssuesClasses } from '@teambit/component-issues';

type BuildOpts = {
unmodified?: boolean;
Expand All @@ -21,6 +22,7 @@ type BuildOpts = {
failFast?: boolean;
includeSnap?: boolean;
includeTag?: boolean;
ignoreIssues?: string;
};

export class BuilderCmd implements Command {
Expand Down Expand Up @@ -81,6 +83,13 @@ specify the task-name (e.g. "TypescriptCompiler") or the task-aspect-id (e.g. te
'include-tag',
'EXPERIMENTAL. include tag pipeline tasks. Warning: this may deploy/publish if you have such tasks',
],
[
'i',
'ignore-issues <issues>',
`ignore component issues (shown in "bit status" as "issues found"), issues to ignore:
[${Object.keys(IssuesClasses).join(', ')}]
to ignore multiple issues, separate them by a comma and wrap with quotes. to ignore all issues, specify "*".`,
],
] as CommandOptions;

constructor(private builder: BuilderMain, private workspace: Workspace, private logger: Logger) {}
Expand All @@ -101,6 +110,7 @@ specify the task-name (e.g. "TypescriptCompiler") or the task-aspect-id (e.g. te
failFast,
includeSnap,
includeTag,
ignoreIssues,
}: BuildOpts
): Promise<string> {
if (rewrite && !reuseCapsules) throw new Error('cannot use --rewrite without --reuse-capsules');
Expand Down Expand Up @@ -141,6 +151,7 @@ specify the task-name (e.g. "TypescriptCompiler") or the task-aspect-id (e.g. te
{
includeSnap,
includeTag,
ignoreIssues,
}
);
this.logger.console(`build output can be found in path: ${envsExecutionResults.capsuleRootDir}`);
Expand Down
64 changes: 58 additions & 6 deletions scopes/pipelines/builder/builder.main.runtime.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { cloneDeep } from 'lodash';
import ConsumerComponent from '@teambit/legacy/dist/consumer/component/consumer-component';
import { ArtifactVinyl, ArtifactFiles, ArtifactObject } from '@teambit/component.sources';
import { AspectLoaderAspect, AspectLoaderMain } from '@teambit/aspect-loader';
import { CLIAspect, CLIMain, MainRuntime } from '@teambit/cli';
Expand All @@ -16,6 +17,8 @@ import { getBitVersion } from '@teambit/bit.get-bit-version';
import { findDuplications } from '@teambit/toolbox.array.duplications-finder';
import { GeneratorAspect, GeneratorMain } from '@teambit/generator';
import { UIAspect, UiMain, BundleUiTask } from '@teambit/ui';
import { IssuesAspect, IssuesMain } from '@teambit/issues';
import { BitError } from '@teambit/bit-error';
import { Artifact, ArtifactList, FsArtifact } from './artifact';
import { ArtifactFactory } from './artifact/artifact-factory'; // it gets undefined when importing it from './artifact'
import { BuilderAspect } from './builder.aspect';
Expand All @@ -31,6 +34,7 @@ import { TaskMetadata } from './types';
import { ArtifactsCmd } from './artifact/artifacts.cmd';
import { buildTaskTemplate } from './templates/build-task';
import { BuilderRoute } from './builder.route';
import { ComponentsHaveIssues } from './exceptions/components-have-issues';

export type TaskSlot = SlotRegistry<BuildTask[]>;
export type OnTagResults = { builderDataMap: ComponentMap<RawBuilderData>; pipeResults: TaskResultsList[] };
Expand Down Expand Up @@ -74,7 +78,8 @@ export class BuilderMain {
private buildTaskSlot: TaskSlot,
private tagTaskSlot: TaskSlot,
private snapTaskSlot: TaskSlot,
private logger: Logger
private logger: Logger,
private issues: IssuesMain
) {}

private async storeArtifacts(tasksResults: TaskResults[]) {
Expand Down Expand Up @@ -124,7 +129,8 @@ export class BuilderMain {
...builderOptions,
// even when build is skipped (in case of tag-from-scope), the pre-build/post-build and teambit.harmony/aspect tasks are needed
tasks: populateArtifactsFrom ? [AspectAspect.id] : undefined,
}
},
{ ignoreIssues: '*' }
);
if (throwOnError && !forceDeploy) buildEnvsExecutionResults.throwErrorsIfExist();
allTasksResults.push(...buildEnvsExecutionResults.tasksResults);
Expand Down Expand Up @@ -366,8 +372,9 @@ export class BuilderMain {
components: Component[],
isolateOptions?: IsolateComponentsOptions,
builderOptions?: BuilderServiceOptions,
extraOptions?: { includeTag?: boolean; includeSnap?: boolean }
extraOptions?: { includeTag?: boolean; includeSnap?: boolean; ignoreIssues?: string }
): Promise<TaskResultsList> {
await this.throwForVariousIssues(components, extraOptions?.ignoreIssues);
const ids = components.map((c) => c.id);
const capsulesBaseDir = this.buildService.getComponentsCapsulesBaseDir();
const baseIsolateOpts = {
Expand Down Expand Up @@ -465,6 +472,34 @@ export class BuilderMain {
return `/api/${componentId}/~aspect/builder/${taskId}/${path ? `${FILE_PATH_PARAM_DELIM}${path}` : ''}`;
}

private async throwForVariousIssues(components: Component[], ignoreIssues?: string) {
const componentsToCheck = components.filter((c) => !c.isDeleted());
await this.throwForComponentIssues(componentsToCheck, ignoreIssues);
}

async throwForComponentIssues(components: Component[], ignoreIssues?: string) {
if (ignoreIssues === '*') {
// ignore all issues
return;
}
const issuesToIgnoreFromFlag = ignoreIssues?.split(',').map((issue) => issue.trim()) || [];
const issuesToIgnoreFromConfig = this.issues.getIssuesToIgnoreGlobally();
const issuesToIgnore = [...issuesToIgnoreFromFlag, ...issuesToIgnoreFromConfig];
await this.issues.triggerAddComponentIssues(components, issuesToIgnore);
this.issues.removeIgnoredIssuesFromComponents(components, issuesToIgnore);
const legacyComponents = components.map((c) => c.state._consumer) as ConsumerComponent[];
const componentsWithBlockingIssues = legacyComponents.filter((component) => component.issues?.shouldBlockTagging());
if (componentsWithBlockingIssues.length) {
throw new ComponentsHaveIssues(componentsWithBlockingIssues);
}

const workspaceIssues = this.workspace.getWorkspaceIssues();
if (workspaceIssues.length) {
const issuesStr = workspaceIssues.map((issueErr) => issueErr.message).join('\n');
throw new BitError(`the workspace has the following issues:\n${issuesStr}`);
}
}

static slots = [Slot.withType<BuildTask>(), Slot.withType<BuildTask>(), Slot.withType<BuildTask>()];

static runtime = MainRuntime;
Expand All @@ -481,10 +516,25 @@ export class BuilderMain {
ComponentAspect,
UIAspect,
GlobalConfigAspect,
IssuesAspect,
];

static async provider(
[cli, envs, workspace, scope, isolator, loggerExt, aspectLoader, graphql, generator, component, ui, globalConfig]: [
[
cli,
envs,
workspace,
scope,
isolator,
loggerExt,
aspectLoader,
graphql,
generator,
component,
ui,
globalConfig,
issues,
]: [
CLIMain,
EnvsMain,
Workspace,
Expand All @@ -496,7 +546,8 @@ export class BuilderMain {
GeneratorMain,
ComponentMain,
UiMain,
GlobalConfigMain
GlobalConfigMain,
IssuesMain
],
config,
[buildTaskSlot, tagTaskSlot, snapTaskSlot]: [TaskSlot, TaskSlot, TaskSlot]
Expand Down Expand Up @@ -548,7 +599,8 @@ export class BuilderMain {
buildTaskSlot,
tagTaskSlot,
snapTaskSlot,
logger
logger,
issues
);
builder.registerBuildTasks([new BundleUiTask(ui, logger)]);
component.registerRoute([new BuilderRoute(builder, scope, logger)]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ ${issuesColored}
to get the list of component-issues names and suggestions how to resolve them, run "bit component-issues".
while highly not recommended, it's possible to ignore issues in two ways:
1) temporarily ignore for this tag/snap command by entering "--ignore-issues" flag, e.g. \`bit tag --ignore-issues "${allIssueNames.join(
1) temporarily ignore for this tag/snap/build command by entering "--ignore-issues" flag, e.g. \`bit tag --ignore-issues "${allIssueNames.join(
', '
)}" \`
2) ignore the issue completely by configuring it in the workspace.jsonc file. e.g:
Expand Down

0 comments on commit 1f2351e

Please sign in to comment.