Skip to content

Commit

Permalink
more informative profile load error (#420)
Browse files Browse the repository at this point in the history
* more informative profile load error
* avoid profile load error on some commands
  • Loading branch information
Roy Razon authored Feb 6, 2024
1 parent 6cf2fdf commit dfb5e24
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 7 deletions.
2 changes: 2 additions & 0 deletions packages/cli/src/commands/profile/ls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export default class ListProfile extends ProfileCommand<typeof ListProfile> {
json: Flags.boolean({}),
}

protected throwOnProfileNotFound = false

async run(): Promise<unknown> {
const { profiles, current } = await this.profileConfig.list()

Expand Down
2 changes: 2 additions & 0 deletions packages/cli/src/commands/profile/rm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ export default class RemoveProfile extends ProfileCommand<typeof RemoveProfile>

static enableJsonFlag = true

protected throwOnProfileNotFound = false

async run(): Promise<unknown> {
const alias = this.args.name
if (await this.profileConfig.delete(alias, { throwOnNotFound: !this.flags.force })) {
Expand Down
12 changes: 10 additions & 2 deletions packages/cli/src/profile-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import path from 'path'
import { Command, Flags, Interfaces } from '@oclif/core'
import {
tryParseUrl, LocalProfilesConfig, Profile, Store, detectCiProvider, fsTypeFromUrl,
localProfilesConfig, telemetryEmitter, LocalProfilesConfigGetResult,
localProfilesConfig, telemetryEmitter, LocalProfilesConfigGetResult, ProfileLoadError,
} from '@preevy/core'
import { BaseCommand, text } from '@preevy/cli-common'
import { fsFromUrl } from './fs.js'
Expand Down Expand Up @@ -86,10 +86,18 @@ abstract class ProfileCommand<T extends typeof Command> extends BaseCommand<T> {
protected flags!: Flags<T>
protected args!: Args<T>

protected throwOnProfileNotFound = true

public async init(): Promise<void> {
await super.init()
const { profileConfig, flags } = this
const profile = await findProfile({ profileConfig, flags })
const profile = await findProfile({ profileConfig, flags }).catch(e => {
if (!(e instanceof ProfileLoadError) || this.throwOnProfileNotFound) {
throw e
}
this.logger.warn(`Profile load error: ${e.message}`)
return undefined
})
if (!profile) {
return
}
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export {
profileStore, Profile, ProfileStore, ProfileStoreRef, ProfileStoreTransaction, ProfileEditor,
ProfileEditorOp,
link, Org, LocalProfilesConfigGetResult,
ProfileLoadError,
} from './profile/index.js'
export { telemetryEmitter, registerEmitter, wireProcessExit, createTelemetryEmitter, machineId } from './telemetry/index.js'
export { fsTypeFromUrl, Store, VirtualFS, FsReader, localFsFromUrl, localFs } from './store/index.js'
Expand Down
26 changes: 22 additions & 4 deletions packages/core/src/profile/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ const listPersistence = ({ localDir }: { localDir: string }) => {
}
}

export class ProfileLoadError extends Error {
constructor(message: string) {
super(message)
}
}

export const localProfilesConfig = (
localDir: string,
fsFromUrl: (url: string, baseDir: string) => Promise<VirtualFS>,
Expand All @@ -74,22 +80,31 @@ export const localProfilesConfig = (
async function get(alias: string | undefined, opts?: { throwOnNotFound: boolean }): Promise<GetResult | undefined> {
const throwOrUndefined = () => {
if (opts?.throwOnNotFound) {
throw new Error(`Profile ${alias} not found`)
throw new ProfileLoadError(`Profile ${alias} not found`)
}
return undefined
}

const { profiles, current } = await listP.read()
const aliasToGet = alias ?? current
if (!aliasToGet) {
return throwOrUndefined()
if (opts?.throwOnNotFound) {
throw new ProfileLoadError('Profile not specified and no current profile is set')
}
return undefined
}
const locationUrl = profiles[aliasToGet]?.location
if (!locationUrl) {
if (opts?.throwOnNotFound) {
throw new ProfileLoadError(`No profile with alias ${aliasToGet}`)
}
return throwOrUndefined()
}
const tarSnapshotStore = await storeFromUrl(locationUrl)
const profileInfo = await profileStore(tarSnapshotStore).ref.info()
const profileInfo = await profileStore(tarSnapshotStore).ref.info({ throwOnNotFound: false })
if (!profileInfo) {
throw new ProfileLoadError(`Could not load profile "${aliasToGet}" at ${locationUrl}. The profile may have been deleted`)
}
return {
alias: aliasToGet,
location: locationUrl,
Expand Down Expand Up @@ -200,7 +215,10 @@ export const localProfilesConfig = (
throw new Error(`Profile ${alias} already exists`)
}
const tarSnapshotStore = await storeFromUrl(fromLocation)
const info = await profileStore(tarSnapshotStore).ref.info()
const info = await profileStore(tarSnapshotStore).ref.info({ throwOnNotFound: false })
if (!info) {
throw new Error(`Cannot import profile from ${fromLocation}. The profile may have been deleted`)
}
const newProfile = {
id: info.id,
alias,
Expand Down
9 changes: 8 additions & 1 deletion packages/core/src/profile/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,14 @@ const readLines = (buffer: Buffer | undefined) => {

const profileReader = (reader: FsReader) => {
const { readJsonOrThrow, readJSON } = jsonReader(reader)
const info = async () => await readJsonOrThrow<Profile>(filenames.info)
async function info(opts: { throwOnNotFound: false }): Promise<Profile | undefined>
async function info(opts: { throwOnNotFound: true }): Promise<Profile>
async function info(): Promise<Profile>
async function info(
{ throwOnNotFound = true }: { throwOnNotFound?: boolean } = { throwOnNotFound: true },
): Promise<Profile | undefined> {
return await (throwOnNotFound ? readJsonOrThrow<Profile> : readJSON<Profile>)(filenames.info)
}
return {
info,
driver: async () => (await info()).driver,
Expand Down

0 comments on commit dfb5e24

Please sign in to comment.