[layerindex-web][PATCH 3/3] layerindex/management/commands/update_git_urls.py

Tim Orling <[email protected]>
Newsgroups org.yoctoproject.lists.yocto-patches
Message-ID <[email protected]>
* Run with python manage.py update_git_urls [--dry-run] [--quiet]
* For docker compose, run with for example:
  docker compose exec layersapp python3 /opt/layerindex/manage.py update_git_urls
* Updates vcs_url, vcs_web_url, vcs_web_tree_base_url,
  vcs_web_file_base_url, vcs_web_commit_url on every LayerItem
* Rewrites git://git.openembedded.org/ and git://git.yoctoproject.org/
  → https://, and as a bonus rewrites the matching
  http://cgit.openembedded.org/ and http://git.yoctoproject.org/
  web-interface URLs to https://
* Rewrites git:// and http:// to https:// for github.com, gitlab.com
  and bitbucket.org.
* Wraps work in a transaction; --dry-run rolls back and just prints the
  diff per layer

Fixes [Yocto #16240]
Fixes [Yocto #16305]

Signed-off-by: Tim Orling <[email protected]>
---
 layerindex/management/__init__.py             |   0
 layerindex/management/commands/__init__.py    |   0
 .../management/commands/update_git_urls.py    | 139 ++++++++++++++++++
 3 files changed, 139 insertions(+)
 create mode 100644 layerindex/management/__init__.py
 create mode 100644 layerindex/management/commands/__init__.py
 create mode 100644 layerindex/management/commands/update_git_urls.py

diff --git a/layerindex/management/__init__.py b/layerindex/management/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/layerindex/management/commands/__init__.py b/layerindex/management/commands/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/layerindex/management/commands/update_git_urls.py b/layerindex/management/commands/update_git_urls.py
new file mode 100644
index 0000000..518a548
--- /dev/null
+++ b/layerindex/management/commands/update_git_urls.py
@@ -0,0 +1,139 @@
+# Update git:// URLs to https:// in layer metadata
+#
+# Replaces git://git.openembedded.org/ and git://git.yoctoproject.org/ with
+# their https:// equivalents on LayerItem records. Also rewrites legacy
+# http:// cgit web-interface URLs for those hosts to https:// so the
+# generated browse/tree/file/commit links work.
+#
+# References:
+#   https://bugzilla.yoctoproject.org/show_bug.cgi?id=16240
+#   https://bugzilla.yoctoproject.org/show_bug.cgi?id=16272
+#
+# Usage:
+#   python manage.py update_git_urls --dry-run
+#   python manage.py update_git_urls
+#
+# Copyright (C) 2026 Konsulko Group
+# SPDX-License-Identifier: MIT
+
+from django.core.management.base import BaseCommand
+from django.db import transaction
+
+from layerindex.models import LayerItem
+
+
+# Fields on LayerItem that may carry a URL we want to rewrite.
+LAYER_URL_FIELDS = (
+    'vcs_url',
+    'vcs_web_url',
+    'vcs_web_tree_base_url',
+    'vcs_web_file_base_url',
+    'vcs_web_commit_url',
+)
+
+# (old_prefix, new_prefix) replacements. Order matters: longer / more-specific
+# prefixes must come first so we never partially-match a shorter one.
+REPLACEMENTS = (
+    # Fetch URLs: git:// -> https://
+    ('git://git.openembedded.org/', 'https://git.openembedded.org/'),
+    ('git://git.yoctoproject.org/', 'https://git.yoctoproject.org/'),
+    ('git://github.com/', 'https://github.com/'),
+    ('git://gitlab.com/', 'https://gitlab.com/'),
+    ('git://bitbucket.org/', 'https://bitbucket.org/'),
+    # Old /cgit/cgit.cgi/ paths redirect to https://git...
+    ('http://cgit.openembedded.org/cgit/cgit.cgi/', 'https://git.openembedded.org/'),
+    ('https://cgit.openembedded.org/cgit/cgit.cgi/', 'https://git.openembedded.org/'),
+    ('http://git.openembedded.org/cgit/cgit.cgi/', 'https://git.openembedded.org/'),
+    ('https://git.openembedded.org/cgit/cgit.cgi/', 'https://git.openembedded.org/'),
+    ('http://git.yoctoproject.org/cgit/cgit.cgi/', 'https://git.yoctoproject.org/'),
+    ('https://git.yoctoproject.org/cgit/cgit.cgi/', 'https://git.yoctoproject.org/'),
+    # Web-interface URLs: http:// -> https:// for the same hosts. Both
+    # cgit.openembedded.org and git.yoctoproject.org redirect http to
+    # https, but storing https directly avoids the extra round-trip and
+    # mixed-content warnings inside the layer index UI.
+    ('http://cgit.openembedded.org/', 'https://git.openembedded.org/'),
+    ('http://git.yoctoproject.org/', 'https://git.yoctoproject.org/'),
+    ('http://github.com/', 'https://github.com/'),
+    ('http://gitlab.com/', 'https://gitlab.com/'),
+    ('http://bitbucket.org/', 'https://bitbucket.org/'),
+)
+
+
+def rewrite(value):
+    """Return (new_value, changed) after applying REPLACEMENTS."""
+    if not value:
+        return value, False
+    new_value = value
+    for old, new in REPLACEMENTS:
+        if new_value.startswith(old):
+            new_value = new + new_value[len(old):]
+            # A given URL only matches one prefix, so we can stop here.
+            return new_value, True
+    return value, False
+
+
+class Command(BaseCommand):
+    help = (
+        'Rewrite legacy git:// (and matching http:// cgit) URLs on layer '
+        'metadata to their https:// equivalents.'
+    )
+
+    def add_arguments(self, parser):
+        parser.add_argument(
+            '-n', '--dry-run',
+            action='store_true',
+            help='Show what would change without writing to the database.',
+        )
+        parser.add_argument(
+            '-q', '--quiet',
+            action='store_true',
+            help='Only print a summary line.',
+        )
+
+    def handle(self, *args, **options):
+        dry_run = options['dry_run']
+        quiet = options['quiet']
+
+        changed_layers = 0
+        changed_fields = 0
+
+        # Wrap the whole pass in a transaction so a --dry-run can be rolled
+        # back cleanly and a real run is atomic. We bail out at the end if
+        # this is a dry run.
+        with transaction.atomic():
+            for layer in LayerItem.objects.all().order_by('name'):
+                layer_changes = []
+                for field in LAYER_URL_FIELDS:
+                    old_value = getattr(layer, field)
+                    new_value, changed = rewrite(old_value)
+                    if changed:
+                        setattr(layer, field, new_value)
+                        layer_changes.append((field, old_value, new_value))
+
+                if layer_changes:
+                    changed_layers += 1
+                    changed_fields += len(layer_changes)
+                    if not quiet:
+                        self.stdout.write(
+                            self.style.MIGRATE_HEADING(
+                                'Layer "%s" (id=%d):' % (layer.name, layer.pk)
+                            )
+                        )
+                        for field, old_value, new_value in layer_changes:
+                            self.stdout.write(
+                                '  %s:\n    - %s\n    + %s'
+                                % (field, old_value, new_value)
+                            )
+                    if not dry_run:
+                        layer.save(update_fields=[c[0] for c in layer_changes])
+
+            if dry_run:
+                # Roll back any pending writes (there shouldn't be any since
+                # we skipped save(), but stay defensive).
+                transaction.set_rollback(True)
+
+        verb = 'Would update' if dry_run else 'Updated'
+        self.stdout.write(self.style.SUCCESS(
+            '%s %d field(s) across %d layer(s).'
+            % (verb, changed_fields, changed_layers)
+        ))
-- 
2.54.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.