diff --git a/docusaurus.config.ts b/docusaurus.config.ts
index c8bdc924209..018dfb5fbef 100644
--- a/docusaurus.config.ts
+++ b/docusaurus.config.ts
@@ -26,6 +26,10 @@ const config: Config = {
customFields: {
SEGMENT_API_KEY: process.env.SEGMENT_API_KEY,
HARNESS_GENERIC_READ_ONLY_KEY: process.env.HARNESS_GENERIC_READ_ONLY_KEY,
+ future: {
+ v4: true,
+ experimental_faster: true,
+ },
},
//Mermaid Diagram Functionality
@@ -47,10 +51,6 @@ const config: Config = {
locales: ['en'],
},
- future: {
- experimental_faster: true,
- },
-
presets: [
[
'classic',
@@ -439,6 +439,10 @@ const config: Config = {
label: 'Feature Requests',
to: 'https://ideas.harness.io',
},
+ {
+ label: 'Feature Flags GA List',
+ to: '/feature-flags',
+ },
{
label: 'Instructor-Led Training',
to: '/university?ilt',
@@ -528,6 +532,16 @@ const config: Config = {
},
},
],
+ [
+ path.resolve(__dirname, './plugins/docs-rss-plugin'),
+ {
+ id: 'feature-flags',
+ path: 'feature-flags',
+ routeBasePath: 'feature-flags',
+ exclude: ['**/shared/**', '**/static/**', '**/content/**'],
+ editUrl: 'https://github.com/harness/developer-hub/tree/main',
+ },
+ ],
// redirect plugin start
[
path.resolve(__dirname, './plugins/docsEnhanced-plugin'),
@@ -613,6 +627,7 @@ const config: Config = {
path.join(__dirname, '/plugins/utmcookie-plugin'),
path.join(__dirname, '/plugins/focusOnAnchor-plugin'),
path.join(__dirname, '/plugins/feedback-plugin'),
+ path.join(__dirname, '/plugins/feature-flags-rss-plugin'),
],
clientModules: [
path.join(__dirname, '/client-modules/searchBar'),
diff --git a/feature-flags/index.md b/feature-flags/index.md
new file mode 100644
index 00000000000..2e18a726831
--- /dev/null
+++ b/feature-flags/index.md
@@ -0,0 +1,13 @@
+---
+sidebar_position: 0
+hide_table_of_contents: true
+hide_title: true
+title: Feature Flags
+slug: /
+date: 2024-11-13T10:00
+---
+
+import FeatureFlagsLanding from '@site/src/components/FeatureFlagGA/FeatureFlagsLanding';
+import ffGAFeed from './static/ff-ga-feed.json';
+
+
diff --git a/feature-flags/static/ff-ga-feed.json b/feature-flags/static/ff-ga-feed.json
new file mode 100644
index 00000000000..aeca9ee93db
--- /dev/null
+++ b/feature-flags/static/ff-ga-feed.json
@@ -0,0 +1,128 @@
+[
+ {
+ "flagKey": "CDS_ADD_DEPLOYMENT_FREEZE_BYPASS_AUDIT",
+ "description": "Enables audit logging for deployment freeze bypass actions.",
+ "gaStartDate": "2025-09-15",
+ "module": "Continuous Delivery"
+ },
+ {
+ "flagKey": "CDS_DEPLOYMENT_FREEZE_GRANULAR_RBAC",
+ "description": "Provides granular role-based access control for deployment freeze operations based on environment type.",
+ "gaStartDate": "2025-09-15",
+ "module": "Continuous Delivery"
+ },
+ {
+ "flagKey": "CDS_GITLAB_TRIGGER_TAG_EVENT",
+ "description": "Enables GitLab tag event triggers for pipelines.",
+ "gaStartDate": "2025-09-15",
+ "module": "Continuous Delivery"
+ },
+ {
+ "flagKey": "CDS_TF_POLICY_EVALUATION",
+ "description": "Enables Terraform policy evaluation in Continuous Delivery workflows.",
+ "gaStartDate": "2025-09-15",
+ "module": "Continuous Delivery"
+ },
+ {
+ "flagKey": "CDS_TEXTAREA_FOR_OVERRIDE_VARIABLES",
+ "description": "Provides textarea input for override variables in deployment configurations.",
+ "gaStartDate": "2025-09-15",
+ "module": "Continuous Delivery"
+ },
+ {
+ "flagKey": "CDS_TAS_LOGIN_OPTIMIZATION",
+ "description": "Optimizes Tanzu Application Service (TAS) login performance and reliability by reducing the number of CLI initialisation calls.",
+ "gaStartDate": "2025-09-15",
+ "module": "Continuous Delivery"
+ },
+ {
+ "flagKey": "CDS_SUPPORT_TF_CLOUD_PLAN_REFRESH_TYPE",
+ "description": "Adds support for Terraform Cloud plan refresh type operations.",
+ "gaStartDate": "2025-09-15",
+ "module": "Continuous Delivery"
+ },
+ {
+ "flagKey": "CDS_UI_ENABLE_DISALLOWED_USER_EMAILS_IN_APPROVAL_STEP",
+ "description": "Enables configuration to block certain users from approving in Harness Approval steps based on emails.",
+ "gaStartDate": "2025-09-15",
+ "module": "Pipeline"
+ },
+ {
+ "flagKey": "CDS_AZURE_OIDC_AUTHENTICATION",
+ "description": "Enables OIDC authentication for Azure connectors.",
+ "gaStartDate": "2025-09-15",
+ "module": "Continuous Delivery"
+ },
+ {
+ "flagKey": "CDS_GCP_LIST_PROJECTS_AUTH_FIX",
+ "description": "Fixes authentication issues when listing Google Cloud Platform projects.",
+ "gaStartDate": "2025-09-15",
+ "module": "Continuous Delivery"
+ },
+ {
+ "flagKey": "CDS_GCP_OIDC_CONNECTOR_CROSS_PROJECT_ACCESS",
+ "description": "Enables cross-project access for Google Cloud Platform connectors.",
+ "gaStartDate": "2025-09-15",
+ "module": "Continuous Delivery"
+ },
+ {
+ "flagKey": "CDS_OPTIONAL_VALUES_YAML",
+ "description": "Enables optional values.yaml files in K8s & Helm deployments.",
+ "gaStartDate": "2025-09-15",
+ "module": "Continuous Delivery"
+ },
+ {
+ "flagKey": "CDS_ENFORCE_GIT_EXPERIENCE",
+ "description": "Enforces Git-based experience for CD entities such as service, environment, infrastructure and override configurations.",
+ "gaStartDate": "2025-09-15",
+ "module": "Continuous Delivery"
+ },
+ {
+ "flagKey": "CDS_K8S_DETAILED_LOGS",
+ "description": "Provides detailed logging for Kubernetes operations in CD.",
+ "gaStartDate": "2025-09-15",
+ "module": "Continuous Delivery"
+ },
+ {
+ "flagKey": "CDS_INFRA_DEFINITION_VALIDATION",
+ "description": "Enforces certain validation for infrastructure definitions in CD.",
+ "gaStartDate": "2025-09-15",
+ "module": "Continuous Delivery"
+ },
+ {
+ "flagKey": "CDS_USE_SECONDARY_NODE_FOR_TIMESCALE_QUERIES",
+ "description": "Internal optimisation to use secondary database nodes for TimescaleDB queries to improve performance.",
+ "gaStartDate": "2025-09-15",
+ "module": "Continuous Delivery"
+ },
+ {
+ "flagKey": "CDS_ADD_EXPRESSION_RESOLUTION_FOR_OVERRIDES_V2_AT_SERVICE_STEP",
+ "description": "Fixes a bug that resolves expressions in overrides at the service step.",
+ "gaStartDate": "2025-09-15",
+ "module": "Continuous Delivery"
+ },
+ {
+ "flagKey": "CDS_OVERRIDES_GITX",
+ "description": "Enables Git experience for service and infrastructure overrides management.",
+ "gaStartDate": "2025-09-15",
+ "module": "Continuous Delivery"
+ },
+ {
+ "flagKey": "CDS_EVENT_BRIDGE_WEBHOOK",
+ "description": "Enables Event Relay triggers for pipelines.",
+ "gaStartDate": "2025-09-15",
+ "module": "Continuous Delivery"
+ },
+ {
+ "flagKey": "CDS_SERVICE_INFRA_FAILURE_STRATEGY",
+ "description": "Implements failure strategies for service and infrastructure deployment steps.",
+ "gaStartDate": "2025-09-15",
+ "module": "Continuous Delivery"
+ },
+ {
+ "flagKey": "CDS_OVERRIDES_V2_IDENTIFIER_SUPPORT",
+ "description": "Adds identifier support for version 2 service and infrastructure overrides.",
+ "gaStartDate": "2025-09-15",
+ "module": "Continuous Delivery"
+ }
+]
\ No newline at end of file
diff --git a/package.json b/package.json
index 468f423d2ca..e2d73b8eeb7 100644
--- a/package.json
+++ b/package.json
@@ -4,6 +4,7 @@
"license": "MIT",
"private": true,
"scripts": {
+ "prebuild": "node scripts/generate-feature-flags-rss.js",
"docusaurus": "docusaurus",
"start": "docusaurus start --host 0.0.0.0",
"build": "export NODE_OPTIONS=--max-old-space-size=7296 && DOCUSAURUS_IGNORE_SSG_WARNINGS=true docusaurus build",
diff --git a/plugins/feature-flags-rss-plugin/index.js b/plugins/feature-flags-rss-plugin/index.js
new file mode 100644
index 00000000000..7750a388ece
--- /dev/null
+++ b/plugins/feature-flags-rss-plugin/index.js
@@ -0,0 +1,72 @@
+const fs = require('fs-extra');
+const path = require('path');
+const { Feed } = require('feed');
+
+function filterAndSortLast3Months(entries) {
+ const now = new Date();
+ const threeMonthsAgo = new Date();
+ threeMonthsAgo.setMonth(now.getMonth() - 3);
+ const filtered = entries.filter(entry => {
+ const entryDate = new Date(entry.gaStartDate);
+ return entryDate >= threeMonthsAgo;
+ });
+ filtered.sort((a, b) => new Date(b.gaStartDate) - new Date(a.gaStartDate));
+ return filtered;
+}
+
+// WARNING: This plugin generates the RSS file in postBuild, so it will NOT be available in dev mode (docusaurus start),
+// and links to /feature-flags/rss.xml will be flagged as broken unless onBrokenLinks is set to 'warn'.
+
+async function featureFlagsRssPlugin(context, options) {
+ return {
+ name: 'feature-flags-rss-plugin',
+ async postBuild({ outDir }) {
+ const jsonPath = path.join(
+ context.siteDir,
+ 'feature-flags/static/ff-ga-feed.json'
+ );
+ if (!fs.existsSync(jsonPath)) {
+ console.warn('[feature-flags-rss-plugin] JSON file not found:', jsonPath);
+ return;
+ }
+ const data = JSON.parse(fs.readFileSync(jsonPath, 'utf-8'));
+ const filtered = filterAndSortLast3Months(data);
+ if (!filtered || filtered.length === 0) {
+ console.warn('[feature-flags-rss-plugin] No entries for RSS feed. Skipping RSS file generation.');
+ return;
+ }
+ const siteUrl = context.siteConfig.url || '';
+ const baseUrl = context.siteConfig.baseUrl || '/';
+ const rssFeed = new Feed({
+ title: 'Feature Flags GA RSS Feed',
+ id: siteUrl + baseUrl + 'feature-flags/rss.xml',
+ link: siteUrl + baseUrl + 'feature-flags/rss.xml',
+ updated: new Date(),
+ feedLinks: {
+ rss: siteUrl + baseUrl + 'feature-flags/rss.xml',
+ },
+ });
+ filtered.forEach(item => {
+ rssFeed.addItem({
+ title: item.flagKey,
+ id: item.flagKey,
+ link: siteUrl + baseUrl + 'feature-flags/',
+ description: item.description,
+ date: new Date(item.gaStartDate),
+ category: [
+ {
+ name: item.module,
+ term: item.module,
+ },
+ ],
+ });
+ });
+ const outputPathRSS = path.join(outDir, 'feature-flags', 'rss.xml');
+ fs.ensureDirSync(path.dirname(outputPathRSS));
+ fs.writeFileSync(outputPathRSS, rssFeed.rss2());
+ console.log('[feature-flags-rss-plugin] RSS feed generated at', outputPathRSS);
+ },
+ };
+}
+
+module.exports = featureFlagsRssPlugin;
\ No newline at end of file
diff --git a/scripts/generate-feature-flags-rss.js b/scripts/generate-feature-flags-rss.js
new file mode 100644
index 00000000000..d82ba5e8d79
--- /dev/null
+++ b/scripts/generate-feature-flags-rss.js
@@ -0,0 +1,45 @@
+const fs = require('fs-extra');
+const path = require('path');
+const { Feed } = require('feed');
+
+const jsonPath = path.join(__dirname, '../feature-flags/static/ff-ga-feed.json');
+const outputPath = path.join(__dirname, '../build/feature-flags/rss.xml');
+const siteUrl = 'https://developer.harness.io';
+const baseUrl = '/';
+
+if (!fs.existsSync(jsonPath)) {
+ console.warn('[ff-ga-rss] JSON file not found:', jsonPath);
+ process.exit(0);
+}
+const data = JSON.parse(fs.readFileSync(jsonPath, 'utf-8'));
+if (!Array.isArray(data) || data.length === 0) {
+ console.warn('[ff-ga-rss] No entries for RSS feed. Skipping RSS file generation.');
+ process.exit(0);
+}
+const feed = new Feed({
+ title: 'Feature Flags GA RSS Feed',
+ id: siteUrl + baseUrl + 'feature-flags/rss.xml',
+ link: siteUrl + baseUrl + 'feature-flags/rss.xml',
+ updated: new Date(),
+ feedLinks: {
+ rss: siteUrl + baseUrl + 'feature-flags/rss.xml',
+ },
+});
+data.forEach(item => {
+ feed.addItem({
+ title: item.flagKey,
+ id: item.flagKey,
+ link: siteUrl + baseUrl + 'feature-flags/',
+ description: item.description,
+ date: new Date(item.gaStartDate),
+ category: [
+ {
+ name: item.module,
+ term: item.module,
+ },
+ ],
+ });
+});
+fs.ensureDirSync(path.dirname(outputPath));
+fs.writeFileSync(outputPath, feed.rss2());
+console.log('[ff-ga-rss] RSS feed generated at', outputPath);
\ No newline at end of file
diff --git a/src/components/FeatureFlagGA/FeatureFlagsGAListPage.tsx b/src/components/FeatureFlagGA/FeatureFlagsGAListPage.tsx
new file mode 100644
index 00000000000..5c0bdbc8f5a
--- /dev/null
+++ b/src/components/FeatureFlagGA/FeatureFlagsGAListPage.tsx
@@ -0,0 +1,65 @@
+import React from 'react';
+import Link from '@docusaurus/Link';
+import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
+import styles from './ga-list.module.css';
+import releaseNotesStyles from '@site/src/components/ReleaseNotes/styles.module.scss';
+
+function FeatureFlagsGATable({ flags }) {
+ return (
+
+
+
+
+ Flag Key
+ Description
+ GA Start Date
+ Module
+
+
+
+ {flags.map((flag) => (
+
+ {flag.flagKey}
+ {flag.description}
+ {new Date(flag.gaStartDate).toLocaleDateString()}
+ {flag.module}
+
+ ))}
+
+
+
+ );
+}
+
+export default function FeatureFlagsGAListPage({ flags = [], loading = false, error = null, compact = false }) {
+ const { siteConfig: { baseUrl = '/' } = {} } = useDocusaurusContext();
+ if (loading) return Loading...
;
+ if (error) return {error}
;
+ if (compact) {
+ return ;
+ }
+ return (
+
+
+
+
+
+
+
+ Subscribe via Atom
+
+
+
+
+
+
+
+ This page lists all Feature Flags that have reached General Availability (GA) in the last several months. You can track when a flag was GA'd, view its description, and filter by module. For a machine-readable feed, see the Atom link above.
+
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/components/FeatureFlagGA/FeatureFlagsLanding.tsx b/src/components/FeatureFlagGA/FeatureFlagsLanding.tsx
new file mode 100644
index 00000000000..301a28e4ba6
--- /dev/null
+++ b/src/components/FeatureFlagGA/FeatureFlagsLanding.tsx
@@ -0,0 +1,31 @@
+import React from 'react';
+import FeatureFlagsGAListPage from './FeatureFlagsGAListPage';
+
+export default function FeatureFlagsLanding({ staticFlags }) {
+ return (
+
+
+
+
+
Feature Flags GA Timeline
+
+
+
+
+ Track when Harness Feature Flags reach General Availability (GA). This timeline shows GA dates, descriptions, and the modules where each feature flag was implemented.
+
+
+
+
+
About This Timeline
+
+ View GA dates for all major Harness Feature Flags
+ Filter Feature Flags by module and date
+ Access detailed descriptions of each Feature Flag
+ Subscribe to updates via the Atom feed below
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/components/FeatureFlagGA/ga-list.module.css b/src/components/FeatureFlagGA/ga-list.module.css
new file mode 100644
index 00000000000..34965c6d5f7
--- /dev/null
+++ b/src/components/FeatureFlagGA/ga-list.module.css
@@ -0,0 +1,79 @@
+.infoBlock {
+ background: var(--ifm-alert-background-color, #f5f6fa);
+ border-left: 4px solid var(--ifm-color-primary, #00adef);
+ padding: 1rem 1.5rem;
+ margin-bottom: 1.5rem;
+ border-radius: 4px;
+ color: var(--ifm-font-color-base, #222);
+}
+
+.tableWrapper {
+ overflow-x: auto;
+ margin-top: 1.5rem;
+ margin-bottom: 2rem;
+}
+
+.gaTable {
+ width: 100%;
+ border-collapse: collapse;
+ background: white;
+ box-shadow: 0 1.5px 3px 0 rgb(0 0 0 / 8%);
+}
+
+.gaTable th, .gaTable td {
+ border: 1px solid var(--ifm-table-border-color, #eaeaea);
+ padding: 0.75rem 1rem;
+ text-align: left;
+}
+
+.gaTable th {
+ background: var(--ifm-table-head-background, #f5f6fa);
+ color: var(--ifm-color-primary, #00adef);
+ font-weight: 600;
+}
+
+.gaTable tr:nth-child(even) {
+ background: var(--ifm-table-stripe-background, #fafbfc);
+}
+
+.gaTable tr:hover {
+ background: var(--ifm-hover-overlay, #f0f4fa);
+}
+
+.btnContainer {
+ display: flex;
+ align-items: center;
+ align-content: center;
+ flex-shrink: 0;
+ flex-flow: row wrap;
+ gap: 12px;
+}
+
+.btn {
+ padding: 12px 20px;
+ min-width: 180px;
+ background: var(--gray-100);
+ background: #f3f3fa;
+ border: 1px solid var(--gray-300);
+ border-radius: 4px;
+ font-weight: 600;
+ font-size: 16px;
+ line-height: 24px;
+ color: var(--gray-600);
+ white-space: nowrap;
+ display: flex;
+ align-items: center;
+ cursor: pointer;
+ filter: drop-shadow(0px 0px 1px rgba(40, 41, 61, 0.04))
+ drop-shadow(0px 2px 4px rgba(96, 97, 112, 0.16));
+ transition: background 0.2s, color 0.2s;
+}
+.btn:hover {
+ color: var(--primary-8);
+ background: #fafbfc;
+}
+.btn img {
+ margin-right: 4px;
+ height: 24px;
+ width: auto;
+}
\ No newline at end of file
diff --git a/src/components/FeatureFlagGA/ga-list.tsx b/src/components/FeatureFlagGA/ga-list.tsx
new file mode 100644
index 00000000000..195b23e9d39
--- /dev/null
+++ b/src/components/FeatureFlagGA/ga-list.tsx
@@ -0,0 +1,90 @@
+import React, { useEffect, useState } from 'react';
+import Layout from '@theme/Layout';
+import DocsButton from '../DocsButton';
+import styles from './ga-list.module.css';
+
+const FEED_URL = '/feature-flags/static/ff-ga-feed.json';
+const ATOM_URL = 'https://developer.harness.io/feature-flags/rss.xml';
+
+interface FFGAEntry {
+ flagKey: string;
+ description: string;
+ gaStartDate: string;
+ module: string;
+}
+
+const FeatureFlagsGAList: React.FC = () => {
+ const [flags, setFlags] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+
+ useEffect(() => {
+ fetch(FEED_URL)
+ .then((res) => {
+ if (!res.ok) throw new Error('Failed to fetch GA feed');
+ return res.json();
+ })
+ .then((data: FFGAEntry[]) => {
+ // Sort descending by date
+ setFlags(
+ data.sort((a, b) => new Date(b.gaStartDate).getTime() - new Date(a.gaStartDate).getTime())
+ );
+ setLoading(false);
+ })
+ .catch((err) => {
+ setError(err.message);
+ setLoading(false);
+ });
+ }, []);
+
+ return (
+
+
+ Feature Flags General Availability (GA) List
+
+
About Feature Flags GA List
+
+ This page lists all Feature Flags that have reached General Availability (GA) in the last several months.
+ You can track when a flag was GA'd, view its description, and filter by module.
+ For a machine-readable feed, see the Atom link below.
+
+
+
+ {loading && Loading...
}
+ {error && {error}
}
+ {!loading && !error && (
+
+
+
+
+ Flag Key
+ Description
+ GA Start Date
+ Module
+
+
+
+ {flags.map((flag) => (
+
+ {flag.flagKey}
+ {flag.description}
+ {new Date(flag.gaStartDate).toLocaleDateString()}
+ {flag.module}
+
+ ))}
+
+
+
+ )}
+
+
+ );
+};
+
+export default FeatureFlagsGAList;
\ No newline at end of file