5555]
5656
5757
58+ _PRIVATE_REPO_CI_OPTIONAL_PATTERNS = {
59+ " permissions:\n contents: write\n " ,
60+ " python -m frequenz.repo.config.cli.version.mike.info\n " ,
61+ " run: |\n "
62+ ' mike deploy --update-aliases --title "$TITLE" "$VERSION" '
63+ "$ALIASES\n " ,
64+ " python -m frequenz.repo.config.cli.version.mike.sort versions.json\n " ,
65+ }
66+
67+
5868def main () -> None :
5969 """Run the migration steps."""
6070 # Add a separation line like this one after each migration step.
@@ -92,6 +102,8 @@ def main() -> None:
92102
93103def migrate_ci_workflows () -> None :
94104 """Update the generated CI workflows to the latest template."""
105+ is_private_repo = has_private_repo_indicators ()
106+
95107 _migrate_workflow_file (
96108 Path (".github/workflows/ci-pr.yaml" ),
97109 [
@@ -120,8 +132,15 @@ def migrate_ci_workflows() -> None:
120132 description = "updated CI pull-request workflow" ,
121133 )
122134
135+ ci_workflow = Path (".github/workflows/ci.yaml" )
136+ private_ci_publish_jobs : list [str ] = []
137+ if is_private_repo and ci_workflow .exists ():
138+ private_ci_publish_jobs = find_ci_publish_jobs (
139+ _normalize_content (ci_workflow .read_text (encoding = "utf-8" ))
140+ )
141+
123142 _migrate_workflow_file (
124- Path ( ".github/workflows/ci.yaml" ) ,
143+ ci_workflow ,
125144 [
126145 (
127146 " workflow_dispatch:\n \n env:\n " ,
@@ -247,8 +266,19 @@ def migrate_ci_workflows() -> None:
247266 ),
248267 ],
249268 description = "updated main CI workflow" ,
269+ ignore_missing_patterns = (
270+ _PRIVATE_REPO_CI_OPTIONAL_PATTERNS if is_private_repo else None
271+ ),
250272 )
251273
274+ if is_private_repo and private_ci_publish_jobs :
275+ jobs = ", " .join (f"`{ job } `" for job in private_ci_publish_jobs )
276+ manual_step (
277+ f"{ ci_workflow } still contains { jobs } . This repository appears to be "
278+ "private, so those publish jobs usually should be removed manually "
279+ "after the migration, even though the workflow was updated."
280+ )
281+
252282
253283def migrate_dependabot_workflows () -> None :
254284 """Update the generated Dependabot automation workflows."""
@@ -444,6 +474,7 @@ def _migrate_workflow_file(
444474 replacements : list [tuple [str , str ]],
445475 * ,
446476 description : str ,
477+ ignore_missing_patterns : set [str ] | None = None ,
447478) -> None :
448479 """Apply text replacements to a generated workflow file.
449480
@@ -463,6 +494,12 @@ def _migrate_workflow_file(
463494
464495 updated = _pin_workflow_action_references (content )
465496 updated , missing_patterns = _apply_idempotent_replacements (updated , replacements )
497+ if ignore_missing_patterns :
498+ missing_patterns = [
499+ pattern
500+ for pattern in missing_patterns
501+ if pattern not in ignore_missing_patterns
502+ ]
466503
467504 if updated == content :
468505 if not missing_patterns :
@@ -734,6 +771,49 @@ def read_cookiecutter_str_var(name: str) -> str | None:
734771 return value
735772
736773
774+ def has_private_repo_indicators () -> bool :
775+ """Return whether the repository appears to be private."""
776+ github_org = read_cookiecutter_str_var ("github_org" )
777+ if github_org is not None and github_org != "frequenz-floss" :
778+ return True
779+
780+ license_name = read_cookiecutter_str_var ("license" )
781+ if license_name == "Proprietary" :
782+ return True
783+
784+ pyproject_path = Path ("pyproject.toml" )
785+ if pyproject_path .exists ():
786+ pyproject = _normalize_content (pyproject_path .read_text (encoding = "utf-8" ))
787+ if 'license = "LicenseRef-Proprietary"' in pyproject :
788+ return True
789+
790+ try :
791+ stdout = subprocess .check_output (
792+ ["gh" , "repo" , "view" , "--json" , "owner" ],
793+ text = True ,
794+ stderr = subprocess .PIPE ,
795+ )
796+ except (FileNotFoundError , subprocess .CalledProcessError ):
797+ return False
798+
799+ try :
800+ info : dict [str , Any ] = json .loads (stdout )
801+ owner = info ["owner" ]["login" ]
802+ except (KeyError , TypeError , json .JSONDecodeError ):
803+ return False
804+
805+ return owner != "frequenz-floss"
806+
807+
808+ def find_ci_publish_jobs (content : str ) -> list [str ]:
809+ """Return CI publish job names found in the workflow content."""
810+ jobs : list [str ] = []
811+ for job_name in ("publish-docs" , "publish-to-pypi" ):
812+ if f"\n { job_name } :\n " in f"\n { content } " :
813+ jobs .append (job_name )
814+ return jobs
815+
816+
737817def manual_step (message : str ) -> None :
738818 """Print a manual step message in yellow."""
739819 _manual_steps .append (message )
0 commit comments