diff --git a/.eslintrc.json b/.eslintrc.json index 73acfd71ca..5d0e8a7fa1 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,10 +1,12 @@ { + "root": true, "extends": "./node_modules/gts/", "env": { "mocha": true }, "rules": { "node/no-missing-require": ["off"], - "node/no-unpublished-require": ["off"] + "node/no-unpublished-require": ["off"], + "node/no-unsupported-features/es-syntax": ["off"] } -} \ No newline at end of file +} diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml new file mode 100644 index 0000000000..a231469f2d --- /dev/null +++ b/.github/.OwlBot.lock.yaml @@ -0,0 +1,17 @@ +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +docker: + image: gcr.io/cloud-devrel-public-resources/owlbot-nodejs:latest + digest: sha256:fe04ae044dadf5ad88d979dbcc85e0e99372fb5d6316790341e6aca5e4e3fbc8 + diff --git a/.github/.OwlBot.yaml b/.github/.OwlBot.yaml new file mode 100644 index 0000000000..b550fa110e --- /dev/null +++ b/.github/.OwlBot.yaml @@ -0,0 +1,17 @@ +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +docker: + image: gcr.io/cloud-devrel-public-resources/owlbot-nodejs:latest + +begin-after-commit-hash: db31e3ff07d737b61ce968625aabbf660501688c diff --git a/.github/workflows/appengine-typescript.yaml b/.github/workflows/appengine-typescript.yaml index 2cd2c6a34c..71430f16ee 100644 --- a/.github/workflows/appengine-typescript.yaml +++ b/.github/workflows/appengine-typescript.yaml @@ -1,3 +1,17 @@ +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + name: appengine-typescript on: push: @@ -26,6 +40,9 @@ jobs: contents: 'write' pull-requests: 'write' id-token: 'write' + defaults: + run: + working-directory: appengine/typescript steps: - uses: actions/checkout@v3.3.0 with: @@ -40,9 +57,9 @@ jobs: with: node-version: 16 - run: npm install - working-directory: appengine/typescript + working-directory: . + - run: npm install - run: npm test - working-directory: appengine/typescript env: MOCHA_REPORTER_SUITENAME: appengine_typescript MOCHA_REPORTER_OUTPUT: appengine_typescript_sponge_log.xml diff --git a/.github/workflows/scheduler.yaml b/.github/workflows/scheduler.yaml index 86a59a6db6..be2bc7dfda 100644 --- a/.github/workflows/scheduler.yaml +++ b/.github/workflows/scheduler.yaml @@ -1,3 +1,17 @@ +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + name: scheduler on: push: @@ -26,6 +40,9 @@ jobs: contents: 'write' pull-requests: 'write' id-token: 'write' + defaults: + run: + working-directory: scheduler steps: - uses: actions/checkout@v3.3.0 with: @@ -40,9 +57,10 @@ jobs: with: node-version: 16 - run: npm install - working-directory: scheduler + working-directory: . + - run: npm install + - run: npm run build - run: npm test - working-directory: scheduler env: MOCHA_REPORTER_SUITENAME: scheduler MOCHA_REPORTER_OUTPUT: scheduler_sponge_log.xml diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 11bdcdf6a4..4d9c634de3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -42,6 +42,25 @@ For new samples, a GitHub Actions workflow should be created to run your tests o > All tests need a corresponding job file outside of GitHub. If you are a Googler, please provide the CL alongside your PR. See the internal codelab for Kokoro for details. If you don't work at Google, the person reviewing your PR will create the job config for you. +### TypeScript Support + +This repository also supports TypeScript samples. We use the [typeless-sample-bot](https://github.com/googleapis/google-cloud-node/tree/main/packages/typeless-sample-bot) to convert TypeScript samples to pure JavaScript, which avoids having to maintain both TypeScript and JavaScript variants. + +If you choose to write a TypeScript based sample, please follow these guidelines: + +* **Testing**: Use a `.ts` test runner, which is executed by the `npm test` command `mocha --require ts-node/register test/*.ts` in `package.json`. **Do not** check in any generated `.js` tests from running `npm build`. +* **Imports** Use an `import` statement at the beginning of the file to enable importing types. Within each "region tag," import required libraries with `required`. Each of these region tag sections are directly embedded in the Cloud documentation, so the imports help show our users which libraries are needed. +* **Linting** See the example [.eslintrc.yml](https://github.com/GoogleCloudPlatform/nodejs-docs-samples/tree/main/scheduler/.eslintrc.yml). +* **JavaScript** Do not modify any `.js` files. The [typeless-sample-bot](https://github.com/googleapis/google-cloud-node/tree/main/packages/typeless-sample-bot) will overwrite them as it converts from TypeScript into JavaScript. +* **package.json**: See a full set of npm targets in the scheduler [package.json](https://github.com/GoogleCloudPlatform/nodejs-docs-samples/tree/main/scheduler/package.json). Include any relevant `@types` in your `devDependencies` section. +* **tsconfig.json**: + * Include a [tsconfig.json](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html) in the root of your sample directory. + * You can find a minimal example [here](https://github.com/GoogleCloudPlatform/nodejs-docs-samples/tree/main/scheduler/tsconfig.json). + * It is recommended to set `noImplicitAny` to `false`, but it may be needed to set this to `true` if you haven't fully migrated the sample to TypeScript. + * Add an `excludes` entry with your test files to avoid building `*.js` versions. + +You can look at the [scheduler](https://github.com/GoogleCloudPlatform/nodejs-docs-samples/tree/main/scheduler) sample directory for an example of a TypeScript sample and its matching test runner. + ### Style The [Google Cloud Samples Style Guide][style-guide] is considered the primary diff --git a/appengine/typescript/.eslintrc.json b/appengine/typescript/.eslintrc.json new file mode 100644 index 0000000000..3f5de17b1b --- /dev/null +++ b/appengine/typescript/.eslintrc.json @@ -0,0 +1,6 @@ +{ + "parser": "@typescript-eslint/parser", + "parserOptions": { + "sourceType": "module" + } +} diff --git a/appengine/typescript/index.ts b/appengine/typescript/index.ts index 4e1d823d3a..82a7ef100e 100644 --- a/appengine/typescript/index.ts +++ b/appengine/typescript/index.ts @@ -12,12 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -import express = require('express'); - +import express, {Express, Request, Response} from 'express'; const PORT = process.env.PORT || 8080; -const app = express(); +const app: Express = express(); -app.get('/', (req, res) => { +app.get('/', (req: Request, res: Response) => { res.send('🎉 Hello TypeScript! 🎉'); }); diff --git a/appengine/typescript/package.json b/appengine/typescript/package.json index 10c3b2c696..156b2f786e 100644 --- a/appengine/typescript/package.json +++ b/appengine/typescript/package.json @@ -9,26 +9,27 @@ "node": "16.x.x" }, "scripts": { - "prepare": "npm run gcp-build", - "pretest": "npm run gcp-build", + "prepare": "npm run compile", + "pretest": "npm run compile", "test": "mocha test/*.test.js --exit", - "posttest": "npm run lint", - "lint": "tslint -p .", + "lint": "gts lint", "start": "node ./index.js", - "gcp-build": "tsc -p .", - "deploy": "gcloud app deploy" + "deploy": "gcloud app deploy", + "clean": "gts clean", + "compile": "tsc -p .", + "fix": "gts fix", + "build": "tsc -p ." }, "dependencies": { - "express": "^4.16.3", - "typescript": "^4.0.0" + "express": "^4.16.3" }, "devDependencies": { - "mocha": "^10.0.0", - "@types/express": "^4.16.0", - "@types/node": "^14.14.7", - "chai": "^4.2.0", - "tslint": "^6.0.0", - "typescript": "^4.0.0", + "@types/express": "^4.17.17", + "@types/node": "^18.13.0", + "chai": "^4.3.7", + "gts": "^3.1.1", + "mocha": "^10.2.0", + "typescript": "^4.9.5", "wait-port": "^1.0.0" } } diff --git a/appengine/typescript/tsconfig.json b/appengine/typescript/tsconfig.json index a9e8642431..6303936305 100644 --- a/appengine/typescript/tsconfig.json +++ b/appengine/typescript/tsconfig.json @@ -1,9 +1,10 @@ { "compilerOptions": { - "target": "es2016", - "module": "commonjs" - }, - "include": [ - "*.ts" - ] + "module": "CommonJS", + "target": "ESNext", + "strict": true, + "noImplicitAny": false, + "esModuleInterop": true, + "moduleResolution": "node" + } } diff --git a/appengine/typescript/tslint.json b/appengine/typescript/tslint.json deleted file mode 100644 index c6e7accdfa..0000000000 --- a/appengine/typescript/tslint.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": "tslint:recommended", - "rules": { - "no-console": false - } -} diff --git a/owlbot.py b/owlbot.py new file mode 100644 index 0000000000..ed5ece8942 --- /dev/null +++ b/owlbot.py @@ -0,0 +1,148 @@ +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import re +import subprocess +from pathlib import Path + +from synthtool import shell +from synthtool.log import logger + +_TOOLS_DIRECTORY = "/synthtool" +_EXCLUDED_DIRS = [r"node_modules", r"^\."] +_TYPELESS_EXPRESSION = "s/export {};$/\\n/" +_NPM_CONFIG_CACHE = "/var/tmp/.npm" + + +def walk_through_owlbot_dirs(dir: Path, search_for_changed_files: bool) -> list[str]: + """ + Walks through all sample directories + Returns: + A list of directories + """ + owlbot_dirs: list[str] = [] + packages_to_exclude = _EXCLUDED_DIRS + if search_for_changed_files: + try: + # Need to run this step first in the post processor since we only clone + # the branch the PR is on in the Docker container + output = subprocess.run( + ["git", "fetch", "origin", "main:main", "--deepen=200"], check=False + ) + output.check_returncode() + except subprocess.CalledProcessError as e: + if e.returncode == 128: + logger.info(f"Error: ${e.output}; skipping fetching main") + else: + raise e + for path_object in dir.glob("**/package.json"): + object_dir = str(Path(path_object).parents[0]) + if ( + path_object.is_file() + and object_dir != str(dir) + and not re.search( + "(?:% s)" % "|".join(packages_to_exclude), str(Path(path_object)) + ) + ): + if search_for_changed_files: + if ( + subprocess.run( + ["git", "diff", "--quiet", "main...", object_dir], check=False + ).returncode + == 1 + ): + owlbot_dirs.append(object_dir) + else: + owlbot_dirs.append(object_dir) + for path_object in dir.glob("owl-bot-staging/*"): + owlbot_dirs.append( + f"{Path(path_object).parents[1]}/packages/{Path(path_object).name}" + ) + return owlbot_dirs + + +def typeless_samples_hermetic( + output_path: str, targets: str, hide_output: bool = False +) -> None: + """ + Converts TypeScript samples in the current Node.js library + to JavaScript samples. Run this step before fix() and friends. + Assumes that typeless-sample-bot is already installed in a well + known location on disk (node_modules/.bin). + This is currently an optional, opt-in part of an individual repo's + OwlBot.py, and must be called from there before calling owlbot_main. + """ + logger.debug("Run typeless sample bot") + shell.run( + [ + f"{_TOOLS_DIRECTORY}/node_modules/.bin/typeless-sample-bot", + "--outputpath", + output_path, + "--targets", + targets, + "--recursive", + ], + check=False, + hide_output=hide_output, + ) + + +def trim(targets: str, hide_output: bool = False) -> None: + """ + Fixes the formatting of generated JS files + """ + logger.debug("Trim generated files") + for file in os.listdir(f"{targets}"): + if file.endswith(".js"): + logger.debug(f"Updating {targets}/{file}") + shell.run( + ["sed", "-i", "-e", _TYPELESS_EXPRESSION, f"{targets}/{file}"], + check=False, + hide_output=hide_output, + ) + + +def fix_hermetic(targets=".", hide_output=False): + """ + Fixes the formatting in the current Node.js library. It assumes that gts + is already installed in a well known location on disk (node_modules/.bin). + """ + logger.debug("Copy eslint config") + shell.run( + ["cp", "-r", f"{_TOOLS_DIRECTORY}/node_modules", "."], + check=True, + hide_output=hide_output, + ) + logger.debug("Running fix...") + shell.run( + [f"{_TOOLS_DIRECTORY}/node_modules/.bin/gts", "fix", f"{targets}"], + check=False, + hide_output=hide_output, + ) + + +# Avoid "Your cache folder contains root-owned files" error +os.environ["npm_config_cache"] = _NPM_CONFIG_CACHE + +# Retrieve list of directories +dirs: list[str] = walk_through_owlbot_dirs(Path.cwd(), search_for_changed_files=True) +for d in dirs: + logger.debug(f"Directory: {d}") + # Run typeless bot to convert from TS -> JS + typeless_samples_hermetic(output_path=d, targets=d) + # Remove extra characters + trim(targets=d) + # Fix formatting + fix_hermetic(targets=d) diff --git a/package.json b/package.json index 2ab18ef324..caf62fd74a 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,8 @@ "generate-ci": "node .github/workflows/generate.js" }, "devDependencies": { + "@typescript-eslint/eslint-plugin": "^5.52.0", + "@typescript-eslint/parser": "^5.52.0", "gts": "^3.1.0", "nunjucks": "^3.2.3", "typescript": "^4.6.4" @@ -31,6 +33,7 @@ "chai": "^4.3.7", "express": "^4.18.2", "mocha": "^10.1.0", + "prettier": "^2.8.4", "uuid": "^9.0.0" } } diff --git a/scheduler/.eslintrc.json b/scheduler/.eslintrc.json new file mode 100644 index 0000000000..771dd3f55c --- /dev/null +++ b/scheduler/.eslintrc.json @@ -0,0 +1,6 @@ +{ + "parser": "@typescript-eslint/parser", + "parserOptions": { + "sourceType": "module" + } +} \ No newline at end of file diff --git a/scheduler/.eslintrc.yml b/scheduler/.eslintrc.yml deleted file mode 100644 index 282535f55f..0000000000 --- a/scheduler/.eslintrc.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -rules: - no-console: off diff --git a/scheduler/createJob.js b/scheduler/createJob.js index 2b29031351..d7d175e2dc 100644 --- a/scheduler/createJob.js +++ b/scheduler/createJob.js @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -17,21 +17,24 @@ // description: Create a job that posts to /log_payload on an App Engine service. // usage: node createJob.js [project-id] [location-id] [app-engine-service-id] +const args = process.argv.slice(2); +const [projectId, locationId, serviceId] = args; + +// [START cloudscheduler_create_job] +const {CloudSchedulerClient} = require('@google-cloud/scheduler'); + +// TODO(developer): Uncomment and set the following variables +// const projectId = "PROJECT_ID" +// const locationId = "LOCATION_ID" +// const serviceId = "my-serivce" + +// Create a client. +const client = new CloudSchedulerClient(); + /** * Create a job with an App Engine target via the Cloud Scheduler API */ async function createJob(projectId, locationId, serviceId) { - // [START cloudscheduler_create_job] - const scheduler = require('@google-cloud/scheduler'); - - // Create a client. - const client = new scheduler.CloudSchedulerClient(); - - // TODO(developer): Uncomment and set the following variables - // const projectId = "PROJECT_ID" - // const locationId = "LOCATION_ID" - // const serviceId = "my-serivce" - // Construct the fully qualified location path. const parent = client.locationPath(projectId, locationId); @@ -57,11 +60,11 @@ async function createJob(projectId, locationId, serviceId) { // Use the client to send the job creation request. const [response] = await client.createJob(request); console.log(`Created job: ${response.name}`); - // [END cloudscheduler_create_job] } -const args = process.argv.slice(2); -createJob(...args).catch(err => { +createJob(projectId, locationId, serviceId).catch(err => { console.error(err.message); process.exitCode = 1; }); + +// [END cloudscheduler_create_job] diff --git a/scheduler/createJob.ts b/scheduler/createJob.ts new file mode 100644 index 0000000000..8d9b5e53e5 --- /dev/null +++ b/scheduler/createJob.ts @@ -0,0 +1,74 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// sample-metadata: +// title: Create Job +// description: Create a job that posts to /log_payload on an App Engine service. +// usage: node createJob.js [project-id] [location-id] [app-engine-service-id] + +const args = process.argv.slice(2); +const [projectId, locationId, serviceId] = args; + +// [START cloudscheduler_create_job] +import {protos, CloudSchedulerClient} from '@google-cloud/scheduler'; + +// TODO(developer): Uncomment and set the following variables +// const projectId = "PROJECT_ID" +// const locationId = "LOCATION_ID" +// const serviceId = "my-serivce" + +// Create a client. +const client: CloudSchedulerClient = new CloudSchedulerClient(); + +/** + * Create a job with an App Engine target via the Cloud Scheduler API + */ +async function createJob( + projectId: string, + locationId: string, + serviceId: string +) { + // Construct the fully qualified location path. + const parent: string = client.locationPath(projectId, locationId); + + // Construct the request body. + const job: protos.google.cloud.scheduler.v1.IJob = { + appEngineHttpTarget: { + appEngineRouting: { + service: serviceId, + }, + relativeUri: '/log_payload', + httpMethod: 'POST', + body: Buffer.from('Hello World'), + }, + schedule: '* * * * *', + timeZone: 'America/Los_Angeles', + }; + + const request: protos.google.cloud.scheduler.v1.ICreateJobRequest = { + parent: parent, + job: job, + }; + + // Use the client to send the job creation request. + const [response] = await client.createJob(request); + console.log(`Created job: ${response.name}`); +} + +createJob(projectId, locationId, serviceId).catch(err => { + console.error(err.message); + process.exitCode = 1; +}); + +// [END cloudscheduler_create_job] diff --git a/scheduler/package.json b/scheduler/package.json index b85eabd34a..55d579e4ec 100644 --- a/scheduler/package.json +++ b/scheduler/package.json @@ -6,20 +6,31 @@ "node": ">=12.0.0" }, "files": [ - "*.js" + "*.js", + "*.ts" ], "license": "Apache-2.0", "scripts": { - "start": "node app.js", - "test": "mocha --timeout 10000 --exit" + "build": "tsc -p .", + "fix": "gts fix", + "lint": "gts lint", + "test": "mocha --require ts-node/register test/*.ts --timeout 10000 --exit" }, "dependencies": { - "@google-cloud/scheduler": "^3.0.5", - "body-parser": "^1.18.3", - "express": "^4.16.4" + "@google-cloud/scheduler": "^3.2.1", + "body-parser": "^1.20.1", + "express": "^4.18.2" }, "devDependencies": { - "chai": "^4.2.0", - "mocha": "^10.0.0" + "@types/chai": "^4.3.4", + "@types/express": "^4.17.17", + "@types/mocha": "^10.0.1", + "@types/node": "^18.13.0", + "chai": "^4.3.7", + "eslint": "^8.34.0", + "gts": "^3.1.1", + "mocha": "^10.2.0", + "ts-node": "^10.9.1", + "typescript": "^4.9.5" } } diff --git a/scheduler/test/test.samples.js b/scheduler/test/test.samples.ts similarity index 65% rename from scheduler/test/test.samples.js rename to scheduler/test/test.samples.ts index 0479a4708f..f7e6191e4c 100644 --- a/scheduler/test/test.samples.js +++ b/scheduler/test/test.samples.ts @@ -1,4 +1,4 @@ -// Copyright 2018 Google LLC +// Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -14,25 +14,25 @@ 'use strict'; -const {CloudSchedulerClient} = require('@google-cloud/scheduler'); -const {assert} = require('chai'); -const {describe, it, before} = require('mocha'); -const cp = require('child_process'); +import {CloudSchedulerClient} from '@google-cloud/scheduler'; +import {assert} from 'chai'; +import {after, before, describe, it} from 'mocha'; +import * as cp from 'child_process'; -const client = new CloudSchedulerClient(); -const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); +const client: CloudSchedulerClient = new CloudSchedulerClient(); +const execSync = (cmd: string) => cp.execSync(cmd, {encoding: 'utf-8'}); const LOCATION_ID = process.env.LOCATION_ID || 'us-central1'; const SERVICE_ID = 'my-service'; describe('Cloud Scheduler Sample Tests', () => { - let PROJECT_ID, stdout; + let PROJECT_ID: string, stdout: string; before(async () => { PROJECT_ID = await client.getProjectId(); }); after(async () => { - const jobName = stdout && stdout.trim().split(' ').slice(-1); + const jobName = (stdout && stdout.trim().split(' ').slice(-1))[0]; if (jobName) { await client.deleteJob({name: jobName}); } @@ -40,7 +40,7 @@ describe('Cloud Scheduler Sample Tests', () => { it('should create a scheduler job', async () => { stdout = execSync( - `node createJob.js ${PROJECT_ID} ${LOCATION_ID} ${SERVICE_ID}` + `node --loader ts-node/esm createJob.ts ${PROJECT_ID} ${LOCATION_ID} ${SERVICE_ID}` ); assert.match(stdout, /Created job/); }); diff --git a/scheduler/tsconfig.json b/scheduler/tsconfig.json new file mode 100644 index 0000000000..e30332e556 --- /dev/null +++ b/scheduler/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "ESNext", + "strict": true, + "noImplicitAny": false, + "esModuleInterop": true, + "moduleResolution": "node" + }, + "exclude": [ + "**/*test*/*.ts" + ] + }