diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7ac9d76df..61ac4d550 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,7 +18,7 @@ jobs: contents: read strategy: matrix: - node: ['20','22'] + node: ['20', '22'] name: Node ${{ matrix.node }} steps: - uses: actions/checkout@v6 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 3382006c1..28a4ab3ba 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -33,6 +33,8 @@ jobs: - name: Install Dependencies if: steps.cache.outputs.cache-hit != 'true' run: pnpm install --frozen-lockfile + - name: Format Check + run: pnpm format:check - name: Type Check run: pnpm typecheck - name: Lint diff --git a/.prettierignore b/.prettierignore index ef229ed3d..44cdc9a68 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,3 +1,6 @@ coverage/ dist/ node_modules/ +pnpm-lock.yaml +temp_*/ +docs/ diff --git a/AGENTS.md b/AGENTS.md index d0c04d662..955135396 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -113,24 +113,26 @@ Some operations need explicit `isDryRun()` checks: - User experience optimizations (e.g., skipping sleep timers) + ## Long-term Knowledge -### Gotcha +### Architecture + + - -* **Craft changelog: commits without PRs were forced into leftovers regardless of category match**: In \`src/utils/changelog.ts\`, the leftovers guard at the categorization step had \`if (!categoryTitle || !raw.pr)\` — the \`|| !raw.pr\` condition forced ALL commits without associated PRs into the "Other" leftovers section, even when they matched a category via \`commit\_patterns\` or labels. This meant direct pushes (no PR) like \`feat(auth): add SSO\` would correctly contribute to version bump calculation (which runs before the leftovers check) but would appear under "Other" in the changelog instead of their matched category. Fix: remove \`|| !raw.pr\` so only \`!categoryTitle\` controls leftover placement. The downstream rendering already handles PR-less commits gracefully — \`createPREntriesFromRaw\` uses \`raw.pr ?? ''\`, and \`formatChangelogEntry\` falls back to commit-hash links when \`prNumber\` is falsy (empty string). +- **Registry target: repo_url auto-derived from git remote, not user-configurable**: \`repo_url\` in registry manifests is always set by Craft as \`https://github.com/${owner}/${repo}\`. Resolution: (1) explicit \`github: { owner, repo }\` in \`.craft.yml\` (rare), (2) fallback: auto-detect from git \`origin\` remote URL via \`git-url-parse\` library (\`git.ts:194-217\`, \`config.ts:286-316\`). Works with HTTPS and SSH remote URLs. Always overwritten on every publish — existing manifest values are replaced (\`registry.ts:417-418\`). Result is cached globally with \`Object.freeze\`. If remote isn't \`github.com\` and no explicit config exists, throws \`ConfigurationError\`. Most repos need no configuration — the git origin remote is sufficient. - -* **Edit tool triggers Prettier reformatting on the entire file**: When using the Edit tool to make a targeted change to a file in a repo with Prettier configured, the tool may reformat the entire file (e.g., aligning markdown table columns, changing quote styles). This causes the git diff to show cosmetic changes far beyond the intended edit. To keep commits clean: either accept the Prettier reformatting as a net improvement (if the file passes \`prettier --check\`), or use \`git checkout -- \\` to restore the original and re-apply the change via bash/sed for surgical precision. In this codebase, Prettier is configured (\`.prettierrc.yml\`) but the lint CI workflow does NOT run \`prettier --check\`, only ESLint and typecheck. + + +- **Registry target: urlTemplate generates artifact download URLs in manifest**: \`urlTemplate\` in the registry target config generates download URLs for release artifacts in the registry manifest's \`files\` field. Uses Mustache rendering with variables \`{{version}}\`, \`{{file}}\`, \`{{revision}}\`. Primarily useful for apps (standalone binaries) and CDN-hosted assets — SDK packages published to public registries (npm, PyPI, gem) typically don't need it. If neither \`urlTemplate\` nor \`checksums\` is configured, Craft skips adding file data entirely (warns at \`registry.ts:341-349\`). Real-world pattern: \`https://downloads.sentry-cdn.com/\/{{version}}/{{file}}\`. + +### Gotcha - -* **pnpm-lock.yaml merge conflicts: regenerate don't manually merge**: pnpm gotchas: (1) Lock file conflicts: never manually resolve — \`git checkout --theirs pnpm-lock.yaml\` then \`pnpm install\` to regenerate. \`git stash pop\` after merge can re-conflict; drop the stash and re-run instead. (2) Overrides: \`>=\` crosses major versions — use \`^\` to stay in-major. Version-range selectors don't reliably force re-resolution of compatible transitive deps; use blanket overrides when all consumers are on same major. (3) Overrides go stale on tree changes — audit with \`pnpm why\` and remove orphans. + -### Pattern +- **ESM modules prevent vi.spyOn of child_process.spawnSync — use test subclass pattern**: In ESM (Vitest or Bun), you cannot \`vi.spyOn\` exports from Node built-in modules — throws 'Module namespace is not configurable'. Workaround: create a test subclass that overrides the method calling the built-in and injects controllable values. \`vi.mock\` at module level works but affects all tests in the file. - -* **AGENTS.md lore section: only include project-relevant entries**: The \`\\` in AGENTS.md is auto-maintained by the lore tool and can accumulate cross-project entries (React, Kubernetes, etc.) that are irrelevant to the Craft codebase. When committing AGENTS.md changes, review the lore section and strip entries that don't pertain to this repo. Only project-specific patterns (like the \`publish\_repo: self\` sentinel) should be included. + - -* **Craft publish\_repo 'self' sentinel resolves to GITHUB\_REPOSITORY at runtime**: The composite action's \`publish\_repo\` input supports a special sentinel value \`"self"\` which resolves to \`$GITHUB\_REPOSITORY\` at runtime in the bash script of the 'Request publish' step. This allows repos to create publish request issues in themselves rather than in a separate \`{owner}/publish\` repo. The resolution happens in bash (not in the GitHub Actions expression) because the expression layer sets \`PUBLISH\_REPO\` via \`inputs.publish\_repo || format('{0}/publish', github.repository\_owner)\` — the string \`"self"\` passes through as-is and gets resolved to the actual repo name in the shell. Useful for personal/small repos where the default GITHUB\_TOKEN already has write access to the repo itself. +- **pnpm overrides with >= can cross major versions — use ^ to constrain**: pnpm overrides gotchas: (1) \`>=\` crosses major versions — use \`^\` to constrain within same major. (2) Version-range selectors don't reliably force re-resolution of compatible transitive deps; use blanket overrides when safe. (3) Overrides become stale — audit with \`pnpm why \\` after dependency changes. (4) Never manually resolve pnpm-lock.yaml conflicts — \`git checkout --theirs\` then \`pnpm install\` to regenerate deterministically. diff --git a/CHANGELOG.md b/CHANGELOG.md index fb904165a..f7c8f3be6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -356,7 +356,7 @@ ### Bug Fixes 🐛 -- fix(changelog): Unscoped entries should be grouped under "other" by @BYK in [#659](https://github.com/getsentry/craft/pull/659) +- fix(changelog): Unscoped entries should be grouped under "other" by @BYK in [#659](https://github.com/getsentry/craft/pull/659) ### Build / dependencies / internal 🔧 @@ -364,7 +364,7 @@ ### Other -- fix(docker): Support regional Artifact Registry endpoints in isGoogleCloudRegistry by @BYK in [#661](https://github.com/getsentry/craft/pull/661) +- fix(docker): Support regional Artifact Registry endpoints in isGoogleCloudRegistry by @BYK in [#661](https://github.com/getsentry/craft/pull/661) ## 2.13.1 @@ -779,7 +779,7 @@ ### Various fixes & improvements -- ref: Pin cocoapods version (#496) by @brustolin +- ref: Pin cocoapods version (#496) by @brustolin ## 1.6.0 @@ -795,14 +795,14 @@ - build(deps): bump semver from 6.3.0 to 6.3.1 (#470) by @dependabot - build(deps): bump @babel/traverse from 7.22.5 to 7.23.2 (#494) by @dependabot - ref: remove volta from CI (#493) by @asottile-sentry -- fix: Handle `{major}.json` and `{minor}.json` symlinks when publishing older versions (#483) by @cleptric +- fix: Handle `{major}.json` and `{minor}.json` symlinks when publishing older versions (#483) by @cleptric - Bump symbol collector 1.12.0 (#491) by @bruno-garcia ## 1.4.4 ### Various fixes & improvements -- fix(brew): Replace version in artifact names with '__VERSION__' to access checksums from mustache (#488) by @romtsn +- fix(brew): Replace version in artifact names with '**VERSION**' to access checksums from mustache (#488) by @romtsn ## 1.4.3 diff --git a/blog-post-draft.md b/blog-post-draft.md index 0452cb2b1..e840e661d 100644 --- a/blog-post-draft.md +++ b/blog-post-draft.md @@ -23,7 +23,7 @@ And for teams that prefer calendar-based versioning, Craft now supports `calver` versioning: policy: calver calver: - format: "%y.%-m" # e.g., 24.12 for December 2024 + format: '%y.%-m' # e.g., 24.12 for December 2024 ``` The best part? When using auto-versioning, you get a preview in your PR with a "Semver Impact of This PR" section so you know exactly what _your specific change_ will do to the version. @@ -202,5 +202,7 @@ Drop me a message, file an issue, or just leave a comment. I'm actively working LFG 🚀 [^1]: It's not actually living there. That would be creepy. It just reads the messages. Still creepy? OK moving on. + [^2]: It doesn't. But it handles reverts correctly, which is almost the same thing in software. + [^3]: The Vitest migration alone touches ~30 test files. Don't ask me how I know this. diff --git a/build.mjs b/build.mjs index a0801a426..77773d007 100644 --- a/build.mjs +++ b/build.mjs @@ -15,10 +15,12 @@ if (process.env.SENTRY_AUTH_TOKEN) { assets: ['dist/craft.js', 'dist/craft.js.map'], filesToDeleteAfterUpload: ['dist/**/*.map'], }, - }) + }), ); } else { - console.log('[build] SENTRY_AUTH_TOKEN not found, skipping source map upload'); + console.log( + '[build] SENTRY_AUTH_TOKEN not found, skipping source map upload', + ); } // Build to .js file first so Sentry plugin can properly handle source maps @@ -33,7 +35,9 @@ await esbuild.build({ 'import.meta.url': 'import_meta_url', 'process.env.NODE_ENV': JSON.stringify('production'), ...(process.env.CRAFT_BUILD_SHA && { - 'process.env.CRAFT_BUILD_SHA': JSON.stringify(process.env.CRAFT_BUILD_SHA), + 'process.env.CRAFT_BUILD_SHA': JSON.stringify( + process.env.CRAFT_BUILD_SHA, + ), }), }, outfile: 'dist/craft.js', diff --git a/package.json b/package.json index 3e7be9b56..c29af7ae7 100644 --- a/package.json +++ b/package.json @@ -82,6 +82,8 @@ "clean": "rm -rf dist coverage", "lint": "eslint --cache --cache-strategy content", "fix": "pnpm lint --fix", + "format": "prettier --write .", + "format:check": "prettier --check .", "test": "vitest run", "test:watch": "vitest", "typecheck": "tsc --noEmit", diff --git a/src/__mocks__/@aws-sdk/client-lambda.ts b/src/__mocks__/@aws-sdk/client-lambda.ts index bcfc23479..b82d28d86 100644 --- a/src/__mocks__/@aws-sdk/client-lambda.ts +++ b/src/__mocks__/@aws-sdk/client-lambda.ts @@ -1,5 +1,3 @@ - - const PUBLISHED_LAYER_TEST = { Version: 1, LayerVersionArn: 'test:layer:version:arn', diff --git a/src/artifact_providers/base.ts b/src/artifact_providers/base.ts index 65e350fe4..0e82f6abc 100644 --- a/src/artifact_providers/base.ts +++ b/src/artifact_providers/base.ts @@ -86,7 +86,7 @@ export interface ParsedFilterOptions { * Returns parsed the given raw filters. */ export function parseFilterOptions( - rawFilters: RawFilterOptions + rawFilters: RawFilterOptions, ): ParsedFilterOptions { const parsedFilters: ParsedFilterOptions = {}; if (rawFilters.includeNames) { @@ -193,7 +193,7 @@ export abstract class BaseArtifactProvider { */ public async downloadArtifact( artifact: RemoteArtifact, - downloadDirectory?: string + downloadDirectory?: string, ): Promise { let finalDownloadDirectory; if (downloadDirectory) { @@ -210,14 +210,14 @@ export abstract class BaseArtifactProvider { return cached; } this.logger.debug( - `Downloading \`${artifact.filename}\` to \`${finalDownloadDirectory}\`` + `Downloading \`${artifact.filename}\` to \`${finalDownloadDirectory}\``, ); const promise = this.doDownloadArtifact( artifact, - finalDownloadDirectory + finalDownloadDirectory, ).catch(err => { this.logger.error( - `Unable to download ${artifact.filename} from artifact provider!` + `Unable to download ${artifact.filename} from artifact provider!`, ); throw err; }); @@ -237,7 +237,7 @@ export abstract class BaseArtifactProvider { */ protected abstract doDownloadArtifact( artifact: RemoteArtifact, - downloadDirectory: string + downloadDirectory: string, ): Promise; /** @@ -254,12 +254,12 @@ export abstract class BaseArtifactProvider { */ public async downloadArtifacts( artifacts: RemoteArtifact[], - downloadDirectory?: string + downloadDirectory?: string, ): Promise { return Promise.all( artifacts.map(async artifact => - this.downloadArtifact(artifact, downloadDirectory) - ) + this.downloadArtifact(artifact, downloadDirectory), + ), ); } @@ -271,7 +271,7 @@ export abstract class BaseArtifactProvider { * @returns List of artifacts associated with that commit */ public async listArtifactsForRevision( - revision: string + revision: string, ): Promise { this.logger.debug(`Fetching artifact list for revision \`${revision}\`.`); // check the cache first @@ -288,7 +288,7 @@ export abstract class BaseArtifactProvider { artifacts = await this.fileListCache[revision]; } catch (err) { this.logger.error( - `Unable to retrieve artifact list for revision ${revision}!` + `Unable to retrieve artifact list for revision ${revision}!`, ); throw err; } @@ -314,7 +314,7 @@ export abstract class BaseArtifactProvider { * be found */ protected abstract doListArtifactsForRevision( - revision: string + revision: string, ): Promise; /** @@ -330,7 +330,7 @@ export abstract class BaseArtifactProvider { public async getChecksum( artifact: RemoteArtifact, algorithm: HashAlgorithm, - format: HashOutputFormat + format: HashOutputFormat, ): Promise { const filePath = await this.downloadArtifact(artifact); const checksumKey = `${algorithm}__${format}`; @@ -356,7 +356,7 @@ export abstract class BaseArtifactProvider { */ public async filterArtifactsForRevision( revision: string, - filterOptions?: ParsedFilterOptions + filterOptions?: ParsedFilterOptions, ): Promise { let filteredArtifacts = await this.listArtifactsForRevision(revision); if (!filterOptions || filteredArtifacts.length === 0) { @@ -365,12 +365,12 @@ export abstract class BaseArtifactProvider { const { includeNames, excludeNames } = filterOptions; if (includeNames) { filteredArtifacts = filteredArtifacts.filter(artifact => - includeNames.test(artifact.filename) + includeNames.test(artifact.filename), ); } if (excludeNames) { filteredArtifacts = filteredArtifacts.filter( - artifact => !excludeNames.test(artifact.filename) + artifact => !excludeNames.test(artifact.filename), ); } return filteredArtifacts; diff --git a/src/artifact_providers/gcs.ts b/src/artifact_providers/gcs.ts index 01caa49b6..2c2c142b1 100644 --- a/src/artifact_providers/gcs.ts +++ b/src/artifact_providers/gcs.ts @@ -29,13 +29,13 @@ export class GCSArtifactProvider extends BaseArtifactProvider { }, { name: 'CRAFT_GCS_STORE_CREDS_PATH', - } + }, ); // TODO (kmclb) get rid of this check once config validation is working if (!config.bucket) { throw new ConfigurationError( - 'No GCS bucket provided in artifact provider config!' + 'No GCS bucket provided in artifact provider config!', ); } @@ -51,11 +51,11 @@ export class GCSArtifactProvider extends BaseArtifactProvider { */ protected async doDownloadArtifact( artifact: RemoteArtifact, - downloadDirectory: string + downloadDirectory: string, ): Promise { const result = await this.gcsClient.downloadArtifact( artifact.storedFile.downloadFilepath, - downloadDirectory + downloadDirectory, ); // In dry-run mode, downloadArtifact returns null. Return a placeholder path // that indicates the file would have been downloaded here. @@ -69,13 +69,13 @@ export class GCSArtifactProvider extends BaseArtifactProvider { * @inheritDoc */ protected async doListArtifactsForRevision( - revision: string + revision: string, ): Promise { const { repoName, repoOwner } = this.config; return this.gcsClient.listArtifactsForRevision( repoOwner, repoName, - revision + revision, ); } } diff --git a/src/artifact_providers/none.ts b/src/artifact_providers/none.ts index caf7e4246..146ecf964 100644 --- a/src/artifact_providers/none.ts +++ b/src/artifact_providers/none.ts @@ -13,7 +13,7 @@ export class NoneArtifactProvider extends BaseArtifactProvider { repoName: 'none', repoOwner: 'none', name: 'none', - } + }, ) { super(config); } @@ -24,10 +24,10 @@ export class NoneArtifactProvider extends BaseArtifactProvider { */ protected async doDownloadArtifact( _artifact: RemoteArtifact, - _downloadDirectory: string + _downloadDirectory: string, ): Promise { return Promise.reject( - new Error('NoneProvider does not suuport file downloads!') + new Error('NoneProvider does not suuport file downloads!'), ); } @@ -37,7 +37,7 @@ export class NoneArtifactProvider extends BaseArtifactProvider { * @returns An empty array */ protected async doListArtifactsForRevision( - _revision: string + _revision: string, ): Promise { return []; } diff --git a/src/commands/__tests__/targets.test.ts b/src/commands/__tests__/targets.test.ts index 59262f223..ba8bf6af0 100644 --- a/src/commands/__tests__/targets.test.ts +++ b/src/commands/__tests__/targets.test.ts @@ -1,4 +1,13 @@ -import { vi, describe, test, expect, beforeEach, afterEach, type Mock, type MockInstance } from 'vitest'; +import { + vi, + describe, + test, + expect, + beforeEach, + afterEach, + type Mock, + type MockInstance, +} from 'vitest'; import { handler } from '../targets'; vi.mock('../../config', () => ({ @@ -29,10 +38,7 @@ describe('targets command', () => { }); test('lists targets without expansion when no workspaces', async () => { - const targets = [ - { name: 'npm' }, - { name: 'github' }, - ]; + const targets = [{ name: 'npm' }, { name: 'github' }]; mockedGetConfiguration.mockReturnValue({ targets }); mockedExpandWorkspaceTargets.mockResolvedValue(targets); diff --git a/src/commands/artifacts_cmds/download.ts b/src/commands/artifacts_cmds/download.ts index 614def6b8..d26971247 100644 --- a/src/commands/artifacts_cmds/download.ts +++ b/src/commands/artifacts_cmds/download.ts @@ -45,7 +45,7 @@ interface ArtifactsDownloadOptions extends ArtifactsOptions { * @param argv Full path to the target directory */ async function prepareOutputDirectory( - argv: ArtifactsDownloadOptions + argv: ArtifactsDownloadOptions, ): Promise { if (argv.directory) { const fullPath = resolve(argv.directory); @@ -78,7 +78,7 @@ async function handlerMain(argv: ArtifactsDownloadOptions): Promise { const artifactProvider = await getArtifactProviderFromConfig(); if (artifactProvider instanceof NoneArtifactProvider) { logger.warn( - `Artifact provider is disabled in the configuration, nothing to do.` + `Artifact provider is disabled in the configuration, nothing to do.`, ); return undefined; } @@ -94,10 +94,13 @@ async function handlerMain(argv: ArtifactsDownloadOptions): Promise { const filesToDownload = argv.all ? artifacts.map(ar => ar.filename) : argv.names; - const nameToArtifact = artifacts.reduce((dict, artifact) => { - dict[artifact.filename] = artifact; - return dict; - }, {} as { [index: string]: RemoteArtifact }); + const nameToArtifact = artifacts.reduce( + (dict, artifact) => { + dict[artifact.filename] = artifact; + return dict; + }, + {} as { [index: string]: RemoteArtifact }, + ); logger.info(`Fetching artifacts for revision: ${revision}`); for (const name of filesToDownload) { @@ -110,7 +113,7 @@ async function handlerMain(argv: ArtifactsDownloadOptions): Promise { const artifactPath = await artifactProvider.downloadArtifact( filteredArtifact, - outputDirectory + outputDirectory, ); logger.info(`Saved artifact to: ${artifactPath}`); } diff --git a/src/commands/artifacts_cmds/list.ts b/src/commands/artifacts_cmds/list.ts index e918360c4..4c9699e16 100644 --- a/src/commands/artifacts_cmds/list.ts +++ b/src/commands/artifacts_cmds/list.ts @@ -18,7 +18,7 @@ async function handlerMain(argv: ArtifactsOptions): Promise { const artifactProvider = await getArtifactProviderFromConfig(); if (artifactProvider instanceof NoneArtifactProvider) { logger.warn( - `Artifact provider is disabled in the configuration, nothing to do.` + `Artifact provider is disabled in the configuration, nothing to do.`, ); return undefined; } @@ -41,10 +41,10 @@ async function handlerMain(argv: ArtifactsOptions): Promise { head: ['File Name', 'Size', 'Updated'], style: { head: ['cyan'] }, }, - artifactData + artifactData, ); logger.info( - `Available artifacts for revision ${revision}: \n${table.toString()}\n` + `Available artifacts for revision ${revision}: \n${table.toString()}\n`, ); return argv.rev; diff --git a/src/instrument.ts b/src/instrument.ts index 74fc9e848..1f8b2a527 100644 --- a/src/instrument.ts +++ b/src/instrument.ts @@ -4,7 +4,8 @@ import isCI from 'is-ci'; import { getPackageVersion } from './utils/version'; // Detect CI environment at runtime -const isCIEnv = isCI || process.env.CI === 'true' || process.env.GITHUB_ACTIONS === 'true'; +const isCIEnv = + isCI || process.env.CI === 'true' || process.env.GITHUB_ACTIONS === 'true'; init({ dsn: 'https://965f09d9d64681174a6617b1e11d7572@o1.ingest.us.sentry.io/4510674351620096', diff --git a/src/logger.ts b/src/logger.ts index b1026aed1..f7c6a663e 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -26,7 +26,7 @@ consola.setReporters([new StderrReporter()]); */ export function formatTable( options: Record, - values: any[] + values: any[], ): string { const table = new Table(options); table.push(...values); diff --git a/src/targets/__tests__/brew.test.ts b/src/targets/__tests__/brew.test.ts index 18fa51c0c..a73ddf23c 100644 --- a/src/targets/__tests__/brew.test.ts +++ b/src/targets/__tests__/brew.test.ts @@ -21,7 +21,7 @@ const DEFAULT_GITHUB_REPO = { /** Returns a new BrewTarget test instance. */ function getBrewTarget( config: Record = {}, - githubRepo = DEFAULT_GITHUB_REPO + githubRepo = DEFAULT_GITHUB_REPO, ): BrewTarget { return new BrewTarget( { @@ -31,7 +31,7 @@ function getBrewTarget( ...config, }, new NoneArtifactProvider(), - githubRepo + githubRepo, ); } @@ -41,7 +41,7 @@ describe('BrewTarget configuration', () => { new BrewTarget( { name: 'brew' }, new NoneArtifactProvider(), - DEFAULT_GITHUB_REPO + DEFAULT_GITHUB_REPO, ); }).toThrow(ConfigurationError); }); @@ -111,7 +111,7 @@ describe('formula name templating', () => { test('repo name with version template', () => { const brewTarget = getBrewTarget( { formula: undefined }, - { owner: 'getsentry', repo: 'craft-v{{{major}}}' } + { owner: 'getsentry', repo: 'craft-v{{{major}}}' }, ); const resolved = brewTarget.resolveFormulaName('2.5.0'); expect(resolved).toBe('craft-v2'); diff --git a/src/targets/__tests__/registry.test.ts b/src/targets/__tests__/registry.test.ts index c87365e14..d6e1741a4 100644 --- a/src/targets/__tests__/registry.test.ts +++ b/src/targets/__tests__/registry.test.ts @@ -11,16 +11,15 @@ describe('getUpdatedManifest', () => { beforeEach(() => { vi.resetAllMocks(); mockClient = vi.fn(); - (getGitHubClient as MockedFunction< - typeof getGitHubClient - // @ts-ignore we only need to mock a subset - >).mockReturnValue({ graphql: mockClient }); + (getGitHubClient as MockedFunction) + // @ts-ignore -- we only need to mock a subset + .mockReturnValue({ graphql: mockClient }); }); const target = new RegistryTarget( { name: 'pypi' }, new NoneArtifactProvider(), - { owner: 'testSourceOwner', repo: 'testSourceRepo' } + { owner: 'testSourceOwner', repo: 'testSourceRepo' }, ); it('check if created_at exists', async () => { @@ -40,7 +39,7 @@ describe('getUpdatedManifest', () => { packageManifest, canonical, version, - revision + revision, ); // check if property created_at exists @@ -62,11 +61,11 @@ describe('getUpdatedManifest', () => { packageManifest, 'example-package', '1.0.0', - 'abc123' + 'abc123', ); expect(updatedManifest.repo_url).toBe( - 'https://github.com/testSourceOwner/testSourceRepo' + 'https://github.com/testSourceOwner/testSourceRepo', ); }); @@ -88,11 +87,13 @@ describe('getUpdatedManifest', () => { packageManifest, 'example-package', '1.0.0', - 'abc123' + 'abc123', ); expect(updatedManifest.name).toBe('Example Package'); - expect(updatedManifest.package_url).toBe('https://npmjs.com/package/example'); + expect(updatedManifest.package_url).toBe( + 'https://npmjs.com/package/example', + ); expect(updatedManifest.main_docs_url).toBe('https://docs.example.com'); expect(updatedManifest.api_docs_url).toBe('https://api.example.com/docs'); }); @@ -116,14 +117,16 @@ describe('getUpdatedManifest', () => { packageManifest, 'example-package', '1.0.0', - 'abc123' + 'abc123', ); // Config values should override expect(updatedManifest.name).toBe('New Name'); expect(updatedManifest.main_docs_url).toBe('https://new-docs.example.com'); // Existing value not in config should be preserved - expect(updatedManifest.package_url).toBe('https://npmjs.com/package/example'); + expect(updatedManifest.package_url).toBe( + 'https://npmjs.com/package/example', + ); }); it('does not set optional fields when not specified in config', async () => { @@ -140,7 +143,7 @@ describe('getUpdatedManifest', () => { packageManifest, 'example-package', '1.0.0', - 'abc123' + 'abc123', ); expect(updatedManifest.name).toBeUndefined(); diff --git a/src/targets/base.ts b/src/targets/base.ts index 7fb08e457..f754c3880 100644 --- a/src/targets/base.ts +++ b/src/targets/base.ts @@ -35,7 +35,7 @@ export class BaseTarget { public constructor( config: TargetConfig, artifactProvider: BaseArtifactProvider, - githubRepo?: GitHubGlobalConfig + githubRepo?: GitHubGlobalConfig, ) { this.logger = loggerRaw.withScope(`[target/${config.name}]`); this.artifactProvider = artifactProvider; @@ -45,12 +45,12 @@ export class BaseTarget { this.filterOptions = {}; if (this.config.includeNames) { this.filterOptions.includeNames = stringToRegexp( - this.config.includeNames + this.config.includeNames, ); } if (this.config.excludeNames) { this.filterOptions.excludeNames = stringToRegexp( - this.config.excludeNames + this.config.excludeNames, ); } } @@ -64,7 +64,7 @@ export class BaseTarget { public async publish( _version: string, - _revision: string + _revision: string, ): Promise { throw new Error('Not implemented'); return; @@ -80,7 +80,7 @@ export class BaseTarget { */ public async getArtifactsForRevision( revision: string, - defaultFilterOptions: RawFilterOptions = {} + defaultFilterOptions: RawFilterOptions = {}, ): Promise { const filterOptions = { ...parseFilterOptions(defaultFilterOptions), @@ -88,12 +88,12 @@ export class BaseTarget { }; this.logger.debug( `Getting artifact list for revision "${revision}", filtering options: {includeNames: ${String( - filterOptions.includeNames - )}, excludeNames:${String(filterOptions.excludeNames)}}` + filterOptions.includeNames, + )}, excludeNames:${String(filterOptions.excludeNames)}}`, ); return this.artifactProvider.filterArtifactsForRevision( revision, - filterOptions + filterOptions, ); } } diff --git a/src/targets/docker.ts b/src/targets/docker.ts index 5309b0b12..2d1576179 100644 --- a/src/targets/docker.ts +++ b/src/targets/docker.ts @@ -17,7 +17,11 @@ const DEFAULT_DOCKER_BIN = 'docker'; const DOCKER_BIN = process.env.DOCKER_BIN || DEFAULT_DOCKER_BIN; /** Docker Hub registry hostnames that should be treated as the default registry */ -const DOCKER_HUB_REGISTRIES = ['docker.io', 'index.docker.io', 'registry-1.docker.io']; +const DOCKER_HUB_REGISTRIES = [ + 'docker.io', + 'index.docker.io', + 'registry-1.docker.io', +]; /** * Google Cloud registry patterns. @@ -68,7 +72,7 @@ export function hasGcloudCredentials(): boolean { homedir(), '.config', 'gcloud', - 'application_default_credentials.json' + 'application_default_credentials.json', ); if (existsSync(defaultAdcPath)) { return true; @@ -188,14 +192,14 @@ const LEGACY_KEYS: Record<'source' | 'target', LegacyConfigKeys> = { */ export function normalizeImageRef( config: Record, - type: 'source' | 'target' + type: 'source' | 'target', ): ImageRefConfig { const ref = config[type] as ImageRef; // Validate that the required field is present if (ref === undefined || ref === null) { throw new ConfigurationError( - `Docker target requires a '${type}' property. Please specify the ${type} image.` + `Docker target requires a '${type}' property. Please specify the ${type} image.`, ); } @@ -260,7 +264,7 @@ export class DockerTarget extends BaseTarget { public constructor( config: TargetConfig, - artifactProvider: BaseArtifactProvider + artifactProvider: BaseArtifactProvider, ) { super(config, artifactProvider); this.dockerConfig = this.getDockerConfig(); @@ -292,7 +296,7 @@ export class DockerTarget extends BaseTarget { usernameVar?: string, passwordVar?: string, required = true, - useDefaultFallback = true + useDefaultFallback = true, ): RegistryCredentials | undefined { let username: string | undefined; let password: string | undefined; @@ -301,7 +305,7 @@ export class DockerTarget extends BaseTarget { if (usernameVar || passwordVar) { if (!usernameVar || !passwordVar) { throw new ConfigurationError( - 'Both usernameVar and passwordVar must be specified together' + 'Both usernameVar and passwordVar must be specified together', ); } username = process.env[usernameVar]; @@ -310,7 +314,7 @@ export class DockerTarget extends BaseTarget { if (!username || !password) { if (required) { throw new ConfigurationError( - `Missing credentials: ${usernameVar} and/or ${passwordVar} environment variable(s) not set` + `Missing credentials: ${usernameVar} and/or ${passwordVar} environment variable(s) not set`, ); } return undefined; @@ -334,7 +338,10 @@ export class DockerTarget extends BaseTarget { // GITHUB_API_TOKEN is used by getsentry/publish workflow with release bot token // x-access-token works with GitHub App installation tokens and PATs username = username || process.env.GITHUB_ACTOR || 'x-access-token'; - password = password || process.env.GITHUB_TOKEN || process.env.GITHUB_API_TOKEN; + password = + password || + process.env.GITHUB_TOKEN || + process.env.GITHUB_API_TOKEN; } } @@ -351,12 +358,12 @@ export class DockerTarget extends BaseTarget { const registryHint = registry ? `DOCKER_${registryToEnvPrefix(registry)}_USERNAME/PASSWORD or ` : ''; - throw new ConfigurationError( - `Cannot perform Docker release: missing credentials. + throw new ConfigurationError( + `Cannot perform Docker release: missing credentials. Please use ${registryHint}DOCKER_USERNAME and DOCKER_PASSWORD environment variables.`.replace( - /^\s+/gm, - '' - ) + /^\s+/gm, + '', + ), ); } return undefined; @@ -400,7 +407,7 @@ Please use ${registryHint}DOCKER_USERNAME and DOCKER_PASSWORD environment variab target.usernameVar, target.passwordVar, // Required unless it's a GCR registry (which can use gcloud auth) - !isGcrTarget + !isGcrTarget, ); } @@ -416,7 +423,7 @@ Please use ${registryHint}DOCKER_USERNAME and DOCKER_PASSWORD environment variab // Only required if explicit source env vars are specified !!(source.usernameVar || source.passwordVar), // Don't fall back to DOCKER_USERNAME/PASSWORD for source - false + false, ); } @@ -441,7 +448,9 @@ Please use ${registryHint}DOCKER_USERNAME and DOCKER_PASSWORD environment variab * * @param credentials The registry credentials to use */ - private async loginToRegistry(credentials: RegistryCredentials): Promise { + private async loginToRegistry( + credentials: RegistryCredentials, + ): Promise { const { username, password, registry } = credentials; const args = ['login', `--username=${username}`, '--password-stdin']; if (registry) { @@ -467,23 +476,34 @@ Please use ${registryHint}DOCKER_USERNAME and DOCKER_PASSWORD environment variab // Check if gcloud credentials are available if (!hasGcloudCredentials()) { - this.logger.debug('No gcloud credentials detected, skipping gcloud auth configure-docker'); + this.logger.debug( + 'No gcloud credentials detected, skipping gcloud auth configure-docker', + ); return false; } // Check if gcloud is available if (!(await isGcloudAvailable())) { - this.logger.debug('gcloud CLI not available, skipping gcloud auth configure-docker'); + this.logger.debug( + 'gcloud CLI not available, skipping gcloud auth configure-docker', + ); return false; } const registryList = registries.join(','); - this.logger.debug(`Configuring Docker for Google Cloud registries: ${registryList}`); + this.logger.debug( + `Configuring Docker for Google Cloud registries: ${registryList}`, + ); try { // Run gcloud auth configure-docker with the registries // This configures Docker's credential helper to use gcloud for these registries - await spawnProcess('gcloud', ['auth', 'configure-docker', registryList, '--quiet'], {}, {}); + await spawnProcess( + 'gcloud', + ['auth', 'configure-docker', registryList, '--quiet'], + {}, + {}, + ); this.logger.info(`Configured Docker authentication for: ${registryList}`); return true; } catch (error) { @@ -555,18 +575,23 @@ Please use ${registryHint}DOCKER_USERNAME and DOCKER_PASSWORD environment variab ) { // Source registry needs auth but we couldn't configure it // This is okay - source might be public or already authenticated - this.logger.debug(`No credentials for source registry ${sourceRegistry}, assuming public`); + this.logger.debug( + `No credentials for source registry ${sourceRegistry}, assuming public`, + ); } // Login to target registry (if needed and not already configured via gcloud) if (target.credentials) { await this.loginToRegistry(target.credentials); - } else if (!target.skipLogin && !gcrConfiguredRegistries.has(targetRegistry || '')) { + } else if ( + !target.skipLogin && + !gcrConfiguredRegistries.has(targetRegistry || '') + ) { // Target registry needs auth but we have no credentials and couldn't configure gcloud // This will likely fail when pushing, but we let it proceed if (targetRegistry) { this.logger.warn( - `No credentials for target registry ${targetRegistry}. Push may fail.` + `No credentials for target registry ${targetRegistry}. Push may fail.`, ); } } @@ -601,7 +626,7 @@ Please use ${registryHint}DOCKER_USERNAME and DOCKER_PASSWORD environment variab DOCKER_BIN, ['buildx', 'imagetools', 'create', '--tag', targetImage, sourceImage], {}, - { showStdout: true } + { showStdout: true }, ); } diff --git a/src/targets/hex.ts b/src/targets/hex.ts index 36098b186..21cfbaea5 100644 --- a/src/targets/hex.ts +++ b/src/targets/hex.ts @@ -44,7 +44,7 @@ export class HexTarget extends BaseTarget { */ public static async bumpVersion( rootDir: string, - newVersion: string + newVersion: string, ): Promise { const mixExsPath = join(rootDir, 'mix.exs'); if (!existsSync(mixExsPath)) { @@ -86,7 +86,7 @@ export class HexTarget extends BaseTarget { public constructor( config: TargetConfig, artifactProvider: BaseArtifactProvider, - githubRepo: GitHubGlobalConfig + githubRepo: GitHubGlobalConfig, ) { super(config, artifactProvider, githubRepo); checkExecutableIsPresent(MIX_BIN); @@ -101,7 +101,7 @@ export class HexTarget extends BaseTarget { if (!process.env.HEX_API_KEY) { reportError( `Cannot publish to hex.pm: missing credentials. - Please use HEX_API_KEY environment variable to pass the API token.` + Please use HEX_API_KEY environment variable to pass the API token.`, ); } } @@ -116,7 +116,7 @@ export class HexTarget extends BaseTarget { async cloneRepository( config: GitHubGlobalConfig, revision: string, - directory: string + directory: string, ): Promise { const { owner, repo } = config; const git = createGitClient(directory); @@ -144,29 +144,29 @@ export class HexTarget extends BaseTarget { MIX_BIN, ['local.hex', '--force'], spawnOptions, - spawnProcessOptions + spawnProcessOptions, ); await spawnProcess( MIX_BIN, ['local.rebar', '--force'], spawnOptions, - spawnProcessOptions + spawnProcessOptions, ); await spawnProcess( MIX_BIN, ['deps.get'], spawnOptions, - spawnProcessOptions + spawnProcessOptions, ); await spawnProcess( MIX_BIN, ['hex.publish', '--yes'], spawnOptions, - spawnProcessOptions + spawnProcessOptions, ); }, true, - 'craft-hex-' + 'craft-hex-', ); this.logger.info('Hex release complete'); diff --git a/src/targets/index.ts b/src/targets/index.ts index 6ca249d17..802c22525 100644 --- a/src/targets/index.ts +++ b/src/targets/index.ts @@ -69,7 +69,7 @@ export function getAllTargetNames(): string[] { * @returns Corresponding target class or undefined */ export function getTargetByName( - targetName: string + targetName: string, ): typeof BaseTarget | undefined { return TARGET_MAP[targetName]; } diff --git a/src/utils/__fixtures__/workspaces/npm-workspace/package.json b/src/utils/__fixtures__/workspaces/npm-workspace/package.json index 4114dcf9d..85293e419 100644 --- a/src/utils/__fixtures__/workspaces/npm-workspace/package.json +++ b/src/utils/__fixtures__/workspaces/npm-workspace/package.json @@ -1,5 +1,7 @@ { "name": "npm-workspace-root", "private": true, - "workspaces": ["packages/*"] + "workspaces": [ + "packages/*" + ] } diff --git a/src/utils/__tests__/dryRun.test.ts b/src/utils/__tests__/dryRun.test.ts index 24f4f57b8..19e367aa7 100644 --- a/src/utils/__tests__/dryRun.test.ts +++ b/src/utils/__tests__/dryRun.test.ts @@ -50,7 +50,7 @@ describe('dryRun utilities', () => { it('logs with consistent format', () => { logDryRun('test operation'); expect(logger.info).toHaveBeenCalledWith( - '[dry-run] Would execute: test operation' + '[dry-run] Would execute: test operation', ); }); }); @@ -98,7 +98,7 @@ describe('dryRun utilities', () => { await git.push(); expect(mockGit.push).not.toHaveBeenCalled(); expect(logger.info).toHaveBeenCalledWith( - expect.stringContaining('[dry-run]') + expect.stringContaining('[dry-run]'), ); }); @@ -110,7 +110,7 @@ describe('dryRun utilities', () => { await git.raw('push', 'origin', 'main'); expect(mockGit.raw).not.toHaveBeenCalled(); expect(logger.info).toHaveBeenCalledWith( - expect.stringContaining('git push origin main') + expect.stringContaining('git push origin main'), ); }); @@ -162,7 +162,7 @@ describe('dryRun utilities', () => { // Verify dry-run messages were logged expect(logger.info).toHaveBeenCalledWith( - expect.stringContaining('[dry-run]') + expect.stringContaining('[dry-run]'), ); }); @@ -194,7 +194,11 @@ describe('dryRun utilities', () => { vi.mocked(helpers.isDryRun).mockReturnValue(false); const octokit = createDryRunOctokit(mockOctokit as any); - await octokit.repos.getContent({ owner: 'test', repo: 'test', path: '/' }); + await octokit.repos.getContent({ + owner: 'test', + repo: 'test', + path: '/', + }); expect(mockOctokit.repos.getContent).toHaveBeenCalled(); }); @@ -222,7 +226,7 @@ describe('dryRun utilities', () => { }); expect(mockOctokit.repos.createRelease).not.toHaveBeenCalled(); expect(logger.info).toHaveBeenCalledWith( - expect.stringContaining('[dry-run]') + expect.stringContaining('[dry-run]'), ); }); @@ -244,7 +248,11 @@ describe('dryRun utilities', () => { vi.mocked(helpers.isDryRun).mockReturnValue(true); const octokit = createDryRunOctokit(mockOctokit as any); - await octokit.repos.getContent({ owner: 'test', repo: 'test', path: '/' }); + await octokit.repos.getContent({ + owner: 'test', + repo: 'test', + path: '/', + }); expect(mockOctokit.repos.getContent).toHaveBeenCalled(); }); }); @@ -256,7 +264,7 @@ describe('dryRun utilities', () => { await safeFs.writeFile('/tmp/test.txt', 'content'); expect(logger.info).toHaveBeenCalledWith( - '[dry-run] Would execute: fs.writeFile(/tmp/test.txt)' + '[dry-run] Would execute: fs.writeFile(/tmp/test.txt)', ); }); @@ -265,7 +273,7 @@ describe('dryRun utilities', () => { await safeFs.unlink('/tmp/test.txt'); expect(logger.info).toHaveBeenCalledWith( - '[dry-run] Would execute: fs.unlink(/tmp/test.txt)' + '[dry-run] Would execute: fs.unlink(/tmp/test.txt)', ); }); @@ -274,7 +282,7 @@ describe('dryRun utilities', () => { await safeFs.rename('/tmp/old.txt', '/tmp/new.txt'); expect(logger.info).toHaveBeenCalledWith( - '[dry-run] Would execute: fs.rename(/tmp/old.txt, /tmp/new.txt)' + '[dry-run] Would execute: fs.rename(/tmp/old.txt, /tmp/new.txt)', ); }); }); @@ -299,7 +307,7 @@ describe('dryRun utilities', () => { expect(action).not.toHaveBeenCalled(); expect(result).toBeUndefined(); expect(logger.info).toHaveBeenCalledWith( - '[dry-run] Would execute: test action' + '[dry-run] Would execute: test action', ); }); @@ -336,7 +344,7 @@ describe('dryRun utilities', () => { expect(action).not.toHaveBeenCalled(); expect(result).toBeUndefined(); expect(logger.info).toHaveBeenCalledWith( - '[dry-run] Would execute: test action' + '[dry-run] Would execute: test action', ); }); @@ -406,7 +414,7 @@ describe('dryRun utilities', () => { await git.push(); expect(mockGit.push).not.toHaveBeenCalled(); expect(logger.info).toHaveBeenCalledWith( - expect.stringContaining('[dry-run]') + expect.stringContaining('[dry-run]'), ); }); }); @@ -422,7 +430,7 @@ describe('dryRun utilities', () => { // Should NOT have logged a dry-run message (operation is allowed) expect(logger.info).not.toHaveBeenCalledWith( - expect.stringContaining('[dry-run] Would execute: fs.writeFile') + expect.stringContaining('[dry-run] Would execute: fs.writeFile'), ); }); @@ -432,7 +440,7 @@ describe('dryRun utilities', () => { await safeFs.writeFile('/tmp/test.txt', 'content'); expect(logger.info).toHaveBeenCalledWith( - '[dry-run] Would execute: fs.writeFile(/tmp/test.txt)' + '[dry-run] Would execute: fs.writeFile(/tmp/test.txt)', ); }); }); diff --git a/src/utils/__tests__/fixtures/changelog.ts b/src/utils/__tests__/fixtures/changelog.ts index a322fcbc7..745d40de3 100644 --- a/src/utils/__tests__/fixtures/changelog.ts +++ b/src/utils/__tests__/fixtures/changelog.ts @@ -12,7 +12,7 @@ * Avoids template literal indentation issues. */ export function createChangelog( - sections: Array<{ version: string; body: string; style?: 'atx' | 'setext' }> + sections: Array<{ version: string; body: string; style?: 'atx' | 'setext' }>, ): string { return sections .map(({ version, body, style = 'atx' }) => { @@ -29,7 +29,7 @@ export function createChangelog( */ export function createFullChangelog( title: string, - sections: Array<{ version: string; body: string; style?: 'atx' | 'setext' }> + sections: Array<{ version: string; body: string; style?: 'atx' | 'setext' }>, ): string { return `# ${title}\n\n${createChangelog(sections)}`; } @@ -75,7 +75,7 @@ export interface TestCommit { export function localCommit( hash: string, title: string, - body = '' + body = '', ): TestCommit { return { hash, title, body }; } @@ -93,7 +93,7 @@ export function prCommit( labels?: string[]; prTitle?: string; prBody?: string; - } = {} + } = {}, ): TestCommit { return { hash, @@ -210,7 +210,7 @@ export function commitLink(hash: string, shortHash?: string): string { */ export function changelogEntry( title: string, - options: { author?: string; prNumber?: string; hash?: string } = {} + options: { author?: string; prNumber?: string; hash?: string } = {}, ): string { const parts = [title]; @@ -233,8 +233,7 @@ export function changelogEntry( export function changelogSection( title: string, emoji: string, - entries: string[] + entries: string[], ): string { return `### ${title} ${emoji}\n\n${entries.join('\n')}`; } - diff --git a/src/utils/__tests__/gpg.test.ts b/src/utils/__tests__/gpg.test.ts index cf2be98c5..435edf241 100644 --- a/src/utils/__tests__/gpg.test.ts +++ b/src/utils/__tests__/gpg.test.ts @@ -5,7 +5,7 @@ import { spawnProcess } from '../system'; vi.mock('../system'); -vi.mock('fs', async (importOriginal) => { +vi.mock('fs', async importOriginal => { const actual = await importOriginal(); return { ...actual, @@ -24,7 +24,7 @@ describe('importGPGKey', () => { importGPGKey(KEY); expect(fsPromises.writeFile).toHaveBeenCalledWith( PRIVATE_KEY_FILE_MATCHER, - KEY + KEY, ); }); diff --git a/src/utils/__tests__/version.test.ts b/src/utils/__tests__/version.test.ts index dc2df1291..486fa9d6d 100644 --- a/src/utils/__tests__/version.test.ts +++ b/src/utils/__tests__/version.test.ts @@ -7,55 +7,55 @@ import { parseVersion, semVerToString, versionGreaterOrEqualThan, -} from "../version"; +} from '../version'; -describe("getVersion", () => { - test("extracts a basic SemVer versions", () => { - expect(getVersion("1.0.0")).toBe("1.0.0"); +describe('getVersion', () => { + test('extracts a basic SemVer versions', () => { + expect(getVersion('1.0.0')).toBe('1.0.0'); }); test('extracts a SemVer version with leading "v"', () => { - expect(getVersion("v1.0.0")).toBe("1.0.0"); + expect(getVersion('v1.0.0')).toBe('1.0.0'); }); - test("extracts a SemVer version from text", () => { - expect(getVersion("1.0.0 (foobar)")).toBe("1.0.0"); + test('extracts a SemVer version from text', () => { + expect(getVersion('1.0.0 (foobar)')).toBe('1.0.0'); }); - test("extracts a SemVer, but ignores subpatch level", () => { - expect(getVersion("1.0.0.1")).toBe("1.0.0"); + test('extracts a SemVer, but ignores subpatch level', () => { + expect(getVersion('1.0.0.1')).toBe('1.0.0'); }); - test("extracts a SemVer version from scoped package tag", () => { - expect(getVersion("@spotlightjs/spotlight@4.10.0")).toBe("4.10.0"); + test('extracts a SemVer version from scoped package tag', () => { + expect(getVersion('@spotlightjs/spotlight@4.10.0')).toBe('4.10.0'); }); }); -describe("isValidVersion", () => { - test("accepts valid version", () => { - expect(isValidVersion("1.2.3")).toBe(true); +describe('isValidVersion', () => { + test('accepts valid version', () => { + expect(isValidVersion('1.2.3')).toBe(true); }); - test("accepts valid pre-release version", () => { - expect(isValidVersion("1.2.3-beta")).toBe(true); + test('accepts valid pre-release version', () => { + expect(isValidVersion('1.2.3-beta')).toBe(true); }); - test("accepts valid Python-style version", () => { - expect(isValidVersion("1.2.3rc1")).toBe(true); + test('accepts valid Python-style version', () => { + expect(isValidVersion('1.2.3rc1')).toBe(true); }); - test("accepts valid Python-style post release version", () => { - expect(isValidVersion("1.2.3-1")).toBe(true); + test('accepts valid Python-style post release version', () => { + expect(isValidVersion('1.2.3-1')).toBe(true); }); test('does not accept leading "v"', () => { - expect(isValidVersion("v1.2.3")).toBe(false); + expect(isValidVersion('v1.2.3')).toBe(false); }); }); -describe("parseVersion", () => { - test("parses a full SemVer version", () => { - expect(parseVersion("1.2.3")).toEqual({ +describe('parseVersion', () => { + test('parses a full SemVer version', () => { + expect(parseVersion('1.2.3')).toEqual({ major: 1, minor: 2, patch: 3, @@ -63,180 +63,200 @@ describe("parseVersion", () => { }); test('parses a SemVer with leading "v"', () => { - expect(parseVersion("v1.2.3")).toEqual({ + expect(parseVersion('v1.2.3')).toEqual({ major: 1, minor: 2, patch: 3, }); }); - test("parses a pre-release SemVer", () => { - expect(parseVersion("v1.2.3-beta")).toEqual({ + test('parses a pre-release SemVer', () => { + expect(parseVersion('v1.2.3-beta')).toEqual({ major: 1, minor: 2, patch: 3, - pre: "beta", + pre: 'beta', }); }); - test("parses a complicated pre-release SemVer", () => { - expect(parseVersion("v1.2.3-beta.1")).toEqual({ + test('parses a complicated pre-release SemVer', () => { + expect(parseVersion('v1.2.3-beta.1')).toEqual({ major: 1, minor: 2, patch: 3, - pre: "beta.1", + pre: 'beta.1', }); }); - test("parses a SemVer with build metadata", () => { - expect(parseVersion("v1.2.3+linux")).toEqual({ - build: "linux", + test('parses a SemVer with build metadata', () => { + expect(parseVersion('v1.2.3+linux')).toEqual({ + build: 'linux', major: 1, minor: 2, patch: 3, }); }); - test("parses a pre-release SemVer with build metadata", () => { - expect(parseVersion("v1.2.3-beta+linux")).toEqual({ - build: "linux", + test('parses a pre-release SemVer with build metadata', () => { + expect(parseVersion('v1.2.3-beta+linux')).toEqual({ + build: 'linux', major: 1, minor: 2, patch: 3, - pre: "beta", + pre: 'beta', }); }); - test("parses a Python-style version", () => { - expect(parseVersion("v11.22.33rc1")).toEqual({ + test('parses a Python-style version', () => { + expect(parseVersion('v11.22.33rc1')).toEqual({ major: 11, minor: 22, patch: 33, - pre: "rc1", + pre: 'rc1', }); }); - test("parses a Python-style post release version", () => { - expect(parseVersion("1.2.3-1")).toEqual({ + test('parses a Python-style post release version', () => { + expect(parseVersion('1.2.3-1')).toEqual({ major: 1, minor: 2, patch: 3, // we misinterpret the post release number as `pre` but this is fine as we // have specific checks for what we consider a preview release - pre: "1", + pre: '1', }); }); - test("does not parse an invalid version", () => { - expect(parseVersion("v1.2")).toBeNull(); + test('does not parse an invalid version', () => { + expect(parseVersion('v1.2')).toBeNull(); }); - test("cannot parse empty value", () => { - expect(parseVersion("")).toBeNull(); + test('cannot parse empty value', () => { + expect(parseVersion('')).toBeNull(); }); }); -describe("isPreviewRelease", () => { - test.each(["preview", "pre", "alpha.0", "beta", "rc.1", "dev"])("accepts semver preview release", previewSuffix => { - expect(isPreviewRelease(`2.3.4-${previewSuffix}1`)).toBe(true); - }); +describe('isPreviewRelease', () => { + test.each(['preview', 'pre', 'alpha.0', 'beta', 'rc.1', 'dev'])( + 'accepts semver preview release', + previewSuffix => { + expect(isPreviewRelease(`2.3.4-${previewSuffix}1`)).toBe(true); + }, + ); - test("accepts Python-style preview release", () => { - expect(isPreviewRelease("2.3.4rc0")).toBe(true); + test('accepts Python-style preview release', () => { + expect(isPreviewRelease('2.3.4rc0')).toBe(true); }); - test("does not accept non-preview release", () => { - expect(isPreviewRelease("2.3.4")).toBe(false); + test('does not accept non-preview release', () => { + expect(isPreviewRelease('2.3.4')).toBe(false); }); - test("does not accept non-release strings", () => { - expect(isPreviewRelease("4-preview")).toBe(false); + test('does not accept non-release strings', () => { + expect(isPreviewRelease('4-preview')).toBe(false); }); - test("does not accept Python-style post release", () => { - expect(isPreviewRelease("1.2.3-1")).toBe(false); + test('does not accept Python-style post release', () => { + expect(isPreviewRelease('1.2.3-1')).toBe(false); }); }); -describe("versionGreaterOrEqualThan", () => { - function semVerFactory(major: number, minor: number, patch: number, pre?: string, build?: string): SemVer { +describe('versionGreaterOrEqualThan', () => { + function semVerFactory( + major: number, + minor: number, + patch: number, + pre?: string, + build?: string, + ): SemVer { return { major, minor, patch, pre, build }; } - test("compares different patch versions", () => { + test('compares different patch versions', () => { const v1 = semVerFactory(1, 2, 3); const v2 = semVerFactory(1, 2, 2); expect(versionGreaterOrEqualThan(v1, v2)).toBe(true); expect(versionGreaterOrEqualThan(v2, v1)).toBe(false); }); - test("compares different major versions", () => { + test('compares different major versions', () => { const v1 = semVerFactory(2, 0, 0); const v2 = semVerFactory(3, 0, 0); expect(versionGreaterOrEqualThan(v1, v2)).toBe(false); expect(versionGreaterOrEqualThan(v2, v1)).toBe(true); }); - test("compares different major versions", () => { + test('compares different major versions', () => { const v1 = semVerFactory(3, 1, 0); const v2 = semVerFactory(3, 0, 1); expect(versionGreaterOrEqualThan(v1, v2)).toBe(true); expect(versionGreaterOrEqualThan(v2, v1)).toBe(false); }); - test("equals true for equal versions", () => { + test('equals true for equal versions', () => { const v1 = semVerFactory(0, 1, 2); const v2 = semVerFactory(0, 1, 2); expect(versionGreaterOrEqualThan(v1, v2)).toBe(true); }); - test("prefers versions with pre-release parts", () => { - const v1 = semVerFactory(0, 1, 2, "rc0"); + test('prefers versions with pre-release parts', () => { + const v1 = semVerFactory(0, 1, 2, 'rc0'); const v2 = semVerFactory(0, 1, 2); expect(versionGreaterOrEqualThan(v1, v2)).toBe(false); expect(versionGreaterOrEqualThan(v2, v1)).toBe(true); }); - test("can compare pre parts", () => { - const v1 = parseVersion("1.2.3-1")!; - const v2 = parseVersion("1.2.3-2")!; + test('can compare pre parts', () => { + const v1 = parseVersion('1.2.3-1')!; + const v2 = parseVersion('1.2.3-2')!; expect(versionGreaterOrEqualThan(v1, v2)).toBe(false); expect(versionGreaterOrEqualThan(v2, v1)).toBe(true); }); - test("throws an exception if there are build parts", () => { - const v1 = semVerFactory(0, 1, 2, undefined, "build123"); + test('throws an exception if there are build parts', () => { + const v1 = semVerFactory(0, 1, 2, undefined, 'build123'); const v2 = semVerFactory(0, 1, 2); expect(() => versionGreaterOrEqualThan(v1, v2)).toThrow(); expect(() => versionGreaterOrEqualThan(v2, v1)).toThrow(); }); }); -describe("getPackage", () => { - test("reads package.json", () => { +describe('getPackage', () => { + test('reads package.json', () => { const pkg = getPackage(); - expect(pkg.name).toBe("@sentry/craft"); + expect(pkg.name).toBe('@sentry/craft'); }); }); -describe("getPackageVersion", () => { - test("reads package.json", () => { +describe('getPackageVersion', () => { + test('reads package.json', () => { const version = getPackage().version; expect(isValidVersion(version)).toBe(true); }); }); -describe("semVerToString", () => { +describe('semVerToString', () => { test.each([ - ["basic", { major: 1, minor: 2, patch: 3 }, "1.2.3"], - ["with pre-release", { major: 1, minor: 2, patch: 3, pre: "beta.1" }, "1.2.3-beta.1"], - ["with build metadata", { major: 1, minor: 2, patch: 3, build: "linux" }, "1.2.3+linux"], + ['basic', { major: 1, minor: 2, patch: 3 }, '1.2.3'], [ - "with pre-release and build metadata", - { major: 1, minor: 2, patch: 3, pre: "beta.1", build: "linux" }, - "1.2.3-beta.1+linux", + 'with pre-release', + { major: 1, minor: 2, patch: 3, pre: 'beta.1' }, + '1.2.3-beta.1', ], - ])("converts a SemVer object (%s) to a string", (_, semver, expectedString) => { - expect(semVerToString(semver)).toBe(expectedString); - }); + [ + 'with build metadata', + { major: 1, minor: 2, patch: 3, build: 'linux' }, + '1.2.3+linux', + ], + [ + 'with pre-release and build metadata', + { major: 1, minor: 2, patch: 3, pre: 'beta.1', build: 'linux' }, + '1.2.3-beta.1+linux', + ], + ])( + 'converts a SemVer object (%s) to a string', + (_, semver, expectedString) => { + expect(semVerToString(semver)).toBe(expectedString); + }, + ); }); diff --git a/src/utils/async.ts b/src/utils/async.ts index 729482a03..02d2863d8 100644 --- a/src/utils/async.ts +++ b/src/utils/async.ts @@ -47,7 +47,7 @@ export async function retrySpawnProcess( args: string[] = [], spawnOptions: SpawnOptions = {}, spawnProcessOptions: SpawnProcessOptions = {}, - retryOptions: RetryOptions = {} + retryOptions: RetryOptions = {}, ): Promise { const maxRetries = retryOptions.maxRetries ?? MAX_RETRIES; const retryExpFactor = retryOptions.retryExpFactor ?? RETRY_EXP_FACTOR; @@ -62,7 +62,7 @@ export async function retrySpawnProcess( await sleep(retryDelay * 1000); retryDelay *= retryExpFactor; return true; - } + }, ); } @@ -79,7 +79,7 @@ export async function retrySpawnProcess( export async function filterAsync( array: T[], predicate: (arg: T) => boolean | Promise, - thisArg?: unknown + thisArg?: unknown, ): Promise { const verdicts = await Promise.all(array.map(predicate, thisArg)); return array.filter((_element, index) => verdicts[index]); @@ -103,15 +103,15 @@ export async function filterAsync( export async function forEachChained( array: T[], iteratee: (x: T) => any, - - thisArg?: any + + thisArg?: any, ): Promise { return array.reduce( async (prev, ...args: [T]) => // catching errors after each .then() lets us report them and keep going // if in dry-run mode (in regular mode, reportError() just re-throws) prev.then(() => iteratee.apply(thisArg, args)).catch(reportError), - Promise.resolve() + Promise.resolve(), ); } @@ -155,7 +155,7 @@ class RetryError extends ExtendedError { export async function withRetry( fn: () => Promise, maxRetries = 3, - onRetry?: (err: Error) => Promise + onRetry?: (err: Error) => Promise, ): Promise { if (!onRetry) { onRetry = () => Promise.resolve(true); diff --git a/src/utils/autoVersion.ts b/src/utils/autoVersion.ts index 7537af410..ec330778e 100644 --- a/src/utils/autoVersion.ts +++ b/src/utils/autoVersion.ts @@ -23,7 +23,7 @@ export { BUMP_TYPES, isBumpType, type BumpType, type ChangelogResult }; */ export function calculateNextVersion( currentVersion: string, - bumpType: BumpType + bumpType: BumpType, ): string { // Handle empty/missing current version (new project) const versionToBump = currentVersion || '0.0.0'; @@ -32,7 +32,7 @@ export function calculateNextVersion( if (!newVersion) { throw new Error( - `Failed to increment version "${versionToBump}" with bump type "${bumpType}"` + `Failed to increment version "${versionToBump}" with bump type "${bumpType}"`, ); } @@ -49,10 +49,10 @@ export function calculateNextVersion( */ export async function getChangelogWithBumpType( git: SimpleGit, - rev: string + rev: string, ): Promise { logger.info( - `Analyzing commits since ${rev || '(beginning of history)'} for auto-versioning...` + `Analyzing commits since ${rev || '(beginning of history)'} for auto-versioning...`, ); const result = await generateChangesetFromGit(git, rev); @@ -60,7 +60,7 @@ export async function getChangelogWithBumpType( if (result.bumpType) { logger.info( `Auto-version: determined ${result.bumpType} bump ` + - `(${result.matchedCommitsWithSemver}/${result.totalCommits} commits matched)` + `(${result.matchedCommitsWithSemver}/${result.totalCommits} commits matched)`, ); } @@ -73,10 +73,12 @@ export async function getChangelogWithBumpType( * @param result The changelog result to validate * @throws Error if no commits found or none match categories with semver fields */ -export function validateBumpType(result: ChangelogResult): asserts result is ChangelogResult & { bumpType: BumpType } { +export function validateBumpType( + result: ChangelogResult, +): asserts result is ChangelogResult & { bumpType: BumpType } { if (result.totalCommits === 0) { throw new Error( - 'Cannot determine version automatically: no commits found since the last release.' + 'Cannot determine version automatically: no commits found since the last release.', ); } @@ -85,7 +87,7 @@ export function validateBumpType(result: ChangelogResult): asserts result is Cha `Cannot determine version automatically: ${result.totalCommits} commit(s) found, ` + 'but none matched a category with a "semver" field in the release configuration. ' + 'Please ensure your .github/release.yml categories have "semver" fields defined, ' + - 'or specify the version explicitly.' + 'or specify the version explicitly.', ); } } diff --git a/src/utils/awsLambdaLayerManager.ts b/src/utils/awsLambdaLayerManager.ts index 7a66f738c..6cdbc7b82 100644 --- a/src/utils/awsLambdaLayerManager.ts +++ b/src/utils/awsLambdaLayerManager.ts @@ -54,7 +54,7 @@ export class AwsLambdaLayerManager { license: string, artifactBuffer: Buffer, awsRegions: string[], - sdkVersion: string + sdkVersion: string, ) { this.runtime = runtime; this.layerName = layerName; @@ -114,11 +114,11 @@ export class AwsLambdaLayerManager { } catch (error) { logger.warn( 'Something went wrong with AWS trying to publish to region ' + - `${region}: ${error.message}` + `${region}: ${error.message}`, ); return undefined; } - }) + }), ); return publishedLayers.filter(layer => { return layer !== undefined; @@ -154,17 +154,19 @@ export async function getRegionsFromAws(): Promise { }); const url = `https://${hostname}${path}`; - const response = await fetch(url, { headers: headers as Record }); + const response = await fetch(url, { + headers: headers as Record, + }); if (!response.ok) { throw new Error( - `Unexpected HTTP response from ${url}: ${response.status} (${response.statusText})` + `Unexpected HTTP response from ${url}: ${response.status} (${response.statusText})`, ); } const data = await response.text(); return new XMLParser() .parse(data) .DescribeRegionsResponse.regionInfo.item.map( - (region: Region) => region.regionName + (region: Region) => region.regionName, ) .filter(Boolean); } diff --git a/src/utils/calver.ts b/src/utils/calver.ts index 4ecccd9f3..be01b4aaf 100644 --- a/src/utils/calver.ts +++ b/src/utils/calver.ts @@ -62,7 +62,7 @@ export function formatCalVerDate(date: Date, format: string): string { */ export async function calculateCalVer( git: SimpleGit, - config: CalVerConfig + config: CalVerConfig, ): Promise { // Calculate date with offset const date = new Date(); @@ -71,7 +71,9 @@ export async function calculateCalVer( // Format date part const datePart = formatCalVerDate(date, config.format); - logger.debug(`CalVer: using date ${date.toISOString()}, date part: ${datePart}`); + logger.debug( + `CalVer: using date ${date.toISOString()}, date part: ${datePart}`, + ); // Find existing tags and determine next patch version // Account for git tag prefix (e.g., 'v') when searching diff --git a/src/utils/checksum.ts b/src/utils/checksum.ts index 5e7229f31..491938956 100644 --- a/src/utils/checksum.ts +++ b/src/utils/checksum.ts @@ -26,30 +26,28 @@ export function castChecksums(checksums: any[]): ChecksumEntry[] { } if (!Array.isArray(checksums)) { throw new ConfigurationError( - 'Invalid type of "checksums": should be an array' + 'Invalid type of "checksums": should be an array', ); } - return checksums.map( - (item: any): ChecksumEntry => { - if (typeof item !== 'object' || !item.algorithm || !item.format) { - throw new ConfigurationError( - `Invalid checksum type: ${JSON.stringify(item)}` - ); - } - if ( - !Object.values(HashAlgorithm).includes(item.algorithm) || - !Object.values(HashOutputFormat).includes(item.format) - ) { - throw new ConfigurationError( - `Invalid checksum type: ${JSON.stringify(item)}` - ); - } - return { - algorithm: item.algorithm, - format: item.format, - }; + return checksums.map((item: any): ChecksumEntry => { + if (typeof item !== 'object' || !item.algorithm || !item.format) { + throw new ConfigurationError( + `Invalid checksum type: ${JSON.stringify(item)}`, + ); } - ); + if ( + !Object.values(HashAlgorithm).includes(item.algorithm) || + !Object.values(HashOutputFormat).includes(item.format) + ) { + throw new ConfigurationError( + `Invalid checksum type: ${JSON.stringify(item)}`, + ); + } + return { + algorithm: item.algorithm, + format: item.format, + }; + }); } /** @@ -61,7 +59,7 @@ export function castChecksums(checksums: any[]): ChecksumEntry[] { export async function getArtifactChecksums( checksums: ChecksumEntry[], artifact: RemoteArtifact, - artifactProvider: BaseArtifactProvider + artifactProvider: BaseArtifactProvider, ): Promise<{ [key: string]: string; }> { @@ -71,7 +69,7 @@ export async function getArtifactChecksums( const currentChecksum = await artifactProvider.getChecksum( artifact, algorithm, - format + format, ); fileChecksums[`${algorithm}-${format}`] = currentChecksum; } diff --git a/src/utils/dryRun.ts b/src/utils/dryRun.ts index a4c432866..e5809bf91 100644 --- a/src/utils/dryRun.ts +++ b/src/utils/dryRun.ts @@ -178,7 +178,7 @@ const SYMLINK_DIRS = [ */ function symlinkDependencyDirs( originalCwd: string, - worktreePath: string + worktreePath: string, ): void { for (const dir of SYMLINK_DIRS) { const srcPath = join(originalCwd, dir); @@ -225,7 +225,7 @@ function symlinkDependencyDirs( */ export async function createDryRunIsolation( git: SimpleGit, - rev?: string + rev?: string, ): Promise { // If not in dry-run mode, return a passthrough that does nothing if (!isDryRun()) { @@ -279,7 +279,7 @@ export async function createDryRunIsolation( await cleanupGit.raw(['worktree', 'remove', '--force', worktreePath]); } catch (err) { logger.debug( - `[dry-run] Git worktree remove failed, cleaning up manually: ${err}` + `[dry-run] Git worktree remove failed, cleaning up manually: ${err}`, ); try { await rm(worktreePath, { recursive: true, force: true }); @@ -520,7 +520,7 @@ const GITHUB_MUTATING_PREFIXES = [ */ function isGitHubMutatingMethod(methodName: string): boolean { return GITHUB_MUTATING_PREFIXES.some(prefix => - methodName.toLowerCase().startsWith(prefix.toLowerCase()) + methodName.toLowerCase().startsWith(prefix.toLowerCase()), ); } @@ -529,7 +529,7 @@ function isGitHubMutatingMethod(methodName: string): boolean { */ function createGitHubNamespaceProxy( target: Record, - path: string[] = [] + path: string[] = [], ): Record { return new Proxy(target, { get(obj, prop: string) { @@ -555,7 +555,7 @@ function createGitHubNamespaceProxy( if (typeof value === 'object' && value !== null) { return createGitHubNamespaceProxy( value as Record, - currentPath + currentPath, ); } @@ -575,7 +575,7 @@ function createGitHubNamespaceProxy( */ export function createDryRunOctokit(octokit: Octokit): Octokit { return createGitHubNamespaceProxy( - octokit as unknown as Record + octokit as unknown as Record, ) as unknown as Octokit; } @@ -619,7 +619,7 @@ const FS_MUTATING_METHODS: Record = { * Creates a proxy handler for file system modules. */ function createFsProxyHandler( - isAsync: boolean + isAsync: boolean, ): ProxyHandler { return { get(target, prop: string) { @@ -651,7 +651,7 @@ function createFsProxyHandler( */ export const safeFsPromises = new Proxy( fsPromises, - createFsProxyHandler(true) + createFsProxyHandler(true), ) as typeof fsPromises; /** @@ -659,7 +659,7 @@ export const safeFsPromises = new Proxy( */ export const safeFsSync = new Proxy( fs, - createFsProxyHandler(false) + createFsProxyHandler(false), ) as typeof fs; /** @@ -696,7 +696,7 @@ export const safeFs = { */ export async function safeExec( action: () => Promise, - description: string + description: string, ): Promise { if (isDryRun() && !isInWorktreeMode()) { logDryRun(description); @@ -714,7 +714,7 @@ export async function safeExec( */ export function safeExecSync( action: () => T, - description: string + description: string, ): T | undefined { if (isDryRun() && !isInWorktreeMode()) { logDryRun(description); diff --git a/src/utils/env.ts b/src/utils/env.ts index f6f3c85be..4bc5261ed 100644 --- a/src/utils/env.ts +++ b/src/utils/env.ts @@ -57,13 +57,13 @@ function envHasVar(envVar: RequiredConfigVar): boolean { // the less simple cases - only using legacy name or using both else if (process.env[legacyName] && !process.env[name]) { logger.warn( - `Usage of ${legacyName} is deprecated, and will be removed in later versions. Please use ${name} instead.` + `Usage of ${legacyName} is deprecated, and will be removed in later versions. Please use ${name} instead.`, ); logger.debug(`Moving legacy environment variable ${legacyName} to ${name}`); process.env[name] = process.env[legacyName]; } else if (process.env[legacyName] && process.env[name]) { logger.warn( - `When searching configuration files and your environment, found ${name} but also found legacy ${legacyName}. Do you mean to be using both?` + `When searching configuration files and your environment, found ${name} but also found legacy ${legacyName}. Do you mean to be using both?`, ); } @@ -86,7 +86,7 @@ function checkFileIsPrivate(path: string): boolean { if (mode & GROUP_MODE_MASK || mode & OTHER_MODE_MASK) { const perms = (mode & FULL_MODE_MASK).toString(8); logger.warn( - `Permissions 0${perms} for file "${path}" are too open. Consider making it readable only for the user.` + `Permissions 0${perms} for file "${path}" are too open. Consider making it readable only for the user.`, ); return false; } @@ -111,7 +111,7 @@ export function readEnvironmentConfig(overwriteExisting = false): void { if (existsSync(homedirEnvFile)) { logger.debug( 'Found environment file in the home directory:', - homedirEnvFile + homedirEnvFile, ); checkFileIsPrivate(homedirEnvFile); const homedirEnv = {}; @@ -121,7 +121,7 @@ export function readEnvironmentConfig(overwriteExisting = false): void { } else { logger.debug( 'No environment file found in the home directory:', - homedirEnvFile + homedirEnvFile, ); } @@ -134,7 +134,7 @@ export function readEnvironmentConfig(overwriteExisting = false): void { } else if (configDirEnvFile && existsSync(configDirEnvFile)) { logger.debug( 'Found environment file in the configuration directory:', - configDirEnvFile + configDirEnvFile, ); checkFileIsPrivate(configDirEnvFile); const configDirEnv = {}; @@ -144,7 +144,7 @@ export function readEnvironmentConfig(overwriteExisting = false): void { } else { logger.debug( 'No environment file found in the configuration directory:', - configDirEnvFile + configDirEnvFile, ); } @@ -174,7 +174,7 @@ export function checkEnvForPrerequisite(...varList: RequiredConfigVar[]): void { // all of the places they might have stuck these variables. throw new ConfigurationError( `Required value(s) ${varNames} not found in configuration files or ` + - `the environment. See the documentation for more details.` + `the environment. See the documentation for more details.`, ); } } diff --git a/src/utils/errors.ts b/src/utils/errors.ts index aa6d07234..12aa84f0c 100644 --- a/src/utils/errors.ts +++ b/src/utils/errors.ts @@ -31,7 +31,7 @@ export function reportError( errorLogger: { error: (...message: string[]) => void; [key: string]: any; - } = logger + } = logger, ): void { if (!isDryRun()) { // wrap the error in an Error object if it isn't already one @@ -53,7 +53,7 @@ export function reportError( * * @param e Error (exception) object to handle */ - + export function handleGlobalError(e: any): void { if (!(e instanceof ConfigurationError)) { captureException(e); diff --git a/src/utils/githubApi.ts b/src/utils/githubApi.ts index bcca7f20d..f6ab4b14f 100644 --- a/src/utils/githubApi.ts +++ b/src/utils/githubApi.ts @@ -79,7 +79,7 @@ export function getGitHubApiToken(): string { if (!githubApiToken) { throw new ConfigurationError( 'GITHUB_TOKEN not found. This is required to fetch PR information from GitHub.\n' + - 'Tip: Run `gh auth token` if you have GitHub CLI installed.' + 'Tip: Run `gh auth token` if you have GitHub CLI installed.', ); } return githubApiToken; @@ -137,7 +137,7 @@ export async function getFile( owner: string, repo: string, path: string, - ref: string + ref: string, ): Promise { return withTracing( async () => { @@ -169,6 +169,6 @@ export async function getFile( 'github.path': path, 'github.ref': ref, }, - } + }, )(); } diff --git a/src/utils/packagePath.ts b/src/utils/packagePath.ts index b5c996924..264b10235 100644 --- a/src/utils/packagePath.ts +++ b/src/utils/packagePath.ts @@ -23,7 +23,7 @@ function getAppPackagePath(canonical: string): string { const packageDirs = parseCanonical(canonical); if (packageDirs[0] !== 'app') { throw new ConfigurationError( - `Invalid canonical entry for an app: ${canonical}` + `Invalid canonical entry for an app: ${canonical}`, ); } return path.join('apps', ...packageDirs.slice(1)); @@ -37,7 +37,7 @@ function getAppPackagePath(canonical: string): string { */ export function getPackageDirPath( packageType: RegistryPackageType, - canonical: string + canonical: string, ): string { switch (packageType) { case RegistryPackageType.SDK: @@ -46,7 +46,7 @@ export function getPackageDirPath( return getAppPackagePath(canonical); default: throw new ConfigurationError( - `Unknown registry package type: ${packageType}` + `Unknown registry package type: ${packageType}`, ); } } @@ -67,7 +67,7 @@ export function parseCanonical(canonicalName: string): string[] { const registrySepPosition = canonicalName.indexOf(':'); if (registrySepPosition === -1) { throw new ConfigurationError( - `Cannot parse canonical name for the package: ${canonicalName}` + `Cannot parse canonical name for the package: ${canonicalName}`, ); } const registry = canonicalName.slice(0, registrySepPosition); @@ -76,7 +76,7 @@ export function parseCanonical(canonicalName: string): string[] { const packageDirs = packageName.split(/[:/]/); if (packageDirs.some(x => !x)) { throw new ConfigurationError( - `Cannot parse canonical name for the package: ${canonicalName}` + `Cannot parse canonical name for the package: ${canonicalName}`, ); } return [registry, ...packageDirs]; diff --git a/src/utils/symlink.ts b/src/utils/symlink.ts index 62807084d..a46d5c77d 100644 --- a/src/utils/symlink.ts +++ b/src/utils/symlink.ts @@ -37,7 +37,7 @@ function forceSymlink(target: string, newFile: string): void { export function createSymlinks( versionFilePath: string, newVersion: string, - oldVersion?: string + oldVersion?: string, ): void { const parsedNewVersion = parseVersion(newVersion) || undefined; if (!parsedNewVersion) { @@ -62,13 +62,13 @@ export function createSymlinks( // Read possibly existing symlinks for major and minor versions of the new version const existingLinkedMajorVersion = getExistingSymlinkedVersion( - path.join(packageDir, `${parsedNewVersion.major}.json`) + path.join(packageDir, `${parsedNewVersion.major}.json`), ); const existingLinkedMinorVersion = getExistingSymlinkedVersion( path.join( packageDir, - `${parsedNewVersion.major}.${parsedNewVersion.minor}.json` - ) + `${parsedNewVersion.major}.${parsedNewVersion.minor}.json`, + ), ); // link {major}.json if there's no link yet for that major diff --git a/src/utils/version.ts b/src/utils/version.ts index 37b3372c7..8fca3c9bb 100644 --- a/src/utils/version.ts +++ b/src/utils/version.ts @@ -100,8 +100,8 @@ export function versionGreaterOrEqualThan(v1: SemVer, v2: SemVer): boolean { } else if (v1.build || v2.build || v1.pre || v2.pre) { throw new Error( `Cannot compare the two versions: "${JSON.stringify( - v1 - )}" and "${JSON.stringify(v2)}"` + v1, + )}" and "${JSON.stringify(v2)}"`, ); } return true; @@ -110,7 +110,8 @@ export function versionGreaterOrEqualThan(v1: SemVer, v2: SemVer): boolean { /** * A regular expression to detect that a version is a pre-release version. */ -export const PREVIEW_RELEASE_REGEX = /(?:[^a-z])(preview|pre|rc|dev|alpha|beta|unstable|a|b)(?:[^a-z]|$)/i; +export const PREVIEW_RELEASE_REGEX = + /(?:[^a-z])(preview|pre|rc|dev|alpha|beta|unstable|a|b)(?:[^a-z]|$)/i; /** * Checks that the provided string is a pre-release version.