Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .buildkite/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"private": true,
"scripts": {
"build:bk:types": "ts-node scripts/get_buildkite_types.ts",
"build:release-site": "ts-node scripts/build_release_site.ts",
"postinstall": "yarn build:bk:types",
"build:pipeline": "ts-node pipelines/pull_request/pipeline.ts",
"print:pipeline": "yarn -s build:bk:types && TEST_BK_PIPELINE=true ts-node -r dotenv/config pipelines/pull_request/pipeline.ts",
Expand Down
38 changes: 38 additions & 0 deletions .buildkite/scripts/build_release_site.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import path from 'path';

import { assembleSite, buildDocsSite, buildStorybookSite, buildTypeDocs } from '../utils/site';

const releaseSiteDir = path.resolve(__dirname, '../../.release-site');

/**
* Builds the release-ready static site consumed by the GitHub release workflow.
* The assembled output matches the published GitHub Pages layout with docs at
* the root and Storybook under `/storybook`.
*/
console.log('Building release-ready docs');
buildTypeDocs();
buildDocsSite({
docsUrl: 'https://elastic.github.io',
baseUrl: '/elastic-charts',
nodeEnv: 'production',
});

console.log('Building release-ready Storybook');
buildStorybookSite({
nodeEnv: 'production',
});

console.log('Assembling release site');
assembleSite({
outDir: releaseSiteDir,
});

console.log(`Release site assembled at ${path.relative(path.resolve(__dirname, '../..'), releaseSiteDir)}`);
45 changes: 15 additions & 30 deletions .buildkite/scripts/steps/docs.ts
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The legacy Buildkite Deploy - GitHub Pages step is no longer the authoritative stable-site publisher, which prevents main-branch pushes from overwriting the release-driven public site.

I'm not sure this is the best approach. What about for changes to the docs alone where there is no fix or feature, that requires to run a release?

I can see pros and cons to both but I think having this on main that represents the current code is best.

@markov00 WDYT?

Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,16 @@
* Side Public License, v 1.
*/

import { bkEnv, compress, exec, getOrCreateDeploymentUrl, startGroup, yarnInstall } from '../../utils';
import {
bkEnv,
buildDocsSite,
buildTypeDocs,
compress,
docsOutDir,
getOrCreateDeploymentUrl,
startGroup,
yarnInstall,
} from '../../utils';
import { createDeploymentStatus, createOrUpdateDeploymentComment } from '../../utils/deployment';

void (async () => {
Expand All @@ -23,38 +32,14 @@ void (async () => {

startGroup('Building docs - firebase');
const firebaseChannelUrl = await getOrCreateDeploymentUrl();
await exec('yarn typedoc');
await exec('yarn build', {
cwd: 'docs',
env: {
DOCUSAURUS_URL: firebaseChannelUrl,
NODE_ENV: bkEnv.isMainBranch ? 'production' : 'development',
NODE_OPTIONS: '--openssl-legacy-provider',
},
buildTypeDocs();
buildDocsSite({
docsUrl: firebaseChannelUrl,
nodeEnv: bkEnv.isMainBranch ? 'production' : 'development',
});

const outDir = `docs/build`;

await compress({
src: outDir,
src: docsOutDir,
dest: '.buildkite/artifacts/docs/firebase.gz',
});

if (bkEnv.isMainBranch) {
startGroup('Building docs - github pages');
await exec('yarn build', {
cwd: 'docs',
env: {
DOCUSAURUS_URL: 'https://elastic.github.io',
DOCUSAURUS_BASE_URL: '/elastic-charts',
NODE_ENV: 'production',
NODE_OPTIONS: '--openssl-legacy-provider',
},
});

await compress({
src: outDir,
dest: '.buildkite/artifacts/docs/github.gz',
});
}
})();
13 changes: 4 additions & 9 deletions .buildkite/scripts/steps/storybook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* Side Public License, v 1.
*/

import { bkEnv, compress, exec, startGroup, yarnInstall } from '../../utils';
import { bkEnv, buildStorybookSite, compress, startGroup, storybookOutDir, yarnInstall } from '../../utils';
import { createDeploymentStatus, createOrUpdateDeploymentComment } from '../../utils/deployment';

void (async () => {
Expand All @@ -21,17 +21,12 @@ void (async () => {
}

startGroup('Building storybook');
await exec('yarn build', {
cwd: 'storybook',
env: {
NODE_ENV: bkEnv.isMainBranch ? 'production' : 'development',
NODE_OPTIONS: '--openssl-legacy-provider',
},
buildStorybookSite({
nodeEnv: bkEnv.isMainBranch ? 'production' : 'development',
});

const outDir = `.out`;
await compress({
src: outDir,
src: storybookOutDir,
dest: '.buildkite/artifacts/storybook.gz',
});
})();
10 changes: 2 additions & 8 deletions .buildkite/steps/ghp_deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,16 @@
*/

import type { CustomCommandStep } from '../utils';
import { createStep, commandStepDefaults, bkEnv } from '../utils';
import { createStep, commandStepDefaults } from '../utils';

export const ghpDeployStep = createStep<CustomCommandStep>(() => {
const isMainBranch = bkEnv.isMainBranch;

return {
...commandStepDefaults,
label: ':github: Deploy - GitHub Pages',
key: 'deploy_ghp',
ignoreForced: true,
skip: isMainBranch ? false : 'Not target branch',
skip: 'Public GitHub Pages publishing is handled by the release workflow',
depends_on: ['build_docs', 'build_storybook'],
commands: ['npx ts-node .buildkite/scripts/steps/ghp_deploy.ts'],
env: {
// ignore check run reporting when not main
ECH_CHECK_ID: isMainBranch ? 'deploy_ghp' : undefined,
},
};
});
1 change: 1 addition & 0 deletions .buildkite/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ export * from './exec';
export * from './pipeline';
export * from './firebase';
export * from './common';
export * from './site';
114 changes: 114 additions & 0 deletions .buildkite/utils/site.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { spawnSync } from 'child_process';
import fs from 'fs';
import path from 'path';

const repoRoot = path.resolve(__dirname, '../..');
const docsDir = path.join(repoRoot, 'docs');
const storybookDir = path.join(repoRoot, 'storybook');

export const docsOutDir = path.join(docsDir, 'build');
export const storybookOutDir = path.join(repoRoot, '.out');

interface CommandOptions {
cwd?: string;
env?: NodeJS.ProcessEnv;
}

interface DocsBuildOptions {
docsUrl: string;
nodeEnv: 'production' | 'development';
baseUrl?: string;
}

interface StorybookBuildOptions {
nodeEnv: 'production' | 'development';
}

interface AssembleSiteOptions {
outDir: string;
docsSourceDir?: string;
storybookSourceDir?: string;
}

/**
* Generates the TypeDoc content consumed by the Docusaurus docs build.
*/
export function buildTypeDocs() {
run('yarn', ['typedoc']);
}

/**
* Builds the Docusaurus site for a specific public base URL.
*/
export function buildDocsSite({ docsUrl, nodeEnv, baseUrl }: DocsBuildOptions) {
run('yarn', ['build'], {
cwd: docsDir,
env: {
DOCUSAURUS_URL: docsUrl,
...(baseUrl ? { DOCUSAURUS_BASE_URL: baseUrl } : {}),
NODE_ENV: nodeEnv,
NODE_OPTIONS: toNodeOptions(),
},
});
}

/**
* Builds the static Storybook bundle used by preview and release publishing flows.
*/
export function buildStorybookSite({ nodeEnv }: StorybookBuildOptions) {
run('yarn', ['build'], {
cwd: storybookDir,
env: {
NODE_ENV: nodeEnv,
NODE_OPTIONS: toNodeOptions(),
},
});
}

/**
* Assembles the final static site tree with docs at the root and Storybook under `/storybook`.
*/
export function assembleSite({
outDir,
docsSourceDir = docsOutDir,
storybookSourceDir = storybookOutDir,
}: AssembleSiteOptions) {
fs.rmSync(outDir, { force: true, recursive: true });
fs.cpSync(docsSourceDir, outDir, { recursive: true });
fs.cpSync(storybookSourceDir, path.join(outDir, 'storybook'), { recursive: true });
fs.writeFileSync(path.join(outDir, '.nojekyll'), '');

ensureFileExists(path.join(outDir, 'index.html'));
ensureFileExists(path.join(outDir, 'storybook', 'index.html'));
}

function ensureFileExists(filePath: string) {
if (!fs.existsSync(filePath)) {
throw new Error(`Expected build output at ${path.relative(repoRoot, filePath)}`);
}
}

function run(command: string, args: string[], { cwd = repoRoot, env = {} }: CommandOptions = {}) {
const result = spawnSync(command, args, {
cwd,
env: { ...process.env, ...env },
shell: process.platform === 'win32',
stdio: 'inherit',
});

if (result.status !== 0) {
throw new Error(`Command failed: ${command} ${args.join(' ')}`);
}
}

function toNodeOptions() {
return [process.env.NODE_OPTIONS, '--openssl-legacy-provider'].filter(Boolean).join(' ');
}
68 changes: 65 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,30 @@
name: Publish a Release

on:
workflow_dispatch
workflow_dispatch:
inputs:
dry_run:
description: "--dry-run - Run workflow without publishing a release?"
required: true
default: false
type: boolean
publish_pages:
description: "Also publish `elastic.github.io/elastic-charts` site?"
required: true
default: true
type: boolean
pages_target_branch:
description: "github.io branch, only change when testing"
required: true
default: gh-pages
type: string

permissions:
contents: read # for checkout

env:
ECH_NODE_VERSION: '22.22.0'
RELEASE_SITE_DIR: .release-site

jobs:
checks:
Expand Down Expand Up @@ -40,7 +57,7 @@ jobs:
with:
working-directory: e2e
useRollingCache: true
- name: Install e2e node_modules
- name: Install buildkite node_modules
uses: bahmutov/npm-install@v1.10.9
with:
working-directory: .buildkite
Expand Down Expand Up @@ -84,15 +101,60 @@ jobs:
uses: bahmutov/npm-install@v1.10.9
with:
useRollingCache: true
- name: Install docs node_modules
uses: bahmutov/npm-install@v1.10.9
with:
working-directory: docs
useRollingCache: true
- name: Install Buildkite node_modules
uses: bahmutov/npm-install@v1.10.9
with:
working-directory: .buildkite
useRollingCache: true

- name: Validate Pages publish parameters
if: ${{ inputs.dry_run && inputs.publish_pages && inputs.pages_target_branch == 'gh-pages' }}
run: |
echo "Dry-run releases may only publish to a non-production Pages branch." >&2
exit 1

- name: Build library
run: yarn build

- name: Build release site
run: yarn --cwd .buildkite build:release-site

- name: Upload release site artifact
uses: actions/upload-artifact@v4
with:
name: release-site
path: ${{ env.RELEASE_SITE_DIR }}
retention-days: 5
include-hidden-files: true

- name: Upgrade npm for trusted publishing (OIDC) support
if: ${{ !inputs.dry_run }}
run: npm install -g npm@11.5.1

- name: Release
env:
GITHUB_TOKEN: ${{ secrets.ADMIN_TOKEN_GH }}
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
run: yarn semantic-release
run: |
if [ "${{ inputs.dry_run }}" = "true" ]; then
yarn semantic-release --dry-run
else
yarn semantic-release
fi

- name: Publish GitHub Pages branch
if: ${{ inputs.publish_pages }}
uses: peaceiris/actions-gh-pages@v4
with:
personal_token: ${{ secrets.ADMIN_TOKEN_GH }}
publish_branch: ${{ inputs.pages_target_branch }}
publish_dir: ${{ env.RELEASE_SITE_DIR }}
force_orphan: true
user_name: elastic-datavis[bot]
user_email: 98618603+elastic-datavis[bot]@users.noreply.github.com
full_commit_message: Deploy release site for ${{ github.sha }}
Loading