diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json new file mode 100644 index 0000000..95743ec --- /dev/null +++ b/.codex-plugin/plugin.json @@ -0,0 +1,37 @@ +{ + "author": { + "name": "Anton Babenko", + "url": "https://github.com/antonbabenko" + }, + "description": "Terraform and OpenTofu best-practices skill for writing, reviewing, debugging, testing, CI, scans, and state operations.", + "homepage": "https://github.com/antonbabenko/terraform-skill", + "interface": { + "capabilities": [ + "Interactive", + "Read", + "Write" + ], + "category": "Development", + "developerName": "Anton Babenko", + "displayName": "Terraform Skill", + "longDescription": "Diagnose-first Terraform and OpenTofu guidance with version-aware guardrails for module design, testing strategy, CI/CD workflows, security scanning, compliance checks, and state management.", + "shortDescription": "Terraform and OpenTofu guidance for modules, tests, CI, scans, and state operations.", + "websiteURL": "https://github.com/antonbabenko/terraform-skill" + }, + "keywords": [ + "ci-cd", + "iac", + "infrastructure-as-code", + "modules", + "opentofu", + "security-scanning", + "state-management", + "terraform", + "testing" + ], + "license": "Apache-2.0", + "name": "terraform-skill", + "repository": "https://github.com/antonbabenko/terraform-skill", + "skills": "./skills", + "version": "1.12.0" +} diff --git a/.github/workflows/automated-release.yml b/.github/workflows/automated-release.yml index 909585f..aac8818 100644 --- a/.github/workflows/automated-release.yml +++ b/.github/workflows/automated-release.yml @@ -48,11 +48,11 @@ jobs: skip-version-file: 'false' skip-commit: 'false' - # 2b. Sync plugin version, SKILL.md version, and git ref - # This ensures all three version fields stay synchronized: - # - root version (updated by conventional-changelog-action above) - # - plugins[0].version (synced here) + # 2b. Sync version fields and git ref + # This ensures all version fields stay synchronized: + # - root version.json (updated by conventional-changelog-action above) # - SKILL.md metadata.version (synced here) + # - .codex-plugin/plugin.json version (synced here, if present) - name: Sync Plugin Version and SKILL.md if: steps.changelog.outputs.skipped == 'false' env: @@ -90,6 +90,40 @@ jobs: return skill_path + def update_codex_manifest(version): + """Mirror the version into .codex-plugin/plugin.json if present. + + The Codex plugin host reads this manifest when the + agent-plugins marketplace resolves terraform-skill as a + url-source plugin. Keep its version in lockstep with the + SKILL.md single version source so it never goes stale. + """ + manifest_path = '.codex-plugin/plugin.json' + + if not os.path.exists(manifest_path): + return None + + with open(manifest_path, 'r') as f: + lines = f.readlines() + + updated = False + for i, line in enumerate(lines): + # Top-level key only: exactly 2-space indent. Anchored so a + # nested "version" key could never be matched by accident. + if line.startswith(' "version":'): + lines[i] = f' "version": "{version}"\n' + updated = True + break + + if not updated: + raise ValueError( + "Could not find version field in .codex-plugin/plugin.json") + + with open(manifest_path, 'w') as f: + f.writelines(lines) + + return manifest_path + try: version = os.environ['VERSION'] @@ -97,6 +131,12 @@ jobs: skill_path = update_skill_version(version) print(f"✅ Synced {skill_path} metadata.version to {version}") + manifest_path = update_codex_manifest(version) + if manifest_path: + print(f"✅ Synced {manifest_path} version to {version}") + else: + print("ℹ️ No .codex-plugin/plugin.json; skipped") + except Exception as e: print(f"❌ ERROR: Failed to sync versions: {e}") sys.exit(1) @@ -106,6 +146,9 @@ jobs: git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" git add version.json skills/terraform-skill/SKILL.md + if [ -f .codex-plugin/plugin.json ]; then + git add .codex-plugin/plugin.json + fi git commit --amend --no-edit git push --force-with-lease diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index ac6eec3..4f711e2 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -5,11 +5,13 @@ on: paths: - 'skills/**' - '.claude-plugin/**' + - '.codex-plugin/**' push: branches: [master, main] paths: - 'skills/**' - '.claude-plugin/**' + - '.codex-plugin/**' workflow_dispatch: jobs: @@ -89,6 +91,51 @@ jobs: print(f"✅ Frontmatter valid ({desc_len} chars)") EOF + - name: Check Codex Manifest Version Sync + run: | + python3 << 'EOF' + import json + import os + import re + import sys + import yaml + + SEMVER = re.compile(r'^\d+\.\d+\.\d+$') + + manifest_path = '.codex-plugin/plugin.json' + if not os.path.exists(manifest_path): + print("ℹ️ No .codex-plugin/plugin.json; skipping sync check") + sys.exit(0) + + with open('skills/terraform-skill/SKILL.md') as f: + fm = yaml.safe_load(f.read().split('---', 2)[1]) + skill_version = (fm.get('metadata') or {}).get('version') + + with open(manifest_path) as f: + manifest_version = json.load(f).get('version') + + # Both must exist and be real semver, not merely equal: equality + # alone passes when both are missing/empty. + for label, value in ( + ('SKILL.md metadata.version', skill_version), + (f'{manifest_path} version', manifest_version), + ): + if not (isinstance(value, str) and SEMVER.match(value)): + print( + f"❌ ERROR: {label} is not a valid semver: {value!r}. " + f"CI owns these; do not hand-edit (see CLAUDE.md).") + sys.exit(1) + + if skill_version != manifest_version: + print( + f"❌ ERROR: version mismatch - SKILL.md metadata.version=" + f"{skill_version!r} vs {manifest_path}={manifest_version!r}. " + f"CI owns these; do not hand-edit (see CLAUDE.md).") + sys.exit(1) + + print(f"✅ Codex manifest version in sync ({manifest_version})") + EOF + - name: Check File Size run: | LINES=$(wc -l < skills/terraform-skill/SKILL.md) diff --git a/README.md b/README.md index 0c0a145..eff37cd 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,11 @@ git clone https://github.com/antonbabenko/terraform-skill.git ~/.agents/skills/t Codex auto-discovers skills from `~/.agents/skills/` and `.agents/skills/`. Update with `cd ~/.agents/skills/terraform-skill && git pull`. +For a managed Codex plugin install, use the `antonbabenko/agent-plugins` +marketplace (`codex plugin marketplace add antonbabenko/agent-plugins`, then +install `terraform-skill`). Do not add `antonbabenko/terraform-skill` as a +separate marketplace - it clashes by name with `agent-plugins`. +