GCSS-1132: detect manually-created (unmanaged) repositories#52
Merged
Conversation
…in drift-check workflow
…r unmanaged repo handling in drift-check workflow
… and improve drift handling logic
…e drift detection consistency
| import-repos: | ||
| go run main.go bulk-import -c import-config.yaml | ||
| mkdir -p "../../feature/github-repo-provisioning/gcss_config/importer_tmp_dir/" | ||
| mkdir -p "configs/$OWNER" "../../feature/github-repo-provisioning/gcss_config/importer_tmp_dir/" |
Contributor
There was a problem hiding this comment.
I think this change is not needed. The configs/$OWNER dir is primarily used for debugging locally, and should not be visible in the PR, or the repo. Am i missing something, why is this change present here?
Collaborator
Author
There was a problem hiding this comment.
Good catch. It was a leftover from an earlier version of this PR that used an optimization (exclude already-managed repos, import only the unmanaged ones)
…ion for config paths
pavlovic-ivan
approved these changes
Jun 22, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Extends the existing periodic
drift-checkworkflow to detect all drift between the desired config and the actual GitHub state - both repos created outside GCSS (invisible toterraform plan) and manual edits to already-managed repos - and surface everything in a single, continuously-updated PR assigned to reviewers. Merge to adopt the change into config, or revert it manually.It's folded into the existing
drift-checkjob, so there's no new schedule or consumer workflow - the config repo's existing scheduled caller just gains a few args.Closes #1132. Builds on the compare fix in #51.
Why
The Terraform-plan drift check only sees resources already in Terraform state, so a repo created by hand is invisible to it and never flagged. Importing the org before the plan makes those repos visible (their YAML is generated, and the module's
import {}blocks make the plan report them), so one plan now covers new repos and managed-repo drift.How it works
The single
drift-checkjob runs:The PR is gated on the terraform plan (
plan-exitcode == 2), not on the rawcompareoutput. Terraform is the authority on whether drift is real;compareonly builds the PR content. This avoids false positives from config differences terraform treats as no-ops.Merging the drift PR feeds the existing import-on-merge machinery (
promote-imported-configs): new repos are Terraform-imported into state (not recreated); managed-repo edits updaterepos/.Changes
.github/workflows/drift-check.yamldrift-checkjob now: generate app token → config setup →import-repos→compare→ terraform plan →drift-pr.Inspect driftno longerexit 1; it logs and emits a::warning::so terraform-level drift that can't be represented as a config change (e.g. a deleted/archived managed repo) stays visible even when no PR is raised.reviewers; new secretapp_private_key. Workflow-levelconcurrencyso overlapping scheduled runs don't race on the shared branch/PR..github/actions/drift-pr/action.yaml(new)feature/github-repo-importer/pkg/github/github.gonilIfEmpty: normalises empty optional free-text fields (description,homepage_url) tonil. GitHub returns these inconsistently as""ornullover a repo's lifetime (Terraform treats them as equivalent); collapsing""tonilkeeps a fresh import byte-stable against the committed config, socomparedoesn't report spurious changes.feature/github-repo-importer/Justfileimport-reposmkdir -ps the per-owner configs dir so it tolerates a zero-repo import (the steady-state "no drift" case) instead of failing onfind.Consumer setup (config repo)
The existing scheduled
drift-checkcaller just gains a few args — no second workflow:Requirements — the job runs in the
scheduleenvironment, which must now provide all of:APP_ID+app_private_key— the management GitHub App (already used byimport/plan/promote); it must have org-wide ("All repositories") access, or new repos are invisible to the importer.WORKSPACE+tfc_token— for the plan (already present).scheduleenvironment must have no required-reviewer/wait-timer protection rules (it runs unattended on a schedule, so any approval gate stalls every run).Testing
Validated end-to-end in a test org (milos-org) against real CI:
repos/homepage_url""vsnull)::warning::raised, no spurious/empty PRKnown considerations
cancel-in-progress: false).nilIfEmptychange that containdescription: ""/homepage_url: ""will be flagged bycompareuntil re-adopted, but plan-gating suppresses spurious PRs (terraform sees no real drift), so they self-heal.drift-checkmeans no detection.