Skip to content

Instantly share code, notes, and snippets.

@ErisDS
Last active January 14, 2023 15:26
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save ErisDS/cca4f5ab70eb3d55ee495323d605d8be to your computer and use it in GitHub Desktop.
Save ErisDS/cca4f5ab70eb3d55ee495323d605d8be to your computer and use it in GitHub Desktop.
Deployment tools for Ghost themes

Gulp tools to deploy a Ghost theme

To set it up:

  • copy gulp-config.json.example to gulp-config.json
  • enter the blog admin url, no trailing slash, e.g. https://myblog.ghost.io
  • grab the client secret for the frontend from the source of any page of the blog
  • enter the email address & password you use to login to the blog (must be administrator-level)

To deploy:

  • Run gulp deploy
  • Resolve any gscan errors
{
"url": "http://localhost:2368",
"client_id": "ghost-frontend",
"client_secret": "",
"username": "me@myaddress.com",
"password": ""
}
var gulp = require('gulp');
// gulp plugins and utils
var gutil = require('gulp-util');
var livereload = require('gulp-livereload');
var nodemon = require('gulp-nodemon');
var postcss = require('gulp-postcss');
var sourcemaps = require('gulp-sourcemaps');
var zip = require('gulp-zip');
var gitignore = require('gulp-gitignore');
// postcss plugins
var autoprefixer = require('autoprefixer');
var colorFunction = require('postcss-color-function');
var cssnano = require('cssnano');
var customProperties = require('postcss-custom-properties');
var easyimport = require('postcss-easy-import');
// dependencies for deployment
var fs = require('fs');
var path = require('path');
var got = require('got');
var FormData = require('form-data');
// Shared variables
var themeZipPath = '.dist';
var themeZipName = require('./package.json').name + '.zip';
var swallowError = function swallowError(error) {
gutil.log(error.toString());
gutil.beep();
this.emit('end');
};
var nodemonServerInit = function () {
livereload.listen(1234);
};
gulp.task('build', ['css'], function (/* cb */) {
return nodemonServerInit();
});
gulp.task('css', function () {
var processors = [
easyimport,
customProperties,
colorFunction(),
autoprefixer({browsers: ['last 2 versions']}),
cssnano()
];
gulp.src('assets/css/*.css')
.on('error', swallowError)
.pipe(sourcemaps.init())
.pipe(postcss(processors))
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest('assets/built/'))
.pipe(livereload());
});
gulp.task('watch', function () {
gulp.watch('assets/css/**', ['css']);
});
gulp.task('default', ['build'], function () {
gulp.start('watch');
});
/*
* Deployment tasks
*/
gulp.task('zip', ['css'], function () {
// Include all files, except node_modules which are large and make this slow
return gulp.src(['**', '!node_modules/**'])
// Now also exclude everything mentioned in gitignore
.pipe(gitignore())
// Zip up what is left & save it
.pipe(zip(themeZipName))
.pipe(gulp.dest(themeZipPath));
});
function doUpload(url, bodyData) {
return got
.post(url + '/ghost/api/v0.1/authentication/token', {form: true, body: bodyData})
.then(function (res) {
var form = new FormData();
form.append('theme', fs.createReadStream(path.join(themeZipPath, themeZipName)));
return got
.post(url + '/ghost/api/v0.1/themes/upload', {
headers: {
'Authorization': 'Bearer ' + JSON.parse(res.body).access_token
},
body: form
})
.then(function handleSuccess(res) {
var theme = JSON.parse(res.body).themes[0];
gutil.log(gutil.colors.green('Successfully uploaded: ' + theme.name));
if (!theme.active) {
gutil.log(gutil.colors.yellow('Warning: ' + theme.name + ' is not the active theme, please activate it to see changes.'))
}
})
.catch(function handleError(err) {
if (err.statusCode === 422) {
var response = JSON.parse(err.response.body),
error = response.errors[0];
gutil.log(gutil.colors.red(error.errorType + ': ' + error.message));
if (error.errorDetails && Array.isArray(error.errorDetails)) {
gutil.log(gutil.colors.red('ErrorDetails: '));
error.errorDetails.forEach(function (details) {
gutil.log(details);
});
}
} else {
gutil.log(gutil.colors.red('Upload Error: ' + err));
}
gutil.beep();
});
}
)
.catch(function (err) {
gutil.log(gutil.colors.red('Auth Error: ' + err));
gutil.beep();
});
}
gulp.task('upload', function () {
try {
var deployConfig = require('./gulp-config.json');
var url = deployConfig.url;
var bodyData = {
grant_type: 'password',
client_id: deployConfig.client_id,
client_secret: deployConfig.client_secret,
username: deployConfig.username,
password: deployConfig.password
};
} catch (err) {
gutil.log(gutil.colors.red('Please copy gulp-config.json.example to gulp-config.json & fill out all of your details'));
}
return doUpload(url, bodyData);
});
gulp.task('deploy', ['zip'], function () {
gulp.start(['upload']);
});
@ilyazub
Copy link

ilyazub commented Dec 23, 2021

Another option is to use @tryghost/admin-api.

// gulpfile.js

const GhostAdminAPI = require('@tryghost/admin-api');

function uploader(done) {
    const filename = require('./package.json').name + '.zip';

    const api = new GhostAdminAPI({
        // Replace URL with your own
        url: 'http://localhost:2368',
        version: "v3",
        // Obtain API key by creating a new integration at http://localhost:2368/ghost/#/settings/integrations
        key: '86858839df28e6ffaec6b765:f819fbb7b9985fa6ac58086bb50d7ca2aea8a0f09201b40ecb7973457e03b87d' 
    });

    let data = { file: `./dist/${filename}` }

    return api.themes.upload(data)
        .catch(done);
}

const build = series(css, js);
exports.deploy = series(build, zipper, uploader);

function css(done) {
 // Omitted because it's in a regular Casper theme
}

function js(done) {
 // Omitted because it's in a regular Casper theme
}

function zipper(done) {
 // Omitted because it's in a regular Casper theme
}

Usage

$ gulp deploy
[17:47:17] Using gulpfile ~/Git/serpapi-theme/gulpfile.js
[17:47:17] Starting 'deploy'...
[17:47:17] Starting 'css'...
[17:47:19] Finished 'css' after 2.14 s
[17:47:19] Starting 'js'...
[17:47:19] Finished 'js' after 169 ms
[17:47:19] Starting 'zipper'...
[17:47:20] Finished 'zipper' after 335 ms
[17:47:20] Starting 'uploader'...
[17:47:21] Finished 'uploader' after 960 ms
[17:47:21] Finished 'deploy' after 3.61 s

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment