Repository: elastic/eui
Pull request #8638: chore(website): add postrelease script
Author: login: weronikaolejniczak
Labels: name: skip-changelog
description: Use on PRs to skip changelog requirement (Don't delete - used for automation)
Created: 2025-04-25T11:57:47Z | Merged: 2025-04-29T16:15:46Z

## Summary

Closes https://github.com/elastic/eui-private/issues/316

This PR adds a `postrelease` script to `@elastic/eui-website` package so that on each EUI release, a new version is added to the `versions.json` file, and subsequently to the version switcher.

Only official releases following the semantic versioning `vX.Y.Z` are added. Snapshot and backport releases are skipped.

## QA

### Instructions

1. Checkout this branch: `gh checkout pr 8638`
2. Ensure you're logged out of npm: `yarn npm logout`
3. Run a local registry to which the packages are publish instead of npm: `docker run -it --rm --name verdaccio -p 4873:4873 verdaccio/verdaccio`
4. Update the root `.yarnrc.yml` file by adding:
```yaml
npmRegistryServer: "http://localhost:4873"
unsafeHttpWhitelist:
  - localhost
  - "localhost:4873"
```
5. Login to the local registry. When asked for a user and password you can set anything, e.g. test/test. When asked for an OTP token click Enter, as the local registry has no two-factor authentication configured and OTP is not needed. `yarn npm login`
6. Disable checking if the working directory is clean in `packages/release-cli/src/git_utils.ts` by returning true in `isWorkingTreeClean():`
```tsx
# example
export const isWorkingTreeClean = async () => {
  return true;
};
```
7. Uncomment `packages/release-cli/src/steps/init_checks.ts:34` check. We're running the official release script on verdaccio instance so we don't need to worry about launching it not from `main`. **Be sure you changed `.yarnrc.yml` file!**
```tsx
if (currentBranch !== 'main' && type === 'official') {
    throw new ValidationError(
      'Official releases are only allowed from the `main` branch!'
    );
}
```
8. Build the dependencies: `yarn`
9. Build the `release-cli` package: `yarn workspace @elastic/eui-release-cli run build`
10. Build the release from the monorepo root: `yarn release run official --workspaces @elastic/eui-theme-common @elastic/eui-theme-borealis @elastic/eui`

### Checklist

- [x] Verify that you have correct staged changes in `packages/website/static/versions.json`
- [ ] Verify that a snapshot release is skipped: `yarn release run snapshot --allow-custom --workspaces @elastic/eui-theme-common @elastic/eui-theme-borealis @elastic/eui`
- [ ] Verify that on releasing e.g. `@elastic/eslint-plugin-eui` (by adding a temporary changelog) it's released without any issue and `packages/website/static/versions.json` remains unchanged


Diff:
diff --git a/packages/website/package.json b/packages/website/package.json
index 347fcf1acd76..f94a0d46e6cb 100644
--- a/packages/website/package.json
+++ b/packages/website/package.json
@@ -14,7 +14,8 @@
     "write-translations": "docusaurus write-translations",
     "write-heading-ids": "docusaurus write-heading-ids",
     "typecheck": "tsc",
-    "spellcheck": "cspell ./docs"
+    "spellcheck": "cspell ./docs",
+    "postrelease": "node ./scripts/update-versions.js"
   },
   "dependencies": {
     "@docusaurus/core": "^3.7.0",
diff --git a/packages/website/scripts/update-versions.js b/packages/website/scripts/update-versions.js
new file mode 100644
index 000000000000..5731df24d894
--- /dev/null
+++ b/packages/website/scripts/update-versions.js
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+const path = require('path');
+const fs = require('fs');
+const { exec } = require('child_process');
+
+const version = require('@elastic/eui/package.json').version;
+const versionsFile = path.join(__dirname, '../static/versions.json');
+
+const isValidVersion = (version) => {
+  const semverRegex = /^\d+\.\d+\.\d+$/;
+  return semverRegex.test(version);
+};
+
+/**
+ * Updates the `versions.json` file used by the documentation version switcher.
+ *
+ * To test locally, run `node ./scripts/update-versions.js`,
+ * then verify the contents of `static/versions.json`.
+ */
+
+const updateVersions = async () => {
+  try {
+    const fileContent = fs.readFileSync(versionsFile, 'utf8');
+    const jsonData = JSON.parse(fileContent);
+
+    if (!jsonData.euiVersions || !Array.isArray(jsonData.euiVersions)) {
+      throw new Error(
+        'Invalid JSON structure: Missing or invalid `euiVersions` array'
+      );
+    }
+
+    if (!isValidVersion(version)) {
+      console.warn(
+        'Skipping update: Invalid version format or unofficial release'
+      );
+      return;
+    }
+
+    if (jsonData.euiVersions.includes(version)) {
+      console.warn('Skipping update: Current version has already been logged');
+      return;
+    }
+
+    // Prepend the new version
+    jsonData.euiVersions.unshift(version);
+
+    // Write the updated JSON back to the file
+    fs.writeFileSync(versionsFile, JSON.stringify(jsonData, null, 2));
+
+    // Stage the file for commit
+    exec(`git add ${versionsFile}`, (err) => {
+      if (err) {
+        throw new Error('Error staging the file:', err);
+      } else {
+        console.log('Version added and file staged successfully');
+      }
+    });
+  } catch (error) {
+    throw new Error('Error updating versions:', err);
+  }
+};
+
+updateVersions();


Reviews:
user: login: tkajtoch
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-29T11:57:26Z
user: login: weronikaolejniczak
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-29T12:08:44Z
user: login: mgadewoll
body: 🟢 Changes look good to me. The script works as expected 👍 
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-29T16:15:44Z

Review comments:
diff_hunk: @@ -0,0 +1,62 @@
+const path = require('path');
path: packages/website/scripts/update-versions.js
user: login: tkajtoch
body: Could you please add the copyright comment here?
created_at: 2025-04-29T11:57:26Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,62 @@
+const path = require('path');
path: packages/website/scripts/update-versions.js
user: login: weronikaolejniczak
body: on it 🫡 
created_at: 2025-04-29T12:08:44Z
author_association: CONTRIBUTOR

PR comments:
user: login: mgadewoll
created_at: 2025-04-29T09:26:10Z
body: @weronikaolejniczak The provided testing instructions seem to not be correct.

>9. Build the release from the monorepo root: yarn release run official --workspaces @elastic/eui-theme-common @elastic/eui-theme-borealis @elastic/eui

This step is not possible as we're not on `main` branch. I guess as additional point after point 6. we'd need to also remove blocking checks in `init_checks.ts` to allow it locally?

Having run it locally this way, I can verify the `version.json` file was updated 👍 
author_association: CONTRIBUTOR
user: login: weronikaolejniczak
created_at: 2025-04-29T09:54:55Z
body: @mgadewoll you're right! I'll update for the sake of correctness. Thanks for testing! 💚 
author_association: CONTRIBUTOR
user: login: tkajtoch
created_at: 2025-04-29T11:56:33Z
body: @mgadewoll You could also add `--allow-custom` to skip this branch check :)
author_association: MEMBER
user: login: mgadewoll
created_at: 2025-04-29T12:04:36Z
body: > @mgadewoll You could also add `--allow-custom` to skip this branch check :)

