# Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
version: '{build}.{branch}'

skip_branch_with_pr: true

clone_folder: C:\deno
clone_depth: 1

environment:
  APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
  DENO_BUILD_MODE: release
  DENO_BUILD_PATH: $(APPVEYOR_BUILD_FOLDER)\target\release
  DENO_THIRD_PARTY_PATH: $(APPVEYOR_BUILD_FOLDER)\third_party
  RELEASE_ARTIFACT: deno_win_x64.zip
  RUST_VERSION: 1.34.1
  RUST_DIR: $(USERPROFILE)\rust-$(RUST_VERSION)
  CARGO_HOME: $(RUST_DIR)\cargo
  RUSTUP_HOME: $(RUST_DIR)\rustup
  RUST_BACKTRACE: full
  RUSTC_WRAPPER: sccache
  PYTHONPATH: third_party\python_packages
  SCCACHE_BUCKET: deno-sccache
  AWS_ACCESS_KEY_ID: AKIAIVRN52PLDBP55LBQ
  AWS_SECRET_ACCESS_KEY:
    secure: 8ybpi/y5qE2baChsCBhNHmykng3FitELAtTYOiqZd0mw38i88dzdAX8ETNtBogMV


  # Appveyor uses 7zip to pack cache directories. We use these options:
  #   -t7z  : Use '7z' format.
  #   -snl  : Store symlinks; doesn't work, but it prevents following symlinks.
  #   -mtc  : Use UTC timestamps. This is required for incremental builds.
  #   -mx=1 : Fast compression.
  APPVEYOR_CACHE_ENTRY_ZIP_ARGS: -t7z -snl -mtc -mx=1

  # Define some PowerShell helper functions which are used in the scripts below.
  # They're defined in an environment variable to reduce noise in the build log.
  PS_UTILS: |-
      # `Exec` runs a regular executable. It looks at the process' exit code,
      # rather than its stderr output, to tell if a command has failed.
      function Exec([ScriptBlock] $Command, [switch] $NoNewLines) {
        "$Command".TrimStart(" &") | Write-Host              # Echo command.
        & $Command 2>&1 | Write-Host -NoNewLine:$NoNewLines  # Execute command.
        if ($NoNewLines) { Write-Host }                      # Write newline.
        if ($LastExitCode -ne 0) { throw "Failure. Exit code: $LastExitCode" }
      }

      # Get-Tree lists all objects in a tree. It's different from Get-ChildItem
      # in that the latter recurses through symlinks, which is problematic.
      function Get-Tree([string[]] $Path, [switch] $Recurse, [switch] $Force) {
        function Get-SubDirs([string[]] $Path) {
          Get-ChildItem $Path -Force:$Force `
                        -Attributes Directory+!ReparsePoint |
            foreach { $_.FullName }                         |
            foreach { $_; Get-SubDirs $_ }
        }
        if ($Recurse) { $Path += Get-SubDirs $Path }
        Get-ChildItem $Path -Force:$Force @args
      }

      # `Delete-Tree` is a simple wrapper around Remove-Item. It doesn't set
      # an error status if one of the paths to be deleted doesn't exist.
      function Delete-Tree([string[]] $Path) {
        $Path | foreach {
          "Deleting '$_'" | Write-Host -NoNewLine
          if (Test-Path $_) {
            Remove-Item $_ -Recurse -Force -ErrorAction Ignore
            $(if ($?) { " - ok" } else { " - failed" }) | Write-Host
          } else {
            " - not found" | Write-Host
          }
        }
      }

      # Get-SaveCache returns $true if the cache will be saved at the end.
      function Get-SaveCache {
        -not $env:APPVEYOR_PULL_REQUEST_NUMBER -and
        -not ($env:APPVEYOR_CACHE_SKIP_SAVE -eq "true")
      }

for:
  # Do no save the build cache for feature branches. TODO: Once we have multiple
  # permanent branches, use a build matrix so each branch has it's own cache.
  - branches:
      except:
        - master
    environment:
      APPVEYOR_CACHE_SKIP_SAVE: true

cache:
  # Rust stuff.
  - $(RUST_DIR)
  - $(APPVEYOR_BUILD_FOLDER)\prebuilt\win\

init:
  # Load utility functions
  - ps: Invoke-Expression $env:PS_UTILS

  # Upgrade git.
  # TODO: remove when Appveyor upgrades to version 2.19.2.windows.1 or higher.
  - ps: |-
      $git_setup_uri = "https://github.com/git-for-windows/git/releases/" +
                       "download/v2.21.0.windows.1/Git-2.21.0-64-bit.exe"
      Invoke-WebRequest -Uri $git_setup_uri -OutFile "$env:TEMP\git-setup.exe"
      Start-Process -FilePath "$env:TEMP\git-setup.exe" `
                    -ArgumentList "/verysilent"         `
                    -Wait

  # Make git check out symlinks (not placeholder text files).
  - git config --global core.symlinks true

install:
  # Make sure the PATH includes the prebuilt files (downloaded during setup.py)
  - set PATH=%PATH%;%CD%\prebuilt\win\

  # Clone the third_party submodule.
  - ps: |-
      try {
        Exec { & git submodule update --init --force --depth 1 }
      } catch {
        # Git will fail if the `third_party` directory was restored from cache,
        # but the `.git/modules` directory wasn't. Rebuild it from scratch.
        Delete-Tree $env:DENO_THIRD_PARTY_PATH
        Exec -NoNewLines { & git submodule update --init --force --depth 1 }
      }

  # Configure depot_tools and add it to the search path. This is necessary
  # because, later in this script, we need to invoke ninja directly.
  - ps: |-
      $env:PATH = "$env:DENO_THIRD_PARTY_PATH\depot_tools;$env:PATH"
      $env:DEPOT_TOOLS_WIN_TOOLCHAIN = "0"

  # Install a recent Node.js version.
  # TODO(ry) Upgrade to v12 once Install-Produce supports it.
  - ps: Install-Product -Product node -Version 10 -Platform x64

  # Make sure the right Python version is in PATH, and others are not.
  - ps: |-
      # Remove the wrong Python version(s) from PATH.
      $p = $env:PATH -split ";" | where { -not (Test-Path "$_\python.exe") }
      # Add python27-x64.
      $p += "C:\Python27-x64"
      $env:PATH = $p -join ";"

  # Add Rust/Cargo to PATH.
  - ps: $env:PATH += ";$env:CARGO_HOME\bin"

  # Install Rust via rustup-init.
  # * After install, the rustup directory is very big, with many files,
  #   slowing down cache save/restore a lot, so we remove unnecessary stuff.
  # * TODO: Use `rustup component remove docs` instead, when this issue
  #   is resolved: https://github.com/rust-lang-nursery/rustup.rs/issues/998.
  # * TODO: Ship Rust in the third_party repo. See issue #386.
  - ps: |-
      if (-not (Test-Path $env:CARGO_HOME)) {
        Invoke-WebRequest -Uri "https://win.rustup.rs" `
                          -OutFile "$env:TEMP\rustup-init.exe"
        Exec -NoNewLines {
          & "$env:TEMP\rustup-init.exe" -y --default-toolchain $env:RUST_VERSION
        }
        Delete-Tree @(
          "$env:RUSTUP_HOME\downloads",
          "$env:RUSTUP_HOME\tmp",
          "$env:RUSTUP_HOME\toolchains\stable-x86_64-pc-windows-msvc\share\doc"
        )
        Exec { rustup component add clippy }
      }

  # Log installed Node.js version + processor architecture.
  - node -p "`Node ${process.version} ${process.arch}`"

  # Log installed Python version + processor architecture.
  - ps: |-
      @("from sys import version",
        "print 'Python', version") -join "`n" | & python -

  # Log some more versions.
  - rustc --version
  - cargo --version

before_build:
  # Download clang and gn, generate ninja files.
  - python tools\setup.py

  # Start sccache, then throw away the S3 access key.
  - ps: |-
      sccache --start-server
      $env:AWS_SECRET_ACCESS_KEY = $null

build_script:
  # Build with Cargo first. Both builds produce a deno.exe in the same dir. We
  # want the final one (which gets tested and released) to be built by Ninja.
  - cargo build -vv --release --locked
  - cargo clippy --all-targets --release --locked -- -D clippy::all
  - python tools\build.py

test_script:
  - python tools\lint.py
  - python tools\test_format.py
  - ps: Exec { & python tools\test.py $env:DENO_BUILD_PATH }

after_test:
  # Delete the the rollup cache, which is unreliable, so that it doesn't get
  # persisted in the appveyor cache.
  - ps: if (Get-SaveCache) { Delete-Tree "$env:DENO_BUILD_PATH\.rpt2_cache" }

  # Stop sccache and show stats.
  - ps: sccache --stop-server

  # Verify that the build is fully up-to-date. Running ninja should be a no-op.
  # This catches erroneous file cleanup, and incorrectly set up build deps.
  - ps: |-
      $out = ninja -C $env:DENO_BUILD_PATH -n -d explain
      if ($out -notcontains "ninja: no work to do.") {
        throw "Build should be up-to-date but isn't."
      }

  # Verify that javascript and typescript files which are bundled by rollup are
  # listed explicitly in cli\BUILD.gn. This is not an air-tight check.
  # TODO: make rollup or another bundler write a depfile.
  - ps: |-
      $ignore = "test_util.ts", "unit_tests.ts", "*_test.ts"
      Get-ChildItem "js" -File -Force -Name                               |
        where   { $name = $_; -not ($ignore | where { $name -like $_ }) } |
        where   { -not (Select-String -Pattern $_ -Path cli\BUILD.gn `
                                      -SimpleMatch -CaseSensitive) }      |
        foreach { throw "$_ should be listed in cli\BUILD.gn but isn't." }

  # Verify that generated ninja files do not use absolute path names.
  # If they do, it makes ccache/sccache much less effective.
  - ps: |-
      $trap = "NO_ABS_PATH_PLS"
      $dir = "$env:APPVEYOR_BUILD_FOLDER\out\$trap"
      Exec { gn gen $dir | Out-Null }
      $files = Get-Tree $dir -File -Force -Recurse | where Extension -ne ".dll"
      Select-String $trap -Path $files -SimpleMatch | where {
        # V8 took the liberty to produce an absolute path in their ninja
        # output. We can't do much about that, so we just ignore it.
        $_.Line -notmatch "v8/builtins-generated/bytecodes-builtins-list.h"
      } | tee -Variable line_matches
      if ($line_matches) {
        $ctx = $line_matches.Line                                          |
          Select-String "[^\s;,]*[\s=]*[^\s;,=]*$trap[^\s;,]*" -AllMatches |
          foreach { $_.Matches.Value -replace '\$(.)', '$1' }              |
          sort -Unique
        throw @("Absolute path(s) found in build script:") + $ctx -join "`n  "
      }

  # If this build is going to be deployed, build a zip file.
  - ps: |-
      if ($env:APPVEYOR_REPO_TAG -eq "true") {
        Compress-Archive -CompressionLevel Optimal -Force `
          -Path "$env:DENO_BUILD_PATH\deno.exe" `
          -DestinationPath "$env:APPVEYOR_BUILD_FOLDER\$env:RELEASE_ARTIFACT"
      }

artifacts:
  path: $(RELEASE_ARTIFACT)

deploy:
  provider: GitHub
  auth_token:
    secure: HQIIUEOtep3yRiBacZCtX8hVmgtdNvt6Hx7u9fP4Wj2ZYp+eBFP2OLf67RKVa5VZ
  on:
    APPVEYOR_REPO_NAME: denoland/deno
    APPVEYOR_REPO_TAG: true