# Build metadata and tags

**URL:** https://community.tuist.dev/t/build-metadata-and-tags/885
**Category:** RFCs
**Created:** [February 2, 2026, 11:48am UTC](https://community.tuist.dev/t/build-metadata-and-tags/885 "2026-02-02T11:48:04Z")
**Posts on this page:** 4
**Page:** 1

<div class="post-metadata">

### Author: ![marekfort](https://community.tuist.dev/user_avatar/community.tuist.dev/marekfort/32/8_2.png) [@marekfort](https://community.tuist.dev/u/marekfort)
#### Post date: [February 2, 2026, 11:48am UTC](https://community.tuist.dev/t/build-metadata-and-tags/885/1 "2026-02-02T11:48:04Z")

</div>

# RFC: Custom Build Metadata for Tuist Insights

## Summary

This RFC proposes adding support for custom metadata (tags and key-value pairs) to Tuist’s build insights feature. Teams will be able to attach arbitrary metadata to builds, enabling better filtering, categorization, and correlation with external systems.

## Motivation

Build insights become significantly more valuable when teams can attach context specific to their workflows. Common use cases include:

- **Categorizing builds** : Tag builds by feature team, project area, or build type (nightly, release candidate, PR validation)
- **Correlating with external systems** : Link builds to Jira tickets, Linear issues, GitHub PRs, or internal dashboards
- **Tracking experiments** : Attach A/B test identifiers, feature flag states, or configuration variants
- **Environment context** : Capture CI runner labels, machine pools, or deployment targets
- **Performance baselines** : Mark baseline builds for regression comparisons

### Prior Art

#### Gradle Enterprise (Develocity)

[Develocity provides three mechanisms](https://docs.gradle.com/develocity/tutorials/extending-build-scans/) for extending build scans:

1. **Tags** — Simple labels for categorization (e.g., `CI`, `LOCAL`, `dirty`, `release`)
2. **Links** — Hyperlinks to external resources (CI builds, tickets, source commits)
3. **Custom Values** — Key-value pairs for metrics and computed data

Example Gradle DSL:

```groovy
buildScan {
    tag("CI")
    link("CI Build", System.getenv("BUILD_URL"))
    value("Git Commit ID", commitId)
    value("Git Branch Name", branchName)
}

```

Gradle also provides the [Common Custom User Data plugin](https://github.com/gradle/common-custom-user-data-gradle-plugin) which automatically captures common metadata like Git info, IDE vs CLI invocation, and CI-specific data. Tuist already captures similar CI metadata automatically — this RFC focuses on enabling **user-defined** custom metadata.

#### Bazel

Bazel uses the [`--build_metadata` flag](https://www.buildbuddy.io/docs/guide-metadata/) to attach key-value pairs:

```bash
bazel build //... --build_metadata=REPO_URL=https://github.com/org/repo
bazel build //... --build_metadata=BRANCH_NAME=main
bazel build //... --build_metadata=TAG_ENV=production

```

Metadata flows through the [Build Event Protocol](https://bazel.build/remote/bep-glossary) as `BuildMetadata` events, which tools like BuildBuddy consume for display and filtering.

Bazel also supports a **workspace status command** — a script that outputs key-value pairs, enabling dynamic metadata computation at build time.

## Proposed Solution

### Primary Mechanism: Environment Variables

Environment variables are the recommended approach for attaching custom metadata. This choice is driven by how build insights are integrated: the primary integration point is the **post-action scheme** that runs `tuist inspect build` after each build. Environment variables are naturally inherited by post-action scripts, making them the most practical mechanism.

```bash
# Tags (comma-separated or multiple prefixed variables)
export TUIST_BUILD_TAGS="nightly,ios-team,release-candidate"

# Or using prefixed individual tags
export TUIST_BUILD_TAG_TEAM="ios-team"
export TUIST_BUILD_TAG_TYPE="nightly"

# Custom key-value pairs
export TUIST_BUILD_METADATA_TICKET="PROJ-1234"
export TUIST_BUILD_METADATA_RUNNER="macos-14-xlarge"
export TUIST_BUILD_METADATA_JIRA="https://jira.company.com/browse/PROJ-1234"
export TUIST_BUILD_METADATA_PR="https://github.com/org/repo/pull/123"

```

The dashboard will automatically detect URL values in metadata and render them as clickable links.

**Why environment variables?**

- **Post-action compatibility** : Environment variables are inherited by post-action scripts, where `tuist inspect build` runs
- **Works seamlessly with all CI systems** : Set variables in your CI workflow and they flow through to the post-action
- **No code changes required** : No need to modify schemes or project configuration
- **Easy to set conditionally** : CI workflows can set different metadata based on branch, trigger, etc.
- **Familiar pattern** : Developers already use environment variables for CI configuration
- **Cross-platform** : Environment variables work the same way across Apple and Android/Gradle builds

#### Android/Gradle Support

The initial implementation will focus on Apple platforms (via `tuist inspect build` post-actions). However, the environment variable approach is designed to be cross-platform.

For Android/Gradle projects, custom metadata will work the same way — Gradle plugins can read environment variables via `System.getenv()`. When Tuist adds build insights for Gradle projects, a Tuist Gradle plugin would:

1. Hook into Gradle build events
2. Read `TUIST_BUILD_*` environment variables
3. Send build data + custom metadata to the Tuist server

This means teams with mixed Apple/Android codebases can use the same `TUIST_BUILD_*` environment variables in their CI workflows, and metadata will flow to both platforms’ build insights.

### Metadata Types

| Type | Purpose | Example |
| --- | --- | --- |
| **Tags** | Simple labels for filtering and grouping | `nightly`, `release`, `ios-team`, `flaky-test-retry` |
| **Metadata** | Key-value pairs for structured data (URLs are auto-linked) | `ticket=PROJ-1234`, `runner=macos-14-xlarge`, `jira=https://...` |

### Dashboard Integration

#### Build List View

- Display tags as colored chips/badges next to each build
- Filter builds by tags via multi-select dropdown
- Filter by metadata via a filter control that allows specifying both key and value (e.g., key: `ticket`, value: `PROJ-1234`)

#### Build Detail View

- Show tags prominently at the top (as chips/badges)
- Display custom metadata in a dedicated “Custom Metadata” section, showing key-value pairs in a simple table format
- Auto-detect URL values and render them as clickable links

### API Changes

#### Create Build Run (existing endpoint extension)

```json
{
  "build": {
    "duration": 45000,
    "status": "success",
    "scheme": "App",
    "custom_metadata": {
      "tags": ["nightly", "release-candidate"],
      "values": {
        "ticket": "PROJ-1234",
        "runner": "macos-14-xlarge",
        "jira": "https://jira.company.com/browse/PROJ-1234",
        "pr": "https://github.com/org/repo/pull/123"
      }
    }
  }
}

```

## Alternatives Considered

### 1. Command-Line Flags

Adding `--tag` and `--metadata` flags to `tuist inspect build`:

```bash
tuist inspect build \
  --tag "nightly" \
  --metadata "ticket=PROJ-1234" \
  --metadata "jira=https://jira.company.com/browse/PROJ-1234"

```

**Not recommended as primary mechanism because:**

- **Post-action scheme limitation** : The primary integration for build insights is via post-action schemes that run `tuist inspect build` after each build. Post-action scripts are defined in the Xcode scheme and cannot easily accept dynamic command-line arguments.
- **Requires scheme modification** : To use flags, teams would need to modify their scheme’s post-action, which defeats the purpose of the seamless auto-generated scheme integration.
- **Environment variables solve the same problem** : Any metadata that could be passed via flags can be set as environment variables, which are inherited by post-actions automatically.

Command-line flags could be added later for users who invoke `tuist inspect build` directly (outside of post-actions), but this is a minority use case.

## References

- [Develocity: Customizing Build Scans](https://docs.gradle.com/develocity/tutorials/extending-build-scans/)
- [Gradle Common Custom User Data Plugin](https://github.com/gradle/common-custom-user-data-gradle-plugin)
- [BuildBuddy: Build Metadata Guide](https://www.buildbuddy.io/docs/guide-metadata/)
- [Bazel: Build Event Protocol Glossary](https://bazel.build/remote/bep-glossary)
- [Tuist: Build Insights Documentation](https://docs.tuist.dev/en/guides/features/insights)

---

<div class="post-metadata">

### Author: ![pepicrft](https://community.tuist.dev/user_avatar/community.tuist.dev/pepicrft/32/5_2.png) [@pepicrft](https://community.tuist.dev/u/pepicrft)
#### Post date: [February 2, 2026, 12:02pm UTC](https://community.tuist.dev/t/build-metadata-and-tags/885/2 "2026-02-02T12:02:38Z")

</div>

I like the model.

While reading the proposal, I found myself wondering whether we need an umbrella term to group _metadata_ (key-value pairs) and _tags_ (single values). Or just call everything metadata, and have single or key-pair values. What prompted me thinking about it was this table that used metadata both, as an umbrella concept as an individual type of metadata:

> [@marekfort](#):
>
> ### Metadata Types
> 
> | Type | Purpose | Example |
> | --- | --- | --- |
> | **Tags** | Simple labels for filtering and grouping | `nightly`, `release`, `ios-team`, `flaky-test-retry` |
> | **Metadata** | Key-value pairs for structured data (URLs are auto-linked) | `ticket=PROJ-1234`, `runner=macos-14-xlarge`, `jira=https://...` |

When filtering for a particular tag, how do you plan to model this using columns and indexes?

---

<div class="post-metadata">

### Author: ![marekfort](https://community.tuist.dev/user_avatar/community.tuist.dev/marekfort/32/8_2.png) [@marekfort](https://community.tuist.dev/u/marekfort)
#### Post date: [February 2, 2026, 12:09pm UTC](https://community.tuist.dev/t/build-metadata-and-tags/885/3 "2026-02-02T12:09:41Z")

</div>

> [@pepicrft](#):
>
> I found myself wondering whether we need an umbrella term to group _metadata_ (key-value pairs) and _tags_ (single values).

The common umbrella term should be custom metadata or just metadata. I agree it’s confusing to then have metadata referring to also specifically to the key-value pairs. We could align with Gradle and call the key-value pairs as “values”? wdyt?

> [@pepicrft](#):
>
> When filtering for a particular tag, how do you plan to model this using columns and indexes?

I would probably skip surfacing the values (kv pairs) as columns, although we could do it if you see value in that, such as by having a `Values` column where the kv pairs will be surfaced in a custom UI component. The issue is that columns should not take too much space and I can see teams having a lot of kv pairs, which is why I would maybe consider not having a column and only directly surface the kv pairs in the build detail.

When filtering, we can have a similar component like Supabase where you can specify both the key and the value:

 ![image](https://tuist-community-storage.fly.storage.tigris.dev/original/1X/28a3c74b5627e4544e09936b1868ec6387cad74a.png)

---

<div class="post-metadata">

### Author: ![pepicrft](https://community.tuist.dev/user_avatar/community.tuist.dev/pepicrft/32/5_2.png) [@pepicrft](https://community.tuist.dev/u/pepicrft)
#### Post date: [February 2, 2026, 2:37pm UTC](https://community.tuist.dev/t/build-metadata-and-tags/885/4 "2026-02-02T14:37:49Z")

</div>

> [@marekfort](#):
>
> The common umbrella term should be custom metadata or just metadata. I agree it’s confusing to then have metadata referring to also specifically to the key-value pairs. We could align with Gradle and call the key-value pairs as “values”? wdyt?

That makes sense

> [@marekfort](#):
>
> When filtering, we can have a similar component like Supabase where you can specify both the key and the value:

Very onboard!
