Automation Pipeline4 min read

My Fix Almost Attached to the Wrong App. One Bundle Name Overlapped

A pipeline applying the same store copy fix across many apps failed three times in a row on one of them. The lookup was searching app names as a substring, and one app's bundle identifier still carried an old name that happened to be another app's real name.

#automation#gotchas#tooling#debugging
A search box containing the letter B. The result list shows two apps: the first is named A but its bundle identifier contains B, underlined in red, and the second is the real target named B. An arrow points at the wrong result first and is crossed out.
The manifest key, the bundle ID, the display name and the file name can all be four different things.

How does your automation identify its target? By the name on screen, or by a real key?

I used a substring search for convenience. So a fix meant for one app was applied wholesale to a different one.

The situation

I ran a pipeline that applies the same kind of store copy fix across several apps. You pass an app name, it finds the matching patch file, it applies it.

One app failed submission three times in a row.

The lookup was searching by substring

There's a coincidence buried in why this became a problem.

One of two apps had traces of an old name left in its bundle identifier. Its real name is A, and its bundle still contains the earlier name B. And another app's real name is exactly B.

Search for app B, and the code looks for "apps whose name contains B." App A's bundle contains B, so A matched first.

A small app with two locales received a thirty-seven-locale patch in its entirety.

Why it never reached production

It died on the spot, fortunately. The next stage was a check that halts if any locale isn't present on this version, and thirty-five of them weren't.

That's the point of this post. If the locale counts had happened to line up, another app's copy would have gone live silently.

What stopped it was not a target-identification check — it was a check with a completely different purpose. No safety mechanism did its job; a neighbouring check happened to catch it.

The other failure in the same batch

Another app in the batch died for the opposite reason: its submission script didn't exist at all.

The name in the automation list and the actual script filename differed by one letter. That one failed loudly instead of going quietly wrong, which makes it the better failure.

The fix

# Wrong — substring matching never misses an accidental overlap
if needle in bundle_name or needle in display_name:
    return this_app
 
# Right — exact bundle ID, and refuse to guess on duplicates
candidates = [a for a in all_apps if a.bundle == exact_bundle]
if len(candidates) != 1:
    raise Error(f"matches multiple apps: {[a.name for a in candidates]}")

Dying on duplicates rather than taking the first is the essential part. The real problem with substring search isn't the overlap — it's that on an overlap it silently picked one.

The same batch produced a deploy script about to overwrite live listings with stale repo values, and the root is the same: identify a target by a display value and it goes quietly wrong the day that value changes or collides.

A pre-flight sweep

Before running the batch, I added a check that sweeps every target for three things:

  1. Can the patch file be found?
  2. Does the submission script exist?
  3. Can the app ID be resolved?

This time it surfaced 11 wiring defects before anything ran. Eleven out of 46 targets is close to a quarter. Meeting those one at a time mid-run would have stalled the batch repeatedly.

The core lesson

The manifest key, the bundle ID, the display name and the file name can all be four different things.

Infer one from another and this happens. And the more apps you have, the higher the collision probability — at 46, you already collide.

Three-line self-check

  1. Does your automation find its target with a substring? It's one in. Go grep for it.
  2. What happens when there's more than one candidate? If it takes the first, that automation will eventually be quietly wrong. Dying is correct.
  3. Do you sweep every target before running a batch? A defect met mid-run is a defect met with half the batch already applied.

The honest part

My own automation created this defect. I added substring matching for convenience and didn't anticipate collisions across a wide name list. Not somebody else's code — mine.

This time it was safe by luck. The locale counts didn't line up, so it stopped; a different check ordering wouldn't have caught it. "Safe by luck" is not "safe by design." That's why the conclusion here is only "I added one more guard."

And how did I find this combination among all 46 targets? By running a full sweep after the incident. The pre-flight check is new as of this incident. It's a product of the accident, not of the design.

If you have automation that fans one fix out over many targets, grep the target-identification code for in once. If it's there, the collision day is coming.

Related