Matrix object-typed dimension items

Hello!

A gap I’ve regularly encountered when exploring use of the build matrix feature is that there doesn’t appear to be any way to specify associated values alongside a dimension’s intrinsic values. For example, I may need to use opaque integer IDs or other human-unfriendly data as dimensions, but which don’t display well in a step label, and I’d like to be able to associate a display name with that integer ID. This would more generally come up whenever I have multiple attributes which vary together rather than varying independently (i.e. when the “matrix” is sparse rather than dense).

I can’t meaningfully specify these associated values in a separate dimension because that would have a multiplicative effect in step generation, where almost all of the resulting steps would have non-useful combinations.

What I would like to be able to do is something like:

steps:
- label: "{{matrix.node.display_name}} {{matrix.test_suite.display_name}}"
  command: "..." # not really important to this example
  matrix:
    setup:
      node:
        - {key: "dev", ip: "1.2.3.4", display_name: "Development"}
        - {key: "stage", ip: "1.2.3.4", display_name: "Staging"}
        - {key: "prod", ip: "5.6.7.8", display_name: "Production"}
      test_suite:
        - {key: "read", display_name: "Read-Only Tests"}
        - {key: "write", display_name: "Read-Write Tests"}
    adjustments:
      - with:
          node: "prod"
          test_suite: "write"
        skip: true
      - with:
          node: {key: "load", ip: "9.10.11.12", display_name: "Dedicated Load Cluster"}
          test_suite: {key: "load", display_name: "Load Tests"}
        soft_fail: true

Essentially, I propose:

  1. Extending matrix item type support beyond just strings, booleans, and integers, to also include flat objects whose values are themselves limited to strings, booleans, and integers (no arbitrary nesting nor arrays).
  2. Adjustments which add new values would need to specify the whole value (i.e. whole object) for any object-typed dimension.
  3. Optionally, for modifying adjustments (skip/soft_fail), if a dimension’s objects include a key field, that scalar value may be used to identify the object to remove/modify. It’s fine if it becomes a requirement that all object-typed matrix values include a key field, and that a dimension’s key values are locally unique. In any case, a key field would also be available for interpolation just like any other field.
  4. Object fields would need to follow whatever syntax the dimension names themselves use in order to avoid complicating the interpolation syntax (they’d need to be valid identifiers, and probably wouldn’t be allowed to contain spaces nor most punctuation characters).
  5. Matrix interpolations would be extended to allow {{matrix.dimension.field}} for cases where the dimension is an object.
  6. Interpolating fields which do not exist (i.e. {{matrix.dimension.nonexistant}}) could either evaluate to an empty string or result in a pipeline evaluation error (either approach will be usable enough).

The workarounds to not having something equivalent to this aren’t very compelling (or not sufficiently simple):

  1. I could use a supporting script to infer associated values from a single scalar, but the behavior would be easier to maintain and reason about if I can specify them all in YAML (and scripting data mappings in, e.g. bash, is not particularly clean, readable, or effective).
  2. I could hard-code each variation as its own step in the pipeline.yml file, but that adds a lot of noise and redundancy, and I suspect minimizing that is what the build matrix feature was created for in the first place.
  3. I could avoid using the build matrix feature and instead use dynamic pipelines, but that approach can be undesirable for a few reasons.
  4. I could encode multiple pieces of data in a string-typed dimensions (either as stringified JSON, fixed-width fields, or something else), but I suspect matrix interpolations don’t support any string transforms (such as substring), and I can’t always control the receiver of the data (i.e. a 3rd-party Buildkite plugin which accepts environment variables), and thus can’t count on it to sub-split or re-interpret its inputs in the way I want.
  5. I could use runtime step manipulation as part of the command to dynamically set the label to something readable when the dimension value is unreadable, but that approach is especially magical and has its own self-documented limitations (i.e. potentially breaking CI status tracking).

Hey @kgillette!

Thanks so much for the post and the feedback around matrices. I’ve passed on your feedback and thoughts to our development team who work on the pipeline side of things. I can’t give an ETC for the work, as it’ll need to be planned etc, but we’re always looking to improve the way our platform is used and it sounds like your suggestions would definitely improve the usability of build matrices.

Cheers,

Ben

1 Like