Skip to content

Commit 2d4a8fb

Browse files
committed
docs: add sdkName option and symlink creation for SDK packages in registry
1 parent 05e5874 commit 2d4a8fb

File tree

4 files changed

+135
-4
lines changed

4 files changed

+135
-4
lines changed

docs/src/content/docs/targets/registry.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,13 @@ Avoid having multiple `registry` targets—it supports batching multiple apps an
1919
### App/SDK Options
2020

2121
| Option | Description |
22-
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
22+
|-------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
2323
| `urlTemplate` | URL template for artifact download links in the manifest. Supports `{{version}}`, `{{file}}`, and `{{revision}}` variables. Primarily for apps and CDN-hosted assets—not needed for SDK packages installed from public registries (npm, PyPI, etc.) |
2424
| `linkPrereleases` | Update for preview releases. Default: `false` |
2525
| `checksums` | List of checksum configs |
2626
| `onlyIfPresent` | Only run if matching file exists |
2727
| `name` | Human-readable name (used when creating new packages) |
28+
| `sdkName` | SDK identifier matching the SDK's `sdk_info.name` field in Sentry events (e.g., `sentry.javascript.react`). Will create the `sdks/` symlink. (used when creating new packages) |
2829
| `packageUrl` | Link to package registry page, e.g., npmjs.com (used when creating new packages) |
2930
| `mainDocsUrl` | Link to main documentation (used when creating new packages) |
3031
| `apiDocsUrl` | Link to API documentation (used when creating new packages) |
@@ -59,14 +60,17 @@ targets:
5960
6061
## Creating New Packages
6162
62-
When you introduce a new package that doesn't yet exist in the release registry, Craft will automatically create the required directory structure and initial manifest.
63+
When you introduce a new package that doesn't yet exist in the release registry, Craft will automatically create the required directory structure and initial manifest on the first publish.
64+
65+
Supply `name`, `packageUrl`, `sdkName` and `mainDocsUrl` so the release registry entry is added to the registry for the first time (existing packages just need `onlyIfPresent` since the manifest already exists):
6366

6467
```yaml
6568
targets:
6669
- name: registry
6770
sdks:
6871
'npm:@sentry/wasm':
6972
name: 'Sentry WASM'
73+
sdkName: 'sentry.javascript.wasm'
7074
packageUrl: 'https://www.npmjs.com/package/@sentry/wasm'
7175
mainDocsUrl: 'https://docs.sentry.io/platforms/javascript/'
7276
```
@@ -90,4 +94,4 @@ The value is always overwritten on every publish, so it stays in sync with the a
9094

9195
### Other metadata
9296

93-
When specified, the metadata fields (`name`, `packageUrl`, `mainDocsUrl`, `apiDocsUrl`) are applied to every release, allowing you to update package metadata by changing your `.craft.yml` configuration.
97+
When specified, the metadata fields (`name`, `sdkName`, `packageUrl`, `mainDocsUrl`, `apiDocsUrl`) are applied to every release, allowing you to update package metadata by changing your `.craft.yml` configuration.

