Skip to content

feat: manage organization repository topics centrally - #51

Merged
xnoto merged 3 commits into
mainfrom
manage-org-repository-topics
Sep 2, 2026
Merged

feat: manage organization repository topics centrally#51
xnoto merged 3 commits into
mainfrom
manage-org-repository-topics

Conversation

@xnoto

@xnoto xnoto commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Brings organization repository topics under central management in this root for active repositories. Adds a topics_by_repository map in main.tf next to the repository definitions and wires it into github_repository.repositories through the provider-supported inline topics argument, so active repositories' topics become declared desired state instead of unmanaged GitHub metadata. Archived repositories are skipped (topics = null), so their existing GitHub topics are left untouched and unmanaged.

Design notes:

  • The provider documents github_repository_topics as incompatible with the topics attribute and intended only for repositories not handled via Terraform ([FEAT]: Add github_repository_topics resource integrations/terraform-provider-github#1845). Every repository here is managed by this root, so the inline attribute is the supported path.
  • Because github_repository.repositories is already in state for every repository, managing topics requires no new import {} blocks; the existing tfroot-gcp repository import in imports.tf is unchanged. The CI plan confirms this: every repository plans as an in-place update, with no creates.
  • Archived ansible-* repositories (deprecated) are excluded from the map; gh-repositories.tf gates topics on the existing local.active_github_repositories local, so archived repositories' topics stay unmanaged.
  • No driving issue exists for this change.

Topic audit (live topics read from GitHub on 2026-09-02):

  • Stale/invalid corrected: tfroot-libvirt drops ansible-automation-platform (no Ansible content in that root); tfroot-github renames github-provider-terraformterraform-provider-github (the provider's registry slug).
  • Initial topics for previously untagged repositories: charts, agent-knowledge, tfroot-gcp, tfroot-namecheap.
  • Skipped: archived ansible-* repositories carry no entry and no drift.
  • Verified and kept unchanged: cflan (zero drift in the plan).
  • Factual expansion: remaining repositories gain a few evidenced topics (e.g. opentofu across tfroot roots, app-of-apps/k3s/ksops for kustomize-cluster, pwa/s3/static-site for www, workload-identity-federation for tfroot-gcp), each sourced from repository READMEs, repository source, or the agent-knowledge topology.

Type of change

  • Bug fix
  • Feature / enhancement
  • Documentation
  • Infrastructure (OpenTofu root or module)
  • GitOps desired state (manifests, kustomize, charts, SOPS/KSOPS secrets)
  • Container image
  • CI / reusable workflow
  • Refactor / cleanup
  • Breaking change

Validation

  • Required pull-request checks pass on 208f19fef557c12b7ec0d97e7bb4e0debb879ca3opentofu / test and opentofu / plan are green (validate, tflint, checkov, fmt, terraform-docs, and secret detection all passed). The plan is 0 to add, 14 to change, 0 to destroy — topics-only in-place updates on the 14 active repositories; no archived repository appears in the plan, and there are no protection, secret, file, team, or membership changes.
  • Generated or centrally distributed files were regenerated by their owning automation, not hand-edited — no inputs, variables, or resources were added, so the terraform-docs-generated README.md requires no regeneration.

Impact and rollout

Merging starts the environment-gated apply on main. The only runtime effect is GitHub repository topic metadata on active repositories; branch protections, teams, secrets, distributed files, and archived repositories are unchanged. Rollback is a revert (topics otherwise remain editable in the GitHub UI only until the next apply restores declared state).

Safety and secrets

  • Contains no plaintext secrets, decrypted SOPS values, state files, kubeconfigs, tokens, or private endpoints
  • No local OpenTofu init/plan/apply/destroy/import/state operations were run or claimed — plans come from pull-request checks
  • No breaking or irreversible effects; topic metadata only, with rollback noted above

AI-agent disclosure: this change was authored by the makeitwork OpenCode agent under owner tasking; the evidence and decision trail are in this description.

Add topics_by_repository to main.tf beside the repository definitions and
wire it into github_repository.repositories via the provider-supported
inline topics attribute. Topics were audited against live GitHub state on
2026-09-02: stale tags corrected, initial topics added for previously
untagged repositories, and factual topics expanded where evidenced.
@xnoto
xnoto requested a review from a team as a code owner September 2, 2026 15:43
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

OpenTofu Test

OpenTofu test passed.

View the workflow run.

View run output
Terraform validate.......................................................Passed
Terraform validate with tflint...........................................Passed
Checkov..................................................................Passed
Terraform fmt............................................................Passed
Terraform docs...........................................................Passed
Detect hardcoded secrets.................................................Passed
check for case conflicts.................................................Passed
check for merge conflicts................................................Passed
check for broken symlinks............................(no files to check)Skipped
check vcs permalinks.....................................................Passed
detect destroyed symlinks................................................Passed
detect private key.......................................................Passed
fix end of files.........................................................Passed
mixed line ending........................................................Passed
trim trailing whitespace.................................................Passed
don't commit to branch..................................................Skipped
check for added large files..............................................Passed

Owner feedback: the archived ansible-* repositories are deprecated; limit
topics_by_repository to active repositories and set topics to null for
archived ones so the provider leaves their GitHub topics unmanaged.
Reuse the established active-repository local instead of re-testing the
archived set, and reference it from the map comment for discoverability.
No semantic change: the same 14 active repositories are managed.
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

OpenTofu Plan

OpenTofu plan passed.

View the workflow run.

View run output
OpenTofu will perform the following actions:

  # github_repository.repositories[".github"] will be updated in-place
  ~ resource "github_repository" "repositories" {
        id                                      = ".github"
        name                                    = ".github"
      ~ topics                                  = [
          + "community-health",
          + "org-profile",
            # (1 unchanged element hidden)
        ]
        # (37 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

  # github_repository.repositories["agent-knowledge"] will be updated in-place
  ~ resource "github_repository" "repositories" {
        id                                      = "agent-knowledge"
        name                                    = "agent-knowledge"
      ~ topics                                  = [
          + "agents",
          + "documentation",
          + "knowledge-base",
          + "opencode",
        ]
        # (34 unchanged attributes hidden)
    }

  # github_repository.repositories["charts"] will be updated in-place
  ~ resource "github_repository" "repositories" {
        id                                      = "charts"
        name                                    = "charts"
      ~ topics                                  = [
          + "ghcr",
          + "gitops",
          + "helm",
          + "helm-charts",
          + "oci",
        ]
        # (36 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

  # github_repository.repositories["images"] will be updated in-place
  ~ resource "github_repository" "repositories" {
        id                                      = "images"
        name                                    = "images"
      ~ topics                                  = [
          + "github-actions",
          + "opentofu",
            # (3 unchanged elements hidden)
        ]
        # (37 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

  # github_repository.repositories["kustomize-cluster"] will be updated in-place
  ~ resource "github_repository" "repositories" {
        id                                      = "kustomize-cluster"
        name                                    = "kustomize-cluster"
      ~ topics                                  = [
          + "app-of-apps",
          + "gitops",
          + "k3s",
[REDACTED: potentially sensitive plan output]
[REDACTED: potentially sensitive plan output]
            # (2 unchanged elements hidden)
        ]
        # (35 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

  # github_repository.repositories["shared-workflows"] will be updated in-place
  ~ resource "github_repository" "repositories" {
        id                                      = "shared-workflows"
        name                                    = "shared-workflows"
      ~ topics                                  = [
          + "github-actions",
          + "opentofu",
          + "reusable-workflows",
            # (2 unchanged elements hidden)
        ]
        # (36 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

  # github_repository.repositories["terraform-libvirt-domain"] will be updated in-place
  ~ resource "github_repository" "repositories" {
        id                                      = "terraform-libvirt-domain"
        name                                    = "terraform-libvirt-domain"
      ~ topics                                  = [
          + "cloud-init",
          + "libvirt",
            # (2 unchanged elements hidden)
        ]
        # (37 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

  # github_repository.repositories["tfroot-aws"] will be updated in-place
  ~ resource "github_repository" "repositories" {
        id                                      = "tfroot-aws"
        name                                    = "tfroot-aws"
      ~ topics                                  = [
          + "kms",
          + "opentofu",
            # (4 unchanged elements hidden)
        ]
        # (37 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

  # github_repository.repositories["tfroot-cloudflare"] will be updated in-place
  ~ resource "github_repository" "repositories" {
        id                                      = "tfroot-cloudflare"
        name                                    = "tfroot-cloudflare"
      ~ topics                                  = [
          + "cloudflare-access",
          + "cloudflare-tunnel",
          + "opentofu",
            # (4 unchanged elements hidden)
        ]
        # (37 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

  # github_repository.repositories["tfroot-gcp"] will be updated in-place
  ~ resource "github_repository" "repositories" {
        id                                      = "tfroot-gcp"
        name                                    = "tfroot-gcp"
      ~ topics                                  = [
          + "gcp",
          + "gcs-backend",
          + "kms",
          + "opentofu",
[REDACTED: potentially sensitive plan output]
          + "tfstate",
          + "workload-identity-federation",
        ]
        # (36 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

  # github_repository.repositories["tfroot-github"] will be updated in-place
  ~ resource "github_repository" "repositories" {
        id                                      = "tfroot-github"
        name                                    = "tfroot-github"
      ~ topics                                  = [
          - "github-provider-terraform",
          + "github-actions",
          + "opentofu",
          + "terraform-provider-github",
            # (3 unchanged elements hidden)
        ]
        # (37 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

  # github_repository.repositories["tfroot-libvirt"] will be updated in-place
  ~ resource "github_repository" "repositories" {
        id                                      = "tfroot-libvirt"
        name                                    = "tfroot-libvirt"
      ~ topics                                  = [
          - "ansible-automation-platform",
          + "cloud-init",
          + "k3s",
          + "libvirt",
          + "opentofu",
            # (4 unchanged elements hidden)
        ]
        # (37 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

  # github_repository.repositories["tfroot-namecheap"] will be updated in-place
  ~ resource "github_repository" "repositories" {
        id                                      = "tfroot-namecheap"
        name                                    = "tfroot-namecheap"
      ~ topics                                  = [
          + "cloudflare",
          + "dns",
          + "domains",
          + "namecheap",
          + "opentofu",
        ]
        # (36 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

  # github_repository.repositories["www"] will be updated in-place
  ~ resource "github_repository" "repositories" {
        id                                      = "www"
        name                                    = "www"
      ~ topics                                  = [
          + "cloudflare",
          + "pwa",
          + "s3",
          + "static-site",
            # (2 unchanged elements hidden)
        ]
        # (38 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

Plan: 0 to add, 14 to change, 0 to destroy.
OpenTofu will perform the following actions:

  # github_repository.repositories[".github"] will be updated in-place
  ~ resource "github_repository" "repositories" {
        id                                      = ".github"
        name                                    = ".github"
      ~ topics                                  = [
          + "community-health",
          + "org-profile",
            # (1 unchanged element hidden)
        ]
        # (37 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

  # github_repository.repositories["agent-knowledge"] will be updated in-place
  ~ resource "github_repository" "repositories" {
        id                                      = "agent-knowledge"
        name                                    = "agent-knowledge"
      ~ topics                                  = [
          + "agents",
          + "documentation",
          + "knowledge-base",
          + "opencode",
        ]
        # (34 unchanged attributes hidden)
    }

  # github_repository.repositories["charts"] will be updated in-place
  ~ resource "github_repository" "repositories" {
        id                                      = "charts"
        name                                    = "charts"
      ~ topics                                  = [
          + "ghcr",
          + "gitops",
          + "helm",
          + "helm-charts",
          + "oci",
        ]
        # (36 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

  # github_repository.repositories["images"] will be updated in-place
  ~ resource "github_repository" "repositories" {
        id                                      = "images"
        name                                    = "images"
      ~ topics                                  = [
          + "github-actions",
          + "opentofu",
            # (3 unchanged elements hidden)
        ]
        # (37 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

  # github_repository.repositories["kustomize-cluster"] will be updated in-place
  ~ resource "github_repository" "repositories" {
        id                                      = "kustomize-cluster"
        name                                    = "kustomize-cluster"
      ~ topics                                  = [
          + "app-of-apps",
          + "gitops",
          + "k3s",
[REDACTED: potentially sensitive plan output]
[REDACTED: potentially sensitive plan output]
            # (2 unchanged elements hidden)
        ]
        # (35 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

  # github_repository.repositories["shared-workflows"] will be updated in-place
  ~ resource "github_repository" "repositories" {
        id                                      = "shared-workflows"
        name                                    = "shared-workflows"
      ~ topics                                  = [
          + "github-actions",
          + "opentofu",
          + "reusable-workflows",
            # (2 unchanged elements hidden)
        ]
        # (36 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

  # github_repository.repositories["terraform-libvirt-domain"] will be updated in-place
  ~ resource "github_repository" "repositories" {
        id                                      = "terraform-libvirt-domain"
        name                                    = "terraform-libvirt-domain"
      ~ topics                                  = [
          + "cloud-init",
          + "libvirt",
            # (2 unchanged elements hidden)
        ]
        # (37 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

  # github_repository.repositories["tfroot-aws"] will be updated in-place
  ~ resource "github_repository" "repositories" {
        id                                      = "tfroot-aws"
        name                                    = "tfroot-aws"
      ~ topics                                  = [
          + "kms",
          + "opentofu",
            # (4 unchanged elements hidden)
        ]
        # (37 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

  # github_repository.repositories["tfroot-cloudflare"] will be updated in-place
  ~ resource "github_repository" "repositories" {
        id                                      = "tfroot-cloudflare"
        name                                    = "tfroot-cloudflare"
      ~ topics                                  = [
          + "cloudflare-access",
          + "cloudflare-tunnel",
          + "opentofu",
            # (4 unchanged elements hidden)
        ]
        # (37 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

  # github_repository.repositories["tfroot-gcp"] will be updated in-place
  ~ resource "github_repository" "repositories" {
        id                                      = "tfroot-gcp"
        name                                    = "tfroot-gcp"
      ~ topics                                  = [
          + "gcp",
          + "gcs-backend",
          + "kms",
          + "opentofu",
[REDACTED: potentially sensitive plan output]
          + "tfstate",
          + "workload-identity-federation",
        ]
        # (36 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

  # github_repository.repositories["tfroot-github"] will be updated in-place
  ~ resource "github_repository" "repositories" {
        id                                      = "tfroot-github"
        name                                    = "tfroot-github"
      ~ topics                                  = [
          - "github-provider-terraform",
          + "github-actions",
          + "opentofu",
          + "terraform-provider-github",
            # (3 unchanged elements hidden)
        ]
        # (37 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

  # github_repository.repositories["tfroot-libvirt"] will be updated in-place
  ~ resource "github_repository" "repositories" {
        id                                      = "tfroot-libvirt"
        name                                    = "tfroot-libvirt"
      ~ topics                                  = [
          - "ansible-automation-platform",
          + "cloud-init",
          + "k3s",
          + "libvirt",
          + "opentofu",
            # (4 unchanged elements hidden)
        ]
        # (37 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

  # github_repository.repositories["tfroot-namecheap"] will be updated in-place
  ~ resource "github_repository" "repositories" {
        id                                      = "tfroot-namecheap"
        name                                    = "tfroot-namecheap"
      ~ topics                                  = [
          + "cloudflare",
          + "dns",
          + "domains",
          + "namecheap",
          + "opentofu",
        ]
        # (36 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

  # github_repository.repositories["www"] will be updated in-place
  ~ resource "github_repository" "repositories" {
        id                                      = "www"
        name                                    = "www"
      ~ topics                                  = [
          + "cloudflare",
          + "pwa",
          + "s3",
          + "static-site",
            # (2 unchanged elements hidden)
        ]
        # (38 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

Plan: 0 to add, 14 to change, 0 to destroy.

@xnoto
xnoto merged commit eb056ff into main Sep 2, 2026
3 checks passed
@xnoto
xnoto deleted the manage-org-repository-topics branch September 2, 2026 16:01
xnoto added a commit that referenced this pull request Sep 2, 2026
Comment-only follow-up to #51 per owner feedback; no functional change, plan is a no-op.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant