Skip to content

Commit 89fabec

Browse files
committed
chore(release): 0.4.3
1 parent 761e03f commit 89fabec

13 files changed

Lines changed: 141 additions & 20 deletions

File tree

.github/actions/setup-codex-runtime/action.yml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,54 @@ runs:
6060
exit 1
6161
fi
6262
63+
- name: Install pinned GitHub MCP server
64+
if: inputs.install-codex == 'true'
65+
shell: bash
66+
run: |
67+
set -euo pipefail
68+
# shellcheck source=config/mcp-runtime-versions.env
69+
. config/mcp-runtime-versions.env
70+
: "${GITHUB_MCP_SERVER_VERSION:?GITHUB_MCP_SERVER_VERSION is required}"
71+
case "$RUNNER_OS" in
72+
Linux) os_name="Linux" ;;
73+
macOS) os_name="Darwin" ;;
74+
*)
75+
echo "Unsupported runner OS for github-mcp-server: $RUNNER_OS" >&2
76+
exit 1
77+
;;
78+
esac
79+
case "$(uname -m)" in
80+
x86_64|amd64) arch_name="x86_64" ;;
81+
arm64|aarch64) arch_name="arm64" ;;
82+
i386|i686) arch_name="i386" ;;
83+
*)
84+
echo "Unsupported runner architecture for github-mcp-server: $(uname -m)" >&2
85+
exit 1
86+
;;
87+
esac
88+
archive="github-mcp-server_${os_name}_${arch_name}.tar.gz"
89+
base_url="https://github.com/github/github-mcp-server/releases/download/v${GITHUB_MCP_SERVER_VERSION}"
90+
tmp_dir="$(mktemp -d)"
91+
install_dir="${RUNNER_TEMP:-$tmp_dir}/rldyour-bin"
92+
mkdir -p "$install_dir"
93+
curl -fsSL "${base_url}/${archive}" -o "${tmp_dir}/${archive}"
94+
curl -fsSL "${base_url}/github-mcp-server_${GITHUB_MCP_SERVER_VERSION}_checksums.txt" -o "${tmp_dir}/checksums.txt"
95+
grep " ${archive}$" "${tmp_dir}/checksums.txt" > "${tmp_dir}/checksums.selected"
96+
if [ "$RUNNER_OS" = "Linux" ]; then
97+
(cd "$tmp_dir" && sha256sum -c checksums.selected)
98+
else
99+
expected_sha="$(awk '{print $1}' "${tmp_dir}/checksums.selected")"
100+
actual_sha="$(shasum -a 256 "${tmp_dir}/${archive}" | awk '{print $1}')"
101+
if [ "$expected_sha" != "$actual_sha" ]; then
102+
echo "github-mcp-server checksum mismatch" >&2
103+
exit 1
104+
fi
105+
fi
106+
tar -xzf "${tmp_dir}/${archive}" -C "$tmp_dir"
107+
install -m 0755 "${tmp_dir}/github-mcp-server" "${install_dir}/github-mcp-server"
108+
echo "$install_dir" >> "$GITHUB_PATH"
109+
"${install_dir}/github-mcp-server" --version || "${install_dir}/github-mcp-server" --help >/dev/null
110+
63111
- name: Install pinned Codex CLI
64112
if: inputs.install-codex == 'true'
65113
shell: bash

CHANGELOG.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,26 @@ The format follows Keep a Changelog, and marketplace/plugin versions follow Sema
66

77
## [Unreleased]
88

9+
## [0.4.3] - 2026-05-21
10+
11+
### Changed
12+
13+
- GitHub Actions runtime setup now installs the pinned official GitHub MCP
14+
server `1.0.5` from the upstream release artifact with SHA-256 checksum
15+
verification before strict Codex runtime validation runs.
16+
- Context7 MCP is updated from `2.2.5` to the current stable `2.3.0` in both
17+
`.mcp.json` and `config/mcp-runtime-versions.env`.
18+
- MCP runtime freshness now tracks `GITHUB_MCP_SERVER_VERSION` through the
19+
GitHub releases API alongside npm and PyPI-backed runtime pins.
20+
21+
### Fixed
22+
23+
- `scripts/validate_contract.py` now narrows JSON-derived values before sorting
24+
or passing them to typed helpers, keeping Pyright strict checks green under
25+
the public `security-static` workflow.
26+
- Public Codex CI no longer fails strict runtime prerequisite checks because the
27+
`github` MCP server launcher is absent on hosted Ubuntu/macOS runners.
28+
929
## [0.4.2] - 2026-05-21
1030

1131
### Added

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,9 @@ The Codex adapter contract lives in `config/rldyour-contract.json` and is docume
122122

