Skip to content

Feature flags (2/2): wiring, aliases, discovery command, native-histograms - #6056

Merged
inancgumus merged 11 commits into
masterfrom
feature-flags-integration
Jun 9, 2026
Merged

inancgumus merged 11 commits into
masterfrom
feature-flags-integration

Conversation

@inancgumus

@inancgumus inancgumus commented Jun 5, 2026 •

Copy link
Copy Markdown
Contributor

What?

Wires the feature-flag infra (#6055) and adds the first flag.

  • Adds --features to k6 run and k6 cloud run.
  • Adds k6 features and k6 features --json for discovery.
  • Adds flags usage telemetry, metric tags, and propagates K6_FEATURES into archives.

Why?

Allows users to list and toggle flags.

How it works

Discover flags:

$ k6 features
FEATURE             LIFECYCLE      DESCRIPTION
native-histograms   Experimental   Use native histograms for trend metrics

$ k6 features --json
[
  {
    "feature": "native-histograms",
    "lifecycle": "Experimental",
    "description": "Use native histograms for trend metrics"
  }
]

Enable for a local run (CLI; comma-separated or repeated --features both work):

$ k6 run --features native-histograms script.js
INFO[0000] Experimental feature enabled  feature=native-histograms lifecycle=experimental

Same via env var or JSON config (K6_FEATURES, then config.json):

$ K6_FEATURES=native-histograms k6 run script.js
INFO[0000] Experimental feature enabled  feature=native-histograms lifecycle=experimental
// config.json
{ "features": ["native-histograms"] }

Cloud run โ€” flags bake into the archive as K6_FEATURES and propagate to workers:

k6 cloud run --features native-histograms script.js

Active flags add a k6_feature_<name>="true" tag to every metric sample:

{
  "metric": "data_sent",
  "type": "Point",
  "data": {
    "value": 0,
    "tags": { "scenario": "s", "k6_feature_native_histograms": "true" }
  }
}

Legacy K6_PROMETHEUS_RW_TREND_AS_NATIVE_HISTOGRAM=true still work native-histograms, and warns:

WARN[0000] Legacy env var detected, use --features or K6_FEATURES iTREND_AS_NATIVE_HISTOGRAM feature=native-histogramssource=env_legacy_alias
INFO[0000] Experimental feature enabled  feature=native-histograms

Unknown names log an error and are skipped (run still proceeds, exit 0):

$ k6 run --features not-a-flag script.js
ERRO[0000] Unknown feature flag  feature=not-a-flag outcome=unknown source=cli

options.features in the script is unsupported and ignored with a

WARN[0000] Feature flags in script options are not supported; use --features, K6_FEATURES, or the JSON config  source=options

Precedence: --features > env (K6_FEATURES / legacy alias) > JSON.

Note

Related

@inancgumus
inancgumus temporarily deployed to azure-trusted-signing June 5, 2026 18:30 — with GitHub Actions Inactive
@inancgumus
inancgumus temporarily deployed to azure-trusted-signing June 5, 2026 18:32 — with GitHub Actions Inactive
@inancgumus
inancgumus force-pushed the feature-flags-library branch from 8a93642 to 2cefe8f Compare June 7, 2026 19:59
@inancgumus
inancgumus force-pushed the feature-flags-integration branch from f93435b to 573bb5e Compare June 7, 2026 20:00
@inancgumus
inancgumus temporarily deployed to azure-trusted-signing June 7, 2026 20:05 — with GitHub Actions Inactive
@inancgumus
inancgumus temporarily deployed to azure-trusted-signing June 7, 2026 20:07 — with GitHub Actions Inactive
@inancgumus inancgumus self-assigned this Jun 7, 2026
@inancgumus
inancgumus force-pushed the feature-flags-library branch from 2cefe8f to b1d1b01 Compare June 8, 2026 03:07
@inancgumus
inancgumus force-pushed the feature-flags-integration branch from 573bb5e to 06c3c4e Compare June 8, 2026 03:07
@inancgumus
inancgumus temporarily deployed to azure-trusted-signing June 8, 2026 03:13 — with GitHub Actions Inactive
@inancgumus
inancgumus temporarily deployed to azure-trusted-signing June 8, 2026 03:15 — with GitHub Actions Inactive
@inancgumus
inancgumus force-pushed the feature-flags-library branch from b1d1b01 to 5bf5d2e Compare June 8, 2026 14:00
@inancgumus
inancgumus force-pushed the feature-flags-integration branch from 06c3c4e to 03db4fa Compare June 8, 2026 14:00
@inancgumus
inancgumus temporarily deployed to azure-trusted-signing June 8, 2026 14:06 — with GitHub Actions Inactive
@inancgumus
inancgumus temporarily deployed to azure-trusted-signing June 8, 2026 14:08 — with GitHub Actions Inactive
@inancgumus inancgumus added this to the v2.1.0 milestone Jun 8, 2026
@inancgumus
inancgumus force-pushed the feature-flags-integration branch 2 times, most recently from 0c90bdd to 5d9be61 Compare June 8, 2026 22:04
@inancgumus
inancgumus force-pushed the feature-flags-library branch from 5bf5d2e to 07451ed Compare June 8, 2026 22:05
@inancgumus
inancgumus force-pushed the feature-flags-integration branch from 5d9be61 to 46d2ac3 Compare June 9, 2026 00:16
@inancgumus
inancgumus temporarily deployed to azure-trusted-signing June 9, 2026 00:22 — with GitHub Actions Inactive
@inancgumus
inancgumus temporarily deployed to azure-trusted-signing June 9, 2026 00:24 — with GitHub Actions Inactive
@inancgumus
inancgumus requested review from oleiade and szkiba June 9, 2026 00:34
@inancgumus
inancgumus marked this pull request as ready for review June 9, 2026 00:34
@inancgumus
inancgumus requested a review from a team as a code owner June 9, 2026 00:34
szkiba
szkiba previously approved these changes Jun 9, 2026

@szkiba szkiba left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

Base automatically changed from feature-flags-library to master June 9, 2026 15:01
@inancgumus
inancgumus dismissed szkibaโ€™s stale review June 9, 2026 15:01

The base branch was changed.

@inancgumus
inancgumus requested a review from szkiba June 9, 2026 15:01
inancgumus added 11 commits June 9, 2026 11:04
The feature definitions existed but nothing resolved or applied flags
during a run, and the one shipped flag was reachable only by outputs.

Add a --features flag (and a JSON config-file surface) to run and cloud
run, and resolve the winning surface (CLI > env > JSON) once at test-load
time, before the init context runs. Carry the resolved flags like the
secret manager: own them on the global state and copy the pointer onto
the test pre-init state, so engine code and outputs read the typed fields
directly instead of threading a config struct through the command layer.

Read the JSON surface straight from the config file so a bare FEATURES
env var can't leak in through envconfig binding, and restore an archive's
baked env before init so a worker replaying it offline resolves the
canonical K6_FEATURES the initiator baked. Report usage via
flags.Activated(), tag the derived run options via flags.Tags(), and
propagate the canonical set back into the runtime env so it bakes into
archives and reaches distributed workers.

Before, flags resolved late during config consolidation, after the JS
init context had run, and reached only the remote-write output. Now they
resolve once before init and reach the whole engine by reference.
Exercise the wiring end to end against a flag with no matching
definition: an unknown --features name logs an ERROR with source=cli,
leaves the run exiting zero, and never reaches usage telemetry.
Users had no way to see which feature flags exist or their maturity.

Add a features subcommand that calls features.All() to list flag
definitions as a table of name, lifecycle stage, and description, and
register it on the root command.
Tooling and scripts need a machine-readable feature list. Add a --json
flag that emits the same name, lifecycle, and description fields as a
JSON array.
The mechanism had no real flag, and Prometheus native-histogram mapping
lived behind a standalone env var disconnected from any shared lifecycle.

Add the experimental native-histograms flag, fold the legacy
K6_PROMETHEUS_RW_TREND_AS_NATIVE_HISTOGRAM env var into it as an alias,
deliver the resolved flags to the remote-write output, and have it gate
its Trend mapping on the typed field.
Assert k6 features prints the FEATURE/LIFECYCLE/DESCRIPTION header and at
least one flag row now that native-histograms is registered.
Assert k6 features --json emits a valid JSON array with each entry
carrying its name, lifecycle, and description.
Verify the remote-write output selects the native-histogram sink when
the native-histograms feature flag is passed to its constructor.
Drive the real flag end to end: activation from CLI, JSON config, and the
legacy env alias each tags samples and reports usage, an empty --features
overrides the alias, and an unactivated run carries no tag.
A script's exported options.features is not a supported configuration
surface, but k6 dropped it silently. lib.Options had no matching field, so
the key vanished during options decoding and the user got no feedback.

Decode the list onto lib.Options so the engine can detect it, then emit a
WARN (carrying source=options) that names the supported surfaces
(--features, K6_FEATURES, and the JSON config) without contributing it to
the activation set.
Confirm cloud run bakes the resolved K6_FEATURES into the uploaded
archive so a worker replaying it activates the feature, and that an
explicit empty --features clears an inherited value instead of leaking
it to workers.
@inancgumus
inancgumus force-pushed the feature-flags-integration branch from 46d2ac3 to d9e507e Compare June 9, 2026 15:04
@inancgumus
inancgumus temporarily deployed to azure-trusted-signing June 9, 2026 15:22 — with GitHub Actions Inactive
@inancgumus
inancgumus temporarily deployed to azure-trusted-signing June 9, 2026 15:24 — with GitHub Actions Inactive

@szkiba szkiba left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

@oleiade oleiade left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

๐Ÿš€

@inancgumus
inancgumus merged commit abda40a into master Jun 9, 2026
51 of 54 checks passed
@inancgumus
inancgumus deleted the feature-flags-integration branch June 9, 2026 15:56
@joanlopez joanlopez mentioned this pull request Jul 23, 2026
3 of 8 tasks
social4hyq pushed a commit to social4hyq/homebrew-core that referenced this pull request Sep 20, 2026
k6 2.1.0

Created-by: HarmonybrewBot
Commit-by: HarmonybrewBot
Merged-by: HarmonybrewBot
Description: Created by `brew bump`

---

Created with `brew bump-formula-pr`.<details>
  <summary>release notes</summary>
  <pre>k6 `v2.1.0` is here ๐ŸŽ‰  

This release includes:
- An opt-in feature-flag system โ€” `--features`, the `K6_FEATURES` environment variable, and a `k6 features` discovery command โ€” shipping with experimental native histograms for trend metrics as its first flag.
- A context-level `proxy` option for browser contexts.
- Subcommand discovery in `k6 x`, so binaries can report which extension commands they expose.

## Breaking changes

There are no breaking changes in this release.

## New features

### `k6 cloud test list` command [#6007](grafana/k6#6007)

A new `k6 cloud test` command group has been added, with a `k6 cloud test list` subcommand that lists the load tests of a Grafana Cloud k6 project. It complements the `k6 cloud project list` command introduced in v2.0.0.

The project to list tests for is resolved in the following order:

1. The `--project-id` flag.
2. The `K6_CLOUD_PROJECT_ID` environment variable (cloud config `projectID`).
3. The default project of the configured stack, populated by `k6 cloud login`.

Output defaults to a human-readable table. Pass `--json` to emit a JSON array instead, mirroring the format established by `k6 cloud project list`.

```shell
k6 cloud test list
k6 cloud test list --project-id 12345
k6 cloud test list --json
```

### Feature flags and experimental native histograms [#6055](grafana/k6#6055), [#6056](grafana/k6#6056)

k6 now has an opt-in feature-flag mechanism for trialing new, not-yet-stable behavior without affecting existing runs. Flags can be enabled on `k6 run` and `k6 cloud run` through the `--features` flag (comma-separated or repeated), the `K6_FEATURES` environment variable, or the `features` key in `config.json`. Enabled flags are surfaced as metric tags and propagated into archives and cloud workers so a run behaves consistently wherever it executes.

Use `k6 features` (or `k6 features --json`) to discover the available flags and their lifecycle:

```console
$ k6 features
FEATURE             LIFECYCLE      DESCRIPTION
native-histograms   Experimental   Use native histograms for trend metrics
```

The first flag shipped is `native-histograms`, an experimental flag that makes k6 use native histograms for trend metrics:

```shell
k6 run --features native-histograms script.js
# or
K6_FEATURES=native-histograms k6 run script.js
```

### Subcommand discovery in `k6 x` [#5972](grafana/k6#5972)

Running `k6 x` now lists the available subcommands โ€” both the ones baked into the binary and those advertised by the extension registry (official and community). Tab-completion surfaces the same set once the catalog has been cached locally by a prior `k6 x` run, so completion never blocks on the network.

```text
$ k6 x
...
Available Commands:
  agent       Bootstrap an AI-assisted k6 testing workflow in any editor
  docs        CLI k6 docs for AI agents and users
  explore     Explore k6 extensions for Automatic Resolution
  mcp         An MCP server for k6 for AI agents
```

This makes a k6 binary self-describing โ€” particularly useful for AI agents driving k6, which previously had no way to introspect which extension subcommands were available.

### Browser context proxy option [#5924](grafana/k6#5924)

Browser contexts can now be configured with a context-level `proxy` option, letting you route a context's traffic through a proxy without launching a custom-built binary or relying on environment proxy variables (which only affected k6's DevTools WebSocket connection). The option is wired to Chromium through `Target.createBrowserContext`, and invalid proxy configuration now fails early when `proxy.server` is missing. Thanks, @nightt5879!

