CI, 技术, Ghost, DevOps

使用 GitLab CI 自动部署 Ghost 主题

2022年05月24日,居家隔离的第 N 天

今天收到了 Ghost 5.0 发布的邮件,第一时间更新了下,发现主题里有些功能已经不兼容了,于是准备对主题做下更新。在看 Ghost Integrations 的时候发现有个 GitHub 的插件特别好用,支持通过 GitHub Actions 自动部署你的主题。但是我自己的项目用的都是 GitLab,找了一圈,没有官方的插件。于是尝试自己通过 GitLab CI 来实现。

大致看了下基于 GitHub Actions 自动部署的实现方式,通过官方提供的一个 TryGhost/action-deploy-theme 的步骤,代码很简单,总共 40 行,我们来看下它做了什么:

const path = require('path');
const core = require('@actions/core');
const exec = require('@actions/exec');
const GhostAdminApi = require('@tryghost/admin-api');

(async function main() {
    try {
        const url = core.getInput('api-url');
        const api = new GhostAdminApi({
            url,
            key: core.getInput('api-key'),
            version: 'canary'
        });

        const basePath = process.env.GITHUB_WORKSPACE;
        const pkgPath = path.join(process.env.GITHUB_WORKSPACE, 'package.json');

        let zipPath = core.getInput('file');

        // Zip file was not provided - zip everything up!
        if (!zipPath) {
            const themeName = core.getInput('theme-name') || require(pkgPath).name;
            const themeZip = `${themeName}.zip`;
            const exclude = core.getInput('exclude') || '';
            zipPath = themeZip;

            // Create a zip
            await exec.exec(`zip -r ${themeZip} ${core.getInput('working-directory') || '.'} -x *.git* *.zip yarn* npm* node_modules* *routes.yaml *redirects.yaml *redirects.json ${exclude}`, [], {cwd: basePath});
        }

        zipPath = path.join(basePath, zipPath);

        // Deploy it to the configured site
        await api.themes.upload({file: zipPath});
        console.log(`${zipPath} successfully uploaded.`); // eslint-disable-line no-console
    } catch (err) {
        console.error(err); // eslint-disable-line no-console
        process.exit(1);
    }
}());

把主题打包成 zip 包,然后提供 Ghost 上创建的 Admin API KeyAPI URL,通过 API 去上传,那么我们应该也可以自己去实现。

首先,我们也需要去 Ghost 后台创建一个自定义的 Integration,比如取名叫 GitLab CI,目的是为了获得 Admin API keyAPI URL,后面在 GitLab CI 中需要用到。

iShot_2022-05-24_11.17.16

下一步,去 GitLab CI 中,把这两个内容配置成变量,取名 GHOST_ADMIN_API_KEYGHOST_API_URL 以便在 CI 脚本中使用。

iShot_2022-05-24_16.50.45

在项目中添加 Ghost Admin API 库:

yarn add @tryghost/admin-api --dev

gulpfile.js 中插入部署的任务:

const { series, src, dest } = require('gulp')
const pump = require('pump')
const less = require('gulp-less')
const zip = require('gulp-zip')
const GhostAdminApi = require('@tryghost/admin-api')

const handleError = (done) => {
    return function (err) {
        if (err) {
            console.error(err)
        }
        return done(err)
    }
}

function css(done) {
    pump(
        [
            src('./assets/css/*.less', { sourcemaps: true }),
            less({}),
            dest('assets/css', { sourcemaps: './' }),
        ],
        handleError(done)
    )
}

function zipper(done) {
    var targetDir = 'dist/'
    var themeName = require('./package.json').name
    var filename = themeName + '.zip'

    pump(
        [
            src(['**', '!node_modules', '!node_modules/**', '!dist', '!dist/**']),
            zip(filename),
            dest(targetDir),
        ],
        handleError(done)
    )
}

async function deploy(done) {
    try {
        const zipFile = `dist/${require('./package.json').name}.zip`
        const api = new GhostAdminApi({
            url: process.env.GHOST_API_URL,
            key: process.env.GHOST_ADMIN_API_KEY,
            version: `v${require('./package.json').version}`,
        })

        await api.themes.upload({ file: zipFile })
        console.log(`${zipFile} successfully uploaded.`)
        done()
    } catch (err) {
        console.error(err)
        done(err)
    }
}

const build = series(css)

exports.build = build
exports.zip = series(build, zipper)
exports.deploy = deploy
exports.default = build

package.json 中插入脚本:

{
    /* ... */    
    "scripts": {
        "build": "gulp build",
        "zip": "gulp zip",
        "deploy": "gulp deploy"
    }
    /* ... */
}

添加 .gitlab-ci.yml 文件:

image: node:14-slim # 注意:不要用 alpine 的镜像,上传至 https 站点会有问题

stages:
    - deploy

deploy:
    stage: deploy
    script:
        - yarn install
        - yarn zip
        - yarn deploy
    only:
        - tags
    cache:
        paths:
            - node_modules/

注意,为了避免每次提交代码都部署,deploy 任务限制了只有打了 tagcommit 才会触发。

好了,更新代码,打个 tag 就会自动打包上传至 Ghost 后台了!

您已成功订阅 HADB.ME
真棒!下一步,完成结账以便解锁 HADB.ME
欢迎回来!您已登录成功。
登录失败,请重试。
操作成功!您的账户已全面激活,现在您有所有内容的权限了。
错误!Stripe 结账失败。
成功!您的账单信息已更新。
错误!账单信息更新失败。