diff --git a/.github/actions/check_if_latest_release/action.yml b/.github/actions/check_if_latest_release/action.yml index 10006447bb..067976f3a5 100644 --- a/.github/actions/check_if_latest_release/action.yml +++ b/.github/actions/check_if_latest_release/action.yml @@ -17,10 +17,10 @@ runs: echo "Release event tag: $RELEASE_EVENT_TAG_NAME" if [ "$LATEST_TAG_NAME" == "$RELEASE_EVENT_TAG_NAME" ]; then - echo "We are releasing the latest tag. Continuing deployment workflow." - exit 0 + echo "We are releasing the latest tag." + echo "IS_LATEST_RELEASE=true" >> $GITHUB_ENV else - echo "We are not releasing the latest tag. Stopping deployment workflow." - exit 1 + echo "We are not releasing the latest tag." + echo "IS_LATEST_RELEASE=false" >> $GITHUB_ENV fi shell: bash \ No newline at end of file diff --git a/.github/workflows/deploy_latest_release.yml b/.github/workflows/deploy_latest_release.yml index 4628951b4c..e2d67ebe62 100644 --- a/.github/workflows/deploy_latest_release.yml +++ b/.github/workflows/deploy_latest_release.yml @@ -3,7 +3,7 @@ on: # For testing - to be removed push: branches: - - 'mrt-test-ci-deploy' + - 'deploy-demo-env-on-release' # Run this workflow when a new Github release is published release: types: [released] @@ -11,6 +11,8 @@ on: jobs: check-latest-release: runs-on: ubuntu-latest + outputs: + IS_LATEST_RELEASE: ${{ steps.checkRelease.outputs.IS_LATEST_RELEASE }} steps: - name: Checkout uses: actions/checkout@v4 @@ -19,10 +21,20 @@ jobs: uses: ./.github/actions/check_if_latest_release with: token: ${{ secrets.GITHUB_TOKEN }} + + - name: Update Github Outputs + id: checkRelease + run: |- + IS_LATEST_RELEASE=$(echo ${{ env.IS_LATEST_RELEASE }}) + echo "IS_LATEST_RELEASE=${{ env.IS_LATEST_RELEASE }}" >> "$GITHUB_OUTPUT" + if [ $IS_LATEST_RELEASE != "true" ]; then + echo "Deploy is not for the latest release. Stopping deployment" + fi deploy-bug-bounty: needs: check-latest-release runs-on: ubuntu-latest + if: needs.check-latest-release.outputs.IS_LATEST_RELEASE == 'true' steps: - name: Checkout uses: actions/checkout@v4 @@ -58,3 +70,43 @@ jobs: PROJECT: pwa-kit MESSAGE: "tag ${{ github.event.release.tag_name }}" FLAGS: --cloud-origin https://cloud-testing.mobify-staging.com -c ~/.mobify --wait + + deploy-demo-site: + needs: check-latest-release + runs-on: ubuntu-latest + if: needs.check-latest-release.outputs.IS_LATEST_RELEASE == 'true' + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup node + id: setup_node + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: "npm" + + - name: Install Monorepo Dependencies + run: |- + # Install node dependencies + node ./scripts/gtime.js monorepo_install npm ci + + - name: Generate Retail App Demo + uses: ./.github/actions/e2e_generate_app + with: + PROJECT_KEY: "retail-react-app-demo-site" + + - name: Create MRT credentials file + uses: "./.github/actions/create_mrt" + with: + mobify_user: ${{ secrets.MOBIFY_CLIENT_USER }} + mobify_api_key: ${{ secrets.MOBIFY_CLIENT_API_KEY }} + + - name: Push Bundle to MRT (Demo Site) + uses: "./.github/actions/push_to_mrt" + with: + CWD: "../generated-projects/retail-react-app-demo-site" + TARGET: production + PROJECT: scaffold-pwa + MESSAGE: "tag ${{ github.event.release.tag_name }}" + FLAGS: --wait diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 12813f5c15..3e945289d5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -101,6 +101,7 @@ jobs: mobify_user: ${{ secrets.MOBIFY_CLIENT_USER }} mobify_api_key: ${{ secrets.MOBIFY_CLIENT_API_KEY }} + # TODO - Do we still need this? - name: Push Bundle to MRT (Development) if: env.IS_NOT_FORK == 'true' && env.IS_MRT_NODE == 'true' && env.DEVELOP == 'true' uses: "./.github/actions/push_to_mrt" @@ -108,13 +109,6 @@ jobs: CWD: "./packages/template-retail-react-app" TARGET: staging - - name: Push Bundle to MRT (Production) - if: env.IS_NOT_FORK == 'true' && env.IS_MRT_NODE == 'true' && env.RELEASE == 'true' - uses: "./.github/actions/push_to_mrt" - with: - CWD: "./packages/template-retail-react-app" - TARGET: production - - name: Push Bundle to MRT (Commerce SDK React) if: env.IS_NOT_FORK == 'true' && env.IS_MRT_NODE == 'true' && env.DEVELOP == 'true' uses: "./.github/actions/push_to_mrt" diff --git a/e2e/config.js b/e2e/config.js index 14f9be37df..78aad54683 100644 --- a/e2e/config.js +++ b/e2e/config.js @@ -102,10 +102,12 @@ module.exports = { ], "retail-app-private-client": [], "retail-react-app-bug-bounty": [], + "retail-react-app-demo-site": [], }, PRESET: { "retail-app-private-client": "retail-react-app-private-slas-client", "retail-react-app-bug-bounty": "retail-react-app-bug-bounty", + "retail-react-app-demo-site": "retail-react-app-demo-private" }, EXPECTED_GENERATED_ARTIFACTS: { "retail-app-demo": [ diff --git a/e2e/scripts/generate-project.js b/e2e/scripts/generate-project.js index 8f10792bbb..6405a4942c 100644 --- a/e2e/scripts/generate-project.js +++ b/e2e/scripts/generate-project.js @@ -63,7 +63,8 @@ program 'retail-app-ext', 'retail-app-no-ext', 'retail-app-private-client', - 'retail-react-app-bug-bounty' + 'retail-react-app-bug-bounty', + 'retail-react-app-demo-site' ] if (!validKeys.includes(value)) { throw new Error('Invalid project key.') diff --git a/packages/pwa-kit-create-app/assets/bootstrap/js/config/default.js.hbs b/packages/pwa-kit-create-app/assets/bootstrap/js/config/default.js.hbs index 1ef907c78c..2e05c72921 100644 --- a/packages/pwa-kit-create-app/assets/bootstrap/js/config/default.js.hbs +++ b/packages/pwa-kit-create-app/assets/bootstrap/js/config/default.js.hbs @@ -10,17 +10,32 @@ module.exports = { app: { // Customize how your 'site' and 'locale' are displayed in the url. url: { + {{#if answers.project.demo.enableDemoSettings}} // Determine where the siteRef is located. Valid values include 'path|query_param|none'. Defaults to: 'none' - // site: 'none', + site: 'path', // Determine where the localeRef is located. Valid values include 'path|query_param|none'. Defaults to: 'none' - locale: 'none' + locale: 'path', // This boolean value dictates whether or not default site or locale values are shown in the url. Defaults to: false - // showDefaults: true + showDefaults: true, + {{else}} + // Determine where the siteRef is located. Valid values include 'path|query_param|none'. Defaults to: 'none' + site: 'none', + // Determine where the localeRef is located. Valid values include 'path|query_param|none'. Defaults to: 'none' + locale: 'none', + // This boolean value dictates whether or not default site or locale values are shown in the url. Defaults to: false + showDefaults: false, + {{/if}} + // This boolean value dictates whether the plus sign (+) is interpreted as space for query param string. Defaults to: false + interpretPlusSignAsSpace: false }, login: { passwordless: { // Enables or disables passwordless login for the site. Defaults to: false + {{#if answers.project.demo.enableDemoSettings}} + enabled: true, + {{else}} enabled: false, + {{/if}} // The callback URI, which can be an absolute URL (including third-party URIs) or a relative path set up by the developer. // Required in 'callback' mode; if missing, passwordless login defaults to 'sms' mode, which requires Marketing Cloud configuration. // If the env var `PASSWORDLESS_LOGIN_CALLBACK_URI` is set, it will override the config value. @@ -31,7 +46,11 @@ module.exports = { }, social: { // Enables or disables social login for the site. Defaults to: false + {{#if answers.project.demo.enableDemoSettings}} + enabled: true, + {{else}} enabled: false, + {{/if}} // The third-party identity providers supported by your app. The PWA Kit supports Google and Apple by default. // Additional IDPs will also need to be added to the IDP_CONFIG in the SocialLogin component. idps: ['google', 'apple'], @@ -51,9 +70,10 @@ module.exports = { // The default site for your app. This value will be used when a siteRef could not be determined from the url defaultSite: '{{answers.project.commerce.siteId}}', // Provide aliases for your sites. These will be used in place of your site id when generating paths throughout the application. - // siteAliases: { - // RefArch: 'us' - // }, + siteAliases: { + RefArch: 'us', + RefArchGlobal: 'global' + }, // The sites for your app, which is imported from sites.js sites, // Commerce api config diff --git a/packages/pwa-kit-create-app/assets/bootstrap/js/config/sites.js.hbs b/packages/pwa-kit-create-app/assets/bootstrap/js/config/sites.js.hbs index eea02f1045..87ff79047c 100644 --- a/packages/pwa-kit-create-app/assets/bootstrap/js/config/sites.js.hbs +++ b/packages/pwa-kit-create-app/assets/bootstrap/js/config/sites.js.hbs @@ -10,6 +10,59 @@ module.exports = [ { id: '{{answers.project.commerce.siteId}}', + {{#ifEquals answers.project.commerce.siteId "RefArchGlobal" }} + l10n: { + supportedCurrencies: ['USD', 'GBP', 'EUR', 'CNY', 'JPY'], + defaultCurrency: 'USD', + defaultLocale: 'en-US', + supportedLocales: [ + { + id: 'en-US', + preferredCurrency: 'USD' + }, + { + id: 'en-GB', + preferredCurrency: 'GBP' + }, + { + id: 'de-DE', + preferredCurrency: 'EUR' + }, + { + id: 'es-MX', + preferredCurrency: 'MXN' + }, + { + id: 'fr-FR', + preferredCurrency: 'EUR' + }, + { + id: 'it-IT', + preferredCurrency: 'EUR' + }, + { + id: 'ja-JP', + preferredCurrency: 'JPY' + }, + { + id: 'ko-KR', + preferredCurrency: 'KRW' + }, + { + id: 'pt-BR', + preferredCurrency: 'BRL' + }, + { + id: 'zh-CN', + preferredCurrency: 'CNY' + }, + { + id: 'zh-TW', + preferredCurrency: 'TWD' + } + ] + } + {{else}} l10n: { supportedCurrencies: ['USD'], defaultCurrency: 'USD', @@ -22,5 +75,6 @@ module.exports = [ } ] } + {{/ifEquals}} } ] diff --git a/packages/pwa-kit-create-app/assets/bootstrap/js/overrides/app/pages/home/index.jsx.hbs b/packages/pwa-kit-create-app/assets/bootstrap/js/overrides/app/pages/home/index.jsx.hbs index ee1854e2c5..c8a31056f1 100644 --- a/packages/pwa-kit-create-app/assets/bootstrap/js/overrides/app/pages/home/index.jsx.hbs +++ b/packages/pwa-kit-create-app/assets/bootstrap/js/overrides/app/pages/home/index.jsx.hbs @@ -82,7 +82,14 @@ const Home = () => { /> object.replaceAll('"', '\\"')) +Handlebars.registerHelper('ifEquals', function (arg1, arg2, options) { + return arg1 == arg2 ? options.fn(this) : options.inverse(this) +}) + // Validations const validPreset = (preset) => { return ALL_PRESET_NAMES.includes(preset) @@ -265,11 +269,45 @@ const PRESETS = [ ['project.commerce.shortCode']: 'kv7kzm78', ['project.commerce.isSlasPrivate']: false, ['project.einstein.clientId']: '1ea06c6e-c936-4324-bcf0-fada93f83bb1', - ['project.einstein.siteId']: 'aaij-MobileFirst' + ['project.einstein.siteId']: 'aaij-MobileFirst', + ['project.demo.enableDemoSettings']: false }, assets: ['translations'], private: false }, + { + id: 'retail-react-app-demo-private', + name: 'Retail React App Demo Private Client', + description: ` + Generates a project using the settings for a special B2C Commerce + instance that is used for demo purposes and sets it up with a private SLAS client. + + This has social and passwordless login enabled but requires a client secret to run. + `, + shortDescription: + 'The Retail app with demo Commerce Cloud instance and a private SLAS client', + templateSource: { + type: TEMPLATE_SOURCE_NPM, + id: '@salesforce/retail-react-app' + }, + questions: [...EXTENSIBILITY_QUESTIONS, ...RETAIL_REACT_APP_QUESTIONS], + answers: { + ['project.extend']: false, // Intentionally not an extensible project so that the correct logos appear on demo site + ['project.hybrid']: false, + ['project.name']: 'demo-storefront', + ['project.commerce.instanceUrl']: 'https://zzrf-001.dx.commercecloud.salesforce.com', + ['project.commerce.clientId']: '083859f2-5d93-4209-b999-a112266d63a0', + ['project.commerce.siteId']: 'RefArchGlobal', + ['project.commerce.organizationId']: 'f_ecom_zzrf_001', + ['project.commerce.shortCode']: 'kv7kzm78', + ['project.commerce.isSlasPrivate']: true, + ['project.einstein.clientId']: '1ea06c6e-c936-4324-bcf0-fada93f83bb1', + ['project.einstein.siteId']: 'aaij-MobileFirst', + ['project.demo.enableDemoSettings']: true // True only for presets deployed to demo environments like pwa-kit.mobify-storefront.com + }, + assets: ['translations'], + private: true + }, { id: 'retail-react-app-test-project', name: 'Retail React App Test Project', @@ -290,7 +328,8 @@ const PRESETS = [ ['project.commerce.shortCode']: 'kv7kzm78', ['project.commerce.isSlasPrivate']: false, ['project.einstein.clientId']: '1ea06c6e-c936-4324-bcf0-fada93f83bb1', - ['project.einstein.siteId']: 'aaij-MobileFirst' + ['project.einstein.siteId']: 'aaij-MobileFirst', + ['project.demo.enableDemoSettings']: false }, assets: ['translations'], private: true @@ -315,7 +354,8 @@ const PRESETS = [ ['project.commerce.shortCode']: 'kv7kzm78', ['project.commerce.isSlasPrivate']: true, ['project.einstein.clientId']: '1ea06c6e-c936-4324-bcf0-fada93f83bb1', - ['project.einstein.siteId']: 'aaij-MobileFirst' + ['project.einstein.siteId']: 'aaij-MobileFirst', + ['project.demo.enableDemoSettings']: false }, assets: ['translations'], private: true @@ -340,7 +380,8 @@ const PRESETS = [ ['project.commerce.shortCode']: 'staging-001', ['project.einstein.clientId']: '1ea06c6e-c936-4324-bcf0-fada93f83bb1', ['project.einstein.siteId']: 'aaij-MobileFirst', - ['project.commerce.isSlasPrivate']: true + ['project.commerce.isSlasPrivate']: true, + ['project.demo.enableDemoSettings']: false }, assets: ['translations'], private: true @@ -365,7 +406,8 @@ const PRESETS = [ ['project.commerce.shortCode']: 'xitgmcd3', ['project.einstein.clientId']: '1ea06c6e-c936-4324-bcf0-fada93f83bb1', ['project.einstein.siteId']: 'aaij-MobileFirst', - ['project.commerce.isSlasPrivate']: true + ['project.commerce.isSlasPrivate']: true, + ['project.demo.enableDemoSettings']: false }, assets: ['translations'], private: true @@ -390,7 +432,8 @@ const PRESETS = [ ['project.commerce.shortCode']: 'performance-001', ['project.einstein.clientId']: '1ea06c6e-c936-4324-bcf0-fada93f83bb1', ['project.einstein.siteId']: 'aaij-MobileFirst', - ['project.commerce.isSlasPrivate']: false + ['project.commerce.isSlasPrivate']: false, + ['project.demo.enableDemoSettings']: false }, assets: ['translations'], private: true @@ -809,6 +852,11 @@ const main = async (opts) => { // Add the preset to the context. context.preset = selectedPreset + // If using the preset, output the preset name + if (presetId) { + console.log(`Using preset "${selectedPreset.name}"`) + } + if (!OUTPUT_DIR_FLAG_ACTIVE) { outputDir = p.join(process.cwd(), selectedPreset.id) }