@@ -102,6 +102,8 @@ jobs:
102102 --durations=0 \
103103 --nunit-xml=test-results.xml \
104104 --verbose \
105+ --reruns 1 \
106+ --reruns-delay 2 \
105107 env :
106108 # Tell Pytest that we're running in a CI environment
107109 CI : 1
@@ -123,20 +125,39 @@ jobs:
123125 # This will give a user name like 'something macOS 2.7'
124126 SG_HUMAN_NAME : $(python_api_human_name) ${{ parameters.os_name }} ${{ parameters.python_version }}
125127 SG_HUMAN_PASSWORD : $(python_api_human_password)
126- # So, first, we need to make sure that two builds running at the same time do not manipulate
127- # the same entities, so we're sandboxing build nodes based on their name.
128- SG_PROJECT_NAME : Python API CI - $(Agent.Name)
129- # The entities created and then reused between tests assume that the same user is always
130- # manipulating them. Because different builds will be assigned different agents and therefore
131- # different projects, it means each project needs to have an entity specific to a given user.
132- # Again, this would have been a lot simpler if we could simply have had a login based on the
133- # agent name, but alas, the agent name has a space in it which needs to be replaced to something
134- # else and string substitution can't be made on build variables, only template parameters.
135- SG_ASSET_CODE : CI-$(python_api_human_login)-${{ parameters.os_name }}-${{ parameters.python_version }}
136- SG_VERSION_CODE : CI-$(python_api_human_login)-${{ parameters.os_name }}-${{ parameters.python_version }}
137- SG_SHOT_CODE : CI-$(python_api_human_login)-${{ parameters.os_name }}-${{ parameters.python_version }}
138- SG_TASK_CONTENT : CI-$(python_api_human_login)-${{ parameters.os_name }}-${{ parameters.python_version }}
139- SG_PLAYLIST_CODE : CI-$(python_api_human_login)-${{ parameters.os_name }}-${{ parameters.python_version }}
128+ # Each job gets its own ephemeral project, scoped to this build + OS + Python version.
129+ # This eliminates state bleed between concurrent runs and across successive builds on the
130+ # same agent. The project is retired in the "Cleanup test project" step below.
131+ SG_PROJECT_NAME : Python API CI - $(Build.BuildId) - ${{ parameters.os_name }} - ${{ parameters.python_version }}
132+
133+ - task : PythonScript@0
134+ displayName : Cleanup test project
135+ condition : always()
136+ inputs :
137+ scriptSource : inline
138+ workingDirectory : $(Build.SourcesDirectory)
139+ script : |
140+ import os, shotgun_api3, sys
141+ sg = shotgun_api3.Shotgun(
142+ os.environ['SG_SERVER_URL'],
143+ script_name=os.environ['SG_SCRIPT_NAME'],
144+ api_key=os.environ['SG_API_KEY'],
145+ )
146+ project = sg.find_one(
147+ entity_type='Project',
148+ filters=[['name', 'is', os.environ['SG_PROJECT_NAME']]],
149+ )
150+ if not project:
151+ print('Project not found, nothing to clean up.')
152+ sys.exit(0)
153+ sg.delete(entity_type='Project', entity_id=project['id'])
154+ print('Retired project:', os.environ['SG_PROJECT_NAME'])
155+ env :
156+ PYTHONPATH : $(Build.SourcesDirectory)
157+ SG_SERVER_URL : $(ci_site)
158+ SG_SCRIPT_NAME : $(ci_site_script_name)
159+ SG_API_KEY : $(ci_site_script_key)
160+ SG_PROJECT_NAME : Python API CI - $(Build.BuildId) - ${{ parameters.os_name }} - ${{ parameters.python_version }}
140161
141162 # Explicit call to PublishTestResults@2 and PublishCodeCoverageResults@2 here
142163 # instead of relying on pytest-azurepipelines because pytest-azurepipelines
0 commit comments