> ## Documentation Index
> Fetch the complete documentation index at: https://site.aspect.build/llms.txt
> Use this file to discover all available pages before exploring further.

# Customize Bazel with Aspect CLI plugins

> Customize Bazel effortlessly with Aspect CLI plugins for tailored developer workflows and enhanced control over Bazel deployments

export const BlogPost = ({title, date, authors, tags, image, children}) => {
  const tagList = tags ? tags.split(", ").filter(Boolean) : [];
  const tagSlug = t => t.toLowerCase().replace(/&/g, "").replace(/\+/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
  const formattedDate = date ? new Date(date + "T00:00:00").toLocaleDateString("en-US", {
    year: "numeric",
    month: "long",
    day: "numeric"
  }) : "";
  return <section className="w-full flex justify-center px-4 py-12 md:py-16">
      <div style={{
    maxWidth: "800px",
    width: "100%"
  }}>
        {image && (typeof image === "string" ? <img noZoom src={image} alt={title} className="w-full rounded-xl mb-8" style={{
    maxHeight: "400px",
    objectFit: "cover"
  }} /> : <div className="blog-post-hero-image">{image}</div>)}
        <h1 className="text-3xl md:text-4xl font-bold text-zinc-900 dark:text-white">
          {title}
        </h1>
        <div className="flex flex-wrap items-center gap-3 mt-4 text-sm text-zinc-500 dark:text-zinc-400">
          {authors && <span>{authors}</span>}
          {authors && formattedDate && <span>·</span>}
          {formattedDate && <span>{formattedDate}</span>}
        </div>
        {tagList.length > 0 && <div className="flex flex-wrap gap-2 mt-3">
            {tagList.map(tag => <a key={tag} href={"/blog/tags/" + tagSlug(tag)} className="px-2 py-0.5 rounded-full text-xs bg-zinc-100 dark:bg-zinc-800 text-zinc-600 dark:text-zinc-400 hover:bg-blue-100 dark:hover:bg-blue-900/40 hover:text-blue-700 dark:hover:text-blue-300 transition">
                {tag}
              </a>)}
          </div>}
        <hr className="my-8 border-zinc-200 dark:border-zinc-700" />
        <div className="prose dark:prose-invert max-w-none">{children}</div>
      </div>
    </section>;
};

export const MarketingPage = () => <div className="marketing-page-marker" style={{
  display: "none"
}} />;

export const Section = ({children, className = "", gray = false, dark = false, id}) => <section id={id} className={`w-full flex justify-center px-4 py-16 md:py-24 ${gray ? "bg-gray-50 dark:bg-zinc-900" : dark ? "bg-zinc-900 dark:bg-zinc-950" : ""} ${className}`}>
    <div className="w-full" style={{
  maxWidth: "1140px"
}}>
      {children}
    </div>
  </section>;

<MarketingPage />

<BlogPost title="Customize Bazel with Aspect CLI plugins" date="2022-03-17" authors="Alex Eagle" tags="Aspect CLI, Bazel">
  <img noZoom src="https://mintcdn.com/aspectbuild/x1L7Iep716jCyJVo/images/blog/hashnode/hn-e93e7c9b9c.jpg?fit=max&auto=format&n=x1L7Iep716jCyJVo&q=85&s=060d8ac91890d1d3b084bdc711cf6d2b" alt="" className="blog-post-cover" width="4032" height="3024" data-path="images/blog/hashnode/hn-e93e7c9b9c.jpg" />

  A consistent theme of Bazel deployments at our clients is the steep on-ramp: how difficult it is for developers to understand and for DevInfra teams to integrate. On the other hand, Bazel's popularity is partly because Googlers have experienced how well it can work, and have told their story other companies who want that experience too.

  What's the missing piece? Bazel was customized for Google's developer workflow, and now you can customize it for yours!

  The `aspect` CLI is a wrapper around Bazel. If you've ever installed `bazelisk` following the [Bazel recommended install](https://bazel.build/install/bazelisk) you already run a wrapper around Bazel. In fact, `aspect` includes `bazelisk` so it's an even better wrapper.

  `aspect` supports plugins, which give you the missing "control point" to solve for issues in local developer workflows, such as:

  * amend error messages to point to your internal documentation
  * offer to apply auto-fixes to incorrect source code
  * add commands for deploying, linting, rebasing, or other common developer workflows
  * "cookiecutter": stamp out new Bazel projects following your local conventions

  Plugins also run on Continuous Integration servers where you might want to:

  * "fail fast" by reporting a red status as soon as the first build/test step fails
  * feed systems that manage flakiness or trigger buildcop actions

  A plugin is any program that serves our gRPC protocol. While you can write a plugin from scratch in any language, in practice there's a much faster way to get started: clone our [template repository](https://github.com/aspect-build/aspect-cli-plugin-template) and write your plugin using our Go SDK.

  This article shows how to write your first plugin.

  > Note: the plugin API is still in alpha, so breaking changes are likely.

  ## Tour of the plugin

  The template repository includes a full Bazel setup and CI/CD using GitHub actions, so all you need to look at is one file: [`plugin.go`](https://github.com/aspect-build/aspect-cli-plugin-template/blob/main/plugin.go). It serves as an example of how to write your own plugin.

  After the imports, you'll see the `main` one-liner. This just uses the excellent [`go-plugin` library from Hashicorp](https://github.com/hashicorp/go-plugin) to start up your plugin and connect it to the CLI.

  ```go theme={null}
  func main() {
  	goplugin.Serve(config.NewConfigFor(&HelloWorldPlugin{}))
  }
  ```

  Next we declare the plugin type, `HelloWorldPlugin` so we have an instance to store state. The `Base` field lets us inherit implementations so we only need to implement the functions we care about.

  After that, we implement the `CustomCommands` function, so we can declare that `aspect hello-world` is available to users. Our command appears in the output of `aspect help` and when run, it just prints "Hello World!". Your custom command can do anything you like, including spawning sub-processes to interact with your other tools.

  The next function in the example is `BEPEventCallback`. This is called for each BuildEvent received from Bazel. Your editor will give type-completion here, making it pretty easy to navigate the available events in Bazel's [`build_event_stream` protocol buffer](https://github.com/bazelbuild/bazel/blob/master/src/main/java/com/google/devtools/build/lib/buildeventstream/proto/build_event_stream.proto). This is all you have to do to consume the Build Event Protocol! In this example we just store some useful information, but you could also immediately call an API like telling your CI server that the build is going to go red.

  Finally we implement the `PostBuildHook` which is called after `bazel build` exits. In that function we're using the supplied `PromptRunner` interface to ask the user for input, when we're running in interactive mode.

  ## A Full-featured Example

  The [fix-visibility plugin](https://github.com/aspect-build/aspect-cli/blob/v0.3.0/plugins/fix-visibility/plugin.go) shows a very useful real-world use case. We've seen developers new to Bazel struggle with the concept of "visibility" and it's often the first error they run into requiring manual `BUILD` file edits.

  Here's what it looks like in action: <a href="https://asciinema.org/a/eL4HL3BZhobRD8U4UIRKzyb8R">  <img noZoom src="https://asciinema.org/a/eL4HL3BZhobRD8U4UIRKzyb8R.svg" alt="asciicast" /></a>

  This plugin uses the same functions we saw in the template. It subscribes to the Build Event Protocol to watch for Bazel analysis failures including the `is not visible from target` message, storing them for after the build finishes.

  When running interactively, it prompts the user if they'd like to have the problem auto-fixed. Note that since our plugin is written in Go, and most of the tooling ecosystem around Bazel is also written in Go, it's easy to reuse [buildozer](https://github.com/bazelbuild/buildtools/blob/master/buildozer/README.md) to apply the edits to the BUILD file.

  If running non-interactive, like on CI, it prints the equivalent buildozer command the user could run locally to fix their `BUILD` file.

  As a result, users feel that Bazel is "easy to use", and put less support/training burden on your DevInfra team.

  ## What plugins can you imagine?

  We can't wait to see how you extend Bazel! As more plugins are available, we'll put together a plugin gallery to help users discover ones that solve their own local workflow woes.
</BlogPost>
