From d07d41184c6ef4c5650feb4996b62089925d3559 Mon Sep 17 00:00:00 2001 From: Ted Spence Date: Sun, 18 Aug 2024 12:51:27 -0700 Subject: [PATCH] Make publish scripts available for all languages but create-only (#27) * Make C# nuget publish workflow script create-only * Standardize naming of publish.yml and make it optional * Save history of patch notes --- README.md | 52 ++++++++++++++ src/SdkGenerator/Languages/CSharpSdk.cs | 6 +- src/SdkGenerator/Languages/JavaSdk.cs | 3 + src/SdkGenerator/Languages/PythonSdk.cs | 3 + src/SdkGenerator/Languages/TypescriptSdk.cs | 3 + src/SdkGenerator/PatchNotes.md | 69 +++++++++++++++++++ src/SdkGenerator/SdkGenerator.csproj | 8 +-- ...ublish.yml.scriban => publish.yml.scriban} | 0 .../Templates/java/publish.yml.scriban | 58 ++++++++++++++++ .../Templates/python/publish.yml.scriban | 39 +++++++++++ .../Templates/ts/publish.yml.scriban | 34 +++++++++ 11 files changed, 268 insertions(+), 7 deletions(-) rename src/SdkGenerator/Templates/csharp/{nuget-publish.yml.scriban => publish.yml.scriban} (100%) create mode 100644 src/SdkGenerator/Templates/java/publish.yml.scriban create mode 100644 src/SdkGenerator/Templates/python/publish.yml.scriban create mode 100644 src/SdkGenerator/Templates/ts/publish.yml.scriban diff --git a/README.md b/README.md index 79dca2a..db9b543 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,58 @@ Here's how to use this program. You can automate these steps in a Github workflow to execute this program automatically on new releases. +## Automating SDK patches + +If you publish updates to your API regularly, you can use GitHub Actions to automatically check for changes to your +OpenAPI / Swagger file and generate a new software development kit. + +Create a GitHub action using this template: + +```yaml +name: Check for OpenAPI updates + +on: + schedule: + - cron: "0 0 * * 0" # Run once per week + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup .NET Core @ Latest + uses: actions/setup-dotnet@v4 + with: + dotnet-version: "8.0.x" + + - name: Add the SDK Generator + run: dotnet tool install SdkGenerator --global + + - name: Generate the latest SDK + run: SdkGenerator build -p ./sdk-config.json + + - name: Save patch notes + id: patch-notes + run: SdkGenerator get-patch-notes -p ./sdk-config.json + + - name: Save pull request name + id: pr-name + run: SdkGenerator get-release-name -p ./sdk-config.json + + - name: Create Pull Request + id: cpr + uses: peter-evans/create-pull-request@v6 + with: + commit-message: ${{ steps.patch-notes.outputs }} + title: ${{ steps.pr-name.outputs }} +``` + ## Supported Languages | Language | Supported | Github Workflows | Notes | diff --git a/src/SdkGenerator/Languages/CSharpSdk.cs b/src/SdkGenerator/Languages/CSharpSdk.cs index 1a5ffba..d6f5053 100644 --- a/src/SdkGenerator/Languages/CSharpSdk.cs +++ b/src/SdkGenerator/Languages/CSharpSdk.cs @@ -470,9 +470,9 @@ public async Task Export(GeneratorContext context) await ExportEndpoints(context); // Let's try using Scriban to populate these files - await ScribanFunctions.ExecuteTemplate(context, - "SdkGenerator.Templates.csharp.nuget-publish.yml.scriban", - context.MakePath(context.Project.Csharp.Folder, ".github", "workflows", "nuget-publish.yml")); + await ScribanFunctions.ExecuteTemplateIfNotExists(context, + "SdkGenerator.Templates.csharp.publish.yml.scriban", + context.MakePath(context.Project.Csharp.Folder, ".github", "workflows", "publish.yml")); await ScribanFunctions.ExecuteTemplate(context, "SdkGenerator.Templates.csharp.ApiClient.scriban", context.MakePath(context.Project.Csharp.Folder, "src", context.Project.Csharp.ClassName + ".cs")); diff --git a/src/SdkGenerator/Languages/JavaSdk.cs b/src/SdkGenerator/Languages/JavaSdk.cs index 82fd765..d2d9cf0 100644 --- a/src/SdkGenerator/Languages/JavaSdk.cs +++ b/src/SdkGenerator/Languages/JavaSdk.cs @@ -418,6 +418,9 @@ public async Task Export(GeneratorContext context) await ExportEndpoints(context); // Let's try using Scriban to populate these files + await ScribanFunctions.ExecuteTemplateIfNotExists(context, + "SdkGenerator.Templates.java.publish.yml.scriban", + context.MakePath(context.Project.Csharp.Folder, ".github", "workflows", "publish.yml")); await ScribanFunctions.ExecuteTemplate(context, "SdkGenerator.Templates.java.ApiClient.java.scriban", context.MakePath(context.Project.Java.Folder, "src", "main", "java", diff --git a/src/SdkGenerator/Languages/PythonSdk.cs b/src/SdkGenerator/Languages/PythonSdk.cs index efd765e..34d7810 100644 --- a/src/SdkGenerator/Languages/PythonSdk.cs +++ b/src/SdkGenerator/Languages/PythonSdk.cs @@ -450,6 +450,9 @@ public async Task Export(GeneratorContext context) await ExportEndpoints(context); // Let's try using Scriban to populate these files + await ScribanFunctions.ExecuteTemplateIfNotExists(context, + "SdkGenerator.Templates.python.publish.yml.scriban", + context.MakePath(context.Project.Csharp.Folder, ".github", "workflows", "publish.yml")); await ScribanFunctions.ExecuteTemplate(context, "SdkGenerator.Templates.python.ApiClient.scriban", context.MakePath(context.Project.Python.Folder, "src", context.Project.Python.Namespace, context.Project.Python.ClassName.WordsToSnakeCase() + ".py")); diff --git a/src/SdkGenerator/Languages/TypescriptSdk.cs b/src/SdkGenerator/Languages/TypescriptSdk.cs index 5bf12be..273a315 100644 --- a/src/SdkGenerator/Languages/TypescriptSdk.cs +++ b/src/SdkGenerator/Languages/TypescriptSdk.cs @@ -363,6 +363,9 @@ public async Task Export(GeneratorContext context) await ExportEndpoints(context); // Let's try using Scriban to populate these files + await ScribanFunctions.ExecuteTemplateIfNotExists(context, + "SdkGenerator.Templates.ts.publish.yml.scriban", + context.MakePath(context.Project.Csharp.Folder, ".github", "workflows", "publish.yml")); await ScribanFunctions.ExecuteTemplate(context, "SdkGenerator.Templates.ts.ApiClient.scriban", context.MakePath(context.Project.Typescript.Folder, "src", context.Project.Typescript.ClassName + ".ts")); diff --git a/src/SdkGenerator/PatchNotes.md b/src/SdkGenerator/PatchNotes.md index 0698c46..7a42ac9 100644 --- a/src/SdkGenerator/PatchNotes.md +++ b/src/SdkGenerator/PatchNotes.md @@ -1,3 +1,72 @@ +# 1.3.1 +August 18, 2024 + +* Make automated publishing workflow scripts create-only so updates do not trigger GitHub security + +# 1.3.0 +August 13, 2024 + +* New modes to calculate patch notes and release name for use with automated PRs + +# 1.2.6 +July 25, 2024 + +* Fix issue with path combining +* Upgrade to DotNet 8.0 +* Do not generate markdown files if not requested +* Make security schemes section blankout optional if users don't want to save keys +* Python: Fix capitalization/list issue +* TypeScript: Fix duplicated import for nested classes + +# 1.2.5 +March 13, 2024 + +* Fix issues with embedded resources so this can work from DotNet tool + +# 1.2.4 +February 10, 2024 + +* More capitalization improvements - better handle imprecise inputs +* Fixed issues with Python SDK for array uploads +* C# SDK uses URI object instead of string for custom endpoint + +# 1.2.3 +January 29, 2024 + +* Allow specific endpoints to be ignored during SDK generation +* Better capitalization for multi-word phrases converted to PascalCase +* Fixed issues with Python SDK generation, json double conversion + +# 1.2.2 +January 12, 2024 + +* Numerous small fixes for API endpoints that download octet-streams/byte arrays +* Better logic for excluding endpoints and excluding parameters - patch notes generate correctly +* SDKs now generate endpoints that download blobs and still parse errors correctly +* Treat both "byte" and "byte[]" as blob download endpoints +* Python uses immutable bytes object +* Fixes for readme uploads + +# 1.2.1 +October 22, 2023 + +* Class and property generation can now avoid language-specific keywords +* Variable names are now cleansed for parameters (some APIs use $param=value) +* Java now uses semver3 as is becoming the standard in most places +* Generated API documentation will now only link to data model pages if specified +* Readme can now select between `list` and `table` style data model documentation + +# 1.2.0 +October 11, 2023 + +* Verified that Python and TypeScript work correctly, at least for my current SDKs + +# 1.1.9 +October 9, 2023 + +* Improvements for patch notes generation +* Cleaned up the Python export to use the latest idioms and to work with single-result-object + # 1.1.8 September 14, 2023 diff --git a/src/SdkGenerator/SdkGenerator.csproj b/src/SdkGenerator/SdkGenerator.csproj index 8e967cc..6de1d23 100644 --- a/src/SdkGenerator/SdkGenerator.csproj +++ b/src/SdkGenerator/SdkGenerator.csproj @@ -12,14 +12,14 @@ SDK generator swagger openapi swashbuckle Copyright 2021 - 2024 - # 1.3.0 - August 13, 2024 + # 1.3.1 + August 18, 2024 - * New modes to calculate patch notes and release name for use with automated PRs + * Make automated publishing workflow scripts create-only so updates do not trigger GitHub security docs/icons-puzzle.png MIT - 1.3.0 + 1.3.1 Ted Spence diff --git a/src/SdkGenerator/Templates/csharp/nuget-publish.yml.scriban b/src/SdkGenerator/Templates/csharp/publish.yml.scriban similarity index 100% rename from src/SdkGenerator/Templates/csharp/nuget-publish.yml.scriban rename to src/SdkGenerator/Templates/csharp/publish.yml.scriban diff --git a/src/SdkGenerator/Templates/java/publish.yml.scriban b/src/SdkGenerator/Templates/java/publish.yml.scriban new file mode 100644 index 0000000..a9648d2 --- /dev/null +++ b/src/SdkGenerator/Templates/java/publish.yml.scriban @@ -0,0 +1,58 @@ +# This is a basic workflow to help you get started with Actions + +name: Java CI with Maven + +# Controls when the workflow will run +on: + # Triggers the workflow on push or pull request events but only for the main branch + push: + branches: [ main ] + pull_request: + branches: [ main ] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 11 + uses: actions/setup-java@v2 + with: + java-version: 11 + distribution: 'temurin' + server-id: ossrh + server-username: OSSRH_USERNAME + server-password: OSSRH_PASSWORD + gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} + gpg-passphrase: MAVEN_GPG_PASSPHRASE + + - name: Verify with Maven + if: github.event_name == 'pull_request' + run: mvn -B clean verify + + - name: Verify javadoc + if: github.event_name == 'pull_request' + run: mvn -B -Prelease javadoc:test-javadoc + + - name: Compile + run: mvn -B -Prelease compile + + - name: Javadoc + run: mvn -B -Prelease javadoc:jar + + - name: Source + run: mvn -B -Prelease source:jar + + - name: Publish to Apache Maven Central + if: github.ref == 'refs/heads/main' && github.event_name == 'push' + run: mvn -B -Prelease deploy + env: + OSSRH_USERNAME: ${{ secrets.MAVEN_USERNAME }} + OSSRH_PASSWORD: ${{ secrets.MAVEN_CENTRAL_TOKEN }} + MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} \ No newline at end of file diff --git a/src/SdkGenerator/Templates/python/publish.yml.scriban b/src/SdkGenerator/Templates/python/publish.yml.scriban new file mode 100644 index 0000000..773fb54 --- /dev/null +++ b/src/SdkGenerator/Templates/python/publish.yml.scriban @@ -0,0 +1,39 @@ +name: Upload Python Package + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +jobs: + deploy: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.x' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install build + - name: Run Pyre + run: | + python -m pip install pyre-check + python -m pip install dacite + pyre --source-directory src check + - name: Build package + run: python -m build + - name: Publish package + if: github.ref == 'refs/heads/main' && github.event_name == 'push' + uses: pypa/gh-action-pypi-publish@release/v1 + with: + user: __token__ + password: ${{ secrets.PYPI_API_TOKEN }} \ No newline at end of file diff --git a/src/SdkGenerator/Templates/ts/publish.yml.scriban b/src/SdkGenerator/Templates/ts/publish.yml.scriban new file mode 100644 index 0000000..be234e7 --- /dev/null +++ b/src/SdkGenerator/Templates/ts/publish.yml.scriban @@ -0,0 +1,34 @@ +name: Publish to NPM CI/CD + +on: + push: + branches: [main] + pull_request: + branches: [main] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v3 + with: + node-version: 18.18.0 + registry-url: 'https://registry.npmjs.org' + - name: npm install + run: npm install + - name: npm audit + run: npm audit + - name: ESLint + run: npm run lint + - name: npm compile + run: npm run compile + - name: Publish NPM Package + if: github.ref == 'refs/heads/main' && github.event_name == 'push' + run: + npm publish + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} \ No newline at end of file