relizy 1.3.2 → 1.4.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.mjs CHANGED
@@ -5,7 +5,7 @@ import process from 'node:process';
5
5
  import { fileURLToPath } from 'node:url';
6
6
  import { printBanner, logger } from '@maz-ui/node';
7
7
  import { Command } from 'commander';
8
- import { am as isInCI, an as getCIName, b as bump, c as changelog, g as publish, e as providerRelease, h as social, p as prComment, r as release } from './shared/relizy.ubFyghBI.mjs';
8
+ import { ao as isInCI, ap as getCIName, b as bump, c as changelog, g as publish, e as providerRelease, h as social, p as prComment, r as release } from './shared/relizy.CxbwJj6k.mjs';
9
9
  import 'node:child_process';
10
10
  import '@maz-ui/utils';
11
11
  import 'c12';
package/dist/index.d.mts CHANGED
@@ -60,6 +60,8 @@ declare function getDefaultConfig(): {
60
60
  slack: {
61
61
  enabled: boolean;
62
62
  onlyStable: boolean;
63
+ postMaxLength: number;
64
+ noAuthors: boolean;
63
65
  };
64
66
  };
65
67
  prComment: Required<PrCommentConfig>;
@@ -233,6 +235,16 @@ declare function buildChangelogBody({ commits, config, minify }: {
233
235
  config: ResolvedRelizyConfig;
234
236
  minify?: boolean;
235
237
  }): string;
