From bc65f33995e7aefc8a7817026f21800c5bd52bd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Scott=20C=C3=B4t=C3=A9?= Date: Tue, 5 Dec 2023 23:54:44 -0500 Subject: [PATCH] Add --once and --prefer --- .../__snapshots__/command.spec.ts.snap | 6 ++- src/commands/sync.ts | 17 ++++++-- src/services/filesync/filesync.ts | 39 ++++++++++++++----- 3 files changed, 46 insertions(+), 16 deletions(-) diff --git a/spec/services/command/__snapshots__/command.spec.ts.snap b/spec/services/command/__snapshots__/command.spec.ts.snap index d7dba27b9..8244a2f08 100644 --- a/spec/services/command/__snapshots__/command.spec.ts.snap +++ b/spec/services/command/__snapshots__/command.spec.ts.snap @@ -52,8 +52,10 @@ ARGUMENTS DIRECTORY The directory to sync files to (default: \\".\\") FLAGS - -a, --app= The Gadget application to sync files to - --force Sync regardless of local file state + -a, --app= The Gadget application to sync files to + --prefer= Conflict resolution strategy + --once Sync once and exit + --force Sync regardless of local file state DESCRIPTION Sync allows you to synchronize your Gadget application's source diff --git a/src/commands/sync.ts b/src/commands/sync.ts index 75569a6e7..e2ee7f24a 100644 --- a/src/commands/sync.ts +++ b/src/commands/sync.ts @@ -10,7 +10,7 @@ import type { Command, Usage } from "../services/command/command.js"; import { config } from "../services/config/config.js"; import { Changes } from "../services/filesync/changes.js"; import { YarnNotFoundError } from "../services/filesync/error.js"; -import { FileSync } from "../services/filesync/filesync.js"; +import { ConflictPreferenceArg, FileSync } from "../services/filesync/filesync.js"; import { notify } from "../services/output/notify.js"; import { reportErrorAndExit } from "../services/output/report.js"; import { sprint } from "../services/output/sprint.js"; @@ -29,8 +29,10 @@ export const usage: Usage = () => sprint` DIRECTORY The directory to sync files to (default: ".") {bold FLAGS} - -a, --app= The Gadget application to sync files to - --force Sync regardless of local file state + -a, --app= The Gadget application to sync files to + --prefer= Conflict resolution strategy + --once Sync once and exit + --force Sync regardless of local file state {bold DESCRIPTION} Sync allows you to synchronize your Gadget application's source @@ -91,6 +93,8 @@ export const usage: Usage = () => sprint` export const args = { "--app": { type: AppArg, alias: "-a" }, "--force": Boolean, + "--once": Boolean, + "--prefer": ConflictPreferenceArg, "--file-push-delay": { type: Number, default: ms("100ms") }, "--file-watch-debounce": { type: Number, default: ms("300ms") }, "--file-watch-poll-interval": { type: Number, default: ms("3s") }, @@ -109,7 +113,12 @@ export const command: Command = async (ctx) => { force: ctx.args["--force"], }); - await filesync.sync(); + await filesync.sync({ preference: ctx.args["--prefer"] }); + + if (ctx.args["--once"]) { + ctx.log.println("Done!"); + return; + } if (!which.sync("yarn", { nothrow: true })) { throw new YarnNotFoundError(); diff --git a/src/services/filesync/filesync.ts b/src/services/filesync/filesync.ts index 76fd6df77..21be4f8c5 100644 --- a/src/services/filesync/filesync.ts +++ b/src/services/filesync/filesync.ts @@ -329,7 +329,7 @@ export class FileSync { * local changes or keep Gadget's changes. * - This function will not return until the filesystem is in sync. */ - async sync({ attempt = 0 }: { attempt?: number } = {}): Promise { + async sync({ attempt = 0, preference }: { attempt?: number; preference?: ConflictPreference } = {}): Promise { if (attempt > 10) { throw new TooManySyncAttemptsError(attempt); } @@ -355,15 +355,18 @@ export class FileSync { const conflicts = getConflicts({ localChanges, gadgetChanges }); if (conflicts.size > 0) { this.log.debug("conflicts detected", { conflicts }); - printConflicts({ - message: sprint`{bold You have conflicting changes with Gadget}`, - conflicts, - }); - const preference = await select({ - message: "How would you like to resolve these conflicts?", - choices: Object.values(ConflictPreference), - }); + if (!preference) { + printConflicts({ + message: sprint`{bold You have conflicting changes with Gadget}`, + conflicts, + }); + + preference = await select({ + message: "How would you like to resolve these conflicts?", + choices: Object.values(ConflictPreference), + }); + } switch (preference) { case ConflictPreference.CANCEL: { @@ -392,7 +395,7 @@ export class FileSync { } // recursively call this function until we're in sync - return this.sync({ attempt: ++attempt }); + return this.sync({ attempt: ++attempt, preference }); } private async _getChangesFromGadget({ @@ -679,3 +682,19 @@ export const ConflictPreference = Object.freeze({ LOCAL: "Keep my conflicting changes", GADGET: "Keep Gadget's conflicting changes", }); + +export type ConflictPreference = (typeof ConflictPreference)[keyof typeof ConflictPreference]; + +export const ConflictPreferenceArg = (value: string, name: string): ConflictPreference => { + if (["local", "gadget"].includes(value)) { + return ConflictPreference[value.toUpperCase() as keyof typeof ConflictPreference]; + } + + throw new ArgError(sprint` + ${name} must be {bold local} or {bold gadget} + + {bold EXAMPLES:} + ${name} local + ${name} gadget + `); +};