Skip to content

Latest commit

 

History

History
198 lines (134 loc) · 11.4 KB

TypeSpec-Project-Scripts.md

File metadata and controls

198 lines (134 loc) · 11.4 KB

Integrating with typespec sync and generate scripts

There are three common scripts provided for each language to be able to generate from within the language repo and use the remote typespec definition in the spec repo.

One time language repo setup

There are three things that these common scripts expect to be set up in your language repo before they will run correctly.

  1. Make sure your .gitignore is ignoring the TempTypeSpecFiles
  2. Create a common emitter-package.json for your language
  3. Write the language specific hooks in Language-Settings.ps1

TempTypeSpecFiles

You should add a new entry in your .gitignore for your repo so that none of these files are accidentally checked in if -SaveInputs flag is passed in.

# .gitignore file
TempTypeSpecFiles/

emitter-package.json

This will be the package.json that gets used when npm install is called. This replaces the package.json checked into the spec repo and allows each language to fix the version of their emitter to be the same for all packages in their repo. The file should be checked into this location ./eng/emitter-package.json

Example

{
    "main": "dist/src/index.js",
    "dependencies": {
      "@azure-tools/typespec-csharp": "0.1.11-beta.20230123.1"
    }
}

Note that tsp compile currently requires the "main" line to be there.

Emitter additionalProperties

The -SaveInputs flag will get forwarded to your emitter as --option @azure-tools/typespec-csharp.save-inputs=true. If your emitter or generator creates any temporary files similar to CodeModel.yaml and Configuration.json from autorest then you should honor this flag and not delete those files. If your emitter does not does not have any of these files you can ignore this flag but be sure you have additionalProperties set to true or have added save-inputs into your schema.

Language-Settings.ps1

There are three methods you can write in your language repo to adjust the behavior of the scripts one of which is required. For each of these replace ${Language} with the language identifier in your repo. If you don't know what this is you can look at ./eng/scripts/Language-Settings.ps1 in your language repo and you will find other functions that match this pattern that already exist.

Get-${Language}-EmitterName (Required)

This function simply returns the emitter name string.

Example

function Get-dotnet-EmitterName() {
  return "@azure-tools/typespec-csharp"
}

Get-${Language}-EmitterPackageJsonPath (Optional)

This function allows you to specify the location and name of the emitter package.json to use. If this is omitted the script will assume the default location listed above ./eng/emitter-package.json. The path must be absolute.

Example

function Get-dotnet-EmitterPackageJsonPath() {
  return "D:\SomeOtherLocation\some-other-emitter-package.json"
}

Get-${Language}-EmitterAdditionalOptions (Optional)

This function allows you to append additional --option arguments that will be passed into typespec compile. One example of this is the emitter-output-dir. For dotnet we want the location of the generated files to be {projectDir}/src however in other languages emitter-output-dir would be $projectDirectory. This method will take in a fully qualified path to the project directory so you can construct your relative path to that as the output.

Example

function Get-dotnet-EmitterAdditionalOptions([string]$projectDirectory) {
  return "--option @azure-tools/typespec-csharp.emitter-output-dir=$projectDirectory/src"
}

function Get-java-EmitterAdditionalOptions([string]$projectDirectory) {
  return "--option @azure-tools/typespec-java.emitter-output-dir=$projectDirectory/"
}

Per project setup

Each project will need to have a configuration file that will tell the scripts where to find the typespec spec.

tsp-location.yaml

This file should live under the project directory for each service and has the following properties

Property Description IsRequired
directory The top level directory where the main.tsp for the service lives. This should be relative to the spec repo root such as specification/cognitiveservices/OpenAI.Inference true
additionalDirectories Sometimes a typespec file will use a relative import that might not be under the main directory. In this case a single directory will not be enough to pull down all necessary files. To support this you can specify additional directories as a list to sync so that all needed files are synced. false: default = null
commit The commit sha for the version of the typespec files you want to generate off of. This allows us to have idempotence on generation until we opt into pointing at a later version. true
repo The repo this spec lives in. This should be either Azure/azure-rest-api-specs or Azure/azure-rest-api-specs-pr. Note that pr will work locally but not in CI until we add another change to handle token based auth. true

Example

directory: specification/cognitiveservices/OpenAI.Inference
additionalDirectories:
  - specification/cognitiveservices/OpenAI.Authoring
commit: 14f11cab735354c3e253045f7fbd2f1b9f90f7ca
repo: Azure/azure-rest-api-specs

TypeSpec-Project-Process.ps1

  • What does this script do?
    • fetch tspconfig.yaml from remote if it doesn’t exist locally
    • parse tspconfig.yaml
    • create the service folder if none exists
    • create an sdk project folder if none exists
    • create or update tsp-location.yaml
    • call TypeSpec-Project-Sync.ps1
    • call TypeSpec-Project-Generate.ps1
  • input:
    • TypespecProjectDirectory (required) either a folder of tspconfig.yaml or a remoteUrl of tspconfig.yaml
    • CommitSha (optional)
    • RepoUrl (optional)
    • SkipSyncAndGenerate (optional) skip the call to TypeSpec-Project-Sync.ps1 and TypeSpec-Project-Generate.ps1 in case of existed sdk project
  • output: sdk project folder