src/targets/registry.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ export interface RegistryConfig {
6767
onlyIfPresent?: RegExp;
6868
/** Human-readable name for new packages */
6969
name?: string;
70+
/** SDK identifier used in the `sdk_info.name` field of the event (e.g. "sentry.javascript.react"). Used to create an entry in the registry's sdks/ directory. */
71+
sdkName?: string;
7072
/** Link to package registry (PyPI, npm, etc.) */
7173
packageUrl?: string;
7274
/** Link to main documentation */
@@ -459,6 +461,7 @@ export class RegistryTarget extends BaseTarget {
459461
canonical: registryConfig.canonicalName,
460462
repoUrl: `https://github.com/${owner}/${repo}`,
461463
name: registryConfig.name,
464+
sdkName: registryConfig.sdkName,
462465
packageUrl: registryConfig.packageUrl,
463466
mainDocsUrl: registryConfig.mainDocsUrl,
464467
apiDocsUrl: registryConfig.apiDocsUrl,

src/utils/__tests__/registry.test.ts

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,5 +200,115 @@ describe('getPackageManifest', () => {
200200
api_docs_url: 'https://docs.sentry.io/api/',
201201
});
202202
});
203+
204+
describe('sdkName symlink creation', () => {
205+
it('creates a sdks/ symlink when sdkName is provided', async () => {
206+
fs.mkdirSync(path.join(tempDir, 'sdks'), { recursive: true });
207+
const initialData: InitialManifestData = {
208+
canonical: 'npm:@sentry/hono',
209+
repoUrl: 'https://github.com/getsentry/sentry-javascript',
210+
name: 'Sentry Hono SDK',
211+
sdkName: 'sentry.javascript.hono',
212+
};
213+
214+
await getPackageManifest(
215+
tempDir,
216+
RegistryPackageType.SDK,
217+
'npm:@sentry/hono',
218+
'1.0.0',
219+
initialData,
220+
);
221+
222+
const symlinkPath = path.join(tempDir, 'sdks', 'sentry.javascript.hono');
223+
expect(fs.existsSync(symlinkPath)).toBe(true);
224+
expect(fs.lstatSync(symlinkPath).isSymbolicLink()).toBe(true);
225+
expect(fs.readlinkSync(symlinkPath)).toBe(
226+
path.join('..', 'packages', 'npm', '@sentry', 'hono'),
227+
);
228+
});
229+
230+
it('does not create a sdks/ symlink when sdkName is not provided', async () => {
231+
fs.mkdirSync(path.join(tempDir, 'sdks'), { recursive: true });
232+
const initialData: InitialManifestData = {
233+
canonical: 'npm:@sentry/hono',
234+
repoUrl: 'https://github.com/getsentry/sentry-javascript',
235+
};
236+
237+
await getPackageManifest(
238+
tempDir,
239+
RegistryPackageType.SDK,
240+
'npm:@sentry/hono',
241+
'1.0.0',
242+
initialData,
243+
);
244+
245+
const sdksDir = path.join(tempDir, 'sdks');
246+
expect(fs.readdirSync(sdksDir)).toHaveLength(0);
247+
});
248+
249+
it('skips symlink creation when the symlink already exists', async () => {
250+
fs.mkdirSync(path.join(tempDir, 'sdks'), { recursive: true });
251+
const symlinkPath = path.join(tempDir, 'sdks', 'sentry.javascript.hono');
252+
const existingTarget = path.join(
253+
'..',
254+
'packages',
255+
'npm',
256+
'@sentry',
257+
'hono',
258+
);
259+
fs.symlinkSync(existingTarget, symlinkPath);
260+
261+
const initialData: InitialManifestData = {
262+
canonical: 'npm:@sentry/hono',
263+
repoUrl: 'https://github.com/getsentry/sentry-javascript',
264+
sdkName: 'sentry.javascript.hono',
265+
};
266+
267+
// Should not throw or overwrite the existing symlink
268+
await expect(
269+
getPackageManifest(
270+
tempDir,
271+
RegistryPackageType.SDK,
272+
'npm:@sentry/hono',
273+
'1.0.0',
274+
initialData,
275+
),
276+
).resolves.not.toThrow();
277+
278+
expect(fs.readlinkSync(symlinkPath)).toBe(existingTarget);
279+
});
280+
281+
it('does not create a sdks/ symlink for existing packages (only new ones)', async () => {
282+
fs.mkdirSync(path.join(tempDir, 'sdks'), { recursive: true });
283+
284+
// Set up an existing package with latest.json
285+
const packageDir = path.join(
286+
tempDir,
287+
'packages',
288+
'npm',
289+
'@sentry',
290+
'browser',
291+
);
292+
fs.mkdirSync(packageDir, { recursive: true });
293+
fs.writeFileSync(
294+
path.join(packageDir, 'latest.json'),
295+
JSON.stringify({
296+
canonical: 'npm:@sentry/browser',
297+
version: '1.0.0',
298+
}),
299+
);
300+
301+
await getPackageManifest(
302+
tempDir,
303+
RegistryPackageType.SDK,
304+
'npm:@sentry/browser',
305+
'1.1.0',
306+
// sdkName is only consulted when the package is new
307+
);
308+
309+
const sdksDir = path.join(tempDir, 'sdks');
310+
expect(fs.readdirSync(sdksDir)).toHaveLength(0);
311+
});
312+
});
203313
});
204314
});

src/utils/registry.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { promises as fsPromises, existsSync, mkdirSync } from 'fs';
1+
import { promises as fsPromises, existsSync, mkdirSync, symlinkSync } from 'fs';
22
import * as path from 'path';
33

44
import { logger } from '../logger';
@@ -29,6 +29,8 @@ export interface InitialManifestData {
2929
mainDocsUrl?: string;
3030
/** Link to API documentation */
3131
apiDocsUrl?: string;
32+
/** SDK identifier used in the `sdk_info.name` field of the event (e.g. "sentry.javascript.react"). Used to create an entry in the registry's sdks/ directory. */
33+
sdkName?: string;
3234
}
3335

3436
/**
@@ -108,6 +110,18 @@ export async function getPackageManifest(
108110
mkdirSync(fullPackageDir, { recursive: true });
109111
}
110112

113+
// Create the sdks/ symlink when an sdkName is provided
114+
if (initialManifestData.sdkName) {
115+
const sdkSymlinkPath = path.join(baseDir, 'sdks', initialManifestData.sdkName);
116+
if (!existsSync(sdkSymlinkPath)) {
117+
const relativeTarget = path.join('..', packageDirPath);
118+
logger.info(
119+
`Creating sdks symlink "${initialManifestData.sdkName}" -> "${relativeTarget}"...`,
120+
);
121+
symlinkSync(relativeTarget, sdkSymlinkPath);
122+
}
123+
}
124+
111125
logger.info(
112126
`Creating initial manifest for new package "${canonicalName}"...`,
113127
);

0 commit comments

Comments
 (0)