Skip to content

Commit 8fc8d3a

Browse files
kweinmeisterpartheagcf-owl-bot[bot]
authored
feat: add TypeScript samples support (#3029)
* chore: add owlbot * add new lines * Add owlbot.py * Rename .Owlbot.yaml to .OwlBot.yaml * Rename .Owlbot.lock.yaml to .OwlBot.lock.yaml * updates * test typeless bot * cleanup owlbot.py * add typescript support for tests * update copyright year * update copyright year * add ts-node dependency * update ts support * linting * remove comment * add npm build step * change build cmd * test updating pr * update comments * updated owlbot yaml * Update .github/.OwlBot.lock.yaml Co-authored-by: Anthonios Partheniou <[email protected]> * Update .OwlBot.lock.yaml * update owlbot yaml * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * remove extra comment * Update createJob.js * update tsconfig * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * add export filter * update filter * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * owlbot update * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * owlbot update * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * final updates * license * update linter * add build step * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * more updates * updates * eslint * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * fix gts check * reduce ecmascript version * fixes * add express types * skiplibcheck * remove global tsconfig * use root gts rules * add root=true * add extends to individual dirs * update workflow * reorganize * update ts workflow * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * revert ccai change * revert ccai change part deux * fix spacing * appengine include ts only * eslintignore * update package json * exclude test files * fix newline * updates based on feedback * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --------- Co-authored-by: Anthonios Partheniou <[email protected]> Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
1 parent ca863d0 commit 8fc8d3a

20 files changed

+417
-72
lines changed

.eslintrc.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
{
2+
"root": true,
23
"extends": "./node_modules/gts/",
34
"env": {
45
"mocha": true
56
},
67
"rules": {
78
"node/no-missing-require": ["off"],
8-
"node/no-unpublished-require": ["off"]
9+
"node/no-unpublished-require": ["off"],
10+
"node/no-unsupported-features/es-syntax": ["off"]
911
}
10-
}
12+
}

.github/.OwlBot.lock.yaml

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Copyright 2023 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
docker:
15+
image: gcr.io/cloud-devrel-public-resources/owlbot-nodejs:latest
16+
digest: sha256:fe04ae044dadf5ad88d979dbcc85e0e99372fb5d6316790341e6aca5e4e3fbc8
17+

.github/.OwlBot.yaml

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Copyright 2023 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
docker:
15+
image: gcr.io/cloud-devrel-public-resources/owlbot-nodejs:latest
16+
17+
begin-after-commit-hash: db31e3ff07d737b61ce968625aabbf660501688c

.github/workflows/appengine-typescript.yaml

+19-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
# Copyright 2023 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
115
name: appengine-typescript
216
on:
317
push:
@@ -26,6 +40,9 @@ jobs:
2640
contents: 'write'
2741
pull-requests: 'write'
2842
id-token: 'write'
43+
defaults:
44+
run:
45+
working-directory: appengine/typescript
2946
steps:
3047
- uses: actions/[email protected]
3148
with:
@@ -40,9 +57,9 @@ jobs:
4057
with:
4158
node-version: 16
4259
- run: npm install
43-
working-directory: appengine/typescript
60+
working-directory: .
61+
- run: npm install
4462
- run: npm test
45-
working-directory: appengine/typescript
4663
env:
4764
MOCHA_REPORTER_SUITENAME: appengine_typescript
4865
MOCHA_REPORTER_OUTPUT: appengine_typescript_sponge_log.xml

.github/workflows/scheduler.yaml

+20-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
# Copyright 2023 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
115
name: scheduler
216
on:
317
push:
@@ -26,6 +40,9 @@ jobs:
2640
contents: 'write'
2741
pull-requests: 'write'
2842
id-token: 'write'
43+
defaults:
44+
run:
45+
working-directory: scheduler
2946
steps:
3047
- uses: actions/[email protected]
3148
with:
@@ -40,9 +57,10 @@ jobs:
4057
with:
4158
node-version: 16
4259
- run: npm install
43-
working-directory: scheduler
60+
working-directory: .
61+
- run: npm install
62+
- run: npm run build
4463
- run: npm test
45-
working-directory: scheduler
4664
env:
4765
MOCHA_REPORTER_SUITENAME: scheduler
4866
MOCHA_REPORTER_OUTPUT: scheduler_sponge_log.xml

CONTRIBUTING.md

+19
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,25 @@ For new samples, a GitHub Actions workflow should be created to run your tests o
4242
> 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.
4343
4444

