fix: unique molecule container names per CI runner

When multiple molecule matrix runners share the same Docker host,
they conflict on the container name "ubuntu-2604" (409 Conflict
from Docker). This appends the MATRIX_INDEX as a suffix (e.g.
ubuntu-2604-r3) so each runner gets a unique container name.

Closes DEVX-148

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
emil
2026-08-04 00:52:29 +02:00
co-authored by Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
parent a8a8b743f3
commit caa38fed3d
7 changed files with 135 additions and 0 deletions
+12
View File
@@ -0,0 +1,12 @@
extends: existence
message: "Don't attribute human qualities to software or hardware ('%s')."
link: https://developers.google.com/style/anthropomorphism
level: suggestion
ignorecase: true
# Limited to the two verbs the guide itself names. Broader lists (wants, knows,
# thinks) can't tell a software subject from a human one: on a 950-file corpus
# they produced 8 false positives ('the customer wants', 'your audience knows')
# for every 2 real ones.
tokens:
- sees
- tells
+14
View File
@@ -0,0 +1,14 @@
extends: existence
message: "Avoid the unverifiable claim '%s'."
link: https://developers.google.com/style/excessive-claims
level: suggestion
ignorecase: true
# The guide also names 'never', 'always', and 'ensure', but in technical writing
# those are usually legitimate instructions ('never commit secrets') rather than
# product claims: they accounted for 125 of 142 hits on a 950-file corpus.
# 'best practices' is a fixed term, not a superlative.
tokens:
- 'best(?! practices?)'
- simplest
- fastest
- guarantees?
+13
View File
@@ -0,0 +1,13 @@
extends: existence
message: "Avoid the jargon '%s'."
link: https://developers.google.com/style/jargon
level: suggestion
ignorecase: true
# The guide also cites 'solution', 'support', and 'workload' as overloaded
# terms, but those have ordinary technical meanings and accounted for every hit
# on a 950-file corpus, so only the unambiguous figurative terms are listed.
tokens:
- break-glass
- camel ?case
- out-of-the-box
- swim ?lane
+13
View File
@@ -0,0 +1,13 @@
extends: existence
message: "Avoid time-based words like '%s' in product documentation."
link: https://developers.google.com/style/timeless-documentation
level: suggestion
ignorecase: true
# The guide also names 'now' and 'new', but both have common senses that aren't
# time-anchored ('create a new project'): adding them took a 950-file corpus of
# technical documentation from 14 hits to 117. 'recently' is left out too — every
# hit in that corpus was the UI idiom 'recently used'.
tokens:
- currently
- latest
- soon
+68
View File
@@ -0,0 +1,68 @@
extends: substitution
message: "Use '%s' instead of '%s'."
link: "https://developers.google.com/style/word-list"
level: warning
# The case-insensitive half of the word list, so sentence-initial use is caught
# ('Touch the screen', not only 'touch the screen'). Entries that must stay
# case-sensitive are in WordList.yml.
ignorecase: true
action:
name: replace
swap:
"(?:API Console|dev|developer) key": API key
"(?:cell ?phone|smart ?phone)": phone|mobile phone
"(?:dev|developer|APIs) console": API console
"(?:e-mail|Email|E-mail)": email
"(?:file ?path|path ?name)": path
"(?:kill|terminate|abort)": stop|exit|cancel|end
# Longest form first: with the shortest alternative leading, 'OAuth 2' matched
# only 'OAuth', so applying the suggestion produced 'OAuth 2.0 2'. The rule is
# already case-insensitive, so the inline (?i) is redundant. See issue #41.
'\bOauth2\.0\b|\bOAuth ?2\b(?!\.0)|\bOauth\b(?! ?2)': OAuth 2.0
"(?:ok|Okay)": OK|okay
"(?:WiFi|wifi)": Wi-Fi
'[\.]+apk': APK
'3\-D': 3D
'Google (?:I\-O|IO)': Google I/O
"tap (?:&|and) hold": touch & hold
"un(?:check|select)": clear
above: preceding
account name: username
action bar: app bar
admin: administrator
a\.k\.a|aka: or|also known as
application: app
approx\.: approximately
autoupdate: automatically update
cellular data: mobile data
cellular network: mobile network
chapter: documents|pages|sections
check box: checkbox
click on: click|click in
content type: media type
curated roles: predefined roles
data are: data is
disabled?: turn off|off
ephemeral IP address: ephemeral external IP address
fewer data: less data
file name: filename
firewalls: firewall rules
functionality: capability|feature
grayed-out: unavailable
in order to: to
ingest: import|load
long press: touch & hold
network IP address: internal IP address
omnibox: address bar
open-source: open source
overview screen: recents screen
regex: regular expression
sign into: sign in to
'(?<!single )sign-?on': single sign-on
static IP address: static external IP address
stylesheet: style sheet
synch: sync
tablename: table name
tablet: device
'touch(?! ?(?:&|and) hold)': tap
vs\.: versus
+5
View File
@@ -125,6 +125,11 @@ def build_env_for_pair(pair: str, base_env: dict[str, str]) -> dict[str, str]:
"""Build environment for a single molecule pair."""
_role, _scenario, platform_name, platform_image, platform_command = parse_pair(pair)
env = base_env.copy()
# Append runner index to platform name when running in CI matrix to avoid
# Docker container name conflicts when multiple runners share the same Docker host.
matrix_index = env.get("MATRIX_INDEX")
if matrix_index:
platform_name = f"{platform_name}-r{matrix_index}"
env["MOLECULE_PLATFORM_NAME"] = platform_name
env["MOLECULE_PLATFORM_IMAGE"] = platform_image
if platform_command:
+10
View File
@@ -102,6 +102,16 @@ class TestBuildEnvForPair:
env = build_env_for_pair("default|ubuntu-2204|img:latest|", {"MOLECULE_HOME": "/custom/home"})
assert env["MOLECULE_HOME"] == "/custom/home"
def test_appends_matrix_index_to_platform_name(self) -> None:
"""When MATRIX_INDEX is set, platform name gets a unique suffix."""
env = build_env_for_pair("default|ubuntu-2604|img:latest|sleep infinity", {"MATRIX_INDEX": "3"})
assert env["MOLECULE_PLATFORM_NAME"] == "ubuntu-2604-r3"
def test_no_matrix_index_keeps_platform_name(self) -> None:
"""Without MATRIX_INDEX, platform name is unchanged."""
env = build_env_for_pair("default|ubuntu-2604|img:latest|sleep infinity", {})
assert env["MOLECULE_PLATFORM_NAME"] == "ubuntu-2604"
class TestPollForOtherFailures:
def test_sets_failed_event_when_other_runner_fails(self) -> None: