3 Ways to Build and Release Components

How to Build and Release Bit Components

Ashan Fernando
Bits and Pieces

--

Over the past few years, building software applications as a collection of components has gained immense popularity. Dividing the application into smaller, more manageable pieces and composing them together allows for easier maintenance, testing, and scaling, making it an ideal choice for modern software development.

However, moving into component-based software development also requires a new perspective on setting up CI and using tooling to manage the lifecycle of components.

There are multiple options, depending on your repository structure, the maturity of adopting components, and the stage of migration into components.

Using Ripple CI

The first approach we will consider is using a CI platform tailored to the unique demands of component-based software development.

It provides a highly efficient and optimized CI experience offering features like:

  • Parallelized component builds
  • Efficient builds that include only affected components
  • Faster feedback loops with incremental component builds
  • Automatic propagation of dependent component builds (including components not maintained by your workspace)
  • Works out of the box without any additional configuration

Ripple directly supports Bit components and their default configuration. You can customize the build and release configurations by modifying your dev environment’s build pipeline. You can customize the compiler, tester, linter, formatter, preview (for UI components), and build configurations by modifying the Env. The following skeleton shows how a build-pipeline configuration looks in an Envt.

// imports...

export class MyEnv {
/* a shorthand name for the env. */
name = 'my-env';
/* icon for the env. */
icon = 'path/to/my-env-icon.svg';

/**
* the compiler for components maintained by this env.
* used for compilation during development ('bit compile', 'bit watch', 'bit start').
*/
compiler(): EnvHandler<Compiler> {
return MyCompiler.from(myCompilerConfig);
}

/**
* the tester for components maintained by this env.
* used for testing during development ('bit test').
*/
tester(): EnvHandler<Tester> {
return MyTester.from(myTesterConfig);
}

/**
* the linter for components maintained by this env.
* used for linting during development ('bit lint').
*/
linter() {
return MyLinter.from(myLinterConfig);
}

/**
* the code formatter for components maintained by this env.
* used for code formatting during development ('bit lint').
*/
formatter(): EnvHandler<Formatter> {
return MyCodeFormatter.from(myFormatterConfig);
}

/**
* the bundler and renderer for the components maintained by this env.
* used for previewing components and their docs
* (used during the component development and build)
*/
preview(): EnvHandler<Preview> {
return MyComponentPreviewHandler.from({
myComponentPreviewWrapper,
myComponentDocsTemplate,
});
}

/**
* the build pipeline for components maintained by this env
* this pipeline includes compilation, tests, and linting tasks
* ('bit build', 'bit snap', 'bit tag')
*/
build() {
return Pipeline.from([
MyCompilerTask.from(myCompilerConfig),
MyLinterTask.from(myLinterConfig),
MyTesterTask.from(myTesterConfig),
]);
}

/**
* the snap pipeline for components maintained by this env
* ('bit snap')
*/
snap() {
return Pipeline.from([]);
}

/**
* he tag pipeline for components maintained by this env
* ('bit tag')
*/
tag() {
return Pipeline.from([]);
}

/**
* the package config for packages generated
* for components maintained by this env.
*/
package() {
return PackageGenerator.from({
packageJson: {
main: 'dist/{main}.js',
},
});
}

/**
* write workspace configuration files for the IDE,
* this improves developer experience as it enables the IDE to read from the same
* configuration files (for the compiler, linter, etc) used by components maintained by this env,
* and provide feedback to the user accordingly.
*/
workspaceConfig(): ConfigWriterList {
return ConfigWriterList.from([
MyCompilerConfigWriter.from({ myCompilerConfig }),
MyLinterConfigWriter.from({ myLinterConfig }),
MyCodeFormatterConfigWriter.from({ myFormatterConfig }),
]);
}

/**
* workspace templates to generate pre-configured workspaces,
* possibly with a set of new/imported components.
*/
starters() {
return StarterList.from([MyWorkspaceTemplate.from()]);
}

/**
* set a list of component templates to use across your
* workspaces. new workspaces would be set to include
* your envs by default.
*/
generators() {
return TemplateList.from([
MyTsComponentTemplate.from(),
MyCssComponentTemplate.from(),
]);
}

/**
* the app types supported by this env.
*/
apps(): EnvHandler<AppTypeList> {
return AppTypeList.from([MyAppType.from()]);
}
}

