Compare commits

..

1 Commits

Author SHA1 Message Date
YuCheng Hu
9034b08773
docs: 请简述你的修改...
app.component.css 这个文件,初始化后,新的文件名是 app.component.styl。

app.component.css 在我们的版本初始化项目后已经找不到了。
2019-05-16 15:27:09 -04:00
16745 changed files with 363404 additions and 1457499 deletions

View File

@ -1,94 +1,5 @@
# Bazel does not yet support wildcards or other .gitignore semantics for
# .bazelignore. Two issues for this feature request are outstanding:
# https://github.com/bazelbuild/bazel/issues/7093
# https://github.com/bazelbuild/bazel/issues/8106
.git
node_modules node_modules
dist dist
aio/content
aio/node_modules aio/node_modules
aio/tools/examples/shared/node_modules aio/tools/examples/shared/node_modules
packages/bazel/node_modules integration/bazel
integration/bazel/bazel-bazel
integration/bazel/bazel-bin
integration/bazel/bazel-out
integration/bazel/bazel-testlogs
integration/bazel-schematics/demo
# All integration test node_modules folders
integration/bazel/node_modules
integration/bazel-schematics/node_modules
integration/cli-elements-universal/node_modules
integration/cli-hello-world/node_modules
integration/cli-hello-world-ivy-compat/node_modules
integration/cli-hello-world-ivy-i18n/node_modules
integration/cli-hello-world-ivy-minimal/node_modules
integration/cli-hello-world-lazy/node_modules
integration/dynamic-compiler/node_modules
integration/hello_world__closure/node_modules
integration/hello_world__systemjs_umd/node_modules
integration/i18n/node_modules
integration/injectable-def/node_modules
integration/ivy-i18n/node_modules
integration/ng_elements/node_modules
integration/ng_elements_schematics/node_modules
integration/ng_update/node_modules
integration/ng_update_migrations/node_modules
integration/ngcc/node_modules
integration/platform-server/node_modules
integration/service-worker-schema/node_modules
integration/side-effects/node_modules
integration/terser/node_modules
integration/typings_test_ts36/node_modules
integration/typings_test_ts37/node_modules
# All integration test .yarn_local_cache folders
integration/bazel/.yarn_local_cache
integration/bazel-schematics/.yarn_local_cache
integration/cli-elements-universal/.yarn_local_cache
integration/cli-hello-world/.yarn_local_cache
integration/cli-hello-world-ivy-compat/.yarn_local_cache
integration/cli-hello-world-ivy-i18n/.yarn_local_cache
integration/cli-hello-world-ivy-minimal/.yarn_local_cache
integration/cli-hello-world-lazy/.yarn_local_cache
integration/dynamic-compiler/.yarn_local_cache
integration/hello_world__closure/.yarn_local_cache
integration/hello_world__systemjs_umd/.yarn_local_cache
integration/i18n/.yarn_local_cache
integration/injectable-def/.yarn_local_cache
integration/ivy-i18n/.yarn_local_cache
integration/ng_elements/.yarn_local_cache
integration/ng_elements_schematics/.yarn_local_cache
integration/ng_update/.yarn_local_cache
integration/ng_update_migrations/.yarn_local_cache
integration/ngcc/.yarn_local_cache
integration/platform-server/.yarn_local_cache
integration/service-worker-schema/.yarn_local_cache
integration/side-effects/.yarn_local_cache
integration/terser/.yarn_local_cache
integration/typings_test_ts36/.yarn_local_cache
integration/typings_test_ts37/.yarn_local_cache
# All integration test NPM_PACKAGE_MANIFEST.json folders
integration/bazel/NPM_PACKAGE_MANIFEST.json
integration/bazel-schematics/NPM_PACKAGE_MANIFEST.json
integration/cli-elements-universal/NPM_PACKAGE_MANIFEST.json
integration/cli-hello-world/NPM_PACKAGE_MANIFEST.json
integration/cli-hello-world-ivy-compat/NPM_PACKAGE_MANIFEST.json
integration/cli-hello-world-ivy-i18n/NPM_PACKAGE_MANIFEST.json
integration/cli-hello-world-ivy-minimal/NPM_PACKAGE_MANIFEST.json
integration/cli-hello-world-lazy/NPM_PACKAGE_MANIFEST.json
integration/dynamic-compiler/NPM_PACKAGE_MANIFEST.json
integration/hello_world__closure/NPM_PACKAGE_MANIFEST.json
integration/hello_world__systemjs_umd/NPM_PACKAGE_MANIFEST.json
integration/i18n/NPM_PACKAGE_MANIFEST.json
integration/injectable-def/NPM_PACKAGE_MANIFEST.json
integration/ivy-i18n/NPM_PACKAGE_MANIFEST.json
integration/ng_elements/NPM_PACKAGE_MANIFEST.json
integration/ng_elements_schematics/NPM_PACKAGE_MANIFEST.json
integration/ng_update/NPM_PACKAGE_MANIFEST.json
integration/ng_update_migrations/NPM_PACKAGE_MANIFEST.json
integration/ngcc/NPM_PACKAGE_MANIFEST.json
integration/platform-server/NPM_PACKAGE_MANIFEST.json
integration/service-worker-schema/NPM_PACKAGE_MANIFEST.json
integration/side-effects/NPM_PACKAGE_MANIFEST.json
integration/terser/NPM_PACKAGE_MANIFEST.json
integration/typings_test_ts36/NPM_PACKAGE_MANIFEST.json
integration/typings_test_ts37/NPM_PACKAGE_MANIFEST.json

130
.bazelrc
View File

@ -1,3 +1,20 @@
# Load any settings specific to the current user
try-import .bazelrc.user
################################
# Settings for Angular team members only
################################
# To enable this feature check the "Remote caching" section in docs/BAZEL.md.
build:angular-team --remote_http_cache=https://storage.googleapis.com/angular-team-cache
###############################
# Typescript / Angular / Sass #
###############################
# Make compilation fast, by keeping a few copies of the compilers
# running as daemons, and cache SourceFile AST's to reduce parse time.
build --strategy=TypeScriptCompile=worker
build --strategy=AngularTemplateCompile=worker
# Enable debugging tests with --config=debug # Enable debugging tests with --config=debug
test:debug --test_arg=--node_options=--inspect-brk --test_output=streamed --test_strategy=exclusive --test_timeout=9999 --nocache_test_results test:debug --test_arg=--node_options=--inspect-brk --test_output=streamed --test_strategy=exclusive --test_timeout=9999 --nocache_test_results
@ -18,8 +35,10 @@ test:debug --test_arg=--node_options=--inspect-brk --test_output=streamed --test
# See https://github.com/bazelbuild/bazel/issues/4603 # See https://github.com/bazelbuild/bazel/issues/4603
build --symlink_prefix=dist/ build --symlink_prefix=dist/
# Performance: avoid stat'ing input files
build --watchfs
# Turn off legacy external runfiles # Turn off legacy external runfiles
build --nolegacy_external_runfiles
run --nolegacy_external_runfiles run --nolegacy_external_runfiles
test --nolegacy_external_runfiles test --nolegacy_external_runfiles
@ -33,11 +52,6 @@ build --incompatible_strict_action_env
run --incompatible_strict_action_env run --incompatible_strict_action_env
test --incompatible_strict_action_env test --incompatible_strict_action_env
# Do not build runfile trees by default. If an execution strategy relies on runfile
# symlink teee, the tree is created on-demand. See: https://github.com/bazelbuild/bazel/issues/6627
# and https://github.com/bazelbuild/bazel/commit/03246077f948f2790a83520e7dccc2625650e6df
build --nobuild_runfile_links
############################### ###############################
# Release support # # Release support #
# Turn on these settings with # # Turn on these settings with #
@ -47,12 +61,7 @@ build --nobuild_runfile_links
# Releases should always be stamped with version control info # Releases should always be stamped with version control info
# This command assumes node on the path and is a workaround for # This command assumes node on the path and is a workaround for
# https://github.com/bazelbuild/bazel/issues/4802 # https://github.com/bazelbuild/bazel/issues/4802
build:release --workspace_status_command="yarn -s ng-dev release build-env-stamp --mode=release" build:release --workspace_status_command="node ./tools/bazel_stamp_vars.js"
build:release --stamp
# Snapshots should also be stamped with version control information.
build:snapshot --workspace_status_command="yarn -s ng-dev release build-env-stamp --mode=snapshot"
build:snapshot --stamp
############################### ###############################
# Output # # Output #
@ -65,89 +74,48 @@ query --output=label_kind
# By default, failing tests don't print any output, it goes to the log file # By default, failing tests don't print any output, it goes to the log file
test --test_output=errors test --test_output=errors
# Show which actions are run under workers,
# and print all the actions running in parallel.
# Helps to demonstrate that bazel uses all the cores on the machine.
build --experimental_ui
test --experimental_ui
################################ ################################
# Settings for CircleCI # # Settings for CircleCI #
################################ ################################
# Bazel flags for CircleCI are in /.circleci/bazel.linux.rc and /.circleci/bazel.windows.rc # Bazel flags for CircleCI are in /.circleci/bazel.rc
##################################
# Settings for integration tests #
##################################
# Trick bazel into treating BUILD files under integration/bazel as being regular files
# This lets us glob() up all the files inside this integration test to make them inputs to tests
# (Note, we cannot use common --deleted_packages because the bazel version command doesn't support it)
build --deleted_packages=integration/bazel,integration/bazel/src,integration/bazel/src/hello-world,integration/bazel/test,integration/bazel/tools,integration/bazel/test/e2e
query --deleted_packages=integration/bazel,integration/bazel/src,integration/bazel/src/hello-world,integration/bazel/test,integration/bazel/tools,integration/bazel/test/e2e
################################ ################################
# Temporary Settings for Ivy # # Temporary Settings for Ivy #
################################ ################################
# To determine if the compiler used should be Ivy instead of ViewEngine, one can use `--config=ivy` # to determine if the compiler used should be Ivy or ViewEngine one can use `--define=compile=aot` on
# on any bazel target. This is a temporary flag until codebase is permanently switched to Ivy. # any bazel target. This is a temporary flag until codebase is permanently switched to Ivy.
build --define=angular_ivy_enabled=False build --define=compile=legacy
build:view-engine --define=angular_ivy_enabled=False ###############################
build:ivy --define=angular_ivy_enabled=True # Remote Build Execution support
# Turn on these settings with
# --config=remote
###############################
################################## # Load default settings for Remote Build Execution
# Remote Build Execution support # # When updating, the URLs of bazel_toolchains in packages/bazel/package.bzl
# Turn on these settings with # # may also need to be updated (see https://github.com/angular/angular/pull/27935)
# --config=remote # import %workspace%/third_party/github.com/bazelbuild/bazel-toolchains/bazelrc/bazel-0.21.0.bazelrc
##################################
# The following --define=EXECUTOR=remote will be able to be removed
# once https://github.com/bazelbuild/bazel/issues/7254 is fixed
build:remote --define=EXECUTOR=remote
# Set a higher timeout value, just in case.
build:remote --remote_timeout=600
# Increase the default number of jobs by 50% because our build has lots of # Increase the default number of jobs by 50% because our build has lots of
# parallelism # parallelism
build:remote --jobs=150 build:remote --jobs=150
build:remote --google_default_credentials
# Force remote exeuctions to consider the entire run as linux # Point to our custom execution platform; see tools/BUILD.bazel
build:remote --cpu=k8 build:remote --extra_execution_platforms=//tools:rbe_ubuntu1604-angular
build:remote --host_cpu=k8 build:remote --host_platform=//tools:rbe_ubuntu1604-angular
build:remote --platforms=//tools:rbe_ubuntu1604-angular
# Toolchain and platform related flags # Remote instance.
build:remote --crosstool_top=//dev-infra/bazel/remote-execution/cpp:cc_toolchain_suite build:remote --remote_instance_name=projects/internal-200822/instances/default_instance
build:remote --extra_toolchains=//dev-infra/bazel/remote-execution/cpp:cc_toolchain
build:remote --extra_execution_platforms=//dev-infra/bazel/remote-execution:platform
build:remote --host_platform=//dev-infra/bazel/remote-execution:platform
build:remote --platforms=//dev-infra/bazel/remote-execution:platform
# Remote instance and caching # Do not accept remote cache.
build:remote --remote_instance_name=projects/internal-200822/instances/primary_instance # We need to understand the security risks of using prior build artifacts.
build:remote --project_id=internal-200822 build:remote --remote_accept_cached=false
build:remote --remote_cache=remotebuildexecution.googleapis.com
build:remote --remote_executor=remotebuildexecution.googleapis.com
##################################
# Saucelabs tests settings #
# Turn on these settings with #
# --config=saucelabs #
##################################
# For saucelabs tests we don't want to enable flaky test attempts. Karma has its own integrated
# retry mechanism and we do not want to retry unnecessarily if Karma already tried multiple times.
test:saucelabs --flaky_test_attempts=1
################
# Flag Aliases #
################
# --ng_perf will ask the Ivy compiler to produce performance results for each build.
build --flag_alias=ng_perf=//packages/compiler-cli:ng_perf
####################################################
# User bazel configuration
# NOTE: This needs to be the *last* entry in the config.
####################################################
# Load any settings which are specific to the current user. Needs to be *last* statement
# in this config, as the user configuration should be able to overwrite flags from this file.
try-import .bazelrc.user

View File

@ -1 +0,0 @@
4.0.0

42
.buildkite/Dockerfile Normal file
View File

@ -0,0 +1,42 @@
# Heavily based on https://github.com/StefanScherer/dockerfiles-windows/ images.
# Combines the node windowsservercore image with the Bazel Prerequisites (https://docs.bazel.build/versions/master/install-windows.html).
# msys install taken from https://github.com/StefanScherer/dockerfiles-windows/issues/30
# VS redist install taken from https://github.com/StefanScherer/dockerfiles-windows/blob/master/apache/Dockerfile
# The nanoserver image won't work because MSYS2 does not run in it https://github.com/Alexpux/MSYS2-packages/issues/1493
# Before building this image, you must locally build node-windows:10.13.0-windowsservercore-1803.
# Clone https://github.com/StefanScherer/dockerfiles-windows/commit/4ce7101a766b9b880ac262479dd9126b64d656cf and build using
# docker build -t node-windows:10.13.0-windowsservercore-1803 --build-arg core=microsoft/windowsservercore:1803 --build-arg target=microsoft/windowsservercore:1803 .
FROM node-windows:10.13.0-windowsservercore-1803
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
# Install 7zip to extract msys2
RUN Invoke-WebRequest -UseBasicParsing 'https://www.7-zip.org/a/7z1805-x64.exe' -OutFile 7z.exe
# For some reason the last letter in the destination directory is lost. So '/D=C:\\7zip0' will extract to '/D=C:\\7zip'.
RUN Start-Process -FilePath 'C:\\7z.exe' -ArgumentList '/S', '/D=C:\\7zip0' -NoNewWindow -Wait
# Extract msys2
RUN Invoke-WebRequest -UseBasicParsing 'http://repo.msys2.org/distrib/x86_64/msys2-base-x86_64-20180531.tar.xz' -OutFile msys2.tar.xz
RUN Start-Process -FilePath 'C:\\7zip\\7z' -ArgumentList 'e', 'msys2.tar.xz' -Wait
RUN Start-Process -FilePath 'C:\\7zip\\7z' -ArgumentList 'x', 'msys2.tar', '-oC:\\' -Wait
RUN Remove-Item msys2.tar.xz
RUN Remove-Item msys2.tar
RUN Remove-Item 7z.exe
RUN Remove-Item -Recurse 7zip
# Add MSYS2 to PATH, and set BAZEL_SH
RUN [Environment]::SetEnvironmentVariable('Path', $env:Path + ';C:\msys64\usr\bin', [System.EnvironmentVariableTarget]::Machine)
RUN [Environment]::SetEnvironmentVariable('BAZEL_SH', 'C:\msys64\usr\bin\bash.exe', [System.EnvironmentVariableTarget]::Machine)
# Install Microsoft Visual C++ Redistributable for Visual Studio 2015
RUN Invoke-WebRequest -UseBasicParsing 'https://download.microsoft.com/download/9/3/F/93FCF1E7-E6A4-478B-96E7-D4B285925B00/vc_redist.x64.exe' -OutFile vc_redist.x64.exe
RUN Start-Process 'c:\\vc_redist.x64.exe' -ArgumentList '/Install', '/Passive', '/NoRestart' -NoNewWindow -Wait
RUN Remove-Item vc_redist.x64.exe
# Add a fix for https://github.com/docker/for-win/issues/2920 as entry point to the container.
SHELL ["cmd", "/c"]
COPY "fix-msys64.cmd" "C:\\fix-msys64.cmd"
ENTRYPOINT cmd /C C:\\fix-msys64.cmd && cmd /c
CMD ["cmd.exe"]

96
.buildkite/README.md Normal file
View File