238
+ /**
239
+ * Collect unique contributor names from a set of commits.
240
+ * Respects `config.noAuthors`, filters `[bot]` authors, and applies `config.excludeAuthors`.
241
+ * Returns plain formatted names (no emails, no GitHub handles) — useful for lightweight
242
+ * rendering contexts like Slack messages.
243
+ */
244
+ declare function collectContributorNames({ commits, config }: {
245
+ commits: GitCommit[];
246
+ config: ResolvedRelizyConfig;
247
+ }): string[];
236
248
  declare function buildContributors({ commits, config }: {
237
249
  commits: GitCommit[];
238
250
  config: ResolvedRelizyConfig;
@@ -347,6 +359,13 @@ declare function getSlackToken(options: {
347
359
  socialCredentials?: SlackCredentials;
348
360
  tokenCredential?: string;
349
361
  }): string | null;
362
+ /**
363
+ * Get Slack Incoming Webhook URL from config or environment variables.
364
+ * Priority: social.slack.webhookUrl > RELIZY_SLACK_WEBHOOK_URL > SLACK_WEBHOOK_URL.
365
+ */
366
+ declare function getSlackWebhookUrl(options: {
367
+ socialWebhookUrl?: string;
368
+ }): string | null;
350
369
  /**
351
370
  * Format changelog for Slack (convert markdown to Slack's mrkdwn format)
352
371
  */
@@ -354,18 +373,25 @@ declare function formatChangelogForSlack(changelog: string, maxLength?: number):
354
373
  /**
355
374
  * Format the Slack message using blocks
356
375
  */
357
- declare function formatSlackMessage({ projectName, version, changelog, releaseUrl, changelogUrl, template }: {
376
+ declare function formatSlackMessage({ projectName, version, changelog, releaseUrl, changelogUrl, template, contributors, postMaxLength }: {
358
377
  template?: string;
359
378
  projectName: string;
360
379
  version: string;
361
380
  changelog: string;
362
381
  releaseUrl?: string;
363
382
  changelogUrl?: string;
383
+ contributors?: string[];
384
+ postMaxLength?: number;
364
385
  }): any[];
365
386
  /**
366
- * Post a release announcement to Slack
387
+ * Post a release announcement to Slack.
388
+ * Dispatches to Incoming Webhook (if `webhookUrl` is set) or Web API (`token` + `channel`).
389
+ * When both are provided, the webhook takes priority.
367
390
  */
368
- declare function postReleaseToSlack({ version, projectName, changelog, releaseUrl, changelogUrl, channel, token, template, dryRun, }: SlackOptions): Promise<_slack_web_api.ChatPostMessageResponse | undefined>;
391
+ declare function postReleaseToSlack({ version, projectName, changelog, releaseUrl, changelogUrl, channel, token, webhookUrl, template, contributors, postMaxLength, dryRun, }: SlackOptions): Promise<{
392
+ ok: true;
393
+ transport: "webhook";
394
+ } | _slack_web_api.ChatPostMessageResponse | undefined>;
369
395
 
370
396
  /**
371
397
  * Extract a summary from changelog content
@@ -1269,8 +1295,10 @@ interface TwitterSocialConfig {
1269
1295
  }
1270
1296
  interface SlackCredentials {
1271
1297
  /**
1272
- * Slack Bot Token or User OAuth Token
1273
- * Required scopes: chat:write, chat:write.public (for public channels)
1298
+ * Slack Bot Token or User OAuth Token (starts with `xoxb-`).
1299
+ * Required scopes: chat:write (and chat:write.public for public channels without bot invite).
1300
+ * Env fallback: SLACK_TOKEN, RELIZY_SLACK_TOKEN.
1301
+ * Ignored when social.slack.webhookUrl is set (webhook takes priority).
1274
1302
  */
1275
1303
  token?: string;
1276
1304
  }
@@ -1287,18 +1315,38 @@ interface SlackSocialConfig {
1287
1315
  */
1288
1316
  onlyStable?: boolean;
1289
1317
  /**
1290
- * Slack channel ID or name (e.g., "#releases" or "C1234567890")
1318
+ * Slack channel ID or name (e.g., "#releases" or "C1234567890").
1319
+ * Required when using token-based authentication.
1320
+ * Ignored (with warning) when webhookUrl is set — the channel is baked into the webhook URL.
1291
1321
  */
1292
- channel: string;
1322
+ channel?: string;
1323
+ /**
1324
+ * Slack Incoming Webhook URL. When set, takes priority over token-based auth.
1325
+ * Env fallback: SLACK_WEBHOOK_URL, RELIZY_SLACK_WEBHOOK_URL.
1326
+ * See: https://api.slack.com/messaging/webhooks
1327
+ */
1328
+ webhookUrl?: string;
1293
1329
  /**
1294
1330
  * Custom message template
1295
- * Available variables: {{projectName}}, {{newVersion}}, {{changelog}}, {{releaseUrl}}, {{changelogUrl}}
1331
+ * Available variables: {{projectName}}, {{newVersion}}, {{changelog}}, {{releaseUrl}}, {{changelogUrl}}, {{contributors}}
1296
1332
  */
1297
1333
  template?: string;
1298
1334
  /**
1299
1335
  * Slack credentials (optional - falls back to environment variables)
1300
1336
  */
1301
1337
  credentials?: SlackCredentials;
1338
+ /**
1339
+ * Maximum length (in characters) of the changelog rendered inside the Slack message.
1340
+ * Slack's per-section-block limit is 3000; default 2500 leaves margin for emoji/formatting.
1341
+ * @default 2500
1342
+ */
1343
+ postMaxLength?: number;
1344
+ /**
1345
+ * Hide the contributors block in Slack messages.
1346
+ * If config.noAuthors is true globally, contributors are always hidden regardless of this setting.
1347
+ * @default false
1348
+ */
1349
+ noAuthors?: boolean;
1302
1350
  }
1303
1351
  type AIProviderName = 'claude-code';
1304
1352
  interface ClaudeCodeProviderOptions {
@@ -1462,17 +1510,30 @@ interface SlackOptions {
1462
1510
  */
1463
1511
  changelogUrl?: string;
1464
1512
  /**
1465
- * Slack channel ID or name
1513
+ * Slack channel ID or name. Required when using token-based auth.
1466
1514
  */
1467
- channel: string;
1515
+ channel?: string;
1468
1516
  /**
1469
- * Slack token (required)
1517
+ * Slack Bot Token. Ignored if webhookUrl is set.
1470
1518
  */
1471
- token: string;
1519
+ token?: string;
1520
+ /**
1521
+ * Slack Incoming Webhook URL. Takes priority over token.
1522
+ */
1523
+ webhookUrl?: string;
1472
1524
  /**
1473
1525
  * Custom message template
1474
1526
  */
1475
1527
  template?: string;
1528
+ /**
1529
+ * Maximum chars of the changelog rendered in the message.
1530
+ * @default 2500
1531
+ */
1532
+ postMaxLength?: number;
1533
+ /**
1534
+ * Contributor names (plain strings, no email/handle). Empty array or undefined → no contributors block.
1535
+ */
1536
+ contributors?: string[];
1476
1537
  /**
1477
1538
  * Run without side effects
1478
1539
  * @default false
@@ -1754,5 +1815,5 @@ declare function socialSafetyCheck({ config }: {
1754
1815
  }): Promise<void>;
1755
1816
  declare function social(options?: Partial<SocialOptions>): Promise<SocialResult>;
1756
1817
 
1757
- export { NEW_PACKAGE_MARKER, PR_COMMENT_MARKER, buildChangelogBody, buildCommentBody, buildCompareLink, buildContributors, bump, capReleaseTypeForZeroMajor, changelog, checkGitStatusIfDirty, confirmBump, createCommitAndTags, createGitlabRelease, defineConfig, detectGitProvider, detectPackageManager, detectPullRequest, determinePublishTag, determineReleaseType, determineSemverChange, executeBuildCmd, executeFormatCmd, executeHook, expandPackagesToBumpWithDependents, extractChangelogSummary, extractVersionFromPackageTag, extractVersionFromTag, fetchGitTags, filterOutPrivatePackages, findGitHubPR, findGitLabMR, formatChangelogForSlack, formatSlackMessage, formatTweetMessage, generateChangelog, generateMarkDown, getAuthCommand, getBumpedIndependentPackages, getBumpedPackageIndependently, getCIName, getCanaryVersion, getCurrentGitBranch, getCurrentGitRef, getDefaultConfig, getDependentsOf, getFirstCommit, getGitStatus, getIndependentTag, getLastPackageTag, getLastRepoTag, getLastStableTag, getLastTag, getModifiedReleaseFilePatterns, getPackageCommits, getPackageDependencies, getPackageNewVersion, getPackages, getPackagesOrBumpedPackages, getPackagesToPublishInIndependentMode, getPackagesToPublishInSelectiveMode, getPreid, getReleaseUrl, getRootPackage, getShortCommitSha, getSlackToken, getTwitterCredentials, github, gitlab, hasLernaJson, isBumpedPackage, isChangedPreid, isGraduating, isGraduatingToStableBetweenVersion, isInCI, isPrerelease, isPrereleaseReleaseType, isStableReleaseType, isTagVersionCompatibleWithCurrent, loadRelizyConfig, mergeTypes, parseChangelogMarkdown, parseGitRemoteUrl, postPrComment, postReleaseToSlack, postReleaseToTwitter, prComment, providerRelease, providerReleaseSafetyCheck, publish, publishPackage, publishSafetyCheck, pushCommitAndTags, readPackageJson, readPackages, release, resolveTags, rollbackModifiedFiles, shouldFilterPrereleaseTags, social, socialSafetyCheck, topologicalSort, updateLernaVersion, writeChangelogToFile, writeVersion };
1818
+ export { NEW_PACKAGE_MARKER, PR_COMMENT_MARKER, buildChangelogBody, buildCommentBody, buildCompareLink, buildContributors, bump, capReleaseTypeForZeroMajor, changelog, checkGitStatusIfDirty, collectContributorNames, confirmBump, createCommitAndTags, createGitlabRelease, defineConfig, detectGitProvider, detectPackageManager, detectPullRequest, determinePublishTag, determineReleaseType, determineSemverChange, executeBuildCmd, executeFormatCmd, executeHook, expandPackagesToBumpWithDependents, extractChangelogSummary, extractVersionFromPackageTag, extractVersionFromTag, fetchGitTags, filterOutPrivatePackages, findGitHubPR, findGitLabMR, formatChangelogForSlack, formatSlackMessage, formatTweetMessage, generateChangelog, generateMarkDown, getAuthCommand, getBumpedIndependentPackages, getBumpedPackageIndependently, getCIName, getCanaryVersion, getCurrentGitBranch, getCurrentGitRef, getDefaultConfig, getDependentsOf, getFirstCommit, getGitStatus, getIndependentTag, getLastPackageTag, getLastRepoTag, getLastStableTag, getLastTag, getModifiedReleaseFilePatterns, getPackageCommits, getPackageDependencies, getPackageNewVersion, getPackages, getPackagesOrBumpedPackages, getPackagesToPublishInIndependentMode, getPackagesToPublishInSelectiveMode, getPreid, getReleaseUrl, getRootPackage, getShortCommitSha, getSlackToken, getSlackWebhookUrl, getTwitterCredentials, github, gitlab, hasLernaJson, isBumpedPackage, isChangedPreid, isGraduating, isGraduatingToStableBetweenVersion, isInCI, isPrerelease, isPrereleaseReleaseType, isStableReleaseType, isTagVersionCompatibleWithCurrent, loadRelizyConfig, mergeTypes, parseChangelogMarkdown, parseGitRemoteUrl, postPrComment, postReleaseToSlack, postReleaseToTwitter, prComment, providerRelease, providerReleaseSafetyCheck, publish, publishPackage, publishSafetyCheck, pushCommitAndTags, readPackageJson, readPackages, release, resolveTags, rollbackModifiedFiles, shouldFilterPrereleaseTags, social, socialSafetyCheck, topologicalSort, updateLernaVersion, writeChangelogToFile, writeVersion };
1758
1819
  export type { AIConfig, AIPromptTarget, AIProviderName, AISocialConfig, AISystemPromptOverrides, AITargetConfig, BumpConfig, BumpOptions, BumpResult, BumpResultFalsy, BumpResultTruthy, ChangelogConfig, ChangelogOptions, ClaudeCodeProviderOptions, ConfigType, GitProvider, GitlabRelease, GitlabReleaseResponse, HookConfig, HookStep, HookType, MonorepoConfig, PackageBase, PackageManager, PostedRelease, PrCommentConfig, PrCommentMode, PrCommentOptions, PrCommentStatus, ProviderReleaseOptions, ProviderReleaseResult, PublishConfig, PublishOptions, PublishResponse, PullRequestInfo, ReadPackage, Reference, ReleaseConfig, ReleaseContext, ReleaseOptions, RelizyConfig, RepoConfig, ResolvedConfig, ResolvedRelizyConfig, ResolvedTags, ResolvedTwitterCredentials, RootPackage, SlackCredentials, SlackOptions, SlackSocialConfig, SocialConfig, SocialNetworkResult, SocialOptions, SocialResult, Step, TemplatesConfig, TokensConfig, TwitterCredentials, TwitterOptions, TwitterSocialConfig, VersionMode };
package/dist/index.d.ts CHANGED
@@ -60,6 +60,8 @@ declare function getDefaultConfig(): {
60
60
  slack: {
61
61
  enabled: boolean;
62
62
  onlyStable: boolean;
63
+ postMaxLength: number;
64
+ noAuthors: boolean;
63
65
  };
64
66
  };
65
67
  prComment: Required<PrCommentConfig>;
@@ -233,6 +235,16 @@ declare function buildChangelogBody({ commits, config, minify }: {
233
235
  config: ResolvedRelizyConfig;
234
236
  minify?: boolean;
235
237
  }): string;
238
+ /**
239
+ * Collect unique contributor names from a set of commits.
240
+ * Respects `config.noAuthors`, filters `[bot]` authors, and applies `config.excludeAuthors`.
241
+ * Returns plain formatted names (no emails, no GitHub handles) — useful for lightweight
242
+ * rendering contexts like Slack messages.
243
+ */
244
+ declare function collectContributorNames({ commits, config }: {
245
+ commits: GitCommit[];
246
+ config: ResolvedRelizyConfig;
247
+ }): string[];
236
248
  declare function buildContributors({ commits, config }: {
237
249
  commits: GitCommit[];
238
250
  config: ResolvedRelizyConfig;
@@ -347,6 +359,13 @@ declare function getSlackToken(options: {
347
359
  socialCredentials?: SlackCredentials;
348
360
  tokenCredential?: string;
349
361
  }): string | null;
362
+ /**
363
+ * Get Slack Incoming Webhook URL from config or environment variables.
364
+ * Priority: social.slack.webhookUrl > RELIZY_SLACK_WEBHOOK_URL > SLACK_WEBHOOK_URL.
365
+ */
366
+ declare function getSlackWebhookUrl(options: {
367
+ socialWebhookUrl?: string;
368
+ }): string | null;
350
369
  /**
351
370
  * Format changelog for Slack (convert markdown to Slack's mrkdwn format)
352
371
  */
@@ -354,18 +373,25 @@ declare function formatChangelogForSlack(changelog: string, maxLength?: number):
354
373
  /**
355
374
  * Format the Slack message using blocks
356
375
  */
357
- declare function formatSlackMessage({ projectName, version, changelog, releaseUrl, changelogUrl, template }: {
376
+ declare function formatSlackMessage({ projectName, version, changelog, releaseUrl, changelogUrl, template, contributors, postMaxLength }: {
358
377
  template?: string;
359
378
  projectName: string;
360
379
  version: string;
361
380
  changelog: string;
362
381
  releaseUrl?: string;
363
382
  changelogUrl?: string;
383
+ contributors?: string[];
384
+ postMaxLength?: number;
364
385
  }): any[];
365
386
  /**
366
- * Post a release announcement to Slack
387
+ * Post a release announcement to Slack.
388
+ * Dispatches to Incoming Webhook (if `webhookUrl` is set) or Web API (`token` + `channel`).
389
+ * When both are provided, the webhook takes priority.
367
390
  */
368
- declare function postReleaseToSlack({ version, projectName, changelog, releaseUrl, changelogUrl, channel, token, template, dryRun, }: SlackOptions): Promise<_slack_web_api.ChatPostMessageResponse | undefined>;
391
+ declare function postReleaseToSlack({ version, projectName, changelog, releaseUrl, changelogUrl, channel, token, webhookUrl, template, contributors, postMaxLength, dryRun, }: SlackOptions): Promise<{
392
+ ok: true;
393
+ transport: "webhook";
394
+ } | _slack_web_api.ChatPostMessageResponse | undefined>;
369
395
 
370
396
  /**
371
397
  * Extract a summary from changelog content
@@ -1269,8 +1295,10 @@ interface TwitterSocialConfig {
1269
1295
  }
1270
1296
  interface SlackCredentials {
1271
1297
  /**
1272
- * Slack Bot Token or User OAuth Token
1273
- * Required scopes: chat:write, chat:write.public (for public channels)
1298
+ * Slack Bot Token or User OAuth Token (starts with `xoxb-`).
1299
+ * Required scopes: chat:write (and chat:write.public for public channels without bot invite).
1300
+ * Env fallback: SLACK_TOKEN, RELIZY_SLACK_TOKEN.
1301
+ * Ignored when social.slack.webhookUrl is set (webhook takes priority).
1274
1302
  */
1275
1303
  token?: string;
1276
1304
  }
@@ -1287,18 +1315,38 @@ interface SlackSocialConfig {
1287
1315
  */
1288
1316
  onlyStable?: boolean;
1289
1317
  /**
1290
- * Slack channel ID or name (e.g., "#releases" or "C1234567890")
1318
+ * Slack channel ID or name (e.g., "#releases" or "C1234567890").
1319
+ * Required when using token-based authentication.
1320
+ * Ignored (with warning) when webhookUrl is set — the channel is baked into the webhook URL.
1291
1321
  */
1292
- channel: string;
1322
+ channel?: string;
1323
+ /**
1324
+ * Slack Incoming Webhook URL. When set, takes priority over token-based auth.
1325
+ * Env fallback: SLACK_WEBHOOK_URL, RELIZY_SLACK_WEBHOOK_URL.
1326
+ * See: https://api.slack.com/messaging/webhooks
1327
+ */
1328
+ webhookUrl?: string;
1293
1329
  /**
1294
1330
  * Custom message template
1295
- * Available variables: {{projectName}}, {{newVersion}}, {{changelog}}, {{releaseUrl}}, {{changelogUrl}}
1331
+ * Available variables: {{projectName}}, {{newVersion}}, {{changelog}}, {{releaseUrl}}, {{changelogUrl}}, {{contributors}}
1296
1332
  */
1297
1333
  template?: string;
1298
1334
  /**
1299
1335
  * Slack credentials (optional - falls back to environment variables)
1300
1336
  */
1301
1337
  credentials?: SlackCredentials;
1338
+ /**
1339
+ * Maximum length (in characters) of the changelog rendered inside the Slack message.
1340
+ * Slack's per-section-block limit is 3000; default 2500 leaves margin for emoji/formatting.
1341
+ * @default 2500
1342
+ */
1343
+ postMaxLength?: number;
1344
+ /**
1345
+ * Hide the contributors block in Slack messages.
1346
+ * If config.noAuthors is true globally, contributors are always hidden regardless of this setting.
1347
+ * @default false
1348
+ */
1349
+ noAuthors?: boolean;
1302
1350
  }
1303
1351
  type AIProviderName = 'claude-code';
1304
1352
  interface ClaudeCodeProviderOptions {
@@ -1462,17 +1510,30 @@ interface SlackOptions {
1462
1510
  */
1463
1511
  changelogUrl?: string;
1464
1512
  /**
1465
- * Slack channel ID or name
1513
+ * Slack channel ID or name. Required when using token-based auth.
1466
1514
  */
1467
- channel: string;
1515
+ channel?: string;
1468
1516
  /**
1469
- * Slack token (required)
1517
+ * Slack Bot Token. Ignored if webhookUrl is set.
1470
1518
  */
1471
- token: string;
1519
+ token?: string;
1520
+ /**
1521
+ * Slack Incoming Webhook URL. Takes priority over token.
1522
+ */
1523
+ webhookUrl?: string;
1472
1524
  /**
1473
1525
  * Custom message template
1474
1526
  */
1475
1527
  template?: string;
1528
+ /**
1529
+ * Maximum chars of the changelog rendered in the message.
1530
+ * @default 2500
1531
+ */
1532
+ postMaxLength?: number;
1533
+ /**
1534
+ * Contributor names (plain strings, no email/handle). Empty array or undefined → no contributors block.
1535
+ */
1536
+ contributors?: string[];
1476
1537
  /**
1477
1538
  * Run without side effects
1478
1539
  * @default false
@@ -1754,5 +1815,5 @@ declare function socialSafetyCheck({ config }: {
1754
1815
  }): Promise<void>;
1755
1816
  declare function social(options?: Partial<SocialOptions>): Promise<SocialResult>;
1756
1817
 
1757
- export { NEW_PACKAGE_MARKER, PR_COMMENT_MARKER, buildChangelogBody, buildCommentBody, buildCompareLink, buildContributors, bump, capReleaseTypeForZeroMajor, changelog, checkGitStatusIfDirty, confirmBump, createCommitAndTags, createGitlabRelease, defineConfig, detectGitProvider, detectPackageManager, detectPullRequest, determinePublishTag, determineReleaseType, determineSemverChange, executeBuildCmd, executeFormatCmd, executeHook, expandPackagesToBumpWithDependents, extractChangelogSummary, extractVersionFromPackageTag, extractVersionFromTag, fetchGitTags, filterOutPrivatePackages, findGitHubPR, findGitLabMR, formatChangelogForSlack, formatSlackMessage, formatTweetMessage, generateChangelog, generateMarkDown, getAuthCommand, getBumpedIndependentPackages, getBumpedPackageIndependently, getCIName, getCanaryVersion, getCurrentGitBranch, getCurrentGitRef, getDefaultConfig, getDependentsOf, getFirstCommit, getGitStatus, getIndependentTag, getLastPackageTag, getLastRepoTag, getLastStableTag, getLastTag, getModifiedReleaseFilePatterns, getPackageCommits, getPackageDependencies, getPackageNewVersion, getPackages, getPackagesOrBumpedPackages, getPackagesToPublishInIndependentMode, getPackagesToPublishInSelectiveMode, getPreid, getReleaseUrl, getRootPackage, getShortCommitSha, getSlackToken, getTwitterCredentials, github, gitlab, hasLernaJson, isBumpedPackage, isChangedPreid, isGraduating, isGraduatingToStableBetweenVersion, isInCI, isPrerelease, isPrereleaseReleaseType, isStableReleaseType, isTagVersionCompatibleWithCurrent, loadRelizyConfig, mergeTypes, parseChangelogMarkdown, parseGitRemoteUrl, postPrComment, postReleaseToSlack, postReleaseToTwitter, prComment, providerRelease, providerReleaseSafetyCheck, publish, publishPackage, publishSafetyCheck, pushCommitAndTags, readPackageJson, readPackages, release, resolveTags, rollbackModifiedFiles, shouldFilterPrereleaseTags, social, socialSafetyCheck, topologicalSort, updateLernaVersion, writeChangelogToFile, writeVersion };
1818
+ export { NEW_PACKAGE_MARKER, PR_COMMENT_MARKER, buildChangelogBody, buildCommentBody, buildCompareLink, buildContributors, bump, capReleaseTypeForZeroMajor, changelog, checkGitStatusIfDirty, collectContributorNames, confirmBump, createCommitAndTags, createGitlabRelease, defineConfig, detectGitProvider, detectPackageManager, detectPullRequest, determinePublishTag, determineReleaseType, determineSemverChange, executeBuildCmd, executeFormatCmd, executeHook, expandPackagesToBumpWithDependents, extractChangelogSummary, extractVersionFromPackageTag, extractVersionFromTag, fetchGitTags, filterOutPrivatePackages, findGitHubPR, findGitLabMR, formatChangelogForSlack, formatSlackMessage, formatTweetMessage, generateChangelog, generateMarkDown, getAuthCommand, getBumpedIndependentPackages, getBumpedPackageIndependently, getCIName, getCanaryVersion, getCurrentGitBranch, getCurrentGitRef, getDefaultConfig, getDependentsOf, getFirstCommit, getGitStatus, getIndependentTag, getLastPackageTag, getLastRepoTag, getLastStableTag, getLastTag, getModifiedReleaseFilePatterns, getPackageCommits, getPackageDependencies, getPackageNewVersion, getPackages, getPackagesOrBumpedPackages, getPackagesToPublishInIndependentMode, getPackagesToPublishInSelectiveMode, getPreid, getReleaseUrl, getRootPackage, getShortCommitSha, getSlackToken, getSlackWebhookUrl, getTwitterCredentials, github, gitlab, hasLernaJson, isBumpedPackage, isChangedPreid, isGraduating, isGraduatingToStableBetweenVersion, isInCI, isPrerelease, isPrereleaseReleaseType, isStableReleaseType, isTagVersionCompatibleWithCurrent, loadRelizyConfig, mergeTypes, parseChangelogMarkdown, parseGitRemoteUrl, postPrComment, postReleaseToSlack, postReleaseToTwitter, prComment, providerRelease, providerReleaseSafetyCheck, publish, publishPackage, publishSafetyCheck, pushCommitAndTags, readPackageJson, readPackages, release, resolveTags, rollbackModifiedFiles, shouldFilterPrereleaseTags, social, socialSafetyCheck, topologicalSort, updateLernaVersion, writeChangelogToFile, writeVersion };
1758
1819
  export type { AIConfig, AIPromptTarget, AIProviderName, AISocialConfig, AISystemPromptOverrides, AITargetConfig, BumpConfig, BumpOptions, BumpResult, BumpResultFalsy, BumpResultTruthy, ChangelogConfig, ChangelogOptions, ClaudeCodeProviderOptions, ConfigType, GitProvider, GitlabRelease, GitlabReleaseResponse, HookConfig, HookStep, HookType, MonorepoConfig, PackageBase, PackageManager, PostedRelease, PrCommentConfig, PrCommentMode, PrCommentOptions, PrCommentStatus, ProviderReleaseOptions, ProviderReleaseResult, PublishConfig, PublishOptions, PublishResponse, PullRequestInfo, ReadPackage, Reference, ReleaseConfig, ReleaseContext, ReleaseOptions, RelizyConfig, RepoConfig, ResolvedConfig, ResolvedRelizyConfig, ResolvedTags, ResolvedTwitterCredentials, RootPackage, SlackCredentials, SlackOptions, SlackSocialConfig, SocialConfig, SocialNetworkResult, SocialOptions, SocialResult, Step, TemplatesConfig, TokensConfig, TwitterCredentials, TwitterOptions, TwitterSocialConfig, VersionMode };
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export { ag as NEW_PACKAGE_MARKER, Z as PR_COMMENT_MARKER, M as buildChangelogBody, a as buildCommentBody, L as buildCompareLink, N as buildContributors, b as bump, au as capReleaseTypeForZeroMajor, c as changelog, v as checkGitStatusIfDirty, aI as confirmBump, B as createCommitAndTags, J as createGitlabRelease, k as defineConfig, y as detectGitProvider, Q as detectPackageManager, Y as detectPullRequest, R as determinePublishTag, aw as determineReleaseType, av as determineSemverChange, ap as executeBuildCmd, ao as executeFormatCmd, al as executeHook, q as expandPackagesToBumpWithDependents, a9 as extractChangelogSummary, aA as extractVersionFromPackageTag, aL as extractVersionFromTag, x as fetchGitTags, ar as filterOutPrivatePackages, W as findGitHubPR, X as findGitLabMR, a6 as formatChangelogForSlack, a7 as formatSlackMessage, aj as formatTweetMessage, i as generateChangelog, O as generateMarkDown, U as getAuthCommand, aJ as getBumpedIndependentPackages, aH as getBumpedPackageIndependently, an as getCIName, aM as getCanaryVersion, F as getCurrentGitBranch, G as getCurrentGitRef, j as getDefaultConfig, o as getDependentsOf, E as getFirstCommit, u as getGitStatus, ab as getIndependentTag, af as getLastPackageTag, ae as getLastRepoTag, ac as getLastStableTag, ad as getLastTag, A as getModifiedReleaseFilePatterns, a3 as getPackageCommits, n as getPackageDependencies, ay as getPackageNewVersion, a2 as getPackages, as as getPackagesOrBumpedPackages, T as getPackagesToPublishInIndependentMode, S as getPackagesToPublishInSelectiveMode, aF as getPreid, aa as getReleaseUrl, a0 as getRootPackage, H as getShortCommitSha, a5 as getSlackToken, ai as getTwitterCredentials, I as github, K as gitlab, a4 as hasLernaJson, aq as isBumpedPackage, aG as isChangedPreid, aE as isGraduating, at as isGraduatingToStableBetweenVersion, am as isInCI, aB as isPrerelease, aD as isPrereleaseReleaseType, aC as isStableReleaseType, aN as isTagVersionCompatibleWithCurrent, l as loadRelizyConfig, m as mergeTypes, P as parseChangelogMarkdown, z as parseGitRemoteUrl, _ as postPrComment, a8 as postReleaseToSlack, ak as postReleaseToTwitter, p as prComment, e as providerRelease, d as providerReleaseSafetyCheck, g as publish, V as publishPackage, f as publishSafetyCheck, C as pushCommitAndTags, $ as readPackageJson, a1 as readPackages, r as release, ah as resolveTags, D as rollbackModifiedFiles, aK as shouldFilterPrereleaseTags, h as social, s as socialSafetyCheck, t as topologicalSort, az as updateLernaVersion, w as writeChangelogToFile, ax as writeVersion } from './shared/relizy.ubFyghBI.mjs';
1
+ export { ai as NEW_PACKAGE_MARKER, _ as PR_COMMENT_MARKER, M as buildChangelogBody, a as buildCommentBody, L as buildCompareLink, O as buildContributors, b as bump, aw as capReleaseTypeForZeroMajor, c as changelog, v as checkGitStatusIfDirty, N as collectContributorNames, aK as confirmBump, B as createCommitAndTags, J as createGitlabRelease, k as defineConfig, y as detectGitProvider, R as detectPackageManager, Z as detectPullRequest, S as determinePublishTag, ay as determineReleaseType, ax as determineSemverChange, ar as executeBuildCmd, aq as executeFormatCmd, an as executeHook, q as expandPackagesToBumpWithDependents, ab as extractChangelogSummary, aC as extractVersionFromPackageTag, aN as extractVersionFromTag, x as fetchGitTags, at as filterOutPrivatePackages, X as findGitHubPR, Y as findGitLabMR, a8 as formatChangelogForSlack, a9 as formatSlackMessage, al as formatTweetMessage, i as generateChangelog, P as generateMarkDown, V as getAuthCommand, aL as getBumpedIndependentPackages, aJ as getBumpedPackageIndependently, ap as getCIName, aO as getCanaryVersion, F as getCurrentGitBranch, G as getCurrentGitRef, j as getDefaultConfig, o as getDependentsOf, E as getFirstCommit, u as getGitStatus, ad as getIndependentTag, ah as getLastPackageTag, ag as getLastRepoTag, ae as getLastStableTag, af as getLastTag, A as getModifiedReleaseFilePatterns, a4 as getPackageCommits, n as getPackageDependencies, aA as getPackageNewVersion, a3 as getPackages, au as getPackagesOrBumpedPackages, U as getPackagesToPublishInIndependentMode, T as getPackagesToPublishInSelectiveMode, aH as getPreid, ac as getReleaseUrl, a1 as getRootPackage, H as getShortCommitSha, a6 as getSlackToken, a7 as getSlackWebhookUrl, ak as getTwitterCredentials, I as github, K as gitlab, a5 as hasLernaJson, as as isBumpedPackage, aI as isChangedPreid, aG as isGraduating, av as isGraduatingToStableBetweenVersion, ao as isInCI, aD as isPrerelease, aF as isPrereleaseReleaseType, aE as isStableReleaseType, aP as isTagVersionCompatibleWithCurrent, l as loadRelizyConfig, m as mergeTypes, Q as parseChangelogMarkdown, z as parseGitRemoteUrl, $ as postPrComment, aa as postReleaseToSlack, am as postReleaseToTwitter, p as prComment, e as providerRelease, d as providerReleaseSafetyCheck, g as publish, W as publishPackage, f as publishSafetyCheck, C as pushCommitAndTags, a0 as readPackageJson, a2 as readPackages, r as release, aj as resolveTags, D as rollbackModifiedFiles, aM as shouldFilterPrereleaseTags, h as social, s as socialSafetyCheck, t as topologicalSort, aB as updateLernaVersion, w as writeChangelogToFile, az as writeVersion } from './shared/relizy.CxbwJj6k.mjs';
2
2
  import '@maz-ui/node';
3
3
  import 'node:child_process';
4
4
  import 'node:process';
@@ -1914,7 +1914,9 @@ function getDefaultConfig() {
1914
1914
  },
1915
1915
  slack: {
1916
1916
  enabled: false,
1917
- onlyStable: true
1917
+ onlyStable: true,
1918
+ postMaxLength: 2500,
1919
+ noAuthors: false
1918
1920
  }
1919
1921
  },
1920
1922
  prComment: {
@@ -2382,11 +2384,11 @@ function buildChangelogBody({ commits, config, minify }) {
2382
2384
  }
2383
2385
  return convert(markdown.join("\n").trim(), true);
2384
2386
  }
2385
- async function buildContributors({ commits, config }) {
2387
+ function collectContributorNames({ commits, config }) {
2386
2388
  if (config.noAuthors) {
2387
- return "";
2389
+ return [];
2388
2390
  }
2389
- const _authors = /* @__PURE__ */ new Map();
2391
+ const names = /* @__PURE__ */ new Set();
2390
2392
  for (const commit of commits) {
2391
2393
  if (!commit.author) {
2392
2394
  continue;
@@ -2400,11 +2402,26 @@ async function buildContributors({ commits, config }) {
2400
2402
  )) {
2401
2403
  continue;
2402
2404
  }
2403
- if (_authors.has(name)) {
2404
- const entry = _authors.get(name);
2405
- entry?.email.add(commit.author.email);
2406
- } else {
2407
- _authors.set(name, { email: /* @__PURE__ */ new Set([commit.author.email]), name });
2405
+ names.add(name);
2406
+ }
2407
+ return Array.from(names);
2408
+ }
2409
+ async function buildContributors({ commits, config }) {
2410
+ const names = collectContributorNames({ commits, config });
2411
+ if (names.length === 0) {
2412
+ return "";
2413
+ }
2414
+ const _authors = /* @__PURE__ */ new Map();
2415
+ for (const name of names) {
2416
+ _authors.set(name, { email: /* @__PURE__ */ new Set(), name });
2417
+ }
2418
+ for (const commit of commits) {
2419
+ if (!commit.author)
2420
+ continue;
2421
+ const name = formatName(commit.author.name);
2422
+ const entry = _authors.get(name);
2423
+ if (entry && commit.author.email) {
2424
+ entry.email.add(commit.author.email);
2408
2425
  }
2409
2426
  }
2410
2427
  if (config.repo?.provider === "github") {
@@ -2638,24 +2655,25 @@ The user message contains a markdown changelog built from conventional commits,
2638
2655
  Never ask for clarification. Never reply with questions, greetings, or meta-commentary. Your only output is the rewritten changelog content.
2639
2656
  Never invent changes that are not in the input \u2014 if something is not mentioned, it does not exist.
2640
2657
  Never include compare links, contributor lists, or release metadata \u2014 only the content provided.
2641
- Preserve exactly as given: PR and issue references like #123, commit hashes, commit scopes like **auth:**, and all markdown links.
2642
2658
  Respond with the rewritten content only \u2014 no preamble, no explanation, no surrounding code fence, no <changelog> tag.
2643
2659
  Output language: {{language}}.`;
2644
2660
  const PROVIDER_RELEASE_PROMPT = `Format the output as markdown with "### <Type>" sections matching the input (Features, Bug Fixes, etc.).
2645
2661
  Merge redundant items that describe the same change.
2646
- Rewrite each bullet for end-user clarity \u2014 not "fix: typo in var name" but "Fixed X".
2647
- Preserve the \u26A0\uFE0F marker on breaking items and keep the "#### \u26A0\uFE0F Breaking Changes" section if present.
2662
+ Rewrite each bullet for end-user clarity \u2014 focus on what changed for the user, not internal details.
2663
+ Preserve exactly as given: PR and issue references like #123, commit hashes, commit scopes like **auth:**, and all markdown links.
2664
+ Preserve the \u26A0\uFE0F marker on breaking items and the "#### \u26A0\uFE0F Breaking Changes" section if present.
2648
2665
  Purely internal commits (chore, refactor with no user-visible impact) may be dropped unless they carry meaningful information.
2649
2666
  Tone: professional, concise, public-facing release notes.`;
2650
2667
  const TWITTER_PROMPT = `Output plain text \u2014 no markdown. A leading "#" becomes a hashtag on Twitter, so avoid it.
2651
- Hard maximum: {{maxLength}} characters. Never exceed it. Don't count characters \u2014 just stay concise.
2668
+ Hard maximum: {{maxLength}} characters \u2014 never exceed it.
2669
+ Remove commit hashes (e.g. "aabf96b") and PR/issue references (e.g. "#123") \u2014 they are meaningless on Twitter.
2652
2670
  If the input has one change, write one substantive sentence about it (ground it in the input, never invent).
2653
2671
  If the input has multiple changes, surface 2 to 4 highlights.
2654
2672
  You produce ONLY the changelog content \u2014 the outer template will add the project name, version, and URLs around it.
2655
2673
  Tone: enthusiastic but not cringy. At most 1-2 emojis total.
2656
2674
  Do not add hashtags unless the user explicitly supplies them.`;
2657
2675
  const SLACK_PROMPT = `Format with Slack-compatible markdown: *bold*, _italic_, \`code\`, and "-" bullet lists.
2658
- Keep it synthetic \u2014 3 to 8 bullets maximum.
2676
+ Keep it concise \u2014 3 to 8 bullets maximum.
2659
2677
  If any breaking changes are present, lead with them.
2660
2678
  Tone: factual, oriented toward an internal team audience.`;
2661
2679
 
@@ -3716,7 +3734,10 @@ function getSlackToken(options) {
3716
3734
  }
3717
3735
  return token;
3718
3736
  }
3719
- function formatChangelogForSlack(changelog, maxLength = 500) {
3737
+ function getSlackWebhookUrl(options) {
3738
+ return options.socialWebhookUrl || process.env.RELIZY_SLACK_WEBHOOK_URL || process.env.SLACK_WEBHOOK_URL || null;
3739
+ }
3740
+ function formatChangelogForSlack(changelog, maxLength = 2500) {
3720
3741
  let formatted = changelog.replace(/^### (.+)$/gm, "*$1*").replace(/^## (.+)$/gm, "*$1*").replace(/^# (.+)$/gm, "*$1*").replace(/\*\*(.+?)\*\*/g, "*$1*");
3721
3742
  const linkPattern = /\[([^\]]*)]\(([^)]*)\)/g;
3722
3743
  formatted = formatted.replace(linkPattern, (_, text, url) => `<${url}|${text}>`);
@@ -3725,10 +3746,11 @@ function formatChangelogForSlack(changelog, maxLength = 500) {
3725
3746
  }
3726
3747
  return formatted;
3727
3748
  }
3728
- function formatSlackMessage({ projectName, version, changelog, releaseUrl, changelogUrl, template }) {
3749
+ function formatSlackMessage({ projectName, version, changelog, releaseUrl, changelogUrl, template, contributors = [], postMaxLength = 2500 }) {
3750
+ const contributorsLine = contributors.length > 0 ? contributors.map((n) => `\u2022 ${n}`).join("\n") : "";
3729
3751
  if (template) {
3730
- const summary = extractChangelogSummary(changelog, { maxLength: 500 });
3731
- let message = template.replace("{{projectName}}", projectName).replace("{{newVersion}}", version).replace("{{changelog}}", summary);
3752
+ const summary = extractChangelogSummary(changelog, { maxLength: postMaxLength });
3753
+ let message = template.replace("{{projectName}}", projectName).replace("{{newVersion}}", version).replace("{{changelog}}", summary).replace("{{contributors}}", contributorsLine);
3732
3754
  if (releaseUrl) {
3733
3755
  message = message.replace("{{releaseUrl}}", releaseUrl);
3734
3756
  } else {
@@ -3759,7 +3781,7 @@ function formatSlackMessage({ projectName, version, changelog, releaseUrl, chang
3759
3781
  }
3760
3782
  }
3761
3783
  ];
3762
- const formattedChangelog = formatChangelogForSlack(changelog, 500);
3784
+ const formattedChangelog = formatChangelogForSlack(changelog, postMaxLength);
3763
3785
  if (formattedChangelog) {
3764
3786
  blocks.push({
3765
3787
  type: "section",
@@ -3769,6 +3791,17 @@ function formatSlackMessage({ projectName, version, changelog, releaseUrl, chang
3769
3791
  }
3770
3792
  });
3771
3793
  }
3794
+ if (contributors.length > 0) {
3795
+ blocks.push({
3796
+ type: "section",
3797
+ text: {
3798
+ type: "mrkdwn",
3799
+ text: `*\u2764\uFE0F Contributors*
3800
+
3801
+ ${contributorsLine}`
3802
+ }
3803
+ });
3804
+ }
3772
3805
  blocks.push({
3773
3806
  type: "divider"
3774
3807
  });
@@ -3805,44 +3838,44 @@ function formatSlackMessage({ projectName, version, changelog, releaseUrl, chang
3805
3838
  }
3806
3839
  return blocks;
3807
3840
  }
3808
- async function postReleaseToSlack({
3809
- version,
3810
- projectName,
3811
- changelog,
3812
- releaseUrl,
3813
- changelogUrl,
3814
- channel,
3815
- token,
3816
- template,
3817
- dryRun = false
3818
- }) {
3819
- logger.debug("Preparing Slack post...");
3820
- const blocks = formatSlackMessage({
3821
- template,
3822
- projectName,
3823
- version,
3824
- changelog,
3825
- releaseUrl,
3826
- changelogUrl
3841
+ function mapWebhookError(status, body) {
3842
+ if (status === 404 || body.includes("no_service")) {
3843
+ return "The webhook URL is invalid or has been deactivated. Regenerate it in your Slack app settings.";
3844
+ }
3845
+ if (body.includes("invalid_payload")) {
3846
+ return "The message payload was rejected by Slack (likely exceeds 3000 chars per block). Lower social.slack.postMaxLength.";
3847
+ }
3848
+ if (body.includes("channel_not_found")) {
3849
+ return "The channel bound to this webhook was archived or removed. Create a new webhook.";
3850
+ }
3851
+ if (body.includes("action_prohibited")) {
3852
+ return "Your workspace has blocked the webhook. Check workspace settings.";
3853
+ }
3854
+ return null;
3855
+ }
3856
+ async function postViaWebhook({ url, blocks, text }) {
3857
+ const response = await fetch(url, {
3858
+ method: "POST",
3859
+ headers: { "Content-Type": "application/json" },
3860
+ body: JSON.stringify({ blocks, text })
3827
3861
  });
3828
- logger.debug(`Message blocks (${blocks.length} blocks)`);
3829
- if (dryRun) {
3830
- const preview = blocks.filter((b) => b.type === "header" || b.type === "section").map((b) => b.text?.text ?? "").filter(Boolean).join("\n\n");
3831
- logger.box(`[dry-run] Slack Post Preview (channel: ${channel})
3832
-
3833
- ${preview}`);
3834
- return;
3862
+ if (!response.ok) {
3863
+ const body = await response.text().catch(() => "");
3864
+ const hint = mapWebhookError(response.status, body);
3865
+ const detail = body ? ` - ${body}` : "";
3866
+ const hintLine = hint ? `
3867
+ \u2192 ${hint}` : "";
3868
+ throw new Error(`Slack webhook failed: ${response.status} ${response.statusText}${detail}${hintLine}`);
3835
3869
  }
3870
+ logger.success("Message posted successfully via Slack webhook!");
3871
+ return { ok: true, transport: "webhook" };
3872
+ }
3873
+ async function postViaWebApi({ token, channel, blocks, text }) {
3836
3874
  try {
3837
3875
  const { WebClient } = await import('@slack/web-api');
3838
3876
  const client = new WebClient(token);
3839
3877
  logger.debug(`Posting message to Slack channel: ${channel}`);
3840
- const result = await client.chat.postMessage({
3841
- channel,
3842
- blocks,
3843
- text: `${projectName} ${version} is out!`
3844
- // Fallback text for notifications
3845
- });
3878
+ const result = await client.chat.postMessage({ channel, blocks, text });
3846
3879
  logger.success(`Message posted successfully! Channel: ${result.channel}, Timestamp: ${result.ts}`);
3847
3880
  return result;
3848
3881
  } catch (error) {
@@ -3869,6 +3902,54 @@ ${preview}`);
3869
3902
  throw error;
3870
3903
  }
3871
3904
  }
3905
+ async function postReleaseToSlack({
3906
+ version,
3907
+ projectName,
3908
+ changelog,
3909
+ releaseUrl,
3910
+ changelogUrl,
3911
+ channel,
3912
+ token,
3913
+ webhookUrl,
3914
+ template,
3915
+ contributors,
3916
+ postMaxLength,
3917
+ dryRun = false
3918
+ }) {
3919
+ const useWebhook = Boolean(webhookUrl);
3920
+ if (!useWebhook && !token) {
3921
+ throw new Error("Slack: either webhookUrl or token must be provided");
3922
+ }
3923
+ if (!useWebhook && !channel) {
3924
+ throw new Error("Slack: channel is required when using token-based authentication");
3925
+ }
3926
+ logger.debug(`Slack transport: ${useWebhook ? "webhook" : "web-api"}`);
3927
+ logger.debug("Preparing Slack post...");
3928
+ const blocks = formatSlackMessage({
3929
+ template,
3930
+ projectName,
3931
+ version,
3932
+ changelog,
3933
+ releaseUrl,
3934
+ changelogUrl,
3935
+ contributors,
3936
+ postMaxLength
3937
+ });
3938
+ const fallbackText = `${projectName} ${version} is out!`;
3939
+ logger.debug(`Message blocks (${blocks.length} blocks)`);
3940
+ if (dryRun) {
3941
+ const preview = blocks.filter((b) => b.type === "header" || b.type === "section").map((b) => b.text?.text ?? "").filter(Boolean).join("\n\n");
3942
+ const target = useWebhook ? "webhook" : `channel: ${channel}`;
3943
+ logger.box(`[dry-run] Slack Post Preview (${target})
3944
+
3945
+ ${preview}`);
3946
+ return;
3947
+ }
3948
+ if (useWebhook) {
3949
+ return await postViaWebhook({ url: webhookUrl, blocks, text: fallbackText });
3950
+ }
3951
+ return await postViaWebApi({ token, channel, blocks, text: fallbackText });
3952
+ }
3872
3953
 
3873
3954
  function getTwitterCredentials({ socialCredentials, tokenCredentials }) {
3874
3955
  const apiKey = socialCredentials?.apiKey || tokenCredentials?.apiKey;
@@ -5179,27 +5260,37 @@ async function socialSafetyCheck({ config }) {
5179
5260
  }
5180
5261
  const slackConfig = config.social?.slack;
5181
5262
  if (slackConfig?.enabled) {
5263
+ const webhookUrl = getSlackWebhookUrl({ socialWebhookUrl: slackConfig.webhookUrl });
5182
5264
  const token = getSlackToken({
5183
5265
  socialCredentials: slackConfig.credentials,
5184
5266
  tokenCredential: config.tokens?.slack
5185
5267
  });
5186
- try {
5187
- await import('@slack/web-api');
5188
- } catch {
5189
- logger.fail("@slack/web-api is not installed, please install it");
5190
- throw new Error("@slack/web-api is not installed");
5191
- }
5192
- if (!token) {
5193
- logger.fail("Slack is enabled but credentials are missing.");
5194
- logger.log("Set the following environment variables or configure them in social.slack.credentials or tokens.slack:");
5195
- logger.log(" - SLACK_TOKEN or RELIZY_SLACK_TOKEN");
5268
+ if (webhookUrl) {
5269
+ if (slackConfig.channel) {
5270
+ logger.warn("social.slack.channel is ignored when webhookUrl is set (channel is baked into the webhook URL).");
5271
+ }
5272
+ if (token) {
5273
+ logger.warn("Slack token is ignored when webhookUrl is set (webhook takes priority).");
5274
+ }
5275
+ } else if (token) {
5276
+ try {
5277
+ await import('@slack/web-api');
5278
+ } catch {
5279
+ logger.fail("@slack/web-api is not installed, please install it");
5280
+ throw new Error("@slack/web-api is not installed");
5281
+ }
5282
+ if (!slackConfig.channel) {
5283
+ logger.fail("Slack is enabled but no channel is configured.");
5284
+ logger.log('Set the channel in social.slack.channel (e.g., "#releases" or "C1234567890") or switch to webhookUrl for a simpler setup.');
5285
+ throw new Error("Slack channel not found");
5286
+ }
5287
+ } else {
5288
+ logger.fail("Slack is enabled but no credentials are configured.");
5289
+ logger.log("Provide ONE of the following:");
5290
+ logger.log(" (a) social.slack.webhookUrl (or SLACK_WEBHOOK_URL / RELIZY_SLACK_WEBHOOK_URL env var) \u2014 simpler setup, channel baked in");
5291
+ logger.log(" (b) social.slack.credentials.token (or SLACK_TOKEN / RELIZY_SLACK_TOKEN env var) + social.slack.channel \u2014 requires bot invite");
5196
5292
  throw new Error("Slack credentials not found");
5197
5293
  }
5198
- if (!slackConfig.channel) {
5199
- logger.fail("Slack is enabled but no channel is configured.");
5200
- logger.log('Set the channel in social.slack.channel (e.g., "#releases" or "C1234567890")');
5201
- throw new Error("Slack channel not found");
5202
- }
5203
5294
  }
5204
5295
  if (isAISocialEnabled(config, "twitter") || isAISocialEnabled(config, "slack")) {
5205
5296
  await aiSafetyCheck({ config });
@@ -5280,7 +5371,8 @@ async function handleSlackPost({
5280
5371
  changelog,
5281
5372
  dryRun,
5282
5373
  newVersion,
5283
- tag
5374
+ tag,
5375
+ commits
5284
5376
  }) {
5285
5377
  const slackConfig = config.social?.slack;
5286
5378
  if (!slackConfig?.enabled) {
@@ -5289,22 +5381,23 @@ async function handleSlackPost({
5289
5381
  }
5290
5382
  logger.debug("Slack posting is enabled");
5291
5383
  try {
5384
+ const webhookUrl = getSlackWebhookUrl({ socialWebhookUrl: slackConfig.webhookUrl });
5292
5385
  const token = getSlackToken({
5293
5386
  socialCredentials: slackConfig.credentials,
5294
5387
  tokenCredential: config.tokens?.slack
5295
5388
  });
5296
- if (!token) {
5297
- logger.warn("Slack token not found. Set SLACK_TOKEN or RELIZY_SLACK_TOKEN environment variable or configure it in social.slack.credentials or tokens.slack.");
5389
+ if (!webhookUrl && !token) {
5390
+ logger.warn("Neither Slack webhookUrl nor token is configured. Set SLACK_WEBHOOK_URL / SLACK_TOKEN or configure social.slack.webhookUrl / credentials.token.");
5298
5391
  logger.info("Skipping Slack post");
5299
- return { success: false, error: "Slack token not found" };
5392
+ return { success: false, error: "Slack credentials not found" };
5300
5393
  }
5301
- logger.debug("Token found \u2713");
5302
- if (!slackConfig.channel) {
5303
- logger.warn("Slack channel not configured. Set it in social.slack.channel.");
5394
+ if (!webhookUrl && !slackConfig.channel) {
5395
+ logger.warn("Slack channel not configured (required in token mode). Set it in social.slack.channel.");
5304
5396
  logger.info("Skipping Slack post");
5305
5397
  return { success: false, error: "Slack channel not configured" };
5306
5398
  }
5307
- logger.debug(`Channel configured: ${slackConfig.channel}`);
5399
+ const slackMode = webhookUrl ? "webhook mode" : `token mode, channel: ${slackConfig.channel}`;
5400
+ logger.debug(`Slack: ${slackMode}`);
5308
5401
  logger.debug(`Preparing Slack message for release: ${tag} (${newVersion})`);
5309
5402
  const onlyStable = slackConfig.onlyStable ?? true;
5310
5403
  if (onlyStable && isPrerelease(newVersion)) {
@@ -5324,6 +5417,9 @@ async function handleSlackPost({
5324
5417
  logger.debug(`Changelog URL: ${changelogUrl || "none"}`);
5325
5418
  logger.debug(`Changelog generated (${changelog.length} chars)`);
5326
5419
  const template = slackConfig.template || config.templates.slackMessage;
5420
+ const shouldHideContributors = config.noAuthors === true || slackConfig.noAuthors === true;
5421
+ const contributors = shouldHideContributors ? [] : collectContributorNames({ commits, config });
5422
+ logger.debug(`Contributors: ${contributors.length}`);
5327
5423
  const response = await postReleaseToSlack({
5328
5424
  version: newVersion,
5329
5425
  projectName: config.projectName || rootPackageBase.name,
@@ -5331,8 +5427,11 @@ async function handleSlackPost({
5331
5427
  releaseUrl,
5332
5428
  changelogUrl,
5333
5429
  channel: slackConfig.channel,
5334
- token,
5430
+ token: token ?? void 0,
5431
+ webhookUrl: webhookUrl ?? void 0,
5335
5432
  template,
5433
+ contributors,
5434
+ postMaxLength: slackConfig.postMaxLength ?? 2500,
5336
5435
  dryRun
5337
5436
  });
5338
5437
  await executeHook("success:slack", config, dryRun);
@@ -5447,7 +5546,8 @@ ${twitterChangelog}`);
5447
5546
  changelog: slackChangelog,
5448
5547
  dryRun,
5449
5548
  newVersion,
5450
- tag: to
5549
+ tag: to,
5550
+ commits: rootPackage.commits
5451
5551
  });
5452
5552
  const results = [];
5453
5553
  if (config.social?.twitter?.enabled) {
@@ -5817,4 +5917,4 @@ Git provider: ${provider}`);
5817
5917
  }
5818
5918
  }
5819
5919
 
5820
- export { readPackageJson as $, getModifiedReleaseFilePatterns as A, createCommitAndTags as B, pushCommitAndTags as C, rollbackModifiedFiles as D, getFirstCommit as E, getCurrentGitBranch as F, getCurrentGitRef as G, getShortCommitSha as H, github as I, createGitlabRelease as J, gitlab as K, buildCompareLink as L, buildChangelogBody as M, buildContributors as N, generateMarkDown as O, parseChangelogMarkdown as P, detectPackageManager as Q, determinePublishTag as R, getPackagesToPublishInSelectiveMode as S, getPackagesToPublishInIndependentMode as T, getAuthCommand as U, publishPackage as V, findGitHubPR as W, findGitLabMR as X, detectPullRequest as Y, PR_COMMENT_MARKER as Z, postPrComment as _, buildCommentBody as a, getRootPackage as a0, readPackages as a1, getPackages as a2, getPackageCommits as a3, hasLernaJson as a4, getSlackToken as a5, formatChangelogForSlack as a6, formatSlackMessage as a7, postReleaseToSlack as a8, extractChangelogSummary as a9, extractVersionFromPackageTag as aA, isPrerelease as aB, isStableReleaseType as aC, isPrereleaseReleaseType as aD, isGraduating as aE, getPreid as aF, isChangedPreid as aG, getBumpedPackageIndependently as aH, confirmBump as aI, getBumpedIndependentPackages as aJ, shouldFilterPrereleaseTags as aK, extractVersionFromTag as aL, getCanaryVersion as aM, isTagVersionCompatibleWithCurrent as aN, getReleaseUrl as aa, getIndependentTag as ab, getLastStableTag as ac, getLastTag as ad, getLastRepoTag as ae, getLastPackageTag as af, NEW_PACKAGE_MARKER as ag, resolveTags as ah, getTwitterCredentials as ai, formatTweetMessage as aj, postReleaseToTwitter as ak, executeHook as al, isInCI as am, getCIName as an, executeFormatCmd as ao, executeBuildCmd as ap, isBumpedPackage as aq, filterOutPrivatePackages as ar, getPackagesOrBumpedPackages as as, isGraduatingToStableBetweenVersion as at, capReleaseTypeForZeroMajor as au, determineSemverChange as av, determineReleaseType as aw, writeVersion as ax, getPackageNewVersion as ay, updateLernaVersion as az, bump as b, changelog as c, providerReleaseSafetyCheck as d, providerRelease as e, publishSafetyCheck as f, publish as g, social as h, generateChangelog as i, getDefaultConfig as j, defineConfig as k, loadRelizyConfig as l, mergeTypes as m, getPackageDependencies as n, getDependentsOf as o, prComment as p, expandPackagesToBumpWithDependents as q, release as r, socialSafetyCheck as s, topologicalSort as t, getGitStatus as u, checkGitStatusIfDirty as v, writeChangelogToFile as w, fetchGitTags as x, detectGitProvider as y, parseGitRemoteUrl as z };
5920
+ export { postPrComment as $, getModifiedReleaseFilePatterns as A, createCommitAndTags as B, pushCommitAndTags as C, rollbackModifiedFiles as D, getFirstCommit as E, getCurrentGitBranch as F, getCurrentGitRef as G, getShortCommitSha as H, github as I, createGitlabRelease as J, gitlab as K, buildCompareLink as L, buildChangelogBody as M, collectContributorNames as N, buildContributors as O, generateMarkDown as P, parseChangelogMarkdown as Q, detectPackageManager as R, determinePublishTag as S, getPackagesToPublishInSelectiveMode as T, getPackagesToPublishInIndependentMode as U, getAuthCommand as V, publishPackage as W, findGitHubPR as X, findGitLabMR as Y, detectPullRequest as Z, PR_COMMENT_MARKER as _, buildCommentBody as a, readPackageJson as a0, getRootPackage as a1, readPackages as a2, getPackages as a3, getPackageCommits as a4, hasLernaJson as a5, getSlackToken as a6, getSlackWebhookUrl as a7, formatChangelogForSlack as a8, formatSlackMessage as a9, getPackageNewVersion as aA, updateLernaVersion as aB, extractVersionFromPackageTag as aC, isPrerelease as aD, isStableReleaseType as aE, isPrereleaseReleaseType as aF, isGraduating as aG, getPreid as aH, isChangedPreid as aI, getBumpedPackageIndependently as aJ, confirmBump as aK, getBumpedIndependentPackages as aL, shouldFilterPrereleaseTags as aM, extractVersionFromTag as aN, getCanaryVersion as aO, isTagVersionCompatibleWithCurrent as aP, postReleaseToSlack as aa, extractChangelogSummary as ab, getReleaseUrl as ac, getIndependentTag as ad, getLastStableTag as ae, getLastTag as af, getLastRepoTag as ag, getLastPackageTag as ah, NEW_PACKAGE_MARKER as ai, resolveTags as aj, getTwitterCredentials as ak, formatTweetMessage as al, postReleaseToTwitter as am, executeHook as an, isInCI as ao, getCIName as ap, executeFormatCmd as aq, executeBuildCmd as ar, isBumpedPackage as as, filterOutPrivatePackages as at, getPackagesOrBumpedPackages as au, isGraduatingToStableBetweenVersion as av, capReleaseTypeForZeroMajor as aw, determineSemverChange as ax, determineReleaseType as ay, writeVersion as az, bump as b, changelog as c, providerReleaseSafetyCheck as d, providerRelease as e, publishSafetyCheck as f, publish as g, social as h, generateChangelog as i, getDefaultConfig as j, defineConfig as k, loadRelizyConfig as l, mergeTypes as m, getPackageDependencies as n, getDependentsOf as o, prComment as p, expandPackagesToBumpWithDependents as q, release as r, socialSafetyCheck as s, topologicalSort as t, getGitStatus as u, checkGitStatusIfDirty as v, writeChangelogToFile as w, fetchGitTags as x, detectGitProvider as y, parseGitRemoteUrl as z };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "relizy",
3
3
  "type": "module",
4
- "version": "1.3.2",
4
+ "version": "1.4.0-beta.0",
5
5
  "description": "Changelogen adapter for monorepo management with unified and independent versioning",
6
6
  "author": "Louis Mazel <me@loicmazuel.com>",
7
7
  "license": "MIT",
@@ -71,7 +71,7 @@
71
71
  }
72
72
  },
73
73
  "dependencies": {
74
- "@inquirer/prompts": "^8.4.1",
74
+ "@inquirer/prompts": "^8.4.2",
75
75
  "@maz-ui/node": "4.6.1",
76
76
  "@maz-ui/utils": "^4.7.6",
77
77
  "c12": "^3.3.3",
@@ -88,23 +88,23 @@
88
88
  "@commitlint/config-conventional": "20.5.0",
89
89
  "@commitlint/cz-commitlint": "^20.5.1",
90
90
  "@commitlint/types": "^20.5.0",
91
- "@maz-ui/eslint-config": "^4.8.0",
92
- "@slack/web-api": "7.15.0",
91
+ "@maz-ui/eslint-config": "^4.9.1",
92
+ "@slack/web-api": "7.15.1",
93
93
  "@types/node": "^25.6.0",
94
94
  "@types/semver": "^7.7.1",
95
- "@vitest/coverage-v8": "^4.1.4",
95
+ "@vitest/coverage-v8": "^4.1.5",
96
96
  "@yoloship/claude-sdk": "0.1.0-beta.3",
97
97
  "cross-env": "10.1.0",
98
- "eslint": "^10.2.0",
98
+ "eslint": "^10.2.1",
99
99
  "husky": "9.1.7",
100
100
  "jiti": "2.6.1",
101
101
  "lint-staged": "^16.4.0",
102
- "memfs": "^4.57.1",
102
+ "memfs": "^4.57.2",
103
103
  "tsx": "^4.21.0",
104
104
  "twitter-api-v2": "^1.29.0",
105
105
  "typescript": "^5.9.3",
106
106
  "unbuild": "^3.6.1",
107
- "vitest": "^4.1.4"
107
+ "vitest": "^4.1.5"
108
108
  },
109
109
  "lint-staged": {
110
110
  "*.{js,jsx,ts,tsx,mjs,mts,cjs,md,yml,json}": [