Skip to content

Commit

Permalink
chore(ci): add deploy docs workflow (akveo#2950)
Browse files Browse the repository at this point in the history
  • Loading branch information
katebatura authored Dec 2, 2021
1 parent 303e708 commit 8171e0b
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 13 deletions.
19 changes: 19 additions & 0 deletions .github/workflows/deploy-docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: Deploy docs
on:
push:
branches:
- master
release:
types:
- published
jobs:
deploy-docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: '14'
cache: 'npm'
- run: npm ci
- run: npm run docs:gh-pages
92 changes: 79 additions & 13 deletions tools/deploy-docs/build-docs.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import { join } from 'path';
import { writeFile } from 'fs/promises';
import { copy, mkdirp, remove, outputFile, writeJson, readJson } from 'fs-extra';
import { copy, mkdirp, remove, writeJson, readJson, readFile, stat, readdir, pathExists, outputFile } from 'fs-extra';

import { generateGithubSpaScript } from './ghspa-template';
import { runCommand } from './run-command';
import { log } from './log';

import { REPO_URL, OUT_DIR, REPO_OWNER, REPO_NAME } from './config';
import { getStdout } from './get-stdout';

const WORK_DIR = join(process.cwd(), '../_DOCS_BUILD_WORK_DIR_');
const MASTER_BRANCH_DIR = join(WORK_DIR, 'MASTER');
const DOCS_VERSIONS_PATH = join(MASTER_BRANCH_DIR, 'docs/versions.json');
const GH_PAGES_DIR = join(WORK_DIR, 'gh-pages');
const FILE_WITH_HASH = 'last-commit-hash.txt';

export interface Version {
checkoutTarget: string;
Expand All @@ -30,6 +34,12 @@ export interface Version {
log(`Cloning ${REPO_URL} into ${MASTER_BRANCH_DIR}`);
await runCommand(`git clone ${REPO_URL} ${MASTER_BRANCH_DIR}`, { cwd: WORK_DIR });

log('Get build versions from gh-pages branch');
await copyToBuildDir(MASTER_BRANCH_DIR, GH_PAGES_DIR);
await checkoutVersion('gh-pages', GH_PAGES_DIR);
const builtVersions: { hash; path }[] = await checkBuiltVersions();
log(`Built versions in gh-pages: ${builtVersions}`);

log('Reading versions configuration');
const config: Version[] = await import(DOCS_VERSIONS_PATH);
ensureSingleCurrentVersion(config);
Expand All @@ -39,7 +49,7 @@ export interface Version {
log(jsonConfig);

log(`Building docs`);
await buildDocs(config);
await buildDocs(config, builtVersions);

log(`Adding versions.json to ${OUT_DIR}`);
await outputFile(join(OUT_DIR, 'versions.json'), jsonConfig);
Expand All @@ -58,31 +68,83 @@ function ensureSingleCurrentVersion(versions: Version[]) {
}
}

async function buildDocs(versions: Version[]) {
async function checkBuiltVersions() {
let builtVersions: { hash; path }[] = [];

//take hash from the GH_PAGES_DIR directory
const hashFilePath = join(GH_PAGES_DIR, FILE_WITH_HASH);
const exists = await pathExists(hashFilePath);

if (exists) {
builtVersions.push({ hash: await readFile(hashFilePath, 'utf8'), path: GH_PAGES_DIR });
}

//take hash from the first-level directories of GH_PAGES_DIR
const files = await readdir(GH_PAGES_DIR);
for await (let file of files) {
const stats = await stat(join(GH_PAGES_DIR, file));
if (stats.isDirectory()) {
const hashFilePathInDirectory = join(GH_PAGES_DIR, file, FILE_WITH_HASH);
const existsInDirectory = await pathExists(hashFilePathInDirectory);
if (existsInDirectory) {
builtVersions.push({
hash: await readFile(hashFilePathInDirectory, 'utf8'),
path: join(GH_PAGES_DIR, file),
});
}
}
}

return builtVersions;
}

async function buildDocs(versions: Version[], builtVersions: { hash; path }[]) {
const ghspaScript = generateGithubSpaScript(versions);

return Promise.all(
versions.map((version: Version) => {
const versionDistDir = version.isCurrent ? OUT_DIR : join(OUT_DIR, version.name);

return prepareVersion(version, versionDistDir, ghspaScript);
return prepareVersion(version, versionDistDir, ghspaScript, builtVersions);
}),
);
}

async function prepareVersion(version: Version, distDir: string, ghspaScript: string) {
async function prepareVersion(version: Version, distDir: string, ghspaScript: string, builtVersions: { hash; path }[]) {
const projectDir = join(WORK_DIR, `${version.name}`);

await copyToBuildDir(MASTER_BRANCH_DIR, projectDir);
await checkoutVersion(version.checkoutTarget, projectDir);
await runCommand('npm ci', { cwd: projectDir });
await addVersionNameToPackageJson(version.name, join(projectDir, 'package.json'));
await addVersionTs(version, join(projectDir, 'package.json'));
await buildDocsApp(projectDir, version.path);
await copy(join(projectDir, 'docs/dist'), distDir);
await outputFile(join(distDir, 'assets/ghspa.js'), ghspaScript);

await remove(projectDir);

const currentHash = getStdout('git rev-parse HEAD', { cwd: projectDir, showLog: true });

const existInGhPages = builtVersions.find((item) => currentHash === item.hash);

if (existInGhPages) {
await copyFromGhPages(version, existInGhPages.path, distDir);
} else {
await runCommand('npm ci', { cwd: projectDir });
await addVersionNameToPackageJson(version.name, join(projectDir, 'package.json'));
await addVersionTs(version, join(projectDir, 'version.ts'));
await buildDocsApp(projectDir, version.path);
await addCommitHash(join(OUT_DIR, FILE_WITH_HASH), projectDir);
await copy(join(projectDir, OUT_DIR), distDir);
await outputFile(join(distDir, 'assets/ghspa.js'), ghspaScript);

await remove(projectDir);
}
}

async function copyFromGhPages(version, correspondingVersionPath, distDir) {
log(`Copying existing docs ${version.name} from ${correspondingVersionPath}`);

const files = await readdir(correspondingVersionPath);
for await (let file of files) {
const stats = await stat(join(correspondingVersionPath, file));
if (!stats.isDirectory() || file === 'docs' || file === 'assets') {
await copy(join(correspondingVersionPath, file), join(distDir, file));
}
}
}

async function copyToBuildDir(from: string, to: string) {
Expand Down Expand Up @@ -113,6 +175,10 @@ async function addVersionTs(version: Version, versionTsPath: string) {
await writeFile(versionTsPath, source, 'utf8');
}

async function addCommitHash(path: string, projectDir: string) {
await runCommand(`git rev-parse HEAD > ${path}`, { cwd: projectDir });
}

async function buildDocsApp(projectDir: string, baseHref: string) {
if (!baseHref.endsWith('/')) {
baseHref = baseHref + '/';
Expand Down
31 changes: 31 additions & 0 deletions tools/deploy-docs/get-stdout.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { promisify } from 'util';
import { exec } from 'child_process';

export interface GetStdoutOptions {
cwd?: string;
showLog?: boolean;
}

const DEFAULT_OPTIONS: GetStdoutOptions = { cwd: process.cwd(), showLog: false };

export async function getStdout(command: string, options?: GetStdoutOptions) {
let { cwd, showLog } = Object.assign({}, DEFAULT_OPTIONS, options);

try {
console.log(`Reading stdout when running "${command}" in "${cwd}"`);
const { stdout } = await promisify(exec)(command, { cwd });

if (showLog && stdout) {
console.log(stdout);
}

return stdout;
} catch ({ message, stdout }) {
let errorMessage = `Error running "${command}" in "${cwd}": ${message}.`;
if (stdout) {
errorMessage += `\nstdout: ${stdout}`;
console.error(stdout);
}
throw new Error(errorMessage);
}
}

0 comments on commit 8171e0b

Please sign in to comment.