GitLab Pages部署Storybook遇脚本URL错误导致CORS问题
GitLab CI托管Storybook资源路径错误导致CORS问题修复方案
背景
通过GitLab CI推送代码至任意分支时构建并托管Storybook页面,当前CI脚本如下:
image: node:16 stages: - setup - build-and-test - deployment - pages setup: stage: setup cache: key: ${CI_COMMIT_REF_SLUG} paths: - node_modules/ artifacts: paths: - node_modules/ script: - yarn build: stage: build-and-test artifacts: paths: - dist/ script: - yarn build storybook: stage: build-and-test artifacts: expire_in: 2 weeks when: always paths: - storybook/ script: - yarn build-storybook --output-dir storybook deploy-storybook: stage: deployment script: - echo "Enjoy the day. 🥳 Every job needs a script, but this job was just created to configure an environment." environment: name: storybook/$CI_COMMIT_REF_SLUG url: https://<my_username>.gitlab.io/<my_project>/$CI_COMMIT_REF_SLUG/storybook/ on_stop: remove-storybook only: - branches remove-storybook: stage: deployment cache: key: 'my-storybook' paths: - public script: - rm -rf "public/$CI_COMMIT_REF_SLUG/storybook" when: manual variables: GIT_STRATEGY: none # needed to prevent "Couldn't find remote ref" error environment: name: storybook/$CI_COMMIT_REF_SLUG action: stop pages: stage: pages cache: key: 'my-storybook' paths: - public script: - if [ "$CI_COMMIT_REF_NAME" = "main" ]; then mkdir -p public; touch public/index.html; echo "<!DOCTYPE HTML><script>window.location.href = 'https://<my_username>.gitlab.io/<my_project>/main/storybook'</script>" > public/index.html; fi; - rm -rf "public/$CI_COMMIT_REF_SLUG" - mkdir -p "public/$CI_COMMIT_REF_SLUG"; - mv storybook "public/$CI_COMMIT_REF_SLUG" artifacts: paths: - public
错误现象
构建正常,但访问时控制台触发CORS拦截错误:
iframe.html:1 Access to script at 'https://gitlab.com/oauth/authorize?client_id=<my_client_id>&redirect_uri=https://projects.gitlab.io/auth&response_type=code&state=AFyDa1QTpsd9qXqSzdoy4w==&scope=api' (redirected from 'https://<my_username>.gitlab.io/assets/iframe.8696a5de.js') from origin 'https://<my_username>.gitlab.io' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
iframe.html:374 GET https://gitlab.com/oauth/authorize?client_id=<my_client_id>&redirect_uri=https://projects.gitlab.io/auth&response_type=code&state=AFyDa1QTpsd9qXqSzdoy4w==&scope=api net::ERR_FAILED 302
关键代码片段
iframe.html路径错误示例
<head> ... <script defer src="/node_modules/@fortawesome/fontawesome-free/js/all.min.js"></script> <!-- iframe.html:374 --> <script type="module" crossorigin src="/assets/iframe.8696a5de.js"></script> <link rel="stylesheet" href="/assets/iframe.0f83afa4.css"> ... </head>
资源路径使用根路径/assets,但正确路径应为/<project_name>/<branch>/storybook/assets。
.storybook/main.cjs配置
const { resolve } = require("path"); const { typescript: preprocessTS } = require("svelte-preprocess"); const { loadConfigFromFile, mergeConfig } = require("vite"); module.exports = { "stories": [ "../src/**/*.stories.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx|svelte)" ], "addons": [ "@storybook/addon-links", "@storybook/addon-essentials", "@storybook/addon-interactions", "@storybook/addon-svelte-csf" ], "framework": "@storybook/svelte", "core": { "builder": "@storybook/builder-vite" }, "svelteOptions": { "preprocess": import("../svelte.config.js").preprocess }, svelteOptions: { preprocess: [ preprocessTS(), ], }, async viteFinal(config, { configType }) { const { config: userConfig } = await loadConfigFromFile( resolve(__dirname, "../vite.config.ts") ); return mergeConfig(config, { ...userConfig, // manually specify plugins to avoid conflict plugins: [] }); } }
核心问题
Storybook构建时未指定正确的基础路径,导致生成的资源URL使用根路径/,而实际托管路径是/<project_name>/<branch>/storybook/,资源请求跳转到GitLab根域名下,触发CORS拦截。
解决方案
方案1:构建时指定基础路径(推荐)
修改CI的storybook任务,在build-storybook命令中添加--base-path参数,指定相对于GitLab Pages根目录的完整路径:
storybook: stage: build-and-test artifacts: expire_in: 2 weeks when: always paths: - storybook/ script: - yarn build-storybook --output-dir storybook --base-path "/<my_project>/$CI_COMMIT_REF_SLUG/storybook"
构建后所有资源路径会自动带上前缀,无需后续修改。
方案2:通过Vite配置指定base路径
在.storybook/main.cjs的viteFinal函数中设置Vite的base选项,利用GitLab CI内置变量动态生成路径:
async viteFinal(config, { configType }) { const { config: userConfig } = await loadConfigFromFile( resolve(__dirname, "../vite.config.ts") ); // 动态生成基础路径,GitLab CI自动提供CI_PROJECT_NAME和CI_COMMIT_REF_SLUG变量 const basePath = `/${process.env.CI_PROJECT_NAME}/${process.env.CI_COMMIT_REF_SLUG}/storybook/`; return mergeConfig(config, { ...userConfig, base: basePath, plugins: [] }); }
方案3:CI脚本批量替换资源路径(兜底)
如果前两种方案无法实施,在pages任务中添加文本替换脚本,修正所有文件中的路径引用:
pages: stage: pages cache: key: 'my-storybook' paths: - public script: - if [ "$CI_COMMIT_REF_NAME" = "main" ]; then mkdir -p public; touch public/index.html; echo "<!DOCTYPE HTML><script>window.location.href = 'https://<my_username>.gitlab.io/<my_project>/main/storybook'</script>" > public/index.html; fi; - rm -rf "public/$CI_COMMIT_REF_SLUG" - mkdir -p "public/$CI_COMMIT_REF_SLUG"; # 替换资源路径前缀 - find storybook -type f \( -name "*.html" -o -name "*.js" -o -name "*.css" \) -exec sed -i "s|/assets|/<my_project>/$CI_COMMIT_REF_SLUG/storybook/assets|g" {} \; - find storybook -type f \( -name "*.html" -o -name "*.js" -o -name "*.css" \) -exec sed -i "s|/node_modules|/<my_project>/$CI_COMMIT_REF_SLUG/storybook/node_modules|g" {} \; - mv storybook "public/$CI_COMMIT_REF_SLUG" artifacts: paths: - public
为什么直接修改CI脚本调整根URL不可行?
Storybook构建过程中会将资源路径硬编码到生成的HTML、JS和CSS文件中,CI脚本仅移动文件位置,无法修改文件内部的路径引用。必须在构建阶段指定正确的基础路径,或者事后批量替换文件内容才能修正路径。
内容的提问来源于stack exchange,提问作者LaytonGB
相关产品推荐
相关产品推荐

