From 54e1739640e4d79742146864b0f5a9b16dfbfe15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eligio=20Mari=C3=B1o?= <22875166+gmeligio@users.noreply.github.com> Date: Sun, 16 Mar 2025 22:57:08 +0100 Subject: [PATCH] feat: build windows image (#314) --- .github/workflows/build.yml | 17 +-- .github/workflows/ci.yml | 32 ++--- .github/workflows/release.yml | 8 +- .github/workflows/windows.yml | 99 ++++++++++++++ Dockerfile | 7 +- docker-compose.yml | 9 +- script/build_windows.sh | 5 + ...trypoint.sh => docker_linux_entrypoint.sh} | 0 script/docker_windows_entrypoint.ps1 | 26 ++++ script/setEnvironmentVariables.js | 43 ++++++ script/set_environment_variables.sh | 17 --- windows.Dockerfile | 88 +++++++++++++ windows.md | 124 ++++++++++++++++++ 13 files changed, 419 insertions(+), 56 deletions(-) create mode 100644 .github/workflows/windows.yml create mode 100644 script/build_windows.sh rename script/{docker-entrypoint.sh => docker_linux_entrypoint.sh} (100%) create mode 100644 script/docker_windows_entrypoint.ps1 create mode 100644 script/setEnvironmentVariables.js delete mode 100755 script/set_environment_variables.sh create mode 100644 windows.Dockerfile create mode 100644 windows.md diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e49e679..7d970cb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,23 +32,20 @@ jobs: with: username: ${{ secrets.DOCKER_HUB_USERNAME }} password: ${{ secrets.DOCKER_HUB_TOKEN }} - - - name: Login to GitHub Container Registry - uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ github.token }} - name: Setup CUE uses: cue-lang/setup-cue@a93fa358375740cd8b0078f76355512b9208acb1 # v1.0.1 - - name: Read environment variables from version.json with CUE + - name: Read environment variables from version.json + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 env: GITHUB_REPOSITORY_OWNER: ${{ github.repository_owner }} IMAGE_REPOSITORY_NAME: ${{ env.IMAGE_REPOSITORY_NAME }} VERSION_MANIFEST: ${{ env.VERSION_MANIFEST }} - run: ./script/set_environment_variables.sh + with: + script: | + const script = require('./script/setEnvironmentVariables.js') + return await script({ core }) - name: Load image metadata uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0 @@ -56,8 +53,6 @@ jobs: with: images: | ${{ env.IMAGE_REPOSITORY_PATH }} - ghcr.io/${{ env.IMAGE_REPOSITORY_PATH }} - quay.io/${{ env.IMAGE_REPOSITORY_PATH }} tags: | type=raw,value=${{ env.FLUTTER_VERSION }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c76c61e..7c2cc59 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,29 +27,19 @@ jobs: username: ${{ secrets.DOCKER_HUB_USERNAME }} password: ${{ secrets.DOCKER_HUB_TOKEN }} - - name: Login to GitHub Container Registry - uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ github.token }} - - - name: Login to Quay.io - uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 - with: - registry: quay.io - username: ${{ secrets.QUAY_USERNAME }} - password: ${{ secrets.QUAY_ROBOT_TOKEN }} - - name: Setup CUE uses: cue-lang/setup-cue@a93fa358375740cd8b0078f76355512b9208acb1 # v1.0.1 - - name: Read environment variables from version.json with CUE + - name: Read environment variables from version.json + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 env: GITHUB_REPOSITORY_OWNER: ${{ github.repository_owner }} IMAGE_REPOSITORY_NAME: ${{ env.IMAGE_REPOSITORY_NAME }} VERSION_MANIFEST: ${{ env.VERSION_MANIFEST }} - run: ./script/set_environment_variables.sh + with: + script: | + const script = require('./script/setEnvironmentVariables.js') + return await script({ core }) - name: Load image metadata uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0 @@ -57,8 +47,6 @@ jobs: with: images: | ${{ env.IMAGE_REPOSITORY_PATH }} - ghcr.io/${{ env.IMAGE_REPOSITORY_PATH }} - quay.io/${{ env.IMAGE_REPOSITORY_PATH }} tags: | type=raw,value=${{ env.FLUTTER_VERSION }} @@ -108,12 +96,16 @@ jobs: - name: Setup CUE uses: cue-lang/setup-cue@a93fa358375740cd8b0078f76355512b9208acb1 # v1.0.1 - - name: Read environment variables from version.json with CUE + - name: Read environment variables from version.json + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 env: GITHUB_REPOSITORY_OWNER: ${{ github.repository_owner }} IMAGE_REPOSITORY_NAME: ${{ env.IMAGE_REPOSITORY_NAME }} VERSION_MANIFEST: ${{ env.VERSION_MANIFEST }} - run: ./script/set_environment_variables.sh + with: + script: | + const script = require('./script/setEnvironmentVariables.js') + return await script({ core }) - name: Create Tag for a New Flutter Version uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b33f83b..c968d4c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -54,12 +54,16 @@ jobs: - name: Setup CUE uses: cue-lang/setup-cue@a93fa358375740cd8b0078f76355512b9208acb1 # v1.0.1 - - name: Read environment variables from version.json with CUE + - name: Read environment variables from version.json + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 env: GITHUB_REPOSITORY_OWNER: ${{ github.repository_owner }} IMAGE_REPOSITORY_NAME: ${{ env.IMAGE_REPOSITORY_NAME }} VERSION_MANIFEST: ${{ env.VERSION_MANIFEST }} - run: ./script/set_environment_variables.sh + with: + script: | + const script = require('./script/setEnvironmentVariables.js') + return await script({ core }) - name: Load image metadata uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0 diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml new file mode 100644 index 0000000..773e3ba --- /dev/null +++ b/.github/workflows/windows.yml @@ -0,0 +1,99 @@ +on: + pull_request: + workflow_dispatch: + +# Read-only permissions by default +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.ref_name }} + cancel-in-progress: true + +jobs: + test_image: + permissions: + # Allow to write packages for the docker/scout-action to write a comment + packages: write + # Allow to write pull requests for the docker/scout-action to write a comment + pull-requests: write + # Allow to write security events for github/codeql-action/upload-sarif to upload SARIF results + security-events: write + runs-on: windows-2025 + env: + IMAGE_REPOSITORY_NAME: flutter-android + VERSION_MANIFEST: config/version.json + steps: + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Login to Docker Hub + uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 + with: + username: ${{ secrets.DOCKER_HUB_USERNAME }} + password: ${{ secrets.DOCKER_HUB_TOKEN }} + + - name: Read environment variables from version.json + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + env: + GITHUB_REPOSITORY_OWNER: ${{ github.repository_owner }} + IMAGE_REPOSITORY_NAME: ${{ env.IMAGE_REPOSITORY_NAME }} + VERSION_MANIFEST: ${{ env.VERSION_MANIFEST }} + with: + script: | + const script = require('./script/setEnvironmentVariables.js') + return await script({ core }) + + # - name: Load image metadata + # uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0 + # id: metadata + # with: + # images: | + # ${{ env.IMAGE_REPOSITORY_PATH }} + # tags: | + # type=raw,value=${{ env.FLUTTER_VERSION }} + + # - name: Set up Docker Buildx + # uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0 + + - name: Build image and push to local Docker daemon + shell: powershell + run: | + docker build . -f windows.Dockerfile --build-arg flutter_version=${{ env.FLUTTER_VERSION }} -t ${{ env.IMAGE_REPOSITORY_PATH }} + + # - name: Build image and push to local Docker daemon + # uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0 + # with: + # file: windows.Dockerfile + # load: true + # cache-from: type=gha + # cache-to: type=gha,mode=max + # labels: ${{ steps.metadata.outputs.labels }} + # tags: ${{ steps.metadata.outputs.tags }} + # target: android + # build-args: | + # flutter_version=${{ env.FLUTTER_VERSION }} + + # - name: Test image + # uses: plexsystems/container-structure-test-action@c0a028aa96e8e82ae35be556040340cbb3e280ca # v0.3.0 + # with: + # image: ${{ fromJSON(steps.metadata.outputs.json).tags[0] }} + # config: test/android.yml + + # # TODO: Parallelize testing and vulnerability scanning + # - name: Scan with Docker Scout + # id: docker-scout + # uses: docker/scout-action@0133ff88fe16d4a412dc4827a8fccbccb6b583e0 # v1.16.3 + # with: + # command: compare, recommendations + # # Use the Docker Hub image that is the first tag in the metadata + # image: local://${{ fromJson(steps.metadata.outputs.json).tags[0] }} + # # github-token is needed to be able to write the PR comment + # github-token: ${{ github.token }} + # only-fixed: true + # organization: ${{ secrets.DOCKER_HUB_USERNAME }} + # # sarif-file: output.sarif.json + # to-env: prod + # # Enable debug logging when needed + # # debug: true + # # verbose-debug: true diff --git a/Dockerfile b/Dockerfile index b2701bf..beff5b6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -65,6 +65,7 @@ RUN git clone \ "$FLUTTER_ROOT" \ && chown -R flutter:flutter "$FLUTTER_ROOT" \ && flutter --version \ + && flutter config --no-cli-animations \ && dart --disable-analytics \ && flutter config \ --no-cli-animations \ @@ -79,10 +80,10 @@ RUN git clone \ --no-enable-macos-desktop \ && flutter doctor -COPY --chown=flutter:flutter ./script/docker-entrypoint.sh "$HOME/docker-entrypoint.sh" -RUN chmod +x "$HOME/docker-entrypoint.sh" +COPY --chown=flutter:flutter ./script/docker_linux_entrypoint.sh "$HOME/docker_entrypoint.sh" +RUN chmod +x "$HOME/docker_entrypoint.sh" -ENTRYPOINT [ "/home/flutter/docker-entrypoint.sh" ] +ENTRYPOINT [ "/home/flutter/docker_entrypoint.sh" ] #----------------------------------------------- #----------------------------------------------- diff --git a/docker-compose.yml b/docker-compose.yml index 8ebbdaa..e2fa10e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,6 @@ services: flutter: build: - context: . target: flutter args: flutter_version: $FLUTTER_VERSION @@ -10,7 +9,6 @@ services: fastlane: build: - context: . target: fastlane args: flutter_version: $FLUTTER_VERSION @@ -20,7 +18,6 @@ services: android: build: - context: . target: android args: flutter_version: $FLUTTER_VERSION @@ -31,3 +28,9 @@ services: cmake_version: $CMAKE_VERSION environment: ENABLE_ANALYTICS: $ENABLE_ANALYTICS + + windows: + build: + dockerfile: ./windows.Dockerfile + args: + flutter_version: $FLUTTER_VERSION diff --git a/script/build_windows.sh b/script/build_windows.sh new file mode 100644 index 0000000..104a46c --- /dev/null +++ b/script/build_windows.sh @@ -0,0 +1,5 @@ +# cmake generate +cmake -S . -B ../build/windows/x64 -G "Visual Studio 16 2019" -A x64 -DFLUTTER_TARGET_PLATFORM=windows-x64 + +# cmake build +cmake --build ../build/windows/x64 --config Release --target INSTALL --verbose diff --git a/script/docker-entrypoint.sh b/script/docker_linux_entrypoint.sh similarity index 100% rename from script/docker-entrypoint.sh rename to script/docker_linux_entrypoint.sh diff --git a/script/docker_windows_entrypoint.ps1 b/script/docker_windows_entrypoint.ps1 new file mode 100644 index 0000000..7117b35 --- /dev/null +++ b/script/docker_windows_entrypoint.ps1 @@ -0,0 +1,26 @@ +$analytic_tools_str = "Dart, Flutter and Fastlane" + +if ($env:ENABLE_ANALYTICS -eq "true") { + Write-Output "Received 'ENABLE_ANALYTICS=true'.`nEnabling analytics for $analytic_tools_str." + + dart --enable-analytics + flutter config --analytics + + if (Test-Path env:FASTLANE_OPT_OUT_USAGE) { + Remove-Item env:FASTLANE_OPT_OUT_USAGE + } +} +else { + dart --disable-analytics + flutter --disable-analytics + $env:POWERSHELL_TELEMETRY_OPTOUT = 1 + $env:FASTLANE_OPT_OUT_USAGE = "YES" + # TODO: $env:COCOAPODS_DISABLE_STATS = 1 +} + +if ($args.length -gt 0) { + Invoke-Expression "$args" +} +else { + powershell +} diff --git a/script/setEnvironmentVariables.js b/script/setEnvironmentVariables.js new file mode 100644 index 0000000..9ba8cd0 --- /dev/null +++ b/script/setEnvironmentVariables.js @@ -0,0 +1,43 @@ +module.exports = async ({ core }) => { + const { VERSION_MANIFEST, GITHUB_REPOSITORY_OWNER, IMAGE_REPOSITORY_NAME } = + process.env + + if (!VERSION_MANIFEST) { + core.setFailed('Environment variable VERSION_MANIFEST is required.') + return false + } + + if (!GITHUB_REPOSITORY_OWNER) { + core.setFailed('Environment variable GITHUB_REPOSITORY_OWNER is required.') + return false + } + + if (!IMAGE_REPOSITORY_NAME) { + core.setFailed('Environment variable IMAGE_REPOSITORY_NAME is required.') + return false + } + + const fs = require('fs') + const text = fs.readFileSync(VERSION_MANIFEST, 'utf8') + const data = JSON.parse(text) + + const platforms = data.android.platforms + .map((platform) => platform.version) + .join(' ') + + core.exportVariable('FLUTTER_VERSION', data.flutter.version) + core.exportVariable('FASTLANE_VERSION', data.fastlane.version) + core.exportVariable( + 'ANDROID_BUILD_TOOLS_VERSION', + data.android.buildTools.version + ) + core.exportVariable('ANDROID_PLATFORM_VERSIONS', platforms) + core.exportVariable('ANDROID_NDK_VERSION', data.android.ndk.version) + core.exportVariable('CMAKE_VERSION', data.android.cmake.version) + core.exportVariable( + 'IMAGE_REPOSITORY_PATH', + `${GITHUB_REPOSITORY_OWNER}/${IMAGE_REPOSITORY_NAME}` + ) + + return true +} diff --git a/script/set_environment_variables.sh b/script/set_environment_variables.sh deleted file mode 100755 index e5d02be..0000000 --- a/script/set_environment_variables.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env bash - -{ - echo "FLUTTER_VERSION=$(cue eval -e 'flutter.version' "$VERSION_MANIFEST" | tr -d '"')" - - echo "FASTLANE_VERSION=$(cue eval -e 'fastlane.version' "$VERSION_MANIFEST" | tr -d '"')" - - echo "ANDROID_BUILD_TOOLS_VERSION=$(cue eval -e 'android.buildTools.version' "$VERSION_MANIFEST" | tr -d '"')" - - echo "ANDROID_PLATFORM_VERSIONS=$(cue eval -e 'strings.Join([for p in android.platforms {"\(p.version)"}], " ")' "$VERSION_MANIFEST" | tr -d '"\n')" - - echo "ANDROID_NDK_VERSION=$(cue eval -e 'android.ndk.version' "$VERSION_MANIFEST" | tr -d '"')" - - echo "CMAKE_VERSION=$(cue eval -e 'android.cmake.version' "$VERSION_MANIFEST" | tr -d '"')" - - echo "IMAGE_REPOSITORY_PATH=$GITHUB_REPOSITORY_OWNER/$IMAGE_REPOSITORY_NAME" -} >>"$GITHUB_ENV" diff --git a/windows.Dockerfile b/windows.Dockerfile new file mode 100644 index 0000000..6fafe02 --- /dev/null +++ b/windows.Dockerfile @@ -0,0 +1,88 @@ +# escape=` + +FROM mcr.microsoft.com/windows/servercore:ltsc2022@sha256:b7b2e5b4c2414400c4eef13db747376e0f10ef8e15b8d0587ef5b953ad4e6d43 as flutter + +SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"] + +ARG git_version=2.46.0 +ARG git_installation_path="C:\Program Files\Git" + +# TODO: Find a way to pass $env:USERPROFILE instead of hardcoding C:\Users\ContainerUser. It's hardcoded because environment variables in Windows container works by setting for the Machine scope and that will have $env:USERPROFILE as C:\Users\ContainerAdministrator instead. +ENV USERPROFILE="C:\Users\ContainerUser" +ENV SDK_ROOT="${USERPROFILE}\sdks" +ENV FLUTTER_ROOT="${SDK_ROOT}\flutter" +# Set FLUTTER_GIT_URL to fix warning: "Upstream repository unknown source is not a standard remote. Set environment variable "FLUTTER_GIT_URL" to unknown source to dismiss this error." +ENV FLUTTER_GIT_URL="unknown source" + +WORKDIR "$USERPROFILE" + +# Install Git because is required by Flutter +RUN $installer = \"MinGit-${env:git_version}-busybox-64-bit.zip\"; ` + $url = \"https://github.com/git-for-windows/git/releases/download/v${env:git_version}.windows.1/${installer}\"; ` + Invoke-WebRequest -Uri "$url" -OutFile "$installer"; ` + Expand-Archive -Path "$installer" -DestinationPath "$env:git_installation_path"; ` + Remove-Item -Path "$installer"; + +# The user ContainerAdministrator must be used because is the one that has permissions to set the system PATH +USER ContainerAdministrator + +# The PATH variable will be updated in the next shell session, so the RUN command that sets the PATH needs to be separated from the one that uses it +RUN [Environment]::SetEnvironmentVariable('PATH', \"${env:PATH};${env:git_installation_path}\cmd;${env:git_installation_path}\usr\bin;${env:FLUTTER_ROOT}\bin;${env:FLUTTER_ROOT}\bin\cache\dart-sdk\bin;C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\msbuild\current\bin\", 'Machine'); + +# MinGit has a circular reference in its global configuration, which causes git to crash +# See https://github.com/git-for-windows/git/issues/2387#issuecomment-679367609 +# hadolint ignore=DL3059 +RUN $env:GIT_CONFIG_NOSYSTEM=1; git config --system --unset-all include.path; + +# Switch to the non-admin user when the admin user is not needed anymore +USER ContainerUser + +ARG flutter_version + +RUN git clone ` + --depth 1 ` + --branch "$env:flutter_version" ` + https://github.com/flutter/flutter ` + "$env:FLUTTER_ROOT"; ` + # To fix fatal: detected dubious ownership in repository at 'C:/Users/ContainerUser/sdks/flutter/.git' owned by BUILTIN/Administrators but the current user is: User Manager/ContainerUser + git config --global --add safe.directory "$env:FLUTTER_ROOT"; ` + flutter --version; ` + dart --disable-analytics; ` + flutter config ` + --no-cli-animations ` + --no-analytics ` + --no-enable-android ` + --no-enable-web ` + --no-enable-linux-desktop ` + --enable-windows-desktop ` + --no-enable-fuchsia ` + --no-enable-custom-devices ` + --no-enable-ios ` + --no-enable-macos-desktop; ` + flutter doctor --verbose; ` + flutter precache --windows; ` + flutter create build_app; + + +# The user ContainerAdministrator must be used because is the one that has permissions to install with vs_BuildTools +USER ContainerAdministrator +# Download the Build Tools bootstrapper +# See https://learn.microsoft.com/en-us/visualstudio/install/build-tools-container?view=vs-2022 +RUN Invoke-WebRequest -Uri https://aka.ms/vs/17/release/vs_buildtools.exe -OutFile vs_BuildTools.exe; ` + Start-Process vs_BuildTools.exe -ArgumentList '--quiet --wait --norestart --nocache ` + --add Microsoft.VisualStudio.Component.VC.CMake.Project ` + --add Microsoft.VisualStudio.Component.Windows11SDK.22621 ` + --add Microsoft.VisualStudio.Workload.VCTools' ` + -Wait; ` + Remove-Item vs_BuildTools.exe; +USER ContainerUser + +WORKDIR "$USERPROFILE/build_app" +RUN flutter build windows; + +WORKDIR "$USERPROFILE" +COPY ./script/docker_windows_entrypoint.ps1 "docker_entrypoint.ps1" + +ENTRYPOINT "C:\Users\ContainerUser\docker_entrypoint.ps1" + +RUN Remove-Item -Recurse build_app; diff --git a/windows.md b/windows.md new file mode 100644 index 0000000..96f88ba --- /dev/null +++ b/windows.md @@ -0,0 +1,124 @@ +# Windows + +## Swich between Linux and Windows containers + +& $Env:ProgramFiles\Docker\Docker\DockerCli.exe -SwitchDaemon + +## TODO + +1. Install tools + +```powershell` + # # needed? No +# --add Microsoft.Component.MSBuild' ` + # # needed? No + # --add Microsoft.VisualStudio.Component.TestTools.BuildTools ` + # # needed? No + # --add Microsoft.VisualStudio.Component.VC.ASAN ` + # # needed? no + # # --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 ` +RUN Invoke-WebRequest -Uri https://aka.ms/vs/17/release/vs_buildtools.exe -OutFile vs_BuildTools.exe; ` + Start-Process vs_BuildTools.exe -ArgumentList '--quiet --wait --norestart --nocache ` + # # needed? yes + # --add Microsoft.VisualStudio.Component.VC.CMake.Project ` + # # needed? Yes + # --add Microsoft.VisualStudio.Component.Windows11SDK.22621 ` + # # needed? + # --add Microsoft.VisualStudio.Workload.VCTools' ` + -Wait; ` + Remove-Item vs_BuildTools.exe; +``` + +1. Check how it can be run in Github actions. +1. Check how it can be run in Gitlab CI/CD. +1. Test where is installed. +1. Test that path to powershell.exe exists. +1. Test with a snapshot of flutter config to determine if new feature flags should be enabled or disabled. +1. Test that Build Tools were installed in C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\msbuild\current\bin +1. Check [Windows installation requirements for Flutter](https://docs.flutter.dev/get-started/install/windows/desktop) +1. Add docs explaining to use `$VerbosePreference = 'Continue';` in the SHELL to debug unexpected pwsh problems. + +## Open issue in windows Docker images repo + +1. Some images can be pulled while others give error: + + ```text + Error response from daemon: Get "https://mcr.microsoft.com/v2/": read tcp [2a0c:5a84:e100:e501::a97c]:58039->[2603:1061:f:101::10]:443: wsarecv: An existing connection was forcibly closed by the remote host. + ``` + +Debug with `curl -A github165 -v https://mcr.microsoft.com/v2/powershell/manifests/lts-nanoserver-ltsc2022` + +## Contribute flutter upstream + +1. Remove `WHERE` in bin\internal\shared.bat and use instead: + + ```batch + pwsh.exe -Command "exit" >nul 2>&1 && ( + SET powershell_executable=pwsh.exe + ) || powershell.exe -Command "exit" >nul 2>&1 && ( + SET powershell_executable=PowerShell.exe + ) || ( + ECHO Error: PowerShell executable not found. 1>&2 + ECHO Either pwsh.exe or PowerShell.exe must be in your PATH. 1>&2 + EXIT 1 + ) + ``` + +1. Find if the executable should be pwsh or powershell and put it in a service to remove the hardcoded "powershell" in multiple places, like in: + + - dev\devicelab\lib\framework\running_processes.dart + - packages\flutter_tools\lib\src\windows\windows_version_validator.dart + +## Steps to reproduce in Docker + +1. Enable Windows Developer Settings to solve error: + + >Building with plugins requires symlink support. + > + >Please enable Developer Mode in your system settings. Run + > start ms-settings:developers + >to open settings. + + ```powershell + reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock" /t REG_DWORD /f /v "AllowDevelopmentWithoutDevLicense" /d "1" + ``` + +1. For CI/CD + + 1. Docker version must be pinned in Github workflow to avoid breaking changes: with escaping `\"` syntax inside RUN directive, etc. + + 1. Packaging tool in Windows: . It uses the executables: + + - [makeappx.exe](https://learn.microsoft.com/en-us/windows/win32/appxpkg/make-appx-package--makeappx-exe-) + - [makepri.exe](https://learn.microsoft.com/en-us/windows/uwp/app-resources/makepri-exe-command-options) + - [signtool.exe](https://learn.microsoft.com/en-us/dotnet/framework/tools/signtool-exe) + + - certificate + - Make a note that --install-certificate should be "false" or configured because the certificate can't be installed as ContainerUser. + + ```powershell + # OK + Import-PfxCertificate -FilePath "C:\Users\ContainerUser\AppData\Local\Pub\Cache\hosted\pub.dev\msix-3.16.8\lib\assets\test_certificate.pfx" -Password (ConvertTo-SecureString -AsPlainText -Force "1234") -CertStoreLocation Cert:\LocalMachine\Root + + # Doesn't work + Import-PfxCertificate -FilePath "C:\Users\ContainerUser\AppData\Local\Pub\Cache\hosted\pub.dev\msix-3.16.8\lib\assets\test_certificate.pfx" -Password (ConvertTo-SecureString -AsPlainText -Force "1234") + ``` + + 1. Install msstore CLI https://github.com/microsoft/msstore-cli It seems behind StoreBroker but it looks that it's going to be the primary and recommended way to publish to Microsoft Store + + - According to the [msstore guide](https://learn.microsoft.com/en-us/windows/apps/publish/msstore-dev-cli/commands?pivots=msstoredevcli-installer-linux#installation), It will be needed to install Microsoft.NetCore.Component.Runtime.8.0 with vs_BuildTools + + 1. From => install This is currently the primary tool to publish to Microsoft Store + + - Not installed right now + + 1. Install the [Windows App Certification Kit](https://learn.microsoft.com/en-us/windows/uwp/debug-test-perf/windows-app-certification-kit) or the [Windows SDK that already includes it](https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/) + + - Installed currently by one of the workloads in vs_BuildTools + +## References + +- [How environment variables work on Windows containers?](https://blog.sixeyed.com/windows-weekly-dockerfile-14-environment-variables/) +- [Windows deployment in Flutter](https://docs.flutter.dev/deployment/windows) +- [vs_BuildTools workloads](https://learn.microsoft.com/en-us/visualstudio/install/workload-component-id-vs-build-tools?view=vs-2022&preserve-view=true) +- Useful Dockerfile https://git.openprivacy.ca/openprivacy/flutter-desktop/src/branch/main/windows/Dockerfile