mirror of
https://github.com/aspnet/JavaScriptServices.git
synced 2025-12-23 10:08:57 +00:00
Compare commits
36 Commits
rel/2.0.0-
...
2.1.0-prev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f35c814fc7 | ||
|
|
116f33c66c | ||
|
|
c3964a0437 | ||
|
|
f291f87dfc | ||
|
|
a1c2c18326 | ||
|
|
d6588c31bf | ||
|
|
15d2f5a898 | ||
|
|
814441c933 | ||
|
|
a98c1459b5 | ||
|
|
975d537a0a | ||
|
|
4af2e8670e | ||
|
|
160c91f1b9 | ||
|
|
8ded472fe9 | ||
|
|
f9c62ebb5a | ||
|
|
d5a664e481 | ||
|
|
74512dc3b2 | ||
|
|
e6285f30ae | ||
|
|
bd5793e284 | ||
|
|
02bbcb68f1 | ||
|
|
18140929e7 | ||
|
|
50ba6114ee | ||
|
|
cd3e3c667c | ||
|
|
0de9f0e3ce | ||
|
|
9b1509a52b | ||
|
|
a8809f9a96 | ||
|
|
64389a9bbe | ||
|
|
86e94d7812 | ||
|
|
9f05a3d34b | ||
|
|
63e0af2ee8 | ||
|
|
dc5e980efa | ||
|
|
e0ab3ddcca | ||
|
|
0c058894c2 | ||
|
|
98385cbcb0 | ||
|
|
77cac3b6be | ||
|
|
051150475f | ||
|
|
128683be0e |
@@ -1,21 +1,19 @@
|
|||||||
init:
|
init:
|
||||||
- git config --global core.autocrlf true
|
- git config --global core.autocrlf true
|
||||||
install:
|
install:
|
||||||
- ps: Install-Product node 6.9.2 x64
|
- ps: Install-Product node 6.9.2 x64
|
||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
- master
|
- dev
|
||||||
- release
|
- /^release\/.*$/
|
||||||
- dev
|
- /^(.*\/)?ci-.*$/
|
||||||
- /^(.*\/)?ci-.*$/
|
|
||||||
- /^rel\/.*/
|
|
||||||
build_script:
|
build_script:
|
||||||
- ps: .\run.ps1 default-build
|
- ps: .\run.ps1 default-build
|
||||||
clone_depth: 1
|
clone_depth: 1
|
||||||
environment:
|
environment:
|
||||||
global:
|
global:
|
||||||
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
|
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
|
||||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||||
test: off
|
test: 'off'
|
||||||
deploy: off
|
deploy: 'off'
|
||||||
os: Visual Studio 2017
|
os: Visual Studio 2017
|
||||||
|
|||||||
11
.travis.yml
11
.travis.yml
@@ -12,8 +12,13 @@ addons:
|
|||||||
- zlib1g
|
- zlib1g
|
||||||
mono: none
|
mono: none
|
||||||
os:
|
os:
|
||||||
- linux
|
- linux
|
||||||
- osx
|
- osx
|
||||||
osx_image: xcode7.1
|
osx_image: xcode7.1
|
||||||
script:
|
script:
|
||||||
- ./build.sh
|
- ./build.sh
|
||||||
|
branches:
|
||||||
|
only:
|
||||||
|
- dev
|
||||||
|
- /^release\/.*$/
|
||||||
|
- /^(.*\/)?ci-.*$/
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<Project>
|
<Project>
|
||||||
<Import Project="version.props" />
|
<Import Project="version.props" />
|
||||||
<Import Project="build\dependencies.props" />
|
<Import Project="build\dependencies.props" />
|
||||||
|
<Import Project="build\sources.props" />
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Product>Microsoft ASP.NET Core</Product>
|
<Product>Microsoft ASP.NET Core</Product>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<Project>
|
<Project>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<RuntimeFrameworkVersion Condition=" '$(TargetFramework)' == 'netcoreapp2.0' ">$(MicrosoftNETCoreApp20PackageVersion)</RuntimeFrameworkVersion>
|
<RuntimeFrameworkVersion Condition=" '$(TargetFramework)' == 'netcoreapp2.0' ">$(MicrosoftNETCoreApp20PackageVersion)</RuntimeFrameworkVersion>
|
||||||
|
<RuntimeFrameworkVersion Condition=" '$(TargetFramework)' == 'netcoreapp2.1' ">$(MicrosoftNETCoreApp21PackageVersion)</RuntimeFrameworkVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
<configuration>
|
<configuration>
|
||||||
<packageSources>
|
<packageSources>
|
||||||
<clear />
|
<clear />
|
||||||
<add key="AspNetCore" value="https://dotnet.myget.org/F/aspnetcore-ci-dev/api/v3/index.json" />
|
<!-- Restore sources should be defined in build/sources.props. -->
|
||||||
<add key="AspNetCoreTools" value="https://dotnet.myget.org/F/aspnetcore-tools/api/v3/index.json" />
|
|
||||||
<add key="NuGet" value="https://api.nuget.org/v3/index.json" />
|
|
||||||
</packageSources>
|
</packageSources>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|||||||
@@ -1,26 +1,27 @@
|
|||||||
<Project>
|
<Project>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
|
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Label="Package Versions">
|
<PropertyGroup Label="Package Versions">
|
||||||
<InternalAspNetCoreSdkPackageVersion>2.1.0-preview1-15549</InternalAspNetCoreSdkPackageVersion>
|
<InternalAspNetCoreSdkPackageVersion>2.1.0-preview1-1010</InternalAspNetCoreSdkPackageVersion>
|
||||||
<MicrosoftAspNetCoreDiagnosticsPackageVersion>2.1.0-preview1-27478</MicrosoftAspNetCoreDiagnosticsPackageVersion>
|
<MicrosoftAspNetCoreDiagnosticsPackageVersion>2.1.0-preview1-28193</MicrosoftAspNetCoreDiagnosticsPackageVersion>
|
||||||
<MicrosoftAspNetCoreHostingAbstractionsPackageVersion>2.1.0-preview1-27478</MicrosoftAspNetCoreHostingAbstractionsPackageVersion>
|
<MicrosoftAspNetCoreHostingAbstractionsPackageVersion>2.1.0-preview1-28193</MicrosoftAspNetCoreHostingAbstractionsPackageVersion>
|
||||||
<MicrosoftAspNetCoreHostingPackageVersion>2.1.0-preview1-27478</MicrosoftAspNetCoreHostingPackageVersion>
|
<MicrosoftAspNetCoreHostingPackageVersion>2.1.0-preview1-28193</MicrosoftAspNetCoreHostingPackageVersion>
|
||||||
<MicrosoftAspNetCoreMvcPackageVersion>2.1.0-preview1-27478</MicrosoftAspNetCoreMvcPackageVersion>
|
<MicrosoftAspNetCoreMvcPackageVersion>2.1.0-preview1-28193</MicrosoftAspNetCoreMvcPackageVersion>
|
||||||
<MicrosoftAspNetCoreMvcTagHelpersPackageVersion>2.1.0-preview1-27478</MicrosoftAspNetCoreMvcTagHelpersPackageVersion>
|
<MicrosoftAspNetCoreMvcTagHelpersPackageVersion>2.1.0-preview1-28193</MicrosoftAspNetCoreMvcTagHelpersPackageVersion>
|
||||||
<MicrosoftAspNetCoreMvcViewFeaturesPackageVersion>2.1.0-preview1-27478</MicrosoftAspNetCoreMvcViewFeaturesPackageVersion>
|
<MicrosoftAspNetCoreMvcViewFeaturesPackageVersion>2.1.0-preview1-28193</MicrosoftAspNetCoreMvcViewFeaturesPackageVersion>
|
||||||
<MicrosoftAspNetCoreServerIISIntegrationPackageVersion>2.1.0-preview1-27478</MicrosoftAspNetCoreServerIISIntegrationPackageVersion>
|
<MicrosoftAspNetCoreServerIISIntegrationPackageVersion>2.1.0-preview1-28193</MicrosoftAspNetCoreServerIISIntegrationPackageVersion>
|
||||||
<MicrosoftAspNetCoreServerKestrelPackageVersion>2.1.0-preview1-27478</MicrosoftAspNetCoreServerKestrelPackageVersion>
|
<MicrosoftAspNetCoreServerKestrelPackageVersion>2.1.0-preview1-28193</MicrosoftAspNetCoreServerKestrelPackageVersion>
|
||||||
<MicrosoftAspNetCoreStaticFilesPackageVersion>2.1.0-preview1-27478</MicrosoftAspNetCoreStaticFilesPackageVersion>
|
<MicrosoftAspNetCoreStaticFilesPackageVersion>2.1.0-preview1-28193</MicrosoftAspNetCoreStaticFilesPackageVersion>
|
||||||
<MicrosoftAspNetCoreWebSocketsPackageVersion>2.1.0-preview1-27478</MicrosoftAspNetCoreWebSocketsPackageVersion>
|
<MicrosoftAspNetCoreWebSocketsPackageVersion>2.1.0-preview1-28193</MicrosoftAspNetCoreWebSocketsPackageVersion>
|
||||||
<MicrosoftExtensionsDependencyInjectionPackageVersion>2.1.0-preview1-27478</MicrosoftExtensionsDependencyInjectionPackageVersion>
|
<MicrosoftExtensionsDependencyInjectionPackageVersion>2.1.0-preview1-28193</MicrosoftExtensionsDependencyInjectionPackageVersion>
|
||||||
<MicrosoftExtensionsFileProvidersPhysicalPackageVersion>2.1.0-preview1-27478</MicrosoftExtensionsFileProvidersPhysicalPackageVersion>
|
<MicrosoftExtensionsFileProvidersPhysicalPackageVersion>2.1.0-preview1-28193</MicrosoftExtensionsFileProvidersPhysicalPackageVersion>
|
||||||
<MicrosoftExtensionsLoggingConsolePackageVersion>2.1.0-preview1-27478</MicrosoftExtensionsLoggingConsolePackageVersion>
|
<MicrosoftExtensionsLoggingConsolePackageVersion>2.1.0-preview1-28193</MicrosoftExtensionsLoggingConsolePackageVersion>
|
||||||
<MicrosoftExtensionsLoggingDebugPackageVersion>2.1.0-preview1-27478</MicrosoftExtensionsLoggingDebugPackageVersion>
|
<MicrosoftExtensionsLoggingDebugPackageVersion>2.1.0-preview1-28193</MicrosoftExtensionsLoggingDebugPackageVersion>
|
||||||
<MicrosoftNETCoreApp20PackageVersion>2.0.0</MicrosoftNETCoreApp20PackageVersion>
|
<MicrosoftNETCoreApp20PackageVersion>2.0.0</MicrosoftNETCoreApp20PackageVersion>
|
||||||
|
<MicrosoftNETCoreApp21PackageVersion>2.1.0-preview1-26122-01</MicrosoftNETCoreApp21PackageVersion>
|
||||||
<NewtonsoftJsonPackageVersion>10.0.1</NewtonsoftJsonPackageVersion>
|
<NewtonsoftJsonPackageVersion>10.0.1</NewtonsoftJsonPackageVersion>
|
||||||
<SystemThreadingTasksDataflowPackageVersion>4.8.0</SystemThreadingTasksDataflowPackageVersion>
|
<SystemThreadingTasksDataflowPackageVersion>4.9.0-preview1-26119-06</SystemThreadingTasksDataflowPackageVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Import Project="$(DotNetPackageVersionPropsPath)" Condition=" '$(DotNetPackageVersionPropsPath)' != '' " />
|
<Import Project="$(DotNetPackageVersionPropsPath)" Condition=" '$(DotNetPackageVersionPropsPath)' != '' " />
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
<Project>
|
<Project>
|
||||||
|
<Import Project="dependencies.props" />
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<!-- These properties are use by the automation that updates dependencies.props -->
|
<!-- These properties are use by the automation that updates dependencies.props -->
|
||||||
<LineupPackageId>Internal.AspNetCore.Universe.Lineup</LineupPackageId>
|
<LineupPackageId>Internal.AspNetCore.Universe.Lineup</LineupPackageId>
|
||||||
<LineupPackageRestoreSource>https://dotnet.myget.org/F/aspnetcore-ci-dev/api/v3/index.json</LineupPackageRestoreSource>
|
<LineupPackageRestoreSource>https://dotnet.myget.org/F/aspnetcore-release/api/v3/index.json</LineupPackageRestoreSource>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<DotNetCoreRuntime Include="$(MicrosoftNETCoreApp20PackageVersion)" />
|
||||||
|
<DotNetCoreRuntime Include="$(MicrosoftNETCoreApp21PackageVersion)" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
16
build/sources.props
Normal file
16
build/sources.props
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<Project>
|
||||||
|
<Import Project="$(DotNetRestoreSourcePropsPath)" Condition="'$(DotNetRestoreSourcePropsPath)' != ''"/>
|
||||||
|
|
||||||
|
<PropertyGroup Label="RestoreSources">
|
||||||
|
<RestoreSources>$(DotNetRestoreSources)</RestoreSources>
|
||||||
|
<RestoreSources Condition="'$(DotNetBuildOffline)' != 'true' AND '$(AspNetUniverseBuildOffline)' != 'true' ">
|
||||||
|
$(RestoreSources);
|
||||||
|
https://dotnet.myget.org/F/aspnetcore-release/api/v3/index.json;
|
||||||
|
https://dotnet.myget.org/F/aspnetcore-tools/api/v3/index.json;
|
||||||
|
</RestoreSources>
|
||||||
|
<RestoreSources Condition="'$(DotNetBuildOffline)' != 'true'">
|
||||||
|
$(RestoreSources);
|
||||||
|
https://api.nuget.org/v3/index.json;
|
||||||
|
</RestoreSources>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
version:2.1.0-preview1-15549
|
version:2.1.0-preview1-1010
|
||||||
commithash:f570e08585fec510dd60cd4bfe8795388b757a95
|
commithash:75ca924dfbd673c38841025b04c4dcd93b84f56d
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://raw.githubusercontent.com/aspnet/BuildTools/dev/tools/korebuild.schema.json",
|
"$schema": "https://raw.githubusercontent.com/aspnet/BuildTools/release/2.1/tools/korebuild.schema.json",
|
||||||
"channel": "dev",
|
"channel": "release/2.1",
|
||||||
"toolsets": {
|
"toolsets": {
|
||||||
"nodejs": {
|
"nodejs": {
|
||||||
"required": true,
|
"required": true,
|
||||||
|
|||||||
17
run.ps1
17
run.ps1
@@ -29,6 +29,9 @@ Updates KoreBuild to the latest version even if a lock file is present.
|
|||||||
.PARAMETER ConfigFile
|
.PARAMETER ConfigFile
|
||||||
The path to the configuration file that stores values. Defaults to korebuild.json.
|
The path to the configuration file that stores values. Defaults to korebuild.json.
|
||||||
|
|
||||||
|
.PARAMETER ToolsSourceSuffix
|
||||||
|
The Suffix to append to the end of the ToolsSource. Useful for query strings in blob stores.
|
||||||
|
|
||||||
.PARAMETER Arguments
|
.PARAMETER Arguments
|
||||||
Arguments to be passed to the command
|
Arguments to be passed to the command
|
||||||
|
|
||||||
@@ -51,7 +54,7 @@ Example config file:
|
|||||||
#>
|
#>
|
||||||
[CmdletBinding(PositionalBinding = $false)]
|
[CmdletBinding(PositionalBinding = $false)]
|
||||||
param(
|
param(
|
||||||
[Parameter(Mandatory=$true, Position = 0)]
|
[Parameter(Mandatory = $true, Position = 0)]
|
||||||
[string]$Command,
|
[string]$Command,
|
||||||
[string]$Path = $PSScriptRoot,
|
[string]$Path = $PSScriptRoot,
|
||||||
[Alias('c')]
|
[Alias('c')]
|
||||||
@@ -63,6 +66,7 @@ param(
|
|||||||
[Alias('u')]
|
[Alias('u')]
|
||||||
[switch]$Update,
|
[switch]$Update,
|
||||||
[string]$ConfigFile,
|
[string]$ConfigFile,
|
||||||
|
[string]$ToolsSourceSuffix,
|
||||||
[Parameter(ValueFromRemainingArguments = $true)]
|
[Parameter(ValueFromRemainingArguments = $true)]
|
||||||
[string[]]$Arguments
|
[string[]]$Arguments
|
||||||
)
|
)
|
||||||
@@ -79,7 +83,7 @@ function Get-KoreBuild {
|
|||||||
$lockFile = Join-Path $Path 'korebuild-lock.txt'
|
$lockFile = Join-Path $Path 'korebuild-lock.txt'
|
||||||
|
|
||||||
if (!(Test-Path $lockFile) -or $Update) {
|
if (!(Test-Path $lockFile) -or $Update) {
|
||||||
Get-RemoteFile "$ToolsSource/korebuild/channels/$Channel/latest.txt" $lockFile
|
Get-RemoteFile "$ToolsSource/korebuild/channels/$Channel/latest.txt" $lockFile $ToolsSourceSuffix
|
||||||
}
|
}
|
||||||
|
|
||||||
$version = Get-Content $lockFile | Where-Object { $_ -like 'version:*' } | Select-Object -first 1
|
$version = Get-Content $lockFile | Where-Object { $_ -like 'version:*' } | Select-Object -first 1
|
||||||
@@ -96,7 +100,7 @@ function Get-KoreBuild {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
$tmpfile = Join-Path ([IO.Path]::GetTempPath()) "KoreBuild-$([guid]::NewGuid()).zip"
|
$tmpfile = Join-Path ([IO.Path]::GetTempPath()) "KoreBuild-$([guid]::NewGuid()).zip"
|
||||||
Get-RemoteFile $remotePath $tmpfile
|
Get-RemoteFile $remotePath $tmpfile $ToolsSourceSuffix
|
||||||
if (Get-Command -Name 'Expand-Archive' -ErrorAction Ignore) {
|
if (Get-Command -Name 'Expand-Archive' -ErrorAction Ignore) {
|
||||||
# Use built-in commands where possible as they are cross-plat compatible
|
# Use built-in commands where possible as they are cross-plat compatible
|
||||||
Expand-Archive -Path $tmpfile -DestinationPath $korebuildPath
|
Expand-Archive -Path $tmpfile -DestinationPath $korebuildPath
|
||||||
@@ -124,7 +128,7 @@ function Join-Paths([string]$path, [string[]]$childPaths) {
|
|||||||
return $path
|
return $path
|
||||||
}
|
}
|
||||||
|
|
||||||
function Get-RemoteFile([string]$RemotePath, [string]$LocalPath) {
|
function Get-RemoteFile([string]$RemotePath, [string]$LocalPath, [string]$RemoteSuffix) {
|
||||||
if ($RemotePath -notlike 'http*') {
|
if ($RemotePath -notlike 'http*') {
|
||||||
Copy-Item $RemotePath $LocalPath
|
Copy-Item $RemotePath $LocalPath
|
||||||
return
|
return
|
||||||
@@ -134,7 +138,7 @@ function Get-RemoteFile([string]$RemotePath, [string]$LocalPath) {
|
|||||||
while ($retries -gt 0) {
|
while ($retries -gt 0) {
|
||||||
$retries -= 1
|
$retries -= 1
|
||||||
try {
|
try {
|
||||||
Invoke-WebRequest -UseBasicParsing -Uri $RemotePath -OutFile $LocalPath
|
Invoke-WebRequest -UseBasicParsing -Uri $($RemotePath + $RemoteSuffix) -OutFile $LocalPath
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
@@ -161,7 +165,8 @@ if (Test-Path $ConfigFile) {
|
|||||||
if (!($Channel) -and (Get-Member -Name 'channel' -InputObject $config)) { [string] $Channel = $config.channel }
|
if (!($Channel) -and (Get-Member -Name 'channel' -InputObject $config)) { [string] $Channel = $config.channel }
|
||||||
if (!($ToolsSource) -and (Get-Member -Name 'toolsSource' -InputObject $config)) { [string] $ToolsSource = $config.toolsSource}
|
if (!($ToolsSource) -and (Get-Member -Name 'toolsSource' -InputObject $config)) { [string] $ToolsSource = $config.toolsSource}
|
||||||
}
|
}
|
||||||
} catch {
|
}
|
||||||
|
catch {
|
||||||
Write-Warning "$ConfigFile could not be read. Its settings will be ignored."
|
Write-Warning "$ConfigFile could not be read. Its settings will be ignored."
|
||||||
Write-Warning $Error[0]
|
Write-Warning $Error[0]
|
||||||
}
|
}
|
||||||
|
|||||||
30
run.sh
30
run.sh
@@ -17,6 +17,7 @@ update=false
|
|||||||
repo_path="$DIR"
|
repo_path="$DIR"
|
||||||
channel=''
|
channel=''
|
||||||
tools_source=''
|
tools_source=''
|
||||||
|
tools_source_suffix=''
|
||||||
|
|
||||||
#
|
#
|
||||||
# Functions
|
# Functions
|
||||||
@@ -29,13 +30,14 @@ __usage() {
|
|||||||
echo " <Arguments>... Arguments passed to the command. Variable number of arguments allowed."
|
echo " <Arguments>... Arguments passed to the command. Variable number of arguments allowed."
|
||||||
echo ""
|
echo ""
|
||||||
echo "Options:"
|
echo "Options:"
|
||||||
echo " --verbose Show verbose output."
|
echo " --verbose Show verbose output."
|
||||||
echo " -c|--channel <CHANNEL> The channel of KoreBuild to download. Overrides the value from the config file.."
|
echo " -c|--channel <CHANNEL> The channel of KoreBuild to download. Overrides the value from the config file.."
|
||||||
echo " --config-file <FILE> The path to the configuration file that stores values. Defaults to korebuild.json."
|
echo " --config-file <FILE> The path to the configuration file that stores values. Defaults to korebuild.json."
|
||||||
echo " -d|--dotnet-home <DIR> The directory where .NET Core tools will be stored. Defaults to '\$DOTNET_HOME' or '\$HOME/.dotnet."
|
echo " -d|--dotnet-home <DIR> The directory where .NET Core tools will be stored. Defaults to '\$DOTNET_HOME' or '\$HOME/.dotnet."
|
||||||
echo " --path <PATH> The directory to build. Defaults to the directory containing the script."
|
echo " --path <PATH> The directory to build. Defaults to the directory containing the script."
|
||||||
echo " -s|--tools-source|-ToolsSource <URL> The base url where build tools can be downloaded. Overrides the value from the config file."
|
echo " -s|--tools-source|-ToolsSource <URL> The base url where build tools can be downloaded. Overrides the value from the config file."
|
||||||
echo " -u|--update Update to the latest KoreBuild even if the lock file is present."
|
echo " --tools-source-suffix|-ToolsSourceSuffix <SUFFIX> The suffix to append to tools-source. Useful for query strings."
|
||||||
|
echo " -u|--update Update to the latest KoreBuild even if the lock file is present."
|
||||||
echo ""
|
echo ""
|
||||||
echo "Description:"
|
echo "Description:"
|
||||||
echo " This function will create a file \$DIR/korebuild-lock.txt. This lock file can be committed to source, but does not have to be."
|
echo " This function will create a file \$DIR/korebuild-lock.txt. This lock file can be committed to source, but does not have to be."
|
||||||
@@ -50,7 +52,7 @@ get_korebuild() {
|
|||||||
local version
|
local version
|
||||||
local lock_file="$repo_path/korebuild-lock.txt"
|
local lock_file="$repo_path/korebuild-lock.txt"
|
||||||
if [ ! -f "$lock_file" ] || [ "$update" = true ]; then
|
if [ ! -f "$lock_file" ] || [ "$update" = true ]; then
|
||||||
__get_remote_file "$tools_source/korebuild/channels/$channel/latest.txt" "$lock_file"
|
__get_remote_file "$tools_source/korebuild/channels/$channel/latest.txt" "$lock_file" "$tools_source_suffix"
|
||||||
fi
|
fi
|
||||||
version="$(grep 'version:*' -m 1 "$lock_file")"
|
version="$(grep 'version:*' -m 1 "$lock_file")"
|
||||||
if [[ "$version" == '' ]]; then
|
if [[ "$version" == '' ]]; then
|
||||||
@@ -66,7 +68,7 @@ get_korebuild() {
|
|||||||
local remote_path="$tools_source/korebuild/artifacts/$version/korebuild.$version.zip"
|
local remote_path="$tools_source/korebuild/artifacts/$version/korebuild.$version.zip"
|
||||||
tmpfile="$(mktemp)"
|
tmpfile="$(mktemp)"
|
||||||
echo -e "${MAGENTA}Downloading KoreBuild ${version}${RESET}"
|
echo -e "${MAGENTA}Downloading KoreBuild ${version}${RESET}"
|
||||||
if __get_remote_file "$remote_path" "$tmpfile"; then
|
if __get_remote_file "$remote_path" "$tmpfile" "$tools_source_suffix"; then
|
||||||
unzip -q -d "$korebuild_path" "$tmpfile"
|
unzip -q -d "$korebuild_path" "$tmpfile"
|
||||||
fi
|
fi
|
||||||
rm "$tmpfile" || true
|
rm "$tmpfile" || true
|
||||||
@@ -98,6 +100,7 @@ __machine_has() {
|
|||||||
__get_remote_file() {
|
__get_remote_file() {
|
||||||
local remote_path=$1
|
local remote_path=$1
|
||||||
local local_path=$2
|
local local_path=$2
|
||||||
|
local remote_path_suffix=$3
|
||||||
|
|
||||||
if [[ "$remote_path" != 'http'* ]]; then
|
if [[ "$remote_path" != 'http'* ]]; then
|
||||||
cp "$remote_path" "$local_path"
|
cp "$remote_path" "$local_path"
|
||||||
@@ -106,14 +109,14 @@ __get_remote_file() {
|
|||||||
|
|
||||||
local failed=false
|
local failed=false
|
||||||
if __machine_has wget; then
|
if __machine_has wget; then
|
||||||
wget --tries 10 --quiet -O "$local_path" "$remote_path" || failed=true
|
wget --tries 10 --quiet -O "$local_path" "${remote_path}${remote_path_suffix}" || failed=true
|
||||||
else
|
else
|
||||||
failed=true
|
failed=true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$failed" = true ] && __machine_has curl; then
|
if [ "$failed" = true ] && __machine_has curl; then
|
||||||
failed=false
|
failed=false
|
||||||
curl --retry 10 -sSL -f --create-dirs -o "$local_path" "$remote_path" || failed=true
|
curl --retry 10 -sSL -f --create-dirs -o "$local_path" "${remote_path}${remote_path_suffix}" || failed=true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$failed" = true ]; then
|
if [ "$failed" = true ]; then
|
||||||
@@ -164,6 +167,11 @@ while [[ $# -gt 0 ]]; do
|
|||||||
tools_source="${1:-}"
|
tools_source="${1:-}"
|
||||||
[ -z "$tools_source" ] && __usage
|
[ -z "$tools_source" ] && __usage
|
||||||
;;
|
;;
|
||||||
|
--tools-source-suffix|-ToolsSourceSuffix)
|
||||||
|
shift
|
||||||
|
tools_source_suffix="${1:-}"
|
||||||
|
[ -z "$tools_source_suffix" ] && __usage
|
||||||
|
;;
|
||||||
-u|--update|-Update)
|
-u|--update|-Update)
|
||||||
update=true
|
update=true
|
||||||
;;
|
;;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks>
|
<TargetFrameworks>netcoreapp2.1;net461</TargetFrameworks>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
<OutputType>exe</OutputType>
|
<OutputType>exe</OutputType>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks>
|
<TargetFrameworks>netcoreapp2.1;net461</TargetFrameworks>
|
||||||
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
|
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks>
|
<TargetFrameworks>netcoreapp2.1;net461</TargetFrameworks>
|
||||||
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
|
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|||||||
@@ -14,8 +14,7 @@ namespace Microsoft.AspNetCore.NodeServices.Sockets
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A specialisation of the OutOfProcessNodeInstance base class that uses a lightweight binary streaming protocol
|
/// A specialisation of the OutOfProcessNodeInstance base class that uses a lightweight binary streaming protocol
|
||||||
/// to perform RPC invocations. The physical transport is Named Pipes on Windows, or Domain Sockets on Linux/Mac.
|
/// to perform RPC invocations. The physical transport is Named Pipes on Windows, or Domain Sockets on Linux/Mac.
|
||||||
/// For details on the binary streaming protocol, see
|
/// For details on the binary streaming protocol, see <see cref="Microsoft.AspNetCore.NodeServices.Sockets.VirtualConnections.VirtualConnectionClient" />
|
||||||
/// Microsoft.AspNetCore.NodeServices.HostingModels.VirtualConnections.VirtualConnectionClient.
|
|
||||||
/// The advantage versus using HTTP for RPC is that this is faster (not surprisingly - there's much less overhead
|
/// The advantage versus using HTTP for RPC is that this is faster (not surprisingly - there's much less overhead
|
||||||
/// because we don't need most of the functionality of HTTP.
|
/// because we don't need most of the functionality of HTTP.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ namespace Microsoft.AspNetCore.SpaServices.AngularCli
|
|||||||
public class AngularCliBuilder : ISpaPrerendererBuilder
|
public class AngularCliBuilder : ISpaPrerendererBuilder
|
||||||
{
|
{
|
||||||
private static TimeSpan RegexMatchTimeout = TimeSpan.FromSeconds(5); // This is a development-time only feature, so a very long timeout is fine
|
private static TimeSpan RegexMatchTimeout = TimeSpan.FromSeconds(5); // This is a development-time only feature, so a very long timeout is fine
|
||||||
private static TimeSpan BuildTimeout = TimeSpan.FromSeconds(50); // Note that the HTTP request itself by default times out after 60s, so you only get useful error information if this is shorter
|
|
||||||
|
|
||||||
private readonly string _npmScriptName;
|
private readonly string _npmScriptName;
|
||||||
|
|
||||||
@@ -39,7 +38,7 @@ namespace Microsoft.AspNetCore.SpaServices.AngularCli
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Task Build(ISpaBuilder spaBuilder)
|
public async Task Build(ISpaBuilder spaBuilder)
|
||||||
{
|
{
|
||||||
var sourcePath = spaBuilder.Options.SourcePath;
|
var sourcePath = spaBuilder.Options.SourcePath;
|
||||||
if (string.IsNullOrEmpty(sourcePath))
|
if (string.IsNullOrEmpty(sourcePath))
|
||||||
@@ -57,18 +56,26 @@ namespace Microsoft.AspNetCore.SpaServices.AngularCli
|
|||||||
null);
|
null);
|
||||||
npmScriptRunner.AttachToLogger(logger);
|
npmScriptRunner.AttachToLogger(logger);
|
||||||
|
|
||||||
|
using (var stdOutReader = new EventedStreamStringReader(npmScriptRunner.StdOut))
|
||||||
using (var stdErrReader = new EventedStreamStringReader(npmScriptRunner.StdErr))
|
using (var stdErrReader = new EventedStreamStringReader(npmScriptRunner.StdErr))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return npmScriptRunner.StdOut.WaitForMatch(
|
await npmScriptRunner.StdOut.WaitForMatch(
|
||||||
new Regex("chunk", RegexOptions.None, RegexMatchTimeout),
|
new Regex("Date", RegexOptions.None, RegexMatchTimeout));
|
||||||
BuildTimeout);
|
|
||||||
}
|
}
|
||||||
catch (EndOfStreamException ex)
|
catch (EndOfStreamException ex)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException(
|
throw new InvalidOperationException(
|
||||||
$"The NPM script '{_npmScriptName}' exited without indicating success. " +
|
$"The NPM script '{_npmScriptName}' exited without indicating success.\n" +
|
||||||
|
$"Output was: {stdOutReader.ReadAsString()}\n" +
|
||||||
|
$"Error output was: {stdErrReader.ReadAsString()}", ex);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException ex)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
$"The NPM script '{_npmScriptName}' timed out without indicating success. " +
|
||||||
|
$"Output was: {stdOutReader.ReadAsString()}\n" +
|
||||||
$"Error output was: {stdErrReader.ReadAsString()}", ex);
|
$"Error output was: {stdErrReader.ReadAsString()}", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ using System;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Net.Http;
|
||||||
|
using Microsoft.AspNetCore.SpaServices.Extensions.Util;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.SpaServices.AngularCli
|
namespace Microsoft.AspNetCore.SpaServices.AngularCli
|
||||||
{
|
{
|
||||||
@@ -17,7 +20,6 @@ namespace Microsoft.AspNetCore.SpaServices.AngularCli
|
|||||||
{
|
{
|
||||||
private const string LogCategoryName = "Microsoft.AspNetCore.SpaServices";
|
private const string LogCategoryName = "Microsoft.AspNetCore.SpaServices";
|
||||||
private static TimeSpan RegexMatchTimeout = TimeSpan.FromSeconds(5); // This is a development-time only feature, so a very long timeout is fine
|
private static TimeSpan RegexMatchTimeout = TimeSpan.FromSeconds(5); // This is a development-time only feature, so a very long timeout is fine
|
||||||
private static TimeSpan StartupTimeout = TimeSpan.FromSeconds(50); // Note that the HTTP request itself by default times out after 60s, so you only get useful error information if this is shorter
|
|
||||||
|
|
||||||
public static void Attach(
|
public static void Attach(
|
||||||
ISpaBuilder spaBuilder,
|
ISpaBuilder spaBuilder,
|
||||||
@@ -47,7 +49,16 @@ namespace Microsoft.AspNetCore.SpaServices.AngularCli
|
|||||||
var targetUriTask = angularCliServerInfoTask.ContinueWith(
|
var targetUriTask = angularCliServerInfoTask.ContinueWith(
|
||||||
task => new UriBuilder("http", "localhost", task.Result.Port).Uri);
|
task => new UriBuilder("http", "localhost", task.Result.Port).Uri);
|
||||||
|
|
||||||
SpaProxyingExtensions.UseProxyToSpaDevelopmentServer(spaBuilder, targetUriTask);
|
SpaProxyingExtensions.UseProxyToSpaDevelopmentServer(spaBuilder, () =>
|
||||||
|
{
|
||||||
|
// On each request, we create a separate startup task with its own timeout. That way, even if
|
||||||
|
// the first request times out, subsequent requests could still work.
|
||||||
|
var timeout = spaBuilder.Options.StartupTimeout;
|
||||||
|
return targetUriTask.WithTimeout(timeout,
|
||||||
|
$"The Angular CLI process did not start listening for requests " +
|
||||||
|
$"within the timeout period of {timeout.Seconds} seconds. " +
|
||||||
|
$"Check the log output for error information.");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<AngularCliServerInfo> StartAngularCliServerAsync(
|
private static async Task<AngularCliServerInfo> StartAngularCliServerAsync(
|
||||||
@@ -66,8 +77,7 @@ namespace Microsoft.AspNetCore.SpaServices.AngularCli
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
openBrowserLine = await npmScriptRunner.StdOut.WaitForMatch(
|
openBrowserLine = await npmScriptRunner.StdOut.WaitForMatch(
|
||||||
new Regex("open your browser on (http\\S+)", RegexOptions.None, RegexMatchTimeout),
|
new Regex("open your browser on (http\\S+)", RegexOptions.None, RegexMatchTimeout));
|
||||||
StartupTimeout);
|
|
||||||
}
|
}
|
||||||
catch (EndOfStreamException ex)
|
catch (EndOfStreamException ex)
|
||||||
{
|
{
|
||||||
@@ -76,26 +86,45 @@ namespace Microsoft.AspNetCore.SpaServices.AngularCli
|
|||||||
$"Angular CLI was listening for requests. The error output was: " +
|
$"Angular CLI was listening for requests. The error output was: " +
|
||||||
$"{stdErrReader.ReadAsString()}", ex);
|
$"{stdErrReader.ReadAsString()}", ex);
|
||||||
}
|
}
|
||||||
catch (TaskCanceledException ex)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException(
|
|
||||||
$"The Angular CLI process did not start listening for requests " +
|
|
||||||
$"within the timeout period of {StartupTimeout.Seconds} seconds. " +
|
|
||||||
$"Check the log output for error information.", ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var uri = new Uri(openBrowserLine.Groups[1].Value);
|
var uri = new Uri(openBrowserLine.Groups[1].Value);
|
||||||
var serverInfo = new AngularCliServerInfo { Port = uri.Port };
|
var serverInfo = new AngularCliServerInfo { Port = uri.Port };
|
||||||
|
|
||||||
// Even after the Angular CLI claims to be listening for requests, there's a short
|
// Even after the Angular CLI claims to be listening for requests, there's a short
|
||||||
// period where it will give an error if you make a request too quickly. Give it
|
// period where it will give an error if you make a request too quickly
|
||||||
// a moment to finish starting up.
|
await WaitForAngularCliServerToAcceptRequests(uri);
|
||||||
await Task.Delay(500);
|
|
||||||
|
|
||||||
return serverInfo;
|
return serverInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static async Task WaitForAngularCliServerToAcceptRequests(Uri cliServerUri)
|
||||||
|
{
|
||||||
|
// To determine when it's actually ready, try making HEAD requests to '/'. If it
|
||||||
|
// produces any HTTP response (even if it's 404) then it's ready. If it rejects the
|
||||||
|
// connection then it's not ready. We keep trying forever because this is dev-mode
|
||||||
|
// only, and only a single startup attempt will be made, and there's a further level
|
||||||
|
// of timeouts enforced on a per-request basis.
|
||||||
|
using (var client = new HttpClient())
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// If we get any HTTP response, the CLI server is ready
|
||||||
|
await client.SendAsync(
|
||||||
|
new HttpRequestMessage(HttpMethod.Head, cliServerUri),
|
||||||
|
new CancellationTokenSource(1000).Token);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
await Task.Delay(1000); // 1 second
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class AngularCliServerInfo
|
class AngularCliServerInfo
|
||||||
{
|
{
|
||||||
public int Port { get; set; }
|
public int Port { get; set; }
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Http;
|
|||||||
using Microsoft.AspNetCore.Http.Features;
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
using Microsoft.AspNetCore.NodeServices;
|
using Microsoft.AspNetCore.NodeServices;
|
||||||
using Microsoft.AspNetCore.SpaServices;
|
using Microsoft.AspNetCore.SpaServices;
|
||||||
|
using Microsoft.AspNetCore.SpaServices.Extensions.Util;
|
||||||
using Microsoft.AspNetCore.SpaServices.Prerendering;
|
using Microsoft.AspNetCore.SpaServices.Prerendering;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
@@ -68,6 +69,7 @@ namespace Microsoft.AspNetCore.Builder
|
|||||||
var excludePathStrings = (options.ExcludeUrls ?? Array.Empty<string>())
|
var excludePathStrings = (options.ExcludeUrls ?? Array.Empty<string>())
|
||||||
.Select(url => new PathString(url))
|
.Select(url => new PathString(url))
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
var buildTimeout = spaBuilder.Options.StartupTimeout;
|
||||||
|
|
||||||
applicationBuilder.Use(async (context, next) =>
|
applicationBuilder.Use(async (context, next) =>
|
||||||
{
|
{
|
||||||
@@ -85,9 +87,15 @@ namespace Microsoft.AspNetCore.Builder
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If we're building on demand, wait for that to finish, or raise any build errors
|
// If we're building on demand, wait for that to finish, or raise any build errors
|
||||||
if (buildOnDemandTask != null)
|
if (buildOnDemandTask != null && !buildOnDemandTask.IsCompleted)
|
||||||
{
|
{
|
||||||
await buildOnDemandTask;
|
// For better debuggability, create a per-request timeout that makes it clear if the
|
||||||
|
// prerendering builder took too long for this request, but without aborting the
|
||||||
|
// underlying build task so that subsequent requests could still work.
|
||||||
|
await buildOnDemandTask.WithTimeout(buildTimeout,
|
||||||
|
$"The prerendering build process did not complete within the " +
|
||||||
|
$"timeout period of {buildTimeout.Seconds} seconds. " +
|
||||||
|
$"Check the log output for error information.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// It's no good if we try to return a 304. We need to capture the actual
|
// It's no good if we try to return a 304. We need to capture the actual
|
||||||
@@ -225,7 +233,8 @@ namespace Microsoft.AspNetCore.Builder
|
|||||||
|
|
||||||
if (!string.IsNullOrEmpty(renderResult.RedirectUrl))
|
if (!string.IsNullOrEmpty(renderResult.RedirectUrl))
|
||||||
{
|
{
|
||||||
context.Response.Redirect(renderResult.RedirectUrl);
|
var permanentRedirect = renderResult.StatusCode.GetValueOrDefault() == 301;
|
||||||
|
context.Response.Redirect(renderResult.RedirectUrl, permanentRedirect);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -239,6 +248,11 @@ namespace Microsoft.AspNetCore.Builder
|
|||||||
$"embed any information you wish to return to the client.");
|
$"embed any information you wish to return to the client.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (renderResult.StatusCode.HasValue)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = renderResult.StatusCode.Value;
|
||||||
|
}
|
||||||
|
|
||||||
context.Response.ContentType = "text/html";
|
context.Response.ContentType = "text/html";
|
||||||
await context.Response.WriteAsync(renderResult.Html);
|
await context.Response.WriteAsync(renderResult.Html);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ namespace Microsoft.AspNetCore.Builder
|
|||||||
{
|
{
|
||||||
UseProxyToSpaDevelopmentServer(
|
UseProxyToSpaDevelopmentServer(
|
||||||
spaBuilder,
|
spaBuilder,
|
||||||
Task.FromResult(baseUri));
|
() => Task.FromResult(baseUri));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -54,10 +54,10 @@ namespace Microsoft.AspNetCore.Builder
|
|||||||
/// development. Do not enable this middleware in production applications.
|
/// development. Do not enable this middleware in production applications.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="spaBuilder">The <see cref="ISpaBuilder"/>.</param>
|
/// <param name="spaBuilder">The <see cref="ISpaBuilder"/>.</param>
|
||||||
/// <param name="baseUriTask">A <see cref="Task"/> that resolves with the target base URI to which requests should be proxied.</param>
|
/// <param name="baseUriTaskFactory">A callback that will be invoked on each request to supply a <see cref="Task"/> that resolves with the target base URI to which requests should be proxied.</param>
|
||||||
public static void UseProxyToSpaDevelopmentServer(
|
public static void UseProxyToSpaDevelopmentServer(
|
||||||
this ISpaBuilder spaBuilder,
|
this ISpaBuilder spaBuilder,
|
||||||
Task<Uri> baseUriTask)
|
Func<Task<Uri>> baseUriTaskFactory)
|
||||||
{
|
{
|
||||||
var applicationBuilder = spaBuilder.ApplicationBuilder;
|
var applicationBuilder = spaBuilder.ApplicationBuilder;
|
||||||
var applicationStoppingToken = GetStoppingToken(applicationBuilder);
|
var applicationStoppingToken = GetStoppingToken(applicationBuilder);
|
||||||
@@ -72,11 +72,11 @@ namespace Microsoft.AspNetCore.Builder
|
|||||||
var neverTimeOutHttpClient =
|
var neverTimeOutHttpClient =
|
||||||
SpaProxy.CreateHttpClientForProxy(Timeout.InfiniteTimeSpan);
|
SpaProxy.CreateHttpClientForProxy(Timeout.InfiniteTimeSpan);
|
||||||
|
|
||||||
// Proxy all requests into the Angular CLI server
|
// Proxy all requests to the SPA development server
|
||||||
applicationBuilder.Use(async (context, next) =>
|
applicationBuilder.Use(async (context, next) =>
|
||||||
{
|
{
|
||||||
var didProxyRequest = await SpaProxy.PerformProxyRequest(
|
var didProxyRequest = await SpaProxy.PerformProxyRequest(
|
||||||
context, neverTimeOutHttpClient, baseUriTask, applicationStoppingToken,
|
context, neverTimeOutHttpClient, baseUriTaskFactory(), applicationStoppingToken,
|
||||||
proxy404s: true);
|
proxy404s: true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ using System.IO;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.SpaServices.Extensions.Util;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer
|
namespace Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer
|
||||||
{
|
{
|
||||||
@@ -18,7 +19,6 @@ namespace Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer
|
|||||||
{
|
{
|
||||||
private const string LogCategoryName = "Microsoft.AspNetCore.SpaServices";
|
private const string LogCategoryName = "Microsoft.AspNetCore.SpaServices";
|
||||||
private static TimeSpan RegexMatchTimeout = TimeSpan.FromSeconds(5); // This is a development-time only feature, so a very long timeout is fine
|
private static TimeSpan RegexMatchTimeout = TimeSpan.FromSeconds(5); // This is a development-time only feature, so a very long timeout is fine
|
||||||
private static TimeSpan StartupTimeout = TimeSpan.FromSeconds(50); // Note that the HTTP request itself by default times out after 60s, so you only get useful error information if this is shorter
|
|
||||||
|
|
||||||
public static void Attach(
|
public static void Attach(
|
||||||
ISpaBuilder spaBuilder,
|
ISpaBuilder spaBuilder,
|
||||||
@@ -48,7 +48,16 @@ namespace Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer
|
|||||||
var targetUriTask = portTask.ContinueWith(
|
var targetUriTask = portTask.ContinueWith(
|
||||||
task => new UriBuilder("http", "localhost", task.Result).Uri);
|
task => new UriBuilder("http", "localhost", task.Result).Uri);
|
||||||
|
|
||||||
SpaProxyingExtensions.UseProxyToSpaDevelopmentServer(spaBuilder, targetUriTask);
|
SpaProxyingExtensions.UseProxyToSpaDevelopmentServer(spaBuilder, () =>
|
||||||
|
{
|
||||||
|
// On each request, we create a separate startup task with its own timeout. That way, even if
|
||||||
|
// the first request times out, subsequent requests could still work.
|
||||||
|
var timeout = spaBuilder.Options.StartupTimeout;
|
||||||
|
return targetUriTask.WithTimeout(timeout,
|
||||||
|
$"The create-react-app server did not start listening for requests " +
|
||||||
|
$"within the timeout period of {timeout.Seconds} seconds. " +
|
||||||
|
$"Check the log output for error information.");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<int> StartCreateReactAppServerAsync(
|
private static async Task<int> StartCreateReactAppServerAsync(
|
||||||
@@ -75,8 +84,7 @@ namespace Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer
|
|||||||
// no compiler warnings. So instead of waiting for that, consider it ready as soon
|
// no compiler warnings. So instead of waiting for that, consider it ready as soon
|
||||||
// as it starts listening for requests.
|
// as it starts listening for requests.
|
||||||
await npmScriptRunner.StdOut.WaitForMatch(
|
await npmScriptRunner.StdOut.WaitForMatch(
|
||||||
new Regex("Starting the development server", RegexOptions.None, RegexMatchTimeout),
|
new Regex("Starting the development server", RegexOptions.None, RegexMatchTimeout));
|
||||||
StartupTimeout);
|
|
||||||
}
|
}
|
||||||
catch (EndOfStreamException ex)
|
catch (EndOfStreamException ex)
|
||||||
{
|
{
|
||||||
@@ -85,13 +93,6 @@ namespace Microsoft.AspNetCore.SpaServices.ReactDevelopmentServer
|
|||||||
$"create-react-app server was listening for requests. The error output was: " +
|
$"create-react-app server was listening for requests. The error output was: " +
|
||||||
$"{stdErrReader.ReadAsString()}", ex);
|
$"{stdErrReader.ReadAsString()}", ex);
|
||||||
}
|
}
|
||||||
catch (TaskCanceledException ex)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException(
|
|
||||||
$"The create-react-app server did not start listening for requests " +
|
|
||||||
$"within the timeout period of {StartupTimeout.Seconds} seconds. " +
|
|
||||||
$"Check the log output for error information.", ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return portNumber;
|
return portNumber;
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ namespace Microsoft.AspNetCore.SpaServices
|
|||||||
// Developers who need to host more than one SPA with distinct default pages can
|
// Developers who need to host more than one SPA with distinct default pages can
|
||||||
// override the file provider
|
// override the file provider
|
||||||
app.UseSpaStaticFilesInternal(
|
app.UseSpaStaticFilesInternal(
|
||||||
overrideFileProvider: options.DefaultPageFileProvider,
|
options.DefaultPageStaticFileOptions ?? new StaticFileOptions(),
|
||||||
allowFallbackOnServingWebRootFiles: true);
|
allowFallbackOnServingWebRootFiles: true);
|
||||||
|
|
||||||
// If the default file didn't get served as a static file (usually because it was not
|
// If the default file didn't get served as a static file (usually because it was not
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.FileProviders;
|
using Microsoft.Extensions.FileProviders;
|
||||||
@@ -29,7 +30,7 @@ namespace Microsoft.AspNetCore.SpaServices
|
|||||||
internal SpaOptions(SpaOptions copyFromOptions)
|
internal SpaOptions(SpaOptions copyFromOptions)
|
||||||
{
|
{
|
||||||
_defaultPage = copyFromOptions.DefaultPage;
|
_defaultPage = copyFromOptions.DefaultPage;
|
||||||
DefaultPageFileProvider = copyFromOptions.DefaultPageFileProvider;
|
DefaultPageStaticFileOptions = copyFromOptions.DefaultPageStaticFileOptions;
|
||||||
SourcePath = copyFromOptions.SourcePath;
|
SourcePath = copyFromOptions.SourcePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,14 +53,14 @@ namespace Microsoft.AspNetCore.SpaServices
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the <see cref="IFileProvider"/> that supplies content
|
/// Gets or sets the <see cref="StaticFileOptions"/> that supplies content
|
||||||
/// for serving the SPA's default page.
|
/// for serving the SPA's default page.
|
||||||
///
|
///
|
||||||
/// If not set, a default file provider will read files from the
|
/// If not set, a default file provider will read files from the
|
||||||
/// <see cref="IHostingEnvironment.WebRootPath"/>, which by default is
|
/// <see cref="IHostingEnvironment.WebRootPath"/>, which by default is
|
||||||
/// the <c>wwwroot</c> directory.
|
/// the <c>wwwroot</c> directory.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IFileProvider DefaultPageFileProvider { get; set; }
|
public StaticFileOptions DefaultPageStaticFileOptions { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the path, relative to the application working directory,
|
/// Gets or sets the path, relative to the application working directory,
|
||||||
@@ -67,5 +68,11 @@ namespace Microsoft.AspNetCore.SpaServices
|
|||||||
/// development. The directory may not exist in published applications.
|
/// development. The directory may not exist in published applications.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string SourcePath { get; set; }
|
public string SourcePath { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the maximum duration that a request will wait for the SPA
|
||||||
|
/// to become ready to serve to the client.
|
||||||
|
/// </summary>
|
||||||
|
public TimeSpan StartupTimeout { get; set; } = TimeSpan.FromSeconds(50);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,53 +50,75 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="applicationBuilder">The <see cref="IApplicationBuilder"/>.</param>
|
/// <param name="applicationBuilder">The <see cref="IApplicationBuilder"/>.</param>
|
||||||
public static void UseSpaStaticFiles(this IApplicationBuilder applicationBuilder)
|
public static void UseSpaStaticFiles(this IApplicationBuilder applicationBuilder)
|
||||||
|
{
|
||||||
|
UseSpaStaticFiles(applicationBuilder, new StaticFileOptions());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Configures the application to serve static files for a Single Page Application (SPA).
|
||||||
|
/// The files will be located using the registered <see cref="ISpaStaticFileProvider"/> service.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="applicationBuilder">The <see cref="IApplicationBuilder"/>.</param>
|
||||||
|
/// <param name="options">Specifies options for serving the static files.</param>
|
||||||
|
public static void UseSpaStaticFiles(this IApplicationBuilder applicationBuilder, StaticFileOptions options)
|
||||||
{
|
{
|
||||||
if (applicationBuilder == null)
|
if (applicationBuilder == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(applicationBuilder));
|
throw new ArgumentNullException(nameof(applicationBuilder));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(options));
|
||||||
|
}
|
||||||
|
|
||||||
UseSpaStaticFilesInternal(applicationBuilder,
|
UseSpaStaticFilesInternal(applicationBuilder,
|
||||||
overrideFileProvider: null,
|
staticFileOptions: options,
|
||||||
allowFallbackOnServingWebRootFiles: false);
|
allowFallbackOnServingWebRootFiles: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void UseSpaStaticFilesInternal(
|
internal static void UseSpaStaticFilesInternal(
|
||||||
this IApplicationBuilder app,
|
this IApplicationBuilder app,
|
||||||
IFileProvider overrideFileProvider,
|
StaticFileOptions staticFileOptions,
|
||||||
bool allowFallbackOnServingWebRootFiles)
|
bool allowFallbackOnServingWebRootFiles)
|
||||||
{
|
{
|
||||||
var shouldServeStaticFiles = ShouldServeStaticFiles(
|
if (staticFileOptions == null)
|
||||||
app,
|
|
||||||
overrideFileProvider,
|
|
||||||
allowFallbackOnServingWebRootFiles,
|
|
||||||
out var fileProviderOrDefault);
|
|
||||||
|
|
||||||
if (shouldServeStaticFiles)
|
|
||||||
{
|
{
|
||||||
app.UseStaticFiles(new StaticFileOptions
|
throw new ArgumentNullException(nameof(staticFileOptions));
|
||||||
{
|
|
||||||
FileProvider = fileProviderOrDefault
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the file provider was explicitly supplied, that takes precedence over any other
|
||||||
|
// configured file provider. This is most useful if the application hosts multiple SPAs
|
||||||
|
// (via multiple calls to UseSpa()), so each needs to serve its own separate static files
|
||||||
|
// instead of using AddSpaStaticFiles/UseSpaStaticFiles.
|
||||||
|
// But if no file provider was specified, try to get one from the DI config.
|
||||||
|
if (staticFileOptions.FileProvider == null)
|
||||||
|
{
|
||||||
|
var shouldServeStaticFiles = ShouldServeStaticFiles(
|
||||||
|
app,
|
||||||
|
allowFallbackOnServingWebRootFiles,
|
||||||
|
out var fileProviderOrDefault);
|
||||||
|
if (shouldServeStaticFiles)
|
||||||
|
{
|
||||||
|
staticFileOptions.FileProvider = fileProviderOrDefault;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// The registered ISpaStaticFileProvider says we shouldn't
|
||||||
|
// serve static files
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
app.UseStaticFiles(staticFileOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool ShouldServeStaticFiles(
|
private static bool ShouldServeStaticFiles(
|
||||||
IApplicationBuilder app,
|
IApplicationBuilder app,
|
||||||
IFileProvider overrideFileProvider,
|
|
||||||
bool allowFallbackOnServingWebRootFiles,
|
bool allowFallbackOnServingWebRootFiles,
|
||||||
out IFileProvider fileProviderOrDefault)
|
out IFileProvider fileProviderOrDefault)
|
||||||
{
|
{
|
||||||
if (overrideFileProvider != null)
|
|
||||||
{
|
|
||||||
// If the file provider was explicitly supplied, that takes precedence over any other
|
|
||||||
// configured file provider. This is most useful if the application hosts multiple SPAs
|
|
||||||
// (via multiple calls to UseSpa()), so each needs to serve its own separate static files
|
|
||||||
// instead of using AddSpaStaticFiles/UseSpaStaticFiles.
|
|
||||||
fileProviderOrDefault = overrideFileProvider;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var spaStaticFilesService = app.ApplicationServices.GetService<ISpaStaticFileProvider>();
|
var spaStaticFilesService = app.ApplicationServices.GetService<ISpaStaticFileProvider>();
|
||||||
if (spaStaticFilesService != null)
|
if (spaStaticFilesService != null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ namespace Microsoft.AspNetCore.NodeServices.Util
|
|||||||
Task.Factory.StartNew(Run);
|
Task.Factory.StartNew(Run);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<Match> WaitForMatch(Regex regex, TimeSpan timeout = default)
|
public Task<Match> WaitForMatch(Regex regex)
|
||||||
{
|
{
|
||||||
var tcs = new TaskCompletionSource<Match>();
|
var tcs = new TaskCompletionSource<Match>();
|
||||||
var completionLock = new object();
|
var completionLock = new object();
|
||||||
@@ -72,15 +72,6 @@ namespace Microsoft.AspNetCore.NodeServices.Util
|
|||||||
OnReceivedLine += onReceivedLineHandler;
|
OnReceivedLine += onReceivedLineHandler;
|
||||||
OnStreamClosed += onStreamClosedHandler;
|
OnStreamClosed += onStreamClosedHandler;
|
||||||
|
|
||||||
if (timeout != default)
|
|
||||||
{
|
|
||||||
var timeoutToken = new CancellationTokenSource(timeout);
|
|
||||||
timeoutToken.Token.Register(() =>
|
|
||||||
{
|
|
||||||
ResolveIfStillPending(() => tcs.SetCanceled());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return tcs.Task;
|
return tcs.Task;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.SpaServices.Extensions.Util
|
||||||
|
{
|
||||||
|
internal static class TaskTimeoutExtensions
|
||||||
|
{
|
||||||
|
public static async Task WithTimeout(this Task task, TimeSpan timeoutDelay, string message)
|
||||||
|
{
|
||||||
|
if (task == await Task.WhenAny(task, Task.Delay(timeoutDelay)))
|
||||||
|
{
|
||||||
|
task.Wait(); // Allow any errors to propagate
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new TimeoutException(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<T> WithTimeout<T>(this Task<T> task, TimeSpan timeoutDelay, string message)
|
||||||
|
{
|
||||||
|
if (task == await Task.WhenAny(task, Task.Delay(timeoutDelay)))
|
||||||
|
{
|
||||||
|
return task.Result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new TimeoutException(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -561,7 +561,7 @@ Typically, when you change a source file, the effects appear in your local brows
|
|||||||
First ensure you already have a working Webpack dev middleware setup. Then, install the `webpack-hot-middleware` NPM module:
|
First ensure you already have a working Webpack dev middleware setup. Then, install the `webpack-hot-middleware` NPM module:
|
||||||
|
|
||||||
```
|
```
|
||||||
npm install --save webpack-hot-middleware
|
npm install --save-dev webpack-hot-middleware
|
||||||
```
|
```
|
||||||
|
|
||||||
At the top of your `Startup.cs` file, add the following namespace reference:
|
At the top of your `Startup.cs` file, add the following namespace reference:
|
||||||
@@ -620,7 +620,7 @@ app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions {
|
|||||||
Also, install the NPM module `aspnet-webpack-react`, e.g.:
|
Also, install the NPM module `aspnet-webpack-react`, e.g.:
|
||||||
|
|
||||||
```
|
```
|
||||||
npm install --save aspnet-webpack-react
|
npm install --save-dev aspnet-webpack-react
|
||||||
```
|
```
|
||||||
|
|
||||||
Now if you edit any React component (e.g., in `.jsx` or `.tsx` files), the updated component will be injected into the running application, and will even preserve its in-memory state.
|
Now if you edit any React component (e.g., in `.jsx` or `.tsx` files), the updated component will be injected into the running application, and will even preserve its in-memory state.
|
||||||
|
|||||||
Reference in New Issue
Block a user