Skip to content

Conversation

@gonzaloriestra
Copy link
Contributor

@gonzaloriestra gonzaloriestra commented May 29, 2023

WHY are these changes introduced?

Fixes #1843
Fixes #1784
Fixes #1554

The CLI currently restarts the theme dev command every ~2 hours to refresh the session, but this is not working very well and it's very annoying for the users because they can lose some job.

WHAT is this pull request doing?

Instead of passing the tokens as environment variables to the CLI2 process that runs the server, I let the CLI2 to get them from the CLI2 cache and update that file from the CLI3 every ~2 hours. That way, there's no need to stop the server.

How to test your changes?

  • Change the ThemeRefreshTimeoutInMs to 1 min (60*1000) in the Dev command
  • p shopify thme dev --path your-theme --verbose
  • check that it refreshes the tokens every minute (cat ~/.cache/shopify/.db.pstore) and that everything keeps working

Measuring impact

How do we know this change was effective? Please choose one:

  • n/a - this doesn't need measurement, e.g. a linting rule or a bug-fix
  • Existing analytics will cater for this addition
  • PR includes analytics changes to measure impact

Checklist

  • I've considered possible cross-platform impacts (Mac, Linux, Windows)
  • I've considered possible documentation changes
  • I've made sure that any changes to dev or deploy have been reflected in the internal flowchart.

@github-actions
Copy link
Contributor

Thanks for your contribution!

Depending on what you are working on, you may want to request a review from a Shopify team:

  • Themes: @shopify/theme-code-tools
  • UI extensions: @shopify/ui-extensions-cli
    • Checkout UI extensions: @shopify/checkout-ui-extensions-api-stewardship
  • Hydrogen: @shopify/hydrogen
  • Other: @shopify/cli-foundations

@github-actions
Copy link
Contributor

github-actions bot commented May 29, 2023

Coverage report

St.
Category Percentage Covered / Total
🟡 Statements 70.89% 4550/6418
🟡 Branches
67.97% (+0.01% 🔼)
2084/3066
🟡 Functions 68.21% 1193/1749
🟡 Lines 72.37% 4347/6007

Test suite run success

1070 tests passing in 564 suites.

Report generated by 🧪jest coverage report action from 5343f80


// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.execute(adminSession, flags.password, command, controller, false)
// eslint-disable-next-line @typescript-eslint/no-misused-promises
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've been reading about this, but I'm not sure if there's a better way to do this (although it works)...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need to make this function async? You're not using the return value of the promise so wouldn't this work as well?

setInterval(() => {
      outputDebug('Refreshing theme session tokens...')
      refreshTokens(store, flags.password)
    }, this.ThemeRefreshTimeoutInMs)

This would give you @typescript-eslint/no-floating-promises, but maybe it's clearer?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in fact, we shouldn't use awaits inside setIntervals, it won't work on windows. What @matteodepalo proposed will work 👌

// Contains token and store to pass to CLI 2.0, which will be set as environment variables
adminSession?: AdminSession
// Contains store to pass to CLI 2.0 as environment variable
store?: string
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this is a breaking change, but nobody else is using this command 🤷