```js
const context = await browser.newContext({
  proxy: {
    server: 'http://proxy.test:8080',
    bypass: 'localhost,127.0.0.1',
  },
});
```

### Browser `locator.isInViewport()` [#6023](grafana/k6#6023)

A new `locator.isInViewport()` method reports whether an element intersects the browser viewport. It accepts an optional `ratio` (0 to 1) that sets how much of the element must be visible, defaulting to `0` so any visible pixel counts, matching Playwright's `toBeInViewport` semantics. The call waits for the element to attach, honoring the `timeout` option, then measures the intersection once. Thanks, @Anuragp22!

```js
const button = page.locator('button#submit');
if (await button.isInViewport()) {
  await button.click();
}
```

### Basic auth for the OpenTelemetry HTTP exporter [#5997](grafana/k6#5997)

The OpenTelemetry output's HTTP exporter can now send HTTP Basic Auth credentials. Set them through the `K6_OTEL_HTTP_EXPORTER_USERNAME` and `K6_OTEL_HTTP_EXPORTER_PASSWORD` environment variables, or the `username` and `password` keys in the output config.

```shell
K6_OTEL_HTTP_EXPORTER_USERNAME=user \
K6_OTEL_HTTP_EXPORTER_PASSWORD=secret \
k6 run --out opentelemetry script.js
```

