Use private host transport in containerized CI
This commit is contained in:
7
.github/workflows/candidate.yml
vendored
7
.github/workflows/candidate.yml
vendored
@@ -298,6 +298,13 @@ jobs:
|
|||||||
actual_client_sha="$(git -C ruby-client rev-parse HEAD)"
|
actual_client_sha="$(git -C ruby-client rev-parse HEAD)"
|
||||||
test "$actual_client_sha" = "$EXPECTED_CLIENT_SHA"
|
test "$actual_client_sha" = "$EXPECTED_CLIENT_SHA"
|
||||||
echo "value=$actual_client_sha" >> "$GITHUB_OUTPUT"
|
echo "value=$actual_client_sha" >> "$GITHUB_OUTPUT"
|
||||||
|
- name: Resolve the private Docker host transport
|
||||||
|
run: |
|
||||||
|
probe_host="$(ip -4 route show default | awk '/^default / {print $3; exit}')"
|
||||||
|
ruby -ripaddr -e \
|
||||||
|
'address = IPAddr.new(ARGV.fetch(0)); exit(address.ipv4? && address.private? ? 0 : 1)' \
|
||||||
|
"$probe_host"
|
||||||
|
echo "OPENCODE_PROBE_HOST=$probe_host" >> "$GITHUB_ENV"
|
||||||
- name: Exercise the complete manifest matrix without artifact claims
|
- name: Exercise the complete manifest matrix without artifact claims
|
||||||
env:
|
env:
|
||||||
BUNDLE_GEMFILE: ${{ github.workspace }}/ruby-client/Gemfile
|
BUNDLE_GEMFILE: ${{ github.workspace }}/ruby-client/Gemfile
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ image="${OPENCODE_IMAGE:-}"
|
|||||||
gem_path="${OPENCODE_RUBY_PATH:-}"
|
gem_path="${OPENCODE_RUBY_PATH:-}"
|
||||||
expected_gem_commit="${OPENCODE_RUBY_COMMIT:-}"
|
expected_gem_commit="${OPENCODE_RUBY_COMMIT:-}"
|
||||||
evidence_path="${OPENCODE_COMPAT_EVIDENCE_PATH:-}"
|
evidence_path="${OPENCODE_COMPAT_EVIDENCE_PATH:-}"
|
||||||
|
probe_host="${OPENCODE_PROBE_HOST:-127.0.0.1}"
|
||||||
container_name="opencode-compat-${RANDOM}-$$"
|
container_name="opencode-compat-${RANDOM}-$$"
|
||||||
llm_container_name="opencode-compat-llm-${RANDOM}-$$"
|
llm_container_name="opencode-compat-llm-${RANDOM}-$$"
|
||||||
network_name="opencode-compat-net-${RANDOM}-$$"
|
network_name="opencode-compat-net-${RANDOM}-$$"
|
||||||
@@ -97,6 +98,18 @@ if [[ ! "$image" =~ ^[^[:space:]@]+@sha256:[0-9a-f]{64}$ ]]; then
|
|||||||
exit 2
|
exit 2
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
if ! ruby -ripaddr -e '
|
||||||
|
begin
|
||||||
|
address = IPAddr.new(ARGV.fetch(0))
|
||||||
|
valid = address.ipv4? && (address.loopback? || address.private?)
|
||||||
|
rescue IPAddr::InvalidAddressError
|
||||||
|
valid = false
|
||||||
|
end
|
||||||
|
exit(valid ? 0 : 1)
|
||||||
|
' "$probe_host"; then
|
||||||
|
echo "OPENCODE_PROBE_HOST must be a loopback or private IPv4 address" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ ! "$expected_gem_commit" =~ ^[0-9a-f]{40}$ ]]; then
|
if [[ ! "$expected_gem_commit" =~ ^[0-9a-f]{40}$ ]]; then
|
||||||
echo "OPENCODE_RUBY_COMMIT must be a full lowercase 40-character Git commit" >&2
|
echo "OPENCODE_RUBY_COMMIT must be a full lowercase 40-character Git commit" >&2
|
||||||
@@ -171,7 +184,7 @@ config_json="$(jq -cn --arg url "http://compat-llm:8080/v1" '{
|
|||||||
docker run --detach \
|
docker run --detach \
|
||||||
--name "$container_name" \
|
--name "$container_name" \
|
||||||
--network "$network_name" \
|
--network "$network_name" \
|
||||||
--publish 127.0.0.1::4096 \
|
--publish "${probe_host}::4096" \
|
||||||
--env "OPENCODE_CONFIG_CONTENT=$config_json" \
|
--env "OPENCODE_CONFIG_CONTENT=$config_json" \
|
||||||
--env OPENCODE_DISABLE_AUTOUPDATE=1 \
|
--env OPENCODE_DISABLE_AUTOUPDATE=1 \
|
||||||
--env OPENCODE_DISABLE_AUTOCOMPACT=1 \
|
--env OPENCODE_DISABLE_AUTOCOMPACT=1 \
|
||||||
@@ -183,7 +196,7 @@ docker run --detach \
|
|||||||
opencode_container_started=1
|
opencode_container_started=1
|
||||||
|
|
||||||
host_port="$(docker port "$container_name" 4096/tcp | sed -E 's/.*:([0-9]+)$/\1/' | head -1)"
|
host_port="$(docker port "$container_name" 4096/tcp | sed -E 's/.*:([0-9]+)$/\1/' | head -1)"
|
||||||
base_url="http://127.0.0.1:${host_port}"
|
base_url="http://${probe_host}:${host_port}"
|
||||||
|
|
||||||
ready=0
|
ready=0
|
||||||
for _ in $(seq 1 120); do
|
for _ in $(seq 1 120); do
|
||||||
|
|||||||
@@ -108,6 +108,23 @@ class ImageContractEvidenceTest < Minitest::Test
|
|||||||
assert_failed_without_docker(evidence_path)
|
assert_failed_without_docker(evidence_path)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_rejects_malformed_ipv6_and_public_probe_hosts_before_docker
|
||||||
|
checkout, commit = git_checkout
|
||||||
|
|
||||||
|
["not-an-address", "::1", "8.8.8.8"].each_with_index do |probe_host, index|
|
||||||
|
evidence_path = File.join(@tmp, "probe-host-#{index}.json")
|
||||||
|
environment = base_environment(checkout, commit, evidence_path).merge(
|
||||||
|
"OPENCODE_PROBE_HOST" => probe_host
|
||||||
|
)
|
||||||
|
|
||||||
|
_output, error, status = run_contract(environment)
|
||||||
|
|
||||||
|
assert_equal 2, status.exitstatus
|
||||||
|
assert_match(/loopback or private IPv4 address/, error)
|
||||||
|
assert_failed_without_docker(evidence_path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def base_environment(checkout, commit, evidence_path)
|
def base_environment(checkout, commit, evidence_path)
|
||||||
|
|||||||
@@ -287,6 +287,9 @@ class RepositoryTest < Minitest::Test
|
|||||||
assert_includes workflow, "exact-image-contract-gitea:"
|
assert_includes workflow, "exact-image-contract-gitea:"
|
||||||
assert_includes workflow, "if: github.server_url != 'https://github.com'"
|
assert_includes workflow, "if: github.server_url != 'https://github.com'"
|
||||||
assert_includes workflow, "run: scripts/run_image_matrix_contract.sh"
|
assert_includes workflow, "run: scripts/run_image_matrix_contract.sh"
|
||||||
|
assert_includes workflow, "ip -4 route show default"
|
||||||
|
assert_includes workflow, "address.ipv4? && address.private?"
|
||||||
|
assert_includes workflow, 'echo "OPENCODE_PROBE_HOST=$probe_host" >> "$GITHUB_ENV"'
|
||||||
assert_operator workflow.scan('BUNDLE_GEMFILE: ${{ github.workspace }}/ruby-client/Gemfile').length, :>=, 2
|
assert_operator workflow.scan('BUNDLE_GEMFILE: ${{ github.workspace }}/ruby-client/Gemfile').length, :>=, 2
|
||||||
assert_operator workflow.scan("generated JSON is transient and is not review evidence").length, :>=, 3
|
assert_operator workflow.scan("generated JSON is transient and is not review evidence").length, :>=, 3
|
||||||
|
|
||||||
@@ -327,6 +330,9 @@ class RepositoryTest < Minitest::Test
|
|||||||
assert_includes runner, "exact_live_contract.rb"
|
assert_includes runner, "exact_live_contract.rb"
|
||||||
assert_includes runner, 'docker cp "$repo_root/scripts/fake_llm.py"'
|
assert_includes runner, 'docker cp "$repo_root/scripts/fake_llm.py"'
|
||||||
refute_includes runner, '--volume "$repo_root/scripts:/compat:ro"'
|
refute_includes runner, '--volume "$repo_root/scripts:/compat:ro"'
|
||||||
|
assert_includes runner, '--publish "${probe_host}::4096"'
|
||||||
|
assert_includes runner, 'base_url="http://${probe_host}:${host_port}"'
|
||||||
|
refute_includes runner, "--publish 0.0.0.0"
|
||||||
assert_includes runner, "OPENCODE_COMPAT_EVIDENCE_PATH"
|
assert_includes runner, "OPENCODE_COMPAT_EVIDENCE_PATH"
|
||||||
assert_includes runner, "OPENCODE_EXPECTED_VERSION"
|
assert_includes runner, "OPENCODE_EXPECTED_VERSION"
|
||||||
refute_match(/request_count.*-lt\s+1/, runner)
|
refute_match(/request_count.*-lt\s+1/, runner)
|
||||||
|
|||||||
Reference in New Issue
Block a user