Commit afdc6dbf authored by Clement Bois's avatar Clement Bois
Browse files

feat: deny release hash change

parent 953320c7
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -18,7 +18,7 @@ gitlab-cp --help
```bash
usage: gitlab-cp [-h] [--src-api SRC_API] [--src-token SRC_TOKEN] [--src-sync-path SRC_SYNC_PATH] [--dest-api DEST_API] [--dest-token DEST_TOKEN] [--dest-sync-path DEST_SYNC_PATH]
                 [--max-visibility {public,internal,private}] [--skip-visibility] [--exclude EXCLUDE] [--exclude-from EXCLUDE_FROM] [--include INCLUDE] [--include-from INCLUDE_FROM] [--insecure]
                 [--include-branch INCLUDE_BRANCH] [--exclude-branch EXCLUDE_BRANCH] [--update-release] [--update-avatar] [--no-group-description] [--no-project-description] [--new-group-options NEW_GROUP_OPTIONS]
                 [--include-branch INCLUDE_BRANCH] [--exclude-branch EXCLUDE_BRANCH] [--update-release] [--deny-release-hash-change] [--update-avatar] [--no-group-description] [--no-project-description] [--new-group-options NEW_GROUP_OPTIONS]
                 [--new-group-options-from NEW_GROUP_OPTIONS_FROM] [--new-project-options NEW_PROJECT_OPTIONS] [--new-project-options-from NEW_PROJECT_OPTIONS_FROM] [--dry-run] [--halt-on-error]
                 [--cache-dir CACHE_DIR]

@@ -54,6 +54,8 @@ options:
  --exclude-branch EXCLUDE_BRANCH
                        branch to exclude for git sync, comma-separated and supporting globbing; '!default' is translated to the default branch of the source project; Empty by default
  --update-release      force the update of the latest release
  --deny-release-hash-change
                        deny update of release tag hash
  --update-avatar       force update the avatar images even when they exist and look the same
  --no-group-description
                        don't synchronize group description
@@ -93,6 +95,7 @@ options:
| `--include-branch`           | `$INCLUDE_BRANCH`               | branch(s) to include for git sync, comma-separated and supporting globbing; `!default` is the default value and translated to the default branch of the source project; *Examples: `!default`, `develop`, `feat-*`* |
| `--exclude-branch`           | `$EXCLUDE_BRANCH`               | branch(s) to exclude for git sync, comma-separated and supporting globbing; `!default` is translated to the default branch of the source project; Empty by default; Take precedence over `--include-branch`; *Examples: `!default`, `develop`, `feat-*`* |
| `--update-release`           | `$UPDATE_RELEASE`               | set to force the update of the latest release (in order to trigger GitLab CI/CD catalog publication)                  |
| `--deny-release-hash-change` | `$DENY_RELEASE_HASH_CHANGE`     | deny update of release tag hash (to avoid changes in already published releases)                                      |
| `--update-avatar`            | `$UPDATE_AVATAR`                | force update the avatar images even when they exist and look the same                                                 |
| `--no-group-description`     | `$GROUP_DESCRIPTION_DISABLED`   | don't synchronize group description                                                                                   |
| `--no-project-description`   | `$PROJECT_DESCRIPTION_DISABLED` | don't synchronize project description                                                                                 |
+41 −8
Original line number Diff line number Diff line
@@ -73,6 +73,7 @@ class Synchronizer:
        include_branches: Optional[list[str]] = None,
        exclude_branches: Optional[list[str]] = None,
        update_release=False,
        deny_release_hash_change=False,
        group_description=True,
        project_description=True,
        dry_run=True,
@@ -96,6 +97,7 @@ class Synchronizer:
        self.include_branches = include_branches or []
        self.exclude_branches = exclude_branches or []
        self.force_update_latest_release = update_release
        self.deny_release_hash_change = deny_release_hash_change
        self.group_description = group_description
        self.project_description = project_description
        self.dry_run = dry_run
@@ -316,7 +318,28 @@ class Synchronizer:
            )
            return

        # 5: Git sync is required
        # 5: Fail if any release tag hash changed
        if self.deny_release_hash_change:
            src_releases = src_project.releases.list(all=True)
            for dest_release in dest_project.releases.list(all=True):
                tag_name = dest_release.tag_name
                src_release = next(
                    filter(
                        lambda rel: rel.tag_name == tag_name,
                        src_releases,
                    ),
                    None,
                )
                if src_release and src_release.commit['id'] != dest_release.commit['id']:
                    e = AssertionError(f"tag hash mismatch ({src_release.commit['id']} != {dest_release.commit['id']})")
                    print(
                        f"    - release {tag_name}: force update {AnsiColors.HRED}denied{AnsiColors.RESET}",
                        e
                    )
                    self.handle_error(e)
                    return

        # 6: Git sync is required
        repo_dir = self.work_dir / self.rel_path(src_project.path_with_namespace)
        shutil.rmtree(path=repo_dir, ignore_errors=True)

@@ -1005,6 +1028,12 @@ def run() -> None:
        action="store_true",
        help="force the update of the latest release",
    )
    parser.add_argument(
        "--deny-release-hash-change",
        default=trueish_env_var("DENY_RELEASE_HASH_CHANGE"),
        action="store_true",
        help="deny update of release tag hash",
    )
    parser.add_argument(
        "--update-avatar",
        default=trueish_env_var("UPDATE_AVATAR"),
@@ -1186,6 +1215,9 @@ def run() -> None:
    print(
        f"- upd release (--update-release) : {AnsiColors.CYAN}{args.update_release}{AnsiColors.RESET}"
    )
    print(
        f"- no hash chg (--deny-release-hash-change) : {AnsiColors.CYAN}{args.deny_release_hash_change}{AnsiColors.RESET}"
    )
    print(
        f"- upd. avatar (--update-avatar)  : {AnsiColors.CYAN}{args.update_avatar}{AnsiColors.RESET}"
    )
@@ -1253,6 +1285,7 @@ def run() -> None:
        include_branches=args.include_branch,
        exclude_branches=args.exclude_branch,
        update_release=args.update_release,
        deny_release_hash_change=args.deny_release_hash_change,
        group_description=(not args.no_group_description),
        project_description=(not args.no_project_description),
        dry_run=args.dry_run,