@@ -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
3030outputs :
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
3838runs :
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