Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 127 additions & 1 deletion .github/workflows/install.yml

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 [actionlint] reported by reviewdog 🐶
shellcheck reported issue in this script: SC2028:info:264:12: echo may not expand escape sequences. Use printf [shellcheck]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 [actionlint] reported by reviewdog 🐶
shellcheck reported issue in this script: SC2028:info:273:12: echo may not expand escape sequences. Use printf [shellcheck]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 [actionlint] reported by reviewdog 🐶
shellcheck reported issue in this script: SC2028:info:362:12: echo may not expand escape sequences. Use printf [shellcheck]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 [actionlint] reported by reviewdog 🐶
shellcheck reported issue in this script: SC2028:info:371:12: echo may not expand escape sequences. Use printf [shellcheck]

Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ jobs:
set -Eeuo pipefail

case "$CALLBACK" in
powershell | legacy)
powershell | legacy | basic)
;;
*)
echo "Unsupported guest script type: $CALLBACK"
Expand All @@ -249,6 +249,7 @@ jobs:
printf '%s\n' "$token" > "$RUNNER_TEMP/data/validation.token"
printf '%s\n' "$token" > "$RUNNER_TEMP/oem/validation.token"

if [[ "$CALLBACK" != "basic" ]]; then
cat > "$RUNNER_TEMP/oem/sync-log.bat" <<'BATCH'
@echo off
setlocal
Expand Down Expand Up @@ -327,6 +328,7 @@ jobs:

shell.Run "cmd.exe /C echo VALIDATION_CALLBACK_FAILED^>COM1", 0, True
VBSCRIPT
fi

case "$CALLBACK" in
powershell)
Expand Down Expand Up @@ -764,6 +766,34 @@ jobs:
exit /B %result%
EOF
;;

basic)
cat > "$RUNNER_TEMP/oem/install.bat" <<'BATCH'
@echo off

:retry
if not exist \\host.lan\Data\validation.token goto wait

echo TOKEN> C:\OEM\validation.result
type C:\OEM\validation.token >> C:\OEM\validation.result
echo SHARE>> C:\OEM\validation.result
type \\host.lan\Data\validation.token >> C:\OEM\validation.result
echo VERSION>> C:\OEM\validation.result
ver >> C:\OEM\validation.result

if exist \\host.lan\Data\validation.result del \\host.lan\Data\validation.result >nul
copy C:\OEM\validation.result \\host.lan\Data\validation.result >nul
if errorlevel 1 goto wait

goto end

:wait
ping 127.0.0.1 -n 6 >nul
goto retry

:end
BATCH
;;
esac

echo "token=$token" >> "$GITHUB_OUTPUT"
Expand Down Expand Up @@ -890,6 +920,7 @@ jobs:
- name: Install and validate Windows
shell: bash
env:
CALLBACK: ${{ inputs.callback }}
KILL_ON_FAILURE: ${{ inputs.kill_on_failure }}
INSTALL_TIMEOUT: ${{ inputs.install_timeout }}
REBOOT_TIMEOUT: ${{ inputs.reboot_timeout }}
Expand Down Expand Up @@ -1130,6 +1161,101 @@ jobs:
exit 1
fi

basic_result="$RUNNER_TEMP/data/validation.result"

if [[ "$CALLBACK" == "basic" && -s "$basic_result" ]]; then
basic_response="$(tr -d '\r' < "$basic_result")"

oem_file="$(
awk '$0 == "TOKEN" { getline; print; exit }' <<< "$basic_response"
)"

share="$(
awk '$0 == "SHARE" { getline; print; exit }' <<< "$basic_response"
)"

version="$(
awk '
$0 == "VERSION" { found = 1; next }
found && NF { print; exit }
' <<< "$basic_response"
)"

if [[ -z "$oem_file" || -z "$share" || -z "$version" ]] ||
! grep -Eq '[0-9]+\.[0-9]+\.[0-9]+' <<< "$version"; then
sleep 1
continue
fi

stop_logs
trap - EXIT

echo
echo "------------------------------------------------------------"
echo "Received response:"
printf '%s\n' "$basic_response"

if [[ "$oem_file" != "$EXPECTED_TOKEN" ]]; then
echo "Failed to read C:\\OEM\\validation.token."
echo "Expected contents:"
echo " $EXPECTED_TOKEN"
echo "Received:"
echo " ${oem_file:-empty}"
exit 1
fi

