@@ -148,4 +148,58 @@ def to_string(alternate_product_info: Optional[Tuple[str, str]] = None,
148148 base .extend (_extra )
149149 base .extend (_get_upstream_user_agent_info ())
150150 base .extend (_get_runtime_info ())
151+ if cicd_provider () != "" :
152+ base .append ((CICD_KEY , cicd_provider ()))
151153 return " " .join (f"{ k } /{ v } " for k , v in base )
154+
155+
156+ # List of CI/CD providers and pairs of envvar/value that are used to detect them.
157+ _PROVIDERS = {
158+ "github" : [("GITHUB_ACTIONS" , "true" )],
159+ "gitlab" : [("GITLAB_CI" , "true" )],
160+ "jenkins" : [("JENKINS_URL" , "" )],
161+ "azure-devops" : [("TF_BUILD" , "True" )],
162+ "circle" : [("CIRCLECI" , "true" )],
163+ "travis" : [("TRAVIS" , "true" )],
164+ "bitbucket" : [("BITBUCKET_BUILD_NUMBER" , "" )],
165+ "google-cloud-build" : [("PROJECT_ID" , "" ), ("BUILD_ID" , "" ), ("PROJECT_NUMBER" , "" ), ("LOCATION" , "" )],
166+ "aws-code-build" : [("CODEBUILD_BUILD_ARN" , "" )],
167+ "tf-cloud" : [("TFC_RUN_ID" , "" )],
168+ }
169+
170+ # Private variable to store the CI/CD provider. This value is computed at
171+ # the first invocation of cicd_providers() and is cached for subsequent calls.
172+ _cicd_provider = None
173+
174+
175+ def cicd_provider () -> str :
176+ """Return the CI/CD provider if detected, or an empty string otherwise."""
177+
178+ # This function is safe because (i) assignation are atomic, and (ii)
179+ # computating the CI/CD provider is idempotent.
180+ global _cicd_provider
181+ if _cicd_provider is not None :
182+ return _cicd_provider
183+
184+ providers = []
185+ for p in _PROVIDERS :
186+ found = True
187+ for envvar , value in _PROVIDERS [p ]:
188+ v = os .getenv (envvar )
189+ if v is None or (value != "" and v != value ):
190+ found = False
191+ break
192+
193+ if found :
194+ providers .append (p )
195+
196+ if len (providers ) == 0 :
197+ _cicd_provider = ""
198+ else :
199+ # TODO: reconsider what to do if multiple providers are detected.
200+ # The current mechanism as the benefit of being deterministic and
201+ # robust to ordering changes in _PROVIDERS.
202+ providers .sort ()
203+ _cicd_provider = providers [0 ]
204+
205+ return _cicd_provider
0 commit comments