> For the complete documentation index, see [llms.txt](https://developer.harness.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developer.harness.io/continuous-integration/3.0/use-harness-ci/use-harness-ci/use-drone-plugins/custom-plugins.md).

# Write custom plugins

You can write your own plugins.

Plugins perform predefined tasks. They are essentially templated scripts that can be written in any programming language. While you can [run scripts in CI pipelines](/continuous-integration/3.0/use-harness-ci/use-harness-ci/run-step-settings.md), if you reuse a script, it is easier to maintain a single plugin rather than modify multiple instances of a script.

You can write your own plugins and run them in a **Plugin** step in your Harness CI pipelines. There are also many [preexisting plugins](/continuous-integration/3.0/use-harness-ci/use-harness-ci/use-drone-plugins/explore-ci-plugins.md) you can use.

You can write plugins for anything. If it can be scripted, it can be a plugin. For example, you could write plugins that:

* Scrape commit information from a Git repo.
* Extract Jira issue numbers from Git commit messages.
* Print an extended build history.
* Create a file, write to it, and store it remotely.

### Create your plugin <a href="#create-your-plugin" id="create-your-plugin"></a>

To create a plugin, you need to prepare a script, create a Docker image to run the script, and then build and publish the plugin image to a Docker registry.

1. Write the script that you want the plugin to run. You can write plugins in any programming language. For example, the following Bash script, called `clone.sh`, clones a Git repo and prints the latest commit information.

   ```bash
   #!/bin/sh
   set -xe

   # If path setting is not set, then use current directory
   path=${PLUGIN_PATH:-.}
   mkdir -p ${path}
   cd ${path}

   # Clones the public git repo and checkout to a branch
   git clone ${PLUGIN_REPO_URL} .
   git checkout ${PLUGIN_BRANCH}

   # Prints the last commit
   git log -1 --stat
   ```

   <div data-gb-custom-block data-tag="hint" data-style="info" class="hint hint-info"><p><strong>VARIABLES</strong></p><p>The above example script includes variable inputs: <code>PLUGIN_PATH</code>, <code>PLUGIN_REPO_URL</code>, and <code>PLUGIN_BRANCH</code>. <a href="#variables-in-plugin-scripts">Variables in plugin scripts</a> become the plugin's <strong>Settings</strong> when you <a href="#add-the-plugin-step">add the Plugin step</a> to your CI pipeline.</p></div>
2. Create a Docker image to run your script by specifying the script as the [ENTRYPOINT in a Dockerfile](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#entrypoint). For example, the following Dockerfile runs the `clone.sh` script:

   ```
   FROM alpine/git

   # Copies the clone script to the Docker image
   COPY clone.sh /usr/local/bin/

   # Makes the clone script executable
   RUN chmod +x /usr/local/bin/clone.sh

   ENTRYPOINT [ "/usr/local/bin/clone.sh" ]
   ```
3. Using the method of your choice, build and publish the plugin image to a Docker registry. Take note of the Docker repo and image name, such as `my-docker-repo/git-clone-plugin`. You need it when you [add the Plugin step](#add-the-plugin-step) to your CI pipeline.

   ```
   docker build -t my-docker-repo/git-clone-plugin .
   docker push my-docker-repo/git-clone-plugin
   ```

#### Test plugins locally <a href="#test-plugins-locally" id="test-plugins-locally"></a>

You can test your plugin in a local environment by running it as a Docker container. For example, the following Docker command runs the `clone.sh` plugin locally by supplying the required inputs (`PLUGIN_PATH`, `PLUGIN_REPO_URL`, and `PLUGIN_BRANCH`) and specifying the plugin's Docker repo and image (`my-docker-repo/git-clone-plugin`).

```
docker run --rm \
 -e PLUGIN_PATH=codebase \
 -e PLUGIN_REPO_URL=https://github.com/<some-account>/<some-repo>.git \
 -e PLUGIN_BRANCH=main \
 my-docker-repo/git-clone-plugin
```

#### Distribute plugins <a href="#distribute-plugins" id="distribute-plugins"></a>

Plugins are distributed as Docker images.

If your plugin image is private, others in your organization can use your plugin in their Harness CI pipelines by using a [Docker connector](/harness-platform/3.0/harness-platform-resources/connectors/cloud-providers/ref-cloud-providers/docker-registry-connector-settings-reference.md) configured for the private registry where your plugin image is stored.

If your plugin image is public, you can share it with anyone. You can submit a pull request to the [drone-plugin-index repository](https://github.com/drone/drone-plugin-index) if you'd like your plugin to be considered for the [Drone Plugins Marketplace](https://plugins.drone.io/).

### Add the Plugin step <a href="#add-the-plugin-step" id="add-the-plugin-step"></a>

Use a **Plugin** step to run a plugin in a Harness CI pipeline. The following instructions use the `clone.sh` plugin example from [Create a plugin](#create-your-plugin).

For more information about configuring **Plugin** steps, go to the [Plugin step settings reference](/continuous-integration/3.0/use-harness-ci/use-harness-ci/use-drone-plugins/plugin-step-settings-reference.md) and [Use Drone plugins](/continuous-integration/3.0/use-harness-ci/use-harness-ci/use-drone-plugins/run-a-drone-plugin-in-ci.md).

{% tabs %}
{% tab title="Visual" %}
Add the **Plugin** step to the **Build** stage of your CI pipeline. The following settings are always or usually required:

* **Name:** A name for the step.
* **Container Registry:** A [Docker connector](/harness-platform/3.0/harness-platform-resources/connectors/cloud-providers/ref-cloud-providers/docker-registry-connector-settings-reference.md) that can connect to the Docker registry where you uploaded the plugin image.
* **Image:** The plugin's Docker repo and image.
* **Settings:** Key-value pairs representing plugin settings. Settings are derived from [variables in your plugin script](#variables-in-plugin-scripts).

For example, the following configuration could be used for the `clone.sh` plugin:

* **Name:** `Git clone plugin`
* **Container Registry:** `account.docker-connector`
* **Image:** `my-docker-repo/git-clone-plugin`.
* **Settings:** The `clone.sh` script takes the following inputs:
  * `repo_url`: The URL of the repo to clone, such as `https://github.com/<some-account>/<some-repo>.git`.
  * `branch`: The branch to clone, such as `main`.
  * `path`: The location in the [stage workspace](/continuous-integration/3.0/use-harness-ci/use-harness-ci/caching-ci-data/share-ci-data-across-steps-and-stages.md#share-data-between-steps-in-a-stage) where the repo should be cloned, such as `codebase`.
    {% endtab %}

{% tab title="YAML" %}
Add the `Plugin` step to your `CI` stage. The following settings are always or usually required:

* `type: Plugin`
* `name:` A name for the step
* `connectorRef:` The ID of a [Docker connector](/harness-platform/3.0/harness-platform-resources/connectors/cloud-providers/ref-cloud-providers/docker-registry-connector-settings-reference.md) that can connect to the Docker registry where you uploaded the plugin image.
* `image:` The plugin's Docker repo and image.
* `settings:` A mapping of key-value pairs representing plugin settings. Settings are derived from [variables in your plugin script](#variables-in-plugin-scripts).

For example, the following YAML definition could be used for the `clone.sh` plugin:

```yaml
              - step:
                  type: Plugin
                  name: git clone plugin
                  identifier: git_clone_plugin
                  spec:
                    connectorRef: account.docker-connector
                    image: my-docker-repo/git-clone-plugin
                    settings:
                      repo_url: https://github.com/<some-account>/<some-repo>.git ## URL of the repo to clone.
                      branch: main ## Branch to clone.
                      path: codebase ## Location in the stage workspace to clone the repo.
```

{% endtab %}
{% endtabs %}

#### Variables in plugin scripts <a href="#variables-in-plugin-scripts" id="variables-in-plugin-scripts"></a>

**Settings** keys in **Plugin** steps originate as environment variables prefixed by `PLUGIN_` in your [plugin script](#create-your-plugin). For example, the following script includes three variables: `PLUGIN_PATH`, `PLUGIN_REPO_URL`, and `PLUGIN_BRANCH`.

```bash
#!/bin/sh
set -xe

# If path setting is not set, then use current directory
path=${PLUGIN_PATH:-.}
mkdir -p ${path}
cd ${path}

# Clones the public git repo and checkout to a branch
git clone ${PLUGIN_REPO_URL} .
git checkout ${PLUGIN_BRANCH}

# Prints the last commit
git log -1 --stat
```

When defined in a **Plugin** step's **Settings**, don't include the `PLUGIN_` prefix. For example, `PLUGIN_PATH` becomes `path`, `PLUGIN_REPO_URL` becomes `repo_url`, and `PLUGIN_BRANCH` becomes `branch`.

```yaml
                settings:
                  path: value
                  repo_url: value
                  branch: value
```

{% hint style="info" %}
**EXPRESSIONS AND SECRETS**

You can use [Harness expressions](/harness-platform/3.0/harness-platform-resources/variables-and-expressions/runtime-inputs.md#expressions) for **Settings** values. For example `password: <+stage.variables.[TOKEN_SECRET]>` supplies a [stage variable](/harness-platform/3.0/harness-platform-resources/overview/overview.md) containing a [secret](/harness-platform/3.0/harness-platform-resources/secrets.md) to a setting called `password`.

Related to this, it is a best practice to use [text secrets](/harness-platform/3.0/harness-platform-resources/secrets/add-use-text-secrets.md) for sensitive information, such as passwords and tokens, required by plugins.
{% endhint %}

### Custom plugin demos and examples <a href="#custom-plugin-demos-and-examples" id="custom-plugin-demos-and-examples"></a>

The following resources demonstrate how to create Drone plugins. The process for creating the plugin is the same, but, when using the plugin in a pipeline, be aware that there are [differences between Drone YAML and Harness YAML](/continuous-integration/3.0/use-harness-ci/use-harness-ci/use-drone-plugins/run-a-drone-plugin-in-ci.md#convert-drone-yaml-to-harness-yaml).

* [Drone tutorial: Example Bash plugin](https://docs.drone.io/plugins/tutorials/bash/)
* [Drone tutorial: Example Go plugin](https://docs.drone.io/plugins/tutorials/golang/)
* [Video tutorial: Building your first Drone plugin](https://www.youtube.com/watch?v=JJgkX9ZYPpY)

{% hint style="info" %}
Harness CI supports `DRONE_` environment variables. For more information, go to the [CI environment variables reference](/continuous-integration/3.0/troubleshooting-and-resources/troubleshoot-harness-ci/ci-env-var.md).
{% endhint %}

{% @harness-feedback/feedback %}
