Compare commits

..

16 Commits

Author SHA1 Message Date
712d1a7c37 chore(release): v2.0.1 2016-09-23 11:28:36 -07:00
16601f9359 docs(changelog): add changelog for 2.0.1 2016-09-23 10:50:42 -07:00
b81e2e7a31 fix(upgrade): allow attribute selectors for components in ng2 which are not part of upgrade (#11808)
fixes #11280
2016-09-23 10:48:47 -07:00
98fac36706 docs(Component): API docs for .encapsulation and .interpolation 2016-09-23 10:22:24 -07:00
3e780c032e refactor: misc cleanup 2016-09-23 10:22:05 -07:00
e09882180e refactor(common): cleanup (#11668) 2016-09-23 10:21:58 -07:00
0e18c57a17 docs(core): mark TestBed as stable api and add preliminary docs (#11767)
TestBed was accidentaly ommited from the 'stable' api list during the API sweep before final. We do consider it to be stable.
2016-09-23 10:21:50 -07:00
51e2b9c073 docs(contributing): remove preview references (#11795) 2016-09-23 10:21:44 -07:00
f218e240d3 ci(BrowserStack): add Safari 10 (#11796) 2016-09-23 10:21:31 -07:00
af6b219f8e refactor(TemplateParser): clearer error message for on* binding (#11802)
fixes #11756
2016-09-23 10:21:15 -07:00
20addf5f9f chore(ISSUE_TEMPLATE): update Angular version field (#11821) 2016-09-23 10:21:02 -07:00
2860418a3c fix(forms): disable all radios with disable() 2016-09-23 10:19:58 -07:00
39e251eea7 fix(forms): support unbound disabled in ngModel (#11736) 2016-09-23 10:19:46 -07:00
d7d716d5db chore(zone.js): update to 0.6.25 (#11725) 2016-09-23 10:19:30 -07:00
a95d65241c fix(compiler): Safe property access expressions work in event bindings (#11724) 2016-09-23 10:19:24 -07:00
fdb22bd185 refactor: misc cleanup (#11654) 2016-09-23 10:18:38 -07:00
10443 changed files with 159046 additions and 1223768 deletions

View File

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

146
.bazelrc
View File

@ -1,146 +0,0 @@
# 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/
# Turn off legacy external runfiles
build --nolegacy_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
# Do not build runfile trees by default. If an execution strategy relies on runfile
# symlink teee, the tree is created on-demand. See: https://github.com/bazelbuild/bazel/issues/6627
# and https://github.com/bazelbuild/bazel/commit/03246077f948f2790a83520e7dccc2625650e6df
build --nobuild_runfile_links
###############################
# Release support #
# 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="yarn -s ng-dev release build-env-stamp"
build:release --stamp
###############################
# 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
################################
# Settings for CircleCI #
################################
# Bazel flags for CircleCI are in /.circleci/bazel.linux.rc and /.circleci/bazel.windows.rc
##################################
# Settings for integration tests #
##################################
# Trick bazel into treating BUILD files under integration/bazel as being regular files
# This lets us glob() up all the files inside this integration test to make them inputs to tests
# (Note, we cannot use common --deleted_packages because the bazel version command doesn't support it)
build --deleted_packages=integration/bazel,integration/bazel/src,integration/bazel/src/hello-world,integration/bazel/test,integration/bazel/tools,integration/bazel/test/e2e
query --deleted_packages=integration/bazel,integration/bazel/src,integration/bazel/src/hello-world,integration/bazel/test,integration/bazel/tools,integration/bazel/test/e2e
################################
# Temporary Settings for Ivy #
################################
# To determine if the compiler used should be Ivy instead of ViewEngine, one can use `--config=ivy`
# on any bazel target. This is a temporary flag until codebase is permanently switched to Ivy.
build --define=angular_ivy_enabled=False
build:view-engine --define=angular_ivy_enabled=False
build:ivy --define=angular_ivy_enabled=True
##################################
# Remote Build Execution support #
# Turn on these settings with #
# --config=remote #
##################################
# The following --define=EXECUTOR=remote will be able to be removed
# once https://github.com/bazelbuild/bazel/issues/7254 is fixed
build:remote --define=EXECUTOR=remote
# Set a higher timeout value, just in case.
build:remote --remote_timeout=600
# Increase the default number of jobs by 50% because our build has lots of
# parallelism
build:remote --jobs=150
build:remote --google_default_credentials
# Force remote exeuctions to consider the entire run as linux
build:remote --cpu=k8
build:remote --host_cpu=k8
# Toolchain and platform related flags
build:remote --host_javabase=@rbe_ubuntu1604_angular//java:jdk
build:remote --javabase=@rbe_ubuntu1604_angular//java:jdk
build:remote --host_java_toolchain=@bazel_tools//tools/jdk:toolchain_hostjdk8
build:remote --java_toolchain=@bazel_tools//tools/jdk:toolchain_hostjdk8
build:remote --crosstool_top=@rbe_ubuntu1604_angular//cc:toolchain
build:remote --extra_toolchains=@rbe_ubuntu1604_angular//config:cc-toolchain
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 and caching
build:remote --remote_instance_name=projects/internal-200822/instances/default_instance
build:remote --project_id=internal-200822
build:remote --remote_cache=remotebuildexecution.googleapis.com
build:remote --remote_executor=remotebuildexecution.googleapis.com
##################################
# Saucelabs tests settings #
# Turn on these settings with #
# --config=saucelabs #
##################################
# For saucelabs tests we don't want to enable flaky test attempts. Karma has its own integrated
# retry mechanism and we do not want to retry unnecessarily if Karma already tried multiple times.
test:saucelabs --flaky_test_attempts=1
####################################################
# User bazel configuration
# NOTE: This needs to be the *last* entry in the config.
####################################################
# Load any settings which are specific to the current user. Needs to be *last* statement
# in this config, as the user configuration should be able to overwrite flags from this file.
try-import .bazelrc.user

View File

@ -1,3 +0,0 @@
3.5.1
# [NB: this comment has to be after the first line, see https://github.com/bazelbuild/bazelisk/issues/117]
# When updating the Bazel version you also need to update the RBE toolchains version in package.bzl

3
.bowerrc Normal file
View File

@ -0,0 +1,3 @@
{
"directory" : "bower_components"
}

View File

@ -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 the internal pw database
- 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`

View File

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

View File

@ -1,30 +0,0 @@
# These options are enabled when running on CI
# We do this by copying this file to /etc/bazel.bazelrc at the start of the build.
# See documentation in /docs/BAZEL.md
# Import config items common to both Linux and Windows setups.
# https://docs.bazel.build/versions/master/guide.html#bazelrc-syntax-and-semantics
try-import %workspace%/.circleci/bazel.common.rc
# Save downloaded repositories in a location that can be cached by CircleCI. This helps us
# speeding up the analysis time significantly with Bazel managed node dependencies on the CI.
build --repository_cache=/home/circleci/bazel_repository_cache
# Workaround https://github.com/bazelbuild/bazel/issues/3645
# Bazel doesn't calculate the memory ceiling correctly when running under Docker.
# Limit Bazel to consuming resources that fit in CircleCI "xlarge" class
# https://circleci.com/docs/2.0/configuration-reference/#resource_class
build --local_cpu_resources=20
build --local_ram_resources=32768
# All build executed remotely should be done using our RBE configuration.
build:remote --google_default_credentials
# Upload to GCP's Build Status viewer to allow for us to have better viewing of execution/build
# logs. This is only done on CI as the BES (GCP's Build Status viewer) API requires credentials
# from service accounts, rather than end user accounts.
build:remote --bes_backend=buildeventservice.googleapis.com
build:remote --bes_timeout=30s
build:remote --bes_results_url="https://source.cloud.google.com/results/invocations/"
build --config=remote

View File

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

View File

@ -1,905 +0,0 @@
# Configuration file for https://circleci.com/gh/angular/angular
# Note: YAML anchors allow an object to be re-used, reducing duplication.
# The ampersand declares an alias for an object, then later the `<<: *name`
# syntax dereferences it.
# See http://blog.daemonl.com/2016/02/yaml.html
# To validate changes, use an online parser, eg.
# http://yaml-online-parser.appspot.com/
# CircleCI configuration version
# Version 2.1 allows for extra config reuse features
# https://circleci.com/docs/2.0/reusing-config/#getting-started-with-config-reuse
version: 2.1
# 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.
# Windows needs its own cache key because binaries in node_modules are different.
# **NOTE 1 **: If you change the cache key prefix, also sync the cache_key_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 v7-angular-node-12-{{ checksum ".bazelversion" }}-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "packages/bazel/package.bzl" }}-{{ checksum "aio/yarn.lock" }}
# We invalidate the cache if the Bazel version changes because otherwise the `bazelisk` cache
# folder will contain all previously used versions and ultimately cause the cache restoring to
# be slower due to its growing size.
var_4: &cache_key_fallback v7-angular-node-12-{{ checksum ".bazelversion" }}
# Cache key for the `components-repo-unit-tests` job. **Note** when updating the SHA in the
# cache keys also update the SHA for the "COMPONENTS_REPO_COMMIT" environment variable.
var_5: &components_repo_unit_tests_cache_key v9-angular-components-09e68db8ed5b1253f2fe38ff954ef0df019fc25a
var_6: &components_repo_unit_tests_cache_key_fallback v9-angular-components-
# Workspace initially persisted by the `setup` job, and then enhanced by `build-npm-packages` and
# `build-ivy-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_7: &workspace_location ~/
# Filter to run a job on builds for pull requests only.
var_8: &only_on_pull_requests
filters:
branches:
only:
- /pull\/\d+/
# Filter to skip a job on builds for pull requests.
var_9: &skip_on_pull_requests
filters:
branches:
ignore:
- /pull\/\d+/
# Filter to run a job on builds for the master branch only.
var_10: &only_on_master
filters:
branches:
only:
- master
# Executor Definitions
# https://circleci.com/docs/2.0/reusing-config/#authoring-reusable-executors
# **NOTE 1**: Pin to exact images using an ID (SHA). See https://circleci.com/docs/2.0/circleci-images/#using-a-docker-image-id-to-pin-an-image-to-a-fixed-version.
# (Using the tag in not necessary when pinning by ID, but include it anyway for documentation purposes.)
# **NOTE 2**: If you change the version of the docker images, also change the `cache_key` suffix.
executors:
default-executor:
parameters:
resource_class:
type: string
default: medium
docker:
- image: circleci/node:12.14.1@sha256:f9de24fc0017059cc42ef7d07db060008af65a98b1f0cdd1ef3339213226bf6d
resource_class: << parameters.resource_class >>
working_directory: ~/ng
windows-executor:
working_directory: ~/ng
resource_class: windows.2xlarge
# CircleCI windows VMs do have the GitBash shell available:
# https://github.com/CircleCI-Public/windows-preview-docs#shells
# But in this specific case we really should not use it because Bazel must not be ran from
# GitBash. These issues discuss why:
# https://github.com/bazelbuild/bazel/issues/5751
# https://github.com/bazelbuild/bazel/issues/5724#issuecomment-410194038
# https://github.com/bazelbuild/bazel/issues/6339#issuecomment-441600879
shell: powershell.exe -ExecutionPolicy Bypass
machine:
# Windows preview image that includes the following:
# - Visual Studio 2019 build tools
# - Node 12
# - yarn 1.17
# - Python 3 3.7.4
image: windows-server-2019-vs2019:201908-02
# Command Definitions
# https://circleci.com/docs/2.0/reusing-config/#authoring-reusable-commands
commands:
custom_attach_workspace:
description: Attach workspace at a predefined location
steps:
- attach_workspace:
at: *workspace_location
# Install shared libs used by Chrome that is either provisioned by
# rules_webtesting or by puppeteer.
install_chrome_libs:
description: Install shared Chrome libs
steps:
- run:
name: Install shared Chrome libs
command: |
sudo apt-get update
# Install GTK+ graphical user interface (libgtk-3-0), advanced linux sound architecture (libasound2)
# and network security service libraries (libnss3) & X11 Screen Saver extension library (libssx1)
# which are dependencies of chrome & needed for karma & protractor headless chrome tests.
# This is a very small install which takes around 7s in comparing to using the full
# circleci/node:x.x.x-browsers image.
sudo apt-get -y install libgtk-3-0 libasound2 libnss3 libxss1
# Install java runtime which is required by some integration tests such as
# //integration:hello_world__closure_test, //integration:i18n_test and
# //integration:ng_elements_test to run the closure compiler
install_java:
description: Install java
steps:
- run:
name: Install java
command: |
sudo apt-get update
# Install java runtime
sudo apt-get install default-jre
# Initializes the CI environment by setting up common environment variables.
init_environment:
description: Initializing environment (setting up variables)
steps:
- run:
name: Set up environment
environment:
CIRCLE_GIT_BASE_REVISION: << pipeline.git.base_revision >>
CIRCLE_GIT_REVISION: << pipeline.git.revision >>
command: ./.circleci/env.sh
- run:
# Configure git as the CircleCI `checkout` command does.
# This is needed because we only checkout on the setup job.
# Add GitHub to known hosts
name: Configure git
command: |
mkdir -p ~/.ssh
echo 'github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==' >> ~/.ssh/known_hosts
git config --global url."ssh://git@github.com".insteadOf "https://github.com" || true
git config --global gc.auto 0 || true
init_saucelabs_environment:
description: Sets up a domain that resolves to the local host.
steps:
- run:
name: Preparing environment for running tests on Sauce Labs.
command: |
# For SauceLabs jobs, we set up a domain which resolves to the machine which launched
# the tunnel. We do this because devices are sometimes not able to properly resolve
# `localhost` or `127.0.0.1` through the SauceLabs tunnel. Using a domain that does not
# resolve to anything on SauceLabs VMs ensures that such requests are always resolved
# through the tunnel, and resolve to the actual tunnel host machine (i.e. the CircleCI VM).
# More context can be found in: https://github.com/angular/angular/pull/35171.
setPublicVar SAUCE_LOCALHOST_ALIAS_DOMAIN "angular-ci.local"
setSecretVar SAUCE_ACCESS_KEY $(echo $SAUCE_ACCESS_KEY | rev)
- run:
# Sets up a local domain in the machine's host file that resolves to the local
# host. This domain is helpful in Sauce Labs tests where devices are not able to
# properly resolve `localhost` or `127.0.0.1` through the sauce-connect tunnel.
name: Setting up alias domain for local host.
command: echo "127.0.0.1 $SAUCE_LOCALHOST_ALIAS_DOMAIN" | sudo tee -a /etc/hosts
# Normally this would be an individual job instead of a command.
# But startup and setup time for each individual windows job are high enough to discourage
# many small jobs, so instead we use a command for setup unless the gain becomes significant.
setup_win:
description: Setup windows node environment
steps:
- checkout
# Install Bazel pre-requisites that aren't in the preconfigured CircleCI Windows VM.
- run: ./.circleci/windows-env.ps1
- run: node --version
- run: yarn --version
- run: yarn install --frozen-lockfile --non-interactive
notify_webhook_on_fail:
description: Notify a webhook about failure
parameters:
# `webhook_url_env_var` are secret env vars defined in CircleCI project settings.
# The URLs come from https://angular-team.slack.com/apps/A0F7VRE7N-circleci.
webhook_url_env_var:
type: env_var_name
steps:
- run:
when: on_fail
command: |
notificationJson="{\"text\":\":x: \`$CIRCLE_JOB\` job for $CIRCLE_BRANCH branch failed on build $CIRCLE_BUILD_NUM: $CIRCLE_BUILD_URL :scream:\"}"
curl --request POST --header "Content-Type: application/json" --data "$notificationJson" ${<< parameters.webhook_url_env_var >>}
# Job definitions
# Jobs can include parameters that are passed in the workflow job invocation.
# https://circleci.com/docs/2.0/reusing-config/#authoring-parameterized-jobs
jobs:
setup:
executor: default-executor
steps:
- checkout
- init_environment
- run:
name: Rebase PR on target branch
# After checkout, rebase on top of target branch.
command: >
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
else
echo "This build is not over a PR, nothing to do."
fi
# This cache is saved in the build-npm-packages so that Bazel cache is also included.
- restore_cache:
keys:
- *cache_key
- *cache_key_fallback
- run:
name: Running Yarn install
command: yarn install --frozen-lockfile --non-interactive
# Yarn's requests sometimes take more than 10mins to complete.
no_output_timeout: 45m
- 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**: To add new content to the workspace, always persist on the same root.
- persist_to_workspace:
root: *workspace_location
paths:
- ./ng
- ./bazel_repository_cache
lint:
executor: default-executor
steps:
- custom_attach_workspace
- init_environment
- run: yarn -s tslint
- run: yarn -s ng-dev format changed $CI_GIT_BASE_REVISION --check
- run: yarn -s ts-circular-deps:check
- run: yarn -s ng-dev pullapprove verify
- run: yarn -s ng-dev ngbot verify
- run: yarn -s ng-dev commit-message validate-range --range $CI_COMMIT_RANGE
test:
executor:
name: default-executor
# Now that large integration tests are running locally in parallel (they can't run on RBE yet
# as they require network access for yarn install), this test is running out of memory
# consistently with the xlarge machine.
# TODO: switch back to xlarge once integration tests are running on remote-exec
resource_class: 2xlarge+
steps:
- custom_attach_workspace
- init_environment
- install_chrome_libs
- install_java
- run:
command: yarn bazel test //... --build_tag_filters=-ivy-only --test_tag_filters=-ivy-only
no_output_timeout: 20m
# Temporary job to test what will happen when we flip the Ivy flag to true
test_ivy_aot:
executor:
name: default-executor
resource_class: xlarge
steps:
- custom_attach_workspace
- init_environment
- install_chrome_libs
# We need to explicitly specify the --symlink_prefix option because otherwise we would
# not be able to easily find the output bin directory when uploading artifacts for size
# measurements.
- run:
command: yarn test-ivy-aot //... --symlink_prefix=dist/
no_output_timeout: 20m
# 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
# NOTE: This is currently limited to master builds only. See the `monitoring` configuration.
saucelabs_view_engine:
executor:
name: default-executor
# 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:
- custom_attach_workspace
- init_environment
- init_saucelabs_environment
- run:
name: Run Bazel tests on Saucelabs with ViewEngine
# See /tools/saucelabs/README.md for more info
command: |
yarn bazel run //tools/saucelabs:sauce_service_setup
TESTS=$(./node_modules/.bin/bazelisk query --output label '(kind(karma_web_test, ...) intersect attr("tags", "saucelabs", ...)) except attr("tags", "ivy-only", ...) except attr("tags", "fixme-saucelabs-ve", ...)')
yarn bazel test --config=saucelabs ${TESTS}
yarn bazel run //tools/saucelabs:sauce_service_stop
no_output_timeout: 40m
- notify_webhook_on_fail:
webhook_url_env_var: SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL
# NOTE: This is currently limited to master builds only. See the `monitoring` configuration.
saucelabs_ivy:
executor:
name: default-executor
# 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:
- custom_attach_workspace
- init_environment
- init_saucelabs_environment
- run:
name: Run Bazel tests on Saucelabs with Ivy
# See /tools/saucelabs/README.md for more info
command: |
yarn bazel run //tools/saucelabs:sauce_service_setup
TESTS=$(./node_modules/.bin/bazelisk query --output label '(kind(karma_web_test, ...) intersect attr("tags", "saucelabs", ...)) except attr("tags", "no-ivy-aot", ...) except attr("tags", "fixme-saucelabs-ivy", ...)')
yarn bazel test --config=saucelabs --config=ivy ${TESTS}
yarn bazel run //tools/saucelabs:sauce_service_stop
no_output_timeout: 40m
- notify_webhook_on_fail:
webhook_url_env_var: SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL
test_aio:
executor: default-executor
steps:
- custom_attach_workspace
- init_environment
- install_chrome_libs
# Build aio
- run: yarn --cwd aio build --progress=false
# Lint the code
- run: yarn --cwd aio lint
# Run unit tests
- run: yarn --cwd aio test --progress=false --watch=false
# Run e2e tests
- run: yarn --cwd aio e2e --configuration=ci
# Run PWA-score tests
- run: yarn --cwd aio test-pwa-score-localhost $CI_AIO_MIN_PWA_SCORE
# Run accessibility tests
- run: yarn --cwd aio test-a11y-score-localhost
# Check the bundle sizes.
- run: yarn --cwd aio payload-size
# Run unit tests for Firebase redirects
- run: yarn --cwd aio redirects-test
deploy_aio:
executor: default-executor
steps:
- custom_attach_workspace
- init_environment
- install_chrome_libs
# Deploy angular.io to production (if necessary)
- run: setPublicVar_CI_STABLE_BRANCH
- run: yarn --cwd aio deploy-production
test_aio_local:
parameters:
viewengine:
type: boolean
default: false
executor: default-executor
steps:
- custom_attach_workspace
- init_environment
- install_chrome_libs
# Build aio (with local Angular packages)
- run: yarn --cwd aio build-local<<# parameters.viewengine >>-with-viewengine<</ parameters.viewengine >>-ci
# Run unit tests
- run: yarn --cwd aio test --progress=false --watch=false
# Run e2e tests
- run: yarn --cwd aio e2e --configuration=ci
# Run PWA-score tests
- run: yarn --cwd aio test-pwa-score-localhost $CI_AIO_MIN_PWA_SCORE
# Check the bundle sizes.
- run: yarn --cwd aio payload-size aio-local<<# parameters.viewengine >>-viewengine<</ parameters.viewengine >>
test_aio_tools:
executor: default-executor
steps:
- custom_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:
parameters:
viewengine:
type: boolean
default: false
executor:
name: default-executor
resource_class: xlarge
parallelism: 5
steps:
- custom_attach_workspace
- init_environment
- install_chrome_libs
# 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 "5", there will be five parallel CircleCI containers.
# with either "0", "1", etc as node index. This can be passed to the "--shard" argument.
- run: yarn --cwd aio example-e2e --setup --local <<# parameters.viewengine >>--viewengine<</ parameters.viewengine >> --cliSpecsConcurrency=5 --shard=${CIRCLE_NODE_INDEX}/${CIRCLE_NODE_TOTAL} --retry 2
# This job should only be run on PR builds, where `CI_PULL_REQUEST` is not `false`.
aio_preview:
executor: default-executor
environment:
AIO_SNAPSHOT_ARTIFACT_PATH: &aio_preview_artifact_path 'aio/tmp/snapshot.tgz'
steps:
- custom_attach_workspace
- init_environment
- run: ./aio/scripts/build-artifacts.sh $AIO_SNAPSHOT_ARTIFACT_PATH $CI_PULL_REQUEST $CI_COMMIT
- store_artifacts:
path: *aio_preview_artifact_path
# The `destination` needs to be kept in synch with the value of
# `AIO_ARTIFACT_PATH` in `aio/aio-builds-setup/Dockerfile`
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:
executor: default-executor
steps:
- custom_attach_workspace
- init_environment
- install_chrome_libs
- 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:
executor:
name: default-executor
resource_class: xlarge
steps:
- custom_attach_workspace
- init_environment
- run: node scripts/build/build-packages-dist.js
# Save the npm packages from //packages/... for other workflow jobs to read
- persist_to_workspace:
root: *workspace_location
paths:
- ng/dist/packages-dist
- ng/dist/zone.js-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"
- "~/.cache/bazelisk"
# Build the ivy npm packages.
build-ivy-npm-packages:
executor:
name: default-executor
resource_class: xlarge
steps:
- custom_attach_workspace
- init_environment
- run: node scripts/build/build-ivy-npm-packages.js
# Save the npm packages from //packages/... for other workflow jobs to read
- persist_to_workspace:
root: *workspace_location
paths:
- ng/dist/packages-dist-ivy-aot
- ng/dist/zone.js-dist-ivy-aot
# This job creates compressed tarballs (`.tgz` files) for all Angular packages and stores them as
# build artifacts. This makes it easy to try out changes from a PR build for testing purposes.
# More info CircleCI build artifacts: https://circleci.com/docs/2.0/artifacts
#
# NOTE: Currently, this job only runs for PR builds. See `publish_snapshot` for non-PR builds.
publish_packages_as_artifacts:
executor: default-executor
environment:
NG_PACKAGES_DIR: &ng_packages_dir 'dist/packages-dist'
NG_PACKAGES_ARCHIVES_DIR: &ng_packages_archives_dir 'dist/packages-dist-archives'
ZONEJS_PACKAGES_DIR: &zonejs_packages_dir 'dist/zone.js-dist'
ZONEJS_PACKAGES_ARCHIVES_DIR: &zonejs_packages_archives_dir 'dist/zone.js-dist-archives'
steps:
- custom_attach_workspace
- init_environment
# Publish `@angular/*` packages.
- run:
name: Create artifacts for @angular/* packages
command: ./scripts/ci/create-package-archives.sh $CI_BRANCH $CI_COMMIT $NG_PACKAGES_DIR $NG_PACKAGES_ARCHIVES_DIR
- store_artifacts:
path: *ng_packages_archives_dir
destination: angular
# Publish `zone.js` package.
- run:
name: Create artifacts for zone.js package
command: ./scripts/ci/create-package-archives.sh $CI_BRANCH $CI_COMMIT $ZONEJS_PACKAGES_DIR $ZONEJS_PACKAGES_ARCHIVES_DIR
- store_artifacts:
path: *zonejs_packages_archives_dir
destination: zone.js
# This job updates the content of repos like github.com/angular/core-builds
# for every green build on angular/angular.
publish_snapshot:
executor: default-executor
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
- custom_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
# We need ensure that the same default digest is used for encoding and decoding with
# OpenSSL. OpenSSL versions might have different default digests which can cause
# decryption failures based on the installed OpenSSL version. https://stackoverflow.com/a/39641378/4317734
command: 'openssl aes-256-cbc -d -in .circleci/github_token -md md5 -k "${KEY}" -out ~/.git_credentials'
- run: ./scripts/ci/publish-build-artifacts.sh
aio_monitoring_stable:
executor: default-executor
steps:
- custom_attach_workspace
- init_environment
- install_chrome_libs
- run: setPublicVar_CI_STABLE_BRANCH
- run:
name: Check out `aio/` and yarn from the stable branch
command: |
git fetch origin $CI_STABLE_BRANCH
git checkout --force origin/$CI_STABLE_BRANCH -- aio/ .yarn/ .yarnrc
# Ignore yarn's engines check, because we checked out `aio/package.json` from the stable
# branch and there could be a node version skew, which is acceptable in this monitoring job.
- run: yarn config set ignore-engines true
- run:
name: Run tests against https://angular.io/
command: ./aio/scripts/test-production.sh https://angular.io/ $CI_AIO_MIN_PWA_SCORE
- notify_webhook_on_fail:
webhook_url_env_var: SLACK_CARETAKER_WEBHOOK_URL
- notify_webhook_on_fail:
webhook_url_env_var: SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL
aio_monitoring_next:
executor: default-executor
steps:
- custom_attach_workspace
- init_environment
- install_chrome_libs
- run:
name: Run tests against https://next.angular.io/
command: ./aio/scripts/test-production.sh https://next.angular.io/ $CI_AIO_MIN_PWA_SCORE
- notify_webhook_on_fail:
webhook_url_env_var: SLACK_CARETAKER_WEBHOOK_URL
- notify_webhook_on_fail:
webhook_url_env_var: SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL
legacy-unit-tests-saucelabs:
executor:
name: default-executor
# 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:
- custom_attach_workspace
- init_environment
- init_saucelabs_environment
- run:
name: Starting Saucelabs tunnel service
command: ./tools/saucelabs/sauce-service.sh run
background: true
# add module umd tsc compile option so the test can work
# properly in the legacy browsers
- run: yarn tsc -p packages --module UMD
- run: yarn tsc -p modules --module UMD
- run: yarn bazel build //packages/zone.js:npm_package
# Build test fixtures for a test that rely on Bazel-generated fixtures. Note that disabling
# specific tests which are reliant on such generated fixtures is not an option as SystemJS
# in the Saucelabs legacy job always fetches referenced files, even if the imports would be
# guarded by an check to skip in the Saucelabs legacy job. We should be good running such
# test in all supported browsers on Saucelabs anyway until this job can be removed.
- run:
name: Preparing Bazel-generated fixtures required in legacy tests
command: |
yarn bazel build //packages/core/test:downleveled_es5_fixture
# Needed for the ES5 downlevel reflector test in `packages/core/test/reflection`.
cp dist/bin/packages/core/test/reflection/es5_downleveled_inheritance_fixture.js \
dist/all/@angular/core/test/reflection/es5_downleveled_inheritance_fixture.js
- run:
# Waiting on ready ensures that we don't run tests too early without Saucelabs not being ready.
name: Waiting for Saucelabs tunnel to connect
command: ./tools/saucelabs/sauce-service.sh ready-wait
- run:
name: Running tests on Saucelabs.
command: |
browsers=$(node -e 'console.log(require("./browser-providers.conf").sauceAliases.CI_REQUIRED.join(","))')
yarn karma start ./karma-js.conf.js --single-run --browsers=${browsers}
- run:
name: Stop Saucelabs tunnel service
command: ./tools/saucelabs/sauce-service.sh stop
# Job that runs all unit tests of the `angular/components` repository.
components-repo-unit-tests:
executor:
name: default-executor
resource_class: xlarge
steps:
- custom_attach_workspace
- init_environment
# Restore the cache before cloning the repository because the clone script re-uses
# the restored repository if present. This reduces the amount of times the components
# repository needs to be cloned (this is slow and increases based on commits in the repo).
- restore_cache:
keys:
- *components_repo_unit_tests_cache_key
# Whenever the `angular/components` SHA is updated, the cache key will no longer
# match. The fallback cache will still match, and CircleCI will restore the most
# recently cached repository folder. Without the fallback cache, we'd need to download
# the repository from scratch and it would slow down the job. This is because we can't
# clone the repository with reduced `--depth`, but rather need to clone the whole
# repository to be able to support arbitrary SHAs.
- *components_repo_unit_tests_cache_key_fallback
- run:
name: "Fetching angular/components repository"
command: ./scripts/ci/clone_angular_components_repo.sh
- run:
# Run yarn install to fetch the Bazel binaries as used in the components repo.
name: Installing dependencies.
# TODO: remove this once the repo has been updated to use NodeJS v12 and Yarn 1.19.1.
# We temporarily ignore the "engines" because the Angular components repository has
# minimum dependency on NodeJS v12 and Yarn 1.19.1, but the framework repository uses
# older versions.
command: yarn --ignore-engines --cwd ${COMPONENTS_REPO_TMP_DIR} install --frozen-lockfile --non-interactive
- save_cache:
key: *components_repo_unit_tests_cache_key
paths:
# Temporary directory must be kept in sync with the `$COMPONENTS_REPO_TMP_DIR` env
# variable. It needs to be hardcoded here, because env variables interpolation is
# not supported.
- "/tmp/angular-components-repo"
- run:
# Updates the `angular/components` `package.json` file to refer to the release output
# inside the `packages-dist` directory. Note that it's not necessary to perform a yarn
# install as Bazel runs Yarn automatically when needed.
name: Setting up release packages.
command: node scripts/ci/update-deps-to-dist-packages.js ${COMPONENTS_REPO_TMP_DIR}/package.json dist/packages-dist/
- run:
name: "Running `angular/components` unit tests"
command: ./scripts/ci/run_angular_components_unit_tests.sh
test_zonejs:
executor:
name: default-executor
resource_class: xlarge
steps:
- custom_attach_workspace
- init_environment
# Install
- run: yarn --cwd packages/zone.js install --frozen-lockfile --non-interactive
# Run zone.js tools tests
- run: yarn --cwd packages/zone.js promisetest
- run: yarn --cwd packages/zone.js promisefinallytest
- run: yarn bazel build //packages/zone.js:npm_package &&
cp dist/bin/packages/zone.js/npm_package/bundles/zone-mix.umd.js ./packages/zone.js/test/extra/ &&
cp dist/bin/packages/zone.js/npm_package/bundles/zone-patch-electron.umd.js ./packages/zone.js/test/extra/ &&
yarn --cwd packages/zone.js electrontest
- run: yarn --cwd packages/zone.js jest:test
- run: yarn --cwd packages/zone.js jest:nodetest
- run: yarn --cwd packages/zone.js/test/typings install --frozen-lockfile --non-interactive
- run: yarn --cwd packages/zone.js/test/typings test
# Windows jobs
# Docs: https://circleci.com/docs/2.0/hello-world-windows/
test_win:
executor: windows-executor
steps:
- setup_win
- run:
name: Build all windows CI targets
command: yarn bazel build --build_tag_filters=-ivy-only //packages/compiler-cli/... //tools/ts-api-guardian/...
no_output_timeout: 15m
- run:
name: Test all windows CI targets
command: yarn bazel test --test_tag_filters="-ivy-only,-browser:chromium-local" //packages/compiler-cli/... //tools/ts-api-guardian/...
no_output_timeout: 15m
test_ivy_aot_win:
executor: windows-executor
steps:
- setup_win
- run:
name: Build all windows CI targets
command: yarn bazel build --config=ivy --build_tag_filters=-no-ivy-aot,-fixme-ivy-aot //packages/compiler-cli/... //tools/ts-api-guardian/...
no_output_timeout: 15m
- run:
name: Test all windows CI targets
command: yarn bazel test --config=ivy --test_tag_filters="-no-ivy-aot,-fixme-ivy-aot,-browser:chromium-local" //packages/compiler-cli/... //tools/ts-api-guardian/...
no_output_timeout: 15m
workflows:
version: 2
default_workflow:
jobs:
- setup:
filters:
branches:
ignore: g3
- lint:
requires:
- setup
- test:
requires:
- setup
- test_ivy_aot:
requires:
- setup
- build-npm-packages:
requires:
- setup
- build-ivy-npm-packages:
requires:
- setup
- legacy-unit-tests-saucelabs:
requires:
- setup
- test_aio:
requires:
- setup
- deploy_aio:
requires:
- test_aio
- test_aio_local:
requires:
- build-npm-packages
- test_aio_local:
name: test_aio_local_viewengine
viewengine: true
requires:
- build-npm-packages
- test_aio_tools:
requires:
- build-npm-packages
- test_docs_examples:
requires:
- build-npm-packages
- test_docs_examples:
name: test_docs_examples_viewengine
viewengine: true
requires:
- build-npm-packages
- aio_preview:
# Only run on PR builds. (There can be no previews for non-PR builds.)
<<: *only_on_pull_requests
requires:
- setup
- test_aio_preview:
requires:
- aio_preview
- publish_packages_as_artifacts:
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
# Only publish if `aio`/`docs` tests using the locally built Angular packages pass
- test_aio_local
- test_aio_local_viewengine
- test_docs_examples
- test_docs_examples_viewengine
# 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
# Temporarily disabled components-repo-unit-tests to update rules_nodejs to 2.0.0. Breaking changes in
# rules_nodejs create a dependency sandwich between angular/angular & angular/components that are very
# difficult and time consuming to resolve and involve patching @angular/bazel in components repo such
# as https://github.com/angular/components/commit/9e7ba251207df77164d73d66620e619bcbc4d2ad. It is simpler to
# 1) land angular/angular upgrade to rule_nodejs 2.0.0 which has breaking changes
# 2) land angular/components upgrade to rules_nodejs 2.0.0 using the @angular/bazel builds snapshot
# 3) update angular/angular to the landed components commit and re-enable these tests
# - components-repo-unit-tests:
# requires:
# - build-npm-packages
- test_zonejs:
requires:
- setup
- test_win:
requires:
- setup
- test_ivy_aot_win:
requires:
- setup
monitoring:
jobs:
- setup
- aio_monitoring_stable:
requires:
- setup
- aio_monitoring_next:
requires:
- setup
- saucelabs_ivy:
# Testing saucelabs via Bazel currently taking longer than the legacy saucelabs job as it
# each karma_web_test target is provisioning and tearing down browsers which is adding
# a lot of overhead. Running once daily on master only to avoid wasting resources and
# slowing down CI for PRs.
# TODO: Run this job on all branches (including PRs) once karma_web_test targets can
# share provisioned browsers and we can remove the legacy saucelabs job.
requires:
- setup
- saucelabs_view_engine:
# Testing saucelabs via Bazel currently taking longer than the legacy saucelabs job as it
# each karma_web_test target is provisioning and tearing down browsers which is adding
# a lot of overhead. Running once daily on master only to avoid wasting resources and
# slowing down CI for PRs.
# TODO: Run this job on all branches (including PRs) once karma_web_test targets can
# share provisioned browsers and we can remove the legacy saucelabs job.
requires:
- setup
triggers:
- schedule:
<<: *only_on_master
# Runs monitoring jobs at 10:00AM every day.
cron: "0 10 * * *"

View File

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

View File

@ -1,113 +0,0 @@
#!/usr/bin/env bash
# Variables
readonly projectDir=$(realpath "$(dirname ${BASH_SOURCE[0]})/..")
readonly envHelpersPath="$projectDir/.circleci/env-helpers.inc.sh";
readonly bashEnvCachePath="$projectDir/.circleci/bash_env_cache";
# 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 CI "$CI"
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";
setPublicVar CI_BUILD_URL "$CIRCLE_BUILD_URL";
setPublicVar CI_COMMIT "$CIRCLE_SHA1";
# `CI_COMMIT_RANGE` is only used on push builds (a.k.a. non-PR, non-scheduled builds and rerun
# workflows of such builds).
setPublicVar CI_GIT_BASE_REVISION "${CIRCLE_GIT_BASE_REVISION}";
setPublicVar CI_GIT_REVISION "${CIRCLE_GIT_REVISION}";
setPublicVar CI_COMMIT_RANGE "$CIRCLE_GIT_BASE_REVISION..$CIRCLE_GIT_REVISION";
setPublicVar CI_PULL_REQUEST "${CIRCLE_PR_NUMBER:-false}";
setPublicVar CI_REPO_NAME "$CIRCLE_PROJECT_REPONAME";
setPublicVar CI_REPO_OWNER "$CIRCLE_PROJECT_USERNAME";
setPublicVar CI_PR_REPONAME "$CIRCLE_PR_REPONAME";
setPublicVar CI_PR_USERNAME "$CIRCLE_PR_USERNAME";
####################################################################################################
# Define "lazy" PUBLIC environment variables for CircleCI.
# (I.e. functions to set an environment variable when called.)
####################################################################################################
createPublicVarSetter CI_STABLE_BRANCH "\$(npm info @angular/core dist-tags.latest | sed -r 's/^\\s*([0-9]+\\.[0-9]+)\\.[0-9]+.*$/\\1.x/')";
####################################################################################################
# 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.
####################################################################################################
setPublicVar SAUCE_USERNAME "angular-framework";
setSecretVar SAUCE_ACCESS_KEY "0c731274ed5f-cbc9-16f4-021a-9835e39f";
# TODO(josephperrott): Remove environment variables once all saucelabs tests are via bazel method.
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-framework-${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/components` repo unit tests job.
####################################################################################################
# We specifically use a directory within "/tmp" here because we want the cloned repo to be
# completely isolated from angular/angular in order to avoid any bad interactions between
# their separate build setups. **NOTE**: When updating the temporary directory, also update
# the `save_cache` path configuration in `config.yml`
setPublicVar COMPONENTS_REPO_TMP_DIR "/tmp/angular-components-repo"
setPublicVar COMPONENTS_REPO_URL "https://github.com/angular/components.git"
setPublicVar COMPONENTS_REPO_BRANCH "master"
# **NOTE**: When updating the commit SHA, also update the cache key in the CircleCI `config.yml`.
setPublicVar COMPONENTS_REPO_COMMIT "09e68db8ed5b1253f2fe38ff954ef0df019fc25a"
####################################################################################################
# Decrypt GCP Credentials and store them as the Google default credentials.
####################################################################################################
mkdir -p "$HOME/.config/gcloud";
openssl aes-256-cbc -d -in "${projectDir}/.circleci/gcp_token" \
-md md5 -k "$CIRCLE_PROJECT_REPONAME" -out "$HOME/.config/gcloud/application_default_credentials.json"
####################################################################################################
# Set bazel configuration for CircleCI runs.
####################################################################################################
cp "${projectDir}/.circleci/bazel.linux.rc" "$HOME/.bazelrc";
####################################################################################################
# Create shell script in /tmp for Bazel actions to access CI envs without
# busting the cache. Used by payload-size.sh script in integration tests.
####################################################################################################
readonly bazelVarEnv="/tmp/bazel-ci-env.sh"
echo "# Setup by /.circle/env.sh" > $bazelVarEnv
echo "export PROJECT_ROOT=\"${PROJECT_ROOT}\";" >> $bazelVarEnv
echo "export CI_BRANCH=\"${CI_BRANCH}\";" >> $bazelVarEnv
echo "export CI_BUILD_URL=\"${CI_BUILD_URL}\";" >> $bazelVarEnv
echo "export CI_COMMIT=\"${CI_COMMIT}\";" >> $bazelVarEnv
echo "export CI_COMMIT_RANGE=\"${CI_COMMIT_RANGE}\";" >> $bazelVarEnv
echo "export CI_PULL_REQUEST=\"${CI_PULL_REQUEST}\";" >> $bazelVarEnv
echo "export CI_REPO_NAME=\"${CI_REPO_NAME}\";" >> $bazelVarEnv
echo "export CI_REPO_OWNER=\"${CI_REPO_OWNER}\";" >> $bazelVarEnv
echo "export CI_SECRET_PAYLOAD_FIREBASE_TOKEN=\"${CI_SECRET_PAYLOAD_FIREBASE_TOKEN}\";" >> $bazelVarEnv
####################################################################################################
####################################################################################################
## Source `$BASH_ENV` to make the variables available immediately. ##
## ***NOTE: This must remain the the last action in this script*** ##
####################################################################################################
####################################################################################################
source $BASH_ENV;

Binary file not shown.

Binary file not shown.

View File

@ -1,11 +0,0 @@
#!/bin/sh
# Install bazel remote cache proxy
# This is temporary until the feature is no longer experimental on CircleCI.
# See remote cache documentation in /docs/BAZEL.md
set -u -e
readonly DOWNLOAD_URL="https://5-116431813-gh.circle-artifacts.com/0/pkg/bazel-remote-proxy-$(uname -s)_$(uname -m)"
curl --fail -o ~/bazel-remote-proxy "$DOWNLOAD_URL"
chmod +x ~/bazel-remote-proxy

View File

@ -1,105 +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);
}

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
# https://editorconfig.org
# http://editorconfig.org
root = true

3
.gitattributes vendored
View File

@ -5,8 +5,5 @@
*.js eol=lf
*.ts eol=lf
# API guardian patch must always use LF for tests to work
*.patch eol=lf
# Must keep Windows line ending to be parsed correctly
scripts/windows/packages.txt eol=crlf

View File

@ -1,10 +1,31 @@
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑
**I'm submitting a ...** (check one with "x")
```
[ ] bug report => search github for a similar issue or PR before submitting
[ ] feature request
[ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question
```
Please help us process issues more efficiently by filing an
issue using one of the following templates:
**Current behavior**
<!-- Describe how the bug manifests. -->
https://github.com/angular/angular/issues/new/choose
**Expected behavior**
<!-- Describe what the behavior would be without the bug. -->
Thank you!
**Reproduction of the problem**
<!-- If the current behavior is a bug or you can illustrate your feature request better with an example, please provide the steps to reproduce and if possible a minimal demo of the problem via https://plnkr.co or similar (you can use this template as a starting point: http://plnkr.co/edit/tpl:AvJOMERrnz94ekVua0u5). -->
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑
**What is the motivation / use case for changing the behavior?**
<!-- Describe the motivation or the concrete use case -->
**Please tell us about your environment:**
<!-- Operating system, IDE, package manager, HTTP server, ... -->
* **Angular version:** 2.0.X
<!-- Check whether this is still an issue in the most recent Angular version -->
* **Browser:** [all | Chrome XX | Firefox XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView ]
<!-- All browsers where this could be reproduced -->
* **Language:** [all | TypeScript X.X | ES6/7 | ES5]
* **Node (for AoT issues):** `node --version` =

View File

@ -1,69 +0,0 @@
---
name: "\U0001F41E Bug 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-ivy
-->
<!-- ✍️--> 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. -->

View File

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

View File

@ -1,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. -->

View File

@ -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.
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑

View File

@ -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!
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑

View File

@ -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.
🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑🛑

View File

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

View File

@ -1,43 +1,36 @@
## PR Checklist
Please check if your PR fulfills the following requirements:
**Please check if the PR fulfills these requirements**
- [ ] The commit message follows our guidelines: https://github.com/angular/angular/blob/master/CONTRIBUTING.md#commit
- [ ] Tests for the changes have been added (for bug fixes / features)
- [ ] Docs have been added / updated (for bug fixes / features)
## PR Type
What kind of change does this PR introduce?
**What kind of change does this PR introduce?** (check one with "x")
```
[ ] Bugfix
[ ] Feature
[ ] Code style update (formatting, local variables)
[ ] Refactoring (no functional changes, no api changes)
[ ] Build related changes
[ ] CI related changes
[ ] Other... Please describe:
```
<!-- Please check the one that applies to this PR using "x". -->
- [ ] Bugfix
- [ ] Feature
- [ ] Code style update (formatting, local variables)
- [ ] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [ ] CI related changes
- [ ] Documentation content changes
- [ ] angular.io application / infrastructure changes
- [ ] Other... Please describe:
**What is the current behavior?** (You can also link to an open issue here)
## What is the current behavior?
<!-- Please describe the current behavior that you are modifying, or link to a relevant issue. -->
Issue Number: N/A
**What is the new behavior?**
## What is the new behavior?
**Does this PR introduce a breaking change?** (check one with "x")
```
[ ] Yes
[ ] No
```
If this PR contains a breaking change, please describe the impact and migration path for existing applications: ...
## Does this PR introduce a breaking change?
**Other information**:
- [ ] Yes
- [ ] No
<!-- If this PR contains a breaking change, please describe the impact and migration path for existing applications below. -->
## Other information

View File

@ -1,194 +0,0 @@
# 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
merge:
# the status will be added to your pull requests
status:
# set to true to disable
disabled: false
# the name of the status
context: "ci/angular: merge status"
# text to show when all checks pass
successText: "All checks passed!"
# text to show when some checks are failing
failureText: "The following checks are failing:"
# the g3 status will be added to your pull requests if they include files that match the patterns
g3Status:
# set to true to disable
disabled: false
# the name of the status
context: "google3"
# text to show when the status is pending, {{PRNumber}} will be replaced by the PR number
pendingDesc: "Googler: run g3sync presubmit {{PRNumber}}"
# text to show when the status is success
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
# this list must be manually kept in sync with google3/third_party/javascript/angular2/copy.bara.sky
include:
- "LICENSE"
- "modules/benchmarks/**"
- "modules/system.d.ts"
- "packages/**"
- "dev-infra/benchmark/driver-utilities/**"
# list of patterns to ignore for the files changed by the PR
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/src/ngcc/**"
- "packages/compiler-cli/linker/**"
- "packages/compiler-cli/ngcc/**"
- "packages/compiler-cli/src/ngtsc/sourcemaps/**"
- "packages/docs/**"
- "packages/elements/schematics/**"
- "packages/examples/**"
- "packages/language-service/**"
- "packages/localize/**"
- "packages/private/**"
- "packages/service-worker/**"
- "packages/common/locales/**"
- "packages/http/**"
- "**/.gitignore"
- "**/.gitkeep"
- "**/yarn.lock"
- "**/package.json"
- "**/third_party/**"
- "**/tsconfig-build.json"
- "**/tsconfig.json"
- "**/rollup.config.js"
- "**/BUILD.bazel"
- "**/*.md"
- "packages/**/integrationtest/**"
- "packages/**/test/**"
- "packages/zone.js/*"
- "packages/zone.js/dist/**"
- "packages/zone.js/doc/**"
- "packages/zone.js/example/**"
- "packages/zone.js/scripts/**"
# comment that will be added to a PR when there is a conflict, leave empty or set to false to disable
mergeConflictComment: "Hi @{{PRAuthor}}! This PR has merge conflicts due to recent upstream merges.\nPlease help to unblock it by resolving these conflicts. Thanks!"
# label to monitor
mergeLabel: "action: merge"
# adding any of these labels will also add the merge label
mergeLinkedLabels:
- "action: merge-assistance"
# list of checks that will determine if the merge label can be added
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
noConflict: true
# list of labels that a PR needs to have, checked with a regexp (e.g. "target:" will work for the label "target: master")
requiredLabels:
- "target: *"
- "cla: yes"
# list of labels that a PR shouldn't have, checked after the required labels with a regexp
forbiddenLabels:
- "target: TBD"
- "action: cleanup"
- "action: review"
- "state: blocked"
- "cla: no"
# list of PR statuses that need to be successful
requiredStatuses:
- "ci/circleci: build"
- "ci/circleci: lint"
- "ci/circleci: publish_snapshot"
- "ci/angular: size"
- "cla/google"
- "google3"
- "pullapprove"
# 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
# {{PLACEHOLDER}} will be replaced by the list of failing checks
mergeRemovedComment: "I see that you just added the `{{MERGE_LABEL}}` label, but the following checks are still failing:\n{{PLACEHOLDER}}\n\n**If you want your PR to be merged, it has to pass all the CI checks.**\n\nIf you can't get the PR to a green state due to flakes or broken master, please try rebasing to master and/or restarting the CI job. If that fails and you believe that the issue is not due to your change, please contact the caretaker and ask for help."
# options for the triage plugin
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
defaultMilestone: 82,
# arrays of labels that determine if an issue has been triaged by the caretaker
l1TriageLabels:
-
- "comp: *"
# arrays of labels that determine if an issue has been fully triaged
l2TriageLabels:
-
- "P0"
- "comp: *"
-
- "P1"
- "comp: *"
-
- "P2"
- "comp: *"
-
- "P3"
- "comp: *"
-
- "P4"
- "comp: *"
-
- "P5"
- "comp: *"
-
- "feature"
- "comp: *"
-
- "discussion"
- "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: "action: rerun CI at HEAD"

View File

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

32
.gitignore vendored
View File

@ -1,53 +1,27 @@
.DS_STORE
/dist/
/bazel-out
/integration/bazel/bazel-*
*.log
node_modules
bower_components
# Include when developing application packages.
pubspec.lock
.c9
.idea/
.devcontainer/*
!.devcontainer/README.md
!.devcontainer/recommended-devcontainer.json
!.devcontainer/recommended-Dockerfile
.settings/
.vscode/launch.json
.vscode/settings.json
.vscode/tasks.json
*.swo
modules/.settings
.vscode
modules/.vscode
.vimrc
.nvimrc
# Don't check in secret files
*secret.js
# Ignore npm/yarn debug log
# Ignore npm debug log
npm-debug.log
yarn-error.log
# build-analytics
.build-analytics
# rollup-test output
/modules/rollup-test/dist/
# User specific bazel settings
.bazelrc.user
# User specific ng-dev settings
.ng-dev.user*
.notes.md
baseline.json
# Ignore .history for the xyz.local-history VSCode extension
.history
# CLDR data
tools/gulp-tasks/cldr/cldr-data/

View File

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

View File

@ -1 +0,0 @@
Michał Gołębiowski-Owczarek <m.goleb@gmail.com>

View File

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

View File

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

View File

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

View File

@ -1,22 +0,0 @@
import {FormatConfig} from '../dev-infra/format/config';
/**
* Configuration for the `ng-dev format` command.
*/
export const format: FormatConfig = {
'clang-format': {
'matchers': [
'**/*.{js,ts}',
// TODO: burn down format failures and remove aio and integration exceptions.
'!aio/**',
'!integration/**',
// Both third_party and .yarn are directories containing copied code which should
// not be modified.
'!third_party/**',
'!.yarn/**',
// Do not format d.ts files as they are generated
'!**/*.d.ts',
]
},
'buildifier': true
};

View File

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

View File

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

View File

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

View File

@ -1,33 +0,0 @@
import {join} from 'path';
import {exec} from 'shelljs';
import {ReleaseConfig} from '../dev-infra/release/config';
/** Configuration for the `ng-dev release` command. */
export const release: ReleaseConfig = {
npmPackages: [
'@angular/animations',
'@angular/bazel',
'@angular/common',
'@angular/compiler',
'@angular/compiler-cli',
'@angular/core',
'@angular/elements',
'@angular/forms',
'@angular/language-service',
'@angular/localize',
'@angular/platform-browser',
'@angular/platform-browser-dynamic',
'@angular/platform-server',
'@angular/platform-webworker',
'@angular/platform-webworker-dynamic',
'@angular/router',
'@angular/service-worker',
'@angular/upgrade',
],
// TODO: Implement release package building here.
buildPackages: async () => [],
// TODO: This can be removed once there is an org-wide tool for changelog generation.
generateReleaseNotesForHead: async () => {
exec('yarn -s gulp changelog', {cwd: join(__dirname, '../')});
},
};

2
.nvmrc
View File

@ -1 +1 @@
12.14.1
6.6.0

File diff suppressed because it is too large Load Diff

51
.travis.yml Normal file
View File

@ -0,0 +1,51 @@
language: node_js
sudo: false
node_js:
- '6.6.0'
addons:
# firefox: "38.0"
apt:
sources:
# needed to install g++ that is used by npms's native modules
- ubuntu-toolchain-r-test
packages:
- g++-4.8
branches:
except:
- g3_v2_0
cache:
directories:
- ./node_modules
- ./.chrome/chromium
env:
global:
# GITHUB_TOKEN_ANGULAR
# This is needed for the e2e Travis matrix task to publish packages to github for continuous packages delivery.
- secure: "fq/U7VDMWO8O8SnAQkdbkoSe2X92PVqg4d044HmRYVmcf6YbO48+xeGJ8yOk0pCBwl3ISO4Q2ot0x546kxfiYBuHkZetlngZxZCtQiFT9kyId8ZKcYdXaIW9OVdw3Gh3tQyUwDucfkVhqcs52D6NZjyE2aWZ4/d1V4kWRO/LMgo="
matrix:
# Order: a slower build first, so that we don't occupy an idle travis worker waiting for others to complete.
- CI_MODE=js
- CI_MODE=e2e
- CI_MODE=saucelabs_required
- CI_MODE=browserstack_required
- CI_MODE=saucelabs_optional
- CI_MODE=browserstack_optional
matrix:
fast_finish: true
allow_failures:
- env: "CI_MODE=saucelabs_optional"
- env: "CI_MODE=browserstack_optional"
install:
- ./scripts/ci-lite/install.sh
script:
- ./scripts/ci-lite/build.sh && ./scripts/ci-lite/test.sh
after_script:
- ./scripts/ci-lite/cleanup.sh

25
.vscode/README.md vendored
View File

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

View File

@ -1,15 +0,0 @@
{
// See 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",
// The following extensions are useful when working on angular.io (i.e. inside the `aio/` directory).
//"angular.ng-template",
//"dbaeumer.vscode-eslint",
],
}

View File

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

View File

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

View File

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

View File

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

File diff suppressed because one or more lines are too long

View File

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

View File

@ -1,55 +0,0 @@
package(default_visibility = ["//visibility:public"])
exports_files([
"LICENSE",
"karma-js.conf.js",
"browser-providers.conf.js",
"scripts/ci/track-payload-size.sh",
"scripts/ci/payload-size.sh",
"scripts/ci/payload-size.js",
"package.json",
])
alias(
name = "tsconfig.json",
actual = "//packages:tsconfig-build.json",
)
filegroup(
name = "web_test_bootstrap_scripts",
# do not sort
srcs = [
"@npm//:node_modules/core-js/client/core.js",
"//packages/zone.js/bundles:zone.umd.js",
"//packages/zone.js/bundles:zone-testing.umd.js",
"//packages/zone.js/bundles:task-tracking.umd.js",
"//:test-events.js",
"//:third_party/shims_for_IE.js",
# Including systemjs because it defines `__eval`, which produces correct stack traces.
"@npm//:node_modules/systemjs/dist/system.src.js",
"@npm//:node_modules/reflect-metadata/Reflect.js",
],
)
filegroup(
name = "angularjs_scripts",
srcs = [
# We also declare the unminified AngularJS files since these can be used for
# local debugging (e.g. see: packages/upgrade/test/common/test_helpers.ts)
"@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",
],
)
# Detect if the build is running under --stamp
config_setting(
name = "stamp",
values = {"stamp": "true"},
)

10788
CHANGELOG.md

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +0,0 @@
# Contributor Code of Conduct
## Version 0.3b-angular
As contributors and maintainers of the Angular project, we pledge to respect everyone who contributes by posting issues, updating documentation, submitting pull requests, providing feedback in comments, and any other activities.
Communication through any of Angular's channels (GitHub, Discord, Gitter, IRC, mailing lists, Twitter, etc.) must be constructive and never resort to personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
We promise to extend courtesy and respect to everyone involved in this project regardless of gender, gender identity, sexual orientation, disability, age, race, ethnicity, religion, or level of experience. We expect anyone contributing to the Angular project to do the same.
If any member of the community violates this code of conduct, the maintainers of the Angular project may take action, removing issues, comments, and PRs or blocking accounts as deemed appropriate.
If you are subject to or witness unacceptable behavior, or have any other concerns, please email us at [conduct@angular.io](mailto:conduct@angular.io).

34
COMMITTER.md Normal file
View File

@ -0,0 +1,34 @@
# Pushing changes into the Angular 2 tree
Please see [Using git with Angular repositories](https://docs.google.com/document/d/1h8nijFSaa1jG_UE8v4WP7glh5qOUXnYtAtJh_gwOQHI/edit)
for details about how we maintain a linear commit history, and the rules for committing.
As a contributor, just read the instructions in [CONTRIBUTING.md](CONTRIBUTING.md) and send a pull request.
Someone with committer access will do the rest.
## The `PR: merge` label and `presubmit-*` branches
We have automated the process for merging pull requests into master. Our goal is to minimize the disruption for
Angular committers and also prevent breakages on master.
When a PR has `pr_state: LGTM` and is ready to merge, you should add the `pr_action: merge` label.
Currently (late 2015), we need to ensure that each PR will cleanly merge into the Google-internal version control,
so the caretaker reviews the changes manually.
After this review, the caretaker adds `zomg_admin: do_merge` which is restricted to admins only.
A robot running as [mary-poppins](https://github.com/mary-poppins)
is notified that the label was added by an authorized person,
and will create a new branch in the angular project, using the convention `presubmit-{username}-pr-{number}`.
(Note: if the automation fails, committers can instead push the commits to a branch following this naming scheme.)
When a Travis build succeeds for a presubmit branch named following the convention,
Travis will re-base the commits, merge to master, and close the PR automatically.
Finally, after merge `mary-poppins` removes the presubmit branch.
## Administration
The list of users who can trigger a merge by adding the `zomg_admin: do_merge` label is stored in our appengine app datastore.
Edit the contents of the [CoreTeamMember Table](
https://console.developers.google.com/project/angular2-automation/datastore/query?queryType=KindQuery&namespace=&kind=CoreTeamMember)

View File

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

140
DEVELOPER.md Normal file
View File

@ -0,0 +1,140 @@
# Building and Testing Angular 2 for JS
This document describes how to set up your development environment to build and test Angular 2 JS version.
It also explains the basic mechanics of using `git`, `node`, and `npm`.
* [Prerequisite Software](#prerequisite-software)
* [Getting the Sources](#getting-the-sources)
* [Installing NPM Modules](#installing-npm-modules)
* [Building](#building)
* [Running Tests Locally](#running-tests-locally)
See the [contribution guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md)
if you'd like to contribute to Angular.
## Prerequisite Software
Before you can build and test Angular, you must install and configure the
following products on your development machine:
* [Git](http://git-scm.com) and/or the **GitHub app** (for [Mac](http://mac.github.com) or
[Windows](http://windows.github.com)); [GitHub's Guide to Installing
Git](https://help.github.com/articles/set-up-git) is a good source of information.
* [Node.js](http://nodejs.org), (version `>=5.4.1 <6`) which is used to run a development web server,
run tests, and generate distributable files. We also use Node's Package Manager, `npm`
(version `>=3.5.3 <4.0`), which comes with Node. Depending on your system, you can install Node either from
source or as a pre-packaged bundle.
* [Java Development Kit](http://www.oracle.com/technetwork/es/java/javase/downloads/index.html) which is used
to execute the selenium standalone server for e2e testing.
## Getting the Sources
Fork and clone the Angular repository:
1. Login to your GitHub account or create one by following the instructions given
[here](https://github.com/signup/free).
2. [Fork](http://help.github.com/forking) the [main Angular
repository](https://github.com/angular/angular).
3. Clone your fork of the Angular repository and define an `upstream` remote pointing back to
the Angular repository that you forked in the first place.
```shell
# Clone your GitHub repository:
git clone git@github.com:<github username>/angular.git
# Go to the Angular directory:
cd angular
# Add the main Angular repository as an upstream remote to your repository:
git remote add upstream https://github.com/angular/angular.git
```
## Installing NPM Modules
Next, install the JavaScript modules needed to build and test Angular:
```shell
# Install Angular project dependencies (package.json)
npm install
```
**Optional**: In this document, we make use of project local `npm` package scripts and binaries
(stored under `./node_modules/.bin`) by prefixing these command invocations with `$(npm bin)`; in
particular `gulp` and `protractor` commands. If you prefer, you can drop this path prefix by either:
*Option 1*: globally installing these two packages as follows:
* `npm install -g gulp` (you might need to prefix this command with `sudo`)
* `npm install -g protractor` (you might need to prefix this command with `sudo`)
Since global installs can become stale, and required versions can vary by project, we avoid their
use in these instructions.
*Option 2*: defining a bash alias like `alias nbin='PATH=$(npm bin):$PATH'` as detailed in this
[Stackoverflow answer](http://stackoverflow.com/questions/9679932/how-to-use-package-installed-locally-in-node-modules/15157360#15157360) and used like this: e.g., `nbin gulp build`.
## Windows only
In order to create the right symlinks, run **as administrator**:
```shell
./scripts/windows/create-symlinks.sh
```
Before submitting a PR, do not forget to remove them:
```shell
./scripts/windows/remove-symlinks.sh
```
## Building
To build Angular run:
```shell
./build.sh
```
* Results are put in the dist folder.
## Running Tests Locally
To run tests:
```shell
$ ./test.sh node # Run all angular tests on node
$ ./test.sh browser # Run all angular tests in browser
$ ./test.sh browserNoRouter # Optionally run all angular tests without router in browser
$ ./test.sh tools # Run angular tooling (not framework) tests
```
You should execute the 3 test suites before submitting a PR to github.
All the tests are executed on our Continuous Integration infrastructure and a PR could only be merged once the tests pass.
- CircleCI fails if your code is not formatted properly,
- Travis CI fails if any of the test suite describe above fails.
## Update the public API tests
If you happen to modify the public API of Angular, API golden files must be updated using:
``` shell
$ gulp public-api:update
```
Note: The command `./test.sh tools` fails when the API doesn't match the golden files.
## Formatting your source code
Angular uses [clang-format](http://clang.llvm.org/docs/ClangFormat.html) to format the source code. If the source code
is not properly formatted, the CI will fail and the PR can not be merged.
You can automatically format your code by running:
``` shell
$ gulp format
```

View File

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

View File

@ -1,13 +1,13 @@
Naming Conventions in Angular
Naming Conventions in Angular2
---
In general Angular should follow TypeScript naming conventions.
In general Angular2 should follow TypeScript naming conventions.
See: https://github.com/Microsoft/TypeScript/wiki/Coding-guidelines
Classes:
- Example: `Compiler`, `ApplicationMetadata`
- Camel case with first letter uppercase
- Camel case with first letter upper-case
- In general prefer single words. (This is so that when appending `Proto` or `Factory` the class
is still reasonable to work with.)
- Should not end with `Impl` or any other word which describes a specific implementation of an
@ -21,10 +21,10 @@ Interfaces:
Methods and functions:
- Example: `bootstrap`, `someMethod`
- Should be camel case with first letter lowercase
- Should be camel case with first lower case
Constants:
Constants
- Example: `CORE_DIRECTIVES`
- Should be all uppercase with SNAKE_CASE

165
README.md
View File

@ -1,151 +1,38 @@
<h1 align="center">Angular - One framework. Mobile & desktop.</h1>
[![Build Status](https://travis-ci.org/angular/angular.svg?branch=master)](https://travis-ci.org/angular/angular)
[![CircleCI](https://circleci.com/gh/angular/angular/tree/master.svg?style=shield)](https://circleci.com/gh/angular/angular/tree/master)
[![Join the chat at https://gitter.im/angular/angular](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/angular/angular?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Issue Stats](http://issuestats.com/github/angular/angular/badge/pr?style=flat)](http://issuestats.com/github/angular/angular)
[![Issue Stats](http://issuestats.com/github/angular/angular/badge/issue?style=flat)](http://issuestats.com/github/angular/angular)
[![npm version](https://badge.fury.io/js/%40angular%2Fcore.svg)](https://badge.fury.io/js/%40angular%2Fcore)
[![Downloads](http://img.shields.io/npm/dm/angular2.svg)](https://npmjs.org/package/angular2)
<p align="center">
<img src="aio/src/assets/images/logos/angular/angular.png" alt="angular-logo" width="120px" height="120px"/>
<br>
<i>Angular is a development platform for building mobile and desktop web applications
<br> using Typescript/JavaScript and other languages.</i>
<br>
</p>
[![Sauce Test Status](https://saucelabs.com/browser-matrix/angular2-ci.svg)](https://saucelabs.com/u/angular2-ci)
*Safari (7+), iOS (7+), Edge (14) and IE mobile (11) are tested on [BrowserStack][browserstack].*
<p align="center">
<a href="https://www.angular.io"><strong>www.angular.io</strong></a>
<br>
</p>
Angular
=========
<p align="center">
<a href="CONTRIBUTING.md">Contributing Guidelines</a>
·
<a href="https://github.com/angular/angular/issues">Submit an Issue</a>
·
<a href="https://blog.angular.io/">Blog</a>
<br>
<br>
</p>
Angular is a development platform for building mobile and desktop web applications. This is the
repository for [Angular 2][ng2] Typescript/JavaScript (JS).
Angular2 for [Dart][dart] can be found at [dart-lang/angular2][ng2dart].
<p align="center">
<a href="https://circleci.com/gh/angular/workflows/angular/tree/master">
<img src="https://img.shields.io/circleci/build/github/angular/angular/master.svg?logo=circleci&logoColor=fff&label=CircleCI" alt="CI status" />
</a>&nbsp;
<a href="https://www.npmjs.com/@angular/core">
<img src="https://img.shields.io/npm/v/@angular/core.svg?logo=npm&logoColor=fff&label=NPM+package&color=limegreen" alt="Angular on npm" />
</a>&nbsp;
<a href="https://discord.gg/angular">
<img src="https://img.shields.io/discord/463752820026376202.svg?logo=discord&logoColor=fff&label=Discord&color=7389d8" alt="Discord conversation" />
</a>
</p>
<hr>
## Documentation
Get started with Angular, learn the fundamentals and explore advanced topics on our documentation website.
- [Getting Started][quickstart]
- [Architecture][architecture]
- [Components and Templates][componentstemplates]
- [Forms][forms]
- [API][api]
### Advanced
- [Angular Elements][angularelements]
- [Server Side Rendering][ssr]
- [Schematics][schematics]
- [Lazy Loading][lazyloading]
## Development Setup
### Prerequisites
- Install [Node.js] which includes [Node Package Manager][npm]
### Setting Up a Project
Intall the Angular CLI globally:
```
npm install -g @angular/cli
```
Create workspace:
```
ng new [PROJECT NAME]
```
Run the application:
```
cd [PROJECT NAME]
ng serve
```
## Quickstart
[Get started in 5 minutes][quickstart].
## Ecosystem
<p>
<img src="/docs/images/angular-ecosystem-logos.png" alt="angular ecosystem logos" width="500px" height="auto">
</p>
## Want to help?
- [Angular Command Line (CLI)][cli]
- [Angular Material][angularmaterial]
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).
## Changelog
[Learn about the latest improvements][changelog].
## Upgrading
Check out our [upgrade guide](https://update.angular.io/) to find out the best way to upgrade your project.
## Contributing
### Contributing Guidelines
Read through our [contributing guidelines][contributing] to learn about our submission process, coding rules and more.
### Want to Help?
Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on our guidelines for [contributing][contributing] and then check out one of our issues in the [hotlist: community-help](https://github.com/angular/angular/labels/hotlist%3A%20community-help).
### Code of Conduct
Help us keep Angular open and inclusive. Please read and follow our [Code of Conduct][codeofconduct].
## Community
Join the conversation and help the community.
- [Twitter][twitter]
- [Gitter][gitter]
- Find a Local [Meetup][meetup]
[![Love Angular badge](https://img.shields.io/badge/angular-love-blue?logo=angular&angular=love)](https://www.github.com/angular/angular)
**Love Angular? Give our repo a star :star: :arrow_up:.**
[contributing]: CONTRIBUTING.md
[quickstart]: https://angular.io/start
[changelog]: CHANGELOG.md
[ng]: https://angular.io
[documentation]: https://angular.io/docs
[angularmaterial]: https://material.angular.io/
[cli]: https://cli.angular.io/
[architecture]: https://angular.io/guide/architecture
[componentstemplates]: https://angular.io/guide/displaying-data
[forms]: https://angular.io/guide/forms-overview
[api]: https://angular.io/api
[angularelements]: https://angular.io/guide/elements
[ssr]: https://angular.io/guide/universal
[schematics]: https://angular.io/guide/schematics
[lazyloading]: https://angular.io/guide/lazy-loading-ngmodules
[node.js]: https://nodejs.org/
[npm]: https://www.npmjs.com/get-npm
[codeofconduct]: CODE_OF_CONDUCT.md
[twitter]: https://www.twitter.com/angular
[gitter]: https://gitter.im/angular/angular
[meetup]: https://www.meetup.com/find/?keywords=angular"
[browserstack]: https://www.browserstack.com/
[contributing]: http://github.com/angular/angular/blob/master/CONTRIBUTING.md
[dart]: http://www.dartlang.org
[quickstart]: https://angular.io/docs/ts/latest/quickstart.html
[ng2]: http://angular.io
[ngDart]: http://angulardart.org
[ngJS]: http://angularjs.org
[ng2dart]: https://github.com/dart-lang/angular2

View File

@ -1,4 +1,4 @@
# Developer Tools for Angular
# Developer Tools for Angular 2
Here you will find a collection of tools and tips for keeping your application
perform well and contain fewer bugs.
@ -14,17 +14,11 @@ Ctrl + Shift + j.
By default the debug tools are disabled. You can enable debug tools as follows:
```typescript
import {ApplicationRef} from '@angular/core';
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {enableDebugTools} from '@angular/platform-browser';
platformBrowserDynamic().bootstrapModule(AppModule)
.then(moduleRef => {
const applicationRef = moduleRef.injector.get(ApplicationRef);
const appComponent = applicationRef.components[0];
enableDebugTools(appComponent);
})
bootstrap(Application).then((appRef) => {
enableDebugTools(appRef);
});
```
### Using debug tools
@ -43,7 +37,7 @@ ng.profiler.timeChangeDetection();
### Change detection profiler
If your application is janky (it misses frames) or is slow according to other
metrics, it is important to find the root cause of the issue. Change detection
metrics it is important to find the root cause of the issue. Change detection
is a phase in Angular's lifecycle that detects changes in values that are
bound to UI, and if it finds a change it performs the corresponding UI update.
However, sometimes it is hard to tell if the slowness is due to the act of
@ -51,7 +45,7 @@ computing the changes being slow, or due to the act of applying those changes
to the UI. For your application to be performant it is important that the
process of computing changes is very fast. For best results it should be under
3 milliseconds in order to leave room for the application logic, the UI updates
and browser's rendering pipeline to fit within the 16 millisecond frame
and browser's rendering pipeline to fit withing the 16 millisecond frame
(assuming the 60 FPS target frame rate).
Change detection profiler repeatedly performs change detection without invoking

131
TRIAGE_AND_LABELS.md Normal file
View File

@ -0,0 +1,131 @@
# Triage Process and Github Labels for Angular 2
This document describes how the Angular team uses labels and milestones
to triage issues on github. The basic idea of the new process is that
caretaker only assigns a component and type (bug, feature) label. The
owner of the component than is in full control of how the issues should
be triaged further.
Once this process is implemented and in use, we will revisit it to see
if further labeling is needed.
## Components
A caretaker should be able to determine which component the issue
belongs to. The components have a clear piece of source code associated
with it.
* `comp: animations`: `@matsko`
* `comp: benchpress`: `@tbosch`
* `comp: build/ci`: `@IgorMinar` -- All build and CI scripts
* `comp: common`: `@mhevery` -- This includes core components / pipes.
* `comp: core/compiler`: `@tbosch` -- Because core and compiler are very
intertwined, we will be treating them as one.
* `comp: forms`: `@kara`
* `comp: http`: `@jeffbcross`
* `comp: i18n`: `@vicb`
* `comp: metadata-extractor`: `@chuckjaz`
* `comp: router`: `@vsavkin`
* `comp: testing`: `@juliemr`
* `comp: upgrade`: `@mhevery`
* `comp: web-worker`: `@vicb`
* `comp: zone`: `@mhevery`
There are few components which are cross-cutting. They don't have
a clear location in the source tree. We will treat them as a component
even thought no specific source tree is associated with them.
* `comp: documentation`: `@naomiblack`
* `comp: packaging`: `@mhevery`
* `comp: performance`: `@tbosch`
* `comp: security`: `@IgorMinar`
## Type
What kind of problem is this?
* `type: RFC / discussion / question`
* `type: bug`
* `type: chore`
* `type: feature`
* `type: performance`
* `type: refactor`
## Caretaker Triage Process
It is the caretaker's responsibility to assign `comp: *` and `type: *`
to each new issue as they come in. The reason why we limit the
responsibility of the caretaker to these two labels is that it is
unlikely that without domain knowledge the caretaker could add any
additional labels of value.
## Component's owner Triage Process
At this point we are leaving each component owner to determine their own
process for their component.
It will be up to the component owner to determine the order in which the
issues within the component will be resolved.
### Assigning Issues to Milestones
Any issue that is being worked on must have:
* An `assignee`: The person doing the work.
* A `Milestone`: When we expect to complete this work.
We aim to only have at most three milestones open at a time:
* Closing Milestone: A milestone with a very small number of issues, about to release.
* Current Milestone: Work that we plan to complete within one week.
* Next Milestone: Work that is > 1 week but current for the team.
The [backlog](https://github.com/angular/angular/issues?q=is%3Aopen+is%3Aissue+no%3Amilestone)
consists of all issues that have been triaged but do not have an assignee or milestone.
## Triaged vs Untrained PRs
PRs should also be label with a `comp: *` so that it is clear which
primary area the PR effects.
Because of the cumulative pain associated with rebasing PRs, we triage PRs daily, and
closing or reviewing PRs is a top priority ahead of other ongoing work.
Every triaged PR must have a `pr_action` label assigned to it and an assignee:
* `pr_action: review` -- work is complete and comment is needed from the assignee.
* `pr_action: cleanup` -- more work is needed from the current assignee.
* `pr_action: discuss` -- discussion is needed, to be led by the current assignee.
* `pr_action: merge` -- the PR should be merged. Add this to a PR when you would like to
trigger automatic merging following a successful build. This is described in [COMMITTER.md](COMMITTER.md).
In addition, PRs can have the following states:
* `pr_state: LGTM` -- PR may have outstanding changes but does not require further review.
* `pr_state: WIP` -- PR is experimental or rapidly changing. Not ready for review or triage.
* `pr_state: blocked` -- PR is blocked on an issue or other PR. Not ready for review or triage.
Note that an LGTM state does not mean a PR is ready to merge: for example, a reviewer might set the
LGTM state but request a minor tweak that doesn't need further review, e.g., a rebase or small
uncontroversial change.
PRs do not need to be assigned to milestones, unless a milestone release should be held for that
PR to land.
## Special Labels
### action:design
More active discussion is needed before the issue can be worked on further. Typically used for
`type: feature` or `type: RFC/discussion/question`
[See all issues that need discussion](https://github.com/angular/angular/labels/action:%20Design)
### cla
Managed by googlebot. Indicates whether a PR has a CLA on file for its author(s). Only issues with
`cla:yes` should be merged into master.
### WORKS_AS_INTENDED
Only used on closed issues, to indicate to the reporter why we closed it.

View File

@ -1,98 +0,0 @@
workspace(
name = "angular",
managed_directories = {"@npm": ["node_modules"]},
)
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
# Fetch rules_nodejs so we can install our npm dependencies
http_archive(
name = "build_bazel_rules_nodejs",
sha256 = "4952ef879704ab4ad6729a29007e7094aef213ea79e9f2e94cbe1c9a753e63ef",
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/2.2.0/rules_nodejs-2.2.0.tar.gz"],
)
# Check the rules_nodejs version and download npm dependencies
# Note: bazel (version 2 and after) will check the .bazelversion file so we don't need to
# assert on that.
load("@build_bazel_rules_nodejs//:index.bzl", "check_rules_nodejs_version", "node_repositories", "yarn_install")
check_rules_nodejs_version(minimum_version_string = "2.2.0")
# Setup the Node.js toolchain
node_repositories(
node_repositories = {
"12.14.1-darwin_amd64": ("node-v12.14.1-darwin-x64.tar.gz", "node-v12.14.1-darwin-x64", "0be10a28737527a1e5e3784d3ad844d742fe8b0718acd701fd48f718fd3af78f"),
"12.14.1-linux_amd64": ("node-v12.14.1-linux-x64.tar.xz", "node-v12.14.1-linux-x64", "07cfcaa0aa9d0fcb6e99725408d9e0b07be03b844701588e3ab5dbc395b98e1b"),
"12.14.1-windows_amd64": ("node-v12.14.1-win-x64.zip", "node-v12.14.1-win-x64", "1f96ccce3ba045ecea3f458e189500adb90b8bc1a34de5d82fc10a5bf66ce7e3"),
},
node_version = "12.14.1",
package_json = ["//:package.json"],
)
load("//integration:angular_integration_test.bzl", "npm_package_archives")
yarn_install(
name = "npm",
manual_build_file_contents = npm_package_archives(),
package_json = "//:package.json",
yarn_lock = "//:yarn.lock",
)
# Load angular dependencies
load("//packages/bazel:package.bzl", "rules_angular_dev_dependencies")
rules_angular_dev_dependencies()
# Load protractor dependencies
load("@npm//@bazel/protractor:package.bzl", "npm_bazel_protractor_dependencies")
npm_bazel_protractor_dependencies()
# Load karma dependencies
load("@npm//@bazel/karma:package.bzl", "npm_bazel_karma_dependencies")
npm_bazel_karma_dependencies()
# Setup the rules_webtesting toolchain
load("@io_bazel_rules_webtesting//web:repositories.bzl", "web_test_repositories")
web_test_repositories()
load("//dev-infra/browsers:browser_repositories.bzl", "browser_repositories")
browser_repositories()
# Setup the rules_sass toolchain
load("@io_bazel_rules_sass//sass:sass_repositories.bzl", "sass_repositories")
sass_repositories()
# Setup the skydoc toolchain
load("@io_bazel_skydoc//skylark:skylark.bzl", "skydoc_repositories")
skydoc_repositories()
load("@bazel_toolchains//rules:environments.bzl", "clang_env")
load("@bazel_toolchains//rules:rbe_repo.bzl", "rbe_autoconfig")
rbe_autoconfig(
name = "rbe_ubuntu1604_angular",
# Need to specify a base container digest in order to ensure that we can use the checked-in
# platform configurations for the "ubuntu16_04" image. Otherwise the autoconfig rule would
# need to pull the image and run it in order determine the toolchain configuration. See:
# https://github.com/bazelbuild/bazel-toolchains/blob/3.5.1/configs/ubuntu16_04_clang/versions.bzl
base_container_digest = "sha256:f6568d8168b14aafd1b707019927a63c2d37113a03bcee188218f99bd0327ea1",
# Note that if you change the `digest`, you might also need to update the
# `base_container_digest` to make sure marketplace.gcr.io/google/rbe-ubuntu16-04-webtest:<digest>
# and marketplace.gcr.io/google/rbe-ubuntu16-04:<base_container_digest> have
# the same Clang and JDK installed. Clang is needed because of the dependency on
# @com_google_protobuf. Java is needed for the Bazel's test executor Java tool.
digest = "sha256:f743114235a43355bf8324e2ba0fa6a597236fe06f7bc99aaa9ac703631c306b",
env = clang_env(),
registry = "marketplace.gcr.io",
# We can't use the default "ubuntu16_04" RBE image provided by the autoconfig because we need
# a specific Linux kernel that comes with "libx11" in order to run headless browser tests.
repository = "google/rbe-ubuntu16-04-webtest",
use_checked_in_confs = "Force",
)

View File

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

48
aio/.gitignore vendored
View File

@ -1,48 +0,0 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/out-tsc
/src/generated
/tmp
# dependencies
/node_modules
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
# misc
/.firebase/
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
debug.log
firebase-debug.log
npm-debug.log
testem.log
/typings
yarn-error.log
# e2e
/e2e/*.js
/e2e/*.map
protractor-results*.txt
# System Files
.DS_Store
Thumbs.db

View File

@ -1,142 +0,0 @@
# Angular documentation project (https://angular.io)
Everything in this folder is part of the documentation project. This includes
* the web site for displaying the documentation
* the dgeni configuration for converting source files to rendered files that can be viewed in the web site.
* the tooling for setting up examples for development; and generating live-example and zip files from the examples.
<a name="developer-tasks"></a>
## Developer tasks
We use [Yarn](https://yarnpkg.com) to manage the dependencies and to run build tasks.
You should run all these tasks from the `angular/aio` folder.
Here are the most important tasks you might need to use:
* `yarn` - install all the dependencies.
* `yarn setup` - install all the dependencies, boilerplate, stackblitz, zips and run dgeni on the docs.
* `yarn setup-local` - same as `setup`, but build the Angular packages from the source code and use these locally built versions (instead of the ones fetched from npm) for aio and docs examples boilerplate.
* `yarn build` - create a production build of the application (after installing dependencies, boilerplate, etc).
* `yarn build-local` - same as `build`, but use `setup-local` instead of `setup`.
* `yarn build-local-with-viewengine` - same as `build-local`, but in addition also turns on `ViewEngine` (pre-Ivy) mode in aio.
(Note: To turn on `ViewEngine` mode in docs examples, see `yarn boilerplate:add:viewengine` below.)
* `yarn start` - run a development web server that watches the files; then builds the doc-viewer and reloads the page, as necessary.
* `yarn serve-and-sync` - run both the `docs-watch` and `start` in the same console.
* `yarn 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=false` - run all the unit tests once.
* `yarn e2e` - run all the e2e tests for the doc-viewer.
* `yarn docs` - generate all the docs from the source files.
* `yarn docs-watch` - watch the Angular source and the docs files and run a short-circuited doc-gen for the docs that changed.
* `yarn docs-lint` - check that the doc gen code follows our style rules.
* `yarn docs-test` - run the unit tests for the doc generation code.
* `yarn boilerplate:add` - generate all the boilerplate code for the examples, so that they can be run locally.
* `yarn boilerplate:add:viewengine` - same as `boilerplate:add` but also turns on `ViewEngine` (pre-Ivy) mode.
* `yarn boilerplate:remove` - remove all the boilerplate code that was added via `yarn boilerplate:add`.
* `yarn create-example` - create a new example directory containing initial source files.
* `yarn generate-stackblitz` - generate the stackblitz files that are used by the `live-example` tags in the docs.
* `yarn generate-zips` - generate the zip files from the examples. Zip available via the `live-example` tags in the docs.
* `yarn example-e2e` - run all e2e tests for examples. Available options:
- `--setup`: generate boilerplate, force webdriver update & other setup, then run tests.
- `--local`: run e2e tests with the local version of Angular contained in the "dist" folder.
_Requires `--setup` in order to take effect._
- `--viewengine`: run e2e tests in `ViewEngine` (pre-Ivy) mode.
- `--filter=foo`: limit e2e tests to those containing the word "foo".
> **Note for Windows users**
>
> Setting up the examples involves creating some [symbolic links](https://en.wikipedia.org/wiki/Symbolic_link) (see [here](./tools/examples/README.md#symlinked-node_modules) for details). On Windows, this requires to either have [Developer Mode enabled](https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10) (supported on Windows 10 or newer) or run the setup commands as administrator.
>
> The affected commands are:
> - `yarn setup` / `yarn setup-*`
> - `yarn build` / `yarn build-*`
> - `yarn boilerplate:add`
> - `yarn example-e2e --setup`
## Using ServiceWorker locally
Running `yarn start` (even when explicitly targeting production mode) does not set up the
ServiceWorker. If you want to test the ServiceWorker locally, you can use `yarn build` and then
serve the files in `dist/` with `yarn http-server dist -p 4200`.
## Guide to authoring
There are two types of content in the documentation:
* **API docs**: descriptions of the modules, classes, interfaces, decorators, etc that make up the Angular platform.
API docs are generated directly from the source code.
The source code is contained in TypeScript files, located in the `angular/packages` folder.
Each API item may have a preceding comment, which contains JSDoc style tags and content.
The content is written in markdown.
* **Other content**: guides, tutorials, and other marketing material.
All other content is written using markdown in text files, located in the `angular/aio/content` folder.
More specifically, there are sub-folders that contain particular types of content: guides, tutorial and marketing.
* **Code examples**: code examples need to be testable to ensure their accuracy.
Also, our examples have a specific look and feel and allow the user to copy the source code. For larger
examples they are rendered in a tabbed interface (e.g. template, HTML, and TypeScript on separate
tabs). Additionally, some are live examples, which provide links where the code can be edited, executed, and/or downloaded. For details on working with code examples, please read the [Code snippets](https://angular.io/guide/docs-style-guide#code-snippets), [Source code markup](https://angular.io/guide/docs-style-guide#source-code-markup), and [Live examples](https://angular.io/guide/docs-style-guide#live-examples) pages of the [Authors Style Guide](https://angular.io/guide/docs-style-guide).
We use the [dgeni](https://github.com/angular/dgeni) tool to convert these files into docs that can be viewed in the doc-viewer.
The [Authors Style Guide](https://angular.io/guide/docs-style-guide) prescribes guidelines for
writing guide pages, explains how to use the documentation classes and components, and how to markup sample source code to produce code snippets.
### Generating the complete docs
The main task for generating the docs is `yarn docs`. This will process all the source files (API and other),
extracting the documentation and generating JSON files that can be consumed by the doc-viewer.
### Partial doc generation for editors
Full doc generation can take up to one minute. That's too slow for efficient document creation and editing.
You can make small changes in a smart editor that displays formatted markdown:
>In VS Code, _Cmd-K, V_ opens markdown preview in side pane; _Cmd-B_ toggles left sidebar
You also want to see those changes displayed properly in the doc viewer
with a quick, edit/view cycle time.
For this purpose, use the `yarn docs-watch` task, which watches for changes to source files and only
re-processes the files necessary to generate the docs that are related to the file that has changed.
Since this task takes shortcuts, it is much faster (often less than 1 second) but it won't produce full
fidelity content. For example, links to other docs and code examples may not render correctly. This is
most particularly noticed in links to other docs and in the embedded examples, which may not always render
correctly.
The general setup is as follows:
* Open a terminal, ensure the dependencies are installed; run an initial doc generation; then start the doc-viewer:
```bash
yarn setup
yarn start
```
* Open a second terminal and start watching the docs
```bash
yarn docs-watch
```
>Alternatively, try the consolidated `serve-and-sync` command that builds, watches and serves in the same terminal window
```bash
yarn serve-and-sync
```
* Open a browser at https://localhost:4200/ and navigate to the document on which you want to work.
You can automatically open the browser by using `yarn start -o` in the first terminal.
* Make changes to the page's associated doc or example files. Every time a file is saved, the doc will
be regenerated, the app will rebuild and the page will reload.
* If you get a build error complaining about examples or any other odd behavior, be sure to consult
the [Authors Style Guide](https://angular.io/guide/docs-style-guide).

View File

@ -1,3 +0,0 @@
scripts-js/lib
scripts-js/node_modules
scripts-js/**/test

View File

@ -1,179 +0,0 @@
# Image metadata and config
FROM debian:buster
LABEL name="angular.io PR preview" \
description="This image implements the PR preview functionality for angular.io." \
vendor="Angular" \
version="1.0"
VOLUME /aio-secrets
VOLUME /var/www/aio-builds
VOLUME /dockerbuild
EXPOSE 80 443
# Build-time args and env vars
# The AIO_ARTIFACT_PATH path needs to be kept in synch with the value of
# `aio_preview->steps->store_artifacts->destination` property in `.circleci/config.yml`
ARG AIO_ARTIFACT_PATH=aio/dist/aio-snapshot.tgz
ARG TEST_AIO_ARTIFACT_PATH=$AIO_ARTIFACT_PATH
ARG AIO_BUILDS_DIR=/var/www/aio-builds
ARG TEST_AIO_BUILDS_DIR=/tmp/aio-builds
ARG AIO_DOMAIN_NAME=ngbuilds.io
ARG TEST_AIO_DOMAIN_NAME=$AIO_DOMAIN_NAME.localhost
ARG AIO_GITHUB_ORGANIZATION=angular
ARG TEST_AIO_GITHUB_ORGANIZATION=test-org
ARG AIO_GITHUB_REPO=angular
ARG TEST_AIO_GITHUB_REPO=test-repo
ARG AIO_GITHUB_TEAM_SLUGS=aio-auto-previews,aio-contributors
ARG TEST_AIO_GITHUB_TEAM_SLUGS=test-team-1,test-team-2
ARG AIO_NGINX_HOSTNAME=$AIO_DOMAIN_NAME
ARG TEST_AIO_NGINX_HOSTNAME=$TEST_AIO_DOMAIN_NAME
ARG AIO_NGINX_PORT_HTTP=80
ARG TEST_AIO_NGINX_PORT_HTTP=8080
ARG AIO_NGINX_PORT_HTTPS=443
ARG TEST_AIO_NGINX_PORT_HTTPS=4433
ARG AIO_SIGNIFICANT_FILES_PATTERN='^(?:aio|packages)/(?!.*[._]spec\\.[jt]s$)'
ARG TEST_AIO_SIGNIFICANT_FILES_PATTERN=$AIO_SIGNIFICANT_FILES_PATTERN
ARG AIO_TRUSTED_PR_LABEL="aio: preview"
ARG TEST_AIO_TRUSTED_PR_LABEL=$AIO_TRUSTED_PR_LABEL
ARG AIO_PREVIEW_SERVER_HOSTNAME=preview.localhost
ARG TEST_AIO_PREVIEW_SERVER_HOSTNAME=$AIO_PREVIEW_SERVER_HOSTNAME
ARG AIO_ARTIFACT_MAX_SIZE=26214400
ARG TEST_AIO_ARTIFACT_MAX_SIZE=200
ARG AIO_PREVIEW_SERVER_PORT=3000
ARG TEST_AIO_PREVIEW_SERVER_PORT=3001
ENV AIO_ARTIFACT_PATH=$AIO_ARTIFACT_PATH TEST_AIO_ARTIFACT_PATH=$TEST_AIO_ARTIFACT_PATH \
AIO_BUILDS_DIR=$AIO_BUILDS_DIR TEST_AIO_BUILDS_DIR=$TEST_AIO_BUILDS_DIR \
AIO_DOMAIN_NAME=$AIO_DOMAIN_NAME TEST_AIO_DOMAIN_NAME=$TEST_AIO_DOMAIN_NAME \
AIO_GITHUB_ORGANIZATION=$AIO_GITHUB_ORGANIZATION TEST_AIO_GITHUB_ORGANIZATION=$TEST_AIO_GITHUB_ORGANIZATION \
AIO_GITHUB_REPO=$AIO_GITHUB_REPO TEST_AIO_GITHUB_REPO=$TEST_AIO_GITHUB_REPO \
AIO_GITHUB_TEAM_SLUGS=$AIO_GITHUB_TEAM_SLUGS TEST_AIO_GITHUB_TEAM_SLUGS=$TEST_AIO_GITHUB_TEAM_SLUGS \
AIO_LOCALCERTS_DIR=/etc/ssl/localcerts TEST_AIO_LOCALCERTS_DIR=/etc/ssl/localcerts-test \
AIO_NGINX_HOSTNAME=$AIO_NGINX_HOSTNAME TEST_AIO_NGINX_HOSTNAME=$TEST_AIO_NGINX_HOSTNAME \
AIO_NGINX_LOGS_DIR=/var/log/aio/nginx TEST_AIO_NGINX_LOGS_DIR=/var/log/aio/nginx-test \
AIO_NGINX_PORT_HTTP=$AIO_NGINX_PORT_HTTP TEST_AIO_NGINX_PORT_HTTP=$TEST_AIO_NGINX_PORT_HTTP \
AIO_NGINX_PORT_HTTPS=$AIO_NGINX_PORT_HTTPS TEST_AIO_NGINX_PORT_HTTPS=$TEST_AIO_NGINX_PORT_HTTPS \
AIO_SCRIPTS_JS_DIR=/usr/share/aio-scripts-js \
AIO_SCRIPTS_SH_DIR=/usr/share/aio-scripts-sh \
AIO_SIGNIFICANT_FILES_PATTERN=$AIO_SIGNIFICANT_FILES_PATTERN TEST_AIO_SIGNIFICANT_FILES_PATTERN=$TEST_AIO_SIGNIFICANT_FILES_PATTERN \
AIO_TRUSTED_PR_LABEL=$AIO_TRUSTED_PR_LABEL TEST_AIO_TRUSTED_PR_LABEL=$TEST_AIO_TRUSTED_PR_LABEL \
AIO_PREVIEW_SERVER_HOSTNAME=$AIO_PREVIEW_SERVER_HOSTNAME TEST_AIO_PREVIEW_SERVER_HOSTNAME=$TEST_AIO_PREVIEW_SERVER_HOSTNAME \
AIO_ARTIFACT_MAX_SIZE=$AIO_ARTIFACT_MAX_SIZE TEST_AIO_ARTIFACT_MAX_SIZE=$TEST_AIO_ARTIFACT_MAX_SIZE \
AIO_PREVIEW_SERVER_PORT=$AIO_PREVIEW_SERVER_PORT TEST_AIO_PREVIEW_SERVER_PORT=$TEST_AIO_PREVIEW_SERVER_PORT \
AIO_WWW_USER=www-data \
NODE_ENV=production
# Create directory for logs
RUN mkdir /var/log/aio
# Add extra package sources
RUN apt-get update -y && apt-get install -y curl=7.64.0-4+deb10u1
RUN curl --silent --show-error --location https://deb.nodesource.com/setup_12.x | bash -
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
# Install packages
# NOTE: Some packages (such as `nginx`, `nodejs`, `openssl`) make older versions unavailable on the
# repositories, so we cannot pin to specific versions for these packages :(
# See for example:
# - https://github.com/nodesource/distributions/issues/33
# - https://askubuntu.com/questions/715104/how-can-i-downgrade-openssl-via-apt-get
RUN apt-get update -y && apt-get install -y \
cron=3.0pl1-134+deb10u1 \
dnsmasq=2.80-1 \
nano=3.2-3 \
nginx \
nodejs \
openssl \
rsyslog=8.1901.0-1 \
vim=2:8.1.0875-5 \
yarn=1.22.4-1
RUN yarn global add pm2@4.4.0
# Set up log rotation
COPY logrotate/* /etc/logrotate.d/
RUN chmod 0644 /etc/logrotate.d/*
# Set up cronjobs
COPY cronjobs/aio-builds-cleanup /etc/cron.d/
RUN chmod 0744 /etc/cron.d/aio-builds-cleanup
RUN crontab /etc/cron.d/aio-builds-cleanup
RUN printenv | grep AIO_ >> /etc/environment
# Set up dnsmasq
COPY dnsmasq/dnsmasq.conf /etc/
RUN sed -i "s|{{\$AIO_NGINX_HOSTNAME}}|$AIO_NGINX_HOSTNAME|g" /etc/dnsmasq.conf
RUN sed -i "s|{{\$AIO_PREVIEW_SERVER_HOSTNAME}}|$AIO_PREVIEW_SERVER_HOSTNAME|g" /etc/dnsmasq.conf
RUN sed -i "s|{{\$TEST_AIO_NGINX_HOSTNAME}}|$TEST_AIO_NGINX_HOSTNAME|g" /etc/dnsmasq.conf
RUN sed -i "s|{{\$TEST_AIO_PREVIEW_SERVER_HOSTNAME}}|$TEST_AIO_PREVIEW_SERVER_HOSTNAME|g" /etc/dnsmasq.conf
# Set up SSL/TLS certificates
COPY nginx/create-selfsigned-cert.sh /tmp/
RUN chmod a+x /tmp/create-selfsigned-cert.sh
RUN /tmp/create-selfsigned-cert.sh "selfcert-prod" "$AIO_NGINX_HOSTNAME" "$AIO_LOCALCERTS_DIR"
RUN /tmp/create-selfsigned-cert.sh "selfcert-test" "$TEST_AIO_NGINX_HOSTNAME" "$TEST_AIO_LOCALCERTS_DIR"
RUN rm /tmp/create-selfsigned-cert.sh
RUN update-ca-certificates
# Set up nginx (for production and testing)
RUN sed -i -E "s|^user\s+\S+;|user $AIO_WWW_USER;|" /etc/nginx/nginx.conf
RUN rm -f /etc/nginx/conf.d/*
RUN rm -f /etc/nginx/sites-enabled/*
COPY nginx/aio-builds.conf /etc/nginx/conf.d/aio-builds-prod.conf
RUN sed -i "s|{{\$AIO_BUILDS_DIR}}|$AIO_BUILDS_DIR|g" /etc/nginx/conf.d/aio-builds-prod.conf
RUN sed -i "s|{{\$AIO_DOMAIN_NAME}}|$AIO_DOMAIN_NAME|g" /etc/nginx/conf.d/aio-builds-prod.conf
RUN sed -i "s|{{\$AIO_LOCALCERTS_DIR}}|$AIO_LOCALCERTS_DIR|g" /etc/nginx/conf.d/aio-builds-prod.conf
RUN sed -i "s|{{\$AIO_NGINX_LOGS_DIR}}|$AIO_NGINX_LOGS_DIR|g" /etc/nginx/conf.d/aio-builds-prod.conf
RUN sed -i "s|{{\$AIO_NGINX_PORT_HTTP}}|$AIO_NGINX_PORT_HTTP|g" /etc/nginx/conf.d/aio-builds-prod.conf
RUN sed -i "s|{{\$AIO_NGINX_PORT_HTTPS}}|$AIO_NGINX_PORT_HTTPS|g" /etc/nginx/conf.d/aio-builds-prod.conf
RUN sed -i "s|{{\$AIO_PREVIEW_SERVER_HOSTNAME}}|$AIO_PREVIEW_SERVER_HOSTNAME|g" /etc/nginx/conf.d/aio-builds-prod.conf
RUN sed -i "s|{{\$AIO_ARTIFACT_MAX_SIZE}}|$AIO_ARTIFACT_MAX_SIZE|g" /etc/nginx/conf.d/aio-builds-prod.conf
RUN sed -i "s|{{\$AIO_PREVIEW_SERVER_PORT}}|$AIO_PREVIEW_SERVER_PORT|g" /etc/nginx/conf.d/aio-builds-prod.conf
COPY nginx/aio-builds.conf /etc/nginx/conf.d/aio-builds-test.conf
RUN sed -i "s|{{\$AIO_BUILDS_DIR}}|$TEST_AIO_BUILDS_DIR|g" /etc/nginx/conf.d/aio-builds-test.conf
RUN sed -i "s|{{\$AIO_DOMAIN_NAME}}|$TEST_AIO_DOMAIN_NAME|g" /etc/nginx/conf.d/aio-builds-test.conf
RUN sed -i "s|{{\$AIO_LOCALCERTS_DIR}}|$TEST_AIO_LOCALCERTS_DIR|g" /etc/nginx/conf.d/aio-builds-test.conf
RUN sed -i "s|{{\$AIO_NGINX_LOGS_DIR}}|$TEST_AIO_NGINX_LOGS_DIR|g" /etc/nginx/conf.d/aio-builds-test.conf
RUN sed -i "s|{{\$AIO_NGINX_PORT_HTTP}}|$TEST_AIO_NGINX_PORT_HTTP|g" /etc/nginx/conf.d/aio-builds-test.conf
RUN sed -i "s|{{\$AIO_NGINX_PORT_HTTPS}}|$TEST_AIO_NGINX_PORT_HTTPS|g" /etc/nginx/conf.d/aio-builds-test.conf
RUN sed -i "s|{{\$AIO_PREVIEW_SERVER_HOSTNAME}}|$TEST_AIO_PREVIEW_SERVER_HOSTNAME|g" /etc/nginx/conf.d/aio-builds-test.conf
RUN sed -i "s|{{\$AIO_ARTIFACT_MAX_SIZE}}|$TEST_AIO_ARTIFACT_MAX_SIZE|g" /etc/nginx/conf.d/aio-builds-test.conf
RUN sed -i "s|{{\$AIO_PREVIEW_SERVER_PORT}}|$TEST_AIO_PREVIEW_SERVER_PORT|g" /etc/nginx/conf.d/aio-builds-test.conf
# Set up pm2
RUN pm2 startup --user root > /dev/null
# Set up the shell scripts
COPY scripts-sh/ $AIO_SCRIPTS_SH_DIR/
RUN chmod a+x $AIO_SCRIPTS_SH_DIR/*
RUN find $AIO_SCRIPTS_SH_DIR -maxdepth 1 -type f -printf "%P\n" \
| while read file; do ln -s $AIO_SCRIPTS_SH_DIR/$file /usr/local/bin/aio-${file%.*}; done
# Set up the Node.js scripts
COPY scripts-js/ $AIO_SCRIPTS_JS_DIR/
RUN yarn --cwd="$AIO_SCRIPTS_JS_DIR/" install --production --frozen-lockfile
# Set up health check
HEALTHCHECK --interval=5m CMD /usr/local/bin/aio-health-check
# Go!
WORKDIR /
CMD aio-init && tail -f /dev/null

View File

@ -1,2 +0,0 @@
# 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

View File

@ -1,16 +0,0 @@
# Do not read /etc/resolv.conf. Get servers from this file instead.
no-resolv
server=8.8.8.8
server=8.8.4.4
# Listen for DHCP and DNS requests only on this address.
listen-address=127.0.0.1
# Force an IP address for these domains.
address=/{{$AIO_NGINX_HOSTNAME}}/127.0.0.1
address=/{{$AIO_PREVIEW_SERVER_HOSTNAME}}/127.0.0.1
address=/{{$TEST_AIO_NGINX_HOSTNAME}}/127.0.0.1
address=/{{$TEST_AIO_PREVIEW_SERVER_HOSTNAME}}/127.0.0.1
# Run as root (required from inside docker container).
user=root

View File

@ -1,9 +0,0 @@
/var/log/aio/clean-up.log /var/log/aio/init.log /var/log/aio/verify-setup.log {
compress
create
delaycompress
missingok
monthly
notifempty
rotate 6
}

View File

@ -1,13 +0,0 @@
/var/log/aio/nginx/*.log /var/log/aio/nginx-test/*.log {
compress
create
delaycompress
missingok
monthly
notifempty
rotate 6
sharedscripts
postrotate
service nginx rotate >/dev/null 2>&1
endscript
}

View File

@ -1,9 +0,0 @@
/var/log/aio/preview-server-*.log {
compress
copytruncate
delaycompress
missingok
monthly
notifempty
rotate 6
}

View File

@ -1,123 +0,0 @@
# Redirect all HTTP traffic to HTTPS
server {
server_name _;
listen {{$AIO_NGINX_PORT_HTTP}} default_server;
listen [::]:{{$AIO_NGINX_PORT_HTTP}};
access_log {{$AIO_NGINX_LOGS_DIR}}/access.log;
error_log {{$AIO_NGINX_LOGS_DIR}}/error.log;
# Ideally we want 308 (permanent + keep original method),
# but it is relatively new and not supported by some clients (e.g. cURL).
return 307 https://$host:{{$AIO_NGINX_PORT_HTTPS}}$request_uri;
}
# Serve PR-preview requests
server {
server_name "~^pr(?<pr>[1-9][0-9]*)-(?<sha>[0-9a-f]{7,40})\.";
listen {{$AIO_NGINX_PORT_HTTPS}} ssl http2;
listen [::]:{{$AIO_NGINX_PORT_HTTPS}} ssl http2;
ssl_certificate {{$AIO_LOCALCERTS_DIR}}/{{$AIO_DOMAIN_NAME}}.crt;
ssl_certificate_key {{$AIO_LOCALCERTS_DIR}}/{{$AIO_DOMAIN_NAME}}.key;
ssl_prefer_server_ciphers on;
ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
root {{$AIO_BUILDS_DIR}}/$pr/$sha;
disable_symlinks on from=$document_root;
index index.html;
gzip on;
gzip_comp_level 7;
gzip_types *;
access_log {{$AIO_NGINX_LOGS_DIR}}/access.log;
error_log {{$AIO_NGINX_LOGS_DIR}}/error.log;
error_page 404 /404.html;
location "=/404.html" {
internal;
}
location "~/[^/]+\.[^/]+$" {
try_files $uri $uri/ =404;
}
location / {
try_files $uri $uri/ /index.html =404;
}
}
# Handle all other requests
server {
server_name _;
listen {{$AIO_NGINX_PORT_HTTPS}} ssl http2 default_server;
listen [::]:{{$AIO_NGINX_PORT_HTTPS}} ssl http2;
ssl_certificate {{$AIO_LOCALCERTS_DIR}}/{{$AIO_DOMAIN_NAME}}.crt;
ssl_certificate_key {{$AIO_LOCALCERTS_DIR}}/{{$AIO_DOMAIN_NAME}}.key;
ssl_prefer_server_ciphers on;
ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
access_log {{$AIO_NGINX_LOGS_DIR}}/access.log;
error_log {{$AIO_NGINX_LOGS_DIR}}/error.log;
# Health check
location "~^/health-check/?$" {
add_header Content-Type text/plain;
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
location "~^/circle-build/?$" {
if ($request_method != "POST") {
add_header Allow "POST";
return 405;
}
proxy_pass_request_headers on;
proxy_redirect off;
proxy_method POST;
proxy_pass http://{{$AIO_PREVIEW_SERVER_HOSTNAME}}:{{$AIO_PREVIEW_SERVER_PORT}}$request_uri;
resolver 127.0.0.1;
}
# Notify about PR changes
location "~^/pr-updated/?$" {
if ($request_method != "POST") {
add_header Allow "POST";
return 405;
}
proxy_pass_request_headers on;
proxy_redirect off;
proxy_method POST;
proxy_pass http://{{$AIO_PREVIEW_SERVER_HOSTNAME}}:{{$AIO_PREVIEW_SERVER_PORT}}$request_uri;
resolver 127.0.0.1;
}
# Everything else
location / {
return 404;
}
}

View File

@ -1,20 +0,0 @@
#!/bin/bash
set -eu -o pipefail
# Variables
confFile=/tmp/$1.conf
domainName=$2
outDir=$3
# Create certificate
cp /etc/ssl/openssl.cnf "$confFile"
echo "[subjectAltName]" >> "$confFile"
echo "subjectAltName = DNS:$domainName, DNS:*.$domainName" >> "$confFile"
mkdir -p $outDir
openssl req -days 365 -newkey rsa:2048 -nodes -sha256 -x509 \
-config "$confFile" -extensions subjectAltName -subj "/CN=$domainName" \
-out "$outDir/$domainName.crt" -keyout "$outDir/$domainName.key"
chmod -R 400 "$outDir"
cp "$outDir/$domainName.crt" /usr/local/share/ca-certificates

View File

@ -1,2 +0,0 @@
/dist
/node_modules

View File

@ -1,128 +0,0 @@
// Imports
import * as fs from 'fs';
import * as path from 'path';
import * as shell from 'shelljs';
import {HIDDEN_DIR_PREFIX} from '../common/constants';
import {GithubApi} from '../common/github-api';
import {GithubPullRequests} from '../common/github-pull-requests';
import {assertNotMissingOrEmpty, getPrInfoFromDownloadPath, Logger} from '../common/utils';
// Classes
export class BuildCleaner {
private logger = new Logger('BuildCleaner');
// Constructor
constructor(protected buildsDir: string, protected githubOrg: string, protected githubRepo: string,
protected githubToken: string, protected downloadsDir: string, protected artifactPath: string) {
assertNotMissingOrEmpty('buildsDir', buildsDir);
assertNotMissingOrEmpty('githubOrg', githubOrg);
assertNotMissingOrEmpty('githubRepo', githubRepo);
assertNotMissingOrEmpty('githubToken', githubToken);
assertNotMissingOrEmpty('downloadsDir', downloadsDir);
assertNotMissingOrEmpty('artifactPath', artifactPath);
}
// Methods - Public
public async cleanUp(): Promise<void> {
try {
this.logger.log('Cleaning up builds and downloads');
const openPrs = await this.getOpenPrNumbers();
this.logger.log(`Open pull requests: ${openPrs.length}`);
await Promise.all([
this.cleanBuilds(openPrs),
this.cleanDownloads(openPrs),
]);
} catch (error) {
this.logger.error('ERROR:', error);
throw error;
}
}
public async cleanBuilds(openPrs: number[]): Promise<void> {
const existingBuilds = await this.getExistingBuildNumbers();
await this.removeUnnecessaryBuilds(existingBuilds, openPrs);
}
public async cleanDownloads(openPrs: number[]): Promise<void> {
const existingDownloads = await this.getExistingDownloads();
await this.removeUnnecessaryDownloads(existingDownloads, openPrs);
}
public getExistingBuildNumbers(): Promise<number[]> {
return new Promise<number[]>((resolve, reject) => {
fs.readdir(this.buildsDir, (err, files) => {
if (err) {
return reject(err);
}
const buildNumbers = files.
map(name => name.replace(HIDDEN_DIR_PREFIX, '')). // Remove the "hidden dir" prefix
map(Number). // Convert string to number
filter(Boolean); // Ignore NaN (or 0), because they are not builds
resolve(buildNumbers);
});
});
}
public async getOpenPrNumbers(): Promise<number[]> {
const api = new GithubApi(this.githubToken);
const githubPullRequests = new GithubPullRequests(api, this.githubOrg, this.githubRepo);
const prs = await githubPullRequests.fetchAll('open');
return prs.map(pr => pr.number);
}
public removeDir(dir: string): void {
try {
if (shell.test('-d', dir)) {
shell.chmod('-R', 'a+w', dir);
shell.rm('-rf', dir);
}
} catch (err) {
this.logger.error(`ERROR: Unable to remove '${dir}' due to:`, err);
}
}
public removeUnnecessaryBuilds(existingBuildNumbers: number[], openPrNumbers: number[]): void {
const toRemove = existingBuildNumbers.filter(num => !openPrNumbers.includes(num));
this.logger.log(`Existing builds: ${existingBuildNumbers.length}`);
this.logger.log(`Removing ${toRemove.length} build(s): ${toRemove.join(', ')}`);
// Try removing public dirs.
toRemove.
map(num => path.join(this.buildsDir, String(num))).
forEach(dir => this.removeDir(dir));
// Try removing hidden dirs.
toRemove.
map(num => path.join(this.buildsDir, HIDDEN_DIR_PREFIX + String(num))).
forEach(dir => this.removeDir(dir));
}
public getExistingDownloads(): Promise<string[]> {
const artifactFile = path.basename(this.artifactPath);
return new Promise<string[]>((resolve, reject) => {
fs.readdir(this.downloadsDir, (err, files) => {
if (err) {
return reject(err);
}
files = files.filter(file => file.endsWith(artifactFile));
resolve(files);
});
});
}
public removeUnnecessaryDownloads(existingDownloads: string[], openPrNumbers: number[]): void {
const toRemove = existingDownloads.filter(filePath => {
const {pr} = getPrInfoFromDownloadPath(filePath);
return !openPrNumbers.includes(pr);
});
this.logger.log(`Existing downloads: ${existingDownloads.length}`);
this.logger.log(`Removing ${toRemove.length} download(s): ${toRemove.join(', ')}`);
toRemove.forEach(filePath => shell.rm(path.join(this.downloadsDir, filePath)));
}
}

View File

@ -1,26 +0,0 @@
// Imports
import {AIO_DOWNLOADS_DIR} from '../common/constants';
import {
AIO_ARTIFACT_PATH,
AIO_BUILDS_DIR,
AIO_GITHUB_ORGANIZATION,
AIO_GITHUB_REPO,
AIO_GITHUB_TOKEN,
} from '../common/env-variables';
import {BuildCleaner} from './build-cleaner';
// Run
_main();
// Functions
function _main(): void {
const buildCleaner = new BuildCleaner(
AIO_BUILDS_DIR,
AIO_GITHUB_ORGANIZATION,
AIO_GITHUB_REPO,
AIO_GITHUB_TOKEN,
AIO_DOWNLOADS_DIR,
AIO_ARTIFACT_PATH);
buildCleaner.cleanUp().catch(() => process.exit(1));
}

View File

@ -1,90 +0,0 @@
// Imports
import fetch from 'node-fetch';
import {assertNotMissingOrEmpty} from './utils';
// Constants
const CIRCLE_CI_API_URL = 'https://circleci.com/api/v1.1/project/github';
// Interfaces - Types
export interface ArtifactInfo {
path: string;
pretty_path: string;
node_index: number;
url: string;
}
export type ArtifactResponse = ArtifactInfo[];
export interface BuildInfo {
reponame: string;
failed: boolean;
branch: string;
username: string;
build_num: number;
has_artifacts: boolean;
outcome: string; // e.g. 'success'
vcs_revision: string; // HEAD SHA
// there are other fields but they are not used in this code
}
/**
* A Helper that can interact with the CircleCI API.
*/
export class CircleCiApi {
private tokenParam = `circle-token=${this.circleCiToken}`;
/**
* Construct a helper that can interact with the CircleCI REST API.
* @param githubOrg The Github organisation whose repos we want to access in CircleCI (e.g. angular).
* @param githubRepo The Github repo whose builds we want to access in CircleCI (e.g. angular).
* @param circleCiToken The CircleCI API access token (secret).
*/
constructor(
private githubOrg: string,
private githubRepo: string,
private circleCiToken: string,
) {
assertNotMissingOrEmpty('githubOrg', githubOrg);
assertNotMissingOrEmpty('githubRepo', githubRepo);
assertNotMissingOrEmpty('circleCiToken', circleCiToken);
}
/**
* Get the info for a build from the CircleCI API
* @param buildNumber The CircleCI build number that generated the artifact.
* @returns A promise to the info about the build
*/
public async getBuildInfo(buildNumber: number): Promise<BuildInfo> {
try {
const baseUrl = `${CIRCLE_CI_API_URL}/${this.githubOrg}/${this.githubRepo}/${buildNumber}`;
const response = await fetch(`${baseUrl}?${this.tokenParam}`);
if (response.status !== 200) {
throw new Error(`${baseUrl}: ${response.status} - ${response.statusText}`);
}
return response.json();
} catch (error) {
throw new Error(`CircleCI build info request failed (${error.message})`);
}
}
/**
* Query the CircleCI API to get a URL for a specified artifact from a specified build.
* @param artifactPath The path, within the build to the artifact.
* @returns A promise to the URL that can be requested to download the actual build artifact file.
*/
public async getBuildArtifactUrl(buildNumber: number, artifactPath: string): Promise<string> {
const baseUrl = `${CIRCLE_CI_API_URL}/${this.githubOrg}/${this.githubRepo}/${buildNumber}`;
try {
const response = await fetch(`${baseUrl}/artifacts?${this.tokenParam}`);
const artifacts = await response.json() as ArtifactResponse;
const artifact = artifacts.find(item => item.path === artifactPath);
if (!artifact) {
throw new Error(`Missing artifact (${artifactPath}) for CircleCI build: ${buildNumber}`);
}
return artifact.url;
} catch (error) {
throw new Error(`CircleCI artifact URL request failed (${error.message})`);
}
}
}

View File

@ -1,4 +0,0 @@
// Constants
export const AIO_DOWNLOADS_DIR = '/tmp/aio-downloads';
export const HIDDEN_DIR_PREFIX = 'hidden--';
export const SHORT_SHA_LEN = 7;

View File

@ -1,19 +0,0 @@
import {getEnvVar} from './utils';
export const AIO_ARTIFACT_PATH = getEnvVar('AIO_ARTIFACT_PATH');
export const AIO_BUILDS_DIR = getEnvVar('AIO_BUILDS_DIR');
export const AIO_GITHUB_TOKEN = getEnvVar('AIO_GITHUB_TOKEN');
export const AIO_CIRCLE_CI_TOKEN = getEnvVar('AIO_CIRCLE_CI_TOKEN');
export const AIO_DOMAIN_NAME = getEnvVar('AIO_DOMAIN_NAME');
export const AIO_GITHUB_ORGANIZATION = getEnvVar('AIO_GITHUB_ORGANIZATION');
export const AIO_GITHUB_REPO = getEnvVar('AIO_GITHUB_REPO');
export const AIO_GITHUB_TEAM_SLUGS = getEnvVar('AIO_GITHUB_TEAM_SLUGS');
export const AIO_NGINX_HOSTNAME = getEnvVar('AIO_NGINX_HOSTNAME');
export const AIO_NGINX_PORT_HTTP = +getEnvVar('AIO_NGINX_PORT_HTTP');
export const AIO_NGINX_PORT_HTTPS = +getEnvVar('AIO_NGINX_PORT_HTTPS');
export const AIO_SIGNIFICANT_FILES_PATTERN = getEnvVar('AIO_SIGNIFICANT_FILES_PATTERN');
export const AIO_TRUSTED_PR_LABEL = getEnvVar('AIO_TRUSTED_PR_LABEL');
export const AIO_PREVIEW_SERVER_HOSTNAME = getEnvVar('AIO_PREVIEW_SERVER_HOSTNAME');
export const AIO_PREVIEW_SERVER_PORT = +getEnvVar('AIO_PREVIEW_SERVER_PORT');
export const AIO_ARTIFACT_MAX_SIZE = +getEnvVar('AIO_ARTIFACT_MAX_SIZE');
export const AIO_WWW_USER = getEnvVar('AIO_WWW_USER');

View File

@ -1,111 +0,0 @@
// Imports
import {IncomingMessage} from 'http';
import * as https from 'https';
import {assertNotMissingOrEmpty} from './utils';
// Constants
const GITHUB_HOSTNAME = 'api.github.com';
// Interfaces - Types
interface RequestParams {
[key: string]: string | number;
}
type RequestParamsOrNull = RequestParams | null;
// Classes
export class GithubApi {
protected requestHeaders: {[key: string]: string};
// Constructor
constructor(githubToken: string) {
assertNotMissingOrEmpty('githubToken', githubToken);
this.requestHeaders = {
'Authorization': `token ${githubToken}`,
'User-Agent': `Node/${process.versions.node}`,
};
}
// Methods - Public
public get<T = any>(pathname: string, params?: RequestParamsOrNull): Promise<T> {
const path = this.buildPath(pathname, params);
return this.request<T>('get', path);
}
public post<T = any>(pathname: string, params?: RequestParamsOrNull, data?: any): Promise<T> {
const path = this.buildPath(pathname, params);
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 = 1): Promise<T[]> {
const perPage = 100;
const params = {
...baseParams,
page: currentPage,
per_page: perPage,
};
return this.get<T[]>(pathname, params).then(items => {
if (items.length < perPage) {
return items;
}
return this.getPaginated<T>(pathname, baseParams, currentPage + 1).then(moreItems => [...items, ...moreItems]);
});
}
// Methods - Protected
protected buildPath(pathname: string, params?: RequestParamsOrNull): string {
if (params == null) {
return pathname;
}
const search = (params === null) ? '' : this.serializeSearchParams(params);
const joiner = search && '?';
return `${pathname}${joiner}${search}`;
}
protected request<T>(method: string, path: string, data: any = null): Promise<T> {
return new Promise<T>((resolve, reject) => {
const options = {
headers: {...this.requestHeaders},
host: GITHUB_HOSTNAME,
method,
path,
};
const onError = (statusCode: number, responseText: string) => {
const url = `https://${GITHUB_HOSTNAME}${path}`;
reject(`Request to '${url}' failed (status: ${statusCode}): ${responseText}`);
};
const onSuccess = (responseText: string) => {
try { resolve(responseText && JSON.parse(responseText)); } catch (err) { reject(err); }
};
const onResponse = (res: IncomingMessage) => {
const statusCode = res.statusCode || -1;
const isSuccess = (200 <= statusCode) && (statusCode < 400);
let responseText = '';
res.
on('data', d => responseText += d).
on('end', () => isSuccess ? onSuccess(responseText) : onError(statusCode, responseText)).
on('error', reject);
};
https.
request(options, onResponse).
on('error', reject).
end(data && JSON.stringify(data));
});
}
protected serializeSearchParams(params: RequestParams): string {
return Object.keys(params).
filter(key => params[key] != null).
map(key => `${key}=${encodeURIComponent(String(params[key]))}`).
join('&');
}
}

View File

@ -1,79 +0,0 @@
import {GithubApi} from './github-api';
import {assert, assertNotMissingOrEmpty} from './utils';
export interface PullRequest {
number: number;
user: {login: string};
labels: {name: string}[];
}
export interface FileInfo {
sha: string;
filename: string;
}
export type PullRequestState = 'all' | 'closed' | 'open';
/**
* Access pull requests on GitHub.
*/
export class GithubPullRequests {
public repoSlug: string;
/**
* Create an instance of this helper
* @param api An instance of the Github API helper.
* @param githubOrg The organisation on GitHub whose repo we will interrogate.
* @param githubRepo The repository on Github with whose PRs we will interact.
*/
constructor(private api: GithubApi, githubOrg: string, githubRepo: string) {
assertNotMissingOrEmpty('githubOrg', githubOrg);
assertNotMissingOrEmpty('githubRepo', githubRepo);
this.repoSlug = `${githubOrg}/${githubRepo}`;
}
/**
* Post a comment on a PR.
* @param pr The number of the PR on which to comment.
* @param body The body of the comment to post.
* @returns A promise that resolves when the comment has been posted.
*/
public addComment(pr: number, body: string): Promise<any> {
assert(pr > 0, `Invalid PR number: ${pr}`);
assert(!!body, `Invalid or empty comment body: ${body}`);
return this.api.post<any>(`/repos/${this.repoSlug}/issues/${pr}/comments`, null, {body});
}
/**
* Request information about a PR.
* @param pr The number of the PR for which to request info.
* @returns A promise that is resolves with information about the specified PR.
*/
public fetch(pr: number): Promise<PullRequest> {
assert(pr > 0, `Invalid PR number: ${pr}`);
// Using the `/issues/` URL, because the `/pulls/` one does not provide labels.
return this.api.get<PullRequest>(`/repos/${this.repoSlug}/issues/${pr}`);
}
/**
* Request information about all PRs that match the given state.
* @param state Only retrieve PRs that have this state.
* @returns A promise that is resolved with information about the requested PRs.
*/
public fetchAll(state: PullRequestState = 'all'): Promise<PullRequest[]> {
const pathname = `/repos/${this.repoSlug}/pulls`;
const params = {state};
return this.api.getPaginated<PullRequest>(pathname, params);
}
/**
* Request a list of files for the given PR.
* @param pr The number of the PR for which to request files.
* @returns A promise that resolves to an array of file information
*/
public fetchFiles(pr: number): Promise<FileInfo[]> {
assert(pr > 0, `Invalid PR number: ${pr}`);
return this.api.getPaginated<FileInfo>(`/repos/${this.repoSlug}/pulls/${pr}/files`);
}
}

View File

@ -1,72 +0,0 @@
import {GithubApi} from './github-api';
import {assertNotMissingOrEmpty} from './utils';
export interface Team {
id: number;
slug: string;
}
export interface TeamMembership {
state: string;
}
export class GithubTeams {
/**
* Create an instance of this helper
* @param api An instance of the Github API helper.
* @param githubOrg The organisation on GitHub whose repo we will interrogate.
*/
constructor(private api: GithubApi, protected githubOrg: string) {
assertNotMissingOrEmpty('githubOrg', githubOrg);
}
/**
* Request information about all the organisation's teams in GitHub.
* @returns A promise that is resolved with information about the teams.
*/
public fetchAll(): Promise<Team[]> {
return this.api.getPaginated<Team>(`/orgs/${this.githubOrg}/teams`);
}
/**
* Check whether the specified username is a member of the specified team.
* @param username The usernane to check for in the team.
* @param teamIds The team to check for the username.
* @returns a Promise that resolves to `true` if the username is a member of the team.
*/
public async isMemberById(username: string, teamIds: number[]): Promise<boolean> {
const getMembership = async (teamId: number) => {
try {
const {state} = await this.api.get<TeamMembership>(`/teams/${teamId}/memberships/${username}`);
return state === 'active';
} catch (error) {
return false;
}
};
for (const teamId of teamIds) {
if (await getMembership(teamId)) {
return true;
}
}
return false;
}
/**
* Check whether the given username is a member of the teams specified by the team slugs.
* @param username The username to check for in the teams.
* @param teamSlugs A collection of slugs that represent the teams to check for the the username.
* @returns a Promise that resolves to `true` if the usernane is a member of at least one of the specified teams.
*/
public async isMemberBySlug(username: string, teamSlugs: string[]): Promise<boolean> {
try {
const teams = await this.fetchAll();
const teamIds = teams.filter(team => teamSlugs.includes(team.slug)).map(team => team.id);
return await this.isMemberById(username, teamIds);
} catch (error) {
return false;
}
}
}

View File

@ -1,23 +0,0 @@
// We can't use `import...from` here, because of the following mess:
// - 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).
import Jasmine = require('jasmine');
import 'source-map-support/register';
export const runTests = (specFiles: string[]) => {
const config = {
random: true,
spec_files: specFiles,
stopSpecOnExpectationFailure: true,
};
process.on('unhandledRejection', (reason: any) => console.log('Unhandled rejection:', reason));
const runner = new Jasmine({});
runner.loadConfig(config);
runner.onComplete((passed: boolean) => process.exit(passed ? 0 : 1));
runner.execute();
};

View File

@ -1,98 +0,0 @@
import {basename, resolve as resolvePath} from 'path';
import {SHORT_SHA_LEN} from './constants';
/**
* Shorten a SHA to make it more readable
* @param sha The SHA to shorten.
*/
export function computeShortSha(sha: string) {
return sha.substr(0, SHORT_SHA_LEN);
}
/**
* Compute the path for a downloaded artifact file.
* @param downloadsDir The directory where artifacts are downloaded
* @param pr The PR associated with this artifact.
* @param sha The SHA associated with the build for this artifact.
* @param artifactPath The path to the artifact on CircleCI.
* @returns The fully resolved location for the specified downloaded artifact.
*/
export function computeArtifactDownloadPath(downloadsDir: string, pr: number, sha: string, artifactPath: string) {
return resolvePath(downloadsDir, `${pr}-${computeShortSha(sha)}-${basename(artifactPath)}`);
}
/**
* Extract the PR number and latest commit SHA from a downloaded file path.
* @param downloadPath the path to the downloaded file.
* @returns An object whose keys are the PR and SHA extracted from the file path.
*/
export function getPrInfoFromDownloadPath(downloadPath: string) {
const file = basename(downloadPath);
const [pr, sha] = file.split('-');
return {pr: +pr, sha};
}
/**
* Assert that a value is true.
* @param value The value to assert.
* @param message The message if the value is not true.
*/
export function assert(value: boolean, message: string) {
if (!value) {
throw new Error(message);
}
}
/**
* Assert that a parameter is not equal to "".
* @param name The name of the parameter.
* @param value The value of the parameter.
*/
export const assertNotMissingOrEmpty = (name: string, value: string | null | undefined) => {
assert(!!value, `Missing or empty required parameter '${name}'!`);
};
/**
* Get an environment variable.
* @param name The name of the environment variable.
* @param isOptional True if the variable is optional.
* @returns The value of the variable or "" if it is optional and falsy.
* @throws `Error` if the variable is falsy and not optional.
*/
export const getEnvVar = (name: string, isOptional = false): string => {
const value = process.env[name];
if (!isOptional && !value) {
try {
throw new Error(`ERROR: Missing required environment variable '${name}'!`);
} catch (error) {
console.error(error.stack);
process.exit(1);
}
}
return value || '';
};
/**
* A basic logger implementation.
* Delegates to `console`, but prepends each message with the current date and specified scope (i.e caller).
*/
export class Logger {
private padding = ' '.repeat(20 - this.scope.length);
/**
* 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);
}
}

View File

@ -1,144 +0,0 @@
// Imports
import * as cp from 'child_process';
import {EventEmitter} from 'events';
import * as fs from 'fs';
import * as path from 'path';
import * as shell from 'shelljs';
import {HIDDEN_DIR_PREFIX} from '../common/constants';
import {assertNotMissingOrEmpty, computeShortSha, Logger} from '../common/utils';
import {ChangedPrVisibilityEvent, CreatedBuildEvent} from './build-events';
import {PreviewServerError} from './preview-error';
// Classes
export class BuildCreator extends EventEmitter {
private logger = new Logger('BuildCreator');
// Constructor
constructor(protected buildsDir: string) {
super();
assertNotMissingOrEmpty('buildsDir', buildsDir);
}
// Methods - Public
public create(pr: number, sha: string, archivePath: string, isPublic: boolean): Promise<void> {
// Use only part of the SHA for more readable URLs.
sha = computeShortSha(sha);
const {newPrDir: prDir} = this.getCandidatePrDirs(pr, isPublic);
const shaDir = path.join(prDir, sha);
let dirToRemoveOnError: string;
return Promise.resolve().
// If the same PR exists with different visibility, update the visibility first.
then(() => this.updatePrVisibility(pr, isPublic)).
then(() => Promise.all([this.exists(prDir), this.exists(shaDir)])).
then(([prDirExisted, shaDirExisted]) => {
if (shaDirExisted) {
const publicOrNot = isPublic ? 'public' : 'non-public';
throw new PreviewServerError(409, `Request to overwrite existing ${publicOrNot} directory: ${shaDir}`);
}
dirToRemoveOnError = prDirExisted ? shaDir : prDir;
return Promise.resolve().
then(() => shell.mkdir('-p', shaDir)).
then(() => this.extractArchive(archivePath, shaDir)).
then(() => this.emit(CreatedBuildEvent.type, new CreatedBuildEvent(+pr, sha, isPublic))).
then(() => undefined);
}).
catch(err => {
if (dirToRemoveOnError) {
shell.rm('-rf', dirToRemoveOnError);
}
if (!(err instanceof PreviewServerError)) {
err = new PreviewServerError(500, `Error while creating preview at: ${shaDir}\n${err}`);
}
throw err;
});
}
public updatePrVisibility(pr: number, makePublic: boolean): Promise<boolean> {
const {oldPrDir: otherVisPrDir, newPrDir: targetVisPrDir} = this.getCandidatePrDirs(pr, makePublic);
return Promise.
all([this.exists(otherVisPrDir), this.exists(targetVisPrDir)]).
then(([otherVisPrDirExisted, targetVisPrDirExisted]) => {
if (!otherVisPrDirExisted) {
// No visibility change: Either the visibility is up-to-date or the PR does not exist.
return false;
} else if (targetVisPrDirExisted) {
// Error: Directories for both visibilities exist.
throw new PreviewServerError(409,
`Request to move '${otherVisPrDir}' to existing directory '${targetVisPrDir}'.`);
}
// Visibility change: Moving `otherVisPrDir` to `targetVisPrDir`.
return Promise.resolve().
then(() => shell.mv(otherVisPrDir, targetVisPrDir)).
then(() => this.listShasByDate(targetVisPrDir)).
then(shas => this.emit(ChangedPrVisibilityEvent.type, new ChangedPrVisibilityEvent(+pr, shas, makePublic))).
then(() => true);
}).
catch(err => {
if (!(err instanceof PreviewServerError)) {
err = new PreviewServerError(500, `Error while making PR ${pr} ${makePublic ? 'public' : 'hidden'}.\n${err}`);
}
throw err;
});
}
// Methods - Protected
protected exists(fileOrDir: string): Promise<boolean> {
return new Promise(resolve => fs.access(fileOrDir, err => resolve(!err)));
}
protected extractArchive(inputFile: string, outputDir: string): Promise<void> {
return new Promise<void>((resolve, reject) => {
const cmd = `tar --extract --gzip --directory "${outputDir}" --file "${inputFile}"`;
cp.exec(cmd, (err, _stdout, stderr) => {
if (err) {
return reject(err);
}
if (stderr) {
this.logger.warn(stderr);
}
try {
shell.chmod('-R', 'a-w', outputDir);
shell.rm('-f', inputFile);
resolve();
} catch (err) {
reject(err);
}
});
});
}
protected getCandidatePrDirs(pr: number, isPublic: boolean): {oldPrDir: string, newPrDir: string} {
const hiddenPrDir = path.join(this.buildsDir, HIDDEN_DIR_PREFIX + pr);
const publicPrDir = path.join(this.buildsDir, `${pr}`);
const oldPrDir = isPublic ? hiddenPrDir : publicPrDir;
const newPrDir = isPublic ? publicPrDir : hiddenPrDir;
return {oldPrDir, newPrDir};
}
protected listShasByDate(inputDir: string): Promise<string[]> {
return Promise.resolve().
then(() => shell.ls('-l', inputDir) as any as Promise<(fs.Stats & {name: string})[]>).
// Keep directories only.
// (Also, convert to standard Array - ShellJS provides custom `sort()` method for sorting file contents.)
then(items => items.filter(item => item.isDirectory())).
// Sort by modification date.
then(items => items.sort((a, b) => a.mtime.getTime() - b.mtime.getTime())).
// Return directory names.
then(items => items.map(item => item.name));
}
}

View File

@ -1,16 +0,0 @@
// Classes
export class ChangedPrVisibilityEvent {
// Properties - Public, Static
public static type = 'pr.changedVisibility';
// Constructor
constructor(public pr: number, public shas: string[], public isPublic: boolean) {}
}
export class CreatedBuildEvent {
// Properties - Public, Static
public static type = 'build.created';
// Constructor
constructor(public pr: number, public sha: string, public isPublic: boolean) {}
}

View File

@ -1,83 +0,0 @@
import * as fs from 'fs';
import fetch from 'node-fetch';
import {dirname} from 'path';
import {mkdir} from 'shelljs';
import {promisify} from 'util';
import {CircleCiApi} from '../common/circle-ci-api';
import {assert, assertNotMissingOrEmpty, computeArtifactDownloadPath, Logger} from '../common/utils';
import {PreviewServerError} from './preview-error';
export interface GithubInfo {
org: string;
pr: number;
repo: string;
sha: string;
success: boolean;
}
/**
* A helper that can get information about builds and download build artifacts.
*/
export class BuildRetriever {
private logger = new Logger('BuildRetriever');
constructor(private api: CircleCiApi, private downloadSizeLimit: number, private downloadDir: string) {
assert(downloadSizeLimit > 0, 'Invalid parameter "downloadSizeLimit" should be a number greater than 0.');
assertNotMissingOrEmpty('downloadDir', downloadDir);
}
/**
* Get GitHub information about a build
* @param buildNum The number of the build for which to retrieve the info.
* @returns The Github org, repo, PR and latest SHA for the specified build.
*/
public async getGithubInfo(buildNum: number): Promise<GithubInfo> {
const buildInfo = await this.api.getBuildInfo(buildNum);
const githubInfo: GithubInfo = {
org: buildInfo.username,
pr: getPrFromBranch(buildInfo.branch),
repo: buildInfo.reponame,
sha: buildInfo.vcs_revision,
success: !buildInfo.failed,
};
return githubInfo;
}
/**
* Make a request to the given URL for a build artifact and store it locally.
* @param buildNum the number of the CircleCI build whose artifact we want to download.
* @param pr the number of the PR that triggered the CircleCI build.
* @param sha the commit in the PR that triggered the CircleCI build.
* @param artifactPath the path on CircleCI where the artifact was stored.
* @returns A promise to the file path where the downloaded file was stored.
*/
public async downloadBuildArtifact(buildNum: number, pr: number, sha: string, artifactPath: string): Promise<string> {
try {
const outPath = computeArtifactDownloadPath(this.downloadDir, pr, sha, artifactPath);
const downloadExists = await new Promise(resolve => fs.exists(outPath, exists => resolve(exists)));
if (!downloadExists) {
const url = await this.api.getBuildArtifactUrl(buildNum, artifactPath);
const response = await fetch(url, {size: this.downloadSizeLimit});
if (response.status !== 200) {
throw new PreviewServerError(response.status, `Error ${response.status} - ${response.statusText}`);
}
const buffer = await response.buffer();
mkdir('-p', dirname(outPath));
await promisify(fs.writeFile)(outPath, buffer);
}
return outPath;
} catch (error) {
this.logger.warn(error);
const status = (error.type === 'max-size') ? 413 : 500;
throw new PreviewServerError(status, `CircleCI artifact download failed (${error.message || error})`);
}
}
}
function getPrFromBranch(branch: string): number {
// CircleCI only exposes PR numbers via the `branch` field :-(
const match = /^pull\/(\d+)$/.exec(branch);
if (!match) {
throw new Error(`No PR found in branch field: ${branch}`);
}
return +match[1];
}

View File

@ -1,46 +0,0 @@
import {GithubPullRequests, PullRequest} from '../common/github-pull-requests';
import {GithubTeams} from '../common/github-teams';
import {assertNotMissingOrEmpty} from '../common/utils';
/**
* A helper to verify whether builds are trusted.
*/
export class BuildVerifier {
/**
* Construct a new BuildVerifier instance.
* @param prs A helper to access PR information.
* @param teams A helper to access Github team information.
* @param allowedTeamSlugs The teams that are trusted.
* @param trustedPrLabel The github label that indicates that a PR is trusted.
*/
constructor(protected prs: GithubPullRequests, protected teams: GithubTeams,
protected allowedTeamSlugs: string[], protected trustedPrLabel: string) {
assertNotMissingOrEmpty('allowedTeamSlugs', allowedTeamSlugs && allowedTeamSlugs.join(''));
assertNotMissingOrEmpty('trustedPrLabel', trustedPrLabel);
}
/**
* Check whether a PR contains files that are significant to the build.
* @param pr The number of the PR to check
* @param significantFilePattern A regex that selects files that are significant.
*/
public async getSignificantFilesChanged(pr: number, significantFilePattern: RegExp): Promise<boolean> {
const files = await this.prs.fetchFiles(pr);
return files.some(file => significantFilePattern.test(file.filename));
}
/**
* Check whether a PR is trusted.
* @param pr The number of the PR to check.
* @returns true if the PR is trusted.
*/
public async getPrIsTrusted(pr: number): Promise<boolean> {
const prInfo = await this.prs.fetch(pr);
return this.hasLabel(prInfo, this.trustedPrLabel) ||
(await this.teams.isMemberBySlug(prInfo.user.login, this.allowedTeamSlugs));
}
protected hasLabel(prInfo: PullRequest, label: string): boolean {
return prInfo.labels.some(labelObj => labelObj.name === label);
}
}

View File

@ -1,41 +0,0 @@
// Imports
import {AIO_DOWNLOADS_DIR} from '../common/constants';
import {
AIO_ARTIFACT_MAX_SIZE,
AIO_ARTIFACT_PATH,
AIO_BUILDS_DIR,
AIO_CIRCLE_CI_TOKEN,
AIO_DOMAIN_NAME,
AIO_GITHUB_ORGANIZATION,
AIO_GITHUB_REPO,
AIO_GITHUB_TEAM_SLUGS,
AIO_GITHUB_TOKEN,
AIO_PREVIEW_SERVER_HOSTNAME,
AIO_PREVIEW_SERVER_PORT,
AIO_SIGNIFICANT_FILES_PATTERN,
AIO_TRUSTED_PR_LABEL,
} from '../common/env-variables';
import {PreviewServerFactory} from './preview-server-factory';
// Run
_main();
// Functions
function _main(): void {
PreviewServerFactory
.create({
buildArtifactPath: AIO_ARTIFACT_PATH,
buildsDir: AIO_BUILDS_DIR,
circleCiToken: AIO_CIRCLE_CI_TOKEN,
domainName: AIO_DOMAIN_NAME,
downloadSizeLimit: AIO_ARTIFACT_MAX_SIZE,
downloadsDir: AIO_DOWNLOADS_DIR,
githubOrg: AIO_GITHUB_ORGANIZATION,
githubRepo: AIO_GITHUB_REPO,
githubTeamSlugs: AIO_GITHUB_TEAM_SLUGS.split(','),
githubToken: AIO_GITHUB_TOKEN,
significantFilesPattern: AIO_SIGNIFICANT_FILES_PATTERN,
trustedPrLabel: AIO_TRUSTED_PR_LABEL,
})
.listen(AIO_PREVIEW_SERVER_PORT, AIO_PREVIEW_SERVER_HOSTNAME);
}

View File

@ -1,8 +0,0 @@
// Classes
export class PreviewServerError extends Error {
// Constructor
constructor(public status: number = 500, message?: string) {
super(message);
Object.setPrototypeOf(this, PreviewServerError.prototype);
}
}

View File

@ -1,213 +0,0 @@
// Imports
import * as bodyParser from 'body-parser';
import * as express from 'express';
import * as http from 'http';
import {AddressInfo} from 'net';
import {CircleCiApi} from '../common/circle-ci-api';
import {GithubApi} from '../common/github-api';
import {GithubPullRequests} from '../common/github-pull-requests';
import {GithubTeams} from '../common/github-teams';
import {assert, assertNotMissingOrEmpty, computeShortSha, Logger} from '../common/utils';
import {BuildCreator} from './build-creator';
import {ChangedPrVisibilityEvent, CreatedBuildEvent} from './build-events';
import {BuildRetriever} from './build-retriever';
import {BuildVerifier} from './build-verifier';
import {respondWithError, throwRequestError} from './utils';
const AIO_PREVIEW_JOB = 'aio_preview';
// Interfaces - Types
export interface PreviewServerConfig {
downloadsDir: string;
downloadSizeLimit: number;
buildArtifactPath: string;
buildsDir: string;
domainName: string;
githubOrg: string;
githubRepo: string;
githubTeamSlugs: string[];
circleCiToken: string;
githubToken: string;
significantFilesPattern: string;
trustedPrLabel: string;
}
const logger = new Logger('PreviewServer');
// Classes
export class PreviewServerFactory {
// Methods - Public
public static create(cfg: PreviewServerConfig): http.Server {
assertNotMissingOrEmpty('domainName', cfg.domainName);
const circleCiApi = new CircleCiApi(cfg.githubOrg, cfg.githubRepo, cfg.circleCiToken);
const githubApi = new GithubApi(cfg.githubToken);
const prs = new GithubPullRequests(githubApi, cfg.githubOrg, cfg.githubRepo);
const teams = new GithubTeams(githubApi, cfg.githubOrg);
const buildRetriever = new BuildRetriever(circleCiApi, cfg.downloadSizeLimit, cfg.downloadsDir);
const buildVerifier = new BuildVerifier(prs, teams, cfg.githubTeamSlugs, cfg.trustedPrLabel);
const buildCreator = PreviewServerFactory.createBuildCreator(prs, cfg.buildsDir, cfg.domainName);
const middleware = PreviewServerFactory.createMiddleware(buildRetriever, buildVerifier, buildCreator, cfg);
const httpServer = http.createServer(middleware as any);
httpServer.on('listening', () => {
const info = httpServer.address() as AddressInfo;
logger.info(`Up and running (and listening on ${info.address}:${info.port})...`);
});
return httpServer;
}
public static createMiddleware(buildRetriever: BuildRetriever, buildVerifier: BuildVerifier,
buildCreator: BuildCreator, cfg: PreviewServerConfig): express.Express {
const middleware = express();
const jsonParser = bodyParser.json();
const significantFilesRe = new RegExp(cfg.significantFilesPattern);
// RESPOND TO IS-ALIVE PING
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
middleware.post(/^\/circle-build\/?$/, jsonParser, async (req, res) => {
try {
if (!(
req.is('json') &&
req.body &&
req.body.payload &&
req.body.payload.build_num > 0 &&
req.body.payload.build_parameters &&
req.body.payload.build_parameters.CIRCLE_JOB
)) {
throwRequestError(400, `Incorrect body content. Expected JSON`, req);
}
const job = req.body.payload.build_parameters.CIRCLE_JOB;
const buildNum = req.body.payload.build_num;
logger.log(`Build:${buildNum}, Job:${job} - processing web-hook trigger`);
if (job !== AIO_PREVIEW_JOB) {
res.sendStatus(204);
logger.log(`Build:${buildNum}, Job:${job} -`,
`Skipping preview processing because this is not the "${AIO_PREVIEW_JOB}" job.`);
return;
}
const { pr, sha, org, repo, success } = await buildRetriever.getGithubInfo(buildNum);
if (!success) {
res.sendStatus(204);
logger.log(`PR:${pr}, Build:${buildNum} - Skipping preview processing because this build did not succeed.`);
return;
}
assert(cfg.githubOrg === org,
`Invalid webhook: expected "githubOrg" property to equal "${cfg.githubOrg}" but got "${org}".`);
assert(cfg.githubRepo === 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)
if (!await buildVerifier.getSignificantFilesChanged(pr, significantFilesRe)) {
res.sendStatus(204);
logger.log(`PR:${pr}, Build:${buildNum} - ` +
`Skipping preview processing because this PR did not touch any significant files.`);
return;
}
const artifactPath = await buildRetriever.downloadBuildArtifact(buildNum, pr, sha, cfg.buildArtifactPath);
const isPublic = await buildVerifier.getPrIsTrusted(pr);
await buildCreator.create(pr, sha, artifactPath, isPublic);
res.sendStatus(isPublic ? 201 : 202);
logger.log(`PR:${pr}, SHA:${computeShortSha(sha)}, Build:${buildNum} - ` +
`Successfully created ${isPublic ? 'public' : 'non-public'} preview.`);
} catch (err) {
logger.error('CircleCI webhook error', err);
respondWithError(res, err);
}
});
// GITHUB PR UPDATED WEBHOOK
middleware.post(/^\/pr-updated\/?$/, jsonParser, async (req, res) => {
const { action, number: prNo }: { action?: string, number?: number } = req.body;
const visMayHaveChanged = !action || (action === 'labeled') || (action === 'unlabeled');
try {
if (!visMayHaveChanged) {
res.sendStatus(200);
} else if (!prNo) {
throwRequestError(400, `Missing or empty 'number' field`, req);
} else {
const isPublic = await buildVerifier.getPrIsTrusted(prNo);
await buildCreator.updatePrVisibility(prNo, isPublic);
res.sendStatus(200);
}
} catch (err) {
logger.error('PR update hook error', err);
respondWithError(res, err);
}
});
// ALL OTHER REQUESTS
middleware.all('*', req => throwRequestError(404, 'Unknown resource', req));
middleware.use((err: any, _req: any, res: express.Response, _next: any) => {
const statusText = http.STATUS_CODES[err.status] || '???';
logger.error(`Preview server error: ${err.status} - ${statusText}:`, err.message);
respondWithError(res, err);
});
return middleware;
}
public static createBuildCreator(prs: GithubPullRequests, buildsDir: string, domainName: string): BuildCreator {
const buildCreator = new BuildCreator(buildsDir);
const postPreviewsComment = (pr: number, shas: string[]) => {
const body = shas.
map(sha => `You can preview ${sha} at https://pr${pr}-${sha}.${domainName}/.`).
join('\n');
return prs.addComment(pr, body);
};
buildCreator.on(CreatedBuildEvent.type, ({pr, sha, isPublic}: CreatedBuildEvent) => {
if (isPublic) {
postPreviewsComment(pr, [sha]);
}
});
buildCreator.on(ChangedPrVisibilityEvent.type, ({pr, shas, isPublic}: ChangedPrVisibilityEvent) => {
if (isPublic && shas.length) {
postPreviewsComment(pr, shas);
}
});
return buildCreator;
}
}

View File

@ -1,28 +0,0 @@
import * as express from 'express';
import {PreviewServerError} from './preview-error';
/**
* Update the response to report that an error has occurred.
* @param res The response to configure as an error.
* @param err The error that needs to be reported.
*/
export async function respondWithError(res: express.Response, err: any): Promise<void> {
if (!(err instanceof PreviewServerError)) {
err = new PreviewServerError(500, String((err && err.message) || err));
}
res.status(err.status);
return new Promise(resolve => res.end(err.message, resolve));
}
/**
* Throw an exception that describes the given error information.
* @param status The HTTP status code include in the error.
* @param error The error message to include in the error.
* @param req The request that triggered this error.
*/
export function throwRequestError(status: number, error: string, req: express.Request): never {
const message = `${error} in request: ${req.method} ${req.originalUrl}` +
(!req.body ? '' : ` ${JSON.stringify(req.body)}`);
throw new PreviewServerError(status, message);
}

View File

@ -1,37 +0,0 @@
export const enum BuildNums {
BUILD_INFO_ERROR = 1,
BUILD_INFO_404,
BUILD_INFO_BUILD_FAILED,
BUILD_INFO_INVALID_GH_ORG,
BUILD_INFO_INVALID_GH_REPO,
CHANGED_FILES_ERROR,
CHANGED_FILES_404,
CHANGED_FILES_NONE,
BUILD_ARTIFACTS_ERROR,
BUILD_ARTIFACTS_404,
BUILD_ARTIFACTS_EMPTY,
BUILD_ARTIFACTS_MISSING,
DOWNLOAD_ARTIFACT_ERROR,
DOWNLOAD_ARTIFACT_404,
DOWNLOAD_ARTIFACT_TOO_BIG,
TRUST_CHECK_ERROR,
TRUST_CHECK_UNTRUSTED,
TRUST_CHECK_TRUSTED_LABEL,
TRUST_CHECK_ACTIVE_TRUSTED_USER,
TRUST_CHECK_INACTIVE_TRUSTED_USER,
}
export const enum PrNums {
CHANGED_FILES_ERROR = 1,
CHANGED_FILES_404,
CHANGED_FILES_NONE,
TRUST_CHECK_ERROR,
TRUST_CHECK_UNTRUSTED,
TRUST_CHECK_TRUSTED_LABEL,
TRUST_CHECK_ACTIVE_TRUSTED_USER,
TRUST_CHECK_INACTIVE_TRUSTED_USER,
}
export const SHA = '1234567890'.repeat(4);
export const ALT_SHA = 'abcde'.repeat(8);
export const SIMILAR_SHA = SHA.slice(0, -1) + 'A';

View File

@ -1,10 +0,0 @@
declare module 'delete-empty' {
interface Options {
dryRun: boolean;
verbose: boolean;
filter: (filePath: string) => boolean;
}
export default function deleteEmpty(cwd: string, options?: Options): Promise<string[]>;
export default function deleteEmpty(cwd: string, options?: Options, callback?: (err: any, deleted: string[]) => void): void;
export function sync(cwd: string, options?: Options): string[];
}

View File

@ -1,237 +0,0 @@
// Imports
import * as cp from 'child_process';
import * as fs from 'fs';
import * as path from 'path';
import * as shell from 'shelljs';
import {AIO_DOWNLOADS_DIR, HIDDEN_DIR_PREFIX} from '../common/constants';
import {
AIO_BUILDS_DIR,
AIO_NGINX_PORT_HTTP,
AIO_NGINX_PORT_HTTPS,
AIO_WWW_USER,
} from '../common/env-variables';
import {computeShortSha, Logger} from '../common/utils';
// Interfaces - Types
export interface CmdResult { success: boolean; err: Error | null; stdout: string; stderr: string; }
export interface FileSpecs { content?: string; size?: number; }
export type CleanUpFn = () => void;
export type TestSuiteFactory = (scheme: string, port: number) => void;
export type VerifyCmdResultFn = (result: CmdResult) => void;
// Classes
class Helper {
// Properties - Protected
protected cleanUpFns: CleanUpFn[] = [];
protected portPerScheme: {[scheme: string]: number} = {
http: AIO_NGINX_PORT_HTTP,
https: AIO_NGINX_PORT_HTTPS,
};
private logger = new Logger('TestHelper');
// Constructor
constructor() {
shell.mkdir('-p', AIO_BUILDS_DIR);
shell.exec(`chown -R ${AIO_WWW_USER} ${AIO_BUILDS_DIR}`);
shell.mkdir('-p', AIO_DOWNLOADS_DIR);
shell.exec(`chown -R ${AIO_WWW_USER} ${AIO_DOWNLOADS_DIR}`);
}
// Methods - Public
public cleanUp(): void {
while (this.cleanUpFns.length) {
// Clean-up fns remove themselves from the list.
this.cleanUpFns[0]();
}
const leftoverDownloads = fs.readdirSync(AIO_DOWNLOADS_DIR);
const leftoverBuilds = fs.readdirSync(AIO_BUILDS_DIR);
if (leftoverDownloads.length) {
this.logger.log(`Downloads directory '${AIO_DOWNLOADS_DIR}' is not empty after clean-up.`, leftoverDownloads);
shell.rm('-rf', `${AIO_DOWNLOADS_DIR}/*`);
}
if (leftoverBuilds.length) {
this.logger.log(`Builds directory '${AIO_BUILDS_DIR}' is not empty after clean-up.`, leftoverBuilds);
shell.rm('-rf', `${AIO_BUILDS_DIR}/*`);
}
if (leftoverBuilds.length || leftoverDownloads.length) {
throw new Error(`Unexpected test files not cleaned up.`);
}
}
public createDummyBuild(pr: number, sha: string, isPublic = true, force = false, legacy = false): CleanUpFn {
const prDir = this.getPrDir(pr, isPublic);
const shaDir = this.getShaDir(prDir, sha, legacy);
const idxPath = path.join(shaDir, 'index.html');
const barPath = path.join(shaDir, 'foo', 'bar.js');
this.writeFile(idxPath, {content: `PR: ${pr} | SHA: ${sha} | File: /index.html`}, force);
this.writeFile(barPath, {content: `PR: ${pr} | SHA: ${sha} | File: /foo/bar.js`}, force);
shell.exec(`chown -R ${AIO_WWW_USER} ${prDir}`);
return this.createCleanUpFn(() => shell.rm('-rf', prDir));
}
public getPrDir(pr: number, isPublic: boolean): string {
const prDirName = isPublic ? '' + pr : HIDDEN_DIR_PREFIX + pr;
return path.join(AIO_BUILDS_DIR, prDirName);
}
public getShaDir(prDir: string, sha: string, legacy = false): string {
return path.join(prDir, legacy ? sha : computeShortSha(sha));
}
public readBuildFile(pr: number, sha: string, relFilePath: string, isPublic = true, legacy = false): string {
const shaDir = this.getShaDir(this.getPrDir(pr, isPublic), sha, legacy);
const absFilePath = path.join(shaDir, relFilePath);
return fs.readFileSync(absFilePath, 'utf8');
}
public runCmd(cmd: string, opts: cp.ExecOptions = {}): Promise<CmdResult> {
return new Promise(resolve => {
const proc = cp.exec(cmd, opts, (err, stdout, stderr) => resolve({success: !err, err, stdout, stderr}));
this.createCleanUpFn(() => proc.kill());
});
}
public runForAllSupportedSchemes(suiteFactory: TestSuiteFactory): void {
Object.entries(this.portPerScheme).forEach(([scheme, port]) => suiteFactory(scheme, port));
}
public verifyResponse(status: number, regex: string | RegExp = /^/): VerifyCmdResultFn {
return (result: CmdResult) => {
const [headers, body] = result.stdout.
split(/(?:\r?\n){2,}/).
map(s => s.trim()).
slice(-2); // In case of redirect, discard the previous headers.
// Only keep the last to sections (final headers and body).
if (!result.success) {
this.logger.log('Stdout:', result.stdout);
this.logger.error('Stderr:', result.stderr);
this.logger.error('Error:', result.err);
}
expect(result.success).toBe(true);
expect(headers).toMatch(new RegExp(`HTTP/(?:1\\.1|2) ${status} `));
expect(body).toMatch(regex);
};
}
public writeBuildFile(pr: number, sha: string, relFilePath: string, content: string, isPublic = true,
legacy = false): void {
const shaDir = this.getShaDir(this.getPrDir(pr, isPublic), sha, legacy);
const absFilePath = path.join(shaDir, relFilePath);
this.writeFile(absFilePath, {content}, true);
}
public writeFile(filePath: string, {content, size}: FileSpecs, force = false): void {
if (!force && fs.existsSync(filePath)) {
throw new Error(`Refusing to overwrite existing file '${filePath}'.`);
}
let cleanUpTarget = filePath;
while (!fs.existsSync(path.dirname(cleanUpTarget))) {
cleanUpTarget = path.dirname(cleanUpTarget);
}
shell.mkdir('-p', path.dirname(filePath));
if (size) {
// Create a file of the specified size.
cp.execSync(`fallocate -l ${size} ${filePath}`);
} else {
// Create a file with the specified content.
fs.writeFileSync(filePath, content || '');
}
shell.exec(`chown ${AIO_WWW_USER} ${filePath}`);
}
// Methods - Protected
protected createCleanUpFn(fn: () => void): CleanUpFn {
const cleanUpFn = () => {
const idx = this.cleanUpFns.indexOf(cleanUpFn);
if (idx !== -1) {
this.cleanUpFns.splice(idx, 1);
fn();
}
};
this.cleanUpFns.push(cleanUpFn);
return cleanUpFn;
}
}
interface DefaultCurlOptions {
defaultMethod?: CurlOptions['method'];
defaultOptions?: CurlOptions['options'];
defaultHeaders?: CurlOptions['headers'];
defaultData?: CurlOptions['data'];
defaultExtraPath?: CurlOptions['extraPath'];
}
interface CurlOptions {
method?: string;
options?: string;
headers?: string[];
data?: any;
url?: string;
extraPath?: string;
}
export function makeCurl(baseUrl: string, {
defaultMethod = 'POST',
defaultOptions = '',
defaultHeaders = ['Content-Type: application/json'],
defaultData = {},
defaultExtraPath = '',
}: DefaultCurlOptions = {}) {
return function curl({
method = defaultMethod,
options = defaultOptions,
headers = defaultHeaders,
data = defaultData,
url = baseUrl,
extraPath = defaultExtraPath,
}: CurlOptions) {
const dataString = data ? JSON.stringify(data) : '';
const cmd = `curl -iLX ${method} ` +
`${options} ` +
headers.map(header => `--header "${header}" `).join('') +
`--data '${dataString}' ` +
`${url}${extraPath}`;
return helper.runCmd(cmd);
};
}
export interface PayloadData {
data: {
payload: {
build_num: number,
build_parameters: {
CIRCLE_JOB: string;
};
};
};
}
export function payload(buildNum: number): PayloadData {
return {
data: {
payload: {
build_num: buildNum,
build_parameters: { CIRCLE_JOB: 'aio_preview' },
},
},
};
}
// Exports
export const helper = new Helper();

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