diff --git a/packages/b2c-cli/src/commands/code/deploy.ts b/packages/b2c-cli/src/commands/code/deploy.ts index 50fb992e..bb21d54f 100644 --- a/packages/b2c-cli/src/commands/code/deploy.ts +++ b/packages/b2c-cli/src/commands/code/deploy.ts @@ -56,11 +56,33 @@ export default class CodeDeploy extends CartridgeCommand { async run(): Promise { this.requireWebDavCredentials(); - this.requireOAuthCredentials(); const hostname = this.resolvedConfig.values.hostname!; let version = this.resolvedConfig.values.codeVersion; + // OAuth is only required if: + // 1. No code version specified (need to auto-discover via OCAPI) + // 2. --reload flag is set (need to call OCAPI to reload) + const needsOAuth = !version || this.flags.reload; + if (needsOAuth && !this.hasOAuthCredentials()) { + const reason = version + ? t( + 'commands.code.deploy.oauthRequiredForReload', + 'The --reload flag requires OAuth credentials to reload the code version via OCAPI.', + ) + : t( + 'commands.code.deploy.oauthRequiredForDiscovery', + 'No code version specified. OAuth credentials are required to auto-discover the active code version.', + ); + this.error( + t( + 'commands.code.deploy.oauthRequired', + '{{reason}}\n\nProvide --code-version to use basic auth only, or configure OAuth credentials.\nSee: https://salesforcecommercecloud.github.io/b2c-developer-tooling/guide/configuration.html', + {reason}, + ), + ); + } + // If no code version specified, discover the active one if (!version) { this.warn( diff --git a/packages/b2c-cli/src/commands/code/watch.ts b/packages/b2c-cli/src/commands/code/watch.ts index 7ee4f8be..2c154059 100644 --- a/packages/b2c-cli/src/commands/code/watch.ts +++ b/packages/b2c-cli/src/commands/code/watch.ts @@ -33,11 +33,20 @@ export default class CodeWatch extends CartridgeCommand { async run(): Promise { this.requireWebDavCredentials(); - this.requireOAuthCredentials(); const hostname = this.resolvedConfig.values.hostname!; const version = this.resolvedConfig.values.codeVersion; + // OAuth is only required if no code version specified (need to auto-discover via OCAPI) + if (!version && !this.hasOAuthCredentials()) { + this.error( + t( + 'commands.code.watch.oauthRequired', + 'No code version specified. OAuth credentials are required to auto-discover the active code version.\n\nProvide --code-version to use basic auth only, or configure OAuth credentials.\nSee: https://salesforcecommercecloud.github.io/b2c-developer-tooling/guide/configuration.html', + ), + ); + } + this.log(t('commands.code.watch.starting', 'Starting watcher for {{path}}', {path: this.cartridgePath})); this.log(t('commands.code.watch.target', 'Target: {{hostname}}', {hostname})); if (version) { diff --git a/packages/b2c-cli/test/commands/code/deploy.test.ts b/packages/b2c-cli/test/commands/code/deploy.test.ts index 84776b17..0c7ffe06 100644 --- a/packages/b2c-cli/test/commands/code/deploy.test.ts +++ b/packages/b2c-cli/test/commands/code/deploy.test.ts @@ -24,7 +24,7 @@ describe('code deploy', () => { function stubCommon(command: any) { const instance = {config: {hostname: 'example.com', codeVersion: 'v1'}}; sinon.stub(command, 'requireWebDavCredentials').returns(void 0); - sinon.stub(command, 'requireOAuthCredentials').returns(void 0); + sinon.stub(command, 'hasOAuthCredentials').returns(true); sinon.stub(command, 'log').returns(void 0); sinon.stub(command, 'warn').returns(void 0); sinon.stub(command, 'resolvedConfig').get(() => ({values: {hostname: 'example.com', codeVersion: 'v1'}})); @@ -113,11 +113,57 @@ describe('code deploy', () => { expect(result.reloaded).to.equal(false); }); + it('errors when no code version and no OAuth credentials', async () => { + const command: any = await createCommand({}, {cartridgePath: '.'}); + + sinon.stub(command, 'requireWebDavCredentials').returns(void 0); + sinon.stub(command, 'hasOAuthCredentials').returns(false); + sinon.stub(command, 'log').returns(void 0); + sinon.stub(command, 'warn').returns(void 0); + sinon.stub(command, 'resolvedConfig').get(() => ({values: {hostname: 'example.com', codeVersion: undefined}})); + + const errorStub = sinon.stub(command, 'error').throws(new Error('OAuth required')); + + try { + await command.run(); + expect.fail('Should have thrown'); + } catch { + // expected + } + + expect(errorStub.calledOnce).to.equal(true); + const errorMessage = errorStub.firstCall.args[0]; + expect(errorMessage).to.include('auto-discover'); + }); + + it('errors when --reload flag set but no OAuth credentials', async () => { + const command: any = await createCommand({reload: true}, {cartridgePath: '.'}); + + sinon.stub(command, 'requireWebDavCredentials').returns(void 0); + sinon.stub(command, 'hasOAuthCredentials').returns(false); + sinon.stub(command, 'log').returns(void 0); + sinon.stub(command, 'warn').returns(void 0); + sinon.stub(command, 'resolvedConfig').get(() => ({values: {hostname: 'example.com', codeVersion: 'v1'}})); + + const errorStub = sinon.stub(command, 'error').throws(new Error('OAuth required')); + + try { + await command.run(); + expect.fail('Should have thrown'); + } catch { + // expected + } + + expect(errorStub.calledOnce).to.equal(true); + const errorMessage = errorStub.firstCall.args[0]; + expect(errorMessage).to.include('reload'); + }); + it('uses active code version when resolvedConfig is missing codeVersion', async () => { const command: any = await createCommand({}, {cartridgePath: '.'}); sinon.stub(command, 'requireWebDavCredentials').returns(void 0); - sinon.stub(command, 'requireOAuthCredentials').returns(void 0); + sinon.stub(command, 'hasOAuthCredentials').returns(true); sinon.stub(command, 'log').returns(void 0); sinon.stub(command, 'warn').returns(void 0);