diff --git a/packages/cli/cli/versions.yml b/packages/cli/cli/versions.yml index 69ac1f2bc934..24fe2f30e2ce 100644 --- a/packages/cli/cli/versions.yml +++ b/packages/cli/cli/versions.yml @@ -1,4 +1,17 @@ # yaml-language-server: $schema=../../../fern-versions-yml.schema.json +- version: 4.37.1 + changelogEntry: + - summary: | + Optimize protobuf buf generate to reuse a single working directory + across all proto files. Instead of creating a new temp dir, copying + the proto root, checking binaries, and resolving deps for every file, + setup is done once and only `buf generate ` runs per file. + The two buf generate passes (protoc-gen-openapi and protoc-gen-fern) + also now run concurrently. + type: fix + createdAt: "2026-03-18" + irVersion: 65 + - version: 4.37.0 changelogEntry: - summary: | diff --git a/packages/cli/workspace/lazy-fern-workspace/src/OSSWorkspace.ts b/packages/cli/workspace/lazy-fern-workspace/src/OSSWorkspace.ts index a257025087af..8fd6513f6ee6 100644 --- a/packages/cli/workspace/lazy-fern-workspace/src/OSSWorkspace.ts +++ b/packages/cli/workspace/lazy-fern-workspace/src/OSSWorkspace.ts @@ -30,7 +30,6 @@ import { v4 as uuidv4 } from "uuid"; import { loadOpenRpc } from "./loaders/index.js"; import { OpenAPILoader } from "./loaders/OpenAPILoader.js"; import { ProtobufIRGenerator } from "./protobuf/ProtobufIRGenerator.js"; -import { MaybeValid } from "./protobuf/utils.js"; import { getAllOpenAPISpecs } from "./utils/getAllOpenAPISpecs.js"; export declare namespace OSSWorkspace { @@ -95,6 +94,11 @@ export class OSSWorkspace extends BaseOpenAPIWorkspace { private graphqlOperations: Record = {}; private graphqlTypes: Record = {}; + // Cache for protobuf → OpenAPI generation results, keyed by relativePathToDependency. + // This avoids running buf generate twice when both toFernWorkspace() and + // validateOSSWorkspace() need the same OpenAPI specs. + private openApiSpecsCache: Map> = new Map(); + constructor({ allSpecs, specs, ...superArgs }: OSSWorkspace.Args) { const openapiSpecs = specs.filter((spec) => spec.type === "openapi" && spec.source.type === "openapi"); super({ @@ -215,6 +219,28 @@ export class OSSWorkspace extends BaseOpenAPIWorkspace { } } + /** + * Returns cached OpenAPI specs (including protobuf → OpenAPI conversions). + * The expensive buf generate work is performed once per unique + * relativePathToDependency and reused across getOpenAPIIr(), + * getIntermediateRepresentation(), and workspace validation. + */ + public getOpenAPISpecsCached({ + context, + relativePathToDependency + }: { + context: TaskContext; + relativePathToDependency?: RelativeFilePath; + }): Promise { + const key = relativePathToDependency ?? ""; + let cached = this.openApiSpecsCache.get(key); + if (cached == null) { + cached = getAllOpenAPISpecs({ context, specs: this.specs, relativePathToDependency }); + this.openApiSpecsCache.set(key, cached); + } + return cached; + } + public async getOpenAPIIr( { context, @@ -227,7 +253,7 @@ export class OSSWorkspace extends BaseOpenAPIWorkspace { }, settings?: OSSWorkspace.Settings ): Promise { - const openApiSpecs = await getAllOpenAPISpecs({ context, specs: this.specs, relativePathToDependency }); + const openApiSpecs = await this.getOpenAPISpecsCached({ context, relativePathToDependency }); return parse({ context, documents: await this.loader.loadDocuments({ @@ -259,7 +285,10 @@ export class OSSWorkspace extends BaseOpenAPIWorkspace { generateV1Examples: boolean; logWarnings: boolean; }): Promise { - const specs = await getAllOpenAPISpecs({ context, specs: this.specs }); + // Start protobuf IR generation in parallel with OpenAPI processing + const protobufIRResultsPromise = this.generateAllProtobufIRs({ context }); + + const specs = await this.getOpenAPISpecsCached({ context }); const documents = await this.loader.loadDocuments({ context, specs }); const authOverrides = @@ -409,54 +438,21 @@ export class OSSWorkspace extends BaseOpenAPIWorkspace { ? result : mergeIntermediateRepresentation(mergedIr, result, casingsGenerator); } - } else if (spec.type === "protobuf") { - // Handle protobuf specs by calling buf generate with protoc-gen-fern - try { - const protobufIRGenerator = new ProtobufIRGenerator({ context }); - const protobufIRFilepath = await protobufIRGenerator.generate({ - absoluteFilepathToProtobufRoot: spec.absoluteFilepathToProtobufRoot, - absoluteFilepathToProtobufTarget: spec.absoluteFilepathToProtobufTarget, - local: true, - deps: spec.dependencies - }); - - const result = await readFile(protobufIRFilepath, "utf-8"); - - const casingsGenerator = constructCasingsGenerator({ - generationLanguage: "typescript", - keywords: undefined, - smartCasing: false - }); - - if (result != null) { - let serializedIr: MaybeValid; - try { - serializedIr = serialization.IntermediateRepresentation.parse(JSON.parse(result), { - allowUnrecognizedEnumValues: true, - skipValidation: true - }); - if (serializedIr.ok) { - mergedIr = - mergedIr === undefined - ? serializedIr.value - : mergeIntermediateRepresentation( - mergedIr, - serializedIr.value, - casingsGenerator - ); - } else { - throw new Error(); - } - } catch (error) { - context.logger.log("error", "Failed to parse protobuf IR: "); - } - } - } catch (error) { - context.logger.log("warn", "Failed to parse protobuf IR: " + error); - } } } + // Await and merge protobuf IR results (generated in parallel with OpenAPI processing) + const protobufIRResults = await protobufIRResultsPromise; + const protobufCasingsGenerator = constructCasingsGenerator({ + generationLanguage: "typescript", + keywords: undefined, + smartCasing: false + }); + for (const ir of protobufIRResults) { + mergedIr = + mergedIr === undefined ? ir : mergeIntermediateRepresentation(mergedIr, ir, protobufCasingsGenerator); + } + for (const errorCollector of errorCollectors) { if (errorCollector.hasErrors()) { const errorStats = errorCollector.getErrorStats(); @@ -486,6 +482,45 @@ export class OSSWorkspace extends BaseOpenAPIWorkspace { return mergedIr; } + private async generateAllProtobufIRs({ context }: { context: TaskContext }): Promise { + const protobufSpecs = this.allSpecs.filter((spec): spec is ProtobufSpec => spec.type === "protobuf"); + if (protobufSpecs.length === 0) { + return []; + } + + // Process specs sequentially because ProtobufIRGenerator.generate() + // runs `npm install -g` which is not safe to invoke concurrently. + const results: IntermediateRepresentation[] = []; + for (const spec of protobufSpecs) { + try { + const protobufIRGenerator = new ProtobufIRGenerator({ context }); + const protobufIRFilepath = await protobufIRGenerator.generate({ + absoluteFilepathToProtobufRoot: spec.absoluteFilepathToProtobufRoot, + absoluteFilepathToProtobufTarget: spec.absoluteFilepathToProtobufTarget, + local: true, + deps: spec.dependencies + }); + + const result = await readFile(protobufIRFilepath, "utf-8"); + if (result != null) { + const serializedIr = serialization.IntermediateRepresentation.parse(JSON.parse(result), { + allowUnrecognizedEnumValues: true, + skipValidation: true + }); + if (serializedIr.ok) { + results.push(serializedIr.value); + continue; + } + context.logger.log("error", "Failed to parse protobuf IR"); + } + } catch (error) { + context.logger.log("warn", "Failed to parse protobuf IR: " + error); + } + } + + return results; + } + public async toFernWorkspace( { context }: { context: TaskContext }, settings?: OSSWorkspace.Settings, diff --git a/packages/cli/workspace/lazy-fern-workspace/src/protobuf/ProtobufOpenAPIGenerator.ts b/packages/cli/workspace/lazy-fern-workspace/src/protobuf/ProtobufOpenAPIGenerator.ts index 821955e1dcd0..915ff15d4cb5 100644 --- a/packages/cli/workspace/lazy-fern-workspace/src/protobuf/ProtobufOpenAPIGenerator.ts +++ b/packages/cli/workspace/lazy-fern-workspace/src/protobuf/ProtobufOpenAPIGenerator.ts @@ -1,7 +1,7 @@ import { AbsoluteFilePath, join, RelativeFilePath, relative } from "@fern-api/fs-utils"; import { createLoggingExecutable } from "@fern-api/logging-execa"; import { TaskContext } from "@fern-api/task-context"; -import { access, cp, readFile, unlink, writeFile } from "fs/promises"; +import { access, cp, readFile, rename, unlink, writeFile } from "fs/promises"; import path from "path"; import tmp from "tmp-promise"; import { resolveProtocGenOpenAPI } from "./ProtocGenOpenAPIDownloader.js"; @@ -11,6 +11,15 @@ const PROTOBUF_GENERATOR_CONFIG_FILENAME = "buf.gen.yaml"; const PROTOBUF_GENERATOR_OUTPUT_PATH = "output"; const PROTOBUF_GENERATOR_OUTPUT_FILEPATH = `${PROTOBUF_GENERATOR_OUTPUT_PATH}/openapi.yaml`; +/** + * Prepared working directory for running buf generate multiple times + * without repeating setup (copy, which checks, dep resolution). + */ +interface PreparedWorkingDir { + cwd: AbsoluteFilePath; + envOverride: Record | undefined; +} + export class ProtobufOpenAPIGenerator { private context: TaskContext; private isAirGapped: boolean | undefined; @@ -49,6 +58,124 @@ export class ProtobufOpenAPIGenerator { return this.generateRemote(); } + /** + * Prepares a reusable working directory: copies the proto root once, + * writes buf config files, checks for required binaries, and resolves + * dependencies. Returns a handle that can be passed to generateFromPrepared() + * for each target file — no repeated setup. + */ + public async prepare({ + absoluteFilepathToProtobufRoot, + relativeFilepathToProtobufRoot, + local, + deps + }: { + absoluteFilepathToProtobufRoot: AbsoluteFilePath; + relativeFilepathToProtobufRoot: RelativeFilePath; + local: boolean; + deps: string[]; + }): Promise { + if (!local) { + this.context.failAndThrow("Remote Protobuf generation is unimplemented."); + } + + // Resolve buf and protoc-gen-openapi binaries once + await this.ensureBufResolved(); + await this.ensureProtocGenOpenAPIResolved(); + + if (deps.length > 0 && this.isAirGapped === undefined) { + this.isAirGapped = await detectAirGappedModeForProtobuf( + absoluteFilepathToProtobufRoot, + this.context.logger, + this.resolvedBufCommand + ); + } + + const cwd = AbsoluteFilePath.of((await tmp.dir()).path); + await cp(absoluteFilepathToProtobufRoot, cwd, { recursive: true }); + await writeFile( + join(cwd, RelativeFilePath.of(PROTOBUF_GENERATOR_CONFIG_FILENAME)), + getProtobufGeneratorConfig({ relativeFilepathToProtobufRoot }) + ); + + // If we downloaded protoc-gen-openapi, prepend its directory to PATH so buf can find it + const envOverride = + this.protocGenOpenAPIBinDir != null + ? { PATH: `${this.protocGenOpenAPIBinDir}${path.delimiter}${process.env.PATH ?? ""}` } + : undefined; + + // Write buf.yaml and resolve dependencies once + const bufYamlPath = join(cwd, RelativeFilePath.of("buf.yaml")); + const bufLockPath = join(cwd, RelativeFilePath.of("buf.lock")); + await writeFile(bufYamlPath, getProtobufYamlV1(deps)); + + if (deps.length > 0) { + if (this.isAirGapped) { + this.context.logger.debug("Air-gapped mode: skipping buf dep update"); + try { + await access(bufLockPath); + } catch { + this.context.failAndThrow( + "Air-gapped mode requires a pre-cached buf.lock file. Please run 'buf dep update' at build time to cache dependencies." + ); + } + } else { + const bufCommand = this.resolvedBufCommand ?? "buf"; + const buf = createLoggingExecutable(bufCommand, { + cwd, + logger: this.context.logger, + stdout: "ignore", + stderr: "pipe", + ...(envOverride != null ? { env: { ...process.env, ...envOverride } } : {}) + }); + await buf(["dep", "update"]); + } + } + + return { cwd, envOverride }; + } + + /** + * Generates OpenAPI output for a single proto target using a previously + * prepared working directory. Each call only runs `buf generate ` + * — all heavy setup was done once in prepare(). + * + * Because protoc-gen-openapi writes to a fixed output path, each call + * renames the output to a unique temp file to avoid conflicts when called + * sequentially for multiple targets. + */ + public async generateFromPrepared({ + preparedDir, + absoluteFilepathToProtobufRoot, + absoluteFilepathToProtobufTarget + }: { + preparedDir: PreparedWorkingDir; + absoluteFilepathToProtobufRoot: AbsoluteFilePath; + absoluteFilepathToProtobufTarget: AbsoluteFilePath; + }): Promise<{ absoluteFilepath: AbsoluteFilePath }> { + const target = relative(absoluteFilepathToProtobufRoot, absoluteFilepathToProtobufTarget); + const bufCommand = this.resolvedBufCommand ?? "buf"; + const buf = createLoggingExecutable(bufCommand, { + cwd: preparedDir.cwd, + logger: this.context.logger, + stdout: "ignore", + stderr: "pipe", + ...(preparedDir.envOverride != null ? { env: { ...process.env, ...preparedDir.envOverride } } : {}) + }); + + const bufGenerateResult = await buf(["generate", target.toString()]); + if (bufGenerateResult.exitCode !== 0) { + this.context.failAndThrow(bufGenerateResult.stderr); + } + + // Move output to a unique temp file so the next call doesn't overwrite it + const outputPath = join(preparedDir.cwd, RelativeFilePath.of(PROTOBUF_GENERATOR_OUTPUT_FILEPATH)); + const uniqueOutput = AbsoluteFilePath.of((await tmp.file({ postfix: ".yaml" })).path); + await rename(outputPath, uniqueOutput); + + return { absoluteFilepath: uniqueOutput }; + } + private async generateLocal({ absoluteFilepathToProtobufRoot, absoluteFilepathToProtobufTarget, @@ -201,7 +328,13 @@ export class ProtobufOpenAPIGenerator { }); try { - await which(["protoc-gen-openapi"]); + const result = await which(["protoc-gen-openapi"]); + const resolvedPath = result.stdout?.trim(); + if (resolvedPath) { + this.context.logger.debug(`Found protoc-gen-openapi on PATH: ${resolvedPath}`); + } else { + this.context.logger.debug("Found protoc-gen-openapi on PATH"); + } this.protocGenOpenAPIResolved = true; } catch { this.context.logger.debug("protoc-gen-openapi not found on PATH, attempting auto-download"); diff --git a/packages/cli/workspace/lazy-fern-workspace/src/protobuf/utils.ts b/packages/cli/workspace/lazy-fern-workspace/src/protobuf/utils.ts index 5ff2dd30077c..575c8ca2c605 100644 --- a/packages/cli/workspace/lazy-fern-workspace/src/protobuf/utils.ts +++ b/packages/cli/workspace/lazy-fern-workspace/src/protobuf/utils.ts @@ -153,12 +153,19 @@ export async function ensureBufCommand(logger: Logger): Promise { }); try { - await which(["buf"]); + const result = await which(["buf"]); + const bufPath = result.stdout?.trim(); + if (bufPath) { + logger.debug(`Found buf on PATH: ${bufPath}`); + } else { + logger.debug("Found buf on PATH"); + } return "buf"; } catch { logger.debug("buf not found on PATH, attempting auto-download"); const downloadedBufPath = await resolveBuf(logger); if (downloadedBufPath != null) { + logger.debug(`Using auto-downloaded buf: ${downloadedBufPath}`); return downloadedBufPath; } throw new Error("Missing required dependency; please install 'buf' to continue (e.g. 'brew install buf')."); diff --git a/packages/cli/workspace/lazy-fern-workspace/src/utils/getAllOpenAPISpecs.ts b/packages/cli/workspace/lazy-fern-workspace/src/utils/getAllOpenAPISpecs.ts index 9da8d58388de..4f49b236ffea 100644 --- a/packages/cli/workspace/lazy-fern-workspace/src/utils/getAllOpenAPISpecs.ts +++ b/packages/cli/workspace/lazy-fern-workspace/src/utils/getAllOpenAPISpecs.ts @@ -1,6 +1,5 @@ import { OpenAPISpec, ProtobufSpec, Spec } from "@fern-api/api-workspace-commons"; -import { isNonNullish } from "@fern-api/core-utils"; -import { listFiles, RelativeFilePath } from "@fern-api/fs-utils"; +import { AbsoluteFilePath, listFiles, RelativeFilePath } from "@fern-api/fs-utils"; import { TaskContext } from "@fern-api/task-context"; import { ProtobufOpenAPIGenerator } from "../protobuf/ProtobufOpenAPIGenerator.js"; @@ -21,77 +20,88 @@ export async function getAllOpenAPISpecs({ return { ...spec, relativePathToDependency }; }); const protobufSpecs: ProtobufSpec[] = specs.filter((spec): spec is ProtobufSpec => spec.type === "protobuf"); - const openApiSpecsFromProto = await Promise.all( - protobufSpecs.map(async (protobufSpec) => { - if (protobufSpec.absoluteFilepathToProtobufTarget != null) { - const result = await convertProtobufToOpenAPI({ generator, protobufSpec, relativePathToDependency }); - return result ? [result.openApiSpec] : []; - } - const allProtobufTargetFilepaths = await listFiles(protobufSpec.absoluteFilepathToProtobufRoot, "proto"); - let accumulatedBufLockContents: string | undefined; - const openApiSpecs: OpenAPISpec[] = []; - for (const file of allProtobufTargetFilepaths) { - const result = await convertProtobufToOpenAPI({ - generator, - protobufSpec: { - ...protobufSpec, - absoluteFilepathToProtobufTarget: file - }, - relativePathToDependency, - existingBufLockContents: accumulatedBufLockContents - }); - if (result != null) { - openApiSpecs.push(result.openApiSpec); - if (result.bufLockContents != null) { - accumulatedBufLockContents = result.bufLockContents; - } + // Group protobuf specs by their proto root so we can prepare a single + // working directory per root, then generate each target from it. + const specsByRoot = new Map(); + for (const spec of protobufSpecs) { + const key = spec.absoluteFilepathToProtobufRoot; + const group = specsByRoot.get(key) ?? []; + group.push(spec); + specsByRoot.set(key, group); + } + + const openApiSpecsFromProto: OpenAPISpec[] = []; + + for (const group of specsByRoot.values()) { + // Use the first spec in the group for the shared prepare() call. + const representative = group[0]; + if (representative == null) { + continue; + } + + const preparedDir = await generator.prepare({ + absoluteFilepathToProtobufRoot: representative.absoluteFilepathToProtobufRoot, + relativeFilepathToProtobufRoot: representative.relativeFilepathToProtobufRoot, + local: representative.generateLocally, + deps: representative.dependencies + }); + + // Collect all targets: explicit targets from specs + discovered files + // for specs without a target. + const targetEntries: Array<{ target: AbsoluteFilePath; spec: ProtobufSpec }> = []; + for (const spec of group) { + if (spec.absoluteFilepathToProtobufTarget != null) { + targetEntries.push({ target: spec.absoluteFilepathToProtobufTarget, spec }); + } else { + const files = await listFiles(spec.absoluteFilepathToProtobufRoot, "proto"); + for (const file of files) { + targetEntries.push({ target: file, spec }); } } - return openApiSpecs; - }) - ); + } - const flattenedSpecs = openApiSpecsFromProto.flat().filter((spec) => isNonNullish(spec)); - return [...openApiSpecs, ...flattenedSpecs]; + // Generate each target sequentially using the shared working dir. + // Sequential because protoc-gen-openapi writes to a fixed output + // path and buf generate is not safe to run concurrently in the + // same directory. + for (const { target, spec } of targetEntries) { + const result = await generator.generateFromPrepared({ + preparedDir, + absoluteFilepathToProtobufRoot: spec.absoluteFilepathToProtobufRoot, + absoluteFilepathToProtobufTarget: target + }); + openApiSpecsFromProto.push( + makeOpenApiSpec({ result, protobufSpec: spec, relativePathToDependency, target }) + ); + } + } + + return [...openApiSpecs, ...openApiSpecsFromProto]; } -export async function convertProtobufToOpenAPI({ - generator, +function makeOpenApiSpec({ + result, protobufSpec, relativePathToDependency, - existingBufLockContents + target }: { - generator: ProtobufOpenAPIGenerator; + result: { absoluteFilepath: AbsoluteFilePath }; protobufSpec: ProtobufSpec; relativePathToDependency?: RelativeFilePath; - existingBufLockContents?: string; -}): Promise<{ bufLockContents: string | undefined; openApiSpec: OpenAPISpec } | undefined> { - if (protobufSpec.absoluteFilepathToProtobufTarget == null) { - return undefined; - } - const openAPIAbsoluteFilePath = await generator.generate({ - absoluteFilepathToProtobufRoot: protobufSpec.absoluteFilepathToProtobufRoot, - absoluteFilepathToProtobufTarget: protobufSpec.absoluteFilepathToProtobufTarget, - relativeFilepathToProtobufRoot: protobufSpec.relativeFilepathToProtobufRoot, - local: protobufSpec.generateLocally, - deps: protobufSpec.dependencies, - existingBufLockContents - }); + target: AbsoluteFilePath; +}): OpenAPISpec { return { - bufLockContents: openAPIAbsoluteFilePath.bufLockContents, - openApiSpec: { - type: "openapi", - absoluteFilepath: openAPIAbsoluteFilePath.absoluteFilepath, - absoluteFilepathToOverrides: protobufSpec.absoluteFilepathToOverrides, - absoluteFilepathToOverlays: undefined, - settings: protobufSpec.settings, - source: { - type: "protobuf", - relativePathToDependency, - root: protobufSpec.absoluteFilepathToProtobufRoot, - file: protobufSpec.absoluteFilepathToProtobufTarget - } + type: "openapi", + absoluteFilepath: result.absoluteFilepath, + absoluteFilepathToOverrides: protobufSpec.absoluteFilepathToOverrides, + absoluteFilepathToOverlays: undefined, + settings: protobufSpec.settings, + source: { + type: "protobuf", + relativePathToDependency, + root: protobufSpec.absoluteFilepathToProtobufRoot, + file: target } }; } diff --git a/packages/cli/workspace/oss-validator/src/validateOSSWorkspace.ts b/packages/cli/workspace/oss-validator/src/validateOSSWorkspace.ts index 7e644864d173..f061bbdb0367 100644 --- a/packages/cli/workspace/oss-validator/src/validateOSSWorkspace.ts +++ b/packages/cli/workspace/oss-validator/src/validateOSSWorkspace.ts @@ -1,4 +1,4 @@ -import { getAllOpenAPISpecs, OSSWorkspace } from "@fern-api/lazy-fern-workspace"; +import { OSSWorkspace } from "@fern-api/lazy-fern-workspace"; import { TaskContext } from "@fern-api/task-context"; import { getAllRules } from "./getAllRules.js"; import { Rule } from "./Rule.js"; @@ -20,7 +20,7 @@ export async function runRulesOnOSSWorkspace({ context: TaskContext; rules: Rule[]; }): Promise { - const openApiSpecs = await getAllOpenAPISpecs({ context, specs: workspace.specs }); + const openApiSpecs = await workspace.getOpenAPISpecsCached({ context }); const ruleResults = await Promise.all( rules.map(async (rule) => { const violations = await rule.run({ workspace, specs: openApiSpecs, context }); diff --git a/seed/csharp-sdk/basic-auth/.github/workflows/ci.yml b/seed/csharp-sdk/basic-auth/.github/workflows/ci.yml deleted file mode 100644 index 7788a0a054a7..000000000000 --- a/seed/csharp-sdk/basic-auth/.github/workflows/ci.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: ci - -on: [push] - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: false - -env: - DOTNET_NOLOGO: true - -jobs: - ci: - runs-on: ubuntu-latest - - steps: - - name: Checkout repo - uses: actions/checkout@v6 - - - name: Setup .NET - uses: actions/setup-dotnet@v5 - with: - dotnet-version: 10.x - - - name: Install tools - run: dotnet tool restore - - - name: Restore dependencies - run: dotnet restore src/SeedBasicAuth/SeedBasicAuth.csproj - - - name: Build - run: dotnet build src/SeedBasicAuth/SeedBasicAuth.csproj --no-restore -c Release - - - name: Restore test dependencies - run: dotnet restore src/SeedBasicAuth.Test/SeedBasicAuth.Test.csproj - - - name: Build tests - run: dotnet build src/SeedBasicAuth.Test/SeedBasicAuth.Test.csproj --no-restore -c Release - - - name: Test - run: dotnet test src/SeedBasicAuth.Test/SeedBasicAuth.Test.csproj --no-restore --no-build -c Release - - - name: Pack - if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') - run: dotnet pack src/SeedBasicAuth/SeedBasicAuth.csproj --no-build --no-restore -c Release - - - name: Publish to NuGet.org - if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') - env: - NUGET_API_KEY: ${{ secrets.NUGET_API_TOKEN }} - run: dotnet nuget push src/SeedBasicAuth/bin/Release/*.nupkg --api-key $NUGET_API_KEY --source "nuget.org" - diff --git a/seed/csharp-sdk/bearer-token-environment-variable/.github/workflows/ci.yml b/seed/csharp-sdk/bearer-token-environment-variable/.github/workflows/ci.yml deleted file mode 100644 index 40730a91452f..000000000000 --- a/seed/csharp-sdk/bearer-token-environment-variable/.github/workflows/ci.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: ci - -on: [push] - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: false - -env: - DOTNET_NOLOGO: true - -jobs: - ci: - runs-on: ubuntu-latest - - steps: - - name: Checkout repo - uses: actions/checkout@v6 - - - name: Setup .NET - uses: actions/setup-dotnet@v5 - with: - dotnet-version: 10.x - - - name: Install tools - run: dotnet tool restore - - - name: Restore dependencies - run: dotnet restore src/SeedBearerTokenEnvironmentVariable/SeedBearerTokenEnvironmentVariable.csproj - - - name: Build - run: dotnet build src/SeedBearerTokenEnvironmentVariable/SeedBearerTokenEnvironmentVariable.csproj --no-restore -c Release - - - name: Restore test dependencies - run: dotnet restore src/SeedBearerTokenEnvironmentVariable.Test/SeedBearerTokenEnvironmentVariable.Test.csproj - - - name: Build tests - run: dotnet build src/SeedBearerTokenEnvironmentVariable.Test/SeedBearerTokenEnvironmentVariable.Test.csproj --no-restore -c Release - - - name: Test - run: dotnet test src/SeedBearerTokenEnvironmentVariable.Test/SeedBearerTokenEnvironmentVariable.Test.csproj --no-restore --no-build -c Release - - - name: Pack - if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') - run: dotnet pack src/SeedBearerTokenEnvironmentVariable/SeedBearerTokenEnvironmentVariable.csproj --no-build --no-restore -c Release - - - name: Publish to NuGet.org - if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') - env: - NUGET_API_KEY: ${{ secrets.NUGET_API_TOKEN }} - run: dotnet nuget push src/SeedBearerTokenEnvironmentVariable/bin/Release/*.nupkg --api-key $NUGET_API_KEY --source "nuget.org" - diff --git a/seed/csharp-sdk/oauth-client-credentials-mandatory-auth/.github/workflows/ci.yml b/seed/csharp-sdk/oauth-client-credentials-mandatory-auth/.github/workflows/ci.yml deleted file mode 100644 index 0eafcd059722..000000000000 --- a/seed/csharp-sdk/oauth-client-credentials-mandatory-auth/.github/workflows/ci.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: ci - -on: [push] - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: false - -env: - DOTNET_NOLOGO: true - -jobs: - ci: - runs-on: ubuntu-latest - - steps: - - name: Checkout repo - uses: actions/checkout@v6 - - - name: Setup .NET - uses: actions/setup-dotnet@v5 - with: - dotnet-version: 10.x - - - name: Install tools - run: dotnet tool restore - - - name: Restore dependencies - run: dotnet restore src/SeedOauthClientCredentialsMandatoryAuth/SeedOauthClientCredentialsMandatoryAuth.csproj - - - name: Build - run: dotnet build src/SeedOauthClientCredentialsMandatoryAuth/SeedOauthClientCredentialsMandatoryAuth.csproj --no-restore -c Release - - - name: Restore test dependencies - run: dotnet restore src/SeedOauthClientCredentialsMandatoryAuth.Test/SeedOauthClientCredentialsMandatoryAuth.Test.csproj - - - name: Build tests - run: dotnet build src/SeedOauthClientCredentialsMandatoryAuth.Test/SeedOauthClientCredentialsMandatoryAuth.Test.csproj --no-restore -c Release - - - name: Test - run: dotnet test src/SeedOauthClientCredentialsMandatoryAuth.Test/SeedOauthClientCredentialsMandatoryAuth.Test.csproj --no-restore --no-build -c Release - - - name: Pack - if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') - run: dotnet pack src/SeedOauthClientCredentialsMandatoryAuth/SeedOauthClientCredentialsMandatoryAuth.csproj --no-build --no-restore -c Release - - - name: Publish to NuGet.org - if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') - env: - NUGET_API_KEY: ${{ secrets.NUGET_API_TOKEN }} - run: dotnet nuget push src/SeedOauthClientCredentialsMandatoryAuth/bin/Release/*.nupkg --api-key $NUGET_API_KEY --source "nuget.org" -