@tkajtoch I did add `--allow-custom` but it was still not working. Is the check for main happening before the `--allow-custom` one? ([code](https://github.com/elastic/eui/blob/main/packages/release-cli/src/steps/init_checks.ts#L34)) 🤔 
author_association: CONTRIBUTOR
user: login: elasticmachine
created_at: 2025-04-29T12:24:28Z
body: ## :green_heart: Build Succeeded
* [Buildkite Build](https://buildkite.com/elastic/eui-deploy-docs/builds/202)
* Commit: 9c8767f7c66c56f15a84149085b6bdf609079ee9
* [Documentation website](https://eui.elastic.co/pr_8638/)


### History
* :green_heart: [Build #157](https://buildkite.com/elastic/eui-deploy-docs/builds/157) succeeded 81d097df5f85d48a5292eefaa586f209ee72a30a
* :green_heart: [Build #155](https://buildkite.com/elastic/eui-deploy-docs/builds/155) succeeded 01db7f741c285f7a54d8cb3a12b85e055ffb46c6

cc @weronikaolejniczak

<!--buildkite-pr-comment-eui-deploy-docs
{"builds":[{"buildStatus":{"state":"passed","success":true,"hasRetries":false,"hasNonPreemptionRetries":false},"url":"https://buildkite.com/elastic/eui-deploy-docs/builds/202","number":202,"commit":"9c8767f7c66c56f15a84149085b6bdf609079ee9"},{"buildStatus":{"state":"passed","success":true,"hasRetries":false,"hasNonPreemptionRetries":false},"url":"https://buildkite.com/elastic/eui-deploy-docs/builds/157","number":157,"commit":"81d097df5f85d48a5292eefaa586f209ee72a30a"},{"buildStatus":{"state":"passed","success":true,"hasRetries":false,"hasNonPreemptionRetries":false},"url":"https://buildkite.com/elastic/eui-deploy-docs/builds/155","number":155,"commit":"01db7f741c285f7a54d8cb3a12b85e055ffb46c6"}],"number":202}
buildkite-pr-comment-->
author_association: COLLABORATOR
user: login: elasticmachine
created_at: 2025-04-29T12:39:06Z
body: ## :green_heart: Build Succeeded
* [Buildkite Build](https://buildkite.com/elastic/eui-pull-request-test-and-deploy/builds/3969)
* Commit: 9c8767f7c66c56f15a84149085b6bdf609079ee9

### History
* :green_heart: [Build #3936](https://buildkite.com/elastic/eui-pull-request-test-and-deploy/builds/3936) succeeded 81d097df5f85d48a5292eefaa586f209ee72a30a

cc @weronikaolejniczak

<!--buildkite-pr-comment-eui-pull-request-test-and-deploy
{"builds":[{"buildStatus":{"state":"passed","success":true,"hasRetries":false,"hasNonPreemptionRetries":false},"url":"https://buildkite.com/elastic/eui-pull-request-test-and-deploy/builds/3969","number":3969,"commit":"9c8767f7c66c56f15a84149085b6bdf609079ee9"},{"buildStatus":{"state":"passed","success":true,"hasRetries":false,"hasNonPreemptionRetries":false},"url":"https://buildkite.com/elastic/eui-pull-request-test-and-deploy/builds/3936","number":3936,"commit":"81d097df5f85d48a5292eefaa586f209ee72a30a"}],"number":3969}
buildkite-pr-comment-->
author_association: COLLABORATOR