Compare commits
379 Commits
8.0.0-rc.1
...
5.2.x
Author | SHA1 | Date | |
---|---|---|---|
2fa61e8ee1 | |||
1eb0bc8ba8 | |||
c2cde43b59 | |||
bd4a3a88ae | |||
6ee866cb77 | |||
d8b63d0b83 | |||
a065dc24cd | |||
45d90b58b9 | |||
21a9f59ff2 | |||
1c34e02ae6 | |||
2f791ce68b | |||
e5018c4d77 | |||
f7b041c7f5 | |||
08c8d7fe56 | |||
98e3ef4003 | |||
ec3ecfcdf1 | |||
32f67d9161 | |||
55c7430856 | |||
2a276fcabe | |||
408d8cb735 | |||
95e9107899 | |||
ad369903f1 | |||
bc27d4aae4 | |||
3df879fe17 | |||
b004be5169 | |||
65dba9d0a8 | |||
354910203e | |||
402f452761 | |||
d2e7c99a93 | |||
80b9c65667 | |||
c78ae83b5a | |||
509d440bce | |||
7b23983859 | |||
21f3301746 | |||
a3204f87fd | |||
75d1ab9065 | |||
61bddebe65 | |||
31d95c2cd1 | |||
a1133303d3 | |||
c1251a8430 | |||
3cf5719435 | |||
47229fa87b | |||
dabc076267 | |||
af3b308e63 | |||
4ea8b17896 | |||
c5b6e31d97 | |||
22686f8a2f | |||
bda6908484 | |||
a1231bed9c | |||
a50ce6568a | |||
baa444ba7d | |||
73172dd67a | |||
8aa49ac6d7 | |||
3900c36b1c | |||
54e910841e | |||
9703079e0b | |||
dd615950d5 | |||
ae76eeca6a | |||
f43fba64cc | |||
4473da7de7 | |||
55eaeb17d9 | |||
0614b2b941 | |||
650c6e56ec | |||
4f7c369847 | |||
e7b2e97b46 | |||
0d4fe38a09 | |||
ae9c25ff3d | |||
d0f575bc54 | |||
776bb8206f | |||
d7f4aa6936 | |||
4be8b3f481 | |||
8d1e64004b | |||
49f6d1d02e | |||
641cc493ff | |||
a846abbb95 | |||
50761fb73e | |||
65f8943aab | |||
5391f96406 | |||
aca4735c8b | |||
9ee2e9e032 | |||
2fe7595235 | |||
1dd7cebad1 | |||
2731ecafbf | |||
e1b82a0a64 | |||
bbd54285d8 | |||
7d9de17935 | |||
db0afa9394 | |||
5298b2bda3 | |||
25ae886cad | |||
fc6dfc2e08 | |||
c0670ef52d | |||
fe96cafd03 | |||
ad674dad37 | |||
86517f2ad5 | |||
6d9a4f8aea | |||
a1efc27ff2 | |||
311232004c | |||
2a236b4066 | |||
bdee824292 | |||
4aeb04dcb0 | |||
5876fb0125 | |||
5b7b208637 | |||
789a47ec44 | |||
984a13e45b | |||
a3f7e30153 | |||
ff7e2e3f1e | |||
fe0d53f3a9 | |||
27962f8949 | |||
855e3a65db | |||
f8e70fb0c6 | |||
697d31a38c | |||
1593bff1b0 | |||
0ec11e3223 | |||
089769d5c3 | |||
9137650dba | |||
eccce1772d | |||
4aef9de37e | |||
f2fa7a289f | |||
e1fbe20d98 | |||
38bd8d49a5 | |||
d033106adb | |||
de02a7a5de | |||
c30a942329 | |||
2a38d93171 | |||
a9a0e27e94 | |||
66383901a6 | |||
1eb54132e4 | |||
6c9c173e1e | |||
1e08a945e1 | |||
4a08745d3e | |||
cf91906d8f | |||
0723c04a01 | |||
2b7188906b | |||
17c1577de9 | |||
150bac310f | |||
8f0a0641e2 | |||
17762390c9 | |||
da1b4d5ea7 | |||
aa100f69f2 | |||
9cca5a8c9c | |||
7c3b95b4ab | |||
e4e8a68c06 | |||
6460ac0add | |||
ee91de9d5a | |||
5ec38f2f47 | |||
612cfeca14 | |||
dfdade25ea | |||
c2f78e1ca3 | |||
484802cd2a | |||
ee535777bb | |||
94756eb4bd | |||
23b0707707 | |||
a2cb0109f1 | |||
d20a08bc48 | |||
5bdb3acace | |||
c5418c7abe | |||
09b4612bdd | |||
a346d28df6 | |||
bf07837d5d | |||
cdfedc1e49 | |||
af6a0563de | |||
c726d1d6d3 | |||
2030846df7 | |||
9dae97c5d9 | |||
228eb9feef | |||
debf01d7a6 | |||
51abe69b60 | |||
396bc0d9e9 | |||
861250b4e2 | |||
81c1e0a3c3 | |||
ce5e8fad9e | |||
185a6ab562 | |||
6b457843b9 | |||
5f52ea3d06 | |||
6c1e7ac40e | |||
6597616aac | |||
6a57264d38 | |||
1e3e0fad49 | |||
0c88d5dedd | |||
0b8b06ee8b | |||
edd6cd4e29 | |||
fdbfd21bcd | |||
0a5283da1a | |||
3237f1dbfc | |||
106b435297 | |||
ca69dfde31 | |||
1b8ea1005a | |||
da6ab91095 | |||
c4f841f031 | |||
e576b69d10 | |||
f938774748 | |||
c0b78067c4 | |||
2931018b84 | |||
e97afae67a | |||
c6bdc831c1 | |||
1a897e4f48 | |||
603e50d3bf | |||
ca5b72461c | |||
a616dd6c17 | |||
0ed64af5bc | |||
d57fd0b84f | |||
9b280eef63 | |||
d4a9db2af5 | |||
2740b69023 | |||
15ff7ba880 | |||
615bb95138 | |||
2b67400652 | |||
ac815f7281 | |||
854f0ff33a | |||
c55523466f | |||
f80b9da18a | |||
c5ec8d952a | |||
de561f36e1 | |||
47b71d98ae | |||
22d548f4ed | |||
799edd1e2f | |||
ffcb5dd264 | |||
75897d4977 | |||
eb0da530a7 | |||
0b23573573 | |||
47b73fd153 | |||
07769e5caa | |||
9fdb804b63 | |||
9d02db3254 | |||
920b0df32a | |||
b313976ac1 | |||
2d19e7bbea | |||
c3c92d7796 | |||
a887c9339f | |||
89051a0452 | |||
97dafa8460 | |||
56b9591746 | |||
b0ddb5ad0e | |||
103727aadf | |||
44ea80b797 | |||
41046e4a90 | |||
530b824faa | |||
e22d3a605c | |||
c6645e7a04 | |||
f0396f1e54 | |||
adb1d62967 | |||
cfe83939a4 | |||
973607fe9d | |||
664f7fa477 | |||
b155ae116b | |||
ce51ea93a1 | |||
d38e08812e | |||
aa9ba7f9fe | |||
102d06b974 | |||
11ec80a053 | |||
75eecdc351 | |||
965eecc587 | |||
c4fb696189 | |||
72df747dd6 | |||
579bed1a7a | |||
b59fb23f4a | |||
2aa460b30e | |||
e0022ae9cd | |||
f2e923edd8 | |||
c2f5ed545c | |||
5d75df8fb1 | |||
ed2b71799c | |||
fad99cca0e | |||
3f5ead3845 | |||
a89e709515 | |||
6a7689d4ea | |||
696ba01a4e | |||
81d64d6bec | |||
7410941a7c | |||
d159ad8b88 | |||
250c8da768 | |||
778e6e759f | |||
35a0721217 | |||
ba045e88d7 | |||
67806a7b25 | |||
9778a23be8 | |||
87e06d765e | |||
56f3e18c1c | |||
637515e71b | |||
27ecd077d4 | |||
4db1be0292 | |||
a0dbef9ea4 | |||
3aaf43f73c | |||
d952ae24dd | |||
da9e57b3d5 | |||
44d4f82dae | |||
bde2b4425c | |||
2a3de802a0 | |||
71f9eaa743 | |||
a62c186d15 | |||
c8bf281174 | |||
de6c6445af | |||
54238822e6 | |||
8b3fbb5bf4 | |||
2f61d3c320 | |||
5894f6ee1c | |||
6d9fcd62de | |||
0cbccc06dd | |||
ed670a36fb | |||
8e44577df3 | |||
6921c20ea1 | |||
52970c09e1 | |||
eecdf3414e | |||
21f766968d | |||
4b68fdce6f | |||
c12ea3a1f0 | |||
d7dbdc5c36 | |||
0112a903f9 | |||
66bbc84127 | |||
554129d6fe | |||
e32a0cabfe | |||
c828e5627b | |||
1626e74c59 | |||
a15a2b46d1 | |||
379ed75593 | |||
0f619896b3 | |||
7060655806 | |||
b5fc3eb9de | |||
451bdb9a75 | |||
983ccc02ad | |||
00f99b3c4c | |||
ba4ea82f68 | |||
982eb7bba8 | |||
3606c55410 | |||
2c65027391 | |||
4ee92f14a6 | |||
c9b65914d3 | |||
02352bcd9e | |||
0d55600fd8 | |||
af4eb00c91 | |||
d3e7ebb3b4 | |||
420f5c4275 | |||
b773a4ab98 | |||
55f15c54d9 | |||
4556532c26 | |||
54e75766ad | |||
d3333f04ba | |||
75f8522b8d | |||
a771ee5d90 | |||
a4cbe3542a | |||
cc9419d1ca | |||
d5393c7f91 | |||
71dd92bbb8 | |||
977978edb5 | |||
eb70966065 | |||
cf4bea587d | |||
8e9cd57951 | |||
f23896f519 | |||
0e59d18fc2 | |||
54c8a321a9 | |||
9005a6f3cd | |||
3c6a5063f7 | |||
b49d54e606 | |||
55fd82e587 | |||
7f3d0bbf97 | |||
3db02d244a | |||
50b605686e | |||
64d4aafbc7 | |||
a931a419fa | |||
6a97b5b722 | |||
aad1126446 | |||
7b463df52b | |||
a2432c9f10 | |||
e500484ccc | |||
a0dcb0b828 | |||
c4b71920d1 | |||
650f5fb5c7 | |||
c32e83334b | |||
7bdd9aecbd | |||
5ede67c345 | |||
31b671ab54 | |||
3804d20b6d | |||
0a5a87887e | |||
c46afce0f5 | |||
76c781fd37 | |||
51eb3d418e | |||
48c18985cc | |||
167cbed266 | |||
70e8802540 |
@ -1,7 +0,0 @@
|
|||||||
node_modules
|
|
||||||
dist
|
|
||||||
aio/content
|
|
||||||
aio/node_modules
|
|
||||||
aio/tools/examples/shared/node_modules
|
|
||||||
integration/bazel
|
|
||||||
packages/bazel/node_modules
|
|
142
.bazelrc
142
.bazelrc
@ -1,142 +0,0 @@
|
|||||||
################################
|
|
||||||
# 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=AngularTemplateCompile=worker
|
|
||||||
# TODO(alexeagle): re-enable after fixing worker instability with rxjs typings
|
|
||||||
# build --strategy=TypeScriptCompile=worker
|
|
||||||
build --strategy=TypeScriptCompile=standalone
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
###############################
|
|
||||||
# Filesystem interactions #
|
|
||||||
###############################
|
|
||||||
|
|
||||||
# Create symlinks in the project:
|
|
||||||
# - dist/bin for outputs
|
|
||||||
# - dist/testlogs, dist/genfiles
|
|
||||||
# - bazel-out
|
|
||||||
# NB: bazel-out should be excluded from the editor configuration.
|
|
||||||
# The checked-in /.vscode/settings.json does this for VSCode.
|
|
||||||
# Other editors may require manual config to ignore this directory.
|
|
||||||
# In the past, we say a problem where VSCode traversed a massive tree, opening file handles and
|
|
||||||
# eventually a surprising failure with auto-discovery of the C++ toolchain in
|
|
||||||
# MacOS High Sierra.
|
|
||||||
# See https://github.com/bazelbuild/bazel/issues/4603
|
|
||||||
build --symlink_prefix=dist/
|
|
||||||
|
|
||||||
# Disable watchfs as it causes tests to be flaky on Windows
|
|
||||||
# https://github.com/angular/angular/issues/29541
|
|
||||||
build --nowatchfs
|
|
||||||
|
|
||||||
# Turn off legacy external runfiles
|
|
||||||
run --nolegacy_external_runfiles
|
|
||||||
test --nolegacy_external_runfiles
|
|
||||||
|
|
||||||
# Turn on --incompatible_strict_action_env which was on by default
|
|
||||||
# in Bazel 0.21.0 but turned off again in 0.22.0. Follow
|
|
||||||
# https://github.com/bazelbuild/bazel/issues/7026 for more details.
|
|
||||||
# This flag is needed to so that the bazel cache is not invalidated
|
|
||||||
# when running bazel via `yarn bazel`.
|
|
||||||
# See https://github.com/angular/angular/issues/27514.
|
|
||||||
build --incompatible_strict_action_env
|
|
||||||
run --incompatible_strict_action_env
|
|
||||||
test --incompatible_strict_action_env
|
|
||||||
|
|
||||||
###############################
|
|
||||||
# Saucelabs support #
|
|
||||||
# Turn on these settings with #
|
|
||||||
# --config=saucelabs #
|
|
||||||
###############################
|
|
||||||
|
|
||||||
# Expose SauceLabs environment to actions
|
|
||||||
# These environment variables are needed by
|
|
||||||
# web_test_karma to run on Saucelabs
|
|
||||||
test:saucelabs --action_env=SAUCE_USERNAME
|
|
||||||
test:saucelabs --action_env=SAUCE_ACCESS_KEY
|
|
||||||
test:saucelabs --action_env=SAUCE_READY_FILE
|
|
||||||
test:saucelabs --action_env=SAUCE_PID_FILE
|
|
||||||
test:saucelabs --action_env=SAUCE_TUNNEL_IDENTIFIER
|
|
||||||
test:saucelabs --define=KARMA_WEB_TEST_MODE=SL_REQUIRED
|
|
||||||
|
|
||||||
###############################
|
|
||||||
# Release support #
|
|
||||||
# Turn on these settings with #
|
|
||||||
# --config=release #
|
|
||||||
###############################
|
|
||||||
|
|
||||||
# Releases should always be stamped with version control info
|
|
||||||
# This command assumes node on the path and is a workaround for
|
|
||||||
# https://github.com/bazelbuild/bazel/issues/4802
|
|
||||||
build:release --workspace_status_command="node ./tools/bazel_stamp_vars.js"
|
|
||||||
|
|
||||||
###############################
|
|
||||||
# Output #
|
|
||||||
###############################
|
|
||||||
|
|
||||||
# A more useful default output mode for bazel query
|
|
||||||
# Prints eg. "ng_module rule //foo:bar" rather than just "//foo:bar"
|
|
||||||
query --output=label_kind
|
|
||||||
|
|
||||||
# By default, failing tests don't print any output, it goes to the log file
|
|
||||||
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 #
|
|
||||||
################################
|
|
||||||
|
|
||||||
# Bazel flags for CircleCI are in /.circleci/bazel.rc
|
|
||||||
|
|
||||||
################################
|
|
||||||
# Temporary Settings for Ivy #
|
|
||||||
################################
|
|
||||||
# to determine if the compiler used should be Ivy or ViewEngine one can use `--define=compile=aot` on
|
|
||||||
# any bazel target. This is a temporary flag until codebase is permanently switched to Ivy.
|
|
||||||
build --define=compile=legacy
|
|
||||||
|
|
||||||
###############################
|
|
||||||
# Remote Build Execution support
|
|
||||||
# Turn on these settings with
|
|
||||||
# --config=remote
|
|
||||||
###############################
|
|
||||||
|
|
||||||
# Load default settings for Remote Build Execution
|
|
||||||
# When updating, the URLs of bazel_toolchains in packages/bazel/package.bzl
|
|
||||||
# may also need to be updated (see https://github.com/angular/angular/pull/27935)
|
|
||||||
import %workspace%/third_party/github.com/bazelbuild/bazel-toolchains/bazelrc/bazel-0.24.0.bazelrc
|
|
||||||
|
|
||||||
# Increase the default number of jobs by 50% because our build has lots of
|
|
||||||
# parallelism
|
|
||||||
build:remote --jobs=150
|
|
||||||
|
|
||||||
# Point to our custom execution platform; see tools/BUILD.bazel
|
|
||||||
build:remote --extra_execution_platforms=//tools:rbe_ubuntu1604-angular
|
|
||||||
build:remote --host_platform=//tools:rbe_ubuntu1604-angular
|
|
||||||
build:remote --platforms=//tools:rbe_ubuntu1604-angular
|
|
||||||
|
|
||||||
# Remote instance.
|
|
||||||
build:remote --remote_instance_name=projects/internal-200822/instances/default_instance
|
|
||||||
|
|
||||||
# Do not accept remote cache.
|
|
||||||
# We need to understand the security risks of using prior build artifacts.
|
|
||||||
build:remote --remote_accept_cached=false
|
|
||||||
|
|
||||||
# Load any settings 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
|
|
@ -1,42 +0,0 @@
|
|||||||
# 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"]
|
|
@ -1,96 +0,0 @@
|
|||||||
# 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.
|
|
@ -1,6 +0,0 @@
|
|||||||
@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."
|
|
@ -1,10 +0,0 @@
|
|||||||
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
|
|
@ -1,94 +0,0 @@
|
|||||||
# 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
|
|
@ -1,19 +0,0 @@
|
|||||||
# Encryption
|
|
||||||
|
|
||||||
Based on https://github.com/circleci/encrypted-files
|
|
||||||
|
|
||||||
In the CircleCI web UI, we have a secret variable called `KEY`
|
|
||||||
https://circleci.com/gh/angular/angular/edit#env-vars
|
|
||||||
which is only exposed to non-fork builds
|
|
||||||
(see "Pass secrets to builds from forked pull requests" under
|
|
||||||
https://circleci.com/gh/angular/angular/edit#advanced-settings)
|
|
||||||
|
|
||||||
We use this as a symmetric AES encryption key to encrypt tokens like
|
|
||||||
a GitHub token that enables publishing snapshots.
|
|
||||||
|
|
||||||
To create the github_token file, we take this approach:
|
|
||||||
- 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`
|
|
||||||
- echo "https://[token]:@github.com" > credentials
|
|
||||||
- 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`
|
|
@ -1,21 +1,24 @@
|
|||||||
# These options are enabled when running on CI
|
# 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.
|
# We do this by copying this file to /etc/bazel.bazelrc at the start of the build.
|
||||||
# See documentation in /docs/BAZEL.md
|
# See remote cache 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
|
# Don't be spammy in the logs
|
||||||
# TODO(gmagolan): Hide progress again once build performance improves
|
build --noshow_progress
|
||||||
# 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.
|
# Don't run manual tests
|
||||||
# This helps us diagnose which options override others
|
test --test_tag_filters=-manual
|
||||||
# (e.g. /etc/bazel.bazelrc vs. tools/bazel.rc)
|
|
||||||
build --announce_rc
|
# Enable experimental CircleCI bazel remote cache proxy
|
||||||
|
# See remote cache documentation in /docs/BAZEL.md
|
||||||
|
build --experimental_remote_spawn_cache --remote_rest_cache=http://localhost:7643
|
||||||
|
|
||||||
|
# Prevent unstable environment variables from tainting cache keys
|
||||||
|
build --experimental_strict_action_env
|
||||||
|
|
||||||
|
# Save downloaded repositories such as the go toolchain
|
||||||
|
# This directory can then be included in the CircleCI cache
|
||||||
|
# It should save time running the first build
|
||||||
|
build --experimental_repository_cache=/home/circleci/bazel_repository_cache
|
||||||
|
|
||||||
# Workaround https://github.com/bazelbuild/bazel/issues/3645
|
# Workaround https://github.com/bazelbuild/bazel/issues/3645
|
||||||
# Bazel doesn't calculate the memory ceiling correctly when running under Docker.
|
# Bazel doesn't calculate the memory ceiling correctly when running under Docker.
|
||||||
@ -25,6 +28,3 @@ build --local_resources=14336,8.0,1.0
|
|||||||
|
|
||||||
# Retry in the event of flakes, eg. https://circleci.com/gh/angular/angular/31309
|
# Retry in the event of flakes, eg. https://circleci.com/gh/angular/angular/31309
|
||||||
test --flaky_test_attempts=2
|
test --flaky_test_attempts=2
|
||||||
|
|
||||||
# More details on failures
|
|
||||||
build --verbose_failures=true
|
|
||||||
|
@ -7,704 +7,125 @@
|
|||||||
# To validate changes, use an online parser, eg.
|
# To validate changes, use an online parser, eg.
|
||||||
# http://yaml-online-parser.appspot.com/
|
# http://yaml-online-parser.appspot.com/
|
||||||
|
|
||||||
# Note that the browser docker image comes with Chrome and Firefox preinstalled. This is just
|
# Variables
|
||||||
# needed for jobs that run tests without Bazel. Bazel runs tests with browsers that will be
|
|
||||||
# fetched by the Webtesting rules. Therefore for jobs that run tests with Bazel, we don't need a
|
|
||||||
# docker image with browsers pre-installed.
|
|
||||||
# **NOTE 1**: If you change the version of the `*-browsers` docker image, make sure the
|
|
||||||
# `CI_CHROMEDRIVER_VERSION_ARG` env var (in `.circleci/env.sh`) points to a ChromeDriver
|
|
||||||
# version that is compatible with the Chrome version in the image.
|
|
||||||
# **NOTE 2**: If you change the version of the docker images, also change the `cache_key` suffix.
|
|
||||||
var_1: &default_docker_image circleci/node:10.12
|
|
||||||
var_2: &browsers_docker_image circleci/node:10.12-browsers
|
|
||||||
# We don't want to include the current branch name in the cache key because that would prevent
|
|
||||||
# PRs from being able to restore the cache since the branch names are always different for PRs.
|
|
||||||
# The cache key should only consist of dynamic values that change whenever something in the
|
|
||||||
# cache changes. For example:
|
|
||||||
# 1) yarn lock file changes --> cached "node_modules" are different.
|
|
||||||
# 2) bazel repository definitions change --> cached bazel repositories are different.
|
|
||||||
# **NOTE 1 **: If you change the cache key prefix, also sync the restore_cache fallback to match.
|
|
||||||
# **NOTE 2 **: Keep the static part of the cache key as prefix to enable correct fallbacks.
|
|
||||||
# See https://circleci.com/docs/2.0/caching/#restoring-cache for how prefixes work in CircleCI.
|
|
||||||
var_3: &cache_key v3-angular-node-10.12-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "packages/bazel/package.bzl" }}-{{ checksum "aio/yarn.lock" }}
|
|
||||||
|
|
||||||
# Initializes the CI environment by setting up common environment variables.
|
## IMPORTANT
|
||||||
var_4: &init_environment
|
# If you change the `docker_image` version, also change the `cache_key` suffix and the version of
|
||||||
|
# `com_github_bazelbuild_buildtools` in the `/WORKSPACE` file.
|
||||||
|
var_1: &docker_image angular/ngcontainer:0.1.0
|
||||||
|
var_2: &cache_key v2-angular-{{ .Branch }}-{{ checksum "yarn.lock" }}-0.1.0
|
||||||
|
|
||||||
|
# See remote cache documentation in /docs/BAZEL.md
|
||||||
|
var_3: &setup-bazel-remote-cache
|
||||||
run:
|
run:
|
||||||
name: Initializing environment (setting up variables, overwriting Yarn)
|
name: Start up bazel remote cache proxy
|
||||||
# Overwrite the yarn installed in the docker container with our own version.
|
command: ~/bazel-remote-proxy -backend circleci://
|
||||||
command: |
|
background: true
|
||||||
./.circleci/env.sh
|
|
||||||
ourYarn=$(realpath ./third_party/github.com/yarnpkg/yarn/releases/download/v1.13.0/bin/yarn.js)
|
|
||||||
sudo chmod a+x $ourYarn
|
|
||||||
sudo ln -fs $ourYarn /usr/local/bin/yarn
|
|
||||||
echo "Yarn version: $(yarn --version)"
|
|
||||||
|
|
||||||
# Add GitHub to known hosts.
|
|
||||||
mkdir -p ~/.ssh
|
|
||||||
echo 'github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==' >> ~/.ssh/known_hosts
|
|
||||||
|
|
||||||
# use git+ssh instead of https
|
|
||||||
git config --global url."ssh://git@github.com".insteadOf "https://github.com" || true
|
|
||||||
git config --global gc.auto 0 || true
|
|
||||||
|
|
||||||
|
|
||||||
var_5: &setup_bazel_remote_execution
|
|
||||||
run:
|
|
||||||
name: "Setup bazel RBE remote execution"
|
|
||||||
command: |
|
|
||||||
openssl aes-256-cbc -d -in .circleci/gcp_token -k "$CI_REPO_NAME" -out /home/circleci/.gcp_credentials
|
|
||||||
echo "export GOOGLE_APPLICATION_CREDENTIALS=/home/circleci/.gcp_credentials" >> $BASH_ENV
|
|
||||||
sudo bash -c "echo 'build --config=remote' >> /etc/bazel.bazelrc"
|
|
||||||
|
|
||||||
# Settings common to each job
|
# Settings common to each job
|
||||||
var_6: &job_defaults
|
anchor_1: &job_defaults
|
||||||
working_directory: ~/ng
|
working_directory: ~/ng
|
||||||
docker:
|
docker:
|
||||||
- image: *default_docker_image
|
- image: *docker_image
|
||||||
|
|
||||||
# After checkout, rebase on top of target branch.
|
# After checkout, rebase on top of master.
|
||||||
var_7: &post_checkout
|
# Similar to travis behavior, but not quite the same.
|
||||||
run:
|
# See https://discuss.circleci.com/t/1662
|
||||||
name: Rebase PR on target branch
|
anchor_2: &post_checkout
|
||||||
command: >
|
post: git pull --ff-only origin "refs/pull/${CIRCLE_PULL_REQUEST//*pull\//}/merge"
|
||||||
if [[ -n "${CIRCLE_PR_NUMBER}" ]]; then
|
|
||||||
# User is required for rebase.
|
|
||||||
git config user.name "angular-ci"
|
|
||||||
git config user.email "angular-ci"
|
|
||||||
# Rebase PR on top of target branch.
|
|
||||||
node tools/rebase-pr.js angular/angular ${CIRCLE_PR_NUMBER}
|
|
||||||
else
|
|
||||||
echo "This build is not over a PR, nothing to do."
|
|
||||||
fi
|
|
||||||
|
|
||||||
var_8: &yarn_install
|
|
||||||
run:
|
|
||||||
name: Running Yarn install
|
|
||||||
command: |
|
|
||||||
# Yarn's requests sometimes take more than 10mins to complete.
|
|
||||||
# Print something to stdout, to prevent CircleCI from failing due to not output.
|
|
||||||
while true; do sleep 60; echo "[`date`] Keeping alive..."; done &
|
|
||||||
KEEP_ALIVE_PID=$!
|
|
||||||
yarn install --frozen-lockfile --non-interactive
|
|
||||||
kill $KEEP_ALIVE_PID
|
|
||||||
|
|
||||||
var_9: &setup_circleci_bazel_config
|
|
||||||
run:
|
|
||||||
name: Setting up CircleCI bazel configuration
|
|
||||||
command: sudo cp .circleci/bazel.rc /etc/bazel.bazelrc
|
|
||||||
|
|
||||||
var_10: &restore_cache
|
|
||||||
restore_cache:
|
|
||||||
keys:
|
|
||||||
- *cache_key
|
|
||||||
# This fallback should be the cache_key without variables.
|
|
||||||
- v3-angular-node-10.12-
|
|
||||||
|
|
||||||
# Branch filter that can be specified for jobs that should only run on publish branches
|
|
||||||
# (e.g. master or the patch branch)
|
|
||||||
var_12: &publish_branches_filter
|
|
||||||
branches:
|
|
||||||
only:
|
|
||||||
- master
|
|
||||||
# e.g. 7.0.x, 7.1.x, etc.
|
|
||||||
- /\d+\.\d+\.x/
|
|
||||||
|
|
||||||
# Workspace initially persisted by the `install` job, and then enhanced by `test_aio` and
|
|
||||||
# `build-npm-packages`.
|
|
||||||
# https://circleci.com/docs/2.0/workflows/#using-workspaces-to-share-data-among-jobs
|
|
||||||
# https://circleci.com/blog/deep-diving-into-circleci-workspaces/
|
|
||||||
var_13: &attach_workspace
|
|
||||||
attach_workspace:
|
|
||||||
at: ~/
|
|
||||||
|
|
||||||
version: 2
|
version: 2
|
||||||
jobs:
|
jobs:
|
||||||
setup:
|
|
||||||
<<: *job_defaults
|
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
- *post_checkout
|
|
||||||
# This cache is saved in the build-npm-packages so that Bazel cache is also included.
|
|
||||||
- *restore_cache
|
|
||||||
- *init_environment
|
|
||||||
- *yarn_install
|
|
||||||
- run: yarn --cwd aio install --frozen-lockfile --non-interactive
|
|
||||||
# Make the bazel directories and add a file to them if they don't exist already so that
|
|
||||||
# persist_to_workspace does not fail.
|
|
||||||
- run: |
|
|
||||||
if [ ! -d ~/bazel_repository_cache ]; then
|
|
||||||
mkdir ~/bazel_repository_cache
|
|
||||||
touch ~/bazel_repository_cache/MARKER
|
|
||||||
fi
|
|
||||||
# Persist any changes at this point to be reused by further jobs.
|
|
||||||
# **NOTE 1 **: Folders persisted here should be kept in sync with `var_13: &attach_workspace`.
|
|
||||||
# **NOTE 2 **: To add new content to the workspace, always persist on the same root.
|
|
||||||
- persist_to_workspace:
|
|
||||||
root: ~/
|
|
||||||
paths:
|
|
||||||
- ./ng
|
|
||||||
- ./bazel_repository_cache
|
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
<<: *job_defaults
|
<<: *job_defaults
|
||||||
steps:
|
steps:
|
||||||
- *attach_workspace
|
- checkout:
|
||||||
- *init_environment
|
<<: *post_checkout
|
||||||
|
|
||||||
- run: 'yarn bazel:format -mode=check ||
|
# Check BUILD.bazel formatting before we have a node_modules directory
|
||||||
(echo "BUILD files not formatted. Please run ''yarn bazel:format''" ; exit 1)'
|
# Then we don't need any exclude pattern to avoid checking those files
|
||||||
|
- run: 'buildifier -mode=check $(find . -type f \( -name BUILD.bazel -or -name BUILD \)) ||
|
||||||
|
(echo "BUILD files not formatted. Please run ''yarn buildifier''" ; exit 1)'
|
||||||
# Run the skylark linter to check our Bazel rules
|
# Run the skylark linter to check our Bazel rules
|
||||||
- run: 'yarn bazel:lint ||
|
- run: 'find . -type f -name "*.bzl" |
|
||||||
(echo -e "\n.bzl files have lint errors. Please run ''yarn bazel:lint-fix''"; exit 1)'
|
xargs java -jar /usr/local/bin/Skylint_deploy.jar ||
|
||||||
|
(echo -e "\n.bzl files have lint errors. Please run ''yarn skylint''"; exit 1)'
|
||||||
|
|
||||||
- run: yarn gulp lint
|
- restore_cache:
|
||||||
|
key: *cache_key
|
||||||
|
|
||||||
test:
|
- run: yarn install --frozen-lockfile --non-interactive
|
||||||
|
- run: ./node_modules/.bin/gulp lint
|
||||||
|
|
||||||
|
build:
|
||||||
<<: *job_defaults
|
<<: *job_defaults
|
||||||
resource_class: xlarge
|
resource_class: xlarge
|
||||||
steps:
|
steps:
|
||||||
- *attach_workspace
|
- checkout:
|
||||||
- *init_environment
|
<<: *post_checkout
|
||||||
- *setup_circleci_bazel_config
|
# See remote cache documentation in /docs/BAZEL.md
|
||||||
|
- run: .circleci/setup_cache.sh
|
||||||
|
- run: sudo cp .circleci/bazel.rc /etc/bazel.bazelrc
|
||||||
|
- *setup-bazel-remote-cache
|
||||||
|
|
||||||
# Setup remote execution and run RBE-compatible tests.
|
- restore_cache:
|
||||||
- *setup_bazel_remote_execution
|
key: *cache_key
|
||||||
- run: yarn bazel test //... --build_tag_filters=-ivy-only --test_tag_filters=-ivy-only
|
|
||||||
|
|
||||||
# Temporary job to test what will happen when we flip the Ivy flag to true
|
- run: ls /home/circleci/bazel_repository_cache || true
|
||||||
test_ivy_aot:
|
- run: bazel info release
|
||||||
<<: *job_defaults
|
- run: bazel run @yarn//:yarn
|
||||||
resource_class: xlarge
|
# Use bazel query so that we explicitly ask for all buildable targets to be built as well
|
||||||
steps:
|
# This avoids waiting for a build command to finish before running the first test
|
||||||
- *attach_workspace
|
# See https://github.com/bazelbuild/bazel/issues/4257
|
||||||
- *init_environment
|
- run: bazel query --output=label '//modules/... union //packages/... union //tools/...' | xargs bazel test
|
||||||
- *setup_circleci_bazel_config
|
|
||||||
- *setup_bazel_remote_execution
|
|
||||||
|
|
||||||
# We need to explicitly specify the --symlink_prefix option because otherwise we would
|
- save_cache:
|
||||||
# not be able to easily find the output bin directory when uploading artifacts for size
|
key: *cache_key
|
||||||
# measurements.
|
paths:
|
||||||
- run: yarn test-ivy-aot //... --symlink_prefix=dist/
|
- "node_modules"
|
||||||
|
- "~/bazel_repository_cache"
|
||||||
# Publish bundle artifacts which will be used to calculate the size change. **Note**: Make
|
|
||||||
# sure that the size plugin from the Angular robot fetches the artifacts from this CircleCI
|
|
||||||
# job (see .github/angular-robot.yml). Additionally any artifacts need to be stored with the
|
|
||||||
# following path format: "{projectName}/{context}/{fileName}". This format is necessary
|
|
||||||
# because otherwise the bot is not able to pick up the artifacts from CircleCI. See:
|
|
||||||
# https://github.com/angular/github-robot/blob/master/functions/src/plugins/size.ts#L392-L394
|
|
||||||
- store_artifacts:
|
|
||||||
path: dist/bin/packages/core/test/bundling/hello_world/bundle.min.js
|
|
||||||
destination: core/hello_world/bundle
|
|
||||||
- store_artifacts:
|
|
||||||
path: dist/bin/packages/core/test/bundling/todo/bundle.min.js
|
|
||||||
destination: core/todo/bundle
|
|
||||||
- store_artifacts:
|
|
||||||
path: dist/bin/packages/core/test/bundling/hello_world/bundle.min.js.br
|
|
||||||
destination: core/hello_world/bundle.br
|
|
||||||
- store_artifacts:
|
|
||||||
path: dist/bin/packages/core/test/bundling/todo/bundle.min.js.br
|
|
||||||
destination: core/todo/bundle.br
|
|
||||||
|
|
||||||
test_saucelabs_bazel:
|
|
||||||
<<: *job_defaults
|
|
||||||
# In order to avoid the bottleneck of having a slow host machine, we acquire a better
|
|
||||||
# container for this job. This is necessary because we launch a lot of browsers concurrently
|
|
||||||
# and therefore the tunnel and Karma need to process a lot of file requests and tests.
|
|
||||||
resource_class: xlarge
|
|
||||||
steps:
|
|
||||||
- *attach_workspace
|
|
||||||
- *init_environment
|
|
||||||
- *setup_circleci_bazel_config
|
|
||||||
- run:
|
|
||||||
name: Preparing environment for running tests on Saucelabs.
|
|
||||||
command: setSecretVar SAUCE_ACCESS_KEY $(echo $SAUCE_ACCESS_KEY | rev)
|
|
||||||
- run:
|
|
||||||
name: Starting Saucelabs tunnel
|
|
||||||
command: ./scripts/saucelabs/start-tunnel.sh
|
|
||||||
background: true
|
|
||||||
# Waits for the Saucelabs tunnel to be ready. This ensures that we don't run tests
|
|
||||||
# too early without Saucelabs not being ready.
|
|
||||||
- run: ./scripts/saucelabs/wait-for-tunnel.sh
|
|
||||||
# All web tests are contained within a single //:test_web_all target for Saucelabs
|
|
||||||
# as running each set of tests as a separate target will attempt to acquire too
|
|
||||||
# many browsers on Saucelabs (7 per target currently) and some tests will always
|
|
||||||
# fail to acquire browsers. For example:
|
|
||||||
# 14 02 2019 19:52:33.170:WARN [launcher]: chrome beta on SauceLabs have not captured in 180000 ms, killing.
|
|
||||||
# //packages/forms/test:web_test_sauce TIMEOUT in 315.0s
|
|
||||||
- run: yarn bazel test --config=saucelabs //:test_web_all
|
|
||||||
- run: ./scripts/saucelabs/stop-tunnel.sh
|
|
||||||
|
|
||||||
test_aio:
|
|
||||||
<<: *job_defaults
|
|
||||||
docker:
|
|
||||||
# Needed because the AIO tests and the PWA score test depend on Chrome being available.
|
|
||||||
- image: *browsers_docker_image
|
|
||||||
steps:
|
|
||||||
- *attach_workspace
|
|
||||||
- *init_environment
|
|
||||||
# Build aio
|
|
||||||
- run: yarn --cwd aio build --progress=false
|
|
||||||
# Lint the code
|
|
||||||
- run: yarn --cwd aio lint
|
|
||||||
# Run PWA-score tests
|
|
||||||
# (Run before unit and e2e tests, which destroy the `dist/` directory.)
|
|
||||||
- run: yarn --cwd aio test-pwa-score-localhost $CI_AIO_MIN_PWA_SCORE
|
|
||||||
# Check the bundle sizes.
|
|
||||||
# (Run before unit and e2e tests, which destroy the `dist/` directory.)
|
|
||||||
- run: yarn --cwd aio payload-size
|
|
||||||
# Run unit tests
|
|
||||||
- run: yarn --cwd aio test --progress=false --watch=false
|
|
||||||
# Run e2e tests
|
|
||||||
- run: yarn --cwd aio e2e --configuration=ci
|
|
||||||
# Run unit tests for Firebase redirects
|
|
||||||
- run: yarn --cwd aio redirects-test
|
|
||||||
|
|
||||||
deploy_aio:
|
|
||||||
<<: *job_defaults
|
|
||||||
docker:
|
|
||||||
# Needed because before deploying the deploy-production script runs the PWA score tests.
|
|
||||||
- image: *browsers_docker_image
|
|
||||||
steps:
|
|
||||||
- *attach_workspace
|
|
||||||
- *init_environment
|
|
||||||
# Deploy angular.io to production (if necessary)
|
|
||||||
- run: setPublicVar CI_STABLE_BRANCH "$(npm info @angular/core dist-tags.latest | sed -r 's/^\s*([0-9]+\.[0-9]+)\.[0-9]+.*$/\1.x/')"
|
|
||||||
- run: yarn --cwd aio deploy-production
|
|
||||||
|
|
||||||
test_aio_local:
|
|
||||||
<<: *job_defaults
|
|
||||||
docker:
|
|
||||||
# Needed because the AIO tests and the PWA score test depend on Chrome being available.
|
|
||||||
- image: *browsers_docker_image
|
|
||||||
steps:
|
|
||||||
- *attach_workspace
|
|
||||||
- *init_environment
|
|
||||||
# Build aio (with local Angular packages)
|
|
||||||
- run: yarn --cwd aio build-local --progress=false
|
|
||||||
# Run PWA-score tests
|
|
||||||
# (Run before unit and e2e tests, which destroy the `dist/` directory.)
|
|
||||||
- run: yarn --cwd aio test-pwa-score-localhost $CI_AIO_MIN_PWA_SCORE
|
|
||||||
# Run unit tests
|
|
||||||
- run: yarn --cwd aio test --progress=false --watch=false
|
|
||||||
# Run e2e tests
|
|
||||||
- run: yarn --cwd aio e2e --configuration=ci
|
|
||||||
|
|
||||||
test_aio_local_ivy:
|
|
||||||
<<: *job_defaults
|
|
||||||
steps:
|
|
||||||
- *attach_workspace
|
|
||||||
- *init_environment
|
|
||||||
# Build aio with Ivy (using local Angular packages)
|
|
||||||
- run: yarn --cwd aio build-with-ivy --progress=false
|
|
||||||
|
|
||||||
test_aio_tools:
|
|
||||||
<<: *job_defaults
|
|
||||||
steps:
|
|
||||||
- *attach_workspace
|
|
||||||
- *init_environment
|
|
||||||
# Install
|
|
||||||
- run: yarn --cwd aio install --frozen-lockfile --non-interactive
|
|
||||||
- run: yarn --cwd aio extract-cli-command-docs
|
|
||||||
# Run tools tests
|
|
||||||
- run: yarn --cwd aio tools-test
|
|
||||||
- run: ./aio/aio-builds-setup/scripts/test.sh
|
|
||||||
|
|
||||||
test_docs_examples:
|
|
||||||
<<: *job_defaults
|
|
||||||
docker:
|
|
||||||
# Needed because the example e2e tests depend on Chrome.
|
|
||||||
- image: *browsers_docker_image
|
|
||||||
parallelism: 4
|
|
||||||
resource_class: xlarge
|
|
||||||
steps:
|
|
||||||
- *attach_workspace
|
|
||||||
- *init_environment
|
|
||||||
# Install aio
|
|
||||||
- run: yarn --cwd aio install --frozen-lockfile --non-interactive
|
|
||||||
# Run examples tests. The "CIRCLE_NODE_INDEX" will be set if "parallelism" is enabled.
|
|
||||||
# Since the parallelism is set to "3", there will be three parallel CircleCI containers
|
|
||||||
# with either "0", "1" or "2" as node index. This can be passed to the "--shard" argument.
|
|
||||||
- run: yarn --cwd aio example-e2e --setup --local --cliSpecsConcurrency=5 --shard=${CIRCLE_NODE_INDEX}/${CIRCLE_NODE_TOTAL}
|
|
||||||
|
|
||||||
test_docs_examples_ivy:
|
|
||||||
<<: *job_defaults
|
|
||||||
docker:
|
|
||||||
# Needed because the example e2e tests depend on Chrome.
|
|
||||||
- image: *browsers_docker_image
|
|
||||||
resource_class: xlarge
|
|
||||||
# We increase the parallelism here to five while the "test_docs_examples" job runs with
|
|
||||||
# a parallelism of four. This is necessary because this job also need to run NGCC which
|
|
||||||
# takes up more time and we don't want these jobs to impact the overall CI turnaround.
|
|
||||||
parallelism: 5
|
|
||||||
steps:
|
|
||||||
- *attach_workspace
|
|
||||||
- *init_environment
|
|
||||||
# Install aio
|
|
||||||
- run: yarn --cwd aio install --frozen-lockfile --non-interactive
|
|
||||||
# Rename the Ivy packages dist folder to "dist/packages-dist" as the AIO
|
|
||||||
# package installer picks up the locally built packages from that location.
|
|
||||||
# *Note*: We could also adjust the packages installer, but given we won't have
|
|
||||||
# two different folders of Angular distributions in the future, we should keep
|
|
||||||
# the packages installer unchanged.
|
|
||||||
- run: mv dist/packages-dist-ivy-aot dist/packages-dist
|
|
||||||
# Run examples tests with ivy. The "CIRCLE_NODE_INDEX" will be set if "parallelism" is enabled.
|
|
||||||
# Since the parallelism is set to "3", there will be three parallel CircleCI containers
|
|
||||||
# with either "0", "1" or "2" as node index. This can be passed to the "--shard" argument.
|
|
||||||
- run: yarn --cwd aio example-e2e --setup --local --ivy --cliSpecsConcurrency=5 --shard=${CIRCLE_NODE_INDEX}/${CIRCLE_NODE_TOTAL}
|
|
||||||
|
|
||||||
# This job should only be run on PR builds, where `CI_PULL_REQUEST` is not `false`.
|
|
||||||
aio_preview:
|
aio_preview:
|
||||||
<<: *job_defaults
|
<<: *job_defaults
|
||||||
environment:
|
environment:
|
||||||
AIO_SNAPSHOT_ARTIFACT_PATH: &aio_preview_artifact_path 'aio/tmp/snapshot.tgz'
|
AIO_SNAPSHOT_ARTIFACT_PATH: &aio_preview_artifact_path 'aio/tmp/snapshot.tgz'
|
||||||
steps:
|
steps:
|
||||||
- *attach_workspace
|
- checkout:
|
||||||
- *init_environment
|
<<: *post_checkout
|
||||||
- run: ./aio/scripts/build-artifacts.sh $AIO_SNAPSHOT_ARTIFACT_PATH $CI_PULL_REQUEST $CI_COMMIT
|
- restore_cache:
|
||||||
|
key: *cache_key
|
||||||
|
- run: ./aio/scripts/build-artifacts.sh $AIO_SNAPSHOT_ARTIFACT_PATH
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: *aio_preview_artifact_path
|
path: *aio_preview_artifact_path
|
||||||
# The `destination` needs to be kept in synch with the value of
|
# The `destination` needs to be kept in synch with the value of
|
||||||
# `AIO_ARTIFACT_PATH` in `aio/aio-builds-setup/Dockerfile`
|
# `AIO_ARTIFACT_PATH` in `aio/aio-builds-setup/Dockerfile`
|
||||||
destination: aio/dist/aio-snapshot.tgz
|
destination: aio/dist/aio-snapshot.tgz
|
||||||
- run: node ./aio/scripts/create-preview $CIRCLE_BUILD_NUM
|
|
||||||
|
|
||||||
# This job should only be run on PR builds, where `CI_PULL_REQUEST` is not `false`.
|
|
||||||
test_aio_preview:
|
|
||||||
<<: *job_defaults
|
|
||||||
docker:
|
|
||||||
# Needed because the test-preview script runs e2e tests and the PWA score test with Chrome.
|
|
||||||
- image: *browsers_docker_image
|
|
||||||
steps:
|
|
||||||
- *attach_workspace
|
|
||||||
- *init_environment
|
|
||||||
- run: yarn --cwd aio install --frozen-lockfile --non-interactive
|
|
||||||
- run:
|
|
||||||
name: Wait for preview and run tests
|
|
||||||
command: node aio/scripts/test-preview.js $CI_PULL_REQUEST $CI_COMMIT $CI_AIO_MIN_PWA_SCORE
|
|
||||||
|
|
||||||
|
|
||||||
# The `build-npm-packages` tasks exist for backwards-compatibility with old scripts and
|
|
||||||
# tests that rely on the pre-Bazel `dist/packages-dist` output structure (build.sh).
|
|
||||||
# Having multiple jobs that independently build in this manner duplicates some work; we build
|
|
||||||
# the bazel packages more than once. Even though we have a remote cache, these jobs will
|
|
||||||
# typically run in parallel so up-to-date outputs will not be available at the time the build
|
|
||||||
# starts.
|
|
||||||
|
|
||||||
# Build the view engine npm packages. No new jobs should depend on this.
|
|
||||||
build-npm-packages:
|
|
||||||
<<: *job_defaults
|
|
||||||
resource_class: xlarge
|
|
||||||
steps:
|
|
||||||
- *attach_workspace
|
|
||||||
- *init_environment
|
|
||||||
- *setup_circleci_bazel_config
|
|
||||||
- *setup_bazel_remote_execution
|
|
||||||
|
|
||||||
- run: scripts/build-packages-dist.sh
|
|
||||||
|
|
||||||
# Save the npm packages from //packages/... for other workflow jobs to read
|
|
||||||
- persist_to_workspace:
|
|
||||||
root: ~/
|
|
||||||
paths:
|
|
||||||
- ng/dist/packages-dist
|
|
||||||
|
|
||||||
# Save dependencies and bazel repository cache to use on subsequent runs.
|
|
||||||
- save_cache:
|
|
||||||
key: *cache_key
|
|
||||||
paths:
|
|
||||||
- "node_modules"
|
|
||||||
- "aio/node_modules"
|
|
||||||
- "~/bazel_repository_cache"
|
|
||||||
|
|
||||||
# Build the ivy npm packages.
|
|
||||||
build-ivy-npm-packages:
|
|
||||||
<<: *job_defaults
|
|
||||||
resource_class: xlarge
|
|
||||||
steps:
|
|
||||||
- *attach_workspace
|
|
||||||
- *init_environment
|
|
||||||
- *setup_circleci_bazel_config
|
|
||||||
- *setup_bazel_remote_execution
|
|
||||||
|
|
||||||
- run: scripts/build-ivy-npm-packages.sh
|
|
||||||
|
|
||||||
# Save the npm packages from //packages/... for other workflow jobs to read
|
|
||||||
- persist_to_workspace:
|
|
||||||
root: ~/
|
|
||||||
paths:
|
|
||||||
- ng/dist/packages-dist-ivy-aot
|
|
||||||
|
|
||||||
# We run the integration tests outside of Bazel for now.
|
|
||||||
# They are a separate workflow job so that they can be easily re-run.
|
|
||||||
# When the tests are ported to bazel test targets, they should move to the "test"
|
|
||||||
# job above, as part of the bazel test command. That has flaky_test_attempts so the
|
|
||||||
# need to re-run manually should be alleviated.
|
|
||||||
# See comments inside the integration/run_tests.sh script.
|
|
||||||
integration_test:
|
|
||||||
<<: *job_defaults
|
|
||||||
parallelism: 4
|
|
||||||
docker:
|
|
||||||
# Needed because the integration tests expect Chrome to be installed (e.g cli-hello-world)
|
|
||||||
- image: *browsers_docker_image
|
|
||||||
# Note: we run Bazel in one of the integration tests, and it can consume >2G
|
|
||||||
# of memory. Together with the system under test, this can exhaust the RAM
|
|
||||||
# on a 4G worker so we use a larger machine here too.
|
|
||||||
resource_class: xlarge
|
|
||||||
steps:
|
|
||||||
- *attach_workspace
|
|
||||||
- *init_environment
|
|
||||||
# Runs the integration tests in parallel across multiple CircleCI container instances. The
|
|
||||||
# amount of container nodes for this job is controlled by the "parallelism" option.
|
|
||||||
- run: ./integration/run_tests.sh ${CIRCLE_NODE_INDEX} ${CIRCLE_NODE_TOTAL}
|
|
||||||
|
|
||||||
# This job updates the content of repos like github.com/angular/core-builds
|
|
||||||
# for every green build on angular/angular.
|
|
||||||
publish_snapshot:
|
|
||||||
<<: *job_defaults
|
|
||||||
steps:
|
|
||||||
# See below - ideally this job should not trigger for non-upstream builds.
|
|
||||||
# But since it does, we have to check this condition.
|
|
||||||
- run:
|
|
||||||
name: Skip this job for Pull Requests and Fork builds
|
|
||||||
# Note: Using `CIRCLE_*` env variables (instead of those defined in `env.sh` so that this
|
|
||||||
# step can be run before `init_environment`.
|
|
||||||
command: >
|
|
||||||
if [[ -n "${CIRCLE_PR_NUMBER}" ]] ||
|
|
||||||
[[ "$CIRCLE_PROJECT_USERNAME" != "angular" ]] ||
|
|
||||||
[[ "$CIRCLE_PROJECT_REPONAME" != "angular" ]]; then
|
|
||||||
circleci step halt
|
|
||||||
fi
|
|
||||||
- *attach_workspace
|
|
||||||
- *init_environment
|
|
||||||
# CircleCI has a config setting to force SSH for all github connections
|
|
||||||
# This is not compatible with our mechanism of using a Personal Access Token
|
|
||||||
# Clear the global setting
|
|
||||||
- run: git config --global --unset "url.ssh://git@github.com.insteadof"
|
|
||||||
- run:
|
|
||||||
name: Decrypt github credentials
|
|
||||||
command: 'openssl aes-256-cbc -d -in .circleci/github_token -k "${KEY}" -out ~/.git_credentials'
|
|
||||||
- run: ./scripts/ci/publish-build-artifacts.sh
|
|
||||||
|
|
||||||
aio_monitoring:
|
aio_monitoring:
|
||||||
<<: *job_defaults
|
<<: *job_defaults
|
||||||
docker:
|
|
||||||
# This job needs Chrome to be globally installed because the tests run with Protractor
|
|
||||||
# which does not load the browser through the Bazel webtesting rules.
|
|
||||||
- image: *browsers_docker_image
|
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout:
|
||||||
- *post_checkout
|
<<: *post_checkout
|
||||||
- *restore_cache
|
|
||||||
- *init_environment
|
|
||||||
- run:
|
|
||||||
name: Run tests against the deployed apps
|
|
||||||
command: ./aio/scripts/test-production.sh $CI_AIO_MIN_PWA_SCORE
|
|
||||||
- run:
|
|
||||||
name: Notify caretaker about failure
|
|
||||||
# `$SLACK_CARETAKER_WEBHOOK_URL` is a secret env var defined in CircleCI project settings.
|
|
||||||
# The URL comes from https://angular-team.slack.com/apps/A0F7VRE7N-circleci.
|
|
||||||
command: 'curl --request POST --header "Content-Type: application/json" --data "{\"text\":\":x: \`$CIRCLE_JOB\` job failed on build $CIRCLE_BUILD_NUM: $CIRCLE_BUILD_URL :scream:\"}" $SLACK_CARETAKER_WEBHOOK_URL'
|
|
||||||
when: on_fail
|
|
||||||
|
|
||||||
legacy-unit-tests-saucelabs:
|
|
||||||
<<: *job_defaults
|
|
||||||
# In order to avoid the bottleneck of having a slow host machine, we acquire a better
|
|
||||||
# container for this job. This is necessary because we launch a lot of browsers concurrently
|
|
||||||
# and therefore the tunnel and Karma need to process a lot of file requests and tests.
|
|
||||||
resource_class: xlarge
|
|
||||||
steps:
|
|
||||||
- *attach_workspace
|
|
||||||
- *init_environment
|
|
||||||
- run:
|
|
||||||
name: Preparing environment for running tests on Saucelabs.
|
|
||||||
command: |
|
|
||||||
setPublicVar KARMA_JS_BROWSERS $(node -e 'console.log(require("./browser-providers.conf").sauceAliases.CI_REQUIRED.join(","))')
|
|
||||||
setSecretVar SAUCE_ACCESS_KEY $(echo $SAUCE_ACCESS_KEY | rev)
|
|
||||||
- run:
|
|
||||||
name: Starting Saucelabs tunnel
|
|
||||||
command: ./scripts/saucelabs/start-tunnel.sh
|
|
||||||
background: true
|
|
||||||
- run: yarn tsc -p packages
|
|
||||||
- run: yarn tsc -p modules
|
|
||||||
# Waits for the Saucelabs tunnel to be ready. This ensures that we don't run tests
|
|
||||||
# too early without Saucelabs not being ready.
|
|
||||||
- run: ./scripts/saucelabs/wait-for-tunnel.sh
|
|
||||||
- run: yarn karma start ./karma-js.conf.js --single-run --browsers=${KARMA_JS_BROWSERS}
|
|
||||||
- run: ./scripts/saucelabs/stop-tunnel.sh
|
|
||||||
|
|
||||||
legacy-misc-tests:
|
|
||||||
<<: *job_defaults
|
|
||||||
steps:
|
|
||||||
- *attach_workspace
|
|
||||||
- *init_environment
|
|
||||||
- run: yarn gulp check-cycle
|
|
||||||
# TODO: disabled because the Bazel packages-dist does not seem to have map files for
|
|
||||||
# the ESM5/ES2015 output. See: https://github.com/angular/angular/issues/27966
|
|
||||||
# - run: yarn gulp source-map-test
|
|
||||||
|
|
||||||
# Job to run unit tests from angular/material2. Needs a browser since all
|
|
||||||
# component unit tests assume they're running in the browser environment.
|
|
||||||
material-unit-tests:
|
|
||||||
<<: *job_defaults
|
|
||||||
resource_class: xlarge
|
|
||||||
docker:
|
|
||||||
- image: *browsers_docker_image
|
|
||||||
# The Material unit tests support splitting the browsers across multiple CircleCI
|
|
||||||
# instances. Since by default this job launches two browsers, we run each browser
|
|
||||||
# in its own container instance.
|
|
||||||
# https://github.com/angular/material2/blob/7baeaa797b19da2d2998f0d26f6fede3c8a13714/test/karma.conf.js#L107-L110
|
|
||||||
parallelism: 2
|
|
||||||
environment:
|
|
||||||
# The Material unit tests also support launching the same browser multiple times by
|
|
||||||
# sharding individual specs across the defined multiple instances.
|
|
||||||
# See: https://github.com/angular/material2/blob/7baeaa797b19da2d2998f0d26f6fede3c8a13714/test/karma.conf.js#L113-L116
|
|
||||||
KARMA_PARALLEL_BROWSERS: 3
|
|
||||||
steps:
|
|
||||||
- *attach_workspace
|
|
||||||
- *init_environment
|
|
||||||
- run:
|
|
||||||
name: "Cloning Material repository"
|
|
||||||
command: ./scripts/ci/clone_angular_material_repo.sh
|
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
# Material directory must be kept in sync with the `$MATERIAL_REPO_TMP_DIR` env variable.
|
key: *cache_key
|
||||||
# It needs to be hardcoded here, because env variables interpolation is not supported.
|
- run: xvfb-run --auto-servernum ./aio/scripts/test-production.sh
|
||||||
keys:
|
|
||||||
- v2-angular-material-{{ checksum "/tmp/material2/yarn.lock" }}
|
|
||||||
- v2-angular-material-
|
|
||||||
- run:
|
|
||||||
name: Installing Material dependencies.
|
|
||||||
command: yarn --cwd ${MATERIAL_REPO_TMP_DIR} install --frozen-lockfile --non-interactive
|
|
||||||
# Save the cache before we run the Material unit tests script. This is necessary
|
|
||||||
# because we don't want to cache the node modules which have been modified to contain
|
|
||||||
# the attached Ivy package output.
|
|
||||||
- save_cache:
|
|
||||||
# Material directory must be kept in sync with the `$MATERIAL_REPO_TMP_DIR` env variable.
|
|
||||||
# It needs to be hardcoded here, because env variables interpolation is not supported.
|
|
||||||
key: v2-angular-material-{{ checksum "/tmp/material2/yarn.lock" }}
|
|
||||||
paths:
|
|
||||||
- "/tmp/material2/node_modules"
|
|
||||||
- run:
|
|
||||||
name: "Running Material unit tests"
|
|
||||||
command: ./scripts/ci/run_angular_material_unit_tests.sh
|
|
||||||
|
|
||||||
workflows:
|
workflows:
|
||||||
version: 2
|
version: 2
|
||||||
default_workflow:
|
default_workflow:
|
||||||
jobs:
|
jobs:
|
||||||
- setup
|
- lint
|
||||||
- lint:
|
- build
|
||||||
requires:
|
- aio_preview
|
||||||
- setup
|
aio_monitoring:
|
||||||
- test:
|
|
||||||
requires:
|
|
||||||
- setup
|
|
||||||
- test_ivy_aot:
|
|
||||||
requires:
|
|
||||||
- setup
|
|
||||||
- build-npm-packages:
|
|
||||||
requires:
|
|
||||||
- setup
|
|
||||||
- build-ivy-npm-packages:
|
|
||||||
requires:
|
|
||||||
- setup
|
|
||||||
- test_aio:
|
|
||||||
requires:
|
|
||||||
- setup
|
|
||||||
- legacy-unit-tests-saucelabs:
|
|
||||||
requires:
|
|
||||||
- setup
|
|
||||||
- deploy_aio:
|
|
||||||
requires:
|
|
||||||
- test_aio
|
|
||||||
- legacy-misc-tests:
|
|
||||||
requires:
|
|
||||||
- build-npm-packages
|
|
||||||
- test_aio_local:
|
|
||||||
requires:
|
|
||||||
- build-npm-packages
|
|
||||||
- test_aio_local_ivy:
|
|
||||||
requires:
|
|
||||||
- build-npm-packages
|
|
||||||
- test_aio_tools:
|
|
||||||
requires:
|
|
||||||
- build-npm-packages
|
|
||||||
- test_docs_examples:
|
|
||||||
requires:
|
|
||||||
- build-npm-packages
|
|
||||||
- test_docs_examples_ivy:
|
|
||||||
requires:
|
|
||||||
- build-ivy-npm-packages
|
|
||||||
- aio_preview:
|
|
||||||
requires:
|
|
||||||
- setup
|
|
||||||
# Only run on PR builds. (There can be no previews for non-PR builds.)
|
|
||||||
filters:
|
|
||||||
branches:
|
|
||||||
only: /pull\/\d+/
|
|
||||||
- test_aio_preview:
|
|
||||||
requires:
|
|
||||||
- aio_preview
|
|
||||||
- integration_test:
|
|
||||||
requires:
|
|
||||||
- build-npm-packages
|
|
||||||
- publish_snapshot:
|
|
||||||
# Note: no filters on this job because we want it to run for all upstream branches
|
|
||||||
# We'd really like to filter out pull requests here, but not yet available:
|
|
||||||
# https://discuss.circleci.com/t/workflows-pull-request-filter/14396/4
|
|
||||||
# Instead, the job just exits immediately at the first step.
|
|
||||||
requires:
|
|
||||||
# Only publish if tests and integration tests pass
|
|
||||||
- test
|
|
||||||
- test_ivy_aot
|
|
||||||
- integration_test
|
|
||||||
# Only publish if `aio`/`docs` tests using the locally built Angular packages pass
|
|
||||||
- test_aio_local
|
|
||||||
- test_aio_local_ivy
|
|
||||||
- test_docs_examples
|
|
||||||
- test_docs_examples_ivy
|
|
||||||
# Get the artifacts to publish from the build-packages-dist job
|
|
||||||
# since the publishing script expects the legacy outputs layout.
|
|
||||||
- build-npm-packages
|
|
||||||
- build-ivy-npm-packages
|
|
||||||
- legacy-unit-tests-saucelabs
|
|
||||||
- legacy-misc-tests
|
|
||||||
- material-unit-tests:
|
|
||||||
requires:
|
|
||||||
- build-ivy-npm-packages
|
|
||||||
|
|
||||||
saucelabs_tests:
|
|
||||||
jobs:
|
jobs:
|
||||||
- setup
|
- aio_monitoring
|
||||||
- test_saucelabs_bazel:
|
|
||||||
requires:
|
|
||||||
- setup
|
|
||||||
triggers:
|
triggers:
|
||||||
- schedule:
|
- schedule:
|
||||||
# Runs the Saucelabs legacy tests every hour. We still want to run Saucelabs
|
cron: "0 0 * * *"
|
||||||
# frequently as the caretaker needs up-to-date results when merging PRs or creating
|
filters:
|
||||||
# a new release. Also we primarily moved the Saucelabs job into a cronjob that doesn't
|
branches:
|
||||||
# run for PRs, in order to ensure that PRs are not affected by Saucelabs flakiness or
|
only:
|
||||||
# incidents. This is still guaranteed (even if we run the job every hour).
|
- master
|
||||||
cron: "0 * * * *"
|
notify:
|
||||||
filters: *publish_branches_filter
|
webhooks:
|
||||||
|
- url: https://ngbuilds.io/circle-build
|
||||||
# This job is currently disabled due to a version skew problem.
|
|
||||||
# More info is available here: https://github.com/angular/angular/issues/30101
|
|
||||||
# aio_monitoring:
|
|
||||||
# jobs:
|
|
||||||
# - aio_monitoring
|
|
||||||
# triggers:
|
|
||||||
# - schedule:
|
|
||||||
# # Runs AIO monitoring job at 00:00AM every day.
|
|
||||||
# cron: "0 0 * * *"
|
|
||||||
# filters:
|
|
||||||
# branches:
|
|
||||||
# only:
|
|
||||||
# - master
|
|
||||||
|
|
||||||
# TODO:
|
|
||||||
# - don't build the g3 branch
|
|
||||||
# - verify that we are bootstrapping with the right yarn version coming from the docker image
|
|
||||||
# - check local chrome version pulled from docker image
|
|
||||||
# - remove /tools/ngcontainer
|
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
####################################################################################################
|
|
||||||
# Helpers for defining environment variables for CircleCI.
|
|
||||||
#
|
|
||||||
# In CircleCI, each step runs in a new shell. The way to share ENV variables across steps is to
|
|
||||||
# export them from `$BASH_ENV`, which is automatically sourced at the beginning of every step (for
|
|
||||||
# the default `bash` shell).
|
|
||||||
#
|
|
||||||
# See also https://circleci.com/docs/2.0/env-vars/#using-bash_env-to-set-environment-variables.
|
|
||||||
####################################################################################################
|
|
||||||
|
|
||||||
# Set and print an environment variable.
|
|
||||||
#
|
|
||||||
# Use this function for setting environment variables that are public, i.e. it is OK for them to be
|
|
||||||
# visible to anyone through the CI logs.
|
|
||||||
#
|
|
||||||
# Usage: `setPublicVar <name> <value>`
|
|
||||||
function setPublicVar() {
|
|
||||||
setSecretVar $1 "$2";
|
|
||||||
echo "$1=$2";
|
|
||||||
}
|
|
||||||
|
|
||||||
# Set (without printing) an environment variable.
|
|
||||||
#
|
|
||||||
# Use this function for setting environment variables that are secret, i.e. should not be visible to
|
|
||||||
# everyone through the CI logs.
|
|
||||||
#
|
|
||||||
# Usage: `setSecretVar <name> <value>`
|
|
||||||
function setSecretVar() {
|
|
||||||
# WARNING: Secrets (e.g. passwords, access tokens) should NOT be printed.
|
|
||||||
# (Keep original shell options to restore at the end.)
|
|
||||||
local -r originalShellOptions=$(set +o);
|
|
||||||
set +x -eu -o pipefail;
|
|
||||||
|
|
||||||
echo "export $1=\"${2:-}\";" >> $BASH_ENV;
|
|
||||||
|
|
||||||
# Restore original shell options.
|
|
||||||
eval "$originalShellOptions";
|
|
||||||
}
|
|
@ -1,76 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# Variables
|
|
||||||
readonly projectDir=$(realpath "$(dirname ${BASH_SOURCE[0]})/..")
|
|
||||||
readonly envHelpersPath="$projectDir/.circleci/env-helpers.inc.sh";
|
|
||||||
readonly getCommitRangePath="$projectDir/.circleci/get-commit-range.js";
|
|
||||||
|
|
||||||
# Load helpers and make them available everywhere (through `$BASH_ENV`).
|
|
||||||
source $envHelpersPath;
|
|
||||||
echo "source $envHelpersPath;" >> $BASH_ENV;
|
|
||||||
|
|
||||||
|
|
||||||
####################################################################################################
|
|
||||||
# Define PUBLIC environment variables for CircleCI.
|
|
||||||
####################################################################################################
|
|
||||||
# See https://circleci.com/docs/2.0/env-vars/#built-in-environment-variables for more info.
|
|
||||||
####################################################################################################
|
|
||||||
setPublicVar PROJECT_ROOT "$projectDir";
|
|
||||||
setPublicVar CI_AIO_MIN_PWA_SCORE "95";
|
|
||||||
# This is the branch being built; e.g. `pull/12345` for PR builds.
|
|
||||||
setPublicVar CI_BRANCH "$CIRCLE_BRANCH";
|
|
||||||
# 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";
|
|
||||||
# `CI_COMMIT_RANGE` will only be available when `CIRCLE_COMPARE_URL` is also available (or can be
|
|
||||||
# retrieved via `get-compare-url.js`), i.e. on push builds (a.k.a. non-PR, non-scheduled builds and
|
|
||||||
# rerun workflows of such builds). That is fine, since we only need it in push builds.
|
|
||||||
setPublicVar CI_COMMIT_RANGE "`[[ ${CIRCLE_PR_NUMBER:-false} != false ]] && echo "" || node $getCommitRangePath "$CIRCLE_BUILD_NUM" "$CIRCLE_COMPARE_URL"`";
|
|
||||||
setPublicVar CI_PULL_REQUEST "${CIRCLE_PR_NUMBER:-false}";
|
|
||||||
setPublicVar CI_REPO_NAME "$CIRCLE_PROJECT_REPONAME";
|
|
||||||
setPublicVar CI_REPO_OWNER "$CIRCLE_PROJECT_USERNAME";
|
|
||||||
|
|
||||||
|
|
||||||
####################################################################################################
|
|
||||||
# Define SECRET environment variables for CircleCI.
|
|
||||||
####################################################################################################
|
|
||||||
setSecretVar CI_SECRET_AIO_DEPLOY_FIREBASE_TOKEN "$AIO_DEPLOY_TOKEN";
|
|
||||||
setSecretVar CI_SECRET_PAYLOAD_FIREBASE_TOKEN "$ANGULAR_PAYLOAD_TOKEN";
|
|
||||||
|
|
||||||
|
|
||||||
####################################################################################################
|
|
||||||
# Define SauceLabs environment variables for CircleCI.
|
|
||||||
####################################################################################################
|
|
||||||
# In order to have a meaningful SauceLabs badge on the repo page,
|
|
||||||
# the angular2-ci account is used only when pushing commits to master;
|
|
||||||
# in all other cases, the regular angular-ci account is used.
|
|
||||||
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_LOG_FILE /tmp/angular/sauce-connect.log
|
|
||||||
setPublicVar SAUCE_READY_FILE /tmp/angular/sauce-connect-ready-file.lock
|
|
||||||
setPublicVar SAUCE_PID_FILE /tmp/angular/sauce-connect-pid-file.lock
|
|
||||||
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
|
|
||||||
# acquire CircleCI instances for too long if sauceconnect failed, we need a connect timeout.
|
|
||||||
setPublicVar SAUCE_READY_FILE_TIMEOUT 120
|
|
||||||
|
|
||||||
####################################################################################################
|
|
||||||
# Define environment variables for the Angular Material 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.
|
|
||||||
setPublicVar MATERIAL_REPO_TMP_DIR "/tmp/material2"
|
|
||||||
setPublicVar MATERIAL_REPO_URL "https://github.com/angular/material2.git"
|
|
||||||
setPublicVar MATERIAL_REPO_BRANCH "ivy-2019"
|
|
||||||
|
|
||||||
# Source `$BASH_ENV` to make the variables available immediately.
|
|
||||||
source $BASH_ENV;
|
|
Binary file not shown.
@ -1,159 +0,0 @@
|
|||||||
#!/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();
|
|
||||||
});
|
|
||||||
}
|
|
Binary file not shown.
@ -1,107 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Usage (cli):
|
|
||||||
* ```
|
|
||||||
* node create-preview <build-number> <job-name> <webhook-url>
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* Usage (JS):
|
|
||||||
* ```js
|
|
||||||
* require('./trigger-webhook').
|
|
||||||
* triggerWebhook(buildNumber, jobName, webhookUrl).
|
|
||||||
* then(...);
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* Triggers a notification webhook with CircleCI specific info.
|
|
||||||
*
|
|
||||||
* It can be used for notifying external servers and trigger operations based on CircleCI job status
|
|
||||||
* (e.g. triggering the creation of a preview based on previously stored build atrifacts).
|
|
||||||
*
|
|
||||||
* The body of the sent payload is of the form:
|
|
||||||
* ```json
|
|
||||||
* {
|
|
||||||
* "payload": {
|
|
||||||
* "build_num": ${buildNumber}
|
|
||||||
* "build_parameters": {
|
|
||||||
* "CIRCLE_JOB": "${jobName}"
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* When used from JS, it returns a promise which resolves to an object of the form:
|
|
||||||
* ```json
|
|
||||||
* {
|
|
||||||
* "statucCode": ${statusCode},
|
|
||||||
* "responseText": "${responseText}"
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* NOTE:
|
|
||||||
* - When used from the cli, the command will exit with an error code if the response's status code
|
|
||||||
* is outside the [200, 400) range.
|
|
||||||
* - When used from JS, the returned promise will be resolved, even if the response's status code is
|
|
||||||
* outside the [200, 400) range. It is up to the caller to decide how this should be handled.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Imports
|
|
||||||
const {request} = require('https');
|
|
||||||
|
|
||||||
// Exports
|
|
||||||
module.exports = {
|
|
||||||
triggerWebhook,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Run
|
|
||||||
if (require.resolve === module) {
|
|
||||||
_main(process.argv.slice(2));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helpers
|
|
||||||
function _main(args) {
|
|
||||||
triggerWebhook(...args).
|
|
||||||
then(({statusCode, responseText}) => (200 <= statusCode && statusCode < 400) ?
|
|
||||||
console.log(`Status: ${statusCode}\n${responseText}`) :
|
|
||||||
Promise.reject(new Error(`Request failed (status: ${statusCode}): ${responseText}`))).
|
|
||||||
catch(err => {
|
|
||||||
console.error(err);
|
|
||||||
process.exit(1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function postJson(url, data) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const opts = {method: 'post', headers: {'Content-Type': 'application/json'}};
|
|
||||||
const onResponse = res => {
|
|
||||||
const statusCode = res.statusCode || -1;
|
|
||||||
let responseText = '';
|
|
||||||
|
|
||||||
res.
|
|
||||||
on('error', reject).
|
|
||||||
on('data', d => responseText += d).
|
|
||||||
on('end', () => resolve({statusCode, responseText}));
|
|
||||||
};
|
|
||||||
|
|
||||||
request(url, opts, onResponse).
|
|
||||||
on('error', reject).
|
|
||||||
end(JSON.stringify(data));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function triggerWebhook(buildNumber, jobName, webhookUrl) {
|
|
||||||
if (!buildNumber || !jobName || !webhookUrl || isNaN(buildNumber)) {
|
|
||||||
throw new Error(
|
|
||||||
'Missing or invalid arguments.\n' +
|
|
||||||
'Expected: buildNumber (number), jobName (string), webhookUrl (string)');
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = {
|
|
||||||
payload: {
|
|
||||||
build_num: +buildNumber,
|
|
||||||
build_parameters: {CIRCLE_JOB: jobName},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return postJson(webhookUrl, data);
|
|
||||||
}
|
|
@ -1,103 +0,0 @@
|
|||||||
ARG core=mcr.microsoft.com/windows/servercore:1809
|
|
||||||
ARG target=mcr.microsoft.com/powershell:windowsservercore-1809
|
|
||||||
|
|
||||||
FROM $core as download
|
|
||||||
|
|
||||||
ARG node_version=10.13.0
|
|
||||||
ARG yarn_version=1.13.0
|
|
||||||
|
|
||||||
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
|
|
||||||
|
|
||||||
ENV GPG_VERSION 2.3.4
|
|
||||||
|
|
||||||
RUN Invoke-WebRequest $('https://files.gpg4win.org/gpg4win-vanilla-{0}.exe' -f $env:GPG_VERSION) -OutFile 'gpg4win.exe' -UseBasicParsing ; \
|
|
||||||
Start-Process .\gpg4win.exe -ArgumentList '/S' -NoNewWindow -Wait
|
|
||||||
|
|
||||||
RUN @( \
|
|
||||||
'94AE36675C464D64BAFA68DD7434390BDBE9B9C5', \
|
|
||||||
'FD3A5288F042B6850C66B31F09FE44734EB7990E', \
|
|
||||||
'71DCFD284A79C3B38668286BC97EC7A07EDE3FC1', \
|
|
||||||
'DD8F2338BAE7501E3DD5AC78C273792F7D83545D', \
|
|
||||||
'C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8', \
|
|
||||||
'B9AE9905FFD7803F25714661B63B535A4C206CA9', \
|
|
||||||
'77984A986EBC2AA786BC0F66B01FBB92821C587A', \
|
|
||||||
'8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600', \
|
|
||||||
'4ED778F539E3634C779C87C6D7062848A1AB005C', \
|
|
||||||
'A48C2BEE680E841632CD4E44F07496B3EB3C1762', \
|
|
||||||
'B9E2F5981AA6E0CD28160D9FF13993A75599653C' \
|
|
||||||
) | foreach { \
|
|
||||||
gpg --keyserver ha.pool.sks-keyservers.net --recv-keys $_ ; \
|
|
||||||
}
|
|
||||||
|
|
||||||
ENV NODE_VERSION=$node_version
|
|
||||||
|
|
||||||
RUN Invoke-WebRequest $('https://nodejs.org/dist/v{0}/SHASUMS256.txt.asc' -f $env:NODE_VERSION) -OutFile 'SHASUMS256.txt.asc' -UseBasicParsing ; \
|
|
||||||
gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc
|
|
||||||
|
|
||||||
RUN Invoke-WebRequest $('https://nodejs.org/dist/v{0}/node-v{0}-win-x64.zip' -f $env:NODE_VERSION) -OutFile 'node.zip' -UseBasicParsing ; \
|
|
||||||
$sum = $(cat SHASUMS256.txt.asc | sls $(' node-v{0}-win-x64.zip' -f $env:NODE_VERSION)) -Split ' ' ; \
|
|
||||||
if ((Get-FileHash node.zip -Algorithm sha256).Hash -ne $sum[0]) { Write-Error 'SHA256 mismatch' } ; \
|
|
||||||
Expand-Archive node.zip -DestinationPath C:\ ; \
|
|
||||||
Rename-Item -Path $('C:\node-v{0}-win-x64' -f $env:NODE_VERSION) -NewName 'C:\nodejs'
|
|
||||||
|
|
||||||
ENV YARN_VERSION=$yarn_version
|
|
||||||
|
|
||||||
RUN [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 ; \
|
|
||||||
Invoke-WebRequest $('https://yarnpkg.com/downloads/{0}/yarn-{0}.msi' -f $env:YARN_VERSION) -OutFile yarn.msi -UseBasicParsing ; \
|
|
||||||
$sig = Get-AuthenticodeSignature yarn.msi ; \
|
|
||||||
if ($sig.Status -ne 'Valid') { Write-Error 'Authenticode signature is not valid' } ; \
|
|
||||||
Write-Output $sig.SignerCertificate.Thumbprint ; \
|
|
||||||
if (@( \
|
|
||||||
'7E253367F8A102A91D04829E37F3410F14B68A5F', \
|
|
||||||
'AF764E1EA56C762617BDC757C8B0F3780A0CF5F9' \
|
|
||||||
) -notcontains $sig.SignerCertificate.Thumbprint) { Write-Error 'Unknown signer certificate' } ; \
|
|
||||||
Start-Process msiexec.exe -ArgumentList '/i', 'yarn.msi', '/quiet', '/norestart' -NoNewWindow -Wait
|
|
||||||
|
|
||||||
ENV GIT_VERSION 2.20.1
|
|
||||||
ENV GIT_DOWNLOAD_URL https://github.com/git-for-windows/git/releases/download/v${GIT_VERSION}.windows.1/MinGit-${GIT_VERSION}-busybox-64-bit.zip
|
|
||||||
ENV GIT_SHA256 9817ab455d9cbd0b09d8664b4afbe4bbf78d18b556b3541d09238501a749486c
|
|
||||||
|
|
||||||
RUN [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 ; \
|
|
||||||
Invoke-WebRequest -UseBasicParsing $env:GIT_DOWNLOAD_URL -OutFile git.zip; \
|
|
||||||
if ((Get-FileHash git.zip -Algorithm sha256).Hash -ne $env:GIT_SHA256) {exit 1} ; \
|
|
||||||
Expand-Archive git.zip -DestinationPath C:\git; \
|
|
||||||
Remove-Item git.zip
|
|
||||||
|
|
||||||
FROM $target as baseimage
|
|
||||||
|
|
||||||
ENV NPM_CONFIG_LOGLEVEL info
|
|
||||||
|
|
||||||
COPY --from=download /nodejs /nodejs
|
|
||||||
COPY --from=download [ "/Program Files (x86)/yarn", "/yarn" ]
|
|
||||||
COPY --from=download /git /git
|
|
||||||
|
|
||||||
ARG SETX=/M
|
|
||||||
RUN setx %SETX% PATH "%PATH%;C:\nodejs;C:\yarn\bin;C:\git\cmd;C:\git\mingw64\bin;C:\git\usr\bin"
|
|
||||||
|
|
||||||
CMD [ "node.exe" ]
|
|
||||||
|
|
||||||
FROM baseimage
|
|
||||||
|
|
||||||
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
|
|
||||||
|
|
||||||
RUN Invoke-WebRequest -UseBasicParsing 'https://www.7-zip.org/a/7z1805-x64.exe' -OutFile 7z.exe; \
|
|
||||||
Start-Process -FilePath 'C:\\7z.exe' -ArgumentList '/S', '/D=C:\\7zip0' -NoNewWindow -Wait; \
|
|
||||||
Invoke-WebRequest -UseBasicParsing 'http://repo.msys2.org/distrib/x86_64/msys2-base-x86_64-20180531.tar.xz' -OutFile msys2.tar.xz; \
|
|
||||||
Start-Process -FilePath 'C:\\7zip\\7z' -ArgumentList 'e', 'msys2.tar.xz' -Wait; \
|
|
||||||
Start-Process -FilePath 'C:\\7zip\\7z' -ArgumentList 'x', 'msys2.tar', '-oC:\\' -Wait; \
|
|
||||||
Remove-Item msys2.tar.xz; \
|
|
||||||
Remove-Item msys2.tar; \
|
|
||||||
Remove-Item 7z.exe; \
|
|
||||||
Remove-Item -Recurse 7zip; \
|
|
||||||
[Environment]::SetEnvironmentVariable('Path', $env:Path + ';C:\msys64\usr\bin', [System.EnvironmentVariableTarget]::Machine); \
|
|
||||||
[Environment]::SetEnvironmentVariable('BAZEL_SH', 'C:\msys64\usr\bin\bash.exe', [System.EnvironmentVariableTarget]::Machine); \
|
|
||||||
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; \
|
|
||||||
Start-Process 'c:\\vc_redist.x64.exe' -ArgumentList '/Install', '/Passive', '/NoRestart' -NoNewWindow -Wait; \
|
|
||||||
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"]
|
|
@ -1,33 +0,0 @@
|
|||||||
# CodeFresh configuration
|
|
||||||
|
|
||||||
[](https://g.codefresh.io/public/accounts/angular/pipelines/angular/angular/angular)
|
|
||||||
|
|
||||||
This folder contains configuration for the [CodeFresh](<https://codefresh.io/>) based CI checks for this repository.
|
|
||||||
|
|
||||||
## The build pipeline
|
|
||||||
|
|
||||||
CodeFresh uses a several pipeline for each repository. The `codefresh.yml` file defines pipeline [build steps](https://codefresh.io/docs/docs/configure-ci-cd-pipeline/introduction-to-codefresh-pipelines/) for this repository.
|
|
||||||
|
|
||||||
Run results can be seen in the GitHub checks interface and in the [public pipeline](https://g.codefresh.io/public/accounts/angular/pipelines/angular/angular/angular)
|
|
||||||
|
|
||||||
Although most configuration is done via `pipeline.yml`, some options are only available in the online [pipeline settings](https://g.codefresh.io/pipelines/angular/services?repoOwner=angular&repoName=angular&project=angular%2Fangular&context=github&serviceName=angular%2Fangular), which needs a login to access.
|
|
||||||
|
|
||||||
|
|
||||||
## Caretaker
|
|
||||||
|
|
||||||
CodeFresh status can be found at <http://status.codefresh.io/>.
|
|
||||||
|
|
||||||
Issues related to the CodeFresh setup should be escalated to the Tools Team via the current caretaker, followed by Alex Eagle and Filipe Silva.
|
|
||||||
|
|
||||||
## Rollout strategy
|
|
||||||
|
|
||||||
Currently it is only used for tests on Windows platforms, on the master branch, and without pushing user-facing reports. It's only possible to see current builds in the [public pipeline dashboard](https://g.codefresh.io/public/accounts/angular/pipelines/angular/angular/angular).
|
|
||||||
|
|
||||||
After a week or two of running like this, we should reassess how stable and reliable it is.
|
|
||||||
|
|
||||||
Next steps include:
|
|
||||||
- building PRs
|
|
||||||
- showing build status publicly
|
|
||||||
- blocking PRs that break the build
|
|
||||||
- expanding the test suite
|
|
||||||
|
|
@ -1,42 +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
|
|
||||||
|
|
||||||
# Save downloaded repositories in a location that can be cached by CodeFresh and shared between
|
|
||||||
# builds. This helps speed up the analysis time significantly with Bazel managed node dependencies
|
|
||||||
# on the CI.
|
|
||||||
# https://codefresh.io/docs/docs/configure-ci-cd-pipeline/introduction-to-codefresh-pipelines/#caching-the-artifacts-of-your-build-system
|
|
||||||
build --repository_cache=C:/codefresh/volume/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 CodeFresh VMs
|
|
||||||
# TODO(filipesilva): determine the correct memory limit
|
|
||||||
build --local_resources=8000,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
|
|
||||||
|
|
||||||
# Include PATH in Windows build/tests
|
|
||||||
# https://github.com/bazelbuild/rules_typescript/pull/356
|
|
||||||
build --action_env=PATH
|
|
||||||
test --action_env=PATH --test_env=PATH
|
|
||||||
|
|
||||||
# Exclude tests known to not work on Windows.
|
|
||||||
|
|
||||||
# Chrome web tests are currently broken.
|
|
||||||
test --test_tag_filters=-browser:chromium-local
|
|
@ -1,24 +0,0 @@
|
|||||||
version: '1.0'
|
|
||||||
|
|
||||||
steps:
|
|
||||||
BuildImage:
|
|
||||||
type: build
|
|
||||||
image_name: node-bazel-windows
|
|
||||||
working_directory: ./.codefresh
|
|
||||||
no_cf_cache: true
|
|
||||||
build_arguments:
|
|
||||||
- node_version=10.13.0
|
|
||||||
- yarn_version=1.13.0
|
|
||||||
dockerfile: ./Dockerfile.win-1809
|
|
||||||
|
|
||||||
RunTests:
|
|
||||||
title: Run Example
|
|
||||||
image: ${{BuildImage}}
|
|
||||||
commands:
|
|
||||||
# Install dependencies
|
|
||||||
- yarn install --frozen-lockfile --non-interactive --network-timeout 100000 --no-progress
|
|
||||||
# Add Bazel CI config
|
|
||||||
- copy .codefresh\bazel.rc %ProgramData%\bazel.bazelrc
|
|
||||||
# Run tests
|
|
||||||
- yarn bazel test //tools/ts-api-guardian:all
|
|
||||||
- yarn test-ivy-aot //packages/animations/test //packages/common/test //packages/forms/test //packages/http/test //packages/platform-browser/test //packages/platform-browser-dynamic/test //packages/router/test
|
|
@ -1,6 +0,0 @@
|
|||||||
@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."
|
|
@ -1,4 +1,4 @@
|
|||||||
# https://editorconfig.org
|
# http://editorconfig.org
|
||||||
|
|
||||||
root = true
|
root = true
|
||||||
|
|
||||||
|
906
.github/CODEOWNERS
vendored
906
.github/CODEOWNERS
vendored
@ -1,906 +0,0 @@
|
|||||||
# ==================================================================================
|
|
||||||
# ==================================================================================
|
|
||||||
# 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)
|
|
||||||
# ================================================
|
|
||||||
|
|
||||||
# alan-agius4 - Alan Agius
|
|
||||||
# alexeagle - Alex Eagle
|
|
||||||
# alxhub - Alex Rickabaugh
|
|
||||||
# AndrewKushnir - Andrew Kushnir
|
|
||||||
# andrewseguin - Andrew Seguin
|
|
||||||
# benlesh - Ben Lesh
|
|
||||||
# brandonroberts - Brandon Roberts
|
|
||||||
# devversion - Paul Gschwendtner
|
|
||||||
# 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
|
|
||||||
# - alexeagle
|
|
||||||
|
|
||||||
|
|
||||||
# ===========================================================
|
|
||||||
# @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
|
|
||||||
# ===========================================================
|
|
||||||
#
|
|
||||||
# - jenniferfell
|
|
||||||
# - brandonroberts
|
|
||||||
# - IgorMinar
|
|
||||||
# - stephenfluin
|
|
||||||
|
|
||||||
|
|
||||||
# ===========================================================
|
|
||||||
# @angular/fw-docs-observables
|
|
||||||
# ===========================================================
|
|
||||||
#
|
|
||||||
# - benlesh
|
|
||||||
# - jasonaden
|
|
||||||
|
|
||||||
|
|
||||||
# ===========================================================
|
|
||||||
# @angular/fw-docs-packaging
|
|
||||||
# ===========================================================
|
|
||||||
#
|
|
||||||
# - alexeagle
|
|
||||||
# - IgorMinar
|
|
||||||
|
|
||||||
|
|
||||||
# ===========================================================
|
|
||||||
# @angular/tools-docs-libraries
|
|
||||||
# ===========================================================
|
|
||||||
#
|
|
||||||
# - alan-agius4
|
|
||||||
# - alexeagle
|
|
||||||
# - hansl
|
|
||||||
# - IgorMinar
|
|
||||||
# - mgechev
|
|
||||||
|
|
||||||
|
|
||||||
# ===========================================================
|
|
||||||
# @angular/tools-docs-schematics
|
|
||||||
# ===========================================================
|
|
||||||
#
|
|
||||||
# - alan-agius4
|
|
||||||
# - alexeagle
|
|
||||||
# - hansl
|
|
||||||
# - IgorMinar
|
|
||||||
# - mgechev
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ===========================================================
|
|
||||||
# @angular/fw-docs-marketing
|
|
||||||
# ===========================================================
|
|
||||||
#
|
|
||||||
# - IgorMinar
|
|
||||||
# - stephenfluin
|
|
||||||
|
|
||||||
|
|
||||||
# ===========================================================
|
|
||||||
# @angular/fw-public-api
|
|
||||||
# ===========================================================
|
|
||||||
#
|
|
||||||
# - IgorMinar
|
|
||||||
|
|
||||||
|
|
||||||
# ===========================================================
|
|
||||||
# @angular/fw-dev-infra
|
|
||||||
# ===========================================================
|
|
||||||
#
|
|
||||||
# - alexeagle
|
|
||||||
# - devversion
|
|
||||||
# - filipesilva
|
|
||||||
# - gkalpak
|
|
||||||
# - 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
|
|
||||||
|
|
||||||
/aio/content/guide/complex-animation-sequences.md @angular/fw-animations @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
|
|
||||||
/aio/content/guide/reusable-animations.md @angular/fw-animations @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
|
|
||||||
/aio/content/guide/route-animations.md @angular/fw-animations @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
|
|
||||||
/aio/content/guide/transition-and-triggers.md @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/ngcc/
|
|
||||||
# ================================================
|
|
||||||
|
|
||||||
/packages/compiler-cli/ngcc/** @angular/fw-ngcc @angular/framework-global-approvers
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ================================================
|
|
||||||
# Framework/cli integration
|
|
||||||
#
|
|
||||||
# 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
|
|
||||||
/aio/content/guide/ivy.md @angular/tools-cli @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
/aio/content/guide/web-worker.md @angular/tools-cli @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ================================================
|
|
||||||
# @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
|
|
||||||
/packages/examples/common/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
|
|
||||||
/aio/content/guide/architecture-components.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
/aio/content/guide/architecture-modules.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
/aio/content/guide/architecture-next-steps.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
/aio/content/guide/architecture-services.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
/aio/content/guide/architecture.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
/aio/content/examples/architecture/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
/aio/content/images/guide/architecture/** @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/cheatsheet.md @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-navtree.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
|
|
||||||
/aio/content/guide/dependency-injection-providers.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
|
|
||||||
/aio/content/guide/displaying-data.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
/aio/content/examples/displaying-data/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
/aio/content/images/guide/displaying-data/** @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/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/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/built-in-template-functions/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
/aio/content/examples/event-binding/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
/aio/content/examples/interpolation/** @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
|
|
||||||
|
|
||||||
/aio/content/guide/user-input.md @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
/aio/content/examples/user-input/** @angular/fw-core @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
/aio/content/images/guide/user-input/** @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/app-shell.md @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
|
|
||||||
/aio/content/images/guide/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/content/examples/* @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
|
|
||||||
|
|
||||||
# Hidden docs
|
|
||||||
/aio/content/guide/change-log.md @angular/docs-infra @angular/framework-global-approvers
|
|
||||||
/aio/content/guide/docs-style-guide.md @angular/docs-infra @angular/framework-global-approvers
|
|
||||||
/aio/content/examples/docs-style-guide/** @angular/docs-infra @angular/framework-global-approvers
|
|
||||||
/aio/content/images/guide/docs-style-guide/** @angular/docs-infra @angular/framework-global-approvers
|
|
||||||
/aio/content/guide/visual-studio-2015.md @angular/docs-infra @angular/framework-global-approvers
|
|
||||||
/aio/content/examples/visual-studio-2015/** @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/examples/cli-quickstart/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
/aio/content/images/guide/cli-quickstart/** @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
|
|
||||||
/aio/content/images/guide/toh/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
/aio/content/examples/toh-pt0/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
/aio/content/examples/toh-pt1/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
/aio/content/examples/toh-pt2/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
/aio/content/examples/toh-pt3/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
/aio/content/examples/toh-pt4/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
/aio/content/examples/toh-pt5/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
/aio/content/examples/toh-pt6/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
/aio/content/examples/getting-started-v0/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
/aio/content/examples/getting-started/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
/aio/content/getting-started/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
/aio/content/images/guide/getting-started/** @angular/fw-docs-intro @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
|
|
||||||
|
|
||||||
# ================================================
|
|
||||||
# Docs: observables
|
|
||||||
# ================================================
|
|
||||||
|
|
||||||
/aio/content/guide/observables.md @angular/fw-docs-observables @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
/aio/content/examples/observables/** @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/guide/observables-in-angular.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/guide/practical-observable-usage.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/rx-library.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
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ================================================
|
|
||||||
# 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.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/build.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
/aio/content/images/guide/build/** @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/file-structure.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
|
|
||||||
/aio/content/guide/workspace-config.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ================================================
|
|
||||||
# Docs: libraries
|
|
||||||
# ================================================
|
|
||||||
|
|
||||||
/aio/content/guide/creating-libraries.md @angular/tools-docs-libraries @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
/aio/content/guide/libraries.md @angular/tools-docs-libraries @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
/aio/content/guide/using-libraries.md @angular/tools-docs-libraries @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
|
|
||||||
|
|
||||||
# ================================================
|
|
||||||
# Docs: schematics
|
|
||||||
# ================================================
|
|
||||||
|
|
||||||
/aio/content/guide/schematics.md @angular/tools-docs-schematics @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
/aio/content/guide/schematics-authoring.md @angular/tools-docs-schematics @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
/aio/content/guide/schematics-for-libraries.md @angular/tools-docs-schematics @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
/aio/content/images/guide/schematics/** @angular/tools-docs-schematics @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
|
||||||
/aio/content/examples/schematics-for-libraries/** @angular/tools-docs-schematics @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/bios/** @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
|
|
||||||
/.codefresh/** @angular/fw-dev-infra
|
|
||||||
/.github/** @angular/fw-dev-infra
|
|
||||||
/.vscode/** @angular/fw-dev-infra
|
|
||||||
/docs/BAZEL.md @angular/fw-dev-infra
|
|
||||||
/packages/* @angular/fw-dev-infra
|
|
||||||
/scripts/** @angular/fw-dev-infra
|
|
||||||
/third_party/** @angular/fw-dev-infra
|
|
||||||
/tools/build/** @angular/fw-dev-infra
|
|
||||||
/tools/cjs-jasmine/** @angular/fw-dev-infra
|
|
||||||
/tools/gulp-tasks/** @angular/fw-dev-infra
|
|
||||||
/tools/ngcontainer/** @angular/fw-dev-infra
|
|
||||||
/tools/npm/** @angular/fw-dev-infra
|
|
||||||
/tools/npm_workspace/** @angular/fw-dev-infra
|
|
||||||
/tools/public_api_guard/** @angular/fw-dev-infra
|
|
||||||
/tools/rxjs/** @angular/fw-dev-infra
|
|
||||||
/tools/source-map-test/** @angular/fw-dev-infra
|
|
||||||
/tools/symbol-extractor/** @angular/fw-dev-infra
|
|
||||||
/tools/testing/** @angular/fw-dev-infra
|
|
||||||
/tools/ts-api-guardian/** @angular/fw-dev-infra
|
|
||||||
/tools/tslint/** @angular/fw-dev-infra
|
|
||||||
/tools/validate-commit-message/** @angular/fw-dev-infra
|
|
||||||
/tools/yarn/** @angular/fw-dev-infra
|
|
||||||
/tools/*
|
|
||||||
*.bzl @angular/fw-dev-infra
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ================================================
|
|
||||||
# Material CI
|
|
||||||
# ================================================
|
|
||||||
|
|
||||||
/tools/material-ci/** @angular/fw-core @angular/framework-global-approvers
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ================================================
|
|
||||||
# Public API
|
|
||||||
# ================================================
|
|
||||||
|
|
||||||
/tools/public_api_guard/** @angular/fw-public-api
|
|
||||||
/aio/content/guide/glossary.md @angular/fw-public-api
|
|
||||||
/aio/content/guide/styleguide.md @angular/fw-public-api
|
|
||||||
/aio/content/examples/styleguide/** @angular/fw-public-api
|
|
||||||
/aio/content/images/guide/styleguide/** @angular/fw-public-api
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ================================================
|
|
||||||
# CODEOWNERS Owners owners ...
|
|
||||||
# ================================================
|
|
||||||
|
|
||||||
/.github/CODEOWNERS @IgorMinar @angular/framework-global-approvers
|
|
59
.github/ISSUE_TEMPLATE.md
vendored
59
.github/ISSUE_TEMPLATE.md
vendored
@ -1,10 +1,57 @@
|
|||||||
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑
|
<!--
|
||||||
|
PLEASE HELP US PROCESS GITHUB ISSUES FASTER BY PROVIDING THE FOLLOWING INFORMATION.
|
||||||
|
|
||||||
Please help us process issues more efficiently by filing an
|
ISSUES MISSING IMPORTANT INFORMATION MAY BE CLOSED WITHOUT INVESTIGATION.
|
||||||
issue using one of the following templates:
|
-->
|
||||||
|
|
||||||
https://github.com/angular/angular/issues/new/choose
|
## I'm submitting a...
|
||||||
|
<!-- Check one of the following options with "x" -->
|
||||||
|
<pre><code>
|
||||||
|
[ ] Regression (a behavior that used to work and stopped working in a new release)
|
||||||
|
[ ] Bug report <!-- Please search GitHub for a similar issue or PR before submitting -->
|
||||||
|
[ ] Feature request
|
||||||
|
[ ] Documentation issue or request
|
||||||
|
[ ] Support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
Thank you!
|
## Current behavior
|
||||||
|
<!-- Describe how the issue manifests. -->
|
||||||
|
|
||||||
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑
|
|
||||||
|
## Expected behavior
|
||||||
|
<!-- Describe what the desired behavior would be. -->
|
||||||
|
|
||||||
|
|
||||||
|
## Minimal reproduction of the problem with instructions
|
||||||
|
<!--
|
||||||
|
For bug reports please provide the *STEPS TO REPRODUCE* and if possible a *MINIMAL DEMO* of the problem via
|
||||||
|
https://stackblitz.com or similar (you can use this template as a starting point: https://stackblitz.com/fork/angular-gitter).
|
||||||
|
-->
|
||||||
|
|
||||||
|
## What is the motivation / use case for changing the behavior?
|
||||||
|
<!-- Describe the motivation or the concrete use case. -->
|
||||||
|
|
||||||
|
|
||||||
|
## Environment
|
||||||
|
|
||||||
|
<pre><code>
|
||||||
|
Angular version: X.Y.Z
|
||||||
|
<!-- Check whether this is still an issue in the most recent Angular version -->
|
||||||
|
|
||||||
|
Browser:
|
||||||
|
- [ ] Chrome (desktop) version XX
|
||||||
|
- [ ] Chrome (Android) version XX
|
||||||
|
- [ ] Chrome (iOS) version XX
|
||||||
|
- [ ] Firefox version XX
|
||||||
|
- [ ] Safari (desktop) version XX
|
||||||
|
- [ ] Safari (iOS) version XX
|
||||||
|
- [ ] IE version XX
|
||||||
|
- [ ] Edge version XX
|
||||||
|
|
||||||
|
For Tooling issues:
|
||||||
|
- Node version: XX <!-- run `node --version` -->
|
||||||
|
- Platform: <!-- Mac, Linux, Windows -->
|
||||||
|
|
||||||
|
Others:
|
||||||
|
<!-- Anything else relevant? Operating system version, IDE, package manager, HTTP server, ... -->
|
||||||
|
</code></pre>
|
||||||
|
69
.github/ISSUE_TEMPLATE/1-bug-report.md
vendored
69
.github/ISSUE_TEMPLATE/1-bug-report.md
vendored
@ -1,69 +0,0 @@
|
|||||||
---
|
|
||||||
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.
|
|
||||||
A good way to make a minimal reproduction is to create a new app via `ng new repro-app` and add the minimum possible code to show the problem.
|
|
||||||
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. -->
|
|
32
.github/ISSUE_TEMPLATE/2-feature-request.md
vendored
32
.github/ISSUE_TEMPLATE/2-feature-request.md
vendored
@ -1,32 +0,0 @@
|
|||||||
---
|
|
||||||
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?
|
|
55
.github/ISSUE_TEMPLATE/3-docs-bug.md
vendored
55
.github/ISSUE_TEMPLATE/3-docs-bug.md
vendored
@ -1,55 +0,0 @@
|
|||||||
---
|
|
||||||
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. -->
|
|
@ -1,11 +0,0 @@
|
|||||||
---
|
|
||||||
name: ⚠️ Security issue disclosure
|
|
||||||
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.
|
|
||||||
|
|
||||||
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑
|
|
16
.github/ISSUE_TEMPLATE/5-support-request.md
vendored
16
.github/ISSUE_TEMPLATE/5-support-request.md
vendored
@ -1,16 +0,0 @@
|
|||||||
---
|
|
||||||
name: "❓Support request"
|
|
||||||
about: Questions and requests for support
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑
|
|
||||||
|
|
||||||
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:
|
|
||||||
https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question
|
|
||||||
|
|
||||||
Thank you!
|
|
||||||
|
|
||||||
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑
|
|
13
.github/ISSUE_TEMPLATE/6-angular-cli.md
vendored
13
.github/ISSUE_TEMPLATE/6-angular-cli.md
vendored
@ -1,13 +0,0 @@
|
|||||||
---
|
|
||||||
name: "\U0001F6E0️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
|
|
||||||
|
|
||||||
For the time being, we keep Angular CLI issues in a separate repository.
|
|
||||||
|
|
||||||
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑
|
|
13
.github/ISSUE_TEMPLATE/7-angular-material.md
vendored
13
.github/ISSUE_TEMPLATE/7-angular-material.md
vendored
@ -1,13 +0,0 @@
|
|||||||
---
|
|
||||||
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.
|
|
||||||
|
|
||||||
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑
|
|
30
.github/PULL_REQUEST_TEMPLATE.md
vendored
30
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -10,17 +10,17 @@ Please check if your PR fulfills the following requirements:
|
|||||||
What kind of change does this PR introduce?
|
What kind of change does this PR introduce?
|
||||||
|
|
||||||
<!-- Please check the one that applies to this PR using "x". -->
|
<!-- Please check the one that applies to this PR using "x". -->
|
||||||
|
```
|
||||||
- [ ] Bugfix
|
[ ] Bugfix
|
||||||
- [ ] Feature
|
[ ] Feature
|
||||||
- [ ] Code style update (formatting, local variables)
|
[ ] Code style update (formatting, local variables)
|
||||||
- [ ] Refactoring (no functional changes, no api changes)
|
[ ] Refactoring (no functional changes, no api changes)
|
||||||
- [ ] Build related changes
|
[ ] Build related changes
|
||||||
- [ ] CI related changes
|
[ ] CI related changes
|
||||||
- [ ] Documentation content changes
|
[ ] Documentation content changes
|
||||||
- [ ] angular.io application / infrastructure changes
|
[ ] angular.io application / infrastructure changes
|
||||||
- [ ] Other... Please describe:
|
[ ] Other... Please describe:
|
||||||
|
```
|
||||||
|
|
||||||
## What is the current behavior?
|
## What is the current behavior?
|
||||||
<!-- Please describe the current behavior that you are modifying, or link to a relevant issue. -->
|
<!-- Please describe the current behavior that you are modifying, or link to a relevant issue. -->
|
||||||
@ -32,10 +32,10 @@ Issue Number: N/A
|
|||||||
|
|
||||||
|
|
||||||
## Does this PR introduce a breaking change?
|
## Does this PR introduce a breaking change?
|
||||||
|
```
|
||||||
- [ ] Yes
|
[ ] Yes
|
||||||
- [ ] No
|
[ ] No
|
||||||
|
```
|
||||||
|
|
||||||
<!-- If this PR contains a breaking change, please describe the impact and migration path for existing applications below. -->
|
<!-- If this PR contains a breaking change, please describe the impact and migration path for existing applications below. -->
|
||||||
|
|
||||||
|
102
.github/angular-robot.yml
vendored
102
.github/angular-robot.yml
vendored
@ -1,11 +1,5 @@
|
|||||||
# Configuration for angular-robot
|
# Configuration for angular-robot
|
||||||
|
|
||||||
#options for the size plugin
|
|
||||||
size:
|
|
||||||
disabled: false
|
|
||||||
maxSizeIncrease: 2000
|
|
||||||
circleCiStatusName: "ci/circleci: test_ivy_aot"
|
|
||||||
|
|
||||||
# options for the merge plugin
|
# options for the merge plugin
|
||||||
merge:
|
merge:
|
||||||
# the status will be added to your pull requests
|
# the status will be added to your pull requests
|
||||||
@ -25,46 +19,23 @@ merge:
|
|||||||
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
|
||||||
pendingDesc: "Googler: run g3sync presubmit {{PRNumber}}"
|
pendingDesc: "Googler: test this change in google3 http://go/angular-g3sync"
|
||||||
# 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
|
|
||||||
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:
|
||||||
|
- "BUILD.bazel"
|
||||||
- "LICENSE"
|
- "LICENSE"
|
||||||
- "modules/benchmarks/**"
|
- "WORKSPACE"
|
||||||
- "modules/system.d.ts"
|
- "modules/**"
|
||||||
- "packages/**"
|
- "packages/**"
|
||||||
# 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/bazel/*"
|
|
||||||
- "packages/bazel/src/api-extractor/**"
|
|
||||||
- "packages/bazel/src/builders/**"
|
|
||||||
- "packages/bazel/src/ng_package/**"
|
|
||||||
- "packages/bazel/src/protractor/**"
|
|
||||||
- "packages/bazel/src/schematics/**"
|
|
||||||
- "packages/compiler-cli/ngcc/**"
|
|
||||||
- "packages/docs/**"
|
|
||||||
- "packages/elements/schematics/**"
|
|
||||||
- "packages/examples/**"
|
|
||||||
- "packages/language-service/**"
|
- "packages/language-service/**"
|
||||||
- "packages/private/**"
|
|
||||||
- "packages/service-worker/**"
|
|
||||||
- "**/.gitignore"
|
- "**/.gitignore"
|
||||||
- "**/.gitkeep"
|
- "**/.gitkeep"
|
||||||
- "**/yarn.lock"
|
|
||||||
- "**/package.json"
|
|
||||||
- "**/third_party/**"
|
|
||||||
- "**/tsconfig-build.json"
|
|
||||||
- "**/tsconfig.json"
|
|
||||||
- "**/BUILD.bazel"
|
|
||||||
- "**/*.md"
|
|
||||||
- "packages/**/integrationtest/**"
|
|
||||||
- "packages/**/test/**"
|
|
||||||
|
|
||||||
# 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.
|
mergeConflictComment: "Hi @{{PRAuthor}}! This PR has merge conflicts due to recent upstream merges.
|
||||||
@ -73,19 +44,8 @@ merge:
|
|||||||
# label to monitor
|
# label to monitor
|
||||||
mergeLabel: "PR action: merge"
|
mergeLabel: "PR action: merge"
|
||||||
|
|
||||||
# adding any of these labels will also add the merge label
|
|
||||||
mergeLinkedLabels:
|
|
||||||
- "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
|
|
||||||
#
|
|
||||||
# This enables us to request reviews from both eng and tech writers, or multiple eng folks, and prevents accidental merges.
|
|
||||||
# Rather than merging PRs with pending reviews, if all approvals are obtained and additional reviews are not needed, any pending reviewers should be removed via GitHub UI (this also leaves an audit trail behind these decisions).
|
|
||||||
requireReviews: true,
|
|
||||||
|
|
||||||
# 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. "PR target:" will work for the label "PR 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")
|
||||||
@ -103,17 +63,14 @@ merge:
|
|||||||
|
|
||||||
# list of PR statuses that need to be successful
|
# list of PR statuses that need to be successful
|
||||||
requiredStatuses:
|
requiredStatuses:
|
||||||
|
- "continuous-integration/travis-ci/pr"
|
||||||
|
- "code-review/pullapprove"
|
||||||
- "ci/circleci: build"
|
- "ci/circleci: build"
|
||||||
- "ci/circleci: lint"
|
- "ci/circleci: lint"
|
||||||
- "ci/circleci: publish_snapshot"
|
|
||||||
- "ci/angular: size"
|
|
||||||
- "cla/google"
|
|
||||||
- "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:
|
mergeRemovedComment: "I see that you just added the `{{MERGE_LABEL}}` label, but the following checks are still failing:
|
||||||
\n{{PLACEHOLDER}}
|
\n{{PLACEHOLDER}}
|
||||||
\n
|
\n
|
||||||
@ -123,18 +80,12 @@ merge:
|
|||||||
|
|
||||||
# options for the triage plugin
|
# options for the triage plugin
|
||||||
triage:
|
triage:
|
||||||
# number of the milestone to apply when the issue has not been triaged yet
|
|
||||||
needsTriageMilestone: 83,
|
|
||||||
# number of the milestone to apply when the issue is triaged
|
# number of the milestone to apply when the issue is triaged
|
||||||
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 is triaged
|
||||||
l1TriageLabels:
|
triagedLabels:
|
||||||
-
|
-
|
||||||
- "comp: *"
|
- "type: bug"
|
||||||
# arrays of labels that determine if an issue has been fully triaged
|
|
||||||
l2TriageLabels:
|
|
||||||
-
|
|
||||||
- "type: bug/fix"
|
|
||||||
- "severity*"
|
- "severity*"
|
||||||
- "freq*"
|
- "freq*"
|
||||||
- "comp: *"
|
- "comp: *"
|
||||||
@ -147,30 +98,3 @@ triage:
|
|||||||
-
|
-
|
||||||
- "type: RFC / Discussion / question"
|
- "type: RFC / Discussion / question"
|
||||||
- "comp: *"
|
- "comp: *"
|
||||||
|
|
||||||
# options for the triage PR plugin
|
|
||||||
triagePR:
|
|
||||||
# set to true to disable
|
|
||||||
disabled: false
|
|
||||||
# number of the milestone to apply when the PR has not been triaged yet
|
|
||||||
needsTriageMilestone: 83,
|
|
||||||
# number of the milestone to apply when the PR is triaged
|
|
||||||
defaultMilestone: 82,
|
|
||||||
# arrays of labels that determine if a PR has been triaged by the caretaker
|
|
||||||
l1TriageLabels:
|
|
||||||
-
|
|
||||||
- "comp: *"
|
|
||||||
# arrays of labels that determine if a PR has been fully triaged
|
|
||||||
l2TriageLabels:
|
|
||||||
-
|
|
||||||
- "type: *"
|
|
||||||
- "effort*"
|
|
||||||
- "risk*"
|
|
||||||
- "comp: *"
|
|
||||||
|
|
||||||
# options for rerunning CI
|
|
||||||
rerunCircleCI:
|
|
||||||
# set to true to disable
|
|
||||||
disabled: false
|
|
||||||
# the label which when added triggers a rerun of the default CircleCI workflow
|
|
||||||
triggerRerunLabel: "PR action: rerun CI at HEAD"
|
|
||||||
|
11
.gitignore
vendored
11
.gitignore
vendored
@ -1,10 +1,10 @@
|
|||||||
.DS_STORE
|
.DS_STORE
|
||||||
|
|
||||||
/dist/
|
/dist/
|
||||||
/bazel-out
|
bazel-*
|
||||||
/integration/bazel/bazel-*
|
|
||||||
e2e_test.*
|
e2e_test.*
|
||||||
node_modules
|
node_modules
|
||||||
|
bower_components
|
||||||
tools/gulp-tasks/cldr/cldr-data/
|
tools/gulp-tasks/cldr/cldr-data/
|
||||||
|
|
||||||
# Include when developing application packages.
|
# Include when developing application packages.
|
||||||
@ -12,10 +12,9 @@ pubspec.lock
|
|||||||
.c9
|
.c9
|
||||||
.idea/
|
.idea/
|
||||||
.settings/
|
.settings/
|
||||||
.vscode/launch.json
|
|
||||||
.vscode/settings.json
|
|
||||||
*.swo
|
*.swo
|
||||||
modules/.settings
|
modules/.settings
|
||||||
|
.vscode
|
||||||
modules/.vscode
|
modules/.vscode
|
||||||
|
|
||||||
# Don't check in secret files
|
# Don't check in secret files
|
||||||
@ -30,7 +29,3 @@ yarn-error.log
|
|||||||
|
|
||||||
# rollup-test output
|
# rollup-test output
|
||||||
/modules/rollup-test/dist/
|
/modules/rollup-test/dist/
|
||||||
|
|
||||||
# User specific bazel settings
|
|
||||||
.bazelrc.user
|
|
||||||
|
|
||||||
|
350
.pullapprove.yml
Normal file
350
.pullapprove.yml
Normal file
@ -0,0 +1,350 @@
|
|||||||
|
# Configuration for pullapprove.com
|
||||||
|
#
|
||||||
|
# Approval access and primary role is determined by info in the project ownership spreadsheet:
|
||||||
|
# https://docs.google.com/spreadsheets/d/1-HIlzfbPYGsPr9KuYMe6bLfc4LXzPjpoALqtYRYTZB0/edit?pli=1#gid=0&vpid=A5
|
||||||
|
#
|
||||||
|
# === GitHub username to Full name map ===
|
||||||
|
#
|
||||||
|
# alexeagle - Alex Eagle
|
||||||
|
# alxhub - Alex Rickabaugh
|
||||||
|
# brocco - Mike Brocchi
|
||||||
|
# chuckjaz - Chuck Jazdzewski
|
||||||
|
# filipesilva - Filipe Silva
|
||||||
|
# gkalpak - George Kalpakas
|
||||||
|
# hansl - Hans Larsen
|
||||||
|
# IgorMinar - Igor Minar
|
||||||
|
# jasonaden - Jason Aden
|
||||||
|
# kapunahelewong - Kapunahele Wong
|
||||||
|
# kara - Kara Erickson
|
||||||
|
# matsko - Matias Niemelä
|
||||||
|
# mhevery - Misko Hevery
|
||||||
|
# petebacondarwin - Pete Bacon Darwin
|
||||||
|
# pkozlowski-opensource - Pawel Kozlowski
|
||||||
|
# robwormald - Rob Wormald
|
||||||
|
# tinayuangao - Tina Gao
|
||||||
|
# vicb - Victor Berchet
|
||||||
|
# vikerman - Vikram Subramanian
|
||||||
|
|
||||||
|
|
||||||
|
version: 2
|
||||||
|
|
||||||
|
group_defaults:
|
||||||
|
required: 1
|
||||||
|
reset_on_reopened:
|
||||||
|
enabled: true
|
||||||
|
approve_by_comment:
|
||||||
|
enabled: false
|
||||||
|
# see http://docs.pullapprove.com/groups/author_approval/
|
||||||
|
author_approval:
|
||||||
|
# If the author is a reviewer on the PR, they will automatically have an "approved" status.
|
||||||
|
auto: true
|
||||||
|
|
||||||
|
groups:
|
||||||
|
# Require all PRs to have at least one approval from *someone*
|
||||||
|
all:
|
||||||
|
users: all
|
||||||
|
required: 1
|
||||||
|
rejection_value: -999
|
||||||
|
# In this group, your self-approval does not count
|
||||||
|
author_approval:
|
||||||
|
auto: false
|
||||||
|
ignored: true
|
||||||
|
files:
|
||||||
|
include:
|
||||||
|
- "*"
|
||||||
|
|
||||||
|
root:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
include:
|
||||||
|
- "*"
|
||||||
|
exclude:
|
||||||
|
- "WORKSPACE"
|
||||||
|
- "BUILD.bazel"
|
||||||
|
- ".circleci/*"
|
||||||
|
- "aio/*"
|
||||||
|
- "integration/*"
|
||||||
|
- "modules/*"
|
||||||
|
- "packages/*"
|
||||||
|
- "tools/*"
|
||||||
|
users:
|
||||||
|
- IgorMinar
|
||||||
|
- mhevery
|
||||||
|
|
||||||
|
public-api:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
include:
|
||||||
|
- "tools/public_api_guard/*"
|
||||||
|
users:
|
||||||
|
- IgorMinar
|
||||||
|
- mhevery
|
||||||
|
|
||||||
|
bazel:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
include:
|
||||||
|
- "WORKSPACE"
|
||||||
|
- "*.bazel"
|
||||||
|
- "*.bzl"
|
||||||
|
- "packages/bazel/*"
|
||||||
|
- "tools/bazel.rc"
|
||||||
|
users:
|
||||||
|
- alexeagle #primary
|
||||||
|
- chuckjaz
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery
|
||||||
|
- vikerman #fallback
|
||||||
|
|
||||||
|
build-and-ci:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
include:
|
||||||
|
- "*.yml"
|
||||||
|
- "*.json"
|
||||||
|
- "*.lock"
|
||||||
|
- "tools/*"
|
||||||
|
exclude:
|
||||||
|
- "tools/bazel.rc"
|
||||||
|
- "tools/public_api_guard/*"
|
||||||
|
- "aio/*"
|
||||||
|
users:
|
||||||
|
- IgorMinar #primary
|
||||||
|
- alexeagle
|
||||||
|
- jasonaden
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
integration:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "integration/*"
|
||||||
|
users:
|
||||||
|
- alexeagle
|
||||||
|
- mhevery
|
||||||
|
- vicb
|
||||||
|
- IgorMinar #fallback
|
||||||
|
|
||||||
|
core:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "packages/core/*"
|
||||||
|
users:
|
||||||
|
- mhevery #primary
|
||||||
|
- chuckjaz
|
||||||
|
- kara
|
||||||
|
- vicb
|
||||||
|
- IgorMinar #fallback
|
||||||
|
|
||||||
|
animations:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "packages/animations/*"
|
||||||
|
- "packages/platform-browser/animations/*"
|
||||||
|
users:
|
||||||
|
- matsko #primary
|
||||||
|
- mhevery #fallback
|
||||||
|
- IgorMinar #fallback
|
||||||
|
|
||||||
|
compiler/i18n:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "packages/compiler/src/i18n/*"
|
||||||
|
users:
|
||||||
|
- vicb #primary
|
||||||
|
- chuckjaz
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
compiler:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "packages/compiler/*"
|
||||||
|
users:
|
||||||
|
- chuckjaz #primary
|
||||||
|
- vicb
|
||||||
|
- mhevery
|
||||||
|
- IgorMinar #fallback
|
||||||
|
|
||||||
|
compiler-cli/ngtools:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "packages/compiler-cli/src/ngtools*"
|
||||||
|
users:
|
||||||
|
- hansl
|
||||||
|
- filipesilva #fallback
|
||||||
|
- brocco #fallback
|
||||||
|
- IgorMinar #fallback
|
||||||
|
|
||||||
|
compiler-cli:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
include:
|
||||||
|
- "packages/compiler-cli/*"
|
||||||
|
- "packages/bazel/*"
|
||||||
|
exclude:
|
||||||
|
- "packages/compiler-cli/src/ngtools*"
|
||||||
|
users:
|
||||||
|
- alexeagle
|
||||||
|
- chuckjaz
|
||||||
|
- vicb
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
common:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
include:
|
||||||
|
- "packages/common/*"
|
||||||
|
exclude:
|
||||||
|
- "packages/common/http/*"
|
||||||
|
users:
|
||||||
|
- pkozlowski-opensource #primary
|
||||||
|
- vicb
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
forms:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "packages/forms/*"
|
||||||
|
users:
|
||||||
|
- kara #primary
|
||||||
|
- tinayuangao #secondary
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
http:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "packages/common/http/*"
|
||||||
|
- "packages/http/*"
|
||||||
|
users:
|
||||||
|
- alxhub #primary
|
||||||
|
- IgorMinar
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
language-service:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "packages/language-service/*"
|
||||||
|
users:
|
||||||
|
- chuckjaz #primary
|
||||||
|
# needs secondary
|
||||||
|
- vicb
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
router:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "packages/router/*"
|
||||||
|
users:
|
||||||
|
- jasonaden #primary
|
||||||
|
- vicb
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
upgrade:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "packages/upgrade/*"
|
||||||
|
users:
|
||||||
|
- petebacondarwin #primary
|
||||||
|
- gkalpak
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
platform-browser:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "packages/platform-browser/*"
|
||||||
|
users:
|
||||||
|
- vicb #primary
|
||||||
|
# needs secondary
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
platform-server:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "packages/platform-server/*"
|
||||||
|
users:
|
||||||
|
- vikerman #primary
|
||||||
|
- alxhub #secondary
|
||||||
|
- vicb
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
platform-webworker:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "packages/platform-webworker/*"
|
||||||
|
users:
|
||||||
|
- vicb #primary
|
||||||
|
# needs secondary
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
service-worker:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "packages/service-worker/*"
|
||||||
|
users:
|
||||||
|
- alxhub #primary
|
||||||
|
- gkalpak
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
benchpress:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "packages/benchpress/*"
|
||||||
|
users:
|
||||||
|
- alxhub # primary
|
||||||
|
# needs secondary
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
angular.io:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
include:
|
||||||
|
- "aio/*"
|
||||||
|
exclude:
|
||||||
|
- "aio/content/*"
|
||||||
|
users:
|
||||||
|
- petebacondarwin #primary
|
||||||
|
- IgorMinar
|
||||||
|
- gkalpak
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
angular.io-guide-and-tutorial:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
include:
|
||||||
|
- "aio/content/*"
|
||||||
|
exclude:
|
||||||
|
- "aio/content/marketing/*"
|
||||||
|
- "aio/content/navigation.json"
|
||||||
|
- "aio/content/license.md"
|
||||||
|
users:
|
||||||
|
- kapunahelewong
|
||||||
|
- stephenfluin
|
||||||
|
- petebacondarwin
|
||||||
|
- gkalpak
|
||||||
|
- IgorMinar
|
||||||
|
- mhevery #fallback
|
||||||
|
|
||||||
|
angular.io-marketing:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
include:
|
||||||
|
- "aio/content/marketing/*"
|
||||||
|
- "aio/content/navigation.json"
|
||||||
|
- "aio/content/license.md"
|
||||||
|
users:
|
||||||
|
- stephenfluin
|
||||||
|
- petebacondarwin
|
||||||
|
- gkalpak
|
||||||
|
- IgorMinar
|
||||||
|
- mhevery #fallback
|
81
.travis.yml
Normal file
81
.travis.yml
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
language: node_js
|
||||||
|
sudo: false
|
||||||
|
dist: trusty
|
||||||
|
node_js:
|
||||||
|
- '8.9.1'
|
||||||
|
|
||||||
|
addons:
|
||||||
|
# firefox: "38.0"
|
||||||
|
apt:
|
||||||
|
sources:
|
||||||
|
# needed to install g++ that is used by npms's native modules
|
||||||
|
- ubuntu-toolchain-r-test
|
||||||
|
packages:
|
||||||
|
# needed to install g++ that is used by npms's native modules
|
||||||
|
- g++-4.8
|
||||||
|
# https://docs.travis-ci.com/user/jwt
|
||||||
|
jwt:
|
||||||
|
# SAUCE_ACCESS_KEY<=secret for NGBUILDS_IO_KEY to work around travis-ci/travis-ci#7223, unencrypted value in valentine as NGBUILDS_IO_KEY>
|
||||||
|
# we alias NGBUILDS_IO_KEY to $SAUCE_ACCESS_KEY in env.sh and set the SAUCE_ACCESS_KEY there
|
||||||
|
- secure: "L7nrZwkAtFtYrP2DykPXgZvEKjkv0J/TwQ/r2QGxFTaBq4VZn+2Dw0YS7uCxoMqYzDwH0aAOqxoutibVpk8Z/16nE3tNmU5RzltMd6Xmt3qU2f/JDQLMo6PSlBodnjOUsDHJgmtrcbjhqrx/znA237BkNUu6UZRT7mxhXIZpn0U="
|
||||||
|
branches:
|
||||||
|
except:
|
||||||
|
- g3
|
||||||
|
|
||||||
|
cache:
|
||||||
|
yarn: true
|
||||||
|
directories:
|
||||||
|
- ./node_modules
|
||||||
|
- ./.chrome/chromium
|
||||||
|
- ./aio/node_modules
|
||||||
|
|
||||||
|
env:
|
||||||
|
global:
|
||||||
|
# GITHUB_TOKEN_ANGULAR=<github token, a personal access token of the angular-builds account, account access in valentine>
|
||||||
|
# This is needed for the e2e Travis matrix task to publish packages to github for continuous packages delivery.
|
||||||
|
- secure: "aCdHveZuY8AT4Jr1JoJB4LxZsnGWRe/KseZh1YXYe5UtufFCtTVHvUcLn0j2aLBF0KpdyS+hWf0i4np9jthKu2xPKriefoPgCMpisYeC0MFkwbmv+XlgkUbgkgVZMGiVyX7DCYXVahxIoOUjVMEDCbNiHTIrfEuyq24U3ok2tHc="
|
||||||
|
# FIREBASE_TOKEN
|
||||||
|
# This is needed for publishing builds to the "aio-staging" and "angular-io" firebase projects.
|
||||||
|
# This token was generated using the aio-deploy@angular.io account using `firebase login:ci` and password from valentine
|
||||||
|
- secure: "L5CyQmpwWtoR4Qi4xlWQh/cL1M6ZeJL4W4QAr4HdKFMgYt9h+Whqkymyh2NxwmCbPvWa7yUd+OiLQUDCY7L2VIg16hTwoe2CgYDyQA0BEwLzxtRrJXl93TfwMlrUx5JSIzAccD6D4sjtz8kSFMomK2Nls33xOXOukwyhVMjd0Cg="
|
||||||
|
# ANGULAR_PAYLOAD_FIREBASE_TOKEN
|
||||||
|
# This is for payload size data to "angular-payload-size" firebase project
|
||||||
|
# This token was generated using the payload@angular.io account using `firebase login:ci` and password from valentine
|
||||||
|
- secure: "SxotP/ymNy6uWAVbfwM9BlwETPEBpkRvU/F7fCtQDDic99WfQHzzUSQqHTk8eKk3GrGAOSL09vT0WfStQYEIGEoS5UHWNgOnelxhw+d5EnaoB8vQ0dKQBTK092hQg4feFprr+B/tCasyMV6mVwpUzZMbIJNn/Rx7H5g1bp+Gkfg="
|
||||||
|
matrix:
|
||||||
|
# Order: a slower build first, so that we don't occupy an idle travis worker waiting for others to complete.
|
||||||
|
- CI_MODE=e2e
|
||||||
|
- CI_MODE=e2e_2
|
||||||
|
- CI_MODE=js
|
||||||
|
- CI_MODE=saucelabs_required
|
||||||
|
# deactivated, see #19768
|
||||||
|
# - CI_MODE=browserstack_required
|
||||||
|
- CI_MODE=saucelabs_optional
|
||||||
|
- CI_MODE=browserstack_optional
|
||||||
|
- CI_MODE=aio_tools_test
|
||||||
|
- CI_MODE=aio
|
||||||
|
- CI_MODE=aio_e2e AIO_SHARD=0
|
||||||
|
- CI_MODE=aio_e2e AIO_SHARD=1
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
fast_finish: true
|
||||||
|
allow_failures:
|
||||||
|
- env: "CI_MODE=saucelabs_optional"
|
||||||
|
- env: "CI_MODE=browserstack_optional"
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
# source the env.sh script so that the exported variables are available to other scripts later on
|
||||||
|
- source ./scripts/ci/env.sh print
|
||||||
|
|
||||||
|
install:
|
||||||
|
- ./scripts/ci/install.sh
|
||||||
|
|
||||||
|
script:
|
||||||
|
- ./scripts/ci/build.sh
|
||||||
|
- ./scripts/ci/test.sh
|
||||||
|
# deploy is part of 'script' and not 'after_success' so that we fail the build if the deployment fails
|
||||||
|
- ./scripts/ci/deploy.sh
|
||||||
|
- ./scripts/ci/angular.sh
|
||||||
|
# all the scripts under this line will not quickly abort in case ${TRAVIS_TEST_RESULT} is 1 (job failure)
|
||||||
|
- ./scripts/ci/cleanup.sh
|
||||||
|
- ./scripts/ci/print-logs.sh
|
23
.vscode/README.md
vendored
23
.vscode/README.md
vendored
@ -1,23 +0,0 @@
|
|||||||
# VSCode Configuration
|
|
||||||
|
|
||||||
This folder contains opt-in [Workspace Settings](https://code.visualstudio.com/docs/getstarted/settings) 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 settings follow the steps below:
|
|
||||||
|
|
||||||
- install <https://marketplace.visualstudio.com/items?itemName=xaver.clang-format>
|
|
||||||
- copy `.vscode/recommended-settings.json` to `.vscode/settings.json`
|
|
||||||
- restart the editor
|
|
||||||
|
|
||||||
If you already have your custom workspace settings you should instead manually merge the file content.
|
|
||||||
|
|
||||||
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-settings.json`
|
|
||||||
|
|
||||||
If you wish to add extra configuration items please keep in mind any settings you add here will be used by many users.
|
|
||||||
|
|
||||||
Try to keep these settings to things that help facilitate the development process and avoid altering the user workflow whenever possible.
|
|
12
.vscode/extensions.json
vendored
12
.vscode/extensions.json
vendored
@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
// See http://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",
|
|
||||||
],
|
|
||||||
}
|
|
31
.vscode/recommended-settings.json
vendored
31
.vscode/recommended-settings.json
vendored
@ -1,31 +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,
|
|
||||||
},
|
|
||||||
"git.ignoreLimitWarning": true,
|
|
||||||
}
|
|
120
BUILD.bazel
120
BUILD.bazel
@ -1,92 +1,64 @@
|
|||||||
package(default_visibility = ["//visibility:public"])
|
package(default_visibility = ["//visibility:public"])
|
||||||
|
|
||||||
load("//tools:defaults.bzl", "karma_web_test")
|
|
||||||
|
|
||||||
exports_files([
|
exports_files([
|
||||||
"LICENSE",
|
"tsconfig.json",
|
||||||
"protractor-perf.conf.js",
|
|
||||||
"karma-js.conf.js",
|
|
||||||
"browser-providers.conf.js",
|
|
||||||
])
|
])
|
||||||
|
|
||||||
alias(
|
# This rule belongs in node_modules/BUILD
|
||||||
name = "tsconfig.json",
|
# It's here as a workaround for
|
||||||
actual = "//packages:tsconfig-build.json",
|
# https://github.com/bazelbuild/bazel/issues/374#issuecomment-296217940
|
||||||
|
filegroup(
|
||||||
|
name = "node_modules",
|
||||||
|
# Performance workaround: list individual files
|
||||||
|
# Reduces the number of files as inputs to nodejs_binary:
|
||||||
|
# bazel query "deps(:node_modules)" | wc -l
|
||||||
|
# This won't scale in the general case.
|
||||||
|
# TODO(alexeagle): figure out what to do
|
||||||
|
srcs = glob(["/".join([
|
||||||
|
"node_modules",
|
||||||
|
pkg,
|
||||||
|
"**",
|
||||||
|
ext,
|
||||||
|
]) for pkg in [
|
||||||
|
"jasmine",
|
||||||
|
"typescript",
|
||||||
|
"zone.js",
|
||||||
|
"tsutils",
|
||||||
|
"@types",
|
||||||
|
"tsickle",
|
||||||
|
"hammerjs",
|
||||||
|
"protobufjs",
|
||||||
|
"bytebuffer",
|
||||||
|
"reflect-metadata",
|
||||||
|
"source-map-support",
|
||||||
|
"minimist",
|
||||||
|
"tslib",
|
||||||
|
] for ext in [
|
||||||
|
"*.js",
|
||||||
|
"*.json",
|
||||||
|
"*.d.ts",
|
||||||
|
]]),
|
||||||
)
|
)
|
||||||
|
|
||||||
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:client/core.js",
|
"//:node_modules/reflect-metadata/Reflect.js",
|
||||||
"@npm//node_modules/zone.js:dist/zone.js",
|
"//:node_modules/zone.js/dist/zone.js",
|
||||||
"@npm//node_modules/zone.js:dist/zone-testing.js",
|
"//:node_modules/zone.js/dist/async-test.js",
|
||||||
"@npm//node_modules/zone.js:dist/task-tracking.js",
|
"//:node_modules/zone.js/dist/sync-test.js",
|
||||||
"//:test-events.js",
|
"//:node_modules/zone.js/dist/fake-async-test.js",
|
||||||
"//:shims_for_IE.js",
|
"//:node_modules/zone.js/dist/proxy.js",
|
||||||
# Including systemjs because it defines `__eval`, which produces correct stack traces.
|
"//:node_modules/zone.js/dist/jasmine-patch.js",
|
||||||
"@npm//node_modules/systemjs:dist/system.src.js",
|
|
||||||
"@npm//node_modules/reflect-metadata:Reflect.js",
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
filegroup(
|
filegroup(
|
||||||
name = "angularjs_scripts",
|
name = "angularjs",
|
||||||
|
# do not sort
|
||||||
srcs = [
|
srcs = [
|
||||||
# We also declare the unminfied AngularJS files since these can be used for
|
"//:node_modules/angular/angular.js",
|
||||||
# local debugging (e.g. see: packages/upgrade/test/common/test_helpers.ts)
|
"//:node_modules/angular-mocks/angular-mocks.js",
|
||||||
"@npm//node_modules/angular:angular.js",
|
|
||||||
"@npm//node_modules/angular:angular.min.js",
|
|
||||||
"@npm//node_modules/angular-1.5:angular.js",
|
|
||||||
"@npm//node_modules/angular-1.5:angular.min.js",
|
|
||||||
"@npm//node_modules/angular-1.6:angular.js",
|
|
||||||
"@npm//node_modules/angular-1.6:angular.min.js",
|
|
||||||
"@npm//node_modules/angular-mocks:angular-mocks.js",
|
|
||||||
"@npm//node_modules/angular-mocks-1.5:angular-mocks.js",
|
|
||||||
"@npm//node_modules/angular-mocks-1.6:angular-mocks.js",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
# To run a karma_web_test target locally on SauceLabs:
|
|
||||||
# 1) have SAUCE_USERNAME, SAUCE_ACCESS_KEY (and optionally a SAUCE_TUNNEL_IDENTIFIER) set in your environment
|
|
||||||
# 2) open a sauce connection with `./scripts/saucelabs/start-tunnel.sh`
|
|
||||||
# NOTE: start-tunnel.sh uses `node_modules/sauce-connect` which is current linux specific:
|
|
||||||
# "sauce-connect": "https://saucelabs.com/downloads/sc-4.5.3-linux.tar.gz".
|
|
||||||
# On OSX or Windows you'll need to use the appropriate sauce-connect binary.
|
|
||||||
# 3) run target with `yarn bazel test --config=saucelabs <target>`
|
|
||||||
# NOTE: --config=saucelabs is required as it makes the SAUCE_XXX environment variables available to
|
|
||||||
# the action. See /.bazelrc.
|
|
||||||
karma_web_test(
|
|
||||||
name = "test_web_all",
|
|
||||||
tags = [
|
|
||||||
"local",
|
|
||||||
"manual",
|
|
||||||
"saucelabs",
|
|
||||||
],
|
|
||||||
deps = [
|
|
||||||
# We combine all tests into a single karma_web_test target
|
|
||||||
# as running them as seperate targets in parallel leads to too many
|
|
||||||
# browsers being acquired at once in SauceLabs and the tests flake out
|
|
||||||
# TODO: this is an example subset of tests below, add all remaining angular tests
|
|
||||||
"//packages/common/http/test:test_lib",
|
|
||||||
"//packages/common/http/testing/test:test_lib",
|
|
||||||
"//packages/common/test:test_lib",
|
|
||||||
"//packages/core/test:test_lib",
|
|
||||||
"//packages/forms/test:test_lib",
|
|
||||||
"//packages/http/test:test_lib",
|
|
||||||
# "//packages/router/test:test_lib",
|
|
||||||
# //packages/router/test:test_lib fails with:
|
|
||||||
# IE 11.0.0 (Windows 8.1.0.0) bootstrap should restore the scrolling position FAILED
|
|
||||||
# Expected undefined to equal 5000.
|
|
||||||
# at stack (eval code:2338:11)
|
|
||||||
# at buildExpectationResult (eval code:2305:5)
|
|
||||||
# at expectationResultFactory (eval code:858:11)
|
|
||||||
# at Spec.prototype.addExpectationResult (eval code:487:5)
|
|
||||||
# at addExpectationResult (eval code:802:9)
|
|
||||||
# at Anonymous function (eval code:2252:7)
|
|
||||||
# at Anonymous function (eval code:339:25)
|
|
||||||
# at step (eval code:133:17)
|
|
||||||
# at Anonymous function (eval code:114:50)
|
|
||||||
# at fulfilled (eval code:104:47)
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
1497
CHANGELOG.md
1497
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@ -51,15 +51,19 @@ and help you to craft the change so that it is successfully accepted into the pr
|
|||||||
|
|
||||||
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. 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.
|
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 scenario using http://plnkr.co. Having a live, reproducible scenario gives us a wealth of important information without going back & forth to you with additional questions like:
|
||||||
|
|
||||||
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.
|
- version of Angular used
|
||||||
|
- 3rd-party libraries and their versions
|
||||||
|
- and most importantly - a use-case that fails
|
||||||
|
|
||||||
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.
|
A minimal reproduce scenario using http://plnkr.co/ allows us to quickly confirm a bug (or point out coding problem) as well as confirm that we are fixing the right problem. If plunker is not a suitable way to demonstrate the problem (for example for issues related to our npm packaging), please create a standalone git repository demonstrating the problem.
|
||||||
|
|
||||||
|
We will be insisting on a minimal reproduce 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 plunk. We understand that sometimes it might be hard to extract essentials bits of code from a larger code-base 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 filling out our [new issue form](https://github.com/angular/angular/issues/new).
|
||||||
|
|
||||||
|
|
||||||
### <a name="submit-pr"></a> Submitting a Pull Request (PR)
|
### <a name="submit-pr"></a> Submitting a Pull Request (PR)
|
||||||
@ -67,8 +71,6 @@ 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
|
1. Search [GitHub](https://github.com/angular/angular/pulls) for an open or closed PR
|
||||||
that relates to your submission. You don't want to duplicate effort.
|
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.
|
|
||||||
Discussing the design up front helps to ensure that we're ready to accept your work.
|
|
||||||
1. Please sign our [Contributor License Agreement (CLA)](#cla) before sending PRs.
|
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.
|
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.
|
||||||
1. Fork the angular/angular repo.
|
1. Fork the angular/angular repo.
|
||||||
@ -191,7 +193,7 @@ If the commit reverts a previous commit, it should begin with `revert: `, follow
|
|||||||
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)
|
||||||
* **ci**: Changes to our CI configuration files and scripts (example scopes: Circle, BrowserStack, SauceLabs)
|
* **ci**: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
|
||||||
* **docs**: Documentation only changes
|
* **docs**: Documentation only changes
|
||||||
* **feat**: A new feature
|
* **feat**: A new feature
|
||||||
* **fix**: A bug fix
|
* **fix**: A bug fix
|
||||||
@ -210,7 +212,6 @@ The following is the list of supported scopes:
|
|||||||
* **compiler**
|
* **compiler**
|
||||||
* **compiler-cli**
|
* **compiler-cli**
|
||||||
* **core**
|
* **core**
|
||||||
* **elements**
|
|
||||||
* **forms**
|
* **forms**
|
||||||
* **http**
|
* **http**
|
||||||
* **language-service**
|
* **language-service**
|
||||||
@ -225,15 +226,10 @@ The following is the list of supported scopes:
|
|||||||
|
|
||||||
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.
|
* **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.
|
||||||
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
|
* **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
|
* **aio**: used for docs-app (angular.io) related changes within the /aio directory of the repo
|
||||||
repo
|
* none/empty string: useful for `style`, `test` and `refactor` changes that are done across all packages (e.g. `style: add missing semicolons`)
|
||||||
* 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`).
|
|
||||||
|
|
||||||
### Subject
|
### Subject
|
||||||
The subject contains a succinct description of the change:
|
The subject contains a succinct description of the change:
|
||||||
@ -272,7 +268,7 @@ changes to be accepted, the CLA must be signed. It's a quick process, we promise
|
|||||||
* 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.
|
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>
|
<hr>
|
||||||
|
|
||||||
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
|||||||
The MIT License
|
The MIT License
|
||||||
|
|
||||||
Copyright (c) 2010-2019 Google LLC. http://angular.io/license
|
Copyright (c) 2014-2018 Google, Inc. http://angular.io
|
||||||
|
|
||||||
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
|
||||||
|
18
README.md
18
README.md
@ -1,9 +1,14 @@
|
|||||||
[](https://circleci.com/gh/angular/workflows/angular/tree/master)
|
[](https://travis-ci.org/angular/angular)
|
||||||
|
[](https://circleci.com/gh/angular/angular/tree/master)
|
||||||
[](https://www.browserstack.com/automate/public-build/LzF3RzBVVGt6VWE2S0hHaC9uYllOZz09LS1BVjNTclBKV0x4eVRlcjA4QVY1M0N3PT0=--eb4ce8c8dc2c1c5b2b5352d473ee12a73ac20e06)
|
[](https://www.browserstack.com/automate/public-build/LzF3RzBVVGt6VWE2S0hHaC9uYllOZz09LS1BVjNTclBKV0x4eVRlcjA4QVY1M0N3PT0=--eb4ce8c8dc2c1c5b2b5352d473ee12a73ac20e06)
|
||||||
[](https://gitter.im/angular/angular?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
[](https://gitter.im/angular/angular?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||||
[](https://www.npmjs.com/@angular/core)
|
[](https://www.npmjs.com/@angular/core)
|
||||||
|
|
||||||
|
|
||||||
|
[](https://saucelabs.com/u/angular2-ci)
|
||||||
|
|
||||||
|
*Safari (7+), iOS (7+) and IE mobile (11) are tested on [BrowserStack][browserstack].*
|
||||||
|
|
||||||
# Angular
|
# Angular
|
||||||
|
|
||||||
Angular is a development platform for building mobile and desktop web applications using Typescript/JavaScript and other languages.
|
Angular is a development platform for building mobile and desktop web applications using Typescript/JavaScript and other languages.
|
||||||
@ -12,17 +17,12 @@ Angular is a development platform for building mobile and desktop web applicatio
|
|||||||
|
|
||||||
[Get started in 5 minutes][quickstart].
|
[Get started in 5 minutes][quickstart].
|
||||||
|
|
||||||
## Changelog
|
|
||||||
|
|
||||||
[Learn about the latest improvements][changelog].
|
|
||||||
|
|
||||||
## Want to help?
|
## Want to help?
|
||||||
|
|
||||||
Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on our
|
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).
|
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).
|
||||||
|
|
||||||
[browserstack]: https://www.browserstack.com/automate/public-build/LzF3RzBVVGt6VWE2S0hHaC9uYllOZz09LS1BVjNTclBKV0x4eVRlcjA4QVY1M0N3PT0=--eb4ce8c8dc2c1c5b2b5352d473ee12a73ac20e06
|
[browserstack]: https://www.browserstack.com/automate/public-build/LzF3RzBVVGt6VWE2S0hHaC9uYllOZz09LS1BVjNTclBKV0x4eVRlcjA4QVY1M0N3PT0=--eb4ce8c8dc2c1c5b2b5352d473ee12a73ac20e06
|
||||||
[contributing]: https://github.com/angular/angular/blob/master/CONTRIBUTING.md
|
[contributing]: http://github.com/angular/angular/blob/master/CONTRIBUTING.md
|
||||||
[quickstart]: https://angular.io/guide/quickstart
|
[quickstart]: https://angular.io/docs/ts/latest/quickstart.html
|
||||||
[changelog]: https://github.com/angular/angular/blob/master/CHANGELOG.md
|
[ng]: http://angular.io
|
||||||
[ng]: https://angular.io
|
|
||||||
|
145
WORKSPACE
145
WORKSPACE
@ -1,116 +1,61 @@
|
|||||||
workspace(name = "angular")
|
workspace(name = "angular")
|
||||||
|
|
||||||
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 = "npm_bazel_typescript",
|
|
||||||
# path = "../rules_typescript",
|
|
||||||
#)
|
|
||||||
|
|
||||||
# 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 = "3a3efbf223f6de733475602844ad3a8faa02abda25ab8cfe1d1ed0db134887cf",
|
url = "https://github.com/bazelbuild/rules_nodejs/archive/0.5.0.zip",
|
||||||
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/0.27.12/rules_nodejs-0.27.12.tar.gz"],
|
strip_prefix = "rules_nodejs-0.5.0",
|
||||||
|
sha256 = "06aabb253c3867d51724386ac5622a0a238bbd82e2c70ce1d09ee3ceac4c31d6",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Check the bazel version and download npm dependencies
|
load("@build_bazel_rules_nodejs//:defs.bzl", "check_bazel_version", "node_repositories")
|
||||||
load("@build_bazel_rules_nodejs//:defs.bzl", "check_bazel_version", "check_rules_nodejs_version", "node_repositories", "yarn_install")
|
|
||||||
|
|
||||||
# Bazel version must be at least v0.21.0 because:
|
check_bazel_version("0.9.0")
|
||||||
# - 0.21.0 Using --incompatible_strict_action_env flag fixes cache when running `yarn bazel`
|
node_repositories(package_json = ["//:package.json"])
|
||||||
# (see https://github.com/angular/angular/issues/27514#issuecomment-451438271)
|
|
||||||
check_bazel_version(
|
|
||||||
message = """
|
|
||||||
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`)
|
|
||||||
|
|
||||||
""",
|
http_archive(
|
||||||
minimum_bazel_version = "0.21.0",
|
name = "build_bazel_rules_typescript",
|
||||||
|
url = "https://github.com/bazelbuild/rules_typescript/archive/0.11.0.zip",
|
||||||
|
strip_prefix = "rules_typescript-0.11.0",
|
||||||
|
sha256 = "ce7bac7b5287d5162fcbe4f7c14ff507ae7d506ceb44626ad09f6b7e27d3260b",
|
||||||
)
|
)
|
||||||
|
|
||||||
# The NodeJS rules version must be at least v0.15.3 because:
|
load("@build_bazel_rules_typescript//:defs.bzl", "ts_setup_workspace")
|
||||||
# - 0.15.2 Re-introduced the prod_only attribute on yarn_install
|
|
||||||
# - 0.15.3 Includes a fix for the `jasmine_node_test` rule ignoring target tags
|
|
||||||
# - 0.16.8 Supports npm installed bazel workspaces
|
|
||||||
# - 0.26.0 Fix for data files in yarn_install and npm_install
|
|
||||||
# - 0.27.12 Adds NodeModuleSources provider for transtive npm deps support
|
|
||||||
check_rules_nodejs_version("0.27.12")
|
|
||||||
|
|
||||||
# Setup the Node.js toolchain
|
|
||||||
node_repositories(
|
|
||||||
node_version = "10.9.0",
|
|
||||||
package_json = ["//:package.json"],
|
|
||||||
preserve_symlinks = True,
|
|
||||||
# yarn 1.13.0 under Bazel has a regression on Windows that causes build errors on rebuilds:
|
|
||||||
# ```
|
|
||||||
# ERROR: Source forest creation failed: C:/.../fyuc5c3n/execroot/angular/external (Directory not empty)
|
|
||||||
# ```
|
|
||||||
# See https://github.com/angular/angular/pull/29431 for more information.
|
|
||||||
# It possible that versions of yarn past 1.13.0 do not have this issue, however, before
|
|
||||||
# advancing this version we need to test manually on Windows that the above error does not
|
|
||||||
# happen as the issue is not caught by CI.
|
|
||||||
yarn_version = "1.12.1",
|
|
||||||
)
|
|
||||||
|
|
||||||
yarn_install(
|
|
||||||
name = "npm",
|
|
||||||
data = [
|
|
||||||
"//:tools/npm/@angular_bazel/index.js",
|
|
||||||
"//:tools/npm/@angular_bazel/package.json",
|
|
||||||
"//:tools/postinstall-patches.js",
|
|
||||||
"//:tools/yarn/check-yarn.js",
|
|
||||||
],
|
|
||||||
package_json = "//:package.json",
|
|
||||||
# Don't install devDependencies, they are large and not used under Bazel
|
|
||||||
prod_only = True,
|
|
||||||
yarn_lock = "//:yarn.lock",
|
|
||||||
)
|
|
||||||
|
|
||||||
# Install all bazel dependencies of the @npm npm packages
|
|
||||||
load("@npm//:install_bazel_dependencies.bzl", "install_bazel_dependencies")
|
|
||||||
|
|
||||||
install_bazel_dependencies()
|
|
||||||
|
|
||||||
# Load angular dependencies
|
|
||||||
load("//packages/bazel:package.bzl", "rules_angular_dev_dependencies")
|
|
||||||
|
|
||||||
rules_angular_dev_dependencies()
|
|
||||||
|
|
||||||
# Load karma dependencies
|
|
||||||
load("@npm_bazel_karma//:package.bzl", "rules_karma_dependencies")
|
|
||||||
|
|
||||||
rules_karma_dependencies()
|
|
||||||
|
|
||||||
# Setup the rules_webtesting toolchain
|
|
||||||
load("@io_bazel_rules_webtesting//web:repositories.bzl", "web_test_repositories")
|
|
||||||
|
|
||||||
web_test_repositories()
|
|
||||||
|
|
||||||
# Temporary work-around for https://github.com/angular/angular/issues/28681
|
|
||||||
# TODO(gregmagolan): go back to @io_bazel_rules_webtesting browser_repositories
|
|
||||||
load("@npm_bazel_karma//:browser_repositories.bzl", "browser_repositories")
|
|
||||||
|
|
||||||
browser_repositories()
|
|
||||||
|
|
||||||
# Setup the rules_typescript tooolchain
|
|
||||||
load("@npm_bazel_typescript//:index.bzl", "ts_setup_workspace")
|
|
||||||
|
|
||||||
ts_setup_workspace()
|
ts_setup_workspace()
|
||||||
|
|
||||||
# Setup the rules_sass toolchain
|
local_repository(
|
||||||
load("@io_bazel_rules_sass//sass:sass_repositories.bzl", "sass_repositories")
|
name = "rxjs",
|
||||||
|
path = "node_modules/rxjs/src",
|
||||||
|
)
|
||||||
|
|
||||||
sass_repositories()
|
# This commit matches the version of buildifier in angular/ngcontainer
|
||||||
|
# If you change this, also check if it matches the version in the angular/ngcontainer
|
||||||
|
# version in /.circleci/config.yml
|
||||||
|
BAZEL_BUILDTOOLS_VERSION = "b3b620e8bcff18ed3378cd3f35ebeb7016d71f71"
|
||||||
|
|
||||||
# Setup the skydoc toolchain
|
http_archive(
|
||||||
load("@io_bazel_skydoc//skylark:skylark.bzl", "skydoc_repositories")
|
name = "com_github_bazelbuild_buildtools",
|
||||||
|
url = "https://github.com/bazelbuild/buildtools/archive/%s.zip" % BAZEL_BUILDTOOLS_VERSION,
|
||||||
|
strip_prefix = "buildtools-%s" % BAZEL_BUILDTOOLS_VERSION,
|
||||||
|
sha256 = "dad19224258ed67cbdbae9b7befb785c3b966e5a33b04b3ce58ddb7824b97d73",
|
||||||
|
)
|
||||||
|
|
||||||
skydoc_repositories()
|
http_archive(
|
||||||
|
name = "io_bazel_rules_go",
|
||||||
|
url = "https://github.com/bazelbuild/rules_go/releases/download/0.7.1/rules_go-0.7.1.tar.gz",
|
||||||
|
sha256 = "341d5eacef704415386974bc82a1783a8b7ffbff2ab6ba02375e1ca20d9b031c",
|
||||||
|
)
|
||||||
|
|
||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_rules_dependencies", "go_register_toolchains")
|
||||||
|
|
||||||
|
go_rules_dependencies()
|
||||||
|
|
||||||
|
go_register_toolchains()
|
||||||
|
|
||||||
|
# Fetching the Bazel source code allows us to compile the Skylark linter
|
||||||
|
http_archive(
|
||||||
|
name = "io_bazel",
|
||||||
|
url = "https://github.com/bazelbuild/bazel/archive/9755c72b48866ed034bd28aa033e9abd27431b1e.zip",
|
||||||
|
strip_prefix = "bazel-9755c72b48866ed034bd28aa033e9abd27431b1e",
|
||||||
|
sha256 = "5b8443fc3481b5fcd9e7f348e1dd93c1397f78b223623c39eb56494c55f41962",
|
||||||
|
)
|
||||||
|
70
aio/.angular-cli.json
Normal file
70
aio/.angular-cli.json
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
{
|
||||||
|
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||||
|
"project": {
|
||||||
|
"name": "site"
|
||||||
|
},
|
||||||
|
"apps": [
|
||||||
|
{
|
||||||
|
"root": "src",
|
||||||
|
"outDir": "dist",
|
||||||
|
"assets": [
|
||||||
|
"assets",
|
||||||
|
"generated",
|
||||||
|
"app/search/search-worker.js",
|
||||||
|
"favicon.ico",
|
||||||
|
"pwa-manifest.json",
|
||||||
|
"google385281288605d160.html"
|
||||||
|
],
|
||||||
|
"index": "index.html",
|
||||||
|
"main": "main.ts",
|
||||||
|
"polyfills": "polyfills.ts",
|
||||||
|
"test": "test.ts",
|
||||||
|
"tsconfig": "tsconfig.app.json",
|
||||||
|
"testTsconfig": "tsconfig.spec.json",
|
||||||
|
"prefix": "aio",
|
||||||
|
"serviceWorker": false,
|
||||||
|
"styles": [
|
||||||
|
"styles.scss"
|
||||||
|
],
|
||||||
|
"scripts": [
|
||||||
|
],
|
||||||
|
"environmentSource": "environments/environment.ts",
|
||||||
|
"environments": {
|
||||||
|
"dev": "environments/environment.ts",
|
||||||
|
"next": "environments/environment.next.ts",
|
||||||
|
"stable": "environments/environment.stable.ts",
|
||||||
|
"archive": "environments/environment.archive.ts"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"e2e": {
|
||||||
|
"protractor": {
|
||||||
|
"config": "./protractor.conf.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lint": [
|
||||||
|
{
|
||||||
|
"project": "src/tsconfig.app.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"project": "src/tsconfig.spec.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"project": "e2e/tsconfig.e2e.json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"test": {
|
||||||
|
"karma": {
|
||||||
|
"config": "./karma.conf.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaults": {
|
||||||
|
"styleExt": "scss",
|
||||||
|
"component": {
|
||||||
|
"inlineStyle": true
|
||||||
|
},
|
||||||
|
"build": {
|
||||||
|
"namedChunks": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5
aio/.gitignore
vendored
5
aio/.gitignore
vendored
@ -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
|
||||||
@ -46,3 +44,6 @@ protractor-results*.txt
|
|||||||
# System Files
|
# System Files
|
||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
|
||||||
|
# copied dependencies
|
||||||
|
src/assets/js/lunr*
|
||||||
|
@ -8,7 +8,7 @@ Everything in this folder is part of the documentation project. This includes
|
|||||||
|
|
||||||
## Developer tasks
|
## Developer tasks
|
||||||
|
|
||||||
We use [Yarn](https://yarnpkg.com) to manage the dependencies and to run build tasks.
|
We use `yarn` to manage the dependencies and to run build tasks.
|
||||||
You should run all these tasks from the `angular/aio` folder.
|
You should run all these tasks from the `angular/aio` folder.
|
||||||
Here are the most important tasks you might need to use:
|
Here are the most important tasks you might need to use:
|
||||||
|
|
||||||
@ -23,7 +23,6 @@ Here are the most important tasks you might need to use:
|
|||||||
* `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.
|
||||||
* `yarn test` - watch all the source files, for the doc-viewer, and run all the unit tests when any change.
|
* `yarn test` - watch all the source files, for the doc-viewer, and run all the unit tests when any change.
|
||||||
* `yarn test --watch=false` - run all the unit tests once.
|
|
||||||
* `yarn e2e` - run all the e2e tests for the doc-viewer.
|
* `yarn e2e` - run all the e2e tests for the doc-viewer.
|
||||||
|
|
||||||
* `yarn docs` - generate all the docs from the source files.
|
* `yarn docs` - generate all the docs from the source files.
|
||||||
@ -41,12 +40,18 @@ Here are the most important tasks you might need to use:
|
|||||||
- `yarn example-e2e --filter=foo` - limit e2e tests to those containing the word "foo"
|
- `yarn example-e2e --filter=foo` - limit e2e tests to those containing the word "foo"
|
||||||
- `yarn example-e2e --setup --local` - run e2e tests with the local version of Angular contained in the "dist" folder
|
- `yarn example-e2e --setup --local` - run e2e tests with the local version of Angular contained in the "dist" folder
|
||||||
|
|
||||||
|
* `yarn build-ie-polyfills` - generates a js file of polyfills that can be loaded in Internet Explorer.
|
||||||
|
|
||||||
## Using ServiceWorker locally
|
## Using ServiceWorker locally
|
||||||
|
|
||||||
Running `yarn start` (even when explicitly targeting production mode) does not set up the
|
Since abb36e3cb, running `yarn start --prod` will no longer set up the ServiceWorker, which
|
||||||
ServiceWorker. If you want to test the ServiceWorker locally, you can use `yarn build` and then
|
would require manually running `yarn sw-manifest` and `yarn sw-copy` (something that is not possible
|
||||||
serve the files in `dist/` with `yarn http-server dist -p 4200`.
|
with webpack serving the files from memory).
|
||||||
|
|
||||||
|
If you want to test ServiceWorker locally, you can use `yarn build` and serve the files in `dist/`
|
||||||
|
with `yarn http-server dist -p 4200`.
|
||||||
|
|
||||||
|
For more details see #16745.
|
||||||
|
|
||||||
|
|
||||||
## Guide to authoring
|
## Guide to authoring
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Image metadata and config
|
# Image metadata and config
|
||||||
FROM debian:stretch
|
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=team,aio-contributors
|
||||||
ARG TEST_AIO_GITHUB_TEAM_SLUGS=test-team-1,test-team-2
|
ARG TEST_AIO_GITHUB_TEAM_SLUGS=team,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
|
||||||
@ -40,7 +40,7 @@ ARG AIO_TRUSTED_PR_LABEL="aio: preview"
|
|||||||
ARG TEST_AIO_TRUSTED_PR_LABEL="aio: preview"
|
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=preview.localhost
|
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
|
||||||
@ -76,20 +76,21 @@ RUN apt-get update -y && apt-get install -y curl
|
|||||||
RUN curl --silent --show-error --location https://deb.nodesource.com/setup_10.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 stretch-backports main" | tee /etc/apt/sources.list.d/backports.list
|
RUN echo "deb http://ftp.debian.org/debian jessie-backports main" | tee /etc/apt/sources.list.d/backports.list
|
||||||
|
|
||||||
|
|
||||||
# Install packages
|
# Install packages
|
||||||
RUN apt-get update -y && apt-get install -y \
|
RUN apt-get update -y && apt-get install -y \
|
||||||
cron=3.0pl1-128+deb9u1 \
|
chkconfig \
|
||||||
dnsmasq=2.76-5+deb9u2 \
|
cron \
|
||||||
nano=2.7.4-1 \
|
dnsmasq \
|
||||||
nginx=1.10.3-1+deb9u2 \
|
nano \
|
||||||
nodejs=10.15.3-1nodesource1 \
|
nodejs \
|
||||||
openssl=1.1.0j-1~deb9u1 \
|
openssl \
|
||||||
rsyslog=8.24.0-1 \
|
rsyslog \
|
||||||
yarn=1.15.2-1
|
yarn
|
||||||
RUN yarn global add pm2@3.5.0
|
RUN apt-get install -t jessie-backports -y nginx
|
||||||
|
RUN yarn global add pm2@2
|
||||||
|
|
||||||
|
|
||||||
# Set up log rotation
|
# Set up log rotation
|
||||||
@ -150,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
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
# Periodically clean up builds that do not correspond to currently open PRs
|
# Periodically clean up builds that do not correspond to currently open PRs
|
||||||
0 12 * * * /usr/local/bin/aio-clean-up >> /var/log/cron.log 2>&1
|
0 12 * * * root /usr/local/bin/aio-clean-up >> /var/log/cron.log 2>&1
|
||||||
|
@ -36,11 +36,6 @@ server {
|
|||||||
access_log {{$AIO_NGINX_LOGS_DIR}}/access.log;
|
access_log {{$AIO_NGINX_LOGS_DIR}}/access.log;
|
||||||
error_log {{$AIO_NGINX_LOGS_DIR}}/error.log;
|
error_log {{$AIO_NGINX_LOGS_DIR}}/error.log;
|
||||||
|
|
||||||
error_page 404 /404.html;
|
|
||||||
location "=/404.html" {
|
|
||||||
internal;
|
|
||||||
}
|
|
||||||
|
|
||||||
location "~/[^/]+\.[^/]+$" {
|
location "~/[^/]+\.[^/]+$" {
|
||||||
try_files $uri $uri/ =404;
|
try_files $uri $uri/ =404;
|
||||||
}
|
}
|
||||||
@ -71,21 +66,6 @@ server {
|
|||||||
return 200 '';
|
return 200 '';
|
||||||
}
|
}
|
||||||
|
|
||||||
# Check PRs previewability
|
|
||||||
location "~^/can-have-public-preview/\d+/?$" {
|
|
||||||
if ($request_method != "GET") {
|
|
||||||
add_header Allow "GET";
|
|
||||||
return 405;
|
|
||||||
}
|
|
||||||
|
|
||||||
proxy_pass_request_headers on;
|
|
||||||
proxy_redirect off;
|
|
||||||
proxy_method GET;
|
|
||||||
proxy_pass http://{{$AIO_PREVIEW_SERVER_HOSTNAME}}:{{$AIO_PREVIEW_SERVER_PORT}}$request_uri;
|
|
||||||
|
|
||||||
resolver 127.0.0.1;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Notify about CircleCI builds
|
# Notify about CircleCI builds
|
||||||
location "~^/circle-build/?$" {
|
location "~^/circle-build/?$" {
|
||||||
if ($request_method != "POST") {
|
if ($request_method != "POST") {
|
||||||
|
@ -5,12 +5,12 @@ import * as shell from 'shelljs';
|
|||||||
import {HIDDEN_DIR_PREFIX} from '../common/constants';
|
import {HIDDEN_DIR_PREFIX} from '../common/constants';
|
||||||
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 {assertNotMissingOrEmpty, getPrInfoFromDownloadPath, Logger} from '../common/utils';
|
import {assertNotMissingOrEmpty, createLogger, getPrInfoFromDownloadPath} from '../common/utils';
|
||||||
|
|
||||||
// Classes
|
// Classes
|
||||||
export class BuildCleaner {
|
export class BuildCleaner {
|
||||||
|
|
||||||
private logger = new Logger('BuildCleaner');
|
private logger = createLogger('BuildCleaner');
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
constructor(protected buildsDir: string, protected githubOrg: string, protected githubRepo: string,
|
constructor(protected buildsDir: string, protected githubOrg: string, protected githubRepo: string,
|
||||||
@ -75,7 +75,8 @@ export class BuildCleaner {
|
|||||||
public removeDir(dir: string): void {
|
public removeDir(dir: string): void {
|
||||||
try {
|
try {
|
||||||
if (shell.test('-d', dir)) {
|
if (shell.test('-d', dir)) {
|
||||||
shell.chmod('-R', 'a+w', dir);
|
// Undocumented signature (see https://github.com/shelljs/shelljs/pull/663).
|
||||||
|
(shell as any).chmod('-R', 'a+w', dir);
|
||||||
shell.rm('-rf', dir);
|
shell.rm('-rf', dir);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -122,6 +123,6 @@ export class BuildCleaner {
|
|||||||
this.logger.log(`Existing downloads: ${existingDownloads.length}`);
|
this.logger.log(`Existing downloads: ${existingDownloads.length}`);
|
||||||
this.logger.log(`Removing ${toRemove.length} download(s): ${toRemove.join(', ')}`);
|
this.logger.log(`Removing ${toRemove.length} download(s): ${toRemove.join(', ')}`);
|
||||||
|
|
||||||
toRemove.forEach(filePath => shell.rm(path.join(this.downloadsDir, filePath)));
|
toRemove.forEach(filePath => shell.rm(filePath));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@ export class CircleCiApi {
|
|||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
throw new Error(`${baseUrl}: ${response.status} - ${response.statusText}`);
|
throw new Error(`${baseUrl}: ${response.status} - ${response.statusText}`);
|
||||||
}
|
}
|
||||||
return response.json();
|
return response.json<BuildInfo>();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(`CircleCI build info request failed (${error.message})`);
|
throw new Error(`CircleCI build info request failed (${error.message})`);
|
||||||
}
|
}
|
||||||
@ -77,7 +77,7 @@ export class CircleCiApi {
|
|||||||
const baseUrl = `${CIRCLE_CI_API_URL}/${this.githubOrg}/${this.githubRepo}/${buildNumber}`;
|
const baseUrl = `${CIRCLE_CI_API_URL}/${this.githubOrg}/${this.githubRepo}/${buildNumber}`;
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${baseUrl}/artifacts?${this.tokenParam}`);
|
const response = await fetch(`${baseUrl}/artifacts?${this.tokenParam}`);
|
||||||
const artifacts = await response.json() as ArtifactResponse;
|
const artifacts = await response.json<ArtifactResponse>();
|
||||||
const artifact = artifacts.find(item => item.path === artifactPath);
|
const artifact = artifacts.find(item => item.path === artifactPath);
|
||||||
if (!artifact) {
|
if (!artifact) {
|
||||||
throw new Error(`Missing artifact (${artifactPath}) for CircleCI build: ${buildNumber}`);
|
throw new Error(`Missing artifact (${artifactPath}) for CircleCI build: ${buildNumber}`);
|
||||||
|
@ -38,8 +38,7 @@ export class GithubApi {
|
|||||||
return this.request<T>('post', path, data);
|
return this.request<T>('post', path, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// In GitHub API paginated requests, page numbering is 1-based. (https://developer.github.com/v3/#pagination)
|
public getPaginated<T>(pathname: string, baseParams: RequestParams = {}, currentPage: number = 0): Promise<T[]> {
|
||||||
public getPaginated<T>(pathname: string, baseParams: RequestParams = {}, currentPage: number = 1): Promise<T[]> {
|
|
||||||
const perPage = 100;
|
const perPage = 100;
|
||||||
const params = {
|
const params = {
|
||||||
...baseParams,
|
...baseParams,
|
||||||
|
@ -74,6 +74,6 @@ export class GithubPullRequests {
|
|||||||
*/
|
*/
|
||||||
public fetchFiles(pr: number): Promise<FileInfo[]> {
|
public fetchFiles(pr: number): Promise<FileInfo[]> {
|
||||||
assert(pr > 0, `Invalid PR number: ${pr}`);
|
assert(pr > 0, `Invalid PR number: ${pr}`);
|
||||||
return this.api.getPaginated<FileInfo>(`/repos/${this.repoSlug}/pulls/${pr}/files`);
|
return this.api.get<FileInfo[]>(`/repos/${this.repoSlug}/pulls/${pr}/files`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
// We can't use `import...from` here, because of the following mess:
|
export const runTests = (specFiles: string[], helpers?: string[]) => {
|
||||||
// - GitHub project `jasmine/jasmine` is `jasmine-core` on npm and its typings `@types/jasmine`.
|
// We can't use `import` here, because of the following mess:
|
||||||
// - GitHub project `jasmine/jasmine-npm` is `jasmine` on npm and has no typings.
|
// - GitHub project `jasmine/jasmine` is `jasmine-core` on npm and its typings `@types/jasmine`.
|
||||||
//
|
// - GitHub project `jasmine/jasmine-npm` is `jasmine` on npm and has no typings.
|
||||||
// Using `import...from 'jasmine'` here, would import from `@types/jasmine` (which refers to the
|
//
|
||||||
// `jasmine-core` module and the `jasmine` module).
|
// Using `import...from 'jasmine'` here, would import from `@types/jasmine` (which refers to the
|
||||||
import Jasmine = require('jasmine');
|
// `jasmine-core` module and the `jasmine` module).
|
||||||
import 'source-map-support/register';
|
// tslint:disable-next-line: no-var-requires variable-name
|
||||||
|
const Jasmine = require('jasmine');
|
||||||
export const runTests = (specFiles: string[]) => {
|
|
||||||
const config = {
|
const config = {
|
||||||
|
helpers,
|
||||||
random: true,
|
random: true,
|
||||||
spec_files: specFiles,
|
spec_files: specFiles,
|
||||||
stopSpecOnExpectationFailure: true,
|
stopSpecOnExpectationFailure: true,
|
||||||
@ -16,7 +16,7 @@ export const runTests = (specFiles: string[]) => {
|
|||||||
|
|
||||||
process.on('unhandledRejection', (reason: any) => console.log('Unhandled rejection:', reason));
|
process.on('unhandledRejection', (reason: any) => console.log('Unhandled rejection:', reason));
|
||||||
|
|
||||||
const runner = new Jasmine({});
|
const runner = new Jasmine();
|
||||||
runner.loadConfig(config);
|
runner.loadConfig(config);
|
||||||
runner.onComplete((passed: boolean) => process.exit(passed ? 0 : 1));
|
runner.onComplete((passed: boolean) => process.exit(passed ? 0 : 1));
|
||||||
runner.execute();
|
runner.execute();
|
||||||
|
@ -74,25 +74,12 @@ export const getEnvVar = (name: string, isOptional = false): string => {
|
|||||||
return value || '';
|
return value || '';
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
export function createLogger(scope: string) {
|
||||||
* A basic logger implementation.
|
const padding = ' '.repeat(20 - scope.length);
|
||||||
* Delegates to `console`, but prepends each message with the current date and specified scope (i.e caller).
|
return {
|
||||||
*/
|
error: (...args: any[]) => console.error(`[${new Date()}]`, `${scope}:${padding}`, ...args),
|
||||||
export class Logger {
|
info: (...args: any[]) => console.info(`[${new Date()}]`, `${scope}:${padding}`, ...args),
|
||||||
private padding = ' '.repeat(20 - this.scope.length);
|
log: (...args: any[]) => console.log(`[${new Date()}]`, `${scope}:${padding}`, ...args),
|
||||||
|
warn: (...args: any[]) => console.warn(`[${new Date()}]`, `${scope}:${padding}`, ...args),
|
||||||
/**
|
};
|
||||||
* Create a new `Logger` instance for the specified `scope`.
|
|
||||||
* @param scope The logger's scope (added to all messages).
|
|
||||||
*/
|
|
||||||
constructor(private scope: string) {}
|
|
||||||
|
|
||||||
public error(...args: any[]) { this.callMethod('error', args); }
|
|
||||||
public info(...args: any[]) { this.callMethod('info', args); }
|
|
||||||
public log(...args: any[]) { this.callMethod('log', args); }
|
|
||||||
public warn(...args: any[]) { this.callMethod('warn', args); }
|
|
||||||
|
|
||||||
private callMethod(method: 'error' | 'info' | 'log' | 'warn', args: any[]) {
|
|
||||||
console[method](`[${new Date()}]`, `${this.scope}:${this.padding}`, ...args);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -5,14 +5,14 @@ import * as fs from 'fs';
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as shell from 'shelljs';
|
import * as shell from 'shelljs';
|
||||||
import {HIDDEN_DIR_PREFIX} from '../common/constants';
|
import {HIDDEN_DIR_PREFIX} from '../common/constants';
|
||||||
import {assertNotMissingOrEmpty, computeShortSha, Logger} from '../common/utils';
|
import {assertNotMissingOrEmpty, computeShortSha, createLogger} from '../common/utils';
|
||||||
import {ChangedPrVisibilityEvent, CreatedBuildEvent} from './build-events';
|
import {ChangedPrVisibilityEvent, CreatedBuildEvent} from './build-events';
|
||||||
import {PreviewServerError} from './preview-error';
|
import {PreviewServerError} from './preview-error';
|
||||||
|
|
||||||
// Classes
|
// Classes
|
||||||
export class BuildCreator extends EventEmitter {
|
export class BuildCreator extends EventEmitter {
|
||||||
|
|
||||||
private logger = new Logger('BuildCreator');
|
private logger = createLogger('BuildCreator');
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
constructor(protected buildsDir: string) {
|
constructor(protected buildsDir: string) {
|
||||||
@ -110,7 +110,8 @@ export class BuildCreator extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
shell.chmod('-R', 'a-w', outputDir);
|
// Undocumented signature (see https://github.com/shelljs/shelljs/pull/663).
|
||||||
|
(shell as any).chmod('-R', 'a-w', outputDir);
|
||||||
shell.rm('-f', inputFile);
|
shell.rm('-f', inputFile);
|
||||||
resolve();
|
resolve();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -4,7 +4,7 @@ import {dirname} from 'path';
|
|||||||
import {mkdir} from 'shelljs';
|
import {mkdir} from 'shelljs';
|
||||||
import {promisify} from 'util';
|
import {promisify} from 'util';
|
||||||
import {CircleCiApi} from '../common/circle-ci-api';
|
import {CircleCiApi} from '../common/circle-ci-api';
|
||||||
import {assert, assertNotMissingOrEmpty, computeArtifactDownloadPath, Logger} from '../common/utils';
|
import {assert, assertNotMissingOrEmpty, computeArtifactDownloadPath, createLogger} from '../common/utils';
|
||||||
import {PreviewServerError} from './preview-error';
|
import {PreviewServerError} from './preview-error';
|
||||||
|
|
||||||
export interface GithubInfo {
|
export interface GithubInfo {
|
||||||
@ -19,7 +19,7 @@ export interface GithubInfo {
|
|||||||
* A helper that can get information about builds and download build artifacts.
|
* A helper that can get information about builds and download build artifacts.
|
||||||
*/
|
*/
|
||||||
export class BuildRetriever {
|
export class BuildRetriever {
|
||||||
private logger = new Logger('BuildRetriever');
|
private logger = createLogger('BuildRetriever');
|
||||||
constructor(private api: CircleCiApi, private downloadSizeLimit: number, private downloadDir: string) {
|
constructor(private api: CircleCiApi, private downloadSizeLimit: number, private downloadDir: string) {
|
||||||
assert(downloadSizeLimit > 0, 'Invalid parameter "downloadSizeLimit" should be a number greater than 0.');
|
assert(downloadSizeLimit > 0, 'Invalid parameter "downloadSizeLimit" should be a number greater than 0.');
|
||||||
assertNotMissingOrEmpty('downloadDir', downloadDir);
|
assertNotMissingOrEmpty('downloadDir', downloadDir);
|
||||||
@ -34,7 +34,7 @@ export class BuildRetriever {
|
|||||||
const buildInfo = await this.api.getBuildInfo(buildNum);
|
const buildInfo = await this.api.getBuildInfo(buildNum);
|
||||||
const githubInfo: GithubInfo = {
|
const githubInfo: GithubInfo = {
|
||||||
org: buildInfo.username,
|
org: buildInfo.username,
|
||||||
pr: getPrFromBranch(buildInfo.branch),
|
pr: getPrfromBranch(buildInfo.branch),
|
||||||
repo: buildInfo.reponame,
|
repo: buildInfo.reponame,
|
||||||
sha: buildInfo.vcs_revision,
|
sha: buildInfo.vcs_revision,
|
||||||
success: !buildInfo.failed,
|
success: !buildInfo.failed,
|
||||||
@ -73,7 +73,7 @@ export class BuildRetriever {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPrFromBranch(branch: string): number {
|
function getPrfromBranch(branch: string): number {
|
||||||
// CircleCI only exposes PR numbers via the `branch` field :-(
|
// CircleCI only exposes PR numbers via the `branch` field :-(
|
||||||
const match = /^pull\/(\d+)$/.exec(branch);
|
const match = /^pull\/(\d+)$/.exec(branch);
|
||||||
if (!match) {
|
if (!match) {
|
||||||
|
@ -2,12 +2,11 @@
|
|||||||
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 {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, createLogger} 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';
|
||||||
@ -32,7 +31,7 @@ export interface PreviewServerConfig {
|
|||||||
trustedPrLabel: string;
|
trustedPrLabel: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const logger = new Logger('PreviewServer');
|
const logger = createLogger('PreviewServer');
|
||||||
|
|
||||||
// Classes
|
// Classes
|
||||||
export class PreviewServerFactory {
|
export class PreviewServerFactory {
|
||||||
@ -53,7 +52,7 @@ export class PreviewServerFactory {
|
|||||||
const httpServer = http.createServer(middleware as any);
|
const httpServer = http.createServer(middleware as any);
|
||||||
|
|
||||||
httpServer.on('listening', () => {
|
httpServer.on('listening', () => {
|
||||||
const info = httpServer.address() as AddressInfo;
|
const info = httpServer.address();
|
||||||
logger.info(`Up and running (and listening on ${info.address}:${info.port})...`);
|
logger.info(`Up and running (and listening on ${info.address}:${info.port})...`);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -64,36 +63,10 @@ export class PreviewServerFactory {
|
|||||||
buildCreator: BuildCreator, cfg: PreviewServerConfig): express.Express {
|
buildCreator: BuildCreator, cfg: PreviewServerConfig): express.Express {
|
||||||
const middleware = express();
|
const middleware = express();
|
||||||
const jsonParser = bodyParser.json();
|
const jsonParser = bodyParser.json();
|
||||||
const significantFilesRe = new RegExp(cfg.significantFilesPattern);
|
|
||||||
|
|
||||||
// RESPOND TO IS-ALIVE PING
|
// RESPOND TO IS-ALIVE PING
|
||||||
middleware.get(/^\/health-check\/?$/, (_req, res) => res.sendStatus(200));
|
middleware.get(/^\/health-check\/?$/, (_req, res) => res.sendStatus(200));
|
||||||
|
|
||||||
// RESPOND TO CAN-HAVE-PUBLIC-PREVIEW CHECK
|
|
||||||
const canHavePublicPreviewRe = /^\/can-have-public-preview\/(\d+)\/?$/;
|
|
||||||
middleware.get(canHavePublicPreviewRe, async (req, res) => {
|
|
||||||
try {
|
|
||||||
const pr = +canHavePublicPreviewRe.exec(req.url)![1];
|
|
||||||
|
|
||||||
if (!await buildVerifier.getSignificantFilesChanged(pr, significantFilesRe)) {
|
|
||||||
// Cannot have preview: PR did not touch relevant files: `aio/` or `packages/` (except for spec files).
|
|
||||||
res.send({canHavePublicPreview: false, reason: 'No significant files touched.'});
|
|
||||||
logger.log(`PR:${pr} - Cannot have a public preview, because it did not touch any significant files.`);
|
|
||||||
} else if (!await buildVerifier.getPrIsTrusted(pr)) {
|
|
||||||
// Cannot have preview: PR not automatically verifiable as "trusted".
|
|
||||||
res.send({canHavePublicPreview: false, reason: 'Not automatically verifiable as "trusted".'});
|
|
||||||
logger.log(`PR:${pr} - Cannot have a public preview, because not automatically verifiable as "trusted".`);
|
|
||||||
} else {
|
|
||||||
// Can have preview.
|
|
||||||
res.send({canHavePublicPreview: true, reason: null});
|
|
||||||
logger.log(`PR:${pr} - Can have a public preview.`);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
logger.error('Previewability check error', err);
|
|
||||||
respondWithError(res, err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// CIRCLE_CI BUILD COMPLETE WEBHOOK
|
// CIRCLE_CI BUILD COMPLETE WEBHOOK
|
||||||
middleware.post(/^\/circle-build\/?$/, jsonParser, async (req, res) => {
|
middleware.post(/^\/circle-build\/?$/, jsonParser, async (req, res) => {
|
||||||
try {
|
try {
|
||||||
@ -134,7 +107,7 @@ export class PreviewServerFactory {
|
|||||||
`Invalid webhook: expected "githubRepo" property to equal "${cfg.githubRepo}" but got "${repo}".`);
|
`Invalid webhook: expected "githubRepo" property to equal "${cfg.githubRepo}" but got "${repo}".`);
|
||||||
|
|
||||||
// Do not deploy unless this PR has touched relevant files: `aio/` or `packages/` (except for spec files)
|
// Do not deploy unless this PR has touched relevant files: `aio/` or `packages/` (except for spec files)
|
||||||
if (!await buildVerifier.getSignificantFilesChanged(pr, significantFilesRe)) {
|
if (!await buildVerifier.getSignificantFilesChanged(pr, new RegExp(cfg.significantFilesPattern))) {
|
||||||
res.sendStatus(204);
|
res.sendStatus(204);
|
||||||
logger.log(`PR:${pr}, Build:${buildNum} - ` +
|
logger.log(`PR:${pr}, Build:${buildNum} - ` +
|
||||||
`Skipping preview processing because this PR did not touch any significant files.`);
|
`Skipping preview processing because this PR did not touch any significant files.`);
|
||||||
@ -144,10 +117,7 @@ export class PreviewServerFactory {
|
|||||||
const artifactPath = await buildRetriever.downloadBuildArtifact(buildNum, pr, sha, cfg.buildArtifactPath);
|
const artifactPath = await buildRetriever.downloadBuildArtifact(buildNum, pr, sha, cfg.buildArtifactPath);
|
||||||
const isPublic = await buildVerifier.getPrIsTrusted(pr);
|
const isPublic = await buildVerifier.getPrIsTrusted(pr);
|
||||||
await buildCreator.create(pr, sha, artifactPath, isPublic);
|
await buildCreator.create(pr, sha, artifactPath, isPublic);
|
||||||
|
|
||||||
res.sendStatus(isPublic ? 201 : 202);
|
res.sendStatus(isPublic ? 201 : 202);
|
||||||
logger.log(`PR:${pr}, SHA:${computeShortSha(sha)}, Build:${buildNum} - ` +
|
|
||||||
`Successfully created ${isPublic ? 'public' : 'non-public'} preview.`);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error('CircleCI webhook error', err);
|
logger.error('CircleCI webhook error', err);
|
||||||
respondWithError(res, err);
|
respondWithError(res, err);
|
||||||
|
@ -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';
|
||||||
@ -10,7 +11,7 @@ import {
|
|||||||
AIO_NGINX_PORT_HTTPS,
|
AIO_NGINX_PORT_HTTPS,
|
||||||
AIO_WWW_USER,
|
AIO_WWW_USER,
|
||||||
} from '../common/env-variables';
|
} from '../common/env-variables';
|
||||||
import {computeShortSha, Logger} from '../common/utils';
|
import {computeShortSha, createLogger} from '../common/utils';
|
||||||
|
|
||||||
// Interfaces - Types
|
// Interfaces - Types
|
||||||
export interface CmdResult { success: boolean; err: Error | null; stdout: string; stderr: string; }
|
export interface CmdResult { success: boolean; err: Error | null; stdout: string; stderr: string; }
|
||||||
@ -30,7 +31,7 @@ class Helper {
|
|||||||
https: AIO_NGINX_PORT_HTTPS,
|
https: AIO_NGINX_PORT_HTTPS,
|
||||||
};
|
};
|
||||||
|
|
||||||
private logger = new Logger('TestHelper');
|
private logger = createLogger('TestHelper');
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -104,7 +105,18 @@ class Helper {
|
|||||||
Object.keys(this.portPerScheme).forEach(scheme => suiteFactory(scheme, this.portPerScheme[scheme]));
|
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 = /^/): 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);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -168,42 +180,26 @@ class Helper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DefaultCurlOptions {
|
|
||||||
defaultMethod?: CurlOptions['method'];
|
|
||||||
defaultOptions?: CurlOptions['options'];
|
|
||||||
defaultHeaders?: CurlOptions['headers'];
|
|
||||||
defaultData?: CurlOptions['data'];
|
|
||||||
defaultExtraPath?: CurlOptions['extraPath'];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface CurlOptions {
|
interface CurlOptions {
|
||||||
method?: string;
|
method?: string;
|
||||||
options?: string;
|
options?: string;
|
||||||
headers?: string[];
|
|
||||||
data?: any;
|
data?: any;
|
||||||
url?: string;
|
url?: string;
|
||||||
extraPath?: string;
|
extraPath?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function makeCurl(baseUrl: string, {
|
export function makeCurl(baseUrl: string) {
|
||||||
defaultMethod = 'POST',
|
|
||||||
defaultOptions = '',
|
|
||||||
defaultHeaders = ['Content-Type: application/json'],
|
|
||||||
defaultData = {},
|
|
||||||
defaultExtraPath = '',
|
|
||||||
}: DefaultCurlOptions = {}) {
|
|
||||||
return function curl({
|
return function curl({
|
||||||
method = defaultMethod,
|
method = 'POST',
|
||||||
options = defaultOptions,
|
options = '',
|
||||||
headers = defaultHeaders,
|
data = {},
|
||||||
data = defaultData,
|
|
||||||
url = baseUrl,
|
url = baseUrl,
|
||||||
extraPath = defaultExtraPath,
|
extraPath = '',
|
||||||
}: CurlOptions) {
|
}: CurlOptions) {
|
||||||
const dataString = data ? JSON.stringify(data) : '';
|
const dataString = data ? JSON.stringify(data) : '';
|
||||||
const cmd = `curl -iLX ${method} ` +
|
const cmd = `curl -iLX ${method} ` +
|
||||||
`${options} ` +
|
`${options} ` +
|
||||||
headers.map(header => `--header "${header}" `).join('') +
|
`--header "Content-Type: application/json" ` +
|
||||||
`--data '${dataString}' ` +
|
`--data '${dataString}' ` +
|
||||||
`${url}${extraPath}`;
|
`${url}${extraPath}`;
|
||||||
return helper.runCmd(cmd);
|
return helper.runCmd(cmd);
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
import * as nock from 'nock';
|
import * as nock from 'nock';
|
||||||
import * as tar from 'tar-stream';
|
import * as tar from 'tar-stream';
|
||||||
import {gzipSync} from 'zlib';
|
import {gzipSync} from 'zlib';
|
||||||
import {getEnvVar, Logger} from '../common/utils';
|
import {createLogger, getEnvVar} from '../common/utils';
|
||||||
import {BuildNums, PrNums, SHA} from './constants';
|
import {BuildNums, PrNums, SHA} from './constants';
|
||||||
|
|
||||||
// We are using the `nock` library to fake responses from REST requests, when testing.
|
// We are using the `nock` library to fake responses from REST requests, when testing.
|
||||||
@ -14,7 +14,7 @@ import {BuildNums, PrNums, SHA} from './constants';
|
|||||||
// below and return a suitable response. This is quite complicated to setup since the
|
// below and return a suitable response. This is quite complicated to setup since the
|
||||||
// response from, say, CircleCI will affect what request is made to, say, Github.
|
// response from, say, CircleCI will affect what request is made to, say, Github.
|
||||||
|
|
||||||
const logger = new Logger('mock-external-apis');
|
const logger = createLogger('NOCK');
|
||||||
|
|
||||||
const log = (...args: any[]) => {
|
const log = (...args: any[]) => {
|
||||||
// Filter out non-matching URL checks
|
// Filter out non-matching URL checks
|
||||||
@ -76,7 +76,7 @@ const GITHUB_PULLS_URL = `/repos/${AIO_GITHUB_ORGANIZATION}/${AIO_GITHUB_REPO}/p
|
|||||||
const GITHUB_TEAMS_URL = `/orgs/${AIO_GITHUB_ORGANIZATION}/teams`;
|
const GITHUB_TEAMS_URL = `/orgs/${AIO_GITHUB_ORGANIZATION}/teams`;
|
||||||
|
|
||||||
const getIssueUrl = (prNum: number) => `${GITHUB_ISSUES_URL}/${prNum}`;
|
const getIssueUrl = (prNum: number) => `${GITHUB_ISSUES_URL}/${prNum}`;
|
||||||
const getFilesUrl = (prNum: number, pageNum = 1) => `${GITHUB_PULLS_URL}/${prNum}/files?page=${pageNum}&per_page=100`;
|
const getFilesUrl = (prNum: number) => `${GITHUB_PULLS_URL}/${prNum}/files`;
|
||||||
const getCommentUrl = (prNum: number) => `${getIssueUrl(prNum)}/comments`;
|
const getCommentUrl = (prNum: number) => `${getIssueUrl(prNum)}/comments`;
|
||||||
const getTeamMembershipUrl = (teamId: number, username: string) => `/teams/${teamId}/memberships/${username}`;
|
const getTeamMembershipUrl = (teamId: number, username: string) => `/teams/${teamId}/memberships/${username}`;
|
||||||
|
|
||||||
@ -97,7 +97,7 @@ const githubApi = nock(GITHUB_API_HOST).log(log).persist().matchHeader('Authoriz
|
|||||||
//////////////////////////////
|
//////////////////////////////
|
||||||
|
|
||||||
// GENERAL responses
|
// GENERAL responses
|
||||||
githubApi.get(GITHUB_TEAMS_URL + '?page=1&per_page=100').reply(200, TEST_TEAM_INFO);
|
githubApi.get(GITHUB_TEAMS_URL + '?page=0&per_page=100').reply(200, TEST_TEAM_INFO);
|
||||||
githubApi.post(getCommentUrl(PrNums.TRUST_CHECK_ACTIVE_TRUSTED_USER)).reply(200);
|
githubApi.post(getCommentUrl(PrNums.TRUST_CHECK_ACTIVE_TRUSTED_USER)).reply(200);
|
||||||
|
|
||||||
// BUILD_INFO errors
|
// BUILD_INFO errors
|
||||||
|
@ -3,7 +3,6 @@ import * as path from 'path';
|
|||||||
import {rm} from 'shelljs';
|
import {rm} from 'shelljs';
|
||||||
import {AIO_BUILDS_DIR, AIO_NGINX_HOSTNAME, AIO_NGINX_PORT_HTTP, AIO_NGINX_PORT_HTTPS} from '../common/env-variables';
|
import {AIO_BUILDS_DIR, AIO_NGINX_HOSTNAME, AIO_NGINX_PORT_HTTP, AIO_NGINX_PORT_HTTPS} from '../common/env-variables';
|
||||||
import {computeShortSha} from '../common/utils';
|
import {computeShortSha} from '../common/utils';
|
||||||
import {PrNums} from './constants';
|
|
||||||
import {helper as h} from './helper';
|
import {helper as h} from './helper';
|
||||||
import {customMatchers} from './jasmine-custom-matchers';
|
import {customMatchers} from './jasmine-custom-matchers';
|
||||||
|
|
||||||
@ -253,52 +252,16 @@ describe(`nginx`, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
describe(`${host}/can-have-public-preview`, () => {
|
|
||||||
const baseUrl = `${scheme}://${host}/can-have-public-preview`;
|
|
||||||
|
|
||||||
|
|
||||||
it('should disallow non-GET requests', async () => {
|
|
||||||
await Promise.all([
|
|
||||||
h.runCmd(`curl -iLX POST ${baseUrl}/42`).then(h.verifyResponse(405)),
|
|
||||||
h.runCmd(`curl -iLX PUT ${baseUrl}/42`).then(h.verifyResponse(405)),
|
|
||||||
h.runCmd(`curl -iLX PATCH ${baseUrl}/42`).then(h.verifyResponse(405)),
|
|
||||||
h.runCmd(`curl -iLX DELETE ${baseUrl}/42`).then(h.verifyResponse(405)),
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should pass requests through to the preview server', async () => {
|
|
||||||
await h.runCmd(`curl -iLX GET ${baseUrl}/${PrNums.CHANGED_FILES_ERROR}`).
|
|
||||||
then(h.verifyResponse(500, /CHANGED_FILES_ERROR/));
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should respond with 404 for unknown paths', async () => {
|
|
||||||
const cmdPrefix = `curl -iLX GET ${baseUrl}`;
|
|
||||||
|
|
||||||
await Promise.all([
|
|
||||||
h.runCmd(`${cmdPrefix}/foo/42`).then(h.verifyResponse(404)),
|
|
||||||
h.runCmd(`${cmdPrefix}-foo/42`).then(h.verifyResponse(404)),
|
|
||||||
h.runCmd(`${cmdPrefix}nfoo/42`).then(h.verifyResponse(404)),
|
|
||||||
h.runCmd(`${cmdPrefix}/42/foo`).then(h.verifyResponse(404)),
|
|
||||||
h.runCmd(`${cmdPrefix}/f00`).then(h.verifyResponse(404)),
|
|
||||||
h.runCmd(`${cmdPrefix}/`).then(h.verifyResponse(404)),
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
describe(`${host}/circle-build`, () => {
|
describe(`${host}/circle-build`, () => {
|
||||||
|
|
||||||
it('should disallow non-POST requests', done => {
|
it('should disallow non-POST requests', done => {
|
||||||
const url = `${scheme}://${host}/circle-build`;
|
const url = `${scheme}://${host}/circle-build`;
|
||||||
|
|
||||||
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);
|
]).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -324,7 +287,6 @@ describe(`nginx`, () => {
|
|||||||
h.runCmd(`${cmdPrefix}/circle-build/42`).then(h.verifyResponse(404)),
|
h.runCmd(`${cmdPrefix}/circle-build/42`).then(h.verifyResponse(404)),
|
||||||
]).then(done);
|
]).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -334,10 +296,10 @@ describe(`nginx`, () => {
|
|||||||
|
|
||||||
it('should disallow non-POST requests', done => {
|
it('should disallow non-POST requests', done => {
|
||||||
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);
|
]).then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -18,92 +18,6 @@ describe('preview-server', () => {
|
|||||||
afterEach(() => h.cleanUp());
|
afterEach(() => h.cleanUp());
|
||||||
|
|
||||||
|
|
||||||
describe(`${host}/can-have-public-preview`, () => {
|
|
||||||
const curl = makeCurl(`${host}/can-have-public-preview`, {
|
|
||||||
defaultData: null,
|
|
||||||
defaultExtraPath: `/${PrNums.TRUST_CHECK_ACTIVE_TRUSTED_USER}`,
|
|
||||||
defaultHeaders: [],
|
|
||||||
defaultMethod: 'GET',
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should disallow non-GET requests', async () => {
|
|
||||||
const bodyRegex = /^Unknown resource in request/;
|
|
||||||
|
|
||||||
await Promise.all([
|
|
||||||
curl({method: 'POST'}).then(h.verifyResponse(404, bodyRegex)),
|
|
||||||
curl({method: 'PUT'}).then(h.verifyResponse(404, bodyRegex)),
|
|
||||||
curl({method: 'PATCH'}).then(h.verifyResponse(404, bodyRegex)),
|
|
||||||
curl({method: 'DELETE'}).then(h.verifyResponse(404, bodyRegex)),
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should respond with 404 for unknown paths', async () => {
|
|
||||||
const bodyRegex = /^Unknown resource in request/;
|
|
||||||
|
|
||||||
await Promise.all([
|
|
||||||
curl({extraPath: `/foo/${PrNums.TRUST_CHECK_ACTIVE_TRUSTED_USER}`}).then(h.verifyResponse(404, bodyRegex)),
|
|
||||||
curl({extraPath: `-foo/${PrNums.TRUST_CHECK_ACTIVE_TRUSTED_USER}`}).then(h.verifyResponse(404, bodyRegex)),
|
|
||||||
curl({extraPath: `nfoo/${PrNums.TRUST_CHECK_ACTIVE_TRUSTED_USER}`}).then(h.verifyResponse(404, bodyRegex)),
|
|
||||||
curl({extraPath: `/${PrNums.TRUST_CHECK_ACTIVE_TRUSTED_USER}/foo`}).then(h.verifyResponse(404, bodyRegex)),
|
|
||||||
curl({extraPath: '/f00'}).then(h.verifyResponse(404, bodyRegex)),
|
|
||||||
curl({extraPath: '/'}).then(h.verifyResponse(404, bodyRegex)),
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should respond with 500 if checking for significant file changes fails', async () => {
|
|
||||||
await Promise.all([
|
|
||||||
curl({extraPath: `/${PrNums.CHANGED_FILES_404}`}).then(h.verifyResponse(500, /CHANGED_FILES_404/)),
|
|
||||||
curl({extraPath: `/${PrNums.CHANGED_FILES_ERROR}`}).then(h.verifyResponse(500, /CHANGED_FILES_ERROR/)),
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should respond with 200 (false) if no significant files were touched', async () => {
|
|
||||||
const expectedResponse = JSON.stringify({
|
|
||||||
canHavePublicPreview: false,
|
|
||||||
reason: 'No significant files touched.',
|
|
||||||
});
|
|
||||||
|
|
||||||
await curl({extraPath: `/${PrNums.CHANGED_FILES_NONE}`}).then(h.verifyResponse(200, expectedResponse));
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should respond with 500 if checking "trusted" status fails', async () => {
|
|
||||||
await curl({extraPath: `/${PrNums.TRUST_CHECK_ERROR}`}).then(h.verifyResponse(500, 'TRUST_CHECK_ERROR'));
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should respond with 200 (false) if the PR is not automatically verifiable as "trusted"', async () => {
|
|
||||||
const expectedResponse = JSON.stringify({
|
|
||||||
canHavePublicPreview: false,
|
|
||||||
reason: 'Not automatically verifiable as \\"trusted\\".',
|
|
||||||
});
|
|
||||||
|
|
||||||
await Promise.all([
|
|
||||||
curl({extraPath: `/${PrNums.TRUST_CHECK_INACTIVE_TRUSTED_USER}`}).then(h.verifyResponse(200, expectedResponse)),
|
|
||||||
curl({extraPath: `/${PrNums.TRUST_CHECK_UNTRUSTED}`}).then(h.verifyResponse(200, expectedResponse)),
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should respond with 200 (true) if the PR can have a public preview', async () => {
|
|
||||||
const expectedResponse = JSON.stringify({
|
|
||||||
canHavePublicPreview: true,
|
|
||||||
reason: null,
|
|
||||||
});
|
|
||||||
|
|
||||||
await Promise.all([
|
|
||||||
curl({extraPath: `/${PrNums.TRUST_CHECK_ACTIVE_TRUSTED_USER}`}).then(h.verifyResponse(200, expectedResponse)),
|
|
||||||
curl({extraPath: `/${PrNums.TRUST_CHECK_TRUSTED_LABEL}`}).then(h.verifyResponse(200, expectedResponse)),
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
describe(`${host}/circle-build`, () => {
|
describe(`${host}/circle-build`, () => {
|
||||||
|
|
||||||
const curl = makeCurl(`${host}/circle-build`);
|
const curl = makeCurl(`${host}/circle-build`);
|
||||||
|
@ -7,49 +7,43 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prebuild": "yarn clean-dist",
|
"prebuild": "yarn clean-dist",
|
||||||
"build": "yarn ~~build",
|
"build": "tsc",
|
||||||
"prebuild-watch": "yarn prebuild",
|
"build-watch": "yarn tsc --watch",
|
||||||
"build-watch": "yarn ~~build-watch",
|
|
||||||
"clean-dist": "node --eval \"require('shelljs').rm('-rf', 'dist')\"",
|
"clean-dist": "node --eval \"require('shelljs').rm('-rf', 'dist')\"",
|
||||||
"predev": "yarn build || true",
|
"dev": "concurrently --kill-others --raw --success first \"yarn build-watch\" \"yarn test-watch\"",
|
||||||
"dev": "run-p ~~build-watch ~~test-watch",
|
|
||||||
"lint": "tslint --project tsconfig.json",
|
"lint": "tslint --project tsconfig.json",
|
||||||
"pretest": "yarn build",
|
|
||||||
"test": "yarn ~~test-only",
|
|
||||||
"pretest-watch": "yarn pretest",
|
|
||||||
"test-watch": "yarn ~~test-watch",
|
|
||||||
"~~build": "tsc",
|
|
||||||
"~~build-watch": "yarn ~~build --watch",
|
|
||||||
"pre~~test-only": "yarn lint",
|
"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"
|
"pretest": "yarn build",
|
||||||
|
"test": "yarn ~~test-only",
|
||||||
|
"pretest-watch": "yarn build",
|
||||||
|
"test-watch": "nodemon --exec \"yarn ~~test-only\" --watch dist"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"body-parser": "^1.18.3",
|
"body-parser": "^1.18.2",
|
||||||
"delete-empty": "^2.0.0",
|
"delete-empty": "^2.0.0",
|
||||||
"express": "^4.16.3",
|
"express": "^4.15.4",
|
||||||
"jasmine": "^3.2.0",
|
"jasmine": "^2.8.0",
|
||||||
"nock": "^9.6.1",
|
"nock": "^9.2.5",
|
||||||
"node-fetch": "^2.2.0",
|
"node-fetch": "^2.1.2",
|
||||||
"shelljs": "^0.8.2",
|
"shelljs": "^0.8.1",
|
||||||
"source-map-support": "^0.5.9",
|
"tar-stream": "^1.6.0",
|
||||||
"tar-stream": "^1.6.1",
|
"tslib": "^1.7.1"
|
||||||
"tslib": "^1.9.3"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/body-parser": "^1.17.0",
|
"@types/body-parser": "^1.16.5",
|
||||||
"@types/express": "^4.16.0",
|
"@types/express": "^4.0.37",
|
||||||
"@types/jasmine": "^2.8.8",
|
"@types/jasmine": "^2.6.0",
|
||||||
"@types/nock": "^9.3.0",
|
"@types/nock": "^9.1.3",
|
||||||
"@types/node": "^10.9.2",
|
"@types/node": "^8.0.30",
|
||||||
"@types/node-fetch": "^2.1.2",
|
"@types/node-fetch": "^1.6.8",
|
||||||
"@types/shelljs": "^0.8.0",
|
"@types/shelljs": "^0.8.0",
|
||||||
"@types/supertest": "^2.0.5",
|
"@types/supertest": "^2.0.3",
|
||||||
"nodemon": "^1.18.3",
|
"concurrently": "^3.5.0",
|
||||||
"npm-run-all": "^4.1.5",
|
"nodemon": "^1.12.1",
|
||||||
"supertest": "^3.1.0",
|
"supertest": "^3.0.0",
|
||||||
"tslint": "^5.11.0",
|
"tslint": "^5.7.0",
|
||||||
"tslint-jasmine-noSkipOrFocus": "^1.0.9",
|
"tslint-jasmine-noSkipOrFocus": "^1.0.8",
|
||||||
"typescript": "^3.0.1"
|
"typescript": "^2.5.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,28 +5,25 @@ import * as shell from 'shelljs';
|
|||||||
import {BuildCleaner} from '../../lib/clean-up/build-cleaner';
|
import {BuildCleaner} from '../../lib/clean-up/build-cleaner';
|
||||||
import {HIDDEN_DIR_PREFIX} from '../../lib/common/constants';
|
import {HIDDEN_DIR_PREFIX} from '../../lib/common/constants';
|
||||||
import {GithubPullRequests} from '../../lib/common/github-pull-requests';
|
import {GithubPullRequests} from '../../lib/common/github-pull-requests';
|
||||||
import {Logger} from '../../lib/common/utils';
|
|
||||||
|
|
||||||
const EXISTING_BUILDS = [10, 20, 30, 40];
|
const EXISTING_BUILDS = [10, 20, 30, 40];
|
||||||
const EXISTING_DOWNLOADS = [
|
const EXISTING_DOWNLOADS = [
|
||||||
'10-ABCDEF0-build.zip',
|
'downloads/10-ABCDEF0-build.zip',
|
||||||
'10-1234567-build.zip',
|
'downloads/10-1234567-build.zip',
|
||||||
'20-ABCDEF0-build.zip',
|
'downloads/20-ABCDEF0-build.zip',
|
||||||
'20-1234567-build.zip',
|
'downloads/20-1234567-build.zip',
|
||||||
];
|
];
|
||||||
const OPEN_PRS = [10, 40];
|
const OPEN_PRS = [10, 40];
|
||||||
const ANY_DATE = jasmine.any(String);
|
const ANY_DATE = jasmine.any(String);
|
||||||
|
|
||||||
// Tests
|
// Tests
|
||||||
describe('BuildCleaner', () => {
|
describe('BuildCleaner', () => {
|
||||||
let loggerErrorSpy: jasmine.Spy;
|
|
||||||
let loggerLogSpy: jasmine.Spy;
|
|
||||||
let cleaner: BuildCleaner;
|
let cleaner: BuildCleaner;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
loggerErrorSpy = spyOn(Logger.prototype, 'error');
|
spyOn(console, 'error');
|
||||||
loggerLogSpy = spyOn(Logger.prototype, 'log');
|
spyOn(console, 'log');
|
||||||
cleaner = new BuildCleaner('/foo/bar', 'baz', 'qux', '12345', '/downloads', 'build.zip');
|
cleaner = new BuildCleaner('/foo/bar', 'baz', 'qux', '12345', 'downloads', 'build.zip');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('constructor()', () => {
|
describe('constructor()', () => {
|
||||||
@ -54,13 +51,11 @@ describe('BuildCleaner', () => {
|
|||||||
toThrowError('Missing or empty required parameter \'githubToken\'!');
|
toThrowError('Missing or empty required parameter \'githubToken\'!');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should throw if \'downloadsDir\' is empty', () => {
|
it('should throw if \'downloadsDir\' is empty', () => {
|
||||||
expect(() => new BuildCleaner('/foo/bar', 'baz', 'qux', '12345', '', 'build.zip')).
|
expect(() => new BuildCleaner('/foo/bar', 'baz', 'qux', '12345', '', 'build.zip')).
|
||||||
toThrowError('Missing or empty required parameter \'downloadsDir\'!');
|
toThrowError('Missing or empty required parameter \'downloadsDir\'!');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should throw if \'artifactPath\' is empty', () => {
|
it('should throw if \'artifactPath\' is empty', () => {
|
||||||
expect(() => new BuildCleaner('/foo/bar', 'baz', 'qux', '12345', 'downloads', '')).
|
expect(() => new BuildCleaner('/foo/bar', 'baz', 'qux', '12345', 'downloads', '')).
|
||||||
toThrowError('Missing or empty required parameter \'artifactPath\'!');
|
toThrowError('Missing or empty required parameter \'artifactPath\'!');
|
||||||
@ -90,12 +85,9 @@ describe('BuildCleaner', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should return a promise', async () => {
|
it('should return a promise', () => {
|
||||||
const promise = cleaner.cleanUp();
|
const promise = cleaner.cleanUp();
|
||||||
expect(promise).toEqual(jasmine.any(Promise));
|
expect(promise).toEqual(jasmine.any(Promise));
|
||||||
|
|
||||||
// Do not complete the test and release the spies synchronously, to avoid running the actual implementations.
|
|
||||||
await promise;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -168,7 +160,6 @@ describe('BuildCleaner', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should reject if \'removeUnnecessaryDownloads()\' rejects', async () => {
|
it('should reject if \'removeUnnecessaryDownloads()\' rejects', async () => {
|
||||||
try {
|
try {
|
||||||
cleanerRemoveUnnecessaryDownloadsSpy.and.callFake(() => Promise.reject('Test'));
|
cleanerRemoveUnnecessaryDownloadsSpy.and.callFake(() => Promise.reject('Test'));
|
||||||
@ -177,7 +168,6 @@ describe('BuildCleaner', () => {
|
|||||||
expect(err).toBe('Test');
|
expect(err).toBe('Test');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -287,14 +277,11 @@ describe('BuildCleaner', () => {
|
|||||||
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}]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should log the number of open PRs', () => {
|
it('should log the number of open PRs', () => {
|
||||||
promise.then(prNumbers => {
|
promise.then(prNumbers => {
|
||||||
expect(loggerLogSpy).toHaveBeenCalledWith(
|
expect(console.log).toHaveBeenCalledWith(ANY_DATE, 'BuildCleaner: ', `Open pull requests: ${prNumbers}`);
|
||||||
ANY_DATE, 'BuildCleaner: ', `Open pull requests: ${prNumbers}`);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -314,9 +301,9 @@ describe('BuildCleaner', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should get the contents of the downloads directory', () => {
|
it('should get the contents of the builds directory', () => {
|
||||||
expect(fsReaddirSpy).toHaveBeenCalled();
|
expect(fsReaddirSpy).toHaveBeenCalled();
|
||||||
expect(fsReaddirSpy.calls.argsFor(0)[0]).toBe('/downloads');
|
expect(fsReaddirSpy.calls.argsFor(0)[0]).toBe('downloads');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -330,7 +317,7 @@ describe('BuildCleaner', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve with the returned file names', done => {
|
it('should resolve with the returned files (as numbers)', done => {
|
||||||
promise.then(result => {
|
promise.then(result => {
|
||||||
expect(result).toEqual(EXISTING_DOWNLOADS);
|
expect(result).toEqual(EXISTING_DOWNLOADS);
|
||||||
done();
|
done();
|
||||||
@ -396,7 +383,8 @@ describe('BuildCleaner', () => {
|
|||||||
|
|
||||||
cleaner.removeDir('/foo/bar');
|
cleaner.removeDir('/foo/bar');
|
||||||
|
|
||||||
expect(loggerErrorSpy).toHaveBeenCalledWith('ERROR: Unable to remove \'/foo/bar\' due to:', 'Test');
|
expect(console.error).toHaveBeenCalledWith(
|
||||||
|
jasmine.any(String), 'BuildCleaner: ', 'ERROR: Unable to remove \'/foo/bar\' due to:', 'Test');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -413,8 +401,8 @@ describe('BuildCleaner', () => {
|
|||||||
it('should log the number of existing builds and builds to be removed', () => {
|
it('should log the number of existing builds and builds to be removed', () => {
|
||||||
cleaner.removeUnnecessaryBuilds([1, 2, 3], [3, 4, 5, 6]);
|
cleaner.removeUnnecessaryBuilds([1, 2, 3], [3, 4, 5, 6]);
|
||||||
|
|
||||||
expect(loggerLogSpy).toHaveBeenCalledWith('Existing builds: 3');
|
expect(console.log).toHaveBeenCalledWith(ANY_DATE, 'BuildCleaner: ', 'Existing builds: 3');
|
||||||
expect(loggerLogSpy).toHaveBeenCalledWith('Removing 2 build(s): 1, 2');
|
expect(console.log).toHaveBeenCalledWith(ANY_DATE, 'BuildCleaner: ', 'Removing 2 build(s): 1, 2');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -466,36 +454,25 @@ describe('BuildCleaner', () => {
|
|||||||
|
|
||||||
|
|
||||||
describe('removeUnnecessaryDownloads()', () => {
|
describe('removeUnnecessaryDownloads()', () => {
|
||||||
let shellRmSpy: jasmine.Spy;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
shellRmSpy = spyOn(shell, 'rm');
|
spyOn(shell, 'rm');
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should log the number of existing downloads and downloads to be removed', () => {
|
|
||||||
cleaner.removeUnnecessaryDownloads(EXISTING_DOWNLOADS, OPEN_PRS);
|
|
||||||
|
|
||||||
expect(loggerLogSpy).toHaveBeenCalledWith('Existing downloads: 4');
|
|
||||||
expect(loggerLogSpy).toHaveBeenCalledWith('Removing 2 download(s): 20-ABCDEF0-build.zip, 20-1234567-build.zip');
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should construct full paths to directories (by prepending \'downloadsDir\')', () => {
|
|
||||||
cleaner.removeUnnecessaryDownloads(['dl-1', 'dl-2', 'dl-3'], []);
|
|
||||||
|
|
||||||
expect(shellRmSpy).toHaveBeenCalledWith(normalize('/downloads/dl-1'));
|
|
||||||
expect(shellRmSpy).toHaveBeenCalledWith(normalize('/downloads/dl-2'));
|
|
||||||
expect(shellRmSpy).toHaveBeenCalledWith(normalize('/downloads/dl-3'));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should remove the downloads that do not correspond to open PRs', () => {
|
it('should remove the downloads that do not correspond to open PRs', () => {
|
||||||
cleaner.removeUnnecessaryDownloads(EXISTING_DOWNLOADS, OPEN_PRS);
|
cleaner.removeUnnecessaryDownloads(EXISTING_DOWNLOADS, OPEN_PRS);
|
||||||
expect(shellRmSpy).toHaveBeenCalledTimes(2);
|
expect(shell.rm).toHaveBeenCalledTimes(2);
|
||||||
expect(shellRmSpy).toHaveBeenCalledWith(normalize('/downloads/20-ABCDEF0-build.zip'));
|
expect(shell.rm).toHaveBeenCalledWith('downloads/20-ABCDEF0-build.zip');
|
||||||
expect(shellRmSpy).toHaveBeenCalledWith(normalize('/downloads/20-1234567-build.zip'));
|
expect(shell.rm).toHaveBeenCalledWith('downloads/20-1234567-build.zip');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should log the number of existing builds and builds to be removed', () => {
|
||||||
|
cleaner.removeUnnecessaryDownloads(EXISTING_DOWNLOADS, OPEN_PRS);
|
||||||
|
|
||||||
|
expect(console.log).toHaveBeenCalledWith(ANY_DATE, 'BuildCleaner: ', 'Existing downloads: 4');
|
||||||
|
expect(console.log).toHaveBeenCalledWith(ANY_DATE, 'BuildCleaner: ',
|
||||||
|
'Removing 2 download(s): downloads/20-ABCDEF0-build.zip, downloads/20-1234567-build.zip');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -126,8 +126,8 @@ describe('GithubApi', () => {
|
|||||||
(api as any).getPaginated('/foo/bar');
|
(api as any).getPaginated('/foo/bar');
|
||||||
(api as any).getPaginated('/foo/bar', {baz: 'qux'});
|
(api as any).getPaginated('/foo/bar', {baz: 'qux'});
|
||||||
|
|
||||||
expect(api.get).toHaveBeenCalledWith('/foo/bar', {page: 1, per_page: 100});
|
expect(api.get).toHaveBeenCalledWith('/foo/bar', {page: 0, per_page: 100});
|
||||||
expect(api.get).toHaveBeenCalledWith('/foo/bar', {baz: 'qux', page: 1, per_page: 100});
|
expect(api.get).toHaveBeenCalledWith('/foo/bar', {baz: 'qux', page: 0, per_page: 100});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -162,9 +162,9 @@ describe('GithubApi', () => {
|
|||||||
const paramsForPage = (page: number) => ({baz: 'qux', page, per_page: 100});
|
const paramsForPage = (page: number) => ({baz: 'qux', page, per_page: 100});
|
||||||
|
|
||||||
expect(apiGetSpy).toHaveBeenCalledTimes(3);
|
expect(apiGetSpy).toHaveBeenCalledTimes(3);
|
||||||
expect(apiGetSpy.calls.argsFor(0)).toEqual(['/foo/bar', paramsForPage(1)]);
|
expect(apiGetSpy.calls.argsFor(0)).toEqual(['/foo/bar', paramsForPage(0)]);
|
||||||
expect(apiGetSpy.calls.argsFor(1)).toEqual(['/foo/bar', paramsForPage(2)]);
|
expect(apiGetSpy.calls.argsFor(1)).toEqual(['/foo/bar', paramsForPage(1)]);
|
||||||
expect(apiGetSpy.calls.argsFor(2)).toEqual(['/foo/bar', paramsForPage(3)]);
|
expect(apiGetSpy.calls.argsFor(2)).toEqual(['/foo/bar', paramsForPage(2)]);
|
||||||
|
|
||||||
expect(data).toEqual(allItems);
|
expect(data).toEqual(allItems);
|
||||||
|
|
||||||
|
@ -4,13 +4,13 @@ import {GithubPullRequests} from '../../lib/common/github-pull-requests';
|
|||||||
|
|
||||||
// Tests
|
// Tests
|
||||||
describe('GithubPullRequests', () => {
|
describe('GithubPullRequests', () => {
|
||||||
|
|
||||||
let githubApi: jasmine.SpyObj<GithubApi>;
|
let githubApi: jasmine.SpyObj<GithubApi>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
githubApi = jasmine.createSpyObj('githubApi', ['post', 'get', 'getPaginated']);
|
githubApi = jasmine.createSpyObj('githubApi', ['post', 'get', 'getPaginated']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
describe('constructor()', () => {
|
describe('constructor()', () => {
|
||||||
|
|
||||||
it('should throw if \'githubOrg\' is missing or empty', () => {
|
it('should throw if \'githubOrg\' is missing or empty', () => {
|
||||||
@ -95,14 +95,16 @@ describe('GithubPullRequests', () => {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
describe('fetchAll()', () => {
|
describe('fetchAll()', () => {
|
||||||
let prs: GithubPullRequests;
|
let prs: GithubPullRequests;
|
||||||
|
|
||||||
beforeEach(() => prs = new GithubPullRequests(githubApi, 'foo', 'bar'));
|
beforeEach(() => {
|
||||||
|
prs = new GithubPullRequests(githubApi, 'foo', 'bar');
|
||||||
|
spyOn(console, 'log');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should call \'getPaginated()\' with the correct pathname and params', () => {
|
it('should call \'getPaginated()\' with the correct pathname and params', () => {
|
||||||
@ -129,10 +131,8 @@ describe('GithubPullRequests', () => {
|
|||||||
githubApi.getPaginated.and.returnValue('Test');
|
githubApi.getPaginated.and.returnValue('Test');
|
||||||
expect(prs.fetchAll() as any).toBe('Test');
|
expect(prs.fetchAll() as any).toBe('Test');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
describe('fetchFiles()', () => {
|
describe('fetchFiles()', () => {
|
||||||
let prs: GithubPullRequests;
|
let prs: GithubPullRequests;
|
||||||
|
|
||||||
@ -141,21 +141,21 @@ describe('GithubPullRequests', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should make a paginated GET request to GitHub with the correct pathname', () => {
|
it('should make a GET request to GitHub with the correct pathname', () => {
|
||||||
prs.fetchFiles(42);
|
prs.fetchFiles(42);
|
||||||
expect(githubApi.getPaginated).toHaveBeenCalledWith('/repos/foo/bar/pulls/42/files');
|
expect(githubApi.get).toHaveBeenCalledWith('/repos/foo/bar/pulls/42/files');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should resolve with the data returned from GitHub', done => {
|
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.callFake(() => Promise.resolve(expected));
|
githubApi.get.and.callFake(() => Promise.resolve(expected));
|
||||||
prs.fetchFiles(42).then(data => {
|
prs.fetch(42).then(data => {
|
||||||
expect(data).toEqual(expected);
|
expect(data).toEqual(expected);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
// Imports
|
// Imports
|
||||||
import {resolve as resolvePath} from 'path';
|
|
||||||
import {
|
import {
|
||||||
assert,
|
assert,
|
||||||
assertNotMissingOrEmpty,
|
assertNotMissingOrEmpty,
|
||||||
@ -7,7 +6,6 @@ import {
|
|||||||
computeShortSha,
|
computeShortSha,
|
||||||
getEnvVar,
|
getEnvVar,
|
||||||
getPrInfoFromDownloadPath,
|
getPrInfoFromDownloadPath,
|
||||||
Logger,
|
|
||||||
} from '../../lib/common/utils';
|
} from '../../lib/common/utils';
|
||||||
|
|
||||||
// Tests
|
// Tests
|
||||||
@ -21,7 +19,6 @@ describe('utils', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
describe('assert', () => {
|
describe('assert', () => {
|
||||||
it('should throw if passed a false value', () => {
|
it('should throw if passed a false value', () => {
|
||||||
expect(() => assert(false, 'error message')).toThrowError('error message');
|
expect(() => assert(false, 'error message')).toThrowError('error message');
|
||||||
@ -32,7 +29,6 @@ describe('utils', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
describe('computeArtifactDownloadPath', () => {
|
describe('computeArtifactDownloadPath', () => {
|
||||||
it('should compute an absolute path based on the artifact info provided', () => {
|
it('should compute an absolute path based on the artifact info provided', () => {
|
||||||
const downloadDir = '/a/b/c';
|
const downloadDir = '/a/b/c';
|
||||||
@ -40,11 +36,10 @@ describe('utils', () => {
|
|||||||
const sha = 'ABCDEF1234567';
|
const sha = 'ABCDEF1234567';
|
||||||
const artifactPath = 'a/path/to/file.zip';
|
const artifactPath = 'a/path/to/file.zip';
|
||||||
const path = computeArtifactDownloadPath(downloadDir, pr, sha, artifactPath);
|
const path = computeArtifactDownloadPath(downloadDir, pr, sha, artifactPath);
|
||||||
expect(path).toBe(resolvePath('/a/b/c/123-ABCDEF1-file.zip'));
|
expect(path).toEqual('/a/b/c/123-ABCDEF1-file.zip');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
describe('getPrInfoFromDownloadPath', () => {
|
describe('getPrInfoFromDownloadPath', () => {
|
||||||
it('should extract the PR and SHA from the file path', () => {
|
it('should extract the PR and SHA from the file path', () => {
|
||||||
const {pr, sha} = getPrInfoFromDownloadPath('a/b/c/12345-ABCDE-artifact.zip');
|
const {pr, sha} = getPrInfoFromDownloadPath('a/b/c/12345-ABCDE-artifact.zip');
|
||||||
@ -53,7 +48,6 @@ describe('utils', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
describe('assertNotMissingOrEmpty()', () => {
|
describe('assertNotMissingOrEmpty()', () => {
|
||||||
|
|
||||||
it('should throw if passed an empty value', () => {
|
it('should throw if passed an empty value', () => {
|
||||||
@ -128,79 +122,4 @@ describe('utils', () => {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
describe('Logger', () => {
|
|
||||||
let consoleErrorSpy: jasmine.Spy;
|
|
||||||
let consoleInfoSpy: jasmine.Spy;
|
|
||||||
let consoleLogSpy: jasmine.Spy;
|
|
||||||
let consoleWarnSpy: jasmine.Spy;
|
|
||||||
let logger: Logger;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
consoleErrorSpy = spyOn(console, 'error');
|
|
||||||
consoleInfoSpy = spyOn(console, 'info');
|
|
||||||
consoleLogSpy = spyOn(console, 'log');
|
|
||||||
consoleWarnSpy = spyOn(console, 'warn');
|
|
||||||
|
|
||||||
logger = new Logger('TestScope');
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should delegate to `console`', () => {
|
|
||||||
logger.error('foo');
|
|
||||||
expect(consoleErrorSpy).toHaveBeenCalledTimes(1);
|
|
||||||
expect(consoleErrorSpy.calls.argsFor(0)).toContain('foo');
|
|
||||||
|
|
||||||
logger.info('bar');
|
|
||||||
expect(consoleInfoSpy).toHaveBeenCalledTimes(1);
|
|
||||||
expect(consoleInfoSpy.calls.argsFor(0)).toContain('bar');
|
|
||||||
|
|
||||||
logger.log('baz');
|
|
||||||
expect(consoleLogSpy).toHaveBeenCalledTimes(1);
|
|
||||||
expect(consoleLogSpy.calls.argsFor(0)).toContain('baz');
|
|
||||||
|
|
||||||
logger.warn('qux');
|
|
||||||
expect(consoleWarnSpy).toHaveBeenCalledTimes(1);
|
|
||||||
expect(consoleWarnSpy.calls.argsFor(0)).toContain('qux');
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should prepend messages with the current date and logger\'s scope', () => {
|
|
||||||
const mockDate = new Date(1337);
|
|
||||||
const expectedDateStr = `[${mockDate}]`;
|
|
||||||
const expectedScopeStr = 'TestScope: ';
|
|
||||||
|
|
||||||
jasmine.clock().mockDate(mockDate);
|
|
||||||
jasmine.clock().withMock(() => {
|
|
||||||
logger.error();
|
|
||||||
logger.info();
|
|
||||||
logger.log();
|
|
||||||
logger.warn();
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(consoleErrorSpy).toHaveBeenCalledWith(expectedDateStr, expectedScopeStr);
|
|
||||||
expect(consoleInfoSpy).toHaveBeenCalledWith(expectedDateStr, expectedScopeStr);
|
|
||||||
expect(consoleLogSpy).toHaveBeenCalledWith(expectedDateStr, expectedScopeStr);
|
|
||||||
expect(consoleWarnSpy).toHaveBeenCalledWith(expectedDateStr, expectedScopeStr);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should pass all arguments to `console`', () => {
|
|
||||||
const someString = jasmine.any(String);
|
|
||||||
|
|
||||||
logger.error('foo1', 'foo2');
|
|
||||||
expect(consoleErrorSpy).toHaveBeenCalledWith(someString, someString, 'foo1', 'foo2');
|
|
||||||
|
|
||||||
logger.info('bar1', 'bar2');
|
|
||||||
expect(consoleInfoSpy).toHaveBeenCalledWith(someString, someString, 'bar1', 'bar2');
|
|
||||||
|
|
||||||
logger.log('baz1', 'baz2');
|
|
||||||
expect(consoleLogSpy).toHaveBeenCalledWith(someString, someString, 'baz1', 'baz2');
|
|
||||||
|
|
||||||
logger.warn('qux1', 'qux2');
|
|
||||||
expect(consoleWarnSpy).toHaveBeenCalledWith(someString, someString, 'qux1', 'qux2');
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
declare namespace jasmine {
|
||||||
|
export interface DoneFn extends Function {
|
||||||
|
(): void;
|
||||||
|
fail: (message: Error | string) => void;
|
||||||
|
}
|
||||||
|
}
|
@ -3,4 +3,5 @@ import {runTests} from '../lib/common/run-tests';
|
|||||||
|
|
||||||
// Run
|
// Run
|
||||||
const specFiles = [`${__dirname}/**/*.spec.js`];
|
const specFiles = [`${__dirname}/**/*.spec.js`];
|
||||||
runTests(specFiles);
|
const helpers = [`${__dirname}/helpers.js`];
|
||||||
|
runTests(specFiles, helpers);
|
||||||
|
@ -5,7 +5,6 @@ import * as fs from 'fs';
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as shell from 'shelljs';
|
import * as shell from 'shelljs';
|
||||||
import {SHORT_SHA_LEN} from '../../lib/common/constants';
|
import {SHORT_SHA_LEN} from '../../lib/common/constants';
|
||||||
import {Logger} from '../../lib/common/utils';
|
|
||||||
import {BuildCreator} from '../../lib/preview-server/build-creator';
|
import {BuildCreator} from '../../lib/preview-server/build-creator';
|
||||||
import {ChangedPrVisibilityEvent, CreatedBuildEvent} from '../../lib/preview-server/build-events';
|
import {ChangedPrVisibilityEvent, CreatedBuildEvent} from '../../lib/preview-server/build-events';
|
||||||
import {PreviewServerError} from '../../lib/preview-server/preview-error';
|
import {PreviewServerError} from '../../lib/preview-server/preview-error';
|
||||||
@ -492,7 +491,7 @@ describe('BuildCreator', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cpExecCbs = [];
|
cpExecCbs = [];
|
||||||
|
|
||||||
consoleWarnSpy = spyOn(Logger.prototype, 'warn');
|
consoleWarnSpy = spyOn(console, 'warn');
|
||||||
shellChmodSpy = spyOn(shell, 'chmod');
|
shellChmodSpy = spyOn(shell, 'chmod');
|
||||||
shellRmSpy = spyOn(shell, 'rm');
|
shellRmSpy = spyOn(shell, 'rm');
|
||||||
cpExecSpy = spyOn(cp, 'exec').and.callFake((_: string, cb: (...args: any[]) => void) => cpExecCbs.push(cb));
|
cpExecSpy = spyOn(cp, 'exec').and.callFake((_: string, cb: (...args: any[]) => void) => cpExecCbs.push(cb));
|
||||||
@ -514,7 +513,8 @@ describe('BuildCreator', () => {
|
|||||||
|
|
||||||
it('should log (as a warning) any stderr output if extracting succeeded', done => {
|
it('should log (as a warning) any stderr output if extracting succeeded', done => {
|
||||||
(bc as any).extractArchive('foo', 'bar').
|
(bc as any).extractArchive('foo', 'bar').
|
||||||
then(() => expect(consoleWarnSpy).toHaveBeenCalledWith('This is stderr')).
|
then(() => expect(consoleWarnSpy)
|
||||||
|
.toHaveBeenCalledWith(jasmine.any(String), 'BuildCreator: ', 'This is stderr')).
|
||||||
then(done);
|
then(done);
|
||||||
|
|
||||||
cpExecCbs[0](null, 'This is stdout', 'This is stderr');
|
cpExecCbs[0](null, 'This is stdout', 'This is stderr');
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as nock from 'nock';
|
import * as nock from 'nock';
|
||||||
import {resolve as resolvePath} from 'path';
|
|
||||||
import {BuildInfo, CircleCiApi} from '../../lib/common/circle-ci-api';
|
import {BuildInfo, CircleCiApi} from '../../lib/common/circle-ci-api';
|
||||||
import {Logger} from '../../lib/common/utils';
|
|
||||||
import {BuildRetriever} from '../../lib/preview-server/build-retriever';
|
import {BuildRetriever} from '../../lib/preview-server/build-retriever';
|
||||||
|
|
||||||
describe('BuildRetriever', () => {
|
describe('BuildRetriever', () => {
|
||||||
const MAX_DOWNLOAD_SIZE = 10000;
|
const MAX_DOWNLOAD_SIZE = 10000;
|
||||||
const DOWNLOAD_DIR = resolvePath('/DOWNLOAD/DIR');
|
const DOWNLOAD_DIR = '/DOWNLOAD/DIR';
|
||||||
const BASE_URL = 'http://test.com';
|
const BASE_URL = 'http://test.com';
|
||||||
const ARTIFACT_PATH = '/some/path/build.zip';
|
const ARTIFACT_PATH = '/some/path/build.zip';
|
||||||
|
|
||||||
@ -31,6 +29,10 @@ describe('BuildRetriever', () => {
|
|||||||
vcs_revision: 'COMMIT',
|
vcs_revision: 'COMMIT',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
spyOn(console, 'log');
|
||||||
|
spyOn(console, 'warn');
|
||||||
|
spyOn(console, 'error');
|
||||||
|
|
||||||
api = new CircleCiApi('ORG', 'REPO', 'TOKEN');
|
api = new CircleCiApi('ORG', 'REPO', 'TOKEN');
|
||||||
spyOn(api, 'getBuildInfo').and.callFake(() => Promise.resolve(BUILD_INFO));
|
spyOn(api, 'getBuildInfo').and.callFake(() => Promise.resolve(BUILD_INFO));
|
||||||
getBuildArtifactUrlSpy = spyOn(api, 'getBuildArtifactUrl')
|
getBuildArtifactUrlSpy = spyOn(api, 'getBuildArtifactUrl')
|
||||||
@ -89,7 +91,6 @@ describe('BuildRetriever', () => {
|
|||||||
let retriever: BuildRetriever;
|
let retriever: BuildRetriever;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
spyOn(Logger.prototype, 'warn');
|
|
||||||
retriever = new BuildRetriever(api, MAX_DOWNLOAD_SIZE, DOWNLOAD_DIR);
|
retriever = new BuildRetriever(api, MAX_DOWNLOAD_SIZE, DOWNLOAD_DIR);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -132,14 +133,11 @@ describe('BuildRetriever', () => {
|
|||||||
|
|
||||||
it('should write the artifact file to disk', async () => {
|
it('should write the artifact file to disk', async () => {
|
||||||
const artifactRequest = nock(BASE_URL).get(ARTIFACT_PATH).reply(200, ARTIFACT_CONTENTS);
|
const artifactRequest = nock(BASE_URL).get(ARTIFACT_PATH).reply(200, ARTIFACT_CONTENTS);
|
||||||
const downloadPath = resolvePath(`${DOWNLOAD_DIR}/777-COMMIT-build.zip`);
|
|
||||||
|
|
||||||
await retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH);
|
await retriever.downloadBuildArtifact(12345, 777, 'COMMIT', ARTIFACT_PATH);
|
||||||
expect(writeFileSpy).toHaveBeenCalledWith(downloadPath, jasmine.any(Buffer), jasmine.any(Function));
|
expect(writeFileSpy)
|
||||||
|
.toHaveBeenCalledWith(`${DOWNLOAD_DIR}/777-COMMIT-build.zip`, jasmine.any(Buffer), jasmine.any(Function));
|
||||||
const buffer: Buffer = writeFileSpy.calls.mostRecent().args[1];
|
const buffer: Buffer = writeFileSpy.calls.mostRecent().args[1];
|
||||||
expect(buffer.toString()).toEqual(ARTIFACT_CONTENTS);
|
expect(buffer.toString()).toEqual(ARTIFACT_CONTENTS);
|
||||||
|
|
||||||
artifactRequest.done();
|
artifactRequest.done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2,11 +2,11 @@
|
|||||||
import * as express from 'express';
|
import * as express from 'express';
|
||||||
import * as http from 'http';
|
import * as http from 'http';
|
||||||
import * as supertest from 'supertest';
|
import * as supertest from 'supertest';
|
||||||
|
import {promisify} from 'util';
|
||||||
import {CircleCiApi} from '../../lib/common/circle-ci-api';
|
import {CircleCiApi} from '../../lib/common/circle-ci-api';
|
||||||
import {GithubApi} from '../../lib/common/github-api';
|
import {GithubApi} from '../../lib/common/github-api';
|
||||||
import {GithubPullRequests} from '../../lib/common/github-pull-requests';
|
import {GithubPullRequests} from '../../lib/common/github-pull-requests';
|
||||||
import {GithubTeams} from '../../lib/common/github-teams';
|
import {GithubTeams} from '../../lib/common/github-teams';
|
||||||
import {Logger} from '../../lib/common/utils';
|
|
||||||
import {BuildCreator} from '../../lib/preview-server/build-creator';
|
import {BuildCreator} from '../../lib/preview-server/build-creator';
|
||||||
import {ChangedPrVisibilityEvent, CreatedBuildEvent} from '../../lib/preview-server/build-events';
|
import {ChangedPrVisibilityEvent, CreatedBuildEvent} from '../../lib/preview-server/build-events';
|
||||||
import {BuildRetriever, GithubInfo} from '../../lib/preview-server/build-retriever';
|
import {BuildRetriever, GithubInfo} from '../../lib/preview-server/build-retriever';
|
||||||
@ -38,18 +38,15 @@ describe('PreviewServerFactory', () => {
|
|||||||
significantFilesPattern: '^(?:aio|packages)\\/(?!.*[._]spec\\.[jt]s$)',
|
significantFilesPattern: '^(?:aio|packages)\\/(?!.*[._]spec\\.[jt]s$)',
|
||||||
trustedPrLabel: 'trusted: pr-label',
|
trustedPrLabel: 'trusted: pr-label',
|
||||||
};
|
};
|
||||||
let loggerErrorSpy: jasmine.Spy;
|
|
||||||
let loggerInfoSpy: jasmine.Spy;
|
|
||||||
let loggerLogSpy: jasmine.Spy;
|
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
const createPreviewServer = (partialConfig: Partial<PreviewServerConfig> = {}) =>
|
const createPreviewServer = (partialConfig: Partial<PreviewServerConfig> = {}) =>
|
||||||
PreviewServerFactory.create({...defaultConfig, ...partialConfig});
|
PreviewServerFactory.create({...defaultConfig, ...partialConfig});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
loggerErrorSpy = spyOn(Logger.prototype, 'error');
|
spyOn(console, 'error');
|
||||||
loggerInfoSpy = spyOn(Logger.prototype, 'info');
|
spyOn(console, 'info');
|
||||||
loggerLogSpy = spyOn(Logger.prototype, 'log');
|
spyOn(console, 'log');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('create()', () => {
|
describe('create()', () => {
|
||||||
@ -143,10 +140,11 @@ describe('PreviewServerFactory', () => {
|
|||||||
const server = createPreviewServer();
|
const server = createPreviewServer();
|
||||||
server.address = () => ({address: 'foo', family: '', port: 1337});
|
server.address = () => ({address: 'foo', family: '', port: 1337});
|
||||||
|
|
||||||
expect(loggerInfoSpy).not.toHaveBeenCalled();
|
expect(console.info).not.toHaveBeenCalled();
|
||||||
|
|
||||||
server.emit('listening');
|
server.emit('listening');
|
||||||
expect(loggerInfoSpy).toHaveBeenCalledWith('Up and running (and listening on foo:1337)...');
|
expect(console.info).toHaveBeenCalledWith(
|
||||||
|
jasmine.any(String), 'PreviewServer: ', 'Up and running (and listening on foo:1337)...');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -243,6 +241,10 @@ describe('PreviewServerFactory', () => {
|
|||||||
let buildCreator: BuildCreator;
|
let buildCreator: BuildCreator;
|
||||||
let agent: supertest.SuperTest<supertest.Test>;
|
let agent: supertest.SuperTest<supertest.Test>;
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
const promisifyRequest = async (req: supertest.Request) => await promisify(req.end.bind(req))();
|
||||||
|
const verifyRequests = async (reqs: supertest.Request[]) => await Promise.all(reqs.map(promisifyRequest));
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const circleCiApi = new CircleCiApi(defaultConfig.githubOrg, defaultConfig.githubRepo,
|
const circleCiApi = new CircleCiApi(defaultConfig.githubOrg, defaultConfig.githubRepo,
|
||||||
defaultConfig.circleCiToken);
|
defaultConfig.circleCiToken);
|
||||||
@ -255,15 +257,14 @@ describe('PreviewServerFactory', () => {
|
|||||||
buildCreator = new BuildCreator(defaultConfig.buildsDir);
|
buildCreator = new BuildCreator(defaultConfig.buildsDir);
|
||||||
|
|
||||||
const middleware = PreviewServerFactory.createMiddleware(buildRetriever, buildVerifier, buildCreator,
|
const middleware = PreviewServerFactory.createMiddleware(buildRetriever, buildVerifier, buildCreator,
|
||||||
defaultConfig);
|
defaultConfig);
|
||||||
agent = supertest.agent(middleware);
|
agent = supertest.agent(middleware);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
describe('GET /health-check', () => {
|
describe('GET /health-check', () => {
|
||||||
|
|
||||||
it('should respond with 200', async () => {
|
it('should respond with 200', async () => {
|
||||||
await Promise.all([
|
await verifyRequests([
|
||||||
agent.get('/health-check').expect(200),
|
agent.get('/health-check').expect(200),
|
||||||
agent.get('/health-check/').expect(200),
|
agent.get('/health-check/').expect(200),
|
||||||
]);
|
]);
|
||||||
@ -271,7 +272,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should respond with 404 for non-GET requests', async () => {
|
it('should respond with 404 for non-GET requests', async () => {
|
||||||
await Promise.all([
|
await verifyRequests([
|
||||||
agent.put('/health-check').expect(404),
|
agent.put('/health-check').expect(404),
|
||||||
agent.post('/health-check').expect(404),
|
agent.post('/health-check').expect(404),
|
||||||
agent.patch('/health-check').expect(404),
|
agent.patch('/health-check').expect(404),
|
||||||
@ -281,7 +282,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
|
|
||||||
|
|
||||||
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', async () => {
|
||||||
await Promise.all([
|
await verifyRequests([
|
||||||
agent.get('/health-check/foo').expect(404),
|
agent.get('/health-check/foo').expect(404),
|
||||||
agent.get('/health-check-foo').expect(404),
|
agent.get('/health-check-foo').expect(404),
|
||||||
agent.get('/health-checknfoo').expect(404),
|
agent.get('/health-checknfoo').expect(404),
|
||||||
@ -293,104 +294,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('/circle-build', () => {
|
||||||
describe('GET /can-have-public-preview/<pr>', () => {
|
|
||||||
const baseUrl = '/can-have-public-preview';
|
|
||||||
const pr = 777;
|
|
||||||
const url = `${baseUrl}/${pr}`;
|
|
||||||
let bvGetPrIsTrustedSpy: jasmine.Spy;
|
|
||||||
let bvGetSignificantFilesChangedSpy: jasmine.Spy;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
bvGetPrIsTrustedSpy = spyOn(buildVerifier, 'getPrIsTrusted').and.returnValue(Promise.resolve(true));
|
|
||||||
bvGetSignificantFilesChangedSpy = spyOn(buildVerifier, 'getSignificantFilesChanged').
|
|
||||||
and.returnValue(Promise.resolve(true));
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should respond with 404 for non-GET requests', async () => {
|
|
||||||
await Promise.all([
|
|
||||||
agent.put(url).expect(404),
|
|
||||||
agent.post(url).expect(404),
|
|
||||||
agent.patch(url).expect(404),
|
|
||||||
agent.delete(url).expect(404),
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should respond with 404 if the path does not match exactly', async () => {
|
|
||||||
await Promise.all([
|
|
||||||
agent.get('/can-have-public-preview/42/foo').expect(404),
|
|
||||||
agent.get('/can-have-public-preview-foo/42').expect(404),
|
|
||||||
agent.get('/can-have-public-previewnfoo/42').expect(404),
|
|
||||||
agent.get('/foo/can-have-public-preview/42').expect(404),
|
|
||||||
agent.get('/foo-can-have-public-preview/42').expect(404),
|
|
||||||
agent.get('/fooncan-have-public-preview/42').expect(404),
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should respond appropriately if the PR did not touch any significant files', async () => {
|
|
||||||
bvGetSignificantFilesChangedSpy.and.returnValue(Promise.resolve(false));
|
|
||||||
|
|
||||||
const expectedResponse = {canHavePublicPreview: false, reason: 'No significant files touched.'};
|
|
||||||
const expectedLog = `PR:${pr} - Cannot have a public preview, because it did not touch any significant files.`;
|
|
||||||
|
|
||||||
await agent.get(url).expect(200, expectedResponse);
|
|
||||||
|
|
||||||
expect(bvGetSignificantFilesChangedSpy).toHaveBeenCalledWith(pr, jasmine.any(RegExp));
|
|
||||||
expect(bvGetPrIsTrustedSpy).not.toHaveBeenCalled();
|
|
||||||
expect(loggerLogSpy).toHaveBeenCalledWith(expectedLog);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should respond appropriately if the PR is not automatically verifiable as "trusted"', async () => {
|
|
||||||
bvGetPrIsTrustedSpy.and.returnValue(Promise.resolve(false));
|
|
||||||
|
|
||||||
const expectedResponse = {canHavePublicPreview: false, reason: 'Not automatically verifiable as "trusted".'};
|
|
||||||
const expectedLog =
|
|
||||||
`PR:${pr} - Cannot have a public preview, because not automatically verifiable as "trusted".`;
|
|
||||||
|
|
||||||
await agent.get(url).expect(200, expectedResponse);
|
|
||||||
|
|
||||||
expect(bvGetSignificantFilesChangedSpy).toHaveBeenCalledWith(pr, jasmine.any(RegExp));
|
|
||||||
expect(bvGetPrIsTrustedSpy).toHaveBeenCalledWith(pr);
|
|
||||||
expect(loggerLogSpy).toHaveBeenCalledWith(expectedLog);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should respond appropriately if the PR can have a preview', async () => {
|
|
||||||
const expectedResponse = {canHavePublicPreview: true, reason: null};
|
|
||||||
const expectedLog = `PR:${pr} - Can have a public preview.`;
|
|
||||||
|
|
||||||
await agent.get(url).expect(200, expectedResponse);
|
|
||||||
|
|
||||||
expect(bvGetSignificantFilesChangedSpy).toHaveBeenCalledWith(pr, jasmine.any(RegExp));
|
|
||||||
expect(bvGetPrIsTrustedSpy).toHaveBeenCalledWith(pr);
|
|
||||||
expect(loggerLogSpy).toHaveBeenCalledWith(expectedLog);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should respond with error if `getSignificantFilesChanged()` fails', async () => {
|
|
||||||
bvGetSignificantFilesChangedSpy.and.callFake(() => Promise.reject('getSignificantFilesChanged error'));
|
|
||||||
|
|
||||||
await agent.get(url).expect(500, 'getSignificantFilesChanged error');
|
|
||||||
expect(loggerErrorSpy).toHaveBeenCalledWith('Previewability check error', 'getSignificantFilesChanged error');
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should respond with error if `getPrIsTrusted()` fails', async () => {
|
|
||||||
const error = new Error('getPrIsTrusted error');
|
|
||||||
bvGetPrIsTrustedSpy.and.callFake(() => { throw error; });
|
|
||||||
|
|
||||||
await agent.get(url).expect(500, 'getPrIsTrusted error');
|
|
||||||
expect(loggerErrorSpy).toHaveBeenCalledWith('Previewability check error', error);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
describe('POST /circle-build', () => {
|
|
||||||
let getGithubInfoSpy: jasmine.Spy;
|
let getGithubInfoSpy: jasmine.Spy;
|
||||||
let getSignificantFilesChangedSpy: jasmine.Spy;
|
let getSignificantFilesChangedSpy: jasmine.Spy;
|
||||||
let downloadBuildArtifactSpy: jasmine.Spy;
|
let downloadBuildArtifactSpy: jasmine.Spy;
|
||||||
@ -455,8 +359,8 @@ describe('PreviewServerFactory', () => {
|
|||||||
await agent.post(URL).send(BASIC_PAYLOAD).expect(204);
|
await agent.post(URL).send(BASIC_PAYLOAD).expect(204);
|
||||||
expect(getGithubInfoSpy).not.toHaveBeenCalled();
|
expect(getGithubInfoSpy).not.toHaveBeenCalled();
|
||||||
expect(getSignificantFilesChangedSpy).not.toHaveBeenCalled();
|
expect(getSignificantFilesChangedSpy).not.toHaveBeenCalled();
|
||||||
expect(loggerLogSpy).toHaveBeenCalledWith(
|
expect(console.log).toHaveBeenCalledWith(jasmine.any(String), 'PreviewServer: ',
|
||||||
'Build:12345, Job:lint -', 'Skipping preview processing because this is not the "aio_preview" job.');
|
'Build:12345, Job:lint -', 'Skipping preview processing because this is not the "aio_preview" job.');
|
||||||
expect(downloadBuildArtifactSpy).not.toHaveBeenCalled();
|
expect(downloadBuildArtifactSpy).not.toHaveBeenCalled();
|
||||||
expect(getPrIsTrustedSpy).not.toHaveBeenCalled();
|
expect(getPrIsTrustedSpy).not.toHaveBeenCalled();
|
||||||
expect(createBuildSpy).not.toHaveBeenCalled();
|
expect(createBuildSpy).not.toHaveBeenCalled();
|
||||||
@ -467,7 +371,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
await agent.post(URL).send(BASIC_PAYLOAD).expect(204);
|
await agent.post(URL).send(BASIC_PAYLOAD).expect(204);
|
||||||
expect(getGithubInfoSpy).toHaveBeenCalledWith(BUILD_NUM);
|
expect(getGithubInfoSpy).toHaveBeenCalledWith(BUILD_NUM);
|
||||||
expect(getSignificantFilesChangedSpy).toHaveBeenCalledWith(PR, jasmine.any(RegExp));
|
expect(getSignificantFilesChangedSpy).toHaveBeenCalledWith(PR, jasmine.any(RegExp));
|
||||||
expect(loggerLogSpy).toHaveBeenCalledWith(
|
expect(console.log).toHaveBeenCalledWith(jasmine.any(String), 'PreviewServer: ',
|
||||||
'PR:777, Build:12345 - Skipping preview processing because this PR did not touch any significant files.');
|
'PR:777, Build:12345 - Skipping preview processing because this PR did not touch any significant files.');
|
||||||
expect(downloadBuildArtifactSpy).not.toHaveBeenCalled();
|
expect(downloadBuildArtifactSpy).not.toHaveBeenCalled();
|
||||||
expect(getPrIsTrustedSpy).not.toHaveBeenCalled();
|
expect(getPrIsTrustedSpy).not.toHaveBeenCalled();
|
||||||
@ -563,7 +467,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should respond with 404 for non-POST requests', async () => {
|
it('should respond with 404 for non-POST requests', async () => {
|
||||||
await Promise.all([
|
await verifyRequests([
|
||||||
agent.get(url).expect(404),
|
agent.get(url).expect(404),
|
||||||
agent.put(url).expect(404),
|
agent.put(url).expect(404),
|
||||||
agent.patch(url).expect(404),
|
agent.patch(url).expect(404),
|
||||||
@ -578,7 +482,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
const request1 = agent.post(url);
|
const request1 = agent.post(url);
|
||||||
const request2 = agent.post(url).send();
|
const request2 = agent.post(url).send();
|
||||||
|
|
||||||
await Promise.all([
|
await verifyRequests([
|
||||||
request1.expect(400, responseBody),
|
request1.expect(400, responseBody),
|
||||||
request2.expect(400, responseBody),
|
request2.expect(400, responseBody),
|
||||||
]);
|
]);
|
||||||
@ -591,7 +495,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
const request1 = agent.post(url).send({});
|
const request1 = agent.post(url).send({});
|
||||||
const request2 = agent.post(url).send({number: null});
|
const request2 = agent.post(url).send({number: null});
|
||||||
|
|
||||||
await Promise.all([
|
await verifyRequests([
|
||||||
request1.expect(400, `${responseBodyPrefix} {}`),
|
request1.expect(400, `${responseBodyPrefix} {}`),
|
||||||
request2.expect(400, `${responseBodyPrefix} {"number":null}`),
|
request2.expect(400, `${responseBodyPrefix} {"number":null}`),
|
||||||
]);
|
]);
|
||||||
@ -599,7 +503,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
|
|
||||||
|
|
||||||
it('should call \'BuildVerifier#gtPrIsTrusted()\' with the correct arguments', async () => {
|
it('should call \'BuildVerifier#gtPrIsTrusted()\' with the correct arguments', async () => {
|
||||||
await createRequest(+pr);
|
await promisifyRequest(createRequest(+pr));
|
||||||
expect(bvGetPrIsTrustedSpy).toHaveBeenCalledWith(9);
|
expect(bvGetPrIsTrustedSpy).toHaveBeenCalledWith(9);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -607,8 +511,9 @@ describe('PreviewServerFactory', () => {
|
|||||||
it('should propagate errors from BuildVerifier', async () => {
|
it('should propagate errors from BuildVerifier', async () => {
|
||||||
bvGetPrIsTrustedSpy.and.callFake(() => Promise.reject('Test'));
|
bvGetPrIsTrustedSpy.and.callFake(() => Promise.reject('Test'));
|
||||||
|
|
||||||
await createRequest(+pr).expect(500, 'Test');
|
const req = createRequest(+pr).expect(500, 'Test');
|
||||||
|
|
||||||
|
await promisifyRequest(req);
|
||||||
expect(bvGetPrIsTrustedSpy).toHaveBeenCalledWith(9);
|
expect(bvGetPrIsTrustedSpy).toHaveBeenCalledWith(9);
|
||||||
expect(bcUpdatePrVisibilitySpy).not.toHaveBeenCalled();
|
expect(bcUpdatePrVisibilitySpy).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
@ -617,17 +522,19 @@ describe('PreviewServerFactory', () => {
|
|||||||
it('should call \'BuildCreator#updatePrVisibility()\' with the correct arguments', async () => {
|
it('should call \'BuildCreator#updatePrVisibility()\' with the correct arguments', async () => {
|
||||||
bvGetPrIsTrustedSpy.and.callFake((pr2: number) => Promise.resolve(pr2 === 42));
|
bvGetPrIsTrustedSpy.and.callFake((pr2: number) => Promise.resolve(pr2 === 42));
|
||||||
|
|
||||||
await createRequest(24);
|
await promisifyRequest(createRequest(24));
|
||||||
expect(bcUpdatePrVisibilitySpy).toHaveBeenCalledWith(24, false);
|
expect(bcUpdatePrVisibilitySpy).toHaveBeenCalledWith(24, false);
|
||||||
|
|
||||||
await createRequest(42);
|
await promisifyRequest(createRequest(42));
|
||||||
expect(bcUpdatePrVisibilitySpy).toHaveBeenCalledWith(42, true);
|
expect(bcUpdatePrVisibilitySpy).toHaveBeenCalledWith(42, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should propagate errors from BuildCreator', async () => {
|
it('should propagate errors from BuildCreator', async () => {
|
||||||
bcUpdatePrVisibilitySpy.and.callFake(() => Promise.reject('Test'));
|
bcUpdatePrVisibilitySpy.and.callFake(() => Promise.reject('Test'));
|
||||||
await createRequest(+pr).expect(500, 'Test');
|
|
||||||
|
const req = createRequest(+pr).expect(500, 'Test');
|
||||||
|
await verifyRequests([req]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -637,7 +544,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
bvGetPrIsTrustedSpy.and.returnValues(Promise.resolve(true), Promise.resolve(false));
|
bvGetPrIsTrustedSpy.and.returnValues(Promise.resolve(true), Promise.resolve(false));
|
||||||
|
|
||||||
const reqs = [4, 2].map(num => createRequest(num).expect(200, http.STATUS_CODES[200]));
|
const reqs = [4, 2].map(num => createRequest(num).expect(200, http.STATUS_CODES[200]));
|
||||||
await Promise.all(reqs);
|
await verifyRequests(reqs);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -645,7 +552,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
bvGetPrIsTrustedSpy.and.returnValues(Promise.resolve(true), Promise.resolve(false));
|
bvGetPrIsTrustedSpy.and.returnValues(Promise.resolve(true), Promise.resolve(false));
|
||||||
|
|
||||||
const reqs = [4, 2].map(num => createRequest(num, 'labeled').expect(200, http.STATUS_CODES[200]));
|
const reqs = [4, 2].map(num => createRequest(num, 'labeled').expect(200, http.STATUS_CODES[200]));
|
||||||
await Promise.all(reqs);
|
await verifyRequests(reqs);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -653,13 +560,14 @@ describe('PreviewServerFactory', () => {
|
|||||||
bvGetPrIsTrustedSpy.and.returnValues(Promise.resolve(true), Promise.resolve(false));
|
bvGetPrIsTrustedSpy.and.returnValues(Promise.resolve(true), Promise.resolve(false));
|
||||||
|
|
||||||
const reqs = [4, 2].map(num => createRequest(num, 'unlabeled').expect(200, http.STATUS_CODES[200]));
|
const reqs = [4, 2].map(num => createRequest(num, 'unlabeled').expect(200, http.STATUS_CODES[200]));
|
||||||
await Promise.all(reqs);
|
await verifyRequests(reqs);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('should respond with 200 (and do nothing) if \'action\' implies no visibility change', async () => {
|
it('should respond with 200 (and do nothing) if \'action\' implies no visibility change', async () => {
|
||||||
const promises = ['foo', 'notlabeled'].
|
const promises = ['foo', 'notlabeled'].
|
||||||
map(action => createRequest(+pr, action).expect(200, http.STATUS_CODES[200]));
|
map(action => createRequest(+pr, action).expect(200, http.STATUS_CODES[200])).
|
||||||
|
map(promisifyRequest);
|
||||||
|
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
expect(bvGetPrIsTrustedSpy).not.toHaveBeenCalled();
|
expect(bvGetPrIsTrustedSpy).not.toHaveBeenCalled();
|
||||||
@ -676,7 +584,7 @@ describe('PreviewServerFactory', () => {
|
|||||||
it('should respond with 404', async () => {
|
it('should respond with 404', async () => {
|
||||||
const responseFor = (method: string) => `Unknown resource in request: ${method.toUpperCase()} /some/url`;
|
const responseFor = (method: string) => `Unknown resource in request: ${method.toUpperCase()} /some/url`;
|
||||||
|
|
||||||
await Promise.all([
|
await verifyRequests([
|
||||||
agent.get('/some/url').expect(404, responseFor('get')),
|
agent.get('/some/url').expect(404, responseFor('get')),
|
||||||
agent.put('/some/url').expect(404, responseFor('put')),
|
agent.put('/some/url').expect(404, responseFor('put')),
|
||||||
agent.post('/some/url').expect(404, responseFor('post')),
|
agent.post('/some/url').expect(404, responseFor('post')),
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -2,7 +2,6 @@
|
|||||||
set -eu -o pipefail
|
set -eu -o pipefail
|
||||||
|
|
||||||
# Set up env variables
|
# Set up env variables
|
||||||
export AIO_CIRCLE_CI_TOKEN=UNUSED_CIRCLE_CI_TOKEN
|
|
||||||
export AIO_GITHUB_TOKEN=$(head -c -1 /aio-secrets/GITHUB_TOKEN 2>/dev/null)
|
export AIO_GITHUB_TOKEN=$(head -c -1 /aio-secrets/GITHUB_TOKEN 2>/dev/null)
|
||||||
|
|
||||||
# Run the clean-up
|
# Run the clean-up
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
TODO (gkalpak): Add docs. Mention:
|
TODO (gkalpak): Add docs. Mention:
|
||||||
- Testing on CI.
|
- Testing on CI.
|
||||||
Relevant files: `aio/aio-builds-setup/scripts/test.sh`
|
Relevant files: `scripts/ci/test-aio.sh`, `aio/aio-builds-setup/scripts/test.sh`
|
||||||
- Deploying from CI.
|
- Deploying from CI.
|
||||||
Relevant files: `.circleci/config.yml`, `scripts/ci/deploy.sh`, `aio/scripts/build-artifacts.sh`,
|
Relevant files: `scripts/ci/deploy.sh`, `aio/scripts/deploy-to-firebase.sh`
|
||||||
`aio/scripts/deploy-to-firebase.sh`
|
|
||||||
|
@ -34,31 +34,34 @@ container:
|
|||||||
|
|
||||||
|
|
||||||
### On CI (CircleCI)
|
### On CI (CircleCI)
|
||||||
- The CI script builds the angular.io project.
|
- Build job completes successfully.
|
||||||
|
- The CI script checks whether the build job was initiated by a PR against the angular/angular
|
||||||
|
master branch.
|
||||||
|
- The CI script checks whether the PR has touched any files that might affect the angular.io app
|
||||||
|
(currently the `aio/` or `packages/` directories, ignoring spec files).
|
||||||
- The CI script gzips and stores the build artifacts in the CI infrastructure.
|
- The CI script gzips and stores the build artifacts in the CI infrastructure.
|
||||||
- When the build completes, CircleCI triggers a webhook on the preview-server.
|
- When the build completes CircleCI triggers a webhook on the preview-server.
|
||||||
|
|
||||||
More info on how to set things up on CI can be found [here](misc--integrate-with-ci.md).
|
More info on how to set things up on CI can be found [here](misc--integrate-with-ci.md).
|
||||||
|
|
||||||
|
|
||||||
### Hosting build artifacts
|
### Hosting build artifacts
|
||||||
|
|
||||||
- nginx receives the webhook trigger and passes it through to the preview server.
|
- nginx receives the webhook trigger and passes it through to the preview server.
|
||||||
- The preview-server runs several preliminary checks to determine whether the request is valid and
|
|
||||||
whether the corresponding PR can have a (public or non-public) preview (more details can be found
|
|
||||||
[here](overview--security-model.md)).
|
|
||||||
- The preview-server makes a request to CircleCI for the URL of the AIO build artifacts.
|
- The preview-server makes a request to CircleCI for the URL of the AIO build artifacts.
|
||||||
- The preview-server makes a request to this URL to receive the artifact - failing if the size
|
- The preview-server makes a request to this URL to receive the artifact - failing if the size
|
||||||
exceeds the specified max file size - and stores it in a temporary location.
|
exceeds the specified max file size - and stores it in a temporary location.
|
||||||
- The preview-server runs more checks to determine whether the preview should be publicly accessible
|
- The preview-server runs several checks to determine whether the request should be accepted and
|
||||||
or stored for later verification (more details can be found [here](overview--security-model.md)).
|
whether it should be publicly accessible or stored for later verification (more details can be
|
||||||
|
found [here](overview--security-model.md)).
|
||||||
- The preview-server changes the "visibility" of the associated PR, if necessary. For example, if
|
- The preview-server changes the "visibility" of the associated PR, if necessary. For example, if
|
||||||
builds for the same PR had been previously deployed as non-public and the current build has been
|
builds for the same PR had been previously deployed as non-public and the current build has been
|
||||||
automatically verified, all previous builds are made public as well.
|
automatically verified, all previous builds are made public as well.
|
||||||
If the PR transitions from "non-public" to "public", the preview-server posts a comment on the
|
If the PR transitions from "non-public" to "public", the preview-server posts a comment on the
|
||||||
corresponding PR on GitHub mentioning the SHAs and the links where the previews can be found.
|
corresponding PR on GitHub mentioning the SHAs and the links where the previews can be found.
|
||||||
- The preview-server verifies that it is not trying to overwrite an existing build.
|
- The preview-server verifies that it is not trying to overwrite an existing build.
|
||||||
- The preview-server deploys the artifacts to a sub-directory named after the PR number and the
|
- The preview-server deploys the artifacts to a sub-directory named after the PR number and the first
|
||||||
first few characters of the SHA: `<PR>/<SHA>/`
|
few characters of the SHA: `<PR>/<SHA>/`
|
||||||
(Non-publicly accessible PRs will be stored in a different location, but again derived from the PR
|
(Non-publicly accessible PRs will be stored in a different location, but again derived from the PR
|
||||||
number and SHA.)
|
number and SHA.)
|
||||||
- If the PR is publicly accessible, the preview-server posts a comment on the corresponding PR on
|
- If the PR is publicly accessible, the preview-server posts a comment on the corresponding PR on
|
||||||
@ -98,8 +101,8 @@ More info on the possible HTTP status codes and their meaning can be found
|
|||||||
|
|
||||||
### Removing obsolete artifacts
|
### Removing obsolete artifacts
|
||||||
In order to avoid flooding the disk with unnecessary build artifacts, there is a cronjob that runs a
|
In order to avoid flooding the disk with unnecessary build artifacts, there is a cronjob that runs a
|
||||||
clean-up task once a day. The task retrieves all open PRs from GitHub and removes all directories
|
clean-up tasks once a day. The task retrieves all open PRs from GitHub and removes all directories
|
||||||
that do not correspond to an open PR.
|
that do not correspond with an open PR.
|
||||||
|
|
||||||
|
|
||||||
### Health-check
|
### Health-check
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
# Overview - HTTP Status Codes
|
# Overview - HTTP Status Codes
|
||||||
|
|
||||||
|
|
||||||
This is a list of all the possible HTTP status codes returned by the nginx and preview servers,
|
This is a list of all the possible HTTP status codes returned by the nginx and preview servers, along
|
||||||
along with a brief explanation of what they mean:
|
with a brief explanation of what they mean:
|
||||||
|
|
||||||
|
|
||||||
## `http://*.ngbuilds.io/*`
|
## `http://*.ngbuilds.io/*`
|
||||||
@ -25,23 +25,6 @@ along with a brief explanation of what they mean:
|
|||||||
File not found.
|
File not found.
|
||||||
|
|
||||||
|
|
||||||
## `https://ngbuilds.io/can-have-public-preview/<pr>`
|
|
||||||
|
|
||||||
- **200 (OK)**:
|
|
||||||
Whether the PR can have a public preview (based on its author, label, changed files).
|
|
||||||
_Response type:_ JSON
|
|
||||||
_Response format:_
|
|
||||||
```ts
|
|
||||||
{
|
|
||||||
canHavePublicPreview: boolean,
|
|
||||||
reason: string | null,
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- **405 (Method Not Allowed)**:
|
|
||||||
Request method other than GET.
|
|
||||||
|
|
||||||
|
|
||||||
## `https://ngbuilds.io/circle-build`
|
## `https://ngbuilds.io/circle-build`
|
||||||
|
|
||||||
- **201 (Created)**:
|
- **201 (Created)**:
|
||||||
|
@ -11,8 +11,8 @@ part of the CI process and serving them publicly.
|
|||||||
|
|
||||||
## Security objectives
|
## Security objectives
|
||||||
|
|
||||||
- **Prevent hosting arbitrary content on our servers.**
|
- **Prevent hosting arbitrary content to on servers.**
|
||||||
Since there is no restriction on who can submit a PR, we cannot allow arbitrary, untrusted PRs'
|
Since there is no restriction on who can submit a PR, we cannot allow arbitrary untrusted PRs'
|
||||||
build artifacts to be hosted.
|
build artifacts to be hosted.
|
||||||
|
|
||||||
- **Prevent overwriting other people's hosted build artifacts.**
|
- **Prevent overwriting other people's hosted build artifacts.**
|
||||||
@ -40,49 +40,40 @@ part of the CI process and serving them publicly.
|
|||||||
### In a nutshell
|
### In a nutshell
|
||||||
The implemented approach can be broken up to the following sub-tasks:
|
The implemented approach can be broken up to the following sub-tasks:
|
||||||
|
|
||||||
1. Receive notification from CircleCI of a completed build.
|
0. Receive notification from CircleCI of a completed build.
|
||||||
2. Verify that the build is valid and can have a preview.
|
1. Verify that the build is valid and download the artifact.
|
||||||
3. Download the build artifact.
|
2. Fetch the PR's metadata, including author and labels.
|
||||||
4. Fetch the PR's metadata, including author and labels.
|
3. Check whether the PR can be automatically verified as "trusted" (based on its author or labels).
|
||||||
5. Check whether the PR can be automatically verified as "trusted" (based on its author or labels).
|
4. If necessary, update the corresponding PR's verification status.
|
||||||
6. If necessary, update the corresponding PR's verification status.
|
5. Deploy the artifacts to the corresponding PR's directory.
|
||||||
7. Deploy the artifacts to the corresponding PR's directory.
|
6. Prevent overwriting previously deployed artifacts (which ensures that the guarantees established
|
||||||
8. Prevent overwriting previously deployed artifacts (which ensures that the guarantees established
|
|
||||||
during deployment will remain valid until the artifacts are removed).
|
during deployment will remain valid until the artifacts are removed).
|
||||||
9. Prevent hosted preview files from accessing anything outside their directory.
|
7. Prevent hosted preview files from accessing anything outside their directory.
|
||||||
|
|
||||||
|
|
||||||
### Implementation details
|
### Implementation details
|
||||||
This section describes how each of the aforementioned sub-tasks is accomplished:
|
This section describes how each of the aforementioned sub-tasks is accomplished:
|
||||||
|
|
||||||
1. **Receive notification from CircleCI of a completed build**
|
0. **Receive notification from CircleCI of a completed build**
|
||||||
|
|
||||||
CircleCI is configured to trigger a webhook on our preview-server whenever a build completes.
|
CircleCI is configured to trigger a webhook on our preview-server whenever a build completes.
|
||||||
The payload contains the number of the build that completed.
|
The payload contains the number of the build that completed.
|
||||||
|
|
||||||
2. **Verify that the build is valid and can have a preview.**
|
1. **Verify that the build is valid and download the artifact.**
|
||||||
|
|
||||||
We cannot trust that the data in the webhook trigger is authentic, so we only extract the build
|
We cannot trust that the data in the webhook trigger is authentic, so we only extract the build
|
||||||
number and then run a direct query against the CircleCI API to get hold of the real data for
|
number and then run a direct query against the CircleCI API to get hold of the real data for
|
||||||
the given build number.
|
the given build number.
|
||||||
|
|
||||||
We perform a number of preliminary checks:
|
If the build was not successful then we ignore this trigger. Otherwise we check that the
|
||||||
- Was the webhook triggered by the designated CircleCI job (currently `aio_preview`)?
|
associated github organisation and repository are what we expect (e.g. angular/angular).
|
||||||
- Was the build successful?
|
|
||||||
- Are the associated GitHub organisation and repository what we expect (e.g. `angular/angular`)?
|
|
||||||
- Has the PR touched any files that might affect the angular.io app (currently the `aio/` or
|
|
||||||
`packages/` directories, ignoring spec files)?
|
|
||||||
|
|
||||||
If any of the preliminary checks fails, the process is aborted and not preview is generated.
|
Next we make another call to the CircleCI API to get a list of the URLS for artifacts of that
|
||||||
|
|
||||||
3. **Download the build artifact.**
|
|
||||||
|
|
||||||
Next we make another call to the CircleCI API to get a list of the URLs for artifacts of that
|
|
||||||
build. If there is one that matches the configured artifact path, we download the contents of the
|
build. If there is one that matches the configured artifact path, we download the contents of the
|
||||||
build artifact and store it in a local folder. This download has a maximum size limit to prevent
|
build artifact and store it in a local folder. This download has a maximum size limit to prevent
|
||||||
PRs from producing artifacts that are so large they would cause the preview server to crash.
|
PRs from producing artifacts that are so large they would cause the preview server to crash.
|
||||||
|
|
||||||
4. **Fetch the PR's metadata, including author and labels**.
|
2. **Fetch the PR's metadata, including author and labels**.
|
||||||
|
|
||||||
Once we have securely downloaded the artifact for a build, we retrieve the PR's metadata -
|
Once we have securely downloaded the artifact for a build, we retrieve the PR's metadata -
|
||||||
including the author's username and the labels - using the
|
including the author's username and the labels - using the
|
||||||
@ -90,7 +81,7 @@ This section describes how each of the aforementioned sub-tasks is accomplished:
|
|||||||
To avoid rate-limit restrictions, we use a Personal Access Token (issued by
|
To avoid rate-limit restrictions, we use a Personal Access Token (issued by
|
||||||
[@mary-poppins](https://github.com/mary-poppins)).
|
[@mary-poppins](https://github.com/mary-poppins)).
|
||||||
|
|
||||||
5. **Check whether the PR can be automatically verified as "trusted"**.
|
3. **Check whether the PR can be automatically verified as "trusted"**.
|
||||||
|
|
||||||
"Trusted" means that we are confident that the build artifacts are suitable for being deployed
|
"Trusted" means that we are confident that the build artifacts are suitable for being deployed
|
||||||
and publicly accessible on the preview server. There are two ways to check that:
|
and publicly accessible on the preview server. There are two ways to check that:
|
||||||
@ -102,32 +93,31 @@ This section describes how each of the aforementioned sub-tasks is accomplished:
|
|||||||
`read:org` scope issued by a user that can "see" the specified GitHub organization.
|
`read:org` scope issued by a user that can "see" the specified GitHub organization.
|
||||||
Here too, we use the token by @mary-poppins.
|
Here too, we use the token by @mary-poppins.
|
||||||
|
|
||||||
6. **If necessary update the corresponding PR's verification status**.
|
4. **If necessary update the corresponding PR's verification status**.
|
||||||
|
|
||||||
Once we have determined whether the PR is considered "trusted", we update its "visibility" (i.e.
|
Once we have determined whether the PR is considered "trusted", we update its "visibility" (i.e.
|
||||||
whether it is publicly accessible or not), based on the new verification status. For example, if
|
whether it is publicly accessible or not), based on the new verification status. For example, if
|
||||||
a PR was initially considered "not trusted" but the check triggered by a new build determined
|
a PR was initially considered "not trusted" but the check triggered by a new build determined
|
||||||
otherwise, the PR (and all the previously downloaded previews) are made public. It works the same
|
otherwise, the PR (and all the previously hosted previews) are made public. It works the same
|
||||||
way if a PR has gone from "trusted" to "not trusted".
|
way if a PR has gone from "trusted" to "not trusted".
|
||||||
|
|
||||||
7. **Deploy the artifacts to the corresponding PR's directory.**
|
5. **Deploy the artifacts to the corresponding PR's directory.**
|
||||||
|
|
||||||
With the preceding steps, we have verified that the build artifacts are valid. Additionally, we
|
With the preceding steps, we have verified that the build artifacts are valid.
|
||||||
have determined whether the PR can be trusted to have its previews publicly accessible or whether
|
Additionally, we have determined whether the PR can be trusted to have its previews
|
||||||
further verification is necessary.
|
publicly accessible or whether further verification is necessary. The artifacts will be stored to
|
||||||
|
the PR's directory, but will not be publicly accessible unless the PR has been verified.
|
||||||
|
Essentially, as long as sub-tasks 1, 2 and 3 can be securely accomplished, it is possible to
|
||||||
|
"project" the trust we have in a team's members through the PR to the build artifacts.
|
||||||
|
|
||||||
The artifacts will be stored to the PR's directory, but will not be publicly accessible unless
|
6. **Prevent overwriting previously deployed artifacts**.
|
||||||
the PR has been verified. Essentially, as long as sub-tasks 2, 3, 4 and 5 can be securely
|
|
||||||
accomplished, it is possible to "project" the trust we have in a team's members through the PR to
|
|
||||||
the build artifacts.
|
|
||||||
|
|
||||||
8. **Prevent overwriting previously deployed artifacts**.
|
|
||||||
|
|
||||||
In order to enforce this restriction (and ensure that the deployed artifacts' validity is
|
In order to enforce this restriction (and ensure that the deployed artifacts' validity is
|
||||||
preserved throughout their "lifetime"), the server that handles the artifacts (currently a Node.js Express server) rejects builds that have already been handled.
|
preserved throughout their "lifetime"), the server that handles the artifacts (currently a Node.js
|
||||||
|
Express server) rejects builds that have already been handled.
|
||||||
_Note: A PR can contain multiple builds; one for each SHA that was built on CircleCI._
|
_Note: A PR can contain multiple builds; one for each SHA that was built on CircleCI._
|
||||||
|
|
||||||
9. **Prevent hosted preview files from accessing anything outside their directory.**
|
7. **Prevent hosted preview files from accessing anything outside their directory.**
|
||||||
|
|
||||||
Nginx (which is used to serve the hosted preview) has been configured to not follow symlinks
|
Nginx (which is used to serve the hosted preview) has been configured to not follow symlinks
|
||||||
outside of the directory where the preview files are stored.
|
outside of the directory where the preview files are stored.
|
||||||
@ -140,10 +130,10 @@ This section describes how each of the aforementioned sub-tasks is accomplished:
|
|||||||
This means that any secret access keys need only be stored on the preview-server and not on any of
|
This means that any secret access keys need only be stored on the preview-server and not on any of
|
||||||
the CI build infrastructure (e.g. CircleCI).
|
the CI build infrastructure (e.g. CircleCI).
|
||||||
|
|
||||||
- Each trusted PR author has full control over the content that is hosted as a preview for their
|
- Each trusted PR author has full control over the content that is hosted as a preview for their PRs.
|
||||||
PRs. Part of the security model relies on the trustworthiness of these authors.
|
Part of the security model relies on the trustworthiness of these authors.
|
||||||
|
|
||||||
- Adding the specified label on a PR to mark it as trusted, gives the author full control over the
|
- Adding the specified label on a PR to mark it as trusted, gives the author full control over
|
||||||
content that is hosted for the specific PR preview (e.g. by pushing more commits to it). The user
|
the content that is hosted for the specific PR preview (e.g. by pushing more commits to it).
|
||||||
adding the label is responsible for ensuring that this control is not abused and that the PR is
|
The user adding the label is responsible for ensuring that this control is not abused and that
|
||||||
either closed (one way of another) or the access is revoked.
|
the PR is either closed (one way of another) or the access is revoked.
|
||||||
|
@ -8,7 +8,7 @@ Necessary secrets:
|
|||||||
1. `GITHUB_TOKEN`
|
1. `GITHUB_TOKEN`
|
||||||
- Used for:
|
- Used for:
|
||||||
- Retrieving open PRs without rate-limiting.
|
- Retrieving open PRs without rate-limiting.
|
||||||
- Retrieving PR info, such as author, labels, changed files.
|
- Retrieving PR author.
|
||||||
- Retrieving members of the trusted GitHub teams.
|
- Retrieving members of the trusted GitHub teams.
|
||||||
- Posting comments with preview links on PRs.
|
- Posting comments with preview links on PRs.
|
||||||
|
|
||||||
@ -25,9 +25,8 @@ Necessary secrets:
|
|||||||
- Generate new token with the `public_repo` scope.
|
- Generate new token with the `public_repo` scope.
|
||||||
|
|
||||||
2. `CIRCLE_CI_TOKEN`
|
2. `CIRCLE_CI_TOKEN`
|
||||||
- Visit https://circleci.com/gh/angular/angular/edit#api.
|
- Visit https://circleci.com/gh/angular/angular/edit#api
|
||||||
- Create an API token with `Build Artifacts` scope.
|
- Create an API token with `Build Artifacts` scope
|
||||||
|
|
||||||
|
|
||||||
## Save secrets on the VM
|
## Save secrets on the VM
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
set -eux -o pipefail
|
set -eux -o pipefail
|
||||||
exec 3>&1
|
exec 3>&1
|
||||||
|
|
||||||
echo -e "\n\n[`date`] - Updating the preview server..."
|
echo "\n\n[`date`] - Updating the preview server..."
|
||||||
|
|
||||||
# Input
|
# Input
|
||||||
readonly HOST_REPO_DIR=$1
|
readonly HOST_REPO_DIR=$1
|
||||||
|
186
aio/angular.json
186
aio/angular.json
@ -1,186 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
|
||||||
"version": 1,
|
|
||||||
"cli": {
|
|
||||||
"packageManager": "yarn",
|
|
||||||
"warnings": {
|
|
||||||
"typescriptMismatch": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"newProjectRoot": "projects",
|
|
||||||
"projects": {
|
|
||||||
"site": {
|
|
||||||
"root": "",
|
|
||||||
"sourceRoot": "src",
|
|
||||||
"projectType": "application",
|
|
||||||
"prefix": "aio",
|
|
||||||
"schematics": {
|
|
||||||
"@schematics/angular:component": {
|
|
||||||
"inlineStyle": true,
|
|
||||||
"style": "scss"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"architect": {
|
|
||||||
"build": {
|
|
||||||
"builder": "@angular-devkit/build-angular:browser",
|
|
||||||
"options": {
|
|
||||||
"outputPath": "dist",
|
|
||||||
"index": "src/index.html",
|
|
||||||
"main": "src/main.ts",
|
|
||||||
"polyfills": "src/polyfills.ts",
|
|
||||||
"tsConfig": "tsconfig.app.json",
|
|
||||||
"webWorkerTsConfig": "tsconfig.worker.json",
|
|
||||||
"aot": true,
|
|
||||||
"optimization": true,
|
|
||||||
"buildOptimizer": true,
|
|
||||||
"outputHashing": "all",
|
|
||||||
"sourceMap": true,
|
|
||||||
"statsJson": true,
|
|
||||||
"extractCss": true,
|
|
||||||
"extractLicenses": true,
|
|
||||||
"namedChunks": true,
|
|
||||||
"vendorChunk": false,
|
|
||||||
"assets": [
|
|
||||||
"src/assets",
|
|
||||||
"src/generated",
|
|
||||||
"src/pwa-manifest.json",
|
|
||||||
"src/google385281288605d160.html",
|
|
||||||
{
|
|
||||||
"glob": "custom-elements.min.js",
|
|
||||||
"input": "node_modules/@webcomponents/custom-elements",
|
|
||||||
"output": "/assets/js"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"glob": "native-shim.js",
|
|
||||||
"input": "node_modules/@webcomponents/custom-elements/src",
|
|
||||||
"output": "/assets/js"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"styles": [
|
|
||||||
"src/styles.scss"
|
|
||||||
],
|
|
||||||
"scripts": []
|
|
||||||
},
|
|
||||||
"configurations": {
|
|
||||||
"fast": {
|
|
||||||
"optimization": false
|
|
||||||
},
|
|
||||||
"next": {
|
|
||||||
"fileReplacements": [
|
|
||||||
{
|
|
||||||
"replace": "src/environments/environment.ts",
|
|
||||||
"with": "src/environments/environment.next.ts"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"serviceWorker": true
|
|
||||||
},
|
|
||||||
"stable": {
|
|
||||||
"fileReplacements": [
|
|
||||||
{
|
|
||||||
"replace": "src/environments/environment.ts",
|
|
||||||
"with": "src/environments/environment.stable.ts"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"serviceWorker": true
|
|
||||||
},
|
|
||||||
"archive": {
|
|
||||||
"fileReplacements": [
|
|
||||||
{
|
|
||||||
"replace": "src/environments/environment.ts",
|
|
||||||
"with": "src/environments/environment.archive.ts"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"serviceWorker": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"serve": {
|
|
||||||
"builder": "@angular-devkit/build-angular:dev-server",
|
|
||||||
"options": {
|
|
||||||
"browserTarget": "site:build"
|
|
||||||
},
|
|
||||||
"configurations": {
|
|
||||||
"fast": {
|
|
||||||
"browserTarget": "site:build:fast"
|
|
||||||
},
|
|
||||||
"next": {
|
|
||||||
"browserTarget": "site:build:next"
|
|
||||||
},
|
|
||||||
"stable": {
|
|
||||||
"browserTarget": "site:build:stable"
|
|
||||||
},
|
|
||||||
"archive": {
|
|
||||||
"browserTarget": "site:build:archive"
|
|
||||||
},
|
|
||||||
"ci": {
|
|
||||||
"progress": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"extract-i18n": {
|
|
||||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
|
||||||
"options": {
|
|
||||||
"browserTarget": "site:build"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"test": {
|
|
||||||
"builder": "@angular-devkit/build-angular:karma",
|
|
||||||
"options": {
|
|
||||||
"main": "src/test.ts",
|
|
||||||
"polyfills": "src/polyfills.ts",
|
|
||||||
"tsConfig": "tsconfig.spec.json",
|
|
||||||
"webWorkerTsConfig": "tsconfig.worker.json",
|
|
||||||
"karmaConfig": "karma.conf.js",
|
|
||||||
"assets": [
|
|
||||||
"src/assets",
|
|
||||||
"src/generated",
|
|
||||||
"src/pwa-manifest.json",
|
|
||||||
"src/google385281288605d160.html",
|
|
||||||
{
|
|
||||||
"glob": "custom-elements.min.js",
|
|
||||||
"input": "node_modules/@webcomponents/custom-elements",
|
|
||||||
"output": "/assets/js"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"glob": "native-shim.js",
|
|
||||||
"input": "node_modules/@webcomponents/custom-elements/src",
|
|
||||||
"output": "/assets/js"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"styles": [
|
|
||||||
"src/styles.scss"
|
|
||||||
],
|
|
||||||
"scripts": []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"lint": {
|
|
||||||
"builder": "@angular-devkit/build-angular:tslint",
|
|
||||||
"options": {
|
|
||||||
"tsConfig": [
|
|
||||||
"tsconfig.app.json",
|
|
||||||
"tsconfig.spec.json",
|
|
||||||
"tsconfig.worker.json",
|
|
||||||
"tests/e2e/tsconfig.json"
|
|
||||||
],
|
|
||||||
"exclude": [
|
|
||||||
"**/node_modules/**"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"e2e": {
|
|
||||||
"builder": "@angular-devkit/build-angular:protractor",
|
|
||||||
"options": {
|
|
||||||
"protractorConfig": "tests/e2e/protractor.conf.js",
|
|
||||||
"devServerTarget": "site:serve"
|
|
||||||
},
|
|
||||||
"configurations": {
|
|
||||||
"ci": {
|
|
||||||
"devServerTarget": "site:serve:ci"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"defaultProject": "site"
|
|
||||||
}
|
|
@ -1,13 +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 versions
|
|
||||||
Firefox ESR
|
|
||||||
not dead
|
|
||||||
IE 9-11 # For IE 9-11 support.
|
|
||||||
Chrome 41 # For Googlebot support.
|
|
3
aio/content/cli-src/.gitignore
vendored
3
aio/content/cli-src/.gitignore
vendored
@ -1,3 +0,0 @@
|
|||||||
/node_modules
|
|
||||||
package.json
|
|
||||||
yarn.lock
|
|
@ -1,109 +0,0 @@
|
|||||||
<h1 class="no-toc">CLI Command Reference</h1>
|
|
||||||
|
|
||||||
The Angular CLI is a command-line interface tool that you use to initialize, develop, scaffold, and maintain Angular applications. You can use the tool directly in a command shell, or indirectly through an interactive UI such as [Angular Console](https://angularconsole.com).
|
|
||||||
|
|
||||||
## Installing Angular CLI
|
|
||||||
|
|
||||||
Major versions of Angular CLI follow the supported major version of Angular, but minor versions can be released separately.
|
|
||||||
|
|
||||||
Install the CLI using the `npm` package manager:
|
|
||||||
<code-example format="." language="bash">
|
|
||||||
npm install -g @angular/cli
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
For details about changes between versions, and information about updating from previous releases,
|
|
||||||
see the Releases tab on GitHub: https://github.com/angular/angular-cli/releases
|
|
||||||
|
|
||||||
## Basic workflow
|
|
||||||
|
|
||||||
Invoke the tool on the command line through the `ng` executable.
|
|
||||||
Online help is available on the command line.
|
|
||||||
Enter the following to list commands or options for a given command (such as [generate](cli/generate)) with a short description.
|
|
||||||
|
|
||||||
<code-example format="." language="bash">
|
|
||||||
ng help
|
|
||||||
ng generate --help
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
To create, build, and serve a new, basic Angular project on a development server, go to the parent directory of your new workspace use the following commands:
|
|
||||||
|
|
||||||
<code-example format="." language="bash">
|
|
||||||
ng new my-first-project
|
|
||||||
cd my-first-project
|
|
||||||
ng serve
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
In your browser, open http://localhost:4200/ to see the new app run.
|
|
||||||
When you use the [ng serve](cli/serve) command to build an app and serve it locally, the server automatically rebuilds the app and reloads the page when you change any of the source files.
|
|
||||||
|
|
||||||
## Workspaces and project files
|
|
||||||
|
|
||||||
The [ng new](cli/new) command creates an *Angular workspace* folder and generates a new app skeleton.
|
|
||||||
A workspace can contain multiple apps and libraries.
|
|
||||||
The initial app created by the [ng new](cli/new) command is at the top level of the workspace.
|
|
||||||
When you generate an additional app or library in a workspace, it goes into a `projects/` subfolder.
|
|
||||||
|
|
||||||
A newly generated app contains the source files for a root module, with a root component and template.
|
|
||||||
Each app has a `src` folder that contains the logic, data, and assets.
|
|
||||||
|
|
||||||
You can edit the generated files directly, or add to and modify them using CLI commands.
|
|
||||||
Use the [ng generate](cli/generate) command to add new files for additional components and services, and code for new pipes, directives, and so on.
|
|
||||||
Commands such as [add](cli/add) and [generate](cli/generate), which create or operate on apps and libraries, must be executed from within a workspace or project folder.
|
|
||||||
|
|
||||||
* See more about the [Workspace file structure](guide/file-structure).
|
|
||||||
|
|
||||||
### Workspace and project configuration
|
|
||||||
|
|
||||||
A single workspace configuration file, `angular.json`, is created at the top level of the workspace.
|
|
||||||
This is where you can set per-project defaults for CLI command options, and specify configurations to use when the CLI builds a project for different targets.
|
|
||||||
|
|
||||||
The [ng config](cli/config) command lets you set and retrieve configuration values from the command line, or you can edit the `angular.json` file directly.
|
|
||||||
Note that option names in the configuration file must use [camelCase](guide/glossary#case-types), while option names supplied to commands can use either camelCase or dash-case.
|
|
||||||
|
|
||||||
* See more about [Workspace Configuration](guide/workspace-config).
|
|
||||||
* See the [complete schema](https://github.com/angular/angular-cli/wiki/angular-workspace) for `angular.json`.
|
|
||||||
|
|
||||||
## CLI command-language syntax
|
|
||||||
|
|
||||||
Command syntax is shown as follows:
|
|
||||||
|
|
||||||
`ng` *commandNameOrAlias* *requiredArg* [*optionalArg*] `[options]`
|
|
||||||
|
|
||||||
* Most commands, and some options, have aliases. Aliases are shown in the syntax statement for each command.
|
|
||||||
|
|
||||||
* Option names are prefixed with a double dash (--).
|
|
||||||
Option aliases are prefixed with a single dash (-).
|
|
||||||
Arguments are not prefixed.
|
|
||||||
For example:
|
|
||||||
<code-example format="." language="bash">
|
|
||||||
ng build my-app -c production
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
* Typically, the name of a generated artifact can be given as an argument to the command or specified with the --name option.
|
|
||||||
|
|
||||||
* Argument and option names can be given in either
|
|
||||||
[camelCase or dash-case](guide/glossary#case-types).
|
|
||||||
`--myOptionName` is equivalent to `--my-option-name`.
|
|
||||||
|
|
||||||
### Boolean and enumerated options
|
|
||||||
|
|
||||||
Boolean options have two forms: `--thisOption` sets the flag, `--noThisOption` clears it.
|
|
||||||
If neither option is supplied, the flag remains in its default state, as listed in the reference documentation.
|
|
||||||
|
|
||||||
Allowed values are given with each enumerated option description, with the default value in **bold**.
|
|
||||||
|
|
||||||
### Relative paths
|
|
||||||
|
|
||||||
Options that specify files can be given as absolute paths, or as paths relative to the current working directory, which is generally either the workspace or project root.
|
|
||||||
|
|
||||||
### Schematics
|
|
||||||
|
|
||||||
The [ng generate](cli/generate) and [ng add](cli/add) commands take as an argument the artifact or library to be generated or added to the current project.
|
|
||||||
In addition to any general options, each artifact or library defines its own options in a *schematic*.
|
|
||||||
Schematic options are supplied to the command in the same format as immediate command options.
|
|
||||||
|
|
||||||
|
|
||||||
### Building with Bazel
|
|
||||||
|
|
||||||
Optionally, you can configure the Angular CLI to use [Bazel](https://docs.bazel.build) as the build tool. For more information, see [Building with Bazel](guide/bazel).
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
|||||||
# Gathering and Viewing Usage Analytics
|
|
||||||
|
|
||||||
Users can opt in to share their Angular CLI usage data with [Google Analytics](https://support.google.com/analytics/answer/1008015?hl=en), using the [`ng analytics` CLI command](analytics).
|
|
||||||
The data is also shared with the Angular team, and used to improve the CLI.
|
|
||||||
|
|
||||||
The gathering of CLI analytics data is disabled by default, and must be enabled at the project level by individual users.
|
|
||||||
It cannot be enabled at the project level for all users.
|
|
||||||
|
|
||||||
Data gathered in this way can be viewed on the Google Analytics site, but is not automatically visible on your own organization's Analytics site.
|
|
||||||
As an administrator for an Angular development group, you can configure your instance of Angular CLI to be able to see analytics data for your own team's usage of the Angular CLI.
|
|
||||||
This configuration option is separate from and in addition to other usage analytics that your users may be sharing with Google.
|
|
||||||
|
|
||||||
## Enable access to CLI usage data
|
|
||||||
|
|
||||||
To configure access to your own users' CLI usage data, use the `ng config` command to add a key to your global [`angular.json` workspace configuration file](guide/workspace-config).
|
|
||||||
The key goes under `cli.analyticsSharing` at the top level of the file, outside the `projects` sections.
|
|
||||||
The value of the key is your organization's tracking ID, as assigned by Google Analytics.
|
|
||||||
This ID is a string that looks like `UA-123456-12`.
|
|
||||||
|
|
||||||
You can choose to use a descriptive string as the key value, or be assigned a random key when you run the CLI command.
|
|
||||||
For example, the following command adds a configuration key named "tracking".
|
|
||||||
|
|
||||||
<code-example language="sh" class="code-shell">
|
|
||||||
ng config --global cli.analyticsSharing.tracking UA-123456-12
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
To turn off this feature, run the following command:
|
|
||||||
|
|
||||||
<code-example language="sh" class="code-shell">
|
|
||||||
ng config --global --remove cli.analyticsSharing
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
|
|
||||||
## Per user tracking
|
|
||||||
|
|
||||||
You can add a custom user ID to the global configuration, in order to identify unique usage of commands and flags.
|
|
||||||
If that user enables CLI analytics for their own project, your analytics display tracks and labels their individual usage.
|
|
||||||
|
|
||||||
|
|
||||||
<code-example language="sh" class="code-shell">
|
|
||||||
ng config --global cli.analyticsSharing.user SOME_USER_NAME
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
To generate a new random user ID, run the following command:
|
|
||||||
|
|
||||||
<code-example language="sh" class="code-shell">
|
|
||||||
ng config --global cli.analyticsSharing.user ""
|
|
||||||
</code-example>
|
|
BIN
aio/content/examples/.DS_Store
vendored
Normal file
BIN
aio/content/examples/.DS_Store
vendored
Normal file
Binary file not shown.
17
aio/content/examples/.gitignore
vendored
17
aio/content/examples/.gitignore
vendored
@ -13,20 +13,18 @@
|
|||||||
**/src/tsconfig.app.json
|
**/src/tsconfig.app.json
|
||||||
**/src/tsconfig.spec.json
|
**/src/tsconfig.spec.json
|
||||||
**/src/typings.d.ts
|
**/src/typings.d.ts
|
||||||
**/e2e/src/app.po.ts
|
**/e2e/app.po.ts
|
||||||
**/e2e/tsconfig.e2e.json
|
**/e2e/tsconfig.e2e.json
|
||||||
**/src/karma.conf.js
|
|
||||||
**/.angular-cli.json
|
**/.angular-cli.json
|
||||||
**/.editorconfig
|
**/.editorconfig
|
||||||
**/angular.json
|
|
||||||
**/tsconfig.json
|
**/tsconfig.json
|
||||||
**/bs-config.e2e.json
|
**/bs-config.e2e.json
|
||||||
**/bs-config.json
|
**/bs-config.json
|
||||||
**/package.json
|
**/package.json
|
||||||
**/tslint.json
|
**/tslint.json
|
||||||
|
**/karma.conf.js
|
||||||
**/karma-test-shim.js
|
**/karma-test-shim.js
|
||||||
**/browser-test-shim.js
|
**/browser-test-shim.js
|
||||||
**/browserslist
|
|
||||||
**/node_modules
|
**/node_modules
|
||||||
|
|
||||||
# built files
|
# built files
|
||||||
@ -57,13 +55,9 @@ dist/
|
|||||||
|
|
||||||
# aot
|
# aot
|
||||||
**/*.ngsummary.json
|
**/*.ngsummary.json
|
||||||
upgrade-module/tsconfig-aot.json
|
|
||||||
!rollup-config.js
|
!rollup-config.js
|
||||||
upgrade-module/rollup-config.js
|
|
||||||
aot-compiler/**/*.d.ts
|
aot-compiler/**/*.d.ts
|
||||||
aot-compiler/**/*.factory.d.ts
|
aot-compiler/**/*.factory.d.ts
|
||||||
upgrade-phonecat-2-hybrid/aot/**/*
|
|
||||||
!upgrade-phonecat-2-hybrid/aot/index.html
|
|
||||||
|
|
||||||
# i18n
|
# i18n
|
||||||
!i18n/src/systemjs-text-plugin.js
|
!i18n/src/systemjs-text-plugin.js
|
||||||
@ -86,12 +80,5 @@ upgrade-phonecat-2-hybrid/aot/**/*
|
|||||||
*stackblitz.no-link.html
|
*stackblitz.no-link.html
|
||||||
|
|
||||||
# ngUpgrade testing
|
# ngUpgrade testing
|
||||||
upgrade-phonecat-1-typescript/tsconfig-aot.json
|
|
||||||
upgrade-phonecat-1-typescript/rollup-config.js
|
|
||||||
upgrade-phonecat-3-final/tsconfig-aot.json
|
|
||||||
upgrade-phonecat-3-final/rollup-config.js
|
|
||||||
!upgrade-phonecat-*/**/karma.conf.js
|
!upgrade-phonecat-*/**/karma.conf.js
|
||||||
!upgrade-phonecat-*/**/karma-test-shim.js
|
!upgrade-phonecat-*/**/karma-test-shim.js
|
||||||
|
|
||||||
# schematics
|
|
||||||
!schematics-for-libraries/projects/my-lib/package.json
|
|
@ -20,7 +20,7 @@ export class AppComponent {
|
|||||||
movies: IMovie[] = [];
|
movies: IMovie[] = [];
|
||||||
showImage = true;
|
showImage = true;
|
||||||
title = 'AngularJS to Angular Quick Ref Cookbook';
|
title = 'AngularJS to Angular Quick Ref Cookbook';
|
||||||
toggleImage(event?: UIEvent) {
|
toggleImage(event: UIEvent) {
|
||||||
this.showImage = !this.showImage;
|
this.showImage = !this.showImage;
|
||||||
this.eventType = (event && event.type) || 'not provided';
|
this.eventType = (event && event.type) || 'not provided';
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
import { Injectable, Pipe, PipeTransform } from '@angular/core';
|
||||||
|
import { DatePipe } from '@angular/common';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
// #docregion date-pipe
|
||||||
|
@Pipe({name: 'date', pure: true})
|
||||||
|
export class StringSafeDatePipe extends DatePipe implements PipeTransform {
|
||||||
|
transform(value: any, format: string): string {
|
||||||
|
value = typeof value === 'string' ?
|
||||||
|
Date.parse(value) : value;
|
||||||
|
return super.transform(value, format);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// #enddocregion date-pipe
|
351
aio/content/examples/animations/e2e/app.e2e-spec.ts
Normal file
351
aio/content/examples/animations/e2e/app.e2e-spec.ts
Normal file
@ -0,0 +1,351 @@
|
|||||||
|
'use strict'; // necessary for es6 output in node
|
||||||
|
|
||||||
|
import { browser, element, by, ElementFinder } from 'protractor';
|
||||||
|
import { logging, promise } from 'selenium-webdriver';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The tests here basically just checking that the end styles
|
||||||
|
* of each animation are in effect.
|
||||||
|
*
|
||||||
|
* Relies on the Angular testability only becoming stable once
|
||||||
|
* animation(s) have finished.
|
||||||
|
*
|
||||||
|
* Ideally we'd use https://developer.mozilla.org/en-US/docs/Web/API/Document/getAnimations
|
||||||
|
* but they're not supported in Chrome at the moment. The upcoming nganimate polyfill
|
||||||
|
* may also add some introspection support.
|
||||||
|
*/
|
||||||
|
describe('Animation Tests', () => {
|
||||||
|
|
||||||
|
const INACTIVE_COLOR = 'rgba(238, 238, 238, 1)';
|
||||||
|
const ACTIVE_COLOR = 'rgba(207, 216, 220, 1)';
|
||||||
|
const NO_TRANSFORM_MATRIX_REGEX = /matrix\(1,\s*0,\s*0,\s*1,\s*0,\s*0\)/;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
browser.get('');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('basic states', () => {
|
||||||
|
|
||||||
|
let host: ElementFinder;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
host = element(by.css('app-hero-list-basic'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('animates between active and inactive', () => {
|
||||||
|
addInactiveHero();
|
||||||
|
|
||||||
|
let li = host.element(by.css('li'));
|
||||||
|
|
||||||
|
expect(getScaleX(li)).toBe(1.0);
|
||||||
|
expect(li.getCssValue('backgroundColor')).toBe(INACTIVE_COLOR);
|
||||||
|
|
||||||
|
li.click();
|
||||||
|
browser.driver.sleep(300);
|
||||||
|
expect(getScaleX(li)).toBe(1.1);
|
||||||
|
expect(li.getCssValue('backgroundColor')).toBe(ACTIVE_COLOR);
|
||||||
|
|
||||||
|
li.click();
|
||||||
|
browser.driver.sleep(300);
|
||||||
|
expect(getScaleX(li)).toBe(1.0);
|
||||||
|
expect(li.getCssValue('backgroundColor')).toBe(INACTIVE_COLOR);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('styles inline in transitions', () => {
|
||||||
|
|
||||||
|
let host: ElementFinder;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
host = element(by.css('app-hero-list-inline-styles'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('are not kept after animation', () => {
|
||||||
|
addInactiveHero();
|
||||||
|
|
||||||
|
let li = host.element(by.css('li'));
|
||||||
|
|
||||||
|
li.click();
|
||||||
|
browser.driver.sleep(300);
|
||||||
|
expect(getScaleX(li)).toBe(1.0);
|
||||||
|
expect(li.getCssValue('backgroundColor')).toBe(INACTIVE_COLOR);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('combined transition syntax', () => {
|
||||||
|
|
||||||
|
let host: ElementFinder;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
host = element(by.css('app-hero-list-combined-transitions'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('animates between active and inactive', () => {
|
||||||
|
addInactiveHero();
|
||||||
|
|
||||||
|
let li = host.element(by.css('li'));
|
||||||
|
|
||||||
|
expect(getScaleX(li)).toBe(1.0);
|
||||||
|
expect(li.getCssValue('backgroundColor')).toBe(INACTIVE_COLOR);
|
||||||
|
|
||||||
|
li.click();
|
||||||
|
browser.driver.sleep(300);
|
||||||
|
expect(getScaleX(li)).toBe(1.1);
|
||||||
|
expect(li.getCssValue('backgroundColor')).toBe(ACTIVE_COLOR);
|
||||||
|
|
||||||
|
li.click();
|
||||||
|
browser.driver.sleep(300);
|
||||||
|
expect(getScaleX(li)).toBe(1.0);
|
||||||
|
expect(li.getCssValue('backgroundColor')).toBe(INACTIVE_COLOR);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('two-way transition syntax', () => {
|
||||||
|
|
||||||
|
let host: ElementFinder;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
host = element(by.css('app-hero-list-twoway'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('animates between active and inactive', () => {
|
||||||
|
addInactiveHero();
|
||||||
|
|
||||||
|
let li = host.element(by.css('li'));
|
||||||
|
|
||||||
|
expect(getScaleX(li)).toBe(1.0);
|
||||||
|
expect(li.getCssValue('backgroundColor')).toBe(INACTIVE_COLOR);
|
||||||
|
|
||||||
|
li.click();
|
||||||
|
browser.driver.sleep(300);
|
||||||
|
expect(getScaleX(li)).toBe(1.1);
|
||||||
|
expect(li.getCssValue('backgroundColor')).toBe(ACTIVE_COLOR);
|
||||||
|
|
||||||
|
li.click();
|
||||||
|
browser.driver.sleep(300);
|
||||||
|
expect(getScaleX(li)).toBe(1.0);
|
||||||
|
expect(li.getCssValue('backgroundColor')).toBe(INACTIVE_COLOR);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('enter & leave', () => {
|
||||||
|
|
||||||
|
let host: ElementFinder;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
host = element(by.css('app-hero-list-enter-leave'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('adds and removes element', () => {
|
||||||
|
addInactiveHero();
|
||||||
|
|
||||||
|
let li = host.element(by.css('li'));
|
||||||
|
expect(li.getCssValue('transform')).toMatch(NO_TRANSFORM_MATRIX_REGEX);
|
||||||
|
|
||||||
|
removeHero();
|
||||||
|
expect(li.isPresent()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('enter & leave & states', () => {
|
||||||
|
|
||||||
|
let host: ElementFinder;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
host = element(by.css('app-hero-list-enter-leave-states'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('adds and removes and animates between active and inactive', () => {
|
||||||
|
addInactiveHero();
|
||||||
|
|
||||||
|
let li = host.element(by.css('li'));
|
||||||
|
|
||||||
|
expect(li.getCssValue('transform')).toMatch(NO_TRANSFORM_MATRIX_REGEX);
|
||||||
|
|
||||||
|
li.click();
|
||||||
|
browser.driver.sleep(300);
|
||||||
|
expect(getScaleX(li)).toBe(1.1);
|
||||||
|
|
||||||
|
li.click();
|
||||||
|
browser.driver.sleep(300);
|
||||||
|
expect(li.getCssValue('transform')).toMatch(NO_TRANSFORM_MATRIX_REGEX);
|
||||||
|
|
||||||
|
removeHero();
|
||||||
|
expect(li.isPresent()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('auto style calc', () => {
|
||||||
|
|
||||||
|
let host: ElementFinder;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
host = element(by.css('app-hero-list-auto'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('adds and removes element', () => {
|
||||||
|
addInactiveHero();
|
||||||
|
|
||||||
|
let li = host.element(by.css('li'));
|
||||||
|
expect(li.getCssValue('height')).toBe('50px');
|
||||||
|
|
||||||
|
removeHero();
|
||||||
|
expect(li.isPresent()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('different timings', () => {
|
||||||
|
|
||||||
|
let host: ElementFinder;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
host = element(by.css('app-hero-list-timings'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('adds and removes element', () => {
|
||||||
|
addInactiveHero();
|
||||||
|
|
||||||
|
let li = host.element(by.css('li'));
|
||||||
|
expect(li.getCssValue('transform')).toMatch(NO_TRANSFORM_MATRIX_REGEX);
|
||||||
|
expect(li.getCssValue('opacity')).toMatch('1');
|
||||||
|
|
||||||
|
removeHero();
|
||||||
|
expect(li.isPresent()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('multiple keyframes', () => {
|
||||||
|
|
||||||
|
let host: ElementFinder;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
host = element(by.css('app-hero-list-multistep'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('adds and removes element', () => {
|
||||||
|
addInactiveHero();
|
||||||
|
|
||||||
|
let li = host.element(by.css('li'));
|
||||||
|
expect(li.getCssValue('transform')).toMatch(NO_TRANSFORM_MATRIX_REGEX);
|
||||||
|
expect(li.getCssValue('opacity')).toMatch('1');
|
||||||
|
|
||||||
|
removeHero();
|
||||||
|
expect(li.isPresent()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('parallel groups', () => {
|
||||||
|
|
||||||
|
let host: ElementFinder;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
host = element(by.css('app-hero-list-groups'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('adds and removes element', () => {
|
||||||
|
addInactiveHero();
|
||||||
|
|
||||||
|
let li = host.element(by.css('li'));
|
||||||
|
expect(li.getCssValue('transform')).toMatch(NO_TRANSFORM_MATRIX_REGEX);
|
||||||
|
expect(li.getCssValue('opacity')).toMatch('1');
|
||||||
|
|
||||||
|
removeHero(700);
|
||||||
|
expect(li.isPresent()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('adding active heroes', () => {
|
||||||
|
|
||||||
|
let host: ElementFinder;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
host = element(by.css('app-hero-list-basic'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('animates between active and inactive', () => {
|
||||||
|
addActiveHero();
|
||||||
|
|
||||||
|
let li = host.element(by.css('li'));
|
||||||
|
|
||||||
|
expect(getScaleX(li)).toBe(1.1);
|
||||||
|
expect(li.getCssValue('backgroundColor')).toBe(ACTIVE_COLOR);
|
||||||
|
|
||||||
|
li.click();
|
||||||
|
browser.driver.sleep(300);
|
||||||
|
expect(getScaleX(li)).toBe(1.0);
|
||||||
|
expect(li.getCssValue('backgroundColor')).toBe(INACTIVE_COLOR);
|
||||||
|
|
||||||
|
li.click();
|
||||||
|
browser.driver.sleep(300);
|
||||||
|
expect(getScaleX(li)).toBe(1.1);
|
||||||
|
expect(li.getCssValue('backgroundColor')).toBe(ACTIVE_COLOR);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('callbacks', () => {
|
||||||
|
it('fires a callback on start and done', () => {
|
||||||
|
addActiveHero();
|
||||||
|
browser.manage().logs().get(logging.Type.BROWSER)
|
||||||
|
.then((logs: logging.Entry[]) => {
|
||||||
|
const animationMessages = logs.filter((log) => {
|
||||||
|
return log.message.indexOf('Animation') !== -1 ? true : false;
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(animationMessages.length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function addActiveHero(sleep?: number) {
|
||||||
|
sleep = sleep || 500;
|
||||||
|
element(by.buttonText('Add active hero')).click();
|
||||||
|
browser.driver.sleep(sleep);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addInactiveHero(sleep?: number) {
|
||||||
|
sleep = sleep || 500;
|
||||||
|
element(by.buttonText('Add inactive hero')).click();
|
||||||
|
browser.driver.sleep(sleep);
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeHero(sleep?: number) {
|
||||||
|
sleep = sleep || 500;
|
||||||
|
element(by.buttonText('Remove hero')).click();
|
||||||
|
browser.driver.sleep(sleep);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getScaleX(el: ElementFinder) {
|
||||||
|
return Promise.all([
|
||||||
|
getBoundingClientWidth(el),
|
||||||
|
getOffsetWidth(el)
|
||||||
|
]).then(function(promiseResolutions) {
|
||||||
|
let clientWidth = promiseResolutions[0];
|
||||||
|
let offsetWidth = promiseResolutions[1];
|
||||||
|
return clientWidth / offsetWidth;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBoundingClientWidth(el: ElementFinder) {
|
||||||
|
return browser.executeScript(
|
||||||
|
'return arguments[0].getBoundingClientRect().width',
|
||||||
|
el.getWebElement()
|
||||||
|
) as PromiseLike<number>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOffsetWidth(el: ElementFinder) {
|
||||||
|
return browser.executeScript(
|
||||||
|
'return arguments[0].offsetWidth',
|
||||||
|
el.getWebElement()
|
||||||
|
) as PromiseLike<number>;
|
||||||
|
}
|
||||||
|
});
|
@ -1,251 +0,0 @@
|
|||||||
'use strict'; // necessary for es6 output in node
|
|
||||||
|
|
||||||
import { browser, ExpectedConditions as EC } from 'protractor';
|
|
||||||
import { logging } from 'selenium-webdriver';
|
|
||||||
import * as openClose from './open-close.po';
|
|
||||||
import * as statusSlider from './status-slider.po';
|
|
||||||
import * as toggle from './toggle.po';
|
|
||||||
import * as enterLeave from './enter-leave.po';
|
|
||||||
import * as auto from './auto.po';
|
|
||||||
import * as filterStagger from './filter-stagger.po';
|
|
||||||
import * as heroGroups from './hero-groups';
|
|
||||||
import { getLinkById, sleepFor } from './util';
|
|
||||||
|
|
||||||
describe('Animation Tests', () => {
|
|
||||||
const openCloseHref = getLinkById('open-close');
|
|
||||||
const statusSliderHref = getLinkById('status');
|
|
||||||
const toggleHref = getLinkById('toggle');
|
|
||||||
const enterLeaveHref = getLinkById('enter-leave');
|
|
||||||
const autoHref = getLinkById('auto');
|
|
||||||
const filterHref = getLinkById('heroes');
|
|
||||||
const heroGroupsHref = getLinkById('hero-groups');
|
|
||||||
|
|
||||||
beforeAll(() => {
|
|
||||||
browser.get('');
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Open/Close Component', () => {
|
|
||||||
const closedHeight = '100px';
|
|
||||||
const openHeight = '200px';
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
|
||||||
await openCloseHref.click();
|
|
||||||
sleepFor();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be open', async () => {
|
|
||||||
const toggleButton = openClose.getToggleButton();
|
|
||||||
const container = openClose.getComponentContainer();
|
|
||||||
let text = await container.getText();
|
|
||||||
|
|
||||||
if (text.includes('Closed')) {
|
|
||||||
await toggleButton.click();
|
|
||||||
await browser.wait(async () => await container.getCssValue('height') === openHeight, 2000);
|
|
||||||
}
|
|
||||||
|
|
||||||
text = await container.getText();
|
|
||||||
const containerHeight = await container.getCssValue('height');
|
|
||||||
|
|
||||||
expect(text).toContain('The box is now Open!');
|
|
||||||
expect(containerHeight).toBe(openHeight);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be closed', async () => {
|
|
||||||
const toggleButton = openClose.getToggleButton();
|
|
||||||
const container = openClose.getComponentContainer();
|
|
||||||
let text = await container.getText();
|
|
||||||
|
|
||||||
if (text.includes('Open')) {
|
|
||||||
await toggleButton.click();
|
|
||||||
await browser.wait(async () => await container.getCssValue('height') === closedHeight, 2000);
|
|
||||||
}
|
|
||||||
|
|
||||||
text = await container.getText();
|
|
||||||
const containerHeight = await container.getCssValue('height');
|
|
||||||
|
|
||||||
expect(text).toContain('The box is now Closed!');
|
|
||||||
expect(containerHeight).toBe(closedHeight);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should log animation events', async () => {
|
|
||||||
const toggleButton = openClose.getToggleButton();
|
|
||||||
const loggingCheckbox = openClose.getLoggingCheckbox();
|
|
||||||
await loggingCheckbox.click();
|
|
||||||
await toggleButton.click();
|
|
||||||
|
|
||||||
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
|
|
||||||
const animationMessages = logs.filter(({ message }) => message.includes('Animation'));
|
|
||||||
|
|
||||||
expect(animationMessages.length).toBeGreaterThan(0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Status Slider Component', () => {
|
|
||||||
const activeColor = 'rgba(255, 165, 0, 1)';
|
|
||||||
const inactiveColor = 'rgba(0, 0, 255, 1)';
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
|
||||||
await statusSliderHref.click();
|
|
||||||
sleepFor(2000);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be inactive with an orange background', async () => {
|
|
||||||
const toggleButton = statusSlider.getToggleButton();
|
|
||||||
const container = statusSlider.getComponentContainer();
|
|
||||||
let text = await container.getText();
|
|
||||||
|
|
||||||
if (text === 'Active') {
|
|
||||||
await toggleButton.click();
|
|
||||||
await browser.wait(async () => await container.getCssValue('backgroundColor') === inactiveColor, 2000);
|
|
||||||
}
|
|
||||||
|
|
||||||
text = await container.getText();
|
|
||||||
const bgColor = await container.getCssValue('backgroundColor');
|
|
||||||
|
|
||||||
expect(text).toBe('Inactive');
|
|
||||||
expect(bgColor).toBe(inactiveColor);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be active with a blue background', async () => {
|
|
||||||
const toggleButton = statusSlider.getToggleButton();
|
|
||||||
const container = statusSlider.getComponentContainer();
|
|
||||||
let text = await container.getText();
|
|
||||||
|
|
||||||
if (text === 'Inactive') {
|
|
||||||
await toggleButton.click();
|
|
||||||
await browser.wait(async () => await container.getCssValue('backgroundColor') === activeColor, 2000);
|
|
||||||
}
|
|
||||||
|
|
||||||
text = await container.getText();
|
|
||||||
const bgColor = await container.getCssValue('backgroundColor');
|
|
||||||
|
|
||||||
expect(text).toBe('Active');
|
|
||||||
expect(bgColor).toBe(activeColor);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Toggle Animations Component', () => {
|
|
||||||
beforeAll(async () => {
|
|
||||||
await toggleHref.click();
|
|
||||||
sleepFor();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should disabled animations on the child element', async () => {
|
|
||||||
const toggleButton = toggle.getToggleAnimationsButton();
|
|
||||||
|
|
||||||
await toggleButton.click();
|
|
||||||
|
|
||||||
const container = toggle.getComponentContainer();
|
|
||||||
const cssClasses = await container.getAttribute('class');
|
|
||||||
|
|
||||||
expect(cssClasses).toContain('ng-animate-disabled');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Enter/Leave Component', () => {
|
|
||||||
beforeAll(async () => {
|
|
||||||
await enterLeaveHref.click();
|
|
||||||
sleepFor(100);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should attach a flyInOut trigger to the list of items', async () => {
|
|
||||||
const heroesList = enterLeave.getHeroesList();
|
|
||||||
const hero = heroesList.get(0);
|
|
||||||
const cssClasses = await hero.getAttribute('class');
|
|
||||||
const transform = await hero.getCssValue('transform');
|
|
||||||
|
|
||||||
expect(cssClasses).toContain('ng-trigger-flyInOut');
|
|
||||||
expect(transform).toBe('matrix(1, 0, 0, 1, 0, 0)');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should remove the hero from the list when clicked', async () => {
|
|
||||||
const heroesList = enterLeave.getHeroesList();
|
|
||||||
const total = await heroesList.count();
|
|
||||||
const hero = heroesList.get(0);
|
|
||||||
|
|
||||||
await hero.click();
|
|
||||||
await browser.wait(async () => await heroesList.count() < total, 2000);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Auto Calculation Component', () => {
|
|
||||||
beforeAll(async () => {
|
|
||||||
await autoHref.click();
|
|
||||||
sleepFor(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should attach a shrinkOut trigger to the list of items', async () => {
|
|
||||||
const heroesList = auto.getHeroesList();
|
|
||||||
const hero = heroesList.get(0);
|
|
||||||
const cssClasses = await hero.getAttribute('class');
|
|
||||||
|
|
||||||
expect(cssClasses).toContain('ng-trigger-shrinkOut');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should remove the hero from the list when clicked', async () => {
|
|
||||||
const heroesList = auto.getHeroesList();
|
|
||||||
const total = await heroesList.count();
|
|
||||||
const hero = heroesList.get(0);
|
|
||||||
|
|
||||||
await hero.click();
|
|
||||||
await browser.wait(async () => await heroesList.count() < total, 2000);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Filter/Stagger Component', () => {
|
|
||||||
beforeAll(async () => {
|
|
||||||
await filterHref.click();
|
|
||||||
sleepFor();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should attach a filterAnimations trigger to the list container', async () => {
|
|
||||||
const heroesList = filterStagger.getComponentContainer();
|
|
||||||
const cssClasses = await heroesList.getAttribute('class');
|
|
||||||
|
|
||||||
expect(cssClasses).toContain('ng-trigger-filterAnimation');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should filter down the list when a search is performed', async () => {
|
|
||||||
const heroesList = filterStagger.getHeroesList();
|
|
||||||
const total = await heroesList.count();
|
|
||||||
|
|
||||||
const formInput = filterStagger.getFormInput();
|
|
||||||
await formInput.sendKeys('Mag');
|
|
||||||
|
|
||||||
await browser.wait(async () => await heroesList.count() === 2, 2000);
|
|
||||||
|
|
||||||
const newTotal = await heroesList.count();
|
|
||||||
expect(newTotal).toBeLessThan(total);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Hero Groups Component', () => {
|
|
||||||
beforeAll(async () => {
|
|
||||||
await heroGroupsHref.click();
|
|
||||||
sleepFor(300);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should attach a flyInOut trigger to the list of items', async () => {
|
|
||||||
const heroesList = heroGroups.getHeroesList();
|
|
||||||
const hero = heroesList.get(0);
|
|
||||||
const cssClasses = await hero.getAttribute('class');
|
|
||||||
const transform = await hero.getCssValue('transform');
|
|
||||||
const opacity = await hero.getCssValue('opacity');
|
|
||||||
|
|
||||||
expect(cssClasses).toContain('ng-trigger-flyInOut');
|
|
||||||
expect(transform).toBe('matrix(1, 0, 0, 1, 0, 0)');
|
|
||||||
expect(opacity).toBe('1');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should remove the hero from the list when clicked', async () => {
|
|
||||||
const heroesList = heroGroups.getHeroesList();
|
|
||||||
const total = await heroesList.count();
|
|
||||||
const hero = heroesList.get(0);
|
|
||||||
|
|
||||||
await hero.click();
|
|
||||||
await browser.wait(async () => await heroesList.count() < total, 2000);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
|||||||
import { by } from 'protractor';
|
|
||||||
import { locate } from './util';
|
|
||||||
|
|
||||||
export function getPage() {
|
|
||||||
return by.css('app-hero-list-auto-page');
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getComponent() {
|
|
||||||
return by.css('app-hero-list-auto');
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getComponentContainer() {
|
|
||||||
const findContainer = () => by.css('ul');
|
|
||||||
return locate(getComponent(), findContainer());
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getHeroesList() {
|
|
||||||
return getComponentContainer().all(by.css('li'));
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
import { by } from 'protractor';
|
|
||||||
import { locate } from './util';
|
|
||||||
|
|
||||||
export function getPage() {
|
|
||||||
return by.css('app-hero-list-enter-leave-page');
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getComponent() {
|
|
||||||
return by.css('app-hero-list-enter-leave');
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getComponentContainer() {
|
|
||||||
const findContainer = () => by.css('ul');
|
|
||||||
return locate(getComponent(), findContainer());
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getHeroesList() {
|
|
||||||
return getComponentContainer().all(by.css('li'));
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
import { by } from 'protractor';
|
|
||||||
import { locate } from './util';
|
|
||||||
|
|
||||||
export function getPage() {
|
|
||||||
return by.css('app-hero-list-page');
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getComponentContainer() {
|
|
||||||
const findContainer = () => by.css('ul');
|
|
||||||
return locate(getPage(), findContainer());
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getHeroesList() {
|
|
||||||
return getComponentContainer().all(by.css('li'));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getFormInput() {
|
|
||||||
const formInput = () => by.css('form > input');
|
|
||||||
return locate(getPage(), formInput());
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
import { by } from 'protractor';
|
|
||||||
import { locate } from './util';
|
|
||||||
|
|
||||||
export function getPage() {
|
|
||||||
return by.css('app-hero-list-groups-page');
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getComponent() {
|
|
||||||
return by.css('app-hero-list-groups');
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getComponentContainer() {
|
|
||||||
const findContainer = () => by.css('ul');
|
|
||||||
return locate(getComponent(), findContainer());
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getHeroesList() {
|
|
||||||
return getComponentContainer().all(by.css('li'));
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user