使用gitlab里面的pages,实现gitbook(骚)
Pages入口:
仅作为了解学习
官方例子:
第一页:
vuepress有自己的目录格式
第二页:
gitbook有自己的格式
GitBook 3.2.3入门

GitBook目录结构
基本结构如下:
├── book.json
├── README.md
├── SUMMARY.md
├── chapter-1/
| ├── README.md
| └── something.md
└── chapter-2/
├── README.md
└── something.md
book.json:全局配置数据 (可选)
README.md:介绍电子书(必须)
SUMMARY.md:目录 (可选)
GLOSSARY.md:词汇、术语列表(可选)
- SUMMARY.md格式
基本格式为:* [描述](文件路径或者http超链接)。文件路径是相对于SUMMARY.md所在的目录的。
支持多级目录,每级目录多缩进4个空格。例如:
* [介绍](README.md)
* [概述](index.md)
* [接口](api.md)
* [注意事项](note.md)
直接简单粗暴的使用上:(项目用的是下面这种方式)


book.json文件

{ "title": "项目技术特点", "author": "王云鹏", "language": "zh-hans", "ignores": [ "node_modules", "**/resources" ] }
.gitlab-ci.yml文件,直接创建,需要SUMMARY.md

default: tags: - docker - company # requiring the environment of NodeJS 10 image: node:10 # add 'node_modules' to cache for speeding up builds cache: paths: - node_modules/ # Node modules and dependencies before_script: - npm install gitbook-cli -g # install gitbook - gitbook fetch 3.2.3 # fetch final stable version - gitbook install # add any requested plugins in book.json # the 'pages' job will deploy and build your site to the 'public' path pages: stage: deploy script: # - node generateSummary.js - gitbook build . public # build to public path artifacts: paths: - public expire_in: 1 week only: - master # this job will affect only the 'master' branch
.gitlab-ci.yml文件,用自己创建的镜像实现,不需要写SUMMAY.md

default: tags: - docker - company # requiring the environment of NodeJS 10 image: nexus3:8089/wangyp/gitbook:1.0.1 test: stage: test script: - gitbook build . public # build to public path only: - branches # this job will affect every branch except 'master' except: - master # the 'pages' job will deploy and build your site to the 'public' path pages: stage: deploy script: - book sm - gitbook build . public # build to public path artifacts: paths: - public expire_in: 1 day only: - master # this job will affect only the 'master' branch
generateSummary.js文件

const IGNORE_FILES = [ /* gitignore style */ 'SUMMARY.md', ]; const DEFAULT_README_TITLE = 'Introduction'; //////////////////////////////////////////////// const fs = require('fs'); const path = require('path'); const isNotIgnored = (() => { const ignoredPatterns = fs.readFileSync('./.gitignore', 'utf8') .split('\n') .map(f => f.trim()) .filter(f => f.length > 0) .filter(f => f[0] !== '#') .concat(IGNORE_FILES) .map(f => new RegExp(f.replace(/\*/g, '(.*?)'))); return (f) => { if(!(f instanceof File)) throw "Error: expecting File"; return ignoredPatterns.reduce((bool, p) => bool && !p.test(f.path, true)); }; })(); const indent = (s, n=4) => { const spaces = (new Array(n + 1)).join(' '); return spaces + s.replace(/\n/g, '\n' + spaces); }; class File{ constructor(path){ this.path = path; } get filename(){ return this.path.substring(this.path.lastIndexOf('/') + 1); } get name(){ const p = this.filename; if(p.lastIndexOf('.') === -1) return p; return p.substring(0, p.lastIndexOf('.')); } get prettyname(){ const capitalize = (s) => { if(s.length === 0) return ''; return [s[0].toUpperCase(), ...s.substring(1).toLowerCase()].join(''); }; const sepWords = (p) => p.replace(/[-_]/g, ' '); return capitalize(sepWords(this.name)); } get ext(){ return this.path.substring(this.path.lastIndexOf('.')); } } class Dir extends File{ ls(){ const isVisible = (f) => f[0] !== '.'; return fs.readdirSync(this.path) .filter(isVisible) .map((f) => path.join(this.path, f)) .map(p => new (fs.statSync(p).isDirectory()? Dir: File)(p)); } lsdirs(){ return this.ls().filter(f => f instanceof Dir); } } class Root extends Dir{ /* directories are viewed as Parts * files are Articles */ ls(){ return super.ls() .filter(isNotIgnored) .map(f => new (f instanceof Dir? Part: Article)(f.path)) .filter(f => f instanceof Article? f.ext === '.md': f); } getHeader(){ return `# Summary`; } articlesSummary(){ return this.ls() .filter(f => f instanceof Article) .map(f => f.toSummary()) .join('\n'); } directoriesSummary(){ return this.lsdirs() .map(part => part.toSummary()) .join('\n'); } getSummary(){ const artSum = this.articlesSummary() const dirsSum = this.directoriesSummary(); return [artSum, dirsSum].filter(s => s.trim() !== '').join('\n\n'); } toSummary(){ return this.getHeader() + '\n' + this.getSummary() + '\n'; } } class Part extends Root{ /* directories are viewed as Chapters * files are Articles */ ls(){ return super.ls() .map(f => f instanceof Part? new Chapter(f.path): f) } getHeader(){ return `## ${this.prettyname}`; } } class Chapter extends Part{ ls(){ return super.ls().filter(f => f.filename !== 'README.md'); } getHeader(){ const readme_path = path.join(this.path, 'README.md'); return `* [${this.prettyname}](${readme_path})` } getSummary(){ return indent(super.getSummary()); } } class Article extends File{ get prettyname(){ const original_prettyname = super.prettyname; if(original_prettyname !== 'Readme') return original_prettyname; return DEFAULT_README_TITLE; } toSummary(){ return `* [${this.prettyname}](${this.path})`; } } const summary = new Root('.').toSummary(); fs.writeFileSync('SUMMARY.md', summary);
本地安装node环境,直接直接运行:
node generateSummary.js
也能生成Summary。
提交代码之后,gitlab-ci也可以自动生成gitbook。
然后windows系统里设置hosts文件,浏览器访问
192.168.0.82 root.gitlab-server
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现