Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,15 @@ for the architecture, the GitHub App credential setup, and the Observe-first ado
the create-only `initProvider`. upjet builds the Terraform configuration from `forProvider`, an
absent optional bool takes the provider's zero value of `false`, and `false` against a live `true`
is a permanent diff, so every update PATCH carries `web_commit_signoff_required: false` and GitHub
rejects the whole request with 422 "Commit signoff is enforced ... and cannot be disabled". The
error names disabling, not presence: declaring the live value leaves nothing to diff, so Terraform
omits the field from the payload and the update applies. `tests/repository-update-policy.sh` pins
this. Verify
rejects the whole request with 422 "Commit signoff is enforced ... and cannot be disabled".
Declaring the live value removes that field's own diff, but the deployed provider-upjet-github
v0.19.1 embeds terraform-provider-github v6.6.0 and still includes the field when another setting
needs an update; GitHub rejects the entire PATCH while organization signoff is enforced
([upstream #2077](https://github.com/integrations/terraform-provider-github/issues/2077)). Until a
provider-upjet-github release includes the upstream v6.12.0 fix, do not introduce new Repository
drift merely for optional discovery metadata; `agent-plugins` and `agent-skills` topics are
deliberately omitted. `tests/repository-update-policy.sh` pins both the required live signoff
value and this temporary compatibility boundary. Verify
the provider kind/field schema against the authoritative source
([crossplane-contrib/provider-upjet-github `package/crds/`](https://github.com/crossplane-contrib/provider-upjet-github)
+ `examples-generated/namespaced/`) — the CRs cannot be schema-validated locally (no cluster; CI
Expand Down
11 changes: 6 additions & 5 deletions deploy/repositories/agent-plugins.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ metadata:
crossplane.io/external-name: agent-plugins
spec:
# The squash-only merge policy and the org-enforced commit signoff come from
# the shared patch. Description and topics
# are declared here so the config is AUTHORITATIVE for them. Settings declared
# the shared patch. Description is declared here so the config is
# AUTHORITATIVE for it. Settings declared
# nowhere here keep the value adopted from the live repo when it was first
# observed.
managementPolicies:
Expand All @@ -25,9 +25,10 @@ spec:
# that no longer exists. Also names the bundled MCP servers and agents —
# the marketplace is deliberately not skills-only.
description: "Tool-neutral agent-plugin marketplace — bundles curated skills, MCP servers and agents from devantler-tech/agent-skills into category plugins for VS Code, GitHub Copilot CLI, and Claude Code"
# Discovery surface — the repo carried no topics at all, so it was invisible
# to every topic-based browse and directory crawl.
topics: ["agent-plugins", "agent-skills", "ai-agents", "claude-code", "claude-code-marketplace", "claude-code-plugin", "claude-plugin", "github-copilot", "plugin-marketplace", "vscode"]
# Topics stay temporarily unmanaged. provider-upjet-github v0.19.1 embeds
# terraform-provider-github v6.6.0, whose repository update includes the
# org-enforced web-signoff field and makes GitHub reject the whole PATCH.
# Restore topics after a provider release includes the upstream v6.12 fix.
providerConfigRef:
kind: ProviderConfig
name: default
13 changes: 6 additions & 7 deletions deploy/repositories/agent-skills.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ metadata:
crossplane.io/external-name: agent-skills
spec:
# The squash-only merge policy and the org-enforced commit signoff come from
# the shared patch. Description and topics
# are declared here so the config is AUTHORITATIVE for them. Settings declared
# the shared patch. Description is declared here so the config is
# AUTHORITATIVE for it. Settings declared
# nowhere here keep the value adopted from the live repo when it was first
# observed.
managementPolicies:
Expand All @@ -26,11 +26,10 @@ spec:
# either `gh skill install` or `npx skills add`. The old wording undersold
# that and read as Copilot-only.
description: "Agent-neutral skills for Claude Code, Copilot, Cursor and Codex — install with `gh skill install` or `npx skills add`"
# Discovery surface. `gh skill publish --tag` runs non-interactively in
# cd.yaml and therefore SKIPS the interactive "add the agent-skills topic"
# step, so the canonical topic was never applied — this declares it instead.
# The rest are the terms users actually browse/search by.
topics: ["agent-skills", "agent-skill", "ai-agents", "claude-code", "claude-skill", "codex", "cursor", "github-copilot", "gitops", "kubernetes", "skills"]
# Topics stay temporarily unmanaged. provider-upjet-github v0.19.1 embeds
# terraform-provider-github v6.6.0, whose repository update includes the
# org-enforced web-signoff field and makes GitHub reject the whole PATCH.
# Restore topics after a provider release includes the upstream v6.12 fix.
providerConfigRef:
kind: ProviderConfig
name: default
19 changes: 7 additions & 12 deletions deploy/repositories/kustomization.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,13 @@ resources:
# - squash-only merge policy (merge commits + rebase disabled; auto-merge,
# auto-delete head branches, always-suggest-updating-PR-branches enabled).
# - webCommitSignoffRequired: true — declared precisely BECAUSE the org enforces
# it. Leaving the field unconfigured is what breaks updates, not declaring it.
# upjet builds the Terraform configuration from forProvider, an absent optional
# bool takes the provider's zero value of false, and false against a live true
# is a permanent diff — so every update PATCH carries
# web_commit_signoff_required: false and GitHub rejects the whole request with
# 422 "Commit signoff is enforced ... and cannot be disabled". The error names
# disabling, not presence. Declaring the live value leaves nothing to diff, so
# Terraform omits the field from the payload and the rest of the update
# applies. Measured: with this line present, nine write-enabled repositories
# completed update PATCHes at 2026-07-27T04:34:5xZ; two minutes after it was
# removed the same repositories began failing on that 422, and seven were
# still failing on 2026-08-06. See #112.
# it. Leaving the field unconfigured creates a permanent false-versus-live-true
# diff. Declaring true removes that field's own diff, but the deployed
# provider-upjet-github v0.19.1 still includes the unchanged field whenever
# another Repository setting needs an update; GitHub rejects that PATCH under
# organization signoff enforcement. Until a provider release includes the
# terraform-provider-github v6.12.0 fix, optional Repository drift must remain
# absent as pinned by tests/repository-update-policy.sh. See upstream #2077.
# - hasDownloads: false — GitHub has removed the downloads feature and no
# longer returns the field, so status.atProvider never carries it. The
# Terraform provider still defaults it to true, so a spec holding that default
Expand Down
3 changes: 3 additions & 0 deletions deploy/repositories/platform-tenant-template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ spec:
# restoring the write path (#112) unable to change it.
visibility: public
hasIssues: true
# Matches live state and advances this MR's generation so Crossplane drops
# the failed asynchronous update queued before the provider workaround.
hasProjects: false
description: "Template for platform tenants on the devantler-tech platform — framework-agnostic CI/CD plumbing kept current via template-sync."
providerConfigRef:
kind: ProviderConfig
Expand Down
81 changes: 62 additions & 19 deletions tests/repository-update-policy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -59,25 +59,17 @@ unsafe_policies="$(
fail "active Repository resources must not pair Update with LateInitialize, must exclude Delete, and must Observe: $unsafe_policies"

# Every active repository must declare webCommitSignoffRequired: true in
# forProvider, because the org enforces commit signoff and live is therefore
# always true. Leaving the field unconfigured does NOT keep it out of the update
# payload: upjet builds the Terraform configuration from forProvider, an absent
# optional bool takes the provider's zero value of false, and false against a
# live true is a permanent diff — so every update PATCH carries
# web_commit_signoff_required: false and GitHub rejects the whole request with
# 422 "Commit signoff is enforced by the organization and cannot be disabled".
# Declaring the live value leaves nothing to diff, so Terraform omits the field
# from the payload and the rest of the update applies.
# forProvider because the org enforces commit signoff and live is therefore
# always true. Leaving the field unconfigured creates its own permanent false
# versus true diff. Declaring true removes that diff, but it does not make
# arbitrary updates safe with the deployed provider: v0.19.1 embeds
# terraform-provider-github v6.6.0, which still includes the unchanged field
# when another Repository setting changes, and GitHub rejects that PATCH.
# The compatibility guard below therefore also prevents the known optional
# topic drift until a provider release includes the upstream v6.12.0 fix.
#
# The 422 names disabling, not presence, and the cluster agrees: at
# 2026-07-27T04:34:5xZ, with this field declared by the shared patch, nine
# write-enabled repositories recorded LastAsyncOperation=Success — completed
# update PATCHes. Two minutes after the declaration was removed the same
# repositories began recording AsyncUpdateFailure carrying that 422, and seven
# were still failing on 2026-08-06. See devantler-tech/.github#112.
#
# initProvider is not a substitute: Crossplane applies it only at creation, so
# forProvider stays unconfigured and the permanent diff above is unchanged.
# initProvider is not a substitute for the required live declaration:
# Crossplane applies it only at creation, so forProvider would remain false.
missing_signoff="$(
yq -N '
select(
Expand Down Expand Up @@ -123,4 +115,55 @@ platform_tenant_issues="$(
[[ "$platform_tenant_issues" == "true" ]] ||
fail "platform-tenant-template must declare forProvider.hasIssues: true so its issue roadmap remains available"

echo "repository-update-policy: OK — $active_count active repositories declare org-enforced signoff and the platform-tenant-template issue tracker"
# provider-upjet-github v0.19.1 embeds terraform-provider-github v6.6.0.
# GitHub rejects every repository PATCH containing web_commit_signoff_required
# while the organization enforces that setting, even when the requested value
# is true (integrations/terraform-provider-github#2077). Topic-only drift on
# these two resources therefore wedges the complete update. Keep the attempted
# topics absent until a provider-upjet-github release includes the upstream
# v6.12.0 fix, which omits the field when it is unchanged.
compatibility_repositories="$(
yq -N '
select(
.kind == "Repository" and
.spec.forProvider.archived != true and
(.metadata.name == "agent-plugins" or .metadata.name == "agent-skills")
) |
.metadata.name
' "$render" | sort
)"
expected_compatibility_repositories="$(printf '%s\n' agent-plugins agent-skills)"
[[ "$compatibility_repositories" == "$expected_compatibility_repositories" ]] ||
fail "provider compatibility guard requires both agent repositories; got: $compatibility_repositories"

blocked_topic_updates="$(
yq -N '
select(
.kind == "Repository" and
(.metadata.name == "agent-plugins" or .metadata.name == "agent-skills") and
(.spec.forProvider | has("topics"))
) |
.metadata.name
' "$render"
)"
[[ -z "$blocked_topic_updates" ]] ||
fail "provider-upjet-github v0.19.1 cannot update repository topics under org-enforced signoff: $blocked_topic_updates"

# Pin a matching live value to advance this resource's generation and cancel
# the failed async update which predates the signoff workaround. The template
# intentionally does not use GitHub Projects.
platform_tenant_projects="$(
yq -N '
select(
.kind == "Repository" and
.metadata.name == "platform-tenant-template"
) |
.spec.forProvider.hasProjects |
select(tag == "!!bool") |
select(. == false)
' "$render"
)"
[[ "$platform_tenant_projects" == "false" ]] ||
fail "platform-tenant-template must pin forProvider.hasProjects: false while resetting its failed async update"

echo "repository-update-policy: OK — $active_count active repositories declare org-enforced signoff and the provider-v0.19.1 update workaround"
Loading