45+
### TypeScript Support
46+
47+
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.
48+
49+
If you choose to write a TypeScript based sample, please follow these guidelines:
50+
51+
* **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`.
52+
* **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.
53+
* **Linting** See the example [.eslintrc.yml](https://github.com/GoogleCloudPlatform/nodejs-docs-samples/tree/main/scheduler/.eslintrc.yml).
54+
* **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.
55+
* **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.
56+
* **tsconfig.json**:
57+
* Include a [tsconfig.json](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html) in the root of your sample directory.
58+
* You can find a minimal example [here](https://github.com/GoogleCloudPlatform/nodejs-docs-samples/tree/main/scheduler/tsconfig.json).
59+
* 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.
60+
* Add an `excludes` entry with your test files to avoid building `*.js` versions.
61+
62+
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.
63+
4564
### Style
4665

4766
The [Google Cloud Samples Style Guide][style-guide] is considered the primary

appengine/typescript/.eslintrc.json

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"parser": "@typescript-eslint/parser",
3+
"parserOptions": {
4+
"sourceType": "module"
5+
}
6+
}

appengine/typescript/index.ts

+3-4
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,11 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
import express = require('express');
16-
15+
import express, {Express, Request, Response} from 'express';
1716
const PORT = process.env.PORT || 8080;
18-
const app = express();
17+
const app: Express = express();
1918

20-
app.get('/', (req, res) => {
19+
app.get('/', (req: Request, res: Response) => {
2120
res.send('🎉 Hello TypeScript! 🎉');
2221
});
2322

appengine/typescript/package.json

+15-14
Original file line numberDiff line numberDiff line change
@@ -9,26 +9,27 @@
99
"node": "16.x.x"
1010
},
1111
"scripts": {
12-
"prepare": "npm run gcp-build",
13-
"pretest": "npm run gcp-build",
12+
"prepare": "npm run compile",
13+
"pretest": "npm run compile",
1414
"test": "mocha test/*.test.js --exit",
15-
"posttest": "npm run lint",
16-
"lint": "tslint -p .",
15+
"lint": "gts lint",
1716
"start": "node ./index.js",
18-
"gcp-build": "tsc -p .",
19-
"deploy": "gcloud app deploy"
17+
"deploy": "gcloud app deploy",
18+
"clean": "gts clean",
19+
"compile": "tsc -p .",
20+
"fix": "gts fix",
21+
"build": "tsc -p ."
2022
},
2123
"dependencies": {
22-
"express": "^4.16.3",
23-
"typescript": "^4.0.0"
24+
"express": "^4.16.3"
2425
},
2526
"devDependencies": {
26-
"mocha": "^10.0.0",
27-
"@types/express": "^4.16.0",
28-
"@types/node": "^14.14.7",
29-
"chai": "^4.2.0",
30-
"tslint": "^6.0.0",
31-
"typescript": "^4.0.0",
27+
"@types/express": "^4.17.17",
28+
"@types/node": "^18.13.0",
29+
"chai": "^4.3.7",
30+
"gts": "^3.1.1",
31+
"mocha": "^10.2.0",
32+
"typescript": "^4.9.5",
3233
"wait-port": "^1.0.0"
3334
}
3435
}

appengine/typescript/tsconfig.json

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
{
22
"compilerOptions": {
3-
"target": "es2016",
4-
"module": "commonjs"
5-
},
6-
"include": [
7-
"*.ts"
8-
]
3+
"module": "CommonJS",
4+
"target": "ESNext",
5+
"strict": true,
6+
"noImplicitAny": false,
7+
"esModuleInterop": true,
8+
"moduleResolution": "node"
9+
}
910
}

appengine/typescript/tslint.json

-6
This file was deleted.

owlbot.py

+148
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
# Copyright 2023 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import os
16+
import re
17+
import subprocess
18+
from pathlib import Path
19+
20+
from synthtool import shell
21+
from synthtool.log import logger
22+
23+
_TOOLS_DIRECTORY = "/synthtool"
24+
_EXCLUDED_DIRS = [r"node_modules", r"^\."]
25+
_TYPELESS_EXPRESSION = "s/export {};$/\\n/"
26+
_NPM_CONFIG_CACHE = "/var/tmp/.npm"
27+
28+
29+
def walk_through_owlbot_dirs(dir: Path, search_for_changed_files: bool) -> list[str]:
30+
"""
31+
Walks through all sample directories
32+
Returns:
33+
A list of directories
34+
"""
35+
owlbot_dirs: list[str] = []
36+
packages_to_exclude = _EXCLUDED_DIRS
37+
if search_for_changed_files:
38+
try:
39+
# Need to run this step first in the post processor since we only clone
40+
# the branch the PR is on in the Docker container
41+
output = subprocess.run(
42+
["git", "fetch", "origin", "main:main", "--deepen=200"], check=False
43+
)
44+
output.check_returncode()
45+
except subprocess.CalledProcessError as e:
46+
if e.returncode == 128:
47+
logger.info(f"Error: ${e.output}; skipping fetching main")
48+
else:
49+
raise e
50+
for path_object in dir.glob("**/package.json"):
51+
object_dir = str(Path(path_object).parents[0])
52+
if (
53+
path_object.is_file()
54+
and object_dir != str(dir)
55+
and not re.search(
56+
"(?:% s)" % "|".join(packages_to_exclude), str(Path(path_object))
57+
)
58+
):
59+
if search_for_changed_files:
60+
if (
61+
subprocess.run(
62+
["git", "diff", "--quiet", "main...", object_dir], check=False
63+
).returncode
64+
== 1
65+
):
66+
owlbot_dirs.append(object_dir)
67+
else:
68+
owlbot_dirs.append(object_dir)
69+
for path_object in dir.glob("owl-bot-staging/*"):
70+
owlbot_dirs.append(
71+
f"{Path(path_object).parents[1]}/packages/{Path(path_object).name}"
72+
)
73+
return owlbot_dirs
74+
75+
76+
def typeless_samples_hermetic(
77+
output_path: str, targets: str, hide_output: bool = False
78+
) -> None:
79+
"""
80+
Converts TypeScript samples in the current Node.js library
81+
to JavaScript samples. Run this step before fix() and friends.
82+
Assumes that typeless-sample-bot is already installed in a well
83+
known location on disk (node_modules/.bin).
84+
This is currently an optional, opt-in part of an individual repo's
85+
OwlBot.py, and must be called from there before calling owlbot_main.
86+
"""
87+
logger.debug("Run typeless sample bot")
88+
shell.run(
89+
[
90+
f"{_TOOLS_DIRECTORY}/node_modules/.bin/typeless-sample-bot",
91+
"--outputpath",
92+
output_path,
93+
"--targets",
94+
targets,
95+
"--recursive",
96+
],
97+
check=False,
98+
hide_output=hide_output,
99+
)
100+
101+
102+
def trim(targets: str, hide_output: bool = False) -> None:
103+
"""
104+
Fixes the formatting of generated JS files
105+
"""
106+
logger.debug("Trim generated files")
107+
for file in os.listdir(f"{targets}"):
108+
if file.endswith(".js"):
109+
logger.debug(f"Updating {targets}/{file}")
110+
shell.run(
111+
["sed", "-i", "-e", _TYPELESS_EXPRESSION, f"{targets}/{file}"],
112+
check=False,
113+
hide_output=hide_output,
114+
)
115+
116+
117+
def fix_hermetic(targets=".", hide_output=False):
118+
"""
119+
Fixes the formatting in the current Node.js library. It assumes that gts
120+
is already installed in a well known location on disk (node_modules/.bin).
121+
"""
122+
logger.debug("Copy eslint config")
123+
shell.run(
124+
["cp", "-r", f"{_TOOLS_DIRECTORY}/node_modules", "."],
125+
check=True,
126+
hide_output=hide_output,
127+
)
128+
logger.debug("Running fix...")
129+
shell.run(
130+
[f"{_TOOLS_DIRECTORY}/node_modules/.bin/gts", "fix", f"{targets}"],
131+
check=False,
132+
hide_output=hide_output,
133+
)
134+
135+
136+
# Avoid "Your cache folder contains root-owned files" error
137+
os.environ["npm_config_cache"] = _NPM_CONFIG_CACHE
138+
139+
# Retrieve list of directories
140+
dirs: list[str] = walk_through_owlbot_dirs(Path.cwd(), search_for_changed_files=True)
141+
for d in dirs:
142+
logger.debug(f"Directory: {d}")
143+
# Run typeless bot to convert from TS -> JS
144+
typeless_samples_hermetic(output_path=d, targets=d)
145+
# Remove extra characters
146+
trim(targets=d)
147+
# Fix formatting
148+
fix_hermetic(targets=d)

0 commit comments

Comments
 (0)