123123
`plugins/rldyour-mcps/.mcp.json` is the portable source of truth for MCP server definitions. The installer resolves portable commands such as `uvx`, `bunx`, and `dart` to local executable paths in `~/.codex/config.toml`; `scripts/validate_marketplace.sh` checks that the installed MCP config still matches `.mcp.json` apart from that expected command-path resolution.
124124

125-
Local MCP launcher packages are pinned in `.mcp.json` and mirrored in `config/mcp-runtime-versions.env`; marketplace validation fails if these sources drift. Do not use `@latest` or unpinned `uvx --from` package specs for local MCP runtime definitions; update versions intentionally and rerun capability smoke.
125+
Local MCP launcher packages are pinned in `.mcp.json` and mirrored in `config/mcp-runtime-versions.env`; marketplace validation fails if these sources drift. Do not use `@latest` or unpinned `uvx --from` package specs for local MCP runtime definitions; update versions intentionally and rerun capability smoke. The official GitHub MCP server binary is pinned separately as `GITHUB_MCP_SERVER_VERSION` and installed by CI from the matching upstream release artifact with checksum verification.
126126

127-
`dart-flutter` is intentionally declared as an external local Dart SDK runtime in `config/mcp-runtime-versions.env`; all other local package launchers remain package-pinned. Host runtime pins for Node major, Bun, and Dart SDK also live in this file so local setup, GitHub Actions, and the devcontainer share one source of truth. GitHub Actions workflows pin external actions to full commit SHAs, with the source tag kept as an inline comment for review.
127+
`dart-flutter` is intentionally declared as an external local Dart SDK runtime in `config/mcp-runtime-versions.env`; all other local package launchers remain package-pinned or binary-release-pinned. Host runtime pins for Node major, Bun, Dart SDK, and GitHub MCP also live in this file so local setup, GitHub Actions, and the devcontainer share one source of truth. GitHub Actions workflows pin external actions to full commit SHAs, with the source tag kept as an inline comment for review.
128128

129129
Strict runtime mode is available when the environment must be fully reproducible instead of warning-only:
130130

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.4.2
1+
0.4.3

config/mcp-runtime-versions.env

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@ NODE_MAJOR_VERSION=24
66
BUN_VERSION=1.3.14
77
DART_SDK_VERSION=3.11.0
88
CODEX_CLI_VERSION=0.132.0
9+
GITHUB_MCP_SERVER_VERSION=1.0.5
910
MCP_PYTHON_SDK_VERSION=1.27.1
1011
SERENA_AGENT_VERSION=1.5.1
1112
SEMGREP_VERSION=1.163.0
1213
SEQUENTIAL_THINKING_MCP_VERSION=2025.12.18
1314
PLAYWRIGHT_MCP_VERSION=0.0.75
1415
CHROME_DEVTOOLS_MCP_VERSION=1.0.1
15-
CONTEXT7_MCP_VERSION=2.2.5
16+
CONTEXT7_MCP_VERSION=2.3.0
1617
SHADCN_VERSION=4.7.0

