Skip to content

Commit 55528d3

Browse files
committed
implement pack for base publish provider and use in both providers
1 parent 37c223b commit 55528d3

9 files changed

Lines changed: 416 additions & 39 deletions

File tree

common/reviews/api/rush-lib.api.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -832,6 +832,7 @@ export interface IPublishProjectInfo {
832832
// @beta
833833
export interface IPublishProvider {
834834
checkExistsAsync(options: IPublishProviderCheckExistsOptions): Promise<boolean>;
835+
packAsync(options: IPublishProviderPackOptions): Promise<void>;
835836
readonly providerName: string;
836837
publishAsync(options: IPublishProviderPublishOptions): Promise<void>;
837838
}
@@ -843,6 +844,14 @@ export interface IPublishProviderCheckExistsOptions {
843844
readonly version: string;
844845
}
845846

847+
// @beta
848+
export interface IPublishProviderPackOptions {
849+
readonly dryRun: boolean;
850+
readonly logger: ILogger;
851+
readonly projects: ReadonlyArray<IPublishProjectInfo>;
852+
readonly releaseFolder: string;
853+
}
854+
846855
// @beta
847856
export interface IPublishProviderPublishOptions {
848857
readonly dryRun: boolean;

libraries/rush-lib/src/cli/actions/PublishAction.ts

Lines changed: 48 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import { type IChangeInfo, ChangeType } from '../../api/ChangeManagement';
1616
import { type IPublishJson, PUBLISH_CONFIGURATION_FILE } from '../../api/PublishConfiguration';
1717
import type { RushConfigurationProject } from '../../api/RushConfigurationProject';
1818
import type { RushCommandLineParser } from '../RushCommandLineParser';
19-
import { PublishUtilities } from '../../logic/PublishUtilities';
2019
import { ChangelogGenerator } from '../../logic/ChangelogGenerator';
2120
import { PrereleaseToken } from '../../logic/PrereleaseToken';
2221
import { ChangeManager } from '../../logic/ChangeManager';
@@ -409,8 +408,8 @@ export class PublishAction extends BaseRushAction {
409408
};
410409

411410
if (this._pack.value) {
412-
// packs to tarball instead of publishing to NPM repository
413-
await this._npmPackAsync(packageName, packageConfig);
411+
// packs to distributable artifacts via publish providers
412+
await this._packProjectViaProvidersAsync(packageConfig);
414413
await applyTagAsync(this._applyGitTagsOnPack.value);
415414
} else {
416415
const published: boolean = await this._publishProjectViaProvidersAsync(packageConfig);
@@ -500,45 +499,57 @@ export class PublishAction extends BaseRushAction {
500499
return published;
501500
}
502501

503-
private async _npmPackAsync(packageName: string, project: RushConfigurationProject): Promise<void> {
504-
const args: string[] = ['pack'];
505-
const env: { [key: string]: string | undefined } = PublishUtilities.getEnvArgs();
506-
507-
await PublishUtilities.execCommandAsync({
508-
shouldExecute: this._publish.value,
509-
command: this.rushConfiguration.packageManagerToolFilename,
510-
args,
511-
workingDirectory: project.publishFolder,
512-
environment: env
502+
/**
503+
* Pack a project via all of its registered publish targets.
504+
* Returns true if at least one target produced an artifact.
505+
*/
506+
private async _packProjectViaProvidersAsync(project: RushConfigurationProject): Promise<boolean> {
507+
let packed: boolean = false;
508+
const version: string = project.packageJsonEditor.version;
509+
const dryRun: boolean = !this._publish.value;
510+
const logger: Logger = new Logger({
511+
loggerName: 'publish',
512+
terminalProvider: new ConsoleTerminalProvider({ verboseEnabled: false }),
513+
getShouldPrintStacks: () => false
513514
});
514515

515-
if (this._publish.value) {
516-
// Copy the tarball the release folder
517-
const tarballName: string = this._calculateTarballName(project);
518-
const tarballPath: string = path.join(project.publishFolder, tarballName);
519-
const destFolder: string = this._releaseFolder.value
520-
? this._releaseFolder.value
521-
: path.join(this.rushConfiguration.commonTempFolder, 'artifacts', 'packages');
522-
523-
FileSystem.move({
524-
sourcePath: tarballPath,
525-
destinationPath: path.join(destFolder, tarballName),
526-
overwrite: true
527-
});
528-
}
529-
}
516+
// Determine the release folder
517+
const releaseFolder: string = this._releaseFolder.value
518+
? this._releaseFolder.value
519+
: path.join(this.rushConfiguration.commonTempFolder, 'artifacts', 'packages');
530520

531-
private _calculateTarballName(project: RushConfigurationProject): string {
532-
// Same logic as how npm forms the tarball name
533-
const packageName: string = project.packageName;
534-
const name: string = packageName[0] === '@' ? packageName.substr(1).replace(/\//g, '-') : packageName;
521+
// Ensure the release folder exists
522+
FileSystem.ensureFolder(releaseFolder);
535523

536-
if (this.rushConfiguration.packageManager === 'yarn') {
537-
// yarn tarballs have a "v" before the version number
538-
return `${name}-v${project.packageJson.version}.tgz`;
539-
} else {
540-
return `${name}-${project.packageJson.version}.tgz`;
524+
for (const target of project.publishTargets) {
525+
if (target === 'none') {
526+
continue;
527+
}
528+
529+
const provider: IPublishProvider = await this._getProviderAsync(target, project.packageName);
530+
const providerConfig: Record<string, unknown> | undefined = await this._getProviderConfigAsync(
531+
project,
532+
target
533+
);
534+
535+
await provider.packAsync({
536+
projects: [
537+
{
538+
project,
539+
newVersion: version,
540+
previousVersion: version,
541+
changeType: ChangeType.none,
542+
providerConfig
543+
}
544+
],
545+
releaseFolder,
546+
dryRun,
547+
logger
548+
});
549+
packed = true;
541550
}
551+
552+
return packed;
542553
}
543554

544555
private _setDependenciesBeforePublish(): void {

libraries/rush-lib/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ export type {
190190
IPublishProvider,
191191
IPublishProjectInfo,
192192
IPublishProviderPublishOptions,
193+
IPublishProviderPackOptions,
193194
IPublishProviderCheckExistsOptions,
194195
PublishProviderFactory
195196
} from './pluginFramework/IPublishProvider';

libraries/rush-lib/src/pluginFramework/IPublishProvider.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,36 @@ export interface IPublishProviderCheckExistsOptions {
8585
readonly providerConfig: Record<string, unknown> | undefined;
8686
}
8787

88+
/**
89+
* Options passed to {@link IPublishProvider.packAsync}.
90+
* @beta
91+
*/
92+
export interface IPublishProviderPackOptions {
93+
/**
94+
* The set of projects to pack.
95+
*/
96+
readonly projects: ReadonlyArray<IPublishProjectInfo>;
97+
98+
/**
99+
* The folder where packed artifacts should be placed.
100+
* Corresponds to the `--release-folder` CLI parameter.
101+
* When not specified, a default location is used
102+
* (e.g., `<commonTempFolder>/artifacts/packages`).
103+
*/
104+
readonly releaseFolder: string;
105+
106+
/**
107+
* If true, the provider should perform all steps except the actual pack,
108+
* logging what would have been done.
109+
*/
110+
readonly dryRun: boolean;
111+
112+
/**
113+
* A logger instance for reporting progress and errors.
114+
*/
115+
readonly logger: ILogger;
116+
}
117+
88118
/**
89119
* Interface for publish providers that handle publishing packages to a specific target
90120
* (e.g. npm registry, VS Code Marketplace).
@@ -106,6 +136,16 @@ export interface IPublishProvider {
106136
*/
107137
publishAsync(options: IPublishProviderPublishOptions): Promise<void>;
108138

139+
/**
140+
* Packs the specified projects into distributable artifacts for this provider's target.
141+
* Each provider defines what "packing" means for its artifact type:
142+
* - npm: runs `<packageManager> pack` to produce a `.tgz` tarball
143+
* - vsix: runs `vsce package` to produce a `.vsix` file
144+
*
145+
* Artifacts are written to the `releaseFolder` specified in options.
146+
*/
147+
packAsync(options: IPublishProviderPackOptions): Promise<void>;
148+
109149
/**
110150
* Checks whether a specific version of a project already exists at the publish target.
111151
* Returns true if the version is already published.

libraries/rush-lib/src/pluginFramework/test/RushSession.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ function createMockFactory(providerName: string): PublishProviderFactory {
1717
return async () => ({
1818
providerName,
1919
publishAsync: async () => {},
20+
packAsync: async () => {},
2021
checkExistsAsync: async () => false
2122
});
2223
}

rush-plugins/rush-npm-publish-plugin/src/NpmPublishProvider.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { FileSystem } from '@rushstack/node-core-library';
1111
import type {
1212
IPublishProvider,
1313
IPublishProviderPublishOptions,
14+
IPublishProviderPackOptions,
1415
IPublishProviderCheckExistsOptions,
1516
IPublishProjectInfo
1617
} from '@rushstack/rush-sdk';
@@ -140,6 +141,58 @@ export class NpmPublishProvider implements IPublishProvider {
140141
return publishedVersions.indexOf(normalizedVersion) >= 0;
141142
}
142143

144+
public async packAsync(options: IPublishProviderPackOptions): Promise<void> {
145+
const { projects, releaseFolder, dryRun, logger } = options;
146+
147+
for (const projectInfo of projects) {
148+
const { project, newVersion } = projectInfo;
149+
const packageName: string = project.packageName;
150+
const publishFolder: string = project.publishFolder;
151+
152+
logger.terminal.writeLine(`Packing ${packageName}@${newVersion} as npm tarball...`);
153+
154+
const args: string[] = ['pack'];
155+
const env: Record<string, string | undefined> = { ...process.env };
156+
const packageManagerToolFilename: string = project.rushConfiguration.packageManagerToolFilename;
157+
158+
if (dryRun) {
159+
logger.terminal.writeLine(
160+
` [DRY RUN] Would execute: ${packageManagerToolFilename} ${args.join(' ')}`
161+
);
162+
logger.terminal.writeLine(` Working directory: ${publishFolder}`);
163+
} else {
164+
await this._executeCommandAsync(packageManagerToolFilename, args, publishFolder, env);
165+
166+
// Move the tarball to the release folder
167+
const tarballName: string = this._calculateTarballName(project);
168+
const tarballPath: string = path.join(publishFolder, tarballName);
169+
170+
FileSystem.move({
171+
sourcePath: tarballPath,
172+
destinationPath: path.join(releaseFolder, tarballName),
173+
overwrite: true
174+
});
175+
176+
logger.terminal.writeLine(` Packed ${packageName}@${newVersion} to ${tarballName}`);
177+
}
178+
}
179+
}
180+
181+
/**
182+
* Calculate the tarball filename using npm's naming convention.
183+
*/
184+
private _calculateTarballName(project: IPublishProjectInfo['project']): string {
185+
const packageName: string = project.packageName;
186+
const name: string =
187+
packageName[0] === '@' ? packageName.substring(1).replace(/\//g, '-') : packageName;
188+
189+
if (project.rushConfiguration.packageManager === 'yarn') {
190+
return `${name}-v${project.packageJson.version}.tgz`;
191+
} else {
192+
return `${name}-${project.packageJson.version}.tgz`;
193+
}
194+
}
195+
143196
/**
144197
* Configure the HOME directory to use .npmrc-publish from the Rush config.
145198
*/

0 commit comments

Comments
 (0)