Skip to content

Commit

Permalink
[TypeSpec] Add @azure-tools/typespec-validation tool
Browse files Browse the repository at this point in the history
- Replaces bash script used in "specs - typespec - validation" pipeline
- Also intended for spec authors to run on their dev machines
  • Loading branch information
ckairen authored Jun 28, 2023
1 parent 41a6298 commit a862f4f
Show file tree
Hide file tree
Showing 8 changed files with 213 additions and 52 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,7 @@ warnings.txt
*.js.map
*.d.ts.map
*.bak

# Eng Tools
eng/tools/TypeSpecValidation/dist
!eng/tools/TypeSpecValidation/cmd/*.js
45 changes: 1 addition & 44 deletions eng/pipelines/templates/steps/typespec-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,49 +5,6 @@ parameters:
type: string

steps:
- script: |
RED='\033[0;31m'
exit_code=0
expected_npm_prefix=$(pwd)
# Log commands before running
set -x
pushd ${{parameters.Folder}} || exit_code=1
actual_npm_prefix=$(npm prefix)
if [ "$expected_npm_prefix" != "$actual_npm_prefix" ]; then
echo -e "\n${RED}ERROR: TypeSpec folders MUST NOT contain a package.json, and instead MUST rely on the package.json at repo root."
echo -e "${RED}Expected npm prefix: $expected_npm_prefix\n${RED}Actual npm prefix: $actual_npm_prefix\n"
exit_code=1
fi
# Pass "--no" to ensure npx does not install anything (TypeSpec compiler should already be installed)
if test -f main.tsp; then
npx --no tsp compile . --warn-as-error || exit_code=1
fi
if test -f client.tsp; then
npx --no tsp compile client.tsp --no-emit --warn-as-error || exit_code=1
fi
# Format parent folder to include shared files
npx tsp format ../**/*.tsp
popd
# If any files are added, changed, or deleted, print diff and return error
if [ -n "$(git status --porcelain)" ]; then
git status
git diff
exit_code=1
fi
# Revert any changes
git restore .
git clean -df
exit $exit_code
- script: npx --no tsv ${{parameters.Folder}}
displayName: ${{parameters.DisplayName}}
condition: succeededOrFailed()
5 changes: 5 additions & 0 deletions eng/tools/TypeSpecValidation/cmd/tsv.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env node

import { main } from "../dist/TypeSpecValidation.js"

await main();
16 changes: 16 additions & 0 deletions eng/tools/TypeSpecValidation/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "@azure-tools/typespec-validation",
"private": true,
"version": "0.0.1",
"type": "module",
"main": "dist/TypeSpecValidation.js",
"bin": {
"tsv": "cmd/tsv.js"
},
"dependencies": {
"simple-git": "^3.16.0"
},
"devDependencies": {
"@types/node": "^18.16.18"
}
}
88 changes: 88 additions & 0 deletions eng/tools/TypeSpecValidation/src/TypeSpecValidation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import {exec} from "child_process";
import {access} from "fs/promises"
import {parseArgs, ParseArgsConfig} from 'node:util';
import path from "path";
import {simpleGit} from 'simple-git';

async function runCmd(cmd:string, cwd:string) {
console.log(`run command:${cmd}`)
const { err, stdout, stderr } = await new Promise((res) =>
exec(
cmd,
{ encoding: "utf8", maxBuffer: 1024 * 1024 * 64, cwd: cwd},
(err: unknown, stdout: unknown, stderr: unknown) =>
res({ err: err, stdout: stdout, stderr: stderr })
)
) as any;
let resultString = stderr + stdout;
console.log("Stdout output:")
console.log(stdout)
if (stderr) {
console.log("Stderr output:")
console.log(stderr)
}
if (stderr || err) {
throw new Error(err);
}
return resultString as string;
}

async function checkFileExists(file:string) {
return access(file)
.then(() => true)
.catch(() => false)
}

export async function main() {
const args = process.argv.slice(2);
const options = {
folder: {
type: 'string',
short: 'f',
},
};
const parsedArgs = parseArgs({ args, options, allowPositionals: true} as ParseArgsConfig);
const folder = parsedArgs.positionals[0];
console.log("Running TypeSpecValidation on folder:", folder);

// Verify all specs are using root level pacakge.json
let expected_npm_prefix = process.cwd()
const actual_npm_prefix = (await runCmd(`npm prefix`, folder)).trim()
if (expected_npm_prefix !== actual_npm_prefix) {
console.log("ERROR: TypeSpec folders MUST NOT contain a package.json, and instead MUST rely on the package.json at repo root.")
throw new Error ("Expected npm prefix: " + expected_npm_prefix +"\nActual npm prefix: " + actual_npm_prefix)
}

// Spec compilation check
if (await checkFileExists(path.join(folder, "main.tsp"))) {
await runCmd(
`npx --no tsp compile . --warn-as-error`,
folder
);
}
if (await checkFileExists(path.join(folder, "client.tsp"))) {
await runCmd(
`npx --no tsp compile client.tsp --no-emit --warn-as-error`,
folder
);
}

// Format parent folder to include shared files
await runCmd(
`npx tsp format ../**/*.tsp`,
folder
);

// Verify generated swagger file is in sync with one on disk
const git = simpleGit();
let gitStatusIsClean = await (await git.status(['--porcelain'])).isClean()
if (!gitStatusIsClean) {
let gitStatus = await git.status()
let gitDiff = await git.diff()
console.log("git status")
console.log(gitStatus)
console.log("git diff")
console.log(gitDiff)
throw new Error("Generated swagger file does not match swagger file on disk")
}
}
8 changes: 8 additions & 0 deletions eng/tools/TypeSpecValidation/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"target": "ES6",
"module": "Node16",
"outDir": "./dist"
}
}
93 changes: 86 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@
"@azure/avocado": "^0.8.4",
"@types/prettier": "^2.7.2",
"prettier": "^2.8.8",
"typescript": "~5.0.4"
"typescript": "~5.0.4",
"typespec-validation": "file:eng/tools/TypeSpecValidation"
},
"scripts": {
"postinstall": "npx --no tsc -- -p eng/tools/TypeSpecValidation"
},
"private": true
}

0 comments on commit a862f4f

Please sign in to comment.