diff --git a/.circleci/config.yml b/.circleci/config.yml index a04959f52c8..5f7f618a03d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -56,62 +56,15 @@ jobs: - lib chromatic: <<: *defaults + parallelism: 10 steps: - checkout - attach_workspace: at: . - run: - name: Run chromatic on the pre-built official example - command: yarn chromatic --storybook-build-dir="built-storybooks/official-storybook" --exit-zero-on-changes --app-code="ab7m45tp9p" - - run: - name: Run chromatic on the pre-built angular example - command: yarn chromatic --storybook-build-dir="built-storybooks/angular-cli" --app-code="tl92yzsj6w" - - run: - name: Run chromatic on the pre-built cra-kitchen-sink example - command: yarn chromatic --storybook-build-dir="built-storybooks/cra-kitchen-sink" --app-code="tg55gajmdt" - - run: - name: Run chromatic on the pre-built cra-react15 example - command: yarn chromatic --storybook-build-dir="built-storybooks/cra-react15" --app-code="gxk7iqej3wt" - - run: - name: Run chromatic on the pre-built cra-ts-essentials example - command: yarn chromatic --storybook-build-dir="built-storybooks/cra-ts-essentials" --app-code="b311ypk6of" - - run: - name: Run chromatic on the pre-built cra-ts-kitchen-sink example - command: yarn chromatic --storybook-build-dir="built-storybooks/cra-ts-kitchen-sink" --app-code="19whyj1tlac" - - run: - name: Run chromatic on the pre-built dev-kits example - command: yarn chromatic --storybook-build-dir="built-storybooks/dev-kits" --app-code="7yykp9ifdxx" - - run: - name: Run chromatic on the pre-built ember-cli example - command: yarn chromatic --storybook-build-dir="built-storybooks/ember-cli" --app-code="19z23qxndju" - - run: - name: Run chromatic on the pre-built html-kitchen-sink example - command: yarn chromatic --storybook-build-dir="built-storybooks/html-kitchen-sink" --app-code="e8zolxoyg8o" - - run: - name: Run chromatic on the pre-built marko-cli example - command: yarn chromatic --storybook-build-dir="built-storybooks/marko-cli" --app-code="qaegx64axu" - - run: - name: Run chromatic on the pre-built mithril-kitchen-sink example - command: yarn chromatic --storybook-build-dir="built-storybooks/mithril-kitchen-sink" --app-code="8adgm46jzk8" - - run: - name: Run chromatic on the pre-built preact-kitchen-sink example - command: yarn chromatic --storybook-build-dir="built-storybooks/preact-kitchen-sink" --app-code="ls0ikhnwqt" - - run: - name: Run chromatic on the pre-built rax-kitchen-sink example - command: yarn chromatic --storybook-build-dir="built-storybooks/rax-kitchen-sink" --app-code="4co6vptx8qo" - - run: - name: Run chromatic on the pre-built riot-kitchen-sink example - command: yarn chromatic --storybook-build-dir="built-storybooks/riot-kitchen-sink" --app-code="g2dp3lnr34a" - - run: - name: Run chromatic on the pre-built svelte-kitchen-sink example - command: yarn chromatic --storybook-build-dir="built-storybooks/svelte-kitchen-sink" --app-code="8ob73wgl995" - - run: - name: Run chromatic on the pre-built vue-kitchen-sink example - command: yarn chromatic --storybook-build-dir="built-storybooks/vue-kitchen-sink" --app-code="cyxj0e38bqj" - - run: - name: Run chromatic on the pre-built web-components-kitchen-sink example - command: yarn chromatic --storybook-build-dir="built-storybooks/web-components-kitchen-sink" --app-code="npm5gsofwkf" - + name: examples + command: | + yarn run-chromatics packtracker: <<: *defaults steps: diff --git a/examples/angular-cli/package.json b/examples/angular-cli/package.json index a28022a00be..903e14b6c47 100644 --- a/examples/angular-cli/package.json +++ b/examples/angular-cli/package.json @@ -64,5 +64,10 @@ "protractor": "~5.4.3", "ts-node": "~8.6.2", "typescript": "^3.4.0" + }, + "storybook": { + "chromatic": { + "appCode": "tl92yzsj6w" + } } } diff --git a/examples/cra-kitchen-sink/package.json b/examples/cra-kitchen-sink/package.json index 21d24bd1fec..4d5c54625fe 100644 --- a/examples/cra-kitchen-sink/package.json +++ b/examples/cra-kitchen-sink/package.json @@ -34,5 +34,10 @@ "@storybook/react": "6.0.0-alpha.27", "@storybook/theming": "6.0.0-alpha.27", "react-scripts": "^3.0.1" + }, + "storybook": { + "chromatic": { + "appCode": "tg55gajmdt" + } } } diff --git a/examples/cra-react15/package.json b/examples/cra-react15/package.json index f35e462da3d..105e59eac9b 100644 --- a/examples/cra-react15/package.json +++ b/examples/cra-react15/package.json @@ -25,5 +25,10 @@ "@storybook/theming": "6.0.0-alpha.27", "babel-core": "6", "babel-runtime": "6" + }, + "storybook": { + "chromatic": { + "appCode": "gxk7iqej3wt" + } } } diff --git a/examples/cra-ts-essentials/package.json b/examples/cra-ts-essentials/package.json index af254d6d1e5..dfa93672512 100644 --- a/examples/cra-ts-essentials/package.json +++ b/examples/cra-ts-essentials/package.json @@ -40,5 +40,10 @@ "@storybook/addons": "6.0.0-alpha.27", "@storybook/preset-create-react-app": "^1.5.0", "@storybook/react": "6.0.0-alpha.27" + }, + "storybook": { + "chromatic": { + "appCode": "b311ypk6of" + } } } diff --git a/examples/cra-ts-kitchen-sink/package.json b/examples/cra-ts-kitchen-sink/package.json index c8ec9c5921e..8ad656d6c1d 100644 --- a/examples/cra-ts-kitchen-sink/package.json +++ b/examples/cra-ts-kitchen-sink/package.json @@ -57,5 +57,10 @@ "tslint": "^6.0.0", "tslint-config-airbnb": "^5.11.1", "typescript": "^3.4.0" + }, + "storybook": { + "chromatic": { + "appCode": "19whyj1tlac" + } } } diff --git a/examples/dev-kits/package.json b/examples/dev-kits/package.json index 810f61da5b2..4037f0c3b6f 100644 --- a/examples/dev-kits/package.json +++ b/examples/dev-kits/package.json @@ -37,5 +37,10 @@ "ts-loader": "^6.2.0", "uuid": "^3.3.2", "webpack": "^4.33.0" + }, + "storybook": { + "chromatic": { + "appCode": "19whyj1tlac" + } } } diff --git a/examples/ember-cli/package.json b/examples/ember-cli/package.json index 59ee95eac57..535ef8b8569 100644 --- a/examples/ember-cli/package.json +++ b/examples/ember-cli/package.json @@ -51,5 +51,10 @@ }, "engines": { "node": "^4.5 || 6.* || >= 7.*" + }, + "storybook": { + "chromatic": { + "appCode": "19z23qxndju" + } } } diff --git a/examples/html-kitchen-sink/package.json b/examples/html-kitchen-sink/package.json index 767ee764eb4..bdd0cf974ed 100644 --- a/examples/html-kitchen-sink/package.json +++ b/examples/html-kitchen-sink/package.json @@ -36,5 +36,10 @@ "format-json": "^1.0.3", "global": "^4.3.2", "postcss-color-rebeccapurple": "^5.0.0" + }, + "storybook": { + "chromatic": { + "appCode": "e8zolxoyg8o" + } } } diff --git a/examples/marko-cli/package.json b/examples/marko-cli/package.json index f8cd745fa23..e5ad475b8d4 100644 --- a/examples/marko-cli/package.json +++ b/examples/marko-cli/package.json @@ -33,5 +33,10 @@ "@storybook/source-loader": "6.0.0-alpha.27", "prettier": "^1.16.4", "webpack": "^4.33.0" + }, + "storybook": { + "chromatic": { + "appCode": "qaegx64axu" + } } } diff --git a/examples/mithril-kitchen-sink/package.json b/examples/mithril-kitchen-sink/package.json index b98691c734b..221a6282e91 100644 --- a/examples/mithril-kitchen-sink/package.json +++ b/examples/mithril-kitchen-sink/package.json @@ -24,5 +24,10 @@ "@storybook/mithril": "6.0.0-alpha.27", "@storybook/source-loader": "6.0.0-alpha.27", "webpack": "^4.33.0" + }, + "storybook": { + "chromatic": { + "appCode": "8adgm46jzk8" + } } } diff --git a/examples/official-storybook/package.json b/examples/official-storybook/package.json index 70da627e0b4..57fa465c024 100644 --- a/examples/official-storybook/package.json +++ b/examples/official-storybook/package.json @@ -62,5 +62,10 @@ "ts-loader": "^6.0.0", "uuid": "^3.3.2", "webpack": "^4.33.0" + }, + "storybook": { + "chromatic": { + "appCode": "ab7m45tp9p" + } } } diff --git a/examples/preact-kitchen-sink/package.json b/examples/preact-kitchen-sink/package.json index 98a7d67c985..4ca200fc2e7 100644 --- a/examples/preact-kitchen-sink/package.json +++ b/examples/preact-kitchen-sink/package.json @@ -37,5 +37,10 @@ "svg-url-loader": "^3.0.2", "webpack": "^4.33.0", "webpack-dev-server": "^3.8.2" + }, + "storybook": { + "chromatic": { + "appCode": "ls0ikhnwqt" + } } } diff --git a/examples/rax-kitchen-sink/package.json b/examples/rax-kitchen-sink/package.json index 7a01940cca2..99c2c079e44 100644 --- a/examples/rax-kitchen-sink/package.json +++ b/examples/rax-kitchen-sink/package.json @@ -39,5 +39,10 @@ "babel-eslint": "^10.0.3", "build-plugin-rax-app": "^0.2.0", "stylesheet-loader": "^0.7.0" + }, + "storybook": { + "chromatic": { + "appCode": "4co6vptx8qo" + } } } diff --git a/examples/riot-kitchen-sink/package.json b/examples/riot-kitchen-sink/package.json index 386bd5dd890..08560aa8823 100644 --- a/examples/riot-kitchen-sink/package.json +++ b/examples/riot-kitchen-sink/package.json @@ -36,5 +36,10 @@ "svg-url-loader": "^3.0.2", "webpack": "^4.33.0", "webpack-dev-server": "^3.8.2" + }, + "storybook": { + "chromatic": { + "appCode": "g2dp3lnr34a" + } } } diff --git a/examples/svelte-kitchen-sink/package.json b/examples/svelte-kitchen-sink/package.json index 758ba2bf4e6..e5bd5205753 100644 --- a/examples/svelte-kitchen-sink/package.json +++ b/examples/svelte-kitchen-sink/package.json @@ -24,5 +24,10 @@ "@storybook/addons": "6.0.0-alpha.27", "@storybook/source-loader": "6.0.0-alpha.27", "@storybook/svelte": "6.0.0-alpha.27" + }, + "storybook": { + "chromatic": { + "appCode": "8ob73wgl995" + } } } diff --git a/examples/vue-kitchen-sink/package.json b/examples/vue-kitchen-sink/package.json index 6795f417ab5..68c372ab3ec 100644 --- a/examples/vue-kitchen-sink/package.json +++ b/examples/vue-kitchen-sink/package.json @@ -38,5 +38,10 @@ "vue-loader": "^15.7.0", "webpack": "^4.33.0", "webpack-dev-server": "^3.8.2" + }, + "storybook": { + "chromatic": { + "appCode": "cyxj0e38bqj" + } } } diff --git a/examples/web-components-kitchen-sink/package.json b/examples/web-components-kitchen-sink/package.json index b961caf8c5b..b6dc8d67616 100644 --- a/examples/web-components-kitchen-sink/package.json +++ b/examples/web-components-kitchen-sink/package.json @@ -36,5 +36,10 @@ "format-json": "^1.0.3", "global": "^4.3.2", "lit-element": "^2.2.1" + }, + "storybook": { + "chromatic": { + "appCode": "npm5gsofwkf" + } } } diff --git a/package.json b/package.json index d1548188b4e..b3652b29538 100644 --- a/package.json +++ b/package.json @@ -68,6 +68,7 @@ "publish:latest": "lerna publish --exact --concurrency 1 --force-publish", "publish:next": "npm run publish:latest -- --npm-tag=next", "repo-dirty-check": "node ./scripts/repo-dirty-check", + "run-chromatics": "node ./scripts/run-chromatics.js", "serve-storybooks": "http-server ./built-storybooks -p 8001", "start": "yarn --cwd examples/official-storybook storybook", "test": "node ./scripts/test.js", diff --git a/scripts/run-chromatics.js b/scripts/run-chromatics.js new file mode 100755 index 00000000000..3aff85e16c2 --- /dev/null +++ b/scripts/run-chromatics.js @@ -0,0 +1,125 @@ +#!/usr/bin/env node + +const { spawn } = require('child_process'); +const { promisify } = require('util'); +const { + readdir: readdirRaw, + readFile: readFileRaw, + writeFile: writeFileRaw, + statSync, + readFileSync, +} = require('fs'); +const { join } = require('path'); + +const readdir = promisify(readdirRaw); +const writeFile = promisify(writeFileRaw); + +const p = l => join(__dirname, '..', ...l); +const logger = console; + +const exec = async (command, args = [], options = {}) => + new Promise((resolve, reject) => { + const child = spawn(command, args, { ...options, stdio: 'inherit', shell: true }); + + child + .on('close', code => { + if (code) { + reject(); + } else { + resolve(); + } + }) + .on('error', e => { + logger.error(e); + reject(); + }); + }); + +const getDeployables = files => { + return files.filter(f => { + const packageJsonLocation = p(['examples', f, 'package.json']); + let stats = null; + try { + stats = statSync(packageJsonLocation); + } catch (e) { + // the folder had no package.json, we'll ignore + } + + return stats && stats.isFile() && hasChromaticAppCode(packageJsonLocation); + }); +}; + +const hasChromaticAppCode = l => { + const text = readFileSync(l, 'utf8'); + const json = JSON.parse(text); + + return !!(json && json.storybook && json.storybook.chromatic && json.storybook.chromatic.appCode); +}; + +const handleExamples = async deployables => { + await deployables.reduce(async (acc, d) => { + await acc; + + const out = p(['built-storybooks', d]); + const cwd = p([]); + const { + storybook: { + chromatic: { appCode }, + }, + } = JSON.parse(readFileSync(p(['examples', d, 'package.json']))); + + if (appCode) { + await exec( + `yarn`, + [ + 'chromatic', + `--storybook-build-dir="${out}"`, + '--exit-zero-on-changes', + `--app-code="${appCode}"`, + ], + { cwd } + ); + + logger.log('-------'); + logger.log(`✅ ${d} ran`); + logger.log('-------'); + } else { + logger.log('-------'); + logger.log(`❌ ${d} skipped`); + logger.log('-------'); + } + }, Promise.resolve()); +}; + +const run = async () => { + const examples = await readdir(p(['examples'])); + + const { length } = examples; + const [a, b] = [process.env.CIRCLE_NODE_INDEX || 0, process.env.CIRCLE_NODE_TOTAL || 1]; + const step = Math.ceil(length / b); + const offset = step * a; + + const list = examples.slice().splice(offset, step); + const deployables = getDeployables(list); + + if (deployables.length) { + logger.log(`will build: ${deployables.join(', ')}`); + await handleExamples(deployables); + } + + if ( + deployables.length && + (process.env.CIRCLE_NODE_INDEX === undefined || + process.env.CIRCLE_NODE_INDEX === '0' || + process.env.CIRCLE_NODE_INDEX === 0) + ) { + logger.log('-------'); + logger.log('✅ done'); + logger.log('-------'); + } +}; + +run().catch(e => { + logger.error(e); + process.exit(1); +});