1 # -*- coding: utf-8 -*-
2 #
3 # Copyright (C) 2016-2021 Edgewall Software
4 # Copyright (C) 2016 Christian Boos <cboos@edgewall.org>
5 # All rights reserved.
6 #
7 # This software is licensed as described in the file COPYING, which
8 # you should have received as part of this distribution. The terms
9 # are also available at https://trac.edgewall.com/license.html.
10 #
11 # This software consists of voluntary contributions made by many
12 # individuals. For the exact contribution history, see the revision
13 # history and logs, available at https://trac.edgewall.org/.
14 
15 # ------------------------------------------------------------------
16 #  This is a PowerShell script implementing the build steps used by
17 #  the AppVeyor Continuous Delivery service for Windows, Trac project.
18 #
19 #  The builds results are published at:
20 #
21 #    https://ci.appveyor.com/project/edgewall/trac
22 #
23 #  or, for Git topic branches pushed on GitHub forks, at:
24 #
25 #    https://ci.appveyor.com/project/<developer-user-id>/trac
26 #
27 # ------------------------------------------------------------------
28 
29 # ------------------------------------------------------------------
30 # Settings
31 # ------------------------------------------------------------------
32 
33 # Update the following variables to match the current build
34 # environment on AppVeyor.
35 #
36 # See in particular:
37 #  - https://www.appveyor.com/docs/installed-software#python
38 #  - https://www.appveyor.com/docs/installed-software#mingw-msys-cygwin
39 #  - https://www.appveyor.com/docs/services-databases#mysql
40 #  - https://www.appveyor.com/docs/services-databases#postgresql
41 
42 $msysHome = 'C:\msys64\usr\bin'
43 $deps     = 'C:\projects\dependencies'
44 
45 $mysqlHome = 'C:\Program Files\MySql\MySQL Server 5.7'
46 $mysqlPwd  = 'Password12!'
47 
48 $pgHome     = 'C:\Program Files\PostgreSQL\9.3'
49 $pgUser     = 'postgres'
50 $pgPassword = 'Password12!'
51 
52 $firefoxHome = 'C:\Program Files\Mozilla Firefox'
53 
54 # External Python dependencies
55 
56 $pipPackages = @(
57     'setuptools',
58     'jinja2',
59     'babel',
60     'docutils',
61     'passlib',
62     'pygments',
63     'pytz',
64     'textile',
65     'wheel',
66     'selenium'
67 )
68 
69 $condaPackages = @(
70     'lxml'
71 )
72 
73 $svnBase = "svn-win32-1.8.15"
74 $svnBaseAp = "$svnBase-ap24"
75 $svnUrlBase = "https://sourceforge.net/projects/win32svn/files/1.8.15/apache24"
76 
77 # ------------------------------------------------------------------
78 # "Environment" environment variables
79 # ------------------------------------------------------------------
80 
81 # In the build matrix, we can set arbitrary environment variables
82 # which together define the software configuration that will be
83 # tested.
84 
85 # These variables are:
86 #  - PYTHONHOME: the version of python we are testing
87 #  - TRAC_TEST_DB_URI: the database backend we are testing
88 #  - SKIP_ENV: don't perform any step with this environment (optional)
89 #  - SKIP_BUILD: don't execute the Build step for this environment (optional)
90 #  - SKIP_TESTS: don't execute the Tests step for this environment (optional)
91 #
92 # Note that any combination should work, except for MySQL which can
93 # only be installed conveniently from a Conda version of Python.
94 
95 # "Aliases"
96 
97 $pyHome = $env:PYTHONHOME
98 $usingMysql      = $env:TRAC_TEST_DB_URI -match '^mysql:'
99 $usingPostgresql = $env:TRAC_TEST_DB_URI -match '^postgres:'
100 $usingFirefox    = $pipPackages -contains 'selenium'
101 $skipInstall = [bool]$env:SKIP_ENV
102 $skipBuild   = $env:SKIP_BUILD -or $env:SKIP_ENV
103 $skipTests   = $env:SKIP_TESTS -or $env:SKIP_ENV
104 
105 
106 # ------------------------------------------------------------------
107 # Utilities
108 # ------------------------------------------------------------------
109 
110 # Documentation for AppVeyor API (Add-AppveyorMessage, etc.) can be
111 # found at: https://www.appveyor.com/docs/build-worker-api
112 
Write-Step([string]$name, [bool]$skip)113 function Write-Step([string]$name, [bool]$skip) {
114     if ($skip) {
115         $message = "Skipping step $name"
116         Write-Host $message
117         Add-AppveyorMessage -Message $message
118     }
119     else {
120         Write-Host @"
121 
122 ------------------------------------------------------------------
123 $name
124 ------------------------------------------------------------------
125 "@
126     }
127 }
128 
129 # Make it easier to run the tests locally, for debugging.
130 #
131 # Note that for this you may need to enable sourcing local scripts,
132 # from your PowerShell console:
133 #
134 #   Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope Process
135 #
136 #   . .\contrib\appveyor.ps1
137 #
138 # See https://trac.edgewall.org/wiki/AppVeyor for additional info.
139 
140 if (-not $env:APPVEYOR) {
Debug-Callernull141     function Debug-Caller {
142         $caller = (Get-Variable MyInvocation -Scope 1).Value.MyCommand.Name
143         Write-Debug "$caller $args"
144     }
Add-AppveyorMessage()145     function Add-AppveyorMessage() { Debug-Caller @args }
Add-AppveyorTest()146     function Add-AppveyorTest() { Debug-Caller @args }
Update-AppveyorTest()147     function Update-AppveyorTest() { Debug-Caller @args }
Push-AppveyorArtifact()148     function Push-AppveyorArtifact() { Debug-Caller @args }
149 }
150 
151 
152 # ------------------------------------------------------------------
153 # Prologue
154 # ------------------------------------------------------------------
155 
156 # Actions common to all steps (set up the PATH, determine Python version...)
157 
158 $pyV = [string](& python.exe -c 'import sys; print sys.version' 2>&1)
159 $pyVersion = if ($pyV -match '^(\d\.\d)') { $Matches[1] }
160 $py64 = ($pyV -match '64 bit')
161 $pyIsConda = $pyV -match 'Continuum Analytics'
162 
163 # ------------------------------------------------------------------
164 # Steps
165 # ------------------------------------------------------------------
166 
Trac-Install()167 function Trac-Install {
168 
169     $env:Path = "$pyHome;$pyHome\Scripts;$pyHome\Library\bin;$msysHome;$($env:Path)"
170 
171     # Subversion support
172     if (-not $py64) {
173         $env:Path = "$deps\$svnBase\bin;$($env:Path)"
174         $env:PYTHONPATH = "$deps\$pyVersion\$svnBase\python;$($env:PYTHONPATH)"
175     }
176 
177     if ($usingFirefox) {
178         $env:Path = "$($env:Path);$firefoxHome"
179     }
180 
181     Write-Step -Name INSTALL -Skip $skipInstall
182 
183     if ($skipInstall) {
184         return
185     }
186 
187     if (-not (Test-Path $deps)) {
188         & mkdir $deps
189     }
190 
191     # Subversion support via win32svn project, for Python 2.7 32-bits
192 
193     if (-not $py64) {
194         $svnBinariesZip = "$deps\$svnBaseAp.zip"
195         if (-not (Test-Path $svnBinariesZip)) {
196             & curl.exe -Ss -L -o $svnBinariesZip `
197                 "$svnUrlBase/$svnBaseAp.zip/download"
198             & unzip.exe $svnBinariesZip -d $deps
199         }
200 
201         $svnPython = "$($svnBaseAp)_py$($pyVersion -replace '\.', '')"
202         $svnPythonZip = "$deps\$svnPython.zip"
203         if (-not (Test-Path $svnPythonZip)) {
204             & curl.exe -Ss -L -o $svnPythonZip `
205                 "$svnUrlBase/$svnPython.zip/download"
206             & mkdir "$deps\$pyVersion"
207             & unzip $svnPythonZip -d "$deps\$pyVersion"
208         }
209     }
210 
211     # Install packages via pip
212 
213     & python.exe -m pip install -U pip
214     & pip.exe --version
215     & pip.exe install -U $pipPackages
216 
217     if ($pyIsConda) {
218         & conda.exe install -qy $condaPackages
219     }
220 
221     if ($usingMysql) {
222         #
223         # $TRAC_TEST_DB_URI="mysql://tracuser:password@localhost/trac"
224         #
225 
226         # Conda provides PyMySQL support for Windows (x86 and x64)
227 
228         & conda.exe install -qy pymysql
229 
230         Add-AppveyorMessage -Message "1.1. pymysql package installed" `
231           -Category Information
232     }
233     elseif ($usingPostgresql) {
234         #
235         # $TRAC_TEST_DB_URI=
236         # "postgres://tracuser:password@localhost/trac?schema=tractest"
237         #
238 
239         & pip.exe install psycopg2
240 
241         Add-AppveyorMessage -Message "1.1. psycopg2 package installed" `
242           -Category Information
243     }
244 
245     if ($usingFirefox) {
246         & cinst.exe --no-progress firefox
247     }
248 
249     & pip.exe list --format=columns
250 
251     # Prepare local Makefile.cfg
252 
253     ".uri = $env:TRAC_TEST_DB_URI" | Out-File -Encoding ASCII 'Makefile.cfg'
254 
255     # Note 1: echo would create an UCS-2 file with a BOM, make.exe
256     #         doesn't appreciate...
257 
258     # Note 2: we can't do more at this stage, as the services
259     #         (MySQL/PostgreSQL) are not started yet.
260 }
261 
262 
263 
Trac-Buildnull264 function Trac-Build {
265 
266     Write-Step -Name BUILD -Skip $skipBuild
267 
268     if ($skipBuild) {
269         return
270     }
271 
272     # Preparing database if needed
273 
274     if ($usingMysql) {
275         #
276         # $TRAC_TEST_DB_URI="mysql://tracuser:password@localhost/trac"
277         #
278         $env:MYSQL_PWD = $mysqlPwd
279         $env:Path      = "$mysqlHome\bin;$($env:Path)"
280 
281         Write-Host "Creating 'trac' MySQL database with user 'tracuser'"
282 
283         & mysql.exe -u root -e `
284           ('CREATE DATABASE trac DEFAULT CHARACTER SET utf8mb4' +
285            ' COLLATE utf8mb4_bin')
286         & mysql.exe -u root -e `
287           'CREATE USER tracuser@localhost IDENTIFIED BY ''password'';'
288         & mysql.exe -u root -e `
289           'GRANT ALL ON trac.* TO tracuser@localhost; FLUSH PRIVILEGES;'
290 
291         Add-AppveyorMessage -Message "2.1. MySQL database created" `
292           -Category Information
293     }
294     elseif ($usingPostgresql) {
295         #
296         # $TRAC_TEST_DB_URI=
297         # "postgres://tracuser:password@localhost/trac?schema=tractest"
298         #
299         $env:PGUSER     = $pgUser
300         $env:PGPASSWORD = $pgPassword
301         $env:Path       = "$pgHome\bin;$($env:Path)"
302 
303         Write-Host "Creating 'trac' PostgreSQL database with user 'tracuser'"
304 
305         & psql.exe -U postgres -c `
306           ('CREATE USER tracuser NOSUPERUSER NOCREATEDB CREATEROLE' +
307            ' PASSWORD ''password'';')
308         & psql.exe -U postgres -c `
309           'CREATE DATABASE trac OWNER tracuser;'
310 
311         Add-AppveyorMessage -Message "2.1. PostgreSQL database created" `
312           -Category Information
313     }
314 
315     Write-Host "make compile"
316 
317     # compile: if there are fuzzy catalogs, an error message will be
318     # generated on stderr.
319 
320     & make.exe Trac.egg-info compile 2>&1 | Tee-Object -Variable make
321 
322     $stderr = $make | ?{ $_ -is [System.Management.Automation.ErrorRecord] }
323     $stdout = $make | ?{ $_ -isnot [System.Management.Automation.ErrorRecord] }
324 
325     if ($LastExitCode) {
326         Add-AppveyorMessage -Message "2.2. make compile produced errors" `
327           -Category Error -Details ($stderr -join "`n")
328     }
329     elseif ($stderr) {
330         Add-AppveyorMessage -Message "2.2. make compile produced warnings" `
331           -Category Warning -Details ($stderr -join "`n")
332     }
333     else {
334         Add-AppveyorMessage -Message "2.2. make compile was successful" `
335           -Category Information
336     }
337 }
338 
339 
340 
Trac-Tests()341 function Trac-Tests {
342 
343     Write-Step -Name TESTS -Skip $skipTests
344 
345     $config = "$pyHome - $env:TRAC_TEST_DB_URI"
346 
347     if ("$env:TRAC_TEST_DB_URI" -eq '') {
348         $config += 'sqlite :memory:'
349     }
350 
351     function Make-Test([string]$goal, [string]$name, [ref]$code) {
352         if ($skipTests) {
353             Add-AppveyorTest -Name $name -Outcome Skipped
354             return
355         }
356 
357         Write-Host "make $goal"
358 
359         Add-AppveyorTest -Name $name -Outcome Running
360         # Enable verbose output to avoid appearance of hanging job on appveyor
361         & make.exe testopts=-v $goal 2>&1 | Tee-Object -Variable make
362 
363         # Determine outcome Passed or Failed
364         $outcome = 'Passed'
365         if ($LastExitCode) {
366             $outcome = 'Failed'
367             $code.value += 1
368         }
369 
370         $stderr = $make |
371           ?{ $_ -is [System.Management.Automation.ErrorRecord] }
372         $stdout = $make |
373           ?{ $_ -isnot [System.Management.Automation.ErrorRecord] }
374 
375         # Retrieve duration of the tests
376 
377         $msecs = 0
378         if ([string]$stderr -match "Ran \d+ tests in (\d+\.\d+)s") {
379             $secs = $matches[1]
380             $msecs = [math]::Round([float]$secs * 1000)
381         }
382 
383         Update-AppveyorTest -Name $name -Outcome $outcome `
384           -StdOut ($stdout -join "`n") -StdErr ($stderr -join '') `
385           -Duration $msecs
386     }
387 
388     $exit = $fexit = 0
389 
390     #
391     # Running unit-tests
392     #
393 
394     Make-Test -Goal unit-test -Name "Unit tests for $config" `
395       -Code ([ref]$exit)
396 
397     #
398     # Running functional tests
399     #
400 
401     Make-Test -Goal functional-test -Name "Functional tests for $config" `
402       -Code ([ref]$fexit)
403 
404     if (-not $fexit -eq 0) {
405         Write-Host "Saving functional logs in testenv.zip"
406         & 7z.exe a testenv.zip testenv
407 
408         Push-AppveyorArtifact testenv.zip
409 
410         $exit = $fexit
411     }
412 
413     if (-not $exit -eq 0) {
414         Write-Host "Exiting with code $exit"
415         Exit $exit
416     }
417 
418     if (-not $skipTests) {
419         Write-Host "All tests passed."
420     }
421 
422     #
423     # Prepare release artifacts
424     #
425 
426     & make.exe release-src wininst
427 }
428