Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: DataDog/datadog-sync-cli
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 4.5.0
Choose a base ref
...
head repository: DataDog/datadog-sync-cli
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 4.6.0
Choose a head ref
  • 3 commits
  • 5 files changed
  • 3 contributors

Commits on May 22, 2026

  1. fix(roles): reconcile source state when destination silently drops pe…

    …rmissions (#572)
    
    * fix(roles): reconcile source state when destination silently drops permissions
    
    The Datadog roles API silently drops some permission IDs on create/update without
    returning a 400 — e.g. permissions that are gated by feature flags, imply each
    other, or are not grantable to the role being modified. This broke test-integrations
    on main from 2026-05-21 onward: after `sync`, the destination role had fewer
    permissions than the source role's permission set, and the follow-up `diffs` run
    flagged the divergence as `iterable_item_added`, failing the
    `assert "diff:" not in caplog.text` check across test_sync, test_migrate, and
    test_cleanup.
    
    After a successful create/update PATCH, the API response is authoritative — it
    reflects what was actually persisted. This change compares the requested vs.
    persisted permission set and, when the destination dropped some, reverse-maps
    the dropped destination UUIDs back to permission names and trims the in-memory
    source state to match. Subsequent diff comparisons within the same run, and
    subsequent `diffs` invocations after `sync` dumps state to disk, see a converged
    permission set on both sides. Source-on-disk is re-imported from the source org
    on the next `import` run, restoring the canonical view.
    
    The on-disk source state files (written by `import`) intentionally reflect source
    org state. We only adjust the in-memory representation after a sync action so
    the diff in the same `sync`-then-`diffs` flow converges; the next `import` will
    rehydrate source state from the source org.
    
    Added unit tests covering: trimming on drop, no-op when destination persists
    everything, no-op when no permissions requested, graceful handling of missing
    source state. All 608 unit tests pass.
    
    Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
    
    * fix(roles): also reconcile built-in role permissions in pre-action hook
    
    The previous reconciliation in `_reconcile_persisted_permissions` runs after the
    destination API call returns — but built-in roles (Datadog Admin/Standard/Read Only)
    never reach the API call: `create_resource` returns the pre-fetched destination role
    without POST, and `update_resource` raises SkipResource before PATCH. So for the
    exact roles the test_cli integration job was flagging (3020f50c "Datadog Admin Role",
    302b4f34 "Datadog Standard Role", 3032ffb8 "Datadog Read Only Role"), the persisted-
    permissions reconciler was never invoked, and the `iterable_item_added` diff for
    `relationships.permissions.data` reappeared on the post-sync `diffs` run.
    
    The destination org's built-in roles legitimately carry a different permission set
    than the source org (feature flags, sub-org policy, product entitlements). Since
    we can't mutate built-ins via the API, the only way to make sync idempotent is
    to trim the source side to match the destination side at diff-comparison time.
    
    `_reconcile_builtin_role_permissions` runs from `pre_resource_action_hook`, which
    fires for both sync and diffs flows. It intersects the working copy's permissions
    (already remapped to destination UUIDs by `remap_permissions`) with the destination
    role's actual permissions from `_existing_resources_map[role_name]`, and applies
    the trimmed set to both the working copy and `state.source` (for `dump_state` to
    persist after sync). Non-built-in roles are untouched.
    
    Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
    
    ---------
    
    Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
    nathantournant and claude authored May 22, 2026
    Configuration menu
    Copy the full SHA
    1b45cea View commit details
    Browse the repository at this point in the history
  2. Make TaggingConfig.add_default_tags idempotent (#571)

    When a resource already carries `managed_by:datadog-sync` from a prior
    sync (e.g. multi-hop chains: source -> R1 -> R2), the previous code
    unconditionally extended the tags list, producing duplicates that the
    destination API rejects with `400 Bad Request - "All items in
    parameter 'tags' must be unique"`. Repro'd today: SLO
    `e74398cbd95a5ca09827b30950d8f0c4` was rejected by R2 on the second
    hop.
    
    Affects 7 resource types using TaggingConfig: monitors,
    service_level_objectives, synthetics_tests,
    synthetics_global_variables, synthetics_private_locations,
    synthetics_mobile_applications, synthetics_test_suites.
    
    Also defensively copies `self.default_tags` when first-setting the
    tags field so callers can't accidentally mutate the class-level list.
    nathantournant authored May 22, 2026
    Configuration menu
    Copy the full SHA
    ddef61f View commit details
    Browse the repository at this point in the history
  3. Update CHANGELOG (#573)

    Co-authored-by: dd-octo-sts[bot] <200755185+dd-octo-sts[bot]@users.noreply.github.com>
    dd-octo-sts[bot] authored May 22, 2026
    Configuration menu
    Copy the full SHA
    272667f View commit details
    Browse the repository at this point in the history
Loading