# Creating Changesets per Project in Monorepos

<p className="subtitle">
	Learn how to create changesets per project in monorepos.
</p>

Large repositories often contain multiple projects, hence named as **Monorepos**. It can make sense to run the batch spec [`steps`](/batch-changes/batch-spec-yaml-reference#steps) separately in each project and create one changeset per project.

This can be done by using [`workspaces`](/batch-changes/batch-spec-yaml-reference#workspaces) in the batch specs via two steps:

1. Define the project locations with the `workspaces` property
2. Produce unique `changesetTemplate.branch` names

## Define project locations with `workspaces`

Let's say you have a repository containing multiple TypeScript projects in which you want to update TypeScript by running the following command:

```shell
npm update typescript
```

The repository has the following directory and file structure:

```bash
README
project1/package.json
project1/src/...
project2/package.json
project2/src/...
examples/project3/package.json
examples/project3/src/...
```

The location of the `package.json` files tells us that the TypeScript projects are in `project1`, `project2`, and `examples/project3`. You want to run the `npm update` command in each of these and produce an individual changeset per project.

The [`workspaces`](/batch-changes/batch-spec-yaml-reference#workspaces) property in batch specs allows you to do that:

```yaml
name: update-typescript-monorepo
description: This batch change updates the TypeScript dependency to the latest version

on:
    - repositoriesMatchingQuery: our-large-monorepo

workspaces:
    - rootAtLocationOf: package.json
      in: github.com/our-org/our-large-monorepo

steps:
    - run: npm update typescript
      container: node:14
# [...]
```

The `workspaces` property here defines that in `github.com/your-org/your-large-monorepo`, different `workspaces` exist and contain a `package.json` at their root.

When executed with `src batch [apply|preview]`, this would produce up to 3 changesets in `github.com/your-org/your-large-monorepo`, one for each project.

## Produce unique `changesetTemplate.branch` names

Since changesets are uniquely identified by their repository and branch, you **must** ensure that multiple changesets in the same repository will have different branches.

To do that, we make use of [templating](/batch-changes/batch-spec-templating) in the [`changesetTemplate.branch`](/batch-changes/batch-spec-templating#changesettemplate-context) field

```yaml
# [...]
changesetTemplate:
    title: Update TypeScript
    body: This updates TypeScript to the latest version
    published: false
    commit:
        message: Update TypeScript
    # Templating and helper functions allow us to get the `path` in which
    # the `steps` executed and turn that into a branch name:
    branch: batch-changes/update-typescript-${{ replace steps.path "/" "-" }}
```

The `steps.path` [templating variable](/batch-changes/batch-spec-templating) contains the path in which the `steps` were executed relative to the root of the repository.

With the file and directory structure above, that means you'd end up with the following branch names:

-   `batch-changes/update-typescript-project1`
-   `batch-changes/update-typescript-project2`
-   `batch-changes/update-typescript-examples-project3`

And with that, you're done and ready to produce multiple changesets in a single repository, with the full batch spec looking like this:

```yaml
name: update-typescript-monorepo
description: This batch change updates the TypeScript dependency to the latest version

on:
    - repository: github.com/sourcegraph/automation-testing

workspaces:
    - rootAtLocationOf: package.json
      in: github.com/sourcegraph/automation-testing

steps:
    - run: npm update typescript
      container: node:14

changesetTemplate:
    title: Update TypeScript
    body: This updates TypeScript to the latest version
    branch: batch-changes/update-typescript-${{ replace steps.path "/" "-" }}
    commit:
        message: Update TypeScript
    published: false
```

You only need to run `src batch [apply|preview]` to execute your batch spec.

## Dynamic discovery of workspaces

The `workspace` property leverages Sourcegraph search to find the location of the defined workspaces in the repositories yielded by the [`on`](/batch-changes/batch-spec-yaml-reference#on) property of the batch spec.

That has the advantage that it's dynamic: whenever `src batch [apply|preview]` is re-executed, Sourcegraph search is used again to find workspaces, automatically picking up new ones and removing workspaces that no longer exist.

## Only downloading workspace data in large repositories

If the repository containing the workspaces is really large and it's not feasible to download to make it available for the `steps` execution, the [`workspaces.onlyFetchWorkspaces`](/batch-changes/batch-spec-yaml-reference#workspacesonlyfetchworkspace) field can be set to `true` to only download the workspaces, without the rest of the repository.

<Callout type="info">
	To learn more about `workspaces`, read in the [batch spec YAML
	reference](/batch-changes/batch-spec-yaml-reference#workspaces).
</Callout>