plugins/rldyour-mcps/.mcp.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
"context7": {
5858
"command": "bunx",
5959
"args": [
60-
"@upstash/context7-mcp@2.2.5"
60+
"@upstash/context7-mcp@2.3.0"
6161
],
6262
"env_vars": [
6363
"CONTEXT7_API_KEY"

plugins/rldyour-mcps/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,11 @@ The server is restricted to `context,repos,issues,pull_requests,users` so
8686
repository, issue, and PR workflows have parity without exposing every GitHub
8787
toolset by default.
8888

89+
The CI/runtime pin for this binary is `GITHUB_MCP_SERVER_VERSION` in
90+
`config/mcp-runtime-versions.env`. GitHub Actions installs the matching release
91+
archive from `github/github-mcp-server` and verifies its published SHA-256
92+
checksum before strict runtime validation.
93+
8994
## Codex Verification
9095

9196
After installing or updating the plugin, check:

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "rldyour-codex"
3-
version = "0.4.2"
3+
version = "0.4.3"
44
description = "Personal Codex marketplace with rldyour plugins, MCP servers, skills, hooks, and SDLC tooling."
55
readme = "README.md"
66
requires-python = ">=3.13,<3.14"

scripts/check_mcp_runtime_versions.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class Pin:
2323

2424
PINS: tuple[Pin, ...] = (
2525
Pin("CODEX_CLI_VERSION", "npm", "@openai/codex"),
26+
Pin("GITHUB_MCP_SERVER_VERSION", "github-release", "github/github-mcp-server"),
2627
Pin("BUN_VERSION", "npm", "bun"),
2728
Pin("MCP_PYTHON_SDK_VERSION", "pypi", "mcp"),
2829
Pin("SERENA_AGENT_VERSION", "pypi", "serena-agent"),
@@ -72,11 +73,24 @@ def pypi_latest(package: str) -> str:
7273
return version
7374

7475

76+
def github_release_latest(repository: str) -> str:
77+
url = f"https://api.github.com/repos/{repository}/releases/latest"
78+
# PINS is a repository allowlist, so repository cannot be user-controlled.
79+
with urllib.request.urlopen(url, timeout=45) as response: # nosemgrep: python.lang.security.audit.dynamic-urllib-use-detected.dynamic-urllib-use-detected
80+
data = json.load(response)
81+
tag = data.get("tag_name")
82+
if not isinstance(tag, str) or not tag:
83+
raise RuntimeError(f"unexpected GitHub releases response for {repository}")
84+
return tag.removeprefix("v")
85+
86+
7587
def latest_for(pin: Pin) -> str:
7688
if pin.ecosystem == "npm":
7789
return npm_latest(pin.package)
7890
if pin.ecosystem == "pypi":
7991
return pypi_latest(pin.package)
92+
if pin.ecosystem == "github-release":
93+
return github_release_latest(pin.package)
8094
raise RuntimeError(f"unsupported ecosystem: {pin.ecosystem}")
8195

8296

scripts/validate_contract.py

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,18 @@ def main() -> int:
8080
errors.append("config/rldyour-contract.json: schema_version must be 1")
8181

8282
marketplace = load_json(ROOT / ".agents/plugins/marketplace.json")
83-
marketplace_plugins = marketplace.get("plugins") if isinstance(marketplace, dict) else None
84-
actual_plugins = sorted(
85-
entry.get("name")
86-
for entry in marketplace_plugins or []
87-
if isinstance(entry, dict) and isinstance(entry.get("name"), str)
88-
)
89-
expected_plugins = sorted(contract.get("plugins") or [])
83+
marketplace_plugins_raw = marketplace.get("plugins") if isinstance(marketplace, dict) else []
84+
marketplace_plugins = marketplace_plugins_raw if isinstance(marketplace_plugins_raw, list) else []
85+
actual_plugins: list[str] = []
86+
for entry in marketplace_plugins:
87+
if not isinstance(entry, dict):
88+
continue
89+
name = entry.get("name")
90+
if isinstance(name, str):
91+
actual_plugins.append(name)
92+
actual_plugins.sort()
93+
expected_plugins_raw = contract.get("plugins")
94+
expected_plugins = sorted(item for item in expected_plugins_raw if isinstance(item, str)) if isinstance(expected_plugins_raw, list) else []
9095
if actual_plugins != expected_plugins:
9196
errors.append(f"plugins: expected {expected_plugins}, got {actual_plugins}")
9297

@@ -183,17 +188,21 @@ def main() -> int:
183188
if not isinstance(dispatcher, dict):
184189
errors.append(f"hooks.lifecycles.{lifecycle_id}: unknown dispatcher {dispatched_by!r}")
185190
continue
186-
plugin = mapping.get("plugin")
187-
event = mapping.get("event")
188-
needle = mapping.get("command_contains")
189-
if not all(isinstance(item, str) for item in (plugin, event, needle)):
191+
plugin_raw = mapping.get("plugin")
192+
event_raw = mapping.get("event")
193+
needle_raw = mapping.get("command_contains")
194+
if not isinstance(plugin_raw, str) or not isinstance(event_raw, str) or not isinstance(needle_raw, str):
190195
errors.append(f"hooks.lifecycles.{lifecycle_id}: plugin/event/command_contains must be strings")
191196
continue
197+
plugin = plugin_raw
198+
event = event_raw
199+
needle = needle_raw
192200
if not command_for_event(plugin, event, needle):
193201
errors.append(f"hooks.lifecycles.{lifecycle_id}: {plugin} {event} missing command {needle}")
194202

195203
mcp_contract = contract.get("mcp")
196-
expected_servers = sorted(mcp_contract.get("servers") if isinstance(mcp_contract, dict) else [])
204+
expected_servers_raw = mcp_contract.get("servers") if isinstance(mcp_contract, dict) else []
205+
expected_servers = sorted(item for item in expected_servers_raw if isinstance(item, str)) if isinstance(expected_servers_raw, list) else []
197206
mcp_data = load_json(ROOT / "plugins/rldyour-mcps/.mcp.json")
198207
actual_servers = sorted((mcp_data.get("mcpServers") or {}).keys()) if isinstance(mcp_data, dict) else []
199208
if actual_servers != expected_servers:

0 commit comments

Comments
 (0)