Skip to content

Navigation Menu

Sign in
Sign up

All repos getting the same name field during full sync — are we missing something? #930

Unanswered
jimisola asked this question in Q&A
Discussion options

Hi!

We're running safe-settings via npm run full-sync in a GitHub Actions workflow (version 2.1.18). During a full sync with multiple repositories, every PATCH request to the GitHub API ends up with the same "name" field — one specific repo's name applied to all — causing 422 "Validation Failed: name already exists on this account" errors across the board.

For context, in our case the name being sent for every repo was kbrgnhwcx8, which happened to be an existing repository in our org. We're not sure whether this is coincidental or relevant to the error.

We might be missing something obvious in how safe-settings is meant to be configured, so wanted to ask here before assuming it's a bug.

What we observed in lib/settings.js, updateRepos():

repoConfig = Object.assign(repoConfig, { name: repo.repo, org: repo.owner })

Our reading is that since repoConfig references the shared this.config.repository object, and Promise.all runs all repos concurrently (all synchronous setup runs before any await), the last repo to execute Object.assign overwrites name on the shared object — so all repos end up PATCHing with that last repo's name. But we could easily be misreading the code (not my main language).

We worked around it by adding a catch-all suborg config (suborgs/all.yml with suborgrepos: ["*"] and repository: {}), which triggers the mergeDeep({}, repoConfig, suborg) path and creates a fresh object per repo. That seems to fix it, but it feels like a workaround rather than an intended usage pattern.

Are we setting something up incorrectly, or is this a known limitation? Happy to provide more details if helpful.

Also, the version in package.json does not match the released version so the log output is incorrect. See: https://github.com/github/safe-settings/blob/main-enterprise/package.json#L3

safe-settings.yml.txt
all.yml.txt
deployment-settings.yml.txt
settings.yml.txt

You must be logged in to vote

Replies: 3 comments

Comment options

Hey! Quick sanity checks that usually cause this:\n\n1) In each repo’s settings file, confirm name is the actual repo name (not the org) and it matches the repo slug exactly.\n2) Make sure you don’t have a shared defaults or template file that sets name: and gets merged into every repo config.\n3) If you’re using include/inherit, verify the merge order so repo‐specific name isn’t being overwritten by a later include.\n4) If you’re generating the YAML, log the final per‐repo object right before sync to see where name flips.\n\nIf it helps, paste one repo entry (redacted) and the resulting "final" config you see at runtime — happy to take a look.

You must be logged in to vote
0 replies
Comment options

Hey! Quick sanity checks that usually cause this:

  1. In each repo settings file, confirm name is the actual repo name (not the org) and it matches the repo slug exactly.
  2. Make sure you do not have a shared defaults or template file that sets name: and gets merged into every repo config.
  3. If you are using include/inherit, verify the merge order so repo-specific name is not overwritten by a later include.
  4. If you are generating the YAML, log the final per-repo object right before sync to see where name flips.

If it helps, paste one repo entry (redacted) and the resulting "final" config you see at runtime — happy to take a look.

You must be logged in to vote
0 replies
Comment options

Thanks for the suggestions! We went through each of them:

  1. No per-repo settings files in our setup — just a global settings.yml, so no per-repo name: to check.
  2. settings.yml does not set name: at all — safe-settings injects it in code, so no shared template to blame.
  3. Not using include/inherit.
  4. We did not log the final per-repo object, but we did read through the source code instead.

Since none of the checks pointed anywhere, we dug into lib/settings.js and found what we think is the cause — though we could be misreading it. Our best guess: Object.assign(repoConfig, { name: repo.repo, org: repo.owner }) mutates the shared this.config.repository object. Since all the synchronous setup in Promise.all runs before any await, the last repo's name overwrites the shared object and ends up in every PATCH request.

The workaround that fixed it for us was adding a catch-all suborg config (suborgs/all.yml):

suborgrepos:
 - "*"
# Workaround for safe-settings shared-object mutation bug:
# Without a suborg entry, all repos share the same repoConfig reference via
# Object.assign() mutation. Adding a suborg causes mergeDeep({}, ...) which
# creates a fresh object per repo, giving each repo its correct name.
repository: {}

When a suborg entry is present, safe-settings takes the mergeDeep({}, repoConfig, suborg) path which creates a fresh object per repo rather than mutating the shared one. That's been running reliably across ~17 repos for us.

Happy to be corrected if we've misdiagnosed it!

You must be logged in to vote
0 replies
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Category
Q&A
Labels
None yet

AltStyle によって変換されたページ (->オリジナル) /