-
Notifications
You must be signed in to change notification settings - Fork 129
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Themes][Bugfix] - Fix unpublished themes being marked as development themes #3798
Conversation
Thanks for your contribution! Depending on what you are working on, you may want to request a review from a Shopify team:
|
800657b
to
a99b2f8
Compare
@@ -149,22 +151,24 @@ export default class Push extends ThemeCommand { | |||
return | |||
} | |||
|
|||
const developmentThemeManager = new DevelopmentThemeManager(adminSession) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DevelopmentThemeManager
marks all themes as development - any theme created via the push command will be marked as [yours].
To address this, I've created a ThemeManager
and renamed the base class to BaseThemeManager
Take the code tour
{
"$schema": "https://aka.ms/codetour-schema",
"title": "Unpublished Themes",
"steps": [
{
"file": "src/cli/commands/theme/push.ts",
"description": "Here, we always use the developmentThemeManager when the `-u` flag is passed\n\nNote that the -u flag is required to create a new theme (-d and -t) specify filters for existing themes\n\nSide side note - this is an indication of some brittle behaviour, but `-t` actually specifies the name of the theme when the `-u` flag is provided. Pretty weird imo. Since the goal is parity first, I won't change any of this for the moment.",
"line": 160
},
{
"file": "../cli-kit/src/public/node/themes/theme-manager.ts",
"description": "We were using the create method to create a UNPUBLISHED theme, but this callback was getting triggered in DevelopmentThemeManager",
"line": 48
},
{
"file": "src/cli/utilities/development-theme-manager.ts",
"description": "this is the reason why all themes created via the push command are marked as the development theme",
"line": 29
}
],
"ref": "79854e6cc3e591d252a90e2b2071e80299025adc"
}
Coverage report
Test suite run success1633 tests passing in 763 suites. Report generated by 🧪jest coverage report action from 3ecdbcd |
75c2649
to
5ffd438
Compare
Differences in type declarationsWe 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:
New type declarationspackages/cli-kit/dist/public/node/themes/base-theme-manager.d.tsimport { AdminSession } from '@shopify/cli-kit/node/session';
import { Theme } from '@shopify/cli-kit/node/themes/types';
import { Role } from '@shopify/cli-kit/node/themes/utils';
export declare abstract class BaseThemeManager {
protected adminSession: AdminSession;
protected themeId: string | undefined;
protected abstract setTheme(themeId: string): void;
protected abstract removeTheme(): void;
protected abstract context: string;
constructor(adminSession: AdminSession);
findOrCreate(): Promise<Theme>;
fetch(): Promise<Theme | undefined>;
create(themeRole?: Role, themeName?: string): Promise<Theme>;
}
Existing type declarationsWe found no diffs with existing type declarations |
746e189
to
adf027e
Compare
|
||
protected setTheme(themeId: string): void { | ||
this.themeId = themeId | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should I be storing this or doing anything with this?
Currently this class just allows us to call create
WITHOUT setting the development theme, so there's no immediate use case that's popping out at me
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could also no-op this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The CLI doesn't need to store references to unpublished themes; it only saves a reference to the development theme and another to the host theme.
I wonder if the UnpublishedThemeManager
abstraction is the best one to support the push
command because it maintains state, leading readers to think that the state matters for unpublished themes. ThemeManager
does provide some convenience for creating themes, but I believe a better path would involve extracting those features from ThemeManager
.
adf027e
to
b5c0179
Compare
if (!flags.stable) { | ||
const {live, development, unpublished, path, nodelete, theme, publish, json, force, ignore, only} = flags |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've moved this logic into createOrSelectTheme below
b5c0179
to
8e21444
Compare
): Promise<Theme | undefined> { | ||
const {live, development, unpublished, theme} = flags | ||
|
||
if (development) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Decision / Diverging from Ruby
When multiple flags are specified for the theme role, create a single theme with the least risky / invasive role assigned to it
Development
->Unpublished
->Live
Rationale:
The logic in the Ruby implementation is pretty smelly.
shopify theme push -u -d --stable
creates two themes, but was only pushing the files to the unpublished theme
[unpublished]
[development][yours]
<- this theme is empty
There are some more weird issues here that I would at least like to understand / document even if we don't end up changing things
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question: Do we even want to allow for creation of a development theme with theme push
? @karreiro
The --stable
flag creates a new development theme when there is none that already exist but it looks like that's coming from the TS wrapper. May be off base here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it's related to the Ruby implementation, as we may notice here. The Ruby implementation adopts similar reasoning with the theme ID taking precedence.
The creation of the extra theme appears to be related to the development theme creation extracted to TypeScript via the --development-theme-id
flag. Thus, this seems to be more like a problem in the TS <-> Ruby interoperability than on how each implementation selects the theme.
Question: Do we even want to allow for creation of a development theme with theme push? @karreiro
Yes, keeping backward compatibility in mind, it's a good idea to create a development theme.
8e21444
to
8a7409e
Compare
49bfcc0
to
a6d51de
Compare
a6d51de
to
d22f80f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the detailed information. It's worth getting a second opinion as I'm not as familiar with the push logic, but overall it seems to make sense 😁
const themeManager = new UnpublishedThemeManager(adminSession) | ||
return themeManager.create(UNPUBLISHED_THEME_ROLE, themeName) | ||
} else { | ||
if (live && !flags['allow-live'] && !(await confirmPushToLiveTheme(adminSession.storeFqdn))) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
previously we had to check selected_theme.role === 'live'
, are we safe to only rely on the flag being provided? does live
get auto populated if not provided?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for raising this!
I'm going to move this check to us the role again - this will cover cases where the live theme ID is provided via the theme
flag.
Also going to add a test for this!
packages/theme/src/cli/utilities/unpublished-theme-manager.test.ts
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should these also have a test for the [yours]
logic, or is it outside of the scope of this class?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
imo, that falls outside the scope of this class since [yours] is detected by the theme selector
Ideally, these tests should only know that a development theme is created, but I'm going to add assertions on which ThemeManager
is used to provide some extra clarity (don't love this, but I think this balances effort/reward)
Maybe time for some integration tests? wdyt?
09fcecb
to
e715ea9
Compare
e715ea9
to
3ecdbcd
Compare
@@ -24,7 +24,7 @@ function themeLocalStorage() { | |||
function developmentThemeLocalStorage() { | |||
if (!_developmentThemeLocalStorageInstance) { | |||
_developmentThemeLocalStorageInstance = new LocalStorage<DevelopmentThemeLocalStorageSchema>({ | |||
projectName: 'shopify-cli-development-theme-conf', | |||
projectName: 'shopify-cli-development-theme-config', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
flushes local storage key
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you, @jamesmengo! I've left only some minor comments not blocking to merge this PR. Great stuff!
describe('createOrSelectTheme', async () => { | ||
beforeEach(() => { | ||
vi.mocked(DevelopmentThemeManager.prototype.findOrCreate).mockResolvedValue( | ||
buildTheme({id: 1, name: 'Theme', role: DEVELOPMENT_THEME_ROLE})!, | ||
) | ||
vi.mocked(UnpublishedThemeManager.prototype.create).mockResolvedValue( | ||
buildTheme({id: 2, name: 'Unpublished Theme', role: UNPUBLISHED_THEME_ROLE})!, | ||
) | ||
vi.mocked(findOrSelectTheme).mockImplementation( | ||
async (_session: AdminSession, options: {header?: string; filter: FilterProps}) => { | ||
if (options.filter.live) { | ||
return buildTheme({id: 3, name: 'Live Theme', role: LIVE_THEME_ROLE})! | ||
} else if (options.filter.theme) { | ||
return buildTheme({id: 4, name: options.filter.theme, role: DEVELOPMENT_THEME_ROLE})! | ||
} else { | ||
return buildTheme({id: 5, name: 'Theme', role: DEVELOPMENT_THEME_ROLE})! | ||
} | ||
}, | ||
) | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(optional) Personally, I'd move these mocks to the // Given
sections. Alternatively, we could mock at the local-storage.ts
/themes/api.ts
level.
|
||
protected setTheme(themeId: string): void { | ||
this.themeId = themeId | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The CLI doesn't need to store references to unpublished themes; it only saves a reference to the development theme and another to the host theme.
I wonder if the UnpublishedThemeManager
abstraction is the best one to support the push
command because it maintains state, leading readers to think that the state matters for unpublished themes. ThemeManager
does provide some convenience for creating themes, but I believe a better path would involve extracting those features from ThemeManager
.
): Promise<Theme | undefined> { | ||
const {live, development, unpublished, theme} = flags | ||
|
||
if (development) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it's related to the Ruby implementation, as we may notice here. The Ruby implementation adopts similar reasoning with the theme ID taking precedence.
The creation of the extra theme appears to be related to the development theme creation extracted to TypeScript via the --development-theme-id
flag. Thus, this seems to be more like a problem in the TS <-> Ruby interoperability than on how each implementation selects the theme.
Question: Do we even want to allow for creation of a development theme with theme push? @karreiro
Yes, keeping backward compatibility in mind, it's a good idea to create a development theme.
(proceeding with the merge, so partners can benefit from this fix asap) |
WHY are these changes introduced?
shopify theme dev
is losing track of the development theme #3715I left some explanations about why this is happening in the diff view
WHAT is this pull request doing?
1) Prevents unpublished themes from being marked as
[unpublished][yours]
when creating a theme viashopify theme push -u
-d
[development][yours]
[development][yours]
-u
[unpublished][yours]
[unpublished]
-u -d
[unpublished][yours]
[unpublished]
2) Flushes the local storage for development themes
How to test your changes?
Change 1
pnpm shopify theme push -u --path=<PATH>
pnpm shopify theme list
should show your new theme with[unpublished]
pnpm shopify theme push -u -d --path=<PATH>
Change 2
pnpm shopify theme list
[yours]
should be emptyBEFORE
AFTER
Post-release steps
dev
shopify theme delete
and delete the existing[unpublished][yours]
theme if it existsMeasuring impact
How do we know this change was effective? Please choose one:
Checklist
dev
ordeploy
have been reflected in the internal flowchart.