@ -0,0 +1,96 @@
# BuildKite configuration
This folder contains configuration for the [BuildKite](https://buildkite.com) based CI checks for
this repository.
BuildKite is a CI provider that provides build coordination and reports while we provide the
infrastructure.
CI runs are triggered by new PRs and will show up on the GitHub checks interface, along with the
other current CI solutions.
Currently it is only used for tests on Windows platforms.
## The build pipeline
BuildKite uses a pipeline for each repository. The `pipeline.yml` file defines pipeline
[build steps](https://buildkite.com/docs/pipelines/defining-steps) for this repository.
Run results can be seen in the GitHub checks interface and in the
[pipeline dashboard](https://buildkite.com/angular/angular).
Although most configuration is done via `pipeline.yml`, some options are only available
in the online [pipeline settings](https://buildkite.com/angular/angular/settings).
## Infrastructure
BuildKite does not provide the host machines where the builds runs, providing instead the
[BuildKite Agent](https://buildkite.com/docs/agent/v3) that should be run our own infrastructure.
### Agents
This agent polls the BuildKite API for builds, runs them, and reports back the results.
Agents are the unit of concurrency: each agent can run one build at any given time.
Adding agents allows more builds to be ran at the same time.
Individual agents can have tags, and pipeline steps can target only agents with certain tags via the
`agents` field in `pipeline.yml`.
For example: agents on Windows machines are tagged as `windows`, and the Windows specific build
steps list `windows: true` in their `agents` field.
You can see the current agent pool, along with their tags, in the
[agents list](https://buildkite.com/organizations/angular/agents).
### Our host machines
We use [Google Cloud](https://cloud.google.com/) as our cloud provider, under the
[Angular project](https://console.cloud.google.com/home/dashboard?project=internal-200822).
To access this project you need need to be logged in with a Google account that's a member of
team@angular.io.
For googlers this may be your google.com account, for others it is an angular.io account.
In this project we have a number of Windows VMs running, each of them with several agents.
The `provision-windows-buildkite.ps1` file contains instructions on how to create new host VMs that
are fully configured to run the BuildKite agents as services.
Our pipeline uses [docker-buildkite-plugin](https://github.com/buildkite-plugins/docker-buildkite-plugin)
to run build steps inside docker containers.
This way we achieve isolation and hermeticity.
The `Dockerfile` file describes a custom Docker image that includes NodeJs, Yarn, and the Bazel
pre-requisites on Windows.
To upload a new version of the docker image, follow any build instructions in `Dockerfile` and then
run `docker build -t angular/node-bazel-windows:NEW_VERSION`, followed by
`docker push angular/node-bazel-windows:NEW_VERSION`.
After being pushed it should be available online, and you can use the new version in `pipeline.yml`.
## Caretaker
BuildKite status can be found at https://www.buildkitestatus.com/.
Issues related to the BuildKite setup should be escalated to the Tools Team via the current
caretaker, followed by Alex Eagle and Filipe Silva.
Support requests should be submitted via email to support@buildkite.com and cc Igor, Misko, Alex,
Jeremy and Manu
## Rollout strategy
At the moment our BuildKite CI uses 1 host VM running 4 agents, thus being capable of 4 concurrent
builds.
The only test running is `bazel test //tools/ts-api-guardian:all`, and the PR check is not
mandatory.
In the future we should add cache support to speed up the initial `yarn` install, and also Bazel
remote caching to speed up Bazel builds.
After the current setup is verified as stable and reliable the GitHub PR check can become mandatory.
The tests ran should also be expanded to cover most, if not all, of the Bazel tests.

View File

@ -0,0 +1,6 @@
@echo off
REM Fix for https://github.com/docker/for-win/issues/2920
REM echo "Fixing msys64 folder..."
REM Touch all .dll files inside C:\msys64\
forfiles /p C:\msys64\ /s /m *.dll /c "cmd /c Copy /B @path+,, >NUL"
REM echo "Fixed msys64 folder."

10
.buildkite/pipeline.yml Normal file
View File

@ -0,0 +1,10 @@
steps:
- label: windows-test
commands:
- "yarn install --frozen-lockfile --non-interactive --network-timeout 100000"
- "yarn bazel test //tools/ts-api-guardian:all --noshow_progress"
plugins:
- docker#v2.1.0:
image: "filipesilva/node-bazel-windows:0.0.2"
agents:
windows: true

View File

@ -0,0 +1,94 @@
# PowerShell script to provision a Windows Server with BuildKite
# This script follows https://buildkite.com/docs/agent/v3/windows.
# Instructions
# VM creation:
# In Google Cloud Platform, create a Compute Engine instance.
# We recommend machine type n1-standard-16 (16 vCPUs, 60 GB memory).
# Use a recent windows boot disk with container support such as
# "Windows Server version 1803 Datacenter Core for Containers", and add a 128GB SSD disk.
# Give it a name, then click "Create".
# VM setup:
# In the Compute Engine menu, select "VM Instances". Click on the VM name you chose before.
# Click "Set Windows Password" to choose a username and password.
# Click RDP to open a remote desktop via browser, using the username and password.
# In the Windows command prompt start an elevated powershell by inputing
# "powershell -Command "Start-Process PowerShell -Verb RunAs" followed by Enter.
# Download and execute this script from GitHub, passing the token (mandatory), tags (optional)
# and number of agents (optional) as args:
# ```
# Invoke-WebRequest -Uri https://raw.githubusercontent.com/angular/angular/master/.buildkite/provision-windows-buildkite.ps1 -OutFile provision.ps1
# .\provision.ps1 -token "MY_TOKEN" -tags "windows=true,another_tag=true" -agents 4
# ```
# The VM should restart and be fully configured.
# Creating extra VMs
# You can create an image of the current VM by following the instructions below.
# https://cloud.google.com/compute/docs/instances/windows/creating-windows-os-image
# Then create a new VM and choose "Custom images".
# Script proper.
# Get the token and tags from arguments.
param (
[Parameter(Mandatory=$true)][string]$token,
[string]$tags = "",
[Int]$agents = 1
)
# Allow HTTPS
[Net.ServicePointManager]::SecurityProtocol = "tls12, tls11, tls"
# Helper to add to PATH.
# Will take current PATH so avoid running it after anything to modifies only the powershell session path.
function Add-Path ([string]$newPathItem) {
$Env:Path+= ";" + $newPathItem + ";"
[Environment]::SetEnvironmentVariable("Path",$env:Path, [System.EnvironmentVariableTarget]::Machine)
}
# Install Git for Windows
Write-Host "Installing Git for Windows."
Invoke-WebRequest -Uri https://github.com/git-for-windows/git/releases/download/v2.19.1.windows.1/Git-2.19.1-64-bit.exe -OutFile git.exe
.\git.exe /VERYSILENT /NORESTART /NOCANCEL /SP- /CLOSEAPPLICATIONS /RESTARTAPPLICATIONS /COMPONENTS="icons,ext\reg\shellhere,assoc,assoc_sh" /DIR="C:\git"
Add-Path "C:\git\bin"
# Sleep for 15s while git is installed. Trying to remove the git.exe before it finishes install causes an error.
Start-Sleep -s 15
Remove-Item git.exe
# Download NSSM (https://nssm.cc/) to run the BuildKite agent as a service.
Write-Host "Downloading NSSM."
Invoke-WebRequest -Uri https://nssm.cc/ci/nssm-2.24-101-g897c7ad.zip -OutFile nssm.zip
Expand-Archive -Path nssm.zip -DestinationPath C:\nssm
Add-Path "C:\nssm\nssm-2.24-101-g897c7ad\win64"
Remove-Item nssm.zip
# Run the BuildKite agent install script
Write-Host "Installing BuildKite agent."
$env:buildkiteAgentToken = $token
$env:buildkiteAgentTags = $tags
Set-ExecutionPolicy Bypass -Scope Process -Force
iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/buildkite/agent/master/install.ps1'))
# Configure the BuildKite agent clone and timestamp behavior
Add-Content C:\buildkite-agent\buildkite-agent.cfg "`ngit-clone-flags=--config core.autocrlf=input --config core.eol=lf --config core.longpaths=true --config core.symlinks=true`n"
Add-Content C:\buildkite-agent\buildkite-agent.cfg "`ntimestamp-lines=true`n"
# Register the BuildKite agent service using NSSM, so that it persists through restarts and is
# restarted if the process dies.
for ($i=1; $i -le $agents; $i++)
{
$agentName = "buildkite-agent-$i"
Write-Host "Registering $agentName as a service."
nssm.exe install $agentName "C:\buildkite-agent\bin\buildkite-agent.exe" "start"
nssm.exe set $agentName AppStdout "C:\buildkite-agent\$agentName.log"
nssm.exe set $agentName AppStderr "C:\buildkite-agent\$agentName.log"
nssm.exe status $agentName
nssm.exe start $agentName
nssm.exe status $agentName
}
# Restart the machine.
Restart-Computer

View File

@ -12,8 +12,8 @@ We use this as a symmetric AES encryption key to encrypt tokens like
a GitHub token that enables publishing snapshots. a GitHub token that enables publishing snapshots.
To create the github_token file, we take this approach: To create the github_token file, we take this approach:
- Find the angular-builds:token in the internal pw database - Find the angular-builds:token in http://valentine
- Go inside the CircleCI default docker image so you use the same version of openssl as we will at runtime: `docker run --rm -it circleci/node:10.12` - Go inside the CircleCI default docker image so you use the same version of openssl as we will at runtime: `docker run --rm -it circleci/node:10.12`
- echo "https://[token]:@github.com" > credentials - echo "https://[token]:@github.com" > credentials
- openssl aes-256-cbc -e -in credentials -out .circleci/github_token -k $KEY - openssl aes-256-cbc -e -in credentials -out .circleci/github_token -k $KEY
- If needed, base64-encode the result so you can copy-paste it out of docker: `base64 github_token` - If needed, base64-encode the result so you can copy-paste it out of docker: `base64 github_token`

View File

@ -1,15 +0,0 @@
# Settings in this file should be OS agnostic. Use the bazel.<OS>.rc files for OS specific settings.
# Don't be spammy in the logs
build --noshow_progress
# Print all the options that apply to the build.
# This helps us diagnose which options override others
# (e.g. /etc/bazel.bazelrc vs. tools/bazel.rc)
build --announce_rc
# Retry in the event of flakes, eg. https://circleci.com/gh/angular/angular/31309
test --flaky_test_attempts=2
# More details on failures
build --verbose_failures=true

View File

@ -1,23 +0,0 @@
# These options are enabled when running on CI
# We do this by copying this file to /etc/bazel.bazelrc at the start of the build.
# See documentation in /docs/BAZEL.md
# Import config items common to both Linux and Windows setups.
# https://docs.bazel.build/versions/master/guide.html#bazelrc-syntax-and-semantics
try-import %workspace%/.circleci/bazel.common.rc
# Save downloaded repositories in a location that can be cached by CircleCI. This helps us
# speeding up the analysis time significantly with Bazel managed node dependencies on the CI.
build --repository_cache=/home/circleci/bazel_repository_cache
# Workaround https://github.com/bazelbuild/bazel/issues/3645
# Bazel doesn't calculate the memory ceiling correctly when running under Docker.
# Limit Bazel to consuming resources that fit in CircleCI "xlarge" class
# https://circleci.com/docs/2.0/configuration-reference/#resource_class
build --local_cpu_resources=20
build --local_ram_resources=32768
# All build executed remotely should be done using our RBE configuration.
build:remote --google_default_credentials
build --config=remote

30
.circleci/bazel.rc Normal file
View File

@ -0,0 +1,30 @@
# These options are enabled when running on CI
# We do this by copying this file to /etc/bazel.bazelrc at the start of the build.
# See documentation in /docs/BAZEL.md
# Save downloaded repositories in a location that can be cached by CircleCI. This helps us
# speeding up the analysis time significantly with Bazel managed node dependencies on the CI.
build --repository_cache=/home/circleci/bazel_repository_cache
# Don't be spammy in the logs
# TODO(gmagolan): Hide progress again once build performance improves
# Presently, CircleCI can timeout during bazel test ... with the following
# error: Too long with no output (exceeded 10m0s)
# build --noshow_progress
# Print all the options that apply to the build.
# This helps us diagnose which options override others
# (e.g. /etc/bazel.bazelrc vs. tools/bazel.rc)
build --announce_rc
# Workaround https://github.com/bazelbuild/bazel/issues/3645
# Bazel doesn't calculate the memory ceiling correctly when running under Docker.
# Limit Bazel to consuming resources that fit in CircleCI "xlarge" class
# https://circleci.com/docs/2.0/configuration-reference/#resource_class
build --local_resources=14336,8.0,1.0
# Retry in the event of flakes, eg. https://circleci.com/gh/angular/angular/31309
test --flaky_test_attempts=2
# More details on failures
build --verbose_failures=true

View File

@ -1,17 +0,0 @@
# These options are enabled when running on CI
# We do this by copying this file to $env:ProgramData\bazel.bazelrc at the start of the build.
# See documentation in /docs/BAZEL.md
# Import config items common to both Linux and Windows setups.
# https://docs.bazel.build/versions/master/guide.html#bazelrc-syntax-and-semantics
try-import %workspace%/.circleci/bazel.common.rc
# Manually set the local resources used in windows CI runs
build --local_ram_resources=120000
build --local_cpu_resources=32
# All windows jobs run on master and should use http caching
build --remote_http_cache=https://storage.googleapis.com/angular-team-cache
build --remote_accept_cached=true
build --remote_upload_local_results=true
build --google_default_credentials

File diff suppressed because it is too large Load Diff

View File

@ -36,38 +36,3 @@ function setSecretVar() {
# Restore original shell options. # Restore original shell options.
eval "$originalShellOptions"; eval "$originalShellOptions";
} }
# Create a function to set an environment variable, when called.
#
# Use this function for creating setter for public environment variables that require expensive or
# time-consuming computaions and may not be needed. When needed, you can call this function to set
# the environment variable (which will be available through `$BASH_ENV` from that point onwards).
#
# Arguments:
# - `<name>`: The name of the environment variable. The generated setter function will be
# `setPublicVar_<name>`.
# - `<code>`: The code to run to compute the value for the variable. Since this code should be
# executed lazily, it must be properly escaped. For example:
# ```sh
# # DO NOT do this:
# createPublicVarSetter MY_VAR "$(whoami)"; # `whoami` will be evaluated eagerly
#
# # DO this isntead:
# createPublicVarSetter MY_VAR "\$(whoami)"; # `whoami` will NOT be evaluated eagerly
# ```
#
# Usage: `createPublicVarSetter <name> <code>`
#
# Example:
# ```sh
# createPublicVarSetter MY_VAR 'echo "FOO"';
# echo $MY_VAR; # Not defined
#
# setPublicVar_MY_VAR;
# source $BASH_ENV;
# echo $MY_VAR; # FOO
# ```
function createPublicVarSetter() {
echo "setPublicVar_$1() { setPublicVar $1 \"$2\"; }" >> $BASH_ENV;
}

View File

@ -1,9 +1,8 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Variables # Variables
readonly projectDir=$(realpath "$(dirname ${BASH_SOURCE[0]})/..") readonly envHelpersPath="`dirname $0`/env-helpers.inc.sh";
readonly envHelpersPath="$projectDir/.circleci/env-helpers.inc.sh"; readonly getCommitRangePath="`dirname $0`/get-commit-range.js";
readonly bashEnvCachePath="$projectDir/.circleci/bash_env_cache";
# Load helpers and make them available everywhere (through `$BASH_ENV`). # Load helpers and make them available everywhere (through `$BASH_ENV`).
source $envHelpersPath; source $envHelpersPath;
@ -15,30 +14,24 @@ echo "source $envHelpersPath;" >> $BASH_ENV;
#################################################################################################### ####################################################################################################
# See https://circleci.com/docs/2.0/env-vars/#built-in-environment-variables for more info. # See https://circleci.com/docs/2.0/env-vars/#built-in-environment-variables for more info.
#################################################################################################### ####################################################################################################
setPublicVar CI "$CI" setPublicVar PROJECT_ROOT "$(pwd)";
setPublicVar PROJECT_ROOT "$projectDir";
setPublicVar CI_AIO_MIN_PWA_SCORE "95"; setPublicVar CI_AIO_MIN_PWA_SCORE "95";
# This is the branch being built; e.g. `pull/12345` for PR builds. # This is the branch being built; e.g. `pull/12345` for PR builds.
setPublicVar CI_BRANCH "$CIRCLE_BRANCH"; setPublicVar CI_BRANCH "$CIRCLE_BRANCH";
setPublicVar CI_BUILD_URL "$CIRCLE_BUILD_URL"; # ChromeDriver version compatible with the Chrome version included in the docker image used in
# `.circleci/config.yml`. See http://chromedriver.chromium.org/downloads for a list of versions.
# This variable is intended to be passed as an arg to the `webdriver-manager update` command (e.g.
# `"postinstall": "webdriver-manager update $CI_CHROMEDRIVER_VERSION_ARG"`).
setPublicVar CI_CHROMEDRIVER_VERSION_ARG "--versions.chrome 2.45";
setPublicVar CI_COMMIT "$CIRCLE_SHA1"; setPublicVar CI_COMMIT "$CIRCLE_SHA1";
# `CI_COMMIT_RANGE` is only used on push builds (a.k.a. non-PR, non-scheduled builds and rerun # `CI_COMMIT_RANGE` will only be available when `CIRCLE_COMPARE_URL` is also available (or can be
# workflows of such builds). # retrieved via `get-compare-url.js`), i.e. on push builds (a.k.a. non-PR, non-scheduled builds and
setPublicVar CI_GIT_BASE_REVISION "${CIRCLE_GIT_BASE_REVISION}"; # rerun workflows of such builds). That is fine, since we only need it in push builds.
setPublicVar CI_GIT_REVISION "${CIRCLE_GIT_REVISION}"; setPublicVar CI_COMMIT_RANGE "`[[ ${CIRCLE_PR_NUMBER:-false} != false ]] && echo "" || node $getCommitRangePath "$CIRCLE_BUILD_NUM" "$CIRCLE_COMPARE_URL"`";
setPublicVar CI_COMMIT_RANGE "$CIRCLE_GIT_BASE_REVISION..$CIRCLE_GIT_REVISION";
setPublicVar CI_PULL_REQUEST "${CIRCLE_PR_NUMBER:-false}"; setPublicVar CI_PULL_REQUEST "${CIRCLE_PR_NUMBER:-false}";
setPublicVar CI_REPO_NAME "$CIRCLE_PROJECT_REPONAME"; setPublicVar CI_REPO_NAME "$CIRCLE_PROJECT_REPONAME";
setPublicVar CI_REPO_OWNER "$CIRCLE_PROJECT_USERNAME"; setPublicVar CI_REPO_OWNER "$CIRCLE_PROJECT_USERNAME";
setPublicVar CI_PR_REPONAME "$CIRCLE_PR_REPONAME"; setPublicVar CI_YARN_VERSION "1.13.0";
setPublicVar CI_PR_USERNAME "$CIRCLE_PR_USERNAME";
####################################################################################################
# Define "lazy" PUBLIC environment variables for CircleCI.
# (I.e. functions to set an environment variable when called.)
####################################################################################################
createPublicVarSetter CI_STABLE_BRANCH "\$(npm info @angular/core dist-tags.latest | sed -r 's/^\\s*([0-9]+\\.[0-9]+)\\.[0-9]+.*$/\\1.x/')";
#################################################################################################### ####################################################################################################
@ -51,63 +44,23 @@ setSecretVar CI_SECRET_PAYLOAD_FIREBASE_TOKEN "$ANGULAR_PAYLOAD_TOKEN";
#################################################################################################### ####################################################################################################
# Define SauceLabs environment variables for CircleCI. # Define SauceLabs environment variables for CircleCI.
#################################################################################################### ####################################################################################################
setPublicVar SAUCE_USERNAME "angular-framework"; # In order to have a meaningful SauceLabs badge on the repo page,
setSecretVar SAUCE_ACCESS_KEY "0c731274ed5f-cbc9-16f4-021a-9835e39f"; # the angular2-ci account is used only when pushing commits to master;
# TODO(josephperrott): Remove environment variables once all saucelabs tests are via bazel method. # in all other cases, the regular angular-ci account is used.
setPublicVar SAUCE_LOG_FILE /tmp/angular/sauce-connect.log if [ "${CI_PULL_REQUEST}" = "false" ] && [ "${CI_REPO_OWNER}" = "angular" ] && [ "${CI_BRANCH}" = "master" ]; then
setPublicVar SAUCE_USERNAME "angular2-ci";
setSecretVar SAUCE_ACCESS_KEY "693ebc16208a-0b5b-1614-8d66-a2662f4e";
else
setPublicVar SAUCE_USERNAME "angular-ci";
setSecretVar SAUCE_ACCESS_KEY "9b988f434ff8-fbca-8aa4-4ae3-35442987";
fi
setPublicVar SAUCE_READY_FILE /tmp/angular/sauce-connect-ready-file.lock setPublicVar SAUCE_READY_FILE /tmp/angular/sauce-connect-ready-file.lock
setPublicVar SAUCE_PID_FILE /tmp/angular/sauce-connect-pid-file.lock setPublicVar SAUCE_PID_FILE /tmp/angular/sauce-connect-pid-file.lock
setPublicVar SAUCE_TUNNEL_IDENTIFIER "angular-framework-${CIRCLE_BUILD_NUM}-${CIRCLE_NODE_INDEX}" setPublicVar SAUCE_TUNNEL_IDENTIFIER "angular-${CIRCLE_BUILD_NUM}-${CIRCLE_NODE_INDEX}"
# Amount of seconds we wait for sauceconnect to establish a tunnel instance. In order to not # Amount of seconds we wait for sauceconnect to establish a tunnel instance. In order to not
# acquire CircleCI instances for too long if sauceconnect failed, we need a connect timeout. # acquire CircleCI instances for too long if sauceconnect failed, we need a connect timeout.
setPublicVar SAUCE_READY_FILE_TIMEOUT 120 setPublicVar SAUCE_READY_FILE_TIMEOUT 120
#################################################################################################### # Source `$BASH_ENV` to make the variables available immediately.
# Define environment variables for the `angular/components` repo unit tests job.
####################################################################################################
# We specifically use a directory within "/tmp" here because we want the cloned repo to be
# completely isolated from angular/angular in order to avoid any bad interactions between
# their separate build setups. **NOTE**: When updating the temporary directory, also update
# the `save_cache` path configuration in `config.yml`
setPublicVar COMPONENTS_REPO_TMP_DIR "/tmp/angular-components-repo"
setPublicVar COMPONENTS_REPO_URL "https://github.com/angular/components.git"
setPublicVar COMPONENTS_REPO_BRANCH "master"
# **NOTE**: When updating the commit SHA, also update the cache key in the CircleCI `config.yml`.
setPublicVar COMPONENTS_REPO_COMMIT "d090617912da8e70aa336aa5b4d804b1b535402e"
####################################################################################################
# Decrypt GCP Credentials and store them as the Google default credentials.
####################################################################################################
mkdir -p "$HOME/.config/gcloud";
openssl aes-256-cbc -d -in "${projectDir}/.circleci/gcp_token" \
-md md5 -k "$CIRCLE_PROJECT_REPONAME" -out "$HOME/.config/gcloud/application_default_credentials.json"
####################################################################################################
# Set bazel configuration for CircleCI runs.
####################################################################################################
cp "${projectDir}/.circleci/bazel.linux.rc" "$HOME/.bazelrc";
####################################################################################################
# Create shell script in /tmp for Bazel actions to access CI envs without
# busting the cache. Used by payload-size.sh script in integration tests.
####################################################################################################
readonly bazelVarEnv="/tmp/bazel-ci-env.sh"
echo "# Setup by /.circle/env.sh" > $bazelVarEnv
echo "export PROJECT_ROOT=\"${PROJECT_ROOT}\";" >> $bazelVarEnv
echo "export CI_BRANCH=\"${CI_BRANCH}\";" >> $bazelVarEnv
echo "export CI_BUILD_URL=\"${CI_BUILD_URL}\";" >> $bazelVarEnv
echo "export CI_COMMIT=\"${CI_COMMIT}\";" >> $bazelVarEnv
echo "export CI_COMMIT_RANGE=\"${CI_COMMIT_RANGE}\";" >> $bazelVarEnv
echo "export CI_PULL_REQUEST=\"${CI_PULL_REQUEST}\";" >> $bazelVarEnv
echo "export CI_REPO_NAME=\"${CI_REPO_NAME}\";" >> $bazelVarEnv
echo "export CI_REPO_OWNER=\"${CI_REPO_OWNER}\";" >> $bazelVarEnv
echo "export CI_SECRET_PAYLOAD_FIREBASE_TOKEN=\"${CI_SECRET_PAYLOAD_FIREBASE_TOKEN}\";" >> $bazelVarEnv
####################################################################################################
####################################################################################################
## Source `$BASH_ENV` to make the variables available immediately. ##
## ***NOTE: This must remain the last action in this script*** ##
####################################################################################################
####################################################################################################
source $BASH_ENV; source $BASH_ENV;

Binary file not shown.

View File

@ -0,0 +1,159 @@
#!/usr/bin/env node
/**
* **Usage:**
* ```
* node get-commit-range <build-number> [<compare-url> [<circle-token>]]
* ```
*
* Returns the value of the `CIRCLE_COMPARE_URL` environment variable (if defined) or, if this is
* not a PR build (i.e. `CIRCLE_PR_NUMBER` is not defined), retrieves the equivalent of
* `CIRCLE_COMPARE_URL` for jobs that are part of a rerun workflow.
*
* **Context:**
* CircleCI sets the `CIRCLE_COMPARE_URL` environment variable (from which we can extract the commit
* range) on push builds (a.k.a. non-PR, non-scheduled builds). Yet, when a workflow is rerun
* (either from the beginning or from failed jobs) - e.g. when a job flakes - CircleCI does not set
* the `CIRCLE_COMPARE_URL`.
*
* **Implementation details:**
* This script relies on the fact that all rerun workflows share the same CircleCI workspace and the
* (undocumented) fact that the workspace ID happens to be the same as the workflow ID that first
* created it.
*
* For example, for a job on push build workflow, the CircleCI API will return data that look like:
* ```js
* {
* compare: 'THE_COMPARE_URL_WE_ARE_LOOKING_FOR',
* //...
* previous: {
* // ...
* build_num: 12345,
* },
* //...
* workflows: {
* //...
* workflow_id: 'SOME_ID_A',
* workspace_id: 'SOME_ID_A', // Same as `workflow_id`.
* }
* }
* ```
*
* If the workflow is rerun, the data for jobs on the new workflow will look like:
* ```js
* {
* compare: null, // ¯\_(ツ)_/¯
* //...
* previous: {
* // ...
* build_num: 23456,
* },
* //...
* workflows: {
* //...
* workflow_id: 'SOME_ID_B',
* workspace_id: 'SOME_ID_A', // Different from current `workflow_id`.
* // Same as original `workflow_id`. \o/
* }
* }
* ```
*
* This script uses the `previous.build_num` (which points to the previous build number on the same
* branch) to traverse the jobs backwards, until it finds a job from the original workflow. Such a
* job (if found) should also contain the compare URL.
*
* **NOTE 1:**
* This is only useful on workflows which are created by rerunning a workflow for which
* `CIRCLE_COMPARE_URL` was defined.
*
* **NOTE 2:**
* The `circleToken` will be used for CircleCI API requests if provided, but it is not needed for
* accessing the read-only endpoints that we need (as long as the current project is FOSS and the
* corresponding setting is turned on in "Advanced Settings" in the project dashboard).
*
* ---
* Inspired by https://circleci.com/orbs/registry/orb/iynere/compare-url
* (source code: https://github.com/iynere/compare-url-orb).
*
* We are not using the `compare-url` orb for the following reasons:
* 1. (By looking at the code) it would only work if the rerun workflow is the latest workflow on
* the branch (which is not guaranteed to be true).
* 2. It is less efficient (e.g. makes unnecessary CircleCI API requests for builds on different
* branches, installs extra dependencies, persists files to the workspace (as a means of passing
* the result to the calling job), etc.).
* 3. It is slightly more complicated to setup and consume than our own script.
* 4. Its implementation is more complicated than needed for our usecase (e.g. handles different git
* providers, handles newly created branches, etc.).
*/
// Imports
const {get: httpsGet} = require('https');
// Constants
const API_URL_BASE = 'https://circleci.com/api/v1.1/project/github/angular/angular';
const COMPARE_URL_RE = /^.*\/([0-9a-f]+\.\.\.[0-9a-f]+)$/i;
// Run
_main(process.argv.slice(2));
// Helpers
async function _main([buildNumber, compareUrl = '', circleToken = '']) {
try {
if (!buildNumber || isNaN(buildNumber)) {
throw new Error(
'Missing or invalid arguments.\n' +
'Expected: buildNumber (number), compareUrl? (string), circleToken? (string)');
}
if (!compareUrl) {
compareUrl = await getCompareUrl(buildNumber, circleToken);
}
const commitRangeMatch = COMPARE_URL_RE.exec(compareUrl)
const commitRange = commitRangeMatch ? commitRangeMatch[1] : '';
console.log(commitRange);
} catch (err) {
console.error(err);
process.exit(1);
}
}
function getBuildInfo(buildNumber, circleToken) {
console.error(`BUILD ${buildNumber}`);
const url = `${API_URL_BASE}/${buildNumber}?circle-token=${circleToken}`;
return getJson(url);
}
async function getCompareUrl(buildNumber, circleToken) {
let info = await getBuildInfo(buildNumber, circleToken);
const targetWorkflowId = info.workflows.workspace_id;
while (info.workflows.workflow_id !== targetWorkflowId) {
info = await getBuildInfo(info.previous.build_num, circleToken);
}
return info.compare || '';
}
function getJson(url) {
return new Promise((resolve, reject) => {
const opts = {headers: {Accept: 'application/json'}};
const onResponse = res => {
const statusCode = res.statusCode || -1;
const isSuccess = (200 <= statusCode) && (statusCode <= 400);
let responseText = '';
res.
on('error', reject).
on('data', d => responseText += d).
on('end', () => isSuccess ?
resolve(JSON.parse(responseText)) :
reject(`Error getting '${url}' (status ${statusCode}):\n${responseText}`));
};
httpsGet(url, opts, onResponse).
on('error', reject).
end();
});
}

View File

@ -1,207 +0,0 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/**
* Rebases the current branch on top of the GitHub PR target branch.
*
* **Context:**
* Since a GitHub PR is not necessarily up to date with its target branch, it is useful to rebase
* prior to testing it on CI to ensure more up to date test results.
*
* **NOTE:**
* This script cannot use external dependencies or be compiled because it needs to run before the
* environment is setup.
* Use only features supported by the NodeJS versions used in the environment.
*/
// tslint:disable:no-console
const {execSync} = require('child_process');
/** A regex to select a ref that matches our semver refs. */
const semverRegex = /^(\d+)\.(\d+)\.x$/;
// Run
_main().catch(err => {
console.log('Failed to rebase on top of target branch.\n');
console.error(err);
process.exitCode = 1;
});
// Helpers
async function _main() {
const refs = await getRefsAndShasForChange();
// Log known refs and shas
console.log(`--------------------------------`);
console.log(` Target Branch: ${refs.base.ref}`);
console.log(` Latest Commit for Target Branch: ${refs.target.latestSha}`);
console.log(` Latest Commit for PR: ${refs.base.latestSha}`);
console.log(` First Common Ancestor SHA: ${refs.commonAncestorSha}`);
console.log(`--------------------------------`);
console.log();
// Get the count of commits between the latest commit from origin and the common ancestor SHA.
const commitCount =
exec(`git rev-list --count origin/${refs.base.ref}...${refs.commonAncestorSha}`);
console.log(`Checking ${commitCount} commits for changes in the CircleCI config file.`);
// Check if the files changed between the latest commit from origin and the common ancestor SHA
// includes the CircleCI config.
const circleCIConfigChanged = exec(`git diff --name-only origin/${refs.base.ref} ${
refs.commonAncestorSha} -- .circleci/config.yml`);
if (!!circleCIConfigChanged) {
throw Error(`
CircleCI config on ${refs.base.ref} has been modified since commit
${refs.commonAncestorSha.slice(0, 7)}, which this PR is based on.
Please rebase the PR on ${refs.base.ref} after fetching from upstream.
Rebase instructions for PR Author, please run the following commands:
git fetch upstream ${refs.base.ref};
git checkout ${refs.target.ref};
git rebase upstream/${refs.base.ref};
git push --force-with-lease;
`);
} else {
console.log('No change found in the CircleCI config file, continuing.');
}
console.log();
// Rebase the PR.
exec(`git rebase origin/${refs.base.ref}`);
console.log(`Rebased current branch onto ${refs.base.ref}.`);
}
/**
* Sort a list of fullpath refs into a list and then provide the first entry.
*
* The sort order will first find master ref, and then any semver ref, followed
* by the rest of the refs in the order provided.
*
* Branches are sorted in this order as work is primarily done on master, and
* otherwise on a semver branch. If neither of those were to match, the most
* likely correct branch will be the first one encountered in the list.
*/
function getRefFromBranchList(gitOutput) {
const branches = gitOutput.split('\n').map(b => b.split('/').slice(1).join('/').trim());
return branches.sort((a, b) => {
if (a === 'master') {
return -1;
}
if (b === 'master') {
return 1;
}
const aIsSemver = semverRegex.test(a);
const bIsSemver = semverRegex.test(b);
if (aIsSemver && bIsSemver) {
const [, aMajor, aMinor] = a.match(semverRegex);
const [, bMajor, bMinor] = b.match(semverRegex);
return parseInt(bMajor, 10) - parseInt(aMajor, 10) ||
parseInt(aMinor, 10) - parseInt(bMinor, 10) || 0;
}
if (aIsSemver) {
return -1;
}
if (bIsSemver) {
return 1;
}
return 0;
})[0];
}
/**
* Get the full sha of the ref provided.
*
* example: 1bc0c1a6c01ede7168f22fa9b3508ba51f1f464e
*/
function getShaFromRef(ref) {
return exec(`git rev-parse ${ref}`);
}
/**
* Get the list of branches which contain the provided sha, sorted in descending order
* by committerdate.
*
* example:
* upstream/master
* upstream/9.0.x
* upstream/test
* upstream/1.1.x
*/
function getBranchListForSha(sha, remote) {
return exec(`git branch -r '${remote}/*' --sort=-committerdate --contains ${sha}`);
}
/** Get the common ancestor sha of the two provided shas. */
function getCommonAncestorSha(sha1, sha2) {
return exec(`git merge-base ${sha1} ${sha2}`);
}
/**
* Adds the remote to git, if it doesn't already exist. Returns a boolean indicating
* whether the remote was added by the command.
*/
function addAndFetchRemote(owner, name) {
const remoteName = `${owner}_${name}`;
exec(`git remote add ${remoteName} https://github.com/${owner}/${name}.git`, true);
exec(`git fetch ${remoteName}`);
return remoteName;
}
/** Get the ref and latest shas for the provided sha on a specific remote. */
function getRefAndShas(sha, owner, name) {
const remoteName = addAndFetchRemote(owner, name);
// Get the ref on the remote for the sha provided.
const branches = getBranchListForSha(sha, remoteName);
const ref = getRefFromBranchList(branches);
// Get the latest sha on the discovered remote ref.
const latestSha = getShaFromRef(`${remoteName}/${ref}`);
return {remote: remoteName, ref, latestSha, sha};
}
/** Gets the refs and shas for the base and target of the current environment. */
function getRefsAndShasForChange() {
const base = getRefAndShas(
process.env['CI_GIT_BASE_REVISION'], process.env['CI_REPO_OWNER'],
process.env['CI_REPO_NAME']);
const target = getRefAndShas(
process.env['CI_GIT_REVISION'], process.env['CI_PR_USERNAME'], process.env['CI_PR_REPONAME']);
const commonAncestorSha = getCommonAncestorSha(base.sha, target.sha);
return {
base,
target,
commonAncestorSha,
};
}
/**
* Synchronously executes the command.
*
* Return the trimmed stdout as a string, with an added attribute of the exit code.
*/
function exec(command, ignoreError = false) {
try {
return execSync(command, {stdio: 'pipe'}).toString().trim();
} catch (err) {
if (ignoreError) {
return '';
}
throw err;
}
}

View File

@ -60,15 +60,14 @@ if (require.resolve === module) {
// Helpers // Helpers
function _main(args) { function _main(args) {
triggerWebhook(...args) triggerWebhook(...args).
.then( then(({statusCode, responseText}) => (200 <= statusCode && statusCode < 400) ?
({statusCode, responseText}) => (200 <= statusCode && statusCode < 400) ? console.log(`Status: ${statusCode}\n${responseText}`) :
console.log(`Status: ${statusCode}\n${responseText}`) : Promise.reject(new Error(`Request failed (status: ${statusCode}): ${responseText}`))).
Promise.reject(new Error(`Request failed (status: ${statusCode}): ${responseText}`))) catch(err => {
.catch(err => { console.error(err);
console.error(err); process.exit(1);
process.exit(1); });
});
} }
function postJson(url, data) { function postJson(url, data) {
@ -78,12 +77,15 @@ function postJson(url, data) {
const statusCode = res.statusCode || -1; const statusCode = res.statusCode || -1;
let responseText = ''; let responseText = '';
res.on('error', reject) res.
.on('data', d => responseText += d) on('error', reject).
.on('end', () => resolve({statusCode, responseText})); on('data', d => responseText += d).
on('end', () => resolve({statusCode, responseText}));
}; };
request(url, opts, onResponse).on('error', reject).end(JSON.stringify(data)); request(url, opts, onResponse).
on('error', reject).
end(JSON.stringify(data));
}); });
} }

View File

@ -1,57 +0,0 @@
# Install Bazel pre-reqs on Windows
# https://docs.bazel.build/versions/master/install-windows.html
# https://docs.bazel.build/versions/master/windows.html
# Install MSYS2 and packages
choco install msys2 --version 20200903.0.0 --no-progress --package-parameters "/NoUpdate"
C:\tools\msys64\usr\bin\bash.exe -l -c "pacman --needed --noconfirm -S zip unzip patch diffutils"
# Add PATH modifications to the Powershell profile. This is the win equivalent of .bash_profile.
# https://docs.microsoft.com/en-us/previous-versions//bb613488(v=vs.85)
new-item -path $profile -itemtype file -force
# Paths for nodejs, npm, yarn, and msys2. Use single quotes to prevent interpolation.
# Add before the original path to use msys2 instead of the installed gitbash.
Add-Content $profile '$Env:path = "${Env:ProgramFiles}\nodejs\;C:\Users\circleci\AppData\Roaming\npm\;${Env:ProgramFiles(x86)}\Yarn\bin\;C:\Users\circleci\AppData\Local\Yarn\bin\;C:\tools\msys64\usr\bin\;" + $Env:path'
# Environment variables for Bazel
Add-Content $profile '$Env:BAZEL_SH = "C:\tools\msys64\usr\bin\bash.exe"'
# Get the bazelisk version devdep and store it in a global var for use in the circleci job.
$bazeliskVersion = & ${Env:ProgramFiles}\nodejs\node.exe -e "console.log(require('./package.json').devDependencies['@bazel/bazelisk'])"
# This is a tricky situation: we want $bazeliskVersion to be evaluated but not $Env:BAZELISK_VERSION.
# Formatting works https://stackoverflow.com/questions/32127583/expand-variable-inside-single-quotes
$bazeliskVersionGlobalVar = '$Env:BAZELISK_VERSION = "{0}"' -f $bazeliskVersion
Add-Content $profile $bazeliskVersionGlobalVar
# Remove the CircleCI checkout SSH override, because it breaks cloning repositories through Bazel.
# See https://circleci.com/gh/angular/angular/401454 for an example.
# TODO: is this really needed? Maybe there's a better way. It doesn't happen on Linux or on Codefresh.
git config --global --unset url.ssh://git@github.com.insteadOf
####################################################################################################
# Decrypt GCP Credentials and store them as the Google default credentials.
####################################################################################################
mkdir ${env:APPDATA}\gcloud
openssl aes-256-cbc -d -in .circleci\gcp_token -md md5 -out "$env:APPDATA\gcloud\application_default_credentials.json" -k "$env:CIRCLE_PROJECT_REPONAME"
####################################################################################################
# Set bazel configuration for CircleCI runs.
####################################################################################################
copy .circleci\bazel.windows.rc ${Env:USERPROFILE}\.bazelrc
####################################################################################################
# Install specific version of node.
####################################################################################################
nvm install 14.16.1
nvm use 14.16.1
# These Bazel prereqs aren't needed because the CircleCI image already includes them.
# choco install yarn --version 1.16.0 --no-progress
# choco install vcredist2015 --version 14.0.24215.20170201
# We don't need VS Build Tools for the tested bazel targets.
# If it's needed again, uncomment these lines.
# VS Build Tools are needed for Bazel C++ targets (like com_google_protobuf)
# choco install visualstudio2019buildtools --version 16.1.2.0 --no-progress --package-parameters "--add Microsoft.VisualStudio.Workload.VCTools --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 --add Microsoft.Component.VC.Runtime.UCRTSDK --add Microsoft.VisualStudio.Component.Windows10SDK.17763"
# Add-Content $profile '$Env:BAZEL_VC = "${Env:ProgramFiles(x86)}\Microsoft Visual Studio\2019\BuildTools\VC\"'
# Python is needed for Bazel Python targets
# choco install python --version 3.5.1 --no-progress

View File

@ -1,31 +0,0 @@
# VSCode Remote Development - Developing inside a Container
This folder contains configuration files that can be used to opt into working on this repository in a [Docker container](https://www.docker.com/resources/what-container) via [VSCode](https://code.visualstudio.com/)'s Remote Development feature (see below).
Info on remote development and developing inside a container with VSCode:
- [VSCode: Remote Development](https://code.visualstudio.com/docs/remote/remote-overview)
- [VSCode: Developing inside a Container](https://code.visualstudio.com/docs/remote/containers)
- [VSCode: Remote Development FAQ](https://code.visualstudio.com/docs/remote/faq)
## Usage
_Prerequisite: [Install Docker](https://docs.docker.com/install) on your local environment._
To get started, read and follow the instuctions in [Developing inside a Container](https://code.visualstudio.com/docs/remote/containers). The [.devcontainer/](.) directory contains pre-configured `devcontainer.json` and `Dockerfile` files, which you can use to set up remote development with a docker container.
In a nutshell, you need to:
- Install the [Remote - Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) extension.
- Copy [recommended-Dockerfile](./recommended-Dockerfile) to `Dockerfile` (and optionally tweak to suit your needs).
- Copy [recommended-devcontainer.json](./recommended-devcontainer.json) to `devcontainer.json` (and optionally tweak to suit your needs).
- Open VSCode and bring up the [Command Palette](https://code.visualstudio.com/docs/getstarted/userinterface#_command-palette).
- Type `Remote-Containers: Open Folder in Container` and choose your local clone of [angular/angular](https://github.com/angular/angular).
The `.devcontainer/devcontainer.json` and `.devcontainer/Dockerfile` files are ignored by git, so you can have your own local versions. We may occasionally update the template files ([recommended-devcontainer.json](./recommended-devcontainer.json), [recommended-Dockerfile](./recommended-Dockerfile)), in which case you will need to manually update your local copies (if desired).
## Updating `recommended-devcontainer.json` and `recommended-Dockerfile`
You can update and commit the recommended config files (which people use as basis for their local configs), if you find that something is broken, out-of-date or can be improved.
Please, keep in mind that any changes you make will potentially be used by many people on different environments. Try to keep these config files cross-platform compatible and free of personal preferences.

View File

@ -1,24 +0,0 @@
# Image metadata and config.
# Ideally, the Node.js version should match what we use on CI.
# See `executors > default-executor` in `.circleci/config.yml`.
FROM circleci/node:14-browsers
LABEL name="Angular dev environment" \
description="This image can be used to create a dev environment for building Angular." \
vendor="angular" \
version="1.0"
EXPOSE 4000 4200 4433 5000 8080 9876
# Switch to `root` (CircleCI images use `circleci` as the user).
USER root
# Configure `Node.js`/`npm`.
RUN npm config --global set user root
# Go! (And keep going.)
CMD ["tail", "--follow", "/dev/null"]

View File

@ -1,16 +0,0 @@
// Reference: https://code.visualstudio.com/docs/remote/containers#_devcontainerjson-reference
{
"name": "Angular dev container",
"dockerFile": "Dockerfile",
"appPort": [4000, 4200, 4433, 5000, 8080, 9876],
"postCreateCommand": "yarn install",
"extensions": [
"devondcarew.bazel-code",
"gkalpak.aio-docs-utils",
"ms-vscode.vscode-typescript-tslint-plugin",
"xaver.clang-format",
// The following extensions are useful when working on angular.io (i.e. inside the `aio/` directory).
//"angular.ng-template",
//"dbaeumer.vscode-eslint",
],
}

View File

@ -1,23 +1,15 @@
# http://editorconfig.org # https://editorconfig.org
root = true root = true
[*] [*]
charset = utf-8 charset = utf-8
indent_style = space indent_style = space
indent_size = 2 indent_size = 2
end_of_line = lf
insert_final_newline = true insert_final_newline = true
trim_trailing_whitespace = true trim_trailing_whitespace = true
[*.md] [*.md]
max_line_length = 0 insert_final_newline = false
trim_trailing_whitespace = false trim_trailing_whitespace = false
[*.jade]
max_line_length = 0
trim_trailing_whitespace = false
# Indentation override
#[lib/**.js]
#[{package.json,.travis.yml}]
#[**/**.js]

View File

@ -1,30 +0,0 @@
module.exports = {
"globals": {
"describe": true,
"beforeEach": true,
"it": true,
"expect": true
},
"env": {
"node": true
},
"extends": "eslint:recommended",
"rules": {
"indent": [
"error",
2
],
"linebreak-style": [
"error",
"unix"
],
"quotes": [
"error",
"single"
],
"semi": [
"error",
"always"
]
}
};

View File

@ -1,8 +0,0 @@
{
"projects": {
"live": "angular-io",
"ngdocsdev": "ngdocsdev",
"kw-dev": "kw-angular-io",
"dev": "angular-io-dev"
}
}

771
.github/CODEOWNERS vendored Normal file
View File

@ -0,0 +1,771 @@
# ==================================================================================
# ==================================================================================
# Angular CODEOWNERS
# ==================================================================================
# ==================================================================================
#
# Configuration of code ownership and review approvals for the angular/angular repo.
#
# More info: https://help.github.com/articles/about-codeowners/
#
# ================================================
# General rules / philosophy
# ================================================
#
# - we trust that people do the right thing and not approve changes they don't feel confident reviewing
# - we use github teams so that we funnel code reviews to the most appropriate reviewer, this is why the team structure is fine-grained
# - we enforce that only approved PRs get merged to ensure that unreviewed code doesn't get accidentally merged
# - we delegate approval rights as much as possible so that we can scale better
# - each group must have at least one person, but several people are preferable to avoid a single point of failure issues
# - most file groups have one or two global approvers groups as fallbacks:
# - @angular/fw-global-approvers: for approving minor changes, large-scale refactorings, and emergency situations.
# - @angular/fw-global-approvers-for-docs-only-changes: for approving minor documentation-only changes that don't require engineering review
# - a small number of file groups have very limited number of reviewers because incorrect changes to the files they guard would have serious consequences (e.g. security, public api)
#
# Configuration nuances:
#
# - This configuration works in conjunction with the protected branch settings that require all changes to be made via pull requests with at least one approval.
# - This approval can come from an appropriate codeowner, or any repo collaborator (person with write access) if the PR is authored by a codeowner.
# - Each codeowners team must have write access to the repo, otherwise their reviews won't count.
#
# In the case of emergency, the repo administrators which include angular-caretaker can bypass this requirement.
# ================================================
# GitHub username registry
# (just to make this file easier to understand)
# ================================================
# alexeagle - Alex Eagle
# alxhub - Alex Rickabaugh
# AndrewKushnir - Andrew Kushnir
# andrewseguin - Andrew Seguin
# benlesh - Ben Lesh
# brandonroberts - Brandon Roberts
# filipesilva - Filipe Silva
# gkalpak - George Kalpakas
# hansl - Hans Larsen
# IgorMinar - Igor Minar
# jasonaden - Jason Aden
# jenniferfell - Jennifer Fell
# kara - Kara Erickson
# kyliau - Keen Yee Liau
# matsko - Matias Niemelä
# mgechev - Minko Gechev
# mhevery - Misko Hevery
# ocombe - Olivier Combe
# petebacondarwin - Pete Bacon Darwin
# pkozlowski-opensource - Pawel Kozlowski
# robwormald - Rob Wormald
# stephenfluin - Stephen Fluin
# vikerman - Vikram Subramanian
######################################################################################################
#
# Team structure and memberships
# ------------------------------
#
# This section is here just because the GitHub UI is too hard to navigate and audit.
#
# Any changes to team structure or memberships must first be made in this file and only then
# implemented in the GitHub UI.
#######################################################################################################
# ===========================================================
# @angular/framework-global-approvers
# ===========================================================
# Used for approving minor changes, large-scale refactorings, and emergency situations.
# (secret team to avoid review requests, it also doesn't inherit from @angular/framework because nested teams can't be secret)
#
# - IgorMinar
# - kara
# - mhevery
# ===========================================================
# @angular/framework-global-approvers-for-docs-only-changes
# ===========================================================
# Used for approving minor documentation-only changes that don't require engineering review.
# (secret team to avoid review requests, it also doesn't inherit from @angular/framework because nested teams can't be secret)
#
# - brandonroberts
# - gkalpak
# - jenniferfell
# - petebacondarwin
# ===========================================================
# @angular/fw-animations
# ===========================================================
#
# - matsko
# ===========================================================
# @angular/tools-bazel
# ===========================================================
#
# - alexeagle
# - kyliau
# - IgorMinar
# - mgechev
# ===========================================================
# @angular/tools-cli
# ===========================================================
#
# - alexeagle
# - filipesilva
# - hansl
# - mgechev
# ===========================================================
# @angular/fw-compiler
# ===========================================================
#
# - alxhub
# ===========================================================
# @angular/fw-ngcc
# ===========================================================
#
# - alxhub
# - gkalpak
# - petebacondarwin
# ===========================================================
# @angular/fw-core
# ===========================================================
#
# - alxhub
# - AndrewKushnir
# - kara
# - mhevery
# - pkozlowski-opensource
# ===========================================================
# @angular/fw-http
# ===========================================================
#
# - alxhub
# - IgorMinar
# ===========================================================
# @angular/fw-elements
# ===========================================================
#
# - andrewseguin
# - gkalpak
# - robwormald
# ===========================================================
# @angular/fw-forms
# ===========================================================
#
# - kara
# ===========================================================
# @angular/tools-language-service
# ===========================================================
#
# - kyliau
# ===========================================================
# @angular/fw-server
# ===========================================================
#
# - alxhub
# - vikerman
# ===========================================================
# @angular/fw-router
# ===========================================================
#
# - jasonaden
# ===========================================================
# @angular/fw-service-worker
# ===========================================================
#
# - alxhub
# - gkalpak
# - IgorMinar
# ===========================================================
# @angular/fw-upgrade
# ===========================================================
#
# - gkalpak
# - petebacondarwin
# ===========================================================
# @angular/fw-testing
# ===========================================================
#
# - vikerman
# ===========================================================
# @angular/fw-i18n
# ===========================================================
#
# - AndrewKushnir
# - mhevery
# - ocombe
# - vikerman
# ===========================================================
# @angular/fw-security
# ===========================================================
#
# - IgorMinar
# - mhevery
# ===========================================================
# @angular/tools-benchpress
# ===========================================================
#
# - alxhub
# ===========================================================
# @angular/fw-integration
# ===========================================================
#
# - alexeagle
# - IgorMinar
# - mhevery
# ===========================================================
# @angular/docs-infra
# ===========================================================
#
# - brandonroberts
# - gkalpak
# - IgorMinar
# - petebacondarwin
# ===========================================================
# @angular/fw-docs-intro
# ===========================================================
#
# - brandonroberts
# - IgorMinar
# - stephenfluin
# ===========================================================
# @angular/fw-docs-observables
# ===========================================================
#
# - benlesh
# - jasonaden
# ===========================================================
# @angular/fw-docs-packaging
# ===========================================================
#
# - alexeagle
# - IgorMinar
# ===========================================================
# @angular/fw-docs-marketing
# ===========================================================
#
# - IgorMinar
# - stephenfluin
# ===========================================================
# @angular/fw-public-api
# ===========================================================
#
# - IgorMinar
# ===========================================================
# @angular/fw-dev-infra
# ===========================================================
#
# - alexeagle
# - IgorMinar
######################################################################################################
#
# CODEOWNERS rules
# -----------------
#
# All the following rules are applied in the order specified in this file.
# The last rule that matches wins!
#
# See https://git-scm.com/docs/gitignore#_pattern_format for pattern syntax docs.
#
######################################################################################################
# ================================================
# Default Owners
# (in case no pattern matches a path in a PR - this should be treated as a bug and result in adding the path to CODEOWNERS)
# ================================================
* @IgorMinar @angular/framework-global-approvers
# ================================================
# @angular/animations
# ================================================
/packages/animations/** @angular/fw-animations @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/packages/platform-browser/animations/** @angular/fw-animations @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/animations.md @angular/fw-animations @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/animations/** @angular/fw-animations @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/images/guide/animations/** @angular/fw-animations @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
# ================================================
# @angular/bazel
# ================================================
/packages/bazel/** @angular/tools-bazel @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
# ================================================
# @angular/compiler
# @angular/compiler-cli
# ================================================
/packages/compiler/** @angular/fw-compiler @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/packages/compiler-cli/** @angular/fw-compiler @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/aot-compiler.md @angular/fw-compiler @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
# ================================================
# packages/compiler-cli/src/ngcc/
# ================================================
/packages/compiler-cli/src/ngcc/** @angular/fw-ngcc @angular/framework-global-approvers
# ================================================
# @angular/compiler-cli/ngtools
#
# a rule to control API changes between @angular/compiler-cli and @angular/cli
# ================================================
/packages/compiler-cli/src/ngtools/** @angular/tools-cli @angular/framework-global-approvers
# ================================================
# @angular/core
# @angular/common (except @angular/common/http)
# @angular/platform-browser
# @angular/platform-browser-dynamic
# @angular/platform-webworker
# @angular/platform-webworker-dynamic
# ================================================
/packages/core/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/packages/common/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/packages/platform-browser/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/packages/platform-browser-dynamic/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/packages/platform-webworker/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/packages/platform-webworker-dynamic/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/attribute-directives.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/attribute-directives/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/images/guide/attribute-directives/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/bootstrapping.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/bootstrapping/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/component-interaction.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/component-interaction/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/images/guide/component-interaction/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/component-styles.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/component-styles/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/dependency-injection.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/dependency-injection/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/images/guide/dependency-injection/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/dependency-injection-in-action.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/dependency-injection-in-action/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/images/guide/dependency-injection-in-action/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/dependency-injection-pattern.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/dynamic-component-loader.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/dynamic-component-loader/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/images/guide/dynamic-component-loader/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/entry-components.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/feature-modules.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/feature-modules/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/images/guide/feature-modules/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/frequent-ngmodules.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/images/guide/frequent-ngmodules/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/hierarchical-dependency-injection.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/hierarchical-dependency-injection/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/lazy-loading-ngmodules.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/lazy-loading-ngmodules/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/images/guide/lazy-loading-ngmodules/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/lifecycle-hooks.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/lifecycle-hooks/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/images/guide/lifecycle-hooks/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/ngcontainer/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/images/guide/ngcontainer/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/ngmodules.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/ngmodules/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/ngmodule/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/images/guide/ngmodule/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/ngmodule-api.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/ngmodule-faq.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/ngmodule-faq/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/ngmodule-vs-jsmodule.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/module-types.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/template-syntax.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/template-syntax/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/images/guide/template-syntax/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/pipes.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/pipes/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/images/guide/pipes/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/providers.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/providers/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/singleton-services.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/set-document-title.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/set-document-title/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/images/guide/set-document-title/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/sharing-ngmodules.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/structural-directives.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/structural-directives/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/images/guide/structural-directives/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
# ================================================
# @angular/common/http
# @angular/http
# ================================================
/packages/common/http/** @angular/fw-http @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/packages/http/** @angular/fw-http @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/http.md @angular/fw-http @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/http/** @angular/fw-http @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/images/guide/http/** @angular/fw-http @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
# ================================================
# @angular/elements
# ================================================
/packages/elements/** @angular/fw-elements @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/elements/** @angular/fw-elements @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/images/guide/elements/** @angular/fw-elements @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/elements.md @angular/fw-elements @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
# ================================================
# @angular/forms
# ================================================
/packages/forms/** @angular/fw-forms @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/forms.md @angular/fw-forms @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/forms/** @angular/fw-forms @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/images/guide/forms/** @angular/fw-forms @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/forms-overview.md @angular/fw-forms @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/forms-overview/** @angular/fw-forms @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/images/guide/forms-overview/** @angular/fw-forms @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/form-validation.md @angular/fw-forms @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/form-validation/** @angular/fw-forms @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/images/guide/form-validation/** @angular/fw-forms @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/dynamic-form.md @angular/fw-forms @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/dynamic-form/** @angular/fw-forms @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/images/guide/dynamic-form/** @angular/fw-forms @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/reactive-forms.md @angular/fw-forms @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/reactive-forms/** @angular/fw-forms @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/images/guide/reactive-forms/** @angular/fw-forms @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
# ================================================
# @angular/language-service
# ================================================
/packages/language-service/** @angular/tools-language-service @angular/framework-global-approvers
/aio/content/guide/language-service.md @angular/tools-language-service @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/images/guide/language-service/** @angular/tools-language-service @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
# ================================================
# @angular/platform-server
# ================================================
/packages/platform-server/** @angular/fw-server @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/universal.md @angular/fw-server @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/universal/** @angular/fw-server @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
# ================================================
# @angular/router
# ================================================
/packages/router/** @angular/fw-router @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/router.md @angular/fw-router @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/router/** @angular/fw-router @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/images/guide/router/** @angular/fw-router @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
# ================================================
# @angular/service-worker
# ================================================
/packages/service-worker/** @angular/fw-service-worker @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/service-worker-getting-started.md @angular/fw-service-worker @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/service-worker-getting-started/** @angular/fw-service-worker @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/service-worker-communications.md @angular/fw-service-worker @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/service-worker-config.md @angular/fw-service-worker @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/service-worker-devops.md @angular/fw-service-worker @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/service-worker-intro.md @angular/fw-service-worker @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/images/guide/service-worker/** @angular/fw-service-worker @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
# ================================================
# @angular/upgrade
# ================================================
/packages/upgrade/** @angular/fw-upgrade @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/packages/examples/upgrade/** @angular/fw-upgrade @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/upgrade.md @angular/fw-upgrade @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/upgrade-module/** @angular/fw-upgrade @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/images/guide/upgrade/** @angular/fw-upgrade @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/upgrade-phonecat-1-typescript/** @angular/fw-upgrade @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/upgrade-phonecat-2-hybrid/** @angular/fw-upgrade @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/upgrade-phonecat-3-final/** @angular/fw-upgrade @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/upgrade-performance.md @angular/fw-upgrade @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/ajs-quick-reference.md @angular/fw-upgrade @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/ajs-quick-reference/** @angular/fw-upgrade @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
# ================================================
# @angular/**/testing
# ================================================
testing/** @angular/fw-testing @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/testing.md @angular/fw-testing @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/testing/** @angular/fw-testing @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/images/guide/testing/** @angular/fw-testing @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
# ================================================
# @angular i18n
# ================================================
/packages/core/src/i18n/** @angular/fw-i18n @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/packages/core/src/render3/i18n.ts @angular/fw-i18n @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/packages/core/src/render3/i18n.md @angular/fw-i18n @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/packages/core/src/render3/interfaces/i18n.ts @angular/fw-i18n @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/packages/common/locales/** @angular/fw-i18n @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/packages/common/src/i18n/** @angular/fw-i18n @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/packages/common/src/pipes/date_pipe.ts @angular/fw-i18n @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/packages/common/src/pipes/i18n_plural_pipe.ts @angular/fw-i18n @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/packages/common/src/pipes/i18n_select_pipe.ts @angular/fw-i18n @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/packages/common/src/pipes/number_pipe.ts @angular/fw-i18n @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/packages/compiler/src/i18n/** @angular/fw-i18n @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/packages/compiler/src/render3/view/i18n/** @angular/fw-i18n @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/packages/compiler-cli/src/extract_i18n.ts @angular/fw-i18n @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/i18n.md @angular/fw-i18n @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/i18n/** @angular/fw-i18n @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
# ================================================
# @angular security
# ================================================
/packages/core/src/sanitization/** @angular/fw-security
/packages/core/test/linker/security_integration_spec.ts @angular/fw-security
/packages/compiler/src/schema/** @angular/fw-security
/packages/platform-browser/src/security/** @angular/fw-security
/aio/content/guide/security.md @angular/fw-security @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/security/** @angular/fw-security @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
# ================================================
# benchpress
# ================================================
/packages/benchpress/** @angular/tools-benchpress @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
# ================================================
# /integration/*
# ================================================
/integration/** @angular/fw-integration @angular/framework-global-approvers
# ================================================
# docs-infra
# ================================================
/aio/* @angular/docs-infra @angular/framework-global-approvers
/aio/aio-builds-setup/** @angular/docs-infra @angular/framework-global-approvers
/aio/scripts/** @angular/docs-infra @angular/framework-global-approvers
/aio/src/** @angular/docs-infra @angular/framework-global-approvers
/aio/tests/** @angular/docs-infra @angular/framework-global-approvers
/aio/tools/** @angular/docs-infra @angular/framework-global-approvers
# ================================================
# Docs: getting started & tutorial
# ================================================
/aio/content/guide/quickstart.md @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/tutorial/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
# ================================================
# Docs: observables
# ================================================
/aio/content/examples/observables/** @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/images/guide/observables/** @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/observables.md @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/comparing-observables.md @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/observables-in-angular/** @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/images/guide/observables-in-angular/** @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/observables-in-angular.md @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/practical-observable-usage/** @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/practical-observable-usage.md @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/rx-library/** @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/rx-library.md @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
# ================================================
# Docs: packaging, tooling, releasing
# ================================================
/aio/content/guide/npm-packages.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/browser-support.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/typescript-configuration.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/setup-systemjs-anatomy.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/setup.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/examples/setup/** @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/deployment.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/releases.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/guide/updating.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
# ================================================
# Docs: marketing
# ================================================
/aio/content/marketing/** @angular/fw-docs-marketing @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/images/marketing/** @angular/fw-docs-marketing @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/navigation.json @angular/fw-docs-marketing @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
/aio/content/license.md @angular/fw-docs-marketing @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
# ================================================
# Build & CI Owners
# ================================================
/* @angular/fw-dev-infra
/.buildkite/** @angular/fw-dev-infra
/.circleci/** @angular/fw-dev-infra
/.github/** @angular/fw-dev-infra
/docs/BAZEL.md @angular/fw-dev-infra
/scripts/** @angular/fw-dev-infra
/third_party/** @angular/fw-dev-infra
/tools/** @angular/fw-dev-infra
*.bzl @angular/fw-dev-infra
# ================================================
# Material CI
# ================================================
/tools/material-ci/** @angular/fw-core @angular/fw-dev-infra
# ================================================
# Public API
# ================================================
/tools/public_api_guard/** @angular/fw-public-api
# ================================================
# CODEOWNERS Owners owners ...
# ================================================
/.github/CODEOWNERS @IgorMinar @angular/framework-global-approvers

View File

@ -1,6 +1,10 @@
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑
Please help us process issues more efficiently by filing an Please help us process issues more efficiently by filing an
issue using one of the following templates: issue using one of the following templates:
https://github.com/angular/angular/issues/new/choose https://github.com/angular/angular/issues/new/choose
Thank you! Thank you!
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑

67
.github/ISSUE_TEMPLATE/1-bug-report.md vendored Normal file
View File

@ -0,0 +1,67 @@
---
name: "\U0001F41EBug report"
about: Report a bug in the Angular Framework
---
<!--🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅
Oh hi there! 😄
To expedite issue processing please search open and closed issues before submitting a new one.
Existing issues often contain information about workarounds, resolution, or progress updates.
🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅-->
# 🐞 bug report
### Affected Package
<!-- Can you pin-point one or more @angular/* packages as the source of the bug? -->
<!-- ✍edit: --> The issue is caused by package @angular/....
### Is this a regression?
<!-- Did this behavior use to work in the previous version? -->
<!-- ✍️--> Yes, the previous version in which this bug was not present was: ....
### Description
<!-- ✍️--> A clear and concise description of the problem...
## 🔬 Minimal Reproduction
<!--
Please create and share minimal reproduction of the issue starting with this template: https://stackblitz.com/fork/angular-issue-repro2
-->
<!-- ✍️--> https://stackblitz.com/...
<!--
If StackBlitz is not suitable for reproduction of your issue, please create a minimal GitHub repository with the reproduction of the issue. Share the link to the repo below along with step-by-step instructions to reproduce the problem, as well as expected and actual behavior.
Issues that don't have enough info and can't be reproduced will be closed.
You can read more about issue submission guidelines here: https://github.com/angular/angular/blob/master/CONTRIBUTING.md#-submitting-an-issue
-->
## 🔥 Exception or Error
<pre><code>
<!-- If the issue is accompanied by an exception or an error, please share it below: -->
<!-- ✍️-->
</code></pre>
## 🌍 Your Environment
**Angular Version:**
<pre><code>
<!-- run `ng version` and paste output below -->
<!-- ✍️-->
</code></pre>
**Anything else relevant?**
<!--Is this a browser specific issue? If so, please specify the browser and version. -->
<!--Do any of these matter: operating system, IDE, package manager, HTTP server, ...? If so, please mention it below. -->

View File

@ -1,84 +0,0 @@
name: Bug Report
description: Report a bug in the Angular Framework
body:
- type: dropdown
id: affected-packages
attributes:
label: Which @angular/* package(s) are the source of the bug?
options:
- animations
- bazel
- common
- compiler-cli
- compiler
- elements
- forms
- language-service
- localize
- platform-browser-dynamic
- platform-browser
- platform-server
- router
- service-worker
- upgrade
- Don't known / other
multiple: true
validations:
required: true
- type: dropdown
id: is-regression
attributes:
label: Is this a regression?
options:
- 'Yes'
- 'No'
validations:
required: true
- type: textarea
id: description
attributes:
label: Description
validations:
required: true
- type: input
id: reproduction
attributes:
label: Please provide a link to a minimal reproduction of the bug
- type: textarea
id: exception-or-error
attributes:
label: Please provide the exception or error you saw
render: true
- type: textarea
id: environment
attributes:
label: Please provide the environment you discovered this bug in
render: true
placeholder: |
Angular CLI: 12.0.5
Node: 14.17.0
Package Manager: yarn 1.22.10
OS: linux x64
Angular: 12.0.5
... animations, cli, common, compiler, compiler-cli, core, forms
... platform-browser, platform-browser-dynamic, router
Package Version
---------------------------------------------------------
@angular-devkit/architect 0.1200.5
@angular-devkit/build-angular 12.0.5
@angular-devkit/core 12.0.5
rxjs 6.6.7
typescript 4.2.4
- type: textarea
id: other
attributes:
label: Anything else?

View File

@ -0,0 +1,32 @@
---
name: "\U0001F680Feature request"
about: Suggest a feature for Angular Framework
---
<!--🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅
Oh hi there! 😄
To expedite issue processing please search open and closed issues before submitting a new one.
Existing issues often contain information about workarounds, resolution, or progress updates.
🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅-->
# 🚀 feature request
### Relevant Package
<!-- Can you pin-point one or more @angular/* packages the are relevant for this feature request? -->
<!-- ✍edit: --> This feature request is for @angular/....
### Description
<!-- ✍️--> A clear and concise description of the problem or missing capability...
### Describe the solution you'd like
<!-- ✍️--> If you have a solution in mind, please describe it.
### Describe alternatives you've considered
<!-- ✍️--> Have you considered any alternative solutions or workarounds?

View File

@ -1,46 +0,0 @@
name: 'Feature Request'
description: Suggest a feature for Angular Framework
body:
- type: dropdown
id: affected-packages
attributes:
label: Which @angular/* package(s) are relevant/releated to the feature request?
options:
- animations
- bazel
- common
- compiler-cli
- compiler
- elements
- forms
- language-service
- localize
- platform-browser-dynamic
- platform-browser
- platform-server
- router
- service-worker
- upgrade
multiple: true
- type: textarea
id: description
attributes:
label: Description
validations:
required: true
- type: textarea
id: proposed-solution
attributes:
label: Proposed solution
validations:
required: true
- type: textarea
id: alternatives-considered
attributes:
label: Alternatives considered
validations:
required: true

55
.github/ISSUE_TEMPLATE/3-docs-bug.md vendored Normal file
View File

@ -0,0 +1,55 @@
---
name: "📚 Docs or angular.io issue report"
about: Report an issue in Angular's documentation or angular.io application
---
<!--🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅
Oh hi there! 😄
To expedite issue processing please search open and closed issues before submitting a new one.
Existing issues often contain information about workarounds, resolution, or progress updates.
🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅🔅-->
# 📚 Docs or angular.io bug report
### Description
<!--edit:--> A clear and concise description of the problem...
## 🔬 Minimal Reproduction
### What's the affected URL?**
<!--edit:--> https://angular.io/...
### Reproduction Steps**
<!-- If applicable please list the steps to take to reproduce the issue -->
<!--edit:-->
### Expected vs Actual Behavior**
<!-- If applicable please describe the difference between the expected and actual behavior after following the repro steps. -->
<!--edit:-->
## 📷Screenshot
<!-- Often a screenshot can help to capture the issue better than a long description. -->
<!--upload a screenshot:-->
## 🔥 Exception or Error
<pre><code>
<!-- If the issue is accompanied by an exception or an error, please share it below: -->
<!-- ✍️-->
</code></pre>
## 🌍 Your Environment
### Browser info
<!--Is this a browser specific issue? If so, please specify the device, browser, and version. -->
### Anything else relevant?
<!--Please provide additional info if necessary. -->

View File

@ -1,42 +0,0 @@
name: 'Docs or angular.io Bug Report'
description: Report an issue in Angular's documentation or angular.io application
body:
- type: textarea
id: description
attributes:
label: Description
validations:
required: true
- type: input
id: affected-url
attributes:
label: What is the affected URL?
- type: textarea
id: reproduction-steps
attributes:
label: Please provide the steps to reproduce the issue
- type: textarea
id: expected-vs-actual-behavior
attributes:
label: Please provide the expected behavior vs the actual behavior you encountered
- type: textarea
id: screenshot
attributes:
label: Please provide a screenshot if possible
- type: textarea
id: exception-or-error
attributes:
label: Please provide the exception or error you saw
render: true
- type: textarea
id: browser-info
attributes:
label: Is this a browser-specific issue? If so, please specify the device, browser, and version.
render: true

View File

@ -1,6 +1,11 @@
--- ---
name: Security Issue Disclosure name: ⚠️ Security issue disclosure
about: Report a security issue in Angular Framework, Material, or CLI about: Report a security issue in Angular Framework, Material, or CLI
--- ---
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑
Please read https://angular.io/guide/security#report-issues on how to disclose security related issues. Please read https://angular.io/guide/security#report-issues on how to disclose security related issues.
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑

View File

@ -1,11 +1,16 @@
--- ---
name: "Support Request" name: "❓Support request"
about: Questions and requests for support about: Questions and requests for support
--- ---
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑
Please do not file questions or support requests on the GitHub issues tracker. Please do not file questions or support requests on the GitHub issues tracker.
You can get your questions answered using other communication channels. Please see: You can get your questions answered using other communication channels. Please see:
https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question
Thank you! Thank you!
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑

View File

@ -1,8 +1,13 @@
--- ---
name: "Angular CLI" name: "\U0001F6E0Angular CLI"
about: Issues and feature requests for Angular CLI about: Issues and feature requests for Angular CLI
--- ---
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑
Please file any Angular CLI issues at: https://github.com/angular/angular-cli/issues/new Please file any Angular CLI issues at: https://github.com/angular/angular-cli/issues/new
For the time being, we keep Angular CLI issues in a separate repository. For the time being, we keep Angular CLI issues in a separate repository.
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑

View File

@ -1,8 +0,0 @@
---
name: "Angular Components"
about: Issues and feature requests for Angular Components
---
Please file any Angular Components issues at: https://github.com/angular/components/issues/new
For the time being, we keep Angular Components issues in a separate repository.

View File

@ -0,0 +1,13 @@
---
name: "\U0001F48EAngular Material"
about: Issues and feature requests for Angular Material
---
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑
Please file any Angular Material issues at: https://github.com/angular/material2/issues/new
For the time being, we keep Angular Material issues in a separate repository.
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑

View File

@ -4,7 +4,7 @@
size: size:
disabled: false disabled: false
maxSizeIncrease: 2000 maxSizeIncrease: 2000
circleCiStatusName: 'ci/circleci: test_ivy_aot' circleCiStatusName: "ci/circleci: test_ivy_aot"
# options for the merge plugin # options for the merge plugin
merge: merge:
@ -13,84 +13,71 @@ merge:
# set to true to disable # set to true to disable
disabled: false disabled: false
# the name of the status # the name of the status
context: 'ci/angular: merge status' context: "ci/angular: merge status"
# text to show when all checks pass # text to show when all checks pass
successText: 'All checks passed!' successText: "All checks passed!"
# text to show when some checks are failing # text to show when some checks are failing
failureText: 'The following checks are failing:' failureText: "The following checks are failing:"
# the g3 status will be added to your pull requests if they include files that match the patterns # the g3 status will be added to your pull requests if they include files that match the patterns
g3Status: g3Status:
# set to true to disable # set to true to disable
disabled: false disabled: false
# the name of the status # the name of the status
context: 'google3' context: "google3"
# text to show when the status is pending, {{PRNumber}} will be replaced by the PR number # text to show when the status is pending, {{PRNumber}} will be replaced by the PR number
pendingDesc: 'Googler: run g3sync presubmit {{PRNumber}}' pendingDesc: "Googler: run g3sync presubmit {{PRNumber}}"
# text to show when the status is success # text to show when the status is success
successDesc: 'Does not affect google3' successDesc: "Does not affect google3"
# link to use for the details # link to use for the details
url: 'http://go/angular/g3sync' url: "http://go/angular-g3sync"
# list of patterns to check for the files changed by the PR # list of patterns to check for the files changed by the PR
# this list must be manually kept in sync with google3/third_party/javascript/angular2/copy.bara.sky # this list must be manually kept in sync with google3/third_party/javascript/angular2/copy.bara.sky
include: include:
- 'LICENSE' - "LICENSE"
- 'modules/benchmarks/**' - "modules/**"
- 'modules/system.d.ts' - "packages/**"
- 'packages/**'
- 'dev-infra/benchmark/driver-utilities/**'
# list of patterns to ignore for the files changed by the PR # list of patterns to ignore for the files changed by the PR
exclude: exclude:
- 'packages/*' - "packages/*"
- 'packages/bazel/*' - "packages/bazel/*"
- 'packages/bazel/src/api-extractor/**' - "packages/bazel/src/api-extractor/**"
- 'packages/bazel/src/builders/**' - "packages/bazel/src/builders/**"
- 'packages/bazel/src/ng_package/**' - "packages/bazel/src/ng_package/**"
- 'packages/bazel/src/protractor/**' - "packages/bazel/src/protractor/**"
- 'packages/bazel/src/schematics/**' - "packages/bazel/src/schematics/**"
- 'packages/compiler-cli/src/ngcc/**' - "packages/compiler-cli/src/ngcc/**"
- 'packages/compiler-cli/linker/**' - "packages/docs/**"
- 'packages/compiler-cli/ngcc/**' - "packages/elements/schematics/**"
- 'packages/compiler-cli/src/ngtsc/sourcemaps/**' - "packages/examples/**"
- 'packages/docs/**' - "packages/language-service/**"
- 'packages/elements/schematics/**' - "packages/private/**"
- 'packages/examples/**' - "packages/service-worker/**"
- 'packages/language-service/**' - "**/.gitignore"
- 'packages/localize/**' - "**/.gitkeep"
- 'packages/private/**' - "**/yarn.lock"
- 'packages/service-worker/**' - "**/package.json"
- 'packages/common/locales/**' - "**/tsconfig-build.json"
- 'packages/http/**' - "**/tsconfig.json"
- '**/.gitignore' - "**/BUILD.bazel"
- '**/.gitkeep' - "**/*.md"
- '**/yarn.lock' - "packages/**/integrationtest/**"
- '**/package.json' - "packages/**/test/**"
- '**/third_party/**'
- '**/tsconfig-build.json'
- '**/tsconfig.json'
- '**/rollup.config.js'
- '**/BUILD.bazel'
- '**/*.md'
- 'packages/**/integrationtest/**'
- 'packages/**/test/**'
- 'packages/zone.js/*'
- 'packages/zone.js/dist/**'
- 'packages/zone.js/doc/**'
- 'packages/zone.js/example/**'
- 'packages/zone.js/scripts/**'
# comment that will be added to a PR when there is a conflict, leave empty or set to false to disable # comment that will be added to a PR when there is a conflict, leave empty or set to false to disable
mergeConflictComment: "Hi @{{PRAuthor}}! This PR has merge conflicts due to recent upstream merges.\nPlease help to unblock it by resolving these conflicts. Thanks!" mergeConflictComment: "Hi @{{PRAuthor}}! This PR has merge conflicts due to recent upstream merges.
\nPlease help to unblock it by resolving these conflicts. Thanks!"
# label to monitor # label to monitor
mergeLabel: 'action: merge' mergeLabel: "PR action: merge"
# adding any of these labels will also add the merge label # adding any of these labels will also add the merge label
mergeLinkedLabels: mergeLinkedLabels:
- 'action: merge-assistance' - "PR action: merge-assistance"
# list of checks that will determine if the merge label can be added # list of checks that will determine if the merge label can be added
checks: checks:
# require that the PR has reviews from all requested reviewers # require that the PR has reviews from all requested reviewers
# #
# This enables us to request reviews from both eng and tech writers, or multiple eng folks, and prevents accidental merges. # This enables us to request reviews from both eng and tech writers, or multiple eng folks, and prevents accidental merges.
@ -99,32 +86,38 @@ merge:
# whether the PR shouldn't have a conflict with the base branch # whether the PR shouldn't have a conflict with the base branch
noConflict: true noConflict: true
# list of labels that a PR needs to have, checked with a regexp (e.g. "target:" will work for the label "target: master") # list of labels that a PR needs to have, checked with a regexp (e.g. "PR target:" will work for the label "PR target: master")
requiredLabels: requiredLabels:
- 'target: *' - "PR target: *"
- 'cla: yes' - "cla: yes"
# list of labels that a PR shouldn't have, checked after the required labels with a regexp # list of labels that a PR shouldn't have, checked after the required labels with a regexp
forbiddenLabels: forbiddenLabels:
- 'target: TBD' - "PR target: TBD"
- 'action: cleanup' - "PR action: cleanup"
- 'action: review' - "PR action: review"
- 'state: blocked' - "PR state: blocked"
- 'cla: no' - "cla: no"
# list of PR statuses that need to be successful # list of PR statuses that need to be successful
requiredStatuses: requiredStatuses:
- 'ci/circleci: build' - "ci/circleci: build"
- 'ci/circleci: lint' - "ci/circleci: lint"
- 'ci/angular: size' - "ci/circleci: publish_snapshot"
- 'cla/google' - "ci/angular: size"
- 'google3' - "cla/google"
- 'pullapprove' - "google3"
# the comment that will be added when the merge label is added despite failing checks, leave empty or set to false to disable # the comment that will be added when the merge label is added despite failing checks, leave empty or set to false to disable
# {{MERGE_LABEL}} will be replaced by the value of the mergeLabel option # {{MERGE_LABEL}} will be replaced by the value of the mergeLabel option
# {{PLACEHOLDER}} will be replaced by the list of failing checks # {{PLACEHOLDER}} will be replaced by the list of failing checks
mergeRemovedComment: "I see that you just added the `{{MERGE_LABEL}}` label, but the following checks are still failing:\n{{PLACEHOLDER}}\n\n**If you want your PR to be merged, it has to pass all the CI checks.**\n\nIf you can't get the PR to a green state due to flakes or broken master, please try rebasing to master and/or restarting the CI job. If that fails and you believe that the issue is not due to your change, please contact the caretaker and ask for help." mergeRemovedComment: "I see that you just added the `{{MERGE_LABEL}}` label, but the following checks are still failing:
\n{{PLACEHOLDER}}
\n
\n**If you want your PR to be merged, it has to pass all the CI checks.**
\n
\nIf you can't get the PR to a green state due to flakes or broken master, please try rebasing to master and/or restarting the CI job. If that fails and you believe that the issue is not due to your change, please contact the caretaker and ask for help."
# options for the triage plugin # options for the triage plugin
triage: triage:
@ -134,29 +127,24 @@ triage:
defaultMilestone: 82, defaultMilestone: 82,
# arrays of labels that determine if an issue has been triaged by the caretaker # arrays of labels that determine if an issue has been triaged by the caretaker
l1TriageLabels: l1TriageLabels:
- - 'comp: *' -
- "comp: *"
# arrays of labels that determine if an issue has been fully triaged # arrays of labels that determine if an issue has been fully triaged
l2TriageLabels: l2TriageLabels:
- - 'P0' -
- 'comp: *' - "type: bug/fix"
- - 'P1' - "severity*"
- 'comp: *' - "freq*"
- - 'P2' - "comp: *"
- 'comp: *' -
- - 'P3' - "type: feature"
- 'comp: *' - "comp: *"
- - 'P4' -
- 'comp: *' - "type: refactor"
- - 'P5' - "comp: *"
- 'comp: *' -
- - 'feature' - "type: RFC / Discussion / question"
- 'comp: *' - "comp: *"
- - 'discussion'
- 'comp: *'
- - 'needs clarification'
- 'comp: *'
- - 'needs reproduction'
- 'comp: *'
# options for the triage PR plugin # options for the triage PR plugin
triagePR: triagePR:
@ -168,14 +156,12 @@ triagePR:
defaultMilestone: 82, defaultMilestone: 82,
# arrays of labels that determine if a PR has been triaged by the caretaker # arrays of labels that determine if a PR has been triaged by the caretaker
l1TriageLabels: l1TriageLabels:
- - 'comp: *' -
- "comp: *"
# arrays of labels that determine if a PR has been fully triaged # arrays of labels that determine if a PR has been fully triaged
l2TriageLabels: l2TriageLabels:
- - 'comp: *' -
- "type: *"
# options for rerunning CI - "effort*"
rerunCircleCI: - "risk*"
# set to true to disable - "comp: *"
disabled: false
# the label which when added triggers a rerun of the default CircleCI workflow
triggerRerunLabel: 'action: rerun CI at HEAD'

View File

@ -1,15 +0,0 @@
name: Feature request triage bot
on:
schedule:
# Run at 14:00 every day
- cron: '0 14 * * *'
jobs:
feature_triage:
if: github.repository == 'angular/angular'
runs-on: ubuntu-latest
steps:
- uses: angular/dev-infra/github-actions/feature-request@f83903fe1ac848407ef81465f66588e5accb6537
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}

View File

@ -1,15 +0,0 @@
name: Lock closed inactive issues
on:
schedule:
# Run at 16:00 every day
- cron: '0 16 * * *'
jobs:
lock_closed:
if: github.repository == 'angular/angular'
runs-on: ubuntu-latest
steps:
- uses: angular/dev-infra/github-actions/lock-closed@7679cff885633cd33bf5ac7922a5304e8971a5a6
with:
lock-bot-key: ${{ secrets.LOCK_BOT_PRIVATE_KEY }}

23
.gitignore vendored
View File

@ -3,27 +3,19 @@
/dist/ /dist/
/bazel-out /bazel-out
/integration/bazel/bazel-* /integration/bazel/bazel-*
*.log e2e_test.*
node_modules node_modules
tools/gulp-tasks/cldr/cldr-data/
# Include when developing application packages. # Include when developing application packages.
pubspec.lock pubspec.lock
.c9 .c9
.idea/ .idea/
.devcontainer/*
!.devcontainer/README.md
!.devcontainer/recommended-devcontainer.json
!.devcontainer/recommended-Dockerfile
.settings/ .settings/
.vscode/launch.json .vscode/launch.json
.vscode/settings.json
.vscode/tasks.json
*.swo *.swo
*.swp
modules/.settings modules/.settings
modules/.vscode modules/.vscode
.vimrc
.nvimrc
# Don't check in secret files # Don't check in secret files
*secret.js *secret.js
@ -41,14 +33,3 @@ yarn-error.log
# User specific bazel settings # User specific bazel settings
.bazelrc.user .bazelrc.user
# User specific ng-dev settings
.ng-dev.user*
.notes.md
baseline.json
# Ignore .history for the xyz.local-history VSCode extension
.history
# Husky
.husky/_

View File

@ -1,145 +0,0 @@
<type>(<scope>): <summary>
<Describe the motivation behind this change - explain WHY you are making this change. Wrap all lines
at 100 characters.>
Fixes #<issue number>
# ────────────────────────────────────────── 100 chars ────────────────────────────────────────────┤
# Example Commit Messages
# =======================
# ─── Example: Simple refactor ────────────────────────────────────────────────────────────────────┤
# refactor(core): rename refreshDynamicEmbeddedViews to refreshEmbeddedViews
#
# Improve code readability. The original name no longer matches how the function is used.
# ─────────────────────────────────────────────────────────────────────────────────────────────────┤
# ─── Example: Simple docs change ─────────────────────────────────────────────────────────────────┤
# docs: clarify the service limitation in providers.md guide
#
# Fixes #36332
# ─────────────────────────────────────────────────────────────────────────────────────────────────┤
# ─── Example: A bug fix ──────────────────────────────────────────────────────────────────────────┤
# fix(ngcc): ensure lockfile is removed when `analyzeFn` fails
#
# Previously an error thrown in the `analyzeFn` would cause the ngcc process to exit immediately
# without removing the lockfile, and potentially before the unlocker process had been successfully
# spawned resulting in the lockfile being orphaned and left behind.
#
# Now we catch these errors and remove the lockfile as needed.
# ─────────────────────────────────────────────────────────────────────────────────────────────────┤
# ─── Example: Breaking change ────────────────────────────────────────────────────────────────────┤
# feat(bazel): simplify ng_package by dropping esm5 and fesm5
#
# esm5 and fesm5 distributions are no longer needed and have been deprecated in the past.
#
# https://v9.angular.io/guide/deprecations#esm5-and-fesm5-code-formats-in-angular-npm-packages
#
# This commit modifies ng_package to no longer distribute these two formats in npm packages built by
# ng_package (e.g. @angular/core).
#
# This commit intentionally doesn't fully clean up the ng_package rule to remove all traces of esm5
# and fems5 build artifacts as that is a bigger cleanup and currently we are narrowing down the
# scope of this change to the MVP needed for v10, which in this case is 'do not put esm5 and fesm5'
# into the npm packages.
#
# More cleanup to follow: https://angular-team.atlassian.net/browse/FW-2143
#
# BREAKING CHANGE: esm5 and fesm5 format is no longer distributed in Angular's npm packages e.g.
# @angular/core
#
# Angular CLI will automatically downlevel the code to es5 if differential loading is enabled in the
# Angular project, so no action is required from Angular CLI users.
#
# If you are not using Angular CLI to build your application or library, and you need to be able to
# build es5 artifacts, then you will need to downlevel the distributed Angular code to es5 on your
# own.
#
#
# Fixes #1234
# ─────────────────────────────────────────────────────────────────────────────────────────────────┤
# Angular Commit Message Format
# =============================
#
# The full specification of the Angular Commit Message Format can be found at
# https://github.com/angular/angular/blob/master/CONTRIBUTING.md#commit
#
# The following is an excerpt of the specification with the most commonly needed info.
#
# Each commit message consists of a *header*, a *body*, and a *footer*.
#
# <header>
# <BLANK LINE>
# <body>
# <BLANK LINE>
# <footer>
#
# The header is mandatory.
#
# The body is mandatory for all commits except for those of scope "docs". When the body is required
# it must be at least 20 characters long.
#
# The footer is optional.
#
# Any line of the commit message cannot be longer than 100 characters.
#
#
# Commit Message Header
# ---------------------
#
# <type>(<scope>): <short summary>
# │ │ │
# │ │ └─⫸ Summary in present tense. Not capitalized. No period at the end.
# │ │
# │ └─⫸ Commit Scope: animations|bazel|benchpress|common|compiler|compiler-cli|core|
# │ elements|forms|http|language-service|localize|platform-browser|
# │ platform-browser-dynamic|platform-server|router|service-worker|
# │ upgrade|zone.js|packaging|changelog|dev-infra|docs-infra|migrations|
# │ ngcc|ve
# │ https://github.com/angular/angular/blob/master/CONTRIBUTING.md#scope
# │
# └─⫸ Commit Type: build|ci|docs|feat|fix|perf|refactor|style|test
# https://github.com/angular/angular/blob/master/CONTRIBUTING.md#type
#
#
# Commit Message Body
# ---------------------
#
# Just as in the summary, use the imperative, present tense: "fix" not "fixed" nor "fixes".
#
# Explain the motivation for the change in the commit message body. This commit message should
# explain WHY you are making the change. You can include a comparison of the previous behavior with
# the new behavior in order to illustrate the impact of the change.
#
#
# Commit Message Footer
# ---------------------
#
# The footer can contain information about breaking changes and is also the place to reference
# GitHub issues, Jira tickets, and other PRs that this commit closes or is related to.
#
# ```
# BREAKING CHANGE: <breaking change summary>
# <BLANK LINE>
# <breaking change description + migration instructions>
# <BLANK LINE>
# <BLANK LINE>
# Fixes #<issue number>
# ```
#
# Breaking Change section should start with the phrase "BREAKING CHANGE: " followed by a summary of
# the breaking change, a blank line, and a detailed description of the breaking change that also
# includes migration instructions.
#

View File

@ -1,30 +0,0 @@
#!/bin/sh
if [ -z "$husky_skip_init" ]; then
debug () {
[ "$HUSKY_DEBUG" = "1" ] && echo "husky (debug) - $1"
}
readonly hook_name="$(basename "$0")"
debug "starting $hook_name..."
if [ "$HUSKY" = "0" ]; then
debug "HUSKY env variable is set to 0, skipping hook"
exit 0
fi
if [ -f ~/.huskyrc ]; then
debug "sourcing ~/.huskyrc"
. ~/.huskyrc
fi
export readonly husky_skip_init=1
sh -e "$0" "$@"
exitCode="$?"
if [ $exitCode != 0 ]; then
echo "husky - $hook_name hook exited with code $exitCode (error)"
exit $exitCode
fi
exit 0
fi

View File

@ -1,11 +0,0 @@
#!/bin/sh
. "$(dirname $0)/_/husky.sh"
set +e
yarn -s ng-dev commit-message pre-commit-validate --file $1 2>/dev/null
if [ $? -ne 0 ]; then
echo "WARNING: failed to run commit message validation (ng-dev commit-mesage pre-commit-validate)"
fi
exit 0;

View File

@ -1,11 +0,0 @@
#!/bin/sh
. "$(dirname $0)/_/husky.sh"
set +e
yarn -s ng-dev format staged 2>/dev/null
if [ $? -ne 0 ]; then
echo "WARNING: failed to run file formatting (ng-dev format staged)"
fi
exit 0;

View File

@ -1,11 +0,0 @@
#!/bin/sh
. "$(dirname $0)/_/husky.sh"
set +e
yarn -s ng-dev commit-message restore-commit-message-draft $1 $2 2>/dev/null
if [ $? -ne 0 ]; then
echo "WARNING: failed to attempt to restore commit message draft (ng-dev commit-message restore-commit-message-draft)"
fi
exit 0;

View File

@ -1,8 +0,0 @@
####################################################################################################
Command: ng-dev commit-message restore-commit-message-draft
Ran at: Mon Apr 05 2021 09:25:46 GMT-0400 (Eastern Daylight Time)
LOG: Skipping commit message restoration attempt
DEBUG: A commit message was already provided via the command with a -m or -F flag
####################################################################################################
Command ran in 4ms
Exit Code: 0

View File

@ -1,20 +0,0 @@
import {CaretakerConfig} from '../dev-infra/caretaker/config';
/** The configuration for `ng-dev caretaker` commands. */
export const caretaker: CaretakerConfig = {
githubQueries: [
{
name: 'Merge Queue',
query: `is:pr is:open status:success label:"action: merge"`,
},
{
name: 'Merge Assistance Queue',
query: `is:pr is:open label:"action: merge-assistance"`,
},
{
name: 'Initial Triage Queue',
query: `is:open no:milestone`,
}
],
caretakerGroup: 'angular-caretaker',
};

View File

@ -1,40 +0,0 @@
import {CommitMessageConfig} from '../dev-infra/commit-message/config';
/**
* The configuration for `ng-dev commit-message` commands.
*/
export const commitMessage: CommitMessageConfig = {
maxLineLength: Infinity,
minBodyLength: 20,
minBodyLengthTypeExcludes: ['docs'],
scopes: [
'animations',
'bazel',
'benchpress',
'changelog',
'common',
'compiler',
'compiler-cli',
'core',
'dev-infra',
'docs-infra',
'elements',
'forms',
'http',
'language-service',
'localize',
'migrations',
'ngcc',
'packaging',
'platform-browser',
'platform-browser-dynamic',
'platform-server',
'platform-webworker',
'platform-webworker-dynamic',
'router',
'service-worker',
'upgrade',
've',
'zone.js',
]
};

View File

@ -1,15 +0,0 @@
import {caretaker} from './caretaker';
import {commitMessage} from './commit-message';
import {format} from './format';
import {github} from './github';
import {merge} from './merge';
import {release} from './release';
module.exports = {
commitMessage,
format,
github,
merge,
caretaker,
release,
};

View File

@ -1,37 +0,0 @@
import {FormatConfig} from '../dev-infra/format/config';
/**
* Configuration for the `ng-dev format` command.
*/
export const format: FormatConfig = {
'prettier': {
'matchers': [
'**/*.{yaml,yml}',
]
},
'clang-format': {
'matchers': [
'**/*.{js,ts}',
// TODO: burn down format failures and remove aio and integration exceptions.
'!aio/**',
'!integration/**',
// Both third_party and .yarn are directories containing copied code which should
// not be modified.
'!third_party/**',
'!.yarn/**',
// Do not format d.ts files as they are generated
'!**/*.d.ts',
// Do not format generated ng-dev script
'!dev-infra/ng-dev.js',
'!dev-infra/build-worker.js',
// Do not format compliance test-cases since they must match generated code
'!packages/compiler-cli/test/compliance/test_cases/**/*.js',
// Do not format the locale files which are checked-in for Google3, but generated using
// the `generate-locales-tool` from `packages/common/locales`.
'!packages/core/src/i18n/locale_en.ts',
'!packages/common/locales/closure-locale.ts',
'!packages/common/src/i18n/currencies.ts',
]
},
'buildifier': true
};

View File

@ -1,15 +0,0 @@
# The file is inert unless it's explicitly included into the local git config via:
#
# ```
# git config --add include.path '../.ng-dev/gitconfig'
# ```
#
# Calling that command will append the following into `.git/config` of the current git workspace
# (i.e. $GIT_DIR, typically `angular/.git/config`):
#
# ```
# [include]
# path = ../.ng-dev/gitconfig
# ```
[commit]
template = .gitmessage

View File

@ -1,11 +0,0 @@
import {GithubConfig} from '../dev-infra/utils/config';
/**
* Github configuration for the `ng-dev` command. This repository is used as
* remote for the merge script and other utilities like `ng-dev pr rebase`.
*/
export const github: GithubConfig = {
owner: 'angular',
name: 'angular'
};

View File

@ -1,33 +0,0 @@
import {DevInfraMergeConfig} from '../dev-infra/pr/merge/config';
import {getDefaultTargetLabelConfiguration} from '../dev-infra/pr/merge/defaults';
import {github} from './github';
import {release} from './release';
/**
* Configuration for the merge tool in `ng-dev`. This sets up the labels which
* are respected by the merge script (e.g. the target labels).
*/
export const merge: DevInfraMergeConfig['merge'] = async api => {
return {
githubApiMerge: false,
claSignedLabel: 'cla: yes',
mergeReadyLabel: /^action: merge(-assistance)?/,
caretakerNoteLabel: /^(action: merge-assistance)|(PullApprove: disable)/,
commitMessageFixupLabel: 'commit message fixup',
breakingChangeLabel: 'flag: breaking change',
// We can pick any of the NPM packages as we are in a monorepo where all packages are
// published together with the same version and branching.
labels: await getDefaultTargetLabelConfiguration(api, github, release),
requiredBaseCommits: {
// PRs that target either `master` or the patch branch, need to be rebased
// on top of the latest commit message validation fix.
// These SHAs are the commits that update the required license text in the header.
'master': '5aeb9a4124922d8ac08eb73b8f322905a32b0b3a',
'10.0.x': '27b95ba64a5d99757f4042073fd1860e20e3ed24',
},
// `dev-infra` and `docs-infra` are not affecting the public NPM packages. Similarly,
// the `bazel` package is not considered part of the public API so that features
// can land in patch branches.
targetLabelExemptScopes: ['dev-infra', 'docs-infra', 'bazel']
};
};

View File

@ -1,35 +0,0 @@
import {join} from 'path';
import {ReleaseConfig} from '../dev-infra/release/config';
/** Configuration for the `ng-dev release` command. */
export const release: ReleaseConfig = {
publishRegistry: 'https://wombat-dressing-room.appspot.com',
npmPackages: [
'@angular/animations',
'@angular/bazel',
'@angular/common',
'@angular/compiler',
'@angular/compiler-cli',
'@angular/core',
'@angular/elements',
'@angular/forms',
'@angular/language-service',
'@angular/localize',
'@angular/platform-browser',
'@angular/platform-browser-dynamic',
'@angular/platform-server',
'@angular/router',
'@angular/service-worker',
'@angular/upgrade',
],
buildPackages: async (stampForRelease: boolean) => {
// The buildTargetPackages function is loaded at runtime as the loading the script causes an
// invocation of bazel.
const {buildTargetPackages} = require(join(__dirname, '../scripts/build/package-builder'));
return buildTargetPackages('dist/release-output', false, 'Release', stampForRelease);
},
releaseNotes: {
hiddenScopes: ['aio', 'dev-infra', 'docs-infra', 'zone.js'],
},
releasePrLabels: ['comp: build & ci', 'action: merge', 'PullApprove: disable'],
};

1
.npmrc
View File

@ -1 +0,0 @@
engine-strict = true

2
.nvmrc
View File

@ -1 +1 @@
14.16.1 10.15.0

View File

@ -1,9 +0,0 @@
{
"printWidth": 100,
"tabWidth": 2,
"tabs": false,
"singleQuote": true,
"semicolon": true,
"quoteProps": "preserve",
"bracketSpacing": false
}

File diff suppressed because it is too large Load Diff

View File

@ -1,47 +1,27 @@
dist: trusty
sudo: required
language: node_js language: node_js
sudo: false
dist: trusty
node_js: node_js:
- 12.14.1 - 10.9.0
os: addons:
- linux apt:
env: sources:
global: - ubuntu-toolchain-r-test
- DBUS_SESSION_BUS_ADDRESS=/dev/null packages:
- DISPLAY=:99.0 - g++-4.8
- CHROME_BIN=chromium-browser chrome: stable
- LATEST_RELEASE=4.0.0 branches:
# Temporarily disabled until there is a new release branch for 4.0.0 except:
# - LATEST_RELEASE_BRANCH=2.4.xk - g3
- TASK_FLAGS="--dgeni-log=warn" cache:
matrix: yarn: true
# current angular release jobs directories:
- TASK=lint - "./node_modules"
- TASK="run-e2e-tests --fast" SCRIPT=examples-install.sh - "./.chrome/chromium"
- TASK=build-compile SCRIPT=deploy-install.sh WAIT="travis_wait 50" POST_SCRIPT="check-docs.sh -v" - "./aio/node_modules"
# current angular release branch jobs
# - TASK="run-e2e-tests --fast" SCRIPT=examples-install-preview.sh PREVIEW_BRANCH=$LATEST_RELEASE_BRANCH
# - TASK=build-compile SCRIPT=deploy-install-preview.sh PREVIEW_BRANCH=$LATEST_RELEASE_BRANCH WAIT="travis_wait 50" POST_SCRIPT="check-docs.sh -v"
# angular master jobs
- TASK="run-e2e-tests --fast" SCRIPT=examples-install-preview.sh PREVIEW_BRANCH=master
- TASK=build-compile SCRIPT=deploy-install-preview.sh PREVIEW_BRANCH=master WAIT="travis_wait 50" POST_SCRIPT="check-docs.sh -v"
matrix:
fast_finish: true
allow_failures:
# allow current angular release branch and master to fail
# these should be moved to a daily task instead of being ran on every PR
# - env: TASK="run-e2e-tests --fast" SCRIPT=examples-install-preview.sh PREVIEW_BRANCH=$LATEST_RELEASE_BRANCH
# - env: TASK=build-compile SCRIPT=deploy-install-preview.sh PREVIEW_BRANCH=$LATEST_RELEASE_BRANCH WAIT="travis_wait 50" POST_SCRIPT="check-docs.sh -v"
- env: TASK="run-e2e-tests --fast" SCRIPT=examples-install-preview.sh PREVIEW_BRANCH=master
- env: TASK=build-compile SCRIPT=deploy-install-preview.sh PREVIEW_BRANCH=master WAIT="travis_wait 50" POST_SCRIPT="check-docs.sh -v"
before_install: before_install:
- source ./scripts/env-set.sh - curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.13.0
- ./scripts/before-install.sh - export PATH="$HOME/.yarn/bin:$PATH"
install: - google-chrome-stable --headless --disable-gpu --remote-debugging-port=9222 http://localhost &
- npm install --no-optional
- if [[ -n "$SCRIPT" ]]; then echo "EXTRA INSTALL $SCRIPT"; ./scripts/$SCRIPT; fi
before_script:
- sh -e /etc/init.d/xvfb start
script: script:
- $WAIT gulp $TASK $TASK_FLAGS - "./aio/deploy-cn.sh"
- if [[ -n "$POST_SCRIPT" ]]; then ./scripts/$POST_SCRIPT; fi

25
.vscode/README.md vendored
View File

@ -1,25 +0,0 @@
# VSCode Configuration
This folder contains opt-in [Workspace Settings](https://code.visualstudio.com/docs/getstarted/settings), [Tasks](https://code.visualstudio.com/docs/editor/tasks), [Launch Configurations](https://code.visualstudio.com/Docs/editor/debugging#_launch-configurations) and [Extension Recommendations](https://code.visualstudio.com/docs/editor/extension-gallery#_workspace-recommended-extensions) that the Angular team recommends using when working on this repository.
## Usage
To use the recommended configurations follow the steps below:
- install the recommneded extensions in `.vscode/extensions.json`
- copy (or link) `.vscode/recommended-settings.json` to `.vscode/settings.json`
- copy (or link) `.vscode/recommended-launch.json` to `.vscode/launch.json`
- copy (or link) `.vscode/recommended-tasks.json` to `.vscode/tasks.json`
- restart the editor
If you already have your custom workspace settings you should instead manually merge the file contents.
This isn't an automatic process so you will need to repeat it when settings are updated.
To see the recommended extensions select "Extensions: Show Recommended Extensions" in the [Command Palette](https://code.visualstudio.com/docs/getstarted/userinterface#_command-palette).
## Editing `.vscode/recommended-*.json` files
If you wish to add extra configuration items please keep in mind any modifications you make here will be used by many users.
Try to keep these settings/configuations to things that help facilitate the development process and avoid altering the user workflow whenever possible.

View File

@ -1,15 +0,0 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
// List of extensions which should be recommended for users of this workspace.
"recommendations": [
"devondcarew.bazel-code",
"gkalpak.aio-docs-utils",
"ms-vscode.vscode-typescript-tslint-plugin",
"xaver.clang-format",
// The following extensions are useful when working on angular.io (i.e. inside the `aio/` directory).
//"angular.ng-template",
//"dbaeumer.vscode-eslint",
],
}

View File

@ -1,90 +0,0 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Attach to bazel test ... --config=debug",
"type": "node",
"request": "attach",
"port": 9229,
"address": "localhost",
"restart": false,
"sourceMaps": true,
"localRoot": "${workspaceRoot}",
"remoteRoot": "${workspaceRoot}",
"stopOnEntry": false,
"timeout": 600000,
"outFiles": ["${workspaceFolder}/bazel-out/**/angular/**/*.js"],
},
{
"name": "Attach to bazel test ... --config=debug (no source maps)",
"type": "node",
"request": "attach",
"port": 9229,
"address": "localhost",
"restart": false,
"sourceMaps": false,
"localRoot": "${workspaceRoot}",
"remoteRoot": "${workspaceRoot}",
"stopOnEntry": false,
"timeout": 600000,
"outFiles": ["${workspaceFolder}/bazel-out/**/angular/**/*.js"],
},
{
"name": "IVY:packages/core/test/acceptance",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/node_modules/.bin/bazelisk",
"args": [
"test",
"--config=ivy",
"packages/core/test/acceptance",
"--config=debug"
],
"port": 9229,
"address": "localhost",
"restart": true,
"sourceMaps": true,
"timeout": 600000,
"outFiles": ["${workspaceFolder}/bazel-out/**/angular/**/*.js"],
},
{
"name": "IVY:packages/core/test/render3",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/node_modules/.bin/bazelisk",
"args": [
"test",
"--config=ivy",
"packages/core/test/render3",
"--config=debug"
],
"port": 9229,
"address": "localhost",
"restart": true,
"sourceMaps": true,
"timeout": 600000,
"outFiles": ["${workspaceFolder}/bazel-out/**/angular/**/*.js"],
},
{
"name": "IVY:packages/core/test",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/node_modules/.bin/bazelisk",
"args": [
"test",
"--config=ivy",
"packages/core/test",
"--config=debug"
],
"port": 9229,
"address": "localhost",
"restart": true,
"sourceMaps": true,
"timeout": 600000,
"outFiles": ["${workspaceFolder}/bazel-out/**/angular/**/*.js"],
},
]
}

View File

@ -1,32 +0,0 @@
{
// Format js and ts files on save with `clang-format.executable`
// If `clang-format.executable` is not being used, these two settings should be removed otherwise it will break existing formatting.
// You can instead run `yarn gulp format` to manually format your code.
"[javascript]": {
"editor.formatOnSave": true,
},
"[typescript]": {
"editor.formatOnSave": true,
},
// Please install https://marketplace.visualstudio.com/items?itemName=xaver.clang-format to take advantage of `clang-format` in VSCode.
// (See https://clang.llvm.org/docs/ClangFormat.html for more info `clang-format`.)
"clang-format.executable": "${workspaceRoot}/node_modules/.bin/clang-format",
// Exclude third party modules and build artifacts from the editor watchers/searches.
"files.watcherExclude": {
"**/.git/objects/**": true,
"**/.git/subtree-cache/**": true,
"**/node_modules/**": true,
"**/bazel-out/**": true,
"**/dist/**": true,
"**/aio/src/generated/**": true,
},
"search.exclude": {
"**/node_modules": true,
"**/bower_components": true,
"**/bazel-out": true,
"**/dist": true,
"**/aio/src/generated": true,
".history": true,
},
"git.ignoreLimitWarning": true,
}

View File

@ -1,113 +0,0 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "IVY:packages/core/test/...",
"type": "shell",
"command": "${workspaceFolder}/node_modules/.bin/bazelisk",
"args": [
"test",
"--config=ivy",
"packages/core/test",
"packages/core/test/acceptance",
"packages/core/test/render3",
],
"group": "test",
"presentation": {
"reveal": "always",
"panel": "dedicated",
},
},
{
"label": "VE:packages/core/test/...",
"type": "shell",
"command": "${workspaceFolder}/node_modules/.bin/bazelisk",
"args": [
"test",
"packages/core/test",
"packages/core/test/acceptance",
"packages/core/test/render3",
],
"group": "test",
"presentation": {
"reveal": "always",
"panel": "dedicated",
},
},
{
"label": "IVY:packages/core/test/acceptance",
"type": "shell",
"command": "${workspaceFolder}/node_modules/.bin/bazelisk",
"args": [
"test",
"--config=ivy",
"packages/core/test/acceptance",
],
"group": "test",
"presentation": {
"reveal": "always",
"panel": "dedicated",
},
},
{
"label": "VE:packages/core/test/acceptance",
"type": "shell",
"command": "${workspaceFolder}/node_modules/.bin/bazelisk",
"args": [
"test",
"packages/core/test/acceptance",
],
"group": "test",
"presentation": {
"reveal": "always",
"panel": "dedicated",
},
},
{
"label": "IVY:packages/core/test",
"type": "shell",
"command": "${workspaceFolder}/node_modules/.bin/bazelisk",
"args": [
"test",
"--config=ivy",
"packages/core/test",
],
"group": "test",
"presentation": {
"reveal": "always",
"panel": "dedicated",
},
},
{
"label": "VE:packages/core/test",
"type": "shell",
"command": "${workspaceFolder}/node_modules/.bin/bazelisk",
"args": [
"test",
"packages/core/test",
],
"group": "test",
"presentation": {
"reveal": "always",
"panel": "dedicated",
},
},
{
"label": "IVY:packages/core/test/render3",
"type": "shell",
"command": "${workspaceFolder}/node_modules/.bin/bazelisk",
"args": [
"test",
"--config=ivy",
"packages/core/test/render3",
],
"group": "test",
"presentation": {
"reveal": "always",
"panel": "dedicated",
},
},
],
}

19
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,19 @@
{
"editor.formatOnSave": true,
// Please install http://clang.llvm.org/docs/ClangFormat.html in VSCode to take advantage of clang-format
"clang-format.executable": "${workspaceRoot}/node_modules/.bin/clang-format",
"files.watcherExclude": {
"**/.git/objects/**": true,
"**/.git/subtree-cache/**": true,
"**/node_modules/**": true,
"**/bazel-out/**": true,
"**/dist/**": true,
},
"search.exclude": {
"**/node_modules": true,
"**/bower_components": true,
"**/bazel-out": true,
"**/dist": true,
},
"git.ignoreLimitWarning": true,
}

View File

@ -1,13 +0,0 @@
# Yarn Vendoring
We utilize Yarn's `yarn-path` configuration in a shared `.yarnrc` file to enforce
everyone using the same version of Yarn. Yarn checks the `.yarnrc` file to
determine if yarn should delegate the command to a vendored version at the
provided path.
## How to update
To update to the latest version of Yarn as our vendored version:
- Run this command
```sh
yarn policies set-version latest
```
- Remove the previous version

File diff suppressed because one or more lines are too long

View File

@ -1,5 +0,0 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
yarn-path ".yarn/releases/yarn-1.22.10.cjs"

View File

@ -1,58 +1,56 @@
package(default_visibility = ["//visibility:public"]) package(default_visibility = ["//visibility:public"])
exports_files([ load("@build_bazel_rules_nodejs//:defs.bzl", "node_modules_filegroup")
"LICENSE",
"karma-js.conf.js",
"browser-providers.conf.js",
"scripts/ci/track-payload-size.sh",
"scripts/ci/payload-size.sh",
"scripts/ci/payload-size.js",
"package.json",
])
alias( exports_files([
name = "tsconfig.json", "tsconfig.json",
actual = "//packages:tsconfig-build.json", "LICENSE",
) "protractor-perf.conf.js",
])
filegroup( filegroup(
name = "web_test_bootstrap_scripts", name = "web_test_bootstrap_scripts",
# do not sort # do not sort
srcs = [ srcs = [
"@npm//:node_modules/core-js-bundle/index.js", "@ngdeps//node_modules/reflect-metadata:Reflect.js",
"//packages/zone.js/bundles:zone.umd.js", "@ngdeps//node_modules/zone.js:dist/zone.js",
"//packages/zone.js/bundles:zone-testing.umd.js", "@ngdeps//node_modules/zone.js:dist/zone-testing.js",
"//packages/zone.js/bundles:task-tracking.umd.js", "@ngdeps//node_modules/zone.js:dist/task-tracking.js",
"//:test-events.js", "//:test-events.js",
"//:third_party/shims_for_internal_tests.js",
# Including systemjs because it defines `__eval`, which produces correct stack traces.
"@npm//:node_modules/systemjs/dist/system.src.js",
"@npm//:node_modules/reflect-metadata/Reflect.js",
], ],
) )
filegroup( filegroup(
name = "angularjs_scripts", name = "angularjs_scripts",
srcs = [ srcs = [
# We also declare the unminified AngularJS files since these can be used for # We also declare the unminfied AngularJS files since these can be used for
# local debugging (e.g. see: packages/upgrade/test/common/test_helpers.ts) # local debugging (e.g. see: packages/upgrade/test/common/test_helpers.ts)
"@npm//:node_modules/angular-1.5/angular.js", "@ngdeps//node_modules/angular:angular.js",
"@npm//:node_modules/angular-1.5/angular.min.js", "@ngdeps//node_modules/angular:angular.min.js",
"@npm//:node_modules/angular-1.6/angular.js", "@ngdeps//node_modules/angular-1.5:angular.js",
"@npm//:node_modules/angular-1.6/angular.min.js", "@ngdeps//node_modules/angular-1.5:angular.min.js",
"@npm//:node_modules/angular-1.7/angular.js", "@ngdeps//node_modules/angular-1.6:angular.js",
"@npm//:node_modules/angular-1.7/angular.min.js", "@ngdeps//node_modules/angular-1.6:angular.min.js",
"@npm//:node_modules/angular-mocks-1.5/angular-mocks.js", "@ngdeps//node_modules/angular-mocks:angular-mocks.js",
"@npm//:node_modules/angular-mocks-1.6/angular-mocks.js", "@ngdeps//node_modules/angular-mocks-1.5:angular-mocks.js",
"@npm//:node_modules/angular-mocks-1.7/angular-mocks.js", "@ngdeps//node_modules/angular-mocks-1.6:angular-mocks.js",
"@npm//:node_modules/angular-mocks-1.8/angular-mocks.js",
"@npm//:node_modules/angular-1.8/angular.js",
"@npm//:node_modules/angular-1.8/angular.min.js",
], ],
) )
# Detect if the build is running under --stamp load("@build_bazel_rules_nodejs//:defs.bzl", "nodejs_binary")
config_setting(
name = "stamp", # A nodejs_binary for @angular/bazel/ngc-wrapped to use by default in
values = {"stamp": "true"}, # ng_module that depends on @npm//@angular/bazel instead of the
# output of the //packages/bazel/src/ngc-wrapped ts_library rule. This
# default is for downstream users that depend on the @angular/bazel npm
# package. The generated @npm//@angular/bazel/ngc-wrapped target
# does not work because it does not have the node `--expose-gc` flag
# set which is required to support the call to `global.gc()`.
nodejs_binary(
name = "@angular/bazel/ngc-wrapped",
configuration_env_vars = ["compile"],
data = ["@npm//@angular/bazel"],
entry_point = "@angular/bazel/src/ngc-wrapped/index.js",
install_source_map_support = False,
templated_args = ["--node_options=--expose-gc"],
) )

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,7 @@
As contributors and maintainers of the Angular project, we pledge to respect everyone who contributes by posting issues, updating documentation, submitting pull requests, providing feedback in comments, and any other activities. As contributors and maintainers of the Angular project, we pledge to respect everyone who contributes by posting issues, updating documentation, submitting pull requests, providing feedback in comments, and any other activities.
Communication through any of Angular's channels (GitHub, Discord, Gitter, IRC, mailing lists, Twitter, etc.) must be constructive and never resort to personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct. Communication through any of Angular's channels (GitHub, Gitter, IRC, mailing lists, Google+, Twitter, etc.) must be constructive and never resort to personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
We promise to extend courtesy and respect to everyone involved in this project regardless of gender, gender identity, sexual orientation, disability, age, race, ethnicity, religion, or level of experience. We expect anyone contributing to the Angular project to do the same. We promise to extend courtesy and respect to everyone involved in this project regardless of gender, gender identity, sexual orientation, disability, age, race, ethnicity, religion, or level of experience. We expect anyone contributing to the Angular project to do the same.

View File

@ -1,7 +1,7 @@
# Contributing to Angular # Contributing to Angular
We would love for you to contribute to Angular and help make it even better than it is today! We would love for you to contribute to Angular and help make it even better than it is
As a contributor, here are the guidelines we would like you to follow: today! As a contributor, here are the guidelines we would like you to follow:
- [Code of Conduct](#coc) - [Code of Conduct](#coc)
- [Question or Problem?](#question) - [Question or Problem?](#question)
@ -12,168 +12,108 @@ As a contributor, here are the guidelines we would like you to follow:
- [Commit Message Guidelines](#commit) - [Commit Message Guidelines](#commit)
- [Signing the CLA](#cla) - [Signing the CLA](#cla)
## <a name="coc"></a> Code of Conduct ## <a name="coc"></a> Code of Conduct
Help us keep Angular open and inclusive. Please read and follow our [Code of Conduct][coc].
Help us keep Angular open and inclusive.
Please read and follow our [Code of Conduct][coc].
## <a name="question"></a> Got a Question or Problem? ## <a name="question"></a> Got a Question or Problem?
Do not open issues for general support questions as we want to keep GitHub issues for bug reports and feature requests. Do not open issues for general support questions as we want to keep GitHub issues for bug reports and feature requests. You've got much better chances of getting your question answered on [Stack Overflow](https://stackoverflow.com/questions/tagged/angular) where the questions should be tagged with tag `angular`.
Instead, we recommend using [Stack Overflow](https://stackoverflow.com/questions/tagged/angular) to ask support-related questions. When creating a new question on Stack Overflow, make sure to add the `angular` tag.
Stack Overflow is a much better place to ask questions since: Stack Overflow is a much better place to ask questions since:
- there are thousands of people willing to help on Stack Overflow - there are thousands of people willing to help on Stack Overflow
- questions and answers stay available for public viewing so your question/answer might help someone else - questions and answers stay available for public viewing so your question / answer might help someone else
- Stack Overflow's voting system assures that the best answers are prominently visible. - Stack Overflow's voting system assures that the best answers are prominently visible.
To save your and our time, we will systematically close all issues that are requests for general support and redirect people to Stack Overflow. To save your and our time, we will systematically close all issues that are requests for general support and redirect people to Stack Overflow.
If you would like to chat about the question in real-time, you can reach out via [our Discord server][discord]. If you would like to chat about the question in real-time, you can reach out via [our gitter channel][gitter].
## <a name="issue"></a> Found a Bug? ## <a name="issue"></a> Found a Bug?
If you find a bug in the source code, you can help us by
If you find a bug in the source code, you can help us by [submitting an issue](#submit-issue) to our [GitHub Repository][github]. [submitting an issue](#submit-issue) to our [GitHub Repository][github]. Even better, you can
Even better, you can [submit a Pull Request](#submit-pr) with a fix. [submit a Pull Request](#submit-pr) with a fix.
## <a name="feature"></a> Missing a Feature? ## <a name="feature"></a> Missing a Feature?
You can *request* a new feature by [submitting an issue](#submit-issue) to our GitHub Repository. You can *request* a new feature by [submitting an issue](#submit-issue) to our GitHub
If you would like to *implement* a new feature, please consider the size of the change in order to determine the right steps to proceed: Repository. If you would like to *implement* a new feature, please submit an issue with
a proposal for your work first, to be sure that we can use it.
* For a **Major Feature**, first open an issue and outline your proposal so that it can be discussed. Please consider what kind of change it is:
This process allows us to better coordinate our efforts, prevent duplication of work, and help you to craft the change so that it is successfully accepted into the project.
**Note**: Adding a new topic to the documentation, or significantly re-writing a topic, counts as a major feature.
* For a **Major Feature**, first open an issue and outline your proposal so that it can be
discussed. This will also allow us to better coordinate our efforts, prevent duplication of work,
and help you to craft the change so that it is successfully accepted into the project.
* **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr). * **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr).
## <a name="submit"></a> Submission Guidelines ## <a name="submit"></a> Submission Guidelines
### <a name="submit-issue"></a> Submitting an Issue ### <a name="submit-issue"></a> Submitting an Issue
Before you submit an issue, please search the issue tracker, maybe an issue for your problem already exists and the discussion might inform you of workarounds readily available. Before you submit an issue, please search the issue tracker, maybe an issue for your problem already exists and the discussion might inform you of workarounds readily available.
We want to fix all the issues as soon as possible, but before fixing a bug we need to reproduce and confirm it. We want to fix all the issues as soon as possible, but before fixing a bug we need to reproduce and confirm it. In order to reproduce bugs, we will systematically ask you to provide a minimal reproduction. Having a minimal reproducible scenario gives us a wealth of important information without going back & forth to you with additional questions.
In order to reproduce bugs, we require that you provide a minimal reproduction.
Having a minimal reproducible scenario gives us a wealth of important information without going back and forth to you with additional questions.
A minimal reproduction allows us to quickly confirm a bug (or point out a coding problem) as well as confirm that we are fixing the right problem. A minimal reproduction allows us to quickly confirm a bug (or point out a coding problem) as well as confirm that we are fixing the right problem.
We require a minimal reproduction to save maintainers' time and ultimately be able to fix more bugs. We will be insisting on a minimal reproduction scenario in order to save maintainers time and ultimately be able to fix more bugs. Interestingly, from our experience users often find coding problems themselves while preparing a minimal reproduction. We understand that sometimes it might be hard to extract essential bits of code from a larger code-base but we really need to isolate the problem before we can fix it.
Often, developers find coding problems themselves while preparing a minimal reproduction.
We understand that sometimes it might be hard to extract essential bits of code from a larger codebase but we really need to isolate the problem before we can fix it.
Unfortunately, we are not able to investigate / fix bugs without a minimal reproduction, so if we don't hear back from you, we are going to close an issue that doesn't have enough info to be reproduced. Unfortunately, we are not able to investigate / fix bugs without a minimal reproduction, so if we don't hear back from you we are going to close an issue that doesn't have enough info to be reproduced.
You can file new issues by selecting from our [new issue templates](https://github.com/angular/angular/issues/new/choose) and filling out the issue template. You can file new issues by selecting from our [new issue templates](https://github.com/angular/angular/issues/new/choose) and filling out the issue template.
### <a name="submit-pr"></a> Submitting a Pull Request (PR) ### <a name="submit-pr"></a> Submitting a Pull Request (PR)
Before you submit your Pull Request (PR) consider the following guidelines: Before you submit your Pull Request (PR) consider the following guidelines:
1. Search [GitHub](https://github.com/angular/angular/pulls) for an open or closed PR that relates to your submission. 1. Search [GitHub](https://github.com/angular/angular/pulls) for an open or closed PR
You don't want to duplicate existing efforts. that relates to your submission. You don't want to duplicate effort.
1. Be sure that an issue describes the problem you're fixing, or documents the design for the feature you'd like to add.
2. Be sure that an issue describes the problem you're fixing, or documents the design for the feature you'd like to add. Discussing the design up front helps to ensure that we're ready to accept your work.
Discussing the design upfront helps to ensure that we're ready to accept your work. 1. Please sign our [Contributor License Agreement (CLA)](#cla) before sending PRs.
We cannot accept code without this. Make sure you sign with the primary email address of the Git identity that has been granted access to the Angular repository.
3. Please sign our [Contributor License Agreement (CLA)](#cla) before sending PRs. 1. Fork the angular/angular repo.
We cannot accept code without a signed CLA. 1. Make your changes in a new git branch:
Make sure you author all contributed Git commits with email address associated with your CLA signature.
4. [Fork](https://docs.github.com/en/github/getting-started-with-github/fork-a-repo) the angular/angular repo.
5. In your forked repository, make your changes in a new git branch:
```shell ```shell
git checkout -b my-fix-branch master git checkout -b my-fix-branch master
``` ```
6. Create your patch, **including appropriate test cases**. 1. Create your patch, **including appropriate test cases**.
1. Follow our [Coding Rules](#rules).
7. Follow our [Coding Rules](#rules). 1. Run the full Angular test suite, as described in the [developer documentation][dev-doc],
and ensure that all tests pass.
8. Run the full Angular test suite, as described in the [developer documentation][dev-doc], and ensure that all tests pass. 1. Commit your changes using a descriptive commit message that follows our
[commit message conventions](#commit). Adherence to these conventions
9. Commit your changes using a descriptive commit message that follows our [commit message conventions](#commit). is necessary because release notes are automatically generated from these messages.
Adherence to these conventions is necessary because release notes are automatically generated from these messages.
```shell ```shell
git commit --all git commit -a
``` ```
Note: the optional commit `-a` command line option will automatically "add" and "rm" edited files. Note: the optional commit `-a` command line option will automatically "add" and "rm" edited files.
10. Push your branch to GitHub: 1. Push your branch to GitHub:
```shell ```shell
git push origin my-fix-branch git push origin my-fix-branch
``` ```
11. In GitHub, send a pull request to `angular:master`. 1. In GitHub, send a pull request to `angular:master`.
* If we suggest changes then:
### Reviewing a Pull Request * Make the required updates.
* Re-run the Angular test suites to ensure tests are still passing.
The Angular team reserves the right not to accept pull requests from community members who haven't been good citizens of the community. Such behavior includes not following the [Angular code of conduct](https://github.com/angular/code-of-conduct) and applies within or outside of Angular managed channels. * Rebase your branch and force push to your GitHub repository (this will update your Pull Request):
#### Addressing review feedback
If we ask for changes via code reviews then:
1. Make the required updates to the code.
2. Re-run the Angular test suites to ensure tests are still passing.
3. Create a fixup commit and push to your GitHub repository (this will update your Pull Request):
```shell ```shell
git commit --all --fixup HEAD git rebase master -i
git push git push -f
``` ```
For more info on working with fixup commits see [here](docs/FIXUP_COMMITS.md).
That's it! Thank you for your contribution! That's it! Thank you for your contribution!
##### Updating the commit message
A reviewer might often suggest changes to a commit message (for example, to add more context for a change or adhere to our [commit message guidelines](#commit)).
In order to update the commit message of the last commit on your branch:
1. Check out your branch:
```shell
git checkout my-fix-branch
```
2. Amend the last commit and modify the commit message:
```shell
git commit --amend
```
3. Push to your GitHub repository:
```shell
git push --force-with-lease
```
> NOTE:<br />
> If you need to update the commit message of an earlier commit, you can use `git rebase` in interactive mode.
> See the [git docs](https://git-scm.com/docs/git-rebase#_interactive_mode) for more details.
#### After your pull request is merged #### After your pull request is merged
After your pull request is merged, you can safely delete your branch and pull the changes from the main (upstream) repository: After your pull request is merged, you can safely delete your branch and pull the changes
from the main (upstream) repository:
* Delete the remote branch on GitHub either through the GitHub web UI or your local shell as follows: * Delete the remote branch on GitHub either through the GitHub web UI or your local shell as follows:
@ -199,66 +139,55 @@ After your pull request is merged, you can safely delete your branch and pull th
git pull --ff upstream master git pull --ff upstream master
``` ```
## <a name="rules"></a> Coding Rules ## <a name="rules"></a> Coding Rules
To ensure consistency throughout the source code, keep these rules in mind as you are working: To ensure consistency throughout the source code, keep these rules in mind as you are working:
* All features or bug fixes **must be tested** by one or more specs (unit-tests). * All features or bug fixes **must be tested** by one or more specs (unit-tests).
* All public API methods **must be documented**. * All public API methods **must be documented**. (Details TBC).
* We follow [Google's JavaScript Style Guide][js-style-guide], but wrap all code at **100 characters**. * We follow [Google's JavaScript Style Guide][js-style-guide], but wrap all code at
**100 characters**. An automated formatter is available, see
[DEVELOPER.md](docs/DEVELOPER.md#clang-format).
An automated formatter is available, see [DEVELOPER.md](docs/DEVELOPER.md#clang-format). ## <a name="commit"></a> Commit Message Guidelines
We have very precise rules over how our git commit messages can be formatted. This leads to **more
readable messages** that are easy to follow when looking through the **project history**. But also,
we use the git commit messages to **generate the Angular change log**.
## <a name="commit"></a> Commit Message Format ### Commit Message Format
Each commit message consists of a **header**, a **body** and a **footer**. The header has a special
*This specification is inspired by and supersedes the [AngularJS commit message format][commit-message-format].* format that includes a **type**, a **scope** and a **subject**:
We have very precise rules over how our Git commit messages must be formatted.
This format leads to **easier to read commit history**.
Each commit message consists of a **header**, a **body**, and a **footer**.
``` ```
<header> <type>(<scope>): <subject>
<BLANK LINE> <BLANK LINE>
<body> <body>
<BLANK LINE> <BLANK LINE>
<footer> <footer>
``` ```
The `header` is mandatory and must conform to the [Commit Message Header](#commit-header) format. The **header** is mandatory and the **scope** of the header is optional.
The `body` is mandatory for all commits except for those of type "docs". Any line of the commit message cannot be longer 100 characters! This allows the message to be easier
When the body is present it must be at least 20 characters long and must conform to the [Commit Message Body](#commit-body) format. to read on GitHub as well as in various git tools.
The `footer` is optional. The [Commit Message Footer](#commit-footer) format describes what the footer is used for and the structure it must have. The footer should contain a [closing reference to an issue](https://help.github.com/articles/closing-issues-via-commit-messages/) if any.
Any line of the commit message cannot be longer than 100 characters. Samples: (even more [samples](https://github.com/angular/angular/commits/master))
#### <a name="commit-header"></a>Commit Message Header
``` ```
<type>(<scope>): <short summary> docs(changelog): update changelog to beta.5
│ │ │ ```
│ │ └─⫸ Summary in present tense. Not capitalized. No period at the end. ```
│ │ fix(release): need to depend on latest rxjs and zone.js
│ └─⫸ Commit Scope: animations|bazel|benchpress|common|compiler|compiler-cli|core|
│ elements|forms|http|language-service|localize|platform-browser| The version in our package.json gets copied to the one we publish, and users need the latest of these.
│ platform-browser-dynamic|platform-server|router|service-worker|
│ upgrade|zone.js|packaging|changelog|dev-infra|docs-infra|migrations|
│ ngcc|ve
└─⫸ Commit Type: build|ci|docs|feat|fix|perf|refactor|test
``` ```
The `<type>` and `<summary>` fields are mandatory, the `(<scope>)` field is optional. ### Revert
If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit. In the body it should say: `This reverts commit <hash>.`, where the hash is the SHA of the commit being reverted.
##### Type
### Type
Must be one of the following: Must be one of the following:
* **build**: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm) * **build**: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
@ -268,127 +197,96 @@ Must be one of the following:
* **fix**: A bug fix * **fix**: A bug fix
* **perf**: A code change that improves performance * **perf**: A code change that improves performance
* **refactor**: A code change that neither fixes a bug nor adds a feature * **refactor**: A code change that neither fixes a bug nor adds a feature
* **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
* **test**: Adding missing tests or correcting existing tests * **test**: Adding missing tests or correcting existing tests
### Scope
##### Scope The scope should be the name of the npm package affected (as perceived by the person reading the changelog generated from commit messages.
The scope should be the name of the npm package affected (as perceived by the person reading the changelog generated from commit messages).
The following is the list of supported scopes: The following is the list of supported scopes:
* `animations` * **animations**
* `bazel` * **common**
* `benchpress` * **compiler**
* `common` * **compiler-cli**
* `compiler` * **core**
* `compiler-cli` * **elements**
* `core` * **forms**
* `elements` * **http**
* `forms` * **language-service**
* `http` * **platform-browser**
* `language-service` * **platform-browser-dynamic**
* `localize` * **platform-server**
* `platform-browser` * **platform-webworker**
* `platform-browser-dynamic` * **platform-webworker-dynamic**
* `platform-server` * **router**
* `router` * **service-worker**
* `service-worker` * **upgrade**
* `upgrade`
* `zone.js`
There are currently a few exceptions to the "use package name" rule: There are currently a few exceptions to the "use package name" rule:
* `packaging`: used for changes that change the npm package layout in all of our packages, e.g. public path changes, package.json changes done to all packages, d.ts file/format changes, changes to bundles, etc. * **packaging**: used for changes that change the npm package layout in all of our packages, e.g.
public path changes, package.json changes done to all packages, d.ts file/format changes, changes
to bundles, etc.
* **changelog**: used for updating the release notes in CHANGELOG.md
* **docs-infra**: used for docs-app (angular.io) related changes within the /aio directory of the
repo
* none/empty string: useful for `style`, `test` and `refactor` changes that are done across all
packages (e.g. `style: add missing semicolons`) and for docs changes that are not related to a
specific package (e.g. `docs: fix typo in tutorial`).
* `changelog`: used for updating the release notes in CHANGELOG.md ### Subject
The subject contains a succinct description of the change:
* `dev-infra`: used for dev-infra related changes within the directories /scripts, /tools and /dev-infra
* `docs-infra`: used for docs-app (angular.io) related changes within the /aio directory of the repo
* `migrations`: used for changes to the `ng update` migrations.
* `ngcc`: used for changes to the [Angular Compatibility Compiler](./packages/compiler-cli/ngcc/README.md)
* `ve`: used for changes specific to ViewEngine (legacy compiler/renderer).
* none/empty string: useful for `test` and `refactor` changes that are done across all packages (e.g. `test: add missing unit tests`) and for docs changes that are not related to a specific package (e.g. `docs: fix typo in tutorial`).
##### Summary
Use the summary field to provide a succinct description of the change:
* use the imperative, present tense: "change" not "changed" nor "changes" * use the imperative, present tense: "change" not "changed" nor "changes"
* don't capitalize the first letter * don't capitalize the first letter
* no dot (.) at the end * no dot (.) at the end
### Body
Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes".
The body should include the motivation for the change and contrast this with previous behavior.
#### <a name="commit-body"></a>Commit Message Body ### Footer
The footer should contain any information about **Breaking Changes** and is also the place to
reference GitHub issues that this commit **Closes**.
Just as in the summary, use the imperative, present tense: "fix" not "fixed" nor "fixes". **Breaking Changes** should start with the word `BREAKING CHANGE:` with a space or two newlines. The rest of the commit message is then used for this.
Explain the motivation for the change in the commit message body. This commit message should explain _why_ you are making the change.
You can include a comparison of the previous behavior with the new behavior in order to illustrate the impact of the change.
#### <a name="commit-footer"></a>Commit Message Footer
The footer can contain information about breaking changes and is also the place to reference GitHub issues, Jira tickets, and other PRs that this commit closes or is related to.
```
BREAKING CHANGE: <breaking change summary>
<BLANK LINE>
<breaking change description + migration instructions>
<BLANK LINE>
<BLANK LINE>
Fixes #<issue number>
```
Breaking Change section should start with the phrase "BREAKING CHANGE: " followed by a summary of the breaking change, a blank line, and a detailed description of the breaking change that also includes migration instructions.
### Revert commits
If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit.
The content of the commit message body should contain:
- information about the SHA of the commit being reverted in the following format: `This reverts commit <SHA>`,
- a clear description of the reason for reverting the commit message.
A detailed explanation can be found in this [document][commit-message-format].
## <a name="cla"></a> Signing the CLA ## <a name="cla"></a> Signing the CLA
Please sign our Contributor License Agreement (CLA) before sending pull requests. For any code Please sign our Contributor License Agreement (CLA) before sending pull requests. For any code
changes to be accepted, the CLA must be signed. It's a quick process, we promise! changes to be accepted, the CLA must be signed. It's a quick process, we promise!
* For individuals, we have a [simple click-through form][individual-cla]. * For individuals we have a [simple click-through form][individual-cla].
* For corporations, we'll need you to * For corporations we'll need you to
[print, sign and one of scan+email, fax or mail the form][corporate-cla]. [print, sign and one of scan+email, fax or mail the form][corporate-cla].
If you have more than one GitHub accounts, or multiple email addresses associated with a single GitHub account, you must sign the CLA using the primary email address of the GitHub account used to author Git commits and send pull requests. <hr>
The following documents can help you sort out issues with GitHub accounts and multiple email addresses: If you have more than one Git identity, you must make sure that you sign the CLA using the primary email address associated with the ID that has been granted access to the Angular repository. Git identities can be associated with more than one email address, and only one is primary. Here are some links to help you sort out multiple Git identities and email addresses:
* https://help.github.com/articles/setting-your-commit-email-address-in-git/ * https://help.github.com/articles/setting-your-commit-email-address-in-git/
* https://stackoverflow.com/questions/37245303/what-does-usera-committed-with-userb-13-days-ago-on-github-mean * https://stackoverflow.com/questions/37245303/what-does-usera-committed-with-userb-13-days-ago-on-github-mean
* https://help.github.com/articles/about-commit-email-addresses/ * https://help.github.com/articles/about-commit-email-addresses/
* https://help.github.com/articles/blocking-command-line-pushes-that-expose-your-personal-email-address/ * https://help.github.com/articles/blocking-command-line-pushes-that-expose-your-personal-email-address/
Note that if you have more than one Git identity, it is important to verify that you are logged in with the same ID with which you signed the CLA, before you commit changes. If not, your PR will fail the CLA check.
<hr>
[angular-group]: https://groups.google.com/forum/#!forum/angular [angular-group]: https://groups.google.com/forum/#!forum/angular
[coc]: https://github.com/angular/code-of-conduct/blob/master/CODE_OF_CONDUCT.md [coc]: https://github.com/angular/code-of-conduct/blob/master/CODE_OF_CONDUCT.md
[commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit# [commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
[corporate-cla]: https://cla.developers.google.com/about/google-corporate [corporate-cla]: http://code.google.com/legal/corporate-cla-v1.0.html
[dev-doc]: https://github.com/angular/angular/blob/master/docs/DEVELOPER.md [dev-doc]: https://github.com/angular/angular/blob/master/docs/DEVELOPER.md
[github]: https://github.com/angular/angular [github]: https://github.com/angular/angular
[discord]: https://discord.gg/angular [gitter]: https://gitter.im/angular/angular
[individual-cla]: https://cla.developers.google.com/about/google-individual [individual-cla]: http://code.google.com/legal/individual-cla-v1.0.html
[js-style-guide]: https://google.github.io/styleguide/jsguide.html [js-style-guide]: https://google.github.io/styleguide/jsguide.html
[jsfiddle]: https://jsfiddle.net/ [jsfiddle]: http://jsfiddle.net
[plunker]: https://plnkr.co/edit [plunker]: http://plnkr.co/edit
[runnable]: https://runnable.com/ [runnable]: http://runnable.com
[stackoverflow]: https://stackoverflow.com/questions/tagged/angular [stackoverflow]: http://stackoverflow.com/questions/tagged/angular

View File

@ -1,6 +1,6 @@
The MIT License The MIT License
Copyright (c) 2010-2021 Google LLC. https://angular.io/license Copyright (c) 2010-2019 Google LLC. http://angular.io/license
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -1,124 +0,0 @@
# Angular.io
Angular.io is site for Angular 2 **documentation** .
This site also includes links to other helpful angular resources including
Angular 2, Angular 1, Angular Material, and AngularFire.
## Issues
Please file **Developer Guide, Cookbook, and code sample issues _only_** in this
[Angular.io](https://github.com/angular/angular.io/issues) github repo.
**Angular API issues, cheatsheet corrections, feature requests, defect reports, and technical questions** concerning Angular itself
belong in the [**angular source code**](https://github.com/angular/angular/issues) github repo.
We can't handle those topics here and will ask you to re-post them on the angular repo.
## How you can help
Filing issues is helpful but **pull requests** that improve the docs are even better!
Learn how to [contribute to Angular.io](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md).
## Development Setup
This site relies heavily on node and npm.
1. Make sure you are using the latest node and npm;
if not install [nvm](https://github.com/creationix/nvm) to get node going on your machine.
1. install these npm packages *globally*: `npm install -g harp gulp protractor`
1. clone this repo and the [angular source code repo](https://github.com/angular/angular) to the same parent directory.
The two cloned repo directories must be sibling.
1. cd into root directory `Angular.io/`
1. install the *all-docs* local packages by running `npm install`
> If running node v.5+, you probably must rebuild `node-sass` in a separate step: `npm rebuild node-sass`
1. See [below](#code-sample-development) for code sample development preparation.
## Content Development
All documentation content is written in Jade which has [its own syntax](http://jade-lang.com/reference/).
Be aware of the strict demands imposed by this significant-whitespace language.
We strongly recommend running one of the gulp `serve-and-sync` commands [described below](#serve-and-sync)
while editing content so you can see the effect of your changes *as you type*.
The documentation relies on specific styles and mixins.
Learn about those in the [documentation styleguide](https://angular.io/docs/ts/latest/styleguide.html).
The jade documentation files are language-specific directories under either `public/docs/`.
For example, all of the TypeScript docs are in `public/docs/ts/latest`, e.g.
- `public/docs/ts/latest/quickstart.jade`
- `public/docs/ts/latest/guide/architecture.jade`
- `public/docs/ts/latest/cookbook/component-communication.jade`
- `public/docs/ts/latest/tutorial/toh-pt5.jade`
### Local server with watches and browser reload
1. cd into root directory `Angular.io/`
1. run `gulp serve-and-sync`
1. browser will launch on localhost:3000 and stay refreshed automatically.
<a id="serve-and-sync"></a>
If you are only going to work on a specific part of the docs, such as the dev guide, then you can use one of the more specific gulp tasks to only watch those parts of the file system:
* `gulp serve-and-sync` : watch all the local Jade/Sass files, the API source and examples, and the dev guide files
* `gulp serve-and-sync-api` : watch only the API source and example files
* `gulp serve-and-sync-devguide` : watch only the dev guide files
* `gulp build-and-serve` : watch only the local Jade/Sass files
## Code Sample Development
All documentation is supported by sample code and plunkers.
Such code resides in the `public/docs/_examples` directory, under chapter-specific directories, further divided by language track.
For example, the TypeScript QuickStart sample is in `public/docs/_examples/quickstart/ts`.
All samples are in a consistent directory structure using the same styles and the same npm packages, including the latest release of Angular 2.
This consistency is possible in part thanks to gulp-driven tooling.
To run the samples locally and confirm that they work properly,
take the following extra steps to prepare the environment:
1. cd to `public/docs/_examples`
1. install the canonical node packages for all samples by running `npm install`
1. cd back up to `Angular.io` root: `cd ../../..`
1. run `gulp add-example-boilerplate` (elevate to admin on Windows)
to copy canonical files to the sample directories and create symlinks there for node_modules and typings.
Now cd into any particular sample's language directory (e.g., `public/docs/_examples/quickstart/ts`) and try:
- `npm start` to simultaneously compile-with-watch and serve-in-browser-with-watch
- `npm run tsc` to compile only
- `npm run lite` to serve-and-watch in browser
Look at the scripts in `package.json` for other options.
Also, open any `plunkr.no-link.html` to see the code execute in plunker
(you may have to run `gulp build-plunkers` first to create/update).
### Sample end-to-end tests
All samples should be covered to some degree by end-to-end tests:
- `gulp run-e2e-tests` to run all TypeScript and JavaScript tests
- `gulp run-e2e-tests --lang=dart` to run all Dart tests
- `gulp run-e2e-tests --lang=all` to run TypeScript, JavaScript, and Dart tests
- `gulp run-e2e-tests --filter=quickstart` to filter the examples to run, by name
- `gulp run-e2e-tests --fast` to ignore npm install, webdriver update and boilerplate copy
Any combination of options is possible.
## Technology Used
- Angular 1.x: The production ready version of Angular
- Angular Material: An implementation of Material Design in Angular.js
- Gulp: node-based tooling
- Harp: The static web server with built-in preprocessing.
- Sass: A professional grade CSS extension language
- Normalize: A modern, HTML5-ready alternative to CSS resets
- Grids: A highly customizable CSS Grid Framework built with Sass
- Prettify: A JS module and CSS for syntax highlighting of source code snippets.
- Icomoon: Custom built icon fonts
## License
Powered by Google ©2010-2016. Code licensed under an [MIT-style License](https://github.com/angular.io/blob/master/LICENSE). Documentation licensed under [CC BY 4.0](http://creativecommons.org/licenses/by/4.0/).

159
README.md
View File

@ -1,159 +1,28 @@
<h1 align="center">Angular - The modern web developer's platform.</h1> [![CircleCI](https://circleci.com/gh/angular/angular/tree/master.svg?style=shield)](https://circleci.com/gh/angular/workflows/angular/tree/master)
[![BrowserStack Status](https://www.browserstack.com/automate/badge.svg?badge_key=LzF3RzBVVGt6VWE2S0hHaC9uYllOZz09LS1BVjNTclBKV0x4eVRlcjA4QVY1M0N3PT0=--eb4ce8c8dc2c1c5b2b5352d473ee12a73ac20e06)](https://www.browserstack.com/automate/public-build/LzF3RzBVVGt6VWE2S0hHaC9uYllOZz09LS1BVjNTclBKV0x4eVRlcjA4QVY1M0N3PT0=--eb4ce8c8dc2c1c5b2b5352d473ee12a73ac20e06)
[![Join the chat at https://gitter.im/angular/angular](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/angular/angular?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![npm version](https://badge.fury.io/js/%40angular%2Fcore.svg)](https://www.npmjs.com/@angular/core)
<p align="center">
<img src="aio/src/assets/images/logos/angular/angular.png" alt="angular-logo" width="120px" height="120px"/>
<br>
<i>Angular is a development platform for building mobile and desktop web applications
<br> using Typescript/JavaScript and other languages.</i>
<br>
</p>
<p align="center"> # Angular
<a href="https://www.angular.io"><strong>www.angular.io</strong></a>
<br>
</p>
<p align="center"> Angular is a development platform for building mobile and desktop web applications using Typescript/JavaScript and other languages.
<a href="CONTRIBUTING.md">Contributing Guidelines</a>
·
<a href="https://github.com/angular/angular/issues">Submit an Issue</a>
·
<a href="https://blog.angular.io/">Blog</a>
<br>
<br>
</p>
<p align="center">
<a href="https://circleci.com/gh/angular/workflows/angular/tree/master">
<img src="https://img.shields.io/circleci/build/github/angular/angular/master.svg?logo=circleci&logoColor=fff&label=CircleCI" alt="CI status" />
</a>&nbsp;
<a href="https://www.npmjs.com/@angular/core">
<img src="https://img.shields.io/npm/v/@angular/core.svg?logo=npm&logoColor=fff&label=NPM+package&color=limegreen" alt="Angular on npm" />
</a>&nbsp;
<a href="https://discord.gg/angular">
<img src="https://img.shields.io/discord/463752820026376202.svg?logo=discord&logoColor=fff&label=Discord&color=7389d8" alt="Discord conversation" />
</a>
</p>
<hr>
## Documentation
Get started with Angular, learn the fundamentals and explore advanced topics on our documentation website.
- [Getting Started][quickstart]
- [Architecture][architecture]
- [Components and Templates][componentstemplates]
- [Forms][forms]
- [API][api]
### Advanced
- [Angular Elements][angularelements]
- [Server Side Rendering][ssr]
- [Schematics][schematics]
- [Lazy Loading][lazyloading]
## Development Setup
### Prerequisites
- Install [Node.js] which includes [Node Package Manager][npm]
### Setting Up a Project
Install the Angular CLI globally:
```
npm install -g @angular/cli
```
Create workspace:
```
ng new [PROJECT NAME]
```
Run the application:
```
cd [PROJECT NAME]
ng serve
```
Angular is cross-platform, fast, scalable, has incredible tooling, and is loved by millions.
## Quickstart ## Quickstart
[Get started in 5 minutes][quickstart]. [Get started in 5 minutes][quickstart].
## Ecosystem
<p>
<img src="/docs/images/angular-ecosystem-logos.png" alt="angular ecosystem logos" width="500px" height="auto">
</p>
- [Angular Command Line (CLI)][cli]
- [Angular Material][angularmaterial]
## Changelog ## Changelog
[Learn about the latest improvements][changelog]. [Learn about the latest improvements][changelog].
## Upgrading ## Want to help?
Check out our [upgrade guide](https://update.angular.io/) to find out the best way to upgrade your project. Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on our
guidelines for [contributing][contributing] and then check out one of our issues in the [hotlist: community-help](https://github.com/angular/angular/labels/hotlist%3A%20community-help).
## Contributing [browserstack]: https://www.browserstack.com/automate/public-build/LzF3RzBVVGt6VWE2S0hHaC9uYllOZz09LS1BVjNTclBKV0x4eVRlcjA4QVY1M0N3PT0=--eb4ce8c8dc2c1c5b2b5352d473ee12a73ac20e06
[contributing]: https://github.com/angular/angular/blob/master/CONTRIBUTING.md
### Contributing Guidelines [quickstart]: https://angular.io/guide/quickstart
[changelog]: https://github.com/angular/angular/blob/master/CHANGELOG.md
Read through our [contributing guidelines][contributing] to learn about our submission process, coding rules and more.
### Want to Help?
Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on our guidelines for [contributing][contributing] and then check out one of our issues labeled as <kbd>[help wanted](https://github.com/angular/angular/labels/help%20wanted)</kbd> or <kbd>[good first issue](https://github.com/angular/angular/labels/good%20first%20issue)</kbd>.
### Code of Conduct
Help us keep Angular open and inclusive. Please read and follow our [Code of Conduct][codeofconduct].
## Community
Join the conversation and help the community.
- [Twitter][twitter]
- [Discord][discord]
- [Gitter][gitter]
- [YouTube][youtube]
- [StackOverflow][stackoverflow]
- Find a Local [Meetup][meetup]
[![Love Angular badge](https://img.shields.io/badge/angular-love-blue?logo=angular&angular=love)](https://www.github.com/angular/angular)
**Love Angular? Give our repo a star :star: :arrow_up:.**
[contributing]: CONTRIBUTING.md
[quickstart]: https://angular.io/start
[changelog]: CHANGELOG.md
[ng]: https://angular.io [ng]: https://angular.io
[documentation]: https://angular.io/docs
[angularmaterial]: https://material.angular.io/
[cli]: https://cli.angular.io/
[architecture]: https://angular.io/guide/architecture
[componentstemplates]: https://angular.io/guide/displaying-data
[forms]: https://angular.io/guide/forms-overview
[api]: https://angular.io/api
[angularelements]: https://angular.io/guide/elements
[ssr]: https://angular.io/guide/universal
[schematics]: https://angular.io/guide/schematics
[lazyloading]: https://angular.io/guide/lazy-loading-ngmodules
[node.js]: https://nodejs.org/
[npm]: https://www.npmjs.com/get-npm
[codeofconduct]: CODE_OF_CONDUCT.md
[twitter]: https://www.twitter.com/angular
[discord]: https://discord.gg/angular
[gitter]: https://gitter.im/angular/angular
[stackoverflow]: https://stackoverflow.com/questions/tagged/angular
[youtube]: https://youtube.com/angular
[meetup]: https://www.meetup.com/find/?keywords=angular"

115
WORKSPACE
View File

@ -1,64 +1,107 @@
workspace( workspace(name = "angular")
name = "angular",
managed_directories = {"@npm": ["node_modules"]},
)
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
# Uncomment for local bazel rules development
#local_repository(
# name = "build_bazel_rules_nodejs",
# path = "../rules_nodejs",
#)
#local_repository(
# name = "build_bazel_rules_typescript",
# path = "../rules_typescript",
#)
# Fetch rules_nodejs so we can install our npm dependencies # Fetch rules_nodejs so we can install our npm dependencies
http_archive( http_archive(
name = "build_bazel_rules_nodejs", name = "build_bazel_rules_nodejs",
sha256 = "4681ca88d512d57196d064d1441549080d8d17d119174a1229d1717a16a4a489", sha256 = "1416d03823fed624b49a0abbd9979f7c63bbedfd37890ddecedd2fe25cccebc6",
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/4.0.0-beta.1/rules_nodejs-4.0.0-beta.1.tar.gz"], urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/0.18.6/rules_nodejs-0.18.6.tar.gz"],
) )
# Check the rules_nodejs version and download npm dependencies # Fetch the rxjs repository since we build rxjs from source
# Note: bazel (version 2 and after) will check the .bazelversion file so we don't need to # TODO(gregmagolan): use rxjs bundles in the bazel build
# assert on that. http_archive(
load("@build_bazel_rules_nodejs//:index.bzl", "check_rules_nodejs_version", "node_repositories", "yarn_install") name = "rxjs",
sha256 = "72b0b4e517f43358f554c125e40e39f67688cd2738a8998b4a266981ed32f403",
strip_prefix = "package/src",
url = "https://registry.yarnpkg.com/rxjs/-/rxjs-6.3.3.tgz",
)
check_rules_nodejs_version(minimum_version_string = "2.2.0") # Use a mock @npm repository while we are building angular from source
# downstream. Angular will get its npm dependencies with in @ngdeps which
# is setup in ng_setup_workspace().
# TODO(gregmagolan): remove @ngdeps once angular is no longer build from source
# downstream and have build use @npm for npm dependencies
local_repository(
name = "npm",
path = "tools/npm_workspace",
)
# Check the bazel version and download npm dependencies
load("@build_bazel_rules_nodejs//:defs.bzl", "check_bazel_version", "node_repositories")
# Bazel version must be at least v0.21.0 because:
# - 0.21.0 Using --incompatible_strict_action_env flag fixes cache when running `yarn bazel`
# (see https://github.com/angular/angular/issues/27514#issuecomment-451438271)
check_bazel_version("0.21.0", """
You no longer need to install Bazel on your machine.
Angular has a dependency on the @bazel/bazel package which supplies it.
Try running `yarn bazel` instead.
(If you did run that, check that you've got a fresh `yarn install`)
""")
# Setup the Node.js toolchain # Setup the Node.js toolchain
node_repositories( node_repositories(
node_version = "14.16.1", node_version = "10.9.0",
package_json = ["//:package.json"], package_json = ["//:package.json"],
preserve_symlinks = True,
yarn_version = "1.12.1",
) )
load("//integration:angular_integration_test.bzl", "npm_package_archives") # Setup the angular toolchain which installs npm dependencies into @ngdeps
load("//tools:ng_setup_workspace.bzl", "ng_setup_workspace")
yarn_install( ng_setup_workspace()
name = "npm",
manual_build_file_contents = npm_package_archives(),
package_json = "//:package.json",
yarn_lock = "//:yarn.lock",
)
# Load protractor dependencies # Install all bazel dependencies of the @ngdeps npm packages
load("@npm//@bazel/protractor:package.bzl", "npm_bazel_protractor_dependencies") load("@ngdeps//:install_bazel_dependencies.bzl", "install_bazel_dependencies")
npm_bazel_protractor_dependencies() install_bazel_dependencies()
# Load angular dependencies
load("//packages/bazel:package.bzl", "rules_angular_dev_dependencies")
rules_angular_dev_dependencies()
# Load karma dependencies
load("@build_bazel_rules_karma//:package.bzl", "rules_karma_dependencies")
rules_karma_dependencies()
# Setup the rules_webtesting toolchain # Setup the rules_webtesting toolchain
load("@io_bazel_rules_webtesting//web:repositories.bzl", "web_test_repositories") load("@io_bazel_rules_webtesting//web:repositories.bzl", "web_test_repositories")
web_test_repositories() web_test_repositories()
load("//dev-infra/bazel/browsers:browser_repositories.bzl", "browser_repositories") # Temporary work-around for https://github.com/angular/angular/issues/28681
# TODO(gregmagolan): go back to @io_bazel_rules_webtesting browser_repositories
load("@angular//:browser_repositories.bzl", "browser_repositories")
browser_repositories() browser_repositories()
load("//packages/common/locales/generate-locales-tool:cldr-data.bzl", "cldr_data_repository") # Setup the rules_typescript tooolchain
load("@build_bazel_rules_typescript//:defs.bzl", "ts_setup_workspace")
cldr_data_repository( ts_setup_workspace()
name = "cldr_data",
# Since we use the Github archives for CLDR 37, we need to specify a path # Setup the rules_sass toolchain
# to the available locales. This wouldn't be needed with CLDR 39 as that load("@io_bazel_rules_sass//sass:sass_repositories.bzl", "sass_repositories")
# comes with an official JSON archive not containing a version suffix.
available_locales_path = "cldr-core-37.0.0/availableLocales.json", sass_repositories()
urls = {
"https://github.com/unicode-cldr/cldr-core/archive/37.0.0.zip": "32b5c49c3874aa342b90412c207b42e7aefb2435295891fb714c34ce58b3c706", # Setup the skydoc toolchain
"https://github.com/unicode-cldr/cldr-dates-full/archive/37.0.0.zip": "e1c410dd8ad7d75df4a5393efaf5d28f0d56c0fa126c5d66e171a3f21a988a1e", load("@io_bazel_skydoc//skylark:skylark.bzl", "skydoc_repositories")
"https://github.com/unicode-cldr/cldr-numbers-full/archive/37.0.0.zip": "a921b90cf7f436e63fbdd55880f96e39a203acd9e174b0ceafa20a02c242a12e",
}, skydoc_repositories()
)

View File

@ -1,12 +0,0 @@
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# Googlebot uses an older version of Chrome
# For additional information see: https://developers.google.com/search/docs/guides/rendering
> 0.5%
last 2 major versions
Firefox ESR
not dead
IE 11

View File

@ -1,91 +0,0 @@
{
"root": true,
"overrides": [
{
"files": [
"*.ts"
],
"parserOptions": {
"project": [
"tsconfig.json",
"tests/e2e/tsconfig.json"
],
"createDefaultProgram": true
},
"extends": [
"plugin:@angular-eslint/ng-cli-compat",
"plugin:@angular-eslint/ng-cli-compat--formatting-add-on",
"plugin:@angular-eslint/template/process-inline-templates"
],
"rules": {
"@typescript-eslint/ban-types": "error",
"@angular-eslint/component-selector": [
"error",
{
"type": "element",
"prefix": "aio",
"style": "kebab-case"
}
],
"@angular-eslint/directive-selector": [
"error",
{
"type": "attribute",
"prefix": "aio",
"style": "camelCase"
}
],
"dot-notation": "error",
"indent": "off",
"@typescript-eslint/member-delimiter-style": ["error", {
"singleline": {
"delimiter": "comma",
"requireLast": false
}
}],
"@typescript-eslint/member-ordering": "off",
"@typescript-eslint/naming-convention": "off",
"no-console": ["error", {"allow": ["log", "warn", "error"]}],
"no-empty-function": "off",
"no-restricted-syntax": [
"error",
{
"selector": "CallExpression[callee.name=/^(fdescribe|fit)$/]",
"message": "Don't keep jasmine focus methods."
}
],
"no-shadow": "off",
"@typescript-eslint/no-shadow": ["error"],
"no-tabs": "error",
"no-underscore-dangle": "off",
"no-unused-expressions": "error",
"no-use-before-define": "off",
"prefer-arrow/prefer-arrow-functions": "off",
"quotes": "off",
"@typescript-eslint/quotes": ["error", "single", {"avoidEscape": true}],
"semi": "error"
}
},
{
"files": [
"*.html"
],
"extends": [
"plugin:@angular-eslint/template/recommended"
],
"rules": {
"@angular-eslint/template/accessibility-alt-text": "error",
"@angular-eslint/template/accessibility-elements-content": "error",
"@angular-eslint/template/accessibility-label-has-associated-control": "error",
"@angular-eslint/template/accessibility-table-scope": "error",
"@angular-eslint/template/accessibility-valid-aria": "error",
"@angular-eslint/template/click-events-have-key-events": "error",
"@angular-eslint/template/eqeqeq": "off",
"@angular-eslint/template/mouse-events-have-key-events": "error",
"@angular-eslint/template/no-autofocus": "error",
"@angular-eslint/template/no-distracting-elements": "error",
"@angular-eslint/template/no-positive-tabindex": "error"
}
}
]
}

4
aio/.gitignore vendored
View File

@ -1,4 +1,4 @@
# See https://help.github.com/ignore-files/ for more about ignoring files. # See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output # compiled output
/dist /dist
@ -26,13 +26,11 @@
!.vscode/extensions.json !.vscode/extensions.json
# misc # misc
/.firebase/
/.sass-cache /.sass-cache
/connect.lock /connect.lock
/coverage /coverage
/libpeerconnection.log /libpeerconnection.log
debug.log debug.log
firebase-debug.log
npm-debug.log npm-debug.log
testem.log testem.log
/typings /typings

View File

@ -1 +0,0 @@
engine-strict = true

View File

@ -6,7 +6,6 @@ Everything in this folder is part of the documentation project. This includes
* the dgeni configuration for converting source files to rendered files that can be viewed in the web site. * the dgeni configuration for converting source files to rendered files that can be viewed in the web site.
* the tooling for setting up examples for development; and generating live-example and zip files from the examples. * the tooling for setting up examples for development; and generating live-example and zip files from the examples.
<a name="developer-tasks"></a>
## Developer tasks ## Developer tasks
We use [Yarn](https://yarnpkg.com) to manage the dependencies and to run build tasks. We use [Yarn](https://yarnpkg.com) to manage the dependencies and to run build tasks.
@ -15,10 +14,11 @@ Here are the most important tasks you might need to use:
* `yarn` - install all the dependencies. * `yarn` - install all the dependencies.
* `yarn setup` - install all the dependencies, boilerplate, stackblitz, zips and run dgeni on the docs. * `yarn setup` - install all the dependencies, boilerplate, stackblitz, zips and run dgeni on the docs.
* `yarn setup-local` - same as `setup`, but build the Angular packages from the source code and use these locally built versions (instead of the ones fetched from npm) for aio and docs examples boilerplate. * `yarn setup-local` - same as `setup`, but use the locally built Angular packages for aio and docs examples boilerplate.
* `yarn build` - create a production build of the application (after installing dependencies, boilerplate, etc). * `yarn build` - create a production build of the application (after installing dependencies, boilerplate, etc).
* `yarn build-local` - same as `build`, but use `setup-local` instead of `setup`. * `yarn build-local` - same as `build`, but use `setup-local` instead of `setup`.
* `yarn start` - run a development web server that watches the files; then builds the doc-viewer and reloads the page, as necessary. * `yarn start` - run a development web server that watches the files; then builds the doc-viewer and reloads the page, as necessary.
* `yarn serve-and-sync` - run both the `docs-watch` and `start` in the same console. * `yarn serve-and-sync` - run both the `docs-watch` and `start` in the same console.
* `yarn lint` - check that the doc-viewer code follows our style rules. * `yarn lint` - check that the doc-viewer code follows our style rules.
@ -31,28 +31,26 @@ Here are the most important tasks you might need to use:
* `yarn docs-lint` - check that the doc gen code follows our style rules. * `yarn docs-lint` - check that the doc gen code follows our style rules.
* `yarn docs-test` - run the unit tests for the doc generation code. * `yarn docs-test` - run the unit tests for the doc generation code.
* `yarn boilerplate:add` - generate all the boilerplate code for the examples, so that they can be run locally. * `yarn boilerplate:add` - generate all the boilerplate code for the examples, so that they can be run locally. Add the option `--local` to use your local version of Angular contained in the "dist" folder.
* `yarn boilerplate:remove` - remove all the boilerplate code that was added via `yarn boilerplate:add`. * `yarn boilerplate:remove` - remove all the boilerplate code that was added via `yarn boilerplate:add`.
* `yarn create-example` - create a new example directory containing initial source files.
* `yarn generate-stackblitz` - generate the stackblitz files that are used by the `live-example` tags in the docs. * `yarn generate-stackblitz` - generate the stackblitz files that are used by the `live-example` tags in the docs.
* `yarn generate-zips` - generate the zip files from the examples. Zip available via the `live-example` tags in the docs. * `yarn generate-zips` - generate the zip files from the examples. Zip available via the `live-example` tags in the docs.
* `yarn example-e2e` - run all e2e tests for examples. Available options: * `yarn example-e2e` - run all e2e tests for examples
- `--setup`: generate boilerplate, force webdriver update & other setup, then run tests. - `yarn example-e2e --setup` - force webdriver update & other setup, then run tests
- `--local`: run e2e tests with the local version of Angular contained in the "dist" folder. - `yarn example-e2e --filter=foo` - limit e2e tests to those containing the word "foo"
_Requires `--setup` in order to take effect._ - `yarn example-e2e --setup --local` - run e2e tests with the local version of Angular contained in the "dist" folder
- `--filter=foo`: limit e2e tests to those containing the word "foo".
> **Note for Windows users** ## Developing on Windows
> The `packages/` directory may contain Linux-specific symlinks, which are not recognized by Windows.
> Setting up the examples involves creating some [symbolic links](https://en.wikipedia.org/wiki/Symbolic_link) (see [here](./tools/examples/README.md#symlinked-node_modules) for details). On Windows, this requires to either have [Developer Mode enabled](https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10) (supported on Windows 10 or newer) or run the setup commands as administrator. These unresolved links cause the docs generation process to fail because it cannot locate certain files.
>
> The affected commands are: > Hint: The following steps require administration rights or [Windows Developer Mode](https://docs.microsoft.com/en-us/windows/uwp/get-started/enable-your-device-for-development) enabled!
> - `yarn setup` / `yarn setup-*`
> - `yarn build` / `yarn build-*` To fix this problem, run `scripts/windows/create-symlinks.sh`. This command creates temporary files where the symlinks used to be. Make sure not to commit those files with your documentation changes.
> - `yarn boilerplate:add` When you are done making and testing your documentation changes, you can restore the original symlinks and delete the temporary files by running `scripts/windows/remove-symlinks.sh`.
> - `yarn example-e2e --setup`
It's necessary to remove the temporary files, because otherwise they're displayed as local changes in your git working copy and certain operations are blocked.
## Using ServiceWorker locally ## Using ServiceWorker locally
@ -101,7 +99,7 @@ You also want to see those changes displayed properly in the doc viewer
with a quick, edit/view cycle time. with a quick, edit/view cycle time.
For this purpose, use the `yarn docs-watch` task, which watches for changes to source files and only For this purpose, use the `yarn docs-watch` task, which watches for changes to source files and only
re-processes the files necessary to generate the docs that are related to the file that has changed. re-processes the the files necessary to generate the docs that are related to the file that has changed.
Since this task takes shortcuts, it is much faster (often less than 1 second) but it won't produce full Since this task takes shortcuts, it is much faster (often less than 1 second) but it won't produce full
fidelity content. For example, links to other docs and code examples may not render correctly. This is fidelity content. For example, links to other docs and code examples may not render correctly. This is
most particularly noticed in links to other docs and in the embedded examples, which may not always render most particularly noticed in links to other docs and in the embedded examples, which may not always render

View File

@ -1,5 +1,5 @@
# Image metadata and config # Image metadata and config
FROM debian:buster FROM debian:jessie
LABEL name="angular.io PR preview" \ LABEL name="angular.io PR preview" \
description="This image implements the PR preview functionality for angular.io." \ description="This image implements the PR preview functionality for angular.io." \
@ -26,8 +26,8 @@ ARG AIO_GITHUB_ORGANIZATION=angular
ARG TEST_AIO_GITHUB_ORGANIZATION=test-org ARG TEST_AIO_GITHUB_ORGANIZATION=test-org
ARG AIO_GITHUB_REPO=angular ARG AIO_GITHUB_REPO=angular
ARG TEST_AIO_GITHUB_REPO=test-repo ARG TEST_AIO_GITHUB_REPO=test-repo
ARG AIO_GITHUB_TEAM_SLUGS=aio-auto-previews,aio-contributors ARG AIO_GITHUB_TEAM_SLUGS=aio-contributors
ARG TEST_AIO_GITHUB_TEAM_SLUGS=test-team-1,test-team-2 ARG TEST_AIO_GITHUB_TEAM_SLUGS=aio-contributors
ARG AIO_NGINX_HOSTNAME=$AIO_DOMAIN_NAME ARG AIO_NGINX_HOSTNAME=$AIO_DOMAIN_NAME
ARG TEST_AIO_NGINX_HOSTNAME=$TEST_AIO_DOMAIN_NAME ARG TEST_AIO_NGINX_HOSTNAME=$TEST_AIO_DOMAIN_NAME
ARG AIO_NGINX_PORT_HTTP=80 ARG AIO_NGINX_PORT_HTTP=80
@ -37,10 +37,10 @@ ARG TEST_AIO_NGINX_PORT_HTTPS=4433
ARG AIO_SIGNIFICANT_FILES_PATTERN='^(?:aio|packages)/(?!.*[._]spec\\.[jt]s$)' ARG AIO_SIGNIFICANT_FILES_PATTERN='^(?:aio|packages)/(?!.*[._]spec\\.[jt]s$)'
ARG TEST_AIO_SIGNIFICANT_FILES_PATTERN=$AIO_SIGNIFICANT_FILES_PATTERN ARG TEST_AIO_SIGNIFICANT_FILES_PATTERN=$AIO_SIGNIFICANT_FILES_PATTERN
ARG AIO_TRUSTED_PR_LABEL="aio: preview" ARG AIO_TRUSTED_PR_LABEL="aio: preview"
ARG TEST_AIO_TRUSTED_PR_LABEL=$AIO_TRUSTED_PR_LABEL ARG TEST_AIO_TRUSTED_PR_LABEL="aio: preview"
ARG AIO_PREVIEW_SERVER_HOSTNAME=preview.localhost ARG AIO_PREVIEW_SERVER_HOSTNAME=preview.localhost
ARG TEST_AIO_PREVIEW_SERVER_HOSTNAME=$AIO_PREVIEW_SERVER_HOSTNAME ARG TEST_AIO_PREVIEW_SERVER_HOSTNAME=preview.localhost
ARG AIO_ARTIFACT_MAX_SIZE=26214400 ARG AIO_ARTIFACT_MAX_SIZE=20971520
ARG TEST_AIO_ARTIFACT_MAX_SIZE=200 ARG TEST_AIO_ARTIFACT_MAX_SIZE=200
ARG AIO_PREVIEW_SERVER_PORT=3000 ARG AIO_PREVIEW_SERVER_PORT=3000
ARG TEST_AIO_PREVIEW_SERVER_PORT=3001 ARG TEST_AIO_PREVIEW_SERVER_PORT=3001
@ -72,29 +72,25 @@ RUN mkdir /var/log/aio
# Add extra package sources # Add extra package sources
RUN apt-get update -y && apt-get install -y curl=7.64.0-4+deb10u1 RUN apt-get update -y && apt-get install -y curl
RUN curl --silent --show-error --location https://deb.nodesource.com/setup_12.x | bash - RUN curl --silent --show-error --location https://deb.nodesource.com/setup_10.x | bash -
RUN curl --silent --show-error https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - RUN curl --silent --show-error https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
RUN echo "deb http://ftp.debian.org/debian jessie-backports main" | tee /etc/apt/sources.list.d/backports.list
# Install packages # Install packages
# NOTE: Some packages (such as `nginx`, `nodejs`, `openssl`) make older versions unavailable on the
# repositories, so we cannot pin to specific versions for these packages :(
# See for example:
# - https://github.com/nodesource/distributions/issues/33
# - https://askubuntu.com/questions/715104/how-can-i-downgrade-openssl-via-apt-get
RUN apt-get update -y && apt-get install -y \ RUN apt-get update -y && apt-get install -y \
cron=3.0pl1-134+deb10u1 \ chkconfig \
dnsmasq=2.80-1 \ cron \
nano=3.2-3 \ dnsmasq \
nginx \ nano \
nodejs \ nodejs \
openssl \ openssl \
rsyslog=8.1901.0-1 \ rsyslog \
vim=2:8.1.0875-5 \ yarn
yarn=1.22.4-1 RUN apt-get install -t jessie-backports -y nginx
RUN yarn global add pm2@4.4.0 RUN yarn global add pm2@2
# Set up log rotation # Set up log rotation
@ -155,7 +151,8 @@ RUN sed -i "s|{{\$AIO_PREVIEW_SERVER_PORT}}|$TEST_AIO_PREVIEW_SERVER_PORT|g" /et
# Set up pm2 # Set up pm2
RUN pm2 startup --user root > /dev/null RUN pm2 startup systemv -u root > /dev/null
RUN chkconfig pm2-root on
# Set up the shell scripts # Set up the shell scripts
@ -167,7 +164,8 @@ RUN find $AIO_SCRIPTS_SH_DIR -maxdepth 1 -type f -printf "%P\n" \
# Set up the Node.js scripts # Set up the Node.js scripts
COPY scripts-js/ $AIO_SCRIPTS_JS_DIR/ COPY scripts-js/ $AIO_SCRIPTS_JS_DIR/
RUN yarn --cwd="$AIO_SCRIPTS_JS_DIR/" install --production --frozen-lockfile WORKDIR $AIO_SCRIPTS_JS_DIR/
RUN yarn install --production --frozen-lockfile
# Set up health check # Set up health check

View File

@ -35,7 +35,6 @@ export class BuildCleaner {
]); ]);
} catch (error) { } catch (error) {
this.logger.error('ERROR:', error); this.logger.error('ERROR:', error);
throw error;
} }
} }

View File

@ -57,7 +57,7 @@ export class GithubTeams {
/** /**
* Check whether the given username is a member of the teams specified by the team slugs. * Check whether the given username is a member of the teams specified by the team slugs.
* @param username The username to check for in the teams. * @param username The username to check for in the teams.
* @param teamSlugs A collection of slugs that represent the teams to check for the username. * @param teamSlugs A collection of slugs that represent the teams to check for the the username.
* @returns a Promise that resolves to `true` if the usernane is a member of at least one of the specified teams. * @returns a Promise that resolves to `true` if the usernane is a member of at least one of the specified teams.
*/ */
public async isMemberBySlug(username: string, teamSlugs: string[]): Promise<boolean> { public async isMemberBySlug(username: string, teamSlugs: string[]): Promise<boolean> {

View File

@ -2,17 +2,17 @@
import * as bodyParser from 'body-parser'; import * as bodyParser from 'body-parser';
import * as express from 'express'; import * as express from 'express';
import * as http from 'http'; import * as http from 'http';
import {AddressInfo} from 'net'; import { AddressInfo } from 'net';
import {CircleCiApi} from '../common/circle-ci-api'; import { CircleCiApi } from '../common/circle-ci-api';
import {GithubApi} from '../common/github-api'; import { GithubApi } from '../common/github-api';
import {GithubPullRequests} from '../common/github-pull-requests'; import { GithubPullRequests } from '../common/github-pull-requests';
import {GithubTeams} from '../common/github-teams'; import { GithubTeams } from '../common/github-teams';
import {assert, assertNotMissingOrEmpty, computeShortSha, Logger} from '../common/utils'; import { assert, assertNotMissingOrEmpty, computeShortSha, Logger } from '../common/utils';
import {BuildCreator} from './build-creator'; import { BuildCreator } from './build-creator';
import {ChangedPrVisibilityEvent, CreatedBuildEvent} from './build-events'; import { ChangedPrVisibilityEvent, CreatedBuildEvent } from './build-events';
import {BuildRetriever} from './build-retriever'; import { BuildRetriever } from './build-retriever';
import {BuildVerifier} from './build-verifier'; import { BuildVerifier } from './build-verifier';
import {respondWithError, throwRequestError} from './utils'; import { respondWithError, throwRequestError } from './utils';
const AIO_PREVIEW_JOB = 'aio_preview'; const AIO_PREVIEW_JOB = 'aio_preview';

View File

@ -1,4 +1,5 @@
import * as express from 'express'; import * as express from 'express';
import {promisify} from 'util';
import {PreviewServerError} from './preview-error'; import {PreviewServerError} from './preview-error';
/** /**
@ -12,7 +13,7 @@ export async function respondWithError(res: express.Response, err: any): Promise
} }
res.status(err.status); res.status(err.status);
return new Promise(resolve => res.end(err.message, resolve)); await promisify(res.end.bind(res))(err.message);
} }
/** /**

View File

@ -1,6 +1,7 @@
// Imports // Imports
import * as cp from 'child_process'; import * as cp from 'child_process';
import * as fs from 'fs'; import * as fs from 'fs';
import * as http from 'http';
import * as path from 'path'; import * as path from 'path';
import * as shell from 'shelljs'; import * as shell from 'shelljs';
import {AIO_DOWNLOADS_DIR, HIDDEN_DIR_PREFIX} from '../common/constants'; import {AIO_DOWNLOADS_DIR, HIDDEN_DIR_PREFIX} from '../common/constants';
@ -93,7 +94,7 @@ class Helper {
return fs.readFileSync(absFilePath, 'utf8'); return fs.readFileSync(absFilePath, 'utf8');
} }
public runCmd(cmd: string, opts: cp.ExecOptions = {}): Promise<CmdResult> { public runCmd(cmd: string, opts: cp.ExecFileOptions = {}): Promise<CmdResult> {
return new Promise(resolve => { return new Promise(resolve => {
const proc = cp.exec(cmd, opts, (err, stdout, stderr) => resolve({success: !err, err, stdout, stderr})); const proc = cp.exec(cmd, opts, (err, stdout, stderr) => resolve({success: !err, err, stdout, stderr}));
this.createCleanUpFn(() => proc.kill()); this.createCleanUpFn(() => proc.kill());
@ -101,10 +102,21 @@ class Helper {
} }
public runForAllSupportedSchemes(suiteFactory: TestSuiteFactory): void { public runForAllSupportedSchemes(suiteFactory: TestSuiteFactory): void {
Object.entries(this.portPerScheme).forEach(([scheme, port]) => suiteFactory(scheme, port)); Object.keys(this.portPerScheme).forEach(scheme => suiteFactory(scheme, this.portPerScheme[scheme]));
} }
public verifyResponse(status: number, regex: string | RegExp = /^/): VerifyCmdResultFn { public verifyResponse(status: number | [number, string], regex: string | RegExp = /^/): VerifyCmdResultFn {
let statusCode: number;
let statusText: string;
if (Array.isArray(status)) {
statusCode = status[0];
statusText = status[1];
} else {
statusCode = status;
statusText = http.STATUS_CODES[statusCode] || 'UNKNOWN_STATUS_CODE';
}
return (result: CmdResult) => { return (result: CmdResult) => {
const [headers, body] = result.stdout. const [headers, body] = result.stdout.
split(/(?:\r?\n){2,}/). split(/(?:\r?\n){2,}/).
@ -119,7 +131,7 @@ class Helper {
} }
expect(result.success).toBe(true); expect(result.success).toBe(true);
expect(headers).toMatch(new RegExp(`HTTP/(?:1\\.1|2) ${status} `)); expect(headers).toContain(`${statusCode} ${statusText}`);
expect(body).toMatch(regex); expect(body).toMatch(regex);
}; };
} }

View File

@ -16,6 +16,13 @@ import {BuildNums, PrNums, SHA} from './constants';
const logger = new Logger('mock-external-apis'); const logger = new Logger('mock-external-apis');
const log = (...args: any[]) => {
// Filter out non-matching URL checks
if (!/^matching.+: false$/.test(args[0])) {
logger.log(...args);
}
};
const AIO_CIRCLE_CI_TOKEN = getEnvVar('AIO_CIRCLE_CI_TOKEN'); const AIO_CIRCLE_CI_TOKEN = getEnvVar('AIO_CIRCLE_CI_TOKEN');
const AIO_GITHUB_TOKEN = getEnvVar('AIO_GITHUB_TOKEN'); const AIO_GITHUB_TOKEN = getEnvVar('AIO_GITHUB_TOKEN');
@ -84,8 +91,8 @@ const createArchive = (buildNum: number, prNum: number, sha: string) => {
}; };
// Create request scopes // Create request scopes
const circleCiApi = nock(CIRCLE_CI_API_HOST).persist(); const circleCiApi = nock(CIRCLE_CI_API_HOST).log(log).persist();
const githubApi = nock(GITHUB_API_HOST).persist().matchHeader('Authorization', `token ${AIO_GITHUB_TOKEN}`); const githubApi = nock(GITHUB_API_HOST).log(log).persist().matchHeader('Authorization', `token ${AIO_GITHUB_TOKEN}`);
////////////////////////////// //////////////////////////////

View File

@ -15,7 +15,7 @@ describe(`nginx`, () => {
afterEach(() => h.cleanUp()); afterEach(() => h.cleanUp());
it('should redirect HTTP to HTTPS', async () => { it('should redirect HTTP to HTTPS', done => {
const httpHost = `${AIO_NGINX_HOSTNAME}:${AIO_NGINX_PORT_HTTP}`; const httpHost = `${AIO_NGINX_HOSTNAME}:${AIO_NGINX_PORT_HTTP}`;
const httpsHost = `${AIO_NGINX_HOSTNAME}:${AIO_NGINX_PORT_HTTPS}`; const httpsHost = `${AIO_NGINX_HOSTNAME}:${AIO_NGINX_PORT_HTTPS}`;
const urlMap = { const urlMap = {
@ -24,15 +24,16 @@ describe(`nginx`, () => {
[`http://foo.${httpHost}/`]: `https://foo.${httpsHost}/`, [`http://foo.${httpHost}/`]: `https://foo.${httpsHost}/`,
}; };
const verifyRedirection = async (fromUrl: string, toUrl: string) => { const verifyRedirection = (httpUrl: string) => h.runCmd(`curl -i ${httpUrl}`).then(result => {
const result = await h.runCmd(`curl -i ${fromUrl}`);
h.verifyResponse(307)(result); h.verifyResponse(307)(result);
const headers = result.stdout.split(/(?:\r?\n){2,}/)[0]; const headers = result.stdout.split(/(?:\r?\n){2,}/)[0];
expect(headers).toContain(`Location: ${toUrl}`); expect(headers).toContain(`Location: ${urlMap[httpUrl]}`);
}; });
await Promise.all(Object.entries(urlMap).map(urls => verifyRedirection(...urls))); Promise.
all(Object.keys(urlMap).map(verifyRedirection)).
then(done);
}); });
@ -61,15 +62,15 @@ describe(`nginx`, () => {
}); });
it('should return /index.html', async () => { it('should return /index.html', done => {
const origin = `${scheme}://pr${pr}-${shortSha9}.${host}`; const origin = `${scheme}://pr${pr}-${shortSha9}.${host}`;
const bodyRegex = new RegExp(`^PR: ${pr} | SHA: ${sha9} | File: /index\\.html$`); const bodyRegex = new RegExp(`^PR: ${pr} | SHA: ${sha9} | File: /index\\.html$`);
await Promise.all([ Promise.all([
h.runCmd(`curl -iL ${origin}/index.html`).then(h.verifyResponse(200, bodyRegex)), h.runCmd(`curl -iL ${origin}/index.html`).then(h.verifyResponse(200, bodyRegex)),
h.runCmd(`curl -iL ${origin}/`).then(h.verifyResponse(200, bodyRegex)), h.runCmd(`curl -iL ${origin}/`).then(h.verifyResponse(200, bodyRegex)),
h.runCmd(`curl -iL ${origin}`).then(h.verifyResponse(200, bodyRegex)), h.runCmd(`curl -iL ${origin}`).then(h.verifyResponse(200, bodyRegex)),
]); ]).then(done);
}); });
@ -89,11 +90,12 @@ describe(`nginx`, () => {
}); });
it('should return /foo/bar.js', async () => { it('should return /foo/bar.js', done => {
const bodyRegex = new RegExp(`^PR: ${pr} | SHA: ${sha9} | File: /foo/bar\\.js$`); const bodyRegex = new RegExp(`^PR: ${pr} | SHA: ${sha9} | File: /foo/bar\\.js$`);
await h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}.${host}/foo/bar.js`). h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}.${host}/foo/bar.js`).
then(h.verifyResponse(200, bodyRegex)); then(h.verifyResponse(200, bodyRegex)).
then(done);
}); });
@ -109,46 +111,47 @@ describe(`nginx`, () => {
}); });
it('should respond with 403 for directories', async () => { it('should respond with 403 for directories', done => {
await Promise.all([ Promise.all([
h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}.${host}/foo/`).then(h.verifyResponse(403)), h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}.${host}/foo/`).then(h.verifyResponse(403)),
h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}.${host}/foo`).then(h.verifyResponse(403)), h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}.${host}/foo`).then(h.verifyResponse(403)),
]); ]).then(done);
}); });
it('should respond with 404 for unknown paths to files', async () => { it('should respond with 404 for unknown paths to files', done => {
await h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}.${host}/foo/baz.css`). h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}.${host}/foo/baz.css`).
then(h.verifyResponse(404)); then(h.verifyResponse(404)).
then(done);
}); });
it('should rewrite to \'index.html\' for unknown paths that don\'t look like files', async () => { it('should rewrite to \'index.html\' for unknown paths that don\'t look like files', done => {
const origin = `${scheme}://pr${pr}-${shortSha9}.${host}`; const origin = `${scheme}://pr${pr}-${shortSha9}.${host}`;
const bodyRegex = new RegExp(`^PR: ${pr} | SHA: ${sha9} | File: /index\\.html$`); const bodyRegex = new RegExp(`^PR: ${pr} | SHA: ${sha9} | File: /index\\.html$`);
await Promise.all([ Promise.all([
h.runCmd(`curl -iL ${origin}/foo/baz`).then(h.verifyResponse(200, bodyRegex)), h.runCmd(`curl -iL ${origin}/foo/baz`).then(h.verifyResponse(200, bodyRegex)),
h.runCmd(`curl -iL ${origin}/foo/baz/`).then(h.verifyResponse(200, bodyRegex)), h.runCmd(`curl -iL ${origin}/foo/baz/`).then(h.verifyResponse(200, bodyRegex)),
]); ]).then(done);
}); });
it('should respond with 404 for unknown PRs/SHAs', async () => { it('should respond with 404 for unknown PRs/SHAs', done => {
const otherPr = 54321; const otherPr = 54321;
const otherShortSha = computeShortSha('8'.repeat(40)); const otherShortSha = computeShortSha('8'.repeat(40));
await Promise.all([ Promise.all([
h.runCmd(`curl -iL ${scheme}://pr${pr}9-${shortSha9}.${host}`).then(h.verifyResponse(404)), h.runCmd(`curl -iL ${scheme}://pr${pr}9-${shortSha9}.${host}`).then(h.verifyResponse(404)),
h.runCmd(`curl -iL ${scheme}://pr${otherPr}-${shortSha9}.${host}`).then(h.verifyResponse(404)), h.runCmd(`curl -iL ${scheme}://pr${otherPr}-${shortSha9}.${host}`).then(h.verifyResponse(404)),
h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}9.${host}`).then(h.verifyResponse(404)), h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}9.${host}`).then(h.verifyResponse(404)),
h.runCmd(`curl -iL ${scheme}://pr${pr}-${otherShortSha}.${host}`).then(h.verifyResponse(404)), h.runCmd(`curl -iL ${scheme}://pr${pr}-${otherShortSha}.${host}`).then(h.verifyResponse(404)),
]); ]).then(done);
}); });
it('should respond with 404 if the subdomain format is wrong', async () => { it('should respond with 404 if the subdomain format is wrong', done => {
await Promise.all([ Promise.all([
h.runCmd(`curl -iL ${scheme}://xpr${pr}-${shortSha9}.${host}`).then(h.verifyResponse(404)), h.runCmd(`curl -iL ${scheme}://xpr${pr}-${shortSha9}.${host}`).then(h.verifyResponse(404)),
h.runCmd(`curl -iL ${scheme}://prx${pr}-${shortSha9}.${host}`).then(h.verifyResponse(404)), h.runCmd(`curl -iL ${scheme}://prx${pr}-${shortSha9}.${host}`).then(h.verifyResponse(404)),
h.runCmd(`curl -iL ${scheme}://xx${pr}-${shortSha9}.${host}`).then(h.verifyResponse(404)), h.runCmd(`curl -iL ${scheme}://xx${pr}-${shortSha9}.${host}`).then(h.verifyResponse(404)),
@ -157,25 +160,26 @@ describe(`nginx`, () => {
h.runCmd(`curl -iL ${scheme}://${pr}-${shortSha9}.${host}`).then(h.verifyResponse(404)), h.runCmd(`curl -iL ${scheme}://${pr}-${shortSha9}.${host}`).then(h.verifyResponse(404)),
h.runCmd(`curl -iL ${scheme}://pr${pr}${shortSha9}.${host}`).then(h.verifyResponse(404)), h.runCmd(`curl -iL ${scheme}://pr${pr}${shortSha9}.${host}`).then(h.verifyResponse(404)),
h.runCmd(`curl -iL ${scheme}://pr${pr}_${shortSha9}.${host}`).then(h.verifyResponse(404)), h.runCmd(`curl -iL ${scheme}://pr${pr}_${shortSha9}.${host}`).then(h.verifyResponse(404)),
]); ]).then(done);
}); });
it('should reject PRs with leading zeros', async () => { it('should reject PRs with leading zeros', done => {
await h.runCmd(`curl -iL ${scheme}://pr0${pr}-${shortSha9}.${host}`). h.runCmd(`curl -iL ${scheme}://pr0${pr}-${shortSha9}.${host}`).
then(h.verifyResponse(404)); then(h.verifyResponse(404)).
then(done);
}); });
it('should accept SHAs with leading zeros (but not trim the zeros)', async () => { it('should accept SHAs with leading zeros (but not trim the zeros)', done => {
const bodyRegex9 = new RegExp(`^PR: ${pr} | SHA: ${sha9} | File: /index\\.html$`); const bodyRegex9 = new RegExp(`^PR: ${pr} | SHA: ${sha9} | File: /index\\.html$`);
const bodyRegex0 = new RegExp(`^PR: ${pr} | SHA: ${sha0} | File: /index\\.html$`); const bodyRegex0 = new RegExp(`^PR: ${pr} | SHA: ${sha0} | File: /index\\.html$`);
await Promise.all([ Promise.all([
h.runCmd(`curl -iL ${scheme}://pr${pr}-0${shortSha9}.${host}`).then(h.verifyResponse(404)), h.runCmd(`curl -iL ${scheme}://pr${pr}-0${shortSha9}.${host}`).then(h.verifyResponse(404)),
h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}.${host}`).then(h.verifyResponse(200, bodyRegex9)), h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha9}.${host}`).then(h.verifyResponse(200, bodyRegex9)),
h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha0}.${host}`).then(h.verifyResponse(200, bodyRegex0)), h.runCmd(`curl -iL ${scheme}://pr${pr}-${shortSha0}.${host}`).then(h.verifyResponse(200, bodyRegex0)),
]); ]).then(done);
}); });
}); });
@ -227,23 +231,23 @@ describe(`nginx`, () => {
describe(`${host}/health-check`, () => { describe(`${host}/health-check`, () => {
it('should respond with 200', async () => { it('should respond with 200', done => {
await Promise.all([ Promise.all([
h.runCmd(`curl -iL ${scheme}://${host}/health-check`).then(h.verifyResponse(200)), h.runCmd(`curl -iL ${scheme}://${host}/health-check`).then(h.verifyResponse(200)),
h.runCmd(`curl -iL ${scheme}://${host}/health-check/`).then(h.verifyResponse(200)), h.runCmd(`curl -iL ${scheme}://${host}/health-check/`).then(h.verifyResponse(200)),
]); ]).then(done);
}); });
it('should respond with 404 if the path does not match exactly', async () => { it('should respond with 404 if the path does not match exactly', done => {
await Promise.all([ Promise.all([
h.runCmd(`curl -iL ${scheme}://${host}/health-check/foo`).then(h.verifyResponse(404)), h.runCmd(`curl -iL ${scheme}://${host}/health-check/foo`).then(h.verifyResponse(404)),
h.runCmd(`curl -iL ${scheme}://${host}/health-check-foo`).then(h.verifyResponse(404)), h.runCmd(`curl -iL ${scheme}://${host}/health-check-foo`).then(h.verifyResponse(404)),
h.runCmd(`curl -iL ${scheme}://${host}/health-checknfoo`).then(h.verifyResponse(404)), h.runCmd(`curl -iL ${scheme}://${host}/health-checknfoo`).then(h.verifyResponse(404)),
h.runCmd(`curl -iL ${scheme}://${host}/foo/health-check`).then(h.verifyResponse(404)), h.runCmd(`curl -iL ${scheme}://${host}/foo/health-check`).then(h.verifyResponse(404)),
h.runCmd(`curl -iL ${scheme}://${host}/foo-health-check`).then(h.verifyResponse(404)), h.runCmd(`curl -iL ${scheme}://${host}/foo-health-check`).then(h.verifyResponse(404)),
h.runCmd(`curl -iL ${scheme}://${host}/foonhealth-check`).then(h.verifyResponse(404)), h.runCmd(`curl -iL ${scheme}://${host}/foonhealth-check`).then(h.verifyResponse(404)),
]); ]).then(done);
}); });
}); });
@ -255,10 +259,10 @@ describe(`nginx`, () => {
it('should disallow non-GET requests', async () => { it('should disallow non-GET requests', async () => {
await Promise.all([ await Promise.all([
h.runCmd(`curl -iLX POST ${baseUrl}/42`).then(h.verifyResponse(405)), h.runCmd(`curl -iLX POST ${baseUrl}/42`).then(h.verifyResponse([405, 'Not Allowed'])),
h.runCmd(`curl -iLX PUT ${baseUrl}/42`).then(h.verifyResponse(405)), h.runCmd(`curl -iLX PUT ${baseUrl}/42`).then(h.verifyResponse([405, 'Not Allowed'])),
h.runCmd(`curl -iLX PATCH ${baseUrl}/42`).then(h.verifyResponse(405)), h.runCmd(`curl -iLX PATCH ${baseUrl}/42`).then(h.verifyResponse([405, 'Not Allowed'])),
h.runCmd(`curl -iLX DELETE ${baseUrl}/42`).then(h.verifyResponse(405)), h.runCmd(`curl -iLX DELETE ${baseUrl}/42`).then(h.verifyResponse([405, 'Not Allowed'])),
]); ]);
}); });
@ -287,28 +291,29 @@ describe(`nginx`, () => {
describe(`${host}/circle-build`, () => { describe(`${host}/circle-build`, () => {
it('should disallow non-POST requests', async () => { it('should disallow non-POST requests', done => {
const url = `${scheme}://${host}/circle-build`; const url = `${scheme}://${host}/circle-build`;
await Promise.all([ Promise.all([
h.runCmd(`curl -iLX GET ${url}`).then(h.verifyResponse(405)), h.runCmd(`curl -iLX GET ${url}`).then(h.verifyResponse([405, 'Not Allowed'])),
h.runCmd(`curl -iLX PUT ${url}`).then(h.verifyResponse(405)), h.runCmd(`curl -iLX PUT ${url}`).then(h.verifyResponse([405, 'Not Allowed'])),
h.runCmd(`curl -iLX PATCH ${url}`).then(h.verifyResponse(405)), h.runCmd(`curl -iLX PATCH ${url}`).then(h.verifyResponse([405, 'Not Allowed'])),
h.runCmd(`curl -iLX DELETE ${url}`).then(h.verifyResponse(405)), h.runCmd(`curl -iLX DELETE ${url}`).then(h.verifyResponse([405, 'Not Allowed'])),
]); ]).then(done);
}); });
it('should pass requests through to the preview server', async () => { it('should pass requests through to the preview server', done => {
await h.runCmd(`curl -iLX POST ${scheme}://${host}/circle-build`). h.runCmd(`curl -iLX POST ${scheme}://${host}/circle-build`).
then(h.verifyResponse(400, /Incorrect body content. Expected JSON/)); then(h.verifyResponse(400, /Incorrect body content. Expected JSON/)).
then(done);
}); });
it('should respond with 404 for unknown paths', async () => { it('should respond with 404 for unknown paths', done => {
const cmdPrefix = `curl -iLX POST ${scheme}://${host}`; const cmdPrefix = `curl -iLX POST ${scheme}://${host}`;
await Promise.all([ Promise.all([
h.runCmd(`${cmdPrefix}/foo/circle-build/`).then(h.verifyResponse(404)), h.runCmd(`${cmdPrefix}/foo/circle-build/`).then(h.verifyResponse(404)),
h.runCmd(`${cmdPrefix}/foo-circle-build/`).then(h.verifyResponse(404)), h.runCmd(`${cmdPrefix}/foo-circle-build/`).then(h.verifyResponse(404)),
h.runCmd(`${cmdPrefix}/fooncircle-build/`).then(h.verifyResponse(404)), h.runCmd(`${cmdPrefix}/fooncircle-build/`).then(h.verifyResponse(404)),
@ -317,7 +322,7 @@ describe(`nginx`, () => {
h.runCmd(`${cmdPrefix}/circle-buildnfoo/`).then(h.verifyResponse(404)), h.runCmd(`${cmdPrefix}/circle-buildnfoo/`).then(h.verifyResponse(404)),
h.runCmd(`${cmdPrefix}/circle-build/pr`).then(h.verifyResponse(404)), h.runCmd(`${cmdPrefix}/circle-build/pr`).then(h.verifyResponse(404)),
h.runCmd(`${cmdPrefix}/circle-build/42`).then(h.verifyResponse(404)), h.runCmd(`${cmdPrefix}/circle-build/42`).then(h.verifyResponse(404)),
]); ]).then(done);
}); });
}); });
@ -327,33 +332,38 @@ describe(`nginx`, () => {
const url = `${scheme}://${host}/pr-updated`; const url = `${scheme}://${host}/pr-updated`;
it('should disallow non-POST requests', async () => { it('should disallow non-POST requests', done => {
await Promise.all([ Promise.all([
h.runCmd(`curl -iLX GET ${url}`).then(h.verifyResponse(405)), h.runCmd(`curl -iLX GET ${url}`).then(h.verifyResponse([405, 'Not Allowed'])),
h.runCmd(`curl -iLX PUT ${url}`).then(h.verifyResponse(405)), h.runCmd(`curl -iLX PUT ${url}`).then(h.verifyResponse([405, 'Not Allowed'])),
h.runCmd(`curl -iLX PATCH ${url}`).then(h.verifyResponse(405)), h.runCmd(`curl -iLX PATCH ${url}`).then(h.verifyResponse([405, 'Not Allowed'])),
h.runCmd(`curl -iLX DELETE ${url}`).then(h.verifyResponse(405)), h.runCmd(`curl -iLX DELETE ${url}`).then(h.verifyResponse([405, 'Not Allowed'])),
]); ]).then(done);
}); });
it('should pass requests through to the preview server', async () => { it('should pass requests through to the preview server', done => {
await h.runCmd(`curl -iLX POST --header "Content-Type: application/json" ${url}`). const cmdPrefix = `curl -iLX POST --header "Content-Type: application/json"`;
then(h.verifyResponse(400, /Missing or empty 'number' field/));
const cmd1 = `${cmdPrefix} ${url}`;
Promise.all([
h.runCmd(cmd1).then(h.verifyResponse(400, /Missing or empty 'number' field/)),
]).then(done);
}); });
it('should respond with 404 for unknown paths', async () => { it('should respond with 404 for unknown paths', done => {
const cmdPrefix = `curl -iLX POST ${scheme}://${host}`; const cmdPrefix = `curl -iLX POST ${scheme}://${host}`;
await Promise.all([ Promise.all([
h.runCmd(`${cmdPrefix}/foo/pr-updated`).then(h.verifyResponse(404)), h.runCmd(`${cmdPrefix}/foo/pr-updated`).then(h.verifyResponse(404)),
h.runCmd(`${cmdPrefix}/foo-pr-updated`).then(h.verifyResponse(404)), h.runCmd(`${cmdPrefix}/foo-pr-updated`).then(h.verifyResponse(404)),
h.runCmd(`${cmdPrefix}/foonpr-updated`).then(h.verifyResponse(404)), h.runCmd(`${cmdPrefix}/foonpr-updated`).then(h.verifyResponse(404)),
h.runCmd(`${cmdPrefix}/pr-updated/foo`).then(h.verifyResponse(404)), h.runCmd(`${cmdPrefix}/pr-updated/foo`).then(h.verifyResponse(404)),
h.runCmd(`${cmdPrefix}/pr-updated-foo`).then(h.verifyResponse(404)), h.runCmd(`${cmdPrefix}/pr-updated-foo`).then(h.verifyResponse(404)),
h.runCmd(`${cmdPrefix}/pr-updatednfoo`).then(h.verifyResponse(404)), h.runCmd(`${cmdPrefix}/pr-updatednfoo`).then(h.verifyResponse(404)),
]); ]).then(done);
}); });
}); });
@ -364,7 +374,7 @@ describe(`nginx`, () => {
beforeEach(() => { beforeEach(() => {
['index.html', 'foo.js', 'foo/index.html'].forEach(relFilePath => { ['index.html', 'foo.js', 'foo/index.html'].forEach(relFilePath => {
const absFilePath = path.join(AIO_BUILDS_DIR, relFilePath); const absFilePath = path.join(AIO_BUILDS_DIR, relFilePath);
h.writeFile(absFilePath, {content: `File: /${relFilePath}`}); return h.writeFile(absFilePath, {content: `File: /${relFilePath}`});
}); });
}); });

View File

@ -105,8 +105,8 @@ describe('preview-server', () => {
describe(`${host}/circle-build`, () => { describe(`${host}/circle-build`, () => {
const curl = makeCurl(`${host}/circle-build`);
const curl = makeCurl(`${host}/circle-build`);
it('should disallow non-POST requests', async () => { it('should disallow non-POST requests', async () => {
const bodyRegex = /^Unknown resource/; const bodyRegex = /^Unknown resource/;
@ -189,7 +189,8 @@ describe('preview-server', () => {
}); });
it('should respond with 201 if a new public build is created', async () => { it('should respond with 201 if a new public build is created', async () => {
await curl(payload(BuildNums.TRUST_CHECK_ACTIVE_TRUSTED_USER)).then(h.verifyResponse(201)); await curl(payload(BuildNums.TRUST_CHECK_ACTIVE_TRUSTED_USER))
.then(h.verifyResponse(201));
expect({ prNum: PrNums.TRUST_CHECK_ACTIVE_TRUSTED_USER }).toExistAsABuild(); expect({ prNum: PrNums.TRUST_CHECK_ACTIVE_TRUSTED_USER }).toExistAsABuild();
}); });
@ -198,7 +199,7 @@ describe('preview-server', () => {
expect({ prNum: PrNums.TRUST_CHECK_UNTRUSTED, isPublic: false }).toExistAsABuild(); expect({ prNum: PrNums.TRUST_CHECK_UNTRUSTED, isPublic: false }).toExistAsABuild();
}); });
[true, false].forEach(isPublic => { [true].forEach(isPublic => {
const build = isPublic ? BuildNums.TRUST_CHECK_ACTIVE_TRUSTED_USER : BuildNums.TRUST_CHECK_UNTRUSTED; const build = isPublic ? BuildNums.TRUST_CHECK_ACTIVE_TRUSTED_USER : BuildNums.TRUST_CHECK_UNTRUSTED;
const prNum = isPublic ? PrNums.TRUST_CHECK_ACTIVE_TRUSTED_USER : PrNums.TRUST_CHECK_UNTRUSTED; const prNum = isPublic ? PrNums.TRUST_CHECK_ACTIVE_TRUSTED_USER : PrNums.TRUST_CHECK_UNTRUSTED;
const label = isPublic ? 'public' : 'non-public'; const label = isPublic ? 'public' : 'non-public';
@ -363,23 +364,23 @@ describe('preview-server', () => {
describe(`${host}/health-check`, () => { describe(`${host}/health-check`, () => {
it('should respond with 200', async () => { it('should respond with 200', done => {
await Promise.all([ Promise.all([
h.runCmd(`curl -iL ${host}/health-check`).then(h.verifyResponse(200)), h.runCmd(`curl -iL ${host}/health-check`).then(h.verifyResponse(200)),
h.runCmd(`curl -iL ${host}/health-check/`).then(h.verifyResponse(200)), h.runCmd(`curl -iL ${host}/health-check/`).then(h.verifyResponse(200)),
]); ]).then(done);
}); });
it('should respond with 404 if the path does not match exactly', async () => { it('should respond with 404 if the path does not match exactly', done => {
await Promise.all([ Promise.all([
h.runCmd(`curl -iL ${host}/health-check/foo`).then(h.verifyResponse(404)), h.runCmd(`curl -iL ${host}/health-check/foo`).then(h.verifyResponse(404)),
h.runCmd(`curl -iL ${host}/health-check-foo`).then(h.verifyResponse(404)), h.runCmd(`curl -iL ${host}/health-check-foo`).then(h.verifyResponse(404)),
h.runCmd(`curl -iL ${host}/health-checknfoo`).then(h.verifyResponse(404)), h.runCmd(`curl -iL ${host}/health-checknfoo`).then(h.verifyResponse(404)),
h.runCmd(`curl -iL ${host}/foo/health-check`).then(h.verifyResponse(404)), h.runCmd(`curl -iL ${host}/foo/health-check`).then(h.verifyResponse(404)),
h.runCmd(`curl -iL ${host}/foo-health-check`).then(h.verifyResponse(404)), h.runCmd(`curl -iL ${host}/foo-health-check`).then(h.verifyResponse(404)),
h.runCmd(`curl -iL ${host}/foonhealth-check`).then(h.verifyResponse(404)), h.runCmd(`curl -iL ${host}/foonhealth-check`).then(h.verifyResponse(404)),
]); ]).then(done);
}); });
}); });
@ -425,18 +426,18 @@ describe('preview-server', () => {
}); });
it('should respond with 404 for unknown paths', async () => { it('should respond with 404 for unknown paths', done => {
const mockPayload = JSON.stringify({number: 1}); // MockExternalApiFlags.TRUST_CHECK_ACTIVE_TRUSTED_USER }); const mockPayload = JSON.stringify({number: 1}); // MockExternalApiFlags.TRUST_CHECK_ACTIVE_TRUSTED_USER });
const cmdPrefix = `curl -iLX POST --data "${mockPayload}" ${host}`; const cmdPrefix = `curl -iLX POST --data "${mockPayload}" ${host}`;
await Promise.all([ Promise.all([
h.runCmd(`${cmdPrefix}/foo/pr-updated`).then(h.verifyResponse(404)), h.runCmd(`${cmdPrefix}/foo/pr-updated`).then(h.verifyResponse(404)),
h.runCmd(`${cmdPrefix}/foo-pr-updated`).then(h.verifyResponse(404)), h.runCmd(`${cmdPrefix}/foo-pr-updated`).then(h.verifyResponse(404)),
h.runCmd(`${cmdPrefix}/foonpr-updated`).then(h.verifyResponse(404)), h.runCmd(`${cmdPrefix}/foonpr-updated`).then(h.verifyResponse(404)),
h.runCmd(`${cmdPrefix}/pr-updated/foo`).then(h.verifyResponse(404)), h.runCmd(`${cmdPrefix}/pr-updated/foo`).then(h.verifyResponse(404)),
h.runCmd(`${cmdPrefix}/pr-updated-foo`).then(h.verifyResponse(404)), h.runCmd(`${cmdPrefix}/pr-updated-foo`).then(h.verifyResponse(404)),
h.runCmd(`${cmdPrefix}/pr-updatednfoo`).then(h.verifyResponse(404)), h.runCmd(`${cmdPrefix}/pr-updatednfoo`).then(h.verifyResponse(404)),
]); ]).then(done);
}); });
@ -550,10 +551,10 @@ describe('preview-server', () => {
describe(`${host}/*`, () => { describe(`${host}/*`, () => {
it('should respond with 404 for requests to unknown URLs', async () => { it('should respond with 404 for requests to unknown URLs', done => {
const bodyRegex = /^Unknown resource/; const bodyRegex = /^Unknown resource/;
await Promise.all([ Promise.all([
h.runCmd(`curl -iL ${host}/index.html`).then(h.verifyResponse(404, bodyRegex)), h.runCmd(`curl -iL ${host}/index.html`).then(h.verifyResponse(404, bodyRegex)),
h.runCmd(`curl -iL ${host}/`).then(h.verifyResponse(404, bodyRegex)), h.runCmd(`curl -iL ${host}/`).then(h.verifyResponse(404, bodyRegex)),
h.runCmd(`curl -iL ${host}`).then(h.verifyResponse(404, bodyRegex)), h.runCmd(`curl -iL ${host}`).then(h.verifyResponse(404, bodyRegex)),
@ -561,7 +562,7 @@ describe('preview-server', () => {
h.runCmd(`curl -iLX POST ${host}`).then(h.verifyResponse(404, bodyRegex)), h.runCmd(`curl -iLX POST ${host}`).then(h.verifyResponse(404, bodyRegex)),
h.runCmd(`curl -iLX PATCH ${host}`).then(h.verifyResponse(404, bodyRegex)), h.runCmd(`curl -iLX PATCH ${host}`).then(h.verifyResponse(404, bodyRegex)),
h.runCmd(`curl -iLX DELETE ${host}`).then(h.verifyResponse(404, bodyRegex)), h.runCmd(`curl -iLX DELETE ${host}`).then(h.verifyResponse(404, bodyRegex)),
]); ]).then(done);
}); });
}); });

View File

@ -14,41 +14,42 @@
"predev": "yarn build || true", "predev": "yarn build || true",
"dev": "run-p ~~build-watch ~~test-watch", "dev": "run-p ~~build-watch ~~test-watch",
"lint": "tslint --project tsconfig.json", "lint": "tslint --project tsconfig.json",
"pretest": "run-s build lint", "pretest": "yarn build",
"test": "yarn ~~test-only", "test": "yarn ~~test-only",
"pretest-watch": "yarn pretest", "pretest-watch": "yarn pretest",
"test-watch": "yarn ~~test-watch", "test-watch": "yarn ~~test-watch",
"~~build": "tsc", "~~build": "tsc",
"~~build-watch": "yarn ~~build --watch", "~~build-watch": "yarn ~~build --watch",
"pre~~test-only": "yarn lint",
"~~test-only": "node dist/test", "~~test-only": "node dist/test",
"~~test-watch": "nodemon --delay 1 --exec \"yarn ~~test-only\" --watch dist" "~~test-watch": "nodemon --delay 1 --exec \"yarn ~~test-only\" --watch dist"
}, },
"dependencies": { "dependencies": {
"body-parser": "^1.19.0", "body-parser": "^1.18.3",
"delete-empty": "^3.0.0", "delete-empty": "^2.0.0",
"express": "^4.17.1", "express": "^4.16.3",
"jasmine": "^3.6.1", "jasmine": "^3.2.0",
"nock": "^13.0.4", "nock": "^9.6.1",
"node-fetch": "^2.6.1", "node-fetch": "^2.2.0",
"shelljs": "^0.8.4", "shelljs": "^0.8.2",
"source-map-support": "^0.5.19", "source-map-support": "^0.5.9",
"tar-stream": "^2.1.3", "tar-stream": "^1.6.1",
"tslib": "^2.2.0" "tslib": "^1.9.3"
}, },
"devDependencies": { "devDependencies": {
"@types/body-parser": "^1.19.0", "@types/body-parser": "^1.17.0",
"@types/express": "^4.17.8", "@types/express": "^4.16.0",
"@types/jasmine": "^3.5.14", "@types/jasmine": "^2.8.8",
"@types/nock": "^11.1.0", "@types/nock": "^9.3.0",
"@types/node": "^14.6.4", "@types/node": "^10.9.2",
"@types/node-fetch": "^2.5.7", "@types/node-fetch": "^2.1.2",
"@types/shelljs": "^0.8.8", "@types/shelljs": "^0.8.0",
"@types/supertest": "^2.0.10", "@types/supertest": "^2.0.5",
"nodemon": "^2.0.4", "nodemon": "^1.18.3",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"supertest": "^4.0.2", "supertest": "^3.1.0",
"tslint": "^6.1.3", "tslint": "^5.11.0",
"tslint-jasmine-noSkipOrFocus": "^1.0.9", "tslint-jasmine-noSkipOrFocus": "^1.0.9",
"typescript": "~4.3.4" "typescript": "^3.0.1"
} }
} }

View File

@ -15,6 +15,7 @@ const EXISTING_DOWNLOADS = [
'20-1234567-build.zip', '20-1234567-build.zip',
]; ];
const OPEN_PRS = [10, 40]; const OPEN_PRS = [10, 40];
const ANY_DATE = jasmine.any(String);
// Tests // Tests
describe('BuildCleaner', () => { describe('BuildCleaner', () => {
@ -76,18 +77,22 @@ describe('BuildCleaner', () => {
let cleanerRemoveUnnecessaryDownloadsSpy: jasmine.Spy; let cleanerRemoveUnnecessaryDownloadsSpy: jasmine.Spy;
beforeEach(() => { beforeEach(() => {
cleanerGetExistingBuildNumbersSpy = spyOn(cleaner, 'getExistingBuildNumbers').and.resolveTo(EXISTING_BUILDS); cleanerGetExistingBuildNumbersSpy = spyOn(cleaner, 'getExistingBuildNumbers')
cleanerGetOpenPrNumbersSpy = spyOn(cleaner, 'getOpenPrNumbers').and.resolveTo(OPEN_PRS); .and.callFake(() => Promise.resolve(EXISTING_BUILDS));
cleanerGetExistingDownloadsSpy = spyOn(cleaner, 'getExistingDownloads').and.resolveTo(EXISTING_DOWNLOADS); cleanerGetOpenPrNumbersSpy = spyOn(cleaner, 'getOpenPrNumbers')
.and.callFake(() => Promise.resolve(OPEN_PRS));
cleanerGetExistingDownloadsSpy = spyOn(cleaner, 'getExistingDownloads')
.and.callFake(() => Promise.resolve(EXISTING_DOWNLOADS));
cleanerRemoveUnnecessaryBuildsSpy = spyOn(cleaner, 'removeUnnecessaryBuilds'); cleanerRemoveUnnecessaryBuildsSpy = spyOn(cleaner, 'removeUnnecessaryBuilds');
cleanerRemoveUnnecessaryDownloadsSpy = spyOn(cleaner, 'removeUnnecessaryDownloads'); cleanerRemoveUnnecessaryDownloadsSpy = spyOn(cleaner, 'removeUnnecessaryDownloads');
}); });
it('should return a promise', async () => { it('should return a promise', async () => {
const promise = cleaner.cleanUp(); const promise = cleaner.cleanUp();
expect(promise).toBeInstanceOf(Promise); expect(promise).toEqual(jasmine.any(Promise));
// Do not complete the test and release the spies synchronously, to avoid running the actual implementations. // Do not complete the test and release the spies synchronously, to avoid running the actual implementations.
await promise; await promise;
@ -125,32 +130,52 @@ describe('BuildCleaner', () => {
it('should reject if \'getOpenPrNumbers()\' rejects', async () => { it('should reject if \'getOpenPrNumbers()\' rejects', async () => {
cleanerGetOpenPrNumbersSpy.and.rejectWith('Test'); try {
await expectAsync(cleaner.cleanUp()).toBeRejectedWith('Test'); cleanerGetOpenPrNumbersSpy.and.callFake(() => Promise.reject('Test'));
await cleaner.cleanUp();
} catch (err) {
expect(err).toBe('Test');
}
}); });
it('should reject if \'getExistingBuildNumbers()\' rejects', async () => { it('should reject if \'getExistingBuildNumbers()\' rejects', async () => {
cleanerGetExistingBuildNumbersSpy.and.rejectWith('Test'); try {
await expectAsync(cleaner.cleanUp()).toBeRejectedWith('Test'); cleanerGetExistingBuildNumbersSpy.and.callFake(() => Promise.reject('Test'));
await cleaner.cleanUp();
} catch (err) {
expect(err).toBe('Test');
}
}); });
it('should reject if \'getExistingDownloads()\' rejects', async () => { it('should reject if \'getExistingDownloads()\' rejects', async () => {
cleanerGetExistingDownloadsSpy.and.rejectWith('Test'); try {
await expectAsync(cleaner.cleanUp()).toBeRejectedWith('Test'); cleanerGetExistingDownloadsSpy.and.callFake(() => Promise.reject('Test'));
await cleaner.cleanUp();
} catch (err) {
expect(err).toBe('Test');
}
}); });
it('should reject if \'removeUnnecessaryBuilds()\' rejects', async () => { it('should reject if \'removeUnnecessaryBuilds()\' rejects', async () => {
cleanerRemoveUnnecessaryBuildsSpy.and.rejectWith('Test'); try {
await expectAsync(cleaner.cleanUp()).toBeRejectedWith('Test'); cleanerRemoveUnnecessaryBuildsSpy.and.callFake(() => Promise.reject('Test'));
await cleaner.cleanUp();
} catch (err) {
expect(err).toBe('Test');
}
}); });
it('should reject if \'removeUnnecessaryDownloads()\' rejects', async () => { it('should reject if \'removeUnnecessaryDownloads()\' rejects', async () => {
cleanerRemoveUnnecessaryDownloadsSpy.and.rejectWith('Test'); try {
await expectAsync(cleaner.cleanUp()).toBeRejectedWith('Test'); cleanerRemoveUnnecessaryDownloadsSpy.and.callFake(() => Promise.reject('Test'));
await cleaner.cleanUp();
} catch (err) {
expect(err).toBe('Test');
}
}); });
}); });
@ -162,15 +187,13 @@ describe('BuildCleaner', () => {
let promise: Promise<number[]>; let promise: Promise<number[]>;
beforeEach(() => { beforeEach(() => {
fsReaddirSpy = spyOn(fs, 'readdir').and.callFake( fsReaddirSpy = spyOn(fs, 'readdir').and.callFake((_: string, cb: typeof readdirCb) => readdirCb = cb);
((_: string, cb: typeof readdirCb) => readdirCb = cb) as unknown as typeof fs.readdir,
);
promise = cleaner.getExistingBuildNumbers(); promise = cleaner.getExistingBuildNumbers();
}); });
it('should return a promise', () => { it('should return a promise', () => {
expect(promise).toBeInstanceOf(Promise); expect(promise).toEqual(jasmine.any(Promise));
}); });
@ -180,27 +203,43 @@ describe('BuildCleaner', () => {
}); });
it('should reject if an error occurs while getting the files', async () => { it('should reject if an error occurs while getting the files', done => {
promise.catch(err => {
expect(err).toBe('Test');
done();
});
readdirCb('Test'); readdirCb('Test');
await expectAsync(promise).toBeRejectedWith('Test');
}); });
it('should resolve with the returned files (as numbers)', async () => { it('should resolve with the returned files (as numbers)', done => {
promise.then(result => {
expect(result).toEqual([12, 34, 56]);
done();
});
readdirCb(null, ['12', '34', '56']); readdirCb(null, ['12', '34', '56']);
await expectAsync(promise).toBeResolvedTo([12, 34, 56]);
}); });
it('should remove `HIDDEN_DIR_PREFIX` from the filenames', async () => { it('should remove `HIDDEN_DIR_PREFIX` from the filenames', done => {
promise.then(result => {
expect(result).toEqual([12, 34, 56]);
done();
});
readdirCb(null, [`${HIDDEN_DIR_PREFIX}12`, '34', `${HIDDEN_DIR_PREFIX}56`]); readdirCb(null, [`${HIDDEN_DIR_PREFIX}12`, '34', `${HIDDEN_DIR_PREFIX}56`]);
await expectAsync(promise).toBeResolvedTo([12, 34, 56]);
}); });
it('should ignore files with non-numeric (or zero) names', async () => { it('should ignore files with non-numeric (or zero) names', done => {
promise.then(result => {
expect(result).toEqual([12, 34, 56]);
done();
});
readdirCb(null, ['12', 'foo', '34', 'bar', '56', '000']); readdirCb(null, ['12', 'foo', '34', 'bar', '56', '000']);
await expectAsync(promise).toBeResolvedTo([12, 34, 56]);
}); });
}); });
@ -220,7 +259,7 @@ describe('BuildCleaner', () => {
it('should return a promise', () => { it('should return a promise', () => {
expect(promise).toBeInstanceOf(Promise); expect(promise).toEqual(jasmine.any(Promise));
}); });
@ -229,15 +268,31 @@ describe('BuildCleaner', () => {
}); });
it('should reject if an error occurs while fetching PRs', async () => { it('should reject if an error occurs while fetching PRs', done => {
promise.catch(err => {
expect(err).toBe('Test');
done();
});
prDeferred.reject('Test'); prDeferred.reject('Test');
await expectAsync(promise).toBeRejectedWith('Test');
}); });
it('should resolve with the numbers of the fetched PRs', async () => { it('should resolve with the numbers of the fetched PRs', done => {
promise.then(prNumbers => {
expect(prNumbers).toEqual([1, 2, 3]);
done();
});
prDeferred.resolve([{id: 0, number: 1}, {id: 1, number: 2}, {id: 2, number: 3}]); prDeferred.resolve([{id: 0, number: 1}, {id: 1, number: 2}, {id: 2, number: 3}]);
await expectAsync(promise).toBeResolvedTo([1, 2, 3]); });
it('should log the number of open PRs', () => {
promise.then(prNumbers => {
expect(loggerLogSpy).toHaveBeenCalledWith(
ANY_DATE, 'BuildCleaner: ', `Open pull requests: ${prNumbers}`);
});
}); });
}); });
@ -249,15 +304,13 @@ describe('BuildCleaner', () => {
let promise: Promise<string[]>; let promise: Promise<string[]>;
beforeEach(() => { beforeEach(() => {
fsReaddirSpy = spyOn(fs, 'readdir').and.callFake( fsReaddirSpy = spyOn(fs, 'readdir').and.callFake((_: string, cb: typeof readdirCb) => readdirCb = cb);
((_: string, cb: typeof readdirCb) => readdirCb = cb) as unknown as typeof fs.readdir,
);
promise = cleaner.getExistingDownloads(); promise = cleaner.getExistingDownloads();
}); });
it('should return a promise', () => { it('should return a promise', () => {
expect(promise).toBeInstanceOf(Promise); expect(promise).toEqual(jasmine.any(Promise));
}); });
@ -267,21 +320,33 @@ describe('BuildCleaner', () => {
}); });
it('should reject if an error occurs while getting the files', async () => { it('should reject if an error occurs while getting the files', done => {
promise.catch(err => {
expect(err).toBe('Test');
done();
});
readdirCb('Test'); readdirCb('Test');
await expectAsync(promise).toBeRejectedWith('Test');
}); });
it('should resolve with the returned file names', async () => { it('should resolve with the returned file names', done => {
promise.then(result => {
expect(result).toEqual(EXISTING_DOWNLOADS);
done();
});
readdirCb(null, EXISTING_DOWNLOADS); readdirCb(null, EXISTING_DOWNLOADS);
await expectAsync(promise).toBeResolvedTo(EXISTING_DOWNLOADS);
}); });
it('should ignore files that do not match the artifactPath', async () => { it('should ignore files that do not match the artifactPath', done => {
promise.then(result => {
expect(result).toEqual(['10-ABCDEF-build.zip', '30-FFFFFFF-build.zip']);
done();
});
readdirCb(null, ['10-ABCDEF-build.zip', '20-AAAAAAA-otherfile.zip', '30-FFFFFFF-build.zip']); readdirCb(null, ['10-ABCDEF-build.zip', '20-AAAAAAA-otherfile.zip', '30-FFFFFFF-build.zip']);
await expectAsync(promise).toBeResolvedTo(['10-ABCDEF-build.zip', '30-FFFFFFF-build.zip']);
}); });
}); });
@ -299,7 +364,7 @@ describe('BuildCleaner', () => {
}); });
it('should test if the directory exists (and return if it does not)', () => { it('should test if the directory exists (and return if is does not)', () => {
shellTestSpy.and.returnValue(false); shellTestSpy.and.returnValue(false);
cleaner.removeDir('/foo/bar'); cleaner.removeDir('/foo/bar');
@ -316,19 +381,22 @@ describe('BuildCleaner', () => {
it('should make the directory and its content writable before removing', () => { it('should make the directory and its content writable before removing', () => {
shellRmSpy.and.callFake(() => expect(shellChmodSpy).toHaveBeenCalledWith('-R', 'a+w', '/foo/bar'));
cleaner.removeDir('/foo/bar'); cleaner.removeDir('/foo/bar');
expect(shellChmodSpy).toHaveBeenCalledBefore(shellRmSpy);
expect(shellChmodSpy).toHaveBeenCalledWith('-R', 'a+w', '/foo/bar');
expect(shellRmSpy).toHaveBeenCalled(); expect(shellRmSpy).toHaveBeenCalled();
}); });
it('should catch errors and log them', () => { it('should catch errors and log them', () => {
shellRmSpy.and.throwError('Test'); shellRmSpy.and.callFake(() => {
// tslint:disable-next-line: no-string-throw
throw 'Test';
});
cleaner.removeDir('/foo/bar'); cleaner.removeDir('/foo/bar');
expect(loggerErrorSpy).toHaveBeenCalledWith('ERROR: Unable to remove \'/foo/bar\' due to:', new Error('Test')); expect(loggerErrorSpy).toHaveBeenCalledWith('ERROR: Unable to remove \'/foo/bar\' due to:', 'Test');
}); });
}); });
@ -381,7 +449,7 @@ describe('BuildCleaner', () => {
expect(cleanerRemoveDirSpy).toHaveBeenCalledTimes(0); expect(cleanerRemoveDirSpy).toHaveBeenCalledTimes(0);
cleanerRemoveDirSpy.calls.reset(); cleanerRemoveDirSpy.calls.reset();
cleaner.removeUnnecessaryBuilds([1, 2, 3, 4], []); (cleaner as any).removeUnnecessaryBuilds([1, 2, 3, 4], []);
expect(cleanerRemoveDirSpy).toHaveBeenCalledTimes(8); expect(cleanerRemoveDirSpy).toHaveBeenCalledTimes(8);
expect(cleanerRemoveDirSpy).toHaveBeenCalledWith(normalize('/foo/bar/1')); expect(cleanerRemoveDirSpy).toHaveBeenCalledWith(normalize('/foo/bar/1'));
expect(cleanerRemoveDirSpy).toHaveBeenCalledWith(normalize('/foo/bar/2')); expect(cleanerRemoveDirSpy).toHaveBeenCalledWith(normalize('/foo/bar/2'));

View File

@ -45,15 +45,25 @@ describe('CircleCIApi', () => {
const errorMessage = 'Invalid request'; const errorMessage = 'Invalid request';
const request = nock(BASE_URL).get(`/${buildNum}?circle-token=${TOKEN}`); const request = nock(BASE_URL).get(`/${buildNum}?circle-token=${TOKEN}`);
request.replyWithError(errorMessage); try {
await expectAsync(api.getBuildInfo(buildNum)).toBeRejectedWithError( request.replyWithError(errorMessage);
await api.getBuildInfo(buildNum);
throw new Error('Exception Expected');
} catch (err) {
expect(err.message).toEqual(
`CircleCI build info request failed ` + `CircleCI build info request failed ` +
`(request to ${BASE_URL}/${buildNum}?circle-token=${TOKEN} failed, reason: ${errorMessage})`); `(request to ${BASE_URL}/${buildNum}?circle-token=${TOKEN} failed, reason: ${errorMessage})`);
}
request.reply(404, errorMessage); try {
await expectAsync(api.getBuildInfo(buildNum)).toBeRejectedWithError( request.reply(404, errorMessage);
await api.getBuildInfo(buildNum);
throw new Error('Exception Expected');
} catch (err) {
expect(err.message).toEqual(
`CircleCI build info request failed ` + `CircleCI build info request failed ` +
`(request to ${BASE_URL}/${buildNum}?circle-token=${TOKEN} failed, reason: ${errorMessage})`); `(request to ${BASE_URL}/${buildNum}?circle-token=${TOKEN} failed, reason: ${errorMessage})`);
}
}); });
}); });
@ -68,7 +78,8 @@ describe('CircleCIApi', () => {
.get(`/${buildNum}/artifacts?circle-token=${TOKEN}`) .get(`/${buildNum}/artifacts?circle-token=${TOKEN}`)
.reply(200, [artifact0, artifact1, artifact2]); .reply(200, [artifact0, artifact1, artifact2]);
await expectAsync(api.getBuildArtifactUrl(buildNum, 'some/path/1')).toBeResolvedTo('https://url/1'); const artifactUrl = await api.getBuildArtifactUrl(buildNum, 'some/path/1');
expect(artifactUrl).toEqual('https://url/1');
request.done(); request.done();
}); });
@ -79,15 +90,25 @@ describe('CircleCIApi', () => {
const errorMessage = 'Invalid request'; const errorMessage = 'Invalid request';
const request = nock(BASE_URL).get(`/${buildNum}/artifacts?circle-token=${TOKEN}`); const request = nock(BASE_URL).get(`/${buildNum}/artifacts?circle-token=${TOKEN}`);
request.replyWithError(errorMessage); try {
await expectAsync(api.getBuildArtifactUrl(buildNum, 'some/path/1')).toBeRejectedWithError( request.replyWithError(errorMessage);
await api.getBuildArtifactUrl(buildNum, 'some/path/1');
throw new Error('Exception Expected');
} catch (err) {
expect(err.message).toEqual(
`CircleCI artifact URL request failed ` + `CircleCI artifact URL request failed ` +
`(request to ${BASE_URL}/${buildNum}/artifacts?circle-token=${TOKEN} failed, reason: ${errorMessage})`); `(request to ${BASE_URL}/${buildNum}/artifacts?circle-token=${TOKEN} failed, reason: ${errorMessage})`);
}
request.reply(404, errorMessage); try {
await expectAsync(api.getBuildArtifactUrl(buildNum, 'some/path/1')).toBeRejectedWithError( request.reply(404, errorMessage);
await api.getBuildArtifactUrl(buildNum, 'some/path/1');
throw new Error('Exception Expected');
} catch (err) {
expect(err.message).toEqual(
`CircleCI artifact URL request failed ` + `CircleCI artifact URL request failed ` +
`(request to ${BASE_URL}/${buildNum}/artifacts?circle-token=${TOKEN} failed, reason: ${errorMessage})`); `(request to ${BASE_URL}/${buildNum}/artifacts?circle-token=${TOKEN} failed, reason: ${errorMessage})`);
}
}); });
it('should throw an error if the response does not contain the specified artifact', async () => { it('should throw an error if the response does not contain the specified artifact', async () => {
@ -100,9 +121,14 @@ describe('CircleCIApi', () => {
.get(`/${buildNum}/artifacts?circle-token=${TOKEN}`) .get(`/${buildNum}/artifacts?circle-token=${TOKEN}`)
.reply(200, [artifact0, artifact1, artifact2]); .reply(200, [artifact0, artifact1, artifact2]);
await expectAsync(api.getBuildArtifactUrl(buildNum, 'some/path/3')).toBeRejectedWithError( try {
await api.getBuildArtifactUrl(buildNum, 'some/path/3');
throw new Error('Exception Expected');
} catch (err) {
expect(err.message).toEqual(
`CircleCI artifact URL request failed ` + `CircleCI artifact URL request failed ` +
`(Missing artifact (some/path/3) for CircleCI build: ${buildNum})`); `(Missing artifact (some/path/3) for CircleCI build: ${buildNum})`);
}
}); });
}); });
}); });

View File

@ -118,7 +118,7 @@ describe('GithubApi', () => {
it('should return a promise', () => { it('should return a promise', () => {
expect((api as any).getPaginated()).toBeInstanceOf(Promise); expect((api as any).getPaginated()).toEqual(jasmine.any(Promise));
}); });
@ -131,30 +131,45 @@ describe('GithubApi', () => {
}); });
it('should reject if the request fails', async () => { it('should reject if the request fails', done => {
const responsePromise = (api as any).getPaginated('/foo/bar'); (api as any).getPaginated('/foo/bar').catch((err: any) => {
expect(err).toBe('Test');
done();
});
deferreds[0].reject('Test'); deferreds[0].reject('Test');
await expectAsync(responsePromise).toBeRejectedWith('Test');
}); });
it('should resolve with the returned items', async () => { it('should resolve with the returned items', done => {
const items = [{id: 1}, {id: 2}]; const items = [{id: 1}, {id: 2}];
const responsePromise = (api as any).getPaginated('/foo/bar');
deferreds[0].resolve(items);
await expectAsync(responsePromise).toBeResolvedTo(items); (api as any).getPaginated('/foo/bar').then((data: any) => {
expect(data).toEqual(items);
done();
});
deferreds[0].resolve(items);
}); });
it('should iteratively call \'get()\' to fetch all items', async () => { it('should iteratively call \'get()\' to fetch all items', done => {
// Create an array or 250 objects. // Create an array or 250 objects.
const allItems = new Array(250).fill(null).map((_, i) => ({id: i})); const allItems = '.'.repeat(250).split('').map((_, i) => ({id: i}));
const apiGetSpy = api.get as jasmine.Spy; const apiGetSpy = api.get as jasmine.Spy;
const paramsForPage = (page: number) => ({baz: 'qux', page, per_page: 100});
const responsePromise = (api as any).getPaginated('/foo/bar', {baz: 'qux'}); (api as any).getPaginated('/foo/bar', {baz: 'qux'}).then((data: any) => {
const paramsForPage = (page: number) => ({baz: 'qux', page, per_page: 100});
expect(apiGetSpy).toHaveBeenCalledTimes(3);
expect(apiGetSpy.calls.argsFor(0)).toEqual(['/foo/bar', paramsForPage(1)]);
expect(apiGetSpy.calls.argsFor(1)).toEqual(['/foo/bar', paramsForPage(2)]);
expect(apiGetSpy.calls.argsFor(2)).toEqual(['/foo/bar', paramsForPage(3)]);
expect(data).toEqual(allItems);
done();
});
deferreds[0].resolve(allItems.slice(0, 100)); deferreds[0].resolve(allItems.slice(0, 100));
setTimeout(() => { setTimeout(() => {
@ -163,13 +178,6 @@ describe('GithubApi', () => {
deferreds[2].resolve(allItems.slice(200)); deferreds[2].resolve(allItems.slice(200));
}, 0); }, 0);
}, 0); }, 0);
await expectAsync(responsePromise).toBeResolvedTo(allItems);
expect(apiGetSpy).toHaveBeenCalledTimes(3);
expect(apiGetSpy.calls.argsFor(0)).toEqual(['/foo/bar', paramsForPage(1)]);
expect(apiGetSpy.calls.argsFor(1)).toEqual(['/foo/bar', paramsForPage(2)]);
expect(apiGetSpy.calls.argsFor(2)).toEqual(['/foo/bar', paramsForPage(3)]);
}); });
}); });
@ -209,29 +217,28 @@ describe('GithubApi', () => {
describe('request()', () => { describe('request()', () => {
it('should return a promise', () => { it('should return a promise', () => {
nock('https://api.github.com').get('/').reply(200); nock('https://api.github.com').get('').reply(200);
expect((api as any).request()).toBeInstanceOf(Promise); expect((api as any).request()).toEqual(jasmine.any(Promise));
}); });
it('should call \'https.request()\' with the correct options', async () => { it('should call \'https.request()\' with the correct options', () => {
const requestHandler = nock('https://api.github.com') const requestHandler = nock('https://api.github.com')
.intercept('/path', 'method') .intercept('/path', 'method')
.reply(200); .reply(200);
await (api as any).request('method', '/path'); (api as any).request('method', '/path');
requestHandler.done(); requestHandler.done();
}); });
it('should add the \'Authorization\' header containing the \'githubToken\'', async () => { it('should add the \'Authorization\' header containing the \'githubToken\'', () => {
const requestHandler = nock('https://api.github.com') const requestHandler = nock('https://api.github.com')
.intercept('/path', 'method', undefined, { .intercept('/path', 'method', undefined, {
reqheaders: {Authorization: 'token 12345'}, reqheaders: {Authorization: 'token 12345'},
}) })
.reply(200); .reply(200);
(api as any).request('method', '/path');
await (api as any).request('method', '/path');
requestHandler.done(); requestHandler.done();
}); });
@ -240,85 +247,97 @@ describe('GithubApi', () => {
nock('https://api.github.com') nock('https://api.github.com')
.intercept('/path', 'method') .intercept('/path', 'method')
.replyWithError('Test'); .replyWithError('Test');
let message = 'Failed to reject error';
await expectAsync((api as any).request('method', '/path')).toBeRejectedWithError('Test'); await (api as any).request('method', '/path').catch((err: any) => message = err.message);
expect(message).toEqual('Test');
}); });
it('should \'JSON.stringify\' and send the data along with the request', async () => { it('should \'JSON.stringify\' and send the data along with the request', () => {
const data = {key: 'value'}; const data = {key: 'value'};
const requestHandler = nock('https://api.github.com') const requestHandler = nock('https://api.github.com')
.intercept('/path', 'method', JSON.stringify(data)) .intercept('/path', 'method', JSON.stringify(data))
.reply(200); .reply(200);
(api as any).request('method', '/path', data);
await (api as any).request('method', '/path', data);
requestHandler.done(); requestHandler.done();
}); });
it('should reject if response statusCode is <200', async () => { it('should reject if response statusCode is <200', done => {
const requestHandler = nock('https://api.github.com') const requestHandler = nock('https://api.github.com')
.intercept('/path', 'method') .intercept('/path', 'method')
.reply(199); .reply(199);
const responsePromise = (api as any).request('method', '/path');
await expectAsync(responsePromise).toBeRejectedWith(jasmine.stringMatching('failed'));
await expectAsync(responsePromise).toBeRejectedWith(jasmine.stringMatching('status: 199'));
(api as any).request('method', '/path')
.catch((err: string) => {
expect(err).toContain('failed');
expect(err).toContain('status: 199');
done();
});
requestHandler.done(); requestHandler.done();
}); });
it('should reject if response statusCode is >=400', async () => { it('should reject if response statusCode is >=400', done => {
const requestHandler = nock('https://api.github.com') const requestHandler = nock('https://api.github.com')
.intercept('/path', 'method') .intercept('/path', 'method')
.reply(400); .reply(400);
const responsePromise = (api as any).request('method', '/path');
await expectAsync(responsePromise).toBeRejectedWith(jasmine.stringMatching('failed'));
await expectAsync(responsePromise).toBeRejectedWith(jasmine.stringMatching('status: 400'));
(api as any).request('method', '/path')
.catch((err: string) => {
expect(err).toContain('failed');
expect(err).toContain('status: 400');
done();
});
requestHandler.done(); requestHandler.done();
}); });
it('should include the response text in the rejection message', async () => { it('should include the response text in the rejection message', done => {
const requestHandler = nock('https://api.github.com') const requestHandler = nock('https://api.github.com')
.intercept('/path', 'method') .intercept('/path', 'method')
.reply(500, 'Test'); .reply(500, 'Test');
const responsePromise = (api as any).request('method', '/path');
await expectAsync(responsePromise).toBeRejectedWith(jasmine.stringMatching('Test'));
(api as any).request('method', '/path')
.catch((err: string) => {
expect(err).toContain('Test');
done();
});
requestHandler.done(); requestHandler.done();
}); });
it('should resolve if returned statusCode is >=200 and <400', async () => { it('should resolve if returned statusCode is >=200 and <400', done => {
const requestHandler = nock('https://api.github.com') const requestHandler = nock('https://api.github.com')
.intercept('/path', 'method') .intercept('/path', 'method')
.reply(200); .reply(200);
await expectAsync((api as any).request('method', '/path')).toBeResolved(); (api as any).request('method', '/path').then(done);
requestHandler.done(); requestHandler.done();
}); });
it('should parse the response body into an object using \'JSON.parse\'', async () => { it('should parse the response body into an object using \'JSON.parse\'', done => {
const requestHandler = nock('https://api.github.com') const requestHandler = nock('https://api.github.com')
.intercept('/path', 'method') .intercept('/path', 'method')
.reply(300, '{"foo": "bar"}'); .reply(300, '{"foo": "bar"}');
await expectAsync((api as any).request('method', '/path')).toBeResolvedTo({foo: 'bar'}); (api as any).request('method', '/path').then((data: any) => {
expect(data).toEqual({foo: 'bar'});
done();
});
requestHandler.done(); requestHandler.done();
}); });
it('should reject if the response text is malformed JSON', async () => { it('should reject if the response text is malformed JSON', done => {
const requestHandler = nock('https://api.github.com') const requestHandler = nock('https://api.github.com')
.intercept('/path', 'method') .intercept('/path', 'method')
.reply(300, '}'); .reply(300, '}');
await expectAsync((api as any).request('method', '/path')).toBeRejectedWithError(SyntaxError); (api as any).request('method', '/path').catch((err: any) => {
expect(err).toEqual(jasmine.any(SyntaxError));
done();
});
requestHandler.done(); requestHandler.done();
}); });

View File

@ -1,6 +1,6 @@
// Imports // Imports
import {GithubApi} from '../../lib/common/github-api'; import {GithubApi} from '../../lib/common/github-api';
import {GithubPullRequests, PullRequest} from '../../lib/common/github-pull-requests'; import {GithubPullRequests} from '../../lib/common/github-pull-requests';
// Tests // Tests
describe('GithubPullRequests', () => { describe('GithubPullRequests', () => {
@ -47,21 +47,27 @@ describe('GithubPullRequests', () => {
it('should make a POST request to Github with the correct pathname, params and data', () => { it('should make a POST request to Github with the correct pathname, params and data', () => {
githubApi.post.and.resolveTo(); githubApi.post.and.callFake(() => Promise.resolve());
prs.addComment(42, 'body'); prs.addComment(42, 'body');
expect(githubApi.post).toHaveBeenCalledWith('/repos/foo/bar/issues/42/comments', null, {body: 'body'}); expect(githubApi.post).toHaveBeenCalledWith('/repos/foo/bar/issues/42/comments', null, {body: 'body'});
}); });
it('should reject if the request fails', async () => { it('should reject if the request fails', done => {
githubApi.post.and.rejectWith('Test'); githubApi.post.and.callFake(() => Promise.reject('Test'));
await expectAsync(prs.addComment(42, 'body')).toBeRejectedWith('Test'); prs.addComment(42, 'body').catch(err => {
expect(err).toBe('Test');
done();
});
}); });
it('should resolve with the data from the Github POST', async () => { it('should resolve with the data from the Github POST', done => {
githubApi.post.and.resolveTo('Test'); githubApi.post.and.callFake(() => Promise.resolve('Test'));
await expectAsync(prs.addComment(42, 'body')).toBeResolvedTo('Test'); prs.addComment(42, 'body').then(data => {
expect(data).toBe('Test');
done();
});
}); });
}); });
@ -81,11 +87,13 @@ describe('GithubPullRequests', () => {
}); });
it('should resolve with the data returned from GitHub', async () => { it('should resolve with the data returned from GitHub', done => {
const expected: any = {number: 42}; const expected: any = {number: 42};
githubApi.get.and.resolveTo(expected); githubApi.get.and.callFake(() => Promise.resolve(expected));
prs.fetch(42).then(data => {
await expectAsync(prs.fetch(42)).toBeResolvedTo(expected); expect(data).toEqual(expected);
done();
});
}); });
}); });
@ -117,14 +125,9 @@ describe('GithubPullRequests', () => {
}); });
it('should forward the value returned by \'getPaginated()\'', async () => { it('should forward the value returned by \'getPaginated()\'', () => {
const mockPrs: PullRequest[] = [ githubApi.getPaginated.and.returnValue('Test');
{number: 1, user: {login: 'foo'}, labels: []}, expect(prs.fetchAll() as any).toBe('Test');
{number: 2, user: {login: 'bar'}, labels: []},
];
githubApi.getPaginated.and.resolveTo(mockPrs);
expect(await prs.fetchAll()).toBe(mockPrs);
}); });
}); });
@ -144,11 +147,13 @@ describe('GithubPullRequests', () => {
}); });
it('should resolve with the data returned from GitHub', async () => { it('should resolve with the data returned from GitHub', done => {
const expected: any = [{sha: 'ABCDE', filename: 'a/b/c'}, {sha: '12345', filename: 'x/y/z'}]; const expected: any = [{sha: 'ABCDE', filename: 'a/b/c'}, {sha: '12345', filename: 'x/y/z'}];
githubApi.getPaginated.and.resolveTo(expected); githubApi.getPaginated.and.callFake(() => Promise.resolve(expected));
prs.fetchFiles(42).then(data => {
await expectAsync(prs.fetchFiles(42)).toBeResolvedTo(expected); expect(data).toEqual(expected);
done();
});
}); });
}); });

Some files were not shown because too many files have changed in this diff Show More