./eng/common/scripts/TypeSpec-Project-Process.ps1 /home/rc/repos/tmp/spec-repo/specification/contosowidgetmanager/Contoso.WidgetManager 677e272f33a3eaa724abd769af79383a5ac2bba5 https://github.com/Azure/azure-rest-api-specs

or

./eng/common/scripts/TypeSpec-Project-Process.ps1 https://github.com/Azure/azure-rest-api-specs/blob/677e272f33a3eaa724abd769af79383a5ac2bba5/specification/contosowidgetmanager/Contoso.WidgetManager/tspconfig.yaml

TypeSpec-Project-Sync.ps1

It can be found at ./eng/common/scripts/TypeSpec-Project-Sync.ps1. It takes in one parameter which is the root directory of the project which is typically one layer lower than the service directory. As an example for dotnet this is ./sdk/openai/Azure.AI.OpenAI where openai is the service directory and Azure.AI.OpenAI is the project directory.

  • input:
    • ProjectDirectory (required)
    • LocalSpecRepoPath (optional) If this parameter is provided, then this script would use the typespecs in this local repo path for sync.
./eng/common/scripts/TypeSpec-Project-Sync.ps1 ./sdk/openai/Azure.AI.OpenAI

This script will create a sparse-spec folder as a sibling to the root of your current git clone. Each project that is generated will get a sub directory inside of this folder named after the project you are generating. It will then automatically filter to only the files in the directory defined in tsp-location.yaml, and sync to the commit sha defined in tsp-location.yaml.

As an example if you have your language repo at D:\git\azure-sdk-for-net there will be a new directory D:\git\sparse-spec\Azure.AI.OpenAI where the sparse spec will live.

This is then copied over to your project directory so that you can make temporary changes if needed. The location will be ./{projectDir}/TempTypeSpecFiles. This temporary directory will be cleaned up at the end of the generate script unless the -SaveInputs flag is passed into the generate script.

TypeSpec-Project-Generate.ps1

It can be found at ./eng/common/scripts/TypeSpec-Project-Generate.ps1. It takes the exact same parameter as the sync script.

  • input:
    • ProjectDirectory (required)
    • TypespecAdditionalOptions (optional) additional typespec emitter options, separated by semicolon if more than one, e.g. option1=value1;option2=value2
    • SaveInputs (optional) saves the temporary files during execution, default value is false
./eng/common/scripts/TypeSpec-Project-Generate.ps1 ./sdk/openai/Azure.AI.OpenAI

This script takes an optional -SaveInputs flag which acts similar to the old save-inputs: true configuration in autorest. It will not delete the TempTypeSpecFiles folder and it will forward this flag to your emitter in case your emitter creates any additional intermediate files. For example in dotnet a cadl.json and configuration.json file will be created as an intermediate step between the emitter and the generator and these will not be cleaned up if this flag is passed in.

The first thing this does is clean up the npm install that might exist in ./{projectDir}/TempTypeSpecFiles, followed by replacing the package.json with the language static one.

Once this is done it will run npm install followed by tsp compile which is the standard way to generate a typespec project.

The exact command that gets run is output stdout to enable debugging if needed.

We currently don't do anything to the tspconfig.yaml that gets pulled in from the spec repo to limit to just your language emitter instead we use the filter option on the command line --emit $emitterName. This allows you to isolate the generation to only things owned by your language so you can safely add generation dependencies in CI without needing to worry about noisy neighbors.

Local scenario

User is recommended to run TypeSpec-Project-Process.ps1 to generate tsp-location.yaml automatically then it would call the other two common scripts to generate sdk code. Alternatively, user can prepare tsp-location.yaml manually then run the other two scripts manually to generate sdk code.

Build tool integration

One use case that some languages have is to have their CI regenerate the project and then do a git diff to validate that there are no differences. This helps detect if people modify the generated files manually. To support this its valuable to have the exact same command to generate a project regardless of whether the individual library is autorest or typespec.

To achieve this each language will have their own idiomatic tool set but whatever that toolset is can check to see if a tsp-location.yaml file exists, and if it does they can call the TypeSpec-Project-Sync.ps1 and TypeSpec-Project-Generate.ps1 scripts, otherwise they can call the autorest command they call today for all other libraries.

In dotnet this is achieved by running dotnet build /t:GenerateCode regardless of which type of project it is the correct commands get called and it remains consistent and idiomatic to the language. In other languages this could be npm generate or python generate.py to do the same.

Since the generate script simply is a wrapper for npm install and tsp compile you can still run those commands directly manually after the sync if you want to instead.