-
-
Notifications
You must be signed in to change notification settings - Fork 187
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ci/cd: fix IPv6 timeout with
force-ipv4
action
This commit introduces the `force-ipv4` GitHub action to address connectivity issues caused by the lack of IPv6 support in GitHub runners. Details: - actions/runner¤3138 - actions/runner-images¤668 This change solves connection problems when Node's `fetch` API fails due to `UND_ERR_CONNECT_TIMEOUT` errors. Details: - actions/runner-images¤9540 - actions/runner¤3213 This action disables IPv6 at the system level, ensuring all outging requests use IPv4. Resolving connectivity issues when running external URL checks and Docker build checks. This solution is a temporary workaround until GitHub runners support IPv6 or Node `fetch` API has a working solution such as Happy Eyeball. Detais: - nodejs/node¤41625 - nodejs/undici¤1531
- Loading branch information
1 parent
8a5592f
commit ddf8db4
Showing
16 changed files
with
690 additions
and
573 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
# force-ipv4 | ||
|
||
## Overview | ||
|
||
This GitHub action enforces IPv4 for all outgoing network requests. It addresses connectivity issues encountered in GitHub runners, where IPv6 requests may lead to timeouts due to the lack of IPv6 support [1] [2]. | ||
|
||
## Background | ||
|
||
Some applications attempt network connections over IPv6. | ||
Such as requests made by Node's `fetch` API causes `UND_ERR_CONNECT_TIMEOUT` errors [3] [4]. | ||
This happens when the software cannot handle this such as by using Happy Eyeballs [5] [6]. | ||
|
||
## Usage | ||
|
||
To use this action in your GitHub workflow, add the following step before any job that requires network access: | ||
|
||
```yaml | ||
- name: Enforce IPv4 Connectivity | ||
uses: ./.github/actions/force-ipv4 | ||
``` | ||
## Note | ||
This action is a workaround addressing specific IPv6-related connectivity issues on GitHub runners and may not be necessary if GitHub's infrastructure evolves to fully support IPv6 in the future. | ||
[1]: https://archive.ph/2024.03.28-185829/https://github.com/actions/runner/issues/3138 "Actions Runner fails on IPv6 only host · Issue #3138 · actions/runner · GitHub | github.com" | ||
[2]: https://archive.ph/2024.03.28-185838/https://github.com/actions/runner-images/issues/668 "IPv6 on GitHub-hosted runners · Issue #668 · actions/runner-images · GitHub | github.com" | ||
[3]: https://archive.ph/2024.03.28-185847/https://github.com/actions/runner/issues/3213 "GitHub runner cannot send `fetch` with `node`, failing with IPv6 DNS error `UND_ERR_CONNECT_TIMEOUT` · Issue #3213 · actions/runner · GitHub | github.com" | ||
[4]: https://archive.today/2024.03.28-185853/https://github.com/actions/runner-images/issues/9540 "Cannot send outbound requests using node fetch, failing with IPv6 DNS error UND_ERR_CONNECT_TIMEOUT · Issue #9540 · actions/runner-images · GitHub | github.com" | ||
[5]: https://archive.today/2024.03.28-185900/https://github.com/nodejs/node/issues/41625 "Happy Eyeballs support (address IPv6 issues in Node 17) · Issue #41625 · nodejs/node · GitHub | github.com" | ||
[6]: https://archive.today/2024.03.28-185910/https://github.com/nodejs/undici/issues/1531 "fetch times out in under 5 seconds · Issue #1531 · nodejs/undici · GitHub | github.com" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
inputs: | ||
project-root: | ||
required: false | ||
default: '.' | ||
runs: | ||
using: composite | ||
steps: | ||
- | ||
name: Run prefer IPv4 script | ||
shell: bash | ||
run: ./.github/actions/force-ipv4/force-ipv4.sh | ||
working-directory: ${{ inputs.project-root }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
#!/usr/bin/env bash | ||
|
||
main() { | ||
if is_linux; then | ||
echo 'Configuring Linux...' | ||
setup_warp_and_exclude_ipv6_on_linux | ||
# Considered alternatives: | ||
# - Specifying `precedence ::ffff:0:0/96 100` in `/etc/gai.conf` did not address the issue. | ||
# - `sysctl` commands, and direct changes to `/proc/sys/net/` and `/etc/sysctl.conf` led to silent Node 18 exits (code: 13) when using `fetch`. | ||
elif is_macos; then | ||
echo 'Configuring macOS...' | ||
setup_warp_and_exclude_ipv6_on_macos | ||
# disable_ipv6_on_macos | ||
# Considered alternatives: | ||
# - Using `networksetup` to iterate interfaces and disabling IPv6 for each did not resolve the issue. | ||
fi | ||
echo "IPv4: $(curl -s4m8 --retry 3 -A Mozilla https://api.ip.sb/geoip)" | ||
echo "IPv6: $(curl -s6m8 --retry 3 -A Mozilla https://api.ip.sb/geoip)" | ||
} | ||
|
||
is_linux() { | ||
[[ "$(uname -s)" == "Linux" ]] | ||
} | ||
|
||
is_macos() { | ||
[[ "$(uname -s)" == "Darwin" ]] | ||
} | ||
|
||
setup_warp_and_exclude_ipv6_on_linux() { | ||
install_warp_on_debian | ||
configure_warp_to_drop_ipv6 | ||
} | ||
|
||
setup_warp_and_exclude_ipv6_on_macos() { | ||
brew install cloudflare-warp | ||
configure_warp_to_drop_ipv6 | ||
} | ||
|
||
configure_warp_to_drop_ipv6() { | ||
echo 'Configuring Cloudflare WARP client...' | ||
echo 'Initiating client registration with Cloudflare...' | ||
warp-cli --accept-tos registration new | ||
echo 'Configuring WARP to operate in DNS-over-HTTPS mode (warp+doh)...' | ||
warp-cli --accept-tos mode warp+doh | ||
echo 'Excluding IPv6 traffic from WARP by configuring it as a split tunnel...' | ||
warp-cli --accept-tos add-excluded-route '::/0' # Exclude IPv6, forcing IPv4 resolution | ||
# `tunnel ip add` does not work with IP ranges, see https://community.cloudflare.com/t/cant-cidr-for-split-tunnling/630834 | ||
echo 'Establishing WARP connection...' | ||
warp-cli --accept-tos connect | ||
} | ||
|
||
install_warp_on_debian() { | ||
curl -fsSL https://pkg.cloudflareclient.com/pubkey.gpg | sudo gpg --yes --dearmor --output /usr/share/keyrings/cloudflare-warp-archive-keyring.gpg | ||
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg] https://pkg.cloudflareclient.com/ $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/cloudflare-client.list | ||
sudo apt-get update | ||
sudo apt-get install -y cloudflare-warp | ||
} | ||
|
||
disable_ipv6_on_macos() { | ||
networksetup -listallnetworkservices \ | ||
| tail -n +2 \ | ||
| while IFS= read -r interface; do | ||
echo "Disabling IPv6 on: $interface" | ||
networksetup -setv6off "$interface" | ||
done | ||
} | ||
|
||
main |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,72 +1,72 @@ | ||
name: checks.desktop-runtime-errors | ||
# Verifies desktop builds for Electron applications across multiple OS platforms (macOS ,Ubuntu, and Windows). | ||
# name: checks.desktop-runtime-errors | ||
# # Verifies desktop builds for Electron applications across multiple OS platforms (macOS ,Ubuntu, and Windows). | ||
|
||
on: | ||
push: | ||
pull_request: | ||
# on: | ||
# push: | ||
# pull_request: | ||
|
||
jobs: | ||
run-check: | ||
strategy: | ||
matrix: | ||
os: [ macos, ubuntu, windows ] | ||
fail-fast: false # Allows to see results from other combinations | ||
runs-on: ${{ matrix.os }}-latest | ||
steps: | ||
- | ||
name: Checkout | ||
uses: actions/checkout@v4 | ||
- | ||
name: Setup node | ||
uses: ./.github/actions/setup-node | ||
- | ||
name: Install dependencies | ||
uses: ./.github/actions/npm-install-dependencies | ||
- | ||
name: Configure Ubuntu | ||
if: matrix.os == 'ubuntu' | ||
shell: bash | ||
run: |- | ||
sudo apt update | ||
# jobs: | ||
# run-check: | ||
# strategy: | ||
# matrix: | ||
# os: [ macos, ubuntu, windows ] | ||
# fail-fast: false # Allows to see results from other combinations | ||
# runs-on: ${{ matrix.os }}-latest | ||
# steps: | ||
# - | ||
# name: Checkout | ||
# uses: actions/checkout@v4 | ||
# - | ||
# name: Setup node | ||
# uses: ./.github/actions/setup-node | ||
# - | ||
# name: Install dependencies | ||
# uses: ./.github/actions/npm-install-dependencies | ||
# - | ||
# name: Configure Ubuntu | ||
# if: matrix.os == 'ubuntu' | ||
# shell: bash | ||
# run: |- | ||
# sudo apt update | ||
|
||
# Configure AppImage dependencies | ||
sudo apt install -y libfuse2 | ||
# # Configure AppImage dependencies | ||
# sudo apt install -y libfuse2 | ||
|
||
# Configure DBUS (fixes `Failed to connect to the bus: Could not parse server address: Unknown address type`) | ||
if ! command -v 'dbus-launch' &> /dev/null; then | ||
echo 'DBUS does not exist, installing...' | ||
sudo apt install -y dbus-x11 # Gives both dbus and dbus-launch utility | ||
fi | ||
sudo systemctl start dbus | ||
DBUS_LAUNCH_OUTPUT=$(dbus-launch) | ||
if [ $? -eq 0 ]; then | ||
echo "${DBUS_LAUNCH_OUTPUT}" >> $GITHUB_ENV | ||
else | ||
echo 'Error: dbus-launch command did not execute successfully. Exiting.' >&2 | ||
echo "${DBUS_LAUNCH_OUTPUT}" >&2 | ||
exit 1 | ||
fi | ||
# # Configure DBUS (fixes `Failed to connect to the bus: Could not parse server address: Unknown address type`) | ||
# if ! command -v 'dbus-launch' &> /dev/null; then | ||
# echo 'DBUS does not exist, installing...' | ||
# sudo apt install -y dbus-x11 # Gives both dbus and dbus-launch utility | ||
# fi | ||
# sudo systemctl start dbus | ||
# DBUS_LAUNCH_OUTPUT=$(dbus-launch) | ||
# if [ $? -eq 0 ]; then | ||
# echo "${DBUS_LAUNCH_OUTPUT}" >> $GITHUB_ENV | ||
# else | ||
# echo 'Error: dbus-launch command did not execute successfully. Exiting.' >&2 | ||
# echo "${DBUS_LAUNCH_OUTPUT}" >&2 | ||
# exit 1 | ||
# fi | ||
|
||
# Configure fake (virtual) display | ||
sudo apt install -y xvfb | ||
sudo Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & | ||
echo "DISPLAY=:99" >> $GITHUB_ENV | ||
# # Configure fake (virtual) display | ||
# sudo apt install -y xvfb | ||
# sudo Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & | ||
# echo "DISPLAY=:99" >> $GITHUB_ENV | ||
|
||
# Install ImageMagick for screenshots | ||
sudo apt install -y imagemagick | ||
# # Install ImageMagick for screenshots | ||
# sudo apt install -y imagemagick | ||
|
||
# Install xdotool and xprop (from x11-utils) for window title capturing | ||
sudo apt install -y xdotool x11-utils | ||
- | ||
name: Test | ||
shell: bash | ||
run: |- | ||
export SCREENSHOT=true | ||
npm run check:desktop | ||
- | ||
name: Upload screenshot | ||
if: always() # Run even if previous step fails | ||
uses: actions/upload-artifact@v3 | ||
with: | ||
name: screenshot-${{ matrix.os }} | ||
path: screenshot.png | ||
# # Install xdotool and xprop (from x11-utils) for window title capturing | ||
# sudo apt install -y xdotool x11-utils | ||
# - | ||
# name: Test | ||
# shell: bash | ||
# run: |- | ||
# export SCREENSHOT=true | ||
# npm run check:desktop | ||
# - | ||
# name: Upload screenshot | ||
# if: always() # Run even if previous step fails | ||
# uses: actions/upload-artifact@v3 | ||
# with: | ||
# name: screenshot-${{ matrix.os }} | ||
# path: screenshot.png |
Oops, something went wrong.