if [[ "$share" != "$EXPECTED_TOKEN" ]]; then
echo "Failed to read \\\\host.lan\\Data\\validation.token."
echo "Expected contents:"
echo " $EXPECTED_TOKEN"
echo "Received:"
echo " ${share:-empty}"
exit 1
fi

normalized_caption="${version//\(R\)/}"
normalized_expected_caption="${EXPECTED_CAPTION//\(R\)/}"

if [[ "$normalized_caption" != *"$normalized_expected_caption"* ]]; then
echo "Expected caption containing:"
echo " $EXPECTED_CAPTION"
echo "Received:"
echo " $version"
exit 1
fi

version_number="$(
grep -Eo '[0-9]+\.[0-9]+\.[0-9]+' <<< "$version" |
head -n 1 || true
)"

if [[ ! "$version_number" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Unexpected Windows version: $version"
exit 1
fi

build="${version_number##*.}"

if [[ ! "$build" =~ ^[0-9]+$ ]]; then
echo "Invalid Windows build number: $build"
exit 1
fi

if (( build < MINIMUM_BUILD )); then
echo "Expected build $MINIMUM_BUILD or newer."
echo "Received build: $build"
exit 1
fi

echo
echo "$DISPLAY_NAME installed successfully."
echo "Version: $version_number"
echo "Build: $build"
echo "OEM files: copied successfully"
echo "Shared folder: readable and writable"
exit 0
fi

response="$(
grep -F 'VALIDATION_RESULT=' <<< "$container_log" |
tail -n 1 |
Expand Down
33 changes: 33 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,39 @@ jobs:
minimum_build: ${{ matrix.minimum_build }}
platform: ${{ matrix.platform }}

win9x:
strategy:
fail-fast: false
matrix:
include:
- name: Windows 95
version: "95"
expected_caption: Windows 95
minimum_build: 950
platform: x86

- name: Windows 98
version: "98"
expected_caption: Windows 98
minimum_build: 2222
platform: x86

- name: Windows ME
version: "me"
expected_caption: Windows Millennium
minimum_build: 3000
platform: x86

name: ${{ matrix.name }}
uses: ./.github/workflows/settings.yml
with:
name: ${{ matrix.name }}
version: ${{ matrix.version }}
callback: basic
expected_caption: ${{ matrix.expected_caption }}
minimum_build: ${{ matrix.minimum_build }}
platform: ${{ matrix.platform }}

tiny:
strategy:
fail-fast: false
Expand Down
3 changes: 3 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ For a complete graphical desktop experience, see [WinBoat](https://winboat.app),
| `vu` | Windows Vista Ultimate | 3.0 GB |
| `xp` | Windows XP Professional | 0.6 GB |
| `2k` | Windows 2000 Professional | 0.4 GB |
| `me` | Windows ME | 0.5 GB |
| `98` | Windows 98 | 0.7 GB |
| `95` | Windows 95 | 0.6 GB |
||||
| `2025` | Windows Server 2025 | 7.6 GB |
| `2022` | Windows Server 2022 | 6.0 GB |
Expand Down
91 changes: 82 additions & 9 deletions src/define.sh
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,12 @@ parseVersion() {
VERSION="winxpx64" ;;
"2k" | "2000" | "win2k" | "win2000" | "windows2k" | "windows2000" )
VERSION="win2kx86" ;;
"me" | "winme" | "win9x" | "windowsme" | "windows me" )
VERSION="win9x" ;;
"98" | "98se" | "win98" | "win98se" | "windows98" | "windows98se" | "windows 98" | "windows 98 se" )
VERSION="win98" ;;
"95" | "95c" | "win95" | "win95c" | "windows95" | "windows 95" )
VERSION="win95" ;;
"25" | "2025" | "win25" | "win2025" | "windows2025" | "windows 2025" )
VERSION="win2025-eval" ;;
"22" | "2022" | "win22" | "win2022" | "windows2022" | "windows 2022" )
Expand Down Expand Up @@ -570,6 +576,14 @@ fromFile() {
id="win11${arch}" ;;
*"winxp"* | *"win_xp"* | *"windowsxp"* | *"windows_xp"* )
id="winxpx86" ;;
*"winme"* | *"win_me"* | *"win9x"* | *"windowsme"* | *"windows_me"* )
id="win9x" ;;
*"win98"* | *"win_98"* | *"windows98"* | *"windows_98"* )
id="win98" ;;
*"win95"* | *"win_95"* | *"windows95"* | *"windows_95"* )
id="win95" ;;
*"winnt4"* | *"win_nt4"* | *"windowsnt4"* | *"windows_nt4"* | *"windows_nt_4"* )
id="winnt4" ;;
*"winvista"* | *"win_vista"* | *"windowsvista"* | *"windows_vista"* )
id="winvista${arch}" ;;
"tiny11core"* | "tiny11_core"* | "tiny_11_core"* )
Expand Down Expand Up @@ -1038,7 +1052,7 @@ isLegacy() {
local id="$1"

case "${id,,}" in
"win9"* | "winnt4"* | "win2k"* | "winxp"* | "win2003"* | \
"win9"* | "winnt4" | "win2k"* | "winxp"* | "win2003"* | \
"winvista"* | "win7"* | "win2008"* | "reactos" )
return 0 ;;
esac
Expand All @@ -1051,36 +1065,65 @@ supportsUnattended() {
local id="$1"

case "${id,,}" in
"win9"* | "winnt4"* | "reactos" )
"winnt4" | "reactos" )
return 1 ;;
esac

