151151]
152152
153153
154+ _PRIVATE_REPO_CI_OPTIONAL_PATTERNS = {
155+ " permissions:\n contents: write\n " ,
156+ " python -m frequenz.repo.config.cli.version.mike.info\n " ,
157+ " run: |\n "
158+ ' mike deploy --update-aliases --title "$TITLE" "$VERSION" '
159+ "$ALIASES\n " ,
160+ " python -m frequenz.repo.config.cli.version.mike.sort versions.json\n " ,
161+ }
162+
163+
154164def main () -> None :
155165 """Run the migration steps."""
156166 # Add a separation line like this one after each migration step.
@@ -191,6 +201,8 @@ def main() -> None:
191201
192202def migrate_ci_workflows () -> None :
193203 """Update the generated CI workflows to the latest template."""
204+ is_private_repo = has_private_repo_indicators ()
205+
194206 _migrate_workflow_file (
195207 Path (".github/workflows/ci-pr.yaml" ),
196208 [
@@ -219,8 +231,15 @@ def migrate_ci_workflows() -> None:
219231 description = "updated CI pull-request workflow" ,
220232 )
221233
234+ ci_workflow = Path (".github/workflows/ci.yaml" )
235+ private_ci_publish_jobs : list [str ] = []
236+ if is_private_repo and ci_workflow .exists ():
237+ private_ci_publish_jobs = find_ci_publish_jobs (
238+ _normalize_content (ci_workflow .read_text (encoding = "utf-8" ))
239+ )
240+
222241 _migrate_workflow_file (
223- Path ( ".github/workflows/ci.yaml" ) ,
242+ ci_workflow ,
224243 [
225244 (
226245 " workflow_dispatch:\n \n env:\n " ,
@@ -346,8 +365,19 @@ def migrate_ci_workflows() -> None:
346365 ),
347366 ],
348367 description = "updated main CI workflow" ,
368+ ignore_missing_patterns = (
369+ _PRIVATE_REPO_CI_OPTIONAL_PATTERNS if is_private_repo else None
370+ ),
349371 )
350372
373+ if is_private_repo and private_ci_publish_jobs :
374+ jobs = ", " .join (f"`{ job } `" for job in private_ci_publish_jobs )
375+ manual_step (
376+ f"{ ci_workflow } still contains { jobs } . This repository appears to be "
377+ "private, so those publish jobs usually should be removed manually "
378+ "after the migration, even though the workflow was updated."
379+ )
380+
351381
352382def migrate_dependabot_workflows () -> None :
353383 """Update the generated Dependabot automation workflows."""
@@ -561,6 +591,7 @@ def _migrate_workflow_file(
561591 replacements : list [tuple [str , str ]],
562592 * ,
563593 description : str ,
594+ ignore_missing_patterns : set [str ] | None = None ,
564595) -> None :
565596 """Apply text replacements to a generated workflow file.
566597
@@ -580,6 +611,12 @@ def _migrate_workflow_file(
580611
581612 updated = _pin_workflow_action_references (content )
582613 updated , missing_patterns = _apply_idempotent_replacements (updated , replacements )
614+ if ignore_missing_patterns :
615+ missing_patterns = [
616+ pattern
617+ for pattern in missing_patterns
618+ if pattern not in ignore_missing_patterns
619+ ]
583620
584621 if updated == content :
585622 if not missing_patterns :
@@ -851,6 +888,49 @@ def read_cookiecutter_str_var(name: str) -> str | None:
851888 return value
852889
853890
891+ def has_private_repo_indicators () -> bool :
892+ """Return whether the repository appears to be private."""
893+ github_org = read_cookiecutter_str_var ("github_org" )
894+ if github_org is not None and github_org != "frequenz-floss" :
895+ return True
896+
897+ license_name = read_cookiecutter_str_var ("license" )
898+ if license_name == "Proprietary" :
899+ return True
900+
901+ pyproject_path = Path ("pyproject.toml" )
902+ if pyproject_path .exists ():
903+ pyproject = _normalize_content (pyproject_path .read_text (encoding = "utf-8" ))
904+ if 'license = "LicenseRef-Proprietary"' in pyproject :
905+ return True
906+
907+ try :
908+ stdout = subprocess .check_output (
909+ ["gh" , "repo" , "view" , "--json" , "owner" ],
910+ text = True ,
911+ stderr = subprocess .PIPE ,
912+ )
913+ except (FileNotFoundError , subprocess .CalledProcessError ):
914+ return False
915+
916+ try :
917+ info : dict [str , Any ] = json .loads (stdout )
918+ owner : str = info ["owner" ]["login" ]
919+ except (KeyError , TypeError , json .JSONDecodeError ):
920+ return False
921+
922+ return owner != "frequenz-floss"
923+
924+
925+ def find_ci_publish_jobs (content : str ) -> list [str ]:
926+ """Return CI publish job names found in the workflow content."""
927+ jobs : list [str ] = []
928+ for job_name in ("publish-docs" , "publish-to-pypi" ):
929+ if f"\n { job_name } :\n " in f"\n { content } " :
930+ jobs .append (job_name )
931+ return jobs
932+
933+
854934def manual_step (message : str ) -> None :
855935 """Print a manual step message in yellow."""
856936 _manual_steps .append (message )
0 commit comments