Dashboard › cli › Distillation
b8b957f2-e6fe-4127-9ee5-3cde8c12c4d0["lore_tm_v1_4tjJO-rC8Zd44g5nVZ3t-sf9l3-iZtyb0pESqDhceH4","lore_tm_v1_QbtlfrFpt5vzQy8Uu6c_A_aEXz6aN-cCmZmOWbOT6vM","lore_tm_v1_CcXPxCN-Fy75TuxsizM6t66ePayzyaqJziA1jizXb4M","lore_tm_v1_0GIrLeUA2ifOmz03ij9ab9Vz6u04uexJqlC3PUbN4qQ","lore_tm_v1_wFKYCx9GmfJYwqUmu7IbpheBogggFUNozNM_9uWD8Eg","lore_tm_v1_Cksqu2ZpOFK26LHXKvJHigV4h7XwgL4znAQX4JmlL80","lore_tm_v1_GH8CT_pHkZvq3RSbfw7NmyNazacPg7fV4JftGyVfX8M","lore_tm_v1_eGvEBCB7VxSZA4lHYgxtMs6mBRvDU2IWOfw1C4yJf2A","lore_tm_v1_lwsj5gDMtM8cRopNzokCoSVmUJmb0EVyZruj5cexnKg"]
packages/cli/test/lib/install-script.test.ts contains 2 Vitest tests: (1) passes --no-agent-skills through to sentry cli setup, which invokes the install script with --version 0.31.0 --no-modify-path --no-completions --no-agent-skills and expects setup arguments cli, setup, --install, --method, curl, --channel, stable, --no-modify-path, --no-completions, --no-agent-skills; (2) embeds the shared ordered upgrade source list, which verifies the shell script’s UPGRADE_SOURCES equals UPGRADE_SOURCES.map((source) => source.githubRepo) from packages/cli/src/lib/binary.ts.curl and gunzip executables in a temporary bin directory, capture delegated arguments via SENTRY_TEST_ARGS_FILE, override PATH and TMPDIR, and delete the temporary directory after each test./.packages/cli/src/lib/binary.ts defines KNOWN_CURL_DIRS = [".local/bin", "bin", ".sentry/bin"], LEGACY_INSTALL_SUBDIR = join(".sentry", "bin"), and LEGACY_INSTALL_SUBDIRS = [LEGACY_INSTALL_SUBDIR]; migration is deliberately restricted to the pre-XDG ~/.sentry/bin, because ~/.local/bin and ~/bin remain valid current installation targets.packages/cli/src/lib/binary.ts implements stripTrailingSep(p) as p.length > 1 && p.endsWith(sep) ? p.slice(0, -1) : p; samePath(a, b) tolerates trailing separators and compares case-insensitively on Windows and macOS via the module-level IS_CASE_INSENSITIVE_FS, while preserving case-sensitive comparison on other platforms.packages/cli/src/lib/binary.ts defines getLegacyInstallDirs(homeDir) by joining homeDir with every value in LEGACY_INSTALL_SUBDIRS, and defines InstallationMethod beginning with "curl" | "brew" | "npm" | "pnpm".packages/cli/src/lib/binary.ts exports fetchWithUpgradeError(url, init, serviceName): it calls customFetch, rethrows AbortError unchanged, converts TLS certificate errors into new UpgradeError("network_error", buildTlsErrorDetail(error)), and maps other failures to new UpgradeError("network_error", \Failed to connect to ${serviceName}: ${msg}`)`.packages/cli/src/lib/binary.ts implements synchronous replaceBinarySync(tempPath, installPath): Unix atomically renames the temporary binary over the target; Windows first renames the running executable to ${installPath}.old, retries after unlinking a pre-existing .old, and then renames the temporary file into place. The synchronous sequence is intended to avoid interruption between replacement steps.VercelTarget uses @vercel/client’s createDeployment to deploy an extracted release artifact. Its runtime config fields are prebuilt?: boolean, workingDir?: string, orgId?: string, and projectId?: string; VERCEL_TOKEN is treated as the credential, while org/team and project IDs are identifiers rather than secrets.meta: { release: version }; configured projectId is assigned to deploymentOptions.project, while the createDeployment request receives token, path: deployDir, prebuilt, conditional vercelOutputDir: join(deployDir, '.vercel', 'output'), teamId: orgId, and skipAutoDetectionConfirmation: true.VercelTarget.publish(version, revision) requests artifacts for the revision using DEFAULT_DEPLOY_ARCHIVE_REGEX; it reports Cannot deploy to Vercel: no artifacts found for zero results and a Not implemented: more than one Vercel archive found error for multiple results, downloads the sole artifact, extracts it into a temporary directory, and uses configured workingDir as a subdirectory when present.MAX_TRIES = 3, MILLISECONDS = 1000, ARTIFACTS_POLLING_INTERVAL = 10 * MILLISECONDS, and DOWNLOAD_CONCURRENCY = 3.created_at date should always be greater than or equal to its associated revision date.searchForRevisionArtifact(revision, getRevisionDate) requests repository artifacts in pages of 100, assumes descending creation-date order, searches for artifact.name === revision, and continues paging only when the last artifact has no created_at value or lastArtifact.created_at >= revisionDate.getRevisionArtifact(revision) lazily fetches the commit’s committer date with github.git.getCommit, retries artifact discovery up to MAX_TRIES (3), waits 10 seconds between attempts for GitHub API propagation, and ultimately throws Can't find any artifacts for revision "${revision}" (tries: 3).head_sha: revision, pagination of 100, optionally filters run names by configured workflow regular expressions, fetches each run’s artifacts, deduplicates them by numeric artifact ID, and matches artifact names against applicable regular expressions.validateAllPatternsMatched(filters, allRuns, matchingArtifacts) requires every configured workflow pattern to match at least one workflow run and every artifact pattern to match at least one artifact scoped to the applicable workflow; its errors list available workflow or artifact names to diagnose .craft.yml mismatches.doListArtifactsWithFilters(revision, filters) retries up to 3 times when workflow runs, matching artifacts, or complete pattern matches are unavailable, waiting 10 seconds between attempts; once all patterns match, it downloads all matching artifacts in parallel. Error guidance tells users to verify successful CI completion and that .craft.yml workflow/artifact names match GitHub Actions output.doListArtifactsForRevision(revision) chooses workflow-based lookup when normalizeArtifactsConfig() yields filters; otherwise it retains the legacy convention artifact.name === revision SHA, requests the archive URL, and downloads/unpacks the ZIP.ArtifactProviderConfig requires name, repoName, and repoOwner, permits optional downloadDirectory, and allows provider-specific keys through [key: string]: any; BaseArtifactProvider caches revision-to-artifact promises in fileListCache and per-path checksums in checksumCache.getArtifactProviderFromConfig() defaults an unspecified artifact provider to ArtifactProviderName.GitHub, combines provider-specific config with repoName: githubRepo.repo and repoOwner: githubRepo.owner, and instantiates NoneArtifactProvider, GCSArtifactProvider, or GitHubArtifactProvider; unknown names throw ConfigurationError('Invalid artifact provider').owner or repo while inheriting the other top-level value. The merged github config is retained only when both owner and repo are present; otherwise it is deleted so getGlobalGitHubConfig() can fall back to Git remote detection.getGlobalGitHubConfig(clearCache = false) first uses active configuration, then falls back to Git remotes from getConfigFileDir() || '.', preferring the origin remote and otherwise the first remote; only github.com remotes are accepted. The result is frozen and cached, including a null cache state that produces ConfigurationError('GitHub configuration not found in the config file and cannot be determined from Git').getVersioningPolicy() uses an explicitly configured config.versioning.policy; otherwise it returns VersioningPolicy.Auto when requiresMinVersion(AUTO_VERSION_MIN_VERSION) is true and VersioningPolicy.Manual otherwise. The documented auto-version threshold is minVersion >= 2.14.0.getGitTagPrefix() returns the first active github target’s tagPrefix, defaulting to ''. If active GitHub targets have conflicting prefixes, it warns that the first is being used and recommends one top-level workspaces entry per independently versioned product—or a separate .craft.yml—with one GitHub target and its own tagPrefix.getStatusProviderFromConfig() defaults to StatusProviderName.GitHub, constructs StatusProviderConfig from provider config plus name, and creates GitHubStatusProvider(statusProviderConfig, githubConfig); unsupported providers throw ConfigurationError('Invalid status provider').