return 0
}

supportsBootKey() {
supportsXML() {

local id="$1"

supportsSIF "$id" && return 1
supportsUnattended "$id" || return 1

case "${id,,}" in
"win9"* | "winnt4" | "reactos" )
"win9"* )
return 1 ;;
"win"* | "tiny"* | "core"* )
return 0 ;;
esac

return 0
}

supportsXML() {
supportsSIF() {

local id="$1"

supportsUnattended "$id" || return 1

case "${id,,}" in
"win2k"* | "winxp"* | "win2003"* )
return 0 ;;
esac

return 1
}

supportsACPI() {

local id="$1"

case "${id,,}" in

"reactos" )

# If the ISO is a Live-CD it will ignore ACPI signals.
hasSystemImage && return 1 ;;

esac

return 0
}

supportsBootKey() {

local id="$1"

case "${id,,}" in
"win9"* | "winnt4" | "reactos" )
return 1 ;;
"win"* | "tiny"* | "core"* )
return 0 ;;
esac

return 0
Expand Down Expand Up @@ -1337,6 +1380,21 @@ getLink1() {
sum="a93251b31f92316411bb48458a695d9051b13cdeba714c46f105012fdda45bf3"
url="2000/5.00.2195.6717_x86fre_client-professional_retail_en-us.7z"
;;
"win9x" )
size=448957271
sum="36e75592cf89e072e165da60100f91ca59b7667be90494f1aa4a78091011cfea"
url="9x/me/Windows%20Me_en.zip"
;;
"win98" )
size=558263127
sum="980bfc9abc93c3104d1a00690c5db702efda7a377de7e06cda61889a972e2e08"
url="9x/98/se/Microsoft%20Windows%2098%20Second%20Edition%20%284.10.2222%29.rar"
;;
"win95" )
size=422127699
sum="5456632a73a3c70f8e5ee566374876266a81f61968e623ef5500028dec8e2979"
url="9x/95/Microsoft%20Windows%2095C%20%284.03.1216.osr2.5%29.zip"
;;
esac

case "${ret,,}" in
Expand Down Expand Up @@ -1616,6 +1674,21 @@ getLink4() {
sum="e3816f6e80b66ff686ead03eeafffe9daf020a5e4717b8bd4736b7c51733ba22"
url="MicrosoftWindows2000BuildCollection/5.00.2195.6717_x86fre_client-professional_retail_en-us-ZRMPFPP_EN.iso"
;;
"win9x" )
size=523665408
sum="bff3e9e69e625d2fdffd01b326988c45be8b9285465a4dabbae96ac550de611e"
url="windows-me_202209/Windows%20ME.ISO"
;;
"win98" )
size=655591424
sum="2adfb46df8a9c7bbd2f67bff07461cc2f9d9ec8e01f0e112cb044c9e3e62f607"
url="windows-98-se-isofile/Windows%2098%20Second%20Edition.iso"
;;
"win95" )
size=618229760
sum="493212b2a3cd391269e55a8fde5ee0a35e29ddece4b5910ec49df69ab62f5e26"
url="win-95-osr-25_202103/Win95_OSR25.iso"
;;
"core11" )
size=3304132608
sum="c0e0252b24144b8defb6c7ded2bc09f9297daf1fb8369b16c5b85382331eb47f"
Expand Down
Loading