export default new MyEnv();

Here's a complete example of React Env build-pipeline implementation. It's always recommended to take a fork of the Env configuration and customize it when required.

Component lifecycle with Ripple

If you are familiar with feature branch workflow in Git, you can follow a similar approach using Ripple.

  1. Create a new lane

Before doing any modifications to components, you can create a new Bit lane using the command bit lane create my-lane — scope my-org.my-scope. It’s similar to creating a Git branch. The main difference is unlike Git, Bit Lanes only tracks the modified components from your workspace.

2. Snap changes

Once the components are modified, you can snap the changes by using bit snap --message "message for modified components" . This is similar to the commit operation in Git.

3. Export lane

To build your component using Ripple, snap them, and export them to the respective scope in bit.cloud using bit export command.

After this step, you can preview and test your modified component in the lane exported in bit.cloud. It will also trigger the Ripple CI build task to test all the modified components parallel and their dependents to ensure everything works in order.

4. Create a change request

You can then create a change request to the main lane of your scope via Ripple. Like a Pull Request in Git, it will compare the changes made to the components so that the reviewers can approve the change request and merge it with the main.

5. Merge and release

Merging with the main lane automatically creates a new tag with a version bump for the modified components, releasing new component versions.

Note: Optionally, for App components, there is an additional configuration you can customize in the Env to deploy it to the relevant server.

Using an Existing CI

If you have already used Ripple, you probably have experience using CI out of the box for components. However, there are situations when you have introduced components to an existing codebase in Git. Or, in some cases, the CI is already configured using a CI like GitHub actions for the existing code base. You may also need to extend your CI to support the build and release of components.

First, you must define the primary source of truth, the Git repository. This is helpful when conflicts happen; you fall back on what's available in the Git repository. To support this workflow, Bit has provided direct support for several popular CI platforms and node.js and shell scripts to support others.

CI Scripts Support for Bit Components

In all these cases, the components are built in the CI agent and finally released to a remote scope in bit.cloud.

Component lifecycle with Existing CI

Since your components are tracked in a Git repository, you can continue using your existing feature branch flow and extend it to support Bit components.

For more details on the CI pipeline, refer to the article Integrating Bit with your Existing CI.

The main change would be that once you create a Pull Request, a lane for the modified components will be created in your remote scope. You can visit the remote scope and test the created lane. Also, to keep things simple, almost all the CI scripts post a link to the Pull Request as a comment pointing to the lane in remote scope. Once you merge the Pull Request, all the modified components will be tagged and exported to the remote scope, and the new versions will be committed back to the Git repository.

The following video demonstrates how easy it is to set up your pipeline from the ground up by copying and pasting the example scripts provided by Bit for GitHub Actions.

Combining Ripple and Existing CI

We looked at how Ripple and your existing CI can independently build and release components in the first two approaches. In addition, you can use both Ripple and your existing CI side by side to optimize your build pipeline.

Since there are multiple ways of combining these, let’s look at one approach that reduces the overall build time of the components by utilizing Ripple to build them.

First, you must commit and push the changes to your Git repository, similar to the existing CI workflow. After that, instead of building your components in existing CI, you can use the Pull Request scripts to create a lane and export the components for preview and testing in bit.cloud.

And once you export the components without the --build flag, they will be built in Ripple CI, where you can preview and verify their status. After that, the same flow in the existing CI solution continues.

Conclusion

As we move towards component-based software development, we are marking a significant leap in creating, maintaining, and scaling applications. This approach streamlines development processes and fosters a culture of collaboration and efficiency among developers.

Ripple CI offers a bespoke CI experience tailored for component-driven projects. Its seamless integration with Bit components, coupled with features like parallelized builds and efficient incremental updates, underscores a commitment to optimizing the developer workflow.

You can also find scripts to extend your existing CI platform, directly supporting the component build and release lifecycle.

In essence, whether you’re navigating the component lifecycle with Ripple, integrating Bit components into an existing CI setup, or exploring a hybrid approach, the focus remains on enhancing productivity and collaboration.

Learn More

--

--