diff --git a/fly.js b/fly.js index 2a35da9..2140951 100755 --- a/fly.js +++ b/fly.js @@ -364,8 +364,9 @@ GDF.extend(class extends GDF { } try { - console.log(`${chalk.bold.green('execute'.padStart(11))} shopify app env show --config ${configFile}`) - const stdout = execSync('shopify app env show', { encoding: 'utf8' }) + const cmd = `shopify app env show --config ${configFile}` + console.log(`${chalk.bold.green('execute'.padStart(11))} ${cmd}`) + const stdout = execSync(cmd, { encoding: 'utf8' }) for (const match of stdout.matchAll(/^\s*(\w+)=(.*)/mg)) { if (match[1] === 'SHOPIFY_API_SECRET') { console.log(`${chalk.bold.green('execute'.padStart(11))} flyctl secrets set SHOPIFY_API_SECRET`) @@ -391,8 +392,20 @@ GDF.extend(class extends GDF { if (original !== config) { console.log(`${chalk.bold.green('update'.padStart(11, ' '))} ${configFile}`) fs.writeFileSync(configFile, config) - console.log(`${chalk.bold.green('execute'.padStart(11))} shopify app deploy --force --config ${configFile}`) - execSync(`shopify app deploy --force --config ${configFile}`, { stdio: 'inherit' }) + // Shopify CLI 3.90 added --allow-updates; 4.0 removed --force + let flag = '--allow-updates' + try { + const help = execSync('shopify app deploy --help', { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }) + if (!help.includes('--allow-updates')) flag = '--force' + } catch { } + + const cmd = `shopify app deploy ${flag} --config ${configFile}` + console.log(`${chalk.bold.green('execute'.padStart(11))} ${cmd}`) + try { + execSync(cmd, { stdio: 'inherit' }) + } catch { + console.log(`${chalk.bold.yellow('warn'.padStart(11))} ${chalk.bold.blue(cmd)} failed; run it yourself to update your app's URLs in Shopify.`) + } } } diff --git a/test/test.js b/test/test.js index 00b3925..64c5682 100644 --- a/test/test.js +++ b/test/test.js @@ -133,3 +133,90 @@ for (const group of fs.readdirSync('test', { withFileTypes: true })) { }) } } + +describe('fly: shopify', function() { + const savedPath = process.env.PATH + const savedCwd = process.cwd() + let workdir, bindir, log + + // write an executable stub that logs its arguments + function stub(name, script = '') { + const file = path.join(bindir, name) + fs.writeFileSync(file, `#!/bin/sh\necho "${name} $*" >> "${log}"\n${script}`) + fs.chmodSync(file, 0o755) + } + + beforeEach(function() { + workdir = fs.mkdtempSync(path.join(os.tmpdir(), 'shopify-')) + bindir = fs.mkdtempSync(path.join(os.tmpdir(), 'shopify-bin-')) + log = path.join(bindir, 'log') + fs.writeFileSync(log, '') + + fs.writeFileSync(path.join(workdir, 'package.json'), JSON.stringify({ name: 'shopify-app', scripts: { start: 'node index.js' } })) + fs.writeFileSync(path.join(workdir, 'fly.toml'), 'app = "shopify-test"\n') + fs.writeFileSync(path.join(workdir, 'shopify.app.toml'), [ + 'client_id = "abc123"', + 'application_url = "https://example.trycloudflare.com"', + '', + '[auth]', + 'redirect_urls = ["https://example.trycloudflare.com/auth/callback"]', + '' + ].join('\n')) + + stub('flyctl', 'case "$*" in *" list "*) echo "[]";; esac\n') + + // fly.js looks for shopify.app*.toml in the current directory + process.chdir(workdir) + process.env.PATH = [bindir, path.dirname(process.execPath), '/usr/bin', '/bin'].join(path.delimiter) + }) + + afterEach(function() { + process.chdir(savedCwd) + process.env.PATH = savedPath + fs.rmSync(workdir, { recursive: true }) + fs.rmSync(bindir, { recursive: true }) + }) + + it('deploys with --allow-updates', async function() { + // Shopify CLI 4.x rejects --force + stub('shopify', 'case "$*" in *--help*) echo "--allow-updates";; *--force*) exit 2;; esac\n') + + await new GDF().run(workdir, { ...defaults, force: true }) + + const calls = fs.readFileSync(log, 'utf-8') + expect(calls).to.include('shopify app env show --config shopify.app.toml') + expect(calls).to.include('shopify app deploy --allow-updates --config shopify.app.toml') + expect(calls).not.to.include('--force') + + const config = fs.readFileSync(path.join(workdir, 'shopify.app.toml'), 'utf-8') + expect(config).to.include('application_url = "https://shopify-test.fly.dev"') + expect(config).to.include('"https://shopify-test.fly.dev/auth/callback"') + }) + + it('deploys with --force on Shopify CLI before 3.90', async function() { + stub('shopify', 'case "$*" in *--help*) echo "--force";; *--allow-updates*) exit 2;; esac\n') + + await new GDF().run(workdir, { ...defaults, force: true }) + + const calls = fs.readFileSync(log, 'utf-8') + expect(calls).to.include('shopify app deploy --force --config shopify.app.toml') + expect(calls).not.to.include('deploy --allow-updates') + }) + + it('continues when shopify app deploy fails', async function() { + stub('shopify', 'exit 1\n') + + await new GDF().run(workdir, { ...defaults, force: true }) + + const config = fs.readFileSync(path.join(workdir, 'shopify.app.toml'), 'utf-8') + expect(config).to.include('application_url = "https://shopify-test.fly.dev"') + expect(fs.existsSync(path.join(workdir, 'Dockerfile'))).to.equal(true) + }) + + it('continues when the shopify CLI is not installed', async function() { + await new GDF().run(workdir, { ...defaults, force: true }) + + const config = fs.readFileSync(path.join(workdir, 'shopify.app.toml'), 'utf-8') + expect(config).to.include('application_url = "https://shopify-test.fly.dev"') + }) +})