Skip to content

feat(scripts): add push to mcp-node on release #4784

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ jobs:
type: minimal

- name: Building specs
run: yarn cli build specs
run: yarn cli build specs -s

- name: Store bundled specs
uses: actions/upload-artifact@v4
Expand Down Expand Up @@ -545,7 +545,7 @@ jobs:
type: minimal

- name: Generate documentation specs with code snippets
run: yarn cli build specs --docs
run: yarn cli build specs --docs -s

- name: Read benchmark results
id: benchmark
Expand Down
54 changes: 40 additions & 14 deletions scripts/ci/codegen/pushToRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import fsp from 'fs/promises';
import path, { resolve } from 'path';

import {
CLIENTS,
configureGitHubAuthor,
ensureGitHubToken,
exists,
getOctokit,
gitBranchExists,
Expand All @@ -22,16 +22,45 @@ import { getClientsConfigField } from '../../config.ts';
import { commitStartRelease } from './text.ts';

async function handleSpecFiles(spec: SpecsToPush, tempGitDir: string): Promise<void> {
const pathToSpecs = toAbsolutePath(`${tempGitDir}/${spec.output}`);

await run(`cp ${toAbsolutePath('specs/bundled/README.md')} ${pathToSpecs}`);
await run(`cp ${toAbsolutePath('specs/major-breaking-changes-rename.json')} ${pathToSpecs}`);
await run(`cp ${toAbsolutePath('config/clients.config.json')} ${pathToSpecs}`);
await run(`cp ${toAbsolutePath('docs/bundled/*.json')} ${pathToSpecs}`);
await run(`cp ${toAbsolutePath('docs/bundled/*.yml')} ${pathToSpecs}`);
await run(`cp ${toAbsolutePath('docs/versions-history-with-sla-and-support-policy.json')} ${pathToSpecs}`);
const output = toAbsolutePath(`${tempGitDir}/${spec.output}`);

if (spec.includeSnippets) {
await run(`cp ${toAbsolutePath('docs/bundled/*-snippets.json')} ${output}`);
}

if (spec.includeSLA) {
await run(`cp ${toAbsolutePath('specs/bundled/README.md')} ${output}`);
await run(`cp ${toAbsolutePath('specs/major-breaking-changes-rename.json')} ${output}`);
await run(`cp ${toAbsolutePath('config/clients.config.json')} ${output}`);
await run(`cp ${toAbsolutePath('docs/versions-history-with-sla-and-support-policy.json')} ${output}`);
}

// adblock extensions ban words like `analytics` so we use a different file name just so the doc dans render it
await run(`mv ${pathToSpecs}/analytics.yml ${pathToSpecs}/searchstats.yml`);
if (spec.ext === 'yml') {
await run(`mv ${output}/analytics.yml ${output}/searchstats.yml`);
}

for (const client of CLIENTS) {
const pathToBundledSpec = toAbsolutePath(`docs/bundled/${client}.${spec.ext}`);

if (!(await exists(pathToBundledSpec))) {
continue;
}

const outputFile = `${output}/${client}.${spec.ext}`;

await run(`cp ${pathToBundledSpec} ${outputFile}`);

if (spec.placeholderVariables) {
let file = await fsp.readFile(outputFile, 'utf8');

for (const [k, v] of Object.entries(spec.placeholderVariables)) {
file = file.replace(k, v);
}

await fsp.writeFile(outputFile, file);
}
}
}

async function handleGuideFiles(guide: GuidesToPush, tempGitDir: string): Promise<void> {
Expand Down Expand Up @@ -80,8 +109,6 @@ async function handleGuideFiles(guide: GuidesToPush, tempGitDir: string): Promis
}

async function pushToRepository(repository: string, config: RepositoryConfiguration): Promise<void> {
const githubToken = ensureGitHubToken();

const lastCommitMessage = await run('git log -1 --format="%s"');
const author = (await run('git log -1 --format="Co-authored-by: %an <%ae>"')).trim();
const coAuthors = (await run('git log -1 --format="%(trailers:key=Co-authored-by)"'))
Expand All @@ -100,8 +127,7 @@ async function pushToRepository(repository: string, config: RepositoryConfigurat
const tempGitDir = resolve(process.env.RUNNER_TEMP! || toAbsolutePath('foo/local/test'), repository);
await fsp.rm(tempGitDir, { force: true, recursive: true });

const githubURL = `https://${githubToken}:${githubToken}@github.com/${OWNER}/${repository}`;
await run(`git clone --depth 1 ${githubURL} ${tempGitDir}`);
await run(`gh repo clone ${OWNER}/${repository} ${tempGitDir}`);

for (const task of config.tasks) {
await run(`git checkout ${config.baseBranch}`, { cwd: tempGitDir });
Expand Down
35 changes: 34 additions & 1 deletion scripts/ci/codegen/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,23 @@ export type GuidesToPush = {
};

export type SpecsToPush = {
// the type of changes to push to the repository
type: 'specs';

// the ext of the specs to be pushed to the repository
ext: 'json' | 'yml';

// whether we should include code snippets or not
includeSnippets?: boolean;

// whether we should include SLA related informations or not
includeSLA?: boolean;

// the name of the directory to push the files to
output: string;

// a key-value of the fields to replace, with the name you'd like to use instead
placeholderVariables?: Record<string, string>;
};

type RepositoryTask = {
Expand All @@ -35,7 +50,7 @@ export type RepositoryConfiguration = {
tasks: Array<RepositoryTask>;
};

export const pushToRepositoryConfiguration: { [k in 'AlgoliaWeb' | 'doc']: RepositoryConfiguration } = {
export const pushToRepositoryConfiguration: { [k in 'AlgoliaWeb' | 'doc' | 'mcp-node']: RepositoryConfiguration } = {
AlgoliaWeb: {
baseBranch: 'develop',
tasks: [
Expand Down Expand Up @@ -72,7 +87,10 @@ export const pushToRepositoryConfiguration: { [k in 'AlgoliaWeb' | 'doc']: Repos
commitMessage: 'feat: update specs and supported versions',
files: {
type: 'specs',
ext: 'yml',
output: 'app_data/api/specs',
includeSnippets: true,
includeSLA: true,
},
},
{
Expand All @@ -85,4 +103,19 @@ export const pushToRepositoryConfiguration: { [k in 'AlgoliaWeb' | 'doc']: Repos
},
],
},
'mcp-node': {
baseBranch: 'main',
tasks: [
{
prBranch: 'feat/automated-update-for-specs',
commitMessage: 'feat: update specs',
files: {
type: 'specs',
ext: 'json',
output: 'src/data',
placeholderVariables: { appId: 'applicationId' },
},
},
],
},
};
16 changes: 13 additions & 3 deletions scripts/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ buildCommand
.option('-s, --skip-cache', 'skip cache checking to force building specs')
.option('-j, --json', 'outputs the spec in JSON instead of yml')
.option('-d, --docs', 'generates the doc specs with the code snippets')
.action(async (clientArg: string[], { verbose, skipCache, outputJson, docs }) => {
.action(async (clientArg: string[], { verbose, skipCache, json, docs }) => {
const { client, clientList } = transformSelection({
langArg: ALL,
clientArg,
Expand All @@ -149,10 +149,20 @@ buildCommand

await buildSpecs({
clients: client[0] === ALL ? clientList : client,
outputFormat: outputJson ? 'json' : 'yml',
docs: Boolean(docs),
outputFormat: json ? 'json' : 'yml',
docs: docs || json,
useCache: !skipCache,
});

// when building for the docs we generate both formats
if (docs && !json) {
await buildSpecs({
clients: client[0] === ALL ? clientList : client,
outputFormat: 'json',
docs: true,
useCache: !skipCache,
});
}
});

const ctsCommand = program.command('cts').description('Generate and run the CTS tests');
Expand Down
16 changes: 8 additions & 8 deletions scripts/specs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,14 @@ const ALGOLIASEARCH_LITE_OPERATIONS = ['search', 'customPost', 'getRecommendatio
async function buildLiteSpec({
spec,
bundledPath,
outputFormat,
docs,
useCache,
}: {
}: BaseBuildSpecsOptions & {
spec: string;
bundledPath: string;
docs: boolean;
useCache: boolean;
}): Promise<void> {
await buildSpec({ spec: 'recommend', outputFormat: 'yml', docs, useCache });
await buildSpec({ spec: 'recommend', outputFormat: outputFormat, docs, useCache });

const base = yaml.load(await fsp.readFile(toAbsolutePath(bundledPath), 'utf8')) as Spec;
const recommend = yaml.load(
Expand All @@ -51,7 +50,7 @@ async function buildLiteSpec({
await fsp.writeFile(bundledPath, yaml.dump(lite));

// remove unused components for the outputted light spec
await run(`yarn openapi bundle ${bundledPath} -o ${bundledPath} --ext yml --remove-unused-components`);
await run(`yarn redocly bundle ${bundledPath} -o ${bundledPath} --ext ${outputFormat} --remove-unused-components`);

await bundleSpecsForClient(bundledPath, spec);
}
Expand All @@ -73,7 +72,7 @@ async function buildSpec({

// In case of lite we use a the `search` spec as a base because only its bundled form exists.
const specBase = isLiteSpec ? 'search' : spec;
const logSuffix = docs ? 'doc spec' : 'spec';
const logSuffix = `${outputFormat} ${docs ? 'doc spec' : 'spec'}`;
const basePath = docs ? 'docs/' : 'specs/';
const deps = isLiteSpec ? ['search', 'recommend'] : [spec];
const cache = new Cache({
Expand Down Expand Up @@ -102,7 +101,7 @@ async function buildSpec({

// Then bundle the file
const bundledPath = toAbsolutePath(`${basePath}/bundled/${spec}.${outputFormat}`);
await run(`yarn openapi bundle specs/${specBase}/spec.yml -o ${bundledPath} --ext ${outputFormat}`);
await run(`yarn redocly bundle specs/${specBase}/spec.yml -o ${bundledPath} --ext ${outputFormat} `);

if (!(await exists(bundledPath))) {
throw new Error(`Bundled file not found ${bundledPath}.`);
Expand All @@ -115,13 +114,14 @@ async function buildSpec({
await buildLiteSpec({
spec,
bundledPath: toAbsolutePath(bundledPath),
outputFormat,
docs,
useCache,
});
}

spinner.text = `validating '${spec}' ${logSuffix}`;
await run(`yarn openapi lint ${bundledPath}`);
await run(`yarn redocly lint ${bundledPath}`);

spinner.text = `linting '${spec}' ${logSuffix}`;
await run(`yarn specs:fix ${basePath}/bundled/${spec}.${outputFormat}`);
Expand Down
1 change: 1 addition & 0 deletions specs/abtesting/paths/abtests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ get:
tags:
- abtest
operationId: listABTests
x-mcp-tool: true
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as per @kombucha's comment this might be easier/better to handle at the mcp repository level, unless we see some documentation purposes that we want to reflect on our API reference pages?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be indeed reflected in the documentation at some point! I'd stick with your solution !

Copy link
Contributor

@kombucha kombucha Apr 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this metadata should leave in the source of truth (ie this repository) eventually, I just thought we might want to experiment first in the mcp repo while we're figuring things out before doing it here; especially if we start defining mcp specific description.
Also we might want to think whether we want to opt-in endpoints vs opt-out depending on the ratio.

With that being said, maybe the push mecanism already makes it comfortable to contribute here directly 😄

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you see that as a non blocker so? We can also make it clearer that this is where the source of truth lives if a PR is opened on the mcp side as well, the good part of having it here is mostly reflecting it from the documentation

x-acl:
- analytics
summary: List all A/B tests
Expand Down
1 change: 1 addition & 0 deletions specs/abtesting/spec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ servers:
- url: https://analytics.{region}.algolia.com
variables:
region:
description: The region where your Algolia application is hosted.
enum:
- us
- de
Expand Down
1 change: 1 addition & 0 deletions specs/analytics/paths/search/getNoResultsRate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ get:
tags:
- search
operationId: getNoResultsRate
x-mcp-tool: true
x-acl:
- analytics
summary: Retrieve no results rate
Expand Down
1 change: 1 addition & 0 deletions specs/analytics/paths/search/getTopHits.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ get:
tags:
- search
operationId: getTopHits
x-mcp-tool: true
x-acl:
- analytics
summary: Retrieve top search results
Expand Down
1 change: 1 addition & 0 deletions specs/analytics/paths/search/getTopSearches.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ get:
tags:
- search
operationId: getTopSearches
x-mcp-tool: true
x-acl:
- analytics
summary: Retrieve top searches
Expand Down
1 change: 1 addition & 0 deletions specs/analytics/spec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ servers:
- url: https://analytics.{region}.algolia.com
variables:
region:
description: The region where your Algolia application is hosted.
enum:
- us
- de
Expand Down
1 change: 1 addition & 0 deletions specs/ingestion/paths/destinations/destinations.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ get:
summary: List destinations
description: Retrieves a list of destinations.
operationId: listDestinations
x-mcp-tool: true
x-acl:
- addObject
- deleteIndex
Expand Down
1 change: 1 addition & 0 deletions specs/ingestion/paths/sources/sources.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ get:
summary: List sources
description: Retrieves a list of sources.
operationId: listSources
x-mcp-tool: true
x-acl:
- addObject
- deleteIndex
Expand Down
1 change: 1 addition & 0 deletions specs/ingestion/paths/tasks/v2/tasks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ get:
summary: List tasks
description: Retrieves a list of tasks.
operationId: listTasks
x-mcp-tool: true
x-acl:
- addObject
- deleteIndex
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ get:
summary: List transformations
description: Retrieves a list of transformations.
operationId: listTransformations
x-mcp-tool: true
x-acl:
- addObject
- deleteIndex
Expand Down
1 change: 1 addition & 0 deletions specs/ingestion/spec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ servers:
- url: https://data.{region}.algolia.com
variables:
region:
description: The region where your Algolia application is hosted.
enum:
- eu
- us
Expand Down
1 change: 1 addition & 0 deletions specs/insights/spec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ servers:
- url: https://insights.{region}.algolia.io
variables:
region:
description: The region where your Algolia application is hosted.
enum:
- us
- de
Expand Down
1 change: 1 addition & 0 deletions specs/monitoring/paths/getClusterStatus.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ get:
summary: Retrieve cluster status
description: Retrieves the status of selected clusters.
operationId: getClusterStatus
x-mcp-tool: true
tags:
- status
security: []
Expand Down
1 change: 1 addition & 0 deletions specs/monitoring/paths/getIncidents.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ get:
summary: Retrieve all incidents
description: Retrieves known incidents for all clusters.
operationId: getIncidents
x-mcp-tool: true
security: []
tags:
- incidents
Expand Down
1 change: 1 addition & 0 deletions specs/personalization/spec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ servers:
- url: https://personalization.{region}.algolia.com
variables:
region:
description: The region where your Algolia application is hosted.
enum:
- us
- eu
Expand Down
1 change: 1 addition & 0 deletions specs/query-suggestions/paths/getConfigurationStatus.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ get:
tags:
- configurations
operationId: getConfigStatus
x-mcp-tool: true
x-acl:
- settings
summary: Retrieve configuration status
Expand Down
1 change: 1 addition & 0 deletions specs/query-suggestions/paths/getLogFile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ get:
tags:
- logs
operationId: getLogFile
x-mcp-tool: true
x-acl:
- settings
summary: Retrieve logs
Expand Down
Loading