mirror of
https://github.com/openai/codex.git
synced 2026-03-05 21:45:28 +03:00
Historically, we cloned the Bash repo from https://github.com/bminor/bash, but for whatever reason, it was removed at some point. I had a local clone of it, so I pushed it to https://github.com/bolinfest/bash so that we could continue running our CI job. I did this in https://github.com/openai/codex/pull/9563, and as you can see, I did not tamper with the commit hash we used as the basis of this build. Using a personal fork is not great, so this PR changes the CI job to use what appears to be considered the source of truth for Bash, which is https://git.savannah.gnu.org/git/bash.git. Though in testing this out, it appears this Git server does not support the combination of `git clone --depth 1 https://git.savannah.gnu.org/git/bash` and `git fetch --depth 1 origin a8a1c2fac029404d3f42cd39f5a20f24b6e4fe4b`, as it fails with the following error: ``` error: Server does not allow request for unadvertised object a8a1c2fac029404d3f42cd39f5a20f24b6e4fe4b ``` so unfortunately this means that we have to do a full clone instead of a shallow clone in our CI jobs, which will be a bit slower. Also updated `codex-rs/shell-escalation/README.md` to reflect this change.
549 lines
18 KiB
YAML
549 lines
18 KiB
YAML
name: shell-tool-mcp
|
|
|
|
on:
|
|
workflow_call:
|
|
inputs:
|
|
release-version:
|
|
description: Version to publish (x.y.z or x.y.z-alpha.N). Defaults to GITHUB_REF_NAME when it starts with rust-v.
|
|
required: false
|
|
type: string
|
|
release-tag:
|
|
description: Tag name to use when downloading release artifacts (defaults to rust-v<version>).
|
|
required: false
|
|
type: string
|
|
publish:
|
|
description: Whether to publish to npm when the version is releasable.
|
|
required: false
|
|
default: true
|
|
type: boolean
|
|
|
|
env:
|
|
NODE_VERSION: 22
|
|
|
|
jobs:
|
|
metadata:
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
version: ${{ steps.compute.outputs.version }}
|
|
release_tag: ${{ steps.compute.outputs.release_tag }}
|
|
should_publish: ${{ steps.compute.outputs.should_publish }}
|
|
npm_tag: ${{ steps.compute.outputs.npm_tag }}
|
|
steps:
|
|
- name: Compute version and tags
|
|
id: compute
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
version="${{ inputs.release-version }}"
|
|
release_tag="${{ inputs.release-tag }}"
|
|
|
|
if [[ -z "$version" ]]; then
|
|
if [[ -n "$release_tag" && "$release_tag" =~ ^rust-v.+ ]]; then
|
|
version="${release_tag#rust-v}"
|
|
elif [[ "${GITHUB_REF_NAME:-}" =~ ^rust-v.+ ]]; then
|
|
version="${GITHUB_REF_NAME#rust-v}"
|
|
release_tag="${GITHUB_REF_NAME}"
|
|
else
|
|
echo "release-version is required when GITHUB_REF_NAME is not a rust-v tag."
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
if [[ -z "$release_tag" ]]; then
|
|
release_tag="rust-v${version}"
|
|
fi
|
|
|
|
npm_tag=""
|
|
should_publish="false"
|
|
if [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
should_publish="true"
|
|
elif [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+-alpha\.[0-9]+$ ]]; then
|
|
should_publish="true"
|
|
npm_tag="alpha"
|
|
fi
|
|
|
|
echo "version=${version}" >> "$GITHUB_OUTPUT"
|
|
echo "release_tag=${release_tag}" >> "$GITHUB_OUTPUT"
|
|
echo "npm_tag=${npm_tag}" >> "$GITHUB_OUTPUT"
|
|
echo "should_publish=${should_publish}" >> "$GITHUB_OUTPUT"
|
|
|
|
bash-linux:
|
|
name: Build Bash (Linux) - ${{ matrix.variant }} - ${{ matrix.target }}
|
|
needs: metadata
|
|
runs-on: ${{ matrix.runner }}
|
|
timeout-minutes: 30
|
|
container:
|
|
image: ${{ matrix.image }}
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- runner: ubuntu-24.04
|
|
target: x86_64-unknown-linux-musl
|
|
variant: ubuntu-24.04
|
|
image: ubuntu:24.04
|
|
- runner: ubuntu-24.04
|
|
target: x86_64-unknown-linux-musl
|
|
variant: ubuntu-22.04
|
|
image: ubuntu:22.04
|
|
- runner: ubuntu-24.04
|
|
target: x86_64-unknown-linux-musl
|
|
variant: debian-12
|
|
image: debian:12
|
|
- runner: ubuntu-24.04
|
|
target: x86_64-unknown-linux-musl
|
|
variant: debian-11
|
|
image: debian:11
|
|
- runner: ubuntu-24.04
|
|
target: x86_64-unknown-linux-musl
|
|
variant: centos-9
|
|
image: quay.io/centos/centos:stream9
|
|
- runner: ubuntu-24.04-arm
|
|
target: aarch64-unknown-linux-musl
|
|
variant: ubuntu-24.04
|
|
image: arm64v8/ubuntu:24.04
|
|
- runner: ubuntu-24.04-arm
|
|
target: aarch64-unknown-linux-musl
|
|
variant: ubuntu-22.04
|
|
image: arm64v8/ubuntu:22.04
|
|
- runner: ubuntu-24.04-arm
|
|
target: aarch64-unknown-linux-musl
|
|
variant: ubuntu-20.04
|
|
image: arm64v8/ubuntu:20.04
|
|
- runner: ubuntu-24.04-arm
|
|
target: aarch64-unknown-linux-musl
|
|
variant: debian-12
|
|
image: arm64v8/debian:12
|
|
- runner: ubuntu-24.04-arm
|
|
target: aarch64-unknown-linux-musl
|
|
variant: debian-11
|
|
image: arm64v8/debian:11
|
|
- runner: ubuntu-24.04-arm
|
|
target: aarch64-unknown-linux-musl
|
|
variant: centos-9
|
|
image: quay.io/centos/centos:stream9
|
|
steps:
|
|
- name: Install build prerequisites
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
if command -v apt-get >/dev/null 2>&1; then
|
|
apt-get update
|
|
DEBIAN_FRONTEND=noninteractive apt-get install -y git build-essential bison autoconf gettext libncursesw5-dev
|
|
elif command -v dnf >/dev/null 2>&1; then
|
|
dnf install -y git gcc gcc-c++ make bison autoconf gettext ncurses-devel
|
|
elif command -v yum >/dev/null 2>&1; then
|
|
yum install -y git gcc gcc-c++ make bison autoconf gettext ncurses-devel
|
|
else
|
|
echo "Unsupported package manager in container"
|
|
exit 1
|
|
fi
|
|
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v6
|
|
|
|
- name: Build patched Bash
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
git clone https://git.savannah.gnu.org/git/bash /tmp/bash
|
|
cd /tmp/bash
|
|
git checkout a8a1c2fac029404d3f42cd39f5a20f24b6e4fe4b
|
|
git apply "${GITHUB_WORKSPACE}/shell-tool-mcp/patches/bash-exec-wrapper.patch"
|
|
./configure --without-bash-malloc
|
|
cores="$(command -v nproc >/dev/null 2>&1 && nproc || getconf _NPROCESSORS_ONLN)"
|
|
make -j"${cores}"
|
|
|
|
dest="${GITHUB_WORKSPACE}/artifacts/vendor/${{ matrix.target }}/bash/${{ matrix.variant }}"
|
|
mkdir -p "$dest"
|
|
cp bash "$dest/bash"
|
|
|
|
- uses: actions/upload-artifact@v6
|
|
with:
|
|
name: shell-tool-mcp-bash-${{ matrix.target }}-${{ matrix.variant }}
|
|
path: artifacts/**
|
|
if-no-files-found: error
|
|
|
|
bash-darwin:
|
|
name: Build Bash (macOS) - ${{ matrix.variant }} - ${{ matrix.target }}
|
|
needs: metadata
|
|
runs-on: ${{ matrix.runner }}
|
|
timeout-minutes: 30
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- runner: macos-15-xlarge
|
|
target: aarch64-apple-darwin
|
|
variant: macos-15
|
|
- runner: macos-14
|
|
target: aarch64-apple-darwin
|
|
variant: macos-14
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v6
|
|
|
|
- name: Build patched Bash
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
git clone https://git.savannah.gnu.org/git/bash /tmp/bash
|
|
cd /tmp/bash
|
|
git checkout a8a1c2fac029404d3f42cd39f5a20f24b6e4fe4b
|
|
git apply "${GITHUB_WORKSPACE}/shell-tool-mcp/patches/bash-exec-wrapper.patch"
|
|
./configure --without-bash-malloc
|
|
cores="$(getconf _NPROCESSORS_ONLN)"
|
|
make -j"${cores}"
|
|
|
|
dest="${GITHUB_WORKSPACE}/artifacts/vendor/${{ matrix.target }}/bash/${{ matrix.variant }}"
|
|
mkdir -p "$dest"
|
|
cp bash "$dest/bash"
|
|
|
|
- uses: actions/upload-artifact@v6
|
|
with:
|
|
name: shell-tool-mcp-bash-${{ matrix.target }}-${{ matrix.variant }}
|
|
path: artifacts/**
|
|
if-no-files-found: error
|
|
|
|
zsh-linux:
|
|
name: Build zsh (Linux) - ${{ matrix.variant }} - ${{ matrix.target }}
|
|
needs: metadata
|
|
runs-on: ${{ matrix.runner }}
|
|
timeout-minutes: 30
|
|
container:
|
|
image: ${{ matrix.image }}
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- runner: ubuntu-24.04
|
|
target: x86_64-unknown-linux-musl
|
|
variant: ubuntu-24.04
|
|
image: ubuntu:24.04
|
|
- runner: ubuntu-24.04
|
|
target: x86_64-unknown-linux-musl
|
|
variant: ubuntu-22.04
|
|
image: ubuntu:22.04
|
|
- runner: ubuntu-24.04
|
|
target: x86_64-unknown-linux-musl
|
|
variant: debian-12
|
|
image: debian:12
|
|
- runner: ubuntu-24.04
|
|
target: x86_64-unknown-linux-musl
|
|
variant: debian-11
|
|
image: debian:11
|
|
- runner: ubuntu-24.04
|
|
target: x86_64-unknown-linux-musl
|
|
variant: centos-9
|
|
image: quay.io/centos/centos:stream9
|
|
- runner: ubuntu-24.04-arm
|
|
target: aarch64-unknown-linux-musl
|
|
variant: ubuntu-24.04
|
|
image: arm64v8/ubuntu:24.04
|
|
- runner: ubuntu-24.04-arm
|
|
target: aarch64-unknown-linux-musl
|
|
variant: ubuntu-22.04
|
|
image: arm64v8/ubuntu:22.04
|
|
- runner: ubuntu-24.04-arm
|
|
target: aarch64-unknown-linux-musl
|
|
variant: ubuntu-20.04
|
|
image: arm64v8/ubuntu:20.04
|
|
- runner: ubuntu-24.04-arm
|
|
target: aarch64-unknown-linux-musl
|
|
variant: debian-12
|
|
image: arm64v8/debian:12
|
|
- runner: ubuntu-24.04-arm
|
|
target: aarch64-unknown-linux-musl
|
|
variant: debian-11
|
|
image: arm64v8/debian:11
|
|
- runner: ubuntu-24.04-arm
|
|
target: aarch64-unknown-linux-musl
|
|
variant: centos-9
|
|
image: quay.io/centos/centos:stream9
|
|
steps:
|
|
- name: Install build prerequisites
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
if command -v apt-get >/dev/null 2>&1; then
|
|
apt-get update
|
|
DEBIAN_FRONTEND=noninteractive apt-get install -y git build-essential bison autoconf gettext libncursesw5-dev
|
|
elif command -v dnf >/dev/null 2>&1; then
|
|
dnf install -y git gcc gcc-c++ make bison autoconf gettext ncurses-devel
|
|
elif command -v yum >/dev/null 2>&1; then
|
|
yum install -y git gcc gcc-c++ make bison autoconf gettext ncurses-devel
|
|
else
|
|
echo "Unsupported package manager in container"
|
|
exit 1
|
|
fi
|
|
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v6
|
|
|
|
- name: Build patched zsh
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
git clone https://git.code.sf.net/p/zsh/code /tmp/zsh
|
|
cd /tmp/zsh
|
|
git checkout 77045ef899e53b9598bebc5a41db93a548a40ca6
|
|
git apply "${GITHUB_WORKSPACE}/shell-tool-mcp/patches/zsh-exec-wrapper.patch"
|
|
./Util/preconfig
|
|
./configure
|
|
cores="$(command -v nproc >/dev/null 2>&1 && nproc || getconf _NPROCESSORS_ONLN)"
|
|
make -j"${cores}"
|
|
|
|
dest="${GITHUB_WORKSPACE}/artifacts/vendor/${{ matrix.target }}/zsh/${{ matrix.variant }}"
|
|
mkdir -p "$dest"
|
|
cp Src/zsh "$dest/zsh"
|
|
|
|
- name: Smoke test zsh exec wrapper
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
tmpdir="$(mktemp -d)"
|
|
cat > "$tmpdir/exec-wrapper" <<'EOF'
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
: "${CODEX_WRAPPER_LOG:?missing CODEX_WRAPPER_LOG}"
|
|
printf '%s\n' "$@" > "$CODEX_WRAPPER_LOG"
|
|
file="$1"
|
|
shift
|
|
if [[ "$#" -eq 0 ]]; then
|
|
exec "$file"
|
|
fi
|
|
arg0="$1"
|
|
shift
|
|
exec -a "$arg0" "$file" "$@"
|
|
EOF
|
|
chmod +x "$tmpdir/exec-wrapper"
|
|
|
|
CODEX_WRAPPER_LOG="$tmpdir/wrapper.log" \
|
|
EXEC_WRAPPER="$tmpdir/exec-wrapper" \
|
|
/tmp/zsh/Src/zsh -fc '/bin/echo smoke-zsh' > "$tmpdir/stdout.txt"
|
|
|
|
grep -Fx "smoke-zsh" "$tmpdir/stdout.txt"
|
|
grep -Fx "/bin/echo" "$tmpdir/wrapper.log"
|
|
|
|
- uses: actions/upload-artifact@v6
|
|
with:
|
|
name: shell-tool-mcp-zsh-${{ matrix.target }}-${{ matrix.variant }}
|
|
path: artifacts/**
|
|
if-no-files-found: error
|
|
|
|
zsh-darwin:
|
|
name: Build zsh (macOS) - ${{ matrix.variant }} - ${{ matrix.target }}
|
|
needs: metadata
|
|
runs-on: ${{ matrix.runner }}
|
|
timeout-minutes: 30
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- runner: macos-15-xlarge
|
|
target: aarch64-apple-darwin
|
|
variant: macos-15
|
|
- runner: macos-14
|
|
target: aarch64-apple-darwin
|
|
variant: macos-14
|
|
steps:
|
|
- name: Install build prerequisites
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
if ! command -v autoconf >/dev/null 2>&1; then
|
|
brew install autoconf
|
|
fi
|
|
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v6
|
|
|
|
- name: Build patched zsh
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
git clone https://git.code.sf.net/p/zsh/code /tmp/zsh
|
|
cd /tmp/zsh
|
|
git checkout 77045ef899e53b9598bebc5a41db93a548a40ca6
|
|
git apply "${GITHUB_WORKSPACE}/shell-tool-mcp/patches/zsh-exec-wrapper.patch"
|
|
./Util/preconfig
|
|
./configure
|
|
cores="$(getconf _NPROCESSORS_ONLN)"
|
|
make -j"${cores}"
|
|
|
|
dest="${GITHUB_WORKSPACE}/artifacts/vendor/${{ matrix.target }}/zsh/${{ matrix.variant }}"
|
|
mkdir -p "$dest"
|
|
cp Src/zsh "$dest/zsh"
|
|
|
|
- name: Smoke test zsh exec wrapper
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
tmpdir="$(mktemp -d)"
|
|
cat > "$tmpdir/exec-wrapper" <<'EOF'
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
: "${CODEX_WRAPPER_LOG:?missing CODEX_WRAPPER_LOG}"
|
|
printf '%s\n' "$@" > "$CODEX_WRAPPER_LOG"
|
|
file="$1"
|
|
shift
|
|
if [[ "$#" -eq 0 ]]; then
|
|
exec "$file"
|
|
fi
|
|
arg0="$1"
|
|
shift
|
|
exec -a "$arg0" "$file" "$@"
|
|
EOF
|
|
chmod +x "$tmpdir/exec-wrapper"
|
|
|
|
CODEX_WRAPPER_LOG="$tmpdir/wrapper.log" \
|
|
EXEC_WRAPPER="$tmpdir/exec-wrapper" \
|
|
/tmp/zsh/Src/zsh -fc '/bin/echo smoke-zsh' > "$tmpdir/stdout.txt"
|
|
|
|
grep -Fx "smoke-zsh" "$tmpdir/stdout.txt"
|
|
grep -Fx "/bin/echo" "$tmpdir/wrapper.log"
|
|
|
|
- uses: actions/upload-artifact@v6
|
|
with:
|
|
name: shell-tool-mcp-zsh-${{ matrix.target }}-${{ matrix.variant }}
|
|
path: artifacts/**
|
|
if-no-files-found: error
|
|
|
|
package:
|
|
name: Package npm module
|
|
needs:
|
|
- metadata
|
|
- bash-linux
|
|
- bash-darwin
|
|
- zsh-linux
|
|
- zsh-darwin
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
PACKAGE_VERSION: ${{ needs.metadata.outputs.version }}
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v6
|
|
|
|
- name: Setup pnpm
|
|
uses: pnpm/action-setup@v4
|
|
with:
|
|
run_install: false
|
|
|
|
- name: Setup Node.js
|
|
uses: actions/setup-node@v6
|
|
with:
|
|
node-version: ${{ env.NODE_VERSION }}
|
|
|
|
- name: Install JavaScript dependencies
|
|
run: pnpm install --frozen-lockfile
|
|
|
|
- name: Build (shell-tool-mcp)
|
|
run: pnpm --filter @openai/codex-shell-tool-mcp run build
|
|
|
|
- name: Download build artifacts
|
|
uses: actions/download-artifact@v7
|
|
with:
|
|
path: artifacts
|
|
|
|
- name: Assemble staging directory
|
|
id: staging
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
staging="${STAGING_DIR}"
|
|
mkdir -p "$staging" "$staging/vendor"
|
|
cp shell-tool-mcp/README.md "$staging/"
|
|
cp shell-tool-mcp/package.json "$staging/"
|
|
|
|
found_vendor="false"
|
|
shopt -s nullglob
|
|
for vendor_dir in artifacts/*/vendor; do
|
|
rsync -av "$vendor_dir/" "$staging/vendor/"
|
|
found_vendor="true"
|
|
done
|
|
if [[ "$found_vendor" == "false" ]]; then
|
|
echo "No vendor payloads were downloaded."
|
|
exit 1
|
|
fi
|
|
|
|
node - <<'NODE'
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
|
|
const stagingDir = process.env.STAGING_DIR;
|
|
const version = process.env.PACKAGE_VERSION;
|
|
const pkgPath = path.join(stagingDir, "package.json");
|
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
pkg.version = version;
|
|
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
NODE
|
|
|
|
echo "dir=$staging" >> "$GITHUB_OUTPUT"
|
|
env:
|
|
STAGING_DIR: ${{ runner.temp }}/shell-tool-mcp
|
|
|
|
- name: Ensure binaries are executable
|
|
run: |
|
|
set -euo pipefail
|
|
staging="${{ steps.staging.outputs.dir }}"
|
|
chmod +x \
|
|
"$staging"/vendor/*/bash/*/bash \
|
|
"$staging"/vendor/*/zsh/*/zsh
|
|
|
|
- name: Create npm tarball
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
mkdir -p dist/npm
|
|
staging="${{ steps.staging.outputs.dir }}"
|
|
pack_info=$(cd "$staging" && npm pack --ignore-scripts --json --pack-destination "${GITHUB_WORKSPACE}/dist/npm")
|
|
filename=$(PACK_INFO="$pack_info" node -e 'const data = JSON.parse(process.env.PACK_INFO); console.log(data[0].filename);')
|
|
mv "dist/npm/${filename}" "dist/npm/codex-shell-tool-mcp-npm-${PACKAGE_VERSION}.tgz"
|
|
|
|
- uses: actions/upload-artifact@v6
|
|
with:
|
|
name: codex-shell-tool-mcp-npm
|
|
path: dist/npm/codex-shell-tool-mcp-npm-${{ env.PACKAGE_VERSION }}.tgz
|
|
if-no-files-found: error
|
|
|
|
publish:
|
|
name: Publish npm package
|
|
needs:
|
|
- metadata
|
|
- package
|
|
if: ${{ inputs.publish && needs.metadata.outputs.should_publish == 'true' }}
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
id-token: write
|
|
contents: read
|
|
steps:
|
|
- name: Setup Node.js
|
|
uses: actions/setup-node@v6
|
|
with:
|
|
node-version: ${{ env.NODE_VERSION }}
|
|
registry-url: https://registry.npmjs.org
|
|
scope: "@openai"
|
|
|
|
# Trusted publishing requires npm CLI version 11.5.1 or later.
|
|
- name: Update npm
|
|
run: npm install -g npm@latest
|
|
|
|
- name: Download npm tarball
|
|
uses: actions/download-artifact@v7
|
|
with:
|
|
name: codex-shell-tool-mcp-npm
|
|
path: dist/npm
|
|
|
|
- name: Publish to npm
|
|
env:
|
|
NPM_TAG: ${{ needs.metadata.outputs.npm_tag }}
|
|
VERSION: ${{ needs.metadata.outputs.version }}
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
tag_args=()
|
|
if [[ -n "${NPM_TAG}" ]]; then
|
|
tag_args+=(--tag "${NPM_TAG}")
|
|
fi
|
|
npm publish "dist/npm/codex-shell-tool-mcp-npm-${VERSION}.tgz" "${tag_args[@]}"
|