1010import usethis ._pipeweld .func
1111from usethis ._config import usethis_config
1212from usethis ._console import box_print , tick_print
13- from usethis ._integrations .backend .uv .python import (
14- get_supported_uv_major_python_versions ,
13+ from usethis ._integrations .backend .dispatch import get_backend
14+ from usethis ._integrations .ci .bitbucket .anchor import (
15+ ScriptItemAnchor ,
16+ anchor_name_from_script_item ,
1517)
16- from usethis ._integrations .ci .bitbucket .anchor import ScriptItemAnchor
1718from usethis ._integrations .ci .bitbucket .cache import _add_caches_via_doc , remove_cache
1819from usethis ._integrations .ci .bitbucket .dump import bitbucket_fancy_dump
1920from usethis ._integrations .ci .bitbucket .errors import UnexpectedImportPipelineError
3738 StepItem ,
3839)
3940from usethis ._integrations .ci .bitbucket .schema_utils import step1tostep
41+ from usethis ._integrations .environ .python import get_supported_major_python_versions
4042from usethis ._integrations .file .yaml .update import update_ruamel_yaml_map
43+ from usethis ._types .backend import BackendEnum
4144
4245if TYPE_CHECKING :
4346 from ruamel .yaml .anchor import Anchor
6063source $HOME/.local/bin/env
6164export UV_LINK_MODE=copy
6265uv --version
63- """ )
66+ """ ),
67+ "ensure-venv" : LiteralScalarString ("""\
68+ python -m venv .venv
69+ source .venv/bin/activate
70+ """ ),
6471}
6572for name , script_item in _SCRIPT_ITEM_LOOKUP .items ():
6673 script_item .yaml_set_anchor (value = name , always_dump = True )
@@ -125,6 +132,7 @@ def _add_step_in_default_via_doc(
125132
126133 # If our anchor doesn't have a definition yet, we need to add it.
127134 if script_item .name not in defined_script_item_by_name :
135+ script_item_name = script_item .name
128136 try :
129137 script_item = _SCRIPT_ITEM_LOOKUP [script_item .name ]
130138 except KeyError :
@@ -134,17 +142,19 @@ def _add_step_in_default_via_doc(
134142 if config .definitions is None :
135143 config .definitions = Definitions ()
136144
137- script_items = config .definitions .script_items
145+ existing_script_items = config .definitions .script_items
138146
139- if script_items is None :
140- script_items = CommentedSeq ()
141- config .definitions .script_items = script_items
147+ if existing_script_items is None :
148+ existing_script_items = CommentedSeq ()
149+ config .definitions .script_items = existing_script_items
142150
143- # N.B. Once we support multiple different types of script items, we will
144- # probably want to enforce a canonical order rather than just append.
145- # See also anchor.py.
146- script_items .append (script_item )
147- script_items = CommentedSeq (script_items )
151+ # Insert script item in canonical order based on _SCRIPT_ITEM_LOOKUP
152+ insertion_index = _get_script_item_insertion_index (
153+ script_item_name = script_item_name ,
154+ doc = doc ,
155+ )
156+ existing_script_items .insert (insertion_index , script_item )
157+ existing_script_items = CommentedSeq (existing_script_items )
148158 else :
149159 # Otherwise, if the anchor is already defined, we need to use the
150160 # reference
@@ -158,7 +168,7 @@ def _add_step_in_default_via_doc(
158168 # N.B. Currently, we are not accounting for parallelism, whereas all these steps
159169 # could be parallel potentially.
160170 # See https://github.com/usethis-python/usethis-python/issues/149
161- maj_versions = get_supported_uv_major_python_versions ()
171+ maj_versions = get_supported_major_python_versions ()
162172 step_order = [
163173 "Run pre-commit" ,
164174 # For these tools, sync them with the pre-commit removal logic
@@ -186,6 +196,33 @@ def _add_step_in_default_via_doc(
186196 )
187197
188198
199+ def _get_script_item_insertion_index (
200+ * , script_item_name : ScriptItemName , doc : BitbucketPipelinesYAMLDocument
201+ ) -> int :
202+ """Get the correct insertion index for a script item to maintain canonical order."""
203+ # Check if we have existing script items in the raw YAML content
204+ if not (
205+ dict (doc .content ).get ("definitions" )
206+ and dict (doc .content ["definitions" ]).get ("script_items" )
207+ ):
208+ return 0
209+
210+ existing_script_items = doc .content ["definitions" ]["script_items" ]
211+ canonical_order = list (_SCRIPT_ITEM_LOOKUP .keys ())
212+
213+ for i , existing_item in enumerate (existing_script_items ):
214+ existing_name = anchor_name_from_script_item (existing_item )
215+ if (
216+ existing_name is not None
217+ and existing_name in canonical_order
218+ and canonical_order .index (script_item_name )
219+ < canonical_order .index (existing_name )
220+ ):
221+ return i
222+
223+ return len (existing_script_items ) # Default to end
224+
225+
189226def remove_bitbucket_step_from_default (step : Step ) -> None :
190227 """Remove a step from the default pipeline in the Bitbucket Pipelines configuration.
191228
@@ -437,16 +474,30 @@ def add_placeholder_step_in_default(report_placeholder: bool = True) -> None:
437474
438475
439476def _get_placeholder_step () -> Step :
440- return Step (
441- name = _PLACEHOLDER_NAME ,
442- script = Script (
443- [
444- ScriptItemAnchor (name = "install-uv" ),
445- "echo 'Hello, world!'" ,
446- ]
447- ),
448- caches = ["uv" ],
449- )
477+ backend = get_backend ()
478+
479+ if backend is BackendEnum .uv :
480+ return Step (
481+ name = _PLACEHOLDER_NAME ,
482+ script = Script (
483+ [
484+ ScriptItemAnchor (name = "install-uv" ),
485+ "echo 'Hello, world!'" ,
486+ ]
487+ ),
488+ caches = ["uv" ],
489+ )
490+ elif backend is BackendEnum .none :
491+ return Step (
492+ name = _PLACEHOLDER_NAME ,
493+ script = Script (
494+ [
495+ "echo 'Hello, world!'" ,
496+ ]
497+ ),
498+ )
499+ else :
500+ assert_never (backend )
450501
451502
452503def get_defined_script_items_via_doc (
0 commit comments