Skip to content

New repositories not created during full-sync operation #709

Closed

Description

@PendaGTP

Problem Description

When running safe-settings via GitHub Actions using the full-sync script, new repositories defined in the configuration are not being created, while all other settings synchronization works as expected.

Current vs Expected Behavior

Currently:

  • Full-sync runs successfully without errors
  • Existing repositories are properly synchronized
  • New repositories defined in config are not created

Expected:

  • New repositories should be created based on the configuration during full-sync

Technical Analysis

The issue stems from different code execution paths:

  1. Normal Operation (Working via webhook)

    • Event triggers syncSettings → Settings.sync → Settings.updateRepos
    • Directly processes repositories without filtering
  2. Full-Sync (Not Working)

    • Process: syncInstallation → syncAllSettings → Settings.syncAll → Settings.updateAll()
    • updateAll only processes existing repositories via eachRepositoryRepos (which use GET /installation/repositories to filter)
    • Missing: Check for repositories defined in config but not yet created

Environment

  • safe-settings: 2.1.24
  • Platform: github.com
  • Running on: Github Action (cron + manual trigger)
  • Using full-sync script

Related Issues

Activity

  1. HagegeR commented on Jan 20, 2025

    @HagegeR

    I've checked both with 2.1.15-rc.1 and 2.2.0-rc.1 for full-sync through a GitHub Workflow, I tried trigger the repo creation by adding a file in the repos folder with force_create: true
    I can see that the file is getting parsed, but the repo is not created.
    I think this issue persist.

  2. PendaGTP commented on Jan 20, 2025

    @PendaGTP
    ContributorAuthor

    I've checked both with 2.1.15-rc.1 and 2.2.0-rc.1 for full-sync through a GitHub Workflow, I tried trigger the repo creation by adding a file in the repos folder with force_create: true I can see that the file is getting parsed, but the repo is not created. I think this issue persist.

    @HagegeR

    Could you please provide more details about your setup so I can try to reproduce this issue? This would help determine if it might be related to a configuration problem.

    In my testing, repository creation is working correctly.

    Thank you!

  3. HagegeR commented on Jan 20, 2025

    @HagegeR

    Sure, my workflow look like this:

    name: Safe Settings Sync
    on:
      push:
        branches:
          - main
        paths:
          - safe-settings/**
          - .github/workflows/safe_settings_actions.yml
      pull_request:
        paths:
          - safe-settings/**
          - .github/workflows/safe_settings_actions.yml
      schedule:
        - cron: 0 0 * * *
      workflow_dispatch: {}
    
    concurrency:
      cancel-in-progress: true
      group: >-
        ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
    
    jobs:
      safe-settings-sync:
        runs-on: ubuntu-latest
        env:
          # Version/tag of github/safe-settings repo to use:
          SAFE_SETTINGS_VERSION: 2.2.0-rc.1
          SAFE_SETTINGS_CODE_DIR: .safe-settings-code
        steps:
          - name: Checkout source
            uses: actions/checkout@v4
    
          - name: Checkout GitHub Safe-Settings repository
            uses: actions/checkout@v4
            with:
              path: ${{ env.SAFE_SETTINGS_CODE_DIR }}
              ref: ${{ env.SAFE_SETTINGS_VERSION }}
              repository: github/safe-settings
    
          - name: Setup Node.js
            uses: actions/setup-node@v4
            with:
              cache-dependency-path:
                ${{ env.SAFE_SETTINGS_CODE_DIR }}/package-lock.json
              cache: npm
              node-version-file: ${{ env.SAFE_SETTINGS_CODE_DIR }}/.nvmrc
    
          - name: Install dependencies
            run: npm install
            working-directory: ${{ env.SAFE_SETTINGS_CODE_DIR }}
    
          - name: Run application
            run: npm run full-sync
            working-directory: ${{ env.SAFE_SETTINGS_CODE_DIR }}
            env:
              ADMIN_REPO: org_settings
              APP_ID: ${{ vars.SAFE_SETTINGS_APP_ID }}
              BLOCK_REPO_RENAME_BY_HUMAN: false
              CONFIG_PATH: safe-settings
              DEPLOYMENT_CONFIG_FILE: ${{ github.workspace }}/safe-settings/deployment.yaml
              ENABLE_PR_COMMENT: true
              GH_ORG: ${{ vars.SAFE_SETTINGS_GH_ORG }}
              GITHUB_CLIENT_ID: ${{ vars.SAFE_SETTINGS_GITHUB_CLIENT_ID }}
              GITHUB_CLIENT_SECRET: ${{ secrets.SAFE_SETTINGS_GITHUB_CLIENT_SECRET }}
              LOG_LEVEL: debug
              PRIVATE_KEY: ${{ secrets.SAFE_SETTINGS_PRIVATE_KEY }}
              SETTINGS_FILE_PATH: organisation.yaml
    

    I've added a file in the safe-settings/repos folder that looks like:

    repository:
      force_create: true
      template: MY-ORG/MY.template
    

    commited and pushed it to the main branch, the sync ran and I got no repos, was I supposed to do anything differently?

  4. PendaGTP commented on Jan 20, 2025

    @PendaGTP
    ContributorAuthor

    Thanks @HagegeR

    I haven't tested with a template yet, but it should work the same way. Could you please confirm if you've set repository.name property?

  5. HagegeR commented on Jan 20, 2025

    @HagegeR

    shouldn't the repository.name be the name of the .yml file in the safe-settings/repos folder?
    Ok, I see that adding a name value in the yml create the repo, but I believe this is redundant, the name of the file is the name of the repo in my case, this scheme works to apply settings per repo so why not for the creation too?

  6. PendaGTP commented on Jan 20, 2025

    @PendaGTP
    ContributorAuthor

    shouldn't the repository.name be the name of the .yml file in the safe-settings/repos folder? Ok, I see that adding a name value in the yml create the repo, but I believe this is redundant, the name of the file is the name of the repo in my case, this scheme works to apply settings per repo so why not for the creation too?

    Based on my understanding, you still need to set the name in the config file

    e.g safe-settings/repos/example.yml:

    repository:
      name: example
      force_create: true
  7. HagegeR commented on Jan 20, 2025

    @HagegeR

    Thanks for unblocking this use case for me @PendaGTP!

    Based on my understanding, you still need to set the name in the config file

    I've seen that it's the case from my tests.

    but I did not see this documented anywhere, and as I said previously, only setting the configurations themselves without the name is enough for other features, so it's inconsistent

  8. PendaGTP commented on Jan 20, 2025

    @PendaGTP
    ContributorAuthor

    Thanks for your feedback. I'll investigate the possible improvements.

  9. HagegeR commented on Jan 21, 2025

    @HagegeR

    @PendaGTP your help and contribution is much appreciated!

    so I've ran a more complex scenario and I'd like your opinion on this one (sorry but I'm obfuscating a few details), this is a generic template of a repo file I'm trying to implement to gradually transition to use the teams feature of safe-settings:

    repository:
      name: <SUBORG_NAME>.<REPO_NAME>
      force_create: true
      template: MY.template
    
    collaborators:
      - username: <REQUESTER_ID>
        permission: admin
    
    teams:
      - name: <SUBORG_NAME>.<REPO_NAME>
        permission: push
        maintainers:
          - <REQUESTER_ID>
        repo_names:
          - <SUBORG_NAME>.<REPO_NAME>
        # to get a team id: https://docs.github.com/en/rest/teams/teams?apiVersion=2022-11-28#get-a-team-by-name
        parent_team_id: <SUBORG_TEAM_ID>
      - name: <SUBORG_NAME>
        permission: pull
      - name: <SUBORG_NAME>.PUSH
        permission: push
      - name: <SUBORG_NAME>.ADMIN
        permission: admin
    

    my assumption is that in that case it would:

    1. creates the repo
    2. add the collaborator as admin to the repo (direct access)
    3. create and/or add all the teams to the collaborators of the newly created repo (in my case it would create the first team and just connect the other teams)

    Now, from the runtime log I get, it might be that some of the repo settings are trying to be set before creating the repo, I'm not sure because the error logs state that it's too long to fully be printed (too many changes to report), and it's too difficult to read.

    2025-01-21T14:50:42.4928229Z �[34mDEBUG�[39m (github): �[36mGitHub request: GET https://api.github.com/repos/<ORG_NAME>/<SUBORG_NAME>.<REPO_NAME> - 404�[39m
    2025-01-21T14:50:42.5258294Z �[31mERROR�[39m (probot): �[36mNot Found - https://docs.github.com/rest/repos/repos#get-a-repository�[39m
    2025-01-21T14:50:42.5259267Z �[34mDEBUG�[39m (probot): �[36mNot run in nop�[39m
    

    EDIT: this is actually a regression in 2.2.0-rc.2, in 2.2.0-rc.1 it works a lot closer to what I was expected
    EDIT2: from the logs now it seems like it's always trying to recreate the teams although they already exist (this is a rerun, on the first run the first team was created):

    �[34mDEBUG�[39m (<SUBORG_NAME>.<REPO_NAME>): �[36mGitHub request: POST https://api.github.com/orgs/<MY_ORG>/teams - 422�[39m
    2025-01-21T18:04:45.4811786Z �[31mERROR�[39m (probot): �[36mError adding team: �[39m
    2025-01-21T18:04:46.0601282Z �[34mDEBUG�[39m (<SUBORG_NAME>.PUSH): �[36mGitHub request: POST https://api.github.com/orgs/<MY_ORG>/teams - 422�[39m
    2025-01-21T18:04:46.0602359Z �[31mERROR�[39m (probot): �[36mError adding team: �[39m
    2025-01-21T18:04:47.1010186Z �[34mDEBUG�[39m (<SUBORG_NAME>.ADMIN): �[36mGitHub request: POST https://api.github.com/orgs/<MY_ORG>/teams - 422�[39m
    2025-01-21T18:04:47.1030446Z �[31mERROR�[39m (probot): �[36mError adding team: �[39m
    

    So the connection of the existing teams to the repo with the correct permission in them (on 2.2.0-rc.1) did not work.
    Tell me if you prefer I open a separate issue on this with this content

  10. PendaGTP commented on Jan 22, 2025

    @PendaGTP
    ContributorAuthor

    @HagegeR

    Thank you for your feedback and detailed information.

    Below is my current understanding of how Safe-Settings processes repository reconciliation. There may be errors, but I've tried my best to make it as clear as possible. I apologize for not directly answering your question - I wanted to document my understanding of the process to help us identify if the flow is correct.

    Initial Configuration Resolution:

    1. Load and merge configurations in sequence:
      1. Base repository configuration (defined in .github/settings.yml)
      2. Sub-organization configuration (if exists in (.github/suborgs/*.yml))
      3. Repository-specific override configuration (if exists in .github/repos/*.repo)

    Repository Settings Synchronization:

    1. Check if the repository exists (GH API call)

    If Repository Exists:

    1. Compare current state
    2. Execute in sequence:
      1. Update default branch
      2. Update repository settings (excluding: topics, security, custom properties, teams and permissions, collaborator and permissions, issue labels, milestones, branch protections, autolink, environments)
    3. Execute in parallel:
      1. Topics update
      2. Security update
    4. Execute in parallel:
      1. Update Custom properties
      2. Teams and permissions
      3. Collaborator and permissions
      4. Issue labels
      5. Milestones
      6. Branch protections
      7. Autolink
      8. Environments

    If Repository Does Not exist:

    1. Check if force_create is enabled (skip process if disabled)
    2. If template is specified:
      • Create repository using template with only: {template_owner, template_repo, owner, name, private status, description} (so other settings like has_issues are missing)
      • Note: Does not update default branch, topics, or security (see notes (1) below)
    3. If no template:
      • Create repository with all repo settings (excluding: topics, security, custom properties, teams and permissions, collaborator permissions, issue labels, milestones, branch protections, autolink, environments)
      • Note: Does not update default branch, topics, or security (see notes (1) below)
    4. Then execute in parallel:
      1. Update Custom properties
      2. Teams and permissions
      3. Collaborator and permissions
      4. Issue labels
      5. Milestones
      6. Branch protections
      7. Autolink
      8. Environments

    Notes:

    • (1) This is not a major issue when Safe-Settings is deployed and responds to events like repository.created/edited, as it will reconcile settings immediately after creation.
    • (1) However, this reconciliation does not occur during full-sync.
    • (1) So currently, the full-sync script does not have full compatibility and feature parity with the deployed Safe-Settings app.
    • Additionally, the parallel execution of the listed plugins may cause errors. Further investigation is needed to determine if certain plugins require a specific sequence (e.g., teams may need collaborators to be synchronized first).

    EDIT: format

  11. HagegeR commented on Jan 22, 2025

    @HagegeR

    little correction: repos/.repo => repos/.yml

    I see, thanks for the detailed flow @PendaGTP!

    So from my understanding of this flow (which could very well end up in a .md somewhere, it's very good) the way teams interact with repos in that full-sync flow seems to have flaws, here is what I think should happen:

    1. After Repository Settings Synchronization, a Teams Settings Synchronization should occur to understand what teams should be created and which should only be modified, and create the missing teams
    2. during step 4. the Teams and permissions should leverage the previous step and only modify permissions and repo associations there, not try to create teams

    it could be that the "Teams and permissions" has a bug that always tried to create a team even when it already exist, and just fixing that would not require big refactors

  12. PendaGTP commented on Jan 22, 2025

    @PendaGTP
    ContributorAuthor

    @HagegeR Thanks.

    I suggest simplifying the repository sync flow for both full-sync and app deployed modes as follows:

    Sequential operations:

    • Create repository (if needed):
      • From template (GH API: /repos/{template_owner}/{template_repo}/generate)
      • Or create new (GH API: /orgs/{org}/repos)
    • Update default branch (rename if needed)
    • Update repository settings (GH API: /repos/{owner}/{repo})

    Then parallel operations:

    • Update Custom Properties
    • Update Issue Labels
    • Update Milestones
    • Update Autolink
    • Update Environments
    • Update Topics
    • Update Teams and Permissions
    • Update Collaborators and Permissions

    Then sequential operations:

    • Update Vulnerability Alerts
    • Update Automated Security Fixes
    • Update Branch Protection Rules
    • Archive Repository

    I will investigate the current flow within the Teams plugin to identify the source of the issue. I'll open a issue later to summarize our discussion and highlight potential improvements to the global flow.

  13. HagegeR commented on Jan 22, 2025

    @HagegeR

    I think this simplification of the repo creation if needed makes a lot of sense
    it might very well reduce maintenance and bugs being corrected on the happy flow and not working in the second flow
    thank you for looking into this 🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions