Skip to content

Commit 927f75c

Browse files
NetdocsCopilot
andcommitted
feat(action): run Netdocs from its published container image
Rework the action to execute Netdocs via `docker run` on the published ghcr.io/xtremeownage/netdocs image instead of downloading a ~100MB self-contained binary onto the runner. Root cause of the CI failures: the previous install step resolved the latest version with `curl ... | grep -m1` under `set -o pipefail`. grep -m1 exits on first match and closes the pipe, so curl fails its next write with 'curl: (23) Failure writing output to destination' and pipefail fails the whole step -- in ~0s, regardless of disk space. The runner is GitHub-hosted, so disk was never the problem. The container approach removes the fragile binary download entirely and pins the full toolchain (CLI + theme + deps) to one image. Version resolution still supports 'latest' (resolved via gh / fully-buffered curl -- no grep -m1 pipe) and explicit vX.Y.Z. The workspace is mounted at /github/workspace and netdocs runs as the runner user so generated files (./site) stay readable by later steps. Requires a Linux runner with Docker (ubuntu-latest); errors clearly otherwise. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 33581b1 commit 927f75c

1 file changed

Lines changed: 64 additions & 100 deletions

File tree

action.yml

Lines changed: 64 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -23,130 +23,79 @@ inputs:
2323
required: false
2424
default: ""
2525
working-directory:
26-
description: "Directory to run netdocs in."
26+
description: "Directory (relative to the workspace) to run netdocs in."
2727
required: false
2828
default: "."
2929

3030
outputs:
31-
netdocs-path:
32-
description: "Absolute path to the downloaded netdocs executable."
33-
value: ${{ steps.install.outputs.path }}
31+
image:
32+
description: "The container image reference that was used."
33+
value: ${{ steps.run.outputs.image }}
3434
version:
35-
description: "The resolved Netdocs version that was installed."
36-
value: ${{ steps.install.outputs.version }}
35+
description: "The resolved Netdocs version that was used."
36+
value: ${{ steps.run.outputs.version }}
3737

3838
runs:
3939
using: "composite"
4040
steps:
41-
- id: install
41+
- id: run
4242
shell: bash
4343
env:
4444
GH_TOKEN: ${{ github.token }}
4545
INPUT_VERSION: ${{ inputs.version }}
46+
INPUT_COMMAND: ${{ inputs.command }}
47+
INPUT_CONFIG: ${{ inputs.config }}
48+
INPUT_ARGS: ${{ inputs.args }}
49+
INPUT_WORKDIR: ${{ inputs.working-directory }}
4650
run: |
4751
set -euo pipefail
52+
53+
# Netdocs runs from its published container image. This avoids downloading
54+
# and writing a ~100MB self-contained binary onto the runner (a fragile step
55+
# that could fail with 'curl: (23) Failure writing output to destination'),
56+
# and pins the whole toolchain -- CLI, theme, and dependencies -- to one image.
57+
if ! command -v docker >/dev/null 2>&1; then
58+
echo "::error::The Netdocs action runs via a Linux container and requires Docker on the runner. Use a Linux runner (e.g. ubuntu-latest)." >&2
59+
exit 1
60+
fi
61+
4862
repo="XtremeOwnage/Netdocs"
63+
image="ghcr.io/xtremeownage/netdocs"
4964
version="${INPUT_VERSION}"
5065
51-
# Map the GitHub runner OS/arch onto a published release asset name.
52-
os="${RUNNER_OS}"
53-
arch="${RUNNER_ARCH}"
54-
case "${os}-${arch}" in
55-
Linux-X64) rid="linux-x64"; ext="" ;;
56-
Windows-X64) rid="win-x64"; ext=".exe" ;;
57-
macOS-X64) rid="osx-x64"; ext="" ;;
58-
macOS-ARM64) rid="osx-arm64"; ext="" ;;
59-
*)
60-
echo "::error::Netdocs does not publish a binary for ${os}/${arch}. Supported: Linux-X64, Windows-X64, macOS-X64, macOS-ARM64." >&2
61-
exit 1
62-
;;
63-
esac
64-
65-
# Resolve 'latest' to a concrete version via the GitHub API.
66-
if [ "${version}" = "latest" ] || [ -z "${version}" ]; then
67-
auth=()
68-
if [ -n "${GH_TOKEN:-}" ]; then auth=(-H "Authorization: Bearer ${GH_TOKEN}"); fi
69-
tag="$(curl -fsSL "${auth[@]}" -H "Accept: application/vnd.github+json" \
70-
"https://api.github.com/repos/${repo}/releases/latest" \
71-
| grep -m1 '"tag_name"' | sed -E 's/.*"tag_name": *"([^"]+)".*/\1/')"
66+
# Resolve 'latest' to a concrete released version so the image tag is always a
67+
# real, published vX.Y.Z.
68+
#
69+
# NOTE: never pipe a streaming `curl` into `grep -m1` under `set -o pipefail`:
70+
# grep exits on first match and closes the pipe, so curl fails its next write
71+
# with exit 23 and pipefail fails the step. Resolve via `gh`, and only fall
72+
# back to curl by capturing the full response into a variable first.
73+
if [ -z "${version}" ] || [ "${version}" = "latest" ]; then
74+
tag=""
75+
if command -v gh >/dev/null 2>&1 && [ -n "${GH_TOKEN:-}" ]; then
76+
tag="$(gh release view --repo "${repo}" --json tagName --jq '.tagName' 2>/dev/null || true)"
77+
fi
78+
if [ -z "${tag}" ]; then
79+
auth=()
80+
if [ -n "${GH_TOKEN:-}" ]; then auth=(-H "Authorization: Bearer ${GH_TOKEN}"); fi
81+
json="$(curl -fsSL "${auth[@]}" -H "Accept: application/vnd.github+json" \
82+
"https://api.github.com/repos/${repo}/releases/latest")"
83+
tag="$(printf '%s\n' "${json}" | grep -m1 '"tag_name"' | sed -E 's/.*"tag_name": *"([^"]+)".*/\1/')"
84+
fi
7285
if [ -z "${tag}" ]; then
7386
echo "::error::Could not resolve the latest Netdocs release. Pin an explicit 'version:' instead." >&2
7487
exit 1
7588
fi
7689
version="${tag#v}"
7790
fi
7891
79-
asset="netdocs-${version}-${rid}${ext}"
80-
url="https://github.com/${repo}/releases/download/v${version}/${asset}"
81-
82-
# Download into the workspace disk rather than RUNNER_TEMP. On some runners
83-
# RUNNER_TEMP is backed by a small tmpfs, and writing the ~100MB self-contained
84-
# binary there fails with 'curl: (23) Failure writing output to destination'
85-
# (and likewise breaks `gh release download`).
86-
base_dir="${GITHUB_WORKSPACE:-${RUNNER_TEMP:-/tmp}}"
87-
dest_dir="${base_dir}/.netdocs-bin"
88-
mkdir -p "${dest_dir}"
89-
dest="${dest_dir}/netdocs${ext}"
90-
91-
echo "Downloading ${asset} (v${version}) -> ${dest}"
92-
echo "Disk space at download target:"
93-
df -h "${dest_dir}" || true
94-
95-
download_ok=""
92+
ref="${image}:v${version}"
93+
echo "Using Netdocs image ${ref}"
94+
docker pull "${ref}"
9695
97-
# Prefer the GitHub CLI when available: it authenticates, follows the asset
98-
# redirect and retries transient failures more reliably than raw curl.
99-
if command -v gh >/dev/null 2>&1 && [ -n "${GH_TOKEN:-}" ]; then
100-
if gh release download "v${version}" --repo "${repo}" \
101-
--pattern "${asset}" --output "${dest}" --clobber; then
102-
download_ok="1"
103-
else
104-
echo "::warning::gh release download failed; falling back to curl."
105-
fi
106-
fi
107-
108-
# Fallback (and default when gh is unavailable): curl with retries. Write to a
109-
# temp file first and move it into place only on success, so a partial transfer
110-
# never leaves a truncated 'netdocs' behind.
111-
if [ -z "${download_ok}" ]; then
112-
tmp="${dest}.partial"
113-
rm -f "${tmp}"
114-
if ! curl --fail --location --show-error --silent \
115-
--retry 5 --retry-delay 3 --retry-all-errors \
116-
--connect-timeout 30 \
117-
-o "${tmp}" "${url}"; then
118-
echo "::error::Failed to download ${url}." >&2
119-
echo "Partial file size:"; ls -l "${tmp}" 2>/dev/null || true
120-
echo "Filesystem usage (all mounts):"; df -h || true
121-
exit 1
122-
fi
123-
mv -f "${tmp}" "${dest}"
124-
fi
125-
126-
chmod +x "${dest}" || true
127-
128-
# Guard against a zero-byte / truncated download slipping through.
129-
if [ ! -s "${dest}" ]; then
130-
echo "::error::Downloaded Netdocs binary is missing or empty: ${dest}" >&2
131-
echo "Filesystem usage (all mounts):"; df -h || true
132-
exit 1
133-
fi
134-
135-
echo "path=${dest}" >> "${GITHUB_OUTPUT}"
136-
echo "version=${version}" >> "${GITHUB_OUTPUT}"
137-
echo "${dest_dir}" >> "${GITHUB_PATH}"
138-
"${dest}" --version || true
139-
140-
- shell: bash
141-
working-directory: ${{ inputs.working-directory }}
142-
env:
143-
NETDOCS_BIN: ${{ steps.install.outputs.path }}
144-
INPUT_COMMAND: ${{ inputs.command }}
145-
INPUT_CONFIG: ${{ inputs.config }}
146-
INPUT_ARGS: ${{ inputs.args }}
147-
run: |
148-
set -euo pipefail
149-
cmd=("${NETDOCS_BIN}" "${INPUT_COMMAND}")
96+
# Assemble the netdocs command. The image ENTRYPOINT is `netdocs`, so only its
97+
# arguments are supplied here.
98+
cmd=("${INPUT_COMMAND}")
15099
if [ -n "${INPUT_CONFIG}" ]; then
151100
cmd+=(--config "${INPUT_CONFIG}")
152101
fi
@@ -155,5 +104,20 @@ runs:
155104
extra=(${INPUT_ARGS})
156105
cmd+=("${extra[@]}")
157106
fi
158-
echo "Running: ${cmd[*]}"
159-
"${cmd[@]}"
107+
108+
workdir="${INPUT_WORKDIR:-.}"
109+
run_dir="/github/workspace/${workdir}"
110+
111+
echo "Running: netdocs ${cmd[*]} (in ${run_dir})"
112+
113+
# Mount the checked-out workspace and run as the runner user so generated
114+
# files (e.g. ./site) are owned by the runner and readable by later steps.
115+
docker run --rm \
116+
-v "${GITHUB_WORKSPACE}:/github/workspace" \
117+
-w "${run_dir}" \
118+
-u "$(id -u):$(id -g)" \
119+
-e HOME=/tmp \
120+
"${ref}" "${cmd[@]}"
121+
122+
echo "image=${ref}" >> "${GITHUB_OUTPUT}"
123+
echo "version=${version}" >> "${GITHUB_OUTPUT}"

0 commit comments

Comments
 (0)