Skip to content
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
3 changes: 3 additions & 0 deletions packages/core/src/helpers/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ const yellow = chalk.bold.yellow;
// - DATADOG_SOURCEMAP_INTAKE_URL
// - DD_APPS_INTAKE_URL
// - DATADOG_APPS_INTAKE_URL
// - DD_APPS_PUBLISH
// - DATADOG_APPS_PUBLISH
// - DD_APPS_UPLOAD_ASSETS
// - DATADOG_APPS_UPLOAD_ASSETS
// - DD_APPS_VERSION_NAME
Expand All @@ -29,6 +31,7 @@ const OVERRIDE_VARIABLES = [
'APP_KEY',
'SOURCEMAP_INTAKE_URL',
'APPS_INTAKE_URL',
'APPS_PUBLISH',
'APPS_UPLOAD_ASSETS',
'APPS_VERSION_NAME',
'SITE',
Expand Down
21 changes: 21 additions & 0 deletions packages/plugins/apps/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ A plugin to upload assets to Datadog's storage
- [apps.include](#appsinclude)
- [apps.identifier](#appsidentifier)
- [apps.name](#appsname)
- [apps.publish](#appspublish)
<!-- #toc -->

## Configuration
Expand All @@ -31,6 +32,7 @@ apps?: {
include?: string[];
identifier?: string;
name?: string;
publish?: boolean;
}
```

Expand Down Expand Up @@ -81,3 +83,22 @@ Can be useful to enforce a static identifier instead of relying on possibly chan
Override the app's name used in the assets upload API request.

Can be useful to enforce a static name instead of relying on the package.json name field.

### apps.publish

> default: `true`

When `true` (the default), the plugin publishes the uploaded version to live immediately after upload. Set to `false` to upload a draft without publishing it — useful for staging environments or CI pipelines where a separate approval step controls promotion.

You can also disable publishing via the `DATADOG_APPS_PUBLISH=false` (or `DD_APPS_PUBLISH=false`) environment variable. The explicit `apps.publish` config takes precedence over the environment variable.

To add a dedicated upload-without-publish command to your project, add this script to your `package.json`:

```json
{
"scripts": {
"upload": "DD_APPS_UPLOAD_ASSETS=1 vite build",
"upload-no-publish": "DD_APPS_UPLOAD_ASSETS=1 DD_APPS_PUBLISH=false vite build"
}
}
```
1 change: 1 addition & 0 deletions packages/plugins/apps/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ describe('Apps Plugin - getPlugins', () => {
dryRun: true,
identifier: 'repo:app',
name: 'test-app',
publish: true,
site: DEFAULT_SITE,
version: 'FAKE_VERSION',
},
Expand Down
6 changes: 5 additions & 1 deletion packages/plugins/apps/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ export type AppsOptions = {
dryRun?: boolean;
identifier?: string;
name?: string;
// When false, skips the release/live call after upload so the app is saved
// as a draft without being published. Defaults to true. Can also be set via
// the DD_APPS_PUBLISH=false environment variable.
publish?: boolean;
};

export type AppsManifest = {
Expand All @@ -26,6 +30,6 @@ export type AppsManifest = {

// We don't enforce identifier, as it needs to be dynamically computed if absent.
export type AppsOptionsWithDefaults = Omit<
WithRequired<AppsOptions, 'include' | 'dryRun'>,
WithRequired<AppsOptions, 'include' | 'dryRun' | 'publish'>,
'enable'
>;
25 changes: 25 additions & 0 deletions packages/plugins/apps/src/upload.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ describe('Apps Plugin - upload', () => {
dryRun: false,
identifier: 'repo:app',
name: 'test-app',
publish: true,
site: 'datadoghq.com',
version: '1.0.0',
};
Expand Down Expand Up @@ -286,6 +287,30 @@ describe('Apps Plugin - upload', () => {
);
});

test('Should skip release/live call and log draft message when publish is false', async () => {
doRequestMock.mockResolvedValueOnce({
version_id: 'v123',
application_id: 'app123',
app_builder_id: 'builder123',
});

const { errors, warnings } = await uploadArchive(
archive,
{ ...context, publish: false },
logger,
);

expect(errors).toHaveLength(0);
expect(warnings).toHaveLength(0);
// Only the upload POST — no release PUT.
expect(doRequestMock).toHaveBeenCalledTimes(1);
expect(doRequestMock).toHaveBeenCalledWith(expect.objectContaining({ method: 'POST' }));
expect(mockLogFn).toHaveBeenCalledWith(
expect.stringContaining('draft (publish skipped)'),
'info',
);
});

test('Should collect warnings on retries', async () => {
doRequestMock.mockImplementation(async (opts) => {
opts.onRetry?.(new Error('network'), 2);
Expand Down
5 changes: 4 additions & 1 deletion packages/plugins/apps/src/upload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export type UploadContext = {
dryRun: boolean;
identifier: string;
name: string;
publish: boolean;
site: string;
version: string;
};
Expand Down Expand Up @@ -137,7 +138,7 @@ Would have uploaded ${summary}`,
log.info(`Your application is available at:\n ${cyan(appBuilderUrl)}`);
}

if (response.version_id) {
if (response.version_id && context.publish) {
const releaseUrl = getReleaseUrl(context.site, context.identifier);
await doRequest({
auth: { apiKey: context.apiKey, appKey: context.appKey },
Expand All @@ -160,6 +161,8 @@ Would have uploaded ${summary}`,
},
});
log.info(`Published uploaded version ${bold(response.version_id)} to live.`);
} else if (response.version_id && !context.publish) {
log.info(`Uploaded version ${bold(response.version_id)} as a draft (publish skipped).`);
}
} catch (error: unknown) {
const err = error instanceof Error ? error : new Error(String(error));
Expand Down
37 changes: 37 additions & 0 deletions packages/plugins/apps/src/validate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ describe('Apps Plugin - validateOptions', () => {
include: [],
identifier: undefined,
name: undefined,
publish: true,
});
});

Expand Down Expand Up @@ -45,6 +46,41 @@ describe('Apps Plugin - validateOptions', () => {
delete process.env.DATADOG_APPS_UPLOAD_ASSETS;
}
});

test('Should set publish to false when DD_APPS_PUBLISH=false', () => {
process.env.DD_APPS_PUBLISH = 'false';
try {
const result = validateOptions({ apps: {} });
expect(result.publish).toBe(false);
} finally {
delete process.env.DD_APPS_PUBLISH;
}
});

test('Should set publish to false when DATADOG_APPS_PUBLISH=false', () => {
process.env.DATADOG_APPS_PUBLISH = 'false';
try {
const result = validateOptions({ apps: {} });
expect(result.publish).toBe(false);
} finally {
delete process.env.DATADOG_APPS_PUBLISH;
}
});

test('Should default publish to true when DD_APPS_PUBLISH is not set', () => {
const result = validateOptions({ apps: {} });
expect(result.publish).toBe(true);
});

test('Should respect explicit publish: false option over env var', () => {
process.env.DATADOG_APPS_PUBLISH = 'true';
try {
const result = validateOptions({ apps: { publish: false } });
expect(result.publish).toBe(false);
} finally {
delete process.env.DATADOG_APPS_PUBLISH;
}
});
});

describe('overrides', () => {
Expand All @@ -63,6 +99,7 @@ describe('Apps Plugin - validateOptions', () => {
include: ['public/**/*', 'dist/**/*'],
identifier: 'my-app',
name: undefined,
publish: true,
});
});
});
Expand Down
5 changes: 5 additions & 0 deletions packages/plugins/apps/src/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,15 @@ import type { AppsOptions, AppsOptionsWithDefaults } from './types';
export const validateOptions = (options: Options): AppsOptionsWithDefaults => {
const resolvedOptions = (options[CONFIG_KEY] || {}) as AppsOptions;

const envPublish = getDDEnvValue('APPS_PUBLISH');

return {
include: resolvedOptions.include || [],
dryRun: resolvedOptions.dryRun ?? !getDDEnvValue('APPS_UPLOAD_ASSETS'),
identifier: resolvedOptions.identifier?.trim(),
name: resolvedOptions.name?.trim() || options.metadata?.name?.trim(),
// Default to true (publish after upload). Set DD_APPS_PUBLISH=false or
// options.apps.publish=false to upload without publishing.
publish: resolvedOptions.publish ?? envPublish !== 'false',
};
};
1 change: 1 addition & 0 deletions packages/plugins/apps/src/vite/handle-upload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ Either:
dryRun: options.dryRun,
identifier,
name,
publish: options.publish,
site: auth.site,
version,
},
Expand Down
1 change: 1 addition & 0 deletions packages/plugins/apps/src/vite/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ const defaultOptions = {
enable: true,
include: [],
dryRun: true,
publish: true,
},
};

Expand Down
Loading