diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8592234..4d054fd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -308,29 +308,18 @@ jobs: name: nuget-packages path: artifacts + # The script asserts the result itself: require_windows_frameworks reads the lib// folders + # out of every merged package and fails the step if any of them carries no Windows assets, so + # a pack that quietly emitted no -windows target framework cannot reach the upload below. + # Repeating that here as a shell one-liner was worse than nothing: with `-o pipefail`, which + # this shell sets, `unzip -l pkg | grep -q ...` reports failure whenever grep matches early + # enough to exit before unzip has finished writing, because unzip then dies of SIGPIPE and its + # 141 becomes the pipeline's status. That is a coin toss on the package's entry order, and it + # came up tails on a package that did have its Windows assets. - name: Pack the Windows heads and merge them in shell: bash run: ./build/AddWindowsAssets.sh "${{ inputs.version }}" - # Asserted rather than assumed: the script merges whatever the pack produced, and a pack that - # quietly emitted no Windows target framework — because the workload was missing, or because - # the IsOSPlatform condition in OpenTok.Net.props stopped matching — would still exit 0 and - # upload the unchanged package. - - name: Check the Windows assets are actually in there - shell: bash - run: | - missing=0 - for id in OpenTok.Net OpenTok.Net.Maui; do - pkg="$(ls artifacts/${id}.*.nupkg | grep -v symbols | head -1)" - if unzip -l "${pkg}" | grep -q 'lib/net9.0-windows'; then - echo " ok ${id}" - else - echo "::error::${pkg} has no lib/net9.0-windows*/ assets after the merge" - missing=1 - fi - done - exit "${missing}" - - name: Upload packages uses: actions/upload-artifact@v4 with: @@ -513,7 +502,12 @@ jobs: # # Unlike the Debug sample job, this cannot be fixed by installing both workloads: that job # runs entirely on macOS, where both exist. This one deliberately builds Android on Linux. + # shell: bash on this step and the next, because the windows leg would otherwise get pwsh — + # which reads the line continuations below as an operator and fails to parse the command + # before ever running it. The two legs on macOS and Linux already had bash by default, which + # is why this only ever surfaced here. - name: Build the sample in Release + shell: bash run: | dotnet build samples/OpenTok.Sample.Maui/OpenTok.Sample.Maui.csproj \ --configuration Release \ @@ -525,6 +519,7 @@ jobs: # .app is assembled, and on Android r8 and the packaging step run before the APK is. A failure # in any of them never gets far enough to produce one. - name: Check the app was produced + shell: bash run: | case "${{ matrix.platform }}" in ios) diff --git a/build/AddWindowsAssets.sh b/build/AddWindowsAssets.sh index 44adc97..f17a91e 100755 --- a/build/AddWindowsAssets.sh +++ b/build/AddWindowsAssets.sh @@ -50,6 +50,39 @@ esac PACKAGES=$(grep -v '^#' packages.tsv | grep -v '^[[:space:]]*$' | cut -f1) +# The lib// folders a package carries for Windows, one per line, or nothing at all. +# +# Read from the zip rather than from the merge's own report: merge-packages.py prints the target +# frameworks it *added*, and a package can advertise a target framework in its nuspec while +# carrying no assets for it — which restores, and then fails to compile at whoever picked it. +windows_frameworks() { + python3 - "$1" <<'PY' +import sys +import zipfile + +with zipfile.ZipFile(sys.argv[1]) as package: + frameworks = { + name.split("/")[1] + for name in package.namelist() + if name.startswith("lib/") and "-windows" in name.split("/")[1] and name.count("/") > 1 + } + +print("\n".join(sorted(frameworks))) +PY +} + +# Fails unless the package carries Windows assets, naming it and listing what it does carry. +require_windows_frameworks() { + frameworks=$(windows_frameworks "$1" | tr '\n' ' ') + + if [ -z "$(echo "$frameworks" | tr -d ' ')" ]; then + echo "error: $(basename "$1") carries no lib// assets for Windows." >&2 + exit 1 + fi + + echo " $(basename "$1"): $frameworks" +} + if [ -z "$PACKAGES" ]; then echo "error: no packages found in build/packages.tsv" >&2 exit 1 @@ -74,14 +107,18 @@ fi WIN1_DIR="$OUTPUT/.win9-pass" WIN2_DIR="$OUTPUT/.win10-pass" +PRIMARY_DIR="$OUTPUT/.primary" MERGED_DIR="$OUTPUT/.merged" SDK10_DIR="$(mktemp -d)" -trap 'rm -rf "$SDK10_DIR" "$WIN1_DIR" "$WIN2_DIR" "$MERGED_DIR"' EXIT +trap 'rm -rf "$SDK10_DIR" "$WIN1_DIR" "$WIN2_DIR" "$PRIMARY_DIR" "$MERGED_DIR"' EXIT cat > "$SDK10_DIR/global.json" < packing $package windows heads ($PASS1_BAND band)" dotnet pack "$project" \ @@ -108,13 +145,43 @@ for package in $PACKAGES; do $VERSION_ARG \ -o "$WIN2_DIR") + # merge-packages.py merges every package it finds in the primary directory, so the primary + # here cannot be artifacts/ itself: that holds all the packages, while the Windows passes hold + # only the one just packed, and every other id would be reported as having no counterpart. + # Stage just this package's files — the pass directory names them, so no version is needed. + echo "==> merging windows target frameworks into $package" + mkdir -p "$PRIMARY_DIR" + nupkg="" + for asset in "$WIN1_DIR"/*.nupkg "$WIN1_DIR"/*.snupkg; do + [ -f "$asset" ] || continue + name=$(basename "$asset") + if [ ! -f "$OUTPUT/$name" ]; then + echo "error: $name is not in $OUTPUT — run build/BuildNugets.sh on macOS first and" >&2 + echo " bring its artifacts/ here, at the same version." >&2 + exit 1 + fi + cp "$OUTPUT/$name" "$PRIMARY_DIR/$name" + case "$name" in *.nupkg) nupkg="$name" ;; esac + done + # Merged in two steps because merge-packages.py takes one additional directory at a time, and # into a scratch directory because it will not read and write the same place. - echo "==> merging windows target frameworks into $package" - python3 "$ROOT/build/merge-packages.py" "$OUTPUT" "$WIN1_DIR" "$MERGED_DIR" + python3 "$ROOT/build/merge-packages.py" "$PRIMARY_DIR" "$WIN1_DIR" "$MERGED_DIR" python3 "$ROOT/build/merge-packages.py" "$MERGED_DIR" "$WIN2_DIR" "$OUTPUT" + + # Straight after the merge rather than only at the end, because the next package is packed + # against this one out of artifacts/ — so a package that came out of the merge without its + # Windows assets would otherwise be discovered later, having already been built against. + require_windows_frameworks "$OUTPUT/$nupkg" + MERGED_PACKAGES="$MERGED_PACKAGES $nupkg" done -rm -rf "$WIN1_DIR" "$WIN2_DIR" "$MERGED_DIR" +rm -rf "$WIN1_DIR" "$WIN2_DIR" "$PRIMARY_DIR" "$MERGED_DIR" -echo "==> windows assets added to $OUTPUT" +# And again over the finished directory. Everything above packs and merges one package while the +# rest sit in the same directory, so "each package was right when it was merged" is not the same +# claim as "every package is still right now". +echo "==> windows assets in $OUTPUT" +for nupkg in $MERGED_PACKAGES; do + require_windows_frameworks "$OUTPUT/$nupkg" +done diff --git a/samples/OpenTok.Sample.Maui/OpenTok.Sample.Maui.csproj b/samples/OpenTok.Sample.Maui/OpenTok.Sample.Maui.csproj index eaf5c48..a19d6b4 100644 --- a/samples/OpenTok.Sample.Maui/OpenTok.Sample.Maui.csproj +++ b/samples/OpenTok.Sample.Maui/OpenTok.Sample.Maui.csproj @@ -110,4 +110,30 @@ + + + + <_OpenTokMissingPriPayloadFiles Include="@(_AllChildProjectItemsWithTargetPath)" + Condition=" !Exists('%(FullPath)') " /> + <_AllChildProjectItemsWithTargetPath Remove="@(_OpenTokMissingPriPayloadFiles)" /> + + + + + diff --git a/samples/OpenTok.Sample.Maui/Platforms/Windows/App.xaml b/samples/OpenTok.Sample.Maui/Platforms/Windows/App.xaml index f16a475..1e9789f 100644 --- a/samples/OpenTok.Sample.Maui/Platforms/Windows/App.xaml +++ b/samples/OpenTok.Sample.Maui/Platforms/Windows/App.xaml @@ -2,5 +2,5 @@ x:Class="OpenTok.Sample.Maui.WinUI.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:maui="using:Microsoft.Maui.MauiWinUIApplication"> + xmlns:maui="using:Microsoft.Maui"> diff --git a/src/OpenTok.Net.Maui/OpenTok.Net.Maui.csproj b/src/OpenTok.Net.Maui/OpenTok.Net.Maui.csproj index c7cfde6..3c16a0a 100644 --- a/src/OpenTok.Net.Maui/OpenTok.Net.Maui.csproj +++ b/src/OpenTok.Net.Maui/OpenTok.Net.Maui.csproj @@ -53,4 +53,52 @@ + + + + + + + + + <_OpenTokMissingPriPayloadFiles Include="@(_AllChildProjectItemsWithTargetPath)" + Condition=" !Exists('%(FullPath)') " /> + <_AllChildProjectItemsWithTargetPath Remove="@(_OpenTokMissingPriPayloadFiles)" /> + + + + + diff --git a/src/OpenTok.Net/OpenTok.Net.csproj b/src/OpenTok.Net/OpenTok.Net.csproj index 9444020..37f447e 100644 --- a/src/OpenTok.Net/OpenTok.Net.csproj +++ b/src/OpenTok.Net/OpenTok.Net.csproj @@ -42,8 +42,30 @@ own reference list rather than arriving transitively. --> - - + + +