@gonzaloriestra gonzaloriestra marked this pull request as ready for review May 29, 2023 14:26
@gonzaloriestra gonzaloriestra requested review from a team, amcaplan and matteodepalo and removed request for a team May 29, 2023 14:26
}
async function refreshTokens(store: string, password: string | undefined) {
const adminSession = await ensureAuthenticatedThemes(store, password, [], true)
const storefrontToken = await ensureAuthenticatedStorefront([], password, true)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to forceRefresh the second token. The first one already refreshes the identity token, this second only needs to exchange it.
If you forceRefresh again you are discarding the identity token from the line above.

Copy link
Contributor

@karreiro karreiro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, @gonzaloriestra! Amazing stuff 🔥🚀

This approach is quite clever and it's working well with the standard authentication method (based on the --store flag). The only minor issue I've noticed is that I could not authenticate via Theme Access app.

Other than that minor issue, everything else is excellent 🚀

@alvaro-shopify
Copy link
Contributor

Thank you, @gonzaloriestra! Amazing stuff 🔥🚀

This approach is quite clever and it's working well with the standard authentication method (based on the --store flag). The only minor issue I've noticed is that,I could not authenticate via Theme Access app.

Other than that minor issue, everything else is excellent 🚀

I fixed the problem. 🎩 it and it's working. Please, could you check it again?

@github-actions
Copy link
Contributor

Differences in type declarations

We detected differences in the type declarations generated by Typescript for this branch compared to the baseline ('main' branch). Please, review them to ensure they are backward-compatible. Here are some important things to keep in mind:

  • Some seemingly private modules might be re-exported through public modules.
  • If the branch is behind main you might see odd diffs, rebase main into this branch.

New type declarations

We found no new type declarations in this PR

Existing type declarations

packages/cli-kit/dist/public/common/version.d.ts
@@ -1 +1 @@
-export declare const CLI_KIT_VERSION = "3.46.0";
\ No newline at end of file
+export declare const CLI_KIT_VERSION = "3.46.1";
\ No newline at end of file
packages/cli-kit/dist/public/node/metadata.d.ts
@@ -27,7 +27,7 @@ export type SensitiveSchema<T> = T extends RuntimeMetadataManager<infer _TPublic
 export declare function createRuntimeMetadataContainer<TPublic extends AnyJson, TSensitive extends AnyJson = {
     [key: string]: never;
 }>(): RuntimeMetadataManager<TPublic, TSensitive>;
-type CmdFieldsFromMonorail = PickByPrefix<MonorailEventPublic, 'cmd_all_'> & PickByPrefix<MonorailEventPublic, 'cmd_app_'> & PickByPrefix<MonorailEventPublic, 'cmd_create_app_'>;
+type CmdFieldsFromMonorail = PickByPrefix<MonorailEventPublic, 'cmd_all_'> & PickByPrefix<MonorailEventPublic, 'cmd_app_'>;
 declare const coreData: RuntimeMetadataManager<CmdFieldsFromMonorail, {
     commandStartOptions: {
         startTime: number;
packages/cli-kit/dist/public/node/monorail.d.ts
@@ -2,7 +2,7 @@ import { JsonMap } from '../../private/common/json.js';
 import { DeepRequired } from '../common/ts/deep-required.js';
 export { DeepRequired };
 type Optional<T> = T | null;
-export declare const MONORAIL_COMMAND_TOPIC: "app_cli3_command/1.4";
+export declare const MONORAIL_COMMAND_TOPIC: "app_cli3_command/1.3";
 export interface Schemas {
     [MONORAIL_COMMAND_TOPIC]: {
         sensitive: {
@@ -51,7 +51,6 @@ export interface Schemas {
             cmd_dev_tunnel_type?: Optional<string>;
             cmd_dev_tunnel_custom_hash?: Optional<string>;
             cmd_dev_urls_updated?: Optional<boolean>;
-            cmd_create_app_template?: Optional<string>;
             app_extensions_any?: Optional<boolean>;
             app_extensions_breakdown?: Optional<string>;
             app_extensions_count?: Optional<number>;
packages/cli-kit/dist/public/node/ruby.d.ts
@@ -1,11 +1,11 @@
 /// <reference types="node" resolution-mode="require"/>
 import { AbortSignal } from './abort.js';
-import { AdminSession } from '../../public/node/session.js';
 import { Writable } from 'stream';
 export declare const RubyCLIVersion = "2.35.0";
 export declare const MinWdmWindowsVersion = "0.1.0";
 interface ExecCLI2Options {
-    adminSession?: AdminSession;
+    store?: string;
+    adminToken?: string;
     storefrontToken?: string;
     token?: string;
     directory?: string;
packages/cli-kit/dist/public/node/session.d.ts
@@ -21,9 +21,10 @@ export declare function ensureAuthenticatedPartners(scopes?: string[], _env?: No
  *
  * @param scopes - Optional array of extra scopes to authenticate with.
  * @param password - Optional password to use.
+ * @param forceRefresh - Optional flag to force a refresh of the token.
  * @returns The access token for the Storefront API.
  */
-export declare function ensureAuthenticatedStorefront(scopes?: string[], password?: string | undefined): Promise<string>;
+export declare function ensureAuthenticatedStorefront(scopes?: string[], password?: string | undefined, forceRefresh?: boolean): Promise<string>;
 /**
  * Ensure that we have a valid Admin session for the given store.
  *
packages/cli-kit/dist/private/node/session/schema.d.ts
@@ -9,13 +9,13 @@ declare const IdentityTokenSchema: zod.ZodObject<{
     scopes: zod.ZodArray<zod.ZodString, "many">;
 }, "strip", zod.ZodTypeAny, {
     accessToken: string;
+    scopes: string[];
     refreshToken: string;
     expiresAt: Date;
-    scopes: string[];
 }, {
     accessToken: string;
-    refreshToken: string;
     scopes: string[];
+    refreshToken: string;
     expiresAt?: unknown;
 }>;
 /**
@@ -27,8 +27,8 @@ declare const ApplicationTokenSchema: zod.ZodObject<{
     scopes: zod.ZodArray<zod.ZodString, "many">;
 }, "strip", zod.ZodTypeAny, {
     accessToken: string;
-    expiresAt: Date;
     scopes: string[];
+    expiresAt: Date;
 }, {
     accessToken: string;
     scopes: string[];
@@ -55,13 +55,13 @@ export declare const SessionSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{
         scopes: zod.ZodArray<zod.ZodString, "many">;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
+        scopes: string[];
         refreshToken: string;
         expiresAt: Date;
-        scopes: string[];
     }, {
         accessToken: string;
-        refreshToken: string;
         scopes: string[];
+        refreshToken: string;
         expiresAt?: unknown;
     }>;
     /**
@@ -74,8 +74,8 @@ export declare const SessionSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{
         scopes: zod.ZodArray<zod.ZodString, "many">;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
-        expiresAt: Date;
         scopes: string[];
+        expiresAt: Date;
     }, {
         accessToken: string;
         scopes: string[];
@@ -86,8 +86,8 @@ export declare const SessionSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{
         scopes: zod.ZodArray<zod.ZodString, "many">;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
-        expiresAt: Date;
         scopes: string[];
+        expiresAt: Date;
     }, {
         accessToken: string;
         scopes: string[];
@@ -98,8 +98,8 @@ export declare const SessionSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{
         scopes: zod.ZodArray<zod.ZodString, "many">;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
-        expiresAt: Date;
         scopes: string[];
+        expiresAt: Date;
     }, {
         accessToken: string;
         scopes: string[];
@@ -108,22 +108,22 @@ export declare const SessionSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{
 }, "strip", zod.ZodTypeAny, {
     identity: {
         accessToken: string;
+        scopes: string[];
         refreshToken: string;
         expiresAt: Date;
-        scopes: string[];
     };
     applications: {} & {
         [k: string]: {
             accessToken: string;
-            expiresAt: Date;
             scopes: string[];
+            expiresAt: Date;
         };
     };
 }, {
     identity: {
         accessToken: string;
-        refreshToken: string;
         scopes: string[];
+        refreshToken: string;
         expiresAt?: unknown;
     };
     applications: {} & {
@@ -146,13 +146,13 @@ export declare const SessionSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{
         scopes: zod.ZodArray<zod.ZodString, "many">;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
+        scopes: string[];
         refreshToken: string;
         expiresAt: Date;
-        scopes: string[];
     }, {
         accessToken: string;
-        refreshToken: string;
         scopes: string[];
+        refreshToken: string;
         expiresAt?: unknown;
     }>;
     /**
@@ -165,8 +165,8 @@ export declare const SessionSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{
         scopes: zod.ZodArray<zod.ZodString, "many">;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
-        expiresAt: Date;
         scopes: string[];
+        expiresAt: Date;
     }, {
         accessToken: string;
         scopes: string[];
@@ -177,8 +177,8 @@ export declare const SessionSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{
         scopes: zod.ZodArray<zod.ZodString, "many">;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
-        expiresAt: Date;
         scopes: string[];
+        expiresAt: Date;
     }, {
         accessToken: string;
         scopes: string[];
@@ -189,8 +189,8 @@ export declare const SessionSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{
         scopes: zod.ZodArray<zod.ZodString, "many">;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
-        expiresAt: Date;
         scopes: string[];
+        expiresAt: Date;
     }, {
         accessToken: string;
         scopes: string[];
@@ -199,22 +199,22 @@ export declare const SessionSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{
 }, "strip", zod.ZodTypeAny, {
     identity: {
         accessToken: string;
+        scopes: string[];
         refreshToken: string;
         expiresAt: Date;
-        scopes: string[];
     };
     applications: {} & {
         [k: string]: {
             accessToken: string;
-            expiresAt: Date;
             scopes: string[];
+            expiresAt: Date;
         };
     };
 }, {
     identity: {
         accessToken: string;
-        refreshToken: string;
         scopes: string[];
+        refreshToken: string;
         expiresAt?: unknown;
     };
     applications: {} & {
@@ -237,13 +237,13 @@ export declare const SessionSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{
         scopes: zod.ZodArray<zod.ZodString, "many">;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
+        scopes: string[];
         refreshToken: string;
         expiresAt: Date;
-        scopes: string[];
     }, {
         accessToken: string;
-        refreshToken: string;
         scopes: string[];
+        refreshToken: string;
         expiresAt?: unknown;
     }>;
     /**
@@ -256,8 +256,8 @@ export declare const SessionSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{
         scopes: zod.ZodArray<zod.ZodString, "many">;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
-        expiresAt: Date;
         scopes: string[];
+        expiresAt: Date;
     }, {
         accessToken: string;
         scopes: string[];
@@ -268,8 +268,8 @@ export declare const SessionSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{
         scopes: zod.ZodArray<zod.ZodString, "many">;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
-        expiresAt: Date;
         scopes: string[];
+        expiresAt: Date;
     }, {
         accessToken: string;
         scopes: string[];
@@ -280,8 +280,8 @@ export declare const SessionSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{
         scopes: zod.ZodArray<zod.ZodString, "many">;
     }, "strip", zod.ZodTypeAny, {
         accessToken: string;
-        expiresAt: Date;
         scopes: string[];
+        expiresAt: Date;
     }, {
         accessToken: string;
         scopes: string[];
@@ -290,22 +290,22 @@ export declare const SessionSchema: zod.ZodObject<{}, "strip", zod.ZodObject<{
 }, "strip", zod.ZodTypeAny, {
     identity: {
         accessToken: string;
+        scopes: string[];
         refreshToken: string;
         expiresAt: Date;
-        scopes: string[];
     };
     applications: {} & {
         [k: string]: {
             accessToken: string;
-            expiresAt: Date;
             scopes: string[];
+            expiresAt: Date;
         };
     };
 }, {
     identity: {
         accessToken: string;
-        refreshToken: string;
         scopes: string[];
+        refreshToken: string;
         expiresAt?: unknown;
     };
     applications: {} & {

Copy link
Contributor

@karreiro karreiro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, @alvaro-shopify! It works now — awesome stuff 🚀

@alvaro-shopify alvaro-shopify merged commit b1a8e33 into stable/3.46 May 31, 2023
@alvaro-shopify alvaro-shopify deleted the fix-theme-dev-auth branch May 31, 2023 11:26
@shopify-shipit shopify-shipit bot temporarily deployed to stable_3_46 June 1, 2023 11:35 Inactive
@gonzaloriestra gonzaloriestra mentioned this pull request Jun 9, 2023
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants