aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/action.yml
blob: 27e479994b38b425dd91243b909be253caa607a7 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
name: Visual Studio shell
description: Set up Visual Studio paths and environment variables

inputs:
  arch:
    description: Target architecture
    required: false
    default: x64

runs:
  using: composite
  steps:
    - run: |
        function Normalize-Arch {
            param(
                [Parameter(Mandatory=$true)]
                [string] $Arch
            )
            if ($Arch -eq 'Win32') {
                # 'Win32' is a common name for x86, and the scripts seem to
                # only support 'x86'.
                'x86'
            } else {
                # Default to the user-provided value, in case something like
                # 'ARM' is supported.
                $arch
            }
        }

        New-Variable arch -Value (Normalize-Arch '${{ inputs.arch }}') -Option Constant

        function Locate-VSWhere {
            $path = Get-Command 'vswhere' -ErrorAction SilentlyContinue
            if ($path) {
                $path.Path
            } else {
                Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio' 'Installer' 'vswhere'
            }
        }

        function Locate-VS {
            # vswhere doesn't search for Build Tools by default, God knows why.
            # https://github.com/microsoft/vswhere/issues/22
            $products = 'Community','Professional','Enterprise','BuildTools' | %{ "Microsoft.VisualStudio.Product.$_" }
            $vswhere = Locate-VSWhere
            & $vswhere -products $products -latest -format json | ConvertFrom-Json
        }

        function Import-PS {
            # VS 2019 includes Microsoft.VisualStudio.DevShell.dll, which does
            # what we want.
            #
            # It also includes Launch-VsDevShell.ps1, which sort of does the
            # same thing this function does, but it always sets up 32-bit
            # shell, which is stupid.  I use the same workaround as in
            # https://developercommunity.visualstudio.com/idea/943058/x64-developer-powershell-for-vs-2019.html

            param(
                [Parameter(Mandatory=$true)]
                [object] $Info,
                [Parameter(Mandatory=$true)]
                [string] $Arch
            )

            $tools_path = Join-Path $Info.installationPath 'Common7' 'Tools'
            $module_name = 'Microsoft.VisualStudio.DevShell.dll'
            $module_path = Join-Path $tools_path $module_name

            if (!(Test-Path $module_path -Type Leaf)) {
                $module_path = Join-Path $tools_path 'vsdevshell' $module_name
            }

            if (!(Test-Path $module_path -Type Leaf)) {
                throw "Couldn't find module '$module_name'"
            }

            Import-Module $module_path
            Enter-VsDevShell -VsInstanceId $Info.instanceId -SkipAutomaticLocation -DevCmdArguments "-arch=$Arch -no_logo"
        }

        function Import-CMD {
            # For Visual Studio 2017, there's no Microsoft.VisualStudio.DevShell.dll,
            # so we have to work around that.  One workaround I found here:
            # https://github.com/microsoft/vswhere/issues/150

            param(
                [Parameter(Mandatory=$true)]
                [object] $Info,
                [Parameter(Mandatory=$true)]
                [string] $Arch
            )

            $tools_path = Join-Path $Info.installationPath 'Common7' 'Tools'
            $script_name = 'VsDevCmd.bat'
            $script_path = Join-Path $tools_path $script_name

            if (!(Test-Path $script_path -Type Leaf)) {
                throw "Couldn't find script '$script_name'"
            }

            # OK, the following issue is royally stupid.  At one point, I
            # started getting errors like this:
            #
            #     Conversion from JSON failed with error: Unexpected character encountered while parsing value: W.
            #
            # They appeared out of nowhere, without any changes in the code.
            # Turns out, ConvertTo-Json is pretty much unusable by default.
            # See, it has a `-Depth` parameter, which has a default value of 2,
            # which is insanely low.  I don't know what it does if the input
            # exceeds the nesting depth of 2, but I do know that starting from
            # Powershell 7.1, ConvertTo-Json prints a "helpful" warning in this
            # case.  The warning looks like this:
            #
            #     WARNING: Resulting JSON is truncated as serialization has exceeded the set depth of 2.
            #
            # Apparently, PowerShell on windows-2016 images got upgraded, hence
            # the failure to parse the letter W.  I _accidentally_ fixed it by
            # adding "Select-Object Name,Value" to the pipeline, cutting the
            # nesting depth, so keeping that part in the pipeline is important.
            #
            # See also:
            #
            #     * https://github.com/PowerShell/PowerShell/issues/8393
            #     * https://stackoverflow.com/q/53583677/514684
            #     * https://stackoverflow.com/q/53562942/514684

            $json = $(& "${env:COMSPEC}" /s /c "`"$script_path`" -arch=$Arch -no_logo && pwsh -Command `"Get-ChildItem env: | Select-Object Name,Value | ConvertTo-Json`"")
            if ($LASTEXITCODE -ne 0) {
                throw $json
            } else {
                try {
                    $json | ConvertFrom-Json | %{ Set-Content "env:$($_.Name)" $_.Value }
                } catch {
                    echo 'There was a problem with the following JSON:'
                    echo $json
                    throw
                }
            }
        }

        if ('${{ runner.os }}' -ne 'Windows') {
            echo 'Not going to set up a Visual Studio shell on ${{ runner.os }}'
        } else {
            $old_values = @{}
            Get-ChildItem env: | %{ $old_values.Add($_.Name, $_.Value) }

            $info = Locate-VS
            try {
                Import-PS $info $arch
            } catch {
                echo $_
                Import-CMD $info $arch
            }

            $new_values = @{}
            Get-ChildItem env: | %{ $new_values.Add($_.Name, $_.Value) }

            # Some diagnostics as to which exact variables were modified follows:
            echo '----------------------------------------------------------------'
            echo 'New variables'
            echo '----------------------------------------------------------------'
            $new_values.GetEnumerator() | where { -not $old_values.ContainsKey($_.Name) } | Format-List

            echo '----------------------------------------------------------------'
            echo 'Modified variables'
            echo '----------------------------------------------------------------'
            $new_values.GetEnumerator() | where { $old_values.ContainsKey($_.Name) -and $old_values[$_.Name] -ne $_.Value } | Format-List

            # Update the current environment:
            Get-ChildItem env: | %{ echo "$($_.Name)=$($_.Value)" >> $env:GITHUB_ENV }
        }
      shell: pwsh

branding:
  icon: star
  color: green