### `single()` selection helper in `k6/html` [#6002](grafana/k6#6002)

`Selection.single(selector)` returns at most one matching element, backed by goquery's `Single` matcher for a faster lookup than `find()` when you only need the first match. Thanks, @rohan-patnaik!

```js
import { parseHTML } from 'k6/html';

const doc = parseHTML(content);
const title = doc.single('h1').text();
```

## UX improvements and enhancements

- [#5971](grafana/k6#5971) Tags browser API failures with `module=browser` so that browser errors surfaced in Grafana Cloud Logs can be filtered separately from other log sources.

## Bug fixes

- [#5794](grafana/k6#5794) Makes the `--vus` flag work as a standalone execution shortcut instead of being silently ignored when a script defines scenarios. Running `k6 run script.js --vus N` now creates a `shared-iterations` scenario with `N` VUs and `N` iterations, overriding any script-defined scenarios with a warning โ€” consistent with how `--iterations`, `--duration`, and `--stages` already behave. Thanks, @Reranko05!
- [#6013](grafana/k6#6013) Rejects invalid threshold percentiles. A percentile aggregation value outside the 0 to 100 range (or `NaN`) now fails parsing with a clear message instead of being silently accepted. Thanks, @immanuwell!
- [#6011](grafana/k6#6011) Writes the on-disk k6 config file with owner-only permissions (`0o600`, inside a `0o700` directory). The file can hold the Grafana Cloud API token (`collectors.cloud.token`), so tightening it keeps other local users on shared hosts (CI runners, multi-user boxes, sidecar containers) from reading the token. Existing configs are upgraded on the next write, for example the next `k6 cloud login`.

## Maintenance and internal improvements

- [#6033](grafana/k6#6033) Moves Docker Hub image publishing to a Google Artifact Registry mirror.
- [#5953](grafana/k6#5953) Installs `s3cmd` via `apt` instead of `pip` to fix the `k6packager` image build.
- [#6052](grafana/k6#6052) Fixes the browser end-to-end test workflow.
- [#5984](grafana/k6#5984) Routes per-test logger output to the test instance instead of the global `logrus`, improving test isolation.
- [#5985](grafana/k6#5985) Fixes the flaky `TestPageScreenshotFullpage` browser test.
- [#5981](grafana/k6#5981) Lets Renovate track the Go minor version in the `setup-go` workflows.
- [#5968](grafana/k6#5968) Honors the caller-pinned ref in the shared lint action.
- [#6041](grafana/k6#6041), [#6054](grafana/k6#6054) Aligns the Go module directive and toolchain (1.25.0 / 1.25.11).
- [#5967](grafana/k6#5967) Updates the release notes template after v2.0.0.
- [#6015](grafana/k6#6015) Updates `golang.org/x/net` to `v0.55.0` [security].
- [#6026](grafana/k6#6026) Updates `golang.org/x/crypto` to `v0.52.0` [security].
- [#5962](grafana/k6#5962), [#6028](grafana/k6#6028) Updates `google.golang.org/grpc` to `v1.81.1`.
- [#5960](grafana/k6#5960) Updates `grafana/shared-workflows/get-vault-secrets` to `v1.3.2`.
- [#5958](grafana/k6#5958) Updates `grafana/shared-workflows/azure-trusted-signing` to `v1.0.2`.
- [#5957](grafana/k6#5957) Updates `github/codeql-action` to `v4.35.4`.
- [#5959](grafana/k6#5959) Updates `grafana/shared-workflows/dockerhub-login` to `v1.0.4`.
- [#6009](grafana/k6#6009) Moves the browser `PageScreenshotOptions` parsing into the mapping layer.
- [#5964](grafana/k6#5964) Adds the k6 feature-flags specification under `openspec/`.
- [#6067](grafana/k6#6067) Forces the legacy `x/net/http2` implementation on the `gotip` CI test job.
- [#6065](grafana/k6#6065) Lets Renovate update the `Dockerfile` on the v1.x branch.
- [#6031](grafana/k6#6031) Updates `go.opentelemetry.io/otel` to `v1.44.0`.
- [#6086](grafana/k6#6086) Updates `golang.org/x` dependencies (`crypto` to `v0.53.0`, `net` to `v0.56.0`, `term` to `v0.44.0`).
- [#6061](grafana/k6#6061) Updates `golang.org/x/sync` to `v0.21.0`.
- [#6030](grafana/k6#6030) Updates `github.com/tidwall/gjson` to `v1.19.0`.
- [#6029](https:

See merge request: Harmonybrew/homebrew-core!13169

This branch was previously deployed

1 inactive deployment
azure-trusted-signing โ€” d9e507e4 Deployed Jun 9, 2026 by inancgumus via sign-packages #9622